1 /*  This file, cfileio.c, contains the low-level file access routines.     */
2 
3 /*  The FITSIO software was written by William Pence at the High Energy    */
4 /*  Astrophysic Science Archive Research Center (HEASARC) at the NASA      */
5 /*  Goddard Space Flight Center.                                           */
6 
7 #include <string.h>
8 #include <stdlib.h>
9 #include <math.h>
10 #include <ctype.h>
11 #include <errno.h>
12 #include <stddef.h>  /* apparently needed to define size_t */
13 #include "fitsio2.h"
14 #include "group.h"
15 #ifdef CFITSIO_HAVE_CURL
16   #include <curl/curl.h>
17 #endif
18 
19 #define MAX_PREFIX_LEN 20  /* max length of file type prefix (e.g. 'http://') */
20 #define MAX_DRIVERS 31     /* max number of file I/O drivers */
21 
22 typedef struct    /* structure containing pointers to I/O driver functions */
23 {   char prefix[MAX_PREFIX_LEN];
24     int (*init)(void);
25     int (*shutdown)(void);
26     int (*setoptions)(int option);
27     int (*getoptions)(int *options);
28     int (*getversion)(int *version);
29     int (*checkfile)(char *urltype, char *infile, char *outfile);
30     int (*open)(char *filename, int rwmode, int *driverhandle);
31     int (*create)(char *filename, int *drivehandle);
32     int (*truncate)(int drivehandle, LONGLONG size);
33     int (*close)(int drivehandle);
34     int (*remove)(char *filename);
35     int (*size)(int drivehandle, LONGLONG *size);
36     int (*flush)(int drivehandle);
37     int (*seek)(int drivehandle, LONGLONG offset);
38     int (*read)(int drivehandle, void *buffer, long nbytes);
39     int (*write)(int drivehandle, void *buffer, long nbytes);
40 } fitsdriver;
41 
42 fitsdriver driverTable[MAX_DRIVERS];  /* allocate driver tables */
43 
44 FITSfile *FptrTable[NMAXFILES];  /* this table of Fptr pointers is */
45                                  /* used by fits_already_open */
46 
47 int need_to_initialize = 1;    /* true if CFITSIO has not been initialized */
48 int no_of_drivers = 0;         /* number of currently defined I/O drivers */
49 
50 static int pixel_filter_helper(fitsfile **fptr, char *outfile,
51 				char *expr,  int *status);
52 static int find_quote(char **string);
53 static int find_doublequote(char **string);
54 static int find_paren(char **string);
55 static int find_bracket(char **string);
56 static int find_curlybracket(char **string);
57 int comma2semicolon(char *string);
58 
59 #ifdef _REENTRANT
60 
61 pthread_mutex_t Fitsio_InitLock = PTHREAD_MUTEX_INITIALIZER;
62 
63 #endif
64 
65 /*--------------------------------------------------------------------------*/
fitsio_init_lock(void)66 int fitsio_init_lock(void)
67 {
68   int status = 0;
69 
70 #ifdef _REENTRANT
71 
72   static int need_to_init = 1;
73 
74   pthread_mutexattr_t mutex_init;
75 
76   FFLOCK1(Fitsio_InitLock);
77 
78   if (need_to_init) {
79 
80     /* Init the main fitsio lock here since we need a a recursive lock */
81 
82     status = pthread_mutexattr_init(&mutex_init);
83     if (status) {
84         ffpmsg("pthread_mutexattr_init failed (fitsio_init_lock)");
85         return(status);
86     }
87 
88 #ifdef __GLIBC__
89     status = pthread_mutexattr_settype(&mutex_init,
90 				     PTHREAD_MUTEX_RECURSIVE_NP);
91 #else
92     status = pthread_mutexattr_settype(&mutex_init,
93 				     PTHREAD_MUTEX_RECURSIVE);
94 #endif
95     if (status) {
96         ffpmsg("pthread_mutexattr_settype failed (fitsio_init_lock)");
97         return(status);
98     }
99 
100     status = pthread_mutex_init(&Fitsio_Lock,&mutex_init);
101     if (status) {
102         ffpmsg("pthread_mutex_init failed (fitsio_init_lock)");
103         return(status);
104     }
105 
106     need_to_init = 0;
107   }
108 
109   FFUNLOCK1(Fitsio_InitLock);
110 
111 #endif
112 
113     return(status);
114 }
115 /*--------------------------------------------------------------------------*/
ffomem(fitsfile ** fptr,const char * name,int mode,void ** buffptr,size_t * buffsize,size_t deltasize,void * (* mem_realloc)(void * p,size_t newsize),int * status)116 int ffomem(fitsfile **fptr,      /* O - FITS file pointer                   */
117            const char *name,     /* I - name of file to open                */
118            int mode,             /* I - 0 = open readonly; 1 = read/write   */
119            void **buffptr,       /* I - address of memory pointer           */
120            size_t *buffsize,     /* I - size of buffer, in bytes            */
121            size_t deltasize,     /* I - increment for future realloc's      */
122            void *(*mem_realloc)(void *p, size_t newsize), /* function       */
123            int *status)          /* IO - error status                       */
124 /*
125   Open an existing FITS file in core memory.  This is a specialized version
126   of ffopen.
127 */
128 {
129     int ii, driver, handle, hdutyp, slen, movetotype, extvers, extnum;
130     char extname[FLEN_VALUE];
131     LONGLONG filesize;
132     char urltype[MAX_PREFIX_LEN], infile[FLEN_FILENAME], outfile[FLEN_FILENAME];
133     char extspec[FLEN_FILENAME], rowfilter[FLEN_FILENAME];
134     char binspec[FLEN_FILENAME], colspec[FLEN_FILENAME];
135     char imagecolname[FLEN_VALUE], rowexpress[FLEN_FILENAME];
136     char *url, errmsg[FLEN_ERRMSG];
137     char *hdtype[3] = {"IMAGE", "TABLE", "BINTABLE"};
138 
139     if (*status > 0)
140         return(*status);
141 
142     *fptr = 0;                   /* initialize null file pointer */
143 
144     if (need_to_initialize)           /* this is called only once */
145     {
146         *status = fits_init_cfitsio();
147 
148         if (*status > 0)
149             return(*status);
150     }
151 
152     url = (char *) name;
153     while (*url == ' ')  /* ignore leading spaces in the file spec */
154         url++;
155 
156         /* parse the input file specification */
157     fits_parse_input_url(url, urltype, infile, outfile, extspec,
158               rowfilter, binspec, colspec, status);
159 
160     strcpy(urltype, "memkeep://");   /* URL type for pre-existing memory file */
161 
162     *status = urltype2driver(urltype, &driver);
163 
164     if (*status > 0)
165     {
166         ffpmsg("could not find driver for pre-existing memory file: (ffomem)");
167         return(*status);
168     }
169 
170     /* call driver routine to open the memory file */
171     FFLOCK;  /* lock this while searching for vacant handle */
172     *status =   mem_openmem( buffptr, buffsize,deltasize,
173                             mem_realloc,  &handle);
174     FFUNLOCK;
175 
176     if (*status > 0)
177     {
178          ffpmsg("failed to open pre-existing memory file: (ffomem)");
179          return(*status);
180     }
181 
182         /* get initial file size */
183     *status = (*driverTable[driver].size)(handle, &filesize);
184 
185     if (*status > 0)
186     {
187         (*driverTable[driver].close)(handle);  /* close the file */
188         ffpmsg("failed get the size of the memory file: (ffomem)");
189         return(*status);
190     }
191 
192         /* allocate fitsfile structure and initialize = 0 */
193     *fptr = (fitsfile *) calloc(1, sizeof(fitsfile));
194 
195     if (!(*fptr))
196     {
197         (*driverTable[driver].close)(handle);  /* close the file */
198         ffpmsg("failed to allocate structure for following file: (ffomem)");
199         ffpmsg(url);
200         return(*status = MEMORY_ALLOCATION);
201     }
202 
203         /* allocate FITSfile structure and initialize = 0 */
204     (*fptr)->Fptr = (FITSfile *) calloc(1, sizeof(FITSfile));
205 
206     if (!((*fptr)->Fptr))
207     {
208         (*driverTable[driver].close)(handle);  /* close the file */
209         ffpmsg("failed to allocate structure for following file: (ffomem)");
210         ffpmsg(url);
211         free(*fptr);
212         *fptr = 0;
213         return(*status = MEMORY_ALLOCATION);
214     }
215 
216     slen = strlen(url) + 1;
217     slen = maxvalue(slen, 32); /* reserve at least 32 chars */
218     ((*fptr)->Fptr)->filename = (char *) malloc(slen); /* mem for file name */
219 
220     if ( !(((*fptr)->Fptr)->filename) )
221     {
222         (*driverTable[driver].close)(handle);  /* close the file */
223         ffpmsg("failed to allocate memory for filename: (ffomem)");
224         ffpmsg(url);
225         free((*fptr)->Fptr);
226         free(*fptr);
227         *fptr = 0;              /* return null file pointer */
228         return(*status = MEMORY_ALLOCATION);
229     }
230 
231     /* mem for headstart array */
232     ((*fptr)->Fptr)->headstart = (LONGLONG *) calloc(1001, sizeof(LONGLONG));
233 
234     if ( !(((*fptr)->Fptr)->headstart) )
235     {
236         (*driverTable[driver].close)(handle);  /* close the file */
237         ffpmsg("failed to allocate memory for headstart array: (ffomem)");
238         ffpmsg(url);
239         free( ((*fptr)->Fptr)->filename);
240         free((*fptr)->Fptr);
241         free(*fptr);
242         *fptr = 0;              /* return null file pointer */
243         return(*status = MEMORY_ALLOCATION);
244     }
245 
246     /* mem for file I/O buffers */
247     ((*fptr)->Fptr)->iobuffer = (char *) calloc(NIOBUF, IOBUFLEN);
248 
249     if ( !(((*fptr)->Fptr)->iobuffer) )
250     {
251         (*driverTable[driver].close)(handle);  /* close the file */
252         ffpmsg("failed to allocate memory for iobuffer array: (ffomem)");
253         ffpmsg(url);
254         free( ((*fptr)->Fptr)->headstart);    /* free memory for headstart array */
255         free( ((*fptr)->Fptr)->filename);
256         free((*fptr)->Fptr);
257         free(*fptr);
258         *fptr = 0;              /* return null file pointer */
259         return(*status = MEMORY_ALLOCATION);
260     }
261 
262     /* initialize the ageindex array (relative age of the I/O buffers) */
263     /* and initialize the bufrecnum array as being empty */
264     for (ii = 0; ii < NIOBUF; ii++)  {
265         ((*fptr)->Fptr)->ageindex[ii] = ii;
266         ((*fptr)->Fptr)->bufrecnum[ii] = -1;
267     }
268 
269         /* store the parameters describing the file */
270     ((*fptr)->Fptr)->MAXHDU = 1000;              /* initial size of headstart */
271     ((*fptr)->Fptr)->filehandle = handle;        /* file handle */
272     ((*fptr)->Fptr)->driver = driver;            /* driver number */
273     strcpy(((*fptr)->Fptr)->filename, url);      /* full input filename */
274     ((*fptr)->Fptr)->filesize = filesize;        /* physical file size */
275     ((*fptr)->Fptr)->logfilesize = filesize;     /* logical file size */
276     ((*fptr)->Fptr)->writemode = mode;      /* read-write mode    */
277     ((*fptr)->Fptr)->datastart = DATA_UNDEFINED; /* unknown start of data */
278     ((*fptr)->Fptr)->curbuf = -1;             /* undefined current IO buffer */
279     ((*fptr)->Fptr)->open_count = 1;     /* structure is currently used once */
280     ((*fptr)->Fptr)->validcode = VALIDSTRUC; /* flag denoting valid structure */
281 
282     ffldrc(*fptr, 0, REPORT_EOF, status);     /* load first record */
283 
284     fits_store_Fptr( (*fptr)->Fptr, status);  /* store Fptr address */
285 
286     if (ffrhdu(*fptr, &hdutyp, status) > 0)  /* determine HDU structure */
287     {
288         ffpmsg(
289           "ffomem could not interpret primary array header of file: (ffomem)");
290         ffpmsg(url);
291 
292         if (*status == UNKNOWN_REC)
293            ffpmsg("This does not look like a FITS file.");
294 
295         ffclos(*fptr, status);
296         *fptr = 0;              /* return null file pointer */
297     }
298 
299     /* ---------------------------------------------------------- */
300     /* move to desired extension, if specified as part of the URL */
301     /* ---------------------------------------------------------- */
302 
303     imagecolname[0] = '\0';
304     rowexpress[0] = '\0';
305 
306     if (*extspec)
307     {
308        /* parse the extension specifier into individual parameters */
309        ffexts(extspec, &extnum,
310          extname, &extvers, &movetotype, imagecolname, rowexpress, status);
311 
312 
313       if (*status > 0)
314           return(*status);
315 
316       if (extnum)
317       {
318         ffmahd(*fptr, extnum + 1, &hdutyp, status);
319       }
320       else if (*extname) /* move to named extension, if specified */
321       {
322         ffmnhd(*fptr, movetotype, extname, extvers, status);
323       }
324 
325       if (*status > 0)
326       {
327         ffpmsg("ffomem could not move to the specified extension:");
328         if (extnum > 0)
329         {
330           snprintf(errmsg, FLEN_ERRMSG,
331           " extension number %d doesn't exist or couldn't be opened.",extnum);
332           ffpmsg(errmsg);
333         }
334         else
335         {
336           snprintf(errmsg, FLEN_ERRMSG,
337           " extension with EXTNAME = %s,", extname);
338           ffpmsg(errmsg);
339 
340           if (extvers)
341           {
342              snprintf(errmsg, FLEN_ERRMSG,
343              "           and with EXTVERS = %d,", extvers);
344              ffpmsg(errmsg);
345           }
346 
347           if (movetotype != ANY_HDU)
348           {
349              snprintf(errmsg, FLEN_ERRMSG,
350              "           and with XTENSION = %s,", hdtype[movetotype]);
351              ffpmsg(errmsg);
352           }
353 
354           ffpmsg(" doesn't exist or couldn't be opened.");
355         }
356         return(*status);
357       }
358     }
359 
360     return(*status);
361 }
362 /*--------------------------------------------------------------------------*/
ffdkopn(fitsfile ** fptr,const char * name,int mode,int * status)363 int ffdkopn(fitsfile **fptr,      /* O - FITS file pointer                   */
364            const char *name,     /* I - full name of file to open           */
365            int mode,             /* I - 0 = open readonly; 1 = read/write   */
366            int *status)          /* IO - error status                       */
367 /*
368   Open an existing FITS file on magnetic disk with either readonly or
369   read/write access.  The routine does not support CFITSIO's extended
370   filename syntax and simply uses the entire input 'name' string as
371   the name of the file.
372 */
373 {
374     if (*status > 0)
375         return(*status);
376 
377     *status = OPEN_DISK_FILE;
378 
379     ffopen(fptr, name, mode, status);
380 
381     return(*status);
382 }
383 /*--------------------------------------------------------------------------*/
ffdopn(fitsfile ** fptr,const char * name,int mode,int * status)384 int ffdopn(fitsfile **fptr,      /* O - FITS file pointer                   */
385            const char *name,     /* I - full name of file to open           */
386            int mode,             /* I - 0 = open readonly; 1 = read/write   */
387            int *status)          /* IO - error status                       */
388 /*
389   Open an existing FITS file with either readonly or read/write access. and
390   move to the first HDU that contains 'interesting' data, if the primary
391   array contains a null image (i.e., NAXIS = 0).
392 */
393 {
394     if (*status > 0)
395         return(*status);
396 
397     *status = SKIP_NULL_PRIMARY;
398 
399     ffopen(fptr, name, mode, status);
400 
401     return(*status);
402 }
403 /*--------------------------------------------------------------------------*/
ffeopn(fitsfile ** fptr,const char * name,int mode,char * extlist,int * hdutype,int * status)404 int ffeopn(fitsfile **fptr,      /* O - FITS file pointer                   */
405            const char *name,     /* I - full name of file to open           */
406            int mode,             /* I - 0 = open readonly; 1 = read/write   */
407            char *extlist,        /* I - list of 'good' extensions to move to */
408            int *hdutype,         /* O - type of extension that is moved to  */
409            int *status)          /* IO - error status                       */
410 /*
411   Open an existing FITS file with either readonly or read/write access. and
412   if the primary array contains a null image (i.e., NAXIS = 0) then attempt to
413   move to the first extension named in the extlist of extension names. If
414   none are found, then simply move to the 2nd extension.
415 */
416 {
417     int hdunum, naxis, thdutype, gotext=0;
418     char *ext, *textlist;
419     char *saveptr;
420 
421     if (*status > 0)
422         return(*status);
423 
424     if (ffopen(fptr, name, mode, status) > 0)
425         return(*status);
426 
427     fits_get_hdu_num(*fptr, &hdunum);
428     fits_get_img_dim(*fptr, &naxis, status);
429 
430     if( (hdunum == 1) && (naxis == 0) ){
431       /* look through the extension list */
432       if( extlist ){
433         gotext = 0;
434 	textlist = malloc(strlen(extlist) + 1);
435 	if (!textlist) {
436 	    *status = MEMORY_ALLOCATION;
437 	    return(*status);
438 	}
439 
440         strcpy(textlist, extlist);
441         for(ext=(char *)ffstrtok(textlist, " ",&saveptr); ext != NULL;
442 	    ext=(char *)ffstrtok(NULL," ",&saveptr)){
443 	    fits_movnam_hdu(*fptr, ANY_HDU, ext, 0, status);
444 	    if( *status == 0 ){
445 	      gotext = 1;
446 	      break;
447 	    } else {
448 	      *status = 0;
449 	    }
450         }
451         free(textlist);
452       }
453       if( !gotext ){
454         /* if all else fails, move to extension #2 and hope for the best */
455         fits_movabs_hdu(*fptr, 2, &thdutype, status);
456       }
457     }
458     fits_get_hdu_type(*fptr, hdutype, status);
459     return(*status);
460 }
461 /*--------------------------------------------------------------------------*/
fftopn(fitsfile ** fptr,const char * name,int mode,int * status)462 int fftopn(fitsfile **fptr,      /* O - FITS file pointer                   */
463            const char *name,     /* I - full name of file to open           */
464            int mode,             /* I - 0 = open readonly; 1 = read/write   */
465            int *status)          /* IO - error status                       */
466 /*
467   Open an existing FITS file with either readonly or read/write access. and
468   move to the first HDU that contains 'interesting' table (not an image).
469 */
470 {
471     int hdutype;
472 
473     if (*status > 0)
474         return(*status);
475 
476     *status = SKIP_IMAGE;
477 
478     ffopen(fptr, name, mode, status);
479 
480     if (ffghdt(*fptr, &hdutype, status) <= 0) {
481         if (hdutype == IMAGE_HDU)
482             *status = NOT_TABLE;
483     }
484 
485     return(*status);
486 }
487 /*--------------------------------------------------------------------------*/
ffiopn(fitsfile ** fptr,const char * name,int mode,int * status)488 int ffiopn(fitsfile **fptr,      /* O - FITS file pointer                   */
489            const char *name,     /* I - full name of file to open           */
490            int mode,             /* I - 0 = open readonly; 1 = read/write   */
491            int *status)          /* IO - error status                       */
492 /*
493   Open an existing FITS file with either readonly or read/write access. and
494   move to the first HDU that contains 'interesting' image (not an table).
495 */
496 {
497     int hdutype;
498 
499     if (*status > 0)
500         return(*status);
501 
502     *status = SKIP_TABLE;
503 
504     ffopen(fptr, name, mode, status);
505 
506     if (ffghdt(*fptr, &hdutype, status) <= 0) {
507         if (hdutype != IMAGE_HDU)
508             *status = NOT_IMAGE;
509     }
510 
511     return(*status);
512 }
513 /*--------------------------------------------------------------------------*/
ffopentest(int soname,fitsfile ** fptr,const char * name,int mode,int * status)514 int ffopentest(int soname,       /* I - CFITSIO shared library version     */
515                                  /*     application program (fitsio.h file) */
516            fitsfile **fptr,      /* O - FITS file pointer                   */
517            const char *name,     /* I - full name of file to open           */
518            int mode,             /* I - 0 = open readonly; 1 = read/write   */
519            int *status)          /* IO - error status                       */
520 /*
521   Open an existing FITS file with either readonly or read/write access.
522   First test that the SONAME of fitsio.h used to build the CFITSIO library
523   is the same as was used in compiling the application program that
524   links to the library.
525 */
526 {
527     if (soname != CFITSIO_SONAME)
528     {
529         printf("\nERROR: Mismatch in the CFITSIO_SONAME value in the fitsio.h include file\n");
530 	printf("that was used to build the CFITSIO library, and the value in the include file\n");
531 	printf("that was used when compiling the application program:\n");
532 	printf("   Version used to build the CFITSIO library   = %d\n",CFITSIO_SONAME);
533 	printf("   Version included by the application program = %d\n",soname);
534 	printf("\nFix this by recompiling and then relinking this application program \n");
535 	printf("with the CFITSIO library.\n");
536 
537         *status = FILE_NOT_OPENED;
538 	return(*status);
539     }
540 
541     /* now call the normal file open routine */
542     ffopen(fptr, name, mode, status);
543     return(*status);
544 }
545 /*--------------------------------------------------------------------------*/
ffopen(fitsfile ** fptr,const char * name,int mode,int * status)546 int ffopen(fitsfile **fptr,      /* O - FITS file pointer                   */
547            const char *name,     /* I - full name of file to open           */
548            int mode,             /* I - 0 = open readonly; 1 = read/write   */
549            int *status)          /* IO - error status                       */
550 /*
551   Open an existing FITS file with either readonly or read/write access.
552 */
553 {
554     fitsfile *newptr;
555     int  ii, driver, hdutyp, hdunum, slen, writecopy, isopen;
556     LONGLONG filesize;
557     long rownum, nrows, goodrows;
558     int extnum, extvers, handle, movetotype, tstatus = 0, only_one = 0;
559     char urltype[MAX_PREFIX_LEN], infile[FLEN_FILENAME], outfile[FLEN_FILENAME];
560     char origurltype[MAX_PREFIX_LEN], extspec[FLEN_FILENAME];
561     char extname[FLEN_VALUE], rowfilter[FLEN_FILENAME], tblname[FLEN_VALUE];
562     char imagecolname[FLEN_VALUE], rowexpress[FLEN_FILENAME];
563     char binspec[FLEN_FILENAME], colspec[FLEN_FILENAME], pixfilter[FLEN_FILENAME];
564     char histfilename[FLEN_FILENAME];
565     char filtfilename[FLEN_FILENAME], compspec[FLEN_FILENAME];
566     char wtcol[FLEN_VALUE];
567     char minname[4][FLEN_VALUE], maxname[4][FLEN_VALUE];
568     char binname[4][FLEN_VALUE];
569 
570     char *url;
571     double minin[4], maxin[4], binsizein[4], weight;
572     int imagetype, naxis = 1, haxis, recip;
573     int skip_null = 0, skip_image = 0, skip_table = 0, open_disk_file = 0;
574     char colname[4][FLEN_VALUE];
575     char errmsg[FLEN_ERRMSG];
576     char *hdtype[3] = {"IMAGE", "TABLE", "BINTABLE"};
577     char *rowselect = 0;
578 
579     if (*status > 0)
580         return(*status);
581 
582     if (*status == SKIP_NULL_PRIMARY)
583     {
584       /* this special status value is used as a flag by ffdopn to tell */
585       /* ffopen to skip over a null primary array when opening the file. */
586 
587        skip_null = 1;
588        *status = 0;
589     }
590     else if (*status == SKIP_IMAGE)
591     {
592       /* this special status value is used as a flag by fftopn to tell */
593       /* ffopen to move to 1st significant table when opening the file. */
594 
595        skip_image = 1;
596        *status = 0;
597     }
598     else if (*status == SKIP_TABLE)
599     {
600       /* this special status value is used as a flag by ffiopn to tell */
601       /* ffopen to move to 1st significant image when opening the file. */
602 
603        skip_table = 1;
604        *status = 0;
605     }
606     else if (*status == OPEN_DISK_FILE)
607     {
608       /* this special status value is used as a flag by ffdkopn to tell */
609       /* ffopen to not interpret the input filename using CFITSIO's    */
610       /* extended filename syntax, and simply open the specified disk file */
611 
612        open_disk_file = 1;
613        *status = 0;
614     }
615 
616     *fptr = 0;              /* initialize null file pointer */
617     writecopy = 0;  /* have we made a write-able copy of the input file? */
618 
619     if (need_to_initialize) {          /* this is called only once */
620        *status = fits_init_cfitsio();
621     }
622 
623     if (*status > 0)
624         return(*status);
625 
626     url = (char *) name;
627     while (*url == ' ')  /* ignore leading spaces in the filename */
628         url++;
629 
630     if (*url == '\0')
631     {
632         ffpmsg("Name of file to open is blank. (ffopen)");
633         return(*status = FILE_NOT_OPENED);
634     }
635 
636     if (open_disk_file)
637     {
638       /* treat the input URL literally as the name of the file to open */
639       /* and don't try to parse the URL using the extended filename syntax */
640 
641         if (strlen(url) > FLEN_FILENAME - 1) {
642             ffpmsg("Name of file to open is too long. (ffopen)");
643             return(*status = FILE_NOT_OPENED);
644         }
645 
646         strcpy(infile,url);
647         strcpy(urltype, "file://");
648         outfile[0] = '\0';
649         extspec[0] = '\0';
650         binspec[0] = '\0';
651         colspec[0] = '\0';
652         rowfilter[0] = '\0';
653         pixfilter[0] = '\0';
654         compspec[0] = '\0';
655     }
656     else
657     {
658         /* parse the input file specification */
659 
660         /* NOTE: This routine tests that all the strings do not */
661 	/* overflow the standard buffer sizes (FLEN_FILENAME, etc.) */
662 	/* therefore in general we do not have to worry about buffer */
663 	/* overflow of any of the returned strings. */
664 
665         /* call the newer version of this parsing routine that supports 'compspec' */
666         ffifile2(url, urltype, infile, outfile, extspec,
667               rowfilter, binspec, colspec, pixfilter, compspec, status);
668     }
669 
670     if (*status > 0)
671     {
672         ffpmsg("could not parse the input filename: (ffopen)");
673         ffpmsg(url);
674         return(*status);
675     }
676 
677     imagecolname[0] = '\0';
678     rowexpress[0] = '\0';
679 
680     if (*extspec)
681     {
682        slen = strlen(extspec);
683        if (extspec[slen - 1] == '#') {  /* special symbol to mean only copy this extension */
684            extspec[slen - 1] = '\0';
685 	   only_one = 1;
686        }
687 
688        /* parse the extension specifier into individual parameters */
689        ffexts(extspec, &extnum,
690          extname, &extvers, &movetotype, imagecolname, rowexpress, status);
691 
692       if (*status > 0)
693           return(*status);
694     }
695 
696     /*-------------------------------------------------------------------*/
697     /* special cases:                                                    */
698     /*-------------------------------------------------------------------*/
699 
700     histfilename[0] = '\0';
701     filtfilename[0] = '\0';
702     if (*outfile && (*binspec || *imagecolname || *pixfilter))
703     {
704         /* if binspec or imagecolumn are specified, then the  */
705         /* output file name is intended for the final image,  */
706         /* and not a copy of the input file.                  */
707 
708         strcpy(histfilename, outfile);
709         outfile[0] = '\0';
710     }
711     else if (*outfile && (*rowfilter || *colspec))
712     {
713         /* if rowfilter or colspece are specified, then the    */
714         /* output file name is intended for the filtered file  */
715         /* and not a copy of the input file.                   */
716 
717         strcpy(filtfilename, outfile);
718         outfile[0] = '\0';
719     }
720 
721     /*-------------------------------------------------------------------*/
722     /* check if this same file is already open, and if so, attach to it  */
723     /*-------------------------------------------------------------------*/
724 
725     FFLOCK;
726     if (fits_already_open(fptr, url, urltype, infile, extspec, rowfilter,
727             binspec, colspec, mode, &isopen, status) > 0)
728     {
729         FFUNLOCK;
730         return(*status);
731     }
732     FFUNLOCK;
733 
734     if (isopen) {
735        goto move2hdu;
736     }
737 
738     /* get the driver number corresponding to this urltype */
739     *status = urltype2driver(urltype, &driver);
740 
741     if (*status > 0)
742     {
743         ffpmsg("could not find driver for this file: (ffopen)");
744         ffpmsg(urltype);
745         ffpmsg(url);
746         return(*status);
747     }
748 
749     /*-------------------------------------------------------------------
750         deal with all those messy special cases which may require that
751         a different driver be used:
752             - is disk file compressed?
753             - are ftp:, gsiftp:, or http: files compressed?
754             - has user requested that a local copy be made of
755               the ftp or http file?
756       -------------------------------------------------------------------*/
757 
758     if (driverTable[driver].checkfile)
759     {
760         strcpy(origurltype,urltype);  /* Save the urltype */
761 
762         /* 'checkfile' may modify the urltype, infile and outfile strings */
763         *status =  (*driverTable[driver].checkfile)(urltype, infile, outfile);
764 
765         if (*status)
766         {
767             ffpmsg("checkfile failed for this file: (ffopen)");
768             ffpmsg(url);
769             return(*status);
770         }
771 
772         if (strcmp(origurltype, urltype))  /* did driver changed on us? */
773         {
774             *status = urltype2driver(urltype, &driver);
775             if (*status > 0)
776             {
777                 ffpmsg("could not change driver for this file: (ffopen)");
778                 ffpmsg(url);
779                 ffpmsg(urltype);
780                 return(*status);
781             }
782         }
783     }
784 
785     /* call appropriate driver to open the file */
786     if (driverTable[driver].open)
787     {
788         FFLOCK;  /* lock this while searching for vacant handle */
789         *status =  (*driverTable[driver].open)(infile, mode, &handle);
790         FFUNLOCK;
791         if (*status > 0)
792         {
793             ffpmsg("failed to find or open the following file: (ffopen)");
794             ffpmsg(url);
795             return(*status);
796        }
797     }
798     else
799     {
800         ffpmsg("cannot open an existing file of this type: (ffopen)");
801         ffpmsg(url);
802         return(*status = FILE_NOT_OPENED);
803     }
804 
805         /* get initial file size */
806     *status = (*driverTable[driver].size)(handle, &filesize);
807     if (*status > 0)
808     {
809         (*driverTable[driver].close)(handle);  /* close the file */
810         ffpmsg("failed get the size of the following file: (ffopen)");
811         ffpmsg(url);
812         return(*status);
813     }
814 
815         /* allocate fitsfile structure and initialize = 0 */
816     *fptr = (fitsfile *) calloc(1, sizeof(fitsfile));
817 
818     if (!(*fptr))
819     {
820         (*driverTable[driver].close)(handle);  /* close the file */
821         ffpmsg("failed to allocate structure for following file: (ffopen)");
822         ffpmsg(url);
823         return(*status = MEMORY_ALLOCATION);
824     }
825 
826         /* allocate FITSfile structure and initialize = 0 */
827     (*fptr)->Fptr = (FITSfile *) calloc(1, sizeof(FITSfile));
828 
829     if (!((*fptr)->Fptr))
830     {
831         (*driverTable[driver].close)(handle);  /* close the file */
832         ffpmsg("failed to allocate structure for following file: (ffopen)");
833         ffpmsg(url);
834         free(*fptr);
835         *fptr = 0;
836         return(*status = MEMORY_ALLOCATION);
837     }
838 
839     slen = strlen(url) + 1;
840     slen = maxvalue(slen, 32); /* reserve at least 32 chars */
841     ((*fptr)->Fptr)->filename = (char *) malloc(slen); /* mem for file name */
842 
843     if ( !(((*fptr)->Fptr)->filename) )
844     {
845         (*driverTable[driver].close)(handle);  /* close the file */
846         ffpmsg("failed to allocate memory for filename: (ffopen)");
847         ffpmsg(url);
848         free((*fptr)->Fptr);
849         free(*fptr);
850         *fptr = 0;              /* return null file pointer */
851         return(*status = MEMORY_ALLOCATION);
852     }
853 
854     /* mem for headstart array */
855     ((*fptr)->Fptr)->headstart = (LONGLONG *) calloc(1001, sizeof(LONGLONG));
856 
857     if ( !(((*fptr)->Fptr)->headstart) )
858     {
859         (*driverTable[driver].close)(handle);  /* close the file */
860         ffpmsg("failed to allocate memory for headstart array: (ffopen)");
861         ffpmsg(url);
862         free( ((*fptr)->Fptr)->filename);
863         free((*fptr)->Fptr);
864         free(*fptr);
865         *fptr = 0;              /* return null file pointer */
866         return(*status = MEMORY_ALLOCATION);
867     }
868 
869     /* mem for file I/O buffers */
870     ((*fptr)->Fptr)->iobuffer = (char *) calloc(NIOBUF, IOBUFLEN);
871 
872     if ( !(((*fptr)->Fptr)->iobuffer) )
873     {
874         (*driverTable[driver].close)(handle);  /* close the file */
875         ffpmsg("failed to allocate memory for iobuffer array: (ffopen)");
876         ffpmsg(url);
877         free( ((*fptr)->Fptr)->headstart);    /* free memory for headstart array */
878         free( ((*fptr)->Fptr)->filename);
879         free((*fptr)->Fptr);
880         free(*fptr);
881         *fptr = 0;              /* return null file pointer */
882         return(*status = MEMORY_ALLOCATION);
883     }
884 
885     /* initialize the ageindex array (relative age of the I/O buffers) */
886     /* and initialize the bufrecnum array as being empty */
887     for (ii = 0; ii < NIOBUF; ii++)  {
888         ((*fptr)->Fptr)->ageindex[ii] = ii;
889         ((*fptr)->Fptr)->bufrecnum[ii] = -1;
890     }
891 
892         /* store the parameters describing the file */
893     ((*fptr)->Fptr)->MAXHDU = 1000;              /* initial size of headstart */
894     ((*fptr)->Fptr)->filehandle = handle;        /* file handle */
895     ((*fptr)->Fptr)->driver = driver;            /* driver number */
896     strcpy(((*fptr)->Fptr)->filename, url);      /* full input filename */
897     ((*fptr)->Fptr)->filesize = filesize;        /* physical file size */
898     ((*fptr)->Fptr)->logfilesize = filesize;     /* logical file size */
899     ((*fptr)->Fptr)->writemode = mode;           /* read-write mode    */
900     ((*fptr)->Fptr)->datastart = DATA_UNDEFINED; /* unknown start of data */
901     ((*fptr)->Fptr)->curbuf = -1;            /* undefined current IO buffer */
902     ((*fptr)->Fptr)->open_count = 1;      /* structure is currently used once */
903     ((*fptr)->Fptr)->validcode = VALIDSTRUC; /* flag denoting valid structure */
904     ((*fptr)->Fptr)->only_one = only_one; /* flag denoting only copy single extension */
905 
906     ffldrc(*fptr, 0, REPORT_EOF, status);     /* load first record */
907 
908     fits_store_Fptr( (*fptr)->Fptr, status);  /* store Fptr address */
909 
910     if (ffrhdu(*fptr, &hdutyp, status) > 0)  /* determine HDU structure */
911     {
912         ffpmsg(
913           "ffopen could not interpret primary array header of file: ");
914         ffpmsg(url);
915 
916         if (*status == UNKNOWN_REC)
917            ffpmsg("This does not look like a FITS file.");
918 
919         ffclos(*fptr, status);
920         *fptr = 0;              /* return null file pointer */
921         return(*status);
922     }
923 
924     /* ------------------------------------------------------------- */
925     /* At this point, the input file has been opened. If outfile was */
926     /* specified, then we have opened a copy of the file, not the    */
927     /* original file so it is safe to modify it if necessary         */
928     /* ------------------------------------------------------------- */
929 
930     if (*outfile)
931         writecopy = 1;
932 
933 move2hdu:
934 
935     /* ---------------------------------------------------------- */
936     /* move to desired extension, if specified as part of the URL */
937     /* ---------------------------------------------------------- */
938 
939     if (*extspec)
940     {
941       if (extnum)  /* extension number was specified */
942       {
943         ffmahd(*fptr, extnum + 1, &hdutyp, status);
944       }
945       else if (*extname) /* move to named extension, if specified */
946       {
947         ffmnhd(*fptr, movetotype, extname, extvers, status);
948       }
949 
950       if (*status > 0)  /* clean up after error */
951       {
952         ffpmsg("ffopen could not move to the specified extension:");
953         if (extnum > 0)
954         {
955           snprintf(errmsg, FLEN_ERRMSG,
956           " extension number %d doesn't exist or couldn't be opened.",extnum);
957           ffpmsg(errmsg);
958         }
959         else
960         {
961           snprintf(errmsg, FLEN_ERRMSG,
962           " extension with EXTNAME = %s,", extname);
963           ffpmsg(errmsg);
964 
965           if (extvers)
966           {
967              snprintf(errmsg, FLEN_ERRMSG,
968              "           and with EXTVERS = %d,", extvers);
969              ffpmsg(errmsg);
970           }
971 
972           if (movetotype != ANY_HDU)
973           {
974              snprintf(errmsg, FLEN_ERRMSG,
975              "           and with XTENSION = %s,", hdtype[movetotype]);
976              ffpmsg(errmsg);
977           }
978 
979           ffpmsg(" doesn't exist or couldn't be opened.");
980         }
981 
982         ffclos(*fptr, status);
983         *fptr = 0;              /* return null file pointer */
984         return(*status);
985       }
986     }
987     else if (skip_null || skip_image || skip_table ||
988             (*imagecolname || *colspec || *rowfilter || *binspec))
989     {
990       /* ------------------------------------------------------------------
991 
992       If no explicit extension specifier is given as part of the file
993       name, and, if a) skip_null is true (set if ffopen is called by
994       ffdopn) or b) skip_image or skip_table is true (set if ffopen is
995       called by fftopn or ffdopn) or c) other file filters are
996       specified, then CFITSIO will attempt to move to the first
997       'interesting' HDU after opening an existing FITS file (or to
998       first interesting table HDU if skip_image is true);
999 
1000       An 'interesting' HDU is defined to be either an image with NAXIS
1001       > 0 (i.e., not a null array) or a table which has an EXTNAME
1002       value which does not contain any of the following strings:
1003          'GTI'  - Good Time Interval extension
1004          'OBSTABLE'  - used in Beppo SAX data files
1005 
1006       The main purpose for this is to allow CFITSIO to skip over a null
1007       primary and other non-interesting HDUs when opening an existing
1008       file, and move directly to the first extension that contains
1009       significant data.
1010       ------------------------------------------------------------------ */
1011 
1012       fits_get_hdu_num(*fptr, &hdunum);
1013       if (hdunum == 1) {
1014 
1015         fits_get_img_dim(*fptr, &naxis, status);
1016 
1017         if (naxis == 0 || skip_image) /* skip primary array */
1018         {
1019           while(1)
1020           {
1021             /* see if the next HDU is 'interesting' */
1022             if (fits_movrel_hdu(*fptr, 1, &hdutyp, status))
1023             {
1024                if (*status == END_OF_FILE)
1025                   *status = 0;  /* reset expected error */
1026 
1027                /* didn't find an interesting HDU so move back to beginning */
1028                fits_movabs_hdu(*fptr, 1, &hdutyp, status);
1029                break;
1030             }
1031 
1032             if (hdutyp == IMAGE_HDU && skip_image) {
1033 
1034                 continue;   /* skip images */
1035 
1036             } else if (hdutyp != IMAGE_HDU && skip_table) {
1037 
1038                 continue;   /* skip tables */
1039 
1040             } else if (hdutyp == IMAGE_HDU) {
1041 
1042                fits_get_img_dim(*fptr, &naxis, status);
1043                if (naxis > 0)
1044                   break;  /* found a non-null image */
1045 
1046             } else {
1047 
1048                tstatus = 0;
1049                tblname[0] = '\0';
1050                fits_read_key(*fptr, TSTRING, "EXTNAME", tblname, NULL,&tstatus);
1051 
1052                if ( (!strstr(tblname, "GTI") && !strstr(tblname, "gti")) &&
1053                     fits_strncasecmp(tblname, "OBSTABLE", 8) )
1054                   break;  /* found an interesting table */
1055             }
1056           }  /* end while */
1057         }
1058       } /* end if (hdunum==1) */
1059     }
1060 
1061     if (*imagecolname)
1062     {
1063        /* ----------------------------------------------------------------- */
1064        /* we need to open an image contained in a single table cell         */
1065        /* First, determine which row of the table to use.                   */
1066        /* ----------------------------------------------------------------- */
1067 
1068        if (isdigit((int) *rowexpress))  /* is the row specification a number? */
1069        {
1070           sscanf(rowexpress, "%ld", &rownum);
1071           if (rownum < 1)
1072           {
1073              ffpmsg("illegal rownum for image cell:");
1074              ffpmsg(rowexpress);
1075              ffpmsg("Could not open the following image in a table cell:");
1076              ffpmsg(extspec);
1077              ffclos(*fptr, status);
1078              *fptr = 0;              /* return null file pointer */
1079              return(*status = BAD_ROW_NUM);
1080           }
1081        }
1082        else if (fits_find_first_row(*fptr, rowexpress, &rownum, status) > 0)
1083        {
1084           ffpmsg("Failed to find row matching this expression:");
1085           ffpmsg(rowexpress);
1086           ffpmsg("Could not open the following image in a table cell:");
1087           ffpmsg(extspec);
1088           ffclos(*fptr, status);
1089           *fptr = 0;              /* return null file pointer */
1090           return(*status);
1091        }
1092 
1093        if (rownum == 0)
1094        {
1095           ffpmsg("row statisfying this expression doesn't exist::");
1096           ffpmsg(rowexpress);
1097           ffpmsg("Could not open the following image in a table cell:");
1098           ffpmsg(extspec);
1099           ffclos(*fptr, status);
1100           *fptr = 0;              /* return null file pointer */
1101           return(*status = BAD_ROW_NUM);
1102        }
1103 
1104        /* determine the name of the new file to contain copy of the image */
1105        if (*histfilename && !(*pixfilter) )
1106            strcpy(outfile, histfilename); /* the original outfile name */
1107        else
1108            strcpy(outfile, "mem://_1");  /* create image file in memory */
1109 
1110        /* Copy the image into new primary array and open it as the current */
1111        /* fptr.  This will close the table that contains the original image. */
1112 
1113        /* create new empty file to hold copy of the image */
1114        if (ffinit(&newptr, outfile, status) > 0)
1115        {
1116           ffpmsg("failed to create file for copy of image in table cell:");
1117           ffpmsg(outfile);
1118           return(*status);
1119        }
1120 
1121        if (fits_copy_cell2image(*fptr, newptr, imagecolname, rownum,
1122                                 status) > 0)
1123        {
1124           ffpmsg("Failed to copy table cell to new primary array:");
1125           ffpmsg(extspec);
1126           ffclos(*fptr, status);
1127           *fptr = 0;              /* return null file pointer */
1128           return(*status);
1129        }
1130 
1131        /* close the original file and set fptr to the new image */
1132        ffclos(*fptr, status);
1133 
1134        *fptr = newptr; /* reset the pointer to the new table */
1135 
1136        writecopy = 1;  /* we are now dealing with a copy of the original file */
1137 
1138 
1139        /*  leave it up to calling routine to write any HISTORY keywords */
1140     }
1141 
1142     /* --------------------------------------------------------------------- */
1143     /* edit columns (and/or keywords) in the table, if specified in the URL  */
1144     /* --------------------------------------------------------------------- */
1145 
1146     if (*colspec)
1147     {
1148        /* the column specifier will modify the file, so make sure */
1149        /* we are already dealing with a copy, or else make a new copy */
1150 
1151        if (!writecopy)  /* Is the current file already a copy? */
1152            writecopy = fits_is_this_a_copy(urltype);
1153 
1154        if (!writecopy)
1155        {
1156            if (*filtfilename && *outfile == '\0')
1157                strcpy(outfile, filtfilename); /* the original outfile name */
1158            else
1159                strcpy(outfile, "mem://_1");   /* will create copy in memory */
1160 
1161            writecopy = 1;
1162        }
1163        else
1164        {
1165            ((*fptr)->Fptr)->writemode = READWRITE; /* we have write access */
1166            outfile[0] = '\0';
1167        }
1168 
1169        if (ffedit_columns(fptr, outfile, colspec, status) > 0)
1170        {
1171            ffpmsg("editing columns in input table failed (ffopen)");
1172            ffpmsg(" while trying to perform the following operation:");
1173            ffpmsg(colspec);
1174            ffclos(*fptr, status);
1175            *fptr = 0;              /* return null file pointer */
1176            return(*status);
1177        }
1178     }
1179 
1180     /* ------------------------------------------------------------------- */
1181     /* select rows from the table, if specified in the URL                 */
1182     /* or select a subimage (if this is an image HDU and not a table)      */
1183     /* ------------------------------------------------------------------- */
1184 
1185     if (*rowfilter)
1186     {
1187      fits_get_hdu_type(*fptr, &hdutyp, status);  /* get type of HDU */
1188      if (hdutyp == IMAGE_HDU)
1189      {
1190         /* this is an image so 'rowfilter' is an image section specification */
1191 
1192         if (*filtfilename && *outfile == '\0')
1193             strcpy(outfile, filtfilename); /* the original outfile name */
1194         else if (*outfile == '\0') /* output file name not already defined? */
1195             strcpy(outfile, "mem://_2");  /* will create file in memory */
1196 
1197         /* create new file containing the image section, plus a copy of */
1198         /* any other HDUs that exist in the input file.  This routine   */
1199         /* will close the original image file and return a pointer      */
1200         /* to the new file. */
1201 
1202         if (fits_select_image_section(fptr, outfile, rowfilter, status) > 0)
1203         {
1204            ffpmsg("on-the-fly selection of image section failed (ffopen)");
1205            ffpmsg(" while trying to use the following section filter:");
1206            ffpmsg(rowfilter);
1207            ffclos(*fptr, status);
1208            *fptr = 0;              /* return null file pointer */
1209            return(*status);
1210         }
1211      }
1212      else
1213      {
1214        /* this is a table HDU, so the rowfilter is really a row filter */
1215 
1216       if (*binspec)
1217       {
1218         /*  since we are going to make a histogram of the selected rows,   */
1219         /*  it would be a waste of time and memory to make a whole copy of */
1220         /*  the selected rows.  Instead, just construct an array of TRUE   */
1221         /*  or FALSE values that indicate which rows are to be included    */
1222         /*  in the histogram and pass that to the histogram generating     */
1223         /*  routine                                                        */
1224 
1225         fits_get_num_rows(*fptr, &nrows, status);  /* get no. of rows */
1226 
1227         rowselect = (char *) calloc(nrows, 1);
1228         if (!rowselect)
1229         {
1230            ffpmsg(
1231            "failed to allocate memory for selected columns array (ffopen)");
1232            ffpmsg(" while trying to select rows with the following filter:");
1233            ffpmsg(rowfilter);
1234            ffclos(*fptr, status);
1235            *fptr = 0;              /* return null file pointer */
1236            return(*status = MEMORY_ALLOCATION);
1237         }
1238 
1239         if (fits_find_rows(*fptr, rowfilter, 1L, nrows, &goodrows,
1240             rowselect, status) > 0)
1241         {
1242            ffpmsg("selection of rows in input table failed (ffopen)");
1243            ffpmsg(" while trying to select rows with the following filter:");
1244            ffpmsg(rowfilter);
1245            free(rowselect);
1246            ffclos(*fptr, status);
1247            *fptr = 0;              /* return null file pointer */
1248            return(*status);
1249         }
1250       }
1251       else
1252       {
1253         if (!writecopy)  /* Is the current file already a copy? */
1254            writecopy = fits_is_this_a_copy(urltype);
1255 
1256         if (!writecopy)
1257         {
1258            if (*filtfilename && *outfile == '\0')
1259                strcpy(outfile, filtfilename); /* the original outfile name */
1260            else if (*outfile == '\0') /* output filename not already defined? */
1261                strcpy(outfile, "mem://_2");  /* will create copy in memory */
1262         }
1263         else
1264         {
1265            ((*fptr)->Fptr)->writemode = READWRITE; /* we have write access */
1266            outfile[0] = '\0';
1267         }
1268 
1269         /* select rows in the table.  If a copy of the input file has */
1270         /* not already been made, then this routine will make a copy */
1271         /* and then close the input file, so that the modifications will */
1272         /* only be made on the copy, not the original */
1273 
1274         if (ffselect_table(fptr, outfile, rowfilter, status) > 0)
1275         {
1276           ffpmsg("on-the-fly selection of rows in input table failed (ffopen)");
1277            ffpmsg(" while trying to select rows with the following filter:");
1278            ffpmsg(rowfilter);
1279            ffclos(*fptr, status);
1280            *fptr = 0;              /* return null file pointer */
1281            return(*status);
1282         }
1283 
1284         /* write history records */
1285         ffphis(*fptr,
1286         "CFITSIO used the following filtering expression to create this table:",
1287         status);
1288         ffphis(*fptr, name, status);
1289 
1290       }   /* end of no binspec case */
1291      }   /* end of table HDU case */
1292     }  /* end of rowfilter exists case */
1293 
1294     /* ------------------------------------------------------------------- */
1295     /* make an image histogram by binning columns, if specified in the URL */
1296     /* ------------------------------------------------------------------- */
1297 
1298     if (*binspec)
1299     {
1300        if (*histfilename  && !(*pixfilter) )
1301            strcpy(outfile, histfilename); /* the original outfile name */
1302        else
1303            strcpy(outfile, "mem://_3");  /* create histogram in memory */
1304                                          /* if not already copied the file */
1305 
1306        /* parse the binning specifier into individual parameters */
1307        ffbins(binspec, &imagetype, &haxis, colname,
1308                           minin, maxin, binsizein,
1309                           minname, maxname, binname,
1310                           &weight, wtcol, &recip, status);
1311 
1312        /* Create the histogram primary array and open it as the current fptr */
1313        /* This will close the table that was used to create the histogram. */
1314        ffhist2(fptr, outfile, imagetype, haxis, colname, minin, maxin,
1315               binsizein, minname, maxname, binname,
1316               weight, wtcol, recip, rowselect, status);
1317 
1318        if (rowselect)
1319           free(rowselect);
1320 
1321        if (*status > 0)
1322        {
1323       ffpmsg("on-the-fly histogramming of input table failed (ffopen)");
1324       ffpmsg(" while trying to execute the following histogram specification:");
1325       ffpmsg(binspec);
1326            ffclos(*fptr, status);
1327            *fptr = 0;              /* return null file pointer */
1328            return(*status);
1329        }
1330 
1331         /* write history records */
1332         ffphis(*fptr,
1333         "CFITSIO used the following expression to create this histogram:",
1334         status);
1335         ffphis(*fptr, name, status);
1336     }
1337 
1338     if (*pixfilter)
1339     {
1340        if (*histfilename)
1341            strcpy(outfile, histfilename); /* the original outfile name */
1342        else
1343            strcpy(outfile, "mem://_4");  /* create in memory */
1344                                          /* if not already copied the file */
1345 
1346        /* Ensure type of HDU is consistent with pixel filtering */
1347        fits_get_hdu_type(*fptr, &hdutyp, status);  /* get type of HDU */
1348        if (hdutyp == IMAGE_HDU) {
1349 
1350           pixel_filter_helper(fptr, outfile, pixfilter, status);
1351 
1352           if (*status > 0) {
1353              ffpmsg("pixel filtering of input image failed (ffopen)");
1354              ffpmsg(" while trying to execute the following:");
1355              ffpmsg(pixfilter);
1356              ffclos(*fptr, status);
1357              *fptr = 0;              /* return null file pointer */
1358              return(*status);
1359           }
1360 
1361           /* write history records */
1362           ffphis(*fptr,
1363           "CFITSIO used the following expression to create this image:",
1364           status);
1365           ffphis(*fptr, name, status);
1366        }
1367        else
1368        {
1369           ffpmsg("cannot use pixel filter on non-IMAGE HDU");
1370           ffpmsg(pixfilter);
1371           ffclos(*fptr, status);
1372           *fptr = 0;              /* return null file pointer */
1373           *status = NOT_IMAGE;
1374           return(*status);
1375        }
1376     }
1377 
1378    /* parse and save image compression specification, if given */
1379    if (*compspec) {
1380       ffparsecompspec(*fptr, compspec, status);
1381    }
1382 
1383     return(*status);
1384 }
1385 /*--------------------------------------------------------------------------*/
ffreopen(fitsfile * openfptr,fitsfile ** newfptr,int * status)1386 int ffreopen(fitsfile *openfptr, /* I - FITS file pointer to open file  */
1387              fitsfile **newfptr,  /* O - pointer to new re opened file   */
1388              int *status)        /* IO - error status                   */
1389 /*
1390   Reopen an existing FITS file with either readonly or read/write access.
1391   The reopened file shares the same FITSfile structure but may point to a
1392   different HDU within the file.
1393 */
1394 {
1395     if (*status > 0)
1396         return(*status);
1397 
1398     /* check that the open file pointer is valid */
1399     if (!openfptr)
1400         return(*status = NULL_INPUT_PTR);
1401     else if ((openfptr->Fptr)->validcode != VALIDSTRUC) /* check magic value */
1402         return(*status = BAD_FILEPTR);
1403 
1404         /* allocate fitsfile structure and initialize = 0 */
1405     *newfptr = (fitsfile *) calloc(1, sizeof(fitsfile));
1406 
1407     (*newfptr)->Fptr = openfptr->Fptr; /* both point to the same structure */
1408     (*newfptr)->HDUposition = 0;  /* set initial position to primary array */
1409     (((*newfptr)->Fptr)->open_count)++;   /* increment the file usage counter */
1410 
1411     return(*status);
1412 }
1413 /*--------------------------------------------------------------------------*/
fits_store_Fptr(FITSfile * Fptr,int * status)1414 int fits_store_Fptr(FITSfile *Fptr,  /* O - FITS file pointer               */
1415            int *status)              /* IO - error status                   */
1416 /*
1417    store the new Fptr address for future use by fits_already_open
1418 */
1419 {
1420     int ii;
1421 
1422     if (*status > 0)
1423         return(*status);
1424 
1425     FFLOCK;
1426     for (ii = 0; ii < NMAXFILES; ii++) {
1427         if (FptrTable[ii] == 0) {
1428             FptrTable[ii] = Fptr;
1429             break;
1430         }
1431     }
1432     FFUNLOCK;
1433     return(*status);
1434 }
1435 /*--------------------------------------------------------------------------*/
fits_clear_Fptr(FITSfile * Fptr,int * status)1436 int fits_clear_Fptr(FITSfile *Fptr,  /* O - FITS file pointer               */
1437            int *status)              /* IO - error status                   */
1438 /*
1439    clear the Fptr address from the Fptr Table
1440 */
1441 {
1442     int ii;
1443 
1444     FFLOCK;
1445     for (ii = 0; ii < NMAXFILES; ii++) {
1446         if (FptrTable[ii] == Fptr) {
1447             FptrTable[ii] = 0;
1448             break;
1449         }
1450     }
1451     FFUNLOCK;
1452     return(*status);
1453 }
1454 /*--------------------------------------------------------------------------*/
fits_already_open(fitsfile ** fptr,char * url,char * urltype,char * infile,char * extspec,char * rowfilter,char * binspec,char * colspec,int mode,int * isopen,int * status)1455 int fits_already_open(fitsfile **fptr, /* I/O - FITS file pointer       */
1456            char *url,
1457            char *urltype,
1458            char *infile,
1459            char *extspec,
1460            char *rowfilter,
1461            char *binspec,
1462            char *colspec,
1463            int  mode,             /* I - 0 = open readonly; 1 = read/write   */
1464            int  *isopen,          /* O - 1 = file is already open            */
1465            int  *status)          /* IO - error status                       */
1466 /*
1467   Check if the file to be opened is already open.  If so, then attach to it.
1468 */
1469 
1470    /* the input strings must not exceed the standard lengths */
1471    /* of FLEN_FILENAME, MAX_PREFIX_LEN, etc. */
1472 
1473      /*
1474        this function was changed so that for files of access method FILE://
1475        the file paths are compared using standard URL syntax and absolute
1476        paths (as opposed to relative paths). This eliminates some instances
1477        where a file is already opened but it is not realized because it
1478        was opened with another file path. For instance, if the CWD is
1479        /a/b/c and I open /a/b/c/foo.fits then open ./foo.fits the previous
1480        version of this function would not have reconized that the two files
1481        were the same. This version does recognize that the two files are
1482        the same.
1483      */
1484 {
1485     FITSfile *oldFptr;
1486     int ii;
1487     char oldurltype[MAX_PREFIX_LEN], oldinfile[FLEN_FILENAME];
1488     char oldextspec[FLEN_FILENAME], oldoutfile[FLEN_FILENAME];
1489     char oldrowfilter[FLEN_FILENAME];
1490     char oldbinspec[FLEN_FILENAME], oldcolspec[FLEN_FILENAME];
1491     char cwd[FLEN_FILENAME];
1492     char tmpStr[FLEN_FILENAME];
1493     char tmpinfile[FLEN_FILENAME];
1494 
1495     *isopen = 0;
1496 
1497 /*  When opening a file with readonly access then we simply let
1498     the operating system open the file again, instead of using the CFITSIO
1499     trick of attaching to the previously opened file.  This is required
1500     if CFITSIO is running in a multi-threaded environment, because 2 different
1501     threads cannot share the same FITSfile pointer.
1502 
1503     If the file is opened/reopened with write access, then the file MUST
1504     only be physically opened once..
1505 */
1506     if (mode == 0)
1507         return(*status);
1508 
1509     if(fits_strcasecmp(urltype,"FILE://") == 0)
1510       {
1511         if (fits_path2url(infile,FLEN_FILENAME,tmpinfile,status))
1512            return (*status);
1513 
1514         if(tmpinfile[0] != '/')
1515           {
1516             fits_get_cwd(cwd,status);
1517             strcat(cwd,"/");
1518 
1519             if (strlen(cwd) + strlen(tmpinfile) > FLEN_FILENAME-1) {
1520 		  ffpmsg("File name is too long. (fits_already_open)");
1521                   return(*status = FILE_NOT_OPENED);
1522             }
1523 
1524             strcat(cwd,tmpinfile);
1525             fits_clean_url(cwd,tmpinfile,status);
1526           }
1527       }
1528     else
1529       strcpy(tmpinfile,infile);
1530 
1531     for (ii = 0; ii < NMAXFILES; ii++)   /* check every buffer */
1532     {
1533         if (FptrTable[ii] != 0)
1534         {
1535           oldFptr = FptrTable[ii];
1536 
1537           fits_parse_input_url(oldFptr->filename, oldurltype,
1538                     oldinfile, oldoutfile, oldextspec, oldrowfilter,
1539                     oldbinspec, oldcolspec, status);
1540 
1541           if (*status > 0)
1542           {
1543             ffpmsg("could not parse the previously opened filename: (ffopen)");
1544             ffpmsg(oldFptr->filename);
1545             return(*status);
1546           }
1547 
1548           if(fits_strcasecmp(oldurltype,"FILE://") == 0)
1549             {
1550               if(fits_path2url(oldinfile,FLEN_FILENAME,tmpStr,status))
1551                  return(*status);
1552 
1553               if(tmpStr[0] != '/')
1554                 {
1555                   fits_get_cwd(cwd,status);
1556                   strcat(cwd,"/");
1557 
1558 
1559                   strcat(cwd,tmpStr);
1560                   fits_clean_url(cwd,tmpStr,status);
1561                 }
1562 
1563               strcpy(oldinfile,tmpStr);
1564             }
1565 
1566           if (!strcmp(urltype, oldurltype) && !strcmp(tmpinfile, oldinfile) )
1567           {
1568               /* identical type of file and root file name */
1569 
1570               if ( (!rowfilter[0] && !oldrowfilter[0] &&
1571                     !binspec[0]   && !oldbinspec[0] &&
1572                     !colspec[0]   && !oldcolspec[0])
1573 
1574                   /* no filtering or binning specs for either file, so */
1575                   /* this is a case where the same file is being reopened. */
1576                   /* It doesn't matter if the extensions are different */
1577 
1578                       ||   /* or */
1579 
1580                   (!strcmp(rowfilter, oldrowfilter) &&
1581                    !strcmp(binspec, oldbinspec)     &&
1582                    !strcmp(colspec, oldcolspec)     &&
1583                    !strcmp(extspec, oldextspec) ) )
1584 
1585                   /* filtering specs are given and are identical, and */
1586                   /* the same extension is specified */
1587 
1588               {
1589                   if (mode == READWRITE && oldFptr->writemode == READONLY)
1590                   {
1591                     /*
1592                       cannot assume that a file previously opened with READONLY
1593                       can now be written to (e.g., files on CDROM, or over the
1594                       the network, or STDIN), so return with an error.
1595                     */
1596 
1597                     ffpmsg(
1598                 "cannot reopen file READWRITE when previously opened READONLY");
1599                     ffpmsg(url);
1600                     return(*status = FILE_NOT_OPENED);
1601                   }
1602 
1603                   *fptr = (fitsfile *) calloc(1, sizeof(fitsfile));
1604 
1605                   if (!(*fptr))
1606                   {
1607                      ffpmsg(
1608                    "failed to allocate structure for following file: (ffopen)");
1609                      ffpmsg(url);
1610                      return(*status = MEMORY_ALLOCATION);
1611                   }
1612 
1613                   (*fptr)->Fptr = oldFptr; /* point to the structure */
1614                   (*fptr)->HDUposition = 0;     /* set initial position */
1615                 (((*fptr)->Fptr)->open_count)++;  /* increment usage counter */
1616 
1617                   if (binspec[0])  /* if binning specified, don't move */
1618                       extspec[0] = '\0';
1619 
1620                   /* all the filtering has already been applied, so ignore */
1621                   rowfilter[0] = '\0';
1622                   binspec[0] = '\0';
1623                   colspec[0] = '\0';
1624 
1625                   *isopen = 1;
1626               }
1627             }
1628         }
1629     }
1630     return(*status);
1631 }
1632 /*--------------------------------------------------------------------------*/
fits_is_this_a_copy(char * urltype)1633 int fits_is_this_a_copy(char *urltype) /* I - type of file */
1634 /*
1635   specialized routine that returns 1 if the file is known to be a temporary
1636   copy of the originally opened file.  Otherwise it returns 0.
1637 */
1638 {
1639   int iscopy;
1640 
1641   if (!strncmp(urltype, "mem", 3) )
1642      iscopy = 1;    /* file copy is in memory */
1643   else if (!strncmp(urltype, "compress", 8) )
1644      iscopy = 1;    /* compressed diskfile that is uncompressed in memory */
1645   else if (!strncmp(urltype, "http", 4) )
1646      iscopy = 1;    /* copied file using http protocol */
1647   else if (!strncmp(urltype, "ftp", 3) )
1648      iscopy = 1;    /* copied file using ftp protocol */
1649   else if (!strncmp(urltype, "gsiftp", 6) )
1650      iscopy = 1;    /* copied file using gsiftp protocol */
1651   else if (!strncpy(urltype, "stdin", 5) )
1652      iscopy = 1;    /* piped stdin has been copied to memory */
1653   else
1654      iscopy = 0;    /* file is not known to be a copy */
1655 
1656     return(iscopy);
1657 }
1658 /*--------------------------------------------------------------------------*/
find_quote(char ** string)1659 static int find_quote(char **string)
1660 
1661 /*
1662     look for the closing single quote character in the input string
1663 */
1664 {
1665     char *tstr;
1666 
1667     tstr = *string;
1668 
1669     while (*tstr) {
1670         if (*tstr == '\'') { /* found the closing quote */
1671            *string = tstr + 1;  /* set pointer to next char */
1672            return(0);
1673         } else {  /* skip over any other character */
1674            tstr++;
1675         }
1676     }
1677     return(1);  /* opps, didn't find the closing character */
1678 }
1679 /*--------------------------------------------------------------------------*/
find_doublequote(char ** string)1680 static int find_doublequote(char **string)
1681 
1682 /*
1683     look for the closing double quote character in the input string
1684 */
1685 {
1686     char *tstr;
1687 
1688     tstr = *string;
1689 
1690     while (*tstr) {
1691         if (*tstr == '"') { /* found the closing quote */
1692            *string = tstr + 1;  /* set pointer to next char */
1693            return(0);
1694         } else {  /* skip over any other character */
1695            tstr++;
1696         }
1697     }
1698     return(1);  /* opps, didn't find the closing character */
1699 }
1700 
1701 /*--------------------------------------------------------------------------*/
find_paren(char ** string)1702 static int find_paren(char **string)
1703 
1704 /*
1705     look for the closing parenthesis character in the input string
1706 */
1707 {
1708     char *tstr;
1709 
1710     tstr = *string;
1711 
1712     while (*tstr) {
1713 
1714         if (*tstr == ')') { /* found the closing parens */
1715            *string = tstr + 1;  /* set pointer to next char */
1716            return(0);
1717         } else if (*tstr == '(') { /* found another level of parens */
1718            tstr++;
1719            if (find_paren(&tstr)) return(1);
1720         } else if (*tstr == '[') {
1721            tstr++;
1722            if (find_bracket(&tstr)) return(1);
1723         } else if (*tstr == '{') {
1724            tstr++;
1725            if (find_curlybracket(&tstr)) return(1);
1726         } else if (*tstr == '"') {
1727            tstr++;
1728            if (find_doublequote(&tstr)) return(1);
1729         } else if (*tstr == '\'') {
1730            tstr++;
1731            if (find_quote(&tstr)) return(1);
1732         } else {
1733            tstr++;
1734         }
1735     }
1736     return(1);  /* opps, didn't find the closing character */
1737 }
1738 /*--------------------------------------------------------------------------*/
find_bracket(char ** string)1739 static int find_bracket(char **string)
1740 
1741 /*
1742     look for the closing bracket character in the input string
1743 */
1744 {
1745     char *tstr;
1746 
1747     tstr = *string;
1748 
1749     while (*tstr) {
1750         if (*tstr == ']') { /* found the closing bracket */
1751            *string = tstr + 1;  /* set pointer to next char */
1752            return(0);
1753         } else if (*tstr == '(') { /* found another level of parens */
1754            tstr++;
1755            if (find_paren(&tstr)) return(1);
1756         } else if (*tstr == '[') {
1757            tstr++;
1758            if (find_bracket(&tstr)) return(1);
1759         } else if (*tstr == '{') {
1760            tstr++;
1761            if (find_curlybracket(&tstr)) return(1);
1762         } else if (*tstr == '"') {
1763            tstr++;
1764            if (find_doublequote(&tstr)) return(1);
1765         } else if (*tstr == '\'') {
1766            tstr++;
1767            if (find_quote(&tstr)) return(1);
1768         } else {
1769            tstr++;
1770         }
1771     }
1772     return(1);  /* opps, didn't find the closing character */
1773 }
1774 /*--------------------------------------------------------------------------*/
find_curlybracket(char ** string)1775 static int find_curlybracket(char **string)
1776 
1777 /*
1778     look for the closing curly bracket character in the input string
1779 */
1780 {
1781     char *tstr;
1782 
1783     tstr = *string;
1784 
1785     while (*tstr) {
1786         if (*tstr == '}') { /* found the closing curly bracket */
1787            *string = tstr + 1;  /* set pointer to next char */
1788            return(0);
1789         } else if (*tstr == '(') { /* found another level of parens */
1790            tstr++;
1791            if (find_paren(&tstr)) return(1);
1792         } else if (*tstr == '[') {
1793            tstr++;
1794            if (find_bracket(&tstr)) return(1);
1795         } else if (*tstr == '{') {
1796            tstr++;
1797            if (find_curlybracket(&tstr)) return(1);
1798         } else if (*tstr == '"') {
1799            tstr++;
1800            if (find_doublequote(&tstr)) return(1);
1801         } else if (*tstr == '\'') {
1802            tstr++;
1803            if (find_quote(&tstr)) return(1);
1804         } else {
1805            tstr++;
1806         }
1807     }
1808     return(1);  /* opps, didn't find the closing character */
1809 }
1810 /*--------------------------------------------------------------------------*/
comma2semicolon(char * string)1811 int comma2semicolon(char *string)
1812 
1813 /*
1814     replace commas with semicolons, unless the comma is within a quoted or bracketed expression
1815 */
1816 {
1817     char *tstr;
1818 
1819     tstr = string;
1820 
1821     while (*tstr) {
1822 
1823         if (*tstr == ',') { /* found a comma */
1824            *tstr = ';';
1825            tstr++;
1826         } else if (*tstr == '(') { /* found another level of parens */
1827            tstr++;
1828            if (find_paren(&tstr)) return(1);
1829         } else if (*tstr == '[') {
1830            tstr++;
1831            if (find_bracket(&tstr)) return(1);
1832         } else if (*tstr == '{') {
1833            tstr++;
1834            if (find_curlybracket(&tstr)) return(1);
1835         } else if (*tstr == '"') {
1836            tstr++;
1837            if (find_doublequote(&tstr)) return(1);
1838         } else if (*tstr == '\'') {
1839            tstr++;
1840            if (find_quote(&tstr)) return(1);
1841         } else {
1842            tstr++;
1843         }
1844     }
1845     return(0);  /* reached end of string */
1846 }
1847 /*--------------------------------------------------------------------------*/
ffedit_columns(fitsfile ** fptr,char * outfile,char * expr,int * status)1848 int ffedit_columns(
1849            fitsfile **fptr,  /* IO - pointer to input table; on output it  */
1850                              /*      points to the new selected rows table */
1851            char *outfile,    /* I - name for output file */
1852            char *expr,       /* I - column edit expression    */
1853            int *status)
1854 /*
1855    modify columns in a table and/or header keywords in the HDU
1856 */
1857 {
1858     fitsfile *newptr;
1859     int ii, hdunum, slen, colnum = -1, testnum, deletecol = 0, savecol = 0;
1860     int numcols = 0, *colindex = 0, tstatus = 0;
1861     char *tstbuff=0, *cptr, *cptr2, *cptr3, *clause = NULL, keyname[FLEN_KEYWORD];
1862     char colname[FLEN_VALUE], oldname[FLEN_VALUE], colformat[FLEN_VALUE];
1863     char *file_expr = NULL, testname[FLEN_VALUE], card[FLEN_CARD];
1864 
1865     if (*outfile)
1866     {
1867       /* create new empty file in to hold the selected rows */
1868       if (ffinit(&newptr, outfile, status) > 0)
1869       {
1870         ffpmsg("failed to create file for copy (ffedit_columns)");
1871         return(*status);
1872       }
1873 
1874       fits_get_hdu_num(*fptr, &hdunum);  /* current HDU number in input file */
1875 
1876       /* copy all HDUs to the output copy, if the 'only_one' flag is not set */
1877       if (!((*fptr)->Fptr)->only_one) {
1878         for (ii = 1; 1; ii++)
1879         {
1880           if (fits_movabs_hdu(*fptr, ii, NULL, status) > 0)
1881             break;
1882 
1883           fits_copy_hdu(*fptr, newptr, 0, status);
1884         }
1885 
1886         if (*status == END_OF_FILE)
1887         {
1888           *status = 0;              /* got the expected EOF error; reset = 0  */
1889         }
1890         else if (*status > 0)
1891         {
1892           ffclos(newptr, status);
1893           ffpmsg("failed to copy all HDUs from input file (ffedit_columns)");
1894           return(*status);
1895         }
1896 
1897 
1898       } else {
1899         /* only copy the primary array and the designated table extension */
1900 	fits_movabs_hdu(*fptr, 1, NULL, status);
1901 	fits_copy_hdu(*fptr, newptr, 0, status);
1902 	fits_movabs_hdu(*fptr, hdunum, NULL, status);
1903 	fits_copy_hdu(*fptr, newptr, 0, status);
1904         if (*status > 0)
1905         {
1906           ffclos(newptr, status);
1907           ffpmsg("failed to copy all HDUs from input file (ffedit_columns)");
1908           return(*status);
1909         }
1910         hdunum = 2;
1911       }
1912 
1913       /* close the original file and return ptr to the new image */
1914       ffclos(*fptr, status);
1915 
1916       *fptr = newptr; /* reset the pointer to the new table */
1917 
1918       /* move back to the selected table HDU */
1919       if (fits_movabs_hdu(*fptr, hdunum, NULL, status) > 0)
1920       {
1921          ffpmsg("failed to copy the input file (ffedit_columns)");
1922          return(*status);
1923       }
1924     }
1925 
1926     /* remove the "col " from the beginning of the column edit expression */
1927     cptr = expr + 4;
1928 
1929     while (*cptr == ' ')
1930          cptr++;         /* skip leading white space */
1931 
1932     /* Check if need to import expression from a file */
1933 
1934     if( *cptr=='@' ) {
1935        if( ffimport_file( cptr+1, &file_expr, status ) ) return(*status);
1936        cptr = file_expr;
1937        while (*cptr == ' ')
1938           cptr++;         /* skip leading white space... again */
1939     }
1940 
1941     tstatus = 0;
1942     ffgncl(*fptr, &numcols, &tstatus);  /* get initial # of cols */
1943 
1944     /* as of July 2012, the CFITSIO column filter syntax was modified */
1945     /* so that commas may be used to separate clauses, as well as semi-colons. */
1946     /* This was done because users cannot enter the semi-colon in the HEASARC's */
1947     /* Hera on-line data processing system for computer security reasons.  */
1948     /* Therefore, we must convert those commas back to semi-colons here, but we */
1949     /* must not convert any columns that occur within parenthesies.  */
1950 
1951     if (comma2semicolon(cptr)) {
1952          ffpmsg("parsing error in column filter expression");
1953          ffpmsg(cptr);
1954          if( file_expr ) free( file_expr );
1955          *status = PARSE_SYNTAX_ERR;
1956          return(*status);
1957     }
1958 
1959     /* parse expression and get first clause, if more than 1 */
1960     while ((slen = fits_get_token2(&cptr, ";", &clause, NULL, status)) > 0 )
1961     {
1962         if( *cptr==';' ) cptr++;
1963         clause[slen] = '\0';
1964 
1965         if (clause[0] == '!' || clause[0] == '-')
1966         {
1967 	    char *clause1 = clause+1;
1968 	    int clen = clause1[0] ? strlen(clause1) : 0;
1969             /* ===================================== */
1970             /* Case I. delete this column or keyword */
1971             /* ===================================== */
1972 
1973 	    /* Case Ia. delete column names with 0-or-more wildcard
1974 	            -COLNAME+ - delete repeated columns with exact name
1975 		    -COLNAM*+ - delete columns matching patterns
1976 	    */
1977 	    if (*status == 0 &&
1978 		clen > 1 && clause1[0] != '#' &&
1979 		clause1[clen-1] == '+') {
1980 
1981 	      clause1[clen-1] = 0; clen--;
1982 
1983 	      /* Note that this is a delete 0 or more specification,
1984 		 which means that no matching columns is not an error. */
1985 	      do {
1986 		int status_del = 0;
1987 
1988 		/* Have to set status=0 so we can reset the search at
1989 		   start column.  Because we are deleting columns on
1990 		   the fly here, we have to reset the search every
1991 		   time. The only penalty here is execution time
1992 		   because leaving *status == COL_NOT_UNIQUE is merely
1993 		   an optimization for tables assuming the tables do
1994 		   not change from one call to the next. (an
1995 		   assumption broken in this loop) */
1996 		*status = 0;
1997 		ffgcno(*fptr, CASEINSEN, clause1, &colnum, status);
1998 		/* ffgcno returns COL_NOT_UNIQUE if there are multiple columns,
1999 		   and COL_NOT_FOUND after the last column is found, and
2000 		   COL_NOT_FOUND if no matches were found */
2001 		if (*status != 0 && *status != COL_NOT_UNIQUE) break;
2002 
2003                 if (ffdcol(*fptr, colnum, &status_del) > 0) {
2004 		  ffpmsg("failed to delete column in input file:");
2005 		  ffpmsg(clause);
2006 		  if( colindex ) free( colindex );
2007 		  if( file_expr ) free( file_expr );
2008 		  if( clause ) free(clause);
2009 		  return (*status = status_del);
2010 		}
2011                 deletecol = 1; /* set flag that at least one col was deleted */
2012                 numcols--;
2013 	      } while (*status == COL_NOT_UNIQUE);
2014 
2015 	      *status = 0; /* No matches are still successful */
2016 	      colnum = -1; /* Ignore the column we found */
2017 
2018 	    /* Case Ib. delete column names with wildcard or not
2019 	            -COLNAME  - deleted exact column
2020 		    -COLNAM*  - delete first column that matches pattern
2021 	       Note no leading '#'
2022 	    */
2023 	    } else if (clause1[0] && clause1[0] != '#' &&
2024 		ffgcno(*fptr, CASEINSEN, clause1, &colnum, status) <= 0)
2025             {
2026                 /* a column with this name exists, so try to delete it */
2027                 if (ffdcol(*fptr, colnum, status) > 0)
2028                 {
2029                     ffpmsg("failed to delete column in input file:");
2030                     ffpmsg(clause);
2031                     if( colindex ) free( colindex );
2032                     if( file_expr ) free( file_expr );
2033 		    if( clause ) free(clause);
2034                     return(*status);
2035                 }
2036                 deletecol = 1; /* set flag that at least one col was deleted */
2037                 numcols--;
2038                 colnum = -1;
2039             }
2040 	    /* Case Ic. delete keyword(s)
2041 	            -KEYNAME,#KEYNAME  - delete exact keyword (first match)
2042 		    -KEYNAM*,#KEYNAM*  - delete first matching keyword
2043 		    -KEYNAME+,-#KEYNAME+ - delete 0-or-more exact matches of exact keyword
2044 		    -KEYNAM*+,-#KEYNAM*+ - delete 0-or-more wildcard matches
2045 	       Note the preceding # is optional if no conflicting column name exists
2046 	       and that wildcard patterns are described in "colfilter" section of
2047 	       documentation.
2048 	    */
2049             else
2050             {
2051 	      int delall = 0;
2052 	      int haswild = 0;
2053 	        ffcmsg();   /* clear previous error message from ffgcno */
2054                 /* try deleting a keyword with this name */
2055                 *status = 0;
2056 		/* skip past leading '#' if any */
2057 		if (clause1[0] == '#') clause1++;
2058 		clen = strlen(clause1);
2059 
2060 		/* Repeat deletion of keyword if requested with trailing '+' */
2061 		if (clen > 1 && clause1[clen-1] == '+') {
2062 		  delall = 1;
2063 		  clause1[clen-1] = 0;
2064 		}
2065 		/* Determine if this pattern has wildcards */
2066 		if (strchr(clause1,'?') || strchr(clause1,'*') || strchr(clause1,'#')) {
2067 		  haswild = 1;
2068 		}
2069 
2070 		if (haswild) {
2071 		  /* ffdkey() behaves differently if the pattern has a wildcard:
2072 		     it only checks from the "current" header position to the end, and doesn't
2073 		     check before the "current" header position.  Therefore, for the
2074 		     case of wildcards we will have to reset to the beginning. */
2075 		  ffmaky(*fptr, 1, status);  /* reset pointer to beginning of header */
2076 		}
2077 
2078 		/* Single or repeated deletions until done */
2079 		do {
2080 		  if (ffdkey(*fptr, clause1, status) > 0)
2081 		    {
2082 		      if (delall && *status == KEY_NO_EXIST) {
2083 			/* Found last wildcard item. Stop deleting */
2084 			ffcmsg();
2085 			*status = 0;
2086 			delall = 0; /* Force end of this loop */
2087 		      } else {
2088 			/* This was not a wildcard deletion, or it resulted in
2089 			   another kind of error */
2090 			ffpmsg("column or keyword to be deleted does not exist:");
2091 			ffpmsg(clause1);
2092 			if( colindex ) free( colindex );
2093 			if( file_expr ) free( file_expr );
2094 			if( clause ) free(clause);
2095 			return(*status);
2096 		      }
2097 		    }
2098 		} while(delall); /* end do{} */
2099             }
2100         }
2101         else
2102         {
2103             /* ===================================================== */
2104             /* Case II:
2105 	       this is either a column name, (case 1)
2106 
2107                or a new column name followed by double = ("==") followed
2108                by the old name which is to be renamed. (case 2A)
2109 
2110                or a column or keyword name followed by a single "=" and a
2111 	       calculation expression (case 2B) */
2112             /* ===================================================== */
2113             cptr2 = clause;
2114             slen = fits_get_token2(&cptr2, "( =", &tstbuff, NULL, status);
2115 
2116             if (slen == 0 || *status)
2117             {
2118                 ffpmsg("error: column or keyword name is blank (ffedit_columns):");
2119                 ffpmsg(clause);
2120                 if( colindex ) free( colindex );
2121                 if( file_expr ) free( file_expr );
2122 		if (clause) free(clause);
2123                 if (*status==0)
2124                    *status=URL_PARSE_ERROR;
2125                 return(*status);
2126             }
2127             if (strlen(tstbuff) > FLEN_VALUE-1)
2128             {
2129                 ffpmsg("error: column or keyword name is too long (ffedit_columns):");
2130                 ffpmsg(clause);
2131                 if( colindex ) free( colindex );
2132                 if( file_expr ) free( file_expr );
2133 		if (clause) free(clause);
2134                 free(tstbuff);
2135                 return(*status= URL_PARSE_ERROR);
2136             }
2137             strcpy(colname, tstbuff);
2138             free(tstbuff);
2139             tstbuff=0;
2140 
2141 	    /* If this is a keyword of the form
2142 	         #KEYWORD#
2143 	       then transform to the form
2144 	         #KEYWORDn
2145 	       where n is the previously used column number
2146 	    */
2147 	    if (colname[0] == '#' &&
2148 		strstr(colname+1, "#") == (colname + strlen(colname) - 1))
2149 	    {
2150 		if (colnum <= 0)
2151 		  {
2152 		    ffpmsg("The keyword name:");
2153 		    ffpmsg(colname);
2154 		    ffpmsg("is invalid unless a column has been previously");
2155 		    ffpmsg("created or editted by a calculator command");
2156                     if( file_expr ) free( file_expr );
2157 		    if (clause) free(clause);
2158 		    return(*status = URL_PARSE_ERROR);
2159 		  }
2160 		colname[strlen(colname)-1] = '\0';
2161 		/* Make keyword name and put it in oldname */
2162 		ffkeyn(colname+1, colnum, oldname, status);
2163 		if (*status) return (*status);
2164 		/* Re-copy back into colname */
2165 		strcpy(colname+1,oldname);
2166 	    }
2167             else if  (strstr(colname, "#") == (colname + strlen(colname) - 1))
2168 	    {
2169 	        /*  colname is of the form "NAME#";  if
2170 		      a) colnum is defined, and
2171 		      b) a column with literal name "NAME#" does not exist, and
2172 		      c) a keyword with name "NAMEn" (where n=colnum) exists, then
2173 		    transfrom the colname string to "NAMEn", otherwise
2174 		    do nothing.
2175 		*/
2176 		if (colnum > 0) {  /* colnum must be defined */
2177 		  tstatus = 0;
2178                   ffgcno(*fptr, CASEINSEN, colname, &testnum, &tstatus);
2179 		  if (tstatus != 0 && tstatus != COL_NOT_UNIQUE)
2180 		  {
2181 		    /* OK, column doesn't exist, now see if keyword exists */
2182 		    ffcmsg();   /* clear previous error message from ffgcno */
2183 		    strcpy(testname, colname);
2184  		    testname[strlen(testname)-1] = '\0';
2185 		    /* Make keyword name and put it in oldname */
2186 		    ffkeyn(testname, colnum, oldname, status);
2187 		    if (*status) {
2188                       if( file_expr ) free( file_expr );
2189 		      if (clause) free(clause);
2190 		      return (*status);
2191 		    }
2192 
2193 		    tstatus = 0;
2194 		    if (!fits_read_card(*fptr, oldname, card, &tstatus)) {
2195 		      /* Keyword does exist; copy real name back into colname */
2196 		      strcpy(colname,oldname);
2197 		    }
2198 		  }
2199                 }
2200 	    }
2201 
2202             /* if we encountered an opening parenthesis, then we need to */
2203             /* find the closing parenthesis, and concatinate the 2 strings */
2204             /* This supports expressions like:
2205                 [col #EXTNAME(Extension name)="GTI"]
2206             */
2207             if (*cptr2  == '(')
2208             {
2209                 if (fits_get_token2(&cptr2, ")", &tstbuff, NULL, status)==0)
2210                 {
2211                    strcat(colname,")");
2212                 }
2213                 else
2214                 {
2215                    if ((strlen(tstbuff) + strlen(colname) + 1) >
2216                         FLEN_VALUE-1)
2217                    {
2218                       ffpmsg("error: column name is too long (ffedit_columns):");
2219                       if( file_expr ) free( file_expr );
2220 		      if (clause) free(clause);
2221                       free(tstbuff);
2222                       *status=URL_PARSE_ERROR;
2223 		      return (*status);
2224                    }
2225                    strcat(colname, tstbuff);
2226                    strcat(colname, ")");
2227                    free(tstbuff);
2228                    tstbuff=0;
2229                 }
2230                 cptr2++;
2231             }
2232 
2233             while (*cptr2 == ' ')
2234                  cptr2++;         /* skip white space */
2235 
2236             if (*cptr2 != '=')
2237             {
2238               /* ------------------------------------ */
2239               /* case 1 - simply the name of a column */
2240               /* ------------------------------------ */
2241 
2242               /* look for matching column */
2243               ffgcno(*fptr, CASEINSEN, colname, &testnum, status);
2244 
2245               while (*status == COL_NOT_UNIQUE)
2246               {
2247                  /* the column name contained wild cards, and it */
2248                  /* matches more than one column in the table. */
2249 
2250 		 colnum = testnum;
2251 
2252                  /* keep this column in the output file */
2253                  savecol = 1;
2254 
2255                  if (!colindex)
2256                     colindex = (int *) calloc(999, sizeof(int));
2257 
2258                  colindex[colnum - 1] = 1;  /* flag this column number */
2259 
2260                  /* look for other matching column names */
2261                  ffgcno(*fptr, CASEINSEN, colname, &testnum, status);
2262 
2263                  if (*status == COL_NOT_FOUND)
2264                     *status = 999;  /* temporary status flag value */
2265               }
2266 
2267               if (*status <= 0)
2268               {
2269 	         colnum = testnum;
2270 
2271                  /* keep this column in the output file */
2272                  savecol = 1;
2273 
2274                  if (!colindex)
2275                     colindex = (int *) calloc(999, sizeof(int));
2276 
2277                  colindex[colnum - 1] = 1;  /* flag this column number */
2278               }
2279               else if (*status == 999)
2280               {
2281                   /* this special flag value does not represent an error */
2282                   *status = 0;
2283               }
2284               else
2285               {
2286                ffpmsg("Syntax error in columns specifier in input URL:");
2287                ffpmsg(cptr2);
2288                if( colindex ) free( colindex );
2289                if( file_expr ) free( file_expr );
2290 	       if (clause) free(clause);
2291                return(*status = URL_PARSE_ERROR);
2292               }
2293             }
2294             else
2295             {
2296               /* ----------------------------------------------- */
2297               /* case 2 where the token ends with an equals sign */
2298               /* ----------------------------------------------- */
2299 
2300               cptr2++;   /* skip over the first '=' */
2301 
2302               if (*cptr2 == '=')
2303               {
2304                 /*................................................. */
2305                 /*  Case A:  rename a column or keyword;  syntax is
2306                     "new_name == old_name"  */
2307                 /*................................................. */
2308 
2309                 cptr2++;  /* skip the 2nd '=' */
2310                 while (*cptr2 == ' ')
2311                       cptr2++;       /* skip white space */
2312 
2313                 if (fits_get_token2(&cptr2, " ", &tstbuff, NULL, status)==0)
2314                 {
2315                    oldname[0]=0;
2316                 }
2317                 else
2318                 {
2319                    if (strlen(tstbuff) > FLEN_VALUE-1)
2320                    {
2321                       ffpmsg("error: column name syntax is too long (ffedit_columns):");
2322                       if( file_expr ) free( file_expr );
2323 		      if (clause) free(clause);
2324                       free(tstbuff);
2325                       *status=URL_PARSE_ERROR;
2326 		      return (*status);
2327                    }
2328                    strcpy(oldname, tstbuff);
2329                    free(tstbuff);
2330                    tstbuff=0;
2331                 }
2332                 /* get column number of the existing column */
2333                 if (ffgcno(*fptr, CASEINSEN, oldname, &colnum, status) <= 0)
2334                 {
2335                     /* modify the TTYPEn keyword value with the new name */
2336                     ffkeyn("TTYPE", colnum, keyname, status);
2337 
2338                     if (ffmkys(*fptr, keyname, colname, NULL, status) > 0)
2339                     {
2340                       ffpmsg("failed to rename column in input file");
2341                       ffpmsg(" oldname =");
2342                       ffpmsg(oldname);
2343                       ffpmsg(" newname =");
2344                       ffpmsg(colname);
2345                       if( colindex ) free( colindex );
2346                       if( file_expr ) free( file_expr );
2347 	              if (clause) free(clause);
2348                       return(*status);
2349                     }
2350                     /* keep this column in the output file */
2351                     savecol = 1;
2352                     if (!colindex)
2353                        colindex = (int *) calloc(999, sizeof(int));
2354 
2355                     colindex[colnum - 1] = 1;  /* flag this column number */
2356                 }
2357                 else
2358                 {
2359                     /* try renaming a keyword */
2360 		    ffcmsg();   /* clear error message stack */
2361                     *status = 0;
2362                     if (ffmnam(*fptr, oldname, colname, status) > 0)
2363                     {
2364                       ffpmsg("column or keyword to be renamed does not exist:");
2365                         ffpmsg(clause);
2366                         if( colindex ) free( colindex );
2367                         if( file_expr ) free( file_expr );
2368 			if (clause) free(clause);
2369                         return(*status);
2370                     }
2371                 }
2372               }
2373               else
2374               {
2375                 /*...................................................... */
2376                 /* Case B: */
2377                 /* this must be a general column/keyword calc expression */
2378                 /* "name = expression" or "colname(TFORM) = expression" */
2379                 /*...................................................... */
2380 
2381                 /* parse the name and TFORM values, if present */
2382                 colformat[0] = '\0';
2383                 cptr3 = colname;
2384 
2385                 if (fits_get_token2(&cptr3, "(", &tstbuff, NULL, status)==0)
2386                 {
2387                    oldname[0]=0;
2388                 }
2389                 else
2390                 {
2391                    if (strlen(tstbuff) > FLEN_VALUE-1)
2392                    {
2393                          ffpmsg("column expression is too long (ffedit_columns)");
2394                          if( colindex ) free( colindex );
2395                          if( file_expr ) free( file_expr );
2396 		         if (clause) free(clause);
2397                          free(tstbuff);
2398                          *status=URL_PARSE_ERROR;
2399                          return(*status);
2400                    }
2401                    strcpy(oldname, tstbuff);
2402                    free(tstbuff);
2403                    tstbuff=0;
2404                 }
2405                 if (cptr3[0] == '(' )
2406                 {
2407                    cptr3++;  /* skip the '(' */
2408                    if (fits_get_token2(&cptr3, ")", &tstbuff, NULL, status)==0)
2409                    {
2410                       colformat[0]=0;
2411                    }
2412                    else
2413                    {
2414                       if (strlen(tstbuff) > FLEN_VALUE-1)
2415                       {
2416                             ffpmsg("column expression is too long (ffedit_columns)");
2417                             if( colindex ) free( colindex );
2418                             if( file_expr ) free( file_expr );
2419 		            if (clause) free(clause);
2420                             free(tstbuff);
2421                             *status=URL_PARSE_ERROR;
2422                             return(*status);
2423                       }
2424                       strcpy(colformat, tstbuff);
2425                       free(tstbuff);
2426                       tstbuff=0;
2427                    }
2428                 }
2429 
2430                 /* calculate values for the column or keyword */
2431                 /*   cptr2 = the expression to be calculated */
2432                 /*   oldname = name of the column or keyword */
2433                 /*   colformat = column format, or keyword comment string */
2434                 if (fits_calculator(*fptr, cptr2, *fptr, oldname, colformat,
2435        	                        status) > 0) {
2436 
2437                         ffpmsg("Unable to calculate expression");
2438                         if( colindex ) free( colindex );
2439                         if( file_expr ) free( file_expr );
2440 			if (clause) free(clause);
2441                          return(*status);
2442                 }
2443 
2444                 /* test if this is a column and not a keyword */
2445                 tstatus = 0;
2446                 ffgcno(*fptr, CASEINSEN, oldname, &testnum, &tstatus);
2447                 if (tstatus == 0)
2448                 {
2449                     /* keep this column in the output file */
2450 		    colnum = testnum;
2451                     savecol = 1;
2452 
2453                     if (!colindex)
2454                       colindex = (int *) calloc(999, sizeof(int));
2455 
2456                     colindex[colnum - 1] = 1;
2457                     if (colnum > numcols)numcols++;
2458                 }
2459 		else
2460 		{
2461 		   ffcmsg();  /* clear the error message stack */
2462 		}
2463               }
2464             }
2465         }
2466 	if (clause) free(clause);  /* free old clause before getting new one */
2467         clause = NULL;
2468     }
2469 
2470     if (savecol && !deletecol)
2471     {
2472        /* need to delete all but the specified columns */
2473        for (ii = numcols; ii > 0; ii--)
2474        {
2475          if (!colindex[ii-1])  /* delete this column */
2476          {
2477            if (ffdcol(*fptr, ii, status) > 0)
2478            {
2479              ffpmsg("failed to delete column in input file:");
2480              ffpmsg(clause);
2481              if( colindex ) free( colindex );
2482              if( file_expr ) free( file_expr );
2483 	     if (clause) free(clause);
2484              return(*status);
2485            }
2486          }
2487        }
2488     }
2489 
2490     if( colindex ) free( colindex );
2491     if( file_expr ) free( file_expr );
2492     if (clause) free(clause);
2493 
2494     return(*status);
2495 }
2496 /*--------------------------------------------------------------------------*/
fits_copy_cell2image(fitsfile * fptr,fitsfile * newptr,char * colname,long rownum,int * status)2497 int fits_copy_cell2image(
2498 	   fitsfile *fptr,   /* I - point to input table */
2499 	   fitsfile *newptr, /* O - existing output file; new image HDU
2500 				    will be appended to it */
2501            char *colname,    /* I - column name / number containing the image*/
2502            long rownum,      /* I - number of the row containing the image */
2503            int *status)      /* IO - error status */
2504 
2505 /*
2506   Copy a table cell of a given row and column into an image extension.
2507   The output file must already have been created.  A new image
2508   extension will be created in that file.
2509 
2510   This routine was written by Craig Markwardt, GSFC
2511 */
2512 
2513 {
2514     unsigned char buffer[30000];
2515     int hdutype, colnum, typecode, bitpix, naxis, maxelem, tstatus;
2516     LONGLONG naxes[9], nbytes, firstbyte, ntodo;
2517     LONGLONG repeat, startpos, elemnum, rowlen, tnull;
2518     long twidth, incre;
2519     double scale, zero;
2520     char tform[20];
2521     char card[FLEN_CARD];
2522     char templt[FLEN_CARD] = "";
2523 
2524     /* Table-to-image keyword translation table  */
2525     /*                        INPUT      OUTPUT  */
2526     /*                       01234567   01234567 */
2527     char *patterns[][2] = {{"TSCALn",  "BSCALE"  },  /* Standard FITS keywords */
2528 			   {"TZEROn",  "BZERO"   },
2529 			   {"TUNITn",  "BUNIT"   },
2530 			   {"TNULLn",  "BLANK"   },
2531 			   {"TDMINn",  "DATAMIN" },
2532 			   {"TDMAXn",  "DATAMAX" },
2533 			   {"iCTYPn",  "CTYPEi"  },  /* Coordinate labels */
2534 			   {"iCTYna",  "CTYPEia" },
2535 			   {"iCUNIn",  "CUNITi"  },  /* Coordinate units */
2536 			   {"iCUNna",  "CUNITia" },
2537 			   {"iCRVLn",  "CRVALi"  },  /* WCS keywords */
2538 			   {"iCRVna",  "CRVALia" },
2539 			   {"iCDLTn",  "CDELTi"  },
2540 			   {"iCDEna",  "CDELTia" },
2541 			   {"iCRPXn",  "CRPIXi"  },
2542 			   {"iCRPna",  "CRPIXia" },
2543 			   {"ijPCna",  "PCi_ja"  },
2544 			   {"ijCDna",  "CDi_ja"  },
2545 			   {"iVn_ma",  "PVi_ma"  },
2546 			   {"iSn_ma",  "PSi_ma"  },
2547 			   {"iCRDna",  "CRDERia" },
2548 			   {"iCSYna",  "CSYERia" },
2549 			   {"iCROTn",  "CROTAi"  },
2550 			   {"WCAXna",  "WCSAXESa"},
2551 			   {"WCSNna",  "WCSNAMEa"},
2552 
2553 			   {"LONPna",  "LONPOLEa"},
2554 			   {"LATPna",  "LATPOLEa"},
2555 			   {"EQUIna",  "EQUINOXa"},
2556 			   {"MJDOBn",  "MJD-OBS" },
2557 			   {"MJDAn",   "MJD-AVG" },
2558 			   {"RADEna",  "RADESYSa"},
2559 			   {"iCNAna",  "CNAMEia" },
2560 			   {"DAVGn",   "DATE-AVG"},
2561 
2562                            /* Delete table keywords related to other columns */
2563 			   {"T????#a", "-"       },
2564  			   {"TC??#a",  "-"       },
2565  			   {"TWCS#a",  "-"       },
2566 			   {"TDIM#",   "-"       },
2567 			   {"iCTYPm",  "-"       },
2568 			   {"iCUNIm",  "-"       },
2569 			   {"iCRVLm",  "-"       },
2570 			   {"iCDLTm",  "-"       },
2571 			   {"iCRPXm",  "-"       },
2572 			   {"iCTYma",  "-"       },
2573 			   {"iCUNma",  "-"       },
2574 			   {"iCRVma",  "-"       },
2575 			   {"iCDEma",  "-"       },
2576 			   {"iCRPma",  "-"       },
2577 			   {"ijPCma",  "-"       },
2578 			   {"ijCDma",  "-"       },
2579 			   {"iVm_ma",  "-"       },
2580 			   {"iSm_ma",  "-"       },
2581 			   {"iCRDma",  "-"       },
2582 			   {"iCSYma",  "-"       },
2583 			   {"iCROTm",  "-"       },
2584 			   {"WCAXma",  "-"       },
2585 			   {"WCSNma",  "-"       },
2586 
2587 			   {"LONPma",  "-"       },
2588 			   {"LATPma",  "-"       },
2589 			   {"EQUIma",  "-"       },
2590 			   {"MJDOBm",  "-"       },
2591 			   {"MJDAm",   "-"       },
2592 			   {"RADEma",  "-"       },
2593 			   {"iCNAma",  "-"       },
2594 			   {"DAVGm",   "-"       },
2595 
2596 			   {"EXTNAME", "-"       },  /* Remove structural keywords*/
2597 			   {"EXTVER",  "-"       },
2598 			   {"EXTLEVEL","-"       },
2599 			   {"CHECKSUM","-"       },
2600 			   {"DATASUM", "-"       },
2601 
2602 			   {"*",       "+"       }}; /* copy all other keywords */
2603     int npat;
2604 
2605     if (*status > 0)
2606         return(*status);
2607 
2608     /* get column number */
2609     if (ffgcno(fptr, CASEINSEN, colname, &colnum, status) > 0)
2610     {
2611         ffpmsg("column containing image in table cell does not exist:");
2612         ffpmsg(colname);
2613         return(*status);
2614     }
2615 
2616     /*---------------------------------------------------*/
2617     /*  Check input and get parameters about the column: */
2618     /*---------------------------------------------------*/
2619     if ( ffgcprll(fptr, colnum, rownum, 1L, 1L, 0, &scale, &zero,
2620          tform, &twidth, &typecode, &maxelem, &startpos, &elemnum, &incre,
2621          &repeat, &rowlen, &hdutype, &tnull, (char *) buffer, status) > 0 )
2622          return(*status);
2623 
2624      /* get the actual column name, in case a column number was given */
2625     ffkeyn("", colnum, templt, &tstatus);
2626     ffgcnn(fptr, CASEINSEN, templt, colname, &colnum, &tstatus);
2627 
2628     if (hdutype != BINARY_TBL)
2629     {
2630         ffpmsg("This extension is not a binary table.");
2631         ffpmsg(" Cannot open the image in a binary table cell.");
2632         return(*status = NOT_BTABLE);
2633     }
2634 
2635     if (typecode < 0)
2636     {
2637         /* variable length array */
2638         typecode *= -1;
2639 
2640         /* variable length arrays are 1-dimensional by default */
2641         naxis = 1;
2642         naxes[0] = repeat;
2643     }
2644     else
2645     {
2646         /* get the dimensions of the image */
2647         ffgtdmll(fptr, colnum, 9, &naxis, naxes, status);
2648     }
2649 
2650     if (*status > 0)
2651     {
2652         ffpmsg("Error getting the dimensions of the image");
2653         return(*status);
2654     }
2655 
2656     /* determine BITPIX value for the image */
2657     if (typecode == TBYTE)
2658     {
2659         bitpix = BYTE_IMG;
2660         nbytes = repeat;
2661     }
2662     else if (typecode == TSHORT)
2663     {
2664         bitpix = SHORT_IMG;
2665         nbytes = repeat * 2;
2666     }
2667     else if (typecode == TLONG)
2668     {
2669         bitpix = LONG_IMG;
2670         nbytes = repeat * 4;
2671     }
2672     else if (typecode == TFLOAT)
2673     {
2674         bitpix = FLOAT_IMG;
2675         nbytes = repeat * 4;
2676     }
2677     else if (typecode == TDOUBLE)
2678     {
2679         bitpix = DOUBLE_IMG;
2680         nbytes = repeat * 8;
2681     }
2682     else if (typecode == TLONGLONG)
2683     {
2684         bitpix = LONGLONG_IMG;
2685         nbytes = repeat * 8;
2686     }
2687     else if (typecode == TLOGICAL)
2688     {
2689         bitpix = BYTE_IMG;
2690         nbytes = repeat;
2691     }
2692     else
2693     {
2694         ffpmsg("Error: the following image column has invalid datatype:");
2695         ffpmsg(colname);
2696         ffpmsg(tform);
2697         ffpmsg("Cannot open an image in a single row of this column.");
2698         return(*status = BAD_TFORM);
2699     }
2700 
2701     /* create new image in output file */
2702     if (ffcrimll(newptr, bitpix, naxis, naxes, status) > 0)
2703     {
2704         ffpmsg("failed to write required primary array keywords in the output file");
2705         return(*status);
2706     }
2707 
2708     npat = sizeof(patterns)/sizeof(patterns[0][0])/2;
2709 
2710     /* skip over the first 8 keywords, starting just after TFIELDS */
2711     fits_translate_keywords(fptr, newptr, 9, patterns, npat,
2712 			    colnum, 0, 0, status);
2713 
2714     /* add some HISTORY  */
2715     snprintf(card,FLEN_CARD,"HISTORY  This image was copied from row %ld of column '%s',",
2716             rownum, colname);
2717 /* disable this; leave it up to the caller to write history if needed.
2718     ffprec(newptr, card, status);
2719 */
2720     /* the use of ffread routine, below, requires that any 'dirty' */
2721     /* buffers in memory be flushed back to the file first */
2722 
2723     ffflsh(fptr, FALSE, status);
2724 
2725     /* finally, copy the data, one buffer size at a time */
2726     ffmbyt(fptr, startpos, TRUE, status);
2727     firstbyte = 1;
2728 
2729     /* the upper limit on the number of bytes must match the declaration */
2730     /* read up to the first 30000 bytes in the normal way with ffgbyt */
2731 
2732     ntodo = minvalue(30000, nbytes);
2733     ffgbyt(fptr, ntodo, buffer, status);
2734     ffptbb(newptr, 1, firstbyte, ntodo, buffer, status);
2735 
2736     nbytes    -= ntodo;
2737     firstbyte += ntodo;
2738 
2739     /* read any additional bytes with low-level ffread routine, for speed */
2740     while (nbytes && (*status <= 0) )
2741     {
2742         ntodo = minvalue(30000, nbytes);
2743         ffread((fptr)->Fptr, (long) ntodo, buffer, status);
2744         ffptbb(newptr, 1, firstbyte, ntodo, buffer, status);
2745         nbytes    -= ntodo;
2746         firstbyte += ntodo;
2747     }
2748 
2749     /* Re-scan the header so that CFITSIO knows about all the new keywords */
2750     ffrdef(newptr,status);
2751 
2752     return(*status);
2753 }
2754 /*--------------------------------------------------------------------------*/
fits_copy_image2cell(fitsfile * fptr,fitsfile * newptr,char * colname,long rownum,int copykeyflag,int * status)2755 int fits_copy_image2cell(
2756 	   fitsfile *fptr,   /* I - pointer to input image extension */
2757 	   fitsfile *newptr, /* I - pointer to output table */
2758            char *colname,    /* I - name of column containing the image    */
2759            long rownum,      /* I - number of the row containing the image */
2760            int copykeyflag,  /* I - controls which keywords to copy */
2761            int *status)      /* IO - error status */
2762 
2763 /*
2764    Copy an image extension into a table cell at a given row and
2765    column.  The table must have already been created.  If the "colname"
2766    column exists, it will be used, otherwise a new column will be created
2767    in the table.
2768 
2769    The "copykeyflag" parameter controls which keywords to copy from the
2770    input image to the output table header (with any appropriate translation).
2771 
2772    copykeyflag = 0  -- no keywords will be copied
2773    copykeyflag = 1  -- essentially all keywords will be copied
2774    copykeyflag = 2  -- copy only the WCS related keywords
2775 
2776   This routine was written by Craig Markwardt, GSFC
2777 
2778 */
2779 {
2780     tcolumn *colptr;
2781     unsigned char buffer[30000];
2782     int ii, hdutype, colnum, typecode, bitpix, naxis, ncols, hdunum;
2783     char tformchar, tform[20], card[FLEN_CARD];
2784     LONGLONG imgstart, naxes[9], nbytes, repeat, ntodo,firstbyte;
2785     char filename[FLEN_FILENAME+20];
2786 
2787     int npat;
2788 
2789     int naxis1;
2790     LONGLONG naxes1[9] = {0,0,0,0,0,0,0,0,0}, repeat1, width1;
2791     int typecode1;
2792     unsigned char dummy = 0;
2793 
2794     LONGLONG headstart, datastart, dataend;
2795 
2796     /* Image-to-table keyword translation table  */
2797     /*                        INPUT      OUTPUT  */
2798     /*                       01234567   01234567 */
2799     char *patterns[][2] = {{"BSCALE",  "TSCALn"  },  /* Standard FITS keywords */
2800 			   {"BZERO",   "TZEROn"  },
2801 			   {"BUNIT",   "TUNITn"  },
2802 			   {"BLANK",   "TNULLn"  },
2803 			   {"DATAMIN", "TDMINn"  },
2804 			   {"DATAMAX", "TDMAXn"  },
2805 			   {"CTYPEi",  "iCTYPn"  },  /* Coordinate labels */
2806 			   {"CTYPEia", "iCTYna"  },
2807 			   {"CUNITi",  "iCUNIn"  },  /* Coordinate units */
2808 			   {"CUNITia", "iCUNna"  },
2809 			   {"CRVALi",  "iCRVLn"  },  /* WCS keywords */
2810 			   {"CRVALia", "iCRVna"  },
2811 			   {"CDELTi",  "iCDLTn"  },
2812 			   {"CDELTia", "iCDEna"  },
2813 			   {"CRPIXj",  "jCRPXn"  },
2814 			   {"CRPIXja", "jCRPna"  },
2815 			   {"PCi_ja",  "ijPCna"  },
2816 			   {"CDi_ja",  "ijCDna"  },
2817 			   {"PVi_ma",  "iVn_ma"  },
2818 			   {"PSi_ma",  "iSn_ma"  },
2819 			   {"WCSAXESa","WCAXna"  },
2820 			   {"WCSNAMEa","WCSNna"  },
2821 			   {"CRDERia", "iCRDna"  },
2822 			   {"CSYERia", "iCSYna"  },
2823 			   {"CROTAi",  "iCROTn"  },
2824 
2825 			   {"LONPOLEa","LONPna"},
2826 			   {"LATPOLEa","LATPna"},
2827 			   {"EQUINOXa","EQUIna"},
2828 			   {"MJD-OBS", "MJDOBn" },
2829 			   {"MJD-AVG", "MJDAn" },
2830 			   {"RADESYSa","RADEna"},
2831 			   {"CNAMEia", "iCNAna"  },
2832 			   {"DATE-AVG","DAVGn"},
2833 
2834 			   {"NAXISi",  "-"       },  /* Remove structural keywords*/
2835 			   {"PCOUNT",  "-"       },
2836 			   {"GCOUNT",  "-"       },
2837 			   {"EXTEND",  "-"       },
2838 			   {"EXTNAME", "-"       },
2839 			   {"EXTVER",  "-"       },
2840 			   {"EXTLEVEL","-"       },
2841 			   {"CHECKSUM","-"       },
2842 			   {"DATASUM", "-"       },
2843 			   {"*",       "+"       }}; /* copy all other keywords */
2844 
2845 
2846     if (*status > 0)
2847         return(*status);
2848 
2849     if (fptr == 0 || newptr == 0) return (*status = NULL_INPUT_PTR);
2850 
2851     if (ffghdt(fptr, &hdutype, status) > 0) {
2852       ffpmsg("could not get input HDU type");
2853       return (*status);
2854     }
2855 
2856     if (hdutype != IMAGE_HDU) {
2857         ffpmsg("The input extension is not an image.");
2858         ffpmsg(" Cannot open the image.");
2859         return(*status = NOT_IMAGE);
2860     }
2861 
2862     if (ffghdt(newptr, &hdutype, status) > 0) {
2863       ffpmsg("could not get output HDU type");
2864       return (*status);
2865     }
2866 
2867     if (hdutype != BINARY_TBL) {
2868         ffpmsg("The output extension is not a table.");
2869         return(*status = NOT_BTABLE);
2870     }
2871 
2872 
2873     if (ffgiprll(fptr, 9, &bitpix, &naxis, naxes, status) > 0) {
2874       ffpmsg("Could not read image parameters.");
2875       return (*status);
2876     }
2877 
2878     /* Determine total number of pixels in the image */
2879     repeat = 1;
2880     for (ii = 0; ii < naxis; ii++) repeat *= naxes[ii];
2881 
2882     /* Determine the TFORM value for the table cell */
2883     if (bitpix == BYTE_IMG) {
2884       typecode = TBYTE;
2885       tformchar = 'B';
2886       nbytes = repeat;
2887     } else if (bitpix == SHORT_IMG) {
2888       typecode = TSHORT;
2889       tformchar = 'I';
2890       nbytes = repeat*2;
2891     } else if (bitpix == LONG_IMG) {
2892       typecode = TLONG;
2893       tformchar = 'J';
2894       nbytes = repeat*4;
2895     } else if (bitpix == FLOAT_IMG) {
2896       typecode = TFLOAT;
2897       tformchar = 'E';
2898       nbytes = repeat*4;
2899     } else if (bitpix == DOUBLE_IMG) {
2900       typecode = TDOUBLE;
2901       tformchar = 'D';
2902       nbytes = repeat*8;
2903     } else if (bitpix == LONGLONG_IMG) {
2904       typecode = TLONGLONG;
2905       tformchar = 'K';
2906       nbytes = repeat*8;
2907     } else {
2908       ffpmsg("Error: the image has an invalid datatype.");
2909       return (*status = BAD_BITPIX);
2910     }
2911 
2912     /* get column number */
2913     ffpmrk();
2914     ffgcno(newptr, CASEINSEN, colname, &colnum, status);
2915     ffcmrk();
2916 
2917     /* Column does not exist; create it */
2918     if (*status) {
2919 
2920       *status = 0;
2921       snprintf(tform, 20, "%.0f%c", (double) repeat, tformchar);
2922       ffgncl(newptr, &ncols, status);
2923       colnum = ncols+1;
2924       fficol(newptr, colnum, colname, tform, status);
2925       ffptdmll(newptr, colnum, naxis, naxes, status);
2926 
2927       if (*status) {
2928 	ffpmsg("Could not insert new column into output table.");
2929 	return *status;
2930       }
2931 
2932     } else {
2933 
2934       ffgtdmll(newptr, colnum, 9, &naxis1, naxes1, status);
2935       if (*status > 0 || naxis != naxis1) {
2936 	ffpmsg("Input image dimensions and output table cell dimensions do not match.");
2937 	return (*status = BAD_DIMEN);
2938       }
2939       for (ii=0; ii<naxis; ii++) if (naxes[ii] != naxes1[ii]) {
2940 	ffpmsg("Input image dimensions and output table cell dimensions do not match.");
2941 	return (*status = BAD_DIMEN);
2942       }
2943 
2944       ffgtclll(newptr, colnum, &typecode1, &repeat1, &width1, status);
2945       if ((*status > 0) || (typecode1 != typecode) || (repeat1 != repeat)) {
2946 	ffpmsg("Input image data type does not match output table cell type.");
2947 	return (*status = BAD_TFORM);
2948       }
2949     }
2950 
2951     /* copy keywords from input image to output table, if required */
2952 
2953     if (copykeyflag) {
2954 
2955       npat = sizeof(patterns)/sizeof(patterns[0][0])/2;
2956 
2957       if (copykeyflag == 2) {   /* copy only the WCS-related keywords */
2958 	patterns[npat-1][1] = "-";
2959       }
2960 
2961       /* The 3rd parameter value = 5 means skip the first 4 keywords in the image */
2962       fits_translate_keywords(fptr, newptr, 5, patterns, npat,
2963 			      colnum, 0, 0, status);
2964     }
2965 
2966     /* Here is all the code to compute offsets:
2967      *     * byte offset from start of row to column (dest table)
2968      *     * byte offset from start of file to image data (source image)
2969      */
2970 
2971     /* Force the writing of the row of the table by writing the last byte of
2972         the array, which grows the table, and/or shifts following extensions */
2973     ffpcl(newptr, TBYTE, colnum, rownum, repeat, 1, &dummy, status);
2974 
2975     /* byte offset within the row to the start of the image column */
2976     colptr  = (newptr->Fptr)->tableptr;   /* point to first column */
2977     colptr += (colnum - 1);     /* offset to correct column structure */
2978     firstbyte = colptr->tbcol + 1;
2979 
2980     /* get starting address of input image to be read */
2981     ffghadll(fptr, &headstart, &datastart, &dataend, status);
2982     imgstart = datastart;
2983 
2984     snprintf(card, FLEN_CARD, "HISTORY  Table column '%s' row %ld copied from image",
2985 	    colname, rownum);
2986 /*
2987   Don't automatically write History keywords; leave this up to the caller.
2988     ffprec(newptr, card, status);
2989 */
2990 
2991     /* write HISTORY keyword with the file name (this is now disabled)*/
2992 
2993     filename[0] = '\0'; hdunum = 0;
2994     strcpy(filename, "HISTORY   ");
2995     ffflnm(fptr, filename+strlen(filename), status);
2996     ffghdn(fptr, &hdunum);
2997     snprintf(filename+strlen(filename),FLEN_FILENAME+20-strlen(filename),"[%d]", hdunum-1);
2998 /*
2999     ffprec(newptr, filename, status);
3000 */
3001 
3002     /* the use of ffread routine, below, requires that any 'dirty' */
3003     /* buffers in memory be flushed back to the file first */
3004 
3005     ffflsh(fptr, FALSE, status);
3006 
3007     /* move to the first byte of the input image */
3008     ffmbyt(fptr, imgstart, TRUE, status);
3009 
3010     ntodo = minvalue(30000L, nbytes);
3011     ffgbyt(fptr, ntodo, buffer, status);  /* read input image */
3012     ffptbb(newptr, rownum, firstbyte, ntodo, buffer, status); /* write to table */
3013 
3014     nbytes    -= ntodo;
3015     firstbyte += ntodo;
3016 
3017 
3018     /* read any additional bytes with low-level ffread routine, for speed */
3019     while (nbytes && (*status <= 0) )
3020     {
3021         ntodo = minvalue(30000L, nbytes);
3022         ffread(fptr->Fptr, (long) ntodo, buffer, status);
3023         ffptbb(newptr, rownum, firstbyte, ntodo, buffer, status);
3024         nbytes    -= ntodo;
3025         firstbyte += ntodo;
3026     }
3027 
3028     /* Re-scan the header so that CFITSIO knows about all the new keywords */
3029     ffrdef(newptr,status);
3030 
3031     return(*status);
3032 }
3033 /*--------------------------------------------------------------------------*/
fits_select_image_section(fitsfile ** fptr,char * outfile,char * expr,int * status)3034 int fits_select_image_section(
3035            fitsfile **fptr,  /* IO - pointer to input image; on output it  */
3036                              /*      points to the new subimage */
3037            char *outfile,    /* I - name for output file        */
3038            char *expr,       /* I - Image section expression    */
3039            int *status)
3040 {
3041   /*
3042      copies an image section from the input file to a new output file.
3043      Any HDUs preceding or following the image are also copied to the
3044      output file.
3045   */
3046 
3047     fitsfile *newptr;
3048     int ii, hdunum;
3049 
3050     /* create new empty file to hold the image section */
3051     if (ffinit(&newptr, outfile, status) > 0)
3052     {
3053         ffpmsg(
3054          "failed to create output file for image section:");
3055         ffpmsg(outfile);
3056         return(*status);
3057     }
3058 
3059     fits_get_hdu_num(*fptr, &hdunum);  /* current HDU number in input file */
3060 
3061     /* copy all preceding extensions to the output file, if 'only_one' flag not set */
3062     if (!(((*fptr)->Fptr)->only_one)) {
3063       for (ii = 1; ii < hdunum; ii++)
3064       {
3065         fits_movabs_hdu(*fptr, ii, NULL, status);
3066         if (fits_copy_hdu(*fptr, newptr, 0, status) > 0)
3067         {
3068             ffclos(newptr, status);
3069             return(*status);
3070         }
3071       }
3072 
3073       /* move back to the original HDU position */
3074       fits_movabs_hdu(*fptr, hdunum, NULL, status);
3075     }
3076 
3077     if (fits_copy_image_section(*fptr, newptr, expr, status) > 0)
3078     {
3079         ffclos(newptr, status);
3080         return(*status);
3081     }
3082 
3083     /* copy any remaining HDUs to the output file, if 'only_one' flag not set */
3084 
3085     if (!(((*fptr)->Fptr)->only_one)) {
3086       for (ii = hdunum + 1; 1; ii++)
3087       {
3088         if (fits_movabs_hdu(*fptr, ii, NULL, status) > 0)
3089             break;
3090 
3091         fits_copy_hdu(*fptr, newptr, 0, status);
3092       }
3093 
3094       if (*status == END_OF_FILE)
3095         *status = 0;              /* got the expected EOF error; reset = 0  */
3096       else if (*status > 0)
3097       {
3098         ffclos(newptr, status);
3099         return(*status);
3100       }
3101     } else {
3102       ii = hdunum + 1;  /* this value of ii is required below */
3103     }
3104 
3105     /* close the original file and return ptr to the new image */
3106     ffclos(*fptr, status);
3107 
3108     *fptr = newptr; /* reset the pointer to the new table */
3109 
3110     /* move back to the image subsection */
3111     if (ii - 1 != hdunum)
3112         fits_movabs_hdu(*fptr, hdunum, NULL, status);
3113     else
3114     {
3115         /* may have to reset BSCALE and BZERO pixel scaling, */
3116         /* since the keywords were previously turned off */
3117 
3118         if (ffrdef(*fptr, status) > 0)
3119         {
3120             ffclos(*fptr, status);
3121             return(*status);
3122         }
3123 
3124     }
3125 
3126     return(*status);
3127 }
3128 /*--------------------------------------------------------------------------*/
fits_copy_image_section(fitsfile * fptr,fitsfile * newptr,char * expr,int * status)3129 int fits_copy_image_section(
3130            fitsfile *fptr,  /* I - pointer to input image */
3131            fitsfile *newptr,  /* I - pointer to output image */
3132            char *expr,       /* I - Image section expression    */
3133            int *status)
3134 {
3135   /*
3136      copies an image section from the input file to a new output HDU
3137   */
3138 
3139     int bitpix, naxis, numkeys, nkey;
3140     long naxes[] = {1,1,1,1,1,1,1,1,1}, smin, smax, sinc;
3141     long fpixels[] = {1,1,1,1,1,1,1,1,1};
3142     long lpixels[] = {1,1,1,1,1,1,1,1,1};
3143     long incs[] = {1,1,1,1,1,1,1,1,1};
3144     char *cptr, keyname[FLEN_KEYWORD], card[FLEN_CARD];
3145     int ii, tstatus, anynull;
3146     long minrow, maxrow, minslice, maxslice, mincube, maxcube;
3147     long firstpix;
3148     long ncubeiter, nsliceiter, nrowiter, kiter, jiter, iiter;
3149     int klen, kk, jj;
3150     long outnaxes[9], outsize, buffsize;
3151     double *buffer, crpix, cdelt;
3152 
3153     if (*status > 0)
3154         return(*status);
3155 
3156     /* get the size of the input image */
3157     fits_get_img_type(fptr, &bitpix, status);
3158     fits_get_img_dim(fptr, &naxis, status);
3159     if (fits_get_img_size(fptr, naxis, naxes, status) > 0)
3160         return(*status);
3161 
3162     if (naxis < 1 || naxis > 4)
3163     {
3164         ffpmsg(
3165         "Input image either had NAXIS = 0 (NULL image) or has > 4 dimensions");
3166         return(*status = BAD_NAXIS);
3167     }
3168 
3169     /* create output image with same size and type as the input image */
3170     /*  Will update the size later */
3171     fits_create_img(newptr, bitpix, naxis, naxes, status);
3172 
3173     /* copy all other non-structural keywords from the input to output file */
3174     fits_get_hdrspace(fptr, &numkeys, NULL, status);
3175 
3176     for (nkey = 4; nkey <= numkeys; nkey++) /* skip the first few keywords */
3177     {
3178         fits_read_record(fptr, nkey, card, status);
3179 
3180         if (fits_get_keyclass(card) > TYP_CMPRS_KEY)
3181         {
3182             /* write the record to the output file */
3183             fits_write_record(newptr, card, status);
3184         }
3185     }
3186 
3187     if (*status > 0)
3188     {
3189          ffpmsg("error copying header from input image to output image");
3190          return(*status);
3191     }
3192 
3193     /* parse the section specifier to get min, max, and inc for each axis */
3194     /* and the size of each output image axis */
3195 
3196     cptr = expr;
3197     for (ii=0; ii < naxis; ii++)
3198     {
3199        if (fits_get_section_range(&cptr, &smin, &smax, &sinc, status) > 0)
3200        {
3201           ffpmsg("error parsing the following image section specifier:");
3202           ffpmsg(expr);
3203           return(*status);
3204        }
3205 
3206        if (smax == 0)
3207           smax = naxes[ii];   /* use whole axis  by default */
3208        else if (smin == 0)
3209           smin = naxes[ii];   /* use inverted whole axis */
3210 
3211        if (smin > naxes[ii] || smax > naxes[ii])
3212        {
3213           ffpmsg("image section exceeds dimensions of input image:");
3214           ffpmsg(expr);
3215           return(*status = BAD_NAXIS);
3216        }
3217 
3218        fpixels[ii] = smin;
3219        lpixels[ii] = smax;
3220        incs[ii] = sinc;
3221 
3222        if (smin <= smax)
3223            outnaxes[ii] = (smax - smin + sinc) / sinc;
3224        else
3225            outnaxes[ii] = (smin - smax + sinc) / sinc;
3226 
3227        /* modify the NAXISn keyword */
3228        fits_make_keyn("NAXIS", ii + 1, keyname, status);
3229        fits_modify_key_lng(newptr, keyname, outnaxes[ii], NULL, status);
3230 
3231        /* modify the WCS keywords if necessary */
3232 
3233        if (fpixels[ii] != 1 || incs[ii] != 1)
3234        {
3235             for (kk=-1;kk<26; kk++)  /* modify any alternate WCS keywords */
3236 	{
3237          /* read the CRPIXn keyword if it exists in the input file */
3238          fits_make_keyn("CRPIX", ii + 1, keyname, status);
3239 
3240          if (kk != -1) {
3241 	   klen = strlen(keyname);
3242 	   keyname[klen]='A' + kk;
3243 	   keyname[klen + 1] = '\0';
3244 	 }
3245 
3246          tstatus = 0;
3247          if (fits_read_key(fptr, TDOUBLE, keyname,
3248              &crpix, NULL, &tstatus) == 0)
3249          {
3250            /* calculate the new CRPIXn value */
3251            if (fpixels[ii] <= lpixels[ii]) {
3252              crpix = (crpix - (fpixels[ii])) / incs[ii] + 1.0;
3253               /*  crpix = (crpix - (fpixels[ii] - 1.0) - .5) / incs[ii] + 0.5; */
3254            } else {
3255              crpix = (fpixels[ii] - crpix)  / incs[ii] + 1.0;
3256              /* crpix = (fpixels[ii] - (crpix - 1.0) - .5) / incs[ii] + 0.5; */
3257            }
3258 
3259            /* modify the value in the output file */
3260            fits_modify_key_dbl(newptr, keyname, crpix, 15, NULL, status);
3261 
3262            if (incs[ii] != 1 || fpixels[ii] > lpixels[ii])
3263            {
3264              /* read the CDELTn keyword if it exists in the input file */
3265              fits_make_keyn("CDELT", ii + 1, keyname, status);
3266 
3267              if (kk != -1) {
3268 	       klen = strlen(keyname);
3269 	       keyname[klen]='A' + kk;
3270 	       keyname[klen + 1] = '\0';
3271 	     }
3272 
3273              tstatus = 0;
3274              if (fits_read_key(fptr, TDOUBLE, keyname,
3275                  &cdelt, NULL, &tstatus) == 0)
3276              {
3277                /* calculate the new CDELTn value */
3278                if (fpixels[ii] <= lpixels[ii])
3279                  cdelt = cdelt * incs[ii];
3280                else
3281                  cdelt = cdelt * (-incs[ii]);
3282 
3283                /* modify the value in the output file */
3284                fits_modify_key_dbl(newptr, keyname, cdelt, 15, NULL, status);
3285              }
3286 
3287              /* modify the CDi_j keywords if they exist in the input file */
3288 
3289              fits_make_keyn("CD1_", ii + 1, keyname, status);
3290 
3291              if (kk != -1) {
3292 	       klen = strlen(keyname);
3293 	       keyname[klen]='A' + kk;
3294 	       keyname[klen + 1] = '\0';
3295 	     }
3296 
3297              for (jj=0; jj < 9; jj++)   /* look for up to 9 dimensions */
3298 	     {
3299 	       keyname[2] = '1' + jj;
3300 
3301                tstatus = 0;
3302                if (fits_read_key(fptr, TDOUBLE, keyname,
3303                  &cdelt, NULL, &tstatus) == 0)
3304                {
3305                  /* calculate the new CDi_j value */
3306                  if (fpixels[ii] <= lpixels[ii])
3307                    cdelt = cdelt * incs[ii];
3308                  else
3309                    cdelt = cdelt * (-incs[ii]);
3310 
3311                  /* modify the value in the output file */
3312                  fits_modify_key_dbl(newptr, keyname, cdelt, 15, NULL, status);
3313                }
3314 	     }
3315 
3316            } /* end of if (incs[ii]... loop */
3317          }   /* end of fits_read_key loop */
3318 	}    /* end of for (kk  loop */
3319        }
3320     }  /* end of main NAXIS loop */
3321 
3322     if (ffrdef(newptr, status) > 0)  /* force the header to be scanned */
3323     {
3324         return(*status);
3325     }
3326 
3327     /* turn off any scaling of the pixel values */
3328     fits_set_bscale(fptr,  1.0, 0.0, status);
3329     fits_set_bscale(newptr, 1.0, 0.0, status);
3330 
3331     /* to reduce memory foot print, just read/write image 1 row at a time */
3332 
3333     outsize = outnaxes[0];
3334     buffsize = (abs(bitpix) / 8) * outsize;
3335 
3336     buffer = (double *) malloc(buffsize); /* allocate memory for the image row */
3337     if (!buffer)
3338     {
3339         ffpmsg("fits_copy_image_section: no memory for image section");
3340         return(*status = MEMORY_ALLOCATION);
3341     }
3342 
3343     /* read the image section then write it to the output file */
3344 
3345     minrow = fpixels[1];
3346     maxrow = lpixels[1];
3347     if (minrow > maxrow) {
3348         nrowiter = (minrow - maxrow + incs[1]) / incs[1];
3349     } else {
3350         nrowiter = (maxrow - minrow + incs[1]) / incs[1];
3351     }
3352 
3353     minslice = fpixels[2];
3354     maxslice = lpixels[2];
3355     if (minslice > maxslice) {
3356         nsliceiter = (minslice - maxslice + incs[2]) / incs[2];
3357     } else {
3358         nsliceiter = (maxslice - minslice + incs[2]) / incs[2];
3359     }
3360 
3361     mincube = fpixels[3];
3362     maxcube = lpixels[3];
3363     if (mincube > maxcube) {
3364         ncubeiter = (mincube - maxcube + incs[3]) / incs[3];
3365     } else {
3366         ncubeiter = (maxcube - mincube + incs[3]) / incs[3];
3367     }
3368 
3369     firstpix = 1;
3370     for (kiter = 0; kiter < ncubeiter; kiter++)
3371     {
3372       if (mincube > maxcube) {
3373 	 fpixels[3] = mincube - (kiter * incs[3]);
3374       } else {
3375 	 fpixels[3] = mincube + (kiter * incs[3]);
3376       }
3377 
3378       lpixels[3] = fpixels[3];
3379 
3380       for (jiter = 0; jiter < nsliceiter; jiter++)
3381       {
3382         if (minslice > maxslice) {
3383 	    fpixels[2] = minslice - (jiter * incs[2]);
3384         } else {
3385 	    fpixels[2] = minslice + (jiter * incs[2]);
3386         }
3387 
3388 	lpixels[2] = fpixels[2];
3389 
3390         for (iiter = 0; iiter < nrowiter; iiter++)
3391         {
3392             if (minrow > maxrow) {
3393 	       fpixels[1] = minrow - (iiter * incs[1]);
3394 	    } else {
3395 	       fpixels[1] = minrow + (iiter * incs[1]);
3396             }
3397 
3398 	    lpixels[1] = fpixels[1];
3399 
3400 	    if (bitpix == 8)
3401 	    {
3402 	        ffgsvb(fptr, 1, naxis, naxes, fpixels, lpixels, incs, 0,
3403 	            (unsigned char *) buffer, &anynull, status);
3404 
3405 	        ffpprb(newptr, 1, firstpix, outsize, (unsigned char *) buffer, status);
3406 	    }
3407 	    else if (bitpix == 16)
3408 	    {
3409 	        ffgsvi(fptr, 1, naxis, naxes, fpixels, lpixels, incs, 0,
3410 	            (short *) buffer, &anynull, status);
3411 
3412 	        ffppri(newptr, 1, firstpix, outsize, (short *) buffer, status);
3413 	    }
3414 	    else if (bitpix == 32)
3415 	    {
3416 	        ffgsvk(fptr, 1, naxis, naxes, fpixels, lpixels, incs, 0,
3417 	            (int *) buffer, &anynull, status);
3418 
3419 	        ffpprk(newptr, 1, firstpix, outsize, (int *) buffer, status);
3420 	    }
3421 	    else if (bitpix == -32)
3422 	    {
3423 	        ffgsve(fptr, 1, naxis, naxes, fpixels, lpixels, incs, FLOATNULLVALUE,
3424 	            (float *) buffer, &anynull, status);
3425 
3426 	        ffppne(newptr, 1, firstpix, outsize, (float *) buffer, FLOATNULLVALUE, status);
3427 	    }
3428 	    else if (bitpix == -64)
3429 	    {
3430 	        ffgsvd(fptr, 1, naxis, naxes, fpixels, lpixels, incs, DOUBLENULLVALUE,
3431 	             buffer, &anynull, status);
3432 
3433 	        ffppnd(newptr, 1, firstpix, outsize, buffer, DOUBLENULLVALUE,
3434 	               status);
3435 	    }
3436 	    else if (bitpix == 64)
3437 	    {
3438 	        ffgsvjj(fptr, 1, naxis, naxes, fpixels, lpixels, incs, 0,
3439 	            (LONGLONG *) buffer, &anynull, status);
3440 
3441 	        ffpprjj(newptr, 1, firstpix, outsize, (LONGLONG *) buffer, status);
3442 	    }
3443 
3444             firstpix += outsize;
3445         }
3446       }
3447     }
3448 
3449     free(buffer);  /* finished with the memory */
3450 
3451     if (*status > 0)
3452     {
3453         ffpmsg("fits_copy_image_section: error copying image section");
3454         return(*status);
3455     }
3456 
3457     return(*status);
3458 }
3459 /*--------------------------------------------------------------------------*/
fits_get_section_range(char ** ptr,long * secmin,long * secmax,long * incre,int * status)3460 int fits_get_section_range(char **ptr,
3461                    long *secmin,
3462                    long *secmax,
3463                    long *incre,
3464                    int *status)
3465 /*
3466    Parse the input image section specification string, returning
3467    the  min, max and increment values.
3468    Typical string =   "1:512:2"  or "1:512"
3469 */
3470 {
3471     int slen, isanumber;
3472     char token[FLEN_VALUE], *tstbuff=0;
3473 
3474     if (*status > 0)
3475         return(*status);
3476 
3477     slen = fits_get_token2(ptr, " ,:", &tstbuff, &isanumber, status); /* get 1st token */
3478     if (slen==0)
3479     {
3480        /* support [:2,:2] type syntax, where the leading * is implied */
3481        strcpy(token,"*");
3482     }
3483     else
3484     {
3485        if (strlen(tstbuff) > FLEN_VALUE-1)
3486        {
3487           ffpmsg("Error: image section string too long (fits_get_section_range)");
3488           free(tstbuff);
3489           *status = URL_PARSE_ERROR;
3490           return(*status);
3491        }
3492        strcpy(token, tstbuff);
3493        free(tstbuff);
3494        tstbuff=0;
3495     }
3496 
3497     if (*token == '*')  /* wild card means to use the whole range */
3498     {
3499        *secmin = 1;
3500        *secmax = 0;
3501     }
3502     else if (*token == '-' && *(token+1) == '*' )  /* invert the whole range */
3503     {
3504        *secmin = 0;
3505        *secmax = 1;
3506     }
3507     else
3508     {
3509       if (slen == 0 || !isanumber || **ptr != ':')
3510         return(*status = URL_PARSE_ERROR);
3511 
3512       /* the token contains the min value */
3513       *secmin = atol(token);
3514 
3515       (*ptr)++;  /* skip the colon between the min and max values */
3516       slen = fits_get_token2(ptr, " ,:", &tstbuff, &isanumber, status); /* get token */
3517       if (slen == 0 || !isanumber)
3518       {
3519         if (tstbuff)
3520            free(tstbuff);
3521         return(*status = URL_PARSE_ERROR);
3522       }
3523       if (strlen(tstbuff) > FLEN_VALUE-1)
3524       {
3525          ffpmsg("Error: image section string too long (fits_get_section_range)");
3526          free(tstbuff);
3527          *status = URL_PARSE_ERROR;
3528          return(*status);
3529       }
3530       strcpy(token, tstbuff);
3531       free(tstbuff);
3532       tstbuff=0;
3533 
3534       /* the token contains the max value */
3535       *secmax = atol(token);
3536     }
3537 
3538     if (**ptr == ':')
3539     {
3540         (*ptr)++;  /* skip the colon between the max and incre values */
3541         slen = fits_get_token2(ptr, " ,", &tstbuff, &isanumber, status); /* get token */
3542         if (slen == 0 || !isanumber)
3543         {
3544             if (tstbuff)
3545                free(tstbuff);
3546             return(*status = URL_PARSE_ERROR);
3547         }
3548         if (strlen(tstbuff) > FLEN_VALUE-1)
3549         {
3550            ffpmsg("Error: image section string too long (fits_get_section_range)");
3551            free(tstbuff);
3552            *status = URL_PARSE_ERROR;
3553            return(*status);
3554         }
3555         strcpy(token, tstbuff);
3556         free(tstbuff);
3557         tstbuff=0;
3558 
3559 
3560         *incre = atol(token);
3561     }
3562     else
3563         *incre = 1;  /* default increment if none is supplied */
3564 
3565     if (**ptr == ',')
3566         (*ptr)++;
3567 
3568     while (**ptr == ' ')   /* skip any trailing blanks */
3569          (*ptr)++;
3570 
3571     if (*secmin < 0 || *secmax < 0 || *incre < 1)
3572         *status = URL_PARSE_ERROR;
3573 
3574     return(*status);
3575 }
3576 /*--------------------------------------------------------------------------*/
ffselect_table(fitsfile ** fptr,char * outfile,char * expr,int * status)3577 int ffselect_table(
3578            fitsfile **fptr,  /* IO - pointer to input table; on output it  */
3579                              /*      points to the new selected rows table */
3580            char *outfile,    /* I - name for output file */
3581            char *expr,       /* I - Boolean expression    */
3582            int *status)
3583 {
3584     fitsfile *newptr;
3585     int ii, hdunum;
3586 
3587     if (*outfile)
3588     {
3589       /* create new empty file in to hold the selected rows */
3590       if (ffinit(&newptr, outfile, status) > 0)
3591       {
3592         ffpmsg(
3593          "failed to create file for selected rows from input table");
3594         ffpmsg(outfile);
3595         return(*status);
3596       }
3597 
3598       fits_get_hdu_num(*fptr, &hdunum);  /* current HDU number in input file */
3599 
3600       /* copy all preceding extensions to the output file, if the 'only_one' flag is not set */
3601       if (!((*fptr)->Fptr)->only_one) {
3602         for (ii = 1; ii < hdunum; ii++)
3603         {
3604           fits_movabs_hdu(*fptr, ii, NULL, status);
3605           if (fits_copy_hdu(*fptr, newptr, 0, status) > 0)
3606           {
3607             ffclos(newptr, status);
3608             return(*status);
3609           }
3610         }
3611       } else {
3612           /* just copy the primary array */
3613           fits_movabs_hdu(*fptr, 1, NULL, status);
3614           if (fits_copy_hdu(*fptr, newptr, 0, status) > 0)
3615           {
3616             ffclos(newptr, status);
3617             return(*status);
3618           }
3619       }
3620 
3621       fits_movabs_hdu(*fptr, hdunum, NULL, status);
3622 
3623       /* copy all the header keywords from the input to output file */
3624       if (fits_copy_header(*fptr, newptr, status) > 0)
3625       {
3626         ffclos(newptr, status);
3627         return(*status);
3628       }
3629 
3630       /* set number of rows = 0 */
3631       fits_modify_key_lng(newptr, "NAXIS2", 0, NULL,status);
3632       (newptr->Fptr)->numrows = 0;
3633       (newptr->Fptr)->origrows = 0;
3634 
3635       if (ffrdef(newptr, status) > 0)  /* force the header to be scanned */
3636       {
3637         ffclos(newptr, status);
3638         return(*status);
3639       }
3640     }
3641     else
3642         newptr = *fptr;  /* will delete rows in place in the table */
3643 
3644     /* copy rows which satisfy the selection expression to the output table */
3645     /* or delete the nonqualifying rows if *fptr = newptr.   */
3646     if (fits_select_rows(*fptr, newptr, expr, status) > 0)
3647     {
3648         if (*outfile)
3649             ffclos(newptr, status);
3650 
3651         return(*status);
3652     }
3653 
3654     if (*outfile)
3655     {
3656       /* copy any remaining HDUs to the output copy */
3657 
3658       if (!((*fptr)->Fptr)->only_one) {
3659         for (ii = hdunum + 1; 1; ii++)
3660         {
3661           if (fits_movabs_hdu(*fptr, ii, NULL, status) > 0)
3662             break;
3663 
3664           fits_copy_hdu(*fptr, newptr, 0, status);
3665         }
3666 
3667         if (*status == END_OF_FILE)
3668           *status = 0;              /* got the expected EOF error; reset = 0  */
3669         else if (*status > 0)
3670         {
3671           ffclos(newptr, status);
3672           return(*status);
3673         }
3674       } else {
3675         hdunum = 2;
3676       }
3677 
3678       /* close the original file and return ptr to the new image */
3679       ffclos(*fptr, status);
3680 
3681       *fptr = newptr; /* reset the pointer to the new table */
3682 
3683       /* move back to the selected table HDU */
3684       fits_movabs_hdu(*fptr, hdunum, NULL, status);
3685     }
3686 
3687     return(*status);
3688 }
3689 /*--------------------------------------------------------------------------*/
ffparsecompspec(fitsfile * fptr,char * compspec,int * status)3690 int ffparsecompspec(fitsfile *fptr,  /* I - FITS file pointer               */
3691            char *compspec,     /* I - image compression specification */
3692            int *status)          /* IO - error status                       */
3693 /*
3694   Parse the image compression specification that was give in square brackets
3695   following the output FITS file name, as in these examples:
3696 
3697     myfile.fits[compress]  - default Rice compression, row by row
3698     myfile.fits[compress TYPE] -  the first letter of TYPE defines the
3699                                   compression algorithm:
3700                                    R = Rice
3701                                    G = GZIP
3702                                    H = HCOMPRESS
3703                                    HS = HCOMPRESS (with smoothing)
3704 				   B - BZIP2
3705                                    P = PLIO
3706 
3707     myfile.fits[compress TYPE 100,100] - the numbers give the dimensions
3708                                          of the compression tiles.  Default
3709                                          is NAXIS1, 1, 1, ...
3710 
3711        other optional parameters may be specified following a semi-colon
3712 
3713     myfile.fits[compress; q 8.0]          q specifies the floating point
3714     mufile.fits[compress TYPE; q -.0002]        quantization level;
3715     myfile.fits[compress TYPE 100,100; q 10, s 25]  s specifies the HCOMPRESS
3716                                                      integer scaling parameter
3717 
3718 The compression parameters are saved in the fptr->Fptr structure for use
3719 when writing FITS images.
3720 
3721 */
3722 {
3723     char *ptr1;
3724 
3725     /* initialize with default values */
3726     int ii, compresstype = RICE_1, smooth = 0;
3727     int quantize_method = SUBTRACTIVE_DITHER_1;
3728     long tilesize[MAX_COMPRESS_DIM] = {0,0,0,0,0,0};
3729     float qlevel = -99., scale = 0.;
3730 
3731     ptr1 = compspec;
3732     while (*ptr1 == ' ')    /* ignore leading blanks */
3733            ptr1++;
3734 
3735     if (strncmp(ptr1, "compress", 8) && strncmp(ptr1, "COMPRESS", 8) )
3736     {
3737        /* apparently this string does not specify compression parameters */
3738        return(*status = URL_PARSE_ERROR);
3739     }
3740 
3741     ptr1 += 8;
3742     while (*ptr1 == ' ')    /* ignore leading blanks */
3743            ptr1++;
3744 
3745     /* ========================= */
3746     /* look for compression type */
3747     /* ========================= */
3748 
3749     if (*ptr1 == 'r' || *ptr1 == 'R')
3750     {
3751         compresstype = RICE_1;
3752         while (*ptr1 != ' ' && *ptr1 != ';' && *ptr1 != '\0')
3753            ptr1++;
3754     }
3755     else if (*ptr1 == 'g' || *ptr1 == 'G')
3756     {
3757         compresstype = GZIP_1;
3758         while (*ptr1 != ' ' && *ptr1 != ';' && *ptr1 != '\0')
3759            ptr1++;
3760 
3761     }
3762 /*
3763     else if (*ptr1 == 'b' || *ptr1 == 'B')
3764     {
3765         compresstype = BZIP2_1;
3766         while (*ptr1 != ' ' && *ptr1 != ';' && *ptr1 != '\0')
3767            ptr1++;
3768 
3769     }
3770 */
3771     else if (*ptr1 == 'p' || *ptr1 == 'P')
3772     {
3773         compresstype = PLIO_1;
3774         while (*ptr1 != ' ' && *ptr1 != ';' && *ptr1 != '\0')
3775            ptr1++;
3776     }
3777     else if (*ptr1 == 'h' || *ptr1 == 'H')
3778     {
3779         compresstype = HCOMPRESS_1;
3780         ptr1++;
3781         if (*ptr1 == 's' || *ptr1 == 'S')
3782            smooth = 1;  /* apply smoothing when uncompressing HCOMPRESSed image */
3783 
3784         while (*ptr1 != ' ' && *ptr1 != ';' && *ptr1 != '\0')
3785            ptr1++;
3786     }
3787 
3788     /* ======================== */
3789     /* look for tile dimensions */
3790     /* ======================== */
3791 
3792     while (*ptr1 == ' ')    /* ignore leading blanks */
3793            ptr1++;
3794 
3795     ii = 0;
3796     while (isdigit( (int) *ptr1) && ii < 9)
3797     {
3798        tilesize[ii] = atol(ptr1);  /* read the integer value */
3799        ii++;
3800 
3801        while (isdigit((int) *ptr1))    /* skip over the integer */
3802            ptr1++;
3803 
3804        if (*ptr1 == ',')
3805            ptr1++;   /* skip over the comma */
3806 
3807        while (*ptr1 == ' ')    /* ignore leading blanks */
3808            ptr1++;
3809     }
3810 
3811     /* ========================================================= */
3812     /* look for semi-colon, followed by other optional parameters */
3813     /* ========================================================= */
3814 
3815     if (*ptr1 == ';') {
3816         ptr1++;
3817         while (*ptr1 == ' ')    /* ignore leading blanks */
3818            ptr1++;
3819 
3820           while (*ptr1 != 0) {  /* haven't reached end of string yet */
3821 
3822               if (*ptr1 == 's' || *ptr1 == 'S') {
3823                   /* this should be the HCOMPRESS "scale" parameter; default = 1 */
3824 
3825                   ptr1++;
3826                   while (*ptr1 == ' ')    /* ignore leading blanks */
3827                       ptr1++;
3828 
3829                   scale = (float) strtod(ptr1, &ptr1);
3830 
3831                   while (*ptr1 == ' ' || *ptr1 == ',') /* skip over blanks or comma */
3832                      ptr1++;
3833 
3834             } else if (*ptr1 == 'q' || *ptr1 == 'Q') {
3835                 /* this should be the floating point quantization parameter */
3836 
3837                   ptr1++;
3838                   if (*ptr1 == 'z' || *ptr1 == 'Z') {
3839                       /* use the subtractive_dither_2 option */
3840                       quantize_method = SUBTRACTIVE_DITHER_2;
3841                       ptr1++;
3842 		  } else if (*ptr1 == '0') {
3843                       /* do not dither */
3844                       quantize_method = NO_DITHER;
3845                       ptr1++;
3846 		  }
3847 
3848                   while (*ptr1 == ' ')    /* ignore leading blanks */
3849                       ptr1++;
3850 
3851                   qlevel = (float) strtod(ptr1, &ptr1);
3852 
3853                   while (*ptr1 == ' ' || *ptr1 == ',') /* skip over blanks or comma */
3854                      ptr1++;
3855 
3856             } else {
3857                 return(*status = URL_PARSE_ERROR);
3858             }
3859         }
3860     }
3861 
3862     /* ================================= */
3863     /* finished parsing; save the values */
3864     /* ================================= */
3865 
3866     fits_set_compression_type(fptr, compresstype, status);
3867     fits_set_tile_dim(fptr, MAX_COMPRESS_DIM, tilesize, status);
3868 
3869     if (compresstype == HCOMPRESS_1) {
3870         fits_set_hcomp_scale (fptr, scale,  status);
3871         fits_set_hcomp_smooth(fptr, smooth, status);
3872     }
3873 
3874     if (qlevel != -99.) {
3875         fits_set_quantize_level(fptr, qlevel, status);
3876         fits_set_quantize_method(fptr, quantize_method, status);
3877     }
3878 
3879     return(*status);
3880 }
3881 /*--------------------------------------------------------------------------*/
ffdkinit(fitsfile ** fptr,const char * name,int * status)3882 int ffdkinit(fitsfile **fptr,      /* O - FITS file pointer                   */
3883            const char *name,     /* I - name of file to create              */
3884            int *status)          /* IO - error status                       */
3885 /*
3886   Create and initialize a new FITS file on disk.  This routine differs
3887   from ffinit in that the input 'name' is literally taken as the name
3888   of the disk file to be created, and it does not support CFITSIO's
3889   extended filename syntax.
3890 */
3891 {
3892     *fptr = 0;              /* initialize null file pointer, */
3893                             /* regardless of the value of *status */
3894     if (*status > 0)
3895         return(*status);
3896 
3897     *status = CREATE_DISK_FILE;
3898 
3899     ffinit(fptr, name,status);
3900 
3901     return(*status);
3902 }
3903 /*--------------------------------------------------------------------------*/
ffinit(fitsfile ** fptr,const char * name,int * status)3904 int ffinit(fitsfile **fptr,      /* O - FITS file pointer                   */
3905            const char *name,     /* I - name of file to create              */
3906            int *status)          /* IO - error status                       */
3907 /*
3908   Create and initialize a new FITS file.
3909 */
3910 {
3911     int ii, driver, slen, clobber = 0;
3912     char *url;
3913     char urltype[MAX_PREFIX_LEN], outfile[FLEN_FILENAME];
3914     char tmplfile[FLEN_FILENAME], compspec[80];
3915     int handle, create_disk_file = 0;
3916 
3917     *fptr = 0;              /* initialize null file pointer, */
3918                             /* regardless of the value of *status */
3919     if (*status > 0)
3920         return(*status);
3921 
3922     if (*status == CREATE_DISK_FILE)
3923     {
3924        create_disk_file = 1;
3925        *status = 0;
3926     }
3927 
3928     if (need_to_initialize)  {          /* this is called only once */
3929         *status = fits_init_cfitsio();
3930     }
3931 
3932     if (*status > 0)
3933         return(*status);
3934 
3935     url = (char *) name;
3936     while (*url == ' ')  /* ignore leading spaces in the filename */
3937         url++;
3938 
3939     if (*url == '\0')
3940     {
3941         ffpmsg("Name of file to create is blank. (ffinit)");
3942         return(*status = FILE_NOT_CREATED);
3943     }
3944 
3945     if (create_disk_file)
3946     {
3947        if (strlen(url) > FLEN_FILENAME - 1)
3948        {
3949            ffpmsg("Filename is too long. (ffinit)");
3950            return(*status = FILE_NOT_CREATED);
3951        }
3952 
3953        strcpy(outfile, url);
3954        strcpy(urltype, "file://");
3955        tmplfile[0] = '\0';
3956        compspec[0] = '\0';
3957     }
3958     else
3959     {
3960 
3961       /* check for clobber symbol, i.e,  overwrite existing file */
3962       if (*url == '!')
3963       {
3964           clobber = TRUE;
3965           url++;
3966       }
3967       else
3968           clobber = FALSE;
3969 
3970         /* parse the output file specification */
3971 	/* this routine checks that the strings will not overflow */
3972       ffourl(url, urltype, outfile, tmplfile, compspec, status);
3973 
3974       if (*status > 0)
3975       {
3976         ffpmsg("could not parse the output filename: (ffinit)");
3977         ffpmsg(url);
3978         return(*status);
3979       }
3980     }
3981 
3982         /* find which driver corresponds to the urltype */
3983     *status = urltype2driver(urltype, &driver);
3984 
3985     if (*status)
3986     {
3987         ffpmsg("could not find driver for this file: (ffinit)");
3988         ffpmsg(url);
3989         return(*status);
3990     }
3991 
3992         /* delete pre-existing file, if asked to do so */
3993     if (clobber)
3994     {
3995         if (driverTable[driver].remove)
3996              (*driverTable[driver].remove)(outfile);
3997     }
3998 
3999         /* call appropriate driver to create the file */
4000     if (driverTable[driver].create)
4001     {
4002 
4003         FFLOCK;  /* lock this while searching for vacant handle */
4004         *status = (*driverTable[driver].create)(outfile, &handle);
4005         FFUNLOCK;
4006 
4007         if (*status)
4008         {
4009             ffpmsg("failed to create new file (already exists?):");
4010             ffpmsg(url);
4011             return(*status);
4012        }
4013     }
4014     else
4015     {
4016         ffpmsg("cannot create a new file of this type: (ffinit)");
4017         ffpmsg(url);
4018         return(*status = FILE_NOT_CREATED);
4019     }
4020 
4021         /* allocate fitsfile structure and initialize = 0 */
4022     *fptr = (fitsfile *) calloc(1, sizeof(fitsfile));
4023 
4024     if (!(*fptr))
4025     {
4026         (*driverTable[driver].close)(handle);  /* close the file */
4027         ffpmsg("failed to allocate structure for following file: (ffopen)");
4028         ffpmsg(url);
4029         return(*status = MEMORY_ALLOCATION);
4030     }
4031 
4032         /* allocate FITSfile structure and initialize = 0 */
4033     (*fptr)->Fptr = (FITSfile *) calloc(1, sizeof(FITSfile));
4034 
4035     if (!((*fptr)->Fptr))
4036     {
4037         (*driverTable[driver].close)(handle);  /* close the file */
4038         ffpmsg("failed to allocate structure for following file: (ffopen)");
4039         ffpmsg(url);
4040         free(*fptr);
4041         *fptr = 0;
4042         return(*status = MEMORY_ALLOCATION);
4043     }
4044 
4045     slen = strlen(url) + 1;
4046     slen = maxvalue(slen, 32); /* reserve at least 32 chars */
4047     ((*fptr)->Fptr)->filename = (char *) malloc(slen); /* mem for file name */
4048 
4049     if ( !(((*fptr)->Fptr)->filename) )
4050     {
4051         (*driverTable[driver].close)(handle);  /* close the file */
4052         ffpmsg("failed to allocate memory for filename: (ffinit)");
4053         ffpmsg(url);
4054         free((*fptr)->Fptr);
4055         free(*fptr);
4056         *fptr = 0;              /* return null file pointer */
4057         return(*status = FILE_NOT_CREATED);
4058     }
4059 
4060     /* mem for headstart array */
4061     ((*fptr)->Fptr)->headstart = (LONGLONG *) calloc(1001, sizeof(LONGLONG));
4062 
4063     if ( !(((*fptr)->Fptr)->headstart) )
4064     {
4065         (*driverTable[driver].close)(handle);  /* close the file */
4066         ffpmsg("failed to allocate memory for headstart array: (ffinit)");
4067         ffpmsg(url);
4068         free( ((*fptr)->Fptr)->filename);
4069         free((*fptr)->Fptr);
4070         free(*fptr);
4071         *fptr = 0;              /* return null file pointer */
4072         return(*status = MEMORY_ALLOCATION);
4073     }
4074 
4075     /* mem for file I/O buffers */
4076     ((*fptr)->Fptr)->iobuffer = (char *) calloc(NIOBUF, IOBUFLEN);
4077 
4078     if ( !(((*fptr)->Fptr)->iobuffer) )
4079     {
4080         (*driverTable[driver].close)(handle);  /* close the file */
4081         ffpmsg("failed to allocate memory for iobuffer array: (ffinit)");
4082         ffpmsg(url);
4083         free( ((*fptr)->Fptr)->headstart);    /* free memory for headstart array */
4084         free( ((*fptr)->Fptr)->filename);
4085         free((*fptr)->Fptr);
4086         free(*fptr);
4087         *fptr = 0;              /* return null file pointer */
4088         return(*status = MEMORY_ALLOCATION);
4089     }
4090 
4091     /* initialize the ageindex array (relative age of the I/O buffers) */
4092     /* and initialize the bufrecnum array as being empty */
4093     for (ii = 0; ii < NIOBUF; ii++)  {
4094         ((*fptr)->Fptr)->ageindex[ii] = ii;
4095         ((*fptr)->Fptr)->bufrecnum[ii] = -1;
4096     }
4097 
4098         /* store the parameters describing the file */
4099     ((*fptr)->Fptr)->MAXHDU = 1000;              /* initial size of headstart */
4100     ((*fptr)->Fptr)->filehandle = handle;        /* store the file pointer */
4101     ((*fptr)->Fptr)->driver = driver;            /*  driver number         */
4102     strcpy(((*fptr)->Fptr)->filename, url);      /* full input filename    */
4103     ((*fptr)->Fptr)->filesize = 0;               /* physical file size     */
4104     ((*fptr)->Fptr)->logfilesize = 0;            /* logical file size      */
4105     ((*fptr)->Fptr)->writemode = 1;              /* read-write mode        */
4106     ((*fptr)->Fptr)->datastart = DATA_UNDEFINED; /* unknown start of data  */
4107     ((*fptr)->Fptr)->curbuf = -1;         /* undefined current IO buffer   */
4108     ((*fptr)->Fptr)->open_count = 1;      /* structure is currently used once */
4109     ((*fptr)->Fptr)->validcode = VALIDSTRUC; /* flag denoting valid structure */
4110 
4111     ffldrc(*fptr, 0, IGNORE_EOF, status);     /* initialize first record */
4112 
4113     fits_store_Fptr( (*fptr)->Fptr, status);  /* store Fptr address */
4114 
4115     /* if template file was given, use it to define structure of new file */
4116 
4117     if (tmplfile[0])
4118         ffoptplt(*fptr, tmplfile, status);
4119 
4120     /* parse and save image compression specification, if given */
4121     if (compspec[0])
4122         ffparsecompspec(*fptr, compspec, status);
4123 
4124     return(*status);                       /* successful return */
4125 }
4126 /*--------------------------------------------------------------------------*/
4127 /* ffimem == fits_create_memfile */
4128 
ffimem(fitsfile ** fptr,void ** buffptr,size_t * buffsize,size_t deltasize,void * (* mem_realloc)(void * p,size_t newsize),int * status)4129 int ffimem(fitsfile **fptr,      /* O - FITS file pointer                   */
4130            void **buffptr,       /* I - address of memory pointer           */
4131            size_t *buffsize,     /* I - size of buffer, in bytes            */
4132            size_t deltasize,     /* I - increment for future realloc's      */
4133            void *(*mem_realloc)(void *p, size_t newsize), /* function       */
4134            int *status)          /* IO - error status                       */
4135 
4136 /*
4137   Create and initialize a new FITS file in memory
4138 */
4139 {
4140     int ii, driver, slen;
4141     char urltype[MAX_PREFIX_LEN];
4142     int handle;
4143 
4144     if (*status > 0)
4145         return(*status);
4146 
4147     *fptr = 0;              /* initialize null file pointer */
4148 
4149     if (need_to_initialize)    {        /* this is called only once */
4150        *status = fits_init_cfitsio();
4151     }
4152 
4153     if (*status > 0)
4154         return(*status);
4155 
4156     strcpy(urltype, "memkeep://"); /* URL type for pre-existing memory file */
4157 
4158     *status = urltype2driver(urltype, &driver);
4159 
4160     if (*status > 0)
4161     {
4162         ffpmsg("could not find driver for pre-existing memory file: (ffimem)");
4163         return(*status);
4164     }
4165 
4166     /* call driver routine to "open" the memory file */
4167     FFLOCK;  /* lock this while searching for vacant handle */
4168     *status =   mem_openmem( buffptr, buffsize, deltasize,
4169                             mem_realloc,  &handle);
4170     FFUNLOCK;
4171 
4172     if (*status > 0)
4173     {
4174          ffpmsg("failed to open pre-existing memory file: (ffimem)");
4175          return(*status);
4176     }
4177 
4178         /* allocate fitsfile structure and initialize = 0 */
4179     *fptr = (fitsfile *) calloc(1, sizeof(fitsfile));
4180 
4181     if (!(*fptr))
4182     {
4183         (*driverTable[driver].close)(handle);  /* close the file */
4184         ffpmsg("failed to allocate structure for memory file: (ffimem)");
4185         return(*status = MEMORY_ALLOCATION);
4186     }
4187 
4188         /* allocate FITSfile structure and initialize = 0 */
4189     (*fptr)->Fptr = (FITSfile *) calloc(1, sizeof(FITSfile));
4190 
4191     if (!((*fptr)->Fptr))
4192     {
4193         (*driverTable[driver].close)(handle);  /* close the file */
4194         ffpmsg("failed to allocate structure for memory file: (ffimem)");
4195         free(*fptr);
4196         *fptr = 0;
4197         return(*status = MEMORY_ALLOCATION);
4198     }
4199 
4200     slen = 32; /* reserve at least 32 chars */
4201     ((*fptr)->Fptr)->filename = (char *) malloc(slen); /* mem for file name */
4202 
4203     if ( !(((*fptr)->Fptr)->filename) )
4204     {
4205         (*driverTable[driver].close)(handle);  /* close the file */
4206         ffpmsg("failed to allocate memory for filename: (ffimem)");
4207         free((*fptr)->Fptr);
4208         free(*fptr);
4209         *fptr = 0;              /* return null file pointer */
4210         return(*status = MEMORY_ALLOCATION);
4211     }
4212 
4213     /* mem for headstart array */
4214     ((*fptr)->Fptr)->headstart = (LONGLONG *) calloc(1001, sizeof(LONGLONG));
4215 
4216     if ( !(((*fptr)->Fptr)->headstart) )
4217     {
4218         (*driverTable[driver].close)(handle);  /* close the file */
4219         ffpmsg("failed to allocate memory for headstart array: (ffimem)");
4220         free( ((*fptr)->Fptr)->filename);
4221         free((*fptr)->Fptr);
4222         free(*fptr);
4223         *fptr = 0;              /* return null file pointer */
4224         return(*status = MEMORY_ALLOCATION);
4225     }
4226 
4227     /* mem for file I/O buffers */
4228     ((*fptr)->Fptr)->iobuffer = (char *) calloc(NIOBUF, IOBUFLEN);
4229 
4230     if ( !(((*fptr)->Fptr)->iobuffer) )
4231     {
4232         (*driverTable[driver].close)(handle);  /* close the file */
4233         ffpmsg("failed to allocate memory for iobuffer array: (ffimem)");
4234         free( ((*fptr)->Fptr)->headstart);    /* free memory for headstart array */
4235         free( ((*fptr)->Fptr)->filename);
4236         free((*fptr)->Fptr);
4237         free(*fptr);
4238         *fptr = 0;              /* return null file pointer */
4239         return(*status = MEMORY_ALLOCATION);
4240     }
4241 
4242     /* initialize the ageindex array (relative age of the I/O buffers) */
4243     /* and initialize the bufrecnum array as being empty */
4244     for (ii = 0; ii < NIOBUF; ii++)  {
4245         ((*fptr)->Fptr)->ageindex[ii] = ii;
4246         ((*fptr)->Fptr)->bufrecnum[ii] = -1;
4247     }
4248 
4249         /* store the parameters describing the file */
4250     ((*fptr)->Fptr)->MAXHDU = 1000;              /* initial size of headstart */
4251     ((*fptr)->Fptr)->filehandle = handle;        /* file handle */
4252     ((*fptr)->Fptr)->driver = driver;            /* driver number */
4253     strcpy(((*fptr)->Fptr)->filename, "memfile"); /* dummy filename */
4254     ((*fptr)->Fptr)->filesize = *buffsize;        /* physical file size */
4255     ((*fptr)->Fptr)->logfilesize = *buffsize;     /* logical file size */
4256     ((*fptr)->Fptr)->writemode = 1;               /* read-write mode    */
4257     ((*fptr)->Fptr)->datastart = DATA_UNDEFINED;  /* unknown start of data */
4258     ((*fptr)->Fptr)->curbuf = -1;             /* undefined current IO buffer */
4259     ((*fptr)->Fptr)->open_count = 1;     /* structure is currently used once */
4260     ((*fptr)->Fptr)->validcode = VALIDSTRUC; /* flag denoting valid structure */
4261 
4262     ffldrc(*fptr, 0, IGNORE_EOF, status);     /* initialize first record */
4263     fits_store_Fptr( (*fptr)->Fptr, status);  /* store Fptr address */
4264     return(*status);
4265 }
4266 /*--------------------------------------------------------------------------*/
fits_init_cfitsio(void)4267 int fits_init_cfitsio(void)
4268 /*
4269   initialize anything that is required before using the CFITSIO routines
4270 */
4271 {
4272     int status;
4273 
4274     union u_tag {
4275       short ival;
4276       char cval[2];
4277     } u;
4278 
4279     fitsio_init_lock();
4280 
4281     FFLOCK;   /* lockout other threads while executing this critical */
4282               /* section of code  */
4283 
4284     if (need_to_initialize == 0) { /* already initialized? */
4285       FFUNLOCK;
4286       return(0);
4287     }
4288 
4289     /*   test for correct byteswapping.   */
4290 
4291     u.ival = 1;
4292     if  ((BYTESWAPPED && u.cval[0] != 1) ||
4293          (BYTESWAPPED == FALSE && u.cval[1] != 1) )
4294     {
4295       printf ("\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
4296       printf(" Byteswapping is not being done correctly on this system.\n");
4297       printf(" Check the MACHINE and BYTESWAPPED definitions in fitsio2.h\n");
4298       printf(" Please report this problem to the CFITSIO developers.\n");
4299       printf(  "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
4300       FFUNLOCK;
4301       return(1);
4302     }
4303 
4304 
4305     /*  test that LONGLONG is an 8 byte integer */
4306 
4307     if (sizeof(LONGLONG) != 8)
4308     {
4309       printf ("\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
4310       printf(" CFITSIO did not find an 8-byte long integer data type.\n");
4311       printf("   sizeof(LONGLONG) = %d\n",(int)sizeof(LONGLONG));
4312       printf(" Please report this problem to the CFITSIO developers.\n");
4313       printf(  "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
4314       FFUNLOCK;
4315       return(1);
4316     }
4317 
4318     /* register the standard I/O drivers that are always available */
4319 
4320     /* 1--------------------disk file driver-----------------------*/
4321     status = fits_register_driver("file://",
4322             file_init,
4323             file_shutdown,
4324             file_setoptions,
4325             file_getoptions,
4326             file_getversion,
4327 	    file_checkfile,
4328             file_open,
4329             file_create,
4330 #ifdef HAVE_FTRUNCATE
4331             file_truncate,
4332 #else
4333             NULL,   /* no file truncate function */
4334 #endif
4335             file_close,
4336             file_remove,
4337             file_size,
4338             file_flush,
4339             file_seek,
4340             file_read,
4341             file_write);
4342 
4343     if (status)
4344     {
4345         ffpmsg("failed to register the file:// driver (init_cfitsio)");
4346         FFUNLOCK;
4347         return(status);
4348     }
4349 
4350     /* 2------------ output temporary memory file driver ----------------*/
4351     status = fits_register_driver("mem://",
4352             mem_init,
4353             mem_shutdown,
4354             mem_setoptions,
4355             mem_getoptions,
4356             mem_getversion,
4357             NULL,            /* checkfile not needed */
4358             NULL,            /* open function not allowed */
4359             mem_create,
4360             mem_truncate,
4361             mem_close_free,
4362             NULL,            /* remove function not required */
4363             mem_size,
4364             NULL,            /* flush function not required */
4365             mem_seek,
4366             mem_read,
4367             mem_write);
4368 
4369 
4370     if (status)
4371     {
4372         ffpmsg("failed to register the mem:// driver (init_cfitsio)");
4373         FFUNLOCK;
4374         return(status);
4375     }
4376 
4377     /* 3--------------input pre-existing memory file driver----------------*/
4378     status = fits_register_driver("memkeep://",
4379             NULL,
4380             mem_shutdown,
4381             mem_setoptions,
4382             mem_getoptions,
4383             mem_getversion,
4384             NULL,            /* checkfile not needed */
4385             NULL,            /* file open driver function is not used */
4386             NULL,            /* create function not allowed */
4387             mem_truncate,
4388             mem_close_keep,
4389             NULL,            /* remove function not required */
4390             mem_size,
4391             NULL,            /* flush function not required */
4392             mem_seek,
4393             mem_read,
4394             mem_write);
4395 
4396 
4397     if (status)
4398     {
4399         ffpmsg("failed to register the memkeep:// driver (init_cfitsio)");
4400         FFUNLOCK;
4401         return(status);
4402     }
4403 
4404    /* 4-------------------stdin stream driver----------------------*/
4405    /*  the stdin stream is copied to memory then opened in memory */
4406 
4407     status = fits_register_driver("stdin://",
4408             NULL,
4409             mem_shutdown,
4410             mem_setoptions,
4411             mem_getoptions,
4412             mem_getversion,
4413             stdin_checkfile,
4414             stdin_open,
4415             NULL,            /* create function not allowed */
4416             mem_truncate,
4417             mem_close_free,
4418             NULL,            /* remove function not required */
4419             mem_size,
4420             NULL,            /* flush function not required */
4421             mem_seek,
4422             mem_read,
4423             mem_write);
4424 
4425     if (status)
4426     {
4427         ffpmsg("failed to register the stdin:// driver (init_cfitsio)");
4428         FFUNLOCK;
4429         return(status);
4430     }
4431 
4432    /* 5-------------------stdin file stream driver----------------------*/
4433    /*  the stdin stream is copied to a disk file then the disk file is opened */
4434 
4435     status = fits_register_driver("stdinfile://",
4436             NULL,
4437             mem_shutdown,
4438             mem_setoptions,
4439             mem_getoptions,
4440             mem_getversion,
4441             NULL,            /* checkfile not needed */
4442             stdin_open,
4443             NULL,            /* create function not allowed */
4444 #ifdef HAVE_FTRUNCATE
4445             file_truncate,
4446 #else
4447             NULL,   /* no file truncate function */
4448 #endif
4449             file_close,
4450             file_remove,
4451             file_size,
4452             file_flush,
4453             file_seek,
4454             file_read,
4455             file_write);
4456 
4457     if (status)
4458     {
4459         ffpmsg("failed to register the stdinfile:// driver (init_cfitsio)");
4460         FFUNLOCK;
4461         return(status);
4462     }
4463 
4464 
4465     /* 6-----------------------stdout stream driver------------------*/
4466     status = fits_register_driver("stdout://",
4467             NULL,
4468             mem_shutdown,
4469             mem_setoptions,
4470             mem_getoptions,
4471             mem_getversion,
4472             NULL,            /* checkfile not needed */
4473             NULL,            /* open function not required */
4474             mem_create,
4475             mem_truncate,
4476             stdout_close,
4477             NULL,            /* remove function not required */
4478             mem_size,
4479             NULL,            /* flush function not required */
4480             mem_seek,
4481             mem_read,
4482             mem_write);
4483 
4484     if (status)
4485     {
4486         ffpmsg("failed to register the stdout:// driver (init_cfitsio)");
4487         FFUNLOCK;
4488         return(status);
4489     }
4490 
4491     /* 7------------------iraf disk file to memory driver -----------*/
4492     status = fits_register_driver("irafmem://",
4493             NULL,
4494             mem_shutdown,
4495             mem_setoptions,
4496             mem_getoptions,
4497             mem_getversion,
4498             NULL,            /* checkfile not needed */
4499             mem_iraf_open,
4500             NULL,            /* create function not required */
4501             mem_truncate,
4502             mem_close_free,
4503             NULL,            /* remove function not required */
4504             mem_size,
4505             NULL,            /* flush function not required */
4506             mem_seek,
4507             mem_read,
4508             mem_write);
4509 
4510     if (status)
4511     {
4512         ffpmsg("failed to register the irafmem:// driver (init_cfitsio)");
4513         FFUNLOCK;
4514         return(status);
4515     }
4516 
4517     /* 8------------------raw binary file to memory driver -----------*/
4518     status = fits_register_driver("rawfile://",
4519             NULL,
4520             mem_shutdown,
4521             mem_setoptions,
4522             mem_getoptions,
4523             mem_getversion,
4524             NULL,            /* checkfile not needed */
4525             mem_rawfile_open,
4526             NULL,            /* create function not required */
4527             mem_truncate,
4528             mem_close_free,
4529             NULL,            /* remove function not required */
4530             mem_size,
4531             NULL,            /* flush function not required */
4532             mem_seek,
4533             mem_read,
4534             mem_write);
4535 
4536     if (status)
4537     {
4538         ffpmsg("failed to register the rawfile:// driver (init_cfitsio)");
4539         FFUNLOCK;
4540         return(status);
4541     }
4542 
4543     /* 9------------------compressed disk file to memory driver -----------*/
4544     status = fits_register_driver("compress://",
4545             NULL,
4546             mem_shutdown,
4547             mem_setoptions,
4548             mem_getoptions,
4549             mem_getversion,
4550             NULL,            /* checkfile not needed */
4551             mem_compress_open,
4552             NULL,            /* create function not required */
4553             mem_truncate,
4554             mem_close_free,
4555             NULL,            /* remove function not required */
4556             mem_size,
4557             NULL,            /* flush function not required */
4558             mem_seek,
4559             mem_read,
4560             mem_write);
4561 
4562     if (status)
4563     {
4564         ffpmsg("failed to register the compress:// driver (init_cfitsio)");
4565         FFUNLOCK;
4566         return(status);
4567     }
4568 
4569     /* 10------------------compressed disk file to memory driver -----------*/
4570     /*  Identical to compress://, except it allows READWRITE access      */
4571 
4572     status = fits_register_driver("compressmem://",
4573             NULL,
4574             mem_shutdown,
4575             mem_setoptions,
4576             mem_getoptions,
4577             mem_getversion,
4578             NULL,            /* checkfile not needed */
4579             mem_compress_openrw,
4580             NULL,            /* create function not required */
4581             mem_truncate,
4582             mem_close_free,
4583             NULL,            /* remove function not required */
4584             mem_size,
4585             NULL,            /* flush function not required */
4586             mem_seek,
4587             mem_read,
4588             mem_write);
4589 
4590     if (status)
4591     {
4592         ffpmsg("failed to register the compressmem:// driver (init_cfitsio)");
4593         FFUNLOCK;
4594         return(status);
4595     }
4596 
4597     /* 11------------------compressed disk file to disk file driver -------*/
4598     status = fits_register_driver("compressfile://",
4599             NULL,
4600             file_shutdown,
4601             file_setoptions,
4602             file_getoptions,
4603             file_getversion,
4604             NULL,            /* checkfile not needed */
4605             file_compress_open,
4606             file_create,
4607 #ifdef HAVE_FTRUNCATE
4608             file_truncate,
4609 #else
4610             NULL,   /* no file truncate function */
4611 #endif
4612             file_close,
4613             file_remove,
4614             file_size,
4615             file_flush,
4616             file_seek,
4617             file_read,
4618             file_write);
4619 
4620     if (status)
4621     {
4622         ffpmsg("failed to register the compressfile:// driver (init_cfitsio)");
4623         FFUNLOCK;
4624         return(status);
4625     }
4626 
4627     /* 12---create file in memory, then compress it to disk file on close--*/
4628     status = fits_register_driver("compressoutfile://",
4629             NULL,
4630             mem_shutdown,
4631             mem_setoptions,
4632             mem_getoptions,
4633             mem_getversion,
4634             NULL,            /* checkfile not needed */
4635             NULL,            /* open function not allowed */
4636             mem_create_comp,
4637             mem_truncate,
4638             mem_close_comp,
4639             file_remove,     /* delete existing compressed disk file */
4640             mem_size,
4641             NULL,            /* flush function not required */
4642             mem_seek,
4643             mem_read,
4644             mem_write);
4645 
4646 
4647     if (status)
4648     {
4649         ffpmsg(
4650         "failed to register the compressoutfile:// driver (init_cfitsio)");
4651         FFUNLOCK;
4652         return(status);
4653     }
4654 
4655     /* Register Optional drivers */
4656 
4657 #ifdef HAVE_NET_SERVICES
4658 
4659     /* 13--------------------root driver-----------------------*/
4660 
4661     status = fits_register_driver("root://",
4662 				  root_init,
4663 				  root_shutdown,
4664 				  root_setoptions,
4665 				  root_getoptions,
4666 				  root_getversion,
4667 				  NULL,            /* checkfile not needed */
4668 				  root_open,
4669 				  root_create,
4670 				  NULL,  /* No truncate possible */
4671 				  root_close,
4672 				  NULL,  /* No remove possible */
4673 				  root_size,  /* no size possible */
4674 				  root_flush,
4675 				  root_seek, /* Though will always succeed */
4676 				  root_read,
4677 				  root_write);
4678 
4679     if (status)
4680     {
4681         ffpmsg("failed to register the root:// driver (init_cfitsio)");
4682         FFUNLOCK;
4683         return(status);
4684     }
4685 
4686     /* 14--------------------http  driver-----------------------*/
4687     status = fits_register_driver("http://",
4688             NULL,
4689             mem_shutdown,
4690             mem_setoptions,
4691             mem_getoptions,
4692             mem_getversion,
4693             http_checkfile,
4694             http_open,
4695             NULL,            /* create function not required */
4696             mem_truncate,
4697             mem_close_free,
4698             NULL,            /* remove function not required */
4699             mem_size,
4700             NULL,            /* flush function not required */
4701             mem_seek,
4702             mem_read,
4703             mem_write);
4704 
4705     if (status)
4706     {
4707         ffpmsg("failed to register the http:// driver (init_cfitsio)");
4708         FFUNLOCK;
4709         return(status);
4710     }
4711 
4712     /* 15--------------------http file driver-----------------------*/
4713 
4714     status = fits_register_driver("httpfile://",
4715             NULL,
4716             file_shutdown,
4717             file_setoptions,
4718             file_getoptions,
4719             file_getversion,
4720             NULL,            /* checkfile not needed */
4721             http_file_open,
4722             file_create,
4723 #ifdef HAVE_FTRUNCATE
4724             file_truncate,
4725 #else
4726             NULL,   /* no file truncate function */
4727 #endif
4728             file_close,
4729             file_remove,
4730             file_size,
4731             file_flush,
4732             file_seek,
4733             file_read,
4734             file_write);
4735 
4736     if (status)
4737     {
4738         ffpmsg("failed to register the httpfile:// driver (init_cfitsio)");
4739         FFUNLOCK;
4740         return(status);
4741     }
4742 
4743     /* 16--------------------http memory driver-----------------------*/
4744     /*  same as http:// driver, except memory file can be opened READWRITE */
4745     status = fits_register_driver("httpmem://",
4746             NULL,
4747             mem_shutdown,
4748             mem_setoptions,
4749             mem_getoptions,
4750             mem_getversion,
4751             http_checkfile,
4752             http_file_open,  /* this will simply call http_open */
4753             NULL,            /* create function not required */
4754             mem_truncate,
4755             mem_close_free,
4756             NULL,            /* remove function not required */
4757             mem_size,
4758             NULL,            /* flush function not required */
4759             mem_seek,
4760             mem_read,
4761             mem_write);
4762 
4763     if (status)
4764     {
4765         ffpmsg("failed to register the httpmem:// driver (init_cfitsio)");
4766         FFUNLOCK;
4767         return(status);
4768     }
4769 
4770     /* 17--------------------httpcompress file driver-----------------------*/
4771 
4772     status = fits_register_driver("httpcompress://",
4773             NULL,
4774             mem_shutdown,
4775             mem_setoptions,
4776             mem_getoptions,
4777             mem_getversion,
4778             NULL,            /* checkfile not needed */
4779             http_compress_open,
4780             NULL,            /* create function not required */
4781             mem_truncate,
4782             mem_close_free,
4783             NULL,            /* remove function not required */
4784             mem_size,
4785             NULL,            /* flush function not required */
4786             mem_seek,
4787             mem_read,
4788             mem_write);
4789 
4790     if (status)
4791     {
4792         ffpmsg("failed to register the httpcompress:// driver (init_cfitsio)");
4793         FFUNLOCK;
4794         return(status);
4795     }
4796 
4797 
4798     /* 18--------------------ftp driver-----------------------*/
4799     status = fits_register_driver("ftp://",
4800             NULL,
4801             mem_shutdown,
4802             mem_setoptions,
4803             mem_getoptions,
4804             mem_getversion,
4805             ftp_checkfile,
4806             ftp_open,
4807             NULL,            /* create function not required */
4808             mem_truncate,
4809             mem_close_free,
4810             NULL,            /* remove function not required */
4811             mem_size,
4812             NULL,            /* flush function not required */
4813             mem_seek,
4814             mem_read,
4815             mem_write);
4816 
4817     if (status)
4818     {
4819         ffpmsg("failed to register the ftp:// driver (init_cfitsio)");
4820         FFUNLOCK;
4821         return(status);
4822     }
4823 
4824     /* 19--------------------ftp file driver-----------------------*/
4825     status = fits_register_driver("ftpfile://",
4826             NULL,
4827             file_shutdown,
4828             file_setoptions,
4829             file_getoptions,
4830             file_getversion,
4831             NULL,            /* checkfile not needed */
4832             ftp_file_open,
4833             file_create,
4834 #ifdef HAVE_FTRUNCATE
4835             file_truncate,
4836 #else
4837             NULL,   /* no file truncate function */
4838 #endif
4839             file_close,
4840             file_remove,
4841             file_size,
4842             file_flush,
4843             file_seek,
4844             file_read,
4845             file_write);
4846 
4847     if (status)
4848     {
4849         ffpmsg("failed to register the ftpfile:// driver (init_cfitsio)");
4850         FFUNLOCK;
4851         return(status);
4852     }
4853 
4854     /* 20--------------------ftp mem driver-----------------------*/
4855     /*  same as ftp:// driver, except memory file can be opened READWRITE */
4856     status = fits_register_driver("ftpmem://",
4857             NULL,
4858             mem_shutdown,
4859             mem_setoptions,
4860             mem_getoptions,
4861             mem_getversion,
4862             ftp_checkfile,
4863             ftp_file_open,   /* this will simply call ftp_open */
4864             NULL,            /* create function not required */
4865             mem_truncate,
4866             mem_close_free,
4867             NULL,            /* remove function not required */
4868             mem_size,
4869             NULL,            /* flush function not required */
4870             mem_seek,
4871             mem_read,
4872             mem_write);
4873 
4874     if (status)
4875     {
4876         ffpmsg("failed to register the ftpmem:// driver (init_cfitsio)");
4877         FFUNLOCK;
4878         return(status);
4879     }
4880 
4881     /* 21--------------------ftp compressed file driver------------------*/
4882     status = fits_register_driver("ftpcompress://",
4883             NULL,
4884             mem_shutdown,
4885             mem_setoptions,
4886             mem_getoptions,
4887             mem_getversion,
4888             NULL,            /* checkfile not needed */
4889             ftp_compress_open,
4890             0,            /* create function not required */
4891             mem_truncate,
4892             mem_close_free,
4893             0,            /* remove function not required */
4894             mem_size,
4895             0,            /* flush function not required */
4896             mem_seek,
4897             mem_read,
4898             mem_write);
4899 
4900     if (status)
4901     {
4902         ffpmsg("failed to register the ftpcompress:// driver (init_cfitsio)");
4903         FFUNLOCK;
4904         return(status);
4905     }
4906       /* === End of net drivers section === */
4907 #endif
4908 
4909 /* ==================== SHARED MEMORY DRIVER SECTION ======================= */
4910 
4911 #ifdef HAVE_SHMEM_SERVICES
4912 
4913     /* 22--------------------shared memory driver-----------------------*/
4914     status = fits_register_driver("shmem://",
4915             smem_init,
4916             smem_shutdown,
4917             smem_setoptions,
4918             smem_getoptions,
4919             smem_getversion,
4920             NULL,            /* checkfile not needed */
4921             smem_open,
4922             smem_create,
4923             NULL,            /* truncate file not supported yet */
4924             smem_close,
4925             smem_remove,
4926             smem_size,
4927             smem_flush,
4928             smem_seek,
4929             smem_read,
4930             smem_write );
4931 
4932     if (status)
4933     {
4934         ffpmsg("failed to register the shmem:// driver (init_cfitsio)");
4935         FFUNLOCK;
4936         return(status);
4937     }
4938 
4939 #endif
4940 /* ==================== END OF SHARED MEMORY DRIVER SECTION ================ */
4941 
4942 
4943 #ifdef HAVE_GSIFTP
4944     /* 23--------------------gsiftp driver-----------------------*/
4945     status = fits_register_driver("gsiftp://",
4946             gsiftp_init,
4947             gsiftp_shutdown,
4948             gsiftp_setoptions,
4949             gsiftp_getoptions,
4950             gsiftp_getversion,
4951             gsiftp_checkfile,
4952             gsiftp_open,
4953             gsiftp_create,
4954 #ifdef HAVE_FTRUNCATE
4955             gsiftp_truncate,
4956 #else
4957             NULL,
4958 #endif
4959             gsiftp_close,
4960             NULL,            /* remove function not yet implemented */
4961             gsiftp_size,
4962             gsiftp_flush,
4963             gsiftp_seek,
4964             gsiftp_read,
4965             gsiftp_write);
4966 
4967     if (status)
4968     {
4969         ffpmsg("failed to register the gsiftp:// driver (init_cfitsio)");
4970         FFUNLOCK;
4971         return(status);
4972     }
4973 
4974 #endif
4975 
4976     /* 24---------------stdin and stdout stream driver-------------------*/
4977     status = fits_register_driver("stream://",
4978             NULL,
4979             NULL,
4980             NULL,
4981             NULL,
4982             NULL,
4983 	    NULL,
4984             stream_open,
4985             stream_create,
4986             NULL,   /* no stream truncate function */
4987             stream_close,
4988             NULL,   /* no stream remove */
4989             stream_size,
4990             stream_flush,
4991             stream_seek,
4992             stream_read,
4993             stream_write);
4994 
4995     if (status)
4996     {
4997         ffpmsg("failed to register the stream:// driver (init_cfitsio)");
4998         FFUNLOCK;
4999         return(status);
5000     }
5001 
5002 #ifdef HAVE_NET_SERVICES
5003 
5004     /* 25--------------------https  driver-----------------------*/
5005     status = fits_register_driver("https://",
5006             NULL,
5007             mem_shutdown,
5008             mem_setoptions,
5009             mem_getoptions,
5010             mem_getversion,
5011             https_checkfile,
5012             https_open,
5013             NULL,            /* create function not required */
5014             mem_truncate,
5015             mem_close_free,
5016             NULL,            /* remove function not required */
5017             mem_size,
5018             NULL,            /* flush function not required */
5019             mem_seek,
5020             mem_read,
5021             mem_write);
5022 
5023     if (status)
5024     {
5025         ffpmsg("failed to register the https:// driver (init_cfitsio)");
5026         FFUNLOCK;
5027         return(status);
5028     }
5029 
5030     /* 26--------------------https file driver-----------------------*/
5031 
5032     status = fits_register_driver("httpsfile://",
5033             NULL,
5034             file_shutdown,
5035             file_setoptions,
5036             file_getoptions,
5037             file_getversion,
5038             NULL,            /* checkfile not needed */
5039             https_file_open,
5040             file_create,
5041 #ifdef HAVE_FTRUNCATE
5042             file_truncate,
5043 #else
5044             NULL,   /* no file truncate function */
5045 #endif
5046             file_close,
5047             file_remove,
5048             file_size,
5049             file_flush,
5050             file_seek,
5051             file_read,
5052             file_write);
5053 
5054     if (status)
5055     {
5056         ffpmsg("failed to register the httpsfile:// driver (init_cfitsio)");
5057         FFUNLOCK;
5058         return(status);
5059     }
5060 
5061     /* 27--------------------https memory driver-----------------------*/
5062     /*  same as https:// driver, except memory file can be opened READWRITE */
5063     status = fits_register_driver("httpsmem://",
5064             NULL,
5065             mem_shutdown,
5066             mem_setoptions,
5067             mem_getoptions,
5068             mem_getversion,
5069             https_checkfile,
5070             https_file_open,  /* this will simply call https_open */
5071             NULL,            /* create function not required */
5072             mem_truncate,
5073             mem_close_free,
5074             NULL,            /* remove function not required */
5075             mem_size,
5076             NULL,            /* flush function not required */
5077             mem_seek,
5078             mem_read,
5079             mem_write);
5080 
5081     if (status)
5082     {
5083         ffpmsg("failed to register the httpsmem:// driver (init_cfitsio)");
5084         FFUNLOCK;
5085         return(status);
5086     }
5087       /* === End of https net drivers section === */
5088 
5089     /* 28--------------------ftps  driver-----------------------*/
5090     status = fits_register_driver("ftps://",
5091             NULL,
5092             mem_shutdown,
5093             mem_setoptions,
5094             mem_getoptions,
5095             mem_getversion,
5096             ftps_checkfile,
5097             ftps_open,
5098             NULL,
5099             mem_truncate,
5100             mem_close_free,
5101             NULL,
5102             mem_size,
5103             NULL,
5104             mem_seek,
5105             mem_read,
5106             mem_write);
5107 
5108     if (status)
5109     {
5110         ffpmsg("failed to register the ftps:// driver (init_cfitsio)");
5111         FFUNLOCK;
5112         return(status);
5113     }
5114 
5115     /* 29--------------------ftps file driver-----------------------*/
5116 
5117     status = fits_register_driver("ftpsfile://",
5118             NULL,
5119             file_shutdown,
5120             file_setoptions,
5121             file_getoptions,
5122             file_getversion,
5123             NULL,
5124             ftps_file_open,
5125             file_create,
5126 #ifdef HAVE_FTRUNCATE
5127             file_truncate,
5128 #else
5129             NULL,
5130 #endif
5131             file_close,
5132             file_remove,
5133             file_size,
5134             file_flush,
5135             file_seek,
5136             file_read,
5137             file_write);
5138 
5139     if (status)
5140     {
5141         ffpmsg("failed to register the ftpsfile:// driver (init_cfitsio)");
5142         FFUNLOCK;
5143         return(status);
5144     }
5145 
5146     /* 30--------------------ftps memory driver-----------------------*/
5147     /*  same as ftps:// driver, except memory file can be opened READWRITE */
5148     status = fits_register_driver("ftpsmem://",
5149             NULL,
5150             mem_shutdown,
5151             mem_setoptions,
5152             mem_getoptions,
5153             mem_getversion,
5154             ftps_checkfile,
5155             ftps_file_open,
5156             NULL,
5157             mem_truncate,
5158             mem_close_free,
5159             NULL,
5160             mem_size,
5161             NULL,
5162             mem_seek,
5163             mem_read,
5164             mem_write);
5165 
5166     if (status)
5167     {
5168         ffpmsg("failed to register the ftpsmem:// driver (init_cfitsio)");
5169         FFUNLOCK;
5170         return(status);
5171     }
5172 
5173     /* 31--------------------ftps compressed file driver------------------*/
5174     status = fits_register_driver("ftpscompress://",
5175             NULL,
5176             mem_shutdown,
5177             mem_setoptions,
5178             mem_getoptions,
5179             mem_getversion,
5180             NULL,            /* checkfile not needed */
5181             ftps_compress_open,
5182             0,            /* create function not required */
5183             mem_truncate,
5184             mem_close_free,
5185             0,            /* remove function not required */
5186             mem_size,
5187             0,            /* flush function not required */
5188             mem_seek,
5189             mem_read,
5190             mem_write);
5191 
5192     if (status)
5193     {
5194         ffpmsg("failed to register the ftpscompress:// driver (init_cfitsio)");
5195         FFUNLOCK;
5196         return(status);
5197     }
5198 #endif
5199 
5200 
5201     /* reset flag.  Any other threads will now not need to call this routine */
5202     need_to_initialize = 0;
5203 
5204     FFUNLOCK;
5205     return(status);
5206 }
5207 /*--------------------------------------------------------------------------*/
fits_register_driver(char * prefix,int (* init)(void),int (* shutdown)(void),int (* setoptions)(int option),int (* getoptions)(int * options),int (* getversion)(int * version),int (* checkfile)(char * urltype,char * infile,char * outfile),int (* open)(char * filename,int rwmode,int * driverhandle),int (* create)(char * filename,int * driverhandle),int (* truncate)(int driverhandle,LONGLONG filesize),int (* close)(int driverhandle),int (* fremove)(char * filename),int (* size)(int driverhandle,LONGLONG * sizex),int (* flush)(int driverhandle),int (* seek)(int driverhandle,LONGLONG offset),int (* read)(int driverhandle,void * buffer,long nbytes),int (* write)(int driverhandle,void * buffer,long nbytes))5208 int fits_register_driver(char *prefix,
5209 	int (*init)(void),
5210 	int (*shutdown)(void),
5211 	int (*setoptions)(int option),
5212 	int (*getoptions)(int *options),
5213 	int (*getversion)(int *version),
5214 	int (*checkfile) (char *urltype, char *infile, char *outfile),
5215 	int (*open)(char *filename, int rwmode, int *driverhandle),
5216 	int (*create)(char *filename, int *driverhandle),
5217 	int (*truncate)(int driverhandle, LONGLONG filesize),
5218 	int (*close)(int driverhandle),
5219 	int (*fremove)(char *filename),
5220         int (*size)(int driverhandle, LONGLONG *sizex),
5221 	int (*flush)(int driverhandle),
5222 	int (*seek)(int driverhandle, LONGLONG offset),
5223 	int (*read) (int driverhandle, void *buffer, long nbytes),
5224 	int (*write)(int driverhandle, void *buffer, long nbytes) )
5225 /*
5226   register all the functions needed to support an I/O driver
5227 */
5228 {
5229     int status;
5230 
5231     if (no_of_drivers < 0 ) {
5232 	  /* This is bad. looks like memory has been corrupted. */
5233 	  ffpmsg("Vital CFITSIO parameters held in memory have been corrupted!!");
5234 	  ffpmsg("Fatal condition detected in fits_register_driver.");
5235 	  return(TOO_MANY_DRIVERS);
5236     }
5237 
5238     if (no_of_drivers + 1 > MAX_DRIVERS)
5239         return(TOO_MANY_DRIVERS);
5240 
5241     if (prefix  == NULL)
5242         return(BAD_URL_PREFIX);
5243 
5244 
5245     if (init != NULL)
5246     {
5247         status = (*init)();  /* initialize the driver */
5248         if (status)
5249             return(status);
5250     }
5251 
5252     	/*  fill in data in table */
5253     strncpy(driverTable[no_of_drivers].prefix, prefix, MAX_PREFIX_LEN);
5254     driverTable[no_of_drivers].prefix[MAX_PREFIX_LEN - 1] = 0;
5255     driverTable[no_of_drivers].init = init;
5256     driverTable[no_of_drivers].shutdown = shutdown;
5257     driverTable[no_of_drivers].setoptions = setoptions;
5258     driverTable[no_of_drivers].getoptions = getoptions;
5259     driverTable[no_of_drivers].getversion = getversion;
5260     driverTable[no_of_drivers].checkfile = checkfile;
5261     driverTable[no_of_drivers].open = open;
5262     driverTable[no_of_drivers].create = create;
5263     driverTable[no_of_drivers].truncate = truncate;
5264     driverTable[no_of_drivers].close = close;
5265     driverTable[no_of_drivers].remove = fremove;
5266     driverTable[no_of_drivers].size = size;
5267     driverTable[no_of_drivers].flush = flush;
5268     driverTable[no_of_drivers].seek = seek;
5269     driverTable[no_of_drivers].read = read;
5270     driverTable[no_of_drivers].write = write;
5271 
5272     no_of_drivers++;      /* increment the number of drivers */
5273     return(0);
5274  }
5275 /*--------------------------------------------------------------------------*/
5276 /* fits_parse_input_url */
ffiurl(char * url,char * urltype,char * infilex,char * outfile,char * extspec,char * rowfilterx,char * binspec,char * colspec,int * status)5277 int ffiurl(char *url,               /* input filename */
5278            char *urltype,    /* e.g., 'file://', 'http://', 'mem://' */
5279            char *infilex,    /* root filename (may be complete path) */
5280            char *outfile,    /* optional output file name            */
5281            char *extspec,    /* extension spec: +n or [extname, extver]  */
5282            char *rowfilterx, /* boolean row filter expression */
5283            char *binspec,    /* histogram binning specifier   */
5284            char *colspec,    /* column or keyword modifier expression */
5285            int *status)
5286 /*
5287    parse the input URL into its basic components.
5288    This routine does not support the pixfilter or compspec components.
5289 */
5290 {
5291 	return ffifile2(url, urltype, infilex, outfile,
5292                extspec, rowfilterx, binspec, colspec, 0, 0, status);
5293 }
5294 /*--------------------------------------------------------------------------*/
5295 /* fits_parse_input_file */
ffifile(char * url,char * urltype,char * infilex,char * outfile,char * extspec,char * rowfilterx,char * binspec,char * colspec,char * pixfilter,int * status)5296 int ffifile(char *url,       /* input filename */
5297            char *urltype,    /* e.g., 'file://', 'http://', 'mem://' */
5298            char *infilex,    /* root filename (may be complete path) */
5299            char *outfile,    /* optional output file name            */
5300            char *extspec,    /* extension spec: +n or [extname, extver]  */
5301            char *rowfilterx, /* boolean row filter expression */
5302            char *binspec,    /* histogram binning specifier   */
5303            char *colspec,    /* column or keyword modifier expression */
5304            char *pixfilter,  /* pixel filter expression */
5305            int *status)
5306 /*
5307    fits_parse_input_filename
5308    parse the input URL into its basic components.
5309    This routine does not support the compspec component.
5310 */
5311 {
5312 	return ffifile2(url, urltype, infilex, outfile,
5313                extspec, rowfilterx, binspec, colspec, pixfilter, 0, status);
5314 
5315 }
5316 /*--------------------------------------------------------------------------*/
ffifile2(char * url,char * urltype,char * infilex,char * outfile,char * extspec,char * rowfilterx,char * binspec,char * colspec,char * pixfilter,char * compspec,int * status)5317 int ffifile2(char *url,       /* input filename */
5318            char *urltype,    /* e.g., 'file://', 'http://', 'mem://' */
5319            char *infilex,    /* root filename (may be complete path) */
5320            char *outfile,    /* optional output file name            */
5321            char *extspec,    /* extension spec: +n or [extname, extver]  */
5322            char *rowfilterx, /* boolean row filter expression */
5323            char *binspec,    /* histogram binning specifier   */
5324            char *colspec,    /* column or keyword modifier expression */
5325            char *pixfilter,  /* pixel filter expression */
5326            char *compspec,   /* image compression specification */
5327            int *status)
5328 /*
5329    fits_parse_input_filename
5330    parse the input URL into its basic components.
5331    This routine is big and ugly and should be redesigned someday!
5332 */
5333 {
5334     int ii, jj, slen, infilelen, plus_ext = 0, collen;
5335     char *ptr1, *ptr2, *ptr3, *ptr4, *tmptr;
5336     int hasAt, hasDot, hasOper, followingOper, spaceTerm, rowFilter;
5337     int colStart, binStart, pixStart, compStart;
5338 
5339 
5340     /* must have temporary variable for these, in case inputs are NULL */
5341     char *infile;
5342     char *rowfilter;
5343     char *tmpstr;
5344 
5345     if (*status > 0)
5346         return(*status);
5347 
5348     /* Initialize null strings */
5349     if (infilex) *infilex  = '\0';
5350     if (urltype) *urltype = '\0';
5351     if (outfile) *outfile = '\0';
5352     if (extspec) *extspec = '\0';
5353     if (binspec) *binspec = '\0';
5354     if (colspec) *colspec = '\0';
5355     if (rowfilterx) *rowfilterx = '\0';
5356     if (pixfilter) *pixfilter = '\0';
5357     if (compspec) *compspec = '\0';
5358     slen = strlen(url);
5359 
5360     if (slen == 0)       /* blank filename ?? */
5361         return(*status);
5362 
5363     /* allocate memory for 3 strings, each as long as the input url */
5364     infile = (char *) calloc(3,  slen + 1);
5365     if (!infile)
5366        return(*status = MEMORY_ALLOCATION);
5367 
5368     rowfilter = &infile[slen + 1];
5369     tmpstr = &rowfilter[slen + 1];
5370 
5371     ptr1 = url;
5372 
5373     /* -------------------------------------------------------- */
5374     /*  get urltype (e.g., file://, ftp://, http://, etc.)  */
5375     /* --------------------------------------------------------- */
5376 
5377     if (*ptr1 == '-' && ( *(ptr1 +1) ==  0   || *(ptr1 +1) == ' '  ||
5378                           *(ptr1 +1) == '['  || *(ptr1 +1) == '(' ) )
5379     {
5380         /* "-" means read file from stdin. Also support "- ",        */
5381         /* "-[extname]" and '-(outfile.fits)" but exclude disk file  */
5382         /* names that begin with a minus sign, e.g., "-55d33m.fits"  */
5383 
5384         if (urltype)
5385             strcat(urltype, "stdin://");
5386         ptr1++;
5387     }
5388     else if (!fits_strncasecmp(ptr1, "stdin", 5))
5389     {
5390         if (urltype)
5391             strcat(urltype, "stdin://");
5392         ptr1 = ptr1 + 5;
5393     }
5394     else
5395     {
5396         ptr2 = strstr(ptr1, "://");
5397         ptr3 = strstr(ptr1, "(" );
5398 
5399         if (ptr3 && (ptr3 < ptr2) )
5400         {
5401            /* the urltype follows a '(' character, so it must apply */
5402            /* to the output file, and is not the urltype of the input file */
5403            ptr2 = 0;   /* so reset pointer to zero */
5404         }
5405 
5406         if (ptr2)            /* copy the explicit urltype string */
5407         {
5408             if (ptr2-ptr1+3 >= MAX_PREFIX_LEN)
5409             {
5410                ffpmsg("Name of urltype is too long.");
5411                return(*status = URL_PARSE_ERROR);
5412             }
5413             if (urltype)
5414                  strncat(urltype, ptr1, ptr2 - ptr1 + 3);
5415             ptr1 = ptr2 + 3;
5416         }
5417         else if (!strncmp(ptr1, "ftp:", 4) )
5418         {                              /* the 2 //'s are optional */
5419             if (urltype)
5420                 strcat(urltype, "ftp://");
5421             ptr1 += 4;
5422         }
5423         else if (!strncmp(ptr1, "gsiftp:", 7) )
5424         {                              /* the 2 //'s are optional */
5425             if (urltype)
5426                 strcat(urltype, "gsiftp://");
5427             ptr1 += 7;
5428         }
5429         else if (!strncmp(ptr1, "http:", 5) )
5430         {                              /* the 2 //'s are optional */
5431             if (urltype)
5432                 strcat(urltype, "http://");
5433             ptr1 += 5;
5434         }
5435         else if (!strncmp(ptr1, "mem:", 4) )
5436         {                              /* the 2 //'s are optional */
5437             if (urltype)
5438                 strcat(urltype, "mem://");
5439             ptr1 += 4;
5440         }
5441         else if (!strncmp(ptr1, "shmem:", 6) )
5442         {                              /* the 2 //'s are optional */
5443             if (urltype)
5444                 strcat(urltype, "shmem://");
5445             ptr1 += 6;
5446         }
5447         else if (!strncmp(ptr1, "file:", 5) )
5448         {                              /* the 2 //'s are optional */
5449             if (urltype)
5450                 strcat(urltype, "file://");
5451             ptr1 += 5;
5452         }
5453         else                       /* assume file driver    */
5454         {
5455             if (urltype)
5456                 strcat(urltype, "file://");
5457         }
5458     }
5459 
5460     /* ----------------------------------------------------------
5461        If this is a http:// type file, then the cgi file name could
5462        include the '[' character, which should not be interpreted
5463        as part of CFITSIO's Extended File Name Syntax.  Test for this
5464        case by seeing if the last character is a ']' or ')'.  If it
5465        is not, then just treat the whole input string as the file name
5466        and do not attempt to interprete the name using the extended
5467        filename syntax.
5468      ----------------------------------------------------------- */
5469 
5470     if (urltype && !strncmp(urltype, "http://", 7) )
5471     {
5472         /* test for opening parenthesis or bracket in the file name */
5473         if( strchr(ptr1, '(' ) || strchr(ptr1, '[' ) )
5474         {
5475             slen = strlen(ptr1);
5476             ptr3 = ptr1 + slen - 1;
5477             while (*ptr3 == ' ')    /* ignore trailing blanks */
5478                 ptr3--;
5479 
5480             if (*ptr3 != ']' && *ptr3 != ')' )
5481             {
5482                 /* name doesn't end with a ']' or ')' so don't try */
5483                 /* to parse this unusual string (may be cgi string)  */
5484                 if (infilex) {
5485 
5486                     if (strlen(ptr1) > FLEN_FILENAME - 1) {
5487                         ffpmsg("Name of file is too long.");
5488                         return(*status = URL_PARSE_ERROR);
5489                     }
5490 
5491                     strcpy(infilex, ptr1);
5492                 }
5493 
5494                 free(infile);
5495                 return(*status);
5496             }
5497         }
5498     }
5499 
5500     /* ----------------------------------------------------------
5501        Look for VMS style filenames like:
5502             disk:[directory.subdirectory]filename.ext, or
5503                  [directory.subdirectory]filename.ext
5504 
5505        Check if the first character is a '[' and urltype != stdin
5506        or if there is a ':[' string in the remaining url string. If
5507        so, then need to move past this bracket character before
5508        search for the opening bracket of a filter specification.
5509      ----------------------------------------------------------- */
5510 
5511     tmptr = ptr1;
5512     if (*ptr1 == '[')
5513     {
5514       if (*url != '-')
5515         tmptr = ptr1 + 1; /* this bracket encloses a VMS directory name */
5516     }
5517     else
5518     {
5519        tmptr = strstr(ptr1, ":[");
5520        if (tmptr) /* these 2 chars are part of the VMS disk and directory */
5521           tmptr += 2;
5522        else
5523           tmptr = ptr1;
5524     }
5525 
5526     /* ------------------------ */
5527     /*  get the input file name */
5528     /* ------------------------ */
5529 
5530     ptr2 = strchr(tmptr, '(');   /* search for opening parenthesis ( */
5531     ptr3 = strchr(tmptr, '[');   /* search for opening bracket [ */
5532 
5533     if (ptr2 == ptr3)  /* simple case: no [ or ( in the file name */
5534     {
5535         strcat(infile, ptr1);
5536     }
5537     else if (!ptr3 ||         /* no bracket, so () enclose output file name */
5538          (ptr2 && (ptr2 < ptr3)) ) /* () enclose output name before bracket */
5539     {
5540         strncat(infile, ptr1, ptr2 - ptr1);
5541         ptr2++;
5542 
5543         ptr1 = strchr(ptr2, ')' );   /* search for closing ) */
5544         if (!ptr1)
5545         {
5546             free(infile);
5547             return(*status = URL_PARSE_ERROR);  /* error, no closing ) */
5548         }
5549 
5550         if (outfile) {
5551 
5552 	    if (ptr1 - ptr2 > FLEN_FILENAME - 1)
5553 	    {
5554                  free(infile);
5555                  return(*status = URL_PARSE_ERROR);
5556             }
5557 
5558             strncat(outfile, ptr2, ptr1 - ptr2);
5559         }
5560 
5561         /* the opening [ could have been part of output name,    */
5562         /*      e.g., file(out[compress])[3][#row > 5]           */
5563         /* so search again for opening bracket following the closing ) */
5564         ptr3 = strchr(ptr1, '[');
5565 
5566     }
5567     else    /*   bracket comes first, so there is no output name */
5568     {
5569         strncat(infile, ptr1, ptr3 - ptr1);
5570     }
5571 
5572    /* strip off any trailing blanks in the names */
5573 
5574     slen = strlen(infile);
5575     while ( (--slen) > 0  && infile[slen] == ' ')
5576          infile[slen] = '\0';
5577 
5578     if (outfile)
5579     {
5580         slen = strlen(outfile);
5581         while ( (--slen) > 0  && outfile[slen] == ' ')
5582             outfile[slen] = '\0';
5583     }
5584 
5585     /* --------------------------------------------- */
5586     /* check if this is an IRAF file (.imh extension */
5587     /* --------------------------------------------- */
5588 
5589     ptr4 = strstr(infile, ".imh");
5590 
5591     /* did the infile name end with ".imh" ? */
5592     if (ptr4 && (*(ptr4 + 4) == '\0'))
5593     {
5594         if (urltype)
5595             strcpy(urltype, "irafmem://");
5596     }
5597 
5598     /* --------------------------------------------- */
5599     /* check if the 'filename+n' convention has been */
5600     /* used to specifiy which HDU number to open     */
5601     /* --------------------------------------------- */
5602 
5603     jj = strlen(infile);
5604 
5605     for (ii = jj - 1; ii >= 0; ii--)
5606     {
5607         if (infile[ii] == '+')    /* search backwards for '+' sign */
5608             break;
5609     }
5610 
5611     if (ii > 0 && (jj - ii) < 7)  /* limit extension numbers to 5 digits */
5612     {
5613         infilelen = ii;
5614         ii++;
5615         ptr1 = infile+ii;   /* pointer to start of sequence */
5616 
5617         for (; ii < jj; ii++)
5618         {
5619             if (!isdigit((int) infile[ii] ) ) /* are all the chars digits? */
5620                 break;
5621         }
5622 
5623         if (ii == jj)
5624         {
5625              /* yes, the '+n' convention was used.  Copy */
5626              /* the digits to the output extspec string. */
5627              plus_ext = 1;
5628 
5629              if (extspec) {
5630 	         if (jj - infilelen > FLEN_FILENAME - 1)
5631 	         {
5632                      free(infile);
5633                      return(*status = URL_PARSE_ERROR);
5634                  }
5635 
5636                  strncpy(extspec, ptr1, jj - infilelen);
5637              }
5638 
5639              infile[infilelen] = '\0'; /* delete the extension number */
5640         }
5641     }
5642 
5643     /* -------------------------------------------------------------------- */
5644     /* if '*' was given for the output name expand it to the root file name */
5645     /* -------------------------------------------------------------------- */
5646 
5647     if (outfile && outfile[0] == '*')
5648     {
5649         /* scan input name backwards to the first '/' character */
5650         for (ii = jj - 1; ii >= 0; ii--)
5651         {
5652             if (infile[ii] == '/' || ii == 0)
5653             {
5654 	      if (strlen(&infile[ii + 1]) > FLEN_FILENAME - 1)
5655 	      {
5656                  free(infile);
5657                  return(*status = URL_PARSE_ERROR);
5658               }
5659 
5660                 strcpy(outfile, &infile[ii + 1]);
5661                 break;
5662             }
5663         }
5664     }
5665 
5666     /* ------------------------------------------ */
5667     /* copy strings from local copy to the output */
5668     /* ------------------------------------------ */
5669     if (infilex) {
5670 	if (strlen(infile) > FLEN_FILENAME - 1)
5671 	{
5672                  free(infile);
5673                  return(*status = URL_PARSE_ERROR);
5674         }
5675 
5676         strcpy(infilex, infile);
5677     }
5678     /* ---------------------------------------------------------- */
5679     /* if no '[' character in the input string, then we are done. */
5680     /* ---------------------------------------------------------- */
5681     if (!ptr3)
5682     {
5683         free(infile);
5684         return(*status);
5685     }
5686 
5687     /* ------------------------------------------- */
5688     /* see if [ extension specification ] is given */
5689     /* ------------------------------------------- */
5690 
5691     if (!plus_ext) /* extension no. not already specified?  Then      */
5692                    /* first brackets must enclose extension name or # */
5693                    /* or it encloses a image subsection specification */
5694                    /* or a raw binary image specifier */
5695                    /* or a image compression specifier */
5696 
5697                    /* Or, the extension specification may have been */
5698                    /* omitted and we have to guess what the user intended */
5699     {
5700        ptr1 = ptr3 + 1;    /* pointer to first char after the [ */
5701 
5702        ptr2 = strchr(ptr1, ']' );   /* search for closing ] */
5703        if (!ptr2)
5704        {
5705             ffpmsg("input file URL is missing closing bracket ']'");
5706             free(infile);
5707             return(*status = URL_PARSE_ERROR);  /* error, no closing ] */
5708        }
5709 
5710        /* ---------------------------------------------- */
5711        /* First, test if this is a rawfile specifier     */
5712        /* which looks something like: '[ib512,512:2880]' */
5713        /* Test if first character is b,i,j,d,r,f, or u,  */
5714        /* and optional second character is b or l,       */
5715        /* followed by one or more digits,                */
5716        /* finally followed by a ',', ':', or ']'         */
5717        /* ---------------------------------------------- */
5718 
5719        if (*ptr1 == 'b' || *ptr1 == 'B' || *ptr1 == 'i' || *ptr1 == 'I' ||
5720            *ptr1 == 'j' || *ptr1 == 'J' || *ptr1 == 'd' || *ptr1 == 'D' ||
5721            *ptr1 == 'r' || *ptr1 == 'R' || *ptr1 == 'f' || *ptr1 == 'F' ||
5722            *ptr1 == 'u' || *ptr1 == 'U')
5723        {
5724            /* next optional character may be a b or l (for Big or Little) */
5725            ptr1++;
5726            if (*ptr1 == 'b' || *ptr1 == 'B' || *ptr1 == 'l' || *ptr1 == 'L')
5727               ptr1++;
5728 
5729            if (isdigit((int) *ptr1))  /* must have at least 1 digit */
5730            {
5731              while (isdigit((int) *ptr1))
5732               ptr1++;             /* skip over digits */
5733 
5734              if (*ptr1 == ',' || *ptr1 == ':' || *ptr1 == ']' )
5735              {
5736                /* OK, this looks like a rawfile specifier */
5737 
5738                if (urltype)
5739                {
5740                  if (strstr(urltype, "stdin") )
5741                    strcpy(urltype, "rawstdin://");
5742                  else
5743                    strcpy(urltype, "rawfile://");
5744                }
5745 
5746                /* append the raw array specifier to infilex */
5747                if (infilex)
5748                {
5749 
5750 	         if (strlen(infilex) + strlen(ptr3) > FLEN_FILENAME - 1)
5751 	         {
5752                     free(infile);
5753                     return(*status = URL_PARSE_ERROR);
5754                  }
5755 
5756                  strcat(infilex, ptr3);
5757                  ptr1 = strchr(infilex, ']'); /* find the closing ] char */
5758                  if (ptr1)
5759                    *(ptr1 + 1) = '\0';  /* terminate string after the ] */
5760                }
5761 
5762                if (extspec)
5763                   strcpy(extspec, "0"); /* the 0 ext number is implicit */
5764 
5765                tmptr = strchr(ptr2 + 1, '[' ); /* search for another [ char */
5766 
5767                /* copy any remaining characters into rowfilterx  */
5768                if (tmptr && rowfilterx)
5769                {
5770 
5771 
5772 	         if (strlen(rowfilterx) + strlen(tmptr + 1) > FLEN_FILENAME -1)
5773 	         {
5774                     free(infile);
5775                     return(*status = URL_PARSE_ERROR);
5776                  }
5777 
5778                  strcat(rowfilterx, tmptr + 1);
5779 
5780                  tmptr = strchr(rowfilterx, ']' );   /* search for closing ] */
5781                  if (tmptr)
5782                    *tmptr = '\0'; /* overwrite the ] with null terminator */
5783                }
5784 
5785                free(infile);        /* finished parsing, so return */
5786                return(*status);
5787              }
5788            }
5789        }        /* end of rawfile specifier test */
5790 
5791        /* -------------------------------------------------------- */
5792        /* Not a rawfile, so next, test if this is an image section */
5793        /* i.e., an integer followed by a ':' or a '*' or '-*'      */
5794        /* -------------------------------------------------------- */
5795 
5796        ptr1 = ptr3 + 1;    /* reset pointer to first char after the [ */
5797        tmptr = ptr1;
5798 
5799        while (*tmptr == ' ')
5800           tmptr++;   /* skip leading blanks */
5801 
5802        while (isdigit((int) *tmptr))
5803           tmptr++;             /* skip over leading digits */
5804 
5805        if (*tmptr == ':' || *tmptr == '*' || *tmptr == '-')
5806        {
5807            /* this is an image section specifier */
5808            strcat(rowfilter, ptr3);
5809 /*
5810   don't want to assume 0 extension any more; may imply an image extension.
5811            if (extspec)
5812               strcpy(extspec, "0");
5813 */
5814        }
5815        else
5816        {
5817        /* -----------------------------------------------------------------
5818          Not an image section or rawfile spec so may be an extension spec.
5819 
5820          Examples of valid extension specifiers:
5821             [3]                - 3rd extension; 0 = primary array
5822             [events]           - events extension
5823             [events, 2]        - events extension, with EXTVER = 2
5824             [events,2]         - spaces are optional
5825             [events, 3, b]     - same as above, plus XTENSION = 'BINTABLE'
5826             [PICS; colName(12)] - an image in row 12 of the colName column
5827                                       in the PICS table extension
5828             [PICS; colName(exposure > 1000)] - as above, but find image in
5829                           first row with with exposure column value > 1000.
5830             [Rate Table] - extension name can contain spaces!
5831             [Rate Table;colName(exposure>1000)]
5832 
5833          Examples of other types of specifiers (Not extension specifiers)
5834 
5835             [bin]  !!! this is ambiguous, and can't be distinguished from
5836                        a valid extension specifier
5837             [bini X=1:512:16]  (also binb, binj, binr, and bind are allowed)
5838             [binr (X,Y) = 5]
5839             [bin @binfilter.txt]
5840 
5841             [col Time;rate]
5842             [col PI=PHA * 1.1]
5843             [col -Time; status]
5844 
5845             [X > 5]
5846             [X>5]
5847             [@filter.txt]
5848             [StatusCol]  !!! this is ambiguous, and can't be distinguished
5849                        from a valid extension specifier
5850             [StatusCol==0]
5851             [StatusCol || x>6]
5852             [gtifilter()]
5853             [regfilter("region.reg")]
5854 
5855             [compress Rice]
5856 
5857          There will always be some ambiguity between an extension name and
5858          a boolean row filtering expression, (as in a couple of the above
5859          examples).  If there is any doubt, the expression should be treated
5860          as an extension specification;  The user can always add an explicit
5861          expression specifier to override this interpretation.
5862 
5863          The following decision logic will be used:
5864 
5865          1) locate the first token, terminated with a space, comma,
5866             semi-colon, or closing bracket.
5867 
5868          2) the token is not part of an extension specifier if any of
5869             the following is true:
5870 
5871             - if the token begins with '@' and contains a '.'
5872             - if the token contains an operator: = > < || &&
5873             - if the token begins with "gtifilter(" or "regfilter("
5874             - if the token is terminated by a space and is followed by
5875                additional characters (not a ']')  AND any of the following:
5876                  - the token is 'col'
5877                  - the token is 3 or 4 chars long and begins with 'bin'
5878                  - the second token begins with an operator:
5879                      ! = < > | & + - * / %
5880 
5881 
5882          3) otherwise, the string is assumed to be an extension specifier
5883 
5884          ----------------------------------------------------------------- */
5885 
5886            tmptr = ptr1;
5887            while(*tmptr == ' ')
5888                tmptr++;
5889 
5890            hasAt = 0;
5891            hasDot = 0;
5892            hasOper = 0;
5893            followingOper = 0;
5894            spaceTerm = 0;
5895            rowFilter = 0;
5896            colStart = 0;
5897            binStart = 0;
5898            pixStart = 0;
5899            compStart = 0;
5900 
5901            if (*tmptr == '@')  /* test for leading @ symbol */
5902                hasAt = 1;
5903 
5904            if ( !fits_strncasecmp(tmptr, "col ", 4) )
5905               colStart = 1;
5906 
5907            if ( !fits_strncasecmp(tmptr, "bin", 3) )
5908               binStart = 1;
5909 
5910            if ( !fits_strncasecmp(tmptr, "pix", 3) )
5911               pixStart = 1;
5912 
5913            if ( !fits_strncasecmp(tmptr, "compress ", 9) ||
5914                 !fits_strncasecmp(tmptr, "compress]", 9) )
5915               compStart = 1;
5916 
5917            if ( !fits_strncasecmp(tmptr, "gtifilter(", 10) ||
5918                 !fits_strncasecmp(tmptr, "regfilter(", 10) )
5919            {
5920                rowFilter = 1;
5921            }
5922            else
5923            {
5924              /* parse the first token of the expression */
5925              for (ii = 0; ii < ptr2 - ptr1 + 1; ii++, tmptr++)
5926              {
5927                if (*tmptr == '.')
5928                    hasDot = 1;
5929                else if (*tmptr == '=' || *tmptr == '>' || *tmptr == '<' ||
5930                    (*tmptr == '|' && *(tmptr+1) == '|') ||
5931                    (*tmptr == '&' && *(tmptr+1) == '&') )
5932                    hasOper = 1;
5933 
5934                else if (*tmptr == ',' || *tmptr == ';' || *tmptr == ']')
5935                {
5936                   break;
5937                }
5938                else if (*tmptr == ' ')   /* a space char? */
5939                {
5940                   while(*tmptr == ' ')  /* skip spaces */
5941                     tmptr++;
5942 
5943                   if (*tmptr == ']') /* is this the end? */
5944                      break;
5945 
5946                   spaceTerm = 1; /* 1st token is terminated by space */
5947 
5948                   /* test if this is a column or binning specifier */
5949                   if (colStart || (ii <= 4 && (binStart || pixStart)) )
5950                      rowFilter = 1;
5951                   else
5952                   {
5953 
5954                     /* check if next character is an operator */
5955                     if (*tmptr == '=' || *tmptr == '>' || *tmptr == '<' ||
5956                       *tmptr == '|' || *tmptr == '&' || *tmptr == '!' ||
5957                       *tmptr == '+' || *tmptr == '-' || *tmptr == '*' ||
5958                       *tmptr == '/' || *tmptr == '%')
5959                        followingOper = 1;
5960                   }
5961                   break;
5962                }
5963              }
5964            }
5965 
5966            /* test if this is NOT an extension specifier */
5967            if ( rowFilter || (pixStart && spaceTerm) ||
5968                 (hasAt && hasDot) ||
5969                 hasOper ||
5970                 compStart ||
5971                 (spaceTerm && followingOper) )
5972            {
5973                /* this is (probably) not an extension specifier */
5974                /* so copy all chars to filter spec string */
5975                strcat(rowfilter, ptr3);
5976            }
5977            else
5978            {
5979                /* this appears to be a legit extension specifier */
5980                /* copy the extension specification */
5981                if (extspec) {
5982                    if (ptr2 - ptr1 > FLEN_FILENAME - 1) {
5983                        free(infile);
5984                        return(*status = URL_PARSE_ERROR);
5985 		   }
5986                    strncat(extspec, ptr1, ptr2 - ptr1);
5987                }
5988 
5989                /* copy any remaining chars to filter spec string */
5990                strcat(rowfilter, ptr2 + 1);
5991            }
5992        }
5993     }      /* end of  if (!plus_ext)     */
5994     else
5995     {
5996       /* ------------------------------------------------------------------ */
5997       /* already have extension, so this must be a filter spec of some sort */
5998       /* ------------------------------------------------------------------ */
5999 
6000         strcat(rowfilter, ptr3);
6001     }
6002 
6003     /* strip off any trailing blanks from filter */
6004     slen = strlen(rowfilter);
6005     while ( (--slen) >= 0  && rowfilter[slen] == ' ')
6006          rowfilter[slen] = '\0';
6007 
6008     if (!rowfilter[0])
6009     {
6010         free(infile);
6011         return(*status);      /* nothing left to parse */
6012     }
6013 
6014     /* ------------------------------------------------ */
6015     /* does the filter contain a binning specification? */
6016     /* ------------------------------------------------ */
6017 
6018     ptr1 = strstr(rowfilter, "[bin");      /* search for "[bin" */
6019     if (!ptr1)
6020         ptr1 = strstr(rowfilter, "[BIN");      /* search for "[BIN" */
6021     if (!ptr1)
6022         ptr1 = strstr(rowfilter, "[Bin");      /* search for "[Bin" */
6023 
6024     if (ptr1)
6025     {
6026       ptr2 = ptr1 + 4;     /* end of the '[bin' string */
6027       if (*ptr2 == 'b' || *ptr2 == 'i' || *ptr2 == 'j' ||
6028           *ptr2 == 'r' || *ptr2 == 'd')
6029          ptr2++;  /* skip the datatype code letter */
6030 
6031 
6032       if ( *ptr2 != ' ' && *ptr2 != ']')
6033         ptr1 = NULL;   /* bin string must be followed by space or ] */
6034     }
6035 
6036     if (ptr1)
6037     {
6038         /* found the binning string */
6039         if (binspec)
6040         {
6041 	    if (strlen(ptr1 +1) > FLEN_FILENAME - 1)
6042 	    {
6043                     free(infile);
6044                     return(*status = URL_PARSE_ERROR);
6045             }
6046 
6047             strcpy(binspec, ptr1 + 1);
6048             ptr2 = strchr(binspec, ']');
6049 
6050             if (ptr2)      /* terminate the binning filter */
6051             {
6052                 *ptr2 = '\0';
6053 
6054                 if ( *(--ptr2) == ' ')  /* delete trailing spaces */
6055                     *ptr2 = '\0';
6056             }
6057             else
6058             {
6059                 ffpmsg("input file URL is missing closing bracket ']'");
6060                 ffpmsg(rowfilter);
6061                 free(infile);
6062                 return(*status = URL_PARSE_ERROR);  /* error, no closing ] */
6063             }
6064         }
6065 
6066         /* delete the binning spec from the row filter string */
6067         ptr2 = strchr(ptr1, ']');
6068         strcpy(tmpstr, ptr2+1);  /* copy any chars after the binspec */
6069         strcpy(ptr1, tmpstr);    /* overwrite binspec */
6070     }
6071 
6072     /* --------------------------------------------------------- */
6073     /* does the filter contain a column selection specification? */
6074     /* --------------------------------------------------------- */
6075 
6076     ptr1 = strstr(rowfilter, "[col ");
6077     if (!ptr1)
6078     {
6079         ptr1 = strstr(rowfilter, "[COL ");
6080 
6081         if (!ptr1)
6082             ptr1 = strstr(rowfilter, "[Col ");
6083     }
6084 
6085     if (ptr1)
6086     {           /* find the end of the column specifier */
6087         ptr2 = ptr1 + 5;
6088         while (*ptr2 != ']')
6089         {
6090             if (*ptr2 == '\0')
6091             {
6092                 ffpmsg("input file URL is missing closing bracket ']'");
6093                 free(infile);
6094                 return(*status = URL_PARSE_ERROR);  /* error, no closing ] */
6095             }
6096 
6097             if (*ptr2 == '\'')  /* start of a literal string */
6098             {
6099                 ptr2 = strchr(ptr2 + 1, '\'');  /* find closing quote */
6100                 if (!ptr2)
6101                 {
6102                   ffpmsg
6103           ("literal string in input file URL is missing closing single quote");
6104                   free(infile);
6105                   return(*status = URL_PARSE_ERROR);  /* error, no closing ] */
6106                 }
6107             }
6108 
6109             if (*ptr2 == '[')  /* set of nested square brackets */
6110             {
6111                 ptr2 = strchr(ptr2 + 1, ']');  /* find closing bracket */
6112                 if (!ptr2)
6113                 {
6114                   ffpmsg
6115           ("nested brackets in input file URL is missing closing bracket");
6116                   free(infile);
6117                   return(*status = URL_PARSE_ERROR);  /* error, no closing ] */
6118                 }
6119             }
6120 
6121             ptr2++;  /* continue search for the closing bracket character */
6122         }
6123 
6124         collen = ptr2 - ptr1 - 1;
6125 
6126         if (colspec)    /* copy the column specifier to output string */
6127         {
6128             if (collen > FLEN_FILENAME - 1) {
6129                        free(infile);
6130                        return(*status = URL_PARSE_ERROR);
6131             }
6132 
6133             strncpy(colspec, ptr1 + 1, collen);
6134             colspec[collen] = '\0';
6135 
6136             while (colspec[--collen] == ' ')
6137                 colspec[collen] = '\0';  /* strip trailing blanks */
6138         }
6139 
6140         /* delete the column selection spec from the row filter string */
6141         strcpy(tmpstr, ptr2 + 1);  /* copy any chars after the colspec */
6142         strcpy(ptr1, tmpstr);      /* overwrite binspec */
6143     }
6144 
6145     /* --------------------------------------------------------- */
6146     /* does the filter contain a pixel filter specification?     */
6147     /* --------------------------------------------------------- */
6148 
6149     ptr1 = strstr(rowfilter, "[pix");
6150     if (!ptr1)
6151     {
6152         ptr1 = strstr(rowfilter, "[PIX");
6153 
6154         if (!ptr1)
6155             ptr1 = strstr(rowfilter, "[Pix");
6156     }
6157 
6158     if (ptr1)
6159     {
6160       ptr2 = ptr1 + 4;     /* end of the '[pix' string */
6161       if (*ptr2 == 'b' || *ptr2 == 'i' || *ptr2 == 'j' || *ptr2 == 'B' ||
6162           *ptr2 == 'I' || *ptr2 == 'J' || *ptr2 == 'r' || *ptr2 == 'd' ||
6163            *ptr2 == 'R' || *ptr2 == 'D')
6164          ptr2++;  /* skip the datatype code letter */
6165 
6166       if (*ptr2 == '1')
6167          ptr2++;   /* skip the single HDU indicator */
6168 
6169       if ( *ptr2 != ' ')
6170         ptr1 = NULL;   /* pix string must be followed by space */
6171     }
6172 
6173     if (ptr1)
6174     {           /* find the end of the pixel filter */
6175         while (*ptr2 != ']')
6176         {
6177             if (*ptr2 == '\0')
6178             {
6179                 ffpmsg("input file URL is missing closing bracket ']'");
6180                 free(infile);
6181                 return(*status = URL_PARSE_ERROR);  /* error, no closing ] */
6182             }
6183 
6184             if (*ptr2 == '\'')  /* start of a literal string */
6185             {
6186                 ptr2 = strchr(ptr2 + 1, '\'');  /* find closing quote */
6187                 if (!ptr2)
6188                 {
6189                   ffpmsg
6190           ("literal string in input file URL is missing closing single quote");
6191                   free(infile);
6192                   return(*status = URL_PARSE_ERROR);  /* error, no closing ] */
6193                 }
6194             }
6195 
6196             if (*ptr2 == '[')  /* set of nested square brackets */
6197             {
6198                 ptr2 = strchr(ptr2 + 1, ']');  /* find closing bracket */
6199                 if (!ptr2)
6200                 {
6201                   ffpmsg
6202           ("nested brackets in input file URL is missing closing bracket");
6203                   free(infile);
6204                   return(*status = URL_PARSE_ERROR);  /* error, no closing ] */
6205                 }
6206             }
6207 
6208             ptr2++;  /* continue search for the closing bracket character */
6209         }
6210 
6211         collen = ptr2 - ptr1 - 1;
6212 
6213         if (pixfilter)    /* copy the column specifier to output string */
6214         {
6215             if (collen > FLEN_FILENAME - 1) {
6216                        free(infile);
6217                        return(*status = URL_PARSE_ERROR);
6218             }
6219 
6220             strncpy(pixfilter, ptr1 + 1, collen);
6221             pixfilter[collen] = '\0';
6222 
6223             while (pixfilter[--collen] == ' ')
6224                 pixfilter[collen] = '\0';  /* strip trailing blanks */
6225         }
6226 
6227         /* delete the pixel filter from the row filter string */
6228         strcpy(tmpstr, ptr2 + 1);  /* copy any chars after the pixel filter */
6229         strcpy(ptr1, tmpstr);      /* overwrite binspec */
6230     }
6231 
6232     /* ------------------------------------------------------------ */
6233     /* does the filter contain an image compression specification?  */
6234     /* ------------------------------------------------------------ */
6235 
6236     ptr1 = strstr(rowfilter, "[compress");
6237 
6238     if (ptr1)
6239     {
6240       ptr2 = ptr1 + 9;     /* end of the '[compress' string */
6241 
6242       if ( *ptr2 != ' ' && *ptr2 != ']')
6243         ptr1 = NULL;   /* compress string must be followed by space or ] */
6244     }
6245 
6246     if (ptr1)
6247     {
6248         /* found the compress string */
6249         if (compspec)
6250         {
6251 	    if (strlen(ptr1 +1) > FLEN_FILENAME - 1)
6252 	    {
6253                     free(infile);
6254                     return(*status = URL_PARSE_ERROR);
6255             }
6256 
6257             strcpy(compspec, ptr1 + 1);
6258             ptr2 = strchr(compspec, ']');
6259 
6260             if (ptr2)      /* terminate the binning filter */
6261             {
6262                 *ptr2 = '\0';
6263 
6264                 if ( *(--ptr2) == ' ')  /* delete trailing spaces */
6265                     *ptr2 = '\0';
6266             }
6267             else
6268             {
6269                 ffpmsg("input file URL is missing closing bracket ']'");
6270                 ffpmsg(rowfilter);
6271                 free(infile);
6272                 return(*status = URL_PARSE_ERROR);  /* error, no closing ] */
6273             }
6274         }
6275 
6276         /* delete the compression spec from the row filter string */
6277         ptr2 = strchr(ptr1, ']');
6278         strcpy(tmpstr, ptr2+1);  /* copy any chars after the binspec */
6279         strcpy(ptr1, tmpstr);    /* overwrite binspec */
6280     }
6281 
6282     /* copy the remaining string to the rowfilter output... should only */
6283     /* contain a rowfilter expression of the form "[expr]"              */
6284 
6285     if (rowfilterx && rowfilter[0]) {
6286        ptr2 = rowfilter + strlen(rowfilter) - 1;
6287        if( rowfilter[0]=='[' && *ptr2==']' ) {
6288           *ptr2 = '\0';
6289 
6290 	   if (strlen(rowfilter + 1)  > FLEN_FILENAME - 1)
6291 	   {
6292                     free(infile);
6293                     return(*status = URL_PARSE_ERROR);
6294            }
6295 
6296           strcpy(rowfilterx, rowfilter+1);
6297        } else {
6298           ffpmsg("input file URL lacks valid row filter expression");
6299           *status = URL_PARSE_ERROR;
6300        }
6301     }
6302 
6303     free(infile);
6304     return(*status);
6305 }
6306 /*--------------------------------------------------------------------------*/
ffexist(const char * infile,int * exists,int * status)6307 int ffexist(const char *infile, /* I - input filename or URL */
6308             int *exists,        /* O -  2 = a compressed version of file exists */
6309 	                        /*      1 = yes, disk file exists               */
6310 	                        /*      0 = no, disk file could not be found    */
6311 				/*     -1 = infile is not a disk file (could    */
6312 				/*   be a http, ftp, gsiftp, smem, or stdin file) */
6313             int *status)        /* I/O  status  */
6314 
6315 /*
6316    test if the input file specifier is an existing file on disk
6317    If the specified file can't be found, it then searches for a
6318    compressed version of the file.
6319 */
6320 {
6321     FILE *diskfile;
6322     char rootname[FLEN_FILENAME];
6323     char *ptr1;
6324 
6325     if (*status > 0)
6326         return(*status);
6327 
6328     /* strip off any extname or filters from the name */
6329     ffrtnm( (char *)infile, rootname, status);
6330 
6331     ptr1 = strstr(rootname, "://");
6332 
6333     if (ptr1 || *rootname == '-') {
6334         if (!strncmp(rootname, "file", 4) ) {
6335 	    ptr1 = ptr1 + 3;   /* pointer to start of the disk file name */
6336 	} else {
6337 	    *exists = -1;   /* this is not a disk file */
6338 	    return (*status);
6339 	}
6340     } else {
6341         ptr1 = rootname;
6342     }
6343 
6344     /* see if the disk file exists */
6345     if (file_openfile(ptr1, 0, &diskfile)) {
6346 
6347         /* no, couldn't open file, so see if there is a compressed version */
6348         if (file_is_compressed(ptr1) ) {
6349            *exists = 2;  /* a compressed version of the file exists */
6350         } else {
6351 	   *exists = 0;  /* neither file nor compressed version exist */
6352 	}
6353 
6354     } else {
6355 
6356         /* yes, file exists */
6357         *exists = 1;
6358 	fclose(diskfile);
6359     }
6360 
6361     return(*status);
6362 }
6363 /*--------------------------------------------------------------------------*/
ffrtnm(char * url,char * rootname,int * status)6364 int ffrtnm(char *url,
6365            char *rootname,
6366            int *status)
6367 /*
6368    parse the input URL, returning the root name (filetype://basename).
6369 */
6370 
6371 {
6372     int ii, jj, slen, infilelen;
6373     char *ptr1, *ptr2, *ptr3;
6374     char urltype[MAX_PREFIX_LEN];
6375     char infile[FLEN_FILENAME];
6376 
6377     if (*status > 0)
6378         return(*status);
6379 
6380     ptr1 = url;
6381     *rootname = '\0';
6382     *urltype = '\0';
6383     *infile  = '\0';
6384 
6385     /*  get urltype (e.g., file://, ftp://, http://, etc.)  */
6386     if (*ptr1 == '-')        /* "-" means read file from stdin */
6387     {
6388         strcat(urltype, "-");
6389         ptr1++;
6390     }
6391     else if (!strncmp(ptr1, "stdin", 5) || !strncmp(ptr1, "STDIN", 5))
6392     {
6393         strcat(urltype, "-");
6394         ptr1 = ptr1 + 5;
6395     }
6396     else
6397     {
6398         ptr2 = strstr(ptr1, "://");
6399         ptr3 = strstr(ptr1, "(" );
6400 
6401         if (ptr3 && (ptr3 < ptr2) )
6402         {
6403            /* the urltype follows a '(' character, so it must apply */
6404            /* to the output file, and is not the urltype of the input file */
6405            ptr2 = 0;   /* so reset pointer to zero */
6406         }
6407 
6408 
6409         if (ptr2)                  /* copy the explicit urltype string */
6410         {
6411 
6412 	   if (ptr2 - ptr1 + 3 > MAX_PREFIX_LEN - 1)
6413 	   {
6414                return(*status = URL_PARSE_ERROR);
6415            }
6416             strncat(urltype, ptr1, ptr2 - ptr1 + 3);
6417             ptr1 = ptr2 + 3;
6418         }
6419         else if (!strncmp(ptr1, "ftp:", 4) )
6420         {                              /* the 2 //'s are optional */
6421             strcat(urltype, "ftp://");
6422             ptr1 += 4;
6423         }
6424         else if (!strncmp(ptr1, "gsiftp:", 7) )
6425         {                              /* the 2 //'s are optional */
6426             strcat(urltype, "gsiftp://");
6427             ptr1 += 7;
6428         }
6429         else if (!strncmp(ptr1, "http:", 5) )
6430         {                              /* the 2 //'s are optional */
6431             strcat(urltype, "http://");
6432             ptr1 += 5;
6433         }
6434         else if (!strncmp(ptr1, "mem:", 4) )
6435         {                              /* the 2 //'s are optional */
6436             strcat(urltype, "mem://");
6437             ptr1 += 4;
6438         }
6439         else if (!strncmp(ptr1, "shmem:", 6) )
6440         {                              /* the 2 //'s are optional */
6441             strcat(urltype, "shmem://");
6442             ptr1 += 6;
6443         }
6444         else if (!strncmp(ptr1, "file:", 5) )
6445         {                              /* the 2 //'s are optional */
6446             ptr1 += 5;
6447         }
6448 
6449         /* else assume file driver    */
6450     }
6451 
6452        /*  get the input file name  */
6453     ptr2 = strchr(ptr1, '(');   /* search for opening parenthesis ( */
6454     ptr3 = strchr(ptr1, '[');   /* search for opening bracket [ */
6455 
6456     if (ptr2 == ptr3)  /* simple case: no [ or ( in the file name */
6457     {
6458 
6459 	if (strlen(ptr1) > FLEN_FILENAME - 1)
6460         {
6461             return(*status = URL_PARSE_ERROR);
6462         }
6463 
6464         strcat(infile, ptr1);
6465     }
6466     else if (!ptr3)     /* no bracket, so () enclose output file name */
6467     {
6468 
6469 	if (ptr2 - ptr1 > FLEN_FILENAME - 1)
6470         {
6471             return(*status = URL_PARSE_ERROR);
6472         }
6473 
6474         strncat(infile, ptr1, ptr2 - ptr1);
6475         ptr2++;
6476 
6477         ptr1 = strchr(ptr2, ')' );   /* search for closing ) */
6478         if (!ptr1)
6479             return(*status = URL_PARSE_ERROR);  /* error, no closing ) */
6480 
6481     }
6482     else if (ptr2 && (ptr2 < ptr3)) /* () enclose output name before bracket */
6483     {
6484 
6485 	if (ptr2 - ptr1 > FLEN_FILENAME - 1)
6486         {
6487             return(*status = URL_PARSE_ERROR);
6488         }
6489 
6490         strncat(infile, ptr1, ptr2 - ptr1);
6491         ptr2++;
6492 
6493         ptr1 = strchr(ptr2, ')' );   /* search for closing ) */
6494         if (!ptr1)
6495             return(*status = URL_PARSE_ERROR);  /* error, no closing ) */
6496     }
6497     else    /*   bracket comes first, so there is no output name */
6498     {
6499 	if (ptr3 - ptr1 > FLEN_FILENAME - 1)
6500         {
6501             return(*status = URL_PARSE_ERROR);
6502         }
6503 
6504         strncat(infile, ptr1, ptr3 - ptr1);
6505     }
6506 
6507        /* strip off any trailing blanks in the names */
6508     slen = strlen(infile);
6509     for (ii = slen - 1; ii > 0; ii--)
6510     {
6511         if (infile[ii] == ' ')
6512             infile[ii] = '\0';
6513         else
6514             break;
6515     }
6516 
6517     /* --------------------------------------------- */
6518     /* check if the 'filename+n' convention has been */
6519     /* used to specifiy which HDU number to open     */
6520     /* --------------------------------------------- */
6521 
6522     jj = strlen(infile);
6523 
6524     for (ii = jj - 1; ii >= 0; ii--)
6525     {
6526         if (infile[ii] == '+')    /* search backwards for '+' sign */
6527             break;
6528     }
6529 
6530     if (ii > 0 && (jj - ii) < 5)  /* limit extension numbers to 4 digits */
6531     {
6532         infilelen = ii;
6533         ii++;
6534 
6535 
6536         for (; ii < jj; ii++)
6537         {
6538             if (!isdigit((int) infile[ii] ) ) /* are all the chars digits? */
6539                 break;
6540         }
6541 
6542         if (ii == jj)
6543         {
6544              /* yes, the '+n' convention was used.  */
6545 
6546              infile[infilelen] = '\0'; /* delete the extension number */
6547         }
6548     }
6549 
6550     if (strlen(urltype) + strlen(infile) > FLEN_FILENAME - 1)
6551     {
6552             return(*status = URL_PARSE_ERROR);
6553     }
6554 
6555     strcat(rootname, urltype);  /* construct the root name */
6556     strcat(rootname, infile);
6557 
6558     return(*status);
6559 }
6560 /*--------------------------------------------------------------------------*/
ffourl(char * url,char * urltype,char * outfile,char * tpltfile,char * compspec,int * status)6561 int ffourl(char *url,             /* I - full input URL   */
6562            char *urltype,          /* O - url type         */
6563            char *outfile,          /* O - base file name   */
6564            char *tpltfile,         /* O - template file name, if any */
6565            char *compspec,         /* O - compression specification, if any */
6566            int *status)
6567 /*
6568    parse the output URL into its basic components.
6569 */
6570 
6571 {
6572     char *ptr1, *ptr2, *ptr3;
6573 
6574     if (*status > 0)
6575         return(*status);
6576 
6577     if (urltype)
6578       *urltype = '\0';
6579     if (outfile)
6580       *outfile = '\0';
6581     if (tpltfile)
6582       *tpltfile = '\0';
6583     if (compspec)
6584       *compspec = '\0';
6585 
6586     ptr1 = url;
6587     while (*ptr1 == ' ')    /* ignore leading blanks */
6588            ptr1++;
6589 
6590     if ( ( (*ptr1 == '-') &&  ( *(ptr1 +1) ==  0   || *(ptr1 +1) == ' ' ) )
6591          ||  !strcmp(ptr1, "stdout")
6592          ||  !strcmp(ptr1, "STDOUT"))
6593 
6594          /* "-" means write to stdout;  also support "- "            */
6595          /* but exclude disk file names that begin with a minus sign */
6596          /* e.g., "-55d33m.fits"   */
6597     {
6598       if (urltype)
6599         strcpy(urltype, "stdout://");
6600     }
6601     else
6602     {
6603         /* not writing to stdout */
6604         /*  get urltype (e.g., file://, ftp://, http://, etc.)  */
6605 
6606         ptr2 = strstr(ptr1, "://");
6607         if (ptr2)                  /* copy the explicit urltype string */
6608         {
6609           if (urltype) {
6610 	    if (ptr2 - ptr1 + 3 > MAX_PREFIX_LEN - 1)
6611 	    {
6612                 return(*status = URL_PARSE_ERROR);
6613             }
6614 
6615             strncat(urltype, ptr1, ptr2 - ptr1 + 3);
6616           }
6617 
6618           ptr1 = ptr2 + 3;
6619         }
6620         else                       /* assume file driver    */
6621         {
6622           if (urltype)
6623              strcat(urltype, "file://");
6624         }
6625 
6626         /* look for template file name, enclosed in parenthesis */
6627         ptr2 = strchr(ptr1, '(');
6628 
6629         /* look for image compression parameters, enclosed in sq. brackets */
6630         ptr3 = strchr(ptr1, '[');
6631 
6632         if (outfile)
6633         {
6634           if (ptr2) {  /* template file was specified  */
6635 	     if (ptr2 - ptr1 > FLEN_FILENAME - 1)
6636 	     {
6637                 return(*status = URL_PARSE_ERROR);
6638              }
6639 
6640              strncat(outfile, ptr1, ptr2 - ptr1);
6641           } else if (ptr3) {  /* compression was specified  */
6642 	     if (ptr3 - ptr1 > FLEN_FILENAME - 1)
6643 	     {
6644                 return(*status = URL_PARSE_ERROR);
6645              }
6646              strncat(outfile, ptr1, ptr3 - ptr1);
6647 
6648           } else { /* no template file or compression */
6649 	     if (strlen(ptr1) > FLEN_FILENAME - 1)
6650 	     {
6651                 return(*status = URL_PARSE_ERROR);
6652              }
6653              strcpy(outfile, ptr1);
6654           }
6655         }
6656 
6657 
6658         if (ptr2)   /* template file was specified  */
6659         {
6660             ptr2++;
6661 
6662             ptr1 = strchr(ptr2, ')' );   /* search for closing ) */
6663 
6664             if (!ptr1)
6665             {
6666                 return(*status = URL_PARSE_ERROR);  /* error, no closing ) */
6667             }
6668 
6669             if (tpltfile) {
6670 	        if (ptr1 - ptr2 > FLEN_FILENAME - 1)
6671 	        {
6672                    return(*status = URL_PARSE_ERROR);
6673                 }
6674                  strncat(tpltfile, ptr2, ptr1 - ptr2);
6675             }
6676         }
6677 
6678         if (ptr3)   /* compression was specified  */
6679         {
6680             ptr3++;
6681 
6682             ptr1 = strchr(ptr3, ']' );   /* search for closing ] */
6683 
6684             if (!ptr1)
6685             {
6686                 return(*status = URL_PARSE_ERROR);  /* error, no closing ] */
6687             }
6688 
6689             if (compspec) {
6690 
6691 	        if (ptr1 - ptr3 > FLEN_FILENAME - 1)
6692 	        {
6693                    return(*status = URL_PARSE_ERROR);
6694                 }
6695 
6696                 strncat(compspec, ptr3, ptr1 - ptr3);
6697             }
6698         }
6699 
6700         /* check if a .gz compressed output file is to be created */
6701         /* by seeing if the filename ends in '.gz'   */
6702         if (urltype && outfile)
6703         {
6704             if (!strcmp(urltype, "file://") )
6705             {
6706                 ptr1 = strstr(outfile, ".gz");
6707                 if (ptr1)
6708                 {    /* make sure the ".gz" is at the end of the file name */
6709                    ptr1 += 3;
6710                    if (*ptr1 ==  0  || *ptr1 == ' '  )
6711                       strcpy(urltype, "compressoutfile://");
6712                 }
6713             }
6714         }
6715     }
6716     return(*status);
6717 }
6718 /*--------------------------------------------------------------------------*/
ffexts(char * extspec,int * extnum,char * extname,int * extvers,int * hdutype,char * imagecolname,char * rowexpress,int * status)6719 int ffexts(char *extspec,
6720                        int *extnum,
6721                        char *extname,
6722                        int *extvers,
6723                        int *hdutype,
6724                        char *imagecolname,
6725                        char *rowexpress,
6726                        int *status)
6727 {
6728 /*
6729    Parse the input extension specification string, returning either the
6730    extension number or the values of the EXTNAME, EXTVERS, and XTENSION
6731    keywords in desired extension. Also return the name of the column containing
6732    an image, and an expression to be used to determine which row to use,
6733    if present.
6734 */
6735     char *ptr1, *ptr2;
6736     int slen, nvals;
6737     int notint = 1; /* initially assume specified extname is not an integer */
6738     char tmpname[FLEN_VALUE], *loc;
6739 
6740     *extnum = 0;
6741     *extname = '\0';
6742     *extvers = 0;
6743     *hdutype = ANY_HDU;
6744     *imagecolname = '\0';
6745     *rowexpress = '\0';
6746 
6747     if (*status > 0)
6748         return(*status);
6749 
6750     ptr1 = extspec;       /* pointer to first char */
6751 
6752     while (*ptr1 == ' ')  /* skip over any leading blanks */
6753         ptr1++;
6754 
6755     if (isdigit((int) *ptr1))  /* is the extension specification a number? */
6756     {
6757         notint = 0;  /* looks like extname may actually be the ext. number */
6758         errno = 0;  /* reset this prior to calling strtol */
6759         *extnum = strtol(ptr1, &loc, 10);  /* read the string as an integer */
6760 
6761         while (*loc == ' ')  /* skip over trailing blanks */
6762            loc++;
6763 
6764         /* check for read error, or junk following the integer */
6765         if ((*loc != '\0' && *loc != ';' ) || (errno == ERANGE) )
6766         {
6767            *extnum = 0;
6768            notint = 1;  /* no, extname was not a simple integer after all */
6769            errno = 0;  /* reset error condition flag if it was set */
6770         }
6771 
6772         if ( *extnum < 0 || *extnum > 99999)
6773         {
6774             *extnum = 0;   /* this is not a reasonable extension number */
6775             ffpmsg("specified extension number is out of range:");
6776             ffpmsg(extspec);
6777             return(*status = URL_PARSE_ERROR);
6778         }
6779     }
6780 
6781 
6782 /*  This logic was too simple, and failed on extnames like '1000TEMP'
6783     where it would try to move to the 1000th extension
6784 
6785     if (isdigit((int) *ptr1))
6786     {
6787         sscanf(ptr1, "%d", extnum);
6788         if (*extnum < 0 || *extnum > 9999)
6789         {
6790             *extnum = 0;
6791             ffpmsg("specified extension number is out of range:");
6792             ffpmsg(extspec);
6793             return(*status = URL_PARSE_ERROR);
6794         }
6795     }
6796 */
6797 
6798     if (notint)
6799     {
6800            /* not a number, so EXTNAME must be specified, followed by */
6801            /* optional EXTVERS and XTENSION  values */
6802 
6803            /* don't use space char as end indicator, because there */
6804            /* may be imbedded spaces in the EXTNAME value */
6805            slen = strcspn(ptr1, ",:;");   /* length of EXTNAME */
6806 
6807 	   if (slen > FLEN_VALUE - 1)
6808 	   {
6809                 return(*status = URL_PARSE_ERROR);
6810            }
6811 
6812            strncat(extname, ptr1, slen);  /* EXTNAME value */
6813 
6814            /* now remove any trailing blanks */
6815            while (slen > 0 && *(extname + slen -1) == ' ')
6816            {
6817                *(extname + slen -1) = '\0';
6818                slen--;
6819            }
6820 
6821            ptr1 += slen;
6822            slen = strspn(ptr1, " ,:");  /* skip delimiter characters */
6823            ptr1 += slen;
6824 
6825            slen = strcspn(ptr1, " ,:;");   /* length of EXTVERS */
6826            if (slen)
6827            {
6828                nvals = sscanf(ptr1, "%d", extvers);  /* EXTVERS value */
6829                if (nvals != 1)
6830                {
6831                    ffpmsg("illegal EXTVER value in input URL:");
6832                    ffpmsg(extspec);
6833                    return(*status = URL_PARSE_ERROR);
6834                }
6835 
6836                ptr1 += slen;
6837                slen = strspn(ptr1, " ,:");  /* skip delimiter characters */
6838                ptr1 += slen;
6839 
6840                slen = strcspn(ptr1, ";");   /* length of HDUTYPE */
6841                if (slen)
6842                {
6843                  if (*ptr1 == 'b' || *ptr1 == 'B')
6844                      *hdutype = BINARY_TBL;
6845                  else if (*ptr1 == 't' || *ptr1 == 'T' ||
6846                           *ptr1 == 'a' || *ptr1 == 'A')
6847                      *hdutype = ASCII_TBL;
6848                  else if (*ptr1 == 'i' || *ptr1 == 'I')
6849                      *hdutype = IMAGE_HDU;
6850                  else
6851                  {
6852                      ffpmsg("unknown type of HDU in input URL:");
6853                      ffpmsg(extspec);
6854                      return(*status = URL_PARSE_ERROR);
6855                  }
6856                }
6857            }
6858            else
6859            {
6860                 strcpy(tmpname, extname);
6861                 ffupch(tmpname);
6862                 if (!strcmp(tmpname, "PRIMARY") || !strcmp(tmpname, "P") )
6863                     *extname = '\0';  /* return extnum = 0 */
6864            }
6865     }
6866 
6867     ptr1 = strchr(ptr1, ';');
6868     if (ptr1)
6869     {
6870         /* an image is to be opened; the image is contained in a single */
6871         /* cell of a binary table.  A column name and an expression to  */
6872         /* determine which row to use has been entered.                 */
6873 
6874         ptr1++;  /* skip over the ';' delimiter */
6875         while (*ptr1 == ' ')  /* skip over any leading blanks */
6876             ptr1++;
6877 
6878         ptr2 = strchr(ptr1, '(');
6879         if (!ptr2)
6880         {
6881             ffpmsg("illegal specification of image in table cell in input URL:");
6882             ffpmsg(" did not find a row expression enclosed in ( )");
6883             ffpmsg(extspec);
6884             return(*status = URL_PARSE_ERROR);
6885         }
6886 
6887 	if (ptr2 - ptr1 > FLEN_FILENAME - 1)
6888 	{
6889             return(*status = URL_PARSE_ERROR);
6890         }
6891 
6892         strncat(imagecolname, ptr1, ptr2 - ptr1); /* copy column name */
6893 
6894         ptr2++;  /* skip over the '(' delimiter */
6895         while (*ptr2 == ' ')  /* skip over any leading blanks */
6896             ptr2++;
6897 
6898 
6899         ptr1 = strchr(ptr2, ')');
6900         if (!ptr2)
6901         {
6902             ffpmsg("illegal specification of image in table cell in input URL:");
6903             ffpmsg(" missing closing ')' character in row expression");
6904             ffpmsg(extspec);
6905             return(*status = URL_PARSE_ERROR);
6906         }
6907 
6908 	if (ptr1 - ptr2 > FLEN_FILENAME - 1)
6909         {
6910                 return(*status = URL_PARSE_ERROR);
6911         }
6912 
6913         strncat(rowexpress, ptr2, ptr1 - ptr2); /* row expression */
6914     }
6915 
6916     return(*status);
6917 }
6918 /*--------------------------------------------------------------------------*/
ffextn(char * url,int * extension_num,int * status)6919 int ffextn(char *url,           /* I - input filename/URL  */
6920            int *extension_num,  /* O - returned extension number */
6921            int *status)
6922 {
6923 /*
6924    Parse the input url string and return the number of the extension that
6925    CFITSIO would automatically move to if CFITSIO were to open this input URL.
6926    The extension numbers are one's based, so 1 = the primary array, 2 = the
6927    first extension, etc.
6928 
6929    The extension number that gets returned is determined by the following
6930    algorithm:
6931 
6932    1. If the input URL includes a binning specification (e.g.
6933    'myfile.fits[3][bin X,Y]') then the returned extension number
6934    will always = 1, since CFITSIO would create a temporary primary
6935    image on the fly in this case.  The same is true if an image
6936    within a single cell of a binary table is opened.
6937 
6938    2.  Else if the input URL specifies an extension number (e.g.,
6939    'myfile.fits[3]' or 'myfile.fits+3') then the specified extension
6940    number (+ 1) is returned.
6941 
6942    3.  Else if the extension name is specified in brackets
6943    (e.g., this 'myfile.fits[EVENTS]') then the file will be opened and searched
6944    for the extension number.  If the input URL is '-'  (reading from the stdin
6945    file stream) this is not possible and an error will be returned.
6946 
6947    4.  Else if the URL does not specify an extension (e.g. 'myfile.fits') then
6948    a special extension number = -99 will be returned to signal that no
6949    extension was specified.  This feature is mainly for compatibility with
6950    existing FTOOLS software.  CFITSIO would open the primary array by default
6951    (extension_num = 1) in this case.
6952 
6953 */
6954     fitsfile *fptr;
6955     char urltype[20];
6956     char infile[FLEN_FILENAME];
6957     char outfile[FLEN_FILENAME];
6958     char extspec[FLEN_FILENAME];
6959     char extname[FLEN_FILENAME];
6960     char rowfilter[FLEN_FILENAME];
6961     char binspec[FLEN_FILENAME];
6962     char colspec[FLEN_FILENAME];
6963     char imagecolname[FLEN_VALUE], rowexpress[FLEN_FILENAME];
6964     char *cptr;
6965     int extnum, extvers, hdutype, tstatus = 0;
6966 
6967     if (*status > 0)
6968         return(*status);
6969 
6970     /*  parse the input URL into its basic components  */
6971     fits_parse_input_url(url, urltype, infile, outfile,
6972              extspec, rowfilter,binspec, colspec, status);
6973 
6974     if (*status > 0)
6975         return(*status);
6976 
6977     if (*binspec)   /* is there a binning specification? */
6978     {
6979        *extension_num = 1; /* a temporary primary array image is created */
6980        return(*status);
6981     }
6982 
6983     if (*extspec)   /* is an extension specified? */
6984     {
6985        ffexts(extspec, &extnum,
6986          extname, &extvers, &hdutype, imagecolname, rowexpress, status);
6987 
6988       if (*status > 0)
6989         return(*status);
6990 
6991       if (*imagecolname)   /* is an image within a table cell being opened? */
6992       {
6993          *extension_num = 1; /* a temporary primary array image is created */
6994          return(*status);
6995       }
6996 
6997       if (*extname)
6998       {
6999          /* have to open the file to search for the extension name (curses!) */
7000 
7001          if (!strcmp(urltype, "stdin://"))
7002             /* opening stdin would destroying it! */
7003             return(*status = URL_PARSE_ERROR);
7004 
7005          /* First, strip off any filtering specification */
7006          infile[0] = '\0';
7007 	 strncat(infile, url, FLEN_FILENAME -1);
7008 
7009          cptr = strchr(infile, ']');  /* locate the closing bracket */
7010          if (!cptr)
7011          {
7012              return(*status = URL_PARSE_ERROR);
7013          }
7014          else
7015          {
7016              cptr++;
7017              *cptr = '\0'; /* terminate URl after the extension spec */
7018          }
7019 
7020          if (ffopen(&fptr, infile, READONLY, status) > 0) /* open the file */
7021          {
7022             ffclos(fptr, &tstatus);
7023             return(*status);
7024          }
7025 
7026          ffghdn(fptr, &extnum);    /* where am I in the file? */
7027          *extension_num = extnum;
7028          ffclos(fptr, status);
7029 
7030          return(*status);
7031       }
7032       else
7033       {
7034          *extension_num = extnum + 1;  /* return the specified number (+ 1) */
7035          return(*status);
7036       }
7037     }
7038     else
7039     {
7040          *extension_num = -99;  /* no specific extension was specified */
7041                                 /* defaults to primary array */
7042          return(*status);
7043     }
7044 }
7045 /*--------------------------------------------------------------------------*/
7046 
ffurlt(fitsfile * fptr,char * urlType,int * status)7047 int ffurlt(fitsfile *fptr, char *urlType, int *status)
7048 /*
7049    return the prefix string associated with the driver in use by the
7050    fitsfile pointer fptr
7051 */
7052 
7053 {
7054   strcpy(urlType, driverTable[fptr->Fptr->driver].prefix);
7055   return(*status);
7056 }
7057 
7058 /*--------------------------------------------------------------------------*/
ffimport_file(char * filename,char ** contents,int * status)7059 int ffimport_file( char *filename,   /* Text file to read                   */
7060                    char **contents,  /* Pointer to pointer to hold file     */
7061                    int *status )     /* CFITSIO error code                  */
7062 /*
7063    Read and concatenate all the lines from the given text file.  User
7064    must free the pointer returned in contents.  Pointer is guaranteed
7065    to hold 2 characters more than the length of the text... allows the
7066    calling routine to append (or prepend) a newline (or quotes?) without
7067    reallocating memory.
7068 */
7069 {
7070    int allocLen, totalLen, llen, eoline = 1;
7071    char *lines,line[256];
7072    FILE *aFile;
7073 
7074    if( *status > 0 ) return( *status );
7075 
7076    totalLen =    0;
7077    allocLen = 1024;
7078    lines    = (char *)malloc( allocLen * sizeof(char) );
7079    if( !lines ) {
7080       ffpmsg("Couldn't allocate memory to hold ASCII file contents.");
7081       return(*status = MEMORY_ALLOCATION );
7082    }
7083    lines[0] = '\0';
7084 
7085    if( (aFile = fopen( filename, "r" ))==NULL ) {
7086       snprintf(line,256,"Could not open ASCII file %s.",filename);
7087       ffpmsg(line);
7088       free( lines );
7089       return(*status = FILE_NOT_OPENED);
7090    }
7091 
7092    while( fgets(line,256,aFile)!=NULL ) {
7093       llen = strlen(line);
7094       if ( eoline && (llen > 1) && (line[0] == '/' && line[1] == '/'))
7095           continue;       /* skip comment lines begging with // */
7096 
7097       eoline = 0;
7098 
7099       /* replace CR and newline chars at end of line with nulls */
7100       if ((llen > 0) && (line[llen-1]=='\n' || line[llen-1] == '\r')) {
7101           line[--llen] = '\0';
7102           eoline = 1;   /* found an end of line character */
7103 
7104           if ((llen > 0) && (line[llen-1]=='\n' || line[llen-1] == '\r')) {
7105                  line[--llen] = '\0';
7106           }
7107       }
7108 
7109       if( totalLen + llen + 3 >= allocLen ) {
7110          allocLen += 256;
7111          lines = (char *)realloc(lines, allocLen * sizeof(char) );
7112          if( ! lines ) {
7113             ffpmsg("Couldn't allocate memory to hold ASCII file contents.");
7114             *status = MEMORY_ALLOCATION;
7115             break;
7116          }
7117       }
7118       strcpy( lines+totalLen, line );
7119       totalLen += llen;
7120 
7121       if (eoline) {
7122          strcpy( lines+totalLen, " "); /* add a space between lines */
7123          totalLen += 1;
7124       }
7125    }
7126    fclose(aFile);
7127 
7128    *contents = lines;
7129    return( *status );
7130 }
7131 
7132 /*--------------------------------------------------------------------------*/
fits_get_token(char ** ptr,char * delimiter,char * token,int * isanumber)7133 int fits_get_token(char **ptr,
7134                    char *delimiter,
7135                    char *token,
7136                    int *isanumber)   /* O - is this token a number? */
7137 /*
7138    parse off the next token, delimited by a character in 'delimiter',
7139    from the input ptr string;  increment *ptr to the end of the token.
7140    Returns the length of the token, not including the delimiter char;
7141 */
7142 {
7143     char *loc, tval[73];
7144     int slen;
7145     double dval;
7146 
7147     *token = '\0';
7148 
7149     while (**ptr == ' ')  /* skip over leading blanks */
7150         (*ptr)++;
7151 
7152     slen = strcspn(*ptr, delimiter);  /* length of next token */
7153     if (slen)
7154     {
7155         strncat(token, *ptr, slen);       /* copy token */
7156 
7157         (*ptr) += slen;                   /* skip over the token */
7158 
7159         if (isanumber)  /* check if token is a number */
7160         {
7161             *isanumber = 1;
7162 
7163 	    if (strchr(token, 'D'))  {
7164 	        strncpy(tval, token, 72);
7165 		tval[72] = '\0';
7166 
7167 	        /*  The C language does not support a 'D'; replace with 'E' */
7168 	        if ((loc = strchr(tval, 'D'))) *loc = 'E';
7169 
7170 	        dval =  strtod(tval, &loc);
7171 	    } else {
7172 	        dval =  strtod(token, &loc);
7173  	    }
7174 
7175 	    /* check for read error, or junk following the value */
7176 	    if (*loc != '\0' && *loc != ' ' ) *isanumber = 0;
7177 	    if (errno == ERANGE) *isanumber = 0;
7178         }
7179     }
7180 
7181     return(slen);
7182 }
7183 /*--------------------------------------------------------------------------*/
fits_get_token2(char ** ptr,char * delimiter,char ** token,int * isanumber,int * status)7184 int fits_get_token2(char **ptr,
7185                    char *delimiter,
7186                    char **token,
7187                    int *isanumber,  /* O - is this token a number? */
7188 		   int *status)
7189 
7190 /*
7191    parse off the next token, delimited by a character in 'delimiter',
7192    from the input ptr string;  increment *ptr to the end of the token.
7193    Returns the length of the token, not including the delimiter char;
7194 
7195    This routine allocates the *token string;  the calling routine must free it
7196 */
7197 {
7198     char *loc, tval[73];
7199     int slen;
7200     double dval;
7201 
7202     if (*status)
7203         return(0);
7204 
7205     while (**ptr == ' ')  /* skip over leading blanks */
7206         (*ptr)++;
7207 
7208     slen = strcspn(*ptr, delimiter);  /* length of next token */
7209     if (slen)
7210     {
7211 	*token = (char *) calloc(slen + 1, 1);
7212 	if (!(*token)) {
7213           ffpmsg("Couldn't allocate memory to hold token string (fits_get_token2).");
7214           *status = MEMORY_ALLOCATION ;
7215 	  return(0);
7216         }
7217 
7218         strncat(*token, *ptr, slen);       /* copy token */
7219         (*ptr) += slen;                   /* skip over the token */
7220 
7221         if (isanumber)  /* check if token is a number */
7222         {
7223             *isanumber = 1;
7224 
7225 	    if (strchr(*token, 'D'))  {
7226 	        strncpy(tval, *token, 72);
7227 		tval[72] = '\0';
7228 
7229 	        /*  The C language does not support a 'D'; replace with 'E' */
7230 	        if ((loc = strchr(tval, 'D'))) *loc = 'E';
7231 
7232 	        dval =  strtod(tval, &loc);
7233 	    } else {
7234 	        dval =  strtod(*token, &loc);
7235  	    }
7236 
7237 	    /* check for read error, or junk following the value */
7238 	    if (*loc != '\0' && *loc != ' ' ) *isanumber = 0;
7239 	    if (errno == ERANGE) *isanumber = 0;
7240         }
7241     }
7242 
7243     return(slen);
7244 }
7245 /*---------------------------------------------------------------------------*/
fits_split_names(char * list)7246 char *fits_split_names(
7247    char *list)   /* I   - input list of names */
7248 {
7249 /*
7250    A sequence of calls to fits_split_names will split the input string
7251    into name tokens.  The string typically contains a list of file or
7252    column names.  The names must be delimited by a comma and/or spaces.
7253    This routine ignores spaces and commas that occur within parentheses,
7254    brackets, or curly brackets.  It also strips any leading and trailing
7255    blanks from the returned name.
7256 
7257    This routine is similar to the ANSI C 'strtok' function:
7258 
7259    The first call to fits_split_names has a non-null input string.
7260    It finds the first name in the string and terminates it by
7261    overwriting the next character of the string with a '\0' and returns
7262    a pointer to the name.  Each subsequent call, indicated by a NULL
7263    value of the input string, returns the next name, searching from
7264    just past the end of the previous name.  It returns NULL when no
7265    further names are found.
7266 
7267    The following line illustrates how a string would be split into 3 names:
7268     myfile[1][bin (x,y)=4], file2.fits  file3.fits
7269     ^^^^^^^^^^^^^^^^^^^^^^  ^^^^^^^^^^  ^^^^^^^^^^
7270       1st name               2nd name    3rd name
7271 
7272 
7273 NOTE:  This routine is not thread-safe.
7274 This routine is simply provided as a utility routine for other external
7275 software. It is not used by any CFITSIO routine.
7276 
7277 */
7278     int depth = 0;
7279     char *start;
7280     static char *ptr;
7281 
7282     if (list)  /* reset ptr if a string is given */
7283         ptr = list;
7284 
7285     while (*ptr == ' ')ptr++;  /* skip leading white space */
7286 
7287     if (*ptr == '\0')return(0);  /* no remaining file names */
7288 
7289     start = ptr;
7290 
7291     while (*ptr != '\0') {
7292        if ((*ptr == '[') || (*ptr == '(') || (*ptr == '{')) depth ++;
7293        else if ((*ptr == '}') || (*ptr == ')') || (*ptr == ']')) depth --;
7294        else if ((depth == 0) && (*ptr == ','  || *ptr == ' ')) {
7295           *ptr = '\0';  /* terminate the filename here */
7296           ptr++;  /* save pointer to start of next filename */
7297           break;
7298        }
7299        ptr++;
7300     }
7301 
7302     return(start);
7303 }
7304 /*--------------------------------------------------------------------------*/
urltype2driver(char * urltype,int * driver)7305 int urltype2driver(char *urltype, int *driver)
7306 /*
7307    compare input URL with list of known drivers, returning the
7308    matching driver numberL.
7309 */
7310 
7311 {
7312     int ii;
7313 
7314        /* find matching driver; search most recent drivers first */
7315 
7316     for (ii=no_of_drivers - 1; ii >= 0; ii--)
7317     {
7318         if (0 == strcmp(driverTable[ii].prefix, urltype))
7319         {
7320              *driver = ii;
7321              return(0);
7322         }
7323     }
7324 
7325     return(NO_MATCHING_DRIVER);
7326 }
7327 /*--------------------------------------------------------------------------*/
ffclos(fitsfile * fptr,int * status)7328 int ffclos(fitsfile *fptr,      /* I - FITS file pointer */
7329            int *status)         /* IO - error status     */
7330 /*
7331   close the FITS file by completing the current HDU, flushing it to disk,
7332   then calling the system dependent routine to physically close the FITS file
7333 */
7334 {
7335     int tstatus = NO_CLOSE_ERROR, zerostatus = 0;
7336 
7337     if (!fptr)
7338         return(*status = NULL_INPUT_PTR);
7339     else if ((fptr->Fptr)->validcode != VALIDSTRUC) /* check for magic value */
7340         return(*status = BAD_FILEPTR);
7341 
7342     /* close and flush the current HDU */
7343     if (*status > 0)
7344        ffchdu(fptr, &tstatus);  /* turn off the error message from ffchdu */
7345     else
7346        ffchdu(fptr, status);
7347 
7348     ((fptr->Fptr)->open_count)--;           /* decrement usage counter */
7349 
7350     if ((fptr->Fptr)->open_count == 0)  /* if no other files use structure */
7351     {
7352         ffflsh(fptr, TRUE, status);   /* flush and disassociate IO buffers */
7353 
7354         /* call driver function to actually close the file */
7355         if ((*driverTable[(fptr->Fptr)->driver].close)((fptr->Fptr)->filehandle))
7356         {
7357             if (*status <= 0)
7358             {
7359               *status = FILE_NOT_CLOSED;  /* report if no previous error */
7360 
7361               ffpmsg("failed to close the following file: (ffclos)");
7362               ffpmsg((fptr->Fptr)->filename);
7363             }
7364         }
7365 
7366         fits_clear_Fptr( fptr->Fptr, status);  /* clear Fptr address */
7367         free((fptr->Fptr)->iobuffer);    /* free memory for I/O buffers */
7368         free((fptr->Fptr)->headstart);    /* free memory for headstart array */
7369         free((fptr->Fptr)->filename);     /* free memory for the filename */
7370         (fptr->Fptr)->filename = 0;
7371         (fptr->Fptr)->validcode = 0; /* magic value to indicate invalid fptr */
7372         free(fptr->Fptr);         /* free memory for the FITS file structure */
7373         free(fptr);               /* free memory for the FITS file structure */
7374     }
7375     else
7376     {
7377         /*
7378            to minimize the fallout from any previous error (e.g., trying to
7379            open a non-existent extension in a already opened file),
7380            always call ffflsh with status = 0.
7381         */
7382         /* just flush the buffers, don't disassociate them */
7383         if (*status > 0)
7384             ffflsh(fptr, FALSE, &zerostatus);
7385         else
7386             ffflsh(fptr, FALSE, status);
7387 
7388         free(fptr);               /* free memory for the FITS file structure */
7389     }
7390 
7391     return(*status);
7392 }
7393 /*--------------------------------------------------------------------------*/
ffdelt(fitsfile * fptr,int * status)7394 int ffdelt(fitsfile *fptr,      /* I - FITS file pointer */
7395            int *status)         /* IO - error status     */
7396 /*
7397   close and DELETE the FITS file.
7398 */
7399 {
7400     char *basename;
7401     int slen, tstatus = NO_CLOSE_ERROR, zerostatus = 0;
7402 
7403     if (!fptr)
7404         return(*status = NULL_INPUT_PTR);
7405     else if ((fptr->Fptr)->validcode != VALIDSTRUC) /* check for magic value */
7406         return(*status = BAD_FILEPTR);
7407 
7408     if (*status > 0)
7409        ffchdu(fptr, &tstatus);  /* turn off the error message from ffchdu */
7410     else
7411         ffchdu(fptr, status);
7412 
7413     ffflsh(fptr, TRUE, status);     /* flush and disassociate IO buffers */
7414 
7415         /* call driver function to actually close the file */
7416     if ( (*driverTable[(fptr->Fptr)->driver].close)((fptr->Fptr)->filehandle) )
7417     {
7418         if (*status <= 0)
7419         {
7420             *status = FILE_NOT_CLOSED;  /* report error if no previous error */
7421 
7422             ffpmsg("failed to close the following file: (ffdelt)");
7423             ffpmsg((fptr->Fptr)->filename);
7424         }
7425     }
7426 
7427     /* call driver function to actually delete the file */
7428     if ( (driverTable[(fptr->Fptr)->driver].remove) )
7429     {
7430         /* parse the input URL to get the base filename */
7431         slen = strlen((fptr->Fptr)->filename);
7432         basename = (char *) malloc(slen +1);
7433         if (!basename)
7434             return(*status = MEMORY_ALLOCATION);
7435 
7436         fits_parse_input_url((fptr->Fptr)->filename, NULL, basename, NULL, NULL, NULL, NULL,
7437                NULL, &zerostatus);
7438 
7439        if ((*driverTable[(fptr->Fptr)->driver].remove)(basename))
7440         {
7441             ffpmsg("failed to delete the following file: (ffdelt)");
7442             ffpmsg((fptr->Fptr)->filename);
7443             if (!(*status))
7444                 *status = FILE_NOT_CLOSED;
7445         }
7446         free(basename);
7447     }
7448 
7449     fits_clear_Fptr( fptr->Fptr, status);  /* clear Fptr address */
7450     free((fptr->Fptr)->iobuffer);    /* free memory for I/O buffers */
7451     free((fptr->Fptr)->headstart);    /* free memory for headstart array */
7452     free((fptr->Fptr)->filename);     /* free memory for the filename */
7453     (fptr->Fptr)->filename = 0;
7454     (fptr->Fptr)->validcode = 0;      /* magic value to indicate invalid fptr */
7455     free(fptr->Fptr);              /* free memory for the FITS file structure */
7456     free(fptr);                    /* free memory for the FITS file structure */
7457 
7458     return(*status);
7459 }
7460 /*--------------------------------------------------------------------------*/
fftrun(fitsfile * fptr,LONGLONG filesize,int * status)7461 int fftrun( fitsfile *fptr,    /* I - FITS file pointer           */
7462              LONGLONG filesize,   /* I - size to truncate the file   */
7463              int *status)      /* O - error status                */
7464 /*
7465   low level routine to truncate a file to a new smaller size.
7466 */
7467 {
7468   if (driverTable[(fptr->Fptr)->driver].truncate)
7469   {
7470     ffflsh(fptr, FALSE, status);  /* flush all the buffers first */
7471     (fptr->Fptr)->filesize = filesize;
7472     (fptr->Fptr)->io_pos = filesize;
7473     (fptr->Fptr)->logfilesize = filesize;
7474     (fptr->Fptr)->bytepos = filesize;
7475     ffbfeof(fptr, status);   /* eliminate any buffers beyond current EOF */
7476     return (*status =
7477      (*driverTable[(fptr->Fptr)->driver].truncate)((fptr->Fptr)->filehandle,
7478      filesize) );
7479   }
7480   else
7481     return(*status);
7482 }
7483 /*--------------------------------------------------------------------------*/
ffflushx(FITSfile * fptr)7484 int ffflushx( FITSfile *fptr)     /* I - FITS file pointer                  */
7485 /*
7486   low level routine to flush internal file buffers to the file.
7487 */
7488 {
7489     if (driverTable[fptr->driver].flush)
7490         return ( (*driverTable[fptr->driver].flush)(fptr->filehandle) );
7491     else
7492         return(0);    /* no flush function defined for this driver */
7493 }
7494 /*--------------------------------------------------------------------------*/
ffseek(FITSfile * fptr,LONGLONG position)7495 int ffseek( FITSfile *fptr,   /* I - FITS file pointer              */
7496             LONGLONG position)   /* I - byte position to seek to       */
7497 /*
7498   low level routine to seek to a position in a file.
7499 */
7500 {
7501     return( (*driverTable[fptr->driver].seek)(fptr->filehandle, position) );
7502 }
7503 /*--------------------------------------------------------------------------*/
ffwrite(FITSfile * fptr,long nbytes,void * buffer,int * status)7504 int ffwrite( FITSfile *fptr,   /* I - FITS file pointer              */
7505              long nbytes,      /* I - number of bytes to write       */
7506              void *buffer,     /* I - buffer to write                */
7507              int *status)      /* O - error status                   */
7508 /*
7509   low level routine to write bytes to a file.
7510 */
7511 {
7512     if ( (*driverTable[fptr->driver].write)(fptr->filehandle, buffer, nbytes) )
7513     {
7514         ffpmsg("Error writing data buffer to file:");
7515 	ffpmsg(fptr->filename);
7516 
7517         *status = WRITE_ERROR;
7518     }
7519     return(*status);
7520 }
7521 /*--------------------------------------------------------------------------*/
ffread(FITSfile * fptr,long nbytes,void * buffer,int * status)7522 int ffread( FITSfile *fptr,   /* I - FITS file pointer              */
7523             long nbytes,      /* I - number of bytes to read        */
7524             void *buffer,     /* O - buffer to read into            */
7525             int *status)      /* O - error status                   */
7526 /*
7527   low level routine to read bytes from a file.
7528 */
7529 {
7530     int readstatus;
7531 
7532     readstatus = (*driverTable[fptr->driver].read)(fptr->filehandle,
7533         buffer, nbytes);
7534 
7535     if (readstatus == END_OF_FILE)
7536         *status = END_OF_FILE;
7537     else if (readstatus > 0)
7538     {
7539         ffpmsg("Error reading data buffer from file:");
7540 	ffpmsg(fptr->filename);
7541 
7542         *status = READ_ERROR;
7543     }
7544 
7545     return(*status);
7546 }
7547 /*--------------------------------------------------------------------------*/
fftplt(fitsfile ** fptr,const char * filename,const char * tempname,int * status)7548 int fftplt(fitsfile **fptr,      /* O - FITS file pointer                   */
7549            const char *filename, /* I - name of file to create              */
7550            const char *tempname, /* I - name of template file               */
7551            int *status)          /* IO - error status                       */
7552 /*
7553   Create and initialize a new FITS file  based on a template file.
7554   Uses C fopen and fgets functions.
7555 */
7556 {
7557     *fptr = 0;              /* initialize null file pointer, */
7558                             /* regardless of the value of *status */
7559     if (*status > 0)
7560         return(*status);
7561 
7562     if ( ffinit(fptr, filename, status) )  /* create empty file */
7563         return(*status);
7564 
7565     ffoptplt(*fptr, tempname, status);  /* open and use template */
7566 
7567     return(*status);
7568 }
7569 /*--------------------------------------------------------------------------*/
ffoptplt(fitsfile * fptr,const char * tempname,int * status)7570 int ffoptplt(fitsfile *fptr,      /* O - FITS file pointer                   */
7571             const char *tempname, /* I - name of template file               */
7572             int *status)          /* IO - error status                       */
7573 /*
7574   open template file and use it to create new file
7575 */
7576 {
7577     fitsfile *tptr;
7578     int tstatus = 0, nkeys, nadd, ii;
7579     char card[FLEN_CARD];
7580 
7581     if (*status > 0)
7582         return(*status);
7583 
7584     if (tempname == NULL || *tempname == '\0')     /* no template file? */
7585         return(*status);
7586 
7587     /* try opening template */
7588     ffopen(&tptr, (char *) tempname, READONLY, &tstatus);
7589 
7590     if (tstatus)  /* not a FITS file, so treat it as an ASCII template */
7591     {
7592         ffxmsg(2, card);  /* clear the  error message */
7593         fits_execute_template(fptr, (char *) tempname, status);
7594 
7595         ffmahd(fptr, 1, 0, status);   /* move back to the primary array */
7596         return(*status);
7597     }
7598     else  /* template is a valid FITS file */
7599     {
7600         ffmahd(tptr, 1, NULL, status); /* make sure we are at the beginning */
7601         while (*status <= 0)
7602         {
7603            ffghsp(tptr, &nkeys, &nadd, status); /* get no. of keywords */
7604 
7605            for (ii = 1; ii <= nkeys; ii++)   /* copy keywords */
7606            {
7607               ffgrec(tptr,  ii, card, status);
7608 
7609               /* must reset the PCOUNT keyword to zero in the new output file */
7610               if (strncmp(card, "PCOUNT  ",8) == 0) { /* the PCOUNT keyword? */
7611 	         if (strncmp(card+25, "    0", 5)) {  /* non-zero value? */
7612 		    strncpy(card, "PCOUNT  =                    0", 30);
7613 		 }
7614 	      }
7615 
7616               ffprec(fptr, card, status);
7617            }
7618 
7619            ffmrhd(tptr, 1, 0, status); /* move to next HDU until error */
7620            ffcrhd(fptr, status);  /* create empty new HDU in output file */
7621         }
7622 
7623         if (*status == END_OF_FILE)
7624         {
7625            *status = 0;              /* expected error condition */
7626         }
7627         ffclos(tptr, status);       /* close the template file */
7628     }
7629 
7630     ffmahd(fptr, 1, 0, status);   /* move to the primary array */
7631     return(*status);
7632 }
7633 /*--------------------------------------------------------------------------*/
ffrprt(FILE * stream,int status)7634 void ffrprt( FILE *stream, int status)
7635 /*
7636    Print out report of cfitsio error status and messages on the error stack.
7637    Uses C FILE stream.
7638 */
7639 {
7640     char status_str[FLEN_STATUS], errmsg[FLEN_ERRMSG];
7641 
7642     if (status)
7643     {
7644 
7645       fits_get_errstatus(status, status_str);  /* get the error description */
7646       fprintf(stream, "\nFITSIO status = %d: %s\n", status, status_str);
7647 
7648       while ( fits_read_errmsg(errmsg) )  /* get error stack messages */
7649              fprintf(stream, "%s\n", errmsg);
7650     }
7651     return;
7652 }
7653 /*--------------------------------------------------------------------------*/
pixel_filter_helper(fitsfile ** fptr,char * outfile,char * expr,int * status)7654 int pixel_filter_helper(
7655            fitsfile **fptr,  /* IO - pointer to input image; on output it  */
7656                              /*      points to the new image */
7657            char *outfile,    /* I - name for output file        */
7658            char *expr,       /* I - Image filter expression    */
7659            int *status)
7660 {
7661 	PixelFilter filter = { 0 };
7662 	char * DEFAULT_TAG = "X";
7663 	int ii, hdunum;
7664         int singleHDU = 0;
7665 
7666 	filter.count = 1;
7667 	filter.ifptr = fptr;
7668 	filter.tag = &DEFAULT_TAG;
7669 
7670     /* create new empty file for result */
7671     if (ffinit(&filter.ofptr, outfile, status) > 0)
7672     {
7673         ffpmsg("failed to create output file for pixel filter:");
7674         ffpmsg(outfile);
7675         return(*status);
7676     }
7677 
7678     fits_get_hdu_num(*fptr, &hdunum);  /* current HDU number in input file */
7679 
7680     expr += 3; /* skip 'pix' */
7681     switch (expr[0]) {
7682        case 'b':
7683        case 'B': filter.bitpix = BYTE_IMG; break;
7684        case 'i':
7685        case 'I': filter.bitpix = SHORT_IMG; break;
7686        case 'j':
7687        case 'J': filter.bitpix = LONG_IMG; break;
7688        case 'r':
7689        case 'R': filter.bitpix = FLOAT_IMG; break;
7690        case 'd':
7691        case 'D': filter.bitpix = DOUBLE_IMG; break;
7692     }
7693     if (filter.bitpix) /* skip bitpix indicator */
7694        ++expr;
7695 
7696     if (*expr == '1') {
7697        ++expr;
7698        singleHDU = 1;
7699     }
7700 
7701     if (((*fptr)->Fptr)->only_one)
7702        singleHDU = 1;
7703 
7704     if (*expr != ' ') {
7705        ffpmsg("pixel filtering expression not space separated:");
7706        ffpmsg(expr);
7707     }
7708     while (*expr == ' ')
7709        ++expr;
7710 
7711     /* copy all preceding extensions to the output file */
7712     for (ii = 1; !singleHDU && ii < hdunum; ii++)
7713     {
7714         fits_movabs_hdu(*fptr, ii, NULL, status);
7715         if (fits_copy_hdu(*fptr, filter.ofptr, 0, status) > 0)
7716         {
7717             ffclos(filter.ofptr, status);
7718             return(*status);
7719         }
7720     }
7721 
7722     /* move back to the original HDU position */
7723     fits_movabs_hdu(*fptr, hdunum, NULL, status);
7724 
7725 	filter.expression = expr;
7726     if (fits_pixel_filter(&filter, status)) {
7727         ffpmsg("failed to execute image filter:");
7728         ffpmsg(expr);
7729         ffclos(filter.ofptr, status);
7730         return(*status);
7731     }
7732 
7733 
7734     /* copy any remaining HDUs to the output file */
7735 
7736     for (ii = hdunum + 1; !singleHDU; ii++)
7737     {
7738         if (fits_movabs_hdu(*fptr, ii, NULL, status) > 0)
7739             break;
7740 
7741         fits_copy_hdu(*fptr, filter.ofptr, 0, status);
7742     }
7743 
7744     if (*status == END_OF_FILE)
7745         *status = 0;              /* got the expected EOF error; reset = 0  */
7746     else if (*status > 0)
7747     {
7748         ffclos(filter.ofptr, status);
7749         return(*status);
7750     }
7751 
7752     /* close the original file and return ptr to the new image */
7753     ffclos(*fptr, status);
7754 
7755     *fptr = filter.ofptr; /* reset the pointer to the new table */
7756 
7757     /* move back to the image subsection */
7758     if (ii - 1 != hdunum)
7759         fits_movabs_hdu(*fptr, hdunum, NULL, status);
7760 
7761     return(*status);
7762 }
7763 
7764 /*-------------------------------------------------------------------*/
ffihtps(void)7765 int ffihtps(void)
7766 {
7767    /* Wrapper function for global initialization of curl library.
7768       This is NOT THREAD-SAFE */
7769    int status=0;
7770 #ifdef CFITSIO_HAVE_CURL
7771    if (curl_global_init(CURL_GLOBAL_ALL))
7772       /* Do we want to define a new CFITSIO error code for this? */
7773       status = -1;
7774 #endif
7775    return status;
7776 }
7777 
7778 /*-------------------------------------------------------------------*/
ffchtps(void)7779 int ffchtps(void)
7780 {
7781    /* Wrapper function for global cleanup of curl library.
7782       This is NOT THREAD-SAFE */
7783 #ifdef CFITSIO_HAVE_CURL
7784    curl_global_cleanup();
7785 #endif
7786    return 0;
7787 }
7788 
7789 /*-------------------------------------------------------------------*/
ffvhtps(int flag)7790 void ffvhtps(int flag)
7791 {
7792    /* Turn libcurl's verbose output on (1) or off (0).
7793       This is NOT THREAD-SAFE */
7794 #ifdef HAVE_NET_SERVICES
7795 
7796    https_set_verbose(flag);
7797 #endif
7798 }
7799 
7800 /*-------------------------------------------------------------------*/
ffshdwn(int flag)7801 void ffshdwn(int flag)
7802 {
7803    /* Display download status bar (to stderr), where applicable.
7804       This is NOT THREAD-SAFE */
7805 #ifdef HAVE_NET_SERVICES
7806    fits_dwnld_prog_bar(flag);
7807 #endif
7808 }
7809 
7810 /*-------------------------------------------------------------------*/
ffgtmo(void)7811 int ffgtmo(void)
7812 {
7813    int timeout=0;
7814 #ifdef HAVE_NET_SERVICES
7815    timeout = fits_net_timeout(-1);
7816 #endif
7817    return timeout;
7818 }
7819 
7820 /*-------------------------------------------------------------------*/
ffstmo(int sec,int * status)7821 int ffstmo(int sec, int *status)
7822 {
7823    if (*status > 0)
7824       return (*status);
7825 
7826 #ifdef HAVE_NET_SERVICES
7827    if (sec <= 0)
7828    {
7829       *status = BAD_NETTIMEOUT;
7830       ffpmsg("Bad value for net timeout setting (fits_set_timeout).");
7831       return(*status);
7832    }
7833    fits_net_timeout(sec);
7834 #endif
7835    return(*status);
7836 }
7837