PRO FXHMODIFY, FILENAME, NAME, VALUE, COMMENT, BEFORE=BEFORE, $ AFTER=AFTER, FORMAT=FORMAT, EXTENSION=EXTENSION, ERRMSG=ERRMSG,$ NOGROW=NOGROW, NEW_HEADER=NEW_HEADER ;+ ; NAME: ; FXHMODIFY ; PURPOSE : ; Modify a FITS header in a file on disk. ; Explanation : ; Opens a FITS file, and adds or modifies a parameter in the FITS header. ; Can be used for either the main header, or for an extension header. ; The modification is performed directly on the disk file. ; Use : ; FXHMODIFY, FILENAME, NAME, VALUE, COMMENT ; Inputs : ; FILENAME = String containing the name of the file to be read. ; ; NAME = Name of parameter, scalar string If NAME is already in the ; header the value and possibly comment fields are modified. ; Otherwise a new record is added to the header. If NAME is ; equal to either "COMMENT" or "HISTORY" then the value will be ; added to the record without replacement. In this case the ; comment parameter is ignored. ; ; VALUE = Value for parameter. The value expression must be of the ; correct type, e.g. integer, floating or string. String ; values of 'T' or 'F' are considered logical values. ; ; Opt. Inputs : ; COMMENT = String field. The '/' is added by this routine. Added ; starting in position 31. If not supplied, or set equal to '' ; (the null string), then any previous comment field in the ; header for that keyword is retained (when found). ; Outputs : ; None. ; Opt. Outputs: ; None. ; Keywords : ; EXTENSION = Either the number of the FITS extension, starting with the ; first extension after the primary data unit being one; or a ; character string containing the value of EXTNAME to search ; for. If not passed, then the primary FITS header is ; modified. ; ; BEFORE = Keyword string name. The parameter will be placed before the ; location of this keyword. For example, if BEFORE='HISTORY' ; then the parameter will be placed before the first history ; location. This applies only when adding a new keyword; ; keywords already in the header are kept in the same position. ; ; AFTER = Same as BEFORE, but the parameter will be placed after the ; location of this keyword. This keyword takes precedence over ; BEFORE. ; ; FORMAT = Specifies FORTRAN-like format for parameter, e.g. "F7.3". A ; scalar string should be used. For complex numbers the format ; should be defined so that it can be applied separately to the ; real and imaginary parts. ; ERRMSG = If defined and passed, then any error messages will be ; returned to the user in this parameter rather than ; depending on the MESSAGE routine in IDL. If no errors are ; encountered, then a null string is returned. In order to ; use this feature, ERRMSG must be defined first, e.g. ; ; ERRMSG = '' ; FXHMODIFY, ERRMSG=ERRMSG, ... ; IF ERRMSG NE '' THEN ... ; ; NEW_HEADER = If defined and passed, then ignore NAME, VALUE, ; and COMMENT. Instead replace the old file header ; with the strarr given. ; ; Calls : ; FXHREAD, FXPAR, FXADDPAR, BLKSHIFT ; Restrictions: ; This routine can not be used to modify any of the keywords that control ; the structure of the FITS file, e.g. BITPIX, NAXIS, PCOUNT, etc. Doing ; so could corrupt the readability of the FITS file. ; Example: ; Modify the name 'OBJECT' keyword in the primary FITS header of a FITS ; file 'spec98.ccd' to contain the value 'test domeflat' ; ; IDL> fxhmodify, 'spec98.ccd', 'OBJECT', 'test domeflat' ; ; Side effects: ; If adding a record to the FITS header would increase the ; number of 2880 byte records stored on disk, then the file is ; enlarged before modification, unless the NOGROW keyword is passed. ; ; Category : ; Data Handling, I/O, FITS, Generic. ; Prev. Hist. : ; None. ; Written : ; William Thompson, GSFC, 3 March 1994. ; Modified : ; Version 1, William Thompson, GSFC, 3 March 1994. ; Version 2, William Thompson, GSFC, 31 May 1994 ; Added ERRMSG keyword. ; Version 3, William Thompson, GSFC, 23 June 1994 ; Modified so that ERRMSG is not touched if not defined. ; Version 3.1 Wayne Landsman GSFC 17 March 2006 ; Fix problem in BLKSHIFT call if primary header extended ; Version 3.2 W. Landsman 14 November 204 ; Allow for need for 64bit number of bytes ; Version 4, William Thompson, GSFC, 22-Dec-2014 ; Modified test for keyword EXTEND to only issue warning. ; Version 5, Mats Löfdahl, ISP, 11-Oct-2017 ;; Version : ; Version 5, 11-Oct-2017 ;- ; COMPILE_OPT IDL2 ON_ERROR, 2 ; ; Check the number of parameters. ; IF (N_ELEMENTS(NEW_HEADER) EQ 0) && (N_PARAMS() LT 3) THEN BEGIN MESSAGE = $ ;Need at least 3 parameters 'Syntax: FXHMODIFY, FILENAME, NAME, VALUE [, COMMENT ]' IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN ERRMSG = MESSAGE RETURN END ELSE MESSAGE, MESSAGE ENDIF ; ; If passed, check the type of the EXTENSION parameter. ; IF N_ELEMENTS(EXTENSION) GT 1 THEN BEGIN MESSAGE = 'EXTENSION must be a scalar' IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN ERRMSG = MESSAGE RETURN END ELSE MESSAGE, MESSAGE END ELSE IF N_ELEMENTS(EXTENSION) EQ 1 THEN BEGIN SZ = SIZE(EXTENSION) ETYPE = SZ[SZ[0]+1] IF ETYPE EQ 8 THEN BEGIN MESSAGE = 'EXTENSION must not be a structure' IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN ERRMSG = MESSAGE RETURN END ELSE MESSAGE, MESSAGE ENDIF ; ; If EXTENSION is of type string, then search for the proper extension by ; name. Otherwise, search by number. ; IF ETYPE EQ 7 THEN BEGIN S_EXTENSION = STRTRIM(STRUPCASE(EXTENSION),2) END ELSE BEGIN I_EXTENSION = FIX(EXTENSION) IF I_EXTENSION LT 1 THEN BEGIN MESSAGE = 'EXTENSION must be greater than zero' IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN ERRMSG = MESSAGE RETURN END ELSE MESSAGE, MESSAGE ENDIF ENDELSE ENDIF ; ; Get the UNIT number, and open the file. ; OPENU, UNIT, FILENAME, /BLOCK, /GET_LUN ; ; Read in the primary FITS header. ; FXHREAD,UNIT,HEADER,STATUS IF STATUS NE 0 THEN BEGIN FREE_LUN,UNIT MESSAGE = 'Unable to read FITS header' IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN ERRMSG = MESSAGE RETURN END ELSE MESSAGE, MESSAGE ENDIF MHEAD0 = 0 I_EXT = 0 ; ; If the EXTENSION parameter was passed, then look for the requested ; extension. ; IF N_ELEMENTS(EXTENSION) EQ 1 THEN BEGIN ; ; Make sure that the file does contain extensions. However, only issue a ; warning if EXTEND keyword not set. ; IF ~FXPAR(HEADER,'EXTEND') THEN MESSAGE, /CONTINUE, $ 'Keyword EXTEND not set in file ' + FILENAME ; ; Get the number of bytes taken up by the data. ; NEXT_EXT: BITPIX = FXPAR(HEADER,'BITPIX') NAXIS = FXPAR(HEADER,'NAXIS') GCOUNT = FXPAR(HEADER,'GCOUNT') IF GCOUNT EQ 0 THEN GCOUNT = 1 PCOUNT = FXPAR(HEADER,'PCOUNT') IF NAXIS GT 0 THEN BEGIN DIMS = FXPAR(HEADER,'NAXIS*') ;Read dimensions NDATA = DIMS[0] IF NAXIS GT 1 THEN FOR I=2,NAXIS DO $ NDATA = NDATA*DIMS[I-1] ENDIF ELSE NDATA = 0 NBYTES = LONG64(ABS(BITPIX) / 8) * GCOUNT * (PCOUNT + NDATA) ; ; Read the next extension header in the file. ; NREC = (NBYTES + 2879) / 2880 POINT_LUN, -UNIT, POINTLUN ;Current position MHEAD0 = POINTLUN + NREC*2880L POINT_LUN, UNIT, MHEAD0 ;Next FITS extension FXHREAD,UNIT,HEADER,STATUS POINT_LUN, -UNIT, END_HEADER IF STATUS NE 0 THEN BEGIN FREE_LUN,UNIT MESSAGE = 'Requested extension not found' IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN ERRMSG = MESSAGE RETURN END ELSE MESSAGE, MESSAGE ENDIF I_EXT = I_EXT + 1 ; ; Check to see if the current extension is the one desired. ; IF ETYPE EQ 7 THEN BEGIN EXTNAME = STRTRIM(STRUPCASE(FXPAR(HEADER,'EXTNAME')),2) IF EXTNAME EQ S_EXTENSION THEN GOTO, DONE END ELSE IF I_EXT EQ I_EXTENSION THEN GOTO, DONE GOTO, NEXT_EXT DONE: ENDIF ELSE POINT_LUN, -UNIT, END_HEADER ; ; Add or modify the keyword parameter in the header (or replace the ; header), keeping track of the initial size of the header array. ; IEND = WHERE(STRMID(HEADER,0,8) EQ 'END ') N_INITIAL = 1 + IEND[0]/36 IF N_ELEMENTS(NEW_HEADER) EQ 0 THEN BEGIN IF N_PARAMS() EQ 4 THEN BEGIN FXADDPAR, HEADER, NAME, VALUE , COMMENT, BEFORE=BEFORE, $ AFTER=AFTER, FORMAT=FORMAT END ELSE BEGIN FXADDPAR, HEADER, NAME, VALUE, BEFORE=BEFORE, $ AFTER=AFTER, FORMAT=FORMAT ENDELSE END ELSE BEGIN HEADER = NEW_HEADER ENDELSE ; ; If the length of the header has changed, then print an error message. ; IEND = WHERE(STRMID(HEADER,0,8) EQ 'END ') N_FINAL = 1 + IEND[0]/36 IF N_FINAL NE N_INITIAL THEN BEGIN IF KEYWORD_SET(NOGROW) THEN BEGIN MESSAGE, /CONTINUE, 'Adding parameter would increase ' + $ 'header length, no action taken.' ENDIF ELSE BEGIN ;; Change size of the file by inserting/removing ;; multiples of 2880 bytes at the end of the current ;; header. Then resume normal operations. BLKSHIFT, UNIT, END_HEADER, (N_FINAL-N_INITIAL)*36L*80L GOTO, WRITE_HEADER ENDELSE ; ; Otherwise, rewind to the beginning of the header, and write the new header ; over the old header. Convert to byte and force into 80 character lines. ; ENDIF ELSE BEGIN WRITE_HEADER: BHDR = REPLICATE(32B, 80, 36*N_FINAL) FOR N = 0,IEND[0] DO BHDR[0,N] = BYTE(STRMID(HEADER[N],0,80)) POINT_LUN, UNIT, MHEAD0 WRITEU, UNIT, BHDR ENDELSE ; ; Close the file and return. ; FREE_LUN, UNIT IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = '' RETURN END