1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24/* <DESC> 25 * Stream-parse a document using the streaming Expat parser. 26 * </DESC> 27 */ 28/* Written by David Strauss 29 * 30 * Expat => https://libexpat.github.io/ 31 * 32 * gcc -Wall -I/usr/local/include xmlstream.c -lcurl -lexpat -o xmlstream 33 * 34 */ 35 36#include <stdio.h> 37#include <stdlib.h> 38#include <string.h> 39#include <assert.h> 40 41#include <expat.h> 42#include <curl/curl.h> 43 44struct MemoryStruct { 45 char *memory; 46 size_t size; 47}; 48 49struct ParserStruct { 50 int ok; 51 size_t tags; 52 size_t depth; 53 struct MemoryStruct characters; 54}; 55 56static void startElement(void *userData, const XML_Char *name, 57 const XML_Char **atts) 58{ 59 struct ParserStruct *state = (struct ParserStruct *) userData; 60 state->tags++; 61 state->depth++; 62 63 /* Get a clean slate for reading in character data. */ 64 free(state->characters.memory); 65 state->characters.memory = NULL; 66 state->characters.size = 0; 67} 68 69static void characterDataHandler(void *userData, const XML_Char *s, int len) 70{ 71 struct ParserStruct *state = (struct ParserStruct *) userData; 72 struct MemoryStruct *mem = &state->characters; 73 74 char *ptr = realloc(mem->memory, mem->size + len + 1); 75 if(!ptr) { 76 /* Out of memory. */ 77 fprintf(stderr, "Not enough memory (realloc returned NULL).\n"); 78 state->ok = 0; 79 return; 80 } 81 82 mem->memory = ptr; 83 memcpy(&(mem->memory[mem->size]), s, len); 84 mem->size += len; 85 mem->memory[mem->size] = 0; 86} 87 88static void endElement(void *userData, const XML_Char *name) 89{ 90 struct ParserStruct *state = (struct ParserStruct *) userData; 91 state->depth--; 92 93 printf("%5lu %10lu %s\n", state->depth, state->characters.size, name); 94} 95 96static size_t parseStreamCallback(void *contents, size_t length, size_t nmemb, 97 void *userp) 98{ 99 XML_Parser parser = (XML_Parser) userp; 100 size_t real_size = length * nmemb; 101 struct ParserStruct *state = (struct ParserStruct *) XML_GetUserData(parser); 102 103 /* Only parse if we are not already in a failure state. */ 104 if(state->ok && XML_Parse(parser, contents, real_size, 0) == 0) { 105 int error_code = XML_GetErrorCode(parser); 106 fprintf(stderr, "Parsing response buffer of length %lu failed" 107 " with error code %d (%s).\n", 108 real_size, error_code, XML_ErrorString(error_code)); 109 state->ok = 0; 110 } 111 112 return real_size; 113} 114 115int main(void) 116{ 117 CURL *curl_handle; 118 CURLcode res; 119 XML_Parser parser; 120 struct ParserStruct state; 121 122 /* Initialize the state structure for parsing. */ 123 memset(&state, 0, sizeof(struct ParserStruct)); 124 state.ok = 1; 125 126 /* Initialize a namespace-aware parser. */ 127 parser = XML_ParserCreateNS(NULL, '\0'); 128 XML_SetUserData(parser, &state); 129 XML_SetElementHandler(parser, startElement, endElement); 130 XML_SetCharacterDataHandler(parser, characterDataHandler); 131 132 /* Initialize a libcurl handle. */ 133 curl_global_init(CURL_GLOBAL_DEFAULT); 134 curl_handle = curl_easy_init(); 135 curl_easy_setopt(curl_handle, CURLOPT_URL, 136 "https://www.w3schools.com/xml/simple.xml"); 137 curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, parseStreamCallback); 138 curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)parser); 139 140 printf("Depth Characters Closing Tag\n"); 141 142 /* Perform the request and any follow-up parsing. */ 143 res = curl_easy_perform(curl_handle); 144 if(res != CURLE_OK) { 145 fprintf(stderr, "curl_easy_perform() failed: %s\n", 146 curl_easy_strerror(res)); 147 } 148 else if(state.ok) { 149 /* Expat requires one final call to finalize parsing. */ 150 if(XML_Parse(parser, NULL, 0, 1) == 0) { 151 int error_code = XML_GetErrorCode(parser); 152 fprintf(stderr, "Finalizing parsing failed with error code %d (%s).\n", 153 error_code, XML_ErrorString(error_code)); 154 } 155 else { 156 printf(" --------------\n"); 157 printf(" %lu tags total\n", state.tags); 158 } 159 } 160 161 /* Clean up. */ 162 free(state.characters.memory); 163 XML_ParserFree(parser); 164 curl_easy_cleanup(curl_handle); 165 curl_global_cleanup(); 166 167 return 0; 168} 169