Reputation: 327
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
Reputation: 2370
The code you provided in your question has a few problems:
You aren't including stdlib.h
.
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)
.
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.
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.
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);
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