Skip to content

Instantly share code, notes, and snippets.

@asakura42
Forked from rfc-2549/fetch.c
Last active June 23, 2024 22:04
Show Gist options
  • Select an option

  • Save asakura42/64ad9ffb67c91811262d24a48640cea3 to your computer and use it in GitHub Desktop.

Select an option

Save asakura42/64ad9ffb67c91811262d24a48640cea3 to your computer and use it in GitHub Desktop.
Simple URL fetcher in C using libcurl, can be used as a replacement for curl, if you only use it to download files.
// cc `pkg-config --cflags --libs libcurl` fetch.c -std=c99 -Os -DNDEBUG -g0 -s -Wall -o fetch
#include <stdio.h>
#include <getopt.h>
#include <curl/curl.h>
#include <stdbool.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <libgen.h>
struct __options
{
bool verbose;
bool quiet;
char *output;
int timeout;
};
struct __options options;
void init_options(struct __options *options)
{
options->output = NULL;
options->quiet = 1;
options->verbose = 0;
options->timeout = 30;
return;
}
static int xferinfo_callback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
{
static int last_percent = -1;
if (options.verbose && dltotal > 0) {
int percent = (int)((dlnow / (double)dltotal) * 100);
if (percent!= last_percent) {
if (percent < 100) {
printf("\rDownloading... %d%%", percent);
fflush(stdout);
} else {
printf("\rDownloading... 100%%\n");
fflush(stdout);
}
last_percent = percent;
}
}
return 0;
}
int download_url(char *url, char *dest_file)
{
CURL *handle = curl_easy_init();
if(handle == NULL) {
fprintf(stderr, "Error creating CURL handle\n");
return -1;
}
curl_easy_setopt(handle, CURLOPT_URL, url);
if(!strcmp("-", dest_file)) {
curl_easy_setopt(handle, CURLOPT_WRITEDATA, stdout);
} else {
FILE *fp = fopen(dest_file, "w+");
if(fp == NULL) {
fprintf(stderr, "Error opening file: %s\n", strerror(errno));
return -1;
}
curl_easy_setopt(handle, CURLOPT_WRITEDATA, fp);
}
curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT, options.timeout);
curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, xferinfo_callback);
curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0);
if(options.verbose) {
printf("Fetching URL: %s\n", url);
}
int res = curl_easy_perform(handle);
if(res!= CURLE_OK) {
fprintf(stderr, "Error during request: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(handle);
return -1;
}
curl_easy_cleanup(handle);
if(options.verbose) {
printf("Done, saved to %s\n", dest_file);
}
return 0;
}
void usage(void)
{
printf("USAGE: fetch [-q|-v] -o [FILE] -t [TIMEOUT] URL [URL...]\n");
printf(" -t [TIMEOUT] Set timeout in seconds (default: 30)\n");
return;
}
int main(int argc, char **argv)
{
/* Init the options */
init_options(&options);
/* USAGE */
if(argc <= 1) {
usage();
return -1;
}
/* Process arguments */
int c;
options.output = NULL;
while((c = getopt(argc, argv, "vo:t:q"))!= -1) {
switch(c) {
case 'v':
options.verbose = true;
options.quiet = false;
break;
case 'q':
options.quiet = true;
options.verbose = false;
break;
case 'o':
options.output = optarg;
break;
case 't':
options.timeout = atoi(optarg);
break;
}
}
for(int i = optind; i < argc; i++) {
char *url = argv[i];
char *dest_file;
if(options.output == NULL) {
dest_file = basename(url);
} else {
dest_file = options.output;
}
download_url(url, dest_file);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment