1 /*  This file, editcol.c, contains the set of FITSIO routines that    */
2 /*  insert or delete rows or columns in a table or resize an image    */
3 
4 /*  The FITSIO software was written by William Pence at the High Energy    */
5 /*  Astrophysic Science Archive Research Center (HEASARC) at the NASA      */
6 /*  Goddard Space Flight Center.                                           */
7 
8 #include <string.h>
9 #include <ctype.h>
10 #include <stdlib.h>
11 #include "fitsio2.h"
12 /*--------------------------------------------------------------------------*/
ffrsim(fitsfile * fptr,int bitpix,int naxis,long * naxes,int * status)13 int ffrsim(fitsfile *fptr,      /* I - FITS file pointer           */
14            int bitpix,          /* I - bits per pixel              */
15            int naxis,           /* I - number of axes in the array */
16            long *naxes,         /* I - size of each axis           */
17            int *status)         /* IO - error status               */
18 /*
19    resize an existing primary array or IMAGE extension.
20 */
21 {
22     LONGLONG tnaxes[99];
23     int ii;
24 
25     if (*status > 0)
26         return(*status);
27 
28     for (ii = 0; (ii < naxis) && (ii < 99); ii++)
29         tnaxes[ii] = naxes[ii];
30 
31     ffrsimll(fptr, bitpix, naxis, tnaxes, status);
32 
33     return(*status);
34 }
35 /*--------------------------------------------------------------------------*/
ffrsimll(fitsfile * fptr,int bitpix,int naxis,LONGLONG * naxes,int * status)36 int ffrsimll(fitsfile *fptr,    /* I - FITS file pointer           */
37            int bitpix,          /* I - bits per pixel              */
38            int naxis,           /* I - number of axes in the array */
39            LONGLONG *naxes,     /* I - size of each axis           */
40            int *status)         /* IO - error status               */
41 /*
42    resize an existing primary array or IMAGE extension.
43 */
44 {
45     int ii, simple, obitpix, onaxis, extend, nmodify;
46     long  nblocks, longval;
47     long pcount, gcount, longbitpix;
48     LONGLONG onaxes[99], newsize, oldsize;
49     char comment[FLEN_COMMENT], keyname[FLEN_KEYWORD], message[FLEN_ERRMSG];
50 
51     if (*status > 0)
52         return(*status);
53 
54     /* reset position to the correct HDU if necessary */
55     if (fptr->HDUposition != (fptr->Fptr)->curhdu)
56     {
57         ffmahd(fptr, (fptr->HDUposition) + 1, NULL, status);
58     }
59          /* rescan header if data structure is undefined */
60     else if ((fptr->Fptr)->datastart == DATA_UNDEFINED)
61         if ( ffrdef(fptr, status) > 0)
62             return(*status);
63 
64     /* get current image size parameters */
65     if (ffghprll(fptr, 99, &simple, &obitpix, &onaxis, onaxes, &pcount,
66                &gcount, &extend, status) > 0)
67         return(*status);
68 
69     longbitpix = bitpix;
70 
71     /* test for the 2 special cases that represent unsigned integers */
72     if (longbitpix == USHORT_IMG)
73         longbitpix = SHORT_IMG;
74     else if (longbitpix == ULONG_IMG)
75         longbitpix = LONG_IMG;
76 
77     /* test that the new values are legal */
78 
79     if (longbitpix != BYTE_IMG && longbitpix != SHORT_IMG &&
80         longbitpix != LONG_IMG && longbitpix != LONGLONG_IMG &&
81         longbitpix != FLOAT_IMG && longbitpix != DOUBLE_IMG)
82     {
83         snprintf(message, FLEN_ERRMSG,
84         "Illegal value for BITPIX keyword: %d", bitpix);
85         ffpmsg(message);
86         return(*status = BAD_BITPIX);
87     }
88 
89     if (naxis < 0 || naxis > 999)
90     {
91         snprintf(message, FLEN_ERRMSG,
92         "Illegal value for NAXIS keyword: %d", naxis);
93         ffpmsg(message);
94         return(*status = BAD_NAXIS);
95     }
96 
97     if (naxis == 0)
98         newsize = 0;
99     else
100         newsize = 1;
101 
102     for (ii = 0; ii < naxis; ii++)
103     {
104         if (naxes[ii] < 0)
105         {
106             snprintf(message, FLEN_ERRMSG,
107             "Illegal value for NAXIS%d keyword: %.0f", ii + 1,  (double) (naxes[ii]));
108             ffpmsg(message);
109             return(*status = BAD_NAXES);
110         }
111 
112         newsize *= naxes[ii];  /* compute new image size, in pixels */
113     }
114 
115     /* compute size of old image, in bytes */
116 
117     if (onaxis == 0)
118         oldsize = 0;
119     else
120     {
121         oldsize = 1;
122         for (ii = 0; ii < onaxis; ii++)
123             oldsize *= onaxes[ii];
124         oldsize = (oldsize + pcount) * gcount * (abs(obitpix) / 8);
125     }
126 
127     oldsize = (oldsize + 2879) / 2880; /* old size, in blocks */
128 
129     newsize = (newsize + pcount) * gcount * (abs(longbitpix) / 8);
130     newsize = (newsize + 2879) / 2880; /* new size, in blocks */
131 
132     if (newsize > oldsize)   /* have to insert new blocks for image */
133     {
134         nblocks = (long) (newsize - oldsize);
135         if (ffiblk(fptr, nblocks, 1, status) > 0)
136             return(*status);
137     }
138     else if (oldsize > newsize)  /* have to delete blocks from image */
139     {
140         nblocks = (long) (oldsize - newsize);
141         if (ffdblk(fptr, nblocks, status) > 0)
142             return(*status);
143     }
144 
145     /* now update the header keywords */
146 
147     strcpy(comment,"&");  /* special value to leave comments unchanged */
148 
149     if (longbitpix != obitpix)
150     {                         /* update BITPIX value */
151         ffmkyj(fptr, "BITPIX", longbitpix, comment, status);
152     }
153 
154     if (naxis != onaxis)
155     {                        /* update NAXIS value */
156         longval = naxis;
157         ffmkyj(fptr, "NAXIS", longval, comment, status);
158     }
159 
160     /* modify the existing NAXISn keywords */
161     nmodify = minvalue(naxis, onaxis);
162     for (ii = 0; ii < nmodify; ii++)
163     {
164         ffkeyn("NAXIS", ii+1, keyname, status);
165         ffmkyj(fptr, keyname, naxes[ii], comment, status);
166     }
167 
168     if (naxis > onaxis)  /* insert additional NAXISn keywords */
169     {
170         strcpy(comment,"length of data axis");
171         for (ii = onaxis; ii < naxis; ii++)
172         {
173             ffkeyn("NAXIS", ii+1, keyname, status);
174             ffikyj(fptr, keyname, naxes[ii], comment, status);
175         }
176     }
177     else if (onaxis > naxis) /* delete old NAXISn keywords */
178     {
179         for (ii = naxis; ii < onaxis; ii++)
180         {
181             ffkeyn("NAXIS", ii+1, keyname, status);
182             ffdkey(fptr, keyname, status);
183         }
184     }
185 
186     /* Update the BSCALE and BZERO keywords, if an unsigned integer image */
187     if (bitpix == USHORT_IMG)
188     {
189         strcpy(comment, "offset data range to that of unsigned short");
190         ffukyg(fptr, "BZERO", 32768., 0, comment, status);
191         strcpy(comment, "default scaling factor");
192         ffukyg(fptr, "BSCALE", 1.0, 0, comment, status);
193     }
194     else if (bitpix == ULONG_IMG)
195     {
196         strcpy(comment, "offset data range to that of unsigned long");
197         ffukyg(fptr, "BZERO", 2147483648., 0, comment, status);
198         strcpy(comment, "default scaling factor");
199         ffukyg(fptr, "BSCALE", 1.0, 0, comment, status);
200     }
201 
202     /* re-read the header, to make sure structures are updated */
203     ffrdef(fptr, status);
204     return(*status);
205 }
206 /*--------------------------------------------------------------------------*/
ffirow(fitsfile * fptr,LONGLONG firstrow,LONGLONG nrows,int * status)207 int ffirow(fitsfile *fptr,  /* I - FITS file pointer                        */
208            LONGLONG firstrow,   /* I - insert space AFTER this row              */
209                             /*     0 = insert space at beginning of table   */
210            LONGLONG nrows,      /* I - number of rows to insert                 */
211            int *status)     /* IO - error status                            */
212 /*
213  insert NROWS blank rows immediated after row firstrow (1 = first row).
214  Set firstrow = 0 to insert space at the beginning of the table.
215 */
216 {
217     int tstatus;
218     LONGLONG naxis1, naxis2;
219     LONGLONG datasize, firstbyte, nshift, nbytes;
220     LONGLONG freespace;
221     long nblock;
222 
223     if (*status > 0)
224         return(*status);
225 
226         /* reset position to the correct HDU if necessary */
227     if (fptr->HDUposition != (fptr->Fptr)->curhdu)
228     {
229         ffmahd(fptr, (fptr->HDUposition) + 1, NULL, status);
230     }
231          /* rescan header if data structure is undefined */
232     else if ((fptr->Fptr)->datastart == DATA_UNDEFINED)
233         if ( ffrdef(fptr, status) > 0)
234             return(*status);
235 
236     if ((fptr->Fptr)->hdutype == IMAGE_HDU)
237     {
238         ffpmsg("Can only add rows to TABLE or BINTABLE extension (ffirow)");
239         return(*status = NOT_TABLE);
240     }
241 
242     if (nrows < 0 )
243         return(*status = NEG_BYTES);
244     else if (nrows == 0)
245         return(*status);   /* no op, so just return */
246 
247     /* get the current size of the table */
248     /* use internal structure since NAXIS2 keyword may not be up to date */
249     naxis1 = (fptr->Fptr)->rowlength;
250     naxis2 = (fptr->Fptr)->numrows;
251 
252     if (firstrow > naxis2)
253     {
254         ffpmsg(
255    "Insert position greater than the number of rows in the table (ffirow)");
256         return(*status = BAD_ROW_NUM);
257     }
258     else if (firstrow < 0)
259     {
260         ffpmsg("Insert position is less than 0 (ffirow)");
261         return(*status = BAD_ROW_NUM);
262     }
263 
264     /* current data size */
265     datasize = (fptr->Fptr)->heapstart + (fptr->Fptr)->heapsize;
266     freespace = ( ( (datasize + 2879) / 2880) * 2880) - datasize;
267     nshift = naxis1 * nrows;          /* no. of bytes to add to table */
268 
269     if ( (freespace - nshift) < 0)   /* not enough existing space? */
270     {
271         nblock = (long) ((nshift - freespace + 2879) / 2880);   /* number of blocks */
272         ffiblk(fptr, nblock, 1, status);               /* insert the blocks */
273     }
274 
275     firstbyte = naxis1 * firstrow;    /* relative insert position */
276     nbytes = datasize - firstbyte;           /* no. of bytes to shift down */
277     firstbyte += ((fptr->Fptr)->datastart);  /* absolute insert position */
278 
279     ffshft(fptr, firstbyte, nbytes, nshift, status); /* shift rows and heap */
280 
281     /* update the heap starting address */
282     (fptr->Fptr)->heapstart += nshift;
283 
284     /* update the THEAP keyword if it exists */
285     tstatus = 0;
286     ffmkyj(fptr, "THEAP", (fptr->Fptr)->heapstart, "&", &tstatus);
287 
288     /* update the NAXIS2 keyword */
289     ffmkyj(fptr, "NAXIS2", naxis2 + nrows, "&", status);
290     ((fptr->Fptr)->numrows) += nrows;
291     ((fptr->Fptr)->origrows) += nrows;
292 
293     return(*status);
294 }
295 /*--------------------------------------------------------------------------*/
ffdrow(fitsfile * fptr,LONGLONG firstrow,LONGLONG nrows,int * status)296 int ffdrow(fitsfile *fptr,  /* I - FITS file pointer                        */
297            LONGLONG firstrow,   /* I - first row to delete (1 = first)          */
298            LONGLONG nrows,      /* I - number of rows to delete                 */
299            int *status)     /* IO - error status                            */
300 /*
301  delete NROWS rows from table starting with firstrow (1 = first row of table).
302 */
303 {
304     int tstatus;
305     LONGLONG naxis1, naxis2;
306     LONGLONG datasize, firstbyte, nbytes, nshift;
307     LONGLONG freespace;
308     long nblock;
309     char comm[FLEN_COMMENT];
310 
311     if (*status > 0)
312         return(*status);
313 
314     if (fptr->HDUposition != (fptr->Fptr)->curhdu)
315     {
316         ffmahd(fptr, (fptr->HDUposition) + 1, NULL, status);
317     }
318         /* rescan header if data structure is undefined */
319     else if ((fptr->Fptr)->datastart == DATA_UNDEFINED)
320         if ( ffrdef(fptr, status) > 0)
321             return(*status);
322 
323     if ((fptr->Fptr)->hdutype == IMAGE_HDU)
324     {
325         ffpmsg("Can only delete rows in TABLE or BINTABLE extension (ffdrow)");
326         return(*status = NOT_TABLE);
327     }
328 
329     if (nrows < 0 )
330         return(*status = NEG_BYTES);
331     else if (nrows == 0)
332         return(*status);   /* no op, so just return */
333 
334     ffgkyjj(fptr, "NAXIS1", &naxis1, comm, status); /* get the current   */
335 
336    /* ffgkyj(fptr, "NAXIS2", &naxis2, comm, status);*/ /* size of the table */
337 
338     /* the NAXIS2 keyword may not be up to date, so use the structure value */
339     naxis2 = (fptr->Fptr)->numrows;
340 
341     if (firstrow > naxis2)
342     {
343         ffpmsg(
344    "Delete position greater than the number of rows in the table (ffdrow)");
345         return(*status = BAD_ROW_NUM);
346     }
347     else if (firstrow < 1)
348     {
349         ffpmsg("Delete position is less than 1 (ffdrow)");
350         return(*status = BAD_ROW_NUM);
351     }
352     else if (firstrow + nrows - 1 > naxis2)
353     {
354         ffpmsg("No. of rows to delete exceeds size of table (ffdrow)");
355         return(*status = BAD_ROW_NUM);
356     }
357 
358     nshift = naxis1 * nrows;   /* no. of bytes to delete from table */
359     /* cur size of data */
360     datasize = (fptr->Fptr)->heapstart + (fptr->Fptr)->heapsize;
361 
362     firstbyte = naxis1 * (firstrow + nrows - 1); /* relative del pos */
363     nbytes = datasize - firstbyte;    /* no. of bytes to shift up */
364     firstbyte += ((fptr->Fptr)->datastart);   /* absolute delete position */
365 
366     ffshft(fptr, firstbyte, nbytes,  nshift * (-1), status); /* shift data */
367 
368     freespace = ( ( (datasize + 2879) / 2880) * 2880) - datasize;
369     nblock = (long) ((nshift + freespace) / 2880);   /* number of blocks */
370 
371     /* delete integral number blocks */
372     if (nblock > 0)
373         ffdblk(fptr, nblock, status);
374 
375     /* update the heap starting address */
376     (fptr->Fptr)->heapstart -= nshift;
377 
378     /* update the THEAP keyword if it exists */
379     tstatus = 0;
380     ffmkyj(fptr, "THEAP", (long)(fptr->Fptr)->heapstart, "&", &tstatus);
381 
382     /* update the NAXIS2 keyword */
383     ffmkyj(fptr, "NAXIS2", naxis2 - nrows, "&", status);
384     ((fptr->Fptr)->numrows) -= nrows;
385     ((fptr->Fptr)->origrows) -= nrows;
386 
387     /* Update the heap data, if any.  This will remove any orphaned data */
388     /* that was only pointed to by the rows that have been deleted */
389     ffcmph(fptr, status);
390     return(*status);
391 }
392 /*--------------------------------------------------------------------------*/
ffdrrg(fitsfile * fptr,char * ranges,int * status)393 int ffdrrg(fitsfile *fptr,  /* I - FITS file pointer to table               */
394            char *ranges,    /* I - ranges of rows to delete (1 = first)     */
395            int *status)     /* IO - error status                            */
396 /*
397  delete the ranges of rows from the table (1 = first row of table).
398 
399 The 'ranges' parameter typically looks like:
400     '10-20, 30 - 40, 55' or '50-'
401 and gives a list of rows or row ranges separated by commas.
402 */
403 {
404     char *cptr;
405     int nranges, nranges2, ii;
406     long *minrow, *maxrow, nrows, *rowarray, jj, kk;
407     LONGLONG naxis2;
408 
409     if (*status > 0)
410         return(*status);
411 
412     if (fptr->HDUposition != (fptr->Fptr)->curhdu)
413     {
414         ffmahd(fptr, (fptr->HDUposition) + 1, NULL, status);
415     }
416         /* rescan header if data structure is undefined */
417     else if ((fptr->Fptr)->datastart == DATA_UNDEFINED)
418         if ( ffrdef(fptr, status) > 0)
419             return(*status);
420 
421     if ((fptr->Fptr)->hdutype == IMAGE_HDU)
422     {
423         ffpmsg("Can only delete rows in TABLE or BINTABLE extension (ffdrrg)");
424         return(*status = NOT_TABLE);
425     }
426 
427     /* the NAXIS2 keyword may not be up to date, so use the structure value */
428     naxis2 = (fptr->Fptr)->numrows;
429 
430     /* find how many ranges were specified ( = no. of commas in string + 1) */
431     cptr = ranges;
432     for (nranges = 1; (cptr = strchr(cptr, ',')); nranges++)
433         cptr++;
434 
435     minrow = calloc(nranges, sizeof(long));
436     maxrow = calloc(nranges, sizeof(long));
437 
438     if (!minrow || !maxrow) {
439         *status = MEMORY_ALLOCATION;
440         ffpmsg("failed to allocate memory for row ranges (ffdrrg)");
441         if (maxrow) free(maxrow);
442         if (minrow) free(minrow);
443         return(*status);
444     }
445 
446     /* parse range list into array of range min and max values */
447     ffrwrg(ranges, naxis2, nranges, &nranges2, minrow, maxrow, status);
448     if (*status > 0 || nranges2 == 0) {
449         free(maxrow);
450         free(minrow);
451         return(*status);
452     }
453 
454     /* determine total number or rows to delete */
455     nrows = 0;
456     for (ii = 0; ii < nranges2; ii++) {
457        nrows = nrows + maxrow[ii] - minrow[ii] + 1;
458     }
459 
460     rowarray = calloc(nrows, sizeof(long));
461     if (!rowarray) {
462         *status = MEMORY_ALLOCATION;
463         ffpmsg("failed to allocate memory for row array (ffdrrg)");
464         return(*status);
465     }
466 
467     for (kk = 0, ii = 0; ii < nranges2; ii++) {
468        for (jj = minrow[ii]; jj <= maxrow[ii]; jj++) {
469            rowarray[kk] = jj;
470            kk++;
471        }
472     }
473 
474     /* delete the rows */
475     ffdrws(fptr, rowarray, nrows, status);
476 
477     free(rowarray);
478     free(maxrow);
479     free(minrow);
480     return(*status);
481 }
482 /*--------------------------------------------------------------------------*/
ffdrws(fitsfile * fptr,long * rownum,long nrows,int * status)483 int ffdrws(fitsfile *fptr,  /* I - FITS file pointer                        */
484            long *rownum,    /* I - list of rows to delete (1 = first)       */
485            long nrows,      /* I - number of rows to delete                 */
486            int *status)     /* IO - error status                            */
487 /*
488  delete the list of rows from the table (1 = first row of table).
489 */
490 {
491     LONGLONG naxis1, naxis2, insertpos, nextrowpos;
492     long ii, nextrow;
493     char comm[FLEN_COMMENT];
494     unsigned char *buffer;
495 
496     if (*status > 0)
497         return(*status);
498 
499     if (fptr->HDUposition != (fptr->Fptr)->curhdu)
500         ffmahd(fptr, (fptr->HDUposition) + 1, NULL, status);
501 
502     /* rescan header if data structure is undefined */
503     if ((fptr->Fptr)->datastart == DATA_UNDEFINED)
504         if ( ffrdef(fptr, status) > 0)
505             return(*status);
506 
507     if ((fptr->Fptr)->hdutype == IMAGE_HDU)
508     {
509         ffpmsg("Can only delete rows in TABLE or BINTABLE extension (ffdrws)");
510         return(*status = NOT_TABLE);
511     }
512 
513     if (nrows < 0 )
514         return(*status = NEG_BYTES);
515     else if (nrows == 0)
516         return(*status);   /* no op, so just return */
517 
518     ffgkyjj(fptr, "NAXIS1", &naxis1, comm, status); /* row width   */
519     ffgkyjj(fptr, "NAXIS2", &naxis2, comm, status); /* number of rows */
520 
521     /* check that input row list is in ascending order */
522     for (ii = 1; ii < nrows; ii++)
523     {
524         if (rownum[ii - 1] >= rownum[ii])
525         {
526             ffpmsg("row numbers are not in increasing order (ffdrws)");
527             return(*status = BAD_ROW_NUM);
528         }
529     }
530 
531     if (rownum[0] < 1)
532     {
533         ffpmsg("first row to delete is less than 1 (ffdrws)");
534         return(*status = BAD_ROW_NUM);
535     }
536     else if (rownum[nrows - 1] > naxis2)
537     {
538         ffpmsg("last row to delete exceeds size of table (ffdrws)");
539         return(*status = BAD_ROW_NUM);
540     }
541 
542     buffer = (unsigned char *) malloc( (size_t) naxis1);  /* buffer for one row */
543 
544     if (!buffer)
545     {
546         ffpmsg("malloc failed (ffdrws)");
547         return(*status = MEMORY_ALLOCATION);
548     }
549 
550     /* byte location to start of first row to delete, and the next row */
551     insertpos = (fptr->Fptr)->datastart + ((rownum[0] - 1) * naxis1);
552     nextrowpos = insertpos + naxis1;
553     nextrow = rownum[0] + 1;
554 
555     /* work through the list of rows to delete */
556     for (ii = 1; ii < nrows; nextrow++, nextrowpos += naxis1)
557     {
558         if (nextrow < rownum[ii])
559         {   /* keep this row, so copy it to the new position */
560 
561             ffmbyt(fptr, nextrowpos, REPORT_EOF, status);
562             ffgbyt(fptr, naxis1, buffer, status);  /* read the bytes */
563 
564             ffmbyt(fptr, insertpos, IGNORE_EOF, status);
565             ffpbyt(fptr, naxis1, buffer, status);  /* write the bytes */
566 
567             if (*status > 0)
568             {
569                 ffpmsg("error while copying good rows in table (ffdrws)");
570                 free(buffer);
571                 return(*status);
572             }
573             insertpos += naxis1;
574         }
575         else
576         {   /* skip over this row since it is in the list */
577             ii++;
578         }
579     }
580 
581     /* finished with all the rows to delete; copy remaining rows */
582     while(nextrow <= naxis2)
583     {
584         ffmbyt(fptr, nextrowpos, REPORT_EOF, status);
585         ffgbyt(fptr, naxis1, buffer, status);  /* read the bytes */
586 
587         ffmbyt(fptr, insertpos, IGNORE_EOF, status);
588         ffpbyt(fptr, naxis1, buffer, status);  /* write the bytes */
589 
590         if (*status > 0)
591         {
592             ffpmsg("failed to copy remaining rows in table (ffdrws)");
593             free(buffer);
594             return(*status);
595         }
596         insertpos  += naxis1;
597         nextrowpos += naxis1;
598         nextrow++;
599     }
600     free(buffer);
601 
602     /* now delete the empty rows at the end of the table */
603     ffdrow(fptr, naxis2 - nrows + 1, nrows, status);
604 
605     /* Update the heap data, if any.  This will remove any orphaned data */
606     /* that was only pointed to by the rows that have been deleted */
607     ffcmph(fptr, status);
608 
609     return(*status);
610 }
611 /*--------------------------------------------------------------------------*/
ffdrwsll(fitsfile * fptr,LONGLONG * rownum,LONGLONG nrows,int * status)612 int ffdrwsll(fitsfile *fptr, /* I - FITS file pointer                        */
613            LONGLONG *rownum, /* I - list of rows to delete (1 = first)       */
614            LONGLONG nrows,  /* I - number of rows to delete                 */
615            int *status)     /* IO - error status                            */
616 /*
617  delete the list of rows from the table (1 = first row of table).
618 */
619 {
620     LONGLONG insertpos, nextrowpos;
621     LONGLONG naxis1, naxis2, ii, nextrow;
622     char comm[FLEN_COMMENT];
623     unsigned char *buffer;
624 
625     if (*status > 0)
626         return(*status);
627 
628     if (fptr->HDUposition != (fptr->Fptr)->curhdu)
629         ffmahd(fptr, (fptr->HDUposition) + 1, NULL, status);
630 
631     /* rescan header if data structure is undefined */
632     if ((fptr->Fptr)->datastart == DATA_UNDEFINED)
633         if ( ffrdef(fptr, status) > 0)
634             return(*status);
635 
636     if ((fptr->Fptr)->hdutype == IMAGE_HDU)
637     {
638         ffpmsg("Can only delete rows in TABLE or BINTABLE extension (ffdrws)");
639         return(*status = NOT_TABLE);
640     }
641 
642     if (nrows < 0 )
643         return(*status = NEG_BYTES);
644     else if (nrows == 0)
645         return(*status);   /* no op, so just return */
646 
647     ffgkyjj(fptr, "NAXIS1", &naxis1, comm, status); /* row width   */
648     ffgkyjj(fptr, "NAXIS2", &naxis2, comm, status); /* number of rows */
649 
650     /* check that input row list is in ascending order */
651     for (ii = 1; ii < nrows; ii++)
652     {
653         if (rownum[ii - 1] >= rownum[ii])
654         {
655             ffpmsg("row numbers are not in increasing order (ffdrws)");
656             return(*status = BAD_ROW_NUM);
657         }
658     }
659 
660     if (rownum[0] < 1)
661     {
662         ffpmsg("first row to delete is less than 1 (ffdrws)");
663         return(*status = BAD_ROW_NUM);
664     }
665     else if (rownum[nrows - 1] > naxis2)
666     {
667         ffpmsg("last row to delete exceeds size of table (ffdrws)");
668         return(*status = BAD_ROW_NUM);
669     }
670 
671     buffer = (unsigned char *) malloc( (size_t) naxis1);  /* buffer for one row */
672 
673     if (!buffer)
674     {
675         ffpmsg("malloc failed (ffdrwsll)");
676         return(*status = MEMORY_ALLOCATION);
677     }
678 
679     /* byte location to start of first row to delete, and the next row */
680     insertpos = (fptr->Fptr)->datastart + ((rownum[0] - 1) * naxis1);
681     nextrowpos = insertpos + naxis1;
682     nextrow = rownum[0] + 1;
683 
684     /* work through the list of rows to delete */
685     for (ii = 1; ii < nrows; nextrow++, nextrowpos += naxis1)
686     {
687         if (nextrow < rownum[ii])
688         {   /* keep this row, so copy it to the new position */
689 
690             ffmbyt(fptr, nextrowpos, REPORT_EOF, status);
691             ffgbyt(fptr, naxis1, buffer, status);  /* read the bytes */
692 
693             ffmbyt(fptr, insertpos, IGNORE_EOF, status);
694             ffpbyt(fptr, naxis1, buffer, status);  /* write the bytes */
695 
696             if (*status > 0)
697             {
698                 ffpmsg("error while copying good rows in table (ffdrws)");
699                 free(buffer);
700                 return(*status);
701             }
702             insertpos += naxis1;
703         }
704         else
705         {   /* skip over this row since it is in the list */
706             ii++;
707         }
708     }
709 
710     /* finished with all the rows to delete; copy remaining rows */
711     while(nextrow <= naxis2)
712     {
713         ffmbyt(fptr, nextrowpos, REPORT_EOF, status);
714         ffgbyt(fptr, naxis1, buffer, status);  /* read the bytes */
715 
716         ffmbyt(fptr, insertpos, IGNORE_EOF, status);
717         ffpbyt(fptr, naxis1, buffer, status);  /* write the bytes */
718 
719         if (*status > 0)
720         {
721             ffpmsg("failed to copy remaining rows in table (ffdrws)");
722             free(buffer);
723             return(*status);
724         }
725         insertpos  += naxis1;
726         nextrowpos += naxis1;
727         nextrow++;
728     }
729     free(buffer);
730 
731     /* now delete the empty rows at the end of the table */
732     ffdrow(fptr, naxis2 - nrows + 1, nrows, status);
733 
734     /* Update the heap data, if any.  This will remove any orphaned data */
735     /* that was only pointed to by the rows that have been deleted */
736     ffcmph(fptr, status);
737 
738     return(*status);
739 }
740 /*--------------------------------------------------------------------------*/
ffrwrg(char * rowlist,LONGLONG maxrows,int maxranges,int * numranges,long * minrow,long * maxrow,int * status)741 int ffrwrg(
742       char *rowlist,      /* I - list of rows and row ranges */
743       LONGLONG maxrows,       /* I - number of rows in the table */
744       int maxranges,     /* I - max number of ranges to be returned */
745       int *numranges,    /* O - number ranges returned */
746       long *minrow,       /* O - first row in each range */
747       long *maxrow,       /* O - last row in each range */
748       int *status)        /* IO - status value */
749 {
750 /*
751    parse the input list of row ranges, returning the number of ranges,
752    and the min and max row value in each range.
753 
754    The only characters allowed in the input rowlist are
755        decimal digits, minus sign, and comma (and non-significant spaces)
756 
757    Example:
758 
759      list = "10-20, 30-35,50"
760 
761    would return numranges = 3, minrow[] = {10, 30, 50}, maxrow[] = {20, 35, 50}
762 
763    error is returned if min value of range is > max value of range or if the
764    ranges are not monotonically increasing.
765 */
766     char *next;
767     long minval, maxval;
768 
769     if (*status > 0)
770         return(*status);
771 
772     if (maxrows <= 0 ) {
773         *status = RANGE_PARSE_ERROR;
774         ffpmsg("Input maximum range value is <= 0 (fits_parse_ranges)");
775         return(*status);
776     }
777 
778     next = rowlist;
779     *numranges = 0;
780 
781     while (*next == ' ')next++;   /* skip spaces */
782 
783     while (*next != '\0') {
784 
785       /* find min value of next range; *next must be '-' or a digit */
786       if (*next == '-') {
787           minval = 1;    /* implied minrow value = 1 */
788       } else if ( isdigit((int) *next) ) {
789           minval = strtol(next, &next, 10);
790       } else {
791           *status = RANGE_PARSE_ERROR;
792           ffpmsg("Syntax error in this row range list:");
793           ffpmsg(rowlist);
794           return(*status);
795       }
796 
797       while (*next == ' ')next++;   /* skip spaces */
798 
799       /* find max value of next range; *next must be '-', or ',' */
800       if (*next == '-') {
801           next++;
802           while (*next == ' ')next++;   /* skip spaces */
803 
804           if ( isdigit((int) *next) ) {
805               maxval = strtol(next, &next, 10);
806           } else if (*next == ',' || *next == '\0') {
807               maxval = (long) maxrows;  /* implied max value */
808           } else {
809               *status = RANGE_PARSE_ERROR;
810               ffpmsg("Syntax error in this row range list:");
811               ffpmsg(rowlist);
812               return(*status);
813           }
814       } else if (*next == ',' || *next == '\0') {
815           maxval = minval;  /* only a single integer in this range */
816       } else {
817           *status = RANGE_PARSE_ERROR;
818           ffpmsg("Syntax error in this row range list:");
819           ffpmsg(rowlist);
820           return(*status);
821       }
822 
823       if (*numranges + 1 > maxranges) {
824           *status = RANGE_PARSE_ERROR;
825           ffpmsg("Overflowed maximum number of ranges (fits_parse_ranges)");
826           return(*status);
827       }
828 
829       if (minval < 1 ) {
830           *status = RANGE_PARSE_ERROR;
831           ffpmsg("Syntax error in this row range list: row number < 1");
832           ffpmsg(rowlist);
833           return(*status);
834       }
835 
836       if (maxval < minval) {
837           *status = RANGE_PARSE_ERROR;
838           ffpmsg("Syntax error in this row range list: min > max");
839           ffpmsg(rowlist);
840           return(*status);
841       }
842 
843       if (*numranges > 0) {
844           if (minval <= maxrow[(*numranges) - 1]) {
845              *status = RANGE_PARSE_ERROR;
846              ffpmsg("Syntax error in this row range list.  Range minimum is");
847              ffpmsg("  less than or equal to previous range maximum");
848              ffpmsg(rowlist);
849              return(*status);
850          }
851       }
852 
853       if (minval <= maxrows) {   /* ignore range if greater than maxrows */
854           if (maxval > maxrows)
855               maxval = (long) maxrows;
856 
857            minrow[*numranges] = minval;
858            maxrow[*numranges] = maxval;
859 
860            (*numranges)++;
861       }
862 
863       while (*next == ' ')next++;   /* skip spaces */
864       if (*next == ',') {
865            next++;
866            while (*next == ' ')next++;   /* skip more spaces */
867       }
868     }
869 
870     if (*numranges == 0) {  /* a null string was entered */
871          minrow[0] = 1;
872          maxrow[0] = (long) maxrows;
873          *numranges = 1;
874     }
875 
876     return(*status);
877 }
878 /*--------------------------------------------------------------------------*/
ffrwrgll(char * rowlist,LONGLONG maxrows,int maxranges,int * numranges,LONGLONG * minrow,LONGLONG * maxrow,int * status)879 int ffrwrgll(
880       char *rowlist,      /* I - list of rows and row ranges */
881       LONGLONG maxrows,       /* I - number of rows in the list */
882       int maxranges,     /* I - max number of ranges to be returned */
883       int *numranges,    /* O - number ranges returned */
884       LONGLONG *minrow,       /* O - first row in each range */
885       LONGLONG *maxrow,       /* O - last row in each range */
886       int *status)        /* IO - status value */
887 {
888 /*
889    parse the input list of row ranges, returning the number of ranges,
890    and the min and max row value in each range.
891 
892    The only characters allowed in the input rowlist are
893        decimal digits, minus sign, and comma (and non-significant spaces)
894 
895    Example:
896 
897      list = "10-20, 30-35,50"
898 
899    would return numranges = 3, minrow[] = {10, 30, 50}, maxrow[] = {20, 35, 50}
900 
901    error is returned if min value of range is > max value of range or if the
902    ranges are not monotonically increasing.
903 */
904     char *next;
905     LONGLONG minval, maxval;
906     double dvalue;
907 
908     if (*status > 0)
909         return(*status);
910 
911     if (maxrows <= 0 ) {
912         *status = RANGE_PARSE_ERROR;
913         ffpmsg("Input maximum range value is <= 0 (fits_parse_ranges)");
914         return(*status);
915     }
916 
917     next = rowlist;
918     *numranges = 0;
919 
920     while (*next == ' ')next++;   /* skip spaces */
921 
922     while (*next != '\0') {
923 
924       /* find min value of next range; *next must be '-' or a digit */
925       if (*next == '-') {
926           minval = 1;    /* implied minrow value = 1 */
927       } else if ( isdigit((int) *next) ) {
928 
929         /* read as a double, because the string to LONGLONG function */
930         /* is platform dependent (strtoll, strtol, _atoI64)          */
931 
932           dvalue = strtod(next, &next);
933           minval = (LONGLONG) (dvalue + 0.1);
934 
935       } else {
936           *status = RANGE_PARSE_ERROR;
937           ffpmsg("Syntax error in this row range list:");
938           ffpmsg(rowlist);
939           return(*status);
940       }
941 
942       while (*next == ' ')next++;   /* skip spaces */
943 
944       /* find max value of next range; *next must be '-', or ',' */
945       if (*next == '-') {
946           next++;
947           while (*next == ' ')next++;   /* skip spaces */
948 
949           if ( isdigit((int) *next) ) {
950 
951             /* read as a double, because the string to LONGLONG function */
952             /* is platform dependent (strtoll, strtol, _atoI64)          */
953 
954               dvalue = strtod(next, &next);
955               maxval = (LONGLONG) (dvalue + 0.1);
956 
957           } else if (*next == ',' || *next == '\0') {
958               maxval = maxrows;  /* implied max value */
959           } else {
960               *status = RANGE_PARSE_ERROR;
961               ffpmsg("Syntax error in this row range list:");
962               ffpmsg(rowlist);
963               return(*status);
964           }
965       } else if (*next == ',' || *next == '\0') {
966           maxval = minval;  /* only a single integer in this range */
967       } else {
968           *status = RANGE_PARSE_ERROR;
969           ffpmsg("Syntax error in this row range list:");
970           ffpmsg(rowlist);
971           return(*status);
972       }
973 
974       if (*numranges + 1 > maxranges) {
975           *status = RANGE_PARSE_ERROR;
976           ffpmsg("Overflowed maximum number of ranges (fits_parse_ranges)");
977           return(*status);
978       }
979 
980       if (minval < 1 ) {
981           *status = RANGE_PARSE_ERROR;
982           ffpmsg("Syntax error in this row range list: row number < 1");
983           ffpmsg(rowlist);
984           return(*status);
985       }
986 
987       if (maxval < minval) {
988           *status = RANGE_PARSE_ERROR;
989           ffpmsg("Syntax error in this row range list: min > max");
990           ffpmsg(rowlist);
991           return(*status);
992       }
993 
994       if (*numranges > 0) {
995           if (minval <= maxrow[(*numranges) - 1]) {
996              *status = RANGE_PARSE_ERROR;
997              ffpmsg("Syntax error in this row range list.  Range minimum is");
998              ffpmsg("  less than or equal to previous range maximum");
999              ffpmsg(rowlist);
1000              return(*status);
1001          }
1002       }
1003 
1004       if (minval <= maxrows) {   /* ignore range if greater than maxrows */
1005           if (maxval > maxrows)
1006               maxval = maxrows;
1007 
1008            minrow[*numranges] = minval;
1009            maxrow[*numranges] = maxval;
1010 
1011            (*numranges)++;
1012       }
1013 
1014       while (*next == ' ')next++;   /* skip spaces */
1015       if (*next == ',') {
1016            next++;
1017            while (*next == ' ')next++;   /* skip more spaces */
1018       }
1019     }
1020 
1021     if (*numranges == 0) {  /* a null string was entered */
1022          minrow[0] = 1;
1023          maxrow[0] = maxrows;
1024          *numranges = 1;
1025     }
1026 
1027     return(*status);
1028 }
1029 /*--------------------------------------------------------------------------*/
fficol(fitsfile * fptr,int numcol,char * ttype,char * tform,int * status)1030 int fficol(fitsfile *fptr,  /* I - FITS file pointer                        */
1031            int numcol,      /* I - position for new col. (1 = 1st)          */
1032            char *ttype,     /* I - name of column (TTYPE keyword)           */
1033            char *tform,     /* I - format of column (TFORM keyword)         */
1034            int *status)     /* IO - error status                            */
1035 /*
1036  Insert a new column into an existing table at position numcol.  If
1037  numcol is greater than the number of existing columns in the table
1038  then the new column will be appended as the last column in the table.
1039 */
1040 {
1041     char *name, *format;
1042 
1043     name = ttype;
1044     format = tform;
1045 
1046     fficls(fptr, numcol, 1, &name, &format, status);
1047     return(*status);
1048 }
1049 /*--------------------------------------------------------------------------*/
fficls(fitsfile * fptr,int fstcol,int ncols,char ** ttype,char ** tform,int * status)1050 int fficls(fitsfile *fptr,  /* I - FITS file pointer                        */
1051            int fstcol,      /* I - position for first new col. (1 = 1st)    */
1052            int ncols,       /* I - number of columns to insert              */
1053            char **ttype,    /* I - array of column names(TTYPE keywords)    */
1054            char **tform,    /* I - array of formats of column (TFORM)       */
1055            int *status)     /* IO - error status                            */
1056 /*
1057  Insert 1 or more new columns into an existing table at position numcol.  If
1058  fstcol is greater than the number of existing columns in the table
1059  then the new column will be appended as the last column in the table.
1060 */
1061 {
1062     int colnum, datacode, decims, tfields, tstatus, ii;
1063     LONGLONG datasize, firstbyte, nbytes, nadd, naxis1, naxis2, freespace;
1064     LONGLONG tbcol, firstcol, delbyte;
1065     long nblock, width, repeat;
1066     char tfm[FLEN_VALUE], keyname[FLEN_KEYWORD], comm[FLEN_COMMENT], *cptr;
1067     tcolumn *colptr;
1068 
1069     if (*status > 0)
1070         return(*status);
1071 
1072     if (fptr->HDUposition != (fptr->Fptr)->curhdu)
1073     {
1074         ffmahd(fptr, (fptr->HDUposition) + 1, NULL, status);
1075     }
1076         /* rescan header if data structure is undefined */
1077     else if ((fptr->Fptr)->datastart == DATA_UNDEFINED)
1078         if ( ffrdef(fptr, status) > 0)
1079             return(*status);
1080 
1081     if ((fptr->Fptr)->hdutype == IMAGE_HDU)
1082     {
1083        ffpmsg("Can only add columns to TABLE or BINTABLE extension (fficls)");
1084        return(*status = NOT_TABLE);
1085     }
1086 
1087     /*  is the column number valid?  */
1088     tfields = (fptr->Fptr)->tfield;
1089     if (fstcol < 1 )
1090         return(*status = BAD_COL_NUM);
1091     else if (fstcol > tfields)
1092         colnum = tfields + 1;   /* append as last column */
1093     else
1094         colnum = fstcol;
1095 
1096     /* parse the tform value and calc number of bytes to add to each row */
1097     delbyte = 0;
1098     for (ii = 0; ii < ncols; ii++)
1099     {
1100         if (strlen(tform[ii]) > FLEN_VALUE-1)
1101         {
1102            ffpmsg("Column format string too long (fficls)");
1103            return (*status=BAD_TFORM);
1104         }
1105         strcpy(tfm, tform[ii]);
1106         ffupch(tfm);         /* make sure format is in upper case */
1107 
1108         if ((fptr->Fptr)->hdutype == ASCII_TBL)
1109         {
1110             ffasfm(tfm, &datacode, &width, &decims, status);
1111             delbyte += width + 1;  /*  add one space between the columns */
1112         }
1113         else
1114         {
1115             ffbnfm(tfm, &datacode, &repeat, &width, status);
1116 
1117             if (datacode < 0)  {       /* variable length array column */
1118 	        if (strchr(tfm, 'Q'))
1119 		  delbyte += 16;
1120 		else
1121                   delbyte += 8;
1122             } else if (datacode == 1)          /* bit column; round up  */
1123                 delbyte += (repeat + 7) / 8; /* to multiple of 8 bits */
1124             else if (datacode == 16)  /* ASCII string column */
1125                 delbyte += repeat;
1126             else                      /* numerical data type */
1127                 delbyte += (datacode / 10) * repeat;
1128         }
1129     }
1130 
1131     if (*status > 0)
1132         return(*status);
1133 
1134     /* get the current size of the table */
1135     /* use internal structure since NAXIS2 keyword may not be up to date */
1136     naxis1 = (fptr->Fptr)->rowlength;
1137     naxis2 = (fptr->Fptr)->numrows;
1138 
1139     /* current size of data */
1140     datasize = (fptr->Fptr)->heapstart + (fptr->Fptr)->heapsize;
1141     freespace = ( ( (datasize + 2879) / 2880) * 2880) - datasize;
1142     nadd = delbyte * naxis2;   /* no. of bytes to add to table */
1143 
1144     if ( (freespace - nadd) < 0)   /* not enough existing space? */
1145     {
1146         nblock = (long) ((nadd - freespace + 2879) / 2880);     /* number of blocks  */
1147         if (ffiblk(fptr, nblock, 1, status) > 0)       /* insert the blocks */
1148             return(*status);
1149     }
1150 
1151     /* shift heap down (if it exists) */
1152     if ((fptr->Fptr)->heapsize > 0)
1153     {
1154         nbytes = (fptr->Fptr)->heapsize;    /* no. of bytes to shift down */
1155 
1156         /* absolute heap pos */
1157         firstbyte = (fptr->Fptr)->datastart + (fptr->Fptr)->heapstart;
1158 
1159         if (ffshft(fptr, firstbyte, nbytes, nadd, status) > 0) /* move heap */
1160             return(*status);
1161     }
1162 
1163     /* update the heap starting address */
1164     (fptr->Fptr)->heapstart += nadd;
1165 
1166     /* update the THEAP keyword if it exists */
1167     tstatus = 0;
1168     ffmkyj(fptr, "THEAP", (fptr->Fptr)->heapstart, "&", &tstatus);
1169 
1170     /* calculate byte position in the row where to insert the new column */
1171     if (colnum > tfields)
1172         firstcol = naxis1;
1173     else
1174     {
1175         colptr = (fptr->Fptr)->tableptr;
1176         colptr += (colnum - 1);
1177         firstcol = colptr->tbcol;
1178     }
1179 
1180     /* insert delbyte bytes in every row, at byte position firstcol */
1181     ffcins(fptr, naxis1, naxis2, delbyte, firstcol, status);
1182 
1183     if ((fptr->Fptr)->hdutype == ASCII_TBL)
1184     {
1185         /* adjust the TBCOL values of the existing columns */
1186         for(ii = 0; ii < tfields; ii++)
1187         {
1188             ffkeyn("TBCOL", ii + 1, keyname, status);
1189             ffgkyjj(fptr, keyname, &tbcol, comm, status);
1190             if (tbcol > firstcol)
1191             {
1192                 tbcol += delbyte;
1193                 ffmkyj(fptr, keyname, tbcol, "&", status);
1194             }
1195         }
1196     }
1197 
1198     /* update the mandatory keywords */
1199     ffmkyj(fptr, "TFIELDS", tfields + ncols, "&", status);
1200     ffmkyj(fptr, "NAXIS1", naxis1 + delbyte, "&", status);
1201 
1202     /* increment the index value on any existing column keywords */
1203     if(colnum <= tfields)
1204         ffkshf(fptr, colnum, tfields, ncols, status);
1205 
1206     /* add the required keywords for the new columns */
1207     for (ii = 0; ii < ncols; ii++, colnum++)
1208     {
1209         strcpy(comm, "label for field");
1210         ffkeyn("TTYPE", colnum, keyname, status);
1211         ffpkys(fptr, keyname, ttype[ii], comm, status);
1212 
1213         strcpy(comm, "format of field");
1214         strcpy(tfm, tform[ii]);
1215         ffupch(tfm);         /* make sure format is in upper case */
1216         ffkeyn("TFORM", colnum, keyname, status);
1217 
1218         if (abs(datacode) == TSBYTE)
1219         {
1220            /* Replace the 'S' with an 'B' in the TFORMn code */
1221            cptr = tfm;
1222            while (*cptr != 'S')
1223               cptr++;
1224 
1225            *cptr = 'B';
1226            ffpkys(fptr, keyname, tfm, comm, status);
1227 
1228            /* write the TZEROn and TSCALn keywords */
1229            ffkeyn("TZERO", colnum, keyname, status);
1230            strcpy(comm, "offset for signed bytes");
1231 
1232            ffpkyg(fptr, keyname, -128., 0, comm, status);
1233 
1234            ffkeyn("TSCAL", colnum, keyname, status);
1235            strcpy(comm, "data are not scaled");
1236            ffpkyg(fptr, keyname, 1., 0, comm, status);
1237         }
1238         else if (abs(datacode) == TUSHORT)
1239         {
1240            /* Replace the 'U' with an 'I' in the TFORMn code */
1241            cptr = tfm;
1242            while (*cptr != 'U')
1243               cptr++;
1244 
1245            *cptr = 'I';
1246            ffpkys(fptr, keyname, tfm, comm, status);
1247 
1248            /* write the TZEROn and TSCALn keywords */
1249            ffkeyn("TZERO", colnum, keyname, status);
1250            strcpy(comm, "offset for unsigned integers");
1251 
1252            ffpkyg(fptr, keyname, 32768., 0, comm, status);
1253 
1254            ffkeyn("TSCAL", colnum, keyname, status);
1255            strcpy(comm, "data are not scaled");
1256            ffpkyg(fptr, keyname, 1., 0, comm, status);
1257         }
1258         else if (abs(datacode) == TULONG)
1259         {
1260            /* Replace the 'V' with an 'J' in the TFORMn code */
1261            cptr = tfm;
1262            while (*cptr != 'V')
1263               cptr++;
1264 
1265            *cptr = 'J';
1266            ffpkys(fptr, keyname, tfm, comm, status);
1267 
1268            /* write the TZEROn and TSCALn keywords */
1269            ffkeyn("TZERO", colnum, keyname, status);
1270            strcpy(comm, "offset for unsigned integers");
1271 
1272            ffpkyg(fptr, keyname, 2147483648., 0, comm, status);
1273 
1274            ffkeyn("TSCAL", colnum, keyname, status);
1275            strcpy(comm, "data are not scaled");
1276            ffpkyg(fptr, keyname, 1., 0, comm, status);
1277         }
1278         else
1279         {
1280            ffpkys(fptr, keyname, tfm, comm, status);
1281         }
1282 
1283         if ((fptr->Fptr)->hdutype == ASCII_TBL)   /* write the TBCOL keyword */
1284         {
1285             if (colnum == tfields + 1)
1286                 tbcol = firstcol + 2;  /* allow space between preceding col */
1287             else
1288                 tbcol = firstcol + 1;
1289 
1290             strcpy(comm, "beginning column of field");
1291             ffkeyn("TBCOL", colnum, keyname, status);
1292             ffpkyj(fptr, keyname, tbcol, comm, status);
1293 
1294             /* increment the column starting position for the next column */
1295             ffasfm(tfm, &datacode, &width, &decims, status);
1296             firstcol += width + 1;  /*  add one space between the columns */
1297         }
1298     }
1299     ffrdef(fptr, status); /* initialize the new table structure */
1300     return(*status);
1301 }
1302 /*--------------------------------------------------------------------------*/
ffmvec(fitsfile * fptr,int colnum,LONGLONG newveclen,int * status)1303 int ffmvec(fitsfile *fptr,  /* I - FITS file pointer                        */
1304            int colnum,      /* I - position of col to be modified           */
1305            LONGLONG newveclen,  /* I - new vector length of column (TFORM)       */
1306            int *status)     /* IO - error status                            */
1307 /*
1308   Modify the vector length of a column in a binary table, larger or smaller.
1309   E.g., change a column from TFORMn = '1E' to '20E'.
1310 */
1311 {
1312     int datacode, tfields, tstatus;
1313     LONGLONG datasize, size, firstbyte, nbytes, nadd, ndelete;
1314     LONGLONG naxis1, naxis2, firstcol, freespace;
1315     LONGLONG width, delbyte, repeat;
1316     long nblock;
1317     char tfm[FLEN_VALUE], keyname[FLEN_KEYWORD], tcode[2];
1318     tcolumn *colptr;
1319 
1320     if (*status > 0)
1321         return(*status);
1322 
1323     if (fptr->HDUposition != (fptr->Fptr)->curhdu)
1324     {
1325         ffmahd(fptr, (fptr->HDUposition) + 1, NULL, status);
1326     }
1327         /* rescan header if data structure is undefined */
1328     else if ((fptr->Fptr)->datastart == DATA_UNDEFINED)
1329         if ( ffrdef(fptr, status) > 0)
1330             return(*status);
1331 
1332     if ((fptr->Fptr)->hdutype != BINARY_TBL)
1333     {
1334        ffpmsg(
1335   "Can only change vector length of a column in BINTABLE extension (ffmvec)");
1336        return(*status = NOT_TABLE);
1337     }
1338 
1339     /*  is the column number valid?  */
1340     tfields = (fptr->Fptr)->tfield;
1341     if (colnum < 1 || colnum > tfields)
1342         return(*status = BAD_COL_NUM);
1343 
1344     /* look up the current vector length and element width */
1345 
1346     colptr = (fptr->Fptr)->tableptr;
1347     colptr += (colnum - 1);
1348 
1349     datacode = colptr->tdatatype; /* datatype of the column */
1350     repeat =  colptr->trepeat;  /* field repeat count  */
1351     width =  colptr->twidth;   /*  width of a single element in chars */
1352 
1353     if (datacode < 0)
1354     {
1355         ffpmsg(
1356         "Can't modify vector length of variable length column (ffmvec)");
1357         return(*status = BAD_TFORM);
1358     }
1359 
1360     if (repeat == newveclen)
1361         return(*status);  /* column already has the desired vector length */
1362 
1363     if (datacode == TSTRING)
1364         width = 1;      /* width was equal to width of unit string */
1365 
1366     naxis1 =  (fptr->Fptr)->rowlength;   /* current width of the table */
1367     naxis2 = (fptr->Fptr)->numrows;
1368 
1369     delbyte = (newveclen - repeat) * width;    /* no. of bytes to insert */
1370     if (datacode == TBIT)  /* BIT column is a special case */
1371        delbyte = ((newveclen + 7) / 8) - ((repeat + 7) / 8);
1372 
1373     if (delbyte > 0)  /* insert space for more elements */
1374     {
1375       /* current size of data */
1376       datasize = (fptr->Fptr)->heapstart + (fptr->Fptr)->heapsize;
1377       freespace = ( ( (datasize + 2879) / 2880) * 2880) - datasize;
1378 
1379       nadd = (LONGLONG)delbyte * naxis2;   /* no. of bytes to add to table */
1380 
1381       if ( (freespace - nadd) < 0)   /* not enough existing space? */
1382       {
1383         nblock = (long) ((nadd - freespace + 2879) / 2880);    /* number of blocks  */
1384         if (ffiblk(fptr, nblock, 1, status) > 0)      /* insert the blocks */
1385           return(*status);
1386       }
1387 
1388       /* shift heap down (if it exists) */
1389       if ((fptr->Fptr)->heapsize > 0)
1390       {
1391         nbytes = (fptr->Fptr)->heapsize;    /* no. of bytes to shift down */
1392 
1393         /* absolute heap pos */
1394         firstbyte = (fptr->Fptr)->datastart + (fptr->Fptr)->heapstart;
1395 
1396         if (ffshft(fptr, firstbyte, nbytes, nadd, status) > 0) /* move heap */
1397             return(*status);
1398       }
1399 
1400       /* update the heap starting address */
1401       (fptr->Fptr)->heapstart += nadd;
1402 
1403       /* update the THEAP keyword if it exists */
1404       tstatus = 0;
1405       ffmkyj(fptr, "THEAP", (fptr->Fptr)->heapstart, "&", &tstatus);
1406 
1407       /* Must reset colptr before using it again.  (fptr->Fptr)->tableptr
1408          may have been reallocated down in ffbinit via the call to ffiblk above.*/
1409       colptr = (fptr->Fptr)->tableptr;
1410       colptr += (colnum - 1);
1411 
1412       firstcol = colptr->tbcol + (repeat * width);  /* insert position */
1413 
1414       /* insert delbyte bytes in every row, at byte position firstcol */
1415       ffcins(fptr, naxis1, naxis2, delbyte, firstcol, status);
1416     }
1417     else if (delbyte < 0)
1418     {
1419       /* current size of table */
1420       size = (fptr->Fptr)->heapstart + (fptr->Fptr)->heapsize;
1421       freespace = ((size + 2879) / 2880) * 2880 - size - ((LONGLONG)delbyte * naxis2);
1422       nblock = (long) (freespace / 2880);   /* number of empty blocks to delete */
1423       firstcol = colptr->tbcol + (newveclen * width);  /* delete position */
1424 
1425       /* delete elements from the vector */
1426       ffcdel(fptr, naxis1, naxis2, -delbyte, firstcol, status);
1427 
1428       /* abs heap pos */
1429       firstbyte = (fptr->Fptr)->datastart + (fptr->Fptr)->heapstart;
1430       ndelete = (LONGLONG)delbyte * naxis2; /* size of shift (negative) */
1431 
1432       /* shift heap up (if it exists) */
1433       if ((fptr->Fptr)->heapsize > 0)
1434       {
1435         nbytes = (fptr->Fptr)->heapsize;    /* no. of bytes to shift up */
1436         if (ffshft(fptr, firstbyte, nbytes, ndelete, status) > 0)
1437           return(*status);
1438       }
1439 
1440       /* delete the empty  blocks at the end of the HDU */
1441       if (nblock > 0)
1442         ffdblk(fptr, nblock, status);
1443 
1444       /* update the heap starting address */
1445       (fptr->Fptr)->heapstart += ndelete;  /* ndelete is negative */
1446 
1447       /* update the THEAP keyword if it exists */
1448       tstatus = 0;
1449       ffmkyj(fptr, "THEAP", (fptr->Fptr)->heapstart, "&", &tstatus);
1450     }
1451 
1452     /* construct the new TFORM keyword for the column */
1453     if (datacode == TBIT)
1454       strcpy(tcode,"X");
1455     else if (datacode == TBYTE)
1456       strcpy(tcode,"B");
1457     else if (datacode == TLOGICAL)
1458       strcpy(tcode,"L");
1459     else if (datacode == TSTRING)
1460       strcpy(tcode,"A");
1461     else if (datacode == TSHORT)
1462       strcpy(tcode,"I");
1463     else if (datacode == TLONG)
1464       strcpy(tcode,"J");
1465     else if (datacode == TLONGLONG)
1466       strcpy(tcode,"K");
1467     else if (datacode == TFLOAT)
1468       strcpy(tcode,"E");
1469     else if (datacode == TDOUBLE)
1470       strcpy(tcode,"D");
1471     else if (datacode == TCOMPLEX)
1472       strcpy(tcode,"C");
1473     else if (datacode == TDBLCOMPLEX)
1474       strcpy(tcode,"M");
1475 
1476     /* write as a double value because the LONGLONG conversion */
1477     /* character in snprintf is platform dependent ( %lld, %ld, %I64d ) */
1478 
1479     snprintf(tfm,FLEN_VALUE,"%.0f%s",(double) newveclen, tcode);
1480 
1481     ffkeyn("TFORM", colnum, keyname, status);  /* Keyword name */
1482     ffmkys(fptr, keyname, tfm, "&", status);   /* modify TFORM keyword */
1483 
1484     ffmkyj(fptr, "NAXIS1", naxis1 + delbyte, "&", status); /* modify NAXIS1 */
1485 
1486     ffrdef(fptr, status); /* reinitialize the new table structure */
1487     return(*status);
1488 }
1489 /*--------------------------------------------------------------------------*/
ffcpcl(fitsfile * infptr,fitsfile * outfptr,int incol,int outcol,int create_col,int * status)1490 int ffcpcl(fitsfile *infptr,    /* I - FITS file pointer to input file  */
1491            fitsfile *outfptr,   /* I - FITS file pointer to output file */
1492            int incol,           /* I - number of input column   */
1493            int outcol,          /* I - number for output column  */
1494            int create_col,      /* I - create new col if TRUE, else overwrite */
1495            int *status)         /* IO - error status     */
1496 /*
1497   copy a column from infptr and insert it in the outfptr table.
1498 */
1499 {
1500     int tstatus, colnum, typecode, otypecode, etypecode, anynull;
1501     int inHduType, outHduType;
1502     long tfields, repeat, orepeat, width, owidth, nrows, outrows;
1503     long inloop, outloop, maxloop, ndone, ntodo, npixels;
1504     long firstrow, firstelem, ii;
1505     char keyname[FLEN_KEYWORD], ttype[FLEN_VALUE], tform[FLEN_VALUE];
1506     char ttype_comm[FLEN_COMMENT],tform_comm[FLEN_COMMENT];
1507     char *lvalues = 0, nullflag, **strarray = 0;
1508     char nulstr[] = {'\5', '\0'};  /* unique null string value */
1509     double dnull = 0.l, *dvalues = 0;
1510     float fnull = 0., *fvalues = 0;
1511     long long int *jjvalues = 0;
1512     unsigned long long int *ujjvalues = 0;
1513 
1514     if (*status > 0)
1515         return(*status);
1516 
1517     if (infptr->HDUposition != (infptr->Fptr)->curhdu)
1518     {
1519         ffmahd(infptr, (infptr->HDUposition) + 1, NULL, status);
1520     }
1521     else if ((infptr->Fptr)->datastart == DATA_UNDEFINED)
1522         ffrdef(infptr, status);                /* rescan header */
1523     inHduType = (infptr->Fptr)->hdutype;
1524 
1525     if (outfptr->HDUposition != (outfptr->Fptr)->curhdu)
1526     {
1527         ffmahd(outfptr, (outfptr->HDUposition) + 1, NULL, status);
1528     }
1529     else if ((outfptr->Fptr)->datastart == DATA_UNDEFINED)
1530         ffrdef(outfptr, status);               /* rescan header */
1531     outHduType = (outfptr->Fptr)->hdutype;
1532 
1533     if (*status > 0)
1534         return(*status);
1535 
1536     if (inHduType == IMAGE_HDU || outHduType == IMAGE_HDU)
1537     {
1538        ffpmsg
1539        ("Can not copy columns to or from IMAGE HDUs (ffcpcl)");
1540        return(*status = NOT_TABLE);
1541     }
1542 
1543     if ( inHduType == BINARY_TBL &&  outHduType == ASCII_TBL)
1544     {
1545        ffpmsg
1546        ("Copying from Binary table to ASCII table is not supported (ffcpcl)");
1547        return(*status = NOT_BTABLE);
1548     }
1549 
1550     /* get the datatype and vector repeat length of the column */
1551     ffgtcl(infptr, incol, &typecode, &repeat, &width, status);
1552     /* ... and equivalent type code */
1553     ffeqty(infptr, incol, &etypecode, 0,      0,      status);
1554 
1555     if (typecode < 0)
1556     {
1557         ffpmsg("Variable-length columns are not supported (ffcpcl)");
1558         return(*status = BAD_TFORM);
1559     }
1560 
1561     if (create_col)    /* insert new column in output table? */
1562     {
1563         tstatus = 0;
1564         ffkeyn("TTYPE", incol, keyname, &tstatus);
1565         ffgkys(infptr, keyname, ttype, ttype_comm, &tstatus);
1566         ffkeyn("TFORM", incol, keyname, &tstatus);
1567 
1568         if (ffgkys(infptr, keyname, tform, tform_comm, &tstatus) )
1569         {
1570           ffpmsg
1571           ("Could not find TTYPE and TFORM keywords in input table (ffcpcl)");
1572           return(*status = NO_TFORM);
1573         }
1574 
1575         if (inHduType == ASCII_TBL && outHduType == BINARY_TBL)
1576         {
1577             /* convert from ASCII table to BINARY table format string */
1578             if (typecode == TSTRING)
1579                 ffnkey(width, "A", tform, status);
1580 
1581             else if (typecode == TLONG)
1582                 strcpy(tform, "1J");
1583 
1584             else if (typecode == TSHORT)
1585                 strcpy(tform, "1I");
1586 
1587             else if (typecode == TFLOAT)
1588                 strcpy(tform,"1E");
1589 
1590             else if (typecode == TDOUBLE)
1591                 strcpy(tform,"1D");
1592         }
1593 
1594         if (ffgkyj(outfptr, "TFIELDS", &tfields, 0, &tstatus))
1595         {
1596            ffpmsg
1597            ("Could not read TFIELDS keyword in output table (ffcpcl)");
1598            return(*status = NO_TFIELDS);
1599         }
1600 
1601         colnum = minvalue((int) tfields + 1, outcol); /* output col. number */
1602 
1603         /* create the empty column */
1604         if (fficol(outfptr, colnum, ttype, tform, status) > 0)
1605         {
1606            ffpmsg
1607            ("Could not append new column to output file (ffcpcl)");
1608            return(*status);
1609         }
1610 
1611         if ((infptr->Fptr == outfptr->Fptr)
1612            && (infptr->HDUposition == outfptr->HDUposition)
1613            && (colnum <= incol))  {
1614 	       incol++;  /* the input column has been shifted over */
1615         }
1616 
1617         /* copy the comment strings from the input file for TTYPE and TFORM */
1618         tstatus = 0;
1619         ffkeyn("TTYPE", colnum, keyname, &tstatus);
1620         ffmcom(outfptr, keyname, ttype_comm, &tstatus);
1621         ffkeyn("TFORM", colnum, keyname, &tstatus);
1622         ffmcom(outfptr, keyname, tform_comm, &tstatus);
1623 
1624         /* copy other column-related keywords if they exist */
1625 
1626         ffcpky(infptr, outfptr, incol, colnum, "TUNIT", status);
1627         ffcpky(infptr, outfptr, incol, colnum, "TSCAL", status);
1628         ffcpky(infptr, outfptr, incol, colnum, "TZERO", status);
1629         ffcpky(infptr, outfptr, incol, colnum, "TDISP", status);
1630         ffcpky(infptr, outfptr, incol, colnum, "TLMIN", status);
1631         ffcpky(infptr, outfptr, incol, colnum, "TLMAX", status);
1632         ffcpky(infptr, outfptr, incol, colnum, "TDIM", status);
1633 
1634         /*  WCS keywords */
1635         ffcpky(infptr, outfptr, incol, colnum, "TCTYP", status);
1636         ffcpky(infptr, outfptr, incol, colnum, "TCUNI", status);
1637         ffcpky(infptr, outfptr, incol, colnum, "TCRVL", status);
1638         ffcpky(infptr, outfptr, incol, colnum, "TCRPX", status);
1639         ffcpky(infptr, outfptr, incol, colnum, "TCDLT", status);
1640         ffcpky(infptr, outfptr, incol, colnum, "TCROT", status);
1641 
1642         if (inHduType == ASCII_TBL && outHduType == BINARY_TBL)
1643         {
1644             /* binary tables only have TNULLn keyword for integer columns */
1645             if (typecode == TLONG || typecode == TSHORT)
1646             {
1647                 /* check if null string is defined; replace with integer */
1648                 ffkeyn("TNULL", incol, keyname, &tstatus);
1649                 if (ffgkys(infptr, keyname, ttype, 0, &tstatus) <= 0)
1650                 {
1651                    ffkeyn("TNULL", colnum, keyname, &tstatus);
1652                    if (typecode == TLONG)
1653                       ffpkyj(outfptr, keyname, -9999999L, "Null value", status);
1654                    else
1655                       ffpkyj(outfptr, keyname, -32768L, "Null value", status);
1656                 }
1657             }
1658         }
1659         else
1660         {
1661             ffcpky(infptr, outfptr, incol, colnum, "TNULL", status);
1662         }
1663 
1664         /* rescan header to recognize the new keywords */
1665         if (ffrdef(outfptr, status) )
1666             return(*status);
1667     }
1668     else
1669     {
1670         colnum = outcol;
1671         /* get the datatype and vector repeat length of the output column */
1672         ffgtcl(outfptr, outcol, &otypecode, &orepeat, &owidth, status);
1673 
1674         if (orepeat != repeat) {
1675             ffpmsg("Input and output vector columns must have same length (ffcpcl)");
1676             return(*status = BAD_TFORM);
1677         }
1678     }
1679 
1680     ffgkyj(infptr,  "NAXIS2", &nrows,   0, status);  /* no. of input rows */
1681     ffgkyj(outfptr, "NAXIS2", &outrows, 0, status);  /* no. of output rows */
1682     nrows = minvalue(nrows, outrows);
1683 
1684     if (typecode == TBIT)
1685         repeat = (repeat + 7) / 8;  /* convert from bits to bytes */
1686     else if (typecode == TSTRING && inHduType == BINARY_TBL)
1687         repeat = repeat / width;  /* convert from chars to unit strings */
1688 
1689     /* get optimum number of rows to copy at one time */
1690     ffgrsz(infptr,  &inloop,  status);
1691     ffgrsz(outfptr, &outloop, status);
1692 
1693     /* adjust optimum number, since 2 tables are open at once */
1694     maxloop = minvalue(inloop, outloop); /* smallest of the 2 tables */
1695     maxloop = maxvalue(1, maxloop / 2);  /* at least 1 row */
1696     maxloop = minvalue(maxloop, nrows);  /* max = nrows to be copied */
1697     maxloop *= repeat;                   /* mult by no of elements in a row */
1698 
1699     /* allocate memory for arrays */
1700     if (typecode == TLOGICAL)
1701     {
1702        lvalues   = (char *) calloc(maxloop, sizeof(char) );
1703        if (!lvalues)
1704        {
1705          ffpmsg
1706          ("malloc failed to get memory for logicals (ffcpcl)");
1707          return(*status = ARRAY_TOO_BIG);
1708        }
1709     }
1710     else if (typecode == TSTRING)
1711     {
1712        /* allocate array of pointers */
1713        strarray = (char **) calloc(maxloop, sizeof(strarray));
1714 
1715        /* allocate space for each string */
1716        for (ii = 0; ii < maxloop; ii++)
1717           strarray[ii] = (char *) calloc(width+1, sizeof(char));
1718     }
1719     else if (typecode == TCOMPLEX)
1720     {
1721        fvalues = (float *) calloc(maxloop * 2, sizeof(float) );
1722        if (!fvalues)
1723        {
1724          ffpmsg
1725          ("malloc failed to get memory for complex (ffcpcl)");
1726          return(*status = ARRAY_TOO_BIG);
1727        }
1728        fnull = 0.;
1729     }
1730     else if (typecode == TDBLCOMPLEX)
1731     {
1732        dvalues = (double *) calloc(maxloop * 2, sizeof(double) );
1733        if (!dvalues)
1734        {
1735          ffpmsg
1736          ("malloc failed to get memory for dbl complex (ffcpcl)");
1737          return(*status = ARRAY_TOO_BIG);
1738        }
1739        dnull = 0.;
1740     }
1741     /* These are unsigned long-long ints that are not rescaled to floating point numbers */
1742     else if (typecode == TLONGLONG && etypecode == TULONGLONG) {
1743 
1744        ujjvalues = (unsigned long long int *) calloc(maxloop, sizeof(unsigned long long int) );
1745        if (!ujjvalues)
1746        {
1747          ffpmsg
1748          ("malloc failed to get memory for unsigned long long int (ffcpcl)");
1749          return(*status = ARRAY_TOO_BIG);
1750        }
1751     }
1752     /* These are long-long ints that are not rescaled to floating point numbers */
1753     else if (typecode == TLONGLONG && etypecode != TDOUBLE) {
1754 
1755        jjvalues = (long long int *) calloc(maxloop, sizeof(long long int) );
1756        if (!jjvalues)
1757        {
1758          ffpmsg
1759          ("malloc failed to get memory for long long int (ffcpcl)");
1760          return(*status = ARRAY_TOO_BIG);
1761        }
1762     }
1763     else    /* other numerical datatype; read them all as doubles */
1764     {
1765        dvalues = (double *) calloc(maxloop, sizeof(double) );
1766        if (!dvalues)
1767        {
1768          ffpmsg
1769          ("malloc failed to get memory for doubles (ffcpcl)");
1770          return(*status = ARRAY_TOO_BIG);
1771        }
1772          dnull = -9.99991999E31;  /* use an unlikely value for nulls */
1773     }
1774 
1775     npixels = nrows * repeat;          /* total no. of pixels to copy */
1776     ntodo = minvalue(npixels, maxloop);   /* no. to copy per iteration */
1777     ndone = 0;             /* total no. of pixels that have been copied */
1778 
1779     while (ntodo)      /* iterate through the table */
1780     {
1781         firstrow = ndone / repeat + 1;
1782         firstelem = ndone - ((firstrow - 1) * repeat) + 1;
1783 
1784         /* read from input table */
1785         if (typecode == TLOGICAL)
1786             ffgcl(infptr, incol, firstrow, firstelem, ntodo,
1787                        lvalues, status);
1788         else if (typecode == TSTRING)
1789             ffgcvs(infptr, incol, firstrow, firstelem, ntodo,
1790                        nulstr, strarray, &anynull, status);
1791 
1792         else if (typecode == TCOMPLEX)
1793             ffgcvc(infptr, incol, firstrow, firstelem, ntodo, fnull,
1794                    fvalues, &anynull, status);
1795 
1796         else if (typecode == TDBLCOMPLEX)
1797             ffgcvm(infptr, incol, firstrow, firstelem, ntodo, dnull,
1798                    dvalues, &anynull, status);
1799 
1800 	/* Neither TULONGLONG nor TLONGLONG does null checking.  Whatever
1801 	   null value is in input table is transferred to output table
1802 	   without checking.  Since the TNULL value was copied, this
1803 	   should preserve null values */
1804 	else if (typecode == TLONGLONG && etypecode == TULONGLONG)
1805 	  ffgcvujj(infptr, incol, firstrow, firstelem, ntodo, /*nulval*/ 0,
1806                    ujjvalues, &anynull, status);
1807 
1808 	else if (typecode == TLONGLONG && etypecode != TDOUBLE)
1809 	  ffgcvjj(infptr, incol, firstrow, firstelem, ntodo, /*nulval*/ 0,
1810                    jjvalues, &anynull, status);
1811 
1812         else       /* all numerical types */
1813             ffgcvd(infptr, incol, firstrow, firstelem, ntodo, dnull,
1814                    dvalues, &anynull, status);
1815 
1816         if (*status > 0)
1817         {
1818             ffpmsg("Error reading input copy of column (ffcpcl)");
1819             break;
1820         }
1821 
1822         /* write to output table */
1823         if (typecode == TLOGICAL)
1824         {
1825             nullflag = 2;
1826 
1827             ffpcnl(outfptr, colnum, firstrow, firstelem, ntodo,
1828                        lvalues, nullflag, status);
1829 
1830         }
1831 
1832         else if (typecode == TSTRING)
1833         {
1834             if (anynull)
1835                 ffpcns(outfptr, colnum, firstrow, firstelem, ntodo,
1836                        strarray, nulstr, status);
1837             else
1838                 ffpcls(outfptr, colnum, firstrow, firstelem, ntodo,
1839                        strarray, status);
1840         }
1841 
1842         else if (typecode == TCOMPLEX)
1843         {                      /* doesn't support writing nulls */
1844             ffpclc(outfptr, colnum, firstrow, firstelem, ntodo,
1845                        fvalues, status);
1846         }
1847 
1848         else if (typecode == TDBLCOMPLEX)
1849         {                      /* doesn't support writing nulls */
1850             ffpclm(outfptr, colnum, firstrow, firstelem, ntodo,
1851                        dvalues, status);
1852         }
1853 
1854 	else if (typecode == TLONGLONG && etypecode == TULONGLONG)
1855 	{   /* No null checking because we did none to read */
1856             ffpclujj(outfptr, colnum, firstrow, firstelem, ntodo,
1857 		     ujjvalues, status);
1858 	}
1859 	else if (typecode == TLONGLONG && etypecode != TDOUBLE)
1860 	{   /* No null checking because we did none to read */
1861 	    ffpcljj(outfptr, colnum, firstrow, firstelem, ntodo,
1862 		    jjvalues, status);
1863 	}
1864         else  /* all other numerical types */
1865         {
1866             if (anynull)
1867                 ffpcnd(outfptr, colnum, firstrow, firstelem, ntodo,
1868                        dvalues, dnull, status);
1869             else
1870                 ffpcld(outfptr, colnum, firstrow, firstelem, ntodo,
1871                        dvalues, status);
1872         }
1873 
1874         if (*status > 0)
1875         {
1876             ffpmsg("Error writing output copy of column (ffcpcl)");
1877             break;
1878         }
1879 
1880         npixels -= ntodo;
1881         ndone += ntodo;
1882         ntodo = minvalue(npixels, maxloop);
1883     }
1884 
1885     /* free the previously allocated memory */
1886     if (typecode == TLOGICAL)
1887     {
1888         free(lvalues);
1889     }
1890     else if (typecode == TSTRING)
1891     {
1892          for (ii = 0; ii < maxloop; ii++)
1893              free(strarray[ii]);
1894 
1895          free(strarray);
1896     }
1897     if (ujjvalues) free(ujjvalues);
1898     if (jjvalues)  free(jjvalues);
1899     if (dvalues)   free(dvalues);
1900 
1901     return(*status);
1902 }
1903 /*--------------------------------------------------------------------------*/
ffccls(fitsfile * infptr,fitsfile * outfptr,int incol,int outcol,int ncols,int create_col,int * status)1904 int ffccls(fitsfile *infptr,    /* I - FITS file pointer to input file  */
1905            fitsfile *outfptr,   /* I - FITS file pointer to output file */
1906            int incol,           /* I - number of first input column   */
1907            int outcol,          /* I - number for first output column  */
1908 	   int ncols,           /* I - number of columns to copy from input to output */
1909            int create_col,      /* I - create new col if TRUE, else overwrite */
1910            int *status)         /* IO - error status     */
1911 /*
1912   copy multiple columns from infptr and insert them in the outfptr
1913   table.  Optimized for multiple-column case since it only expands the
1914   output file once using fits_insert_cols() instead of calling
1915   fits_insert_col() multiple times.
1916 */
1917 {
1918     int tstatus, colnum, typecode, otypecode, anynull;
1919     int inHduType, outHduType;
1920     long tfields, repeat, orepeat, width, owidth, nrows, outrows;
1921     long inloop, outloop, maxloop, ndone, ntodo, npixels;
1922     long firstrow, firstelem, ii;
1923     char keyname[FLEN_KEYWORD], ttype[FLEN_VALUE], tform[FLEN_VALUE];
1924     char ttype_comm[FLEN_COMMENT],tform_comm[FLEN_COMMENT];
1925     char *lvalues = 0, nullflag, **strarray = 0;
1926     char nulstr[] = {'\5', '\0'};  /* unique null string value */
1927     double dnull = 0.l, *dvalues = 0;
1928     float fnull = 0., *fvalues = 0;
1929     int typecodes[1000];
1930     char *ttypes[1000], *tforms[1000], keyarr[1001][FLEN_CARD];
1931     int ikey = 0;
1932     int icol, incol1, outcol1;
1933 
1934     if (*status > 0)
1935         return(*status);
1936 
1937     /* Do not allow more than internal array limit to be copied */
1938     if (ncols > 1000) return (*status = ARRAY_TOO_BIG);
1939 
1940     if (infptr->HDUposition != (infptr->Fptr)->curhdu)
1941     {
1942         ffmahd(infptr, (infptr->HDUposition) + 1, NULL, status);
1943     }
1944     else if ((infptr->Fptr)->datastart == DATA_UNDEFINED)
1945         ffrdef(infptr, status);                /* rescan header */
1946     inHduType = (infptr->Fptr)->hdutype;
1947 
1948     if (outfptr->HDUposition != (outfptr->Fptr)->curhdu)
1949     {
1950         ffmahd(outfptr, (outfptr->HDUposition) + 1, NULL, status);
1951     }
1952     else if ((outfptr->Fptr)->datastart == DATA_UNDEFINED)
1953         ffrdef(outfptr, status);               /* rescan header */
1954     outHduType = (outfptr->Fptr)->hdutype;
1955 
1956     if (*status > 0)
1957         return(*status);
1958 
1959     if (inHduType == IMAGE_HDU || outHduType == IMAGE_HDU)
1960     {
1961        ffpmsg
1962        ("Can not copy columns to or from IMAGE HDUs (ffccls)");
1963        return(*status = NOT_TABLE);
1964     }
1965 
1966     if ( (inHduType == BINARY_TBL &&  outHduType == ASCII_TBL) ||
1967 	 (inHduType == ASCII_TBL  &&  outHduType == BINARY_TBL) )
1968     {
1969        ffpmsg
1970        ("Copying between Binary and ASCII tables is not supported (ffccls)");
1971        return(*status = NOT_BTABLE);
1972     }
1973 
1974     /* Do not allow copying multiple columns in the same HDU because the
1975        permutations of possible overlapping copies is mind-bending */
1976     if ((infptr->Fptr == outfptr->Fptr)
1977 	&& (infptr->HDUposition == outfptr->HDUposition))
1978     {
1979        ffpmsg
1980        ("Copying multiple columns in same HDU is not supported (ffccls)");
1981        return(*status = NOT_BTABLE);
1982     }
1983 
1984     /* Retrieve the number of columns in output file */
1985     tstatus=0;
1986     if (ffgkyj(outfptr, "TFIELDS", &tfields, 0, &tstatus))
1987     {
1988       ffpmsg
1989 	("Could not read TFIELDS keyword in output table (ffccls)");
1990       return(*status = NO_TFIELDS);
1991     }
1992 
1993     colnum = minvalue((int) tfields + 1, outcol); /* output col. number */
1994 
1995     /* Collect data about input column (type, repeat, etc) */
1996     for (incol1 = incol, outcol1 = colnum, icol = 0;
1997 	 icol < ncols;
1998 	 icol++, incol1++, outcol1++)
1999     {
2000       ffgtcl(infptr, incol1, &typecode, &repeat, &width, status);
2001 
2002       if (typecode < 0)
2003 	{
2004 	  ffpmsg("Variable-length columns are not supported (ffccls)");
2005 	  return(*status = BAD_TFORM);
2006 	}
2007 
2008       typecodes[icol] = typecode;
2009 
2010       tstatus = 0;
2011       ffkeyn("TTYPE", incol1, keyname, &tstatus);
2012       ffgkys(infptr, keyname, ttype, ttype_comm, &tstatus);
2013 
2014       ffkeyn("TFORM", incol1, keyname, &tstatus);
2015 
2016       if (ffgkys(infptr, keyname, tform, tform_comm, &tstatus) )
2017         {
2018           ffpmsg
2019 	    ("Could not find TTYPE and TFORM keywords in input table (ffccls)");
2020           return(*status = NO_TFORM);
2021         }
2022 
2023       /* If creating columns, we need to save these values */
2024       if ( create_col ) {
2025 	tforms[icol] = keyarr[ikey++];
2026 	ttypes[icol] = keyarr[ikey++];
2027 
2028 	strcpy(tforms[icol], tform);
2029 	strcpy(ttypes[icol], ttype);
2030       } else {
2031 	/* If not creating columns, then check the datatype and vector
2032 	   repeat length of the output column */
2033         ffgtcl(outfptr, outcol1, &otypecode, &orepeat, &owidth, status);
2034 
2035         if (orepeat != repeat) {
2036             ffpmsg("Input and output vector columns must have same length (ffccls)");
2037             return(*status = BAD_TFORM);
2038         }
2039       }
2040     }
2041 
2042     /* Insert columns into output file and copy all meta-data
2043        keywords, if requested */
2044     if (create_col)
2045     {
2046         /* create the empty columns */
2047         if (fficls(outfptr, colnum, ncols, ttypes, tforms, status) > 0)
2048         {
2049            ffpmsg
2050            ("Could not append new columns to output file (ffccls)");
2051            return(*status);
2052         }
2053 
2054 	/* Copy meta-data strings from input column to output */
2055 	for (incol1 = incol, outcol1 = colnum, icol = 0;
2056 	     icol < ncols;
2057 	     icol++, incol1++, outcol1++)
2058 	{
2059 	  /* copy the comment strings from the input file for TTYPE and TFORM */
2060 	  ffkeyn("TTYPE", incol1, keyname, status);
2061 	  ffgkys(infptr, keyname, ttype, ttype_comm, status);
2062 	  ffkeyn("TTYPE", outcol1, keyname, status);
2063 	  ffmcom(outfptr, keyname, ttype_comm, status);
2064 
2065 	  ffkeyn("TFORM", incol1, keyname, status);
2066 	  ffgkys(infptr, keyname, tform, tform_comm, status);
2067 	  ffkeyn("TFORM", outcol1, keyname, status);
2068 	  ffmcom(outfptr, keyname, tform_comm, status);
2069 
2070 	  /* copy other column-related keywords if they exist */
2071 
2072 	  ffcpky(infptr, outfptr, incol1, outcol1, "TUNIT", status);
2073 	  ffcpky(infptr, outfptr, incol1, outcol1, "TSCAL", status);
2074 	  ffcpky(infptr, outfptr, incol1, outcol1, "TZERO", status);
2075 	  ffcpky(infptr, outfptr, incol1, outcol1, "TDISP", status);
2076 	  ffcpky(infptr, outfptr, incol1, outcol1, "TLMIN", status);
2077 	  ffcpky(infptr, outfptr, incol1, outcol1, "TLMAX", status);
2078 	  ffcpky(infptr, outfptr, incol1, outcol1, "TDIM", status);
2079 
2080 	  /*  WCS keywords */
2081 	  ffcpky(infptr, outfptr, incol1, outcol1, "TCTYP", status);
2082 	  ffcpky(infptr, outfptr, incol1, outcol1, "TCUNI", status);
2083 	  ffcpky(infptr, outfptr, incol1, outcol1, "TCRVL", status);
2084 	  ffcpky(infptr, outfptr, incol1, outcol1, "TCRPX", status);
2085 	  ffcpky(infptr, outfptr, incol1, outcol1, "TCDLT", status);
2086 	  ffcpky(infptr, outfptr, incol1, outcol1, "TCROT", status);
2087 
2088 	  ffcpky(infptr, outfptr, incol1, outcol1, "TNULL", status);
2089 
2090 	}
2091 
2092 	/* rescan header to recognize the new keywords */
2093 	if (ffrdef(outfptr, status) )
2094 	  return(*status);
2095     }
2096 
2097     /* Copy columns using standard ffcpcl(); do this in a loop because
2098        the I/O-intensive column expanding is done */
2099     for (incol1 = incol, outcol1 = colnum, icol = 0;
2100 	 icol < ncols;
2101 	 icol++, incol1++, outcol1++)
2102     {
2103       ffcpcl(infptr, outfptr, incol1, outcol1, 0, status);
2104     }
2105 
2106     return(*status);
2107 }
2108 /*--------------------------------------------------------------------------*/
ffcprw(fitsfile * infptr,fitsfile * outfptr,LONGLONG firstrow,LONGLONG nrows,int * status)2109 int ffcprw(fitsfile *infptr,    /* I - FITS file pointer to input file  */
2110            fitsfile *outfptr,   /* I - FITS file pointer to output file */
2111            LONGLONG firstrow,   /* I - number of first row to copy (1 based)  */
2112            LONGLONG nrows,      /* I - number of rows to copy  */
2113            int *status)         /* IO - error status     */
2114 /*
2115   copy consecutive set of rows from infptr and append it in the outfptr table.
2116 */
2117 {
2118     LONGLONG innaxis1, innaxis2, outnaxis1, outnaxis2, ii, jj, icol;
2119     LONGLONG iVarCol, inPos, outPos, nVarBytes, nVarAllocBytes = 0;
2120     unsigned char *buffer, *varColBuff=0;
2121     int nInVarCols=0, nOutVarCols=0, varColDiff=0;
2122     int *inVarCols=0, *outVarCols=0;
2123     long nNewBlocks;
2124     LONGLONG hrepeat=0, hoffset=0;
2125     tcolumn *colptr=0;
2126 
2127     if (*status > 0)
2128         return(*status);
2129 
2130     if (infptr->HDUposition != (infptr->Fptr)->curhdu)
2131     {
2132         ffmahd(infptr, (infptr->HDUposition) + 1, NULL, status);
2133     }
2134     else if ((infptr->Fptr)->datastart == DATA_UNDEFINED)
2135         ffrdef(infptr, status);                /* rescan header */
2136 
2137     if (outfptr->HDUposition != (outfptr->Fptr)->curhdu)
2138     {
2139         ffmahd(outfptr, (outfptr->HDUposition) + 1, NULL, status);
2140     }
2141     else if ((outfptr->Fptr)->datastart == DATA_UNDEFINED)
2142         ffrdef(outfptr, status);               /* rescan header */
2143 
2144     if (*status > 0)
2145         return(*status);
2146 
2147     if ((infptr->Fptr)->hdutype == IMAGE_HDU || (outfptr->Fptr)->hdutype == IMAGE_HDU)
2148     {
2149        ffpmsg
2150        ("Can not copy rows to or from IMAGE HDUs (ffcprw)");
2151        return(*status = NOT_TABLE);
2152     }
2153 
2154     if ( ((infptr->Fptr)->hdutype == BINARY_TBL &&  (outfptr->Fptr)->hdutype == ASCII_TBL) ||
2155          ((infptr->Fptr)->hdutype == ASCII_TBL &&  (outfptr->Fptr)->hdutype == BINARY_TBL) )
2156     {
2157        ffpmsg
2158        ("Copying rows between Binary and ASCII tables is not supported (ffcprw)");
2159        return(*status = NOT_BTABLE);
2160     }
2161 
2162     ffgkyjj(infptr,  "NAXIS1", &innaxis1,  0, status);  /* width of input rows */
2163     ffgkyjj(infptr,  "NAXIS2", &innaxis2,  0, status);  /* no. of input rows */
2164     ffgkyjj(outfptr, "NAXIS1", &outnaxis1, 0, status);  /* width of output rows */
2165     ffgkyjj(outfptr, "NAXIS2", &outnaxis2, 0, status);  /* no. of output rows */
2166 
2167     if (*status > 0)
2168         return(*status);
2169 
2170     if (outnaxis1 != innaxis1) {
2171        ffpmsg
2172        ("Input and output tables do not have same width (ffcprw)");
2173        return(*status = BAD_ROW_WIDTH);
2174     }
2175 
2176     if (firstrow + nrows - 1 > innaxis2) {
2177        ffpmsg
2178        ("Not enough rows in input table to copy (ffcprw)");
2179        return(*status = BAD_ROW_NUM);
2180     }
2181 
2182     if ((infptr->Fptr)->tfield != (outfptr->Fptr)->tfield)
2183     {
2184        ffpmsg
2185        ("Input and output tables do not have same number of columns (ffcprw)");
2186        return(*status = BAD_COL_NUM);
2187     }
2188 
2189     /* allocate buffer to hold 1 row of data */
2190     buffer = malloc( (size_t) innaxis1);
2191     if (!buffer) {
2192        ffpmsg
2193        ("Unable to allocate memory (ffcprw)");
2194        return(*status = MEMORY_ALLOCATION);
2195     }
2196 
2197     inVarCols = malloc(infptr->Fptr->tfield*sizeof(int));
2198     outVarCols = malloc(outfptr->Fptr->tfield*sizeof(int));
2199     fffvcl(infptr, &nInVarCols, inVarCols, status);
2200     fffvcl(outfptr, &nOutVarCols, outVarCols, status);
2201     if (nInVarCols != nOutVarCols)
2202        varColDiff=1;
2203     else
2204     {
2205        for (ii=0; ii<nInVarCols; ++ii)
2206        {
2207           if (inVarCols[ii] != outVarCols[ii])
2208           {
2209              varColDiff=1;
2210              break;
2211           }
2212        }
2213     }
2214 
2215     if (varColDiff)
2216     {
2217        ffpmsg("Input and output tables have different variable columns (ffcprw)");
2218        *status = BAD_COL_NUM;
2219        goto CLEANUP_RETURN;
2220     }
2221 
2222     jj = outnaxis2 + 1;
2223     if (nInVarCols)
2224     {
2225        ffirow(outfptr, outnaxis2, nrows, status);
2226        for (ii = firstrow; ii < firstrow + nrows; ii++)
2227        {
2228           fits_read_tblbytes (infptr, ii, 1, innaxis1, buffer, status);
2229           fits_write_tblbytes(outfptr, jj, 1, innaxis1, buffer, status);
2230           /* Now make corrections for variable length columns */
2231           iVarCol=0;
2232           colptr = (infptr->Fptr)->tableptr;
2233           for (icol=0; icol<(infptr->Fptr)->tfield; ++icol)
2234           {
2235              if (iVarCol < nInVarCols && inVarCols[iVarCol] == icol+1)
2236              {
2237                 /* Copy from a variable length column */
2238 
2239                 ffgdesll(infptr, icol+1, ii, &hrepeat, &hoffset, status);
2240                 /* If this is a bit column, hrepeat will be number of
2241                    bits, not bytes. If it is a string column, hrepeat
2242 		   is the number of bytes, twidth is the max col width
2243 		   and can be ignored.*/
2244                 if (colptr->tdatatype == -TBIT)
2245 		{
2246 		   nVarBytes = (hrepeat+7)/8;
2247 		}
2248 		else if (colptr->tdatatype == -TSTRING)
2249 		{
2250 		   nVarBytes = hrepeat;
2251 		}
2252 		else
2253 		{
2254 		   nVarBytes = hrepeat*colptr->twidth*sizeof(char);
2255 		}
2256                 inPos = (infptr->Fptr)->datastart + (infptr->Fptr)->heapstart
2257 				+ hoffset;
2258 		outPos = (outfptr->Fptr)->datastart + (outfptr->Fptr)->heapstart
2259 				+ (outfptr->Fptr)->heapsize;
2260                 ffmbyt(infptr, inPos, REPORT_EOF, status);
2261 		/* If this is not the last HDU in the file, then check if */
2262 		/* extending the heap would overwrite the following header. */
2263 		/* If so, then have to insert more blocks. */
2264                 if ( !((outfptr->Fptr)->lasthdu) )
2265                 {
2266 		   if (outPos+nVarBytes >
2267 		      (outfptr->Fptr)->headstart[(outfptr->Fptr)->curhdu+1])
2268 		   {
2269 		      nNewBlocks = (long)(((outPos+nVarBytes - 1 -
2270                         (outfptr->Fptr)->headstart[(outfptr->Fptr)->
2271                         curhdu+1]) / 2880) + 1);
2272                       if (ffiblk(outfptr, nNewBlocks, 1, status) > 0)
2273                       {
2274                          ffpmsg("Failed to extend the size of the variable length heap (ffcprw)");
2275 			 goto CLEANUP_RETURN;
2276                       }
2277 
2278 		   }
2279                 }
2280                 if (nVarBytes)
2281 		{
2282 		   if (nVarBytes > nVarAllocBytes)
2283 		   {
2284 		     /* Grow the copy buffer to accomodate the new maximum size.
2285 			Note it is safe to call realloc() with null input pointer,
2286 			which is equivalent to malloc(). */
2287 		     unsigned char *varColBuff1 = (unsigned char *) realloc(varColBuff, nVarBytes);
2288 		     if (! varColBuff1)
2289 		     {
2290 		       *status = MEMORY_ALLOCATION;
2291 		       ffpmsg("failed to allocate memory for variable column copy (ffcprw)");
2292 		       goto CLEANUP_RETURN;
2293 		     }
2294 		     /* Record the new state */
2295 		     varColBuff = varColBuff1;
2296 		     nVarAllocBytes = nVarBytes;
2297 		   }
2298 		   /* Copy date from input to output */
2299                    ffgbyt(infptr, nVarBytes, varColBuff, status);
2300 		   ffmbyt(outfptr, outPos, IGNORE_EOF, status);
2301                    ffpbyt(outfptr, nVarBytes, varColBuff, status);
2302 		}
2303 		ffpdes(outfptr, icol+1, jj, hrepeat, (outfptr->Fptr)->heapsize, status);
2304                 (outfptr->Fptr)->heapsize += nVarBytes;
2305                 ++iVarCol;
2306              }
2307              ++colptr;
2308           }
2309           ++jj;
2310        }
2311     }
2312     else
2313     {
2314        /* copy the rows, 1 at a time */
2315        for (ii = firstrow; ii < firstrow + nrows; ii++) {
2316            fits_read_tblbytes (infptr,  ii, 1, innaxis1, buffer, status);
2317            fits_write_tblbytes(outfptr, jj, 1, innaxis1, buffer, status);
2318            jj++;
2319        }
2320     }
2321     outnaxis2 += nrows;
2322     fits_update_key(outfptr, TLONGLONG, "NAXIS2", &outnaxis2, 0, status);
2323 
2324  CLEANUP_RETURN:
2325     free(buffer);
2326     free(inVarCols);
2327     free(outVarCols);
2328     if (varColBuff) free(varColBuff);
2329     return(*status);
2330 }
2331 /*--------------------------------------------------------------------------*/
ffcpky(fitsfile * infptr,fitsfile * outfptr,int incol,int outcol,char * rootname,int * status)2332 int ffcpky(fitsfile *infptr,    /* I - FITS file pointer to input file  */
2333            fitsfile *outfptr,   /* I - FITS file pointer to output file */
2334            int incol,           /* I - input index number   */
2335            int outcol,          /* I - output index number  */
2336            char *rootname,      /* I - root name of the keyword to be copied */
2337            int *status)         /* IO - error status     */
2338 /*
2339   copy an indexed keyword from infptr to outfptr.
2340 */
2341 {
2342     int tstatus = 0;
2343     char keyname[FLEN_KEYWORD];
2344     char value[FLEN_VALUE], comment[FLEN_COMMENT], card[FLEN_CARD];
2345 
2346     ffkeyn(rootname, incol, keyname, &tstatus);
2347     if (ffgkey(infptr, keyname, value, comment, &tstatus) <= 0)
2348     {
2349         ffkeyn(rootname, outcol, keyname, &tstatus);
2350         ffmkky(keyname, value, comment, card, status);
2351         ffprec(outfptr, card, status);
2352     }
2353     return(*status);
2354 }
2355 /*--------------------------------------------------------------------------*/
ffdcol(fitsfile * fptr,int colnum,int * status)2356 int ffdcol(fitsfile *fptr,  /* I - FITS file pointer                        */
2357            int colnum,      /* I - column to delete (1 = 1st)               */
2358            int *status)     /* IO - error status                            */
2359 /*
2360   Delete a column from a table.
2361 */
2362 {
2363     int ii, tstatus;
2364     LONGLONG firstbyte, size, ndelete, nbytes, naxis1, naxis2, firstcol, delbyte, freespace;
2365     LONGLONG tbcol;
2366     long nblock, nspace;
2367     char keyname[FLEN_KEYWORD], comm[FLEN_COMMENT];
2368     tcolumn *colptr, *nextcol;
2369 
2370     if (*status > 0)
2371         return(*status);
2372 
2373     if (fptr->HDUposition != (fptr->Fptr)->curhdu)
2374     {
2375         ffmahd(fptr, (fptr->HDUposition) + 1, NULL, status);
2376     }
2377     /* rescan header if data structure is undefined */
2378     else if ((fptr->Fptr)->datastart == DATA_UNDEFINED)
2379         if ( ffrdef(fptr, status) > 0)
2380             return(*status);
2381 
2382     if ((fptr->Fptr)->hdutype == IMAGE_HDU)
2383     {
2384        ffpmsg
2385        ("Can only delete column from TABLE or BINTABLE extension (ffdcol)");
2386        return(*status = NOT_TABLE);
2387     }
2388 
2389     if (colnum < 1 || colnum > (fptr->Fptr)->tfield )
2390         return(*status = BAD_COL_NUM);
2391 
2392     colptr = (fptr->Fptr)->tableptr;
2393     colptr += (colnum - 1);
2394     firstcol = colptr->tbcol;  /* starting byte position of the column */
2395 
2396     /* use column width to determine how many bytes to delete in each row */
2397     if ((fptr->Fptr)->hdutype == ASCII_TBL)
2398     {
2399       delbyte = colptr->twidth;  /* width of ASCII column */
2400 
2401       if (colnum < (fptr->Fptr)->tfield) /* check for space between next column */
2402       {
2403         nextcol = colptr + 1;
2404         nspace = (long) ((nextcol->tbcol) - (colptr->tbcol) - delbyte);
2405         if (nspace > 0)
2406             delbyte++;
2407       }
2408       else if (colnum > 1)   /* check for space between last 2 columns */
2409       {
2410         nextcol = colptr - 1;
2411         nspace = (long) ((colptr->tbcol) - (nextcol->tbcol) - (nextcol->twidth));
2412         if (nspace > 0)
2413         {
2414            delbyte++;
2415            firstcol--;  /* delete the leading space */
2416         }
2417       }
2418     }
2419     else   /* a binary table */
2420     {
2421       if (colnum < (fptr->Fptr)->tfield)
2422       {
2423          nextcol = colptr + 1;
2424          delbyte = (nextcol->tbcol) - (colptr->tbcol);
2425       }
2426       else
2427       {
2428          delbyte = ((fptr->Fptr)->rowlength) - (colptr->tbcol);
2429       }
2430     }
2431 
2432     naxis1 = (fptr->Fptr)->rowlength;   /* current width of the table */
2433     naxis2 = (fptr->Fptr)->numrows;
2434 
2435     /* current size of table */
2436     size = (fptr->Fptr)->heapstart + (fptr->Fptr)->heapsize;
2437     freespace = ((LONGLONG)delbyte * naxis2) + ((size + 2879) / 2880) * 2880 - size;
2438     nblock = (long) (freespace / 2880);   /* number of empty blocks to delete */
2439 
2440     ffcdel(fptr, naxis1, naxis2, delbyte, firstcol, status); /* delete col */
2441 
2442     /* absolute heap position */
2443     firstbyte = (fptr->Fptr)->datastart + (fptr->Fptr)->heapstart;
2444     ndelete = (LONGLONG)delbyte * naxis2; /* size of shift */
2445 
2446     /* shift heap up (if it exists) */
2447     if ((fptr->Fptr)->heapsize > 0)
2448     {
2449       nbytes = (fptr->Fptr)->heapsize;    /* no. of bytes to shift up */
2450 
2451       if (ffshft(fptr, firstbyte, nbytes, -ndelete, status) > 0) /* mv heap */
2452           return(*status);
2453     }
2454 
2455     /* delete the empty  blocks at the end of the HDU */
2456     if (nblock > 0)
2457         ffdblk(fptr, nblock, status);
2458 
2459     /* update the heap starting address */
2460     (fptr->Fptr)->heapstart -= ndelete;
2461 
2462     /* update the THEAP keyword if it exists */
2463     tstatus = 0;
2464     ffmkyj(fptr, "THEAP", (long)(fptr->Fptr)->heapstart, "&", &tstatus);
2465 
2466     if ((fptr->Fptr)->hdutype == ASCII_TBL)
2467     {
2468       /* adjust the TBCOL values of the remaining columns */
2469       for (ii = 1; ii <= (fptr->Fptr)->tfield; ii++)
2470       {
2471         ffkeyn("TBCOL", ii, keyname, status);
2472         ffgkyjj(fptr, keyname, &tbcol, comm, status);
2473         if (tbcol > firstcol)
2474         {
2475           tbcol = tbcol - delbyte;
2476           ffmkyj(fptr, keyname, tbcol, "&", status);
2477         }
2478       }
2479     }
2480 
2481     /* update the mandatory keywords */
2482     ffmkyj(fptr, "TFIELDS", ((fptr->Fptr)->tfield) - 1, "&", status);
2483     ffmkyj(fptr,  "NAXIS1",   naxis1 - delbyte, "&", status);
2484     /*
2485       delete the index keywords starting with 'T' associated with the
2486       deleted column and subtract 1 from index of all higher keywords
2487     */
2488     ffkshf(fptr, colnum, (fptr->Fptr)->tfield, -1, status);
2489 
2490     ffrdef(fptr, status);  /* initialize the new table structure */
2491     return(*status);
2492 }
2493 /*--------------------------------------------------------------------------*/
ffcins(fitsfile * fptr,LONGLONG naxis1,LONGLONG naxis2,LONGLONG ninsert,LONGLONG bytepos,int * status)2494 int ffcins(fitsfile *fptr,  /* I - FITS file pointer                        */
2495            LONGLONG naxis1,     /* I - width of the table, in bytes             */
2496            LONGLONG naxis2,     /* I - number of rows in the table              */
2497            LONGLONG ninsert,    /* I - number of bytes to insert in each row    */
2498            LONGLONG bytepos,    /* I - rel. position in row to insert bytes     */
2499            int *status)     /* IO - error status                            */
2500 /*
2501  Insert 'ninsert' bytes into each row of the table at position 'bytepos'.
2502 */
2503 {
2504     unsigned char buffer[10000], cfill;
2505     LONGLONG newlen, fbyte, nbytes, irow, nseg, ii;
2506 
2507     if (*status > 0)
2508         return(*status);
2509 
2510     if (naxis2 == 0)
2511         return(*status);  /* just return if there are 0 rows in the table */
2512 
2513     /* select appropriate fill value */
2514     if ((fptr->Fptr)->hdutype == ASCII_TBL)
2515         cfill = 32;                     /* ASCII tables use blank fill */
2516     else
2517         cfill = 0;    /* primary array and binary tables use zero fill */
2518 
2519     newlen = naxis1 + ninsert;
2520 
2521     if (newlen <= 10000)
2522     {
2523        /*******************************************************************
2524        CASE #1: optimal case where whole new row fits in the work buffer
2525        *******************************************************************/
2526 
2527         for (ii = 0; ii < ninsert; ii++)
2528             buffer[ii] = cfill;      /* initialize buffer with fill value */
2529 
2530         /* first move the trailing bytes (if any) in the last row */
2531         fbyte = bytepos + 1;
2532         nbytes = naxis1 - bytepos;
2533         /* If the last row hasn't yet been accessed in full, it's possible
2534            that logfilesize hasn't been updated to account for it (by way
2535            of an ffldrc call).  This could cause ffgtbb to return with an
2536            EOF error.  To prevent this, we must increase logfilesize here.
2537         */
2538         if ((fptr->Fptr)->logfilesize < (fptr->Fptr)->datastart +
2539                  (fptr->Fptr)->heapstart)
2540         {
2541             (fptr->Fptr)->logfilesize = (((fptr->Fptr)->datastart +
2542                  (fptr->Fptr)->heapstart + 2879)/2880)*2880;
2543         }
2544 
2545         ffgtbb(fptr, naxis2, fbyte, nbytes, &buffer[ninsert], status);
2546         (fptr->Fptr)->rowlength = newlen; /*  new row length */
2547 
2548         /* write the row (with leading fill bytes) in the new place */
2549         nbytes += ninsert;
2550         ffptbb(fptr, naxis2, fbyte, nbytes, buffer, status);
2551         (fptr->Fptr)->rowlength = naxis1;  /* reset to orig. value */
2552 
2553         /*  now move the rest of the rows */
2554         for (irow = naxis2 - 1; irow > 0; irow--)
2555         {
2556             /* read the row to be shifted (work backwards thru the table) */
2557             ffgtbb(fptr, irow, fbyte, naxis1, &buffer[ninsert], status);
2558             (fptr->Fptr)->rowlength = newlen; /* new row length */
2559 
2560             /* write the row (with the leading fill bytes) in the new place */
2561             ffptbb(fptr, irow, fbyte, newlen, buffer, status);
2562             (fptr->Fptr)->rowlength = naxis1; /* reset to orig value */
2563         }
2564     }
2565     else
2566     {
2567         /*****************************************************************
2568         CASE #2:  whole row doesn't fit in work buffer; move row in pieces
2569         ******************************************************************
2570         first copy the data, then go back and write fill into the new column
2571         start by copying the trailing bytes (if any) in the last row.     */
2572 
2573         nbytes = naxis1 - bytepos;
2574         nseg = (nbytes + 9999) / 10000;
2575         fbyte = (nseg - 1) * 10000 + bytepos + 1;
2576         nbytes = naxis1 - fbyte + 1;
2577 
2578         for (ii = 0; ii < nseg; ii++)
2579         {
2580             ffgtbb(fptr, naxis2, fbyte, nbytes, buffer, status);
2581             (fptr->Fptr)->rowlength =   newlen;  /* new row length */
2582 
2583             ffptbb(fptr, naxis2, fbyte + ninsert, nbytes, buffer, status);
2584             (fptr->Fptr)->rowlength =   naxis1; /* reset to orig value */
2585 
2586             fbyte -= 10000;
2587             nbytes = 10000;
2588         }
2589 
2590         /* now move the rest of the rows */
2591         nseg = (naxis1 + 9999) / 10000;
2592         for (irow = naxis2 - 1; irow > 0; irow--)
2593         {
2594           fbyte = (nseg - 1) * 10000 + bytepos + 1;
2595           nbytes = naxis1 - (nseg - 1) * 10000;
2596           for (ii = 0; ii < nseg; ii++)
2597           {
2598             /* read the row to be shifted (work backwards thru the table) */
2599             ffgtbb(fptr, irow, fbyte, nbytes, buffer, status);
2600             (fptr->Fptr)->rowlength =   newlen;  /* new row length */
2601 
2602             /* write the row in the new place */
2603             ffptbb(fptr, irow, fbyte + ninsert, nbytes, buffer, status);
2604             (fptr->Fptr)->rowlength =   naxis1; /* reset to orig value */
2605 
2606             fbyte -= 10000;
2607             nbytes = 10000;
2608           }
2609         }
2610 
2611         /* now write the fill values into the new column */
2612         nbytes = minvalue(ninsert, 10000);
2613         memset(buffer, cfill, (size_t) nbytes); /* initialize with fill value */
2614 
2615         nseg = (ninsert + 9999) / 10000;
2616         (fptr->Fptr)->rowlength =  newlen;  /* new row length */
2617 
2618         for (irow = 1; irow <= naxis2; irow++)
2619         {
2620           fbyte = bytepos + 1;
2621           nbytes = ninsert - ((nseg - 1) * 10000);
2622           for (ii = 0; ii < nseg; ii++)
2623           {
2624             ffptbb(fptr, irow, fbyte, nbytes, buffer, status);
2625             fbyte += nbytes;
2626             nbytes = 10000;
2627           }
2628         }
2629         (fptr->Fptr)->rowlength = naxis1;  /* reset to orig value */
2630     }
2631     return(*status);
2632 }
2633 /*--------------------------------------------------------------------------*/
ffcdel(fitsfile * fptr,LONGLONG naxis1,LONGLONG naxis2,LONGLONG ndelete,LONGLONG bytepos,int * status)2634 int ffcdel(fitsfile *fptr,  /* I - FITS file pointer                        */
2635            LONGLONG naxis1,     /* I - width of the table, in bytes             */
2636            LONGLONG naxis2,     /* I - number of rows in the table              */
2637            LONGLONG ndelete,    /* I - number of bytes to delete in each row    */
2638            LONGLONG bytepos,    /* I - rel. position in row to delete bytes     */
2639            int *status)     /* IO - error status                            */
2640 /*
2641  delete 'ndelete' bytes from each row of the table at position 'bytepos'.  */
2642 {
2643     unsigned char buffer[10000];
2644     LONGLONG i1, i2, ii, irow, nseg;
2645     LONGLONG newlen, remain, nbytes;
2646 
2647     if (*status > 0)
2648         return(*status);
2649 
2650     if (naxis2 == 0)
2651         return(*status);  /* just return if there are 0 rows in the table */
2652 
2653     newlen = naxis1 - ndelete;
2654 
2655     if (newlen <= 10000)
2656     {
2657       /*******************************************************************
2658       CASE #1: optimal case where whole new row fits in the work buffer
2659       *******************************************************************/
2660       i1 = bytepos + 1;
2661       i2 = i1 + ndelete;
2662       for (irow = 1; irow < naxis2; irow++)
2663       {
2664         ffgtbb(fptr, irow, i2, newlen, buffer, status); /* read row */
2665         (fptr->Fptr)->rowlength = newlen;  /* new row length */
2666 
2667         ffptbb(fptr, irow, i1, newlen, buffer, status); /* write row */
2668         (fptr->Fptr)->rowlength = naxis1;  /* reset to orig value */
2669       }
2670 
2671       /* now do the last row */
2672       remain = naxis1 - (bytepos + ndelete);
2673 
2674       if (remain > 0)
2675       {
2676         ffgtbb(fptr, naxis2, i2, remain, buffer, status); /* read row */
2677         (fptr->Fptr)->rowlength = newlen;  /* new row length */
2678 
2679         ffptbb(fptr, naxis2, i1, remain, buffer, status); /* write row */
2680         (fptr->Fptr)->rowlength = naxis1;  /* reset to orig value */
2681       }
2682     }
2683     else
2684     {
2685         /*****************************************************************
2686         CASE #2:  whole row doesn't fit in work buffer; move row in pieces
2687         ******************************************************************/
2688 
2689         nseg = (newlen + 9999) / 10000;
2690         for (irow = 1; irow < naxis2; irow++)
2691         {
2692           i1 = bytepos + 1;
2693           i2 = i1 + ndelete;
2694 
2695           nbytes = newlen - (nseg - 1) * 10000;
2696           for (ii = 0; ii < nseg; ii++)
2697           {
2698             ffgtbb(fptr, irow, i2, nbytes, buffer, status); /* read bytes */
2699             (fptr->Fptr)->rowlength = newlen;  /* new row length */
2700 
2701             ffptbb(fptr, irow, i1, nbytes, buffer, status); /* rewrite bytes */
2702             (fptr->Fptr)->rowlength = naxis1; /* reset to orig value */
2703 
2704             i1 += nbytes;
2705             i2 += nbytes;
2706             nbytes = 10000;
2707           }
2708         }
2709 
2710         /* now do the last row */
2711         remain = naxis1 - (bytepos + ndelete);
2712 
2713         if (remain > 0)
2714         {
2715           nseg = (remain + 9999) / 10000;
2716           i1 = bytepos + 1;
2717           i2 = i1 + ndelete;
2718           nbytes = remain - (nseg - 1) * 10000;
2719           for (ii = 0; ii < nseg; ii++)
2720           {
2721             ffgtbb(fptr, naxis2, i2, nbytes, buffer, status);
2722             (fptr->Fptr)->rowlength = newlen;  /* new row length */
2723 
2724             ffptbb(fptr, naxis2, i1, nbytes, buffer, status); /* write row */
2725             (fptr->Fptr)->rowlength = naxis1;  /* reset to orig value */
2726 
2727             i1 += nbytes;
2728             i2 += nbytes;
2729             nbytes = 10000;
2730           }
2731         }
2732     }
2733     return(*status);
2734 }
2735 /*--------------------------------------------------------------------------*/
ffkshf(fitsfile * fptr,int colmin,int colmax,int incre,int * status)2736 int ffkshf(fitsfile *fptr,  /* I - FITS file pointer                        */
2737            int colmin,      /* I - starting col. to be incremented; 1 = 1st */
2738            int colmax,      /* I - last column to be incremented            */
2739            int incre,       /* I - shift index number by this amount        */
2740            int *status)     /* IO - error status                            */
2741 /*
2742   shift the index value on any existing column keywords
2743   This routine will modify the name of any keyword that begins with 'T'
2744   and has an index number in the range COLMIN - COLMAX, inclusive.
2745 
2746   if incre is positive, then the index values will be incremented.
2747   if incre is negative, then the kewords with index = COLMIN
2748   will be deleted and the index of higher numbered keywords will
2749   be decremented.
2750 */
2751 {
2752     int nkeys, nmore, nrec, tstatus, i1;
2753     long ivalue;
2754     char rec[FLEN_CARD], q[FLEN_KEYWORD], newkey[FLEN_KEYWORD];
2755 
2756     ffghsp(fptr, &nkeys, &nmore, status);  /* get number of keywords */
2757 
2758     /* go thru header starting with the 9th keyword looking for 'TxxxxNNN' */
2759 
2760     for (nrec = 9; nrec <= nkeys; nrec++)
2761     {
2762         ffgrec(fptr, nrec, rec, status);
2763 
2764         if (rec[0] == 'T')
2765         {
2766             i1 = 0;
2767             strncpy(q, &rec[1], 4);
2768             if (!strncmp(q, "BCOL", 4) || !strncmp(q, "FORM", 4) ||
2769                 !strncmp(q, "TYPE", 4) || !strncmp(q, "SCAL", 4) ||
2770                 !strncmp(q, "UNIT", 4) || !strncmp(q, "NULL", 4) ||
2771                 !strncmp(q, "ZERO", 4) || !strncmp(q, "DISP", 4) ||
2772                 !strncmp(q, "LMIN", 4) || !strncmp(q, "LMAX", 4) ||
2773                 !strncmp(q, "DMIN", 4) || !strncmp(q, "DMAX", 4) ||
2774                 !strncmp(q, "CTYP", 4) || !strncmp(q, "CRPX", 4) ||
2775                 !strncmp(q, "CRVL", 4) || !strncmp(q, "CDLT", 4) ||
2776                 !strncmp(q, "CROT", 4) || !strncmp(q, "CUNI", 4) )
2777               i1 = 5;
2778             else if (!strncmp(rec, "TDIM", 4) )
2779               i1 = 4;
2780 
2781             if (i1)
2782             {
2783               /* try reading the index number suffix */
2784               q[0] = '\0';
2785               strncat(q, &rec[i1], 8 - i1);
2786 
2787               tstatus = 0;
2788               ffc2ii(q, &ivalue, &tstatus);
2789 
2790               if (tstatus == 0 && ivalue >= colmin && ivalue <= colmax)
2791               {
2792                 if (incre <= 0 && ivalue == colmin)
2793                 {
2794                   ffdrec(fptr, nrec, status); /* delete keyword */
2795                   nkeys = nkeys - 1;
2796                   nrec = nrec - 1;
2797                 }
2798                 else
2799                 {
2800                   ivalue = ivalue + incre;
2801                   q[0] = '\0';
2802                   strncat(q, rec, i1);
2803 
2804                   ffkeyn(q, ivalue, newkey, status);
2805 		  /* NOTE: because of null termination, it is not
2806 		     equivalent to use strcpy() for the same calls */
2807                   strncpy(rec, "        ", 8);    /* erase old keyword name */
2808                   i1 = strlen(newkey);
2809                   strncpy(rec, newkey, i1);   /* overwrite new keyword name */
2810                   ffmrec(fptr, nrec, rec, status);  /* modify the record */
2811                 }
2812               }
2813             }
2814         }
2815     }
2816     return(*status);
2817 }
2818 /*--------------------------------------------------------------------------*/
fffvcl(fitsfile * fptr,int * nvarcols,int * colnums,int * status)2819 int fffvcl(fitsfile *fptr,   /* I - FITS file pointer                       */
2820            int *nvarcols,    /* O - Number of variable length columns found */
2821            int *colnums,     /* O - 1-based variable column positions       */
2822            int *status)      /* IO - error status                           */
2823 {
2824 /*
2825    Internal function to identify which columns in a binary table are variable length.
2826    The colnums array will be filled with nvarcols elements - the 1-based numbers
2827    of all variable length columns in the table.  This ASSUMES calling function
2828    has passed in a colnums array large enough to hold these.
2829 */
2830    int tfields=0,icol;
2831    tcolumn *colptr=0;
2832 
2833    *nvarcols = 0;
2834    if (*status > 0)
2835        return(*status);
2836 
2837    if ((fptr->Fptr)->hdutype != BINARY_TBL)
2838    {
2839       ffpmsg("Var-length column search can only be performed on Binary tables (fffvcl)");
2840       return(*status = NOT_BTABLE);
2841    }
2842 
2843    if ((fptr->Fptr)->tableptr)
2844    {
2845       colptr = (fptr->Fptr)->tableptr;
2846       tfields = (fptr->Fptr)->tfield;
2847       for (icol=0; icol<tfields; ++icol, ++colptr)
2848       {
2849          /* Condition for variable length column: negative tdatatype */
2850          if (colptr->tdatatype < 0)
2851          {
2852             colnums[*nvarcols] = icol + 1;
2853             *nvarcols += 1;
2854          }
2855       }
2856    }
2857    return(*status);
2858 }
2859 
2860 /*--------------------------------------------------------------------------*/
ffshft(fitsfile * fptr,LONGLONG firstbyte,LONGLONG nbytes,LONGLONG nshift,int * status)2861 int ffshft(fitsfile *fptr,  /* I - FITS file pointer                        */
2862            LONGLONG firstbyte, /* I - position of first byte in block to shift */
2863            LONGLONG nbytes,    /* I - size of block of bytes to shift          */
2864            LONGLONG nshift,    /* I - size of shift in bytes (+ or -)          */
2865            int *status)     /* IO - error status                            */
2866 /*
2867     Shift block of bytes by nshift bytes (positive or negative).
2868     A positive nshift value moves the block down further in the file, while a
2869     negative value shifts the block towards the beginning of the file.
2870 */
2871 {
2872 #define shftbuffsize 100000
2873     long ntomov;
2874     LONGLONG ptr, ntodo;
2875     char buffer[shftbuffsize];
2876 
2877     if (*status > 0)
2878         return(*status);
2879 
2880     ntodo = nbytes;   /* total number of bytes to shift */
2881 
2882     if (nshift > 0)
2883             /* start at the end of the block and work backwards */
2884             ptr = firstbyte + nbytes;
2885     else
2886             /* start at the beginning of the block working forwards */
2887             ptr = firstbyte;
2888 
2889     while (ntodo)
2890     {
2891         /* number of bytes to move at one time */
2892         ntomov = (long) (minvalue(ntodo, shftbuffsize));
2893 
2894         if (nshift > 0)     /* if moving block down ... */
2895             ptr -= ntomov;
2896 
2897         /* move to position and read the bytes to be moved */
2898 
2899         ffmbyt(fptr, ptr, REPORT_EOF, status);
2900         ffgbyt(fptr, ntomov, buffer, status);
2901 
2902         /* move by shift amount and write the bytes */
2903         ffmbyt(fptr, ptr + nshift, IGNORE_EOF, status);
2904         if (ffpbyt(fptr, ntomov, buffer, status) > 0)
2905         {
2906            ffpmsg("Error while shifting block (ffshft)");
2907            return(*status);
2908         }
2909 
2910         ntodo -= ntomov;
2911         if (nshift < 0)     /* if moving block up ... */
2912             ptr += ntomov;
2913     }
2914 
2915     /* now overwrite the old data with fill */
2916     if ((fptr->Fptr)->hdutype == ASCII_TBL)
2917        memset(buffer, 32, shftbuffsize); /* fill ASCII tables with spaces */
2918     else
2919        memset(buffer,  0, shftbuffsize); /* fill other HDUs with zeros */
2920 
2921 
2922     if (nshift < 0)
2923     {
2924         ntodo = -nshift;
2925         /* point to the end of the shifted block */
2926         ptr = firstbyte + nbytes + nshift;
2927     }
2928     else
2929     {
2930         ntodo = nshift;
2931         /* point to original beginning of the block */
2932         ptr = firstbyte;
2933     }
2934 
2935     ffmbyt(fptr, ptr, REPORT_EOF, status);
2936 
2937     while (ntodo)
2938     {
2939         ntomov = (long) (minvalue(ntodo, shftbuffsize));
2940         ffpbyt(fptr, ntomov, buffer, status);
2941         ntodo -= ntomov;
2942     }
2943     return(*status);
2944 }
2945