#include <stdlib.h>
#include <curl/curl.h>

static const char *urls[] = {
    "https://www.microsoft.com",
    "https://www.google.com",
    "https://www.yahoo.com",
    "https://www.ibm.com",
    "https://www.mysql.com",
    "https://www.paypal.com",
    "https://www.ripe.net",
    "https://www.amazon.com",
};

#define MAX 4 /* number of simultaneous transfers */
#define CNT (sizeof(urls)/sizeof(char*)) /* total number of transfers to do */

static int cb(char *d, size_t n, size_t l, void *p)
{
    /* take care of the data here, ignored in this example */
    (void)d;
    (void)p;
    return n*l;
}

static void init(CURLSH *share,CURLM *cm, int i)
{
    CURL *eh = curl_easy_init();

    fprintf(stderr, "init %d %s\n",i,urls[i]);
    curl_easy_setopt(eh, CURLOPT_WRITEFUNCTION, cb);
    curl_easy_setopt(eh, CURLOPT_HEADER, 0);
    curl_easy_setopt(eh, CURLOPT_URL, urls[i]);
    curl_easy_setopt(eh, CURLOPT_PRIVATE, urls[i]);
    curl_easy_setopt(eh, CURLOPT_VERBOSE, 0);
    curl_easy_setopt(eh, CURLOPT_SSL_VERIFYPEER, 0);
    curl_easy_setopt(eh, CURLOPT_SSL_VERIFYHOST, 0);

    curl_multi_add_handle(cm, eh);
    curl_easy_setopt(eh,CURLOPT_SHARE,share);

}

#define NUMM 3

int curltest_main(int argc,char** argv)
{
    CURLM *cm[NUMM];
    CURLMsg *msg;
    CURLSH *share;
    long L;
    unsigned int C=0;
    int M, Q, U = -1;
    fd_set R, W, E;
    int repeat = 2;
    struct timeval T;
    int j;

    curl_global_init(CURL_GLOBAL_ALL);
    share = curl_share_init();

    if ( !(argc>2 && *argv[2]=='n') )
        curl_share_setopt(share,CURLSHOPT_SHARE,CURL_LOCK_DATA_SSL_CTX);

    for (j=0; j<NUMM; ++j)
        cm[j] = curl_multi_init();

    j = 0;
    for (C = 0; C < MAX; ++C) {
        init(share,cm[j%NUMM], C);
        ++j;
    }
    while (U) {
        U = 0;
        for (j=0; j<NUMM; ++j) {
            int u = 0;
            while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(cm[j], &u));
            U += u;
        }

        if (U) {
            for (j=0; j<NUMM; ++j) {

                FD_ZERO(&R);
                FD_ZERO(&W);
                FD_ZERO(&E);

                if (curl_multi_fdset(cm[j], &R, &W, &E, &M)) {
                    fprintf(stderr, "E: curl_multi_fdset\n");
                    return EXIT_FAILURE;
                }

                if ( M==-1 )
                    continue;
                /* In a real-world program you OF COURSE check the return that maxfd is
                   bigger than -1 so that the call to select() below makes sense! */

                if (curl_multi_timeout(cm[j], &L)) {
                    fprintf(stderr, "E: curl_multi_timeout\n");
                    return EXIT_FAILURE;
                }

                T.tv_sec = L/1000;
                T.tv_usec = (L%1000)*1000;

                if (0 > select(M+1, &R, &W, &E, &T)) {
                    fprintf(stderr, "E: select\n");
                    return EXIT_FAILURE;
                }
                while ((msg = curl_multi_info_read(cm[j], &Q))) {
                    if (msg->msg == CURLMSG_DONE) {
                        char *url;
                        CURL *e = msg->easy_handle;
                        curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &url);
                        fprintf(stderr, "R: %d - %s <%s>\n",
                                msg->data.result, curl_easy_strerror(msg->data.result), url);
                        curl_multi_remove_handle(cm[j], e);
                        curl_easy_cleanup(e);
                    }
                    else {
                        fprintf(stderr, "E: CURLMsg (%d)\n", msg->msg);
                    }
                    if (C < CNT*repeat) {
                        init(share,cm[j], (++C)%CNT);
                    }
                }
            }
        }
    }
    for (j=0; j<NUMM; ++j)
        curl_multi_cleanup(cm[j]);
    curl_share_cleanup(share);

    curl_global_cleanup();

    return EXIT_SUCCESS;
}
