diff --git a/gget.c b/gget.c index 5135af2..43ce4ca 100644 --- a/gget.c +++ b/gget.c @@ -1,5 +1,5 @@ /* - Copyright 2014 Grégory Soutadé + Copyright 2014-2016 Grégory Soutadé This file is part of gget. @@ -86,6 +86,7 @@ static int get_console_width() typedef struct { curl_off_t dltotal; curl_off_t dlnow; + curl_off_t dllast; unsigned speed; } stats_t ; @@ -101,6 +102,7 @@ typedef struct { unsigned already_downloaded; unsigned start; unsigned end; + unsigned max_chunk_size; curl_off_t max_speed; stats_t* stats; } transfert_t; @@ -216,8 +218,8 @@ static int progress_cb(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl curl_easy_getinfo(t->curl, CURLINFO_SPEED_DOWNLOAD, &speed_d); - t->stats[t->id].dltotal = dltotal + t->already_downloaded; - t->stats[t->id].dlnow = dlnow + t->already_downloaded; + t->stats[t->id].dlnow += (dlnow - t->stats[t->id].dllast); + t->stats[t->id].dllast = dlnow; t->stats[t->id].speed = speed_d; return 0; @@ -226,14 +228,12 @@ static int progress_cb(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl void* do_transfert(transfert_t* t) { CURLcode res; - char range[50]; - - snprintf(range, sizeof(range), "%u-%u", t->start, t->end); - + char range[64]; + unsigned start, end, chunk_size; + curl_easy_setopt(t->curl, CURLOPT_USERAGENT, t->user_agent); curl_easy_setopt(t->curl, CURLOPT_URL, t->url); curl_easy_setopt(t->curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(t->curl, CURLOPT_RANGE, range); if (t->max_speed) curl_easy_setopt(t->curl, CURLOPT_MAX_RECV_SPEED_LARGE, t->max_speed); @@ -248,13 +248,37 @@ void* do_transfert(transfert_t* t) curl_easy_setopt(t->curl, CURLOPT_XFERINFOFUNCTION, progress_cb); curl_easy_setopt(t->curl, CURLOPT_XFERINFODATA, t); - /* Perform the request, res will get the return code */ - res = curl_easy_perform(t->curl); - /* Check for errors */ - if(res != CURLE_OK) - fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + start = t->start; + if (t->max_chunk_size && (t->end - t->start) > t->max_chunk_size) + chunk_size = t->max_chunk_size; + else + chunk_size = t->end - t->start; + end = start + chunk_size; + while (start < t->end) + { + snprintf(range, sizeof(range), "%u-%u", start, end); + curl_easy_setopt(t->curl, CURLOPT_RANGE, range); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(t->curl); + /* Check for errors */ + if(res != CURLE_OK) + { + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + break; + } + + start += chunk_size + 1; + if (t->max_chunk_size && (t->end - start) > t->max_chunk_size) + chunk_size = t->max_chunk_size; + else + chunk_size = t->end - start; + end = start + chunk_size; + t->stats[t->id].dllast = 0; + } + return NULL; } @@ -262,7 +286,7 @@ static int configure_transfert(int id, transfert_t* t, char* url, char* filename, unsigned start, unsigned end, unsigned max_speed, char* user_agent, - int* exists) + unsigned max_chunk_size, int* exists) { // filename + . + number + \0 unsigned filename_size = strlen(filename)+1+3+1; @@ -284,6 +308,7 @@ static int configure_transfert(int id, transfert_t* t, t->end = end; t->max_speed = max_speed; t->user_agent = user_agent; + t->max_chunk_size = max_chunk_size; if (stat(t->tmp_filename, &s)) { @@ -371,6 +396,7 @@ static int get_file_info(char* url, char* user_agent, curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); if (!quiet) curl_easy_setopt(curl, CURLOPT_HEADER, 1L); @@ -452,13 +478,14 @@ static void find_free_file(char** filename) static void usage(char* program_name) { printf("%s: Parallel HTTP file download\n", program_name); - printf("usage: %s [-n nb_threads] [-l speed_limit] [-o out_filename] [-u user_agent] [-q] [-h] url\n", + printf("usage: %s [-n nb_threads] [-l speed_limit] [-o out_filename] [-u user_agent] [-m max_chunk_size[kKmMgG] ] [-q] [-h] url\n", program_name); printf("\t-n : Specify number of threads (default : %d)\n", DEFAULT_NB_THREADS); printf("\t-l : Download speed limit for all threads (not per thread)\n"); printf("\t-o : Out filename, default is retrieved by GET request or '%s' if not found\n", DEFAULT_OUT_FILENAME); printf("\t-u : User agent, default is '%s'\n", DEFAULT_USER_AGENT); + printf("\t-m : Max chunk size in bytes\n"); printf("\t-q : Quiet mode\n"); printf("\t-h : Display help\n"); } @@ -467,14 +494,15 @@ int main(int argc, char** argv) { pthread_t display_thread = 0; unsigned nb_threads = DEFAULT_NB_THREADS; - char* user_agent = strdup(DEFAULT_USER_AGENT); + char* user_agent = strdup(DEFAULT_USER_AGENT), *endptr; stats_t* stats = NULL; int ret = -1, i; stats_params_t stats_params; transfert_t* transferts = NULL; - unsigned total_size, thread_size; - unsigned start, end, max_speed = 0, quiet = 0; - char* out_filename = NULL, *url = NULL; + unsigned total_size, thread_size, multiplier=1; + double displayed_size; + unsigned start, end, max_speed = 0, quiet = 0, max_chunk_size = 0; + char* out_filename = NULL, *url = NULL, c; char* suffix = "B"; void* res; int opt; @@ -484,7 +512,7 @@ int main(int argc, char** argv) int continuous_mode=0; int exists; - while ((opt = getopt(argc, argv, "hn:l:o:u:q")) != -1) { + while ((opt = getopt(argc, argv, "hn:l:o:u:qm:")) != -1) { switch (opt) { case 'n': nb_threads = atoi(optarg); @@ -499,6 +527,39 @@ int main(int argc, char** argv) case 'l': max_speed = atoi(optarg); break; + case 'm': + if (strlen(optarg) > 1) + { + c = optarg[strlen(optarg)-1]; + if (c < '0' || c > '9') + { + switch(c) + { + case 'g': + case 'G': + multiplier *= 1024; + case 'm': + case 'M': + multiplier *= 1024; + case 'k': + case 'K': + multiplier *= 1024; + optarg[strlen(optarg)-1] = 0; + break; + default: + usage(argv[0]); + return 1; + } + } + } + max_chunk_size = strtoul(optarg, &endptr, 0); + if (*endptr) + { + usage(argv[0]); + return 1; + } + max_chunk_size *= multiplier; + break; case 'o': out_filename = strdup(optarg); break; @@ -539,7 +600,29 @@ int main(int argc, char** argv) goto end; } else - printf("Save in '%s'\n\n", out_filename); + { + displayed_size = (double)total_size; + suffix = "B"; + if (displayed_size > (1024)) + { + displayed_size /= 1024.0; + suffix = "kB"; + } + + if (displayed_size > (1024)) + { + displayed_size /= 1024.0; + suffix = "MB"; + } + + if (displayed_size > (1024)) + { + displayed_size /= 1024.0; + suffix = "GB"; + } + + printf("Save in '%s' (%.2f%s)\n\n", out_filename, displayed_size, suffix); + } stats = malloc(sizeof(*stats)*nb_threads); memset(stats, 0, sizeof(*stats)*nb_threads); @@ -561,10 +644,16 @@ int main(int argc, char** argv) end = total_size; ret = configure_transfert(i, &transferts[i], url, out_filename, - start, end, max_speed, user_agent, &exists); + start, end, max_speed, user_agent, + max_chunk_size, &exists); if (ret) goto end; + transferts[i].stats[i].dltotal = transferts[i].already_downloaded + + (end - start); + transferts[i].stats[i].dlnow = transferts[i].already_downloaded; + transferts[i].stats[i].dllast = 0; + // First set continuous mode if (i == 0) { @@ -583,7 +672,8 @@ int main(int argc, char** argv) // Check for last temporary file configure_transfert(i, &transferts[i], url, out_filename, - start, end, max_speed, user_agent, &exists); + start, end, max_speed, user_agent, max_chunk_size, + &exists); unlink(transferts[i].tmp_filename); if (exists) @@ -665,7 +755,8 @@ end: if (transferts) free(transferts); - printf("\n"); + if (!quiet) + printf("\n"); return ret; }