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 }