365
/* helper for Nlm_StableMergeSort */
367
mergesort_helper( Nlm_CharPtr b, size_t nel, size_t width, int (LIBCALLBACK *compar )PROTO ((Nlm_VoidPtr, Nlm_VoidPtr )), Nlm_CharPtr scratch, Nlm_CharPtr output );
370
/* Also guaranteed O(NlogN) and guarantees a stable sort.
371
That is, elements which are the same
372
according to the compar function are kept in the same order. */
373
NLM_EXTERN void LIBCALL Nlm_StableMergeSort (Nlm_VoidPtr b, size_t nel, size_t width, int (LIBCALLBACK *compar )PROTO ((Nlm_VoidPtr, Nlm_VoidPtr ))) /* Element comparison routine */
376
Nlm_CharPtr scratch = NULL;
377
Nlm_CharPtr output = NULL;
380
return; /* nothing to do */
383
/* We create scratch spaces which will be used throughout the
385
scratch = (Nlm_CharPtr) Nlm_MemNew( nel * width );
386
output = (Nlm_CharPtr) Nlm_MemNew( nel * width );
388
mergesort_helper( (Nlm_CharPtr)b, nel, width, compar, scratch, output );
389
memmove( b, output, nel * width );
391
/* free our scratch space, which is no longer needed */
392
Nlm_MemFree(scratch);
396
/* This helper for Nlm_StableMergeSort sorts b and puts the
397
result in output. It uses "scratch" for its own internal
400
mergesort_helper( Nlm_CharPtr b, size_t nel, size_t width, int (LIBCALLBACK *compar )PROTO ((Nlm_VoidPtr, Nlm_VoidPtr )), Nlm_CharPtr scratch, Nlm_CharPtr output )
402
Nlm_CharPtr output_ptr = NULL;
403
Nlm_CharPtr left_ptr = NULL;
404
Nlm_CharPtr right_ptr = NULL;
405
Nlm_CharPtr midpoint_ptr = NULL;
406
Nlm_CharPtr one_past_end_ptr = NULL;
407
int compar_result = 0;
409
/* non-recursive base case */
411
memmove( output, b, width );
415
/* divide-and-conquer: split roughly in half and sort each subarray,
416
with intermediate results ending up in the scratch array
417
( the subcalls use "output" as a scratch space ) */
418
mergesort_helper( b, nel/2, width, compar, output, scratch );
419
mergesort_helper( b + (nel/2)*width, ((nel + 1) / 2), width, compar, output, scratch + (nel/2)*width );
421
/* merge the sorted subarrays from scratch into output */
424
midpoint_ptr = scratch + (nel/2)*width;
425
right_ptr = midpoint_ptr;
426
one_past_end_ptr = scratch + (nel * width);
428
while( left_ptr < midpoint_ptr && right_ptr < one_past_end_ptr ) {
429
compar_result = (*compar)( left_ptr, right_ptr );
430
if( compar_result <= 0 ) {
431
memmove( output_ptr, left_ptr, width );
434
} else if( compar_result > 0 ) {
435
memmove( output_ptr, right_ptr, width );
441
if( left_ptr < midpoint_ptr ) {
442
/* right_ptr no longer valid, so just bulk copy from left_ptr */
443
memmove( output_ptr, left_ptr, midpoint_ptr - left_ptr );
444
} else if( right_ptr < one_past_end_ptr ) {
445
/* left_ptr no longer valid, so just bulk copy from right_ptr */
446
memmove( output_ptr, right_ptr, one_past_end_ptr - right_ptr );
365
450
/*****************************************************************************
367
452
* ValNodeNew(vnp)
2105
2364
/* ----- End of simplified MD4 algorithm ------ */
2367
/* XML parsing section */
2369
/* function to decode ampersand-protected symbols */
2371
typedef struct xmltable {
2375
} Nlm_XmlTable, PNTR Nlm_XmlTablePtr;
2377
static Nlm_XmlTable xmlcodes [] = {
2379
{ "'", 6, '\''},
2382
{ """, 6, '"'},
2386
static Nlm_CharPtr DecodeXml (
2392
Nlm_CharPtr dst, src;
2394
Nlm_XmlTablePtr xtp;
2396
if (StringHasNoText (str)) return str;
2401
while (ch != '\0') {
2404
for (i = 0; xmlcodes [i].code != NULL; i++) {
2405
if (StringNICmp (src, xmlcodes [i].code, xmlcodes [i].len) == 0) {
2406
xtp = &(xmlcodes [i]);
2431
static Nlm_CharPtr EncodeXml (
2437
Nlm_CharPtr dst, src, tag, tmp;
2440
Nlm_XmlTablePtr xtp;
2442
if (str == NULL) return NULL;
2446
while (ch != '\0') {
2448
for (i = 0; xmlcodes [i].code != NULL; i++) {
2449
if (ch == xmlcodes [i].letter) {
2450
len += xmlcodes [i].len;
2458
if (len == 0) return NULL;
2460
tmp = (Nlm_CharPtr) MemNew (len + 1);
2461
if (tmp == NULL) return NULL;
2466
while (ch != '\0') {
2468
for (i = 0; xmlcodes [i].code != NULL; i++) {
2469
if (ch == xmlcodes [i].letter) {
2470
xtp = &(xmlcodes [i]);
2477
while (ch != '\0') {
2495
#define XML_START_TAG 1
2496
#define XML_END_TAG 2
2497
#define XML_ATTRIBUTE 3
2498
#define XML_CONTENT 4
2500
/* first pass - tokenize XML into linear ValNode chain */
2502
static void TokenizeXmlLine (
2503
ValNodePtr PNTR headp,
2504
ValNodePtr PNTR tailp,
2509
Nlm_CharPtr atr, fst, lst, nxt, ptr;
2510
Nlm_Char ch, cha, chf, chl, quo;
2511
Nlm_Boolean doStart, doEnd;
2513
if (headp == NULL || tailp == NULL) return;
2514
if (StringHasNoText (str)) return;
2519
while (ch != '\0') {
2520
if (ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t') {
2521
/* ignore whitespace between tags */
2525
} else if (ch == '<') {
2527
/* process XML tag */
2528
/* skip past left angle bracket */
2531
/* keep track of pointers to first character after < and last character before > in XML tag */
2535
while (ch != '\0' && ch != '>') {
2548
if (chf == '?' || chf == '!') {
2549
/* skip processing instructions */
2551
/* initial default - if no slashes are present, just do start tag */
2554
/* check for slash just after < or just before > symbol */
2556
/* slash after <, just do end tag */
2560
} else if (chl == '/') {
2561
/* slash before > - self-closing tag - do start tag and end tag - content will be empty */
2566
/* skip past first space to look for attribute strings before closing > symbol */
2569
while (cha != '\0' && cha != ' ') {
2579
/* report start tag */
2581
TrimSpacesAroundString (fst);
2582
ValNodeCopyStrEx (headp, tailp, XML_START_TAG, fst);
2585
/* report individual attribute tag="value" clauses */
2586
while (cha != '\0') {
2589
/* skip to equal sign */
2590
while (cha != '\0' && cha != '=') {
2599
if (cha == '"' || cha == '\'') {
2604
while (cha != '\0' && cha != quo) {
2613
TrimSpacesAroundString (atr);
2614
ValNodeCopyStrEx (headp, tailp, XML_ATTRIBUTE, atr);
2619
/* report end tag */
2621
TrimSpacesAroundString (fst);
2622
ValNodeCopyStrEx (headp, tailp, XML_END_TAG, fst);
2628
/* process content between tags */
2632
while (ch != '\0' && ch != '<') {
2640
/* report content string */
2641
TrimSpacesAroundString (fst);
2643
ValNodeCopyStrEx (headp, tailp, XML_CONTENT, fst);
2653
static ValNodePtr TokenizeXmlString (
2658
ValNodePtr head = NULL, tail = NULL;
2660
if (StringHasNoText (str)) return NULL;
2662
TokenizeXmlLine (&head, &tail, str);
2667
/* second pass - process ValNode chain into hierarchical structure */
2669
static Nlm_XmlObjPtr ProcessAttribute (
2674
Nlm_XmlObjPtr attr = NULL;
2675
Nlm_Char ch, chf, chl, quo;
2676
Nlm_CharPtr eql, lst;
2678
if (StringHasNoText (str)) return NULL;
2682
while (ch != '\0' && ch != '=') {
2686
if (ch == '\0') return NULL;
2692
if (quo == '"' || quo == '\'') {
2697
if (chf == '\0') return NULL;
2701
while (chl != '\0' && chl != quo) {
2709
if (StringHasNoText (str) || StringHasNoText (eql)) return NULL;
2711
attr = (Nlm_XmlObjPtr) MemNew (sizeof (Nlm_XmlObj));
2712
if (attr == NULL) return NULL;
2714
TrimSpacesAroundString (str);
2715
TrimSpacesAroundString (eql);
2718
attr->name = StringSave (str);
2719
attr->contents = StringSave (eql);
2724
static Nlm_XmlObjPtr ProcessStartTag (
2725
ValNodePtr PNTR curr,
2730
Nlm_XmlObjPtr attr, child, lastattr = NULL, lastchild = NULL, xop = NULL;
2735
if (curr == NULL) return NULL;
2737
xop = (Nlm_XmlObjPtr) MemNew (sizeof (Nlm_XmlObj));
2738
if (xop == NULL) return NULL;
2740
xop->name = StringSave (name);
2742
while (*curr != NULL) {
2745
str = (Nlm_CharPtr) vnp->data.ptrvalue;
2746
choice = vnp->choice;
2748
/* advance to next token */
2751
TrimSpacesAroundString (str);
2753
if (StringHasNoText (str)) {
2755
} else if (choice == XML_START_TAG) {
2757
/* recursive call to process next level */
2758
child = ProcessStartTag (curr, str);
2759
/* link into children list */
2760
if (child != NULL) {
2761
if (xop->children == NULL) {
2762
xop->children = child;
2764
if (lastchild != NULL) {
2765
lastchild->next = child;
2770
} else if (choice == XML_END_TAG) {
2772
/* pop out of recursive call */
2775
} else if (choice == XML_ATTRIBUTE) {
2777
/* get attributes within tag */
2778
attr = ProcessAttribute (str);
2780
if (xop->attributes == NULL) {
2781
xop->attributes = attr;
2783
if (lastattr != NULL) {
2784
lastattr->next = attr;
2789
} else if (choice == XML_CONTENT) {
2791
/* get contact between start and end tags */
2792
xop->contents = StringSave (str);
2799
static Nlm_XmlObjPtr ParseXmlTokens (
2806
if (head == NULL) return NULL;
2810
return ProcessStartTag (&curr, "root");
2813
static Nlm_Int4 VisitXmlNodeProc (
2815
Nlm_XmlObjPtr parent,
2817
Nlm_VoidPtr userdata,
2818
VisitXmlNodeFunc callback,
2819
Nlm_CharPtr nodeFilter,
2820
Nlm_CharPtr parentFilter,
2821
Nlm_CharPtr attrTagFilter,
2822
Nlm_CharPtr attrValFilter,
2827
Nlm_XmlObjPtr attr, tmp;
2831
if (xop == NULL) return index;
2833
/* check depth limit */
2834
if (level > maxDepth) return index;
2838
/* check attribute filters */
2839
if (StringDoesHaveText (attrTagFilter)) {
2841
for (attr = xop->attributes; attr != NULL; attr = attr->next) {
2842
if (StringICmp (attr->name, attrTagFilter) == 0) {
2843
if (StringHasNoText (attrValFilter) || StringICmp (attr->contents, attrValFilter) == 0) {
2849
} else if (StringDoesHaveText (attrValFilter)) {
2851
for (attr = xop->attributes; attr != NULL; attr = attr->next) {
2852
if (StringICmp (attr->contents, attrValFilter) == 0) {
2859
/* check node name filter */
2860
if (StringDoesHaveText (nodeFilter)) {
2861
if (StringICmp (xop->name, nodeFilter) != 0) {
2866
/* check parent name filter */
2867
if (StringDoesHaveText (parentFilter)) {
2868
if (parent != NULL && StringICmp (parent->name, parentFilter) != 0) {
2874
/* call callback for this node if all filter tests pass */
2875
if (callback != NULL) {
2876
callback (xop, parent, level, userdata);
2881
/* visit children */
2882
for (tmp = xop->children; tmp != NULL; tmp = tmp->next) {
2883
index += VisitXmlNodeProc (tmp, xop, level + 1, userdata, callback, nodeFilter,
2884
parentFilter, attrTagFilter, attrValFilter, maxDepth);
2890
NLM_EXTERN Nlm_Int4 VisitXmlNodes (
2892
Nlm_VoidPtr userdata,
2893
VisitXmlNodeFunc callback,
2894
Nlm_CharPtr nodeFilter,
2895
Nlm_CharPtr parentFilter,
2896
Nlm_CharPtr attrTagFilter,
2897
Nlm_CharPtr attrValFilter,
2904
if (xop == NULL) return index;
2906
if (maxDepth == 0) {
2907
maxDepth = INT2_MAX;
2910
index += VisitXmlNodeProc (xop, NULL, 1, userdata, callback, nodeFilter,
2911
parentFilter, attrTagFilter, attrValFilter, maxDepth);
2916
NLM_EXTERN Nlm_Int4 VisitXmlAttributes (
2918
Nlm_VoidPtr userdata,
2919
VisitXmlNodeFunc callback,
2920
Nlm_CharPtr attrTagFilter,
2921
Nlm_CharPtr attrValFilter
2929
if (xop == NULL) return index;
2931
for (attr = xop->attributes; attr != NULL; attr = attr->next) {
2935
/* check attribute filters */
2936
if (StringDoesHaveText (attrTagFilter)) {
2938
if (StringICmp (attr->name, attrTagFilter) == 0) {
2939
if (StringHasNoText (attrValFilter) || StringICmp (attr->contents, attrValFilter) == 0) {
2943
} else if (StringDoesHaveText (attrValFilter)) {
2945
if (StringICmp (attr->contents, attrValFilter) == 0) {
2951
/* call callback for this attribute */
2952
if (callback != NULL) {
2953
callback (attr, xop, 0, userdata);
2962
NLM_EXTERN Nlm_XmlObjPtr FreeXmlObject (
2967
Nlm_XmlObjPtr curr, next;
2969
if (xop == NULL) return NULL;
2971
MemFree (xop->name);
2972
MemFree (xop->contents);
2974
curr = xop->attributes;
2975
while (curr != NULL) {
2978
FreeXmlObject (curr);
2982
curr = xop->children;
2983
while (curr != NULL) {
2986
FreeXmlObject (curr);
2995
NLM_EXTERN Nlm_XmlObjPtr ParseXmlString (
3001
Nlm_XmlObjPtr root, xop;
3004
if (StringHasNoText (str)) return NULL;
3005
tmp = StringSave (str);
3006
if (tmp == NULL) return NULL;
3008
head = TokenizeXmlString (tmp);
3011
if (head == NULL) return NULL;
3013
root = ParseXmlTokens (head);
3014
ValNodeFreeData (head);
3016
if (root == NULL) return NULL;
3017
xop = root->children;
3018
root->children = NULL;
3019
FreeXmlObject (root);
3025
Note: Use <urlquery.h> QUERY_CopyResultsToString (conn) to get XML string
3026
directly from network connection without going through file intermediate.
3029
NLM_EXTERN Nlm_CharPtr XmlFileToString (
3035
Nlm_Char line [4096];
3037
ValNodePtr head = NULL, tail = NULL;
3039
if (ifp == NULL) return NULL;
3041
if (! FileCacheSetup (&fc, ifp)) return NULL;
3043
str = FileCacheReadLine (&fc, line, sizeof (line), NULL);
3045
while (str != NULL) {
3046
TrimSpacesAroundString (str);
3047
CompressSpaces (str);
3049
ValNodeCopyStrEx (&head, &tail, 0, line);
3055
str = FileCacheReadLine (&fc, line, sizeof (line), NULL);
3058
str = ValNodeMergeStrs (head);
3059
ValNodeFreeData (head);
3064
static void PrintXmlObject (
3065
Nlm_XmlObjPtr master,
3067
Nlm_Boolean useTabs,
3068
Nlm_Boolean altSelfClose,
3073
Nlm_XmlObjPtr attr, xop;
3075
Nlm_CharPtr tmp, tmp1, tmp2;
3076
Nlm_CharPtr spaces = " ";
3078
if (master == NULL || fp == NULL) return;
3084
for (xop = master; xop != NULL; xop = xop->next) {
3085
if (StringHasNoText (xop->name)) continue;
3086
for (i = 0; i < level; i++) {
3087
fprintf (fp, "%s", spaces);
3089
fprintf (fp, "<%s", xop->name);
3090
for (attr = xop->attributes; attr != NULL; attr = attr->next) {
3091
if (StringHasNoText (attr->name) || StringHasNoText (attr->contents)) continue;
3092
tmp1 = EncodeXml (attr->name);
3093
tmp2 = EncodeXml (attr->contents);
3094
if (tmp1 != NULL && tmp2 != NULL) {
3095
fprintf (fp, " %s=\"%s\"", tmp1, tmp2);
3100
if (xop->contents != NULL) {
3101
tmp = EncodeXml (xop->contents);
3103
fprintf (fp, ">%s", tmp);
3106
fprintf (fp, "</%s>", xop->name);
3107
} else if (xop->children != NULL) {
3108
fprintf (fp, ">\n");
3109
PrintXmlObject (xop->children, fp, useTabs, altSelfClose, level + 1);
3110
for (i = 0; i < level; i++) {
3111
fprintf (fp, "%s", spaces);
3113
fprintf (fp, "</%s>", xop->name);
3114
} else if (altSelfClose) {
3115
fprintf (fp, "></%s>", xop->name);
3123
NLM_EXTERN void WriteXmlObject (
3129
if (xop == NULL || fp == NULL) return;
3131
PrintXmlObject (xop, fp, FALSE, FALSE, 0);
3134
NLM_EXTERN void WriteXmlObjectEx (
3137
Nlm_Boolean useTabs,
3138
Nlm_Boolean altSelfClose
3142
if (xop == NULL || fp == NULL) return;
3144
PrintXmlObject (xop, fp, useTabs, altSelfClose, 0);