Index: /branches/eam_branches/ipp-20140904/Ohana/src/dvomerge/Makefile
===================================================================
--- /branches/eam_branches/ipp-20140904/Ohana/src/dvomerge/Makefile	(revision 37488)
+++ /branches/eam_branches/ipp-20140904/Ohana/src/dvomerge/Makefile	(revision 37489)
@@ -23,5 +23,7 @@
 dvoverify_client : $(BIN)/dvoverify_client.$(ARCH)
 
-all: dvomerge dvomerge_client dvoconvert dvosecfilt dvosecfilt_client dvorepair dvoverify dvoverify_client
+dvoutils        : $(BIN)/dvoutils.$(ARCH)
+
+all: dvomerge dvomerge_client dvoconvert dvosecfilt dvosecfilt_client dvorepair dvoverify dvoverify_client dvoutils
 
 #  $(SRC)/dvomergeContinue.$(ARCH).o
@@ -149,5 +151,13 @@
 $(BIN)/dvoverify_client.$(ARCH) : $(DVOVERIFY_CLIENT)
 
-INSTALL = dvomerge dvomerge_client dvoconvert dvosecfilt dvosecfilt_client dvorepair dvoverify dvoverify_client
+DVOUTILS = \
+$(SRC)/dvoutils.$(ARCH).o \
+$(SRC)/dvoutils_args.$(ARCH).o \
+$(SRC)/dvoutils_uniq_images.$(ARCH).o
+
+$(DVOUTILS)  : $(INC)/dvoutils.h
+$(BIN)/dvoutils.$(ARCH) : $(DVOUTILS)
+
+INSTALL = dvomerge dvomerge_client dvoconvert dvosecfilt dvosecfilt_client dvorepair dvoverify dvoverify_client dvoverify
 
 # dependancy rules for binary code #########################
Index: /branches/eam_branches/ipp-20140904/Ohana/src/dvomerge/include/dvoutils.h
===================================================================
--- /branches/eam_branches/ipp-20140904/Ohana/src/dvomerge/include/dvoutils.h	(revision 37489)
+++ /branches/eam_branches/ipp-20140904/Ohana/src/dvomerge/include/dvoutils.h	(revision 37489)
@@ -0,0 +1,31 @@
+# include <ohana.h>
+# include <dvo.h>
+# include <signal.h>
+# include <sys/time.h>
+# include <time.h>
+# include <zlib.h>
+
+/* solaris requires both of these instead of ip.h:
+   # include <sys/socket.h>
+   # include <netinet/in.h>
+*/
+
+/* linux is happy with this, not solaris */
+# include <netinet/ip.h>
+# include <netdb.h>
+# include <arpa/inet.h>
+# include <glob.h>
+
+typedef enum {
+  DVOUTILS_NONE,
+  DVOUTILS_UNIQ_IMAGES,
+} DVOUTILS_OP_TYPE;
+
+int VERBOSE;
+int DVOUTILS_OP;
+
+char *IMAGES_LIST;
+
+int main (int argc, char **argv);
+int dvoutils_args (int *argc, char **argv);
+int dvoutils_uniq_images (char *filename);
Index: /branches/eam_branches/ipp-20140904/Ohana/src/dvomerge/src/dvoutils.c
===================================================================
--- /branches/eam_branches/ipp-20140904/Ohana/src/dvomerge/src/dvoutils.c	(revision 37489)
+++ /branches/eam_branches/ipp-20140904/Ohana/src/dvomerge/src/dvoutils.c	(revision 37489)
@@ -0,0 +1,20 @@
+# include "dvoutils.h"
+
+int main (int argc, char **argv) {
+
+  // check various options
+  dvoutils_args (&argc, argv);
+
+  switch (DVOUTILS_OP) {
+    
+    case DVOUTILS_UNIQ_IMAGES:
+      dvoutils_uniq_images(IMAGES_LIST);
+      exit (0);
+
+    default:
+      fprintf (stderr, "ERROR: unknown option\n");
+      exit (2);
+  }
+
+  exit (2);
+}
Index: /branches/eam_branches/ipp-20140904/Ohana/src/dvomerge/src/dvoutils_args.c
===================================================================
--- /branches/eam_branches/ipp-20140904/Ohana/src/dvomerge/src/dvoutils_args.c	(revision 37489)
+++ /branches/eam_branches/ipp-20140904/Ohana/src/dvomerge/src/dvoutils_args.c	(revision 37489)
@@ -0,0 +1,37 @@
+# include "dvoutils.h"
+
+int dvoutils_args (int *argc, char **argv) {
+
+  int N;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (*argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, argc, argv);
+  }
+  if ((N = get_argument (*argc, argv, "-verbose"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  DVOUTILS_OP = DVOUTILS_NONE;
+  if ((N = get_argument (*argc, argv, "-uniq-images"))) {
+    DVOUTILS_OP = DVOUTILS_UNIQ_IMAGES;
+    remove_argument (N, argc, argv);
+    IMAGES_LIST = strcreate (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  if (DVOUTILS_OP == DVOUTILS_NONE) {
+    fprintf (stderr, "ERROR: no valid mode selected\n");
+    exit (2);
+  }
+
+  if (*argc != 1) {
+    fprintf (stderr, "USAGE: dvoutils -uniq-images (filename) [-v,-verbose]\n\n");
+    fprintf (stderr, "  -v : VERBOSE\n");
+    exit (2);
+  }
+
+  return TRUE;
+}
Index: /branches/eam_branches/ipp-20140904/Ohana/src/dvomerge/src/dvoutils_uniq_images.c
===================================================================
--- /branches/eam_branches/ipp-20140904/Ohana/src/dvomerge/src/dvoutils_uniq_images.c	(revision 37489)
+++ /branches/eam_branches/ipp-20140904/Ohana/src/dvomerge/src/dvoutils_uniq_images.c	(revision 37489)
@@ -0,0 +1,154 @@
+# include "dvoutils.h"
+
+# ifndef MAX_INT
+# define MAX_INT 2147483647
+# endif
+
+int dvoutils_uniq_images(char *filename) {
+
+  // given a list of Images.dat tables, find the unique subset of extern_id
+  // format of the input list is just (dirname)/Images.dat.  commented lines
+  // starting with # are allowed
+
+  FILE *f = fopen (filename, "r");
+  if (!f) {
+    fprintf (stderr, "cannot read input list %s\n", filename);
+    exit (1);
+  }
+
+  int minID = -1;
+  int maxID = -1;
+  int *IDlist = NULL;
+  
+  char imFile[1024];
+  while (scan_line_maxlen (f, imFile, 1024) != EOF) {
+
+    stripwhite (imFile);
+
+    if (imFile[0] == '#') continue;
+
+    if (strchr(imFile, ' ') != NULL) {
+      fprintf (stderr, "line has extra spaces\n%s\n", imFile);
+      exit (2);
+    }
+
+    FITS_DB db;
+
+    gfits_db_init (&db);
+    db.lockstate = LCK_SOFT;
+    db.timeout   = 120.0;
+
+    if (!gfits_db_lock (&db, imFile)) {
+      fprintf (stderr, "error opening image catalog %s (1)\n", imFile);
+      exit (3);
+    }
+
+    if (db.dbstate == LCK_EMPTY) {
+      fprintf (stderr, "note: image catalog is empty\n");
+      // free db
+      continue;
+    }
+
+    int status = dvo_image_load (&db, TRUE, FALSE);
+    gfits_db_close (&db);
+    
+    if (!status) {
+      fprintf (stderr, "problem loading image database table\n");
+      exit (4);
+    }
+
+    off_t Nimage;
+    Image *image = gfits_table_get_Image (&db.ftable, &Nimage, &db.swapped);
+    if (!image) {
+      fprintf (stderr, "ERROR: failed to read images\n");
+      exit (5);
+    }
+
+    // now loop over all of the images and accumulate the array of extern_id values (skip
+    // the ones with extern_id == 0)
+
+    // things I need to track:
+    // minID, maxID (only allocate an array of length maxID - minID + 1 + padding)
+
+    int myMaxID = 0;
+    int myMinID = MAX_INT;
+
+    int i;
+    for (i = 0; i < Nimage; i++) {
+      if (!image[i].externID) continue;
+      myMaxID = MAX(image[i].externID, myMaxID);
+      myMinID = MIN(image[i].externID, myMinID);
+    }
+
+    fprintf (stderr, "read %s, %d images, %d - %d vs %d - %d\n", imFile, (int) Nimage, myMinID, myMaxID, minID, maxID);
+
+    // if this is new, we treat it a bit differently
+    if (!IDlist) {
+      int Nindex = myMaxID - myMinID + 1;
+      ALLOCATE (IDlist, int, Nindex);
+      memset (IDlist, 0, Nindex*sizeof(int));
+      minID = myMinID;
+      maxID = myMaxID;
+    } else {
+      int newMinID = MIN(myMinID, minID);
+      int newMaxID = MAX(myMaxID, maxID);
+
+      int Nindex = newMaxID - newMinID + 1;
+
+      int oldNindex = maxID - minID + 1;
+      myAssert (oldNindex <= Nindex, "impossible!");
+
+      int *newIDlist = NULL;
+      ALLOCATE (newIDlist, int, Nindex);
+      memset (newIDlist, 0, Nindex*sizeof(int));
+      
+      // if myMinID >= minID, newMinID = minID -> offset = 0
+      // if myMinID <  minID, newMinID = myMinID -> offset = minID - myMinID
+      int offset = minID - newMinID;
+
+      // save the old values
+      memcpy (&newIDlist[offset], IDlist, oldNindex*sizeof(int));
+
+      free (IDlist);
+      IDlist = newIDlist;
+
+      minID = newMinID;
+      maxID = newMaxID;
+    }
+
+    for (i = 0; i < Nimage; i++) {
+      if (!image[i].externID) continue;
+      int n = image[i].externID - minID;
+      IDlist[n] ++;
+    }
+
+    if (0) {
+      fprintf (stderr, "------------ %s ----------\n", imFile);
+
+      for (i = 0; i < Nimage; i++) {
+	if (!image[i].externID) continue;
+	fprintf (stderr, "%d => %d\n", image[i].externID, image[i].externID - minID);
+      }
+
+      int Nindex = maxID - minID + 1;
+      
+      for (i = 0; i < Nindex; i++) {
+	if (!IDlist[i]) continue;
+	fprintf (stderr, "%d : %d : %d\n", i, i + minID, IDlist[i]);
+      }
+    }
+  }
+
+  fprintf (stderr, "duplicates:\n");
+
+  int Nindex = maxID - minID + 1;
+
+  int i;
+  for (i = 0; i < Nindex; i++) {
+    if (IDlist[i] < 2) continue;
+    fprintf (stderr, "%d : %d\n", i + minID, IDlist[i]);
+  }
+
+  exit (0);
+}
+
