Index: /trunk/FACT++/src/fitsdump.cc
===================================================================
--- /trunk/FACT++/src/fitsdump.cc	(revision 10793)
+++ /trunk/FACT++/src/fitsdump.cc	(revision 10793)
@@ -0,0 +1,604 @@
+//****************************************************************
+/** @class FitsDumper
+
+  @brief Dumps contents of fits tables to stdout or a file
+
+ */
+ //****************************************************************
+#include "FACT.h"
+#include "Configuration.h"
+
+#include <map>
+#include <fstream>
+
+#include <CCfits/CCfits>
+
+using namespace std;
+
+class FitsDumper
+{
+
+public:
+    FitsDumper();
+    ~FitsDumper();
+
+private:
+    
+    CCfits::FITS  *fFile;                 /// FITS pointer
+    CCfits::Table *fTable;                /// Table pointer
+    map<string, CCfits::Column*> fColMap; /// map between the column names and their CCfits objects
+
+    // Convert CCfits::ValueType into a human readable string
+    string ValueTypeToStr(CCfits::ValueType type) const;
+
+    // Convert CCfits::ValueType into a number of associated bytes
+    int    ValueTypeToSize(CCfits::ValueType type) const;
+
+    /// Calculate the buffer size required to read a row of the fits table, as well as the offsets to each column
+    vector<int> CalculateOffsets() const;
+
+    template<class T>
+        void Write(ostream &out, const unsigned char* &ptr) const;
+
+    /// Write a single row of the selected data
+    int  WriteRow(ostream &, const vector<string> &, const vector<int> &, unsigned char *) const;
+
+    bool OpenFile(const string &);        /// Open a file
+    bool OpenTable(const string &);       /// Open a table
+
+    /// Lists all columns of an open file
+    void List();                          
+    void ListHeader();
+    void ListKeywords(ostream &);
+
+    /// Perform the dumping, based on the current dump list
+    bool Dump(const string &, const vector<string> &list, int);
+
+
+public:
+    ///Configures the fitsLoader from the config file and/or command arguments.
+    int ExecConfig(Configuration& conf);
+};
+
+// --------------------------------------------------------------------------
+//
+//! Constructor
+//! @param out
+//!        the ostream where to redirect the outputs
+//
+FitsDumper::FitsDumper() : fFile(0), fTable(0)
+{
+}
+
+// --------------------------------------------------------------------------
+//
+//! Destructor
+//
+FitsDumper::~FitsDumper()
+{
+    if (fFile)
+        delete fFile;
+}
+
+
+string FitsDumper::ValueTypeToStr(CCfits::ValueType type) const
+{
+    switch (type)
+    {
+    case CCfits::Tbyte:     return "uint8_t";
+    case CCfits::Tushort:   return "uint16_t";
+    case CCfits::Tshort:    return "int16_t";
+    case CCfits::Tuint:     return "uint32_t";
+    case CCfits::Tint:      return "int32_t";
+    case CCfits::Tulong:    return "uint32_t";
+    case CCfits::Tlong:     return "int32_t";
+    case CCfits::Tlonglong: return "int64_t";
+    case CCfits::Tfloat:    return "float";
+    case CCfits::Tdouble:   return "double";
+
+    default:
+        return "unknwown";
+    }
+}
+
+int FitsDumper::ValueTypeToSize(CCfits::ValueType type) const
+{
+    switch (type)
+    {
+    case CCfits::Tbyte:     return sizeof(uint8_t);
+    case CCfits::Tushort:
+    case CCfits::Tshort:    return sizeof(uint16_t);
+    case CCfits::Tuint:
+    case CCfits::Tint:
+    case CCfits::Tulong:
+    case CCfits::Tlong:     return sizeof(uint32_t);
+    case CCfits::Tlonglong: return sizeof(uint64_t);
+    case CCfits::Tfloat:    return sizeof(float);
+    case CCfits::Tdouble:   return sizeof(double);
+
+    default:
+        return 0;
+    }
+}
+
+template<class T>
+void FitsDumper::Write(ostream &out, const unsigned char* &ptr) const
+{
+    T t;
+    reverse_copy(ptr, ptr+sizeof(T), reinterpret_cast<unsigned char*>(&t));
+    out << t;
+
+    ptr += sizeof(T);
+}
+
+
+// --------------------------------------------------------------------------
+//
+//! Writes a single row of the selected FITS data to the output file.
+//!
+//! Data type \b not yet implemented:
+//!   CCfits::Tnull, CCfits::Tbit, CCfits::Tlogical, CCfits::Tstring,
+//!   CCfits::Tcomplex, CCfits::Tdblcomplex, CCfits::VTbit,
+//!   CCfits::VTbyte, CCfits::VTlogical, CCfits::VTushort,
+//!   CCfits::VTshort, CCfits::VTuint, CCfits::VTint, CCfits::VTulong,
+//!   CCfits::VTlong, CCfits::VTlonglong, CCfits::VTfloat,
+//!   CCfits::VTdouble, CCfits::VTcomplex, CCfits::VTdblcomplex
+//!
+//! @param offsets
+//!         a vector containing the offsets to the columns (in bytes)
+//! @param targetFile
+//!         the ofstream where to write to
+//! @param fitsBuffer
+//!         the memory were the row has been loaded by cfitsio
+//
+int FitsDumper::WriteRow(ostream &out, const vector<string> &list, const vector<int> &offsets, unsigned char* fitsBuffer) const
+{
+    int cnt = 0;
+
+    for (vector<string>::const_iterator it=list.begin(); it!=list.end(); it++)
+    {
+        // Loop over all columns in our list of requested columns
+        const CCfits::Column *col = fColMap.find(*it)->second;
+
+        // CCfits starts counting at 1 not 0
+        const int offset = offsets[col->index()-1];
+
+        // Get the pointer to the array which we are supposed to print
+        const unsigned char *ptr = fitsBuffer + offset;
+
+        // Loop over all array entries
+        for (int width=0; width<col->width(); width++)
+        {
+            switch (col->type())
+            {
+            case CCfits::Tbyte:     Write<uint8_t> (out, ptr); break;
+            case CCfits::Tushort:   Write<uint16_t>(out, ptr); break;
+            case CCfits::Tuint:
+            case CCfits::Tulong:    Write<uint32_t>(out, ptr); break;
+            case CCfits::Tshort:    Write<int16_t> (out, ptr); break;
+            case CCfits::Tint:
+            case CCfits::Tlong:     Write<int32_t> (out, ptr); break;
+            case CCfits::Tlonglong: Write<int64_t> (out, ptr); break;
+            case CCfits::Tfloat:    Write<float>   (out, ptr); break;
+            case CCfits::Tdouble:   Write<double>  (out, ptr); break;
+
+            default:
+                cerr << "Data type not implemented yet." << endl;
+                return 0;
+            }
+
+            out << " ";
+            cnt++;
+
+            if (out.fail())
+            {
+                cerr << "ERROR - writing output: " << strerror(errno) << endl;
+                return 0;
+            }
+        }
+    }
+
+    if (cnt>0)
+        out << endl;
+
+    return cnt;
+}
+
+// --------------------------------------------------------------------------
+//
+//! Calculates the required buffer size for reading one row of the current
+//! table. Also calculates the offsets to all the columns
+//
+vector<int> FitsDumper::CalculateOffsets() const
+{
+    map<int,int> sizes;
+
+    for (map<string, CCfits::Column*>::const_iterator it=fColMap.begin();
+         it!=fColMap.end(); it++)
+    {
+        const int &width = it->second->width();
+        const int &idx   = it->second->index();
+
+        const int size = ValueTypeToSize(it->second->type());
+        if (size==0)
+        {
+            cerr << "Data type " << (int)it->second->type() << " not implemented yet." << endl;
+            return vector<int>();
+        }
+
+        sizes[idx] = size*width;
+    }
+
+    //calculate the offsets in the vector.
+    vector<int> result(1, 0);
+
+    int size = 0;
+    int idx  = 0;
+
+    for (map<int,int>::const_iterator it=sizes.begin(); it!=sizes.end(); it++)
+    {
+        size += it->second;
+        result.push_back(size);
+
+        if (it->first == ++idx)
+            continue;
+
+        cerr << "Expected index " << idx << ", but found " << it->first << endl;
+        return vector<int>();
+    }
+
+    return result;
+}
+
+// --------------------------------------------------------------------------
+//
+//! Loads the fits file based on the current parameters
+//
+bool FitsDumper::OpenFile(const string &filename)
+{
+    if (fFile)
+        delete fFile;
+
+    ostringstream str;
+    try
+    {
+        fFile = new CCfits::FITS(filename);
+    }
+    catch (CCfits::FitsException e)
+    {
+        cerr << "Could not open FITS file " << filename << " reason: " << e.message() << endl;
+        return false;
+    }
+
+    return true;
+}
+
+bool FitsDumper::OpenTable(const string &tablename)
+{
+    if (!fFile)
+    {
+        cerr << "No file open." << endl;
+        return false;
+    }
+
+    const multimap< string, CCfits::ExtHDU * > extMap = fFile->extension();
+    if (extMap.find(tablename) == extMap.end())
+    {
+        cerr << "Table '" << tablename << "' not found." << endl;
+        return false;
+    }
+
+    fTable  = dynamic_cast<CCfits::Table*>(extMap.find(tablename)->second);
+    if (!fTable)
+    {
+        cerr << "Object '" << tablename << "' returned not a CCfits::Table." << endl;
+        return false;
+    }
+
+    fColMap = fTable->column();
+
+    fTable->makeThisCurrent();
+
+    fTable->getComments();
+    fTable->getHistory();
+    fTable->readAllKeys();
+
+    return true;
+}
+
+class MyColumn : public CCfits::Column
+{
+public:
+    const string &comment() const { return CCfits::Column::comment(); }
+};
+
+void FitsDumper::List()
+{
+    if (!fFile)
+    {
+        cerr << "No file open." << endl;
+        return;
+    }
+
+    cout << "\nFile: " << fFile->name() << "\n";
+
+    const multimap< string, CCfits::ExtHDU * > extMap = fFile->extension();
+    for (std::multimap<string, CCfits::ExtHDU*>::const_iterator it=extMap.begin(); it != extMap.end(); it++)
+    {
+
+        CCfits::Table *table = dynamic_cast<CCfits::Table*>(extMap.find(it->first)->second);
+
+        cout << " " << it->first << " [" << table->rows() << "]\n";
+
+        const map<string, CCfits::Column*> &cols = table->column();
+
+        for (map<string, CCfits::Column*>::const_iterator ic=cols.begin();
+             ic != cols.end(); ic++)
+        {
+            const MyColumn *col = static_cast<MyColumn*>(ic->second);
+
+            cout << "   " << col->name() << "[" << col->width() << "] * " << col->scale() << " (" << col->unit() << ":" <<  ValueTypeToStr(col->type())<<  ") " << col->comment() << "\n";
+            /*
+             inline size_t Column::repeat () const
+             inline bool   Column::varLength () const
+             inline double Column::zero () const
+             inline const  String& Column::display () const
+             inline const  String& Column::dimen () const
+             inline const  String& Column::TBCOL ()
+             inline const  String& Column::TTYPE ()
+             inline const  String& Column::TFORM ()
+             inline const  String& Column::TDISP ()
+             inline const  String& Column::TZERO ()
+             inline const  String& Column::TDIM  ()
+             inline const  String& Column::TNULL ()
+             inline const  String& Column::TLMIN ()
+             inline const  String& Column::TLMAX ()
+             inline const  String& Column::TDMAX ()
+             inline const  String& Column::TDMIN ()
+            */
+        }
+        cout << '\n';
+    }
+    cout << flush;
+}
+
+class MyKeyword : public CCfits::Keyword
+{
+public:
+    CCfits::ValueType keytype() const { return CCfits::Keyword::keytype(); }
+};
+
+void FitsDumper::ListKeywords(ostream &out)
+{
+    map<string,CCfits::Keyword*> keys = fTable->keyWord();
+    for (map<string,CCfits::Keyword*>::const_iterator it=keys.begin();
+         it!=keys.end(); it++)
+    {
+        string str;
+        double d;
+
+        const MyKeyword *kw = static_cast<MyKeyword*>(it->second);
+        kw->keytype();
+        out << "## " << setw(8) << kw->name() << "='";
+        if (kw->keytype()==16)
+            out << kw->value(str);
+        if (kw->keytype()==82)
+            out << kw->value(d);
+        out << "' \t(" << kw->comment() << ")" << endl;
+    }
+}
+
+void FitsDumper::ListHeader()
+{
+    if (!fTable)
+    {
+        cerr << "No table open." << endl;
+        return;
+    }
+
+    cout << "\nTable: " << fTable->name() << " (rows=" << fTable->rows() << ")\n";
+    if (!fTable->comment().empty())
+        cout << "Comment: \t" << fTable->comment() << '\n';
+    if (!fTable->history().empty())
+        cout << "History: \t" << fTable->history() << '\n';
+
+    ListKeywords(cout);
+    cout << endl;
+}
+
+
+// --------------------------------------------------------------------------
+//
+//! Perform the actual dump, based on the current parameters
+//
+bool FitsDumper::Dump(const string &filename, const vector<string> &list, int precision)
+{
+    for (vector<string>::const_iterator it=list.begin(); it!=list.end(); it++)
+        if (fColMap.find(*it) == fColMap.end())
+        {
+            cerr << "WARNING - Column '" << *it << "' not found in table." << endl;
+            return false;
+        }
+
+
+    const vector<int> offsets = CalculateOffsets();
+    if (offsets.size()==0)
+        return false;
+
+    const int size = offsets[offsets.size()-1];
+
+    ofstream out(filename=="-"?"/dev/stdout":filename);
+    if (!out)
+    {
+        cerr << "Cannot open file " << filename << ": " << strerror(errno) << endl;
+        return false;
+    }
+
+    out.precision(precision);
+
+    out << "## --------------------------------------------------------------------------\n";
+    if (filename!="-")
+        out << "## File:    \t" << filename << '\n';
+    out << "## Table:   \t" << fTable->name() << '\n';
+    if (!fTable->comment().empty())
+        out << "## Comment: \t" << fTable->comment() << '\n';
+    if (!fTable->history().empty())
+        out << "## History: \t" << fTable->history() << '\n';
+    out << "## NumRows: \t" << fTable->rows() << '\n';
+    out << "## --------------------------------------------------------------------------\n";
+    ListKeywords(out);
+    out << "## --------------------------------------------------------------------------\n";
+    out << "#\n";
+    for (vector<string>::const_iterator it=list.begin(); it!=list.end(); it++)
+    {
+        const MyColumn *col = static_cast<MyColumn*>(fTable->column()[*it]);
+        out << "# " << col->name() << "[" << col->width() << "]: " << col->unit();
+        if (!col->comment().empty())
+            out << " (" <<col->comment() << ")";
+        out << '\n';
+    }
+    out << "#" << endl;
+
+    unsigned char* fitsBuffer = new unsigned char[size];
+
+    int status = 0;
+    for (int i=1; i<=fTable->rows(); i++)
+    {
+        fits_read_tblbytes(fFile->fitsPointer(), i, 1, size, fitsBuffer, &status);
+        if (status)
+        {
+            cerr << "An error occurred while reading fits row #" << i << " error code: " << status << endl;
+            break;
+        }
+        WriteRow(out, list, offsets, fitsBuffer);
+    }
+    delete[] fitsBuffer;
+
+    return status;
+}
+
+// --------------------------------------------------------------------------
+//
+//! Retrieves the configuration parameters
+//! @param conf
+//!             the configuration object
+//
+int FitsDumper::ExecConfig(Configuration& conf)
+{
+    if (conf.Has("fitsfile"))
+    {
+        if (!OpenFile(conf.Get<string>("fitsfile")))
+            return -1;
+    }
+
+    if (conf.Get<bool>("list"))
+        List();
+
+    if (conf.Has("tablename"))
+    {
+        if (!OpenTable(conf.Get<string>("tablename")))
+            return -1;
+    }
+
+    if (conf.Get<bool>("header"))
+        ListHeader();
+
+    if (conf.Get<bool>("header") || conf.Get<bool>("list"))
+        return 1;
+
+    if (conf.Has("outfile"))
+    {
+        if (!Dump(conf.Get<string>("outfile"),
+                  conf.Get<vector<string>>("col"),
+                  conf.Get<int>("precision")))
+            return -1;
+    }
+
+    return 0;
+}
+
+void PrintUsage()
+{
+    cout <<
+        "fitsdump is a tool to dump data from a FITS table as ascii.\n"
+        "\n"
+        "Usage: fitsdump [OPTIONS] fitsfile col col ... \n"
+        "  or:  fitsdump [OPTIONS]\n";
+    cout << endl;
+}
+
+void PrintHelp()
+{
+    // 
+}
+
+void SetupConfiguration(Configuration& conf)
+{
+    po::options_description configs("Fitsdump options");
+    configs.add_options()
+        ("fitsfile,f",  var<string>()
+#if BOOST_VERSION >= 104200
+         ->required()
+#endif
+                                                  , "Name of FITS file")
+        ("tablename,t", var<string>("DATA")
+#if BOOST_VERSION >= 104200
+         ->required()
+#endif
+                                                  , "Name of input table")
+        ("col,c",       vars<string>(),             "List of columns to dump")
+        ("outfile,o",   var<string>("/dev/stdout"), "Name of output file (-:/dev/stdout)")
+        ("precision,p", var<int>(20),               "Precision of ofstream")
+        ("list,l",      po_switch(),                "List all tables and columns in file")
+        ("header,h",    po_switch(),                "Dump header of given table")
+        ;
+
+    po::positional_options_description p;
+    p.add("fitsfile",   1); // The first positional options
+    p.add("col",       -1); // All others
+
+    conf.AddOptions(configs);
+    conf.SetArgumentPositions(p);
+}
+
+int main(int argc, const char** argv)
+{
+    Configuration conf(argv[0]);
+    conf.SetPrintUsage(PrintUsage);
+    SetupConfiguration(conf);
+
+    po::variables_map vm;
+    try
+    {
+        vm = conf.Parse(argc, argv);
+    }
+#if BOOST_VERSION > 104000
+    catch (po::multiple_occurrences &e)
+    {
+        cerr << "Program options invalid due to: " << e.what() << " of option '" << e.get_option_name() << "'." << endl;
+        return -1;
+    }
+#endif
+    catch (exception& e)
+    {
+        cerr << "Program options invalid due to: " << e.what() << endl;
+        return -1;
+    }
+
+    if (conf.HasPrint())
+        return -1;
+    if (conf.HasVersion())
+    {
+        FACT::PrintVersion(argv[0]);
+        return -1;
+    }
+
+    if (conf.HasHelp())
+    {
+        PrintHelp();
+        return -1;
+    }
+
+    FitsDumper loader;
+    return loader.ExecConfig(conf);
+}
