2018-07-22 12:50:51 +02:00
|
|
|
#include "network.h"
|
|
|
|
|
2018-07-18 17:26:26 +02:00
|
|
|
#include <ctype.h>
|
2018-07-21 01:39:51 +02:00
|
|
|
#include <errno.h>
|
2018-07-22 12:50:51 +02:00
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2018-07-18 17:26:26 +02:00
|
|
|
|
2018-07-21 01:39:51 +02:00
|
|
|
#define NETWORK_MAXIMUM_CONNECTION 10
|
|
|
|
|
2018-07-22 12:50:51 +02:00
|
|
|
LinkTable *ROOT_LINK_TBL;
|
|
|
|
|
2018-07-22 13:08:00 +02:00
|
|
|
/* ------------------------ Static variable ------------------------------ */
|
2018-07-22 12:50:51 +02:00
|
|
|
static CURLM *curl_multi;
|
2018-07-22 13:08:00 +02:00
|
|
|
static char *url_append(const char *url, const char *sublink);
|
|
|
|
static size_t
|
|
|
|
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp);
|
|
|
|
static Link *Link_new(const char *p_url);
|
|
|
|
static void Link_free(Link *link);
|
|
|
|
static LinkTable *LinkTable_new(const char *url);
|
|
|
|
static void LinkTable_free(LinkTable *linktbl);
|
|
|
|
static void LinkTable_add(LinkTable *linktbl, Link *link);
|
|
|
|
static void LinkTable_fill(LinkTable *linktbl);
|
|
|
|
static int is_valid_link_p_url(const char *n);
|
|
|
|
static void HTML_to_LinkTable(GumboNode *node, LinkTable *linktbl);
|
|
|
|
static Link *path_to_Link_recursive(char *path, LinkTable *linktbl);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief blocking transfer function
|
|
|
|
* \details
|
|
|
|
* This function does the followings:
|
|
|
|
* - add a curl easy handle to a curl multi handle,
|
|
|
|
* - perform the transfer (This is a blocking operation.)
|
|
|
|
* - return when the transfer is finished.
|
|
|
|
* It is probably unnecessary to use curl multi handle, this is done for future
|
|
|
|
* proofing.
|
|
|
|
*/
|
|
|
|
static void do_transfer(CURL *curl);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief convert a HTML page to a LinkTable
|
|
|
|
* \details Shamelessly copied and pasted from:
|
|
|
|
* https://github.com/google/gumbo-parser/blob/master/examples/find_links.cc
|
|
|
|
*/
|
|
|
|
static void HTML_to_LinkTable(GumboNode *node, LinkTable *linktbl);
|
2018-07-22 12:50:51 +02:00
|
|
|
|
2018-07-22 13:08:00 +02:00
|
|
|
/* -------------------------- Functions ---------------------------------- */
|
2018-07-22 12:50:51 +02:00
|
|
|
static char *url_append(const char *url, const char *sublink)
|
|
|
|
{
|
|
|
|
int needs_separator = 0;
|
|
|
|
if (url[strlen(url)-1] != '/') {
|
|
|
|
needs_separator = 1;
|
|
|
|
}
|
2018-07-21 01:39:51 +02:00
|
|
|
|
2018-07-22 12:50:51 +02:00
|
|
|
char *str;
|
|
|
|
size_t ul = strlen(url);
|
|
|
|
size_t sl = strlen(sublink);
|
|
|
|
str = calloc(ul + sl + needs_separator, sizeof(char));
|
|
|
|
strncpy(str, url, ul);
|
|
|
|
if (needs_separator) {
|
|
|
|
str[ul] = '/';
|
|
|
|
}
|
|
|
|
strncat(str, sublink, sl);
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2018-07-21 01:39:51 +02:00
|
|
|
static void do_transfer(CURL *curl)
|
|
|
|
{
|
|
|
|
/* Add the transfer handle */
|
|
|
|
curl_multi_add_handle(curl_multi, curl);
|
|
|
|
|
|
|
|
CURLMcode res;
|
|
|
|
int num_transfers, max_fd;
|
|
|
|
long timeout;
|
|
|
|
fd_set read_fd_set, write_fd_set, exc_fd_set;
|
|
|
|
do {
|
|
|
|
res = curl_multi_perform(curl_multi, &num_transfers);
|
|
|
|
if (res) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"do_transfer(): curl_multi_perform(): %s\n",
|
|
|
|
curl_multi_strerror(res));
|
|
|
|
}
|
|
|
|
if (!num_transfers) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = curl_multi_fdset(curl_multi, &read_fd_set, &write_fd_set,
|
|
|
|
&exc_fd_set, &max_fd);
|
|
|
|
if (res) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"do_transfer(): curl_multi_fdset(): %s\n",
|
|
|
|
curl_multi_strerror(res));
|
|
|
|
}
|
|
|
|
|
|
|
|
res = curl_multi_timeout(curl_multi, &timeout);
|
|
|
|
if (res) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"do_transfer(): curl_multi_timeout(): %s\n",
|
|
|
|
curl_multi_strerror(res));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (max_fd < 0 || timeout < 0) {
|
|
|
|
/*
|
|
|
|
* To find out why, read:
|
|
|
|
* https://curl.haxx.se/libcurl/c/curl_multi_fdset.html
|
|
|
|
*/
|
|
|
|
timeout = 100;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct timeval t;
|
|
|
|
/* convert timeout (in millisec) to sec */
|
|
|
|
t.tv_sec = timeout/1000;
|
|
|
|
/* convert the remainder to microsec */
|
|
|
|
t.tv_usec = (timeout%1000)*1000;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The select() system call blocks until one or more of a set of
|
|
|
|
* file descriptors becomes ready.
|
|
|
|
* (The Linux Programming Interface, Michael Kerrisk)
|
|
|
|
*
|
|
|
|
* See also:
|
|
|
|
* https://curl.haxx.se/libcurl/c/curl_multi_timeout.html
|
|
|
|
*/
|
|
|
|
if (select(max_fd + 1, &read_fd_set,
|
|
|
|
&write_fd_set, &exc_fd_set, &t) < 0) {
|
|
|
|
fprintf(stderr, "do_transfer(): select(%i, , , , %li): %i: %s\n",
|
|
|
|
max_fd + 1, timeout, errno, strerror(errno));
|
|
|
|
}
|
|
|
|
} while(num_transfers);
|
|
|
|
|
|
|
|
/* Remove the transfer handle */
|
|
|
|
curl_multi_remove_handle(curl_multi, curl);
|
|
|
|
}
|
2018-07-22 12:50:51 +02:00
|
|
|
|
2018-07-20 12:49:20 +02:00
|
|
|
static size_t
|
|
|
|
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
|
|
|
{
|
|
|
|
size_t realsize = size * nmemb;
|
|
|
|
Link *mem = (Link *)userp;
|
|
|
|
|
2018-07-20 14:59:25 +02:00
|
|
|
mem->body = realloc(mem->body, mem->body_sz + realsize + 1);
|
|
|
|
if(mem->body == NULL) {
|
2018-07-20 12:49:20 +02:00
|
|
|
/* out of memory! */
|
|
|
|
printf("not enough memory (realloc returned NULL)\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-07-20 14:59:25 +02:00
|
|
|
memcpy(&(mem->body[mem->body_sz]), contents, realsize);
|
|
|
|
mem->body_sz += realsize;
|
|
|
|
mem->body[mem->body_sz] = 0;
|
2018-07-20 12:49:20 +02:00
|
|
|
|
|
|
|
return realsize;
|
|
|
|
}
|
|
|
|
|
2018-07-22 12:50:51 +02:00
|
|
|
void network_init(const char *url)
|
2018-07-21 01:39:51 +02:00
|
|
|
{
|
|
|
|
curl_global_init(CURL_GLOBAL_ALL);
|
|
|
|
curl_multi = curl_multi_init();
|
|
|
|
curl_multi_setopt(curl_multi, CURLMOPT_MAXCONNECTS,
|
|
|
|
(long)NETWORK_MAXIMUM_CONNECTION);
|
2018-07-22 12:50:51 +02:00
|
|
|
ROOT_LINK_TBL = LinkTable_new(url);
|
2018-07-21 01:39:51 +02:00
|
|
|
}
|
|
|
|
|
2018-07-22 13:08:00 +02:00
|
|
|
static Link *Link_new(const char *p_url)
|
2018-07-18 17:26:26 +02:00
|
|
|
{
|
2018-07-20 03:09:51 +02:00
|
|
|
Link *link = calloc(1, sizeof(Link));
|
|
|
|
|
2018-07-20 14:59:25 +02:00
|
|
|
strncpy(link->p_url, p_url, LINK_LEN_MAX);
|
2018-07-20 03:09:51 +02:00
|
|
|
|
2018-07-22 12:50:51 +02:00
|
|
|
/* remove the '/' from p_url if it exists */
|
2018-07-22 05:26:22 +02:00
|
|
|
char *c = &(link->p_url[strnlen(link->p_url, LINK_LEN_MAX) - 1]);
|
|
|
|
if ( *c == '/') {
|
|
|
|
*c = '\0';
|
|
|
|
}
|
|
|
|
|
2018-07-20 03:09:51 +02:00
|
|
|
link->type = LINK_UNKNOWN;
|
2018-07-21 01:39:51 +02:00
|
|
|
|
2018-07-20 14:59:25 +02:00
|
|
|
link->curl = curl_easy_init();
|
2018-07-20 12:49:20 +02:00
|
|
|
|
|
|
|
/* set up some basic curl stuff */
|
2018-07-20 14:59:25 +02:00
|
|
|
curl_easy_setopt(link->curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
|
|
|
|
curl_easy_setopt(link->curl, CURLOPT_WRITEDATA, (void *)link);
|
|
|
|
curl_easy_setopt(link->curl, CURLOPT_USERAGENT, "mount-http-dir/libcurl");
|
2018-07-21 01:39:51 +02:00
|
|
|
curl_easy_setopt(link->curl, CURLOPT_VERBOSE, 0);
|
2018-07-22 05:26:22 +02:00
|
|
|
curl_easy_setopt(link->curl, CURLOPT_FOLLOWLOCATION, 1);
|
|
|
|
/*
|
|
|
|
* only 1 redirection is really needed
|
|
|
|
* - for following directories without the '/'
|
|
|
|
*/
|
|
|
|
curl_easy_setopt(link->curl, CURLOPT_MAXREDIRS, 3);
|
2018-07-20 03:09:51 +02:00
|
|
|
|
|
|
|
return link;
|
|
|
|
}
|
|
|
|
|
2018-07-22 13:08:00 +02:00
|
|
|
static void Link_free(Link *link)
|
2018-07-20 03:09:51 +02:00
|
|
|
{
|
2018-07-20 14:59:25 +02:00
|
|
|
curl_easy_cleanup(link->curl);
|
|
|
|
free(link->body);
|
2018-07-20 03:09:51 +02:00
|
|
|
free(link);
|
|
|
|
link = NULL;
|
|
|
|
}
|
|
|
|
|
2018-07-20 16:38:44 +02:00
|
|
|
int Link_download(Link *link, size_t start, size_t end)
|
|
|
|
{
|
|
|
|
CURL *curl = link->curl;
|
|
|
|
char range_str[64];
|
|
|
|
snprintf(range_str, sizeof(range_str), "%lu-%lu", start, end);
|
|
|
|
|
|
|
|
curl_easy_setopt(curl, CURLOPT_NOBODY, 0);
|
|
|
|
curl_easy_setopt(curl, CURLOPT_RANGE, range_str);
|
2018-07-21 01:39:51 +02:00
|
|
|
do_transfer(curl);
|
2018-07-20 16:38:44 +02:00
|
|
|
|
|
|
|
long http_resp;
|
|
|
|
curl_easy_getinfo(link->curl, CURLINFO_RESPONSE_CODE, &http_resp);
|
|
|
|
return http_resp;
|
|
|
|
}
|
|
|
|
|
2018-07-22 13:08:00 +02:00
|
|
|
static LinkTable *LinkTable_new(const char *url)
|
2018-07-20 03:09:51 +02:00
|
|
|
{
|
|
|
|
LinkTable *linktbl = calloc(1, sizeof(LinkTable));
|
2018-07-20 14:59:25 +02:00
|
|
|
|
2018-07-20 12:49:20 +02:00
|
|
|
/* populate the base URL */
|
2018-07-22 05:26:22 +02:00
|
|
|
LinkTable_add(linktbl, Link_new("/"));
|
2018-07-20 14:59:25 +02:00
|
|
|
Link *head_link = linktbl->links[0];
|
2018-07-20 16:38:44 +02:00
|
|
|
head_link->type = LINK_HEAD;
|
2018-07-20 14:59:25 +02:00
|
|
|
curl_easy_setopt(head_link->curl, CURLOPT_URL, url);
|
|
|
|
|
|
|
|
/* start downloading the base URL */
|
2018-07-21 01:39:51 +02:00
|
|
|
do_transfer(head_link->curl);
|
2018-07-20 14:59:25 +02:00
|
|
|
|
|
|
|
/* if downloading base URL failed */
|
2018-07-21 01:39:51 +02:00
|
|
|
long http_resp;
|
|
|
|
curl_easy_getinfo(head_link->curl, CURLINFO_RESPONSE_CODE, &http_resp);
|
|
|
|
if (http_resp != HTTP_OK) {
|
2018-07-22 12:50:51 +02:00
|
|
|
fprintf(stderr, "link.c: LinkTable_new() cannot retrive the base URL, \
|
|
|
|
URL: %s, HTTP response: %ld\n", url, http_resp);
|
2018-07-20 12:49:20 +02:00
|
|
|
LinkTable_free(linktbl);
|
|
|
|
linktbl = NULL;
|
2018-07-20 14:59:25 +02:00
|
|
|
return linktbl;
|
2018-07-20 12:49:20 +02:00
|
|
|
};
|
2018-07-20 14:59:25 +02:00
|
|
|
|
|
|
|
/* Otherwise parsed the received data */
|
|
|
|
GumboOutput* output = gumbo_parse(head_link->body);
|
|
|
|
HTML_to_LinkTable(output->root, linktbl);
|
|
|
|
gumbo_destroy_output(&kGumboDefaultOptions, output);
|
|
|
|
|
|
|
|
/* Fill in the link table */
|
|
|
|
LinkTable_fill(linktbl);
|
2018-07-20 03:09:51 +02:00
|
|
|
return linktbl;
|
2018-07-18 17:26:26 +02:00
|
|
|
}
|
|
|
|
|
2018-07-22 13:08:00 +02:00
|
|
|
static void LinkTable_free(LinkTable *linktbl)
|
2018-07-18 17:26:26 +02:00
|
|
|
{
|
2018-07-20 03:09:51 +02:00
|
|
|
for (int i = 0; i < linktbl->num; i++) {
|
|
|
|
Link_free(linktbl->links[i]);
|
2018-07-18 17:26:26 +02:00
|
|
|
}
|
2018-07-20 03:09:51 +02:00
|
|
|
free(linktbl->links);
|
|
|
|
free(linktbl);
|
|
|
|
linktbl = NULL;
|
2018-07-18 17:26:26 +02:00
|
|
|
}
|
|
|
|
|
2018-07-22 13:08:00 +02:00
|
|
|
static void LinkTable_add(LinkTable *linktbl, Link *link)
|
2018-07-18 17:26:26 +02:00
|
|
|
{
|
2018-07-20 03:09:51 +02:00
|
|
|
linktbl->num++;
|
|
|
|
linktbl->links = realloc(
|
|
|
|
linktbl->links,
|
|
|
|
linktbl->num * sizeof(Link *));
|
|
|
|
linktbl->links[linktbl->num - 1] = link;
|
|
|
|
}
|
|
|
|
|
2018-07-20 14:59:25 +02:00
|
|
|
void LinkTable_fill(LinkTable *linktbl)
|
|
|
|
{
|
2018-07-22 05:26:22 +02:00
|
|
|
Link *head_link = linktbl->links[0];
|
2018-07-20 14:59:25 +02:00
|
|
|
for (int i = 0; i < linktbl->num; i++) {
|
|
|
|
Link *this_link = linktbl->links[i];
|
|
|
|
if (this_link->type == LINK_UNKNOWN) {
|
|
|
|
CURL *curl = this_link->curl;
|
|
|
|
char *url;
|
2018-07-22 05:26:22 +02:00
|
|
|
curl_easy_getinfo(head_link->curl, CURLINFO_EFFECTIVE_URL, &url);
|
|
|
|
url = url_append(url, this_link->p_url);
|
2018-07-20 14:59:25 +02:00
|
|
|
curl_easy_setopt(curl, CURLOPT_URL, url);
|
|
|
|
curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
|
2018-07-21 01:39:51 +02:00
|
|
|
|
|
|
|
do_transfer(curl);
|
|
|
|
|
2018-07-20 16:38:44 +02:00
|
|
|
long http_resp;
|
|
|
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp);
|
|
|
|
if (http_resp == HTTP_OK) {
|
2018-07-22 12:50:51 +02:00
|
|
|
strncpy(this_link->f_url, url, URL_LEN_MAX);
|
2018-07-20 16:38:44 +02:00
|
|
|
double cl;
|
|
|
|
curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &cl);
|
|
|
|
if (cl == -1) {
|
|
|
|
this_link->content_length = 0;
|
|
|
|
this_link->type = LINK_DIR;
|
|
|
|
} else {
|
|
|
|
this_link->content_length = cl;
|
|
|
|
this_link->type = LINK_FILE;
|
|
|
|
}
|
2018-07-20 14:59:25 +02:00
|
|
|
} else {
|
2018-07-20 16:38:44 +02:00
|
|
|
this_link->type = LINK_INVALID;
|
2018-07-20 14:59:25 +02:00
|
|
|
}
|
2018-07-22 12:50:51 +02:00
|
|
|
free(url);
|
2018-07-20 14:59:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-20 03:09:51 +02:00
|
|
|
void LinkTable_print(LinkTable *linktbl)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < linktbl->num; i++) {
|
2018-07-20 14:59:25 +02:00
|
|
|
Link *this_link = linktbl->links[i];
|
2018-07-22 12:50:51 +02:00
|
|
|
printf("%d %c %lu %s %s\n",
|
2018-07-20 03:09:51 +02:00
|
|
|
i,
|
2018-07-20 14:59:25 +02:00
|
|
|
this_link->type,
|
|
|
|
this_link->content_length,
|
2018-07-22 12:50:51 +02:00
|
|
|
this_link->p_url,
|
|
|
|
this_link->f_url
|
|
|
|
);
|
2018-07-20 03:09:51 +02:00
|
|
|
}
|
2018-07-18 17:26:26 +02:00
|
|
|
}
|
|
|
|
|
2018-07-22 12:50:51 +02:00
|
|
|
static int is_valid_link_p_url(const char *n)
|
2018-07-18 17:26:26 +02:00
|
|
|
{
|
|
|
|
/* The link name has to start with alphanumerical character */
|
|
|
|
if (!isalnum(n[0])) {
|
|
|
|
return 0;
|
|
|
|
}
|
2018-07-20 03:09:51 +02:00
|
|
|
|
2018-07-18 17:26:26 +02:00
|
|
|
/* check for http:// and https:// */
|
2018-07-20 03:09:51 +02:00
|
|
|
int c = strnlen(n, LINK_LEN_MAX);
|
2018-07-18 17:26:26 +02:00
|
|
|
if (c > 5) {
|
|
|
|
if (n[0] == 'h' && n[1] == 't' && n[2] == 't' && n[3] == 'p') {
|
|
|
|
if ((n[4] == ':' && n[5] == '/' && n[6] == '/') ||
|
|
|
|
(n[4] == 's' && n[5] == ':' && n[6] == '/' && n[7] == '/')) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-07-22 13:08:00 +02:00
|
|
|
static void HTML_to_LinkTable(GumboNode *node, LinkTable *linktbl)
|
2018-07-18 17:26:26 +02:00
|
|
|
{
|
|
|
|
if (node->type != GUMBO_NODE_ELEMENT) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
GumboAttribute* href;
|
|
|
|
|
|
|
|
if (node->v.element.tag == GUMBO_TAG_A &&
|
|
|
|
(href = gumbo_get_attribute(&node->v.element.attributes, "href"))) {
|
|
|
|
/* if it is valid, copy the link onto the heap */
|
2018-07-22 12:50:51 +02:00
|
|
|
if (is_valid_link_p_url(href->value)) {
|
2018-07-20 03:09:51 +02:00
|
|
|
LinkTable_add(linktbl, Link_new(href->value));
|
2018-07-18 17:26:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Note the recursive call, lol. */
|
|
|
|
GumboVector *children = &node->v.element.children;
|
|
|
|
for (size_t i = 0; i < children->length; ++i) {
|
2018-07-20 03:09:51 +02:00
|
|
|
HTML_to_LinkTable((GumboNode*)children->data[i], linktbl);
|
2018-07-18 17:26:26 +02:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-22 13:08:00 +02:00
|
|
|
static Link *path_to_Link_recursive(char *path, LinkTable *linktbl)
|
2018-07-18 17:26:26 +02:00
|
|
|
{
|
2018-07-22 12:50:51 +02:00
|
|
|
/* skip the leading '/' if it exists */
|
|
|
|
if (*path == '/') {
|
|
|
|
path++;
|
|
|
|
}
|
2018-07-18 17:26:26 +02:00
|
|
|
|
2018-07-22 12:50:51 +02:00
|
|
|
/* remove the last '/' if it exists */
|
|
|
|
char *slash = &(path[strnlen(path, URL_LEN_MAX) - 1]);
|
|
|
|
if (*slash == '/') {
|
|
|
|
*slash = '\0';
|
2018-07-18 17:26:26 +02:00
|
|
|
}
|
|
|
|
|
2018-07-22 12:50:51 +02:00
|
|
|
slash = strchr(path, '/');
|
|
|
|
if ( slash == NULL ) {
|
|
|
|
/* We cannot find another '/', we have reached the last level */
|
|
|
|
for (int i = 1; i < linktbl->num; i++) {
|
|
|
|
if (!strncmp(path, linktbl->links[i]->p_url, LINK_LEN_MAX)) {
|
|
|
|
/* We found our link */
|
|
|
|
return linktbl->links[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We can still find '/', time to consume the path and traverse
|
|
|
|
* the tree structure
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* add termination mark to the current string,
|
|
|
|
* effective create two substrings
|
|
|
|
*/
|
|
|
|
*slash = '\0';
|
|
|
|
/* move the pointer past the '/' */
|
|
|
|
char *next_path = slash + 1;
|
|
|
|
for (int i = 1; i < linktbl->num; i++) {
|
|
|
|
if (!strncmp(path, linktbl->links[i]->p_url, LINK_LEN_MAX)) {
|
|
|
|
/* The next sub-directory exists */
|
|
|
|
LinkTable *next_table = linktbl->links[i]->next_table;
|
|
|
|
if (!(next_table)) {
|
|
|
|
next_table = LinkTable_new(linktbl->links[i]->f_url);
|
|
|
|
}
|
|
|
|
return path_to_Link(next_path, next_table);
|
|
|
|
}
|
|
|
|
}
|
2018-07-18 17:26:26 +02:00
|
|
|
}
|
2018-07-22 12:50:51 +02:00
|
|
|
fprintf(stderr, "path_to_Link(): %s does not exist.\n", path);
|
|
|
|
return NULL;
|
2018-07-18 17:26:26 +02:00
|
|
|
}
|
2018-07-22 12:50:51 +02:00
|
|
|
|
|
|
|
Link *path_to_Link(const char *path, LinkTable *linktbl)
|
|
|
|
{
|
|
|
|
char *new_path = strndup(path, URL_LEN_MAX);
|
|
|
|
return path_to_Link_recursive(new_path, linktbl);
|
|
|
|
}
|
|
|
|
|
|
|
|
|