# include <dvo.h>

int dvo_catalog_load_split (Catalog *catalog, int VERBOSE) {

  int Nitems, Nexpect, Naverage, Nmeasure, Nmissing, Nsecfilt, status;
  char *path, string[80];
  Header header;
  Matrix matrix;
  FTable ftable;
  SecFilt *primary;
  Catalog *measure, *missing, *secfilt;

  /* ftable header storage for below */
  ftable.header = &header;

  /* needed to find the split files below */
  path = pathname (catalog[0].filename);

  /* get the components from the header - these duplicate information in the split files (NAXIS2) */
  if (!gfits_scan (&catalog[0].header, "NSTARS",   "%d", 1, &Naverage)) return (FALSE);
  if (!gfits_scan (&catalog[0].header, "NMEAS",    "%d", 1, &Nmeasure)) return (FALSE);
  if (!gfits_scan (&catalog[0].header, "NMISS",    "%d", 1, &Nmissing)) return (FALSE);
  if (!gfits_scan (&catalog[0].header, "NSECFILT", "%d", 1, &Nsecfilt)) Nsecfilt = 0;

  /**  Nsecfilt is unusual: it does not list the number of data items in the table
       instead, the number of items is Nsecfilt * Naverage.  **/
  catalog[0].Nsecfilt = Nsecfilt;

  /* default values */
  catalog[0].average = NULL;
  catalog[0].measure = NULL;
  catalog[0].missing = NULL;
  catalog[0].secfilt = NULL;

  /* XXX EAM : validate table mode */

  /*** Average Table ***/

  if (catalog[0].catflags & LOAD_AVES) {
    /* move pointer past header -- must be already read (load_catalog) */
    fseek (catalog[0].f, catalog[0].header.size, SEEK_SET);
    /* matrix should be empty */
    if (!gfits_fread_matrix (catalog[0].f, &matrix, &catalog[0].header)) {
      if (VERBOSE) fprintf (stderr, "can't read primary matrix");
      return (FALSE);
    }
    /* read Average table header */
    if (!gfits_fread_header (catalog[0].f, &header)) {
      if (VERBOSE) fprintf (stderr, "can't read table average header");
      return (FALSE);
    }
    /* read Average table data */
    if (!gfits_fread_ftable_data (catalog[0].f, &ftable)) {
      if (VERBOSE) fprintf (stderr, "can't read table average data");
      return (FALSE);
    }
    /* old versions of DVO stored one of the average magnitudes in Average. we save this if needed */
    catalog[0].average = FtableToAverage (&ftable, &catalog[0].Naverage, &catalog[0].catformat, &primary);
    if (Naverage != catalog[0].Naverage) {
      fprintf (stderr, "Warning: mismatch between Naverage in PHU and Table headers (%d vs %d)\n", Naverage, catalog[0].Naverage);
    }
    gfits_free_header (&header);
  } 

  /*** Measure Table ***/

  measure = NULL;

  /* (Full Load) */
  if (catalog[0].catflags & LOAD_MEAS) {
    ALLOCATE (measure, Catalog, 1);
    dvo_catalog_init (measure, TRUE);

    /* get split filename from main header (paths relative to cpt file) */
    if (!gfits_scan (&catalog[0].header, "MEASURE",  "%s", 1, string)) return (FALSE);
    ALLOCATE (measure[0].filename, char, strlen(path) + strlen(string) + 2);
    sprintf (measure[0].filename, "%s/%s", path, string);

    /* lock & open catalog file */
    if (dvo_catalog_lock (measure, catalog[0].lockmode) != 1) {
      fprintf (stderr, "cannot access measure file %s\n", measure[0].filename);
      exit (2);
    }

    /* read PHU */
    if (!gfits_load_header (measure[0].f, &measure[0].header)) {
      if (VERBOSE) fprintf (stderr, "catalog file does not exist: %s\n", measure[0].filename);
      return (FALSE);
    }
    /* matrix should be empty */
    if (!gfits_fread_matrix (measure[0].f, &matrix, &measure[0].header)) {
      if (VERBOSE) fprintf (stderr, "can't read primary matrix\n");
      return (FALSE);
    }
    /* read Measure table header */
    if (!gfits_fread_header (measure[0].f, &header)) {
      if (VERBOSE) fprintf (stderr, "can't read measure PHU header\n");
      return (FALSE);
    }
    /* read Measure table data */
    if (!gfits_fread_ftable_data (measure[0].f, &ftable)) {
      if (VERBOSE) fprintf (stderr, "can't read table measure data\n");
      return (FALSE);
    }
    /* convert data format to internal */
    catalog[0].measure = FtableToMeasure (&ftable, &catalog[0].Nmeasure, &catalog[0].catformat);
    if (Nmeasure != catalog[0].Nmeasure) {
      fprintf (stderr, "Warning: mismatch between Nmeasure in PHU and Table headers (%d vs %d)\n", Nmeasure, catalog[0].Nmeasure);
    }
    catalog[0].Nmeas_off = 0;
    gfits_free_header (&header);
    gfits_free_matrix (&matrix);
  }

  /* (Meta Load) */
  if (catalog[0].catflags & LOAD_MEAS_META) {
    ALLOCATE (measure, Catalog, 1);
    dvo_catalog_init (measure, TRUE);

    /* get split filename from main header (paths relative to cpt file) */
    if (!gfits_scan (&catalog[0].header, "MEASURE",  "%s", 1, string)) return (FALSE);
    ALLOCATE (measure[0].filename, char, strlen(path) + strlen(string) + 2);
    sprintf (measure[0].filename, "%s/%s", path, string);

    /* lock & open catalog file */
    if (dvo_catalog_lock (measure, catalog[0].lockmode) != 1) {
      fprintf (stderr, "cannot access measure file %s\n", measure[0].filename);
      exit (2);
    }

    /* read PHU */
    if (!gfits_load_header (measure[0].f, &measure[0].header)) {
      if (VERBOSE) fprintf (stderr, "catalog file does not exist: %s\n", measure[0].filename);
      return (FALSE);
    }

    /* allocate dummy data for valid realloc */
    ALLOCATE (catalog[0].measure, Measure, 1);

    /* set up the table size information */
    catalog[0].Nmeasure  = 0;        /* no rows loaded */
    catalog[0].Nmeas_off = Nmeasure; /* start of new entries */
  }
  catalog[0].measure_catalog = measure;

  /*** Missing Table ***/

  missing = NULL;
  if (catalog[0].catflags & LOAD_MISS) {
    ALLOCATE (missing, Catalog, 1);
    dvo_catalog_init (missing, TRUE);

    /* get split filename from main header (paths relative to cpt file) */
    if (!gfits_scan (&catalog[0].header, "MISSING",  "%s", 1, string)) return (FALSE);
    ALLOCATE (missing[0].filename, char, strlen(path) + strlen(string) + 2);
    sprintf (missing[0].filename, "%s/%s", path, string);

    /* lock & open catalog file */
    status = dvo_catalog_lock (missing, catalog[0].lockmode);
    if (!status) {
      fprintf (stderr, "ERROR: cannot access missing file %s\n", missing[0].filename);
      exit (2);
    }
    if (status == 2) {
	/* MISSING table is empty (this is not an error) */
	gfits_create_header (&missing[0].header);
	goto missing_empty;
    }

    /* read PHU */
    if (!gfits_load_header (missing[0].f, &missing[0].header)) {
      if (VERBOSE) fprintf (stderr, "catalog file does not exist: %s\n", missing[0].filename);
      return (FALSE);
    }
    /* matrix should be empty */
    if (!gfits_fread_matrix (missing[0].f, &matrix, &missing[0].header)) {
      if (VERBOSE) fprintf (stderr, "can't read primary matrix\n");
      return (FALSE);
    }
    /* read Missing table header */
    if (!gfits_fread_header (missing[0].f, &header)) {
      if (VERBOSE) fprintf (stderr, "can't read table missing header\n");
      return (FALSE);
    }
    /* read Missing table data */
    if (!gfits_fread_ftable_data (missing[0].f, &ftable)) {
      if (VERBOSE) fprintf (stderr, "can't read table missing data\n");
      return (FALSE);
    }
    /* no conversions currently defined */
    catalog[0].missing = gfits_table_get_Missing (&ftable, &catalog[0].Nmissing, NULL);
    if (Nmissing != catalog[0].Nmissing) {
      fprintf (stderr, "Warning: mismatch between Nmissing in PHU and Table headers (%d vs %d)\n", Nmissing, catalog[0].Nmissing);
    }
    gfits_free_header (&header);
    gfits_free_matrix (&matrix);
  } 
missing_empty:
  catalog[0].missing_catalog = missing;

  /*** Secfilt Table ***/

  secfilt = NULL;
  if (catalog[0].catflags & LOAD_SECF) {
    ALLOCATE (secfilt, Catalog, 1);
    dvo_catalog_init (secfilt, TRUE);

    /* get split filename from main header (paths relative to cpt file) */
    if (!gfits_scan (&catalog[0].header, "SECFILT",  "%s", 1, string)) return (FALSE);
    ALLOCATE (secfilt[0].filename, char, strlen(path) + strlen(string) + 2);
    sprintf (secfilt[0].filename, "%s/%s", path, string);

    /* lock & open catalog file */
    if (dvo_catalog_lock (secfilt, catalog[0].lockmode) != 1) {
      fprintf (stderr, "cannot access secfilt file %s\n", secfilt[0].filename);
      exit (2);
    }

    /* read PHU */
    if (!gfits_load_header (secfilt[0].f, &secfilt[0].header)) {
      if (VERBOSE) fprintf (stderr, "catalog file does not exist: %s\n", secfilt[0].filename);
      return (FALSE);
    }
    /* matrix should be empty */
    if (!gfits_fread_matrix (secfilt[0].f, &matrix, &secfilt[0].header)) {
      if (VERBOSE) fprintf (stderr, "can't read primary matrix\n");
      return (FALSE);
    }
    /* read secfilt table header */
    if (!gfits_fread_header (secfilt[0].f, &header)) {
      if (VERBOSE) fprintf (stderr, "can't read table secfilt header\n");
      return (FALSE);
    }
    /* read secfilt table data */
    if (!gfits_fread_ftable_data (secfilt[0].f, &ftable)) {
      if (VERBOSE) fprintf (stderr, "can't read table secfilt data\n");
      return (FALSE);
    }
    Nexpect = catalog[0].Nsecfilt * catalog[0].Naverage;
    catalog[0].secfilt = FtableToSecFilt (&ftable, &Nitems, &catalog[0].catformat);
    if (Nexpect != Nitems) {
      fprintf (stderr, "Warning: mismatch between Nsecfilt items in PHU and Table headers (%d vs %d)\n", Nexpect, Nitems);
    }

    /* if primary is defined, we were supplied with one additional average magnitude from Average
       we need to interleave these magnitudes with the secfilt entries just loaded */
    if (primary != NULL) {
      int Ntmpfilt, Ntotal, i, j;
      SecFilt *tmpfilt;
      tmpfilt  = catalog[0].secfilt;
      Ntmpfilt = catalog[0].Nsecfilt;
      Nsecfilt = catalog[0].Nsecfilt + 1;
      Ntotal = Nsecfilt * catalog[0].Naverage;
      ALLOCATE (catalog[0].secfilt, SecFilt, Ntotal);
      for (i = 0; i < catalog[0].Naverage; i++) {
	catalog[0].secfilt[i*Nsecfilt + 0] = primary[i];
	for (j = 0; j < Ntmpfilt; j++) {
	  catalog[0].secfilt[i*Nsecfilt + j + 1] = tmpfilt[i*Ntmpfilt + j];
	}
      }		
      catalog[0].Nsecfilt = Nsecfilt;
      free (primary);
    } 


    gfits_free_header (&header);
    gfits_free_matrix (&matrix);
  } else {
    if (primary != NULL) free (primary);
  }

  catalog[0].secfilt_catalog = secfilt;

  /* save the current number so we can do partial updates */
  catalog[0].Nave_disk  = Naverage;
  catalog[0].Nmeas_disk = Nmeasure;
  catalog[0].Nmiss_disk = Nmissing;

  return (TRUE);
}

/* save_catalog_split writes complete new files from scratch */
int dvo_catalog_save_split (Catalog *catalog, char VERBOSE) {

  int Nitems;
  Matrix matrix;
  Header header;
  FTable ftable;
  Catalog *catfile;
  SecFilt *primary, *secfilt;
  int i, j, Nsecfilt, Nallfilt, Ntotal;

  ftable.header = &header;

  if (catalog[0].Naverage == 0) {
    if (VERBOSE) fprintf (stderr, "no stars in catalog, skipping\n");
    return (TRUE);
  }

  /* for the appropriate types, pull out the first secfilt and pass to AverageToFtable as primary */
  if ((catalog[0].catformat == DVO_FORMAT_ELIXIR) || // special case for ELIXIR
      (catalog[0].catformat == DVO_FORMAT_LONEOS)) { // special case for LONEOS
    if (catalog[0].secfilt == NULL) {       
      fprintf (stderr, "missing secfilt, cannot build output averages (dvo_catalog_split.c:544)\n");
      exit (1);
    }
    secfilt = catalog[0].secfilt;

    Nallfilt = catalog[0].Nsecfilt;
    Nsecfilt = catalog[0].Nsecfilt - 1;
    Ntotal = Nsecfilt * catalog[0].Naverage;
    ALLOCATE (primary, SecFilt, catalog[0].Naverage);
    ALLOCATE (secfilt, SecFilt, Ntotal);

    for (i = 0; i < catalog[0].Naverage; i++) {
      primary[i] = secfilt[i*Nallfilt + 0];
      for (j = 0; j < Nsecfilt; j++) {
	secfilt[i*Nsecfilt + j] = catalog[0].secfilt[i*Nallfilt + j + 1];
      }
    }		
  } else {
    primary = NULL;
    secfilt = catalog[0].secfilt;
    Nsecfilt = catalog[0].Nsecfilt;
  }

  /* make sure header is consistent with data */
  gfits_modify (&catalog[0].header, "NSTARS",   "%d", 1, catalog[0].Naverage);
  gfits_modify (&catalog[0].header, "NMEAS",    "%d", 1, catalog[0].Nmeasure);
  gfits_modify (&catalog[0].header, "NMISS",    "%d", 1, catalog[0].Nmissing);
  gfits_modify (&catalog[0].header, "NSECFILT", "%d", 1, Nsecfilt);
  gfits_modify (&catalog[0].header, "EXTEND",   "%t", 1, TRUE);

  /* rewind file pointers and truncate (file is still open) */
  fseek (catalog[0].f, 0, SEEK_SET);

  /* write table PHU header - always write this out */
  /* XXX EAM : check if disk file size has changed */
  if (!gfits_fwrite_header  (catalog[0].f, &catalog[0].header)) {
    fprintf (stderr, "can't write primary header");
    goto failure;
  }

  /* in split mode, we can save only part of the data */ 

  /*** Average Table ***/

  if (catalog[0].average != NULL) {
    ftruncate (fileno (catalog[0].f), catalog[0].header.size);

    /* this is probably a NOP, do I have to keep it in? */
    gfits_create_matrix (&catalog[0].header, &matrix);
    if (!gfits_fwrite_matrix  (catalog[0].f, &matrix)) {
      fprintf (stderr, "can't write primary matrix");
      goto failure;
    }
    gfits_free_matrix (&matrix);

    /* write out Average table (convert to FITS table format) */
    AverageToFtable (&ftable, catalog[0].average, catalog[0].Naverage, catalog[0].catformat, primary);
    if (!gfits_fwrite_Theader (catalog[0].f, &header)) {
      fprintf (stderr, "can't write table header");
      goto failure;
    }
    if (!gfits_fwrite_table (catalog[0].f, &ftable)) {
      fprintf (stderr, "can't write table data");
      goto failure;
    }
    gfits_free_table (&ftable);
    gfits_free_header (&header);
  }

  /*** Measure Table ***/
  if (catalog[0].measure != NULL) {

    /* catalog file data is stored in separate structure */
    catfile = catalog[0].measure_catalog;

    /* XXX EAM : warn about this condition; add code to handle? */
    if (catalog[0].Nmeas_off != 0) {
      fprintf (stderr, "WARNING: LOAD_MEAS_META mixed with save??\n");
      fprintf (stderr, "WARNING: this should not be allowed to happen!\n");
    }

    /* rewind file pointers and truncate (file is still open) */
    fseek (catfile[0].f, 0, SEEK_SET);
    ftruncate (fileno (catfile[0].f), 0);

    /* write table PHU header */
    if (!gfits_fwrite_header  (catfile[0].f, &catfile[0].header)) {
      fprintf (stderr, "can't write primary header");
      goto failure;
    }

    /* this is probably a NOP, do I have to keep it in? */
    gfits_create_matrix (&catfile[0].header, &matrix);
    if (!gfits_fwrite_matrix  (catfile[0].f, &matrix)) {
      fprintf (stderr, "can't write primary matrix");
      goto failure;
    }
    gfits_free_matrix (&matrix);

    /* write out Measure table (convert to FITS table format) */
    MeasureToFtable (&ftable, catalog[0].measure, catalog[0].Nmeasure, catalog[0].catformat);
    if (!gfits_fwrite_Theader (catfile[0].f, &header)) {
      fprintf (stderr, "can't write table header");
      goto failure;
    }
    if (!gfits_fwrite_table (catfile[0].f, &ftable)) {
      fprintf (stderr, "can't write table data");
      goto failure;
    }
    gfits_free_table (&ftable);
    gfits_free_header (&header);
  }

  /*** Missing Table ***/
  if (catalog[0].missing != NULL) {

    /* catalog data is stored in separate catalog */
    catfile = catalog[0].missing_catalog;

    /* rewind file pointers and truncate (file is still open) */
    fseek (catfile[0].f, 0, SEEK_SET);
    ftruncate (fileno (catfile[0].f), 0);

    /* write table PHU header */
    if (!gfits_fwrite_header  (catfile[0].f, &catfile[0].header)) {
      fprintf (stderr, "can't write primary header");
      goto failure;
    }

    /* this is probably a NOP, do I have to keep it in? */
    gfits_create_matrix (&catfile[0].header, &matrix);
    if (!gfits_fwrite_matrix  (catfile[0].f, &matrix)) {
      fprintf (stderr, "can't write primary matrix");
      goto failure;
    }
    gfits_free_matrix (&matrix);

    /* write out Missing table (convert to FITS table format) */
    gfits_table_set_Missing (&ftable, catalog[0].missing, catalog[0].Nmissing);
    if (!gfits_fwrite_Theader (catfile[0].f, &header)) {
      fprintf (stderr, "can't write table header");
      goto failure;
    }
    if (!gfits_fwrite_table (catfile[0].f, &ftable)) {
      fprintf (stderr, "can't write table data");
      goto failure;
    }
    gfits_free_table (&ftable);
    gfits_free_header (&header);
  }

  /*** Secfilt Table ***/
  if (catalog[0].secfilt != NULL) {

    /* catalog file data is stored in a separate catalog structure */
    catfile = catalog[0].secfilt_catalog;

    /* rewind file pointers and truncate (file is still open) */
    fseek (catfile[0].f, 0, SEEK_SET);
    ftruncate (fileno (catfile[0].f), 0);

    /* write table PHU header */
    if (!gfits_fwrite_header  (catfile[0].f, &catfile[0].header)) {
      fprintf (stderr, "can't write primary header");
      goto failure;
    }

    /* this is probably a NOP, do I have to keep it in? */
    gfits_create_matrix (&catfile[0].header, &matrix);
    if (!gfits_fwrite_matrix  (catfile[0].f, &matrix)) {
      fprintf (stderr, "can't write primary matrix");
      goto failure;
    }
    gfits_free_matrix (&matrix);

    /* write out SecFilt table (convert to FITS table format) */
    Nitems = catalog[0].Naverage * catalog[0].Nsecfilt;
    SecFiltToFtable (&ftable, secfilt, Nitems, catalog[0].catformat);
    if (!gfits_fwrite_Theader (catfile[0].f, &header)) {
      fprintf (stderr, "can't write table header");
      goto failure;
    }
    if (!gfits_fwrite_table (catfile[0].f, &ftable)) {
      fprintf (stderr, "can't write table data");
      goto failure;
    }
    gfits_free_table (&ftable);
    gfits_free_header (&header);
  }

  /* free temp storage */
  if (primary != NULL) {
    free (primary);
    free (secfilt);
  }

  return (TRUE);

failure:
  /* free temp storage */
  if (primary != NULL) {
    free (primary);
    free (secfilt);
  }
  return (FALSE);
}

/* update_catalog_split only writes new lines to file.  
 * if file is empty, call save_catalog_split instead.
 * XXX EAM : save_catalog SHOULD do this
 */

int dvo_catalog_update_split (Catalog *catalog, char VERBOSE) {

  int i, Nx, Ny, Nlines;
  int Nitems, Nskip, Nout, Ndisk, Nstart;
  Matrix matrix;
  Header header;
  FTable ftable;
  VTable vtable;
  Catalog *catfile;
  SecFilt *primary, *secfilt;
  int j, Nsecfilt, Nallfilt, Ntotal;

  ftable.header = &header;
  vtable.header = &header;

  if (catalog[0].Naverage == 0) {
    if (VERBOSE) fprintf (stderr, "no stars in catalog, skipping\n");
    return (TRUE);
  }

  /** for the appropriate types, pull out the first secfilt and pass to AverageToFtable as primary **/
  if ((catalog[0].catformat == DVO_FORMAT_ELIXIR) || // special case for ELIXIR
      (catalog[0].catformat == DVO_FORMAT_LONEOS)) { // special case for LONEOS
    if (catalog[0].secfilt == NULL) { 
      fprintf (stderr, "missing secfilt, cannot build output averages (dvo_catalog_split.c:544)\n");
      exit (1);
    }
    secfilt = catalog[0].secfilt;

    Nallfilt = catalog[0].Nsecfilt;
    Nsecfilt = catalog[0].Nsecfilt - 1;
    Ntotal = Nsecfilt * catalog[0].Naverage;
    ALLOCATE (primary, SecFilt, catalog[0].Naverage);
    ALLOCATE (secfilt, SecFilt, Ntotal);

    for (i = 0; i < catalog[0].Naverage; i++) {
      primary[i] = secfilt[i*Nallfilt + 0];
      for (j = 0; j < Nsecfilt; j++) {
	secfilt[i*Nsecfilt + j] = catalog[0].secfilt[i*Nallfilt + j + 1];
      }
    }		
  } else {
    primary = NULL;
    secfilt = catalog[0].secfilt;
    Nsecfilt = catalog[0].Nsecfilt;
  }

  /* make sure header is consistent with data */
  gfits_modify (&catalog[0].header, "NSTARS",   "%d", 1, catalog[0].Naverage);
  gfits_modify (&catalog[0].header, "NMEAS",    "%d", 1, catalog[0].Nmeasure + catalog[0].Nmeas_off);
  gfits_modify (&catalog[0].header, "NMISS",    "%d", 1, catalog[0].Nmissing);
  gfits_modify (&catalog[0].header, "NSECFILT", "%d", 1, Nsecfilt);
  gfits_modify (&catalog[0].header, "EXTEND",   "%t", 1, TRUE);

  /* rewind file pointers and truncate (file is still open) */
  fseek (catalog[0].f, 0, SEEK_SET);

  /* write table PHU header - always write this out */
  /* XXX EAM : check if disk file size has changed */
  if (!gfits_fwrite_header  (catalog[0].f, &catalog[0].header)) {
    fprintf (stderr, "can't write primary header");
    goto failure;
  }

  /*** Average Table ***/
  if (catalog[0].average != NULL) {

    /* skip past matrix (already at end of header) */
    Nskip = gfits_matrix_size (&catalog[0].header);
    fseek (catalog[0].f, Nskip, SEEK_CUR);

    /* how many lines to write out? */
    Nout = catalog[0].Naverage - catalog[0].Nave_disk;

    /* write out Average table (convert to FITS table format) */
    AverageToFtable (&ftable, catalog[0].average, catalog[0].Naverage, catalog[0].catformat, primary);
    /* convert only output rows to vtable */
    gfits_table_to_vtable (&ftable, &vtable, catalog[0].Nave_disk, Nout);

    if (!gfits_fwrite_Theader (catalog[0].f, &header)) {
      fprintf (stderr, "can't write table header");
      goto failure;
    }
    if (!gfits_fwrite_vtable (catalog[0].f, &vtable)) {
      fprintf (stderr, "can't write table data");
      goto failure;
    }
    gfits_free_vtable (&vtable);
    gfits_free_table (&ftable);
    gfits_free_header (&header);
  }

  /*** Measure Table ***/
  if (catalog[0].measure != NULL) {

    catfile = catalog[0].measure_catalog;

    /* skip past PHU header and matrix */
    Nskip = catfile[0].header.size + gfits_matrix_size (&catfile[0].header);
    fseek (catfile[0].f, Nskip, SEEK_SET);

    Ndisk  = catalog[0].Nmeas_disk;
    Nstart = catalog[0].Nmeas_disk - catalog[0].Nmeas_off; /* where is first new line? */
    Nout   = catalog[0].Nmeasure - Nstart;                   /* how many lines to write out? */
    Nlines = catalog[0].Nmeasure + catalog[0].Nmeas_off;   /* how many lines total in file */

    /* convert to output format FITS table (only rows for output : 0 - Nout) */
    MeasureToFtable (&ftable, &catalog[0].measure[Nstart], Nout, catalog[0].catformat);

    gfits_scan (&header, "NAXIS1", "%d", 1, &Nx);
    gfits_scan (&header, "NAXIS2", "%d", 1, &Ny);

    /* convert all output rows to vtable */
    ALLOCATE (vtable.row, int, MAX (1, Nout));
    ALLOCATE (vtable.buffer, char *, MAX (1, Nout));
    for (i = 0; i < Nout; i++) {
      ALLOCATE (vtable.buffer[i], char, MAX (1, Nx));
      memcpy (vtable.buffer[i], &ftable.buffer[i*Nx], Nx);
      vtable.row[i] = i + Ndisk;
    }

    /* modify vtable to represent full disk table */
    gfits_modify (&header, "NAXIS2", "%d", 1, Nlines);
    header.Naxis[1] = Nlines;

    vtable.size = gfits_matrix_size (&header);
    vtable.Nrow = Nout;
    vtable.pad = vtable.size - Nx*Ny;

    if (!gfits_fwrite_Theader (catfile[0].f, &header)) {
      fprintf (stderr, "can't write table header");
      goto failure;
    }
    if (!gfits_fwrite_vtable (catfile[0].f, &vtable)) {
      fprintf (stderr, "can't write table data");
      goto failure;
    }
    gfits_free_vtable (&vtable);
    gfits_free_table (&ftable);
    gfits_free_header (&header);
  }

  /*** Missing Table ***/
  /* missing table CANNOT be written unsorted, thus it is always written 
     out in full */

  if (catalog[0].missing != NULL) {

    catfile = catalog[0].missing_catalog;

    /* rewind file pointers and truncate (file is still open) */
    fseek (catfile[0].f, 0, SEEK_SET);
    ftruncate (fileno (catfile[0].f), 0);

    /* write table PHU header */
    if (!gfits_fwrite_header  (catfile[0].f, &catfile[0].header)) {
      fprintf (stderr, "can't write primary header");
      goto failure;
    }

    /* this is probably a NOP, do I have to keep it in? */
    gfits_create_matrix (&catfile[0].header, &matrix);
    if (!gfits_fwrite_matrix  (catfile[0].f, &matrix)) {
      fprintf (stderr, "can't write primary matrix");
      goto failure;
    }
    gfits_free_matrix (&matrix);

    /* write out Missing table (convert to FITS table format) */
    gfits_table_set_Missing (&ftable, catalog[0].missing, catalog[0].Nmissing);
    if (!gfits_fwrite_Theader (catfile[0].f, &header)) {
      fprintf (stderr, "can't write table header");
      goto failure;
    }
    if (!gfits_fwrite_table (catfile[0].f, &ftable)) {
      fprintf (stderr, "can't write table data");
      goto failure;
    }
    gfits_free_table (&ftable);
    gfits_free_header (&header);
  }

  /*** Secfilt Table ***/
  if (catalog[0].secfilt != NULL) {

    catfile = catalog[0].secfilt_catalog;

    /* skip past PHU header and matrix */
    Nskip = catfile[0].header.size + gfits_matrix_size (&catfile[0].header);
    fseek (catfile[0].f, Nskip, SEEK_SET);

    /* how many lines to write out? */
    Nout  = catalog[0].Nsecfilt * (catalog[0].Naverage - catalog[0].Nave_disk);
    Ndisk = catalog[0].Nsecfilt * catalog[0].Nave_disk;

    /* convert to output format FITS table */
    Nitems = catalog[0].Naverage * catalog[0].Nsecfilt;
    SecFiltToFtable (&ftable, secfilt, Nitems, catalog[0].catformat);
    /* convert only output rows to vtable */
    gfits_table_to_vtable (&ftable, &vtable, Ndisk, Nout);

    if (!gfits_fwrite_Theader (catfile[0].f, &header)) {
      fprintf (stderr, "can't write table header");
      goto failure;
    }
    if (!gfits_fwrite_vtable (catfile[0].f, &vtable)) {
      fprintf (stderr, "can't write table data");
      goto failure;
    }
    gfits_free_vtable (&vtable);
    gfits_free_table (&ftable);
    gfits_free_header (&header);
  }

  /* free temp storage */
  if (primary != NULL) {
    free (primary);
    free (secfilt);
  }

  return (TRUE);

failure:
  /* free temp storage */
  if (primary != NULL) {
    free (primary);
    free (secfilt);
  }
  return (FALSE);
}

/* in split mode, extra files are linked to catalog->measure_catalog, etc.  Each
   has a valid filename, f, header.  The primary catalog data pointers are set
   to point at the corresponding entries from the elements on the chain. An
   unloaded entry has a null pointer here.
*/

/* XXX EAM : this file needs work on the error exit conditions and memory leaks, esp under errors */

/* XXX EAM : update is not efficient.  MeasureToFtable should only 
   convert the new rows (Nmeas_disk to Nmeasure). the resulting
   table represents the end rows of the vtable.  we need to define
   the vtable based on the ftable, but with Ny = Nmeasure */  
  
