davoid
davoid

Reputation: 327

Program does not display download speed and size of file (curl)

I have a problem with curl on Ubuntu. I want it to show download speed and size of downloaded file. I've declared double* sized and double* speedd and CURL *curl. I keep getting segmentation fault. Program compiles to moment when it says "File downloaded".It should also tell me information from CURLINFO_SIZE_DOWNLOAD and CURLINFO_SPEED_DOWNLOAD. Please help.

#define CURL_STATICLIB
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <stdlib.h>

size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
    size_t written;
    written = fwrite(ptr, size, nmemb, stream);
    return written;
}

int main(void)
{
    CURL *curl;
    FILE *fp;
    CURLcode res;
    FILE *stream = stdout;
    int x;
    char y[1024];
    double* sized;
    double* speedd;
    char* name = calloc(1, 1024);
    char* outfilename;
    char* path_pdf = "/home/user/Desktop/%s.pdf";
    char* path_jpg = "/home/user/Desktop/%s.jpg";
    char* path_txt = "/home/user/Desktop/%s.txt";
    char* path_mp3 = "/home/user/Desktop/%s.mp3";
    char* path_avi = "/home/user/Desktop/%s.avi";
    printf("Enter file url: \n"); // for example http://oi58.tinypic.com/15nk3de.jpg
    scanf ("%s",y);
    char *url = y;
    printf("Type name of file \n");
    scanf("%s",name);
    char *result_pdf = malloc(strlen(path_pdf) - 2 + strlen(name) + 1);
    sprintf(result_pdf, path_pdf, name);
    char *result_jpg = malloc(strlen(path_jpg) - 2 + strlen(name) + 1);
    sprintf(result_jpg, path_jpg, name);
    char *result_txt = malloc(strlen(path_txt) - 2 + strlen(name) + 1);
    sprintf(result_txt, path_txt, name);
    char *result_mp3 = malloc(strlen(path_mp3) - 2 + strlen(name) + 1);
    sprintf(result_mp3, path_mp3, name);
    char *result_avi = malloc(strlen(path_avi) - 2 + strlen(name) + 1);
    sprintf(result_avi, path_avi, name);
    printf("Choose type of file:\n [0] - pdf\n [1] - jpg\n [2] - txt\n [3] - mp3\n [4] - avi\n "); //choose 1
    scanf("%d",&x);
    switch(x){
    case 0:
    outfilename = result_pdf;
    break;
    case 1:
    outfilename = result_jpg;
    break;
    case 2:
    outfilename = result_txt;
    break;
    case 3:
    outfilename = result_mp3;
    break;
    case 4:
    outfilename = result_avi;
    break;

}
    curl = curl_easy_init();
    if (curl)
    {
        fp = fopen(outfilename,"wb");
        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
        printf("Download started!\n\n");
        curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L);
        res = curl_easy_perform(curl);
        if(res == CURLE_OK) {
        printf("File downloaded!\n\n");}
        else {
        printf("Transfer failed!");}
        curl_easy_getinfo(curl,CURLINFO_SIZE_DOWNLOAD,&sized);
        fprintf(stream,"%.3f \n", sized);
        curl_easy_getinfo(curl,CURLINFO_SPEED_DOWNLOAD,&speedd);
        fprintf(stream, "%.3f \n", speedd);
        curl_easy_cleanup(curl);
        fclose(fp);
    }
    return 0;

}

Upvotes: 1

Views: 771

Answers (1)

dho
dho

Reputation: 2370

The code you provided in your question has a few problems:

  1. You aren't including stdlib.h.

  2. name isn't allocated

You have not allocated space for storing the input for the path part of the uri. When you call scanf(3) to read the path name, it tries to store it into the memory location referenced by name. As name is uninitialized, the value of the pointer is undefined. This causes a segmentation fault.

Either allocate memory for name or change the type from a pointer to an array, sized appropriately.

 char *name = calloc(1, 1024);
 printf("Type name of file \n");
 scanf("%s",name);

Or

 char name[1024];
 printf("Type name of file \n");
 scanf("%s",name);

Will both likely work fine for your needs. However, do note that these methods introduce heap or stack overflow bugs because scanf(3) does not perform bounds checking. The usual pattern for this sort of thing is to use fgets(3) to read a line of input and sscanf(3) to tokenize it. In this case, you really only need fgets(3).

Do not use gets(3).

  1. You are not printing the download information on success.

In your code, you only print out the status information from cURL on failure:

if (res == CURLE_OK) {
    printf("File downloaded!\n\n");
} else {
    printf("Transfer failed!");}
    curl_easy_getinfo(curl,CURLINFO_SIZE_DOWNLOAD,sized);
    curl_easy_getinfo(curl,CURLINFO_SPEED_DOWNLOAD,speedd);
    curl_easy_cleanup(curl);
    fclose(fp);
}

Instead, I think you mean to do:

if (res == CURLE_OK) {
    printf("File downloaded!\n\n");
} else {
    printf("Transfer failed!");}
}

curl_easy_getinfo(curl,CURLINFO_SIZE_DOWNLOAD,sized);
curl_easy_getinfo(curl,CURLINFO_SPEED_DOWNLOAD,speedd);
curl_easy_cleanup(curl);
fclose(fp);

I would hazard to guess that the reason for this problem is because of your code style. It seems like you're at least semi-new to C. Writing with consistent style will improve your code readability and help you avoid errors like this. What sort of style you use doesn't matter so much as long as you stick to it.

  1. Your program's behavior is unexpected for what it does, inflating the code size

The architecture of your program is a bit obtuse. It's far more common to allow a user to specify arguments on the command line. In this case, you'd have an option to name the output file and an option to name the source URI.

Even if you're doing this to learn the standard library, it would be good to continue with your implementation to behave in a more expected manner. See also the getopt(3) manpage. You might find that once you do this, your program shrinks in size by about half.

A smaller program is a better program.

  1. You're passing undefined pointer values into curl_easy_getinfo.

You've declared

double* sized;
double* speedd;

but have not provided storage for these. When you call curl_easy_getinfo, it crashes. You mentioned in a comment that you fixed this by passing the address to curl_easy_getinfo. This stops the crash because you've provided the storage for the address. However, if you were to try to print that information, you'd either get nothing, or crash again.

Instead, what you want is:

double sized;
double speedd;
...
curl_easy_getinfo(curl,CURLINFO_SIZE_DOWNLOAD,&sized);
curl_easy_getinfo(curl,CURLINFO_SPEED_DOWNLOAD,&speedd);
  1. You're not printing out the size or speed anywhere.

Once you've gotten them, you're not printing the size or speed at the end of your main function. You need to print them out like:

printf("Size: %g KB\nSpeed: %g KB/s\n", sized / 1024., speedd / 1024.);

With these changes, this is the output of a full run:

Enter file url:
http://oi58.tinypic.com/15nk3de.jpg
Type name of file
foo.jpg
Choose type of file:
 [0] - pdf
 [1] - jpg
 [2] - txt
 [3] - mp3
 [4] - avi
 1
Download started!

* Hostname was NOT found in DNS cache
*   Trying 209.17.68.209...
* Connected to oi58.tinypic.com (209.17.68.209) port 80 (#0)
> GET /15nk3de.jpg HTTP/1.1
Host: oi58.tinypic.com
Accept: */*

< HTTP/1.1 200 OK
* Server Apache is not blacklisted
< Server: Apache
< Last-Modified: Thu, 13 Feb 2014 19:51:01 GMT
< ETag: "1d7a5-4f24f024dc8be"
< Cache-Control: max-age=21600
< Expires: Sun, 04 Jan 2015 00:02:56 GMT
< Content-Type: image/jpeg
< Content-Length: 120741
< Accept-Ranges: bytes
< Date: Sat, 03 Jan 2015 19:24:19 GMT
< X-Varnish: 1269536769 1268437896
< Age: 4883
< Via: 1.1 varnish
< Connection: keep-alive
< X-Varnish-Server: den2tpv65
< X-Cache: HIT
<
* Connection #0 to host oi58.tinypic.com left intact
File downloaded!

Size: 117.911 KB
Speed: 169.138 KB/s

Upvotes: 1

Related Questions