简体   繁体   中英

How to use GTK and libcurl at the same time in C?

I'm trying to develop an application in C with GTK for the GUI, libcurl to get data by a web API and cJSON to parse JSON.

My problem is that when I do my request with libcurl after gtk_init, the data that I get can not be parsing in JSON. Otherwise, if I get the data and parse it before gtk_init, the parsing works fine.

I have an example to demonstrate it, the first printf in the main returns the JSON correctly but the second printf which is after gtk_init returns NULL (to be exact, the parsing stops at the first decimal number and fail) :

initString :

void initString(String * s) {
  s->len = 0;
  s->ptr = malloc(s->len + 1);
  if (s->ptr == NULL) {
    fprintf(stderr, "malloc() failed\n");
    exit(EXIT_FAILURE);
  }
  s->ptr[0] = '\0';
}

writeFunc :

size_t writeFunc(void *ptr, size_t size, size_t nmemb, String * s)
{
  size_t newLen = s->len + size*nmemb;
  s->ptr = realloc(s->ptr, newLen + 1);
  if (s->ptr == NULL) {
    fprintf(stderr, "realloc() failed\n");
    exit(EXIT_FAILURE);
  }
  memcpy(s->ptr + s->len, ptr, size * nmemb);
  s->ptr[newLen] = '\0';
  s->len = newLen;

  return size * nmemb;
}

My function to get data with libcurl :

char * getData(gpointer user_data)
{
curl_global_init(CURL_GLOBAL_ALL);
CURL * curl;
char * url = user_data;
CURLcode res;
String s;
struct curl_slist * headers = NULL;
curl = curl_easy_init();

if(curl)
{
    initString(&s);

    headers = curl_slist_append(headers, "Accept: application/json");
    headers = curl_slist_append(headers, "Content-Type: application/json");
    headers = curl_slist_append(headers, "charsets: utf-8");

    //curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(curl, CURLOPT_URL, url);

    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, FALSE);
    curl_easy_setopt(curl, CURLOPT_SSLVERSION, 6);

    curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
    //write data in a string
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunc);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);

    res = curl_easy_perform(curl);
}

if(res != CURLE_OK)
  fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));

curl_easy_cleanup(curl);
return s.ptr;
}

The main :

int main(int argc, char ** argv)
{
char * str;
cJSON * json;

str = getData("https://data.culture.gouv.fr/api/records/1.0/search/?dataset=liste-et-localisation-des-musees-de-france&facet=ville&sort=ville&facet=nomdep&refine.nomdep=AIN&rows=1");
json = cJSON_Parse(str);
printf("DATA : %s\n", cJSON_Print(json));//returns the JSON perfectly

gtk_init(&argc,&argv);

str = getData("https://data.culture.gouv.fr/api/records/1.0/search/?dataset=liste-et-localisation-des-musees-de-france&facet=ville&sort=ville&facet=nomdep&refine.nomdep=AIN&rows=1");
json = cJSON_Parse(str);
printf("ERROR : %s\n", cJSON_GetErrorPtr());//returns half of data
printf("DATA : %s\n", cJSON_Print(json));//returns NULL

gtk_main();

curl_global_cleanup();

return EXIT_SUCCESS;
}

I have tried to resolve this by creating threads with g_idle_add, gdk_threads_idle_add, gdk_threads_entry and gdk_threads_leave, pthread_create and pthread_join but nothing worked.

Does someone know how to resolve this problem ?

Thanks.

You need to narrow down your problem. In other words, you need "M" in MVCE . Currently you have three libraries:

  • Gtk+
  • cURL
  • cJSON

that might interact in arbitrary ways. What you need to check:

  • Does cURL return different data before and after gtk_init call? If no, then problem is not with cURL.

  • If you hardcode JSON data in your program and parse it with cJSON, are results different before and after gtk_init ? If so, problem is in cJSON. If no, problem is not related to cJSON.


My guess is as follows. gtk_init does several things, amongst them sets locale . Since you fetch data from .fr domain, I assume that you have French locale set on your computer.

to be exact, the parsing stops at the first decimal number and fail

In French, decimal separator is comma , instead of period . , so I suppose that after GTK+ changes locale, cJSON starts to look for , in decimal numbers but it finds . and fails.

Possible solutions

  • As a workaround, call gtk_disable_setlocale before gtk_init . This might cause unintended consequences, eg. your program starts to display numbers in English format instead of French in its UI.

  • The real solution would be to raise bug in cJSON, as JSON parsing should not take locale into account when parsing numbers. JSON mandates period . as decimal separator.

I doubt very much that this has anything at all to do with the gtk_init() call. It sounds like memory corruption. This would likely be the case eg if you returned a pointer to data that was allocated and then cleaned up by curl. In this case both calls are wrong, you just happened to get away with it the first time as the memory was hanging around untouched in that case. Check the API for function calls you make. Some will allocate memory that needs to be freed, others don't.

I suggest you use build with -g and use gdb to test your code. This will help narrow down where the problem lies. Take a look and see if the strings are identical.

cURL returns the same data before and after, I checked it by putting data in 2 strings and strcmp returns 0.

Indeed, I'm french and there may be a confusion between , and . .

gtk_disable_setlocale solved my problem !

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM