/*-------------------------------------------------------------------------*/ /** @file simple.c @author N. Devillard @date Jan 1999 @version $Revision: 1.5 $ @brief Simple FITS access routines. This module offers a number of very basic low-level FITS access routines. */ /*--------------------------------------------------------------------------*/ /* $Id: simple.c,v 1.5 2002/03/05 16:32:02 ndevilla Exp $ $Author: ndevilla $ $Date: 2002/03/05 16:32:02 $ $Revision: 1.5 $ */ /*--------------------------------------------------------------------------- Includes ---------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include "simple.h" #include "fits_p.h" #include "expkey.h" #include "cache.h" #include "fits_std.h" #include "xmemory.h" /*--------------------------------------------------------------------------- Global variables ---------------------------------------------------------------------------*/ /* * The following global variables are only used for regular expression * matching of integers and floats. These definitions are private to * this module. */ /** A regular expression matching a floating-point number */ static char regex_float[] = "^[+-]?([0-9]+[.]?[0-9]*|[.][0-9]+)([eE][+-]?[0-9]+)?$"; /** A regular expression matching an integer */ static char regex_int[] = "^[+-]?[0-9]+$"; /** A regular expression matching a complex number (int or float) */ static char regex_cmp[] = "^[+-]?([0-9]+[.]?[0-9]*|[.][0-9]+)([eE][+-]?[0-9]+)?[ ]+[+-]?([0-9]+[.]?[0-9]*|[.][0-9]+)([eE][+-]?[0-9]+)?$"; /*--------------------------------------------------------------------------- Function codes ---------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/ /** @brief Retrieve the value of a key in a FITS header @param filename Name of the FITS file to browse @param keyword Name of the keyword to find @return pointer to statically allocated character string Provide the name of a FITS file and a keyword to look for. The input file is memory-mapped and the first keyword matching the requested one is located. The value corresponding to this keyword is copied to a statically allocated area, so do not modify it or free it. The input keyword is first converted to upper case and expanded to the HIERARCH scheme if given in the shortFITS notation. This function is pretty fast due to the mmapping. Due to buffering on most Unixes, it is possible to call many times this function in a row on the same file and do not suffer too much from performance problems. If the file contents are already in the cache, the file will not be re-opened every time. It is possible, though, to modify this function to perform several searches in a row. See the source code. Returns NULL in case the requested keyword cannot be found. */ /*--------------------------------------------------------------------------*/ char * qfits_query_hdr(char * filename, char * keyword) { char * where ; char * start ; char * value ; char test1, test2 ; int i ; int len ; int different ; struct stat fileinfo ; long int bufcount; char * exp_key ; /* Bulletproof entries */ if (filename==NULL || keyword==NULL) return NULL ; /* Switch to uppercase and switch shortFITS notation to HIERARCH */ exp_key = qfits_expand_keyword(keyword); start = falloc(filename, 0, NULL) ; if (start==NULL) return NULL ; stat(filename, &fileinfo); bufcount=0; where = start ; len = (int)strlen(exp_key) ; while (1) { different=0 ; for (i=0 ; i=fileinfo.st_size){ /* file damaged or not a fits file, avoid segmentation * fault */ free(start); return NULL ; } } /* Found the keyword, now get its value */ value = qfits_getvalue(where); free(start); return value; } /*-------------------------------------------------------------------------*/ /** @brief Retrieve the value of a keyin a FITS extension header. @param filename name of the FITS file to browse. @param keyword name of the FITS key to look for. @param xtnum xtension number @return pointer to statically allocated character string Same as qfits_query_hdr but for extensions. xtnum starts from 1 to the number of extensions. If the required extension is 0, this function calls qfits_query_hdr(). */ /*--------------------------------------------------------------------------*/ char * qfits_query_ext(char * filename, char * keyword, int xtnum) { char * exp_key ; char * where ; char * start ; char * value ; char test1, test2 ; int seg_start ; int seg_size ; if (xtnum==0) return qfits_query_hdr(filename, keyword); /* Bulletproof entries */ if (filename==NULL || keyword==NULL) return NULL ; /* Expand keyword */ exp_key = qfits_expand_keyword(keyword); /* * Find out offsets to the required extension * Record the xtension start and stop offsets */ if (qfits_get_hdrinfo(filename, xtnum, &seg_start, &seg_size)==-1) { return NULL ; } /* * Memory-map the input file and jump to start of xtension */ start = falloc(filename, seg_start, NULL); if (start==NULL) return NULL ; /* * Look for keyword in xtension */ while (1) { where = strstr(start, exp_key); if (where==NULL) { free(start); return NULL ; } if ((int)(where - start) > seg_size) { free(start); return NULL ; } test1 = where[(int)strlen(exp_key)] ; test2 = where[(int)strlen(exp_key)+1] ; /* If first subsequent character is the equal sign, found it */ if (test1=='=') break ; /* * If first subsequent character is a blank and the one after is * equal or another blank, found it */ if (test1==' ' && (test2=='=' || test2==' ')) break ; /* Not found */ start = where+1 ; } /* Found the keyword, now get its value */ value = qfits_getvalue(where); free(start); return value; } /*-------------------------------------------------------------------------*/ /** @brief Counts the number of extensions in a FITS file @param filename Name of the FITS file to browse. @return int Counts how many extensions are in the file. Returns 0 if no extension is found, and -1 if an error occurred. */ /*--------------------------------------------------------------------------*/ int qfits_query_n_ext(char * filename) { return qfits_query(filename, QFITS_QUERY_N_EXT); } /*-------------------------------------------------------------------------*/ /** @brief Clean out a FITS string value. @param s pointer to allocated FITS value string. @return pointer to statically allocated character string From a string FITS value like 'marvin o''hara', remove head and tail quotes, replace double '' with simple ', trim blanks on each side, and return the result in a statically allocated area. Examples: - ['o''hara'] becomes [o'hara] - [' H '] becomes [H] - ['1.0 '] becomes [1.0] */ /*--------------------------------------------------------------------------*/ #define PRETTY_STRING_STATICBUFS 4 char * qfits_pretty_string(char * s) { static char pretty_buf[PRETTY_STRING_STATICBUFS][81] ; static int flip=0 ; char * pretty ; int i,j ; /* bulletproof */ if (s==NULL) return NULL ; /* Switch between static buffers */ pretty = pretty_buf[flip]; flip++ ; if (flip==PRETTY_STRING_STATICBUFS) flip=0 ; pretty[0] = (char)0 ; if (s[0]!='\'') return s ; /* skip first quote */ i=1 ; j=0 ; /* trim left-side blanks */ while (s[i]==' ') { if (i==(int)strlen(s)) break ; i++ ; } if (i>=(int)(strlen(s)-1)) return pretty ; /* copy string, changing double quotes to single ones */ while (i<(int)strlen(s)) { if (s[i]=='\'') { i++ ; } pretty[j]=s[i]; i++ ; j++ ; } /* NULL-terminate the pretty string */ pretty[j+1]=(char)0; /* trim right-side blanks */ j = (int)strlen(pretty)-1; while (pretty[j]==' ') j-- ; pretty[j+1]=(char)0; return pretty; } #undef PRETTY_STRING_STATICBUFS /*-------------------------------------------------------------------------*/ /** @brief Identify if a FITS value is boolean @param s FITS value as a string @return int 0 or 1 Identifies if a FITS value is boolean. */ /*--------------------------------------------------------------------------*/ int qfits_is_boolean(char * s) { if (s==NULL) return 0 ; if (s[0]==0) return 0 ; if ((int)strlen(s)>1) return 0 ; if (s[0]=='T' || s[0]=='F') return 1 ; return 0 ; } /*-------------------------------------------------------------------------*/ /** @brief Identify if a FITS value is an int. @param s FITS value as a string @return int 0 or 1 Identifies if a FITS value is an integer. */ /*--------------------------------------------------------------------------*/ int qfits_is_int(char * s) { static int first=1; static regex_t re_int; if (s==NULL) return 0 ; if (s[0]==0) return 0 ; /* Compile the rule for integer pattern matching the first time */ if (first) { first=0; if (regcomp(&re_int, ®ex_int[0], REG_EXTENDED|REG_NOSUB)!=0) { fprintf(stderr, "internal error: compiling int rule\n"); exit(-1); } } return regexec(&re_int, s, 0, NULL, 0) ? 0 : 1 ; } /*-------------------------------------------------------------------------*/ /** @brief Identify if a FITS value is float. @param s FITS value as a string @return int 0 or 1 Identifies if a FITS value is float. */ /*--------------------------------------------------------------------------*/ int qfits_is_float(char * s) { static int first=1; static regex_t re_float; if (s==NULL) return 0 ; if (s[0]==0) return 0 ; /* Compile the rule for float pattern matching the first time */ if (first) { first=0; if (regcomp(&re_float, ®ex_float[0], REG_EXTENDED|REG_NOSUB)!=0) { fprintf(stderr, "internal error: compiling float rule\n"); exit(-1); } } return regexec(&re_float, s, 0, NULL, 0) ? 0 : 1 ; } /*-------------------------------------------------------------------------*/ /** @brief Identify if a FITS value is complex. @param s FITS value as a string @return int 0 or 1 Identifies if a FITS value is complex. */ /*--------------------------------------------------------------------------*/ int qfits_is_complex(char * s) { static int first=1; static regex_t re_cmp; if (s==NULL) return 0 ; if (s[0]==0) return 0 ; /* Compile the rule for complex pattern matching the first time */ if (first) { first=0; if (regcomp(&re_cmp, ®ex_cmp[0], REG_EXTENDED|REG_NOSUB)!=0) { fprintf(stderr, "internal error: compiling complex rule\n"); exit(-1); } } return regexec(&re_cmp, s, 0, NULL, 0) ? 0 : 1 ; } /*-------------------------------------------------------------------------*/ /** @brief Identify if a FITS value is string. @param s FITS value as a string @return int 0 or 1 Identifies if a FITS value is a string. */ /*--------------------------------------------------------------------------*/ int qfits_is_string(char * s) { if (s==NULL) return 0 ; if (s[0]=='\'') return 1 ; return 0 ; } /*-------------------------------------------------------------------------*/ /** @brief Identify the type of a FITS value given as a string. @param s FITS value as a string @return integer naming the FITS type Returns the following value: - QFITS_UNKNOWN (0) for an unknown type. - QFITS_BOOLEAN (1) for a boolean type. - QFITS_INT (2) for an integer type. - QFITS_FLOAT (3) for a floating-point type. - QFITS_COMPLEX (4) for a complex number. - QFITS_STRING (5) for a FITS string. */ /*--------------------------------------------------------------------------*/ int qfits_get_type(char * s) { if (qfits_is_boolean(s)) return QFITS_BOOLEAN ; if (qfits_is_int(s)) return QFITS_INT ; if (qfits_is_float(s)) return QFITS_FLOAT ; if (qfits_is_complex(s)) return QFITS_COMPLEX ; if (qfits_is_string(s)) return QFITS_STRING ; return QFITS_UNKNOWN ; } /*-------------------------------------------------------------------------*/ /** @brief Retrieve offset to start and size of a header in a FITS file. @param filename Name of the file to examine @param xtnum Extension number (0 for main) @param seg_start Segment start in bytes (output) @param seg_size Segment size in bytes (output) @return int 0 if Ok, -1 otherwise. This function retrieves the two most important informations about a header in a FITS file: the offset to its beginning, and the size of the header in bytes. Both values are returned in the passed pointers to ints. It is Ok to pass NULL for any pointer if you do not want to retrieve the associated value. You must provide an extension number for the header, 0 meaning the main header in the file. */ /*--------------------------------------------------------------------------*/ int qfits_get_hdrinfo( char * filename, int xtnum, int * seg_start, int * seg_size ) { if (filename==NULL || xtnum<0 || (seg_start==NULL && seg_size==NULL)) { return -1 ; } if (seg_start!=NULL) { *seg_start = qfits_query(filename, QFITS_QUERY_HDR_START | xtnum); if (*seg_start<0) return -1 ; } if (seg_size!=NULL) { *seg_size = qfits_query(filename, QFITS_QUERY_HDR_SIZE | xtnum); if (*seg_size<0) return -1 ; } return 0 ; } /*-------------------------------------------------------------------------*/ /** @brief Retrieve offset to start and size of a data section in a file. @param filename Name of the file to examine. @param xtnum Extension number (0 for main). @param seg_start Segment start in bytes (output). @param seg_size Segment size in bytes (output). @return int 0 if Ok, -1 otherwise. This function retrieves the two most important informations about a data section in a FITS file: the offset to its beginning, and the size of the section in bytes. Both values are returned in the passed pointers to ints. It is Ok to pass NULL for any pointer if you do not want to retrieve the associated value. You must provide an extension number for the header, 0 meaning the main header in the file. */ /*--------------------------------------------------------------------------*/ int qfits_get_datinfo( char * filename, int xtnum, int * seg_start, int * seg_size ) { if (filename==NULL || xtnum<0 || (seg_start==NULL && seg_size==NULL)) { return -1 ; } if (seg_start!=NULL) { *seg_start = qfits_query(filename, QFITS_QUERY_DAT_START | xtnum); if (*seg_start<0) return -1 ; } if (seg_size!=NULL) { *seg_size = qfits_query(filename, QFITS_QUERY_DAT_SIZE | xtnum); if (*seg_size<0) return -1 ; } return 0 ; } /*-------------------------------------------------------------------------*/ /** @brief Retrieve the card containing a keyword in the main header. @param filename Name of the FITS file to examine. @param keyword Keyword to look for in header. @return Pointer to statically allocated character string Retrieve the whole FITS line associated to a given keyword in the main header. The returned string is statically allocated, so do not modify it or try to free it. Returns NULL if an error occurred or the keyword cannot be found. The input keyword is first converted to upper case and expanded to the HIERARCH scheme if given in the shortFITS notation. */ /*--------------------------------------------------------------------------*/ char * qfits_query_card_in_header(char * filename, char * keyword) { char * exp_key ; char * where ; char * start ; static char value[81] ; /* Bulletproof entries */ if (filename==NULL || keyword==NULL) return NULL ; exp_key = qfits_expand_keyword(keyword); start = falloc(filename, 0, NULL) ; if (start==NULL) return NULL ; while (1) { where = strstr(start, exp_key); if (where==NULL) { free(start); return NULL ; } if (((where-start)%80) == 0) /* found */ break ; /* Not found */ start = where+1 ; } /* Found the keyword, now get the corresponding FITS card */ memcpy(value, where, 80); free(start); value[80] = (char)0 ; return value; } /*-------------------------------------------------------------------------*/ /** @brief Replace a card in a FITS (main) header by a given card @param filename Name of the FITS file to modify. @param keyword Where to substitute a card in the header. @param substitute What to replace the line with. @return int 0 if Ok, -1 otherwise Replaces a whole card (80 chars) in a FITS header by a given FITS line (80 chars). The replacing line is assumed correctly formatted and containing at least 80 characters. The file is modified: it must be accessible in read/write mode. The input keyword is first converted to upper case and expanded to the HIERARCH scheme if given in the shortFITS notation. Returns 0 if everything worked Ok, -1 otherwise. */ /*--------------------------------------------------------------------------*/ int qfits_replace_card( char * filename, char * keyword, char * substitute ) { char * exp_key ; int fd ; char * buf ; char * buf2 ; char * where ; int hs ; /* Bulletproof entries */ if (filename==NULL || keyword==NULL || substitute==NULL) return -1 ; /* Expand keyword */ exp_key = qfits_expand_keyword(keyword); /* * Memory-map the FITS header of the input file */ qfits_get_hdrinfo(filename, 0, NULL, &hs) ; if (hs < 1) { fprintf(stderr, "error getting FITS header size for %s\n", filename); return -1 ; } fd = open(filename, O_RDWR) ; if (fd == -1) { return -1 ; } buf = (char*)mmap(0, hs, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0) ; if (buf == (char*)-1) { perror("mmap") ; close(fd) ; return -1 ; } /* * Apply search and replace for the input keyword lists */ buf2 = malloc(hs+1) ; memcpy(buf2, buf, hs) ; buf2[hs] = (char)0 ; where = buf2 ; do { where = strstr(where, exp_key); if (where == NULL) { close(fd); munmap(buf,hs); free(buf2) ; return -1 ; } if ((where-buf2)%80) where++ ; } while ((where-buf2)%80) ; where = buf + (int)(where - buf2) ; /* Replace current placeholder by blanks */ memset(where, ' ', 80); /* Copy substitute into placeholder */ memcpy(where, substitute, strlen(substitute)); close(fd) ; munmap(buf, hs) ; free(buf2) ; return 0 ; } /*-------------------------------------------------------------------------*/ /** @brief Replace a placeholder in a FITS header by a given card. @param filename Name of the FITS file to modify. @param placeholder Name of the placeholder to replace. @param substitute What to replace the line with. @return int 0 if Ok, -1 otherwise Similar to qfits_replace_card, but to replace placeholder in FITS (main) headers. A placeholder is defined as being something like: COMMENT %s In the search process to look for placeholders, %s is replaced with the given character string. The default placeholder to look for is simply 'PLACEHOLDER'. */ /*--------------------------------------------------------------------------*/ int qfits_replace_placeholder( char * filename, char * placeholder, char * substitute ) { char to_find[FITS_LINESZ] ; if (filename==NULL || placeholder==NULL || substitute==NULL) { return -1 ; } /* Format string to search for */ sprintf(to_find, "COMMENT %s", placeholder) ; /* Replace card */ return qfits_replace_card(filename, to_find, substitute) ; } /* vim: set ts=4 et sw=4 tw=75 */