dfd893ed6eb311a9b7c8844db7fec58642083c39
angie
Fri Jan 9 15:46:14 2015 -0800
New toy CGI hgChooseDb based on PPT mockup and ideas from Ann and Matt: popular species icons and autocomplete search for species, as an alternative to hgGateway's menus. Uses the new ReactJS/ImmutableJS framework.
hgChooseDb.c has three modes of operation:
- HTML output for simple main page with a
container to be filled in by javascript
- cart-based JSON responses to ajax requests from javascript (using hg/lib/cartJson.c)
- no cart; fast JSON responses to species-search autocomplete requests
hgChooseDb.jsx is the React/JSX UI view code, compiled to bundle/reactHgChooseDb.js.
hgChooseDbModel.js is a subclass of js/model/lib/ImModel.js that gets initial state
from the server and then responds to user actions.
diff --git src/lib/common.c src/lib/common.c
index a84f414..4839c61 100644
--- src/lib/common.c
+++ src/lib/common.c
@@ -1,3548 +1,3563 @@
/* 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
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;inext;
}
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; inext = 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;
uglyf("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; inext)
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; inext)
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;
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 delimied 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= 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=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= 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.
* 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)
/* 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;
char *quoteBegins = NULL;
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;
if (*in == '"')
quoteBegins = (in+1);
else
quoteBegins = NULL;
}
recordCount += 1;
quoting = FALSE;
for (;;)
{
if ((c = *in) == 0)
break;
if (quoting)
{
if (c == '"')
{
quoting = FALSE;
if (quoteBegins != NULL) // implies out array
{
if ((c = *(in+1) == 0 )|| isspace(c)) // whole word is quoted.
{
outArray[recordCount-1] = quoteBegins; // Fix beginning of word
quoteBegins = NULL;
break;
}
}
}
}
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=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 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 "";
char* spcPos = stringIn(" ", s);
if (spcPos==NULL)
return cloneString(s);
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 *cloneFirstWordByDelimiter(char *line,char delimit)
/* Returns a cloned first word, not harming the memory passed in */
{
if (line == NULL || *line == 0)
return NULL;
line = skipLeadingSpaces(line);
if (*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 *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 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;
}
}
*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, "");
vfprintf(stdout, label, args);
fprintf(stdout, ": %ld millis
\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);
}
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));
}
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;
}