/* -*- Mode: C++ ; Coding: euc-japan -*- */ /* Time-stamp: <2014-05-08 03:14:18 cyamauch> */ /** * @file fits_header.cc * @brief FITS ヘッダ全体を表現するクラス fits_header のコード */ #define CLASS_NAME "fits_header" #include "config.h" #include "fits_header.h" #include "fits_hdu.h" #include "private/err_report.h" #include "private/rm_side_spaces.h" #include "private/c_memcpy.h" namespace sli { static const tstring END = "END"; #include "private/parse_a_header_record.h" #include "private/initialize_csum.h" #include "private/encode_csum.h" #include "private/write_stream_or_get_csum.h" /** * @brief システム側で書いてしまう予約キーワード * * @note 予約キーワードに '.' が入るとややこしい事になるので入れない事.
* ('HOGE DEATH' のようなキーワードが指定された時,fits_header_record * では ' ' を '.' に置き換えるため) */ static const char *Header_reserved_keys[] = { "SIMPLE", "BITPIX","NAXIS","EXTEND", "XTENSION", "PCOUNT","GCOUNT","TFIELDS","TXFLDKWD","EXTNAME","EXTVER","EXTLEVEL", "BSCALE","BZERO", #ifdef BUNIT_IS_SPECIAL "BUNIT", #endif "BLANK", "THEAP", #ifdef FMTTYPE_IS_SPECIAL "FMTTYPE","FTYPEVER", #endif NULL }; /** * @brief システム側で書いてしまう予約キーワード (末尾に数字がつくもの) */ static const char *Header_reserved_keys_with_digit[] = { "NAXIS", "TTYPE","TUNIT","TDISP","TBCOL","TFORM","TDIM", "TNULL","TZERO","TSCAL", "TELEM","TALAS", NULL }; /** * @brief システム側で予約しているキーワードかどうかを判定 (内部用) * * @note private な関数です. */ static bool is_header_reserved_key( const char *keyword ) { int j; const char **reserved_keys; tstring keywd; if ( keyword == NULL ) return false; keywd.assign(keyword).strtrim(); if ( keywd.length() == 0 ) return false; /* SIMPLE とかをチェックする */ reserved_keys = Header_reserved_keys; for ( j=0 ; reserved_keys[j] != NULL ; j++ ) { if ( keywd.strcmp(reserved_keys[j]) == 0 ) break; } if ( reserved_keys[j] != NULL ) return true; /* TTYPE99 とかをチェックする */ reserved_keys = Header_reserved_keys_with_digit; for ( j=0 ; reserved_keys[j] != NULL ; j++ ) { tstring rkeys; size_t len_reserved; len_reserved = rkeys.assign(reserved_keys[j]).length(); /* reserved_keys[j] に続く数字がない場合は,reserved としない */ if ( len_reserved < keywd.length() ) { if ( keywd.strncmp(reserved_keys[j], len_reserved) == 0 ) { size_t p0 = len_reserved; if ( keywd.strpbrk(p0, "[0-9]") == (ssize_t)p0 ) { p0 += keywd.strspn(p0, "[0-9]"); /* 最後までいってたら… */ if ( p0 == keywd.length() ) break; } } } } if ( reserved_keys[j] != NULL ) return true; return false; } /* constructor */ /** * @brief コンストラクタ */ fits_header::fits_header() { this->num_records_rec = 0; this->records_rec.init(sizeof(fits_header_record *), true); this->records_rec.register_extptr(&this->records_ptr_rec); /* [void **] */ c_memcpy(this->encoded_chksum_rec,"0000000000000000",17); this->sysrecords_prohibition_rec = false; this->suppress_dupkey_warning_rec = false; this->manager = NULL; this->shallow_copy_ok = false; this->shallow_copy_dest_obj = NULL; this->shallow_copy_src_obj = NULL; this->__copying = false; return; } /* copy constructor */ /** * @brief コピーコンストラクタ */ fits_header::fits_header(const fits_header &obj) { this->num_records_rec = 0; this->records_rec.init(sizeof(fits_header_record *), true); this->records_rec.register_extptr(&this->records_ptr_rec); /* [void **] */ c_memcpy(this->encoded_chksum_rec,"0000000000000000",17); this->sysrecords_prohibition_rec = false; this->suppress_dupkey_warning_rec = false; this->manager = NULL; this->shallow_copy_ok = false; this->shallow_copy_dest_obj = NULL; this->shallow_copy_src_obj = NULL; this->__copying = false; this->init(obj); return; } /* destructor */ /** * @brief デストラクタ */ fits_header::~fits_header() { if ( this->records_ptr_rec != NULL ) { long i; for ( i=0 ; i < this->num_records_rec ; i++ ) { if ( this->records_ptr_rec[i] != NULL ) { delete this->records_ptr_rec[i]; } } } return; } /** * @brief オブジェクトのコピー * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)を持つレコードはコピーされません. */ fits_header &fits_header::operator=(const fits_header &obj) { return this->init(obj); } /* 注意: このクラスからの this->manager->setup_sys_header() の呼び出しは */ /* 1メンバ関数につき,1度限りとなるようにしなければならない. */ /* 2回呼び出すようになっていると,無限ループに陥いる. */ /* protected */ /** * @brief オブジェクトの初期化 (低レベル) * * @note これは,this->manager->setup_sys_header() を呼び出さない.
* このクラスから init() を使いたい場合に使う.
* このメンバ関数は protected です. */ fits_header &fits_header::_init() { //err_report(__FUNCTION__,"DEBUG","this is fits_header::_init()"); /* clear! */ if ( this->records_ptr_rec != NULL ) { long i; for ( i=0 ; i < this->num_records_rec ; i++ ) { if ( this->records_ptr_rec[i] != NULL ) { delete this->records_ptr_rec[i]; } } this->records_rec.init(sizeof(fits_header_record *), true); } this->num_records_rec = 0; c_memcpy(this->encoded_chksum_rec,"0000000000000000",17); this->suppress_dupkey_warning_rec = false; this->index_rec.init(); this->formatted_rec.init(); return *this; } /** * @brief オブジェクトの初期化 */ fits_header &fits_header::init() { //err_report(__FUNCTION__,"DEBUG","this is fits_header::init()"); this->fits_header::_init(); /* 消去されても Data Unit に関連するレコードはすぐに復活させる */ if ( this->sysrecords_prohibition_rec == true && this->manager != NULL ) { /* this will call an overridden function */ //err_report(__FUNCTION__,"DEBUG","called ...... from [A]"); this->manager->setup_sys_header(); } return *this; } /** * @brief オブジェクトのコピー * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)を持つレコードはコピーされません. */ fits_header &fits_header::init(const fits_header &obj) { if ( &obj == this ) return *this; //err_report(__FUNCTION__,"DEBUG","this is fits_header::init(...)"); this->fits_header::_init(); if ( obj.records_ptr_rec != NULL ) { long i; try { this->records_rec.resize( obj.num_records_rec + 1 ).clean(); } catch (...) { err_throw(__FUNCTION__,"FATAL","records_rec.resize() failed"); } try { for ( i=0 ; i < obj.num_records_rec ; i++ ) { this->records_ptr_rec[i] = new fits_header_record; /* これでうまくいくのか??? */ *(this->records_ptr_rec[i]) = (*(obj.records_ptr_rec[i])); this->records_ptr_rec[i]->register_manager(this); } this->records_ptr_rec[i] = NULL; } catch (...) { for ( i=0 ; i < obj.num_records_rec ; i++ ) { if ( this->records_ptr_rec[i] != NULL ) delete this->records_ptr_rec[i]; } this->records_rec.init(sizeof(fits_header_record *), true); err_throw(__FUNCTION__,"FATAL","'new' or '=' failed"); } this->num_records_rec = obj.num_records_rec; } try { this->index_rec = obj.index_rec; } catch (...) { this->fits_header::_init(); err_throw(__FUNCTION__,"FATAL","'=' failed"); } this->suppress_dupkey_warning_rec = obj.suppress_dupkey_warning_rec; /* システムヘッダが禁止されている場合,あれば削除する */ if ( this->sysrecords_prohibition_rec == true ) { long i; for ( i = 0 ; i < this->length() ; i++ ) { if ( this->is_sysrecord(i) == true ) { //err_report1(__FUNCTION__,"WARNING", // "keyword '%s' cannot be appended by the user", // this->record_cs(i).keyword()); this->fits_header::_erase_records(i,1); i--; } } } /* 消去されても Data Unit に関連するレコードはすぐに復活させる */ if ( this->sysrecords_prohibition_rec == true && this->manager != NULL ) { /* this will call an overridden function */ //err_report(__FUNCTION__,"DEBUG","called ...... from [B]"); this->manager->setup_sys_header(); } //try { // this->formatted_rec = obj.formatted_rec; //} //catch (...) { // err_throw(__FUNCTION__,"FATAL","'=' failed"); //} return *this; } /** * @brief オブジェクトの初期化 (fits::header_def 構造体で指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::init( const fits::header_def defs[] ) { this->fits_header::_init(); if ( defs != NULL ) { this->append_records(defs); } /* 消去されても Data Unit に関連するレコードはすぐに復活させる */ if ( this->sysrecords_prohibition_rec == true && this->manager != NULL ) { /* this will call an overridden function */ //err_report(__FUNCTION__,"DEBUG","called ...... from [C]"); this->manager->setup_sys_header(); } return *this; } /** * @brief 2つのオブジェクト間での内容のスワップ * * 指定されたオブジェクト obj の内容と自身の内容を入れ替えます. * * @param obj fits_header クラスのオブジェクト * @return 自身の参照 * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)を持つレコードは入れ替わりません. */ /* 注: chksum はここでは扱わない */ fits_header &fits_header::swap( fits_header &obj ) { long tmp_num_records_rec = obj.num_records_rec; bool tmp_suppress_dupkey_warning_rec = obj.suppress_dupkey_warning_rec; if ( &obj == this ) return *this; obj.num_records_rec = this->num_records_rec; this->num_records_rec = tmp_num_records_rec; this->records_rec.swap(obj.records_rec); this->index_rec.swap(obj.index_rec); this->formatted_rec.swap(obj.formatted_rec); obj.suppress_dupkey_warning_rec = this->suppress_dupkey_warning_rec; this->suppress_dupkey_warning_rec = tmp_suppress_dupkey_warning_rec; /* システムヘッダが禁止されている場合,あれば削除する */ if ( this->sysrecords_prohibition_rec == true ) { long i; for ( i = 0 ; i < this->length() ; i++ ) { if ( this->is_sysrecord(i) == true ) { //err_report1(__FUNCTION__,"WARNING", // "keyword '%s' cannot be appended by the user", // this->record_cs(i).keyword()); this->fits_header::_erase_records(i,1); i--; } } } if ( obj.sysrecords_prohibition_rec == true ) { long i; for ( i = 0 ; i < obj.length() ; i++ ) { if ( obj.is_sysrecord(i) == true ) { //err_report1(__FUNCTION__,"WARNING", // "keyword '%s' cannot be appended by the user", // obj.record_cs(i).keyword()); obj.fits_header::_erase_records(i,1); i--; } } } /* 消去されても Data Unit に関連するレコードはすぐに復活させる */ if ( this->sysrecords_prohibition_rec == true && this->manager != NULL ) { /* this will call an overridden function */ this->manager->setup_sys_header(); } if ( obj.sysrecords_prohibition_rec == true && obj.manager != NULL ) { /* this will call an overridden function */ obj.manager->setup_sys_header(); } return *this; } /* * ファイル入出力 : 読み書きしたバイト数を返す */ /** * @brief ストリームから Header Unit を読み込む * * @param sref 読み込み対象のストリーム (cstreamioの継承クラス) * @param max_bytes_read 読み込まれるべき最大のバイト数 * @return 非負値: 読み込んだバイト数
* 負値: エラー */ ssize_t fits_header::read_stream( cstreamio &sref, size_t max_bytes_read ) { if ( this->sysrecords_prohibition_rec == false ) { return this->header_load(sref,&max_bytes_read); } else { err_report(__FUNCTION__,"ERROR", "cannot use this member function."); return -1; } } /** * @brief ストリームから Header Unit を読み込む * * @param sref 読み込み対象のストリーム (cstreamioの継承クラス) * @return 非負値: 読み込んだバイト数
* 負値: エラー */ ssize_t fits_header::read_stream( cstreamio &sref ) { if ( this->sysrecords_prohibition_rec == false ) { return this->header_load(sref,NULL); } else { err_report(__FUNCTION__,"ERROR", "cannot use this member function."); return -1; } } /** * @brief 文字列バッファから Header Unit を読み込む * * @param buffer 読み込み対象のバッファ * @return 非負値: 読み込んだバイト数
* 負値: エラー */ ssize_t fits_header::read_buffer( const char *buffer ) { if ( this->sysrecords_prohibition_rec == false ) { return this->header_load(buffer); } else { err_report(__FUNCTION__,"ERROR", "cannot use this member function."); return -1; } } /** * @brief ストリームにアクセスし,Data Unit を読み飛ばす * * @param sref 読み飛ばし対象のストリーム (cstreamioの継承クラス) * @param max_bytes_skip 読み飛ばされるべき最大のバイト数 * @return 非負値: 読み飛ばしたバイト数
* 負値: エラー */ ssize_t fits_header::skip_data_stream( cstreamio &sref, size_t max_bytes_skip ) { return this->data_skip(sref,&max_bytes_skip); } /** * @brief ストリームにアクセスし,Data Unit を読み飛ばす * * @param sref 読み飛ばし対象のストリーム (cstreamioの継承クラス) * @return 非負値: 読み飛ばしたバイト数
* 負値: エラー */ ssize_t fits_header::skip_data_stream( cstreamio &sref ) { return this->data_skip(sref,NULL); } /** * @brief ストリームへの Header Unit の書き込み * * @param sref 書き込み対象のストリーム (cstreamioの継承クラス) * @param end_and_blank 「END」キーワードと2880バイトアラインのための余白を * 追加する場合は true * @return 非負値: 書き込んだバイト数
* 負値: エラー */ ssize_t fits_header::write_stream( cstreamio &sref, bool end_and_blank ) { return this->header_save( &sref, NULL, end_and_blank ); } /* discard original 80-char record, and reformat all records */ /** * @brief 全ヘッダレコードの再フォーマットを行なう * * 各 fits_header_record が現在保持している 80文字のフォーマット済み文字列を, * 現在のキーワード,値,コメントの内容から作りなおします. */ fits_header &fits_header::reformat() { long i; for ( i=0 ; i < this->length() ; i++ ) { this->record(i).reformat(); } return *this; } /** * @brief 全ヘッダレコードのフォーマット済み文字列(80×n文字)を取得 * * オブジェクト内で生成された,全ヘッダレコードのフォーマット済み文字列 * (80×n文字)を返します.改行文字は含まれず,文字列は '\0' で終端しています. * * @return フォーマット済み文字列(80×n文字) */ const char *fits_header::formatted_string() { long i; size_t pos = 0; for ( i=0 ; i < this->length() ; i++ ) { tstring tmp_str; size_t len; tmp_str = this->record(i).formatted_string(); if ( 0 < (len=tmp_str.length()) ) { if ( this->formatted_rec.length() < pos + len ) { size_t to_add; /* ざっくり確保する */ to_add = FITS::HEADER_RECORD_UNIT * this->length() * 2; this->formatted_rec.resizeby(to_add); } this->formatted_rec.put(pos,tmp_str); pos += len; } } /* 最終的な長さ調整 */ this->formatted_rec.resize(pos); return this->formatted_rec.cstr(); } /** * @brief 全ヘッダレコードのフォーマット済み文字列(80×n文字)の長さを取得 * * @note '\0' は長さに含まれません. */ size_t fits_header::formatted_length() { long i; size_t len_all = 0; for ( i=0 ; i < this->length() ; i++ ) { len_all += this->record(i).formatted_length(); } return len_all; } /* 複数行での編集 */ /** * @brief 複数のヘッダレコードの追加,更新 * * キーワードが存在しない場合は追加し,同一のキーワードが存在する場合は * 指定された内容で上書きします. * * @param obj 源泉値 * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::update_records( const fits_header &obj ) { long i, n_new = 0; /* keyrowd, value, length */ fits::header_def *tmp_defs; mdarray tmp_defs_rec(sizeof(*tmp_defs), true, &tmp_defs); /* [void **] */ /* formatted 80-char string */ const char **tmp_formatted; mdarray tmp_formatted_rec(sizeof(*tmp_formatted), true, &tmp_formatted); /* [void **] */ tmp_defs_rec.resize(obj.length()); tmp_formatted_rec.resize(obj.length()); for ( i=0 ; i < obj.length() ; i++ ) { const fits_header_record &robj = obj.record_cs(i); if ( robj.status() == FITS::NORMAL_RECORD ) { long idx; /* description でない index を探す */ idx = this->index(robj.keyword()); if ( 0 <= idx ) this->assign(idx, robj); } } for ( i=0 ; i < obj.length() ; i++ ) { const fits_header_record &robj = obj.record_cs(i); if ( robj.status() == FITS::NORMAL_RECORD ) { long idx; /* description でない index を探す */ idx = this->index(robj.keyword()); if ( idx < 0 ) { tmp_defs[n_new] = robj.raw_record(); tmp_formatted[n_new] = robj.a_formatted_rec.cstr(); n_new ++; } } } this->append_header_records(tmp_defs, tmp_formatted, n_new, true); return *this; } /** * @brief 予約キーワード(BITPIX 等)を却下するための内部用の関数 (内部用) * * @note private な関数です. */ static long reject_sysheader( const fits::header_def defs[], const char *formatted_str[], long num_defs, mdarray *tmp_defs_rec, mdarray *tmp_formatted_str, const char *fncnam, bool warn ) { long i; bool ok_all = true; fits::header_def *tmp_defs = NULL; const char **tmp_formatted = NULL; /* システム用キーワードはリジェクトする */ for ( i=0 ; i < num_defs ; i++ ) { if ( defs[i].value != NULL && defs[i].comment != NULL && is_header_reserved_key(defs[i].keyword) == true ) ok_all = false; } if ( ok_all == false ) { long j; tmp_defs_rec->resize(num_defs); tmp_defs = (fits::header_def *)tmp_defs_rec->data_ptr(); if ( formatted_str != NULL ) { tmp_formatted_str->resize(num_defs); tmp_formatted = (const char **)tmp_formatted_str->data_ptr(); } for ( i=0, j=0 ; i < num_defs ; i++ ) { if ( defs[i].value != NULL && defs[i].comment != NULL && is_header_reserved_key(defs[i].keyword) == true ) { if ( warn == true ) { err_report1(fncnam,"WARNING", "keyword '%s' cannot be appended by the user", defs[i].keyword); } } else { tmp_defs[j].keyword = defs[i].keyword; tmp_defs[j].value = defs[i].value; tmp_defs[j].comment = defs[i].comment; if ( formatted_str != NULL ) { tmp_formatted[j] = formatted_str[i]; } j++; } } num_defs = j; } return num_defs; } /* private */ /** * @brief 複数のヘッダレコードの追加 (内部実装用) * * @note このメンバ関数は private です. */ fits_header &fits_header::append_header_records( const fits::header_def defs[], const char *formatted_str[], long num_defs, bool warn ) { long i; fits_header_record *tmp_rec_ptr; long begin_index; mdarray tmp_defs_rec(sizeof(fits::header_def), true); mdarray tmp_formatted_str(sizeof(const char *), true); const fits::header_def *defs_ptr = defs; begin_index = this->num_records_rec; /* 引数チェック */ if ( num_defs < 0 ) goto quit; if ( defs == NULL || num_defs == 0 ) { goto quit; } /* システムヘッダが禁止されている場合,入れられないものをリジェクトする */ if ( this->sysrecords_prohibition_rec == true ) { long n = reject_sysheader(defs, formatted_str, num_defs, &tmp_defs_rec, &tmp_formatted_str, __FUNCTION__, warn); if ( n < num_defs ) { num_defs = n; defs_ptr = (fits::header_def *)tmp_defs_rec.data_ptr(); if ( formatted_str != NULL ) { formatted_str = (const char **)tmp_formatted_str.data_ptr(); } } } /* アドレステーブルを広げる */ try { this->records_rec.resize(this->num_records_rec + num_defs + 1); } catch (...) { err_throw(__FUNCTION__,"FATAL","records_rec.resize() failed"); } /* 1つ1つ new する */ for ( i=0 ; i < num_defs ; i++ ) { bool register_index = true; //fprintf(stderr,"debug: idx=%d\n",i); try { tmp_rec_ptr = new fits_header_record; tmp_rec_ptr->register_manager(this); } catch (...) { err_throw(__FUNCTION__,"FATAL","'new' failed"); } try { tmp_rec_ptr->assign_any(defs_ptr[i]); if ( formatted_str != NULL ) { tmp_rec_ptr->a_formatted_rec = formatted_str[i]; } } catch (...) { delete tmp_rec_ptr; err_throw(__FUNCTION__,"FATAL","tmp_rec_ptr->assign_any() failed"); } if ( tmp_rec_ptr->status() != FITS::NORMAL_RECORD ) { register_index = false; /* normal 以外では index を作成しない */ } else { if ( 0 <= this->index(tmp_rec_ptr->keyword()) ) { if ( warn == true ) { if ( this->suppress_dupkey_warning_rec == false ) { err_report1(__FUNCTION__,"WARNING", "duplicated keyword '%s' is appended", tmp_rec_ptr->keyword()); } } } } if ( register_index == true ) { /* インデックスを作成する */ int s; /* [注意] index_rec への keyword 登録は fits_header_record オブジェクトに 登録済みのものを追加すること(FITS規格に適合しないキーワードの 場合,文字を置き換える場合があるので) duplicated keyword で警告を出す場合も同様. */ s = this->index_rec.append( tmp_rec_ptr->keyword(), begin_index + i ); if ( s < 0 ) { delete tmp_rec_ptr; err_throw(__FUNCTION__,"FATAL", "this->index_rec.append() failed"); } } this->records_ptr_rec[begin_index + i] = tmp_rec_ptr; tmp_rec_ptr = NULL; this->num_records_rec ++; } quit: return *this; } /* public */ /** * @brief 複数のヘッダレコードの追加 (fits::header_def 構造体で指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::append_records( const fits::header_def defs[] ) { long num_defs = 0; if ( defs == NULL ) { return this->append_records( defs, num_defs, true ); } for ( num_defs=0 ; defs[num_defs].keyword != NULL ; num_defs++ ) { if ( END.strcmp(defs[num_defs].keyword) == 0 && defs[num_defs].value == NULL && defs[num_defs].comment == NULL ) break; } return this->append_records( defs, num_defs, true ); } /** * @brief 複数のヘッダレコードの追加 (fits::header_def 構造体で指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::append_records( const fits::header_def defs[], long num_defs, bool warn ) { return this->append_header_records(defs, NULL, num_defs, warn); } /** * @brief 複数のヘッダレコードの追加 (fits_header オブジェクトで指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::append_records( const fits_header &obj, bool warn ) { if ( 0 < obj.length() ) { long i; /* keyword, value, comment */ fits::header_def *tmp_defs; mdarray tmp_defs_rec(sizeof(*tmp_defs),true,&tmp_defs); /* [void **] */ /* formatted 80-char string */ const char **tmp_formatted; mdarray tmp_formatted_rec(sizeof(*tmp_formatted),true, &tmp_formatted); /* [void **] */ tmp_defs_rec.resize(obj.length()); tmp_formatted_rec.resize(obj.length()); for ( i=0 ; i < obj.length() ; i++ ) { tmp_defs[i] = obj.record_cs(i).raw_record(); tmp_formatted[i] = obj.record_cs(i).a_formatted_rec.cstr(); } this->append_header_records(tmp_defs, tmp_formatted, obj.length(), warn); } return *this; } /* private */ /** * @brief 複数のヘッダレコードの挿入 (内部実装用) * * @note このメンバ関数は private です. */ fits_header &fits_header::insert_header_records( long index0, const fits::header_def defs[], const char *formatted_str[], long num_defs, bool warn ) { fits_header_record *tmp_rec_ptr; long i; mdarray tmp_defs_rec(sizeof(fits::header_def), true); mdarray tmp_formatted_str(sizeof(const char *), true); const fits::header_def *defs_ptr = defs; /* 引数チェック */ if ( index0 < 0 ) index0 = 0; if ( this->num_records_rec < index0 ) index0 = this->num_records_rec; if ( this->num_records_rec == index0 ) { return append_records(defs,num_defs,warn); } if ( num_defs < 0 ) goto quit; if ( defs == NULL || num_defs == 0 ) { goto quit; } /* システムヘッダが禁止されている場合,入れられないものをリジェクトする */ if ( this->sysrecords_prohibition_rec == true ) { long n = reject_sysheader(defs, formatted_str, num_defs, &tmp_defs_rec, &tmp_formatted_str, __FUNCTION__, warn); if ( n < num_defs ) { num_defs = n; defs_ptr = (fits::header_def *)tmp_defs_rec.data_ptr(); if ( formatted_str != NULL ) { formatted_str = (const char **)tmp_formatted_str.data_ptr(); } } } /* アドレステーブルを広げる */ try { this->records_rec.resize(this->num_records_rec + num_defs + 1); } catch (...) { err_throw(__FUNCTION__,"FATAL","records_rec.resize() failed"); } /* 1つ1つ new する */ for ( i=0 ; i < num_defs ; i++ ) { bool register_index = true; long j; try { tmp_rec_ptr = new fits_header_record; tmp_rec_ptr->register_manager(this); } catch (...) { err_throw(__FUNCTION__,"FATAL","'new' failed"); } try { tmp_rec_ptr->assign_any(defs_ptr[i]); if ( formatted_str != NULL ) { tmp_rec_ptr->a_formatted_rec = formatted_str[i]; } } catch (...) { delete tmp_rec_ptr; err_throw(__FUNCTION__,"FATAL","tmp_rec_ptr->assign_any() failed"); } if ( tmp_rec_ptr->status() != FITS::NORMAL_RECORD ) { register_index = false; /* normal 以外では index を作成しない */ } else { if ( 0 <= this->index(tmp_rec_ptr->keyword()) ) { if ( warn == true ) { if ( this->suppress_dupkey_warning_rec == false ) { err_report1(__FUNCTION__,"WARNING", "duplicated keyword '%s' is inserted", tmp_rec_ptr->keyword()); } } } } /* ←ここで insert しようとするのんの index を作成すると */ /* duplicated keyword の時に,重複する index を作成しようとして */ /* 失敗する */ /* ↓ここは失敗しない */ /* ずらして(終端のNULLの分も),隙間に登録する */ this->records_rec.move( index0 + i, this->num_records_rec - i - index0 + 1, index0 + i + 1, false ); this->records_ptr_rec[index0 + i] = tmp_rec_ptr; this->num_records_rec ++; //fprintf(stderr,"debug: i=%ld\n",i); /* ずらしたところの index を作りなおす */ for ( j = index0 + i + 1 ; j < this->num_records_rec ; j++ ) { if ( this->records_ptr_rec[j] != NULL && this->records_ptr_rec[j]->status() == FITS::NORMAL_RECORD ) { int s; //fprintf(stderr,"debug: updated A i=%ld [%ld]\n",i,j); s= this->index_rec.update(this->records_ptr_rec[j]->keyword(), j-1, j); if ( s < 0 ) { err_throw(__FUNCTION__,"FATAL", "Internal ERROR: this->index_rec.update() failed"); } } } /* insert しようとするのんのインデックスを作成する */ if ( register_index == true ) { int s; s = this->index_rec.append(tmp_rec_ptr->keyword(), index0 + i); if ( s < 0 ) { delete tmp_rec_ptr; err_throw(__FUNCTION__,"FATAL", "this->index_rec.append() failed"); } } } quit: return *this; } /* public */ /** * @brief 複数のヘッダレコードの挿入 (fits::header_def 構造体で指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::insert_records( long index0, const fits::header_def defs[] ) { long num_defs = 0; if ( defs == NULL ) { return this->insert_records( index0, defs, num_defs, true ); } for ( num_defs=0 ; defs[num_defs].keyword != NULL ; num_defs++ ) { if ( END.strcmp(defs[num_defs].keyword) == 0 && defs[num_defs].value == NULL && defs[num_defs].comment == NULL ) break; } return this->insert_records( index0, defs, num_defs, true ); } /** * @brief 複数のヘッダレコードの挿入 (fits::header_def 構造体で指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::insert_records( long index0, const fits::header_def defs[], long num_defs, bool warn ) { return this->insert_header_records( index0, defs, NULL, num_defs, warn ); } /** * @brief 複数のヘッダレコードの挿入 (fits::header_def 構造体で指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::insert_records( const char *keyword0, const fits::header_def defs[] ) { return this->insert_records( this->index(keyword0), defs ); } /** * @brief 複数のヘッダレコードの挿入 (fits::header_def 構造体で指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::insert_records( const char *keyword0, const fits::header_def defs[], long num_defs, bool warn ) { return this->insert_records( this->index(keyword0), defs, num_defs, warn ); } /** * @brief 複数のヘッダレコードの挿入 (fits_header オブジェクトで指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::insert_records( long index0, const fits_header &obj, bool warn ) { if ( 0 < obj.length() ) { long i; /* keyword, value, comment */ fits::header_def *tmp_defs; mdarray tmp_defs_rec(sizeof(*tmp_defs),true,&tmp_defs); /* [void **] */ /* formatted 80-char string */ const char **tmp_formatted; mdarray tmp_formatted_rec(sizeof(*tmp_formatted),true, &tmp_formatted); /* [void **] */ tmp_defs_rec.resize(obj.length()); tmp_formatted_rec.resize(obj.length()); for ( i=0 ; i < obj.length() ; i++ ) { tmp_defs[i] = obj.record_cs(i).raw_record(); tmp_formatted[i] = obj.record_cs(i).a_formatted_rec.cstr(); } this->insert_header_records( index0, tmp_defs, tmp_formatted, obj.length(), warn); } return *this; } /** * @brief 複数のヘッダレコードの挿入 (fits_header オブジェクトで指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::insert_records( const char *keyword0, const fits_header &obj, bool warn ) { return this->insert_records( this->index(keyword0), obj, warn ); } /* protected */ /** * @brief 複数のヘッダレコードの削除 (低レベル) * * @note これは,this->manager->setup_sys_header() を呼び出さない.
* このクラスから erase_records() を使いたい場合に使う.
* このメンバ関数は protected です. */ bool fits_header::_erase_records( long index0, long num_records ) { long i, num_rm; long idx_to_rm; bool found_sys_record = false; /* 引数チェック */ if ( index0 < 0 || this->num_records_rec <= index0 ) { goto quit; } if ( this->num_records_rec < index0 + num_records ) { num_records = this->num_records_rec - index0; } if ( num_records < 0 ) { goto quit; } if ( num_records == 0 ) { goto quit; } num_rm = 0; idx_to_rm = index0; /* まず削除された状態を作る */ for ( i=0 ; i < num_records ; i++ ) { //fprintf(stderr,"debug: idx=%d\n",i); if ( this->records_ptr_rec[idx_to_rm] == NULL || this->records_ptr_rec[idx_to_rm]->keyword_protected() == false ) { long sz; if ( this->records_ptr_rec[idx_to_rm] != NULL ) { /* index を削除 */ if (this->records_ptr_rec[idx_to_rm]->status() == FITS::NORMAL_RECORD){ int s; //fprintf(stderr,"debug: erase: [%s]\n", // this->records_ptr_rec[idx_to_rm]->keyword()); s = this->index_rec.erase( this->records_ptr_rec[idx_to_rm]->keyword(), idx_to_rm + num_rm); if ( s < 0 ) { /* 内部でreallocエラーがおこると s=-1 がくるが、 インデックス自体は正常に削除されている */ err_throw(__FUNCTION__,"FATAL", "this->index_rec.erase() failed"); } /* sys record かどうかチェック */ if ( is_header_reserved_key( this->records_ptr_rec[idx_to_rm]->keyword()) == true ) { found_sys_record = true; } } /* 本体を削除 */ delete this->records_ptr_rec[idx_to_rm]; } /* NULL の部分も含めて移動 */ sz = this->num_records_rec - idx_to_rm - num_rm; //fprintf(stderr,"debug: sz=%ld\n",sz); this->records_rec.move( idx_to_rm + 1, sz, idx_to_rm, false ); num_rm ++; } else { /* index を張りなおす */ if ( this->records_ptr_rec[idx_to_rm]->status() == FITS::NORMAL_RECORD ) { int s; //fprintf(stderr,"debug: re-assigned A [%ld]\n",idx_to_rm); s = this->index_rec.update(this->records_ptr_rec[idx_to_rm]->keyword(), idx_to_rm + num_rm, idx_to_rm); if ( s < 0 ) { err_throw(__FUNCTION__,"FATAL", "Internal ERROR: this->index_rec.update() failed"); } } err_report1(__FUNCTION__,"WARNING","keyword='%s' is protected; " "cannot erase", this->records_ptr_rec[idx_to_rm]->keyword()); idx_to_rm ++; } } this->num_records_rec -= num_rm; /* index を張りなおす */ for ( i=idx_to_rm ; i < this->num_records_rec ; i++ ) { if ( this->records_ptr_rec[i] != NULL && this->records_ptr_rec[i]->status() == FITS::NORMAL_RECORD ) { int s; //fprintf(stderr,"debug: re-assigned B [%ld]\n",i); s = this->index_rec.update( this->records_ptr_rec[i]->keyword(), i + num_rm, i ); if ( s < 0 ) { err_throw(__FUNCTION__,"FATAL", "Internal ERROR: this->index_rec.update() failed"); } } } /* アドレステーブルを狭める */ try { this->records_rec.resize( this->num_records_rec + 1 ); } catch (...) { err_throw(__FUNCTION__,"FATAL","records_rec.resize() failed"); } quit: return found_sys_record; } /** * @brief 複数のヘッダレコードの削除 * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::erase_records( long index0, long num_records ) { bool found_sys_record; found_sys_record = this->fits_header::_erase_records(index0, num_records); /* sys record が消去された場合 */ if ( found_sys_record == true ) { /* 消去されても Data Unit に関連するレコードはすぐに復活させる */ if ( this->sysrecords_prohibition_rec == true && this->manager != NULL ) { /* this will call an overridden function */ //err_report(__FUNCTION__,"DEBUG","called ...... from [F]"); this->manager->setup_sys_header(); } } return *this; } /** * @brief 複数のヘッダレコードの削除 * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::erase_records( const char *keyword0, long num_records ) { return this->erase_records( this->index(keyword0), num_records ); } //fits_header &fits_header::erase_all_sysrecords() //{ // long i; // for ( i = this->length() ; 0 < i ; ) { // i--; // if ( this->is_sysrecord(i) == true ) this->erase(i); // } // return *this; //} /* */ /* 1行での追加とか */ /** * @brief 1つのヘッダレコードを追加,更新 * * キーワードが存在しない場合は追加し,同一のキーワードが存在する場合は * 指定された内容で上書きします. * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::update( const char *keyword, const char *value, const char *comment ) { fits::header_def defs[] = { {NULL,NULL,NULL}, {NULL} }; long idx; if ( keyword == NULL ) return this->append_records(defs); defs[0].keyword = keyword; defs[0].value = value; defs[0].comment = comment; /* description でない index を探す */ idx = this->index(keyword); if ( idx < 0 ) { if ( value == NULL ) defs[0].value = ""; if ( comment == NULL ) defs[0].comment = ""; return this->append_records(defs); } else { this->assign(idx,defs[0]); return *this; } } /** * @brief 1つのヘッダレコードを追加,更新(fits_header_recordオブジェクトで指定) * * キーワードが存在しない場合は追加し,同一のキーワードが存在する場合は * 指定された内容で上書きします. * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::update( const fits_header_record &obj ) { if ( obj.status() == FITS::NORMAL_RECORD ) { long idx; /* description でない index を探す */ idx = this->index(obj.keyword()); if ( idx < 0 ) { this->append(obj); } else { this->assign(idx, obj); } } return *this; } /** * @brief 1つのヘッダレコードを追加 (キーワードのみ指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::append( const char *keyword ) { if ( keyword != NULL ) { fits_header_record rec; rec.assign(keyword,"","").assign_default_comment(this->hdutype()); this->append(rec.raw_record()); } return *this; } /** * @brief 1つのヘッダレコードを追加 * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::append( const char *keyword, const char *value, const char *comment ) { if ( keyword != NULL ) { fits_header_record rec; bool flg = false; if ( value == NULL ) value = ""; if ( comment == NULL ) { comment = ""; flg = true; } rec.assign(keyword,value,comment); if ( flg == true ) rec.assign_default_comment(this->hdutype()); this->append(rec.raw_record()); } return *this; } /** * @brief 1つの記述式ヘッダレコード(HISTORYやCOMMENT)を追加 * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::append(const char *keyword, const char *description) { fits::header_def defs[] = { {NULL,NULL,NULL}, {NULL} }; defs[0].keyword = keyword; defs[0].value = description; return this->append_records(defs); } /** * @brief 1つのヘッダレコードを追加 (fits::header_def 構造体で指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::append( const fits::header_def &def ) { fits::header_def defs[] = { {NULL,NULL,NULL}, {NULL} }; defs[0] = def; return this->append_records(defs); } /** * @brief 1つのヘッダレコードを追加 (キーワードのみ指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::append( const fits_header_record &obj ) { const fits::header_def &def = obj.raw_record(); const char *formatted = obj.a_formatted_rec.cstr(); /* use private member function */ return this->append_header_records(&def, &formatted, 1, true); } /** * @brief 1つのヘッダレコードを挿入 (キーワードのみ指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::insert( long index0, const char *keyword ) { if ( keyword != NULL ) { fits_header_record rec; rec.assign(keyword,"","").assign_default_comment(this->hdutype()); this->insert(index0, rec); } return *this; } /** * @brief 1つのヘッダレコードを挿入 (キーワードのみ指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::insert( const char *keyword0, const char *keyword ) { if ( keyword != NULL ) { fits_header_record rec; rec.assign(keyword,"","").assign_default_comment(this->hdutype()); this->insert(keyword0, rec); } return *this; } /** * @brief 1つのヘッダレコードを挿入 * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::insert( long index0, const char *k, const char *v, const char *c ) { if ( k != NULL ) { fits_header_record rec; bool flg = false; if ( v == NULL ) v = ""; if ( c == NULL ) { c = ""; flg = true; } rec.assign(k, v, c); if ( flg == true ) rec.assign_default_comment(this->hdutype()); this->insert(index0, rec.raw_record()); } return *this; } /** * @brief 1つのヘッダレコードを挿入 * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::insert( const char *keyword0, const char *k, const char *v, const char *c ) { if ( k != NULL ) { fits_header_record rec; bool flg = false; if ( v == NULL ) v = ""; if ( c == NULL ) { c = ""; flg = true; } rec.assign(k, v, c); if ( flg == true ) rec.assign_default_comment(this->hdutype()); this->insert(keyword0, rec.raw_record()); } return *this; } /** * @brief 1つの記述式ヘッダレコード(HISTORYやCOMMENT)を挿入 * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::insert( long index0, const char *keywd, const char *description ) { fits::header_def defs[] = { {NULL,NULL,NULL}, {NULL} }; defs[0].keyword = keywd; defs[0].value = description; return this->insert_records(index0,defs); } /** * @brief 1つの記述式ヘッダレコード(HISTORYやCOMMENT)を挿入 * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::insert( const char *keyword0, const char *keywd, const char *description ) { fits::header_def defs[] = { {NULL,NULL,NULL}, {NULL} }; defs[0].keyword = keywd; defs[0].value = description; return this->insert_records(keyword0,defs); } /** * @brief 1つのヘッダレコードを挿入 (fits::header_def 構造体で指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::insert( long index0, const fits::header_def &def ) { fits::header_def defs[] = { {NULL,NULL,NULL}, {NULL} }; defs[0] = def; return this->insert_records(index0,defs); } /** * @brief 1つのヘッダレコードを挿入 (fits::header_def 構造体で指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::insert( const char *keyword0, const fits::header_def &def ) { fits::header_def defs[] = { {NULL,NULL,NULL}, {NULL} }; defs[0] = def; return this->insert_records(keyword0,defs); } /** * @brief 1つのヘッダレコードを挿入 (fits_header_record オブジェクトで指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::insert( long index0, const fits_header_record &obj ) { const fits::header_def &def = obj.raw_record(); const char *formatted = obj.a_formatted_rec.cstr(); /* use private member function */ return this->insert_header_records( index0, &def, &formatted, 1, true ); } /** * @brief 1つのヘッダレコードを挿入 (fits_header_record オブジェクトで指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::insert( const char *keyword0, const fits_header_record &obj ) { const fits::header_def &def = obj.raw_record(); const char *formatted = obj.a_formatted_rec.cstr(); /* use private member function */ return this->insert_header_records( this->index(keyword0), &def, &formatted, 1, true ); } /** * @brief 1つのヘッダキーワード名の変更 * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::rename( long index0, const char *new_name ) { fits::header_def def = {new_name,NULL,NULL}; /* check args */ if ( index0 < 0 || this->num_records_rec <= index0 ) goto quit; if ( new_name == NULL ) goto quit; /* rename */ this->assign(index0, def); quit: return *this; } /** * @brief 1つのヘッダキーワード名の変更 * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::rename( const char *keyword0, const char *new_name ) { return this->rename( this->index(keyword0), new_name ); } /** * @brief 1つのヘッダレコードの消去 * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::erase( long index0 ) { this->erase_records(index0,1); return *this; } /** * @brief 1つのヘッダレコードの消去 * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::erase( const char *keyword0 ) { this->erase_records(keyword0,1); return *this; } /* * wrappers for low-level member functions */ /** * @brief 1つのヘッダレコードを更新 (fits::header_def 構造体で指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::assign( long index0, const fits::header_def &def ) { //err_report(__FUNCTION__,"DEBUG","called!!"); int s; tstring old_key; fits_header_record tmp_hrec; fits::header_def tmp_def = {NULL,def.value,def.comment}; if ( index0 < 0 || this->num_records_rec <= index0 ) { err_report(__FUNCTION__,"WARNING","invalid index0"); goto quit; } if ( this->records_ptr_rec[index0] == NULL ) goto quit; //fprintf(stderr,"debug : def.comment = [%s]\n",def.comment); /* キーワードに空白が与えられ,かつ値かコメントに空白が与えられた場合は */ /* レコードの初期化を行なう */ if ( (def.keyword != NULL && def.keyword[0]=='\0') && ( (def.value != NULL && def.value[0]=='\0') || (def.comment != NULL && def.comment[0]=='\0') ) ) { /* description でない場合,keyword を保存しておく */ if ( this->records_ptr_rec[index0]->status() == FITS::NORMAL_RECORD ) { old_key = this->records_ptr_rec[index0]->keyword(); } /* 初期化を試みる */ this->records_ptr_rec[index0]->assign_any(def); /* index */ if ( old_key.cstr() != NULL ) { /* description でない場合 */ /* キーワードに変更があった場合は登録する */ /* (keyword protected な場合は,変更できない) */ if ( this->records_ptr_rec[index0]->status() != FITS::NORMAL_RECORD || old_key.strcmp(this->records_ptr_rec[index0]->keyword()) != 0 ) { s = this->index_rec.erase(old_key.cstr(),index0); if ( s < 0 ) { err_throw(__FUNCTION__,"FATAL", "this->index_rec.erase() failed"); } } } goto quit; } /* システムヘッダが禁止されている場合,警告を出してエラーとする */ if ( this->sysrecords_prohibition_rec == true ) { if ( def.keyword != NULL && is_header_reserved_key(def.keyword) == true ) { err_report1(__FUNCTION__,"WARNING", "keyword '%s' cannot be assigned by the user", def.keyword); goto quit; } } /* 一旦,テンポラリに書き出してみる(キーワードの文字列置換のため) */ if ( def.keyword != NULL ) { tmp_hrec.assign_any(def); tmp_def.keyword = tmp_hrec.keyword(); } /* 当該のレコードが description でない場合 */ if ( this->records_ptr_rec[index0]->status() == FITS::NORMAL_RECORD ) { /* keyword を保存しておく */ old_key = this->records_ptr_rec[index0]->keyword(); /* 登録済みの keyword を入れようとした場合,警告する */ if ( tmp_def.keyword != NULL && 0 <= this->index(tmp_def.keyword) ) { if ( this->index(tmp_def.keyword) != index0 ) { if ( this->suppress_dupkey_warning_rec == false ) { err_report1(__FUNCTION__,"WARNING", "duplicated keyword '%s' is appended",tmp_def.keyword); } } } } /* 登録する */ try { this->records_ptr_rec[index0]->assign_any(def); } catch (...) { this->fits_header::_erase_records(index0,1); err_throw(__FUNCTION__,"FATAL", "this->records_ptr_rec[index0]->assign_any(def) failed"); } if ( old_key.cstr() == NULL ) { /* 新規の場合 */ if ( this->records_ptr_rec[index0]->status() == FITS::NORMAL_RECORD ) { s= this->index_rec.append(this->records_ptr_rec[index0]->keyword(),index0); if ( s < 0 ) { this->fits_header::_erase_records(index0,1); err_throw(__FUNCTION__,"FATAL", "this->index_rec.append() failed"); } } } else { /* 更新前のが description でない場合 */ /* description になった場合,キーワードに変更があった場合は消去する */ if ( this->records_ptr_rec[index0]->status() != FITS::NORMAL_RECORD || old_key.strcmp(this->records_ptr_rec[index0]->keyword()) != 0) { s= this->index_rec.erase(old_key.cstr(),index0); if ( s < 0 ) { err_throw(__FUNCTION__,"FATAL","this->index_rec.erase() failed"); } } /* description ではなく,キーワードに変更があった場合は消去する */ if ( this->records_ptr_rec[index0]->status() == FITS::NORMAL_RECORD && old_key.strcmp(this->records_ptr_rec[index0]->keyword()) != 0) { s= this->index_rec.append(this->records_ptr_rec[index0]->keyword(),index0); if ( s < 0 ) { this->fits_header::_erase_records(index0,1); err_throw(__FUNCTION__,"FATAL", "this->index_rec.append() failed"); } } } quit: return *this; } /** * @brief 1つのヘッダレコードを更新 (fits::header_def 構造体で指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::assign( const char *keyword0, const fits::header_def &def ) { return this->assign( this->index(keyword0), def ); } /** * @brief 1つのヘッダレコードを更新 (fits_header_record オブジェクトで指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::assign( long index0, const fits_header_record &obj ) { fits::header_def tmp_def = {"","",""}; if ( index0 < 0 || this->num_records_rec <= index0 ) { err_report(__FUNCTION__,"WARNING","invalid index0"); return *this; } /* objがすでに管理下にないかチェック */ fits_header_record &dest = this->at(index0); if ( &dest == &obj ) return *this; this->assign( index0, tmp_def ); /* 消去 */ if ( this->records_ptr_rec[index0]->status() == FITS::NULL_RECORD || this->records_ptr_rec[index0]->status() == obj.status() ) { this->assign(index0, obj.raw_record()); this->at(index0).a_formatted_rec = obj.a_formatted_rec; } return *this; } /** * @brief 1つのヘッダレコードを更新 (fits_header_record オブジェクトで指定) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::assign( const char *keyword0, const fits_header_record &obj ) { return this->assign( this->index(keyword0), obj ); } /** * @brief 1つのヘッダレコードを更新 * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::assign( long index0, const char *keyword, const char *value, const char *comment ) { if ( index0 < 0 || this->num_records_rec <= index0 ) { err_report(__FUNCTION__,"WARNING","invalid index0"); return *this; } if ( this->records_ptr_rec[index0]->status() == FITS::NORMAL_RECORD ) { fits::header_def def = {keyword,value,comment}; this->assign( index0, def ); } else if ( keyword != NULL && keyword[0] != '\0' ) { fits::header_def tmp_def = {"","",""}; this->assign( index0, tmp_def ); /* 消去 */ if ( value == NULL ) value = ""; if ( comment == NULL ) comment = ""; if ( this->records_ptr_rec[index0]->status() == FITS::NULL_RECORD ) { fits::header_def def = {keyword,value,comment}; this->assign( index0, def ); } } else if ( keyword != NULL && keyword[0] == '\0' && ( (value != NULL && value[0] == '\0') || (comment != NULL && comment[0] == '\0') ) ) { fits::header_def tmp_def = {"","",""}; this->assign( index0, tmp_def ); /* 消去 */ } return *this; } /** * @brief 1つのヘッダレコードを更新 * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::assign( const char *keyword0, const char *keyword, const char *value, const char *comment ) { return this->assign( this->index(keyword0), keyword, value, comment ); } /** * @brief 1つのヘッダレコードを記述式レコードで更新 * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::assign( long index0, const char *keyword, const char *description ) { if ( index0 < 0 || this->num_records_rec <= index0 ) { err_report(__FUNCTION__,"WARNING","invalid index0"); return *this; } if ( this->records_ptr_rec[index0]->status() == FITS::DESCRIPTION_RECORD ) { fits::header_def def = {keyword,description,NULL}; this->assign( index0, def ); } else if ( (keyword != NULL && keyword[0] != '\0') || (description != NULL && description[0] != '\0') ) { fits::header_def tmp_def = {"","",""}; this->assign( index0, tmp_def ); /* 消去 */ if ( keyword == NULL ) keyword = ""; if ( description == NULL ) description = ""; if ( this->records_ptr_rec[index0]->status() == FITS::NULL_RECORD ) { fits::header_def def = {keyword,description,NULL}; this->assign( index0, def ); } } else if ( keyword != NULL && keyword[0] == '\0' && description != NULL && description[0] == '\0' ) { fits::header_def tmp_def = {"","",""}; this->assign( index0, tmp_def ); /* 消去 */ } return *this; } /** * @brief 1つのヘッダレコードを記述式レコードで更新 * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::assign( const char *keyword0, const char *keyword, const char *description ) { return this->assign( this->index(keyword0), keyword, description ); } /* value */ /** * @brief 1つのヘッダレコードの値を更新 (低レベル・printf()の記法) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::vassignf_value( long index0, const char *format, va_list ap ) { if ( index0 < 0 || this->num_records_rec <= index0 ) { err_report(__FUNCTION__,"WARNING","invalid index0"); } else this->records_ptr_rec[index0]->vassignf_value(format,ap); return *this; } /** * @brief 1つのヘッダレコードの値を更新 (低レベル・printf()の記法) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::vassignf_value( const char *keyword0, const char *format, va_list ap ) { return this->vassignf_value( this->index(keyword0), format, ap ); } /** * @brief 1つのヘッダレコードの値を更新 (低レベル・printf()の記法) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::assignf_value( long index0, const char *format, ... ) { if ( index0 < 0 || this->num_records_rec <= index0 ) { err_report(__FUNCTION__,"WARNING","invalid index0"); } else { va_list ap; va_start(ap,format); try { this->vassignf_value(index0,format,ap); } catch (...) { va_end(ap); err_throw(__FUNCTION__,"FATAL","this->vassignf_value() failed"); } va_end(ap); } return *this; } /** * @brief 1つのヘッダレコードの値を更新 (低レベル・printf()の記法) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::assignf_value( const char *keyword0, const char *format, ... ) { va_list ap; va_start(ap,format); try { this->vassignf_value(keyword0,format,ap); } catch (...) { va_end(ap); err_throw(__FUNCTION__,"FATAL","this->vassignf_value() failed"); } va_end(ap); return *this; } /* comment */ /** * @brief 1つのヘッダレコードのコメントを更新 (printf()の記法) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::vassignf_comment( long index0, const char *format, va_list ap ) { tstring tstr; fits::header_def rv = {NULL,NULL,NULL}; if ( index0 < 0 || this->num_records_rec <= index0 ) { err_report(__FUNCTION__,"WARNING","invalid index0"); goto quit; } if ( format == NULL ) { this->assign(index0,rv); goto quit; } tstr.vassignf(format,ap); rv.comment = tstr.cstr(); this->assign(index0,rv); quit: return *this; } /** * @brief 1つのヘッダレコードのコメントを更新 (printf()の記法) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::vassignf_comment( const char *keyword0, const char *format, va_list ap ) { return this->vassignf_comment( this->index(keyword0), format, ap ); } /** * @brief 1つのヘッダレコードのコメントを更新 (printf()の記法) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::assignf_comment( long index0, const char *format, ... ) { if ( index0 < 0 || this->num_records_rec <= index0 ) { err_report(__FUNCTION__,"WARNING","invalid index0"); return *this; } else { va_list ap; va_start(ap,format); try { this->vassignf_comment(index0,format,ap); } catch (...) { va_end(ap); err_throw(__FUNCTION__,"FATAL","this->vassignf_comment()"); } va_end(ap); return *this; } } /** * @brief 1つのヘッダレコードのコメントを更新 (printf()の記法) * * @note 自身が fits_hdu オブジェクトの管理下にある場合,Data Unit に関係する * 予約キーワード(BITPIX 等)は指定できません. */ fits_header &fits_header::assignf_comment( const char *keyword0, const char *format, ... ) { va_list ap; va_start(ap,format); try { this->vassignf_comment(keyword0,format,ap); } catch (...) { va_end(ap); err_throw(__FUNCTION__,"FATAL","this->vassignf_comment()"); } va_end(ap); return *this; } /* */ /* Overwrite all header comments with SFITSIO built-in comment strings */ /** * @brief コメントの存在にかかわらず,現在のヘッダコメント辞書の内容で埋める * * 全てのヘッダレコードのうち,現在のコメント辞書とキーワードが一致する場合, * それらのレコードのコメントを現在のコメント辞書が持つコメント文で上書きし * ます. */ fits_header &fits_header::assign_default_comments( int hdutype ) { long i; for ( i=0 ; i < this->length() ; i++ ) { if ( this->records_ptr_rec[i]->status() == FITS::NORMAL_RECORD ) { this->records_ptr_rec[i]->assign_default_comment(hdutype); } } return *this; } /* Fill all blank comments with SFITSIO built-in comment strings */ /** * @brief コメントが存在しない場合,現在のヘッダコメント辞書の内容で埋める * * 全てのヘッダレコードのコメントが空白なものについて,現在のコメント辞書と * キーワードが一致する場合,それらのレコードに現在のコメント辞書が持つ * コメント文をセットします. */ fits_header &fits_header::fill_blank_comments( int hdutype ) { long i; for ( i=0 ; i < this->length() ; i++ ) { if ( this->records_ptr_rec[i]->status() == FITS::NORMAL_RECORD && this->records_ptr_rec[i]->comment_length() == 0 ) { this->records_ptr_rec[i]->assign_default_comment(hdutype); } } return *this; } /* */ /** * @brief fits_header_record オブジェクトへの参照を取得 * * @note fits_header::at() との違いはありません. */ fits_header_record &fits_header::record( long index0 ) { if ( index0 < 0 || this->num_records_rec <= index0 ) { err_throw(__FUNCTION__,"ERROR","Invalid record index"); } return *(this->records_ptr_rec[index0]); } /** * @brief fits_header_record オブジェクトへの参照を取得 * * @note fits_header::at() との違いはありません. */ fits_header_record &fits_header::record( const char *keyword0 ) { long index0 = this->index(keyword0); #if 1 if ( index0 < 0 ) { this->append(keyword0); index0 = this->index(keyword0); } #endif if ( index0 < 0 ) { err_throw1(__FUNCTION__,"ERROR","keyword '%s' is not found",keyword0); } return this->record( index0 ); } #ifdef SLI__OVERLOAD_CONST_AT /** * @brief fits_header_record オブジェクトへの参照を取得 (読取専用) * * @note fits_header::at() との違いはありません. */ const fits_header_record &fits_header::record( long index0 ) const { return this->record_cs(index0); } /** * @brief fits_header_record オブジェクトへの参照を取得 (読取専用) * * @note fits_header::at() との違いはありません. */ const fits_header_record &fits_header::record( const char *keyword0 ) const { return this->record_cs(keyword0); } #endif /** * @brief fits_header_record オブジェクトへの参照を取得 (読取専用) * * @note fits_header::at_cs() との違いはありません. */ const fits_header_record &fits_header::record_cs( long index0 ) const { if ( index0 < 0 || this->num_records_rec <= index0 ) { err_throw(__FUNCTION__,"ERROR","Invalid record index"); } return *(this->records_ptr_rec[index0]); } /** * @brief fits_header_record オブジェクトへの参照を取得 (読取専用) * * @note fits_header::at_cs() との違いはありません. */ const fits_header_record &fits_header::record_cs( const char *keyword0 ) const { long idx = this->index(keyword0); if ( idx < 0 ) { err_throw1(__FUNCTION__,"ERROR","keyword '%s' is not found",keyword0); } return this->record_cs( idx ); } /* same as fits_header::record() */ /** * @brief fits_header_record オブジェクトへの参照を取得 * * @note fits_header::record() との違いはありません. */ fits_header_record &fits_header::at( long index0 ) { return this->record(index0); } /** * @brief fits_header_record オブジェクトへの参照を取得 * * @note fits_header::record() との違いはありません. */ fits_header_record &fits_header::at( const char *keyword0 ) { return this->record(keyword0); } #ifdef SLI__OVERLOAD_CONST_AT /** * @brief fits_header_record オブジェクトへの参照を取得 (読取専用) * * @note fits_header::record() との違いはありません. */ const fits_header_record &fits_header::at( long index0 ) const { return this->record_cs(index0); } /** * @brief fits_header_record オブジェクトへの参照を取得 (読取専用) * * @note fits_header::record() との違いはありません. */ const fits_header_record &fits_header::at( const char *keyword0 ) const { return this->record_cs(keyword0); } #endif /** * @brief fits_header_record オブジェクトへの参照を取得 (読取専用) * * @note fits_header::record_cs() との違いはありません. */ const fits_header_record &fits_header::at_cs( long index0 ) const { return this->record_cs(index0); } /** * @brief fits_header_record オブジェクトへの参照を取得 (読取専用) * * @note fits_header::record_cs() との違いはありません. */ const fits_header_record &fits_header::at_cs( const char *keyword0 ) const { return this->record_cs(keyword0); } /* * 読み取り */ /** * @brief キーワードに対応するヘッダレコード番号を取得 (記述式レコードは除外) */ long fits_header::index( const char *keyword0 ) const { return this->index(keyword0,false); } /** * @brief キーワードに対応するヘッダレコード番号を取得 */ long fits_header::index( const char *keyword0, bool is_description ) const { long return_index = -1; long i; const char *str_beg; size_t j,len; char kwd[FITS::HEADER_KEYWORD_MAX_LENGTH + 1]; if ( keyword0 == NULL ) goto quit; /* keyword0 の両サイドのスペースを除去 */ rm_side_spaces(keyword0," ",&str_beg,&len); for ( j=0 ; j < FITS::HEADER_KEYWORD_MAX_LENGTH && j < len ; j++ ) { kwd[j] = str_beg[j]; } kwd[j] = '\0'; if ( is_description == true ) { /* HISTORY とか COMMENT は単に前から検索するだけ */ for ( i=0 ; i < this->num_records_rec ; i++ ) { if ( this->records_ptr_rec[i] != NULL && this->records_ptr_rec[i]->status() == FITS::DESCRIPTION_RECORD ) { if ( this->records_ptr_rec[i]->keyword_cs().strcmp(kwd) == 0 ) { return_index = i; break; } } } } else { return_index = this->index_rec.index(kwd,0); } quit: return return_index; } /** * @brief POSIX拡張正規表現でキーワードを検索 */ long fits_header::regmatch( const char *keypat, ssize_t *rpos, size_t *rlen ) const { return this->regmatch(0L,keypat,rpos,rlen); } /** * @brief POSIX拡張正規表現でキーワードを連続的に検索 */ long fits_header::regmatch( long index0, const char *keypat, ssize_t *rpos, size_t *rlen ) const { long i; for ( i=index0 ; i < this->length() ; i++ ) { ssize_t pos; size_t len; if ( this->record_cs(i).status() == FITS::NORMAL_RECORD ) { pos = this->record_cs(i).arr_cs().at_cs(0L).regmatch(keypat,&len); if ( 0 <= pos ) { if ( rpos != NULL ) *rpos = pos; if ( rlen != NULL ) *rlen = len; return i; } } } return -1; } /* returns length of raw value. More than 0 means NON-NULL. */ /** * @brief キーワードに対応する生の文字列値の長さを取得 (存在チェックに使用可) * * @param keyword ヘッダキーワード * @return 正の値: 生の文字列値(「'」を含む)の長さ
* 0: 値が存在しない場合
* 負の値: キーワードが存在しない場合 */ long fits_header::value_length( const char *keyword ) const { long idx = this->index(keyword, false); if ( 0 <= idx ) return this->at_cs(idx).value_length(); else return -1; } /** * @brief キーワードに対応する生の文字列値の長さを取得 (存在チェックに使用可) * * @param index レコード番号 * @return 正の値: 生の文字列値(「'」を含む)の長さ
* 0: 値が存在しない場合
* 負の値: キーワードが存在しない場合 */ long fits_header::value_length( long index ) const { if ( 0 <= index && index < this->length() ) { const fits_header_record &h_rec = this->at_cs(index); if (h_rec.status() == FITS::NORMAL_RECORD) return h_rec.value_length(); else return -1; } else return -1; } #if 0 long fits_header::num_records() { return this->num_records_rec; } #endif /** * @brief ヘッダレコードの長さを取得 */ long fits_header::length() const { return this->num_records_rec; } /** * @brief ヘッダレコードの長さを取得 * * @note fits_header::length() との違いはありません. */ long fits_header::size() const { return this->num_records_rec; } /** * @brief ヘッダの情報から HDU のタイプを判定して返す */ int fits_header::hdutype() const { int htype = FITS::ANY_HDU; if ( this->index("SIMPLE") == 0 ) { htype = FITS::IMAGE_HDU; } else if ( this->index("XTENSION") == 0 ) { const tstring &xtension = this->record(0L).svalue_cs(); if ( xtension.strcmp("IMAGE") == 0 ) { htype = FITS::IMAGE_HDU; } else if ( xtension.strcmp("BINTABLE") == 0 ) { htype = FITS::BINARY_TABLE_HDU; } else if ( xtension.strcmp("TABLE") == 0 ) { htype = FITS::ASCII_TABLE_HDU; } } return htype; } /** * @brief Data Unit に関係する予約キーワード(BITPIX 等)かどうかを返す */ bool fits_header::is_sysrecord( long index ) const { if ( index < 0 || this->num_records_rec <= index ) return false; if ( this->record_cs(index).status() == FITS::NORMAL_RECORD ) { return is_header_reserved_key(this->record_cs(index).keyword()); } else return false; } /** * @brief CHECKSUM を計算して返す */ unsigned long fits_header::checksum( unsigned long sum_in ) { fitsio_csum csum_info; /* init */ initialize_csum( &csum_info, sum_in ); /* get chksum */ this->header_save( NULL, (void *)&csum_info, true ); return csum_info.sum; } /** * @brief エンコードされた CHECKSUM を計算して返す */ const char *fits_header::encoded_checksum( unsigned long sum_in ) { fitsio_csum csum_info; /* init */ initialize_csum( &csum_info, sum_in ); /* get chksum */ this->header_save( NULL, (void *)&csum_info, true ); encode_csum( csum_info, this->encoded_chksum_rec ); return this->encoded_chksum_rec; } /** * @brief 重複キーワードの警告に関する設定 * * これを true にすると,重複したキーワードをセットしても警告を出さなく * なります. */ bool &fits_header::suppress_dupkey_warning() { return this->suppress_dupkey_warning_rec; } /** * @brief fits_header::expand_continue_records() で使用 (内部用) * * @note private な関数です. */ static bool is_continue_record( const fits_header_record &ref ) { if ( ref.status() == FITS::DESCRIPTION_RECORD && ref.keyword_cs().strcmp("CONTINUE") == 0 && 1 <= ref.value_cs().length() ) return true; else return false; } /** * @brief CONTINUE レコードを展開 * * @attention ほとんど内部実装用で,プログラマは使う必要が無いメンバ関数です. * @note fitsccからも使われる. */ fits_header &fits_header::expand_continue_records() { fits_header *target_header = this; long j; //fprintf(stderr,"debug: expanding CONTINUE\n"); /* * SFITSIO 'CONTINUE' convension: * * - Basic Convension: * * FOO = 'aaaaaaaaaaaa & ' * CONTINUE 'bbbbbbbbbbb&' / this is * CONTINUE '' / a looooooooooooooooong * CONTINUE / comment. * * - NOTE * * Most easy way to generate header records with long string + long * comment is that we reserve byte 79-80 for "&'" string in a * header record. We show an example: * * 70 80 * 12345678901234567890 ... 01234567890 * KEYWORD = '......... ... .................&' * CONTINUE '......... ... .................&' * CONTINUE '......... ... .................&' * CONTINUE / this is a comment. * * Note that "''" should not be put on 78th column: * * CONTINUE '......... ... ................'&' <= No good * CONTINUE ''........ ... .................&' <= No good * */ for ( j=1 ; j < target_header->length() ; j++ ) { if ( is_continue_record(target_header->record(j)) ) { const long j0 = j - 1; /* master record */ long n_erase; bool prev_value_amp_exists = false; bool prev_comment_amp_exists = false; tstring long_value; tstring long_comment; const tstring &v0_ref = target_header->record(j0).value_cs(); /* 右側の空白は除去されている */ const tstring &c0_ref = target_header->record(j0).comment_cs(); size_t v0_len = v0_ref.length(); size_t c0_len = c0_ref.length(); //err_report1(__FUNCTION__,"DEBUG","key = [%s]", // target_header->record(j0).keyword()); //err_report1(__FUNCTION__,"DEBUG","val = [%s]", v0_ref.cstr()); //err_report1(__FUNCTION__,"DEBUG","com = [%s]", c0_ref.cstr()); /* まずは,v0 を処理(最後の "'" をつけないで代入) */ if ( target_header->record(j0).type() == FITS::STRING_T ) { size_t s_pos; /* * 値 */ /* 2文字以下の場合は,1文字だけ採用するだけ */ if ( v0_len <= 2 ) { long_value.append(v0_ref,0,1); } /* 3文字以上の場合は,最後の "'"," ","&" の処理が必要 */ else { size_t r_spn; s_pos = v0_len - 1; /* 最後の '&' をチェック(これは認めるべきではないが) */ if ( v0_ref.cchr(s_pos) == '&' ) { s_pos --; } /* スペースをチェック */ r_spn = v0_ref.strrspn(s_pos,' '); s_pos -= r_spn; /* 最後のクォーテーションをチェック */ if ( v0_ref.cchr(s_pos) == '\'' ) { s_pos --; } /* さらに内側のスペースもチェック */ r_spn = v0_ref.strrspn(s_pos,' '); s_pos -= r_spn; /* 最後に & をチェック */ if ( v0_ref.cchr(s_pos) == '&' ) { prev_value_amp_exists = true; s_pos --; } long_value.append(v0_ref, 0, s_pos + 1); } /* * コメント */ /* 最後の '&' をチェック */ s_pos = c0_len - 1; //if ( c0_ref.cchr(s_pos) == '&' ) { // s_pos --; // prev_comment_amp_exists = true; //} long_comment.append(c0_ref, 0, s_pos + 1); } else { size_t s_pos; long_value.append(v0_ref); /* 最後の '&' をチェック */ s_pos = c0_len - 1; //if ( c0_ref.cchr(s_pos) == '&' ) { // s_pos --; // prev_comment_amp_exists = true; //} long_comment.append(c0_ref, 0, s_pos + 1); } //err_report1(__FUNCTION__,"DEBUG","l_v = [%s]",long_value.cstr()); //err_report1(__FUNCTION__,"DEBUG","l_c = [%s]",long_comment.cstr()); /* 続く CONTINUE 文を全部やってしまう */ /* (処理済みの CONTINUE レコードは削除する) */ n_erase = 0; while ( j < target_header->length() && is_continue_record(target_header->record(j)) == true ) { bool curr_comment_amp_exists = false; /* ナマの値.ここでは左右の空白はそのまま */ const tstring &vref = target_header->record(j).value_cs(); size_t l_spn, r_spn; bool next_is_continue; /* 次も CONTINUE かどうか */ next_is_continue = ( j+1 < target_header->length() && is_continue_record(target_header->record(j+1)) == true ); /* 左右のスペースをチェック */ l_spn = vref.strspn(' '); if ( vref.length() == l_spn ) r_spn = 0; else { r_spn = vref.strrspn(' '); /* 次も CONTINUE の場合は '&' をチェック */ //if ( next_is_continue ) { // if (vref.cchr(vref.length() - r_spn - 1) == '&') { // r_spn ++; // curr_comment_amp_exists = true; // } //} } if (target_header->record(j0).type() == FITS::STRING_T) { size_t s_pos = l_spn; /* 前から順に "'" を探していく */ if ( prev_value_amp_exists == true && vref.cchr(s_pos) == '\'' ) { size_t v_begin; /* 文字列値の開始点 */ s_pos ++; v_begin = s_pos; while ( 1 ) { ssize_t f_pos; if ( (f_pos=vref.find(s_pos,'\'')) < 0 ) { size_t v_len; /* 終端の "'" が無い場合は,全部値にする */ v_len = vref.length() - v_begin; /* 最後のスペースの消去 */ if ( r_spn <= v_len ) v_len -= r_spn; /* 最後に "&" があったらそのままにしとく */ if ( curr_comment_amp_exists == true ) v_len ++; long_value.append(vref, v_begin, v_len); prev_value_amp_exists = false; break; } if ( vref.cchr(f_pos+1) == '\'' ) { /* 「''」のための処理 */ s_pos = f_pos+2; /* while() へ戻る */ } else { /* comment 文字列を探す */ /* "/" or '&' を探して,ダメなら再度 while() に戻る */ s_pos = f_pos + 1; /* '&' も探すが,本来は来てはいけない*/ s_pos += vref.strspn(s_pos,' '); if ( s_pos == vref.length() || vref.cchr(s_pos) == '/' || vref.cchr(s_pos) == '&' ) { /* ようやく合格 */ size_t s_spn, v_len, c_len; if ( s_pos < vref.length() ) { s_pos++; s_pos += vref.strspn(s_pos,' '); } /* register comment */ c_len = vref.length() - s_pos; /* 最後のスペースと '&' の消去 */ if ( r_spn <= c_len ) c_len -= r_spn; if ( 0 < c_len ) { if ( 0 < long_comment.length() && prev_comment_amp_exists == false ) { long_comment.append(" "); } long_comment.append(vref, s_pos, c_len); } /* register value */ s_spn = vref.strrspn(f_pos-1,' '); /* 次も CONTINUE の場合は '&' をチェック */ prev_value_amp_exists = false; /* reset */ if ( next_is_continue ) { if ( vref.cchr(f_pos-s_spn-1) == '&') { prev_value_amp_exists = true; s_spn++; } } v_len = f_pos - s_spn - v_begin; long_value.append(vref, v_begin,v_len); break; } s_pos = f_pos+1; /* while() へ戻る */ } } } else { size_t c_len; /* 文字列と判定できない場合はコメントとして処理 */ c_len = vref.length() - l_spn - r_spn; if ( 0 < c_len ) { if ( 0 < long_comment.length() && prev_comment_amp_exists == false ) { long_comment.append(" "); } /* 先頭に「/」があれば除去 */ if ( 2 <= c_len && vref.cchr(l_spn) == '/' && vref.cchr(l_spn + 1) == ' ' ) { long_comment.append(vref, l_spn+2, c_len-2); } else if ( 1 <= c_len && vref.cchr(l_spn) == '/' ) { long_comment.append(vref, l_spn+1, c_len-1); } else { long_comment.append(vref, l_spn, c_len); } } } } else { size_t c_len; /* 文字列型以外の場合は単にコメントとして処理 */ c_len = vref.length() - l_spn - r_spn; if ( 0 < c_len ) { if ( 0 < long_comment.length() && prev_comment_amp_exists == false ) { long_comment.append(" "); } /* 先頭に「/」があれば除去 */ if ( 2 <= c_len && vref.cchr(l_spn) == '/' && vref.cchr(l_spn + 1) == ' ' ) { long_comment.append(vref, l_spn+2, c_len-2); } else if ( 1 <= c_len && vref.cchr(l_spn) == '/' ) { long_comment.append(vref, l_spn+1, c_len-1); } else { long_comment.append(vref, l_spn, c_len); } } } /* count CONTINUE records to erase */ n_erase ++; j ++ ; prev_comment_amp_exists = curr_comment_amp_exists; } /* while ( ) ... */ /* 文字列値の場合は,最後の "'" を追加 */ if ( target_header->record(j0).type() == FITS::STRING_T ) { long_value.append("'"); } //err_report1(__FUNCTION__,"DEBUG","length of long_value = [%d]", // (int)(long_value.length())); //err_report1(__FUNCTION__,"DEBUG","long_value = [%s]", // long_value.cstr()); //err_report1(__FUNCTION__,"DEBUG","long_comment = [%s]", // long_comment.cstr()); //err_report1(__FUNCTION__,"DEBUG","%s","========"); /* オリジナルの 80-char を保存し,値を更新 */ { fits_header_record &rec_j0 = target_header->at(j0); tstring a_formatted_rec_bak; long i; /* a_formatted_rec をバックアップ */ a_formatted_rec_bak = rec_j0.a_formatted_rec; /* 値を更新 (a_formatted_rec は消去される) */ target_header->assignf_comment(j0, "%s",long_comment.cstr()); target_header->assignf_value(j0, "%s", long_value.cstr()); /* fitscc から使われた場合,オリジナルの 80-char はない */ if ( a_formatted_rec_bak.cstr() != NULL ) { /* 80 x n 文字の a_formatted_rec を保存 */ rec_j0.a_formatted_rec .resize(FITS::HEADER_RECORD_UNIT * (1 + n_erase)); rec_j0.a_formatted_rec.put(0, a_formatted_rec_bak); for ( i=0 ; i < n_erase ; i++ ) { rec_j0.a_formatted_rec.put( FITS::HEADER_RECORD_UNIT * (1 + i), target_header->at(j0 + 1 + i).a_formatted_rec); } } } /* erase CONTINUE records */ target_header->erase_records(j0 + 1, n_erase); /* important! */ j = j0; } /* if ( is_continue_record(...) ) ... */ } return *this; } /** * @brief shallow copy を許可する場合に使用 (未実装) * @note 一時オブジェクトの return の直前で使用する. */ /* 注意: 参照を返したいところだが,return 文でそれをやるとインスタンス */ /* を2回コピーされるというおかしな事になるので void になっている */ void fits_header::set_scopy_flag() { this->shallow_copy_ok = true; return; } /* * protected member functions */ /** * @brief fits_header_record オブジェクトのアドレスを返す * * @note このメンバ関数は protected です. */ fits_header_record *fits_header::get_ptr( long index0 ) { if ( index0 < 0 || this->num_records_rec <= index0 ) { return NULL; } else return this->records_ptr_rec[index0]; } /** * @brief 指定されたヘッダレコードオブジェクトのアドレスを返す * * @note ユーザの拡張クラスで使用を想定.ポインタを張る場合に必要.
* このメンバ関数は protected です. */ fits_header_record *fits_header::get_ptr( const char *keyword0 ) { return this->get_ptr( this->index(keyword0,false) ); } /** * @brief 指定されたヘッダレコードの保護に関する設定 * * @note ポインタを張った時,消えないように保護するために必要.
* このメンバ関数は protected です. */ fits_header &fits_header::set_protections( long index0, bool keyword, bool value_type, bool value, bool comment ) { fits_header_record *sys_ptr; sys_ptr = this->get_ptr(index0); if ( sys_ptr == NULL ) { err_report(__FUNCTION__,"WARNING","invalid index0; ignored"); return *this; } //bool tf = sys_ptr->keyword_protected_rec; sys_ptr->set_protections(keyword,value_type,value,comment); return *this; } /** * @brief 自身を管理するためのオブジェクトを登録 * * @note fits_hdu のコンストラクタで使用.
* このメンバ関数は protected です. */ fits_header &fits_header::register_manager( fits_hdu *ptr ) { this->manager = ptr; return *this; } /* fits_hdu から使う */ /** * @brief Data Unit に関連するヘッダレコードの編集禁止フラグをセット * * true をセットすると,システム予約キーワードは登録不可になる. * * @note 継承クラスでシステムキーワードを書き込む場合に使用.
* このメンバ関数は protected です. */ fits_header &fits_header::set_sysrecords_prohibition( bool value ) { this->sysrecords_prohibition_rec = value; return *this; } /** * @brief ストリームにアクセスし,Data Unit を読み飛ばす * * @param sref 読み飛ばし対象のストリーム (cstreamioの継承クラス) * @param max_bytes_ptr 読み飛ばされるべき最大のバイト数.設定不要時はNULL. * @return 非負値: 読み飛ばしたバイト数
* 負値: エラー * @note このメンバ関数は private です. */ ssize_t fits_header::data_skip( cstreamio &sref, const size_t *max_bytes_ptr ) { ssize_t return_len_skip = -1; size_t len_data_part, len_all, len_skiped_all; size_t len_to_skip[3]; long naxis; long bytepix; long idx,i; int hdu_type; bool extend = true; hdu_type = this->hdutype(); if ( 0 <= this->index("SIMPLE") ) { idx = this->index("EXTEND"); if ( 0 <= idx ) extend = this->record(idx).bvalue(); } /* normal data area */ idx = this->index("BITPIX"); if ( idx < 0 ) goto quit; bytepix = this->record(idx).llvalue(); if ( bytepix == 0 ) { return_len_skip = 0; goto quit; } if ( bytepix < 0 ) bytepix *= -1; bytepix--; bytepix /= 8; bytepix++; idx = this->index("NAXIS"); if ( idx < 0 ) goto quit; naxis = this->record(idx).llvalue(); if ( naxis < 0 ) goto quit; if ( naxis == 0 ) { return_len_skip = 0; goto quit; } len_to_skip[0] = bytepix; for ( i=0 ; i < naxis ; i++ ) { tstring kwd; long long llv; kwd.assignf("NAXIS%ld",i+1); idx = this->index(kwd.cstr()); if ( idx < 0 ) goto quit; llv = this->record(idx).llvalue(); if ( 0 <= llv ) len_to_skip[0] *= llv; } /* heap area of binary table HDU */ len_to_skip[1] = 0; idx = this->index("PCOUNT"); if ( hdu_type == FITS::BINARY_TABLE_HDU && 0 <= idx ) { long long llv; llv = this->record(idx).llvalue(); if ( 0 <= llv ) len_to_skip[1] += llv; } if ( len_to_skip[0] == 0 && len_to_skip[1] == 0 ) { return_len_skip = 0; goto quit; } len_data_part = len_to_skip[0] + len_to_skip[1]; /* 2880 の倍々にする */ if ( len_data_part % FITS::FILE_RECORD_UNIT != 0 ) { len_to_skip[2] = FITS::FILE_RECORD_UNIT - (len_data_part % FITS::FILE_RECORD_UNIT); } len_all = len_to_skip[0] + len_to_skip[1] + len_to_skip[2]; /* max_bytes_ptr != NULL の制限処置 */ if ( max_bytes_ptr != NULL ) { if ( (*max_bytes_ptr) < len_to_skip[0] ) { len_to_skip[0] = (*max_bytes_ptr); len_to_skip[1] = 0; len_to_skip[2] = 0; } else if ( (*max_bytes_ptr) < len_data_part ) { len_to_skip[1] = (*max_bytes_ptr) - len_to_skip[0]; len_to_skip[2] = 0; } else if ( (*max_bytes_ptr) < len_all ) { len_to_skip[2] = (*max_bytes_ptr) - len_data_part; } } /* EXTEND = F の場合で最後が 0 でパディングされていない場合も考え, */ /* len_to_skip[0], len_to_skip[1], len_to_skip[2] の順にskipする */ len_skiped_all = 0; for ( i=0 ; i < 3 ; i++ ) { size_t len_skiped = 0; if ( 0 < len_to_skip[i] ) { ssize_t sz; /* Use seek when available :-) */ try { sz = sref.rskip(len_to_skip[i]); } catch (...) { err_throw(__FUNCTION__,"FATAL","unexpected exception"); } if ( i == 2 && extend == false && sz == 0 ) { /* EXTEND = F の場合で 0 パディングが無い場合: OK */ len_skiped = sz; } else if ( sz < 0 ) { err_report(__FUNCTION__,"ERROR","sref.rskip() failed"); goto quit; } else if ( (size_t)sz != len_to_skip[i] ) { err_report(__FUNCTION__,"ERROR","sref.rskip() failed"); goto quit; } else { len_skiped = sz; } } len_skiped_all += len_skiped; } //err_report1(__FUNCTION__,"DEBUG","len_skiped_all = %zd",len_skiped_all); return_len_skip = len_skiped_all; quit: return return_len_skip; } /* Update: 2011/11/23 */ /** * @brief ストリームから FITS ヘッダを読み込み,ヘッダオブジェクトを構成 * * @param sref 読み込み対象のストリーム (cstreamioの継承クラス) * @param max_bytes_ptr 読み込まれるべき最大のバイト数.設定不要時はNULL. * @return 非負値: 読み込んだバイト数
* 負値: エラー * @note 次の HDU があるかないかをチェックする場合にも使われる.
* このメンバ関数は private です. */ ssize_t fits_header::header_load(cstreamio &sref, const size_t *max_bytes_ptr) { ssize_t return_len_read = -1; size_t len_read_all = 0; bool flag_end = false; char rec_buf[FITS::HEADER_RECORD_UNIT + 1]; tstring rec_str, header_all; long rec_index = 0; rec_buf[FITS::HEADER_RECORD_UNIT] = '\0'; /* initialize; erase all header records */ this->fits_header::_init(); /* read header records one by one ... */ while ( 1 ) { ssize_t len_read; /* END が見つかっていて,ピッタリの場合はループ抜け */ if ( flag_end == true ) { if ( len_read_all % FITS::FILE_RECORD_UNIT == 0 ) break; } /* 読めるバイト数に制限がある場合のチェック */ if ( max_bytes_ptr != NULL ) { if ( (*max_bytes_ptr) < len_read_all + FITS::HEADER_RECORD_UNIT ) { /* 中途半端の場合は許されているとこまで読みきってしまう */ try { len_read = sref.read(rec_buf, (*max_bytes_ptr) - len_read_all); } catch (...) { err_throw(__FUNCTION__,"FATAL","unexpected exception"); } if ( len_read < 0 ) { err_report(__FUNCTION__,"ERROR","sref.read() failed"); goto quit; } len_read_all += len_read; break; } } /* read stream ... */ try { len_read = sref.read(rec_buf, FITS::HEADER_RECORD_UNIT); } catch (...) { err_throw(__FUNCTION__,"FATAL","unexpected exception"); } if ( len_read < 0 ) { err_report1(__FUNCTION__,"ERROR", "sref.read() failed: len_read=%zd",len_read); goto quit; } len_read_all += len_read; if ( len_read != (ssize_t)FITS::HEADER_RECORD_UNIT ) break; /* EOF */ /* store a record to temp buffer */ rec_str.import_binary(rec_buf, len_read, ' '); if ( flag_end == false ) { /* When END is detected,set flag_end = true */ if ( rec_str.strncmp("END",3) == 0 && rec_str.strspn(3," \t\n\r\f\v") == FITS::HEADER_RECORD_UNIT - 3 ) { flag_end = true; } } /* append a record to header buffer */ if (header_all.length() < (rec_index + 1)*FITS::HEADER_RECORD_UNIT) { if ( header_all.length() < FITS::FILE_RECORD_UNIT * 10 ) { header_all.resizeby(FITS::FILE_RECORD_UNIT * 10); } else { header_all.resizeby( header_all.length() ); } } header_all.put(rec_index * FITS::HEADER_RECORD_UNIT, rec_str); rec_index ++; } header_all.resize(rec_index * FITS::HEADER_RECORD_UNIT); //err_report1(__FUNCTION__,"DEBUG","hdr length = %zd",header_all.length()); //err_report1(__FUNCTION__,"DEBUG","cstr = [%s]",header_all.cstr()); return_len_read = this->header_load(header_all.cstr()); quit: return return_len_read; } /* Update: 2011/11/23 */ /** * @brief 全ヘッダレコードを含んだ文字列を読み込み,ヘッダオブジェクトを構成 * * @param header_all ヘッダの内容 ('\0' で終端した文字列) * @note このメンバ関数は private です. */ ssize_t fits_header::header_load( const char *header_all ) { ssize_t return_len_read = -1; size_t len_read_all = 0; bool flag_end = false; tstring rec_str(FITS::HEADER_RECORD_UNIT); /* fixed-length mode */ long rec_index = 0; /* initialize; erase all header records */ this->fits_header::_init(); /* read header records one by one ... */ while ( 1 ) { ssize_t len_read; if ( header_all == NULL ) break; if ( flag_end == true ) { if ( len_read_all % FITS::FILE_RECORD_UNIT == 0 ) break; } rec_str.assign(header_all + rec_index * FITS::HEADER_RECORD_UNIT, FITS::HEADER_RECORD_UNIT); len_read = rec_str.length(); len_read_all += len_read; if ( len_read != (ssize_t)FITS::HEADER_RECORD_UNIT ) break; /* EOF */ if ( flag_end == false ) { /* When END is detected,set flag_end = true */ if ( rec_str.strncmp("END",3) == 0 && rec_str.strspn(3," \t\n\r\f\v") == FITS::HEADER_RECORD_UNIT - 3 ) { flag_end = true; } } if ( flag_end == false ) { fits_header_record a_record; /* parse a line (private/parse_a_header_record.cc) */ if ( parse_a_header_record(rec_str, false, &a_record) < 0 ) { if ( rec_index == 0 ) { err_report(__FUNCTION__,"ERROR", "This is not FITS header!"); goto quit; } } /* オリジナルの 80-char を保存 */ a_record.a_formatted_rec = rec_str; /* 追加 */ this->append( a_record ); } rec_index ++; } //err_report(__FUNCTION__,"DEBUG","1st step parse end"); if ( this->length() < 1 ) { return_len_read = len_read_all; goto quit; } else { /* CONTINUE を展開する */ this->expand_continue_records(); } #if 0 for ( i=0 ; i < this->length() ; i++ ) { fprintf(stderr,"debug1: [%s] = [%s] / [%s]\n", this->record(i).keyword(), this->record(i).value(), this->record(i).comment()); } #endif //err_report(__FUNCTION__,"DEBUG","2st step parse end"); return_len_read = len_read_all; quit: return return_len_read; } /** * @brief ヘッダの内容をストリームへ書き出す * * @note 注意: ここではチェックサムは初期化されない.呼び出し側で初期化する * 事.
* このメンバ関数は private です. */ ssize_t fits_header::header_save( cstreamio *sptr, void *c_sum_info, bool end_and_blank ) { ssize_t return_size = -1; size_t size_all = 0; fitsio_csum *csum_info = (fitsio_csum *)c_sum_info; ssize_t len; if ( csum_info != NULL && end_and_blank == false ) { err_report(__FUNCTION__,"WARNING", "Invalid end_and_blank arg; ignored"); end_and_blank = true; } /* write formatted string to stream */ this->formatted_string(); len = this->formatted_rec.length(); if ( 0 < len ) { ssize_t len_wrote; try { len_wrote = write_stream_or_get_csum( this->formatted_rec.cstr(), len, sptr, csum_info ); } catch (...) { err_throw(__FUNCTION__,"FATAL", "unexpected exception in write_stream_or_get_csum()"); } if ( len_wrote != len ) { err_report(__FUNCTION__,"ERROR", "write_stream_or_get_csum() failed"); goto quit; } size_all += len; } /* write "END" string and white space to make total length 2880 x N */ if ( 0 < len && end_and_blank == true ) { tstring tmp_str; size_t block; ssize_t len_wrote; len = FITS::HEADER_RECORD_UNIT; /* END を書く */ tmp_str.assign("END"); tmp_str.append(' ',len - 3); try { len_wrote = write_stream_or_get_csum( tmp_str.cstr(), len, sptr, csum_info ); } catch (...) { err_throw(__FUNCTION__,"FATAL", "unexpected exception in write_stream_or_get_csum()"); } if ( len_wrote != len ) { err_report(__FUNCTION__,"ERROR", "write_stream_or_get_csum() failed"); goto quit; } size_all += len; block = 1 + (size_all - 1) / FITS::FILE_RECORD_UNIT; tmp_str.assign(' ',len); while ( size_all < block * FITS::FILE_RECORD_UNIT ) { try { len_wrote = write_stream_or_get_csum( tmp_str.cstr(), len, sptr, csum_info ); } catch (...) { err_throw(__FUNCTION__,"FATAL", "unexpected exception in write_stream_or_get_csum()"); } if ( len_wrote != len ) { err_report(__FUNCTION__,"ERROR", "write_stream_or_get_csum() failed"); goto quit; } size_all += len; } #if 0 try { if ( 0 < size_all && sptr != NULL ) sptr->flush(); } catch (...) { err_throw(__FUNCTION__,"FATAL", "unexpected exception in sptr->flush()"); } #endif } return_size = size_all; quit: return return_size; } /** * @brief shallow copy が可能かを返す (未実装) * * @return shallow copyが可能なら真
* それ以外の時は偽 * @note このメンバ関数は private です */ bool fits_header::request_shallow_copy( fits_header *from_obj ) const { return false; } /** * @brief shallow copy をキャンセルする (未実装) * * @note このメンバ関数は private です */ void fits_header::cancel_shallow_copy( fits_header *from_obj ) const { return; } /** * @brief 自身について,shallow copy 関係のクリーンアップを行なう (未実装) * * @note このメンバ関数は private です */ void fits_header::cleanup_shallow_copy( bool do_deep_copy_for_this ) const { return; } #include "private/parse_a_header_record.cc" #include "private/initialize_csum.cc" #include "private/encode_csum.cc" #include "private/write_stream_or_get_csum.cc" } /* namespace sli */ #include "private/rm_side_spaces.c" #include "private/c_memcpy.cc"