loc.c (4212B)
1 #include <stdio.h> 2 #include <string.h> 3 4 #include <curl/curl.h> 5 #include <expat.h> 6 7 #include "loc.h" 8 9 #ifdef XML_LARGE_SIZE 10 #define XML_FMT_INT_MOD "ll" 11 #else 12 #define XML_FMT_INT_MOD "l" 13 #endif 14 15 #ifdef XML_UNICODE_WCHAR_T 16 #define XML_FMT_STR "ls" 17 #else 18 #define XML_FMT_STR "s" 19 #endif 20 21 int 22 lb_book_destroy(struct lb_book* book) 23 { 24 free(book->title); 25 free(book->classification); 26 } 27 28 /* 29 * Parsing 30 */ 31 32 struct lb_parsing_state 33 { 34 /* Flags */ 35 int found; 36 int in_number_of_records; 37 int in_classification; 38 int in_title; 39 int in_name; 40 int in_name_part; 41 42 /* Output */ 43 struct lb_book* out; 44 }; 45 46 static void XMLCALL 47 start_element(void* user_data, const XML_Char* name, const XML_Char** atts) 48 { 49 struct lb_parsing_state* const state = user_data; 50 51 (void)atts; 52 53 if (strcmp(name, "zs:numberOfRecords") == 0) { 54 state->in_number_of_records = 1; 55 } else if (strcmp(name, "title") == 0) { 56 state->in_title = 1; 57 } else if (strcmp(name, "classification") == 0 && 58 strcmp(atts[0], "authority") == 0 && strcmp(atts[1], "lcc") == 0) { 59 state->in_classification = 1; 60 } else if (strcmp(name, "name") == 0 && strcmp(atts[3], "primary") == 0) { 61 state->in_name = 1; 62 } else if (state->in_name && strcmp(name, "namePart") == 0) { 63 state->in_name_part = 1; 64 } 65 } 66 67 static void 68 strdup_or_concat(char** s0, const char* s1, int s1_length) 69 { 70 if (*s0 == NULL) { 71 *s0 = strndup(s1, s1_length); 72 return; 73 } else { 74 /* TODO: Use realloc */ 75 char* tmp = NULL; 76 asprintf(&tmp, "%s %.*s", *s0, s1_length, s1); 77 free(*s0); 78 *s0 = tmp; 79 } 80 } 81 82 static void XMLCALL 83 element_contents(void* user_data, const XML_Char* contents, int length) 84 { 85 struct lb_parsing_state* const state = user_data; 86 87 if (state->in_number_of_records) { 88 state->found = atoi(contents); 89 } else if (state->in_title) { 90 strdup_or_concat(&state->out->title, contents, length); 91 } else if (state->in_classification) { 92 strdup_or_concat(&state->out->classification, contents, length); 93 } else if (state->in_name_part) { 94 strdup_or_concat(&state->out->author, contents, length); 95 } 96 } 97 98 static void XMLCALL 99 end_element(void* user_data, const XML_Char* name) 100 { 101 struct lb_parsing_state* const state = user_data; 102 103 if (strcmp(name, "zs:numberOfRecords") == 0) { 104 state->in_number_of_records = 0; 105 } else if (strcmp(name, "title") == 0) { 106 state->in_title = 0; 107 } else if (strcmp(name, "classification") == 0) { 108 state->in_classification = 0; 109 } else if (strcmp(name, "name") == 0) { 110 state->in_name = 0; 111 } else if (strcmp(name, "namePart") == 0) { 112 state->in_name_part = 0; 113 } 114 } 115 116 static size_t 117 write_data(char* buffer, size_t size, size_t nmemb, void* userp) 118 { 119 size_t realsize = size * nmemb; 120 XML_Parser parser = (XML_Parser)userp; 121 122 if (XML_Parse(parser, buffer, (int)realsize, 0) == XML_STATUS_ERROR) { 123 fprintf(stderr, 124 "Parse error at line %" XML_FMT_INT_MOD "u:\n%" XML_FMT_STR "\n", 125 XML_GetCurrentLineNumber(parser), 126 XML_ErrorString(XML_GetErrorCode(parser))); 127 return 0; 128 } 129 130 return size * nmemb; 131 } 132 133 /* 134 * Lookup 135 */ 136 137 int 138 lb_loc_lookup(struct lb_book* book, char* isbn) 139 { 140 /* Create parser */ 141 XML_Parser parser = XML_ParserCreate(NULL); 142 if (!parser) { 143 return -1; 144 } 145 146 struct lb_parsing_state parsing_state = { 147 .out = book, 148 }; 149 150 XML_SetUserData(parser, &parsing_state); 151 XML_SetElementHandler(parser, start_element, end_element); 152 XML_SetCharacterDataHandler(parser, element_contents); 153 154 CURL* curl = curl_easy_init(); 155 if (!curl) { 156 return -1; 157 } 158 159 char url[512]; 160 snprintf(url, 161 sizeof(url), 162 "http://lx2.loc.gov:210/" 163 "lcdb?version=2.0&operation=searchRetrieve&startRecord=1&" 164 "maximumRecords=1&recordSchema=mods&query=bath.isbn=%s", 165 isbn); 166 167 curl_easy_setopt(curl, CURLOPT_URL, url); 168 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); 169 curl_easy_setopt(curl, CURLOPT_WRITEDATA, parser); 170 CURLcode result = curl_easy_perform(curl); 171 curl_easy_cleanup(curl); 172 173 if (result == CURLE_OK) 174 XML_Parse(parser, NULL, 0, 1); 175 XML_ParserFree(parser); 176 177 if (result != CURLE_OK) 178 return -1; 179 180 return parsing_state.found; 181 }