e5c786377cee95f737ff9d4bbada817e14f94aa4
angie
  Mon Dec 12 09:25:06 2016 -0800
Better cookies and validation for hgLogin: instead of sending gbMembers.idx as
the login cookie and then never checking the value of the incoming cookie, use
a salted hash.  The salt is a secret text value specified by login.cookieSalt
in hg.conf.private.  For remote login, both hosts' hg.conf.private files must
specify the same login.cookieSalt.  In order to avoid logging out all users,
for now the correct value of gbMembers.idx is accepted in place of the salted
hash for local logins.  For remote logins without login.cookieSalt, there is
still no way to check the incoming cookie.  For local logins without
login.cookieSalt, the correct gbMembers.idx is accepted.
refs #17327

diff --git src/lib/common.c src/lib/common.c
index b44b3ee..3c48e49 100644
--- src/lib/common.c
+++ src/lib/common.c
@@ -1,3616 +1,3619 @@
 /* Commonly used routines in a wide range of applications.
  * Strings, singly-linked lists, and a little file i/o.
  *
  * This file is copyright 2002 Jim Kent, but license is hereby
  * granted for all use - public, private or commercial. */
 
 #include "common.h"
 #include "errAbort.h"
 #include "portable.h"
 #include "linefile.h"
 #include "hash.h"
 
 
 void *cloneMem(void *pt, size_t size)
 /* Allocate a new buffer of given size, and copy pt to it. */
 {
 void *newPt = needLargeMem(size);
 memcpy(newPt, pt, size);
 return newPt;
 }
 
 static char *cloneStringZExt(const char *s, int size, int copySize)
 /* Make a zero terminated copy of string in memory */
 {
 char *d = needMem(copySize+1);
 copySize = min(size,copySize);
 memcpy(d, s, copySize);
 d[copySize] = 0;
 return d;
 }
 
 char *cloneStringZ(const char *s, int size)
 /* Make a zero terminated copy of string in memory */
 {
 return cloneStringZExt(s, strlen(s), size);
 }
 
 char *cloneString(const char *s)
 /* Make copy of string in dynamic memory */
 {
 int size = 0;
 if (s == NULL)
     return NULL;
 size = strlen(s);
 return cloneStringZExt(s, size, size);
 }
 
 char *cloneLongString(char *s)
 /* Make clone of long string. */
 {
 size_t size = strlen(s);
 return cloneMem(s, size+1);
 }
 
 char *catTwoStrings(char *a, char *b)
 /* Allocate new string that is a concatenation of two strings. */
 {
 int aLen = strlen(a), bLen = strlen(b);
 int len = aLen + bLen;
 char *newBuf = needLargeMem(len+1);
 memcpy(newBuf, a, aLen);
 memcpy(newBuf+aLen, b, bLen);
 newBuf[len] = 0;
 return newBuf;
 }
 
 /* Reverse the order of the bytes. */
 void reverseBytes(char *bytes, long length)
 {
 long halfLen = (length>>1);
 char *end = bytes+length;
 char c;
 while (--halfLen >= 0)
     {
     c = *bytes;
     *bytes++ = *--end;
     *end = c;
     }
 }
 
 void reverseInts(int *a, int length)
 /* Reverse the order of the integer array. */
 {
 int halfLen = (length>>1);
 int *end = a+length;
 int c;
 while (--halfLen >= 0)
     {
     c = *a;
     *a++ = *--end;
     *end = c;
     }
 }
 
 void reverseUnsigned(unsigned *a, int length)
 /* Reverse the order of the unsigned array. */
 {
 int halfLen = (length>>1);
 unsigned *end = a+length;
 unsigned c;
 while (--halfLen >= 0)
     {
     c = *a;
     *a++ = *--end;
     *end = c;
     }
 }
 
 void reverseDoubles(double *a, int length)
 /* Reverse the order of the double array. */
 {
 int halfLen = (length>>1);
 double *end = a+length;
 double c;
 while (--halfLen >= 0)
     {
     c = *a;
     *a++ = *--end;
     *end = c;
     }
 }
 
 void reverseStrings(char **a, int length)
 /* Reverse the order of the char* array. */
 {
 int halfLen = (length>>1);
 char **end = a+length;
 char *c;
 while (--halfLen >= 0)
     {
     c = *a;
     *a++ = *--end;
     *end = c;
     }
 }
 
 /* Swap buffers a and b. */
 void swapBytes(char *a, char *b, int length)
 {
 char c;
 int i;
 
 for (i=0; i<length; ++i)
     {
     c = a[i];
     a[i] = b[i];
     b[i] = c;
     }
 }
 
 
 /** List managing routines. */
 
 /* Count up elements in list. */
 int slCount(const void *list)
 {
 struct slList *pt = (struct slList *)list;
 int len = 0;
 
 while (pt != NULL)
     {
     len += 1;
     pt = pt->next;
     }
 return len;
 }
 
 void *slElementFromIx(void *list, int ix)
 /* Return the ix'th element in list.  Returns NULL
  * if no such element. */
 {
 struct slList *pt = (struct slList *)list;
 int i;
 for (i=0;i<ix;i++)
     {
     if (pt == NULL) return NULL;
     pt = pt->next;
     }
 return pt;
 }
 
 int slIxFromElement(void *list, void *el)
 /* Return index of el in list.  Returns -1 if not on list. */
 {
 struct slList *pt;
 int ix = 0;
 
 for (pt = list, ix=0; pt != NULL; pt = pt->next, ++ix)
     if (el == (void*)pt)
 	return ix;
 return -1;
 }
 
 void *slLastEl(void *list)
 /* Returns last element in list or NULL if none. */
 {
 struct slList *next, *el;
 if ((el = list) == NULL)
     return NULL;
 while ((next = el->next) != NULL)
     el = next;
 return el;
 }
 
 /* Add new node to tail of list.
  * Usage:
  *    slAddTail(&list, node);
  * where list and nodes are both pointers to structure
  * that begin with a next pointer.
  */
 void slAddTail(void *listPt, void *node)
 {
 struct slList **ppt = (struct slList **)listPt;
 struct slList *n = (struct slList *)node;
 
 while (*ppt != NULL)
     {
     ppt = &((*ppt)->next);
     }
 n->next = NULL;
 *ppt = n;
 }
 
 void *slPopHead(void *vListPt)
 /* Return head of list and remove it from list. (Fast) */
 {
 struct slList **listPt = (struct slList **)vListPt;
 struct slList *el = *listPt;
 if (el != NULL)
     {
     *listPt = el->next;
     el->next = NULL;
     }
 return el;
 }
 
 void *slPopTail(void *vListPt)
 /* Return tail of list and remove it from list. (Not so fast) */
 {
 struct slList **listPt = (struct slList **)vListPt;
 struct slList *el = *listPt;
 if (el != NULL)
     {
     for (;;)
         {
         if (el->next == NULL)
             {
             *listPt = NULL;
             break;
             }
         listPt = &el->next;
         el = el->next;
         }
     }
 return el;
 }
 
 
 
 void *slCat(void *va, void *vb)
 /* Return concatenation of lists a and b.
  * Example Usage:
  *   struct slName *a = getNames("a");
  *   struct slName *b = getNames("b");
  *   struct slName *ab = slCat(a,b)
  */
 {
 struct slList *a = va;
 struct slList *b = vb;
 struct slList *end;
 if (a == NULL)
     return b;
 for (end = a; end->next != NULL; end = end->next)
     ;
 end->next = b;
 return a;
 }
 
 void slReverse(void *listPt)
 /* Reverse order of a list.
  * Usage:
  *    slReverse(&list);
  */
 {
 struct slList **ppt = (struct slList **)listPt;
 struct slList *newList = NULL;
 struct slList *el, *next;
 
 next = *ppt;
 while (next != NULL)
     {
     el = next;
     next = el->next;
     el->next = newList;
     newList = el;
     }
 *ppt = newList;
 }
 
 void slFreeList(void *listPt)
 /* Free list */
 {
 struct slList **ppt = (struct slList**)listPt;
 struct slList *next = *ppt;
 struct slList *el;
 
 while (next != NULL)
     {
     el = next;
     next = el->next;
     freeMem((char*)el);
     }
 *ppt = NULL;
 }
 
 void slSort(void *pList, int (*compare )(const void *elem1,  const void *elem2))
 /* Sort a singly linked list with Qsort and a temporary array. */
 {
 struct slList **pL = (struct slList **)pList;
 struct slList *list = *pL;
 int count;
 count = slCount(list);
 if (count > 1)
     {
     struct slList *el;
     struct slList **array;
     int i;
     array = needLargeMem(count * sizeof(*array));
     for (el = list, i=0; el != NULL; el = el->next, i++)
         array[i] = el;
     qsort(array, count, sizeof(array[0]), compare);
     list = NULL;
     for (i=0; i<count; ++i)
         {
         array[i]->next = list;
         list = array[i];
         }
     freeMem(array);
     slReverse(&list);
     *pL = list;
     }
 }
 
 void slUniqify(void *pList, int (*compare )(const void *elem1,  const void *elem2), void (*free)())
 /* Return sorted list with duplicates removed.
  * Compare should be same type of function as slSort's compare (taking
  * pointers to pointers to elements.  Free should take a simple
  * pointer to dispose of duplicate element, and can be NULL. */
 {
 struct slList **pSlList = (struct slList **)pList;
 struct slList *oldList = *pSlList;
 struct slList *newList = NULL, *el;
 
 slSort(&oldList, compare);
 while ((el = slPopHead(&oldList)) != NULL)
     {
     if ((newList == NULL) || (compare(&newList, &el) != 0))
         slAddHead(&newList, el);
     else if (free != NULL)
         free(el);
     }
 slReverse(&newList);
 *pSlList = newList;
 }
 
 void slSortMerge(void *pA, void *b, CmpFunction *compare)
 // Merges and sorts a pair of singly linked lists using slSort.
 {
 struct slList **pList = (struct slList **)pA;
 slCat(*pList, b);
 slSort(pList,compare);
 }
 
 void slSortMergeUniq(void *pA, void *b, CmpFunction *compare, void (*free)())
 // Merges and sorts a pair of singly linked lists leaving only unique
 // items via slUniqufy.  duplicate itens are defined by the compare routine
 // returning 0. If free is provided, items dropped from list can disposed of.
 {
 struct slList **pList = (struct slList **)pA;
 *pList = slCat(*pList, b);
 slUniqify(pList,compare,free);
 }
 
 boolean slRemoveEl(void *vpList, void *vToRemove)
 /* Remove element from singly linked list.  Usage:
  *    slRemove(&list, el);
  * Returns TRUE if element in list.  */
 {
 struct slList **pList = vpList;
 struct slList *toRemove = vToRemove;
 struct slList *el, *next, *newList = NULL;
 boolean didRemove = FALSE;
 
 for (el = *pList; el != NULL; el = next)
     {
     next = el->next;
     if (el != toRemove)
 	{
 	slAddHead(&newList, el);
 	}
     else
         didRemove = TRUE;
     }
 slReverse(&newList);
 *pList = newList;
 return didRemove;
 }
 
 struct slInt *slIntNew(int x)
 /* Return a new int. */
 {
 struct slInt *a;
 AllocVar(a);
 a->val = x;
 return a;
 }
 
 int slIntCmp(const void *va, const void *vb)
 /* Compare two slInts. */
 {
 const struct slInt *a = *((struct slInt **)va);
 const struct slInt *b = *((struct slInt **)vb);
 return a->val - b->val;
 }
 
 int slIntCmpRev(const void *va, const void *vb)
 /* Compare two slInts in reverse direction. */
 {
 const struct slInt *a = *((struct slInt **)va);
 const struct slInt *b = *((struct slInt **)vb);
 return b->val - a->val;
 }
 
 struct slInt * slIntFind(struct slInt *list, int target)
 /* Find target in slInt list or return NULL */
 {
 struct slInt *i;
 for (i=list;i;i=i->next)
     if (i->val == target)
 	return i;
 return NULL;
 }
 
 struct slUnsigned *slUnsignedNew(unsigned x)
 /* Return a new int. */
 {
 struct slUnsigned *a;
 AllocVar(a);
 a->val = x;
 return a;
 }
 
 static int doubleCmp(const void *va, const void *vb)
 /* Compare function to sort array of doubles. */
 {
 const double *a = va;
 const double *b = vb;
 double diff = *a - *b;
 if (diff < 0)
     return -1;
 else if (diff > 0)
     return 1;
 else
     return 0;
 }
 
 void doubleSort(int count, double *array)
 /* Sort an array of doubles. */
 {
 if (count > 1)
 qsort(array, count, sizeof(array[0]), doubleCmp);
 }
 
 double doubleMedian(int count, double *array)
 /* Return median value in array.  This will sort
  * the array as a side effect. */
 {
 double median;
 doubleSort(count, array);
 if ((count&1) == 1)
     median = array[count>>1];
 else
     {
     count >>= 1;
     median = (array[count] + array[count-1]) * 0.5;
     }
 return median;
 }
 
 void doubleBoxWhiskerCalc(int count, double *array, double *retMin,
                           double *retQ1, double *retMedian, double *retQ3, double *retMax)
 /* Calculate what you need to draw a box and whiskers plot from an array of doubles. */
 {
 if (count <= 0)
     errAbort("doubleBoxWhiskerCalc needs a positive number, not %d for count", count);
 if (count == 1)
     {
     *retMin = *retQ1 = *retMedian = *retQ3 = *retMax = array[0];
     return;
     }
 doubleSort(count, array);
 double min = array[0];
 double max = array[count-1];
 double median;
 int halfCount = count>>1;
 if ((count&1) == 1)
     median = array[halfCount];
 else
     {
     median = (array[halfCount] + array[halfCount-1]) * 0.5;
     }
 double q1, q3;
 if (count <= 3)
     {
     q1 = 0.5 * (median + min);
     q3 = 0.5 * (median + max);
     }
 else
     {
     int q1Ix = count/4;
     int q3Ix = count - 1 - q1Ix;
     verbose(4, "count %d, q1Ix %d, q3Ix %d\n", count, q1Ix, q3Ix);
     q1 = array[q1Ix];
     q3 = array[q3Ix];
     }
 *retMin = min;
 *retQ1 = q1;
 *retMedian = median;
 *retQ3 = q3;
 *retMax = max;
 }
 
 struct slDouble *slDoubleNew(double x)
 /* Return a new double. */
 {
 struct slDouble *a;
 AllocVar(a);
 a->val = x;
 return a;
 }
 
 int slDoubleCmp(const void *va, const void *vb)
 /* Compare two slDoubles. */
 {
 const struct slDouble *a = *((struct slDouble **)va);
 const struct slDouble *b = *((struct slDouble **)vb);
 double diff = a->val - b->val;
 if (diff < 0)
     return -1;
 else if (diff > 0)
     return 1;
 else
     return 0;
 }
 
 double slDoubleMedian(struct slDouble *list)
 /* Return median value on list. */
 {
 int i,count = slCount(list);
 struct slDouble *el;
 double *array, median;
 if (count == 0)
     errAbort("Can't take median of empty list");
 AllocArray(array,count);
 for (i=0, el=list; i<count; ++i, el=el->next)
     array[i] = el->val;
 median = doubleMedian(count, array);
 freeMem(array);
 return median;
 }
 
 void slDoubleBoxWhiskerCalc(struct slDouble *list, double *retMin,
                             double *retQ1, double *retMedian, double *retQ3, double *retMax)
 /* Calculate what you need to draw a box and whiskers plot from a list of slDoubles. */
 {
 int i,count = slCount(list);
 struct slDouble *el;
 double *array;
 if (count == 0)
     errAbort("Can't take do slDoubleBoxWhiskerCalc of empty list");
 AllocArray(array,count);
 for (i=0, el=list; i<count; ++i, el=el->next)
     array[i] = el->val;
 doubleBoxWhiskerCalc(count, array, retMin, retQ1, retMedian, retQ3, retMax);
 freeMem(array);
 }
 
 static int intCmp(const void *va, const void *vb)
 /* Compare function to sort array of ints. */
 {
 const int *a = va;
 const int *b = vb;
 int diff = *a - *b;
 if (diff < 0)
     return -1;
 else if (diff > 0)
     return 1;
 else
     return 0;
 }
 
 void intSort(int count, int *array)
 /* Sort an array of ints. */
 {
 if (count > 1)
 qsort(array, count, sizeof(array[0]), intCmp);
 }
 
 int intMedian(int count, int *array)
 /* Return median value in array.  This will sort
  * the array as a side effect. */
 {
 int median;
 intSort(count, array);
 if ((count&1) == 1)
     median = array[count>>1];
 else
     {
     count >>= 1;
     median = (array[count] + array[count-1]) * 0.5;
     }
 return median;
 }
 
 
 struct slName *newSlName(char *name)
 /* Return a new name. */
 {
 struct slName *sn;
 if (name != NULL)
     {
     int len = strlen(name);
     sn = needMem(sizeof(*sn)+len);
     strcpy(sn->name, name);
     return sn;
     }
 else
     {
     AllocVar(sn);
     }
 return sn;
 }
 
 struct slName *slNameNewN(char *name, int size)
 /* Return new slName of given size. */
 {
 struct slName *sn = needMem(sizeof(*sn) + size);
 memcpy(sn->name, name, size);
 return sn;
 }
 
 int slNameCmpCase(const void *va, const void *vb)
 /* Compare two slNames, ignore case. */
 {
 const struct slName *a = *((struct slName **)va);
 const struct slName *b = *((struct slName **)vb);
 return strcasecmp(a->name, b->name);
 }
 
 void slNameSortCase(struct slName **pList)
 /* Sort slName list, ignore case. */
 {
 slSort(pList, slNameCmpCase);
 }
 
 int slNameCmp(const void *va, const void *vb)
 /* Compare two slNames. */
 {
 const struct slName *a = *((struct slName **)va);
 const struct slName *b = *((struct slName **)vb);
 return strcmp(a->name, b->name);
 }
 
 int slNameCmpStringsWithEmbeddedNumbers(const void *va, const void *vb)
 /* Compare strings such as gene names that may have embedded numbers,
  * so that bmp4a comes before bmp14a */
 {
 const struct slName *a = *((struct slName **)va);
 const struct slName *b = *((struct slName **)vb);
 return cmpStringsWithEmbeddedNumbers(a->name, b->name);
 }
 
 
 
 void slNameSort(struct slName **pList)
 /* Sort slName list. */
 {
 slSort(pList, slNameCmp);
 }
 
 boolean slNameInList(struct slName *list, char *string)
 /* Return true if string is in name list -- case insensitive. */
 {
 struct slName *el;
 for (el = list; el != NULL; el = el->next)
     if (sameWord(string, el->name))
         return TRUE;
 return FALSE;
 }
 
 boolean slNameInListUseCase(struct slName *list, char *string)
 /* Return true if string is in name list -- case sensitive. */
 {
 struct slName *el;
 for (el = list; el != NULL; el = el->next)
     if (string != NULL && !strcmp(string, el->name))
         return TRUE;
 return FALSE;
 }
 
 void *slNameFind(void *list, char *string)
 /* Return first element of slName list (or any other list starting
  * with next/name fields) that matches string. */
 {
 struct slName *el;
 for (el = list; el != NULL; el = el->next)
     if (sameWord(string, el->name))
         return el;
 return NULL;
 }
 
 int slNameFindIx(struct slName *list, char *string)
 /* Return index of first element of slName list (or any other
  * list starting with next/name fields) that matches string.
  * Return -1 if not found. */
 {
 struct slName *el;
 int ix = 0;
 for (el = list; el != NULL; el = el->next, ix++)
     if (sameString(string, el->name))
         return ix;
 return -1;
 }
 
 char *slNameStore(struct slName **pList, char *string)
 /* Put string into list if it's not there already.
  * Return the version of string stored in list. */
 {
 struct slName *el;
 for (el = *pList; el != NULL; el = el->next)
     {
     if (sameString(string, el->name))
 	return el->name;
     }
 el = newSlName(string);
 slAddHead(pList, el);
 return el->name;
 }
 
 struct slName *slNameAddHead(struct slName **pList, char *name)
 /* Add name to start of list and return it. */
 {
 struct slName *el = slNameNew(name);
 slAddHead(pList, el);
 return el;
 }
 
 struct slName *slNameAddTail(struct slName **pList, char *name)
 /* Add name to end of list (not efficient for long lists),
  * and return it. */
 {
 struct slName *el = slNameNew(name);
 slAddTail(pList, el);
 return el;
 }
 
 struct slName *slNameCloneList(struct slName *list)
 /* Return clone of list. */
 {
 struct slName *el, *newEl, *newList = NULL;
 for (el = list; el != NULL; el = el->next)
     {
     newEl = slNameNew(el->name);
     slAddHead(&newList, newEl);
     }
 slReverse(&newList);
 return newList;
 }
 
 
 struct slName *slNameListFromString(char *s, char delimiter)
 /* Return list of slNames gotten from parsing delimited string.
  * The final delimiter is optional. a,b,c  and a,b,c, are equivalent
  * for comma-delimited lists. */
 {
 char *e;
 struct slName *list = NULL, *el;
 while (s != NULL && s[0] != 0)
     {
     e = strchr(s, delimiter);
     if (e == NULL)
 	el = slNameNew(s);
     else
 	{
         el = slNameNewN(s, e-s);
 	e += 1;
 	}
     slAddHead(&list, el);
     s = e;
     }
 slReverse(&list);
 return list;
 }
 
 struct slName *slNameListOfUniqueWords(char *text,boolean respectQuotes)
 // Return list of unique words found by parsing string delimited by whitespace.
 // If respectQuotes then ["Lucy and Ricky" 'Fred and Ethyl'] will yield 2 slNames no quotes
 {
 struct slName *list = NULL;
 char *word = NULL;
 while (text != NULL)
     {
     if (respectQuotes)
         {
         word = nextWordRespectingQuotes(&text);
         if (word != NULL)
             {
             if (word[0] == '"')
                 stripChar(word, '"');
             else if (word[0] == '\'')
                 stripChar(word, '\'');
             }
         }
     else
         word = nextWord(&text);
     if (word)
         slNameStore(&list, word);
     else
         break;
     }
 
 slReverse(&list);
 return list;
 }
 
 struct slName *slNameListFromStringArray(char *stringArray[], int arraySize)
 /* Return list of slNames from an array of strings of length arraySize.
  * If a string in the array is NULL, the array will be treated as
  * NULL-terminated (shorter than arraySize). */
 {
 char *s;
 struct slName *list = NULL, *el;
 int i;
 if (stringArray == NULL)
     return NULL;
 for (i = 0;  i < arraySize;  i++)
     {
     s = stringArray[i];
     if (s == NULL)
 	break;
     el = slNameNew(s);
     slAddHead(&list, el);
     }
 slReverse(&list);
 return list;
 }
 
 char *slNameListToString(struct slName *list, char delimiter)
 /* Return string created by joining all names with the delimiter. */
 {
 struct slName *el;
 int elCount = 0;
 int len = 0;
 char del[2];
 char *s;
 
+if (list == NULL)
+    return cloneString("");
+
 del[0] = delimiter;
 del[1] = '\0';
 
 for (el = list; el != NULL; el = el->next, elCount++)
 	len += strlen(el->name);
 len += elCount;
 
 AllocArray(s, len);
 
 for (el = list; el != NULL; el = el->next)
 	{
 	strcat(s, el->name);
 	if (el->next != NULL)
 		strcat(s, del);
 	}
 return s;
 }
 
 struct slName *slNameLoadReal(char *fileName)
 /* load file lines that are not blank or start with a '#' into a slName
  * list */
 {
 struct slName *lines = NULL;
 char *line;
 struct lineFile *lf = lineFileOpen(fileName, TRUE);
 while (lineFileNextReal(lf, &line))
     slSafeAddHead(&lines, slNameNew(line));
 lineFileClose(&lf);
 slReverse(&lines);
 return lines;
 }
 
 struct slName *slNameIntersection(struct slName *a, struct slName *b)
 /* return intersection of two slName lists.  */
 {
 struct hash *hashA = newHash(0);
 struct slName *el, *retval = NULL;
 
 for (el = a; el != NULL; el = el->next)
     hashAddInt(hashA, el->name, 1);
 for (el = b; el != NULL; el = el->next)
     if(hashLookup(hashA, el->name) != NULL)
         slNameAddHead(&retval, el->name);
 hashFree(&hashA);
 return retval;
 }
 
 struct slRef *refOnList(struct slRef *refList, void *val)
 /* Return ref if val is already on list, otherwise NULL. */
 {
 struct slRef *ref;
 for (ref = refList; ref != NULL; ref = ref->next)
     if (ref->val == val)
         return ref;
 return NULL;
 }
 
 struct slRef *slRefNew(void *val)
 /* Create new slRef element. */
 {
 struct slRef *ref;
 AllocVar(ref);
 ref->val = val;
 return ref;
 }
 
 void refAdd(struct slRef **pRefList, void *val)
 /* Add reference to list. */
 {
 struct slRef *ref;
 AllocVar(ref);
 ref->val = val;
 slAddHead(pRefList, ref);
 }
 
 void refAddUnique(struct slRef **pRefList, void *val)
 /* Add reference to list if not already on list. */
 {
 if (refOnList(*pRefList, val) == NULL)
     {
     refAdd(pRefList, val);
     }
 }
 
 void slRefFreeListAndVals(struct slRef **pList)
 /* Free up (with simple freeMem()) each val on list, and the list itself as well. */
 {
 struct slRef *el, *next;
 
 for (el = *pList; el != NULL; el = next)
     {
     next = el->next;
     freeMem(el->val);
     freeMem(el);
     }
 *pList = NULL;
 }
 
 struct slRef *refListFromSlList(void *list)
 /* Make a reference list that mirrors a singly-linked list. */
 {
 struct slList *el;
 struct slRef *refList = NULL, *ref;
 for (el= list; el != NULL; el = el->next)
     {
     ref = slRefNew(el);
     slAddHead(&refList, ref);
     }
 slReverse(&refList);
 return refList;
 }
 
 
 struct slPair *slPairNew(char *name, void *val)
 /* Allocate new name/value pair. */
 {
 struct slPair *el;
 AllocVar(el);
 el->name = cloneString(name);
 el->val = val;
 return el;
 }
 
 void slPairAdd(struct slPair **pList, char *name, void *val)
 /* Add new slPair to head of list. */
 {
 struct slPair *el = slPairNew(name, val);
 slAddHead(pList, el);
 }
 
 void slPairFree(struct slPair **pEl)
 /* Free up struct and name.  (Don't free up values.) */
 {
 struct slPair *el = *pEl;
 if (el != NULL)
     {
     freeMem(el->name);
     freez(pEl);
     }
 }
 
 void slPairFreeList(struct slPair **pList)
 /* Free up list.  (Don't free up values.) */
 {
 struct slPair *el, *next;
 
 for (el = *pList; el != NULL; el = next)
     {
     next = el->next;
     slPairFree(&el);
     }
 *pList = NULL;
 }
 
 void slPairFreeVals(struct slPair *list)
 /* Free up all values on list. */
 {
 struct slPair *el;
 for (el = list; el != NULL; el = el->next)
     freez(&el->val);
 }
 
 void slPairFreeValsAndList(struct slPair **pList)
 /* Free up all values on list and list itself */
 {
 slPairFreeVals(*pList);
 slPairFreeList(pList);
 }
 
 struct slPair *slPairFind(struct slPair *list, char *name)
 /* Return list element of given name, or NULL if not found. */
 {
 struct slPair *el;
 for (el = list; el != NULL; el = el->next)
     if (sameString(name, el->name))
         break;
 return el;
 }
 
 void *slPairFindVal(struct slPair *list, char *name)
 /* Return value associated with name in list, or NULL if not found. */
 {
 struct slPair *el = slPairFind(list, name);
 if (el == NULL)
     return NULL;
 return el->val;
 }
 
 struct slPair *slPairListFromString(char *str,boolean respectQuotes)
 // Return slPair list parsed from list in string like:  [name1=val1 name2=val2 ...]
 // if respectQuotes then string can have double quotes: [name1="val 1" "name 2"=val2 ...]
 //    resulting pair strips quotes: {name1}={val 1},{name 2}={val2}
 // Returns NULL if parse error.  Free this up with slPairFreeValsAndList.
 {
 char *s = skipLeadingSpaces(str);  // Would like to remove this and tighten up the standard someday.
 if (isEmpty(s))
     return NULL;
 
 struct slPair *list = NULL;
 char name[1024];
 char val[1024];
 char buf[1024];
 bool inQuote = FALSE;
 char *b = buf;
 char sep = '=';
 char c = ' ';
 int mode = 0;
 while(1)
     {
     c = *s++;
     if (mode == 0 || mode == 2) // reading name or val
 	{
 	boolean term = FALSE;
 	if (respectQuotes && b == buf && !inQuote && c == '"')
 	    inQuote = TRUE;
 	else if (inQuote && c == '"')
 	    term = TRUE;
 	else if ((c == sep || c == 0) && !inQuote)
 	    {
 	    term = TRUE;
 	    --s;  // rewind
 	    }
 	else if (c == ' ' && !inQuote)
 	    {
 	    warn("slPairListFromString: Unexpected whitespace in %s", str);
 	    return NULL;
 	    }
 	else if (c == 0 && inQuote)
 	    {
 	    warn("slPairListFromString: Unterminated quote in %s", str);
 	    return NULL;
 	    }
 	else
 	    {
 	    *b++ = c;
 	    if ((b - buf) > sizeof buf)
 		{
 		warn("slPairListFromString: pair name or value too long in %s", str);
 		return NULL;
 		}
 	    }
 	if (term)
 	    {
 	    inQuote = FALSE;
 	    *b = 0;
 	    if (mode == 0)
 		{
 		safecpy(name, sizeof name, buf);
 		if (strlen(name)<1)
 		    {
 		    warn("slPairListFromString: Pair name cannot be empty in %s", str);
 		    return NULL;
 		    }
 		// Shall we check for name being alphanumeric, at least for the respectQuotes=FALSE case?
 		}
 	    else // mode == 2
                 {
 		safecpy(val, sizeof val, buf);
 		if (!respectQuotes && (hasWhiteSpace(name) || hasWhiteSpace(val))) // should never happen
 		    {
 		    warn("slPairListFromString() Unexpected white space in name=value pair: [%s]=[%s] in string=[%s]\n", name, val, str);
 		    break;
 		    }
 		slPairAdd(&list, name, cloneString(val));
 		}
 	    ++mode;
 	    }
 	}
     else if (mode == 1) // read required "=" sign
 	{
 	if (c != '=')
 	    {
 	    warn("slPairListFromString: Expected character = after name in %s", str);
 	    return NULL;
             }
 	++mode;
 	sep = ' ';
 	b = buf;
 	}
     else // (mode == 3) reading optional separating space
 	{
 	if (c == 0)
 	    break;
 	if (c != ' ')
 	    {
 	    mode = 0;
 	    --s;
 	    b = buf;
 	    sep = '=';
 	    }
 	}
     }
 slReverse(&list);
 return list;
 }
 
 char *slPairListToString(struct slPair *list,boolean quoteIfSpaces)
 // Returns an allocated string of pairs in form of [name1=val1 name2=val2 ...]
 // If requested, will wrap name or val in quotes if contain spaces: [name1="val 1" "name 2"=val2]
 {
 // Don't rely on dyString.  We should do the accounting ourselves and not create extra dependencies.
 int count = 0;
 struct slPair *pair = list;
 for (;pair != NULL; pair = pair->next)
     {
     assert(pair->name != NULL && pair->val != NULL); // Better assert and get this over with,
                                                      // complete with stack
     count += strlen(pair->name);
     count += strlen((char *)(pair->val));
     count += 2; // = and ' ' delimit
     if (quoteIfSpaces)
         {
         if (hasWhiteSpace(pair->name))
             count += 2; // " and "
         if (hasWhiteSpace((char *)(pair->val)))
             count += 2; // " and "
         }
     }
 if (count == 0)
     return NULL;
 
 char *str = needMem(count+5); // A bit of slop
 
 char *strPtr = str;
 for (pair = list; pair != NULL; pair = pair->next, strPtr += strlen(strPtr))
     {
     if (pair != list) // Not first cycle
         *strPtr++ = ' ';
     if (hasWhiteSpace(pair->name))
         {
         if (quoteIfSpaces)
             sprintf(strPtr,"\"%s\"=",pair->name);
         else
             {
             warn("slPairListToString() Unexpected white space in name: [%s]\n", pair->name);
             sprintf(strPtr,"%s=",pair->name); // warn but still make string
             }
         }
     else
         sprintf(strPtr,"%s=",pair->name);
     strPtr += strlen(strPtr);
     if (hasWhiteSpace((char *)(pair->val)))
         {
         if (quoteIfSpaces)
             sprintf(strPtr,"\"%s\"",(char *)(pair->val));
         else
             {
             warn("slPairListToString() Unexpected white space in val: [%s]\n", (char *)(pair->val));
             sprintf(strPtr,"%s",(char *)(pair->val)); // warn but still make string
             }
         }
     else
         sprintf(strPtr,"%s",(char *)(pair->val));
     }
 return str;
 }
 
 char *slPairNameToString(struct slPair *list, char delimiter,boolean quoteIfSpaces)
 // Return string created by joining all names (ignoring vals) with the delimiter.
 // If requested, will wrap name in quotes if contain spaces: [name1,"name 2" ...]
 {
 int elCount = 0;
 int count = 0;
 struct slPair *pair = list;
 for (; pair != NULL; pair = pair->next, elCount++)
     {
     assert(pair->name != NULL);
     count += strlen(pair->name);
     if (quoteIfSpaces && hasWhiteSpace(pair->name))
         count += 2;
     }
 count += elCount;
 if (count == 0)
     return NULL;
 
 char *str = needMem(count+5); // A bit of slop
 
 char *strPtr = str;
 for (pair = list; pair != NULL; pair = pair->next, strPtr += strlen(strPtr))
     {
     if (pair != list)
         *strPtr++ = delimiter;
     if (hasWhiteSpace(pair->name))
         {
         if (quoteIfSpaces)
             sprintf(strPtr,"\"%s\"",pair->name);
         else
             {
             if (delimiter == ' ')  // if delimied by commas, this is entirely okay!
                 warn("slPairListToString() Unexpected white space in name delimited by space: "
                      "[%s]\n", pair->name);
             sprintf(strPtr,"%s",pair->name); // warn but still make string
             }
         }
     else
         sprintf(strPtr,"%s",pair->name);
     }
 return str;
 }
 
 int slPairCmpCase(const void *va, const void *vb)
 /* Compare two slPairs, ignore case. */
 {
 const struct slPair *a = *((struct slPair **)va);
 const struct slPair *b = *((struct slPair **)vb);
 return strcasecmp(a->name, b->name);
 }
 
 void slPairSortCase(struct slPair **pList)
 /* Sort slPair list, ignore case. */
 {
 slSort(pList, slPairCmpCase);
 }
 
 int slPairCmp(const void *va, const void *vb)
 /* Compare two slPairs. */
 {
 const struct slPair *a = *((struct slPair **)va);
 const struct slPair *b = *((struct slPair **)vb);
 return strcmp(a->name, b->name);
 }
 
 int slPairValCmpCase(const void *va, const void *vb)
 /* Case insensitive compare two slPairs on their values (must be string). */
 {
 const struct slPair *a = *((struct slPair **)va);
 const struct slPair *b = *((struct slPair **)vb);
 return strcasecmp((char *)(a->val), (char *)(b->val));
 }
 
 int slPairValCmp(const void *va, const void *vb)
 /* Compare two slPairs on their values (must be string). */
 {
 const struct slPair *a = *((struct slPair **)va);
 const struct slPair *b = *((struct slPair **)vb);
 return strcmp((char *)(a->val), (char *)(b->val));
 }
 
 void slPairValSortCase(struct slPair **pList)
 /* Sort slPair list on values (must be string), ignore case. */
 {
 slSort(pList, slPairValCmpCase);
 }
 
 void slPairValSort(struct slPair **pList)
 /* Sort slPair list on values (must be string). */
 {
 slSort(pList, slPairValCmp);
 }
 
 int slPairIntCmp(const void *va, const void *vb)
 // Compare two slPairs on their integer values.
 {
 const struct slPair *a = *((struct slPair **)va);
 const struct slPair *b = *((struct slPair **)vb);
 return ((char *)(a->val) - (char *)(b->val)); // cast works and val is 0 vased integer
 }
 
 void slPairIntSort(struct slPair **pList)
 // Sort slPair list on integer values.
 {
 slSort(pList, slPairIntCmp);
 }
 
 int slPairAtoiCmp(const void *va, const void *vb)
 // Compare two slPairs on their strings interpreted as integer values.
 {
 const struct slPair *a = *((struct slPair **)va);
 const struct slPair *b = *((struct slPair **)vb);
 return (atoi((char *)(a->val)) - atoi((char *)(b->val)));
 }
 
 void slPairValAtoiSort(struct slPair **pList)
 // Sort slPair list on string values interpreted as integers.
 {
 slSort(pList, slPairAtoiCmp);
 }
 
 void gentleFree(void *pt)
 {
 if (pt != NULL) freeMem((char*)pt);
 }
 
 int differentWord(char *s1, char *s2)
 /* strcmp ignoring case - returns zero if strings are
  * the same (ignoring case) otherwise returns difference
  * between first non-matching characters. */
 {
 char c1, c2;
 for (;;)
     {
     c1 = toupper(*s1++);
     c2 = toupper(*s2++);
     if (c1 != c2) /* Takes care of end of string in one but not the other too */
 	return c2-c1;
     if (c1 == 0)  /* Take care of end of string in both. */
 	return 0;
     }
 }
 
 int differentStringNullOk(char *a, char *b)
 /* Returns 0 if two strings (either of which may be NULL)
  * are the same.  Otherwise it returns a positive or negative
  * number depending on the alphabetical order of the two
  * strings.
  * This is basically a strcmp that can handle NULLs in
  * the input.  If used in a sort the NULLs will end
  * up before any of the cases with data.   */
 {
 if (a == b)
     return FALSE;
 else if (a == NULL)
     return -1;
 else if (b == NULL)
     return 1;
 else
     return strcmp(a,b) != 0;
 }
 
 boolean startsWith(const char *start, const char *string)
 /* Returns TRUE if string begins with start. */
 {
 char c;
 int i;
 
 for (i=0; ;i += 1)
     {
     if ((c = start[i]) == 0)
         return TRUE;
     if (string[i] != c)
         return FALSE;
     }
 }
 
 boolean startsWithNoCase(const char *start, const char *string)
 /* Returns TRUE if string begins with start, case-insensitive. */
 {
 char c;
 int i;
 
 for (i=0; ;i += 1)
     {
     if ((c = tolower(start[i])) == 0)
         return TRUE;
     if (tolower(string[i]) != c)
         return FALSE;
     }
 }
 
 boolean startsWithWord(char *firstWord, char *line)
 /* Return TRUE if first white-space-delimited word in line
  * is same as firstWord.  Comparison is case sensitive. */
 {
 int len = strlen(firstWord);
 int i;
 for (i=0; i<len; ++i)
    if (firstWord[i] != line[i])
        return FALSE;
 char c = line[len];
 return c == 0 || isspace(c);
 }
 
 boolean startsWithWordByDelimiter(char *firstWord,char delimit, char *line)
 /* Return TRUE if first word in line is same as firstWord as delimited by delimit.
    Comparison is case sensitive. Delimit of ' ' uses isspace() */
 {
 if (delimit == ' ')
     return startsWithWord(firstWord,line);
 if (!startsWith(firstWord,line))
     return FALSE;
 char c = line[strlen(firstWord)];
 return (c == '\0' || c == delimit);
 }
 
 char * findWordByDelimiter(char *word,char delimit, char *line)
 /* Return pointer to first occurance of word in line broken by 'delimit' char
    Comparison is case sensitive. Delimit of ' ' uses isspace() */
 {
 int ix;
 char *p=line;
 while (p!=NULL && *p!='\0')
     {
     for (ix = 0;
          word[ix] != '\0' && word[ix] == *p;
          ix++,p++) ; // advance as long as they match
     if (ix == strlen(word))
         {
         if (*p=='\0'
         || *p==delimit
         || (delimit == ' ' && isspace(*p)))
             return p - ix; // matched and delimited
         }
     for (;   *p!='\0' && *p!=delimit && (delimit != ' ' || !isspace(*p)); p++)
         ;  // advance to next delimit
     if (*p!='\0')
         {
         p++;
         continue;  // delimited so start again after delimit
         }
     }
 return NULL;
 }
 
 char *rStringIn(char *needle, char *haystack)
 /* Return last position of needle in haystack, or NULL if it's not there. */
 {
 int nSize = strlen(needle);
 char *pos;
 for (pos = haystack + strlen(haystack) - nSize; pos >= haystack; pos -= 1)
     {
     if (memcmp(needle, pos, nSize) == 0)
         return pos;
     }
 return NULL;
 }
 
 char *stringBetween(char *start, char *end, char *haystack)
 /* Return string between start and end strings, or NULL if
  * none found.  The first such instance is returned.
  * String must be freed by caller. */
 {
 char *pos, *p;
 int len;
 if ((p = stringIn(start, haystack)) != NULL)
     {
     pos = p + strlen(start);
     if ((p = stringIn(end, pos)) != NULL)
         {
         len = p - pos;
         pos = cloneMem(pos, len + 1);
         pos[len] = 0;
         return pos;
         }
     }
 return NULL;
 }
 
 boolean endsWith(char *string, char *end)
 /* Returns TRUE if string ends with end. */
 {
 int sLen, eLen, offset;
 sLen = strlen(string);
 eLen = strlen(end);
 offset = sLen - eLen;
 if (offset < 0)
     return FALSE;
 return sameString(string+offset, end);
 }
 
 char lastChar(char *s)
 /* Return last character in string. */
 {
 if (s == NULL || s[0] == 0)
     return 0;
 return s[strlen(s)-1];
 }
 
 void trimLastChar(char *s)
 /* Erase last character in string. */
 {
 int len = strlen(s);
 if (len > 0)
    s[len-1] = 0;
 }
 
 char *lastNonwhitespaceChar(char *s)
 // Return pointer to last character in string that is not whitespace.
 {
 if (s == NULL || s[0] == 0)
     return NULL;
 
 char *sPos = s + (strlen(s) - 1);
 for (;sPos >= s;sPos--)
     {
     if (!isspace(*sPos))
         return sPos;
     }
 return NULL;
 }
 
 char *matchingCharBeforeInLimits(char *limit, char *s, char c)
 /* Look for character c sometime before s, but going no further than limit.
  * Return NULL if not found. */
 {
 while (--s >= limit)
     if (*s == c)
         return s;
 return NULL;
 }
 
 char *memMatch(char *needle, int nLen, char *haystack, int hLen)
 /* Returns first place where needle (of nLen chars) matches
  * haystack (of hLen chars) */
 {
 char c = *needle++;
 nLen -= 1;
 hLen -= nLen;
 while (--hLen >= 0)
     {
     if (*haystack++ == c && memcmp(needle, haystack, nLen) == 0)
         {
         return haystack-1;
         }
     }
 return NULL;
 }
 
 void toUpperN(char *s, int n)
 /* Convert a section of memory to upper case. */
 {
 int i;
 for (i=0; i<n; ++i)
     s[i] = toupper(s[i]);
 }
 
 void toLowerN(char *s, int n)
 /* Convert a section of memory to lower case. */
 {
 int i;
 for (i=0; i<n; ++i)
     s[i] = tolower(s[i]);
 }
 
 void toggleCase(char *s, int size)
 /* toggle upper and lower case chars in string. */
 {
 char c;
 int i;
 for (i=0; i<size; ++i)
     {
     c = s[i];
     if (isupper(c))
         c = tolower(c);
     else if (islower(c))
         c = toupper(c);
     s[i] = c;
     }
 }
 
 
 char *strUpper(char *s)
 /* Convert entire string to upper case. */
 {
 char c;
 char *ss=s;
 for (;;)
     {
     if ((c = *ss) == 0) break;
     *ss++ = toupper(c);
     }
 return s;
 }
 
 char *replaceChars(char *string, char *old, char *new)
 /*
   Replaces the old with the new. The old and new string need not be of equal size
  Can take any length string.
  Return value needs to be freeMem'd.
 */
 {
 int numTimes = 0;
 int oldLen = strlen(old);
 int newLen = strlen(new);
 int strLen = 0;
 char *result = NULL;
 char *ptr = strstr(string, old);
 char *resultPtr = NULL;
 
 while(NULL != ptr)
     {
     numTimes++;
     ptr += oldLen;
     ptr = strstr(ptr, old);
     }
 strLen = max(strlen(string) + (numTimes * (newLen - oldLen)), strlen(string));
 result = needMem(strLen + 1);
 
 ptr = strstr(string, old);
 resultPtr = result;
 while(NULL != ptr)
     {
     strLen = ptr - string;
     strcpy(resultPtr, string);
     string = ptr + oldLen;
 
     resultPtr += strLen;
     strcpy(resultPtr, new);
     resultPtr += newLen;
     ptr = strstr(string, old);
     }
 
 strcpy(resultPtr, string);
 return result;
 }
 
 int strSwapStrs(char *string, int sz,char *oldStr, char *newStr)
 /* Swaps all occurrences of the old with the new in string. Need not be same size
    Swaps in place but restricted by sz.  Returns count of swaps or -1 for sz failure. */
 {
 // WARNING: called at low level, so no errors allowed.
 int count = 0;
 char *p=NULL;
 for(p=strstr(string,oldStr);p!=NULL;p=strstr(p+strlen(oldStr),oldStr))
     count++;
 if (count == 0)
     return 0;
 if((strlen(string)+(count*(strlen(newStr) - strlen(oldStr))))>=sz)
     return -1;
 for(p=strstr(string,oldStr);p!=NULL;p=strstr(p+strlen(newStr),oldStr))
     {
     memmove(p+strlen(newStr),p+strlen(oldStr),strlen(p+strlen(oldStr))+1); // NULL at end is also moved!
     memcpy(p,newStr,strlen(newStr));
     }
 return count;
 }
 
 char *strLower(char *s)
 /* Convert entire string to lower case */
 {
 char c;
 char *ss=s;
 for (;;)
     {
     if ((c = *ss) == 0) break;
     *ss++ = tolower(c);
     }
 return s;
 }
 
 char * memSwapChar(char *s, int len, char oldChar, char newChar)
 /* Substitute newChar for oldChar throughout memory of given length. */
 {
 int ix=0;
 for (;ix<len;ix++)
     {
     if (s[ix] == oldChar)
         s[ix] =  newChar;
     }
 return s;
 }
 
 void stripChar(char *s, char c)
 /* Remove all occurences of c from s. */
 {
 char *in = s, *out = s;
 char b;
 
 for (;;)
     {
     b = *out = *in++;
     if (b == 0)
        break;
     if (b != c)
        ++out;
     }
 }
 
 char *stripEnclosingChar(char *inout,char encloser)
 // Removes enclosing char if found at both beg and end, preserving pointer
 // Note: handles brackets '(','{' and '[' by complement at end
 {
 if (inout == NULL || strlen(inout) < 2 || *inout != encloser)
     return inout;
 
 char *end = inout + (strlen(inout) - 1);
 char closer = encloser;
 switch (closer)
     {
     case '(': closer = ')'; break;
     case '{': closer = '}'; break;
     case '[': closer = ']'; break;
     default: break;
     }
 if (*end  != closer)
     return inout;
 *end = '\0';
 return memmove(inout,inout+1,strlen(inout));  // use memmove to safely copy in place
 }
 
 void stripString(char *s, char *strip)
 /* Remove all occurences of strip from s. */
 {
 char c, *in = s, *out = s;
 int stripSize = strlen(strip);
 char stripFirst = strip[0];
 
 while ((c = *in) != 0)
     {
     c = *in;
     if (c == stripFirst)
         {
 	if (startsWith(strip, in))
 	    {
 	    in += stripSize;
 	    continue;
 	    }
 	}
     *out = c;
     ++out;
     ++in;
     }
 *out = 0;
 }
 
 int countCase(char *s,boolean upper)
 // Count letters with case (upper or lower)
 {
 char a;
 int count = 0;
 while ((a = *s++) != 0)
     if (( upper && isupper(a))
     ||  (!upper && islower(a)))
         ++count;
 return count;
 }
 
 int countChars(char *s, char c)
 /* Return number of characters c in string s. */
 {
 char a;
 int count = 0;
 while ((a = *s++) != 0)
     if (a == c)
         ++count;
 return count;
 }
 
 int countCharsN(char *s, char c, int size)
 /* Return number of characters c in string s of given size. */
 {
 int i;
 int count = 0;
 for (i=0; i<size; ++i)
     if (s[i] == c)
         ++count;
 return count;
 }
 
 int countLeadingChars(char *s, char c)
 /* Count number of characters c at start of string. */
 {
 int count = 0;
 while (*s++ == c)
    ++count;
 return count;
 }
 
 int countLeadingDigits(const char *s)
 /* Return number of leading digits in s */
 {
 int count = 0;
 while (isdigit(*s))
    {
    ++count;
    ++s;
    }
 return count;
 }
 
 int countLeadingNondigits(const char *s)
 /* Count number of leading non-digit characters in s. */
 {
 int count = 0;
 char c;
 while ((c = *s++) != 0)
    {
    if (isdigit(c))
        break;
    ++count;
    }
 return count;
 }
 
 int countSeparatedItems(char *string, char separator)
 /* Count number of items in string you would parse out with given
  * separator,  assuming final separator is optional. */
 {
 int count = 0;
 char c, lastC = 0;
 while ((c = *string++) != 0)
     {   
     if (c == separator)
        ++count;
     lastC = c;
     }
 if (lastC != separator && lastC != 0)
     ++count;
 return count;
 }
 
 int cmpStringsWithEmbeddedNumbers(const char *a, const char *b)
 /* Compare strings such as gene names that may have embedded numbers,
  * so that bmp4a comes before bmp14a */
 {
 for (;;)
    {
    /* Figure out number of digits at start, and do numerical comparison if there
     * are any.  If numbers agree step over numerical part, otherwise return difference. */
    int aNum = countLeadingDigits(a);
    int bNum = countLeadingDigits(b);
    if (aNum >= 0 && bNum >= 0)
        {
        int diff = atoi(a) - atoi(b);
        if (diff != 0)
            return diff;
        a += aNum;
        b += bNum;
        }
 
    /* Count number of non-digits at start. */
    int aNonNum = countLeadingNondigits(a);
    int bNonNum = countLeadingNondigits(b);
 
    // If different sizes of non-numerical part, then don't match, let strcmp sort out how
    if (aNonNum != bNonNum)
         return strcmp(a,b);
    // If no characters left then they are the same!
    else if (aNonNum == 0)
        return 0;
    // Non-numerical part is the same length and non-zero.  See if it is identical.  Return if not.
    else
        {
         int diff = memcmp(a,b,aNonNum);
        if (diff != 0)
             return diff;
        a += aNonNum;
        b += bNonNum;
        }
    }
 }
 
 int cmpWordsWithEmbeddedNumbers(const char *a, const char *b)
 /* Case insensitive version of cmpStringsWithEmbeddedNumbers. */
 {
 char *A = cloneString(a);
 char *B = cloneString(b);
 int diff = cmpStringsWithEmbeddedNumbers(strUpper(A), strUpper(B));
 freeMem(A);
 freeMem(B);
 return diff;
 }
 
 int countSame(char *a, char *b)
 /* Count number of characters that from start in a,b that are same. */
 {
 char c;
 int i;
 int count = 0;
 for (i=0; ; ++i)
    {
    c = a[i];
    if (b[i] != c)
        break;
    if (c == 0)
        break;
    ++count;
    }
 return count;
 }
 
 
 /* int chopString(in, sep, outArray, outSize); */
 /* This chops up the input string (cannabilizing it)
  * into an array of zero terminated strings in
  * outArray.  It returns the number of strings.
  * If you pass in NULL for outArray, it will just
  * return the number of strings that it *would*
  * chop. This splits the string.
  * GOTCHA: since multiple separators are skipped
  * and treated as one, it is impossible to parse 
  * a list with an empty string. 
  * e.g. cat\t\tdog returns only cat and dog but no empty string */
 int chopString(char *in, char *sep, char *outArray[], int outSize)
 {
 int recordCount = 0;
 
 for (;;)
     {
     if (outArray != NULL && recordCount >= outSize)
 	break;
     /* Skip initial separators. */
     in += strspn(in, sep);
     if (*in == 0)
 	break;
     if (outArray != NULL)
 	outArray[recordCount] = in;
     recordCount += 1;
     in += strcspn(in, sep);
     if (*in == 0)
 	break;
     if (outArray != NULL)
 	*in = 0;
     in += 1;
     }
 return recordCount;
 }
 
 int chopByWhite(char *in, char *outArray[], int outSize)
 /* Like chopString, but specialized for white space separators. 
  * See the GOTCHA in chopString */
 {
 int recordCount = 0;
 char c;
 for (;;)
     {
     if (outArray != NULL && recordCount >= outSize)
 	break;
 
     /* Skip initial separators. */
     while (isspace(*in)) ++in;
     if (*in == 0)
         break;
 
     /* Store start of word and look for end of word. */
     if (outArray != NULL)
         outArray[recordCount] = in;
     recordCount += 1;
     for (;;)
         {
         if ((c = *in) == 0)
             break;
         if (isspace(c))
             break;
         ++in;
         }
     if (*in == 0)
 	break;
 
     /* Tag end of word with zero. */
     if (outArray != NULL)
 	*in = 0;
     /* And skip over the zero. */
     in += 1;
     }
 return recordCount;
 }
 
 int chopByWhiteRespectDoubleQuotes(char *in, char *outArray[], int outSize)
 // NOTE: this routine does not do what this comment says.  It did not ever remove quotes due to
 // a coding error so I took out the code that pretended to be doing this.  
 /* Like chopString, but specialized for white space separators.
  * Further, any doubleQuotes (") are respected.
  * If doubleQuote is encloses whole string, then they are removed:
  *   "Fred and Ethyl" results in word [Fred and Ethyl]
  * If doubleQuotes exist inside string they are retained:
  *   Fred" and Ethyl" results in word [Fred" and Ethyl"]
  * Special note "" is a valid, though empty word. */
 {
 int recordCount = 0;
 char c;
 boolean quoting = FALSE;
 for (;;)
     {
     if (outArray != NULL && recordCount >= outSize)
         break;
 
     /* Skip initial separators. */
     while (isspace(*in)) ++in;
     if (*in == 0)
         break;
 
     /* Store start of word and look for end of word. */
     if (outArray != NULL)
         outArray[recordCount] = in;
     recordCount += 1;
     quoting = FALSE;
     for (;;)
         {
         if ((c = *in) == 0)
             break;
         if (quoting)
             {
             if (c == '"')
                 quoting = FALSE;
             }
         else
             {
             quoting = (c == '"');
             if (isspace(c))
                 break;
             }
         ++in;
         }
     if (*in == 0)
         break;
 
     /* Tag end of word with zero. */
     if (outArray != NULL)
         *in = 0;
     /* And skip over the zero. */
     in += 1;
     }
 return recordCount;
 }
 
 int chopByChar(char *in, char chopper, char *outArray[], int outSize)
 /* Chop based on a single character. */
 {
 int i;
 char c;
 if (*in == 0)
     return 0;
 for (i=0; (i<outSize) || (outArray==NULL); ++i)
     {
     if (outArray != NULL)
         outArray[i] = in;
     for (;;)
 	{
 	if ((c = *in++) == 0)
 	    return i+1;
 	else if (c == chopper)
 	    {
             if (outArray != NULL)
                 in[-1] = 0;
 	    break;
 	    }
 	}
     }
 return i;
 }
 
 char crLfChopper[] = "\n\r";
 char whiteSpaceChopper[] = " \t\n\r";
 
 
 char *skipBeyondDelimit(char *s,char delimit)
 /* Returns NULL or pointer to first char beyond one (or more contiguous) delimit char.
    If delimit is ' ' then skips beyond first patch of whitespace. */
 {
 if (s != NULL)
     {
     char *beyond = NULL;
     if (delimit == ' ')
         return skipLeadingSpaces(skipToSpaces(s));
     else
         beyond = strchr(s,delimit);
     if (beyond != NULL)
         {
         for (beyond++;*beyond == delimit;beyond++) ;
         if (*beyond != '\0')
             return beyond;
         }
     }
 return NULL;
 }
 
 char *skipLeadingSpaces(char *s)
 /* Return first non-white space. */
 {
 char c;
 if (s == NULL) return NULL;
 for (;;)
     {
     c = *s;
     if (!isspace(c))
 	return s;
     ++s;
     }
 }
 
 char *skipToSpaces(char *s)
 /* Return first white space or NULL if none.. */
 {
 char c;
 if (s == NULL)
     return NULL;
 for (;;)
     {
     c = *s;
     if (c == 0)
         return NULL;
     if (isspace(c))
 	return s;
     ++s;
     }
 }
 
 
 
 void eraseTrailingSpaces(char *s)
 /* Replace trailing white space with zeroes. */
 {
 int len = strlen(s);
 int i;
 char c;
 
 for (i=len-1; i>=0; --i)
     {
     c = s[i];
     if (isspace(c))
 	s[i] = 0;
     else
 	break;
     }
 }
 
 /* Remove white space from a string */
 void eraseWhiteSpace(char *s)
 {
 char *in, *out;
 char c;
 
 in = out = s;
 for (;;)
     {
     c = *in++;
     if (c == 0)
 	break;
     if (!isspace(c))
 	*out++ = c;
     }
 *out++ = 0;
 }
 
 /* Remove any chars leaving digits only */
 void eraseNonDigits(char *s)
 {
 char *in, *out;
 char c;
 
 in = out = s;
 for (;;)
     {
     c = *in++;
     if (c == 0)
         break;
     if (isdigit(c))
         *out++ = c;
     }
 *out = 0;
 }
 
 /* Remove non-alphanumeric chars from string */
 void eraseNonAlphaNum(char *s)
 {
 char *in, *out;
 char c;
 
 in = out = s;
 for (;;)
     {
     c = *in++;
     if (c == 0)
         break;
     if (isalnum(c))
         *out++ = c;
     }
 *out = 0;
 }
 
 char *trimSpaces(char *s)
 /* Remove leading and trailing white space. */
 {
 if (s != NULL)
     {
     s = skipLeadingSpaces(s);
     eraseTrailingSpaces(s);
     }
 return s;
 }
 
 void repeatCharOut(FILE *f, char c, int count)
 /* Write character to file repeatedly. */
 {
 while (--count >= 0)
     fputc(c, f);
 }
 
 void spaceOut(FILE *f, int count)
 /* Put out some spaces to file. */
 {
 repeatCharOut(f, ' ', count);
 }
 
 void starOut(FILE *f, int count)
 /* Put out some asterisks to file. */
 {
 repeatCharOut(f, '*', count);
 }
 
 boolean hasWhiteSpace(char *s)
 /* Return TRUE if there is white space in string. */
 {
 char c;
 while ((c = *s++) != 0)
     if (isspace(c))
         return TRUE;
 return FALSE;
 }
 
 char *firstWordInLine(char *line)
 /* Returns first word in line if any (white space separated).
  * Puts 0 in place of white space after word. */
 {
 char *e;
 line = skipLeadingSpaces(line);
 if ((e = skipToSpaces(line)) != NULL)
     *e = 0;
 return line;
 }
 
 char *cloneFirstWord(char *line)
 /* Clone first word in line */
 {
 char *startFirstWord = skipLeadingSpaces(line);
 if (startFirstWord == NULL)
     return NULL;
 char *endFirstWord = skipToSpaces(startFirstWord);
 if (endFirstWord == NULL)
     return cloneString(startFirstWord);
 else
     return cloneStringZ(startFirstWord, endFirstWord - startFirstWord);
 }
 
 char *cloneNotFirstWord(char *s)
 /* return part of string after first space, not changing s. Result has to be freed. */
 {
 if (s==NULL)
     return cloneString("");
 char* spcPos = stringIn(" ", s);
 if (spcPos==NULL)
     return cloneString("");
 return cloneString(spcPos+1);
 }
 
 char *lastWordInLine(char *line)
 /* Returns last word in line if any (white space separated).
  * Returns NULL if string is empty.  Removes any terminating white space
  * from line. */
 {
 char *s = line;
 char *word = NULL, *wordEnd = NULL;
 for (;;)
     {
     s = skipLeadingSpaces(s);
     if (s == NULL || s[0] == 0)
 	break;
     word = s;
     s = wordEnd = skipToSpaces(s);
     if (s == NULL)
         break;
     }
 if (wordEnd != NULL)
     *wordEnd = 0;
 return word;
 }
 
 char *nextWord(char **pLine)
 /* Return next word in *pLine and advance *pLine to next
  * word. */
 {
 char *s = *pLine, *e;
 if (s == NULL || s[0] == 0)
     return NULL;
 s = skipLeadingSpaces(s);
 if (s[0] == 0)
     return NULL;
 e = skipToSpaces(s);
 if (e != NULL)
     *e++ = 0;
 *pLine = e;
 return s;
 }
 
 char *nextWordRespectingQuotes(char **pLine)
 // return next word but respects single or double quotes surrounding sets of words.
 {
 char *s = *pLine, *e;
 if (s == NULL || s[0] == 0)
     return NULL;
 s = skipLeadingSpaces(s);
 if (s[0] == 0)
     return NULL;
 if (s[0] == '"')
     {
     e = skipBeyondDelimit(s+1,'"');
     if (e != NULL && !isspace(e[0]))
         e = skipToSpaces(s);
     }
 else if (s[0] == '\'')
     {
     e = skipBeyondDelimit(s+1,'\'');
     if (e != NULL && !isspace(e[0]))
         e = skipToSpaces(s);
     }
 else
     e = skipToSpaces(s);
 if (e != NULL)
     *e++ = 0;
 *pLine = e;
 return s;
 }
 
 char *nextTabWord(char **pLine)
 /* Return next tab-separated word. */
 {
 char *s = *pLine;
 char *e;
 if (s == NULL || *s == '\n' || *s == 0)
     {
     *pLine = NULL;
     return NULL;
     }
 e = strchr(s, '\t');
 if (e == NULL)
     {
     e = strchr(s, '\n');
     if (e != NULL)
         *e = 0;
     *pLine = NULL;
     }
 else
     {
     *e++ = 0;
     *pLine = e;
     }
 return s;
 }
 
 char *cloneFirstWordByDelimiterNoSkip(char *line,char delimit)
 /* Returns a cloned first word, not harming the memory passed in. Does not skip leading white space.*/
 {
 if (line == NULL || *line == 0)
     return NULL;
 int size=0;
 char *e;
 for (e=line;*e!=0;e++)
     {
     if (*e==delimit)
         break;
     else if (delimit == ' ' && isspace(*e))
         break;
     size++;
     }
 if (size == 0)
     return NULL;
 char *new = needMem(size + 2); // Null terminated by 2
 memcpy(new, line, size);
 return new;
 }
 
 char *cloneFirstWordByDelimiter(char *line,char delimit)
 /* Returns a cloned first word, not harming the memory passed in. Skips over leading white space. */
 {
 if (line == NULL || *line == 0)
     return NULL;
 line = skipLeadingSpaces(line);
 return cloneFirstWordByDelimiterNoSkip(line, delimit);
 }
 
 char *cloneNextWordByDelimiter(char **line,char delimit)
 /* Returns a cloned first word, advancing the line pointer but not harming memory passed in */
 {
 char *new = cloneFirstWordByDelimiter(*line,delimit);
 if (new != NULL)
     {
     *line = skipLeadingSpaces(*line);
     *line += strlen(new);
     if ( **line != 0)
         (*line)++;
     }
 return new;
 }
 
 char *nextStringInList(char **pStrings)
 /* returns pointer to the first string and advances pointer to next in
    list of strings dilimited by 1 null and terminated by 2 nulls. */
 {
 if (pStrings == NULL || *pStrings == NULL || **pStrings == 0)
     return NULL;
 char *p=*pStrings;
 *pStrings += strlen(p)+1;
 return p;
 }
 
 int cntStringsInList(char *pStrings)
 /* returns count of strings in a
    list of strings dilimited by 1 null and terminated by 2 nulls. */
 {
 int cnt=0;
 char *p = pStrings;
 while (nextStringInList(&p) != NULL)
     cnt++;
 return cnt;
 }
 
 int stringArrayIx(char *string, char *array[], int arraySize)
 /* Return index of string in array or -1 if not there. */
 {
 int i;
 for (i=0; i<arraySize; ++i)
     if (!differentWord(array[i], string))
         return i;
 return -1;
 }
 
 int ptArrayIx(void *pt, void *array, int arraySize)
 /* Return index of pt in array or -1 if not there. */
 {
 int i;
 void **a = array;
 for (i=0; i<arraySize; ++i)
     {
     if (pt == a[i])
         return i;
     }
 return -1;
 }
 
 
 
 FILE *mustOpen(char *fileName, char *mode)
 /* Open a file - or squawk and die. */
 {
 FILE *f;
 
 if (sameString(fileName, "stdin"))
     return stdin;
 if (sameString(fileName, "stdout"))
     return stdout;
 if ((f = fopen(fileName, mode)) == NULL)
     {
     char *modeName = "";
     if (mode)
         {
         if (mode[0] == 'r')
             modeName = " to read";
         else if (mode[0] == 'w')
             modeName = " to write";
         else if (mode[0] == 'a')
             modeName = " to append";
         }
     errAbort("mustOpen: Can't open %s%s: %s", fileName, modeName, strerror(errno));
     }
 return f;
 }
 
 void mustWrite(FILE *file, void *buf, size_t size)
 /* Write to a file or squawk and die. */
 {
 if (size != 0 && fwrite(buf, size, 1, file) != 1)
     {
     errAbort("Error writing %lld bytes: %s\n", (long long)size, strerror(ferror(file)));
     }
 }
 
 
 void mustRead(FILE *file, void *buf, size_t size)
 /* Read size bytes from a file or squawk and die. */
 {
 if (size != 0 && fread(buf, size, 1, file) != 1)
     {
     if (ferror(file))
 	errAbort("Error reading %lld bytes: %s", (long long)size, strerror(ferror(file)));
     else
 	errAbort("End of file reading %lld bytes", (long long)size);
     }
 }
 
 void writeString(FILE *f, char *s)
 /* Write a 255 or less character string to a file.
  * This will write the length of the string in the first
  * byte then the string itself. */
 {
 UBYTE bLen;
 int len = strlen(s);
 
 if (len > 255)
     {
     warn("String too long in writeString (%d chars):\n%s", len, s);
     len = 255;
     }
 bLen = len;
 writeOne(f, bLen);
 mustWrite(f, s, len);
 }
 
 char *readString(FILE *f)
 /* Read a string (written with writeString) into
  * memory.  freeMem the result when done. */
 {
 UBYTE bLen;
 int len;
 char *s;
 
 if (!readOne(f, bLen))
     return NULL;
 len = bLen;
 s = needMem(len+1);
 if (len > 0)
     mustRead(f, s, len);
 return s;
 }
 
 char *mustReadString(FILE *f)
 /* Read a string.  Squawk and die at EOF or if any problem. */
 {
 char *s = readString(f);
 if (s == NULL)
     errAbort("Couldn't read string");
 return s;
 }
 
 
 boolean fastReadString(FILE *f, char buf[256])
 /* Read a string into buffer, which must be long enough
  * to hold it.  String is in 'writeString' format. */
 {
 UBYTE bLen;
 int len;
 if (!readOne(f, bLen))
     return FALSE;
 if ((len = bLen)> 0)
     mustRead(f, buf, len);
 buf[len] = 0;
 return TRUE;
 }
 
 void msbFirstWriteBits64(FILE *f, bits64 x)
 /* Write out 64 bit number in manner that is portable across architectures */
 {
 int i;
 UBYTE buf[8];
 for (i=7; i>=0; --i)
     {
     buf[i] = (UBYTE)(x&0xff);
     x >>= 8;
     }
 mustWrite(f, buf, 8);
 }
 
 bits64 msbFirstReadBits64(FILE *f)
 /* Write out 64 bit number in manner that is portable across architectures */
 {
 int i;
 UBYTE buf[8];
 bits64 x = 0;
 mustRead(f, buf, 8);
 for (i=0; i<8; ++i)
     {
     x <<= 8;
     x |= buf[i];
     }
 return x;
 }
 
 void mustGetLine(FILE *file, char *buf, int charCount)
 /* Read at most charCount-1 bytes from file, but stop after newline if one is
  * encountered.  The string in buf is '\0'-terminated.  (See man 3 fgets.)
  * Die if there is an error. */
 {
 char *success = fgets(buf, charCount, file);
 if (success == NULL && charCount > 0)
     buf[0] = '\0';
 if (ferror(file))
     errAbort("mustGetLine: fgets failed: %s", strerror(ferror(file)));
 }
 
 int mustOpenFd(char *fileName, int flags)
 /* Open a file descriptor (see man 2 open) or squawk and die. */
 {
 if (sameString(fileName, "stdin"))
     return STDIN_FILENO;
 if (sameString(fileName, "stdout"))
     return STDOUT_FILENO;
 // mode is necessary when O_CREAT is given, ignored otherwise
 int mode = 00664;
 int fd = open(fileName, flags, mode);
 if (fd < 0)
     {
     char *modeName = "";
     if ((flags & (O_WRONLY | O_CREAT | O_TRUNC)) == (O_WRONLY | O_CREAT | O_TRUNC))
 	modeName = " to create and truncate";
     else if ((flags & (O_WRONLY | O_CREAT)) == (O_WRONLY | O_CREAT))
 	modeName = " to create";
     else if ((flags & O_WRONLY) == O_WRONLY)
 	modeName = " to write";
     else if ((flags & O_RDWR) == O_RDWR)
 	modeName = " to append";
     else
 	modeName = " to read";
     errnoAbort("mustOpenFd: Can't open %s%s", fileName, modeName);
     }
 return fd;
 }
 
 void mustReadFd(int fd, void *buf, size_t size)
 /* Read size bytes from a file or squawk and die. */
 {
 ssize_t actualSize;
 char *cbuf = buf;
 // using a loop because linux was not returning all data in a single request when request size exceeded 2GB.
 while (size > 0)
     {
     actualSize = read(fd, cbuf, size);
     if (actualSize < 0)
 	errnoAbort("Error reading %lld bytes", (long long)size);
     if (actualSize == 0)
 	errAbort("End of file reading %llu bytes (got %lld)", (unsigned long long)size, (long long)actualSize);
     cbuf += actualSize;
     size -= actualSize;
     }
 }
 
 void mustWriteFd(int fd, void *buf, size_t size)
 /* Write size bytes to file descriptor fd or die.  (See man 2 write.) */
 {
 ssize_t result = write(fd, buf, size);
 if (result < size)
     {
     if (result < 0)
 	errnoAbort("mustWriteFd: write failed");
     else 
         errAbort("mustWriteFd only wrote %lld of %lld bytes. Likely the disk is full.",
 	    (long long)result, (long long)size);
     }
 }
 
 off_t mustLseek(int fd, off_t offset, int whence)
 /* Seek to given offset, relative to whence (see man lseek) in file descriptor fd or errAbort.
  * Return final offset (e.g. if this is just an (fd, 0, SEEK_CUR) query for current position). */
 {
 off_t ret = lseek(fd, offset, whence);
 if (ret < 0)
     errnoAbort("lseek(%d, %lld, %s (%d)) failed", fd, (long long)offset,
 	       ((whence == SEEK_SET) ? "SEEK_SET" : (whence == SEEK_CUR) ? "SEEK_CUR" :
 		(whence == SEEK_END) ? "SEEK_END" : "invalid 'whence' value"), whence);
 return ret;
 }
 
 void mustCloseFd(int *pFd)
 /* Close file descriptor *pFd if >= 0, abort if there's an error, set *pFd = -1. */
 {
 if (pFd != NULL && *pFd >= 0)
     {
     if (close(*pFd) < 0)
 	errnoAbort("close failed");
     *pFd = -1;
     }
 }
 
 char *addSuffix(char *head, char *suffix)
 /* Return a needMem'd string containing "headsuffix". Should be free'd
  when finished. */
 {
 char *ret = NULL;
 int size = strlen(head) + strlen(suffix) +1;
 ret = needMem(sizeof(char)*size);
 snprintf(ret, size, "%s%s", head, suffix);
 return ret;
 }
 
 void chopSuffix(char *s)
 /* Remove suffix (last . in string and beyond) if any. */
 {
 char *e = strrchr(s, '.');
 if (e != NULL)
     *e = 0;
 }
 
 void chopSuffixAt(char *s, char c)
 /* Remove end of string from first occurrence of char c.
  * chopSuffixAt(s, '.') is equivalent to regular chopSuffix. */
 {
 char *e = strrchr(s, c);
 if (e != NULL)
     *e = 0;
 }
 
 char *chopPrefixAt(char *s, char c)
 /* Like chopPrefix, but can chop on any character, not just '.' */
 {
 char *e = strchr(s, c);
 if (e == NULL) return s;
 *e++ = 0;
 return e;
 }
 
 char *chopPrefix(char *s)
 /* This will replace the first '.' in a string with
  * 0, and return the character after this.  If there
  * is no '.' in the string this will just return the
  * unchanged s passed in. */
 {
 return chopPrefixAt(s, '.');
 }
 
 
 
 boolean carefulCloseWarn(FILE **pFile)
 /* Close file if open and null out handle to it.
  * Return FALSE and print a warning message if there
  * is a problem.*/
 {
 FILE *f;
 boolean ok = TRUE;
 if ((pFile != NULL) && ((f = *pFile) != NULL))
     {
     if (f != stdin && f != stdout)
         {
         if (fclose(f) != 0)
 	    {
             errnoWarn("fclose failed");
 	    ok = FALSE;
 	    }
         }
     else if (f == stdout)
         {
         // One expects close() to actually flush the file and close it.  If
         // the file was opened using the magic name "stdout" and then does a
         // setvbuf(), writes to file, calls carefulClose, then frees the
         // buffer, the FILE object points to invalid memory.  Then the exit()
         // I/O cleanup causes the invalid memory to be written to the file,
         // possible outputting corruption data.  If would be consistent with
         // stdio behavior to have "stdout" magic name open "/dev/stdout".
         fflush(f);
         }
     *pFile = NULL;
     }
 return ok;
 }
 
 void carefulClose(FILE **pFile)
 /* Close file if open and null out handle to it.
  * Warn and abort if there's a problem. */
 {
 if (!carefulCloseWarn(pFile))
     noWarnAbort();
 }
 
 char *firstWordInFile(char *fileName, char *wordBuf, int wordBufSize)
 /* Read the first word in file into wordBuf. */
 {
 FILE *f = mustOpen(fileName, "r");
 mustGetLine(f, wordBuf, wordBufSize);
 fclose(f);
 return trimSpaces(wordBuf);
 }
 
 int fileOffsetSizeCmp(const void *va, const void *vb)
 /* Help sort fileOffsetSize by offset. */
 {
 const struct fileOffsetSize *a = *((struct fileOffsetSize **)va);
 const struct fileOffsetSize *b = *((struct fileOffsetSize **)vb);
 if (a->offset > b->offset)
     return 1;
 else if (a->offset == b->offset)
     return 0;
 else
     return -1;
 }
 
 struct fileOffsetSize *fileOffsetSizeMerge(struct fileOffsetSize *inList)
 /* Returns a new list which is inList transformed to have adjacent blocks
  * merged.  Best to use this with a sorted list. */
 {
 struct fileOffsetSize *newList = NULL, *newEl = NULL, *oldEl, *nextOld;
 
 for (oldEl = inList; oldEl != NULL; oldEl = nextOld)
     {
     nextOld = oldEl->next;
     if (nextOld != NULL && nextOld->offset < oldEl->offset)
         errAbort("Unsorted inList in fileOffsetSizeMerge %llu %llu", oldEl->offset, nextOld->offset);
     if (newEl == NULL || newEl->offset + newEl->size < oldEl->offset)
         {
 	newEl = CloneVar(oldEl);
 	slAddHead(&newList, newEl);
 	}
     else
         {
 	newEl->size = oldEl->offset + oldEl->size - newEl->offset;
 	}
     }
 slReverse(&newList);
 return newList;
 }
 
 void fileOffsetSizeFindGap(struct fileOffsetSize *list,
                            struct fileOffsetSize **pBeforeGap, struct fileOffsetSize **pAfterGap)
 /* Starting at list, find all items that don't have a gap between them and the previous item.
  * Return at gap, or at end of list, returning pointers to the items before and after the gap. */
 {
 struct fileOffsetSize *pt, *next;
 for (pt = list; ; pt = next)
     {
     next = pt->next;
     if (next == NULL || next->offset != pt->offset + pt->size)
 	{
 	*pBeforeGap = pt;
 	*pAfterGap = next;
 	return;
 	}
     }
 }
 
 
 void mustSystem(char *cmd)
 /* Execute cmd using "sh -c" or die.  (See man 3 system.) fail on errors */
 {
 if (cmd == NULL) // don't allow (system() supports testing for shell this way)
     errAbort("mustSystem: called with NULL command.");
 int status = system(cmd);
 if (status == -1)
     errnoAbort("error starting command: %s", cmd);
 else if (WIFSIGNALED(status))
     errAbort("command terminated by signal %d: %s", WTERMSIG(status), cmd);
 else if (WIFEXITED(status))
     {
     if (WEXITSTATUS(status) != 0)
         errAbort("command exited with %d: %s", WEXITSTATUS(status), cmd);
     }
 else
     errAbort("bug: invalid exit status for command: %s", cmd);
 }
 
 int roundingScale(int a, int p, int q)
 /* returns rounded a*p/q */
 {
 if (a > 100000 || p > 100000)
     {
     double x = a;
     x *= p;
     x /= q;
     return round(x);
     }
 else
     return (a*p + q/2)/q;
 }
 
 int intAbs(int a)
 /* Return integer absolute value */
 {
 return (a >= 0 ? a : -a);
 }
 
 int  rangeIntersection(int start1, int end1, int start2, int end2)
 /* Return amount of bases two ranges intersect over, 0 or negative if no
  * intersection. */
 {
 int s = max(start1,start2);
 int e = min(end1,end2);
 return e-s;
 }
 
 int positiveRangeIntersection(int start1, int end1, int start2, int end2)
 /* Return number of bases in intersection of two ranges, or
  * zero if they don't intersect. */
 {
 int ret = rangeIntersection(start1,end1,start2,end2);
 if (ret < 0)
     ret = 0;
 return ret;
 }
 
 bits64 byteSwap64(bits64 a)
 /* Return byte-swapped version of a */
 {
 union {bits64 whole; UBYTE bytes[8];} u,v;
 u.whole = a;
 v.bytes[0] = u.bytes[7];
 v.bytes[1] = u.bytes[6];
 v.bytes[2] = u.bytes[5];
 v.bytes[3] = u.bytes[4];
 v.bytes[4] = u.bytes[3];
 v.bytes[5] = u.bytes[2];
 v.bytes[6] = u.bytes[1];
 v.bytes[7] = u.bytes[0];
 return v.whole;
 }
 
 bits64 readBits64(FILE *f, boolean isSwapped)
 /* Read and optionally byte-swap 64 bit entity. */
 {
 bits64 val;
 mustReadOne(f, val);
 if (isSwapped)
     val = byteSwap64(val);
 return val;
 }
 
 bits64 fdReadBits64(int fd, boolean isSwapped)
 /* Read and optionally byte-swap 64 bit entity. */
 {
 bits64 val;
 mustReadOneFd(fd, val);
 if (isSwapped)
     val = byteSwap64(val);
 return val;
 }
 
 bits64 memReadBits64(char **pPt, boolean isSwapped)
 /* Read and optionally byte-swap 64 bit entity from memory buffer pointed to by
  * *pPt, and advance *pPt past read area. */
 {
 bits64 val;
 memcpy(&val, *pPt, sizeof(val));
 if (isSwapped)
     val = byteSwap64(val);
 *pPt += sizeof(val);
 return val;
 }
 
 bits32 byteSwap32(bits32 a)
 /* Return byte-swapped version of a */
 {
 union {bits32 whole; UBYTE bytes[4];} u,v;
 u.whole = a;
 v.bytes[0] = u.bytes[3];
 v.bytes[1] = u.bytes[2];
 v.bytes[2] = u.bytes[1];
 v.bytes[3] = u.bytes[0];
 return v.whole;
 }
 
 bits32 readBits32(FILE *f, boolean isSwapped)
 /* Read and optionally byte-swap 32 bit entity. */
 {
 bits32 val;
 mustReadOne(f, val);
 if (isSwapped)
     val = byteSwap32(val);
 return val;
 }
 
 bits32 fdReadBits32(int fd, boolean isSwapped)
 /* Read and optionally byte-swap 32 bit entity. */
 {
 bits32 val;
 mustReadOneFd(fd, val);
 if (isSwapped)
     val = byteSwap32(val);
 return val;
 }
 
 bits32 memReadBits32(char **pPt, boolean isSwapped)
 /* Read and optionally byte-swap 32 bit entity from memory buffer pointed to by
  * *pPt, and advance *pPt past read area. */
 {
 bits32 val;
 memcpy(&val, *pPt, sizeof(val));
 if (isSwapped)
     val = byteSwap32(val);
 *pPt += sizeof(val);
 return val;
 }
 
 bits16 byteSwap16(bits16 a)
 /* Return byte-swapped version of a */
 {
 union {bits16 whole; UBYTE bytes[2];} u,v;
 u.whole = a;
 v.bytes[0] = u.bytes[1];
 v.bytes[1] = u.bytes[0];
 return v.whole;
 }
 
 bits16 readBits16(FILE *f, boolean isSwapped)
 /* Read and optionally byte-swap 16 bit entity. */
 {
 bits16 val;
 mustReadOne(f, val);
 if (isSwapped)
     val = byteSwap16(val);
 return val;
 }
 
 bits16 fdReadBits16(int fd, boolean isSwapped)
 /* Read and optionally byte-swap 16 bit entity. */
 {
 bits16 val;
 mustReadOneFd(fd, val);
 if (isSwapped)
     val = byteSwap16(val);
 return val;
 }
 
 bits16 memReadBits16(char **pPt, boolean isSwapped)
 /* Read and optionally byte-swap 16 bit entity from memory buffer pointed to by
  * *pPt, and advance *pPt past read area. */
 {
 bits16 val;
 memcpy(&val, *pPt, sizeof(val));
 if (isSwapped)
     val = byteSwap16(val);
 *pPt += sizeof(val);
 return val;
 }
 
 double byteSwapDouble(double a)
 /* Return byte-swapped version of a */
 {
 union {double whole; UBYTE bytes[8];} u,v;
 u.whole = a;
 v.bytes[0] = u.bytes[7];
 v.bytes[1] = u.bytes[6];
 v.bytes[2] = u.bytes[5];
 v.bytes[3] = u.bytes[4];
 v.bytes[4] = u.bytes[3];
 v.bytes[5] = u.bytes[2];
 v.bytes[6] = u.bytes[1];
 v.bytes[7] = u.bytes[0];
 return v.whole;
 }
 
 
 double readDouble(FILE *f, boolean isSwapped)
 /* Read and optionally byte-swap double-precision floating point entity. */
 {
 double val;
 mustReadOne(f, val);
 if (isSwapped)
     val = byteSwapDouble(val);
 return val;
 }
 
 double memReadDouble(char **pPt, boolean isSwapped)
 /* Read and optionally byte-swap double-precision floating point entity
  * from memory buffer pointed to by *pPt, and advance *pPt past read area. */
 {
 double val;
 memcpy(&val, *pPt, sizeof(val));
 if (isSwapped)
     val = byteSwapDouble(val);
 *pPt += sizeof(val);
 return val;
 }
 
 float byteSwapFloat(float a)
 /* Return byte-swapped version of a */
 {
 union {float whole; UBYTE bytes[4];} u,v;
 u.whole = a;
 v.bytes[0] = u.bytes[3];
 v.bytes[1] = u.bytes[2];
 v.bytes[2] = u.bytes[1];
 v.bytes[3] = u.bytes[0];
 return v.whole;
 }
 
 
 float readFloat(FILE *f, boolean isSwapped)
 /* Read and optionally byte-swap single-precision floating point entity. */
 {
 float val;
 mustReadOne(f, val);
 if (isSwapped)
     val = byteSwapFloat(val);
 return val;
 }
 
 float memReadFloat(char **pPt, boolean isSwapped)
 /* Read and optionally byte-swap single-precision floating point entity
  * from memory buffer pointed to by *pPt, and advance *pPt past read area. */
 {
 float val;
 memcpy(&val, *pPt, sizeof(val));
 if (isSwapped)
     val = byteSwapFloat(val);
 *pPt += sizeof(val);
 return val;
 }
 
 
 void removeReturns(char *dest, char *src)
 /* Removes the '\r' character from a string.
  * The source and destination strings can be the same, if there are
  * no other threads */
 {
 int i = 0;
 int j = 0;
 
 /* until the end of the string */
 for (;;)
     {
     /* skip the returns */
     while(src[j] == '\r')
 	j++;
 
     /* copy the characters */
     dest[i] = src[j];
 
     /* check to see if done */
     if(src[j] == '\0')
 	break;
 
     /* advance the counters */
     i++;
     j++;
     }
 }
 
 char* readLine(FILE* fh)
 /* Read a line of any size into dynamic memory, return null on EOF */
 {
 int bufCapacity = 256;
 int bufSize = 0;
 char* buf = needMem(bufCapacity);
 int ch;
 
 /* loop until EOF of EOLN */
 while (((ch = getc(fh)) != EOF) && (ch != '\n'))
     {
     /* expand if almost full, always keep one extra char for
      * zero termination */
     if (bufSize >= bufCapacity-2)
         {
         bufCapacity *= 2;
         buf = realloc(buf, bufCapacity);
         if (buf == NULL)
             {
             errAbort("Out of memory in readline - request size %d bytes", bufCapacity);
             }
         }
     buf[bufSize++] = ch;
     }
 
 /* only return EOF if no data was read */
 if ((ch == EOF) && (bufSize == 0))
     {
     freeMem(buf);
     return NULL;
     }
 buf[bufSize] = '\0';
 return buf;
 }
 
 boolean fileExists(char *fileName)
 /* Return TRUE if file exists (may replace this with non-
  * portable faster way some day). */
 {
 /* To make piping easier stdin and stdout always exist. */
 if (sameString(fileName, "stdin")) return TRUE;
 if (sameString(fileName, "stdout")) return TRUE;
 
 return fileSize(fileName) != -1;
 }
 
 /*
  Friendly name for strstrNoCase
 */
 char *containsStringNoCase(char *haystack, char *needle)
 {
 return strstrNoCase(haystack, needle);
 }
 
 char *strstrNoCase(char *haystack, char *needle)
 /*
   A case-insensitive strstr function
 Will also robustly handle null strings
 param haystack - The string to be searched
 param needle - The string to look for in the haystack string
 
 return - The position of the first occurence of the desired substring
 or -1 if it is not found
  */
 {
 char *haystackCopy = NULL;
 char *needleCopy = NULL;
 int index = 0;
 int haystackLen = 0;
 int needleLen = 0;
 char *p, *q;
 
 if (NULL == haystack || NULL == needle)
     {
     return NULL;
     }
 
 haystackLen = strlen(haystack);
 needleLen = strlen(needle);
 
 haystackCopy = (char*) needMem(haystackLen + 1);
 needleCopy = (char*) needMem(needleLen + 1);
 
 for(index = 0; index < haystackLen;  index++)
     {
     haystackCopy[index] = tolower(haystack[index]);
     }
 haystackCopy[haystackLen] = 0; /* Null terminate */
 
 for(index = 0; index < needleLen;  index++)
     {
     needleCopy[index] = tolower(needle[index]);
     }
 needleCopy[needleLen] = 0; /* Null terminate */
 
 p=strstr(haystackCopy, needleCopy);
 q=haystackCopy;
 
 freeMem(haystackCopy);
 freeMem(needleCopy);
 
 if(p==NULL) return NULL;
 
 return p-q+haystack;
 }
 
 int vasafef(char* buffer, int bufSize, char *format, va_list args)
 /* Format string to buffer, vsprintf style, only with buffer overflow
  * checking.  The resulting string is always terminated with zero byte. */
 {
 int sz = vsnprintf(buffer, bufSize, format, args);
 /* note that some version return -1 if too small */
 if ((sz < 0) || (sz >= bufSize))
     {
     buffer[bufSize-1] = (char) 0;
     errAbort("buffer overflow, size %d, format: %s, buffer: '%s'", bufSize, format, buffer);
     }
 return sz;
 }
 
 int safef(char* buffer, int bufSize, char *format, ...)
 /* Format string to buffer, vsprintf style, only with buffer overflow
  * checking.  The resulting string is always terminated with zero byte. */
 {
 int sz;
 va_list args;
 va_start(args, format);
 sz = vasafef(buffer, bufSize, format, args);
 va_end(args);
 return sz;
 }
 
 void safecpy(char *buf, size_t bufSize, const char *src)
 /* copy a string to a buffer, with bounds checking.*/
 {
 size_t slen = strlen(src);
 if (slen > bufSize-1)
     errAbort("buffer overflow, size %lld, string size: %lld", (long long)bufSize, (long long)slen);
 strcpy(buf, src);
 }
 
 void safencpy(char *buf, size_t bufSize, const char *src, size_t n)
 /* copy n characters from a string to a buffer, with bounds checking.
  * Unlike strncpy, always null terminates the result */
 {
 if (n > bufSize-1)
     errAbort("buffer overflow, size %lld, substring size: %lld", (long long)bufSize, (long long)n);
 // strlen(src) can take a long time when src is for example a pointer into a chromosome sequence.
 // Instead of setting slen to max(strlen(src), n), just stop counting length at n.
 size_t slen = 0;
 while (src[slen] != '\0' && slen < n)
     slen++;
 strncpy(buf, src, n);
 buf[slen] = '\0';
 }
 
 void safecat(char *buf, size_t bufSize, const char *src)
 /* Append  a string to a buffer, with bounds checking.*/
 {
 size_t blen = strlen(buf);
 size_t slen = strlen(src);
 if (blen+slen > bufSize-1)
     errAbort("buffer overflow, size %lld, new string size: %lld", (long long)bufSize, (long long)(blen+slen));
 strcat(buf, src);
 }
 
 void safencat(char *buf, size_t bufSize, const char *src, size_t n)
 /* append n characters from a string to a buffer, with bounds checking. */
 {
 size_t blen = strlen(buf);
 if (blen+n > bufSize-1)
     errAbort("buffer overflow, size %lld, new string size: %lld", (long long)bufSize, (long long)(blen+n));
 size_t slen = strlen(src);
 if (slen > n)
     slen = n;
 strncat(buf, src, n);
 buf[blen+slen] = '\0';
 }
 
 
 static char *naStr = "n/a";
 static char *emptyStr = "";
 
 char *naForNull(char *s)
 /* Return 'n/a' if s is NULL, otherwise s. */
 {
 if (s == NULL)
    s = naStr;
 return s;
 }
 
 char *naForEmpty(char *s)
 /* Return n/a if s is "" or NULL, otherwise s. */
 {
 if (s == NULL || s[0] == 0)
     s = naStr;
 return s;
 }
 
 char *emptyForNull(char *s)
 /* Return "" if s is NULL, otherwise s. */
 {
 if (s == NULL)
    s = emptyStr;
 return s;
 }
 
 char *nullIfAllSpace(char *s)
 /* Return NULL if s is all spaces, otherwise s. */
 {
 s = skipLeadingSpaces(s);
 if (s != NULL)
     if (s[0] == 0)
         s = NULL;
 return s;
 }
 
 char *trueFalseString(boolean b)
 /* Return "true" or "false" */
 {
 return (b ? "true" : "false");
 }
 
 void uglyTime(char *label, ...)
 /* Print label and how long it's been since last call.  Call with
  * a NULL label to initialize. */
 {
 static long lastTime = 0;
 long time = clock1000();
 va_list args;
 va_start(args, label);
 if (label != NULL)
     {
     fprintf(stdout, "<span class='timing'>");
     vfprintf(stdout, label, args);
     fprintf(stdout, ": %ld millis<BR></span>\n", time - lastTime);
     }
 lastTime = time;
 va_end(args);
 }
 
 void makeDirs(char* path)
 /* make a directory, including parent directories */
 {
 char pathBuf[PATH_LEN];
 char* next = pathBuf;
 
 strcpy(pathBuf, path);
 if (*next == '/')
     next++;
 
 while((*next != '\0')
       && (next = strchr(next, '/')) != NULL)
     {
     *next = '\0';
     makeDir(pathBuf);
     *next = '/';
     next++;
     }
 makeDir(pathBuf);
 }
 
 boolean isSymbolString(char *s)
 /* Return TRUE if s can be used as a symbol in the C language */
 {
 char c = *s++;
 if (!isalpha(c) && (c != '_'))
     return FALSE;
 while ((c = *s++) != 0)
     {
     if (!(isalnum(c) || (c == '_')))
 	return FALSE;
     }
 return TRUE;
 }
 
 boolean isNumericString(char *s)
 /* Return TRUE if string is numeric (integer or floating point) */
 {
 char *end;
 strtod(s, &end);
 return (end != s && *end == 0);
 }
 
 char *skipNumeric(char *s)
 /* Return first char of s that's not a digit */
 {
 while (isdigit(*s))
    ++s;
 return s;
 }
 
 char *skipToNumeric(char *s)
 /* skip up to where numeric digits appear */
 {
 while (*s != 0 && !isdigit(*s))
     ++s;
 return s;
 }
 
 char *splitOffNonNumeric(char *s)
 /* Split off non-numeric part, e.g. mm of mm8. Result should be freed when done */
 {
 return cloneStringZ(s,skipToNumeric(s)-s);
 }
 
 char *splitOffNumber(char *db)
 /* Split off number part, e.g. 8 of mm8. Result should be freed when done */
 {
 return cloneString(skipToNumeric(db));
 }
 
 boolean isAllDigits(char *s)
 /* Return TRUE if string is non-empty and contains only digits (i.e. is a nonnegative integer). */
 {
 if (isEmpty(s))
     return FALSE;
 char c;
 while ((c = *s++) != 0)
     if (!isdigit(c))
         return FALSE;
 return TRUE;
 }
 
 time_t mktimeFromUtc (struct tm *t)
 /* Return time_t for tm in UTC (GMT)
  * Useful for stuff like converting to time_t the
  * last-modified HTTP response header
  * which is always GMT. Returns -1 on failure of mktime */
 {
     time_t time;
     char *tz;
     char save_tz[100];
     tz=getenv("TZ");
     if (tz)
         safecpy(save_tz, sizeof(save_tz), tz);
     setenv("TZ", "GMT0", 1);
     tzset();
     t->tm_isdst = 0;
     time=mktime(t);
     if (tz)
         setenv("TZ", save_tz, 1);
     else
         unsetenv("TZ");
     tzset();
     return (time);
 }
 
 
 time_t dateToSeconds(const char *date,const char*format)
 // Convert a string date to time_t
 {
 struct tm storage={0,0,0,0,0,0,0,0,0};
 if (strptime(date,format,&storage)==NULL)
     return 0;
 else
     return mktime(&storage);
 }
 
 boolean dateIsOld(const char *date,const char*format)
 // Is this string date older than now?
 {
 time_t test = dateToSeconds(date,format);
 time_t now = clock1();
 return (test < now);
 }
 
 boolean dateIsOlderBy(const char *date,const char*format, time_t seconds)
 // Is this string date older than now by this many seconds?
 {
 time_t test = dateToSeconds(date,format);
 time_t now = clock1();
 return (test + seconds < now);
 }
 
 static int daysOfMonth(struct tm *tp)
 /* Returns the days of the month given the year */
 {
 int days=0;
 switch(tp->tm_mon)
     {
     case 3:
     case 5:
     case 8:
     case 10:    days = 30;
                 break;
     case 1:     days = 28;
                 if ( (tp->tm_year % 4) == 0
                 &&  ((tp->tm_year % 20) != 0 || (tp->tm_year % 100) == 0) )
                     days = 29;
                 break;
     default:    days = 31;
                 break;
     }
 return days;
 }
 
 static void dateAdd(struct tm *tp,int addYears,int addMonths,int addDays)
 /* Add years,months,days to a date */
 {
 tp->tm_mday  += addDays;
 tp->tm_mon   += addMonths;
 tp->tm_year  += addYears;
 int dom=28;
 while ( (tp->tm_mon >11  || tp->tm_mon <0)
     || (tp->tm_mday>dom || tp->tm_mday<1) )
     {
     if (tp->tm_mon>11)   // First month: tm.tm_mon is 0-11 range
         {
         tp->tm_year += (tp->tm_mon / 12);
         tp->tm_mon  = (tp->tm_mon % 12);
         }
     else if (tp->tm_mon<0)
         {
         tp->tm_year += (tp->tm_mon / 12) - 1;
         tp->tm_mon  =  (tp->tm_mon % 12) + 12;
         }
     else
         {
         dom = daysOfMonth(tp);
         if (tp->tm_mday>dom)
             {
             tp->tm_mday -= dom;
             tp->tm_mon  += 1;
             dom = daysOfMonth(tp);
             }
         else if (tp->tm_mday < 1)
             {
             tp->tm_mon  -= 1;
             dom = daysOfMonth(tp);
             tp->tm_mday += dom;
             }
         }
     }
 }
 
 char *dateAddTo(char *date,char *format,int addYears,int addMonths,int addDays)
 /* Add years,months,days to a formatted date and returns the new date as a cloned string
 *  format is a strptime/strftime format: %F = yyyy-mm-dd */
 {
 char *newDate = needMem(12);
 struct tm tp;
 if (strptime(date,format, &tp))
     {
     dateAdd(&tp,addYears,addMonths,addDays); // tp.tm_year only contains years since 1900
     strftime(newDate,12,format,&tp);
     }
 return cloneString(newDate);  // newDate is never freed!
 }
 
 boolean haplotype(const char *name)
 /* Is this name a haplotype name ?  _hap or _alt in the name */
 {
 if (stringIn("_hap", name) || stringIn("_alt", name))
    return TRUE;
 else
    return FALSE;
 }