1 /* Note: this file has been modified from its original form by the
2  Astrometry.net team.  For details see http://astrometry.net */
3 
4 /* $Id: qfits_table.c,v 1.20 2007/01/10 12:24:45 yjung Exp $
5  *
6  * This file is part of the ESO QFITS Library
7  * Copyright (C) 2001-2004 European Southern Observatory
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23 
24 /*
25  * $Author: yjung $
26  * $Date: 2007/01/10 12:24:45 $
27  * $Revision: 1.20 $
28  * $Name: qfits-6_2_0 $
29  */
30 
31 /*-----------------------------------------------------------------------------
32  Includes
33  -----------------------------------------------------------------------------*/
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <ctype.h>
40 #include <errno.h>
41 #include <assert.h>
42 #include <math.h>
43 
44 #include "qfits_config.h"
45 #include "qfits_table.h"
46 #include "qfits_float.h"
47 #include "qfits_std.h"
48 #include "qfits_byteswap.h"
49 #include "qfits_tools.h"
50 #include "qfits_time.h"
51 #include "qfits_rw.h"
52 #include "qfits_md5.h"
53 #include "qfits_error.h"
54 #include "qfits_memory.h"
55 
56 /*-----------------------------------------------------------------------------
57  Define
58  -----------------------------------------------------------------------------*/
59 
60 #define ELEMENT_MAX_DISPLAY_SIZE    50
61 
62 /*-----------------------------------------------------------------------------
63  Function prototypes
64  -----------------------------------------------------------------------------*/
65 
66 static char * qfits_bintable_field_to_string(const qfits_table *, int, int,int);
67 static char * qfits_asciitable_field_to_string(const qfits_table *, int, int,
68                                                int);
69 static char * qfits_build_format(const qfits_col *);
70 static int qfits_table_append_bin_xtension(FILE *, const qfits_table *,
71                                            const void **);
72 static int qfits_table_append_ascii_xtension(FILE *, const qfits_table *,
73                                              const void **);
74 static int qfits_table_append_data(FILE *, const qfits_table *, const void **);
75 static int qfits_table_get_field_size(int, const qfits_col *);
76 static char * qfits_strstrip(const char *);
77 static double qfits_str2dec(const char *, int);
78 
79 /*----------------------------------------------------------------------------*/
80 /**
81  * @defgroup    qfits_table FITS table handling
82  *
83  */
84 /*----------------------------------------------------------------------------*/
85 /**@{*/
86 
87 /*-----------------------------------------------------------------------------
88  Function codes
89  -----------------------------------------------------------------------------*/
90 
qfits_table_get_col(const qfits_table * t,int i)91 const qfits_col* qfits_table_get_col(const qfits_table* t, int i) {
92     return t->col + i;
93 }
94 
qfits_table_copy(const qfits_table * t)95 qfits_table* qfits_table_copy(const qfits_table* t) {
96     qfits_table* dest = calloc(1, sizeof(qfits_table));
97     assert(dest);
98     memcpy(dest, t, sizeof(qfits_table));
99     dest->col = calloc(dest->nc, sizeof(qfits_col));
100     memcpy(dest->col, t->col, dest->nc * sizeof(qfits_col));
101     return dest;
102 }
103 
104 
105 
qfits_is_table_header(const qfits_header * hdr)106 int qfits_is_table_header(const qfits_header* hdr) {
107     char* value;
108     int ttype;
109     char buf[FITS_LINESZ + 1];
110 
111     ttype = QFITS_INVALIDTABLE;
112     value = qfits_header_getstr(hdr, "XTENSION");
113     if (!value)
114         return ttype;
115     qfits_pretty_string_r(value, buf);
116     value = buf;
117     if (!strcmp(value, "TABLE")) {
118         ttype = QFITS_ASCIITABLE;
119     } else if (!strcmp(value, "BINTABLE")) {
120         ttype = QFITS_BINTABLE;
121     }
122     return ttype;
123 }
124 
125 /*----------------------------------------------------------------------------*/
126 /**
127  @brief    Generate a default primary header to store tables
128  @return    the header object
129  */
130 /*----------------------------------------------------------------------------*/
qfits_table_prim_header_default(void)131 qfits_header * qfits_table_prim_header_default(void)
132 {
133     qfits_header    *    fh;
134 
135     fh = qfits_header_new();
136 
137     qfits_header_append(fh, "SIMPLE", "T", "Standard FITS file", NULL);
138     qfits_header_append(fh, "BITPIX", "8", "ASCII or bytes array", NULL);
139     qfits_header_append(fh, "NAXIS", "0", "Minimal header", NULL);
140     qfits_header_append(fh, "EXTEND", "T", "There may be FITS ext", NULL);
141     qfits_header_append(fh, "END", NULL, NULL, NULL);
142 
143     return fh;
144 }
145 
146 /*----------------------------------------------------------------------------*/
147 /**
148  @brief    Generate a default extension header to store tables
149  @return   the header object
150  */
151 /*----------------------------------------------------------------------------*/
qfits_table_ext_header_default(const qfits_table * t)152 qfits_header * qfits_table_ext_header_default(const qfits_table * t)
153 {
154     qfits_header    *   fh;
155     qfits_col       *   curr_col;
156     char                str_val[FITS_LINESZ];
157     char                str_val2[FITS_LINESZ];
158     char            *   date;
159     int                 tab_width;
160     int                 col_pos;
161     int                 i;
162 
163     /* Compute the table width   */
164     if ((tab_width = qfits_compute_table_width(t)) == -1) {
165         qfits_error("cannot get the table width");
166         return NULL;
167     }
168 
169     /* Create fits header */
170     if ((fh=qfits_header_new()) == NULL) {
171         qfits_error("cannot create new fits header");
172         return NULL;
173     }
174 
175     /* Check the kind of table */
176     if (t->tab_t == QFITS_BINTABLE) {
177 
178         /* Write extension header */
179         qfits_header_append(fh, "XTENSION", "BINTABLE",
180                             "FITS Binary Table Extension", NULL);
181         qfits_header_append(fh, "BITPIX", "8", "8-bits character format", NULL);
182         qfits_header_append(fh, "NAXIS", "2","Tables are 2-D char. array",NULL);
183         sprintf(str_val, "%d", tab_width);
184         qfits_header_append(fh, "NAXIS1", str_val, "Bytes in row", NULL);
185         sprintf(str_val, "%d", (int)(t->nr));
186         qfits_header_append(fh, "NAXIS2", str_val, "No. of rows in table",NULL);
187         qfits_header_append(fh, "PCOUNT", "0", "Parameter count always 0",NULL);
188         qfits_header_append(fh, "GCOUNT", "1", "Group count always 1", NULL);
189         sprintf(str_val, "%d", (int)(t->nc));
190         qfits_header_append(fh, "TFIELDS", str_val, "No. of col in table",NULL);
191         /* Columns descriptors */
192         curr_col = t->col;
193         for (i=0; i<t->nc; i++) {
194             sprintf(str_val, "TFORM%d", i+1);
195             sprintf(str_val2, "'%s'", qfits_build_format(curr_col));
196             qfits_header_append(fh, str_val, str_val2, "Format of field", NULL);
197 
198             sprintf(str_val, "TTYPE%d", i+1);
199             sprintf(str_val2, "%s", curr_col->tlabel);
200             qfits_header_append(fh, str_val, str_val2, "Field label", NULL);
201 
202             if (strlen(curr_col->tunit)) {
203                 sprintf(str_val, "TUNIT%d", i+1);
204                 sprintf(str_val2, "%s", curr_col->tunit);
205                 //printf("Setting '%s' = '%s'\n", str_val, str_val2);
206                 qfits_header_append(fh, str_val, str_val2, "Physical unit of field",
207                                     NULL);
208             }
209             if (curr_col->zero_present) {
210                 sprintf(str_val, "TZERO%d", i+1);
211                 sprintf(str_val2, "%f", curr_col->zero);
212                 qfits_header_append(fh, str_val, str_val2,
213                                     "NULL value is defined", NULL);
214             }
215             if (curr_col->scale_present) {
216                 sprintf(str_val, "TSCAL%d", i+1);
217                 sprintf(str_val2, "%f", curr_col->scale);
218                 qfits_header_append(fh, str_val, str_val2, "Scaling applied",
219                                     NULL);
220             }
221             curr_col++;
222         }
223         qfits_header_append(fh,"ORIGIN","ESO-QFITS", "Written by QFITS", NULL);
224 
225         date = qfits_get_datetime_iso8601();
226         sprintf(str_val, "'%s'", date);
227         qfits_header_append(fh, "DATE", str_val, "[UTC] Date of writing", NULL);
228         qfits_header_append(fh, "END", NULL, NULL, NULL);
229 
230     } else if (t->tab_t == QFITS_ASCIITABLE) {
231 
232         /* Write extension header */
233         qfits_header_append(fh, "XTENSION", "TABLE",
234                             "FITS ASCII Table Extension", NULL);
235         qfits_header_append(fh, "BITPIX", "8", "8-bits character format", NULL);
236         qfits_header_append(fh, "NAXIS", "2", "ASCII table has 2 axes", NULL);
237 
238         /* Fill the header  */
239         sprintf(str_val, "%d", tab_width);
240         qfits_header_append(fh, "NAXIS1", str_val, "Characters in a row", NULL);
241         sprintf(str_val, "%d", (int)(t->nr));
242         qfits_header_append(fh, "NAXIS2", str_val, "No. of rows in table",NULL);
243         qfits_header_append(fh, "PCOUNT", "0", "No group parameters", NULL);
244         qfits_header_append(fh, "GCOUNT", "1", "Only one group", NULL);
245         sprintf(str_val, "%d", (int)(t->nc));
246         qfits_header_append(fh, "TFIELDS", str_val, "No. of col in table",NULL);
247         qfits_header_append(fh, "ORIGIN","ESO-QFITS","Written by QFITS",NULL);
248         date = qfits_get_datetime_iso8601();
249         sprintf(str_val, "'%s'", date);
250         qfits_header_append(fh, "DATE", str_val, "[UTC] Date of writing", NULL);
251 
252         /* Columns descriptors */
253         curr_col = t->col;
254         col_pos = 1;
255         for (i=0; i<t->nc; i++) {
256             sprintf(str_val, "TTYPE%d", i+1);
257             sprintf(str_val2, "%s", curr_col->tlabel);
258             qfits_header_append(fh, str_val, str_val2, "Field label", NULL);
259 
260             sprintf(str_val, "TFORM%d", i+1);
261             sprintf(str_val2, "'%s'", qfits_build_format(curr_col));
262             qfits_header_append(fh, str_val, str_val2, "Format of field", NULL);
263 
264             sprintf(str_val, "TBCOL%d", i+1);
265             sprintf(str_val2, "%d", col_pos);
266             qfits_header_append(fh, str_val, str_val2,"Start column of field",
267                                 NULL);
268             col_pos += curr_col->atom_nb;
269 
270             sprintf(str_val, "TUNIT%d", i+1);
271             sprintf(str_val2, "%s", curr_col->tunit);
272             qfits_header_append(fh, str_val, str_val2, "Physical unit of field",
273                                 NULL);
274             if (curr_col->zero_present) {
275                 sprintf(str_val, "TZERO%d", i+1);
276                 sprintf(str_val2, "%f", curr_col->zero);
277                 qfits_header_append(fh, str_val, str_val2,
278                                     "NULL value is defined", NULL);
279             }
280             if (curr_col->scale_present) {
281                 sprintf(str_val, "TSCAL%d", i+1);
282                 sprintf(str_val2, "%f", curr_col->scale);
283                 qfits_header_append(fh, str_val, str_val2, "Scaling applied",
284                                     NULL);
285             }
286             curr_col++;
287         }
288         qfits_header_append(fh, "END", NULL, NULL, NULL);
289 
290     } else {
291         qfits_error("Table type not known");
292         qfits_header_destroy(fh);
293         return NULL;
294     }
295     return fh;
296 }
297 
298 /*----------------------------------------------------------------------------*/
299 /**
300  @brief    Table object constructor
301  @param    filename    Name of the FITS file associated to the table
302  @param    table_type    Type of the table (QFITS_ASCIITABLE or QFITS_BINTABLE)
303  @param    table_width Width in bytes of the table
304  @param    nb_cols        Number of columns
305  @param    nb_raws        Number of raws
306  @return    The table object
307  The columns are also allocated. The object has to be deallocated with
308  qfits_table_close()
309  */
310 /*----------------------------------------------------------------------------*/
qfits_table_new(const char * filename,int table_type,int table_width,int nb_cols,int nb_raws)311 qfits_table * qfits_table_new(
312                               const char  *   filename,
313                               int             table_type,
314                               int             table_width,
315                               int             nb_cols,
316                               int             nb_raws)
317 {
318     qfits_table    *    qt;
319     qt = qfits_malloc(sizeof(qfits_table));
320     strcpy(qt->filename, filename);
321     qt->tab_t = table_type;
322     qt->nc = nb_cols;
323     qt->nr = nb_raws;
324     if (qt->nc)
325         qt->col = qfits_calloc(qt->nc, sizeof(qfits_col));
326     else
327         qt->col = NULL;
328     qt->tab_w = table_width;
329 
330     return qt;
331 }
332 
333 /*----------------------------------------------------------------------------*/
334 /**
335  @brief    Fill a column object with some provided informations
336  @param    qc      Pointer to the column that has to be filled
337  @param    unit    Unit of the data
338  @param    label   Label of the column
339  @param    disp    Way to display the data
340  @param    nullval Null value
341  @param    atom_nb Number of atoms per field. According to the type, an atom
342  is a double, an int, a char, ...
343  @param    atom_dec_nb Number of decimals as specified in TFORM
344  @param    atom_size    Size in bytes of the field for ASCII tables, and of
345  an atom for BIN tables. ASCII tables only contain 1
346  atom per field (except for A type where you can of
347  course have more than one char per field)
348  @param    atom_type    Type of data (11 types for BIN, 5 for ASCII)
349  @param    zero_present    Flag to use or not zero
350  @param    zero            Zero value
351  @param    scale_present   Flag to use or not scale
352  @param    scale           Scale value
353  @param    offset_beg  Gives the position of the column
354  @return     -1 in error case, 0 otherwise
355  */
356 /*----------------------------------------------------------------------------*/
qfits_col_fill(qfits_col * qc,int atom_nb,int atom_dec_nb,int atom_size,tfits_type atom_type,const char * label,const char * unit,const char * nullval,const char * disp,int zero_present,float zero,int scale_present,float scale,int offset_beg)357 int qfits_col_fill(
358                    qfits_col   *   qc,
359                    int             atom_nb,
360                    int             atom_dec_nb,
361                    int             atom_size,
362                    tfits_type      atom_type,
363                    const char  *   label,
364                    const char  *   unit,
365                    const char  *   nullval,
366                    const char  *   disp,
367                    int             zero_present,
368                    float           zero,
369                    int             scale_present,
370                    float           scale,
371                    int             offset_beg)
372 {
373     /* Number of atoms per column */
374     qc->atom_nb = atom_nb;
375 
376     /* Number of decimals in a field in ASCII table (0 in BINTABLE) */
377     qc->atom_dec_nb = atom_dec_nb;
378 
379     /* Size in bytes of an atom  */
380     qc->atom_size = atom_size;
381 
382     /* Data type in the column */
383     qc->atom_type = atom_type;
384 
385     /* Label of the column */
386     strcpy(qc->tlabel, label);
387 
388     /* Unit of the column data */
389     strcpy(qc->tunit, unit);
390 
391     /* Null value*/
392     strcpy(qc->nullval, nullval);
393 
394     /* How to display the data */
395     strcpy(qc->tdisp, disp);
396 
397     /* Default values for zero and scales */
398     qc->zero_present = zero_present;
399     qc->scale_present = scale_present;
400     qc->zero = zero;
401     qc->scale = scale;
402 
403     /* Number of bytes between two consecutive fields of the same column */
404     qc->off_beg = offset_beg;
405 
406     /* A column is a priori readable */
407     qc->readable = 1;
408 
409     return 0;
410 }
411 
412 /*
413  Creates a qfits_table* from the given qfits_header.
414  Also requires the offset to the beginning of the data unit, and its size.
415  (eg, via qfits_get_datinfo)
416  The "filename" and "xtnum" args are purely for printing error messages.
417  (the filename is copied to the qfits_table object, though)
418  */
qfits_table_open2(const qfits_header * hdr,off_t offset_beg,size_t data_size,const char * filename,int xtnum)419 qfits_table * qfits_table_open2(const qfits_header* hdr, off_t offset_beg, size_t data_size,
420                                 const char* filename, int xtnum) {
421     qfits_table     *   tload;
422     qfits_col       *   curr_col;
423     char str_val[FITS_LINESZ+1];
424     char                keyword[FITSVALSZ];
425     /* Table infos  */
426     int                 table_type;
427     int                 nb_col;
428     int                 table_width;
429     int                 nb_rows;
430     /* Column infos */
431     char                label[FITSVALSZ];
432     char                unit[FITSVALSZ];
433     char                disp[FITSVALSZ];
434     char                nullval[FITSVALSZ];
435     int                 atom_nb;
436     int                 atom_dec_nb;
437     int                 atom_size;
438     tfits_type          atom_type;
439     size_t              theory_size;
440     int                 zero_present;
441     int                 scale_present;
442     float               zero;
443     float               scale;
444 
445     /* For ASCII tables */
446     int                    col_pos;
447     int                    next_col_pos;
448 
449     /* For X type */
450     int                    nb_bits;
451 
452     int                    i;
453 
454     /* Identify a table and get the table type : ASCII or BIN */
455     if ((table_type = qfits_is_table_header(hdr)) == QFITS_INVALIDTABLE) {
456         qfits_error("[%s] extension %d is not a table", filename, xtnum);
457         return NULL;
458     }
459 
460     /* Get number of columns and allocate them: nc <-> TFIELDS */
461     nb_col = qfits_header_getint(hdr, "TFIELDS", -1);
462     if (nb_col == -1) {
463         qfits_error("cannot read TFIELDS in [%s]:[%d]", filename, xtnum);
464         return NULL;
465     }
466 
467     /* Get the width in bytes of the table */
468     table_width = qfits_header_getint(hdr, "NAXIS1", -1);
469     if (table_width == -1) {
470         qfits_error("cannot read NAXIS1 in [%s]:[%d]", filename, xtnum);
471         return NULL;
472     }
473 
474     /* Get the number of rows */
475     nb_rows = qfits_header_getint(hdr, "NAXIS2", -1);
476     if (nb_rows == -1) {
477         qfits_error("cannot read NAXIS2 in [%s]:[%d]", filename, xtnum);
478         return NULL;
479     }
480 
481     /* Create the table object */
482     tload = qfits_table_new(filename, table_type, table_width, nb_col, nb_rows);
483 
484     /* Loop on all columns and get column descriptions  */
485     curr_col = tload->col;
486     for (i=0; i<tload->nc; i++) {
487         const char* nil = "";
488 
489         /* label <-> TTYPE     */
490         sprintf(keyword, "TTYPE%d", i+1);
491         qfits_header_getstr_pretty(hdr, keyword, label, nil);
492 
493         /* unit <-> TUNIT */
494         sprintf(keyword, "TUNIT%d", i+1);
495         qfits_header_getstr_pretty(hdr, keyword, unit, nil);
496 
497         /* disp <-> TDISP */
498         sprintf(keyword, "TDISP%d", i+1);
499         qfits_header_getstr_pretty(hdr, keyword, disp, nil);
500 
501         /* nullval <-> TNULL */
502         sprintf(keyword, "TNULL%d", i+1);
503         qfits_header_getstr_pretty(hdr, keyword, nullval, nil);
504 
505         /* atom_size, atom_nb, atom_dec_nb, atom_type    <-> TFORM */
506         sprintf(keyword, "TFORM%d", i+1);
507         if (qfits_header_getstr_pretty(hdr, keyword, str_val, NULL)) {
508             qfits_error("cannot read [%s] in [%s]:[%d]", keyword, filename, xtnum);
509             qfits_table_close(tload);
510             return NULL;
511         }
512         /* Interpret the type in header */
513         if (qfits_table_interpret_type(str_val, &atom_nb, &atom_dec_nb, &atom_type, table_type) == -1) {
514             qfits_error("cannot interpret column type: %s", str_val);
515             qfits_table_close(tload);
516             return NULL;
517         }
518 
519         /* Set atom_size */
520         switch (atom_type) {
521         case TFITS_BIN_TYPE_A:
522         case TFITS_BIN_TYPE_L:
523         case TFITS_BIN_TYPE_B:
524             atom_size = 1;
525             break;
526         case TFITS_BIN_TYPE_I:
527             atom_size = 2;
528             break;
529         case TFITS_BIN_TYPE_J:
530         case TFITS_BIN_TYPE_E:
531         case TFITS_ASCII_TYPE_I:
532         case TFITS_ASCII_TYPE_E:
533         case TFITS_ASCII_TYPE_F:
534             atom_size = 4;
535             break;
536         case TFITS_BIN_TYPE_C:
537         case TFITS_BIN_TYPE_P:
538             atom_size = 4;
539             atom_nb *= 2;
540             break;
541         case TFITS_BIN_TYPE_K:
542         case TFITS_BIN_TYPE_D:
543         case TFITS_ASCII_TYPE_D:
544             atom_size = 8;
545             break;
546         case TFITS_BIN_TYPE_M:
547             atom_size = 8;
548             atom_nb *= 2;
549             break;
550         case TFITS_BIN_TYPE_X:
551             atom_size = 1;
552             nb_bits = atom_nb;
553             atom_nb = (int)((nb_bits - 1)/ 8) + 1;
554             break;
555         case TFITS_ASCII_TYPE_A:
556             atom_size = atom_nb;
557             break;
558         default:
559             qfits_error("unrecognized type");
560             qfits_table_close(tload);
561             return NULL;
562             break;
563         }
564 
565         /* zero <-> TZERO */
566         sprintf(keyword, "TZERO%d", i+1);
567         zero_present = 1;
568         zero = qfits_header_getdouble(hdr, keyword, HUGE_VAL);
569         if (zero == HUGE_VAL) {
570             zero = 0.0;
571             zero_present = 0;
572         }
573 
574         /* scale <-> TSCAL */
575         sprintf(keyword, "TSCAL%d", i+1);
576         scale_present = 1;
577         scale = qfits_header_getdouble(hdr, keyword, HUGE_VAL);
578         if (scale == HUGE_VAL) {
579             scale = 1.0;
580             scale_present = 0;
581         }
582 
583         /* Fill the current column object */
584         qfits_col_fill(curr_col, atom_nb, atom_dec_nb, atom_size, atom_type,
585                        label, unit, nullval, disp, zero_present, zero, scale_present,
586                        scale, offset_beg);
587 
588         /* Compute offset_beg but for the last column */
589         if (i < tload->nc - 1) {
590             if (table_type == QFITS_ASCIITABLE) {
591                 /* column width <-> TBCOLi and TBCOLi+1 */
592                 sprintf(keyword, "TBCOL%d", i+1);
593                 col_pos = qfits_header_getint(hdr, keyword, -1);
594                 if (col_pos == -1) {
595                     qfits_error("cannot read [%s] in [%s]", keyword, filename);
596                     qfits_table_close(tload);
597                     return NULL;
598                 }
599 
600                 sprintf(keyword, "TBCOL%d", i+2);
601                 next_col_pos = qfits_header_getint(hdr, keyword, -1);
602                 if (next_col_pos == -1) {
603                     qfits_error("cannot read [%s] in [%s]", keyword, filename);
604                     qfits_table_close(tload);
605                     return NULL;
606                 }
607                 offset_beg += (int)(next_col_pos - col_pos);
608 
609             } else if (table_type == QFITS_BINTABLE) {
610                 offset_beg += atom_nb * atom_size;
611             }
612         }
613         curr_col++;
614     }
615 
616     /* Check that the theoretical data size is not far from the measured */
617     /* one by more than 2880 */
618     theory_size = (size_t)qfits_compute_table_width(tload) * (size_t)tload->nr;
619     if (data_size < theory_size) {
620         qfits_error("Inconsistent data sizes: found %i, expected %i.", data_size, theory_size);
621         qfits_table_close(tload);
622         return NULL;
623     }
624 
625     /* Return  */
626     return tload;
627 }
628 
629 /*----------------------------------------------------------------------------*/
630 /**
631  @brief    Free a FITS table and associated pointers
632  @param    t qfits_table to free
633  @return    void
634  Frees all memory associated to a qfits_table structure.
635  */
636 /*----------------------------------------------------------------------------*/
qfits_table_close(qfits_table * t)637 void qfits_table_close(qfits_table * t)
638 {
639     if (t==NULL) return;
640     if (t->nc>0) if (t->col!=NULL) qfits_free(t->col);
641     qfits_free(t);
642     return;
643 }
644 
645 /*----------------------------------------------------------------------------*/
646 /**
647  @brief    Extract data from a column in a FITS table
648  @param    th        Allocated qfits_table
649  @param    colnum    Number of the column to extract (from 0 to colnum-1)
650  @param    selection  boolean array to define the selected rows
651  @return    unsigned char array
652 
653  If selection is NULL, select the complete column.
654 
655  Extract a column from a FITS table and return the data as a bytes
656  array. The returned array type and size are determined by the
657  column object in the qfits_table and by the selection parameter.
658 
659  Returned array size in bytes is:
660  nbselected * col->natoms * col->atom_size
661 
662  Numeric types are correctly understood and byte-swapped if needed,
663  to be converted to the local machine type.
664 
665  NULL values have to be handled by the caller.
666 
667  The returned object must be deallocated with qfits_free().
668  */
669 /*----------------------------------------------------------------------------*/
qfits_query_column(const qfits_table * th,int colnum,const int * selection)670 unsigned char * qfits_query_column(
671                                    const qfits_table   *   th,
672                                    int                     colnum,
673                                    const int           *   selection)
674 {
675     char            *    start;
676     qfits_col       *   col;
677     int                    field_size;
678     unsigned char   *   array;
679     unsigned char   *   r;
680     unsigned char   *   inbuf;
681     int                 table_width;
682     int                 nb_rows;
683     size_t              size;
684     int                 i;
685 
686     if (th->tab_w == -1) {
687         /* Compute the table width in bytes */
688         if ((table_width = qfits_compute_table_width(th)) == -1) {
689             qfits_error("cannot compute the table width");
690             return NULL;
691         }
692     } else table_width = th->tab_w;
693 
694     /* Compute the number of selected rows */
695     nb_rows = 0;
696     if (selection == NULL) {
697         nb_rows = th->nr;
698     } else {
699         for (i=0; i<th->nr; i++) if (selection[i] == 1) nb_rows++;
700     }
701 
702     /* Pointer to requested column */
703     col = th->col + colnum;
704 
705     /* Test if column is empty */
706     if (nb_rows * col->atom_size * col->atom_nb == 0) col->readable = 0;
707 
708     /* Test if column is readable */
709     if (col->readable == 0)  return NULL;
710 
711     /* Compute the size in bytes of one field stored in the file */
712     if ((field_size=qfits_table_get_field_size(th->tab_t,col))==-1) return NULL;
713 
714     /* Load input file */
715     if ((start=qfits_falloc((char *)(th->filename), 0, &size))==NULL) {
716         qfits_error("cannot open table for query [%s]", th->filename);
717         return NULL;
718     }
719 
720     /* Allocate data array */
721     array = qfits_malloc(nb_rows * field_size * sizeof(char));
722 
723     /* Position the input pointer at the begining of the column data */
724     r = array;
725     inbuf = (unsigned char*)start + col->off_beg;
726 
727     /* Copy the values in array */
728     if (selection == NULL) {
729         /* No selection : get the complete column */
730         for (i=0; i<th->nr; i++) {
731             /* Copy all atoms on this field into array */
732             memcpy(r, inbuf, field_size);
733             r += field_size;
734             /* Jump to next line */
735             inbuf += table_width;
736         }
737     } else {
738         /* Get only the selected rows */
739         for (i=0; i<th->nr; i++) {
740             if (selection[i] == 1) {
741                 /* Copy all atoms on this field into array */
742                 memcpy(r, inbuf, field_size);
743                 r += field_size;
744             }
745             /* Jump to next line */
746             inbuf += table_width;
747         }
748     }
749     qfits_fdealloc(start, 0, size);
750 
751     /* SWAP the bytes if necessary */
752 #ifndef WORDS_BIGENDIAN
753     if ((th->tab_t == QFITS_BINTABLE) && (col->atom_size > 1)) {
754         r = array;
755         for (i=0; i<nb_rows * col->atom_nb; i++) {
756             qfits_swap_bytes(r, col->atom_size);
757             r += col->atom_size;
758         }
759     }
760 #endif
761 
762     /* Return allocated and converted array */
763     return array;
764 }
765 
766 /*----------------------------------------------------------------------------*/
767 /**
768  @brief    Extract consequtive values from a column in a FITS table
769  @param    th        Allocated qfits_table
770  @param    colnum    Number of the column to extract (from 0 to colnum-1)
771  @param    start_ind   Index of the first row (0 for the first)
772  @param    nb_rows     Number of rows to extract
773  @return    unsigned char array
774  Does the same as qfits_query_column() but on a consequtive sequence of rows
775  Spares the overhead of the selection object allocation
776  The returned object must be deallocated with qfits_free().
777  */
778 /*----------------------------------------------------------------------------*/
qfits_query_column_seq(const qfits_table * th,int colnum,int start_ind,int nb_rows)779 unsigned char * qfits_query_column_seq(
780                                        const qfits_table   *   th,
781                                        int                     colnum,
782                                        int                     start_ind,
783                                        int                     nb_rows)
784 {
785     char            *   start;
786     qfits_col       *   col;
787     int                 field_size;
788     unsigned char   *   array;
789     unsigned char   *   r;
790     unsigned char   *   inbuf;
791     int                 table_width;
792     size_t              size;
793     int                 i;
794 
795     if (th->tab_w == -1) {
796         /* Compute the table width in bytes */
797         if ((table_width = qfits_compute_table_width(th)) == -1) {
798             qfits_error("cannot compute the table width");
799             return NULL;
800         }
801     } else table_width = th->tab_w;
802 
803     /* Check the validity of start_ind and nb_rows */
804     if ((start_ind<0) || (start_ind+nb_rows>th->nr)) {
805         qfits_error("bad start index and number of rows");
806         return NULL;
807     }
808 
809     /* Pointer to requested column */
810     col = th->col + colnum;
811 
812     /* Test if column is empty */
813     if (nb_rows * col->atom_size * col->atom_nb == 0) col->readable = 0;
814 
815     /* Test if column is readable */
816     if (col->readable == 0)  return NULL;
817 
818     /* Compute the size in bytes of one field stored in the file */
819     if ((field_size=qfits_table_get_field_size(th->tab_t,col))==-1) return NULL;
820 
821     /* Load input file */
822     if ((start=qfits_falloc((char *)(th->filename), 0, &size))==NULL) {
823         qfits_error("cannot open table for query [%s]", th->filename);
824         return NULL;
825     }
826 
827     /* Allocate data array */
828     array = qfits_malloc(nb_rows * field_size * sizeof(char));
829 
830     /* Position the input pointer at the begining of the column data */
831     r = array;
832     inbuf = (unsigned char*)start + col->off_beg + table_width * start_ind;
833 
834     /* Copy the values in array */
835     /* Get only the selected rows */
836     for (i=0; i<nb_rows; i++) {
837         /* Copy all atoms on this field into array */
838         memcpy(r, inbuf, field_size);
839         r += field_size;
840         /* Jump to next line */
841         inbuf += table_width;
842     }
843     qfits_fdealloc(start, 0, size);
844 
845     /* SWAP the bytes if necessary */
846 #ifndef WORDS_BIGENDIAN
847     if ((th->tab_t == QFITS_BINTABLE) && (col->atom_size > 1)) {
848         r = array;
849         for (i=0; i<nb_rows * col->atom_nb; i++) {
850             qfits_swap_bytes(r, col->atom_size);
851             r += col->atom_size;
852         }
853     }
854 #endif
855 
856     /* Return allocated and converted array */
857     return array;
858 }
859 
860 
861 
862 
qfits_query_column_seq_to_array_endian(const qfits_table * th,int colnum,int start_ind,const int * indices,int nb_rows,unsigned char * destination,int dest_stride,int swap_endian)863 static int qfits_query_column_seq_to_array_endian(
864                                                   const qfits_table	    *   th,
865                                                   int                 colnum,
866                                                   int                 start_ind,
867                                                   const int* indices,
868                                                   int                 nb_rows,
869                                                   unsigned char*      destination,
870                                                   int                 dest_stride,
871                                                   int swap_endian)
872 {
873     qfits_col       *   col;
874     int					field_size;
875     unsigned char   *   r;
876     unsigned char   *   inbuf;
877     int                 table_width;
878     int                 i;
879     int do_swap;
880 
881     int maxind;
882 
883     char* freeaddr;
884     size_t freesize;
885 
886     size_t mapoffset;
887     size_t maplen;
888 
889     if (th->tab_w == -1) {
890         /* Compute the table width in bytes */
891         if ((table_width = qfits_compute_table_width(th)) == -1) {
892             qfits_error("cannot compute the table width");
893             return -1;
894         }
895     } else table_width = th->tab_w;
896 
897     /* Check the validity of start_ind and nb_rows */
898     if ((start_ind<0) || (start_ind+nb_rows>th->nr)) {
899         qfits_error("bad start index and number of rows");
900         return -1;
901     }
902 
903     /* Pointer to requested column */
904     col = th->col + colnum;
905 
906     /* Test if column is empty */
907     if (nb_rows * col->atom_size * col->atom_nb == 0) col->readable = 0;
908 
909     /* Test if column is readable */
910     if (col->readable == 0)  return -1;
911 
912     /* Compute the size in bytes of one field stored in the file */
913     if ((field_size=qfits_table_get_field_size(th->tab_t,col))==-1)
914         return -1;
915 
916     /* Load input file */
917     if (indices) {
918         maxind = 0;
919         for (i=0; i<nb_rows; i++)
920             maxind = MAX(maxind, indices[i]);
921     } else
922         maxind = nb_rows - 1;
923 
924     // these size_t casts are *essential* to avoid overflow in > 2GB files!
925     mapoffset = col->off_beg + (size_t)table_width * (size_t)start_ind;
926     // NOTE that this *isn't* (table_width * N) -- that can lead to trying to map
927     // beyond the end of the file!
928     maplen = (size_t)maxind * (size_t)table_width + (size_t)field_size;
929 
930     //printf("opening %s: %zu + %zu (column offset = %i, width=%i, start_index=%i)\n", th->filename, mapoffset, maplen, col->off_beg, table_width, start_ind);
931 
932     if ((inbuf = qfits_falloc2(th->filename, mapoffset, maplen,
933                                &freeaddr, &freesize)) == NULL) {
934         qfits_error("cannot open table for reading column data [%s]", th->filename);
935         return -1;
936     }
937 
938     /*
939      if ((start=qfits_falloc(th->filename, 0, &size))==NULL) {
940      qfits_error("cannot open table for query [%s]", th->filename);
941      return -1;
942      }
943      inbuf = (unsigned char*)start + col->off_beg + table_width * start_ind;
944      */
945 
946     /* Position the input pointer at the begining of the column data */
947     r = destination;
948 
949     do_swap = 0;
950 #ifndef WORDS_BIGENDIAN
951     if (swap_endian)
952         if ((th->tab_t == QFITS_BINTABLE) && (col->atom_size > 1))
953             do_swap = 1;
954 #endif
955 
956     /* Copy the values in array */
957     /* Get only the selected rows */
958     for (i=0; i<nb_rows; i++) {
959         /* Copy all atoms on this field into array */
960         if (indices) {
961             memcpy(r, inbuf + (indices[i]*table_width), field_size);
962         } else {
963             memcpy(r, inbuf, field_size);
964             /* Jump to next line */
965             inbuf += table_width;
966         }
967 
968 #ifndef WORDS_BIGENDIAN
969         if (do_swap) {
970             int j;
971             unsigned char* r2 = r;
972             for (j=0; j<col->atom_nb; j++) {
973                 qfits_swap_bytes(r2, col->atom_size);
974                 r2 += col->atom_size;
975             }
976         }
977 #endif
978 
979         r += dest_stride;
980     }
981 
982     //qfits_fdealloc(start, 0, size);
983     qfits_fdealloc2(freeaddr, freesize);
984 
985     return 0;
986 }
987 
988 
qfits_query_column_seq_to_array(const qfits_table * th,int colnum,int start_ind,int nb_rows,unsigned char * destination,int dest_stride)989 int qfits_query_column_seq_to_array(
990                                     const qfits_table	    *   th,
991                                     int                 colnum,
992                                     int                 start_ind,
993                                     int                 nb_rows,
994                                     unsigned char*      destination,
995                                     int                 dest_stride)
996 {
997     return qfits_query_column_seq_to_array_endian(th, colnum, start_ind, NULL, nb_rows, destination, dest_stride, 1);
998 }
999 
qfits_query_column_seq_to_array_inds(const qfits_table * th,int colnum,const int * indices,int Ninds,unsigned char * destination,int dest_stride)1000 int qfits_query_column_seq_to_array_inds(const qfits_table	    *   th,
1001                                          int                 colnum,
1002                                          const int* indices,
1003                                          int Ninds,
1004                                          unsigned char*      destination,
1005                                          int                 dest_stride) {
1006     return qfits_query_column_seq_to_array_endian(th, colnum, 0, indices, Ninds, destination, dest_stride, 1);
1007 }
1008 
qfits_query_column_seq_to_array_no_endian_swap(const qfits_table * th,int colnum,int start_ind,int nb_rows,unsigned char * destination,int dest_stride)1009 int qfits_query_column_seq_to_array_no_endian_swap(
1010                                                    const qfits_table	    *   th,
1011                                                    int                 colnum,
1012                                                    int                 start_ind,
1013                                                    int                 nb_rows,
1014                                                    unsigned char*      destination,
1015                                                    int                 dest_stride)
1016 {
1017     return qfits_query_column_seq_to_array_endian(th, colnum, start_ind, NULL, nb_rows, destination, dest_stride, 0);
1018 }
1019 
1020 /*----------------------------------------------------------------------------*/
1021 /**
1022  @brief    Extract binary data from a column in a FITS table
1023  @param    th        Allocated qfits_table
1024  @param    colnum    Number of the column to extract (from 0 to colnum-1)
1025  @param    selection  bollean array to identify selected rows
1026  @param    null_value    Value to return when a NULL value comes
1027  @return    Pointer to void *
1028 
1029  Extract a column from a FITS table and return the data as a generic
1030  void* array. The returned array type and size are determined by the
1031  column object in the qfits_table.
1032 
1033  Returned array size in bytes is:
1034  nb_selected * col->atom_nb * col->atom_size
1035 
1036  NULL values are recognized and replaced by the specified value.
1037  The returned object must be deallocated with qfits_free().
1038  */
1039 /*----------------------------------------------------------------------------*/
qfits_query_column_data(const qfits_table * th,int colnum,const int * selection,const void * null_value)1040 void * qfits_query_column_data(
1041                                const qfits_table   *   th,
1042                                int                     colnum,
1043                                const int           *   selection,
1044                                const void          *    null_value)
1045 {
1046     void            *    out_array;
1047     qfits_col       *   col;
1048     int                 nb_rows;
1049     unsigned char    *    in_array;
1050     char            *    field;
1051 
1052     unsigned char        ucnull;
1053     short                snull;
1054     int                 inull;
1055     double                dnull;
1056     float                fnull;
1057 
1058     int                 i;
1059 
1060     /* Initialize */
1061     if (null_value == NULL) {
1062         inull  = (int)0;
1063         snull  = (short)0;
1064         ucnull = (unsigned char)0;
1065         fnull  = (float)0.0;
1066         dnull  = (double)0.0;
1067     } else {
1068         inull  = *(int*)null_value;
1069         snull  = *(short*)null_value;
1070         ucnull = *(unsigned char*)null_value;
1071         fnull  = *(float*)null_value;
1072         dnull  = *(double*)null_value;
1073     }
1074 
1075     /* Get the number of selected rows */
1076     nb_rows = 0;
1077     if (selection == NULL) {
1078         nb_rows = th->nr;
1079     } else {
1080         for (i=0; i<th->nr; i++) if (selection[i] == 1) nb_rows++;
1081     }
1082 
1083     /* Pointer to requested column */
1084     col = th->col+colnum;
1085 
1086     /* Test if column is readable */
1087     if (col->readable == 0) return NULL;
1088 
1089     /* Handle each type separately */
1090     switch(col->atom_type) {
1091     case TFITS_ASCII_TYPE_A:
1092         out_array = (char*)qfits_query_column(th, colnum, selection);
1093         break;
1094 
1095     case TFITS_ASCII_TYPE_I:
1096         in_array = (unsigned char*)qfits_query_column(th, colnum, selection);
1097         out_array = qfits_malloc(nb_rows*col->atom_size);
1098         field = qfits_malloc((col->atom_nb+1)*sizeof(char));
1099         for (i=0; i<nb_rows; i++) {
1100             /* Copy all atoms of the field into 'field' */
1101             memcpy(field, &in_array[i*col->atom_nb], col->atom_nb);
1102             field[col->atom_nb]='\0';
1103             /* Write the data in out_array */
1104             /* Test if a NULL val is encoutered */
1105             if (!strcmp(col->nullval, qfits_strstrip(field))) {
1106                 ((int*)out_array)[i] = inull;
1107             } else {
1108                 ((int*)out_array)[i] = (int)atoi(field);
1109             }
1110         }
1111         qfits_free(field);
1112         qfits_free(in_array);
1113         break;
1114 
1115     case TFITS_ASCII_TYPE_E:
1116     case TFITS_ASCII_TYPE_F:
1117         in_array = (unsigned char*)qfits_query_column(th, colnum, selection);
1118         out_array = qfits_malloc(nb_rows*col->atom_size);
1119         field = qfits_malloc((col->atom_nb+1)*sizeof(char));
1120         for (i=0; i<nb_rows; i++) {
1121             /* Copy all atoms of the field into 'field' */
1122             memcpy(field, &in_array[i*col->atom_nb], col->atom_nb);
1123             field[col->atom_nb]='\0';
1124             /* Write the data in out_array */
1125             /* Test if a NULL val is encoutered */
1126             if (!strcmp(col->nullval, qfits_strstrip(field))) {
1127                 ((float*)out_array)[i] = fnull;
1128             } else {
1129                 /* Add the decimal handling */
1130                 ((float*)out_array)[i]=(float)qfits_str2dec(field,
1131                                                             col->atom_dec_nb);
1132             }
1133         }
1134         qfits_free(field);
1135         qfits_free(in_array);
1136         break;
1137 
1138     case TFITS_ASCII_TYPE_D:
1139         in_array = (unsigned char*)qfits_query_column(th, colnum, selection);
1140         out_array = qfits_malloc(nb_rows*col->atom_size);
1141         field = qfits_malloc((col->atom_nb+1)*sizeof(char));
1142         for (i=0; i<nb_rows; i++) {
1143             /* Copy all atoms of the field into 'field' */
1144             memcpy(field, &in_array[i*col->atom_nb], col->atom_nb);
1145             field[col->atom_nb]='\0';
1146             /* Write the data in out_array */
1147             /* Test if a NULL val is encoutered */
1148             if (!strcmp(col->nullval, field)) {
1149                 ((double*)out_array)[i] = dnull;
1150             } else {
1151                 /* Add the decimal handling */
1152                 ((double*)out_array)[i]=qfits_str2dec(field, col->atom_dec_nb);
1153             }
1154         }
1155         qfits_free(field);
1156         qfits_free(in_array);
1157 
1158         break;
1159     case TFITS_BIN_TYPE_A:
1160     case TFITS_BIN_TYPE_L:
1161         out_array = (char*)qfits_query_column(th, colnum, selection);
1162         break;
1163 
1164     case TFITS_BIN_TYPE_D:
1165     case TFITS_BIN_TYPE_M:
1166         out_array = (double*)qfits_query_column(th, colnum, selection);
1167         for (i=0; i<nb_rows * col->atom_nb; i++) {
1168             if (qfits_isnan(((double*)out_array)[i]) ||
1169                 qfits_isinf(((double*)out_array)[i])) {
1170                 ((double*)out_array)[i] = dnull;
1171             }
1172         }
1173         break;
1174 
1175     case TFITS_BIN_TYPE_E:
1176     case TFITS_BIN_TYPE_C:
1177         out_array = (float*)qfits_query_column(th, colnum, selection);
1178         for (i=0; i<nb_rows * col->atom_nb; i++) {
1179             if (qfits_isnan(((float*)out_array)[i]) ||
1180                 qfits_isinf(((float*)out_array)[i])) {
1181                 ((float*)out_array)[i] = fnull;
1182             }
1183         }
1184         break;
1185 
1186     case TFITS_BIN_TYPE_X:
1187         out_array = (unsigned char*)qfits_query_column(th, colnum, selection);
1188         break;
1189 
1190     case TFITS_BIN_TYPE_B:
1191         out_array = (unsigned char*)qfits_query_column(th, colnum, selection);
1192         for (i=0; i<nb_rows * col->atom_nb; i++) {
1193             if (((col->nullval)[0] != '\0') &&
1194                 (atoi(col->nullval) == (int)((unsigned char*)out_array)[i])) {
1195                 ((unsigned char*)out_array)[i] = ucnull;
1196             }
1197         }
1198         break;
1199 
1200     case TFITS_BIN_TYPE_I:
1201         out_array = (short*)qfits_query_column(th, colnum, selection);
1202         for (i=0; i<nb_rows * col->atom_nb; i++) {
1203             if (((col->nullval)[0] != '\0') &&
1204                 (atoi(col->nullval)==(int)((short*)out_array)[i])) {
1205                 ((short*)out_array)[i] = snull;
1206             }
1207         }
1208         break;
1209 
1210     case TFITS_BIN_TYPE_J:
1211         out_array = (int*)qfits_query_column(th, colnum, selection);
1212         for (i=0; i<nb_rows * col->atom_nb; i++) {
1213             if (((col->nullval)[0] != '\0') &&
1214                 (atoi(col->nullval)==((int*)out_array)[i])) {
1215                 ((int*)out_array)[i] = inull;
1216             }
1217         }
1218         break;
1219 
1220     case TFITS_BIN_TYPE_K:
1221         out_array = (int64_t*)qfits_query_column(th, colnum, selection);
1222         for (i=0; i<nb_rows * col->atom_nb; i++) {
1223             if (((col->nullval)[0] != '\0') &&
1224                 (atoll(col->nullval)==((int64_t*)out_array)[i])) {
1225                 ((int64_t*)out_array)[i] = inull;
1226             }
1227         }
1228         break;
1229 
1230     case TFITS_BIN_TYPE_P:
1231         out_array = (int*)qfits_query_column(th, colnum, selection);
1232         break;
1233 
1234     default:
1235         qfits_error("unrecognized data type");
1236         return NULL;
1237     }
1238     return out_array;
1239 }
1240 
1241 /*----------------------------------------------------------------------------*/
1242 /**
1243  @brief    Extract binary data from a column in a FITS table
1244  @param    th        Allocated qfits_table
1245  @param    colnum    Number of the column to extract (from 0 to colnum-1)
1246  @param    start_ind   Index of the first row (0 for the first)
1247  @param    nb_rows     Number of rows to extract
1248  @param    null_value    Value to return when a NULL value comes
1249  @return    Pointer to void *
1250  Does the same as qfits_query_column_data() but on a consequtive sequence
1251  of rows.  Spares the overhead of the selection object allocation
1252  The returned object must be deallocated with qfits_free().
1253  */
1254 /*----------------------------------------------------------------------------*/
qfits_query_column_seq_data(const qfits_table * th,int colnum,int start_ind,int nb_rows,const void * null_value)1255 void * qfits_query_column_seq_data(
1256                                    const qfits_table   *   th,
1257                                    int                     colnum,
1258                                    int                     start_ind,
1259                                    int                     nb_rows,
1260                                    const void          *   null_value)
1261 {
1262     void            *    out_array;
1263     qfits_col       *   col;
1264     unsigned char    *    in_array;
1265     char            *    field;
1266 
1267     unsigned char        ucnull;
1268     short                snull;
1269     int                 inull;
1270     double                dnull;
1271     float                fnull;
1272 
1273     int                 i;
1274 
1275     /* Initialize */
1276     if (null_value == NULL) {
1277         inull  = (int)0;
1278         snull  = (short)0;
1279         ucnull = (unsigned char)0;
1280         fnull  = (float)0.0;
1281         dnull  = (double)0.0;
1282     } else {
1283         inull  = *(int*)null_value;
1284         snull  = *(short*)null_value;
1285         ucnull = *(unsigned char*)null_value;
1286         fnull  = *(float*)null_value;
1287         dnull  = *(double*)null_value;
1288     }
1289 
1290     /* Pointer to requested column */
1291     col = th->col+colnum;
1292 
1293     /* Test if column is readable */
1294     if (col->readable == 0) return NULL;
1295 
1296     /* Handle each type separately */
1297     switch(col->atom_type) {
1298     case TFITS_ASCII_TYPE_A:
1299         out_array = (char*)qfits_query_column_seq(th, colnum, start_ind,
1300                                                   nb_rows);
1301         break;
1302 
1303     case TFITS_ASCII_TYPE_I:
1304         in_array = (unsigned char*)qfits_query_column_seq(th, colnum,
1305                                                           start_ind, nb_rows);
1306         out_array = qfits_malloc(nb_rows*col->atom_size);
1307         field = qfits_malloc((col->atom_nb+1)*sizeof(char));
1308         for (i=0; i<nb_rows; i++) {
1309             /* Copy all atoms of the field into 'field' */
1310             memcpy(field, &in_array[i*col->atom_nb], col->atom_nb);
1311             field[col->atom_nb]='\0';
1312             /* Write the data in out_array */
1313             /* Test if a NULL val is encoutered */
1314             if (!strcmp(col->nullval, qfits_strstrip(field))) {
1315                 ((int*)out_array)[i] = inull;
1316             } else {
1317                 ((int*)out_array)[i] = (int)atoi(field);
1318             }
1319         }
1320         qfits_free(field);
1321         qfits_free(in_array);
1322         break;
1323 
1324     case TFITS_ASCII_TYPE_E:
1325     case TFITS_ASCII_TYPE_F:
1326         in_array = (unsigned char*)qfits_query_column_seq(th, colnum,
1327                                                           start_ind, nb_rows);
1328         out_array = qfits_malloc(nb_rows*col->atom_size);
1329         field = qfits_malloc((col->atom_nb+1)*sizeof(char));
1330         for (i=0; i<nb_rows; i++) {
1331             /* Copy all atoms of the field into 'field' */
1332             memcpy(field, &in_array[i*col->atom_nb], col->atom_nb);
1333             field[col->atom_nb]='\0';
1334             /* Write the data in out_array */
1335             /* Test if a NULL val is encoutered */
1336             if (!strcmp(col->nullval, qfits_strstrip(field))) {
1337                 ((float*)out_array)[i] = fnull;
1338             } else {
1339                 /* Add the decimal handling */
1340                 ((float*)out_array)[i]=(float)qfits_str2dec(field,
1341                                                             col->atom_dec_nb);
1342             }
1343         }
1344         qfits_free(field);
1345         qfits_free(in_array);
1346         break;
1347 
1348     case TFITS_ASCII_TYPE_D:
1349         in_array = (unsigned char*)qfits_query_column_seq(th, colnum,
1350                                                           start_ind, nb_rows);
1351         out_array = qfits_malloc(nb_rows*col->atom_size);
1352         field = qfits_malloc((col->atom_nb+1)*sizeof(char));
1353         for (i=0; i<nb_rows; i++) {
1354             /* Copy all atoms of the field into 'field' */
1355             memcpy(field, &in_array[i*col->atom_nb], col->atom_nb);
1356             field[col->atom_nb]='\0';
1357             /* Write the data in out_array */
1358             /* Test if a NULL val is encoutered */
1359             if (!strcmp(col->nullval, qfits_strstrip(field))) {
1360                 ((double*)out_array)[i] = dnull;
1361             } else {
1362                 /* Add the decimal handling */
1363                 ((double*)out_array)[i]=qfits_str2dec(field, col->atom_dec_nb);
1364             }
1365         }
1366         qfits_free(field);
1367         qfits_free(in_array);
1368         break;
1369 
1370     case TFITS_BIN_TYPE_A:
1371     case TFITS_BIN_TYPE_L:
1372         out_array = (char*)qfits_query_column_seq(th, colnum,
1373                                                   start_ind, nb_rows);
1374         break;
1375 
1376     case TFITS_BIN_TYPE_D:
1377     case TFITS_BIN_TYPE_M:
1378         out_array = (double*)qfits_query_column_seq(th, colnum,
1379                                                     start_ind, nb_rows);
1380         for (i=0; i<nb_rows * col->atom_nb; i++) {
1381             if (qfits_isnan(((double*)out_array)[i]) ||
1382                 qfits_isinf(((double*)out_array)[i])) {
1383                 ((double*)out_array)[i] = dnull;
1384             }
1385         }
1386         break;
1387 
1388     case TFITS_BIN_TYPE_E:
1389     case TFITS_BIN_TYPE_C:
1390         out_array = (float*)qfits_query_column_seq(th, colnum,
1391                                                    start_ind, nb_rows);
1392         for (i=0; i<nb_rows * col->atom_nb; i++) {
1393             if (qfits_isnan(((float*)out_array)[i]) ||
1394                 qfits_isinf(((float*)out_array)[i])) {
1395                 ((float*)out_array)[i] = fnull;
1396             }
1397         }
1398         break;
1399 
1400     case TFITS_BIN_TYPE_X:
1401         out_array = (unsigned char*)qfits_query_column_seq(th, colnum,
1402                                                            start_ind, nb_rows);
1403         break;
1404 
1405     case TFITS_BIN_TYPE_B:
1406         out_array = (unsigned char*)qfits_query_column_seq(th, colnum,
1407                                                            start_ind, nb_rows);
1408         for (i=0; i<nb_rows * col->atom_nb; i++) {
1409             if (((col->nullval)[0] != '\0') &&
1410                 (atoi(col->nullval)== (int)((unsigned char*)out_array)[i])) {
1411                 ((unsigned char*)out_array)[i] = ucnull;
1412             }
1413         }
1414         break;
1415 
1416     case TFITS_BIN_TYPE_I:
1417         out_array = (short*)qfits_query_column_seq(th, colnum,
1418                                                    start_ind, nb_rows);
1419         for (i=0; i<nb_rows * col->atom_nb; i++) {
1420             if (((col->nullval)[0] != '\0') &&
1421                 (atoi(col->nullval)==(int)((short*)out_array)[i])) {
1422                 ((short*)out_array)[i] = snull;
1423             }
1424         }
1425         break;
1426 
1427     case TFITS_BIN_TYPE_J:
1428         out_array = (int*)qfits_query_column_seq(th, colnum,
1429                                                  start_ind, nb_rows);
1430         for (i=0; i<nb_rows * col->atom_nb; i++) {
1431             if (((col->nullval)[0] != '\0') &&
1432                 (atoi(col->nullval)==((int*)out_array)[i])) {
1433                 ((int*)out_array)[i] = inull;
1434             }
1435         }
1436         break;
1437 
1438     case TFITS_BIN_TYPE_K:
1439         out_array = (int64_t*)qfits_query_column_seq(th, colnum,
1440                                                      start_ind, nb_rows);
1441         for (i=0; i<nb_rows * col->atom_nb; i++) {
1442             if (((col->nullval)[0] != '\0') &&
1443                 (atoll(col->nullval)==((int64_t*)out_array)[i])) {
1444                 ((int64_t*)out_array)[i] = inull;
1445             }
1446         }
1447         break;
1448 
1449     case TFITS_BIN_TYPE_P:
1450         out_array = (int*)qfits_query_column_seq(th, colnum,
1451                                                  start_ind, nb_rows);
1452         break;
1453 
1454     default:
1455         qfits_error("unrecognized data type");
1456         return NULL;
1457     }
1458     return out_array;
1459 }
1460 
1461 /*----------------------------------------------------------------------------*/
1462 /**
1463  @brief    Detect NULL values in a column
1464  @param    th        Allocated qfits_table
1465  @param    colnum    Number of the column to check (from 0 to colnum-1)
1466  @param    selection Array to identify selected rows
1467  @param    nb_vals Gives the size of the output array
1468  @param    nb_nulls Gives the number of detected null values
1469  @return     array with 1 for NULLs and 0 for non-NULLs
1470  The returned object must be deallocated with qfits_free().
1471  */
1472 /*----------------------------------------------------------------------------*/
qfits_query_column_nulls(const qfits_table * th,int colnum,const int * selection,int * nb_vals,int * nb_nulls)1473 int * qfits_query_column_nulls(
1474                                const qfits_table   *   th,
1475                                int                     colnum,
1476                                const int           *   selection,
1477                                int                 *   nb_vals,
1478                                int                 *   nb_nulls)
1479 {
1480     int             *    out_array;
1481     qfits_col       *   col;
1482     unsigned char    *    in_array;
1483     void            *    tmp_array;
1484     char            *    field;
1485     int                 nb_rows;
1486     int                 i;
1487 
1488     /* Initialize */
1489     *nb_nulls = 0;
1490     *nb_vals = 0;
1491 
1492     /* Get the number of selected rows */
1493     nb_rows = 0;
1494     if (selection == NULL) {
1495         nb_rows = th->nr;
1496     } else {
1497         for (i=0; i<th->nr; i++) if (selection[i] == 1) nb_rows++;
1498     }
1499 
1500     /* Pointer to requested column */
1501     col = th->col+colnum;
1502 
1503     /* Test if column is readable */
1504     if (col->readable == 0) return NULL;
1505 
1506     /* Handle each type separately */
1507     switch(col->atom_type) {
1508     case TFITS_ASCII_TYPE_A:
1509     case TFITS_ASCII_TYPE_D:
1510     case TFITS_ASCII_TYPE_E:
1511     case TFITS_ASCII_TYPE_F:
1512     case TFITS_ASCII_TYPE_I:
1513         in_array = (unsigned char*)qfits_query_column(th, colnum, selection);
1514         out_array = qfits_calloc(nb_rows, sizeof(int));
1515         *nb_vals = nb_rows;
1516         field = qfits_malloc((col->atom_nb+1)*sizeof(char));
1517         for (i=0; i<nb_rows; i++) {
1518             /* Copy all atoms of the field into 'field' */
1519             memcpy(field, &in_array[i*col->atom_nb], col->atom_nb);
1520             field[col->atom_nb]='\0';
1521             /* Test if a NULL val is encoutered */
1522             if (!strcmp(col->nullval, qfits_strstrip(field))) {
1523                 out_array[i] = 1;
1524                 (*nb_nulls)++;
1525             }
1526         }
1527         qfits_free(field);
1528         if (in_array != NULL) qfits_free(in_array);
1529         break;
1530 
1531     case TFITS_BIN_TYPE_A:
1532         /* No NULL values */
1533         out_array = qfits_calloc(nb_rows * col->atom_nb, sizeof(int));
1534         *nb_vals = nb_rows * col->atom_nb;
1535         break;
1536 
1537     case TFITS_BIN_TYPE_L:
1538     case TFITS_BIN_TYPE_X:
1539     case TFITS_BIN_TYPE_P:
1540         /* No NULL values */
1541         out_array = qfits_calloc(nb_rows * col->atom_nb, sizeof(int));
1542         *nb_vals = nb_rows * col->atom_nb;
1543         break;
1544 
1545     case TFITS_BIN_TYPE_D:
1546     case TFITS_BIN_TYPE_M:
1547         tmp_array = (double*)qfits_query_column(th, colnum, selection);
1548         out_array = qfits_calloc(nb_rows * col->atom_nb, sizeof(int));
1549         *nb_vals = nb_rows * col->atom_nb;
1550         for (i=0; i<nb_rows * col->atom_nb; i++) {
1551             if (qfits_isnan(((double*)tmp_array)[i]) ||
1552                 qfits_isinf(((double*)tmp_array)[i])) {
1553                 out_array[i] = 1;
1554                 (*nb_nulls)++;
1555             }
1556         }
1557         if (tmp_array != NULL) qfits_free(tmp_array);
1558         break;
1559 
1560     case TFITS_BIN_TYPE_E:
1561     case TFITS_BIN_TYPE_C:
1562         tmp_array = (float*)qfits_query_column(th, colnum, selection);
1563         out_array = qfits_calloc(nb_rows * col->atom_nb, sizeof(int));
1564         *nb_vals = nb_rows * col->atom_nb;
1565         for (i=0; i<nb_rows * col->atom_nb; i++) {
1566             if (qfits_isnan(((float*)tmp_array)[i]) ||
1567                 qfits_isinf(((float*)tmp_array)[i])) {
1568                 out_array[i] = 1;
1569                 (*nb_nulls)++;
1570             }
1571         }
1572         if (tmp_array != NULL) qfits_free(tmp_array);
1573         break;
1574 
1575     case TFITS_BIN_TYPE_B:
1576         tmp_array = (unsigned char*)qfits_query_column(th, colnum, selection);
1577         out_array = qfits_calloc(nb_rows * col->atom_nb, sizeof(int));
1578         *nb_vals = nb_rows * col->atom_nb;
1579         for (i=0; i<nb_rows * col->atom_nb; i++) {
1580             if (((col->nullval)[0] != '\0') &&
1581                 (atoi(col->nullval)==(int)((unsigned char*)tmp_array)[i])) {
1582                 out_array[i] = 1;
1583                 (*nb_nulls)++;
1584             }
1585         }
1586         if (tmp_array != NULL) qfits_free(tmp_array);
1587         break;
1588 
1589     case TFITS_BIN_TYPE_I:
1590         tmp_array = (short*)qfits_query_column(th, colnum, selection);
1591         out_array = qfits_calloc(nb_rows * col->atom_nb, sizeof(int));
1592         *nb_vals = nb_rows * col->atom_nb;
1593         for (i=0; i<nb_rows * col->atom_nb; i++) {
1594             if (((col->nullval)[0] != '\0') &&
1595                 (atoi(col->nullval)==(int)((short*)tmp_array)[i])) {
1596                 out_array[i] = 1;
1597                 (*nb_nulls)++;
1598             }
1599         }
1600         if (tmp_array != NULL) qfits_free(tmp_array);
1601         break;
1602 
1603     case TFITS_BIN_TYPE_K:
1604         tmp_array = (int64_t*)qfits_query_column(th, colnum, selection);
1605         out_array = calloc(nb_rows * col->atom_nb, sizeof(int64_t));
1606         *nb_vals = nb_rows * col->atom_nb;
1607         for (i=0; i<nb_rows * col->atom_nb; i++) {
1608             if (((col->nullval)[0] != '\0') &&
1609                 (atoll(col->nullval)==((int64_t*)tmp_array)[i])) {
1610                 out_array[i] = 1;
1611                 (*nb_nulls)++;
1612             }
1613         }
1614         if (tmp_array != NULL) free(tmp_array);
1615         break;
1616 
1617     case TFITS_BIN_TYPE_J:
1618         tmp_array = (int*)qfits_query_column(th, colnum, selection);
1619         out_array = qfits_calloc(nb_rows * col->atom_nb, sizeof(int));
1620         *nb_vals = nb_rows * col->atom_nb;
1621         for (i=0; i<nb_rows * col->atom_nb; i++) {
1622             if (((col->nullval)[0] != '\0') &&
1623                 (atoi(col->nullval)==((int*)tmp_array)[i])) {
1624                 out_array[i] = 1;
1625                 (*nb_nulls)++;
1626             }
1627         }
1628         if (tmp_array != NULL) qfits_free(tmp_array);
1629         break;
1630 
1631     default:
1632         qfits_error("unrecognized data type");
1633         return NULL;
1634     }
1635     return out_array;
1636 }
1637 
1638 
1639 /*----------------------------------------------------------------------------*/
1640 /**
1641  @brief    Appends a std extension header + data to a FITS table file.
1642  @param    outfile        Pointer to (opened) file ready for writing.
1643  @param    t            Pointer to qfits_table
1644  @param    data        Table data to write
1645  @return    int 0 if Ok, -1 otherwise
1646 
1647  Dumps a FITS table to a file. The whole table described by qfits_table, and
1648  the data arrays contained in 'data' are dumped to the file. An extension
1649  header is produced with all keywords needed to describe the table, then the
1650  data is dumped to the file.
1651  The output is then padded to reach a multiple of 2880 bytes in size.
1652  Notice that no main header is produced, only the extension part.
1653  */
1654 /*----------------------------------------------------------------------------*/
qfits_table_append_xtension(FILE * outfile,const qfits_table * t,const void ** data)1655 int qfits_table_append_xtension(
1656                                 FILE                *   outfile,
1657                                 const qfits_table   *   t,
1658                                 const void          **  data)
1659 {
1660     /* Append the extension */
1661     if (t->tab_t == QFITS_BINTABLE) {
1662         if (qfits_table_append_bin_xtension(outfile, t, data) == -1) {
1663             qfits_error("in writing fits table");
1664             return -1;
1665         }
1666     } else if (t->tab_t == QFITS_ASCIITABLE) {
1667         if (qfits_table_append_ascii_xtension(outfile, t, data) == -1) {
1668             qfits_error("in writing fits table");
1669             return -1;
1670         }
1671     } else {
1672         qfits_error("Unrecognized table type");
1673         return -1;
1674     }
1675     return 0;
1676 }
1677 
1678 /*----------------------------------------------------------------------------*/
1679 /**
1680  @brief    Appends a specified extension header + data to a FITS table file.
1681  @param    outfile        Pointer to (opened) file ready for writing.
1682  @param    t            Pointer to qfits_table
1683  @param    data        Table data to write
1684  @param    hdr         Specified extension header
1685  @return    int 0 if Ok, -1 otherwise
1686 
1687  Dumps a FITS table to a file. The whole table described by qfits_table, and
1688  the data arrays contained in 'data' are dumped to the file following the
1689  specified fits header.
1690  The output is then padded to reach a multiple of 2880 bytes in size.
1691  Notice that no main header is produced, only the extension part.
1692  */
1693 /*----------------------------------------------------------------------------*/
qfits_table_append_xtension_hdr(FILE * outfile,const qfits_table * t,const void ** data,const qfits_header * hdr)1694 int qfits_table_append_xtension_hdr(
1695                                     FILE                *   outfile,
1696                                     const qfits_table   *   t,
1697                                     const void          **  data,
1698                                     const qfits_header  *   hdr)
1699 {
1700     /* Write the fits header in the file  */
1701     if (qfits_header_dump(hdr, outfile) == -1) {
1702         qfits_error("cannot dump header in file");
1703         return -1;
1704     }
1705 
1706     /* Append the data to the file */
1707     return qfits_table_append_data(outfile, t, data);
1708 }
1709 
1710 /*----------------------------------------------------------------------------*/
1711 /**
1712  @brief    given a col and a row, find out the string to write for display
1713  @param    table    table structure
1714  @param    col_id    col id (0 -> nbcol-1)
1715  @param    row_id    row id (0 -> nrow-1)
1716  @param    use_zero_scale    Flag to use or not zero and scale
1717  @return    the string
1718 
1719  This function is highly inefficient, it should not be used in loops to
1720  display a complete table. It is more to get one field from time to
1721  time, or for debugging puposes.
1722  The returned object must be deallocated with qfits_free().
1723  */
1724 /*----------------------------------------------------------------------------*/
qfits_table_field_to_string(const qfits_table * table,int col_id,int row_id,int use_zero_scale)1725 char * qfits_table_field_to_string(
1726                                    const qfits_table   *   table,
1727                                    int                     col_id,
1728                                    int                     row_id,
1729                                    int                     use_zero_scale)
1730 {
1731     char    *    str;
1732 
1733     switch (table->tab_t) {
1734     case QFITS_BINTABLE:
1735         str = qfits_bintable_field_to_string(table, col_id, row_id,
1736                                              use_zero_scale);
1737         break;
1738 
1739     case QFITS_ASCIITABLE:
1740         str = qfits_asciitable_field_to_string(table, col_id, row_id,
1741                                                use_zero_scale);
1742         break;
1743     default:
1744         qfits_error("Table type not recognized");
1745         return NULL;
1746         break;
1747     }
1748     return str;
1749 }
1750 
1751 /**@}*/
1752 
1753 /*----------------------------------------------------------------------------*/
1754 /**
1755  @brief    Compute the table width in bytes from the columns infos
1756  @param    th        Allocated qfits_table
1757  @return    the width (-1 in error case)
1758  */
1759 /*----------------------------------------------------------------------------*/
qfits_compute_table_width(const qfits_table * th)1760 int qfits_compute_table_width(const qfits_table * th)
1761 {
1762     int             width;
1763     qfits_col   *   curr_col;
1764     int             i;
1765 
1766     /* Initialize */
1767     width = 0;
1768 
1769     /* Loop on all columns and get column descriptions  */
1770     curr_col = th->col;
1771     for (i=0; i<th->nc; i++) {
1772         if (th->tab_t == QFITS_ASCIITABLE) {
1773             width += curr_col->atom_nb;
1774         } else if (th->tab_t == QFITS_BINTABLE) {
1775             width += curr_col->atom_nb * curr_col->atom_size;
1776         }
1777         curr_col++;
1778     }
1779     return width;
1780 }
1781 
1782 /*----------------------------------------------------------------------------*/
1783 /**
1784  @brief    given a col and a row, find out the string to write for display
1785  @param    table    table structure
1786  @param    col_id    col id (0 -> nbcol-1)
1787  @param    row_id    row id (0 -> nrow-1)
1788  @param    use_zero_scale    Flag to use or not zero and scale
1789  @return    the string to write
1790 
1791  The returned object must be deallocated with qfits_free().
1792  ASCII tables specific
1793  */
1794 /*----------------------------------------------------------------------------*/
qfits_asciitable_field_to_string(const qfits_table * table,int col_id,int row_id,int use_zero_scale)1795 static char * qfits_asciitable_field_to_string(
1796                                                const qfits_table   *   table,
1797                                                int                     col_id,
1798                                                int                     row_id,
1799                                                int                     use_zero_scale)
1800 {
1801     qfits_col       *       col;
1802     char            *       ccol;
1803     int             *       icol;
1804     float           *       fcol;
1805     double          *       dcol;
1806     char                    ctmp[512];
1807     char            *       stmp;
1808     int                     field_size;
1809     void            *        field;
1810     int             *       selection;
1811 
1812     /* Test inputs */
1813     if (table->tab_t != QFITS_ASCIITABLE) return NULL;
1814 
1815     /* Initialize */
1816     ctmp[0] = '\0';
1817 
1818     /* Set selection to select the requested row */
1819     selection = qfits_calloc(table->nr, sizeof(int));
1820     selection[row_id] = 1;
1821 
1822     /* Load the column data */
1823     if ((field = qfits_query_column_data(table, col_id, selection,
1824                                          NULL)) == NULL) return NULL;
1825     qfits_free(selection);
1826 
1827     /* Set reference to the column */
1828     col = table->col + col_id;
1829 
1830     /* Compute field size and allocate stmp */
1831     if (col->atom_nb > ELEMENT_MAX_DISPLAY_SIZE) field_size = col->atom_nb + 1;
1832     else field_size = ELEMENT_MAX_DISPLAY_SIZE;
1833     stmp = qfits_malloc(field_size * sizeof(char));
1834     stmp[0] = '\0';
1835 
1836     /* Get the string to write according to the type */
1837     switch(col->atom_type) {
1838     case TFITS_ASCII_TYPE_A:
1839         ccol = (char*)field;
1840         strncpy(ctmp, ccol, col->atom_nb);
1841         ctmp[col->atom_nb] = '\0';
1842         strcpy(stmp, ctmp);
1843         break;
1844 
1845     case TFITS_ASCII_TYPE_I:
1846         icol = (int*)field;
1847         /* Two cases: use col->zero and col->scale or not */
1848         if (col->zero_present && col->scale_present && use_zero_scale) {
1849             sprintf(stmp, "%f", (float)(col->zero +
1850                                         (float)icol[0] * col->scale));
1851         } else {
1852             sprintf(stmp, "%d", icol[0]);
1853         }
1854         break;
1855 
1856     case TFITS_ASCII_TYPE_E:
1857     case TFITS_ASCII_TYPE_F:
1858         fcol = (float*)field;
1859         /* Two cases: use col->zero and col->scale or not */
1860         if (col->zero_present && col->scale_present && use_zero_scale) {
1861             sprintf(stmp, "%f", (float)(col->zero +
1862                                         fcol[0] * col->scale));
1863         } else {
1864             sprintf(stmp, "%f", fcol[0]);
1865         }
1866         break;
1867 
1868     case TFITS_ASCII_TYPE_D:
1869         dcol = (double*)field;
1870         /* Two cases: use col->zero and col->scale or not */
1871         if (col->zero_present && col->scale_present && use_zero_scale) {
1872             sprintf(stmp, "%f", (float)(col->zero +
1873                                         (float)dcol[0] * col->scale));
1874         } else {
1875             sprintf(stmp, "%g", dcol[0]);
1876         }
1877         break;
1878     default:
1879         qfits_warning("Type not recognized");
1880         break;
1881     }
1882 
1883     /* Free and return */
1884     qfits_free(field);
1885     return stmp;
1886 }
1887 
1888 /*----------------------------------------------------------------------------*/
1889 /**
1890  @brief    Given a col and a row, find out the string to write for display
1891  @param    table    table structure
1892  @param    col_id    col id (0 -> nbcol-1)
1893  @param    row_id    row id (0 -> nrow-1)
1894  @param    use_zero_scale    Flag to use or not zero and scale
1895  @return    the allocated string to write
1896 
1897  The returned object must be deallocated with qfits_free().
1898  BIN tables specific
1899  */
1900 /*----------------------------------------------------------------------------*/
qfits_bintable_field_to_string(const qfits_table * table,int col_id,int row_id,int use_zero_scale)1901 static char * qfits_bintable_field_to_string(
1902                                              const qfits_table   *   table,
1903                                              int                     col_id,
1904                                              int                     row_id,
1905                                              int                     use_zero_scale)
1906 {
1907     qfits_col       *       col;
1908     unsigned char   *       uccol;
1909     char            *       ccol;
1910     int             *       icol;
1911     int64_t         *       kcol;
1912     short           *       scol;
1913     float           *       fcol;
1914     double          *       dcol;
1915     char                    ctmp[512];
1916     char            *       stmp;
1917     int                     field_size;
1918     void            *        field;
1919     int             *       selection;
1920 
1921     int                     i;
1922 
1923     /* Test inputs */
1924     if (table->tab_t != QFITS_BINTABLE) return NULL;
1925 
1926     /* Initialize */
1927     ctmp[0] = '\0';
1928 
1929     /* Set selection to select the requested row */
1930     selection = qfits_calloc(table->nr, sizeof(int));
1931     selection[row_id] = 1;
1932 
1933     /* Load the data column */
1934     if ((field = qfits_query_column_data(table, col_id, selection,
1935                                          NULL)) == NULL) {
1936         qfits_free(selection);
1937         return NULL;
1938     }
1939     qfits_free(selection);
1940 
1941     /* Set reference to the column */
1942     col = table->col + col_id;
1943 
1944     /* Compute field size and allocate stmp */
1945     field_size = col->atom_nb * ELEMENT_MAX_DISPLAY_SIZE;
1946     stmp = qfits_malloc(field_size * sizeof(char));
1947     stmp[0] = '\0';
1948 
1949     /* Get the string to write according to the type */
1950     switch(col->atom_type) {
1951     case TFITS_BIN_TYPE_A:
1952         ccol = (char*)field;
1953         strncpy(ctmp, ccol, col->atom_size * col->atom_nb);
1954         ctmp[col->atom_size*col->atom_nb] = '\0';
1955         strcpy(stmp, ctmp);
1956         break;
1957 
1958     case TFITS_BIN_TYPE_B:
1959         uccol = (unsigned char*)field;
1960         /* Two cases: use col->zero and col->scale or not */
1961         if (col->zero_present && col->scale_present && use_zero_scale) {
1962             /* For each atom of the column */
1963             for (i=0; i<col->atom_nb-1; i++) {
1964                 sprintf(ctmp, "%f, ", (float)(col->zero +
1965                                               (float)uccol[i] * col->scale));
1966                 strcat(stmp, ctmp);
1967             }
1968             /* Handle the last atom differently: no ',' */
1969             sprintf(ctmp, "%f", (float)(col->zero +
1970                                         (float)uccol[col->atom_nb-1]*col->scale));
1971             strcat(stmp, ctmp);
1972         } else {
1973             /* For each atom of the column */
1974             for (i=0; i<col->atom_nb-1; i++) {
1975                 sprintf(ctmp, "%d, ", (int)uccol[i]);
1976                 strcat(stmp, ctmp);
1977             }
1978             /* Handle the last atom differently: no ',' */
1979             sprintf(ctmp,"%d",(int)uccol[col->atom_nb-1]);
1980             strcat(stmp, ctmp);
1981         }
1982         break;
1983 
1984     case TFITS_BIN_TYPE_D:
1985     case TFITS_BIN_TYPE_M:
1986         dcol = (double*)field;
1987         /* Two cases: use col->zero and col->scale or not */
1988         if (col->zero_present && col->scale_present && use_zero_scale) {
1989             /* For each atom of the column */
1990             for (i=0; i<col->atom_nb-1; i++) {
1991                 sprintf(ctmp, "%g, ", (double)((double)col->zero +
1992                                                dcol[i] * (double)col->scale));
1993                 strcat(stmp, ctmp);
1994             }
1995             /* Handle the last atom differently: no ',' */
1996             sprintf(ctmp, "%g", (double)((double)col->zero +
1997                                          dcol[col->atom_nb-1] * (double)col->scale));
1998             strcat(stmp, ctmp);
1999         } else {
2000             /* For each atom of the column */
2001             for (i=0; i<col->atom_nb-1; i++) {
2002                 sprintf(ctmp, "%g, ", dcol[i]);
2003                 strcat(stmp, ctmp);
2004             }
2005             /* Handle the last atom differently: no ',' */
2006             sprintf(ctmp, "%g", dcol[col->atom_nb-1]);
2007             strcat(stmp, ctmp);
2008         }
2009         break;
2010 
2011     case TFITS_BIN_TYPE_E:
2012     case TFITS_BIN_TYPE_C:
2013         fcol = (float*)field;
2014         /* Two cases: use col->zero and col->scale or not */
2015         if (col->zero_present && col->scale_present && use_zero_scale) {
2016             /* For each atom of the column */
2017             for (i=0; i<col->atom_nb-1; i++) {
2018                 sprintf(ctmp, "%f, ", (float)(col->zero +
2019                                               (float)fcol[i] * col->scale));
2020                 strcat(stmp, ctmp);
2021             }
2022             /* Handle the last atom differently: no ',' */
2023             sprintf(ctmp, "%f", (float)(col->zero +
2024                                         (float)fcol[col->atom_nb-1] * col->scale));
2025             strcat(stmp, ctmp);
2026         } else {
2027             /* For each atom of the column */
2028             for (i=0; i<col->atom_nb-1; i++) {
2029                 sprintf(ctmp, "%f, ", fcol[i]);
2030                 strcat(stmp, ctmp);
2031             }
2032             /* Handle the last atom differently: no ',' */
2033             sprintf(ctmp, "%f", fcol[col->atom_nb-1]);
2034             strcat(stmp, ctmp);
2035         }
2036         break;
2037 
2038     case TFITS_BIN_TYPE_I:
2039         scol = (short*)field;
2040         /* Two cases: use col->zero and col->scale or not */
2041         if (col->zero_present && col->scale_present && use_zero_scale) {
2042             /* For each atom of the column */
2043             for (i=0; i<col->atom_nb-1; i++) {
2044                 sprintf(ctmp, "%f, ", (float)(col->zero +
2045                                               (float)scol[i] * col->scale));
2046                 strcat(stmp, ctmp);
2047             }
2048             /* Handle the last atom differently: no ',' */
2049             sprintf(ctmp, "%f", (float)(col->zero +
2050                                         (float)scol[col->atom_nb-1] * col->scale));
2051             strcat(stmp, ctmp);
2052         } else {
2053             /* For each atom of the column */
2054             for (i=0; i<col->atom_nb-1; i++) {
2055                 sprintf(ctmp, "%d, ", (int)scol[i]);
2056                 strcat(stmp, ctmp);
2057             }
2058             /* Handle the last atom differently: no ',' */
2059             sprintf(ctmp, "%d",(int)scol[col->atom_nb-1]);
2060             strcat(stmp, ctmp);
2061         }
2062         break;
2063 
2064     case TFITS_BIN_TYPE_J:
2065         icol = (int*)field;
2066         /* Two cases: use col->zero and col->scale or not */
2067         if (col->zero_present && col->scale_present && use_zero_scale) {
2068             /* For each atom of the column */
2069             for (i=0; i<col->atom_nb-1; i++) {
2070                 sprintf(ctmp, "%f, ", (float)(col->zero +
2071                                               (float)icol[i] * col->scale));
2072                 strcat(stmp, ctmp);
2073             }
2074             /* Handle the last atom differently: no ',' */
2075             sprintf(ctmp, "%f", (float)(col->zero +
2076                                         (float)icol[col->atom_nb-1] * col->scale));
2077             strcat(stmp, ctmp);
2078         } else {
2079             /* For each atom of the column */
2080             for (i=0; i<col->atom_nb-1; i++) {
2081                 sprintf(ctmp, "%d, ", (int)icol[i]);
2082                 strcat(stmp, ctmp);
2083             }
2084             /* Handle the last atom differently: no ',' */
2085             sprintf(ctmp, "%d",(int)icol[col->atom_nb-1]);
2086             strcat(stmp, ctmp);
2087         }
2088         break;
2089 
2090     case TFITS_BIN_TYPE_K:
2091         kcol = (int64_t*)field;
2092         /* Two cases: use col->zero and col->scale or not */
2093         if (col->zero_present && col->scale_present && use_zero_scale) {
2094             /* For each atom of the column */
2095             for (i=0; i<col->atom_nb-1; i++) {
2096                 sprintf(ctmp, "%f, ", (float)(col->zero +
2097                                               (float)kcol[i] * col->scale));
2098                 strcat(stmp, ctmp);
2099             }
2100             /* Handle the last atom differently: no ',' */
2101             sprintf(ctmp, "%f", (float)(col->zero +
2102                                         (float)kcol[col->atom_nb-1] * col->scale));
2103             strcat(stmp, ctmp);
2104         } else {
2105             /* For each atom of the column */
2106             for (i=0; i<col->atom_nb-1; i++) {
2107                 sprintf(ctmp, "%lld, ", (long long int)kcol[i]);
2108                 strcat(stmp, ctmp);
2109             }
2110             /* Handle the last atom differently: no ',' */
2111             sprintf(ctmp, "%lld", (long long int)kcol[col->atom_nb-1]);
2112             strcat(stmp, ctmp);
2113         }
2114         break;
2115 
2116     case TFITS_BIN_TYPE_L:
2117         ccol = (char*)field;
2118         /* For each atom of the column */
2119         for (i=0; i<col->atom_nb-1; i++) {
2120             sprintf(ctmp, "%c, ", ccol[i]);
2121             strcat(stmp, ctmp);
2122         }
2123         /* Handle the last atom differently: no ',' */
2124         sprintf(ctmp, "%c", ccol[col->atom_nb-1]);
2125         strcat(stmp, ctmp);
2126         break;
2127 
2128     case TFITS_BIN_TYPE_X:
2129         uccol = (unsigned char*)field;
2130         /* For each atom of the column */
2131         for (i=0; i<col->atom_nb-1; i++) {
2132             sprintf(ctmp, "%d, ", uccol[i]);
2133             strcat(stmp, ctmp);
2134         }
2135         /* Handle the last atom differently: no ',' */
2136         sprintf(ctmp, "%d", uccol[col->atom_nb-1]);
2137         strcat(stmp, ctmp);
2138         break;
2139 
2140     case TFITS_BIN_TYPE_P:
2141         icol = (int*)field;
2142         /* For each atom of the column */
2143         for (i=0; i<col->atom_nb-1; i++) {
2144             sprintf(ctmp, "%d, ", (int)icol[i]);
2145             strcat(stmp, ctmp);
2146         }
2147         /* Handle the last atom differently: no ',' */
2148         sprintf(ctmp, "%d",(int)icol[col->atom_nb-1]);
2149         strcat(stmp, ctmp);
2150         break;
2151 
2152     default:
2153         qfits_warning("Type not recognized");
2154         break;
2155     }
2156     qfits_free(field);
2157     return stmp;
2158 }
2159 
2160 /*----------------------------------------------------------------------------*/
2161 /**
2162  @brief    Remove blanks at the beginning and the end of a string.
2163  @param    s   String to parse.
2164  @return   ptr to statically allocated string.
2165 
2166  This function returns a pointer to a statically allocated string,
2167  which is identical to the input string, except that all blank
2168  characters at the end and the beg. of the string have been removed.
2169  Do not free or modify the returned string!
2170  Since the returned string is statically allocated, it will be modified at
2171  each function call (not re-entrant).
2172  */
2173 /*----------------------------------------------------------------------------*/
qfits_strstrip(const char * s)2174 static char * qfits_strstrip(const char * s)
2175 {
2176     static char l[1024+1];
2177     char * last;
2178 
2179     if (s==NULL) return NULL;
2180 
2181     while (isspace((int)*s) && *s) s++;
2182 
2183     memset(l, 0, 1024+1);
2184     strcpy(l, s);
2185     last = l + strlen(l);
2186     while (last > l) {
2187         if (!isspace((int)*(last-1)))
2188             break;
2189         last --;
2190     }
2191     *last = '\0';
2192 
2193     return (char*)l;
2194 }
2195 
2196 /*----------------------------------------------------------------------------*/
2197 /**
2198  @brief    Make a double out of a string and a number of decimals
2199  @param    to_format   the string to convert
2200  @param    nb_dec      the number of decimals
2201  @return   the double
2202  A field with 123 of type F3.1 actually contains 12.3
2203  This is handled by this function.
2204  */
2205 /*----------------------------------------------------------------------------*/
qfits_str2dec(const char * to_format,int nb_dec)2206 static double qfits_str2dec(
2207                             const char  *   to_format,
2208                             int             nb_dec)
2209 {
2210     double      val;
2211     int         i;
2212 
2213     /* Test entries */
2214     if (to_format == NULL) return 0.00;
2215 
2216     val = (double)atof(to_format);
2217     /* First handle case where there are no decimals or the dot is there */
2218     if ((strstr(to_format, ".") == NULL) && (nb_dec > 0)) {
2219         for (i=0; i<nb_dec; i++) val /=10;
2220     }
2221     return val;
2222 }
2223 
2224 /*----------------------------------------------------------------------------*/
2225 /**
2226  @brief    Parse a FITS type
2227  @param    str        string read in the FITS header (e.g. TFORM value)
2228  @param    nb        pointer to the number
2229  @param    dec_nb  pointer to the number of decimals
2230  @param    type    pointer to the type
2231  @param    table_type    Table type (BIN, ASCII, ...)
2232  @return    0 if ok, -1 otherwise
2233 
2234  This functions reads the input string and uses it to update nb and type
2235  */
2236 /*----------------------------------------------------------------------------*/
qfits_table_interpret_type(const char * str,int * nb,int * dec_nb,tfits_type * type,int table_type)2237 int qfits_table_interpret_type(
2238                                const char  *   str,
2239                                int         *   nb,
2240                                int         *   dec_nb,
2241                                tfits_type  *   type,
2242                                int             table_type)
2243 {
2244     char        type_c;
2245 
2246     *dec_nb = 0;
2247     if (table_type == QFITS_BINTABLE) {
2248         if (sscanf(str, "%d%c", nb, &type_c) == 0) {
2249             /* nb is 1 by default */
2250             if (sscanf(str, "%c", &type_c) == 0) {
2251                 qfits_error("cannot interpret this type: %s", str);
2252                 return -1;
2253             }
2254             *nb = 1;
2255         }
2256         switch(type_c) {
2257         case 'A': *type = TFITS_BIN_TYPE_A; break;
2258         case 'B': *type = TFITS_BIN_TYPE_B; break;
2259         case 'C': *type = TFITS_BIN_TYPE_C; break;
2260         case 'D': *type = TFITS_BIN_TYPE_D; break;
2261         case 'E': *type = TFITS_BIN_TYPE_E; break;
2262         case 'I': *type = TFITS_BIN_TYPE_I; break;
2263         case 'J': *type = TFITS_BIN_TYPE_J; break;
2264         case 'K': *type = TFITS_BIN_TYPE_K; break;
2265         case 'L': *type = TFITS_BIN_TYPE_L; break;
2266         case 'M': *type = TFITS_BIN_TYPE_M; break;
2267         case 'P': *type = TFITS_BIN_TYPE_P; break;
2268         case 'X': *type = TFITS_BIN_TYPE_X; break;
2269         default: return -1;
2270         }
2271     } else if (table_type == QFITS_ASCIITABLE) {
2272         if (sscanf(str, "%c%d.%d", &type_c, nb, dec_nb) == 0) {
2273             qfits_error("cannot interpret this type: %s", str);
2274             return -1;
2275         }
2276         switch(type_c) {
2277         case 'A': *type = TFITS_ASCII_TYPE_A; break;
2278         case 'D': *type = TFITS_ASCII_TYPE_D; break;
2279         case 'E': *type = TFITS_ASCII_TYPE_E; break;
2280         case 'F': *type = TFITS_ASCII_TYPE_F; break;
2281         case 'I': *type = TFITS_ASCII_TYPE_I; break;
2282         default: return -1;
2283         }
2284     } else {
2285         qfits_error("unrecognized table type");
2286         return -1;
2287     }
2288     return 0;
2289 }
2290 
2291 /*----------------------------------------------------------------------------*/
2292 /**
2293  @brief    Generate a FITS type string
2294  @param    col        input column
2295  @return    The string to write to TFORM
2296  */
2297 /*----------------------------------------------------------------------------*/
qfits_build_format(const qfits_col * col)2298 static char * qfits_build_format(const qfits_col * col)
2299 {
2300     static char sval[10];
2301     int         nb;
2302 
2303     switch (col->atom_type) {
2304     case TFITS_ASCII_TYPE_A:
2305         nb=sprintf(sval, "A%d.%d", col->atom_nb, col->atom_dec_nb); break;
2306     case TFITS_ASCII_TYPE_D:
2307         nb=sprintf(sval, "D%d.%d", col->atom_nb, col->atom_dec_nb); break;
2308     case TFITS_ASCII_TYPE_E:
2309         nb=sprintf(sval, "E%d.%d", col->atom_nb, col->atom_dec_nb); break;
2310     case TFITS_ASCII_TYPE_I:
2311         nb=sprintf(sval, "I%d.%d", col->atom_nb, col->atom_dec_nb); break;
2312     case TFITS_ASCII_TYPE_F:
2313         nb=sprintf(sval, "F%d.%d", col->atom_nb, col->atom_dec_nb); break;
2314     case TFITS_BIN_TYPE_D: nb=sprintf(sval, "%dD", col->atom_nb); break;
2315     case TFITS_BIN_TYPE_E: nb=sprintf(sval, "%dE", col->atom_nb); break;
2316     case TFITS_BIN_TYPE_I: nb=sprintf(sval, "%dI", col->atom_nb); break;
2317     case TFITS_BIN_TYPE_A: nb=sprintf(sval, "%dA", col->atom_nb); break;
2318     case TFITS_BIN_TYPE_B: nb=sprintf(sval, "%dB", col->atom_nb); break;
2319     case TFITS_BIN_TYPE_C: nb=sprintf(sval, "%dC",
2320                                       (int)(col->atom_nb/2)); break;
2321     case TFITS_BIN_TYPE_J: nb=sprintf(sval, "%dJ", col->atom_nb); break;
2322     case TFITS_BIN_TYPE_K: nb=sprintf(sval, "%dK", col->atom_nb); break;
2323     case TFITS_BIN_TYPE_L: nb=sprintf(sval, "%dL", col->atom_nb); break;
2324     case TFITS_BIN_TYPE_M: nb=sprintf(sval, "%dM",
2325                                       (int)(col->atom_nb/2)); break;
2326     case TFITS_BIN_TYPE_P: nb=sprintf(sval, "%dP",
2327                                       (int)(col->atom_nb/2)); break;
2328     case TFITS_BIN_TYPE_X: nb=sprintf(sval, "%dX",
2329                                       8*col->atom_nb); break;
2330     default: return NULL;
2331     }
2332     sval[nb] = '\0';
2333     return sval;
2334 }
2335 
2336 /*----------------------------------------------------------------------------*/
2337 /**
2338  @brief    Appends a std extension header + data to a FITS BIN table file.
2339  @param    outfile        Pointer to (opened) file ready for writing.
2340  @param    t            Pointer to qfits_table
2341  @param    data        Table data to write
2342  @return    int 0 if Ok, -1 otherwise
2343 
2344  Dumps a FITS table to a file. The whole table described by qfits_table, and
2345  the data arrays contained in 'data' are dumped to the file. An extension
2346  header is produced with all keywords needed to describe the table, then the
2347  data is dumped to the file.
2348  The output is then padded to reach a multiple of 2880 bytes in size.
2349  Notice that no main header is produced, only the extension part.
2350  */
2351 /*----------------------------------------------------------------------------*/
qfits_table_append_bin_xtension(FILE * outfile,const qfits_table * t,const void ** data)2352 static int qfits_table_append_bin_xtension(
2353                                            FILE                *   outfile,
2354                                            const qfits_table   *   t,
2355                                            const void          **  data)
2356 {
2357     qfits_header    *    fh;
2358 
2359     if ((fh=qfits_table_ext_header_default(t)) == NULL) {
2360         qfits_error("cannot create new fits header");
2361         return -1;
2362     }
2363 
2364     /* Write the fits header in the file  */
2365     if (qfits_header_dump(fh, outfile) == -1) {
2366         qfits_error("cannot dump header in file");
2367         qfits_header_destroy(fh);
2368         fclose(outfile);
2369         return -1;
2370     }
2371     qfits_header_destroy(fh);
2372 
2373     /* Append the data to the file */
2374     return qfits_table_append_data(outfile, t, data);
2375 }
2376 
2377 /*----------------------------------------------------------------------------*/
2378 /**
2379  @brief    Appends an extension header + data to a FITS ASCII table file.
2380  @param    outfile        Pointer to (opened) file ready for writing.
2381  @param    t            Pointer to qfits_table
2382  @param    data        Table data to write
2383  @return    int 0 if Ok, -1 otherwise
2384 
2385  Dumps a FITS table to a file. The whole table described by
2386  qfits_table, and the data arrays contained in 'data' are dumped to
2387  the file. An extension header is produced with all keywords needed
2388  to describe the table, then the data is dumped to the file.
2389 
2390  The output is then padded to reach a multiple of 2880 bytes in size.
2391 
2392  Notice that no main header is produced, only the extension part.
2393  */
2394 /*----------------------------------------------------------------------------*/
qfits_table_append_ascii_xtension(FILE * outfile,const qfits_table * t,const void ** data)2395 static int qfits_table_append_ascii_xtension(
2396                                              FILE                *   outfile,
2397                                              const qfits_table   *   t,
2398                                              const void          **  data)
2399 {
2400     qfits_header    *    fh;
2401 
2402     if ((fh=qfits_table_ext_header_default(t)) == NULL) {
2403         qfits_error("cannot create new fits header");
2404         return -1;
2405     }
2406 
2407     /* Write the fits header in the file  */
2408     if (qfits_header_dump(fh, outfile) == -1) {
2409         qfits_error("cannot dump header in file");
2410         qfits_header_destroy(fh);
2411         return -1;
2412     }
2413     qfits_header_destroy(fh);
2414 
2415     /* Append the data to the file */
2416     return qfits_table_append_data(outfile, t, data);
2417 }
2418 
2419 /*----------------------------------------------------------------------------*/
2420 /**
2421  @brief    Appends data to a FITS table file.
2422  @param    outfile        Pointer to (opened) file ready for writing.
2423  @param    t            Pointer to qfits_table
2424  @param    data        Table data to write
2425  @return    int 0 if Ok, -1 otherwise
2426 
2427  Dumps the data part of a FITS table to a file. The primary header, as well as
2428  the extension header are supposed to be already there (and properly padded).
2429  The output is then padded to reach a multiple of 2880 bytes in size.
2430  */
2431 /*----------------------------------------------------------------------------*/
qfits_table_append_data(FILE * outfile,const qfits_table * t,const void ** data)2432 static int qfits_table_append_data(
2433                                    FILE                *   outfile,
2434                                    const qfits_table   *   t,
2435                                    const void          **  data)
2436 {
2437     qfits_col       *   curr_col;
2438     char                field[1024];
2439     char            *   line;
2440     unsigned char   **  array;
2441     unsigned char   *   r;
2442     unsigned char   *   inbuf;
2443     int                 writt_char;
2444     int                 nb_blanks;
2445     int                 field_size;
2446     int                 i, j;
2447 
2448     /* Write DATA */
2449     array = qfits_malloc(t->nc*sizeof(unsigned char *));
2450 
2451     curr_col = t->col;
2452     for (i=0; i<t->nc; i++) {
2453         /* Compute the field size */
2454         field_size = qfits_table_get_field_size(t->tab_t, curr_col);
2455 
2456         /* Copy data from data to array (unsigned char) */
2457         array[i] = qfits_malloc(t->nr * field_size);
2458         r = (unsigned char *)array[i];
2459         inbuf = (unsigned char *)(data[i]);
2460 
2461         /* Copy the data */
2462         if (t->tab_t == QFITS_ASCIITABLE) {
2463             /* ASCII table */
2464             for (j=0; j<t->nr; j++) {
2465                 switch(curr_col->atom_type) {
2466                 case TFITS_ASCII_TYPE_A :
2467                     strncpy(field, (char*)inbuf, curr_col->atom_nb);
2468                     field[curr_col->atom_nb] = '\0';
2469                     inbuf += curr_col->atom_nb;
2470                     break;
2471                 case TFITS_ASCII_TYPE_D :
2472                     memset(field, ' ', curr_col->atom_nb);
2473                     sprintf(field, "%g", ((double*)data[i])[j]);
2474                     field[curr_col->atom_nb] = '\0';
2475                     break;
2476                 case TFITS_ASCII_TYPE_E :
2477                 case TFITS_ASCII_TYPE_F :
2478                     memset(field, ' ', curr_col->atom_nb);
2479                     sprintf(field, "%f", ((float*)data[i])[j]);
2480                     field[curr_col->atom_nb] = '\0';
2481                     break;
2482                 case TFITS_ASCII_TYPE_I :
2483                     memset(field, ' ', curr_col->atom_nb);
2484                     sprintf(field, "%d", ((int*)data[i])[j]);
2485                     field[curr_col->atom_nb] = '\0';
2486                     break;
2487                 default:
2488                     break;
2489                 }
2490                 memcpy(r, field, curr_col->atom_nb);
2491                 r += (curr_col->atom_nb);
2492             }
2493         } else if (t->tab_t == QFITS_BINTABLE) {
2494             /* BINARY table */
2495             for (j=0; j<t->nr; j++) {
2496                 memcpy(r, inbuf, field_size);
2497                 inbuf += field_size;
2498                 r += field_size;
2499             }
2500 
2501             /* Byte swapping needed if on a little-endian machine */
2502 #ifndef WORDS_BIGENDIAN
2503             if (curr_col->atom_size > 1) {
2504                 r = array[i];
2505                 for (j=0; j<t->nr * curr_col->atom_nb; j++) {
2506                     qfits_swap_bytes(r, curr_col->atom_size);
2507                     r += curr_col->atom_size;
2508                 }
2509             }
2510 #endif
2511         } else return -1;
2512         curr_col++;
2513     }
2514 
2515     /* Write to the outfile */
2516     writt_char = 0;
2517     for (i=0; i<t->nr; i++) {
2518         curr_col = t->col;
2519         for (j=0; j<t->nc; j++) {
2520             field_size = qfits_table_get_field_size(t->tab_t, curr_col);
2521             r = array[j] + field_size * i;
2522             line = (char *)qfits_calloc (field_size+1, sizeof (char));
2523             memcpy(line, r, field_size);
2524             line[field_size] = '\0';
2525             fwrite(line, 1, field_size, outfile);
2526             writt_char += field_size;
2527             curr_col++;
2528             qfits_free(line);
2529         }
2530     }
2531 
2532     /* Complete with blanks to FITS_BLOCK_SIZE characters */
2533     if (writt_char % FITS_BLOCK_SIZE) {
2534         nb_blanks = FITS_BLOCK_SIZE - (writt_char%FITS_BLOCK_SIZE);
2535         for (i=1; i<=nb_blanks; i++) fwrite(" ", 1, 1, outfile);
2536     }
2537 
2538     /* Free and return  */
2539     for(i=0; i<t->nc; i++) {
2540         if (array[i] != NULL) qfits_free(array[i]);
2541     }
2542     qfits_free(array);
2543     return  0;
2544 }
2545 
2546 /*----------------------------------------------------------------------------*/
2547 /**
2548  @brief    Get the size in bytes of a field
2549  @param    type    table type
2550  @param    col     a column
2551  @return   the size
2552  */
2553 /*----------------------------------------------------------------------------*/
qfits_table_get_field_size(int type,const qfits_col * col)2554 static int qfits_table_get_field_size(
2555                                       int                 type,
2556                                       const qfits_col *   col)
2557 {
2558     int     field_size;
2559 
2560     switch (type) {
2561     case QFITS_BINTABLE:
2562         field_size = col->atom_nb * col->atom_size;
2563         break;
2564     case QFITS_ASCIITABLE:
2565         field_size = col->atom_nb;
2566         break;
2567     default:
2568         qfits_warning("unrecognized table type");
2569         field_size = -1;
2570     }
2571     return field_size;
2572 }
2573