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