1 /*
2  # This file is part of the Astrometry.net suite.
3  # Licensed under a 3-clause BSD style license - see LICENSE
4  */
5 
6 #include <string.h>
7 #include <assert.h>
8 #include <stdarg.h>
9 #include <errors.h>
10 
11 #include "os-features.h"
12 #include "fitstable.h"
13 #include "fitsioutils.h"
14 #include "fitsfile.h"
15 #include "ioutils.h"
16 #include "an-endian.h"
17 #include "anqfits.h"
18 
19 #include "log.h"
20 
21 struct fitscol_t {
22     char* colname;
23 
24     tfits_type fitstype;
25     tfits_type ctype;
26     char* units;
27     int arraysize;
28 
29     anbool required;
30 
31     // size of one data item
32     // computed: fits_sizeof({fits,c}type)
33     int fitssize;
34     int csize;
35 
36     // When being used to write to a C struct, the offset in the struct.
37     anbool in_struct;
38     int coffset;
39 
40     // column number of the FITS table.
41     int col;
42 };
43 typedef struct fitscol_t fitscol_t;
44 
45 // For in-memory: storage of previously-written extensions.
46 struct fitsext {
47     qfits_header* header;
48     qfits_table* table;
49     // bl data size == row size.
50     bl* rows;
51 };
52 typedef struct fitsext fitsext_t;
53 
54 
55 
need_endian_flip()56 static anbool need_endian_flip() {
57     return IS_BIG_ENDIAN == 0;
58 }
59 
60 //static void fitstable_add_columns(fitstable_t* tab, fitscol_t* cols, int Ncols);
61 static fitscol_t* fitstable_add_column(fitstable_t* tab, fitscol_t* col);
62 static void fitstable_create_table(fitstable_t* tab);
63 
ncols(const fitstable_t * t)64 static int ncols(const fitstable_t* t) {
65     return bl_size(t->cols);
66 }
getcol(const fitstable_t * t,int i)67 static fitscol_t* getcol(const fitstable_t* t, int i) {
68     return bl_access(t->cols, i);
69 }
70 
get_row_offset(const fitstable_t * table,int row)71 static off_t get_row_offset(const fitstable_t* table, int row) {
72     assert(table->end_table_offset);
73     assert(table->table);
74     assert(table->table->tab_w);
75     return table->end_table_offset + (off_t)(table->table->tab_w) * (off_t)(row);
76 }
77 
fitstable_n_extensions(const fitstable_t * t)78 int fitstable_n_extensions(const fitstable_t* t) {
79     assert(t);
80     assert(t->anq);
81     return anqfits_n_ext(t->anq);
82 }
83 
fitscolumn_get_size(fitscol_t * col)84 int fitscolumn_get_size(fitscol_t* col) {
85     return col->fitssize * col->arraysize;
86 }
87 
offset_of_column(const fitstable_t * table,int colnum)88 static int offset_of_column(const fitstable_t* table, int colnum) {
89     int i;
90     int offset = 0;
91     assert(colnum <= ncols(table));
92     for (i=0; i<colnum; i++) {
93         fitscol_t* col;
94         col = getcol(table, i);
95         offset += fitscolumn_get_size(col);
96     }
97     return offset;
98 }
99 
fitstable_get_struct_size(const fitstable_t * table)100 int fitstable_get_struct_size(const fitstable_t* table) {
101     int rowsize = offset_of_column(table, bl_size(table->cols));
102     return rowsize;
103 }
104 
is_writing(const fitstable_t * t)105 static anbool is_writing(const fitstable_t* t) {
106     return t->fid ? TRUE : FALSE;
107     //return t->writing;
108 }
109 
ensure_row_list_exists(fitstable_t * table)110 static void ensure_row_list_exists(fitstable_t* table) {
111     if (!table->rows) {
112         // how big are the rows?
113         int rowsize = offset_of_column(table, bl_size(table->cols));
114         table->rows = bl_new(1024, rowsize);
115     }
116 }
117 
in_memory(const fitstable_t * t)118 static anbool in_memory(const fitstable_t* t) {
119     return t->inmemory;
120 }
121 
fitscolumn_int_type()122 tfits_type fitscolumn_int_type() {
123     switch (sizeof(int)) {
124     case 2:
125         return TFITS_BIN_TYPE_I;
126     case 4:
127         return TFITS_BIN_TYPE_J;
128     case 8:
129         return TFITS_BIN_TYPE_K;
130     }
131     return -1;
132 }
133 
134 /*
135  char fitscolumn_tfits_to_char(tfits_type t) {
136  }
137  */
138 
fitscolumn_double_type()139 tfits_type fitscolumn_double_type() {
140     return TFITS_BIN_TYPE_D;
141 }
142 
fitscolumn_float_type()143 tfits_type fitscolumn_float_type() {
144     return TFITS_BIN_TYPE_E;
145 }
146 
fitscolumn_char_type()147 tfits_type fitscolumn_char_type() {
148     return TFITS_BIN_TYPE_A;
149 }
150 
fitscolumn_u8_type()151 tfits_type fitscolumn_u8_type() {
152     return TFITS_BIN_TYPE_B;
153 }
fitscolumn_i16_type()154 tfits_type fitscolumn_i16_type() {
155     return TFITS_BIN_TYPE_I;
156 }
fitscolumn_i32_type()157 tfits_type fitscolumn_i32_type() {
158     return TFITS_BIN_TYPE_J;
159 }
fitscolumn_i64_type()160 tfits_type fitscolumn_i64_type() {
161     return TFITS_BIN_TYPE_K;
162 }
fitscolumn_boolean_type()163 tfits_type fitscolumn_boolean_type() {
164     return TFITS_BIN_TYPE_L;
165 }
166 
fitscolumn_bool_type()167 tfits_type fitscolumn_bool_type() {
168     return TFITS_BIN_TYPE_B;
169 }
170 
fitscolumn_bitfield_type()171 tfits_type fitscolumn_bitfield_type() {
172     return TFITS_BIN_TYPE_X;
173 }
174 
175 // When reading: allow this column to match to any FITS type.
fitscolumn_any_type()176 tfits_type fitscolumn_any_type() {
177     return (tfits_type)-1;
178 }
179 
180 /*
181  const char* fitscolumn_format_string(tfits_type t) {
182  switch (t) {
183  case TFITS_BIN_TYPE_D:
184  return "
185  */
186 
fitstable_ncols(const fitstable_t * t)187 int fitstable_ncols(const fitstable_t* t) {
188     return ncols(t);
189 }
190 
fitstable_read_row_data(fitstable_t * table,int row,void * dest)191 int fitstable_read_row_data(fitstable_t* table, int row, void* dest) {
192     return fitstable_read_nrows_data(table, row, 1, dest);
193 }
194 
fitstable_read_nrows_data(fitstable_t * table,int row0,int nrows,void * dest)195 int fitstable_read_nrows_data(fitstable_t* table, int row0, int nrows,
196                               void* dest) {
197     int R;
198     size_t nread;
199     off_t off;
200     assert(table);
201     assert(row0 >= 0);
202     assert((row0 + nrows) <= fitstable_nrows(table));
203     assert(dest);
204     R = fitstable_row_size(table);
205     if (in_memory(table)) {
206         int i;
207         char* cdest = dest;
208         for (i=0; i<nrows; i++)
209             memcpy(cdest, bl_access(table->rows, row0 + i), R);
210         return 0;
211     }
212     if (!table->readfid) {
213         table->readfid = fopen(table->fn, "rb");
214         if (!table->readfid) {
215             SYSERROR("Failed to open FITS table %s for reading", table->fn);
216             return -1;
217         }
218 
219         assert(table->anq);
220         off_t start;
221         start = anqfits_data_start(table->anq, table->extension);
222         table->end_table_offset = start;
223     }
224     off = get_row_offset(table, row0);
225     if (fseeko(table->readfid, off, SEEK_SET)) {
226         SYSERROR("Failed to fseeko() to read a row");
227         return -1;
228     }
229     nread = (size_t)R * (size_t)nrows;
230     if (fread(dest, 1, nread, table->readfid) != nread) {
231         SYSERROR("Failed to read %i rows starting from %i, from %s", nrows, row0, table->fn);
232         return -1;
233     }
234     return 0;
235 }
236 
fitstable_endian_flip_row_data(fitstable_t * table,void * data)237 void fitstable_endian_flip_row_data(fitstable_t* table, void* data) {
238     int i;
239     char* cursor;
240     if (!need_endian_flip())
241         return;
242     cursor = data;
243     for (i=0; i<ncols(table); i++) {
244         int j;
245         fitscol_t* col = getcol(table, i);
246         for (j=0; j<col->arraysize; j++) {
247             /*
248              if (col->fitssize == 8)
249              printf("col '%s': d=%g, i=%li\n",
250              col->colname, *((double*)cursor), *((uint64_t*)cursor));
251              */
252             endian_swap(cursor, col->fitssize);
253             /*
254              if (col->fitssize == 8)
255              printf(" --> d=%g, i=%li\n",
256              *((double*)cursor), *((uint64_t*)cursor));
257              */
258 
259             cursor += col->fitssize;
260         }
261     }
262 }
263 
write_row_data(fitstable_t * table,void * data,int R)264 static int write_row_data(fitstable_t* table, void* data, int R) {
265     assert(table);
266     assert(data);
267     if (in_memory(table)) {
268         ensure_row_list_exists(table);
269         bl_append(table->rows, data);
270         // ?
271         table->table->nr++;
272         return 0;
273     }
274     if (R == 0)
275         R = fitstable_row_size(table);
276     if (fwrite(data, 1, R, table->fid) != R) {
277         SYSERROR("Failed to write a row to %s", table->fn);
278         return -1;
279     }
280     assert(table->table);
281     table->table->nr++;
282     return 0;
283 }
284 
fitstable_write_row_data(fitstable_t * table,void * data)285 int fitstable_write_row_data(fitstable_t* table, void* data) {
286     return write_row_data(table, data, 0);
287 }
288 
fitstable_copy_rows_data(fitstable_t * intable,int * rows,int N,fitstable_t * outtable)289 int fitstable_copy_rows_data(fitstable_t* intable, int* rows, int N, fitstable_t* outtable) {
290     int R;
291     char* buf = NULL;
292     int i;
293     // We need to endian-flip if we're going from FITS file <--> memory.
294     anbool flip = need_endian_flip() && (in_memory(intable) != in_memory(outtable));
295     R = fitstable_row_size(intable);
296     buf = malloc(R);
297     for (i=0; i<N; i++) {
298         if (fitstable_read_row_data(intable, rows ? rows[i] : i, buf)) {
299             ERROR("Failed to read data from input table");
300             return -1;
301         }
302         // The in-memory side (input/output) does the flip.
303         if (flip) {
304             if (in_memory(intable))
305                 fitstable_endian_flip_row_data(intable, buf);
306             else if (in_memory(outtable))
307                 fitstable_endian_flip_row_data(outtable, buf);
308         }
309 
310         if (write_row_data(outtable, buf, R)) {
311             ERROR("Failed to write data to output table");
312             return -1;
313         }
314     }
315     free(buf);
316     return 0;
317 }
318 
fitstable_copy_row_data(fitstable_t * table,int row,fitstable_t * outtable)319 int fitstable_copy_row_data(fitstable_t* table, int row, fitstable_t* outtable) {
320     return fitstable_copy_rows_data(table, &row, 1, outtable);
321 }
322 
fitstable_row_size(const fitstable_t * t)323 int fitstable_row_size(const fitstable_t* t) {
324     // FIXME - should this return the size of the *existing* FITS table
325     // (when reading), or just the columns we care about (those in "cols")?
326     return t->table->tab_w;
327     /*
328      int i, N, sz;
329      N = ncols(t);
330      sz = 0;
331      for (i=0; i<N; i++)
332      sz += fitscolumn_get_size(getcol(t, i));
333      return sz;
334      */
335 }
336 
fitstable_get_column_name(const fitstable_t * src,int i)337 char* fitstable_get_column_name(const fitstable_t* src, int i) {
338     fitscol_t* col = getcol(src, i);
339     return col->colname;
340 }
341 
fitstable_copy_columns(const fitstable_t * src,fitstable_t * dest)342 void fitstable_copy_columns(const fitstable_t* src, fitstable_t* dest) {
343     int i;
344     for (i=0; i<ncols(src); i++) {
345         fitscol_t* col = getcol(src, i);
346         fitscol_t* newcol = fitstable_add_column(dest, col);
347         // fix string fields...
348         newcol->colname = strdup_safe(newcol->colname);
349         newcol->units   = strdup_safe(newcol->units);
350     }
351 }
352 
fitstable_get_fits_column_names(const fitstable_t * t,sl * lst)353 sl* fitstable_get_fits_column_names(const fitstable_t* t, sl* lst) {
354     int i;
355     if (!lst)
356         lst = sl_new(16);
357     for (i=0; i<t->table->nc; i++) {
358         qfits_col* qcol = t->table->col + i;
359         sl_append(lst, qcol->tlabel);
360     }
361     return lst;
362 }
363 
fitstable_get_N_fits_columns(const fitstable_t * t)364 int fitstable_get_N_fits_columns(const fitstable_t* t) {
365     return t->table->nc;
366 }
367 
fitstable_get_fits_column_name(const fitstable_t * t,int i)368 const char* fitstable_get_fits_column_name(const fitstable_t* t, int i) {
369     assert(i >= 0);
370     assert(i < t->table->nc);
371     return t->table->col[i].tlabel;
372 }
373 
fitstable_get_fits_column_type(const fitstable_t * t,int i)374 tfits_type fitstable_get_fits_column_type(const fitstable_t* t, int i) {
375     assert(i >= 0);
376     assert(i < t->table->nc);
377     return t->table->col[i].atom_type;
378 }
379 
fitstable_get_fits_column_array_size(const fitstable_t * t,int i)380 int fitstable_get_fits_column_array_size(const fitstable_t* t, int i) {
381     assert(i >= 0);
382     assert(i < t->table->nc);
383     return t->table->col[i].atom_nb;
384 }
385 
fitstable_add_write_column(fitstable_t * tab,tfits_type t,const char * name,const char * units)386 void fitstable_add_write_column(fitstable_t* tab, tfits_type t,
387                                 const char* name, const char* units) {
388     fitstable_add_write_column_array_convert(tab, t, t, 1, name, units);
389 }
390 
fitstable_add_write_column_convert(fitstable_t * tab,tfits_type fits_type,tfits_type c_type,const char * name,const char * units)391 void fitstable_add_write_column_convert(fitstable_t* tab,
392                                         tfits_type fits_type,
393                                         tfits_type c_type,
394                                         const char* name,
395                                         const char* units) {
396     fitstable_add_write_column_array_convert(tab, fits_type, c_type, 1, name, units);
397 }
398 
fitstable_add_write_column_array(fitstable_t * tab,tfits_type t,int arraysize,const char * name,const char * units)399 void fitstable_add_write_column_array(fitstable_t* tab, tfits_type t,
400                                       int arraysize,
401                                       const char* name,
402                                       const char* units) {
403     fitstable_add_write_column_array_convert(tab, t, t, arraysize, name, units);
404 }
405 
fitstable_add_write_column_array_convert(fitstable_t * tab,tfits_type fits_type,tfits_type c_type,int arraysize,const char * name,const char * units)406 void fitstable_add_write_column_array_convert(fitstable_t* tab,
407                                               tfits_type fits_type,
408                                               tfits_type c_type,
409                                               int arraysize,
410                                               const char* name,
411                                               const char* units) {
412     fitscol_t col;
413     memset(&col, 0, sizeof(fitscol_t));
414     col.colname = strdup_safe(name);
415     col.units = strdup_safe(units);
416     col.fitstype = fits_type;
417     col.ctype = c_type;
418     col.arraysize = arraysize;
419     col.in_struct = FALSE;
420     fitstable_add_column(tab, &col);
421 }
422 
fitstable_add_write_column_struct(fitstable_t * tab,tfits_type c_type,int arraysize,int structoffset,tfits_type fits_type,const char * name,const char * units)423 void fitstable_add_write_column_struct(fitstable_t* tab,
424                                        tfits_type c_type,
425                                        int arraysize,
426                                        int structoffset,
427                                        tfits_type fits_type,
428                                        const char* name,
429                                        const char* units) {
430     fitstable_add_column_struct(tab, c_type, arraysize, structoffset,
431                                 fits_type, name, units, FALSE);
432 }
433 
fitstable_n_fits_columns(const fitstable_t * tab)434 int fitstable_n_fits_columns(const fitstable_t* tab) {
435     return tab->table->nc;
436 }
437 
fitstable_add_fits_columns_as_struct4(const fitstable_t * intab,fitstable_t * outtab,const sl * colnames,int c_offset,tfits_type fitstype)438 int fitstable_add_fits_columns_as_struct4(const fitstable_t* intab,
439 					  fitstable_t* outtab,
440 					  const sl* colnames,
441 					  int c_offset,
442                                           tfits_type fitstype) {
443     int i, NC;
444     int noc = ncols(outtab);
445     NC = sl_size(colnames);
446     for (i=0; i<NC; i++) {
447         const qfits_col* qcol;
448         fitscol_t* col;
449         const char* name = sl_get_const(colnames, i);
450         int j = fits_find_column(intab->table, name);
451         int off;
452         if (j == -1) {
453             ERROR("Failed to find FITS column \"%s\"", name);
454             return -1;
455         }
456         qcol = qfits_table_get_col(intab->table, j);
457         // We give the offset of the column in the *input* table, so that
458         // the resulting "outtab" can handle raw data from the "intab".
459         off = fits_offset_of_column(intab->table, j);
460 
461         if (fitstype == TFITS_BIN_TYPE_UNKNOWN)
462             fitstable_add_read_column_struct(outtab, qcol->atom_type, qcol->atom_nb,
463                                              c_offset + off, qcol->atom_type, qcol->tlabel, TRUE);
464         else
465             fitstable_add_read_column_struct(outtab, qcol->atom_type, qcol->atom_nb,
466                                              c_offset + off, fitstype, qcol->tlabel, TRUE);
467 
468         // set the FITS column number.
469         col = getcol(outtab, ncols(outtab)-1);
470         col->col = noc + i;
471     }
472     return 0;
473 }
474 
fitstable_add_fits_columns_as_struct3(const fitstable_t * intab,fitstable_t * outtab,const sl * colnames,int c_offset)475 int fitstable_add_fits_columns_as_struct3(const fitstable_t* intab,
476 					  fitstable_t* outtab,
477 					  const sl* colnames,
478 					  int c_offset) {
479     return fitstable_add_fits_columns_as_struct4(intab, outtab, colnames,
480                                                  c_offset, TFITS_BIN_TYPE_UNKNOWN);
481 }
482 
fitstable_add_fits_columns_as_struct2(const fitstable_t * intab,fitstable_t * outtab)483 void fitstable_add_fits_columns_as_struct2(const fitstable_t* intab,
484                                            fitstable_t* outtab) {
485     int i, NC;
486     int off = 0;
487     int noc = ncols(outtab);
488     NC = fitstable_get_N_fits_columns(intab);
489     for (i=0; i<NC; i++) {
490         const qfits_col* qcol = qfits_table_get_col(intab->table, i);
491         fitscol_t* col;
492         /*
493          if (qcol->atom_type == TFITS_BIN_TYPE_X) {
494          }
495          */
496         fitstable_add_read_column_struct(outtab, qcol->atom_type, qcol->atom_nb,
497                                          off, qcol->atom_type, qcol->tlabel, TRUE);
498         // set the FITS column number.
499         col = getcol(outtab, ncols(outtab)-1);
500         col->col = noc + i;
501         off += fitscolumn_get_size(col); //getcol(tab, ncols(tab)-1));
502     }
503 }
504 
fitstable_add_fits_columns_as_struct(fitstable_t * tab)505 void fitstable_add_fits_columns_as_struct(fitstable_t* tab) {
506     int i;
507     int off = 0;
508     for (i=0; i<tab->table->nc; i++) {
509         qfits_col* qcol = tab->table->col + i;
510         fitscol_t* col;
511         /*
512          if (qcol->atom_type == TFITS_BIN_TYPE_X) {
513          }
514          */
515         fitstable_add_read_column_struct(tab, qcol->atom_type, qcol->atom_nb,
516                                          off, qcol->atom_type, qcol->tlabel, TRUE);
517         // set the FITS column number.
518         col = getcol(tab, ncols(tab)-1);
519         col->col = i;
520 
521         off += fitscolumn_get_size(getcol(tab, ncols(tab)-1));
522     }
523 }
524 
fitstable_find_fits_column(fitstable_t * tab,const char * colname,char ** units,tfits_type * type,int * arraysize)525 int fitstable_find_fits_column(fitstable_t* tab, const char* colname,
526                                char** units, tfits_type* type, int* arraysize) {
527     int i;
528     for (i=0; i<tab->table->nc; i++) {
529         qfits_col* qcol = tab->table->col + i;
530         if (!strcaseeq(colname, qcol->tlabel))
531             continue;
532         // found it!
533         if (units)
534             *units = qcol->tunit;
535         if (type)
536             *type = qcol->atom_type;
537         if (arraysize)
538             *arraysize = qcol->atom_nb;
539         return 0;
540     }
541     return -1;
542 }
543 
544 
fitstable_add_read_column_struct(fitstable_t * tab,tfits_type c_type,int arraysize,int structoffset,tfits_type fits_type,const char * name,anbool required)545 void fitstable_add_read_column_struct(fitstable_t* tab,
546                                       tfits_type c_type,
547                                       int arraysize,
548                                       int structoffset,
549                                       tfits_type fits_type,
550                                       const char* name,
551                                       anbool required) {
552     fitstable_add_column_struct(tab, c_type, arraysize, structoffset,
553                                 fits_type, name, NULL, required);
554 }
555 
fitstable_add_column_struct(fitstable_t * tab,tfits_type c_type,int arraysize,int structoffset,tfits_type fits_type,const char * name,const char * units,anbool required)556 void fitstable_add_column_struct(fitstable_t* tab,
557                                  tfits_type c_type,
558                                  int arraysize,
559                                  int structoffset,
560                                  tfits_type fits_type,
561                                  const char* name,
562                                  const char* units,
563                                  anbool required) {
564     fitscol_t col;
565     memset(&col, 0, sizeof(fitscol_t));
566     col.colname = strdup_safe(name);
567     col.units = strdup_safe(units);
568     col.fitstype = fits_type;
569     col.ctype = c_type;
570     col.arraysize = arraysize;
571     col.in_struct = TRUE;
572     col.coffset = structoffset;
573     col.required = required;
574     fitstable_add_column(tab, &col);
575 }
576 
fitstable_remove_column(fitstable_t * tab,const char * name)577 int fitstable_remove_column(fitstable_t* tab, const char* name) {
578     int i;
579     for (i=0; i<ncols(tab); i++) {
580         fitscol_t* col = getcol(tab, i);
581         if (strcasecmp(name, col->colname) == 0) {
582             free(col->colname);
583             free(col->units);
584             bl_remove_index(tab->cols, i);
585             return 0;
586         }
587     }
588     return -1;
589 }
590 
fitstable_print_columns(fitstable_t * tab)591 void fitstable_print_columns(fitstable_t* tab) {
592     int i;
593     printf("Table columns:\n");
594     for (i=0; i<ncols(tab); i++) {
595         fitscol_t* col = getcol(tab, i);
596         printf("  %i: %s: fits type %i, C type %i, arraysize %i, fitssize %i, C size %i, C offset %i (if in a struct), FITS column num: %i\n",
597                i, col->colname, col->fitstype, col->ctype, col->arraysize, col->fitssize, col->csize, col->coffset, col->col);
598     }
599 }
600 
fitstable_read_structs(fitstable_t * tab,void * struc,int strucstride,int offset,int N)601 int fitstable_read_structs(fitstable_t* tab, void* struc,
602                            int strucstride, int offset, int N) {
603     int i;
604     void* tempdata = NULL;
605     int highwater = 0;
606 
607     //printf("fitstable_read_structs: stride %i, offset %i, N %i\n",strucstride, offset, N);
608 
609     for (i=0; i<ncols(tab); i++) {
610         void* dest;
611         int stride;
612         void* finaldest;
613         int finalstride;
614         fitscol_t* col = getcol(tab, i);
615         if (col->col == -1)
616             continue;
617         if (!col->in_struct)
618             continue;
619         finaldest = ((char*)struc) + col->coffset;
620         finalstride = strucstride;
621 
622         if (col->fitstype != col->ctype) {
623             int NB = fitscolumn_get_size(col) * N;
624             if (NB > highwater) {
625                 free(tempdata);
626                 tempdata = malloc(NB);
627                 highwater = NB;
628             }
629             dest = tempdata;
630             stride = fitscolumn_get_size(col);
631         } else {
632             dest = finaldest;
633             stride = finalstride;
634         }
635 
636         if (in_memory(tab)) {
637             int j;
638             int off = offset_of_column(tab, i);
639             int sz;
640             if (!tab->rows) {
641                 ERROR("No data has been written to this fitstable");
642                 return -1;
643             }
644             if (offset + N > bl_size(tab->rows)) {
645                 ERROR("Number of data items requested exceeds number of rows: offset %i, n %i, nrows %zu", offset, N, bl_size(tab->rows));
646                 return -1;
647             }
648 
649             //logverb("column %i: dest offset %i, stride %i, row offset %i, input offset %i, size %i (%ix%i)\n", i, (int)(dest - struc), stride, offset, off, fitscolumn_get_size(col), col->fitssize, col->arraysize);
650             sz = fitscolumn_get_size(col);
651             for (j=0; j<N; j++)
652                 memcpy(((char*)dest) + j * stride,
653                        ((char*)bl_access(tab->rows, offset+j)) + off,
654                        sz);
655         } else {
656             // Read from FITS file...
657             qfits_query_column_seq_to_array(tab->table, col->col, offset, N, dest, stride);
658         }
659 
660         if (col->fitstype != col->ctype) {
661             fits_convert_data(finaldest, finalstride, col->ctype,
662                               dest, stride, col->fitstype,
663                               col->arraysize, N);
664         }
665     }
666     free(tempdata);
667 
668     if (tab->postprocess_read_structs)
669         return tab->postprocess_read_structs(tab, struc, strucstride, offset, N);
670 
671     return 0;
672 }
673 
fitstable_read_struct(fitstable_t * tab,int offset,void * struc)674 int fitstable_read_struct(fitstable_t* tab, int offset, void* struc) {
675     return fitstable_read_structs(tab, struc, 0, offset, 1);
676 }
677 
678 // One of "struc" or "ap" should be non-null.
write_one(fitstable_t * table,const void * struc,anbool flip,va_list * ap)679 static int write_one(fitstable_t* table, const void* struc, anbool flip,
680                      va_list* ap) {
681     int i;
682     char* buf = NULL;
683     int Nbuf = 0;
684     int ret = 0;
685     int nc = ncols(table);
686 
687     char* thisrow = NULL;
688     int rowoff = 0;
689 
690     if (in_memory(table)) {
691         ensure_row_list_exists(table);
692         thisrow = calloc(1, bl_datasize(table->rows));
693     }
694 
695     for (i=0; i<nc; i++) {
696         fitscol_t* col;
697         const char* columndata;
698         col = getcol(table, i);
699         if (col->in_struct) {
700             if (struc)
701                 columndata = struc + col->coffset;
702             else
703                 columndata = NULL;
704         } else {
705             if (struc)
706                 columndata = NULL;
707             else
708                 columndata = va_arg(*ap, void *);
709         }
710         // If "columndata" is NULL, fits_write_data_array
711         // skips the required number of bytes.
712         // This allows both structs and normal columns to coexist
713         // (in theory -- is this ever used?)
714         // (yes, by onefield.c when writing rdls and correspondence files
715         //  with tag-along data...)
716 
717         if (columndata && col->fitstype != col->ctype) {
718             int sz = MAX(256, MAX(col->csize, col->fitssize) * col->arraysize);
719             if (sz > Nbuf) {
720                 free(buf);
721                 buf = malloc(sz);
722             }
723 
724             fits_convert_data(buf, col->fitssize, col->fitstype,
725                               columndata, col->csize, col->ctype,
726                               col->arraysize, 1);
727             columndata = buf;
728         }
729 
730         if (in_memory(table)) {
731             int nb = fitscolumn_get_size(col);
732             memcpy(thisrow + rowoff, columndata, nb);
733             rowoff += nb;
734         } else {
735             ret = fits_write_data_array(table->fid, columndata,
736                                         col->fitstype, col->arraysize, flip);
737             if (ret)
738                 break;
739         }
740     }
741     free(buf);
742     if (in_memory(table))
743         bl_append(table->rows, thisrow);
744     free(thisrow);
745     table->table->nr++;
746     return ret;
747 }
748 
749 
fitstable_write_struct(fitstable_t * table,const void * struc)750 int fitstable_write_struct(fitstable_t* table, const void* struc) {
751     return write_one(table, struc, TRUE, NULL);
752 }
753 
fitstable_write_struct_noflip(fitstable_t * table,const void * struc)754 int fitstable_write_struct_noflip(fitstable_t* table, const void* struc) {
755     return write_one(table, struc, FALSE, NULL);
756 }
757 
758 
fitstable_write_structs(fitstable_t * table,const void * struc,int stride,int N)759 int fitstable_write_structs(fitstable_t* table, const void* struc, int stride, int N) {
760     int i;
761     char* s = (char*)struc;
762     for (i=0; i<N; i++) {
763         if (fitstable_write_struct(table, s)) {
764             return -1;
765         }
766         s += stride;
767     }
768     return 0;
769 }
770 
fitstable_write_row(fitstable_t * table,...)771 int fitstable_write_row(fitstable_t* table, ...) {
772     int ret;
773     va_list ap;
774     if (!table->table)
775         fitstable_create_table(table);
776     va_start(ap, table);
777     ret = write_one(table, NULL, TRUE, &ap);
778     va_end(ap);
779     return ret;
780 }
781 
fitstable_write_row_noflip(fitstable_t * table,...)782 int fitstable_write_row_noflip(fitstable_t* table, ...) {
783     int ret;
784     va_list ap;
785     if (!table->table)
786         fitstable_create_table(table);
787     va_start(ap, table);
788     ret = write_one(table, NULL, FALSE, &ap);
789     va_end(ap);
790     return ret;
791 }
792 
793 
fitstable_write_one_column(fitstable_t * table,int colnum,int rowoffset,int nrows,const void * src,int src_stride)794 int fitstable_write_one_column(fitstable_t* table, int colnum,
795                                int rowoffset, int nrows,
796                                const void* src, int src_stride) {
797     anbool flip = TRUE;
798     off_t foffset = 0;
799     off_t start = 0;
800     int i;
801     char* buf = NULL;
802     fitscol_t* col;
803     int off;
804 
805     off = offset_of_column(table, colnum);
806     if (!in_memory(table)) {
807         foffset = ftello(table->fid);
808         // jump to row start...
809         start = get_row_offset(table, rowoffset) + off;
810         if (fseeko(table->fid, start, SEEK_SET)) {
811             SYSERROR("Failed to fseeko() to the start of the file.");
812             return -1;
813         }
814     }
815 
816     col = getcol(table, colnum);
817     if (col->fitstype != col->ctype) {
818         int sz = col->fitssize * col->arraysize * nrows;
819         buf = malloc(sz);
820         fits_convert_data(buf, col->fitssize * col->arraysize, col->fitstype,
821                           src, src_stride, col->ctype,
822                           col->arraysize, nrows);
823         src = buf;
824         src_stride = col->fitssize * col->arraysize;
825     }
826 
827     if (in_memory(table)) {
828         for (i=0; i<nrows; i++) {
829             memcpy(((char*)bl_access(table->rows, rowoffset + i)) + off,
830                    src, col->fitssize * col->arraysize);
831             src = ((const char*)src) + src_stride;
832         }
833     } else {
834         for (i=0; i<nrows; i++) {
835             if (fseeko(table->fid, start + i * table->table->tab_w, SEEK_SET) ||
836                 fits_write_data_array(table->fid, src, col->fitstype, col->arraysize, flip)) {
837                 SYSERROR("Failed to write row %i of column %i", rowoffset+i, colnum);
838                 return -1;
839             }
840             src = ((const char*)src) + src_stride;
841         }
842     }
843     free(buf);
844 
845     if (!in_memory(table)) {
846         if (fseeko(table->fid, foffset, SEEK_SET)) {
847             SYSERROR("Failed to restore file offset.");
848             return -1;
849         }
850     }
851     return 0;
852 }
853 
fitstable_clear_table(fitstable_t * tab)854 void fitstable_clear_table(fitstable_t* tab) {
855     int i;
856     for (i=0; i<ncols(tab); i++) {
857         fitscol_t* col = getcol(tab, i);
858         free(col->colname);
859         free(col->units);
860     }
861     bl_remove_all(tab->cols);
862 }
863 
864 /**
865  If "inds" is non-NULL, it's a list of indices to read.
866  */
read_array_into(const fitstable_t * tab,const char * colname,tfits_type ctype,anbool array_ok,int offset,const int * inds,int Nread,void * dest,int deststride,int desired_arraysize,int * p_arraysize)867 static void* read_array_into(const fitstable_t* tab,
868                              const char* colname, tfits_type ctype,
869                              anbool array_ok,
870                              int offset, const int* inds, int Nread,
871                              void* dest, int deststride,
872                              int desired_arraysize,
873                              int* p_arraysize) {
874     int colnum;
875     qfits_col* col;
876     int fitssize;
877     int csize;
878     int fitstype;
879     int arraysize;
880 
881     char* tempdata = NULL;
882     char* cdata;
883     char* fitsdata;
884     int cstride;
885     int fitsstride;
886     int N;
887 
888     colnum = fits_find_column(tab->table, colname);
889     if (colnum == -1) {
890         ERROR("Column \"%s\" not found in FITS table %s", colname, tab->fn);
891         return NULL;
892     }
893     col = tab->table->col + colnum;
894     if (!array_ok && (col->atom_nb != 1)) {
895         ERROR("Column \"%s\" in FITS table %s is an array of size %i, not a scalar",
896               colname, tab->fn, col->atom_nb);
897         return NULL;
898     }
899 
900     arraysize = col->atom_nb;
901     if (p_arraysize)
902         *p_arraysize = arraysize;
903     if (desired_arraysize && arraysize != desired_arraysize) {
904         ERROR("Column \"%s\" has array size %i but you wanted %i", colname, arraysize, desired_arraysize);
905         return NULL;
906     }
907     fitstype = col->atom_type;
908     fitssize = fits_get_atom_size(fitstype);
909     csize = fits_get_atom_size(ctype);
910     N = tab->table->nr;
911     if (Nread == -1)
912         Nread = N;
913     if (offset == -1)
914         offset = 0;
915 
916     if (dest)
917         cdata = dest;
918     else
919         cdata = calloc(Nread * arraysize, csize);
920 
921     if (dest && deststride > 0)
922         cstride = deststride;
923     else
924         cstride = csize * arraysize;
925 
926     fitsstride = fitssize * arraysize;
927     if (csize < fitssize) {
928         // Need to allocate a bigger temp array and down-convert the data.
929         // HACK - could set data=tempdata and realloc after (if 'dest' is NULL)
930         tempdata = calloc(Nread * arraysize, fitssize);
931         fitsdata = tempdata;
932     } else {
933         // We'll read the data into the first fraction of the output array.
934         fitsdata = cdata;
935     }
936 
937     if (in_memory(tab)) {
938         int i;
939         int off;
940         int sz;
941         if (!tab->rows) {
942             ERROR("No data has been written to this fitstable");
943             return NULL;
944         }
945         if (offset + Nread > bl_size(tab->rows)) {
946             ERROR("Number of data items requested exceeds number of rows: offset %i, n %i, nrows %zu", offset, Nread, bl_size(tab->rows));
947             return NULL;
948         }
949         off = fits_offset_of_column(tab->table, colnum);
950         sz = fitsstride;
951         if (inds) {
952             for (i=0; i<Nread; i++)
953                 memcpy(fitsdata + i * fitsstride,
954                        ((char*)bl_access(tab->rows, inds[i])) + off,
955                        sz);
956         } else {
957             for (i=0; i<Nread; i++)
958                 memcpy(fitsdata + i * fitsstride,
959                        ((char*)bl_access(tab->rows, offset+i)) + off,
960                        sz);
961         }
962     } else {
963         int res;
964         if (inds) {
965             res = qfits_query_column_seq_to_array_inds(tab->table, colnum, inds, Nread,
966                                                        (unsigned char*)fitsdata, fitsstride);
967         } else {
968             res = qfits_query_column_seq_to_array(tab->table, colnum, offset, Nread,
969                                                   (unsigned char*)fitsdata, fitsstride);
970         }
971         if (res) {
972             ERROR("Failed to read column from FITS file");
973             // MEMLEAK!
974             return NULL;
975         }
976     }
977 
978     if (fitstype != ctype) {
979         if (csize <= fitssize) {
980             // work forward
981             fits_convert_data(cdata, cstride, ctype,
982                               fitsdata, fitsstride, fitstype,
983                               arraysize, Nread);
984         } else {
985             // work backward from the end of the array
986             fits_convert_data(cdata + (((off_t)Nread*(off_t)arraysize)-1) * (off_t)csize,
987                               -csize, ctype,
988                               fitsdata + (((off_t)Nread*(off_t)arraysize)-1) * (off_t)fitssize,
989                               -fitssize, fitstype,
990                               1, Nread * arraysize);
991         }
992     }
993 
994     free(tempdata);
995     return cdata;
996 }
997 
read_array(const fitstable_t * tab,const char * colname,tfits_type ctype,anbool array_ok,int offset,int Nread)998 static void* read_array(const fitstable_t* tab,
999                         const char* colname, tfits_type ctype,
1000                         anbool array_ok, int offset, int Nread) {
1001     return read_array_into(tab, colname, ctype, array_ok, offset, NULL, Nread, NULL, 0, 0, NULL);
1002 }
1003 
fitstable_read_column_inds_into(const fitstable_t * tab,const char * colname,tfits_type read_as_type,void * dest,int stride,const int * inds,int N)1004 int fitstable_read_column_inds_into(const fitstable_t* tab,
1005                                     const char* colname, tfits_type read_as_type,
1006                                     void* dest, int stride, const int* inds, int N) {
1007     return (read_array_into(tab, colname, read_as_type, FALSE, 0, inds, N, dest, stride, 0, NULL)
1008             == NULL ? -1 : 0);
1009 }
1010 
fitstable_read_column_inds(const fitstable_t * tab,const char * colname,tfits_type read_as_type,const int * inds,int N)1011 void* fitstable_read_column_inds(const fitstable_t* tab,
1012                                  const char* colname, tfits_type read_as_type,
1013                                  const int* inds, int N) {
1014     return read_array_into(tab, colname, read_as_type, FALSE, 0, inds, N, NULL, 0, 0, NULL);
1015 }
1016 
fitstable_read_column_array_inds_into(const fitstable_t * tab,const char * colname,tfits_type read_as_type,void * dest,int stride,int arraysize,const int * inds,int N)1017 int fitstable_read_column_array_inds_into(const fitstable_t* tab,
1018                                           const char* colname, tfits_type read_as_type,
1019                                           void* dest, int stride, int arraysize,
1020                                           const int* inds, int N) {
1021     return (read_array_into(tab, colname, read_as_type, TRUE, 0, inds, N, dest, stride, arraysize, NULL)
1022             == NULL ? -1 : 0);
1023 }
1024 
fitstable_read_column_array_inds(const fitstable_t * tab,const char * colname,tfits_type read_as_type,const int * inds,int N,int * arraysize)1025 void* fitstable_read_column_array_inds(const fitstable_t* tab,
1026                                        const char* colname, tfits_type read_as_type,
1027                                        const int* inds, int N, int* arraysize) {
1028     return read_array_into(tab, colname, read_as_type, TRUE, 0, inds, N, NULL, 0, 0, arraysize);
1029 }
1030 
fitstable_read_column_offset_into(const fitstable_t * tab,const char * colname,tfits_type read_as_type,void * dest,int stride,int start,int N)1031 int fitstable_read_column_offset_into(const fitstable_t* tab,
1032                                       const char* colname, tfits_type read_as_type,
1033                                       void* dest, int stride, int start, int N) {
1034     return (read_array_into(tab, colname, read_as_type, FALSE, start, NULL, N, dest, stride, 0, NULL)
1035             == NULL ? -1 : 0);
1036 }
1037 
fitstable_read_column_into(const fitstable_t * tab,const char * colname,tfits_type read_as_type,void * dest,int stride)1038 int fitstable_read_column_into(const fitstable_t* tab,
1039                                const char* colname, tfits_type read_as_type,
1040                                void* dest, int stride) {
1041     return fitstable_read_column_offset_into(tab, colname, read_as_type, dest, stride, 0, -1);
1042 }
1043 
fitstable_read_column(const fitstable_t * tab,const char * colname,tfits_type ctype)1044 void* fitstable_read_column(const fitstable_t* tab,
1045                             const char* colname, tfits_type ctype) {
1046     return read_array(tab, colname, ctype, FALSE, -1, -1);
1047 }
1048 
fitstable_read_column_array(const fitstable_t * tab,const char * colname,tfits_type t)1049 void* fitstable_read_column_array(const fitstable_t* tab,
1050                                   const char* colname, tfits_type t) {
1051     return read_array(tab, colname, t, TRUE, -1, -1);
1052 }
1053 
fitstable_read_column_offset(const fitstable_t * tab,const char * colname,tfits_type ctype,int offset,int N)1054 void* fitstable_read_column_offset(const fitstable_t* tab,
1055                                    const char* colname, tfits_type ctype,
1056                                    int offset, int N) {
1057     return read_array(tab, colname, ctype, FALSE, offset, N);
1058 }
1059 
fitstable_get_primary_header(const fitstable_t * t)1060 qfits_header* fitstable_get_primary_header(const fitstable_t* t) {
1061     return t->primheader;
1062 }
1063 
fitstable_get_header(fitstable_t * t)1064 qfits_header* fitstable_get_header(fitstable_t* t) {
1065     if (!t->header) {
1066         fitstable_new_table(t);
1067     }
1068     return t->header;
1069 }
1070 
fitstable_next_extension(fitstable_t * tab)1071 void fitstable_next_extension(fitstable_t* tab) {
1072     if (is_writing(tab))
1073         fits_pad_file(tab->fid);
1074 
1075     if (in_memory(tab)) {
1076         fitsext_t ext;
1077         if (!tab->table)
1078             return;
1079         // update NAXIS2
1080         fitstable_fix_header(tab);
1081         ext.table = tab->table;
1082         ext.header = tab->header;
1083         ext.rows = tab->rows;
1084         bl_append(tab->extensions, &ext);
1085         tab->rows = NULL;
1086     } else {
1087         qfits_table_close(tab->table);
1088         qfits_header_destroy(tab->header);
1089     }
1090     tab->extension++;
1091     tab->table = NULL;
1092     tab->header = NULL;
1093 }
1094 
fitstable_new()1095 static fitstable_t* fitstable_new() {
1096     fitstable_t* tab;
1097     tab = calloc(1, sizeof(fitstable_t));
1098     if (!tab)
1099         return tab;
1100     tab->cols = bl_new(8, sizeof(fitscol_t));
1101     return tab;
1102 }
1103 
fitstable_open_in_memory()1104 fitstable_t* fitstable_open_in_memory() {
1105     fitstable_t* tab;
1106     tab = fitstable_new();
1107     if (!tab) {
1108         ERROR("Failed to allocate new FITS table structure");
1109         goto bailout;
1110     }
1111     tab->fn = NULL;
1112     tab->fid = NULL;
1113     tab->primheader = qfits_table_prim_header_default();
1114     tab->inmemory = TRUE;
1115     tab->extensions = bl_new(16, sizeof(fitsext_t));
1116     return tab;
1117 
1118  bailout:
1119     if (tab) {
1120         fitstable_close(tab);
1121     }
1122     return NULL;
1123 }
1124 
fitstable_switch_to_reading(fitstable_t * table)1125 int fitstable_switch_to_reading(fitstable_t* table) {
1126     assert(in_memory(table));
1127     // store the current extension.
1128     fitstable_next_extension(table);
1129     // This resets all the meta-data about the table, meaning a reader
1130     // can then re-add columns it is interested in.
1131     fitstable_clear_table(table);
1132     table->extension = 1;
1133     return fitstable_open_extension(table, table->extension);
1134 }
1135 
1136 static
_fitstable_open(const char * fn)1137 fitstable_t* _fitstable_open(const char* fn) {
1138     fitstable_t* tab;
1139     tab = fitstable_new();
1140     if (!tab) {
1141         ERROR("Failed to allocate new FITS table structure");
1142         goto bailout;
1143     }
1144     tab->extension = 1;
1145     tab->fn = strdup_safe(fn);
1146 
1147     tab->anq = anqfits_open(fn);
1148     if (!tab->anq) {
1149         ERROR("Failed to open FITS file \"%s\"", fn);
1150         goto bailout;
1151     }
1152     tab->primheader = anqfits_get_header(tab->anq, 0);
1153     if (!tab->primheader) {
1154         ERROR("Failed to read primary FITS header from %s", fn);
1155         goto bailout;
1156     }
1157     return tab;
1158  bailout:
1159     if (tab) {
1160         fitstable_close(tab);
1161     }
1162     return NULL;
1163 }
1164 
fitstable_open(const char * fn)1165 fitstable_t* fitstable_open(const char* fn) {
1166     fitstable_t* tab = _fitstable_open(fn);
1167     if (!tab) {
1168         return NULL;
1169     }
1170     if (fitstable_open_extension(tab, tab->extension)) {
1171         ERROR("Failed to open extension %i in file %s", tab->extension, fn);
1172         goto bailout;
1173     }
1174     return tab;
1175  bailout:
1176     if (tab) {
1177         fitstable_close(tab);
1178     }
1179     return NULL;
1180 }
1181 
fitstable_open_mixed(const char * fn)1182 fitstable_t* fitstable_open_mixed(const char* fn) {
1183     return _fitstable_open(fn);
1184 }
1185 
fitstable_open_extension_2(const char * fn,int ext)1186 fitstable_t* fitstable_open_extension_2(const char* fn, int ext) {
1187     fitstable_t* tab = _fitstable_open(fn);
1188     if (!tab)
1189         return tab;
1190     if (fitstable_open_extension(tab, ext)) {
1191         fitstable_close(tab);
1192         return NULL;
1193     }
1194     return tab;
1195 }
1196 
open_for_writing(const char * fn,const char * mode,FILE * fid)1197 static fitstable_t* open_for_writing(const char* fn, const char* mode, FILE* fid) {
1198     fitstable_t* tab;
1199     tab = fitstable_new();
1200     if (!tab)
1201         goto bailout;
1202     tab->fn = strdup_safe(fn);
1203     if (fid)
1204         tab->fid = fid;
1205     else {
1206         tab->fid = fopen(fn, mode);
1207         if (!tab->fid) {
1208             SYSERROR("Couldn't open output file %s for writing", fn);
1209             goto bailout;
1210         }
1211     }
1212     return tab;
1213  bailout:
1214     if (tab) {
1215         fitstable_close(tab);
1216     }
1217     return NULL;
1218 }
1219 
fitstable_open_for_writing(const char * fn)1220 fitstable_t* fitstable_open_for_writing(const char* fn) {
1221     fitstable_t* tab = open_for_writing(fn, "wb", NULL);
1222     if (!tab)
1223         return tab;
1224     tab->primheader = qfits_table_prim_header_default();
1225     return tab;
1226 }
1227 
fitstable_open_for_appending(const char * fn)1228 fitstable_t* fitstable_open_for_appending(const char* fn) {
1229     fitstable_t* tab = open_for_writing(fn, "r+b", NULL);
1230     if (!tab)
1231         return tab;
1232     if (fseeko(tab->fid, 0, SEEK_END)) {
1233         SYSERROR("Failed to seek to end of file");
1234         fitstable_close(tab);
1235         return NULL;
1236     }
1237     tab->primheader = anqfits_get_header2(fn, 0);
1238     if (!tab->primheader) {
1239         ERROR("Failed to read primary FITS header from %s", fn);
1240         fitstable_close(tab);
1241         return NULL;
1242     }
1243     return tab;
1244 }
1245 
fitstable_open_for_appending_to(FILE * fid)1246 fitstable_t* fitstable_open_for_appending_to(FILE* fid) {
1247     fitstable_t* tab = open_for_writing(NULL, NULL, fid);
1248     if (!tab)
1249         return tab;
1250     if (fseeko(tab->fid, 0, SEEK_END)) {
1251         SYSERROR("Failed to seek to end of file");
1252         fitstable_close(tab);
1253         return NULL;
1254     }
1255     return tab;
1256 }
1257 
fitstable_append_to(fitstable_t * intable,FILE * fid)1258 int fitstable_append_to(fitstable_t* intable, FILE* fid) {
1259     fitstable_t* outtable;
1260     qfits_header* tmphdr;
1261     outtable = fitstable_open_for_appending_to(fid);
1262     fitstable_clear_table(intable);
1263     fitstable_add_fits_columns_as_struct(intable);
1264     fitstable_copy_columns(intable, outtable);
1265     outtable->table = fits_copy_table(intable->table);
1266     outtable->table->nr = 0;
1267     // swap in the input header.
1268     tmphdr = outtable->header;
1269     outtable->header = intable->header;
1270     if (fitstable_write_header(outtable)) {
1271         ERROR("Failed to write output table header");
1272         return -1;
1273     }
1274     if (fitstable_copy_rows_data(intable, NULL, fitstable_nrows(intable), outtable)) {
1275         ERROR("Failed to copy rows from input table to output");
1276         return -1;
1277     }
1278     if (fitstable_fix_header(outtable)) {
1279         ERROR("Failed to fix output table header");
1280         return -1;
1281     }
1282     outtable->header = tmphdr;
1283     // clear this so that fitstable_close() doesn't fclose() it.
1284     outtable->fid = NULL;
1285     fitstable_close(outtable);
1286     return 0;
1287 }
1288 
fitstable_close(fitstable_t * tab)1289 int fitstable_close(fitstable_t* tab) {
1290     int i;
1291     int rtn = 0;
1292     if (!tab) return 0;
1293     if (is_writing(tab)) {
1294         if (tab->fid) {
1295             if (fclose(tab->fid)) {
1296                 SYSERROR("Failed to close output file %s", tab->fn);
1297                 rtn = -1;
1298             }
1299         }
1300     }
1301     if (tab->anq) {
1302         anqfits_close(tab->anq);
1303     }
1304     if (tab->readfid) {
1305         fclose(tab->readfid);
1306     }
1307     if (tab->primheader)
1308         qfits_header_destroy(tab->primheader);
1309     if (tab->header)
1310         qfits_header_destroy(tab->header);
1311     if (tab->table)
1312         qfits_table_close(tab->table);
1313     free(tab->fn);
1314     for (i=0; i<ncols(tab); i++) {
1315         fitscol_t* col = getcol(tab, i);
1316         free(col->colname);
1317         free(col->units);
1318     }
1319     bl_free(tab->cols);
1320     if (tab->br) {
1321         buffered_read_free(tab->br);
1322         free(tab->br);
1323     }
1324     if (tab->rows) {
1325         bl_free(tab->rows);
1326     }
1327     if (tab->extensions) {
1328         for (i=0; i<bl_size(tab->extensions); i++) {
1329             fitsext_t* ext = bl_access(tab->extensions, i);
1330             if (ext->rows != tab->rows)
1331                 bl_free(ext->rows);
1332             if (ext->header != tab->header)
1333                 qfits_header_destroy(ext->header);
1334             if (ext->table != tab->table)
1335                 qfits_table_close(ext->table);
1336         }
1337         bl_free(tab->extensions);
1338     }
1339     free(tab);
1340     return rtn;
1341 }
1342 
fitstable_add_column(fitstable_t * tab,fitscol_t * col)1343 static fitscol_t* fitstable_add_column(fitstable_t* tab, fitscol_t* col) {
1344     col = bl_append(tab->cols, col);
1345     col->csize = fits_get_atom_size(col->ctype);
1346     col->fitssize = fits_get_atom_size(col->fitstype);
1347     return col;
1348 }
1349 
1350 /*
1351  static void fitstable_add_columns(fitstable_t* tab, fitscol_t* cols, int Ncols) {
1352  int i;
1353  for (i=0; i<Ncols; i++) {
1354  fitstable_add_column(tab, cols + i);
1355  }
1356  }
1357  */
1358 
fitstable_get_array_size(fitstable_t * tab,const char * name)1359 int fitstable_get_array_size(fitstable_t* tab, const char* name) {
1360     qfits_col* qcol;
1361     int colnum;
1362     colnum = fits_find_column(tab->table, name);
1363     if (colnum == -1)
1364         return -1;
1365     qcol = tab->table->col + colnum;
1366     return qcol->atom_nb;
1367 }
1368 
fitstable_get_type(fitstable_t * tab,const char * name)1369 int fitstable_get_type(fitstable_t* tab, const char* name) {
1370     qfits_col* qcol;
1371     int colnum;
1372     colnum = fits_find_column(tab->table, name);
1373     if (colnum == -1)
1374         return -1;
1375     qcol = tab->table->col + colnum;
1376     return qcol->atom_type;
1377 }
1378 
fitstable_open_next_extension(fitstable_t * tab)1379 int fitstable_open_next_extension(fitstable_t* tab) {
1380     tab->extension++;
1381     return fitstable_open_extension(tab, tab->extension);
1382 }
1383 
fitstable_open_extension(fitstable_t * tab,int ext)1384 int fitstable_open_extension(fitstable_t* tab, int ext) {
1385     if (in_memory(tab)) {
1386         fitsext_t* theext;
1387         if (ext > bl_size(tab->extensions)) {
1388             ERROR("Table has only %zu extensions, but you requested #%i",
1389                   bl_size(tab->extensions), ext);
1390             return -1;
1391         }
1392         theext = bl_access(tab->extensions, ext-1);
1393         tab->table = theext->table;
1394         tab->header = theext->header;
1395         tab->rows = theext->rows;
1396         tab->extension = ext;
1397 
1398     } else {
1399         if (tab->table) {
1400             qfits_table_close(tab->table);
1401             tab->table = NULL;
1402         }
1403 
1404         assert(tab->anq);
1405         if (ext >= anqfits_n_ext(tab->anq)) {
1406             ERROR("Requested FITS extension %i in file %s, but there are only %i extensions.\n", ext, tab->fn, anqfits_n_ext(tab->anq));
1407             return -1;
1408         }
1409         tab->table = anqfits_get_table(tab->anq, ext);
1410 
1411         if (!tab->table) {
1412             ERROR("FITS extension %i in file %s is not a table (or there was an error opening the file)", ext, tab->fn);
1413             return -1;
1414         }
1415         if (tab->header) {
1416             qfits_header_destroy(tab->header);
1417         }
1418 
1419         tab->header = anqfits_get_header(tab->anq, ext);
1420         if (!tab->header) {
1421             ERROR("Couldn't get header for FITS extension %i in file %s", ext, tab->fn);
1422             return -1;
1423         }
1424         tab->extension = ext;
1425     }
1426     return 0;
1427 }
1428 
fitstable_read_extension(fitstable_t * tab,int ext)1429 int fitstable_read_extension(fitstable_t* tab, int ext) {
1430     int i;
1431     int ok = 1;
1432 
1433     if (fitstable_open_extension(tab, ext))
1434         return -1;
1435 
1436     if (tab->readfid) {
1437         // close FID so that table->end_table_offset gets refreshed.
1438         fclose(tab->readfid);
1439         tab->readfid = NULL;
1440     }
1441 
1442     for (i=0; i<ncols(tab); i++) {
1443         fitscol_t* col = getcol(tab, i);
1444         qfits_col* qcol;
1445 
1446         // FIXME? set this here?
1447         col->csize = fits_get_atom_size(col->ctype);
1448 
1449         // Column found?
1450         col->col = fits_find_column(tab->table, col->colname);
1451         if (col->col == -1)
1452             continue;
1453         qcol = tab->table->col + col->col;
1454 
1455         // Type & array size correct?
1456         if (col->fitstype != fitscolumn_any_type() &&
1457             col->fitstype != qcol->atom_type) {
1458             col->col = -1;
1459             continue;
1460         }
1461         col->fitstype = qcol->atom_type;
1462         col->fitssize = fits_get_atom_size(col->fitstype);
1463 
1464         if (col->arraysize) {
1465             if (col->arraysize != qcol->atom_nb) {
1466                 col->col = -1;
1467                 continue;
1468             }
1469         }
1470         /*  This was causing problems with copying startree tag-along data (Tycho2 test case: FLAGS column)
1471 
1472          if (col->fitstype == TFITS_BIN_TYPE_X) {
1473          // ??? really??
1474          col->arraysize = 8 * qcol->atom_nb;
1475          } else {
1476          col->arraysize = qcol->atom_nb;
1477          }
1478          */
1479         col->arraysize = qcol->atom_nb;
1480     }
1481 
1482     if (tab->br) {
1483         buffered_read_reset(tab->br);
1484         tab->br->ntotal = tab->table->nr;
1485     }
1486 
1487     for (i=0; i<ncols(tab); i++) {
1488         fitscol_t* col = getcol(tab, i);
1489         if (col->col == -1 && col->required) {
1490             ok = 0;
1491             break;
1492         }
1493     }
1494     if (ok) return 0;
1495     return -1;
1496 }
1497 
fitstable_write_primary_header(fitstable_t * t)1498 int fitstable_write_primary_header(fitstable_t* t) {
1499     if (in_memory(t)) return 0;
1500     return fitsfile_write_primary_header(t->fid, t->primheader,
1501                                          &t->end_header_offset, t->fn);
1502 }
1503 
fitstable_fix_primary_header(fitstable_t * t)1504 int fitstable_fix_primary_header(fitstable_t* t) {
1505     if (in_memory(t)) return 0;
1506     return fitsfile_fix_primary_header(t->fid, t->primheader,
1507                                        &t->end_header_offset, t->fn);
1508 }
1509 
1510 // Called just before starting to write a new field.
fitstable_new_table(fitstable_t * t)1511 int fitstable_new_table(fitstable_t* t) {
1512     if (t->table) {
1513         qfits_table_close(t->table);
1514     }
1515     fitstable_create_table(t);
1516     if (t->header) {
1517         qfits_header_destroy(t->header);
1518     }
1519     t->header = qfits_table_ext_header_default(t->table);
1520     return 0;
1521 }
1522 
fitstable_write_header(fitstable_t * t)1523 int fitstable_write_header(fitstable_t* t) {
1524     if (!t->header) {
1525         if (fitstable_new_table(t)) {
1526             return -1;
1527         }
1528     }
1529     if (in_memory(t)) return 0;
1530 
1531     return fitsfile_write_header(t->fid, t->header,
1532                                  &t->table_offset, &t->end_table_offset,
1533                                  t->extension, t->fn);
1534 }
1535 
fitstable_pad_with(fitstable_t * t,char pad)1536 int fitstable_pad_with(fitstable_t* t, char pad) {
1537     return fitsfile_pad_with(t->fid, pad);
1538 }
1539 
fitstable_fix_header(fitstable_t * t)1540 int fitstable_fix_header(fitstable_t* t) {
1541     // update NAXIS2 to reflect the number of rows written.
1542     fits_header_mod_int(t->header, "NAXIS2", t->table->nr, NULL);
1543 
1544     if (in_memory(t)) return 0;
1545 
1546     //printf("fitstable_fix_header: ext %i, table offset was %lu, fn %s\n",
1547     //t->extension, (long)t->table_offset, t->fn);
1548     if (fitsfile_fix_header(t->fid, t->header,
1549                             &t->table_offset, &t->end_table_offset,
1550                             t->extension, t->fn)) {
1551         return -1;
1552     }
1553     return 0; //fits_pad_file(t->fid);
1554 }
1555 
fitstable_close_table(fitstable_t * tab)1556 void fitstable_close_table(fitstable_t* tab) {
1557     int i;
1558     if (tab->table) {
1559         qfits_table_close(tab->table);
1560         tab->table = NULL;
1561     }
1562     for (i=0; i<ncols(tab); i++) {
1563         fitscol_t* col = getcol(tab, i);
1564         col->col = -1;
1565         col->fitssize = 0;
1566         col->arraysize = 0;
1567         col->fitstype = fitscolumn_any_type();
1568     }
1569 }
1570 
fitstable_nrows(const fitstable_t * t)1571 int fitstable_nrows(const fitstable_t* t) {
1572     if (!t->table) return 0;
1573     return t->table->nr;
1574 }
1575 
fitstable_print_missing(fitstable_t * tab,FILE * f)1576 void fitstable_print_missing(fitstable_t* tab, FILE* f) {
1577     int i;
1578     fprintf(f, "Missing required columns: ");
1579     for (i=0; i<ncols(tab); i++) {
1580         fitscol_t* col = getcol(tab, i);
1581         if (col->col == -1 && col->required) {
1582             fprintf(f, "%s ", col->colname);
1583         }
1584     }
1585 }
1586 
fitstable_error_report_missing(fitstable_t * tab)1587 void fitstable_error_report_missing(fitstable_t* tab) {
1588     int i;
1589     sl* missing = sl_new(4);
1590     char* mstr;
1591     for (i=0; i<ncols(tab); i++) {
1592         fitscol_t* col = getcol(tab, i);
1593         if (col->col == -1 && col->required)
1594             sl_append(missing, col->colname);
1595     }
1596     mstr = sl_join(missing, ", ");
1597     sl_free2(missing);
1598     ERROR("Missing required columns: %s", mstr);
1599     free(mstr);
1600 }
1601 
fitstable_create_table(fitstable_t * tab)1602 static void fitstable_create_table(fitstable_t* tab) {
1603     qfits_table* qt;
1604     int i;
1605 
1606     qt = qfits_table_new("", QFITS_BINTABLE, 0, ncols(tab), 0);
1607     tab->table = qt;
1608 
1609     for (i=0; i<ncols(tab); i++) {
1610         fitscol_t* col = getcol(tab, i);
1611         char* nil = "";
1612         int arraysize;
1613         assert(col->colname);
1614         arraysize = col->arraysize;
1615         if (col->fitstype == TFITS_BIN_TYPE_X)
1616             arraysize = col->arraysize * 8;
1617         fits_add_column(qt, i, col->fitstype, arraysize,
1618                         col->units ? col->units : nil, col->colname);
1619     }
1620 }
1621 
refill_buffer(void * userdata,void * buffer,unsigned int offset,unsigned int n)1622 static int refill_buffer(void* userdata, void* buffer, unsigned int offset, unsigned int n) {
1623     fitstable_t* tab = userdata;
1624     //logverb("fitstable.c:refill_buffer: offset %i, n %i\n", offset, n);
1625     if (fitstable_read_structs(tab, buffer, tab->br->elementsize, offset, n)) {
1626         ERROR("Error refilling FITS table read buffer");
1627         return -1;
1628     }
1629     return 0;
1630 }
1631 
fitstable_use_buffered_reading(fitstable_t * tab,int elementsize,int Nbuffer)1632 void fitstable_use_buffered_reading(fitstable_t* tab, int elementsize, int Nbuffer) {
1633     if (tab->br) {
1634         assert(tab->br->elementsize == elementsize);
1635         buffered_read_resize(tab->br, Nbuffer);
1636     } else {
1637         tab->br = buffered_read_new(elementsize, Nbuffer, 0, refill_buffer, tab);
1638     }
1639 }
1640 
fitstable_set_buffer_fill_function(fitstable_t * tab,int (* refill_buffer)(void * userdata,void * buffer,unsigned int offs,unsigned int nelems),void * userdata)1641 void fitstable_set_buffer_fill_function(fitstable_t* tab,
1642                                         int (*refill_buffer)(void* userdata, void* buffer, unsigned int offs, unsigned int nelems),
1643                                         void* userdata) {
1644     assert(tab->br);
1645     tab->br->refill_buffer = refill_buffer;
1646     tab->br->userdata = userdata;
1647 }
1648 
fitstable_next_struct(fitstable_t * tab)1649 void* fitstable_next_struct(fitstable_t* tab) {
1650     if (!tab->br) return NULL;
1651     return buffered_read(tab->br);
1652 }
1653 
fitstable_pushback(fitstable_t * tab)1654 int fitstable_pushback(fitstable_t* tab) {
1655     if (!tab->br) return -1;
1656     buffered_read_pushback(tab->br);
1657     return 0;
1658 }
1659 
1660