This commit is contained in:
libretroadmin 2023-07-16 18:34:51 +02:00
parent 86d5e4128c
commit 24a9210b56
12 changed files with 432 additions and 306 deletions

View file

@ -142,6 +142,7 @@ static enum IIRFilter str_to_type(const char *str)
return LPF; /* Fallback. */
}
#undef CHECK
static void make_poly_from_roots(
const double *roots, unsigned num_roots, float *poly)

View file

@ -1346,9 +1346,9 @@ struct string_list* cdrom_get_available_drives(void)
if (string_starts_with_size(dir_list->elems[i].data, "/dev/sg",
STRLEN_CONST("/dev/sg")))
{
char drive_string[33];
libretro_vfs_implementation_file *stream;
char drive_model[32] = {0};
char drive_string[33] = {0};
union string_list_elem_attr attr = {0};
int dev_index = 0;
RFILE *file = filestream_open(
@ -1380,9 +1380,9 @@ struct string_list* cdrom_get_available_drives(void)
attr.i = dev_index;
if (!string_is_empty(drive_model))
strlcat(drive_string, drive_model, sizeof(drive_string));
strlcpy(drive_string, drive_model, sizeof(drive_string));
else
strlcat(drive_string, "Unknown Drive", sizeof(drive_string));
strlcpy(drive_string, "Unknown Drive", sizeof(drive_string));
string_list_append(list, drive_string, attr);
}
@ -1462,10 +1462,10 @@ struct string_list* cdrom_get_available_drives(void)
continue;
{
char drive_string[33];
libretro_vfs_implementation_file *stream;
bool is_cdrom = false;
char drive_model[32] = {0};
char drive_string[33] = {0};
union string_list_elem_attr attr = {0};
RFILE *file = filestream_open(cdrom_path, RETRO_VFS_FILE_ACCESS_READ, 0);
if (!file)
@ -1481,9 +1481,9 @@ struct string_list* cdrom_get_available_drives(void)
attr.i = path[0];
if (!string_is_empty(drive_model))
strlcat(drive_string, drive_model, sizeof(drive_string));
strlcpy(drive_string, drive_model, sizeof(drive_string));
else
strlcat(drive_string, "Unknown Drive", sizeof(drive_string));
strlcpy(drive_string, "Unknown Drive", sizeof(drive_string));
string_list_append(list, drive_string, attr);
}

View file

@ -366,8 +366,8 @@ static void config_file_get_realpath(char *s, size_t len,
const char *home = getenv("HOME");
if (home)
{
strlcpy(s, home, len);
strlcat(s, path + 1, len);
size_t _len = strlcpy(s, home, len);
strlcpy(s + _len, path + 1, len - _len);
}
else
strlcpy(s, path + 1, len);

View file

@ -328,14 +328,16 @@ bool path_is_compressed_file(const char* path)
size_t fill_pathname(char *out_path, const char *in_path,
const char *replace, size_t size)
{
size_t _len;
char tmp_path[PATH_MAX_LENGTH];
char *tok = NULL;
strlcpy(tmp_path, in_path, sizeof(tmp_path));
if ((tok = (char*)strrchr(path_basename(tmp_path), '.')))
*tok = '\0';
strlcpy(out_path, tmp_path, size);
return strlcat(out_path, replace, size);
_len = strlcpy(out_path, tmp_path, size);
_len += strlcpy(out_path + _len, replace, size - _len);
return _len;
}
@ -381,12 +383,12 @@ void fill_pathname_slash(char *path, size_t size)
return;
}
path_len = strlen(path);
path_len = strlen(path);
/* Try to preserve slash type. */
if (last_slash != (path + path_len - 1))
{
path[path_len] = last_slash[0];
path[path_len+1] = '\0';
path[ path_len] = last_slash[0];
path[++path_len] = '\0';
}
}
@ -1000,14 +1002,14 @@ size_t fill_pathname_join_special(char *out_path,
/* Try to preserve slash type. */
if (last_slash != (out_path + len - 1))
{
out_path[len] = last_slash[0];
out_path[len+1] = '\0';
out_path[ len] = last_slash[0];
out_path[++len] = '\0';
}
}
else
{
out_path[len] = PATH_DEFAULT_SLASH_C();
out_path[len+1] = '\0';
out_path[ len] = PATH_DEFAULT_SLASH_C();
out_path[++len] = '\0';
}
}
@ -1360,8 +1362,8 @@ void fill_pathname_application_path(char *s, size_t len)
if (realpath(s, resolved_bundle_dir_buf))
{
size_t _len = strlcpy(s, resolved_bundle_dir_buf, len - 1);
s[_len ] = '/';
s[_len+1] = '\0';
s[ _len] = '/';
s[++_len] = '\0';
}
}
#endif

View file

@ -407,7 +407,7 @@ int huffman_build_tree(struct huffman_decoder* decoder, uint32_t totaldata, uint
decoder->huffnode[curcode].bits = curcode;
/* scale the weight by the current effective length, ensuring we don't go to 0 */
decoder->huffnode[curcode].weight = ((uint64_t)decoder->datahisto[curcode]) * ((uint64_t)totalweight) / ((uint64_t)totaldata);
decoder->huffnode[curcode].weight = (uint32_t)(((uint64_t)decoder->datahisto[curcode]) * ((uint64_t)totalweight) / ((uint64_t)totaldata));
if (decoder->huffnode[curcode].weight == 0)
decoder->huffnode[curcode].weight = 1;
}

View file

@ -114,7 +114,7 @@ static void *lzma_fast_alloc(void *p, size_t size)
}
/* set the low bit of the size so we don't match next time */
*addr = size | 1;
*addr = (uint32_t)size | 1;
/* return aligned address */
return (void*)vaddr;

View file

@ -1809,6 +1809,30 @@ enum retro_mod
* even before the microphone driver is ready.
*/
#define RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE 76
/* const struct retro_netpacket_callback * --
* When set, a core gains control over network packets sent and
* received during a multiplayer session. This can be used to
* emulate multiplayer games that were originally played on two
* or more separate consoles or computers connected together.
*
* The frontend will take care of connecting players together,
* and the core only needs to send the actual data as needed for
* the emulation, while handshake and connection management happen
* in the background.
*
* When two or more players are connected and this interface has
* been set, time manipulation features (such as pausing, slow motion,
* fast forward, rewinding, save state loading, etc.) are disabled to
* avoid interrupting communication.
*
* Should be set in either retro_init or retro_load_game, but not both.
*
* When not set, a frontend may use state serialization-based
* multiplayer, where a deterministic core supporting multiple
* input devices does not need to take any action on its own.
*/
/* VFS functionality */
/* File paths:
@ -3030,6 +3054,100 @@ struct retro_disk_control_ext_callback
retro_get_image_label_t get_image_label; /* Optional - may be NULL */
};
/* Definitions for RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE.
* A core can set it if sending and receiving custom network packets
* during a multiplayer session is desired.
*/
/* Netpacket flags for retro_netpacket_send_t */
#define RETRO_NETPACKET_UNRELIABLE 0 /* Packet to be sent unreliable, depending on network quality it might not arrive. */
#define RETRO_NETPACKET_RELIABLE (1 << 0) /* Reliable packets are guaranteed to arrive at the target in the order they were send. */
#define RETRO_NETPACKET_UNSEQUENCED (1 << 1) /* Packet will not be sequenced with other packets and may arrive out of order. Cannot be set on reliable packets. */
/* Used by the core to send a packet to one or more connected players.
* A single packet sent via this interface can contain up to 64 KB of data.
*
* The broadcast flag can be set to true to send to multiple connected clients.
* In a broadcast, the client_id argument indicates 1 client NOT to send the
* packet to (pass 0xFFFF to send to everyone). Otherwise, the client_id
* argument indicates a single client to send the packet to.
*
* A frontend must support sending reliable packets (RETRO_NETPACKET_RELIABLE).
* Unreliable packets might not be supported by the frontend, but the flags can
* still be specified. Reliable transmission will be used instead.
*
* If this function is called passing NULL for buf, it will instead flush all
* previously buffered outgoing packets and instantly read any incoming packets.
* During such a call, retro_netpacket_receive_t and retro_netpacket_stop_t can
* be called. The core can perform this in a loop to do a blocking read, i.e.,
* wait for incoming data, but needs to handle stop getting called and also
* give up after a short while to avoid freezing on a connection problem.
*
* This function is not guaranteed to be thread-safe and must be called during
* retro_run or any of the netpacket callbacks passed with this interface.
*/
typedef void (RETRO_CALLCONV *retro_netpacket_send_t)(int flags, const void* buf, size_t len, uint16_t client_id, bool broadcast);
/* Called by the frontend to signify that a multiplayer session has started.
* If client_id is 0 the local player is the host of the session and at this
* point no other player has connected yet.
*
* If client_id is > 0 the local player is a client connected to a host and
* at this point is already fully connected to the host.
*
* The core must store the retro_netpacket_send_t function pointer provided
* here and use it whenever it wants to send a packet. This function pointer
* remains valid until the frontend calls retro_netpacket_stop_t.
*/
typedef void (RETRO_CALLCONV *retro_netpacket_start_t)(uint16_t client_id, retro_netpacket_send_t send_fn);
/* Called by the frontend when a new packet arrives which has been sent from
* another player with retro_netpacket_send_t. The client_id argument indicates
* who has sent the packet.
*/
typedef void (RETRO_CALLCONV *retro_netpacket_receive_t)(const void* buf, size_t len, uint16_t client_id);
/* Called by the frontend when the multiplayer session has ended.
* Once this gets called the retro_netpacket_send_t function pointer passed
* to retro_netpacket_start_t will not be valid anymore.
*/
typedef void (RETRO_CALLCONV *retro_netpacket_stop_t)(void);
/* Called by the frontend every frame (between calls to retro_run while
* updating the state of the multiplayer session.
* This is a good place for the core to call retro_netpacket_send_t from.
*/
typedef void (RETRO_CALLCONV *retro_netpacket_poll_t)(void);
/* Called by the frontend when a new player connects to the hosted session.
* This is only called on the host side, not for clients connected to the host.
* If this function returns false, the newly connected player gets dropped.
* This can be used for example to limit the number of players.
*/
typedef bool (RETRO_CALLCONV *retro_netpacket_connected_t)(uint16_t client_id);
/* Called by the frontend when a player leaves or disconnects from the hosted session.
* This is only called on the host side, not for clients connected to the host.
*/
typedef void (RETRO_CALLCONV *retro_netpacket_disconnected_t)(uint16_t client_id);
/**
* A callback interface for giving a core the ability to send and receive custom
* network packets during a multiplayer session between two or more instances
* of a libretro frontend.
*
* @see RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE
*/
struct retro_netpacket_callback
{
retro_netpacket_start_t start;
retro_netpacket_receive_t receive;
retro_netpacket_stop_t stop; /* Optional - may be NULL */
retro_netpacket_poll_t poll; /* Optional - may be NULL */
retro_netpacket_connected_t connected; /* Optional - may be NULL */
retro_netpacket_disconnected_t disconnected; /* Optional - may be NULL */
};
enum retro_pixel_format
{
/* 0RGB1555, native endian.

View file

@ -101,6 +101,26 @@ typedef int ssize_t;
#define STRING_REP_UINT64 "%" PRIu64
#define STRING_REP_USIZE "%" PRIuPTR
/* Wrap a declaration in RETRO_DEPRECATED() to produce a compiler warning when
it's used. This is intended for developer machines, so it won't work on ancient
or obscure compilers */
#if defined(_MSC_VER)
#if _MSC_VER >= 1400 /* Visual C 2005 or later */
#define RETRO_DEPRECATED(decl) __declspec(deprecated) decl
#endif
#elif defined(__GNUC__)
#if __GNUC__ >= 3 /* GCC 3 or later */
#define RETRO_DEPRECATED(decl) decl __attribute__((deprecated))
#endif
#elif defined(__clang__)
#if __clang_major__ >= 3 /* clang 3 or later */
#define RETRO_DEPRECATED(decl) decl __attribute__((deprecated))
#endif
#endif
#ifndef RETRO_DEPRECATED /* Unsupported compilers */
#define RETRO_DEPRECATED(decl) decl
#endif
/*
I would like to see retro_inline.h moved in here; possibly boolean too.

View file

@ -39,6 +39,8 @@
#include <psp2/kernel/threadmgr.h>
#elif defined(_3DS)
#include <3ds.h>
#elif defined(EMSCRIPTEN)
#include <emscripten/emscripten.h>
#else
#include <time.h>
#endif
@ -99,6 +101,8 @@ static int nanosleepDOS(const struct timespec *rqtp, struct timespec *rmtp)
#define retro_sleep(msec) (usleep(1000 * (msec)))
#elif defined(WIIU)
#define retro_sleep(msec) (OSSleepTicks(ms_to_ticks((msec))))
#elif defined(EMSCRIPTEN)
#define retro_sleep(msec) (emscripten_sleep(msec))
#else
static INLINE void retro_sleep(unsigned msec)
{

View file

@ -239,13 +239,9 @@ void file_list_set_alt_at_offset(file_list_t *list, size_t idx,
{
if (!list || !alt)
return;
if (list->list[idx].alt)
free(list->list[idx].alt);
list->list[idx].alt = NULL;
if (alt)
list->list[idx].alt = strdup(alt);
list->list[idx].alt = strdup(alt);
}
static int file_list_alt_cmp(const void *a_, const void *b_)

View file

@ -476,17 +476,18 @@ int string_list_find_elem(const struct string_list *list, const char *elem)
bool string_list_find_elem_prefix(const struct string_list *list,
const char *prefix, const char *elem)
{
size_t i;
char prefixed[255];
if (!list)
return false;
strlcpy(prefixed, prefix, sizeof(prefixed));
strlcat(prefixed, elem, sizeof(prefixed));
for (i = 0; i < list->size; i++)
if (list)
{
if ( string_is_equal_noncase(list->elems[i].data, elem)
|| string_is_equal_noncase(list->elems[i].data, prefixed))
return true;
size_t i;
char prefixed[255];
size_t _len = strlcpy(prefixed, prefix, sizeof(prefixed));
strlcpy(prefixed + _len, elem, sizeof(prefixed) - _len);
for (i = 0; i < list->size; i++)
{
if ( string_is_equal_noncase(list->elems[i].data, elem)
|| string_is_equal_noncase(list->elems[i].data, prefixed))
return true;
}
}
return false;
}
@ -494,21 +495,19 @@ bool string_list_find_elem_prefix(const struct string_list *list,
struct string_list *string_list_clone(const struct string_list *src)
{
size_t i;
struct string_list_elem
*elems = NULL;
struct string_list
*dest = (struct string_list*)
struct string_list_elem *elems = NULL;
struct string_list *dest = (struct string_list*)
malloc(sizeof(struct string_list));
if (!dest)
return NULL;
dest->elems = NULL;
dest->size = src->size;
dest->elems = NULL;
dest->size = src->size;
if (src->cap < dest->size)
dest->cap = dest->size;
dest->cap = dest->size;
else
dest->cap = src->cap;
dest->cap = src->cap;
if (!(elems = (struct string_list_elem*)
calloc(dest->cap, sizeof(struct string_list_elem))))
@ -517,7 +516,7 @@ struct string_list *string_list_clone(const struct string_list *src)
return NULL;
}
dest->elems = elems;
dest->elems = elems;
for (i = 0; i < src->size; i++)
{

View file

@ -389,16 +389,15 @@ void net_http_urlencode(char **dest, const char *source)
void net_http_urlencode_full(char *dest,
const char *source, size_t size)
{
size_t buf_pos;
size_t tmp_len;
size_t url_domain_len;
char url_domain[256];
char url_path[PATH_MAX_LENGTH];
size_t buf_pos = 0;
char *tmp = NULL;
int count = 0;
strlcpy(url_path, source, sizeof(url_path));
tmp = url_path;
tmp = url_path;
while (count < 3 && tmp[0] != '\0')
{
@ -408,19 +407,19 @@ void net_http_urlencode_full(char *dest,
}
tmp_len = strlen(tmp);
url_domain_len = ((strlcpy(url_domain, source, tmp - url_path)) - tmp_len) - 1;
buf_pos = ((strlcpy(url_domain, source, tmp - url_path)) - tmp_len) - 1;
strlcpy(url_path,
source + url_domain_len + 1,
tmp_len + 1
source + buf_pos + 1,
tmp_len + 1
);
tmp = NULL;
net_http_urlencode(&tmp, url_path);
buf_pos = strlcpy(dest, url_domain, size);
dest[buf_pos] = '/';
dest[buf_pos+1] = '\0';
strlcat(dest, tmp, size);
free (tmp);
dest[ buf_pos] = '/';
dest[++buf_pos] = '\0';
strlcpy(dest + buf_pos, tmp, size - buf_pos);
free(tmp);
}
static int net_http_new_socket(struct http_connection_t *conn)
@ -717,16 +716,14 @@ const char* net_http_connection_method(struct http_connection_t* conn)
struct http_t *net_http_new(struct http_connection_t *conn)
{
bool error = false;
#ifdef HAVE_SSL
if (!conn || (net_http_new_socket(conn)) < 0)
return NULL;
#else
int fd = -1;
struct http_t *state = NULL;
if (!conn)
goto error;
if ((fd = net_http_new_socket(conn)) < 0)
goto error;
error = false;
if (!conn || (fd = net_http_new_socket(conn)) < 0)
return NULL;
#endif
/* This is a bit lazy, but it works. */
if (conn->methodcopy)
@ -755,12 +752,12 @@ struct http_t *net_http_new(struct http_connection_t *conn)
if (conn->port)
{
char portstr[16];
portstr[0] = '\0';
snprintf(portstr, sizeof(portstr), ":%i", conn->port);
net_http_send_str(&conn->sock_state, &error, portstr,
strlen(portstr));
size_t _len = 0;
portstr[ _len] = ':';
portstr[++_len] = '\0';
_len += snprintf(portstr + _len, sizeof(portstr) - _len,
"%i", conn->port);
net_http_send_str(&conn->sock_state, &error, portstr, _len);
}
net_http_send_str(&conn->sock_state, &error, "\r\n",
@ -787,7 +784,7 @@ struct http_t *net_http_new(struct http_connection_t *conn)
char *len_str = NULL;
if (!conn->postdatacopy)
goto error;
goto err;
if (!conn->headerscopy)
{
@ -843,49 +840,48 @@ struct http_t *net_http_new(struct http_connection_t *conn)
net_http_send_str(&conn->sock_state, &error, conn->postdatacopy,
strlen(conn->postdatacopy));
if (error)
goto error;
if (!error)
{
struct http_t *state = (struct http_t*)malloc(sizeof(struct http_t));
state->sock_state = conn->sock_state;
state->status = -1;
state->data = NULL;
state->part = P_HEADER_TOP;
state->bodytype = T_FULL;
state->error = false;
state->pos = 0;
state->len = 0;
state->buflen = 512;
state = (struct http_t*)malloc(sizeof(struct http_t));
state->sock_state = conn->sock_state;
state->status = -1;
state->data = NULL;
state->part = P_HEADER_TOP;
state->bodytype = T_FULL;
state->error = false;
state->pos = 0;
state->len = 0;
state->buflen = 512;
if ((state->data = (char*)malloc(state->buflen)))
return state;
free(state);
}
if (!(state->data = (char*)malloc(state->buflen)))
goto error;
return state;
error:
err:
if (conn)
{
if (conn->methodcopy)
free(conn->methodcopy);
if (conn->contenttypecopy)
free(conn->contenttypecopy);
conn->methodcopy = NULL;
conn->contenttypecopy = NULL;
conn->postdatacopy = NULL;
}
conn->methodcopy = NULL;
conn->contenttypecopy = NULL;
conn->postdatacopy = NULL;
#ifdef HAVE_SSL
if (conn && conn->sock_state.ssl_ctx)
{
ssl_socket_close(conn->sock_state.ssl_ctx);
ssl_socket_free(conn->sock_state.ssl_ctx);
conn->sock_state.ssl_ctx = NULL;
}
#else
if (fd >= 0)
socket_close(fd);
if (conn->sock_state.ssl_ctx)
{
ssl_socket_close(conn->sock_state.ssl_ctx);
ssl_socket_free(conn->sock_state.ssl_ctx);
conn->sock_state.ssl_ctx = NULL;
}
#endif
}
#ifndef HAVE_SSL
socket_close(fd);
#endif
if (state)
free(state);
return NULL;
}
@ -912,248 +908,238 @@ int net_http_fd(struct http_t *state)
**/
bool net_http_update(struct http_t *state, size_t* progress, size_t* total)
{
ssize_t newlen = 0;
if (!state)
return true;
if (state->error)
{
state->part = P_ERROR;
state->status = -1;
return true;
}
if (state->part < P_BODY)
if (state)
{
ssize_t newlen = 0;
if (state->error)
{
state->part = P_ERROR;
state->status = -1;
return true;
}
goto error;
#ifdef HAVE_SSL
if (state->sock_state.ssl && state->sock_state.ssl_ctx)
newlen = ssl_socket_receive_all_nonblocking(state->sock_state.ssl_ctx, &state->error,
(uint8_t*)state->data + state->pos,
state->buflen - state->pos);
else
#endif
newlen = socket_receive_all_nonblocking(state->sock_state.fd, &state->error,
(uint8_t*)state->data + state->pos,
state->buflen - state->pos);
if (newlen < 0)
{
state->error = true;
state->part = P_ERROR;
state->status = -1;
return true;
}
if (state->pos + newlen >= state->buflen - 64)
{
state->buflen *= 2;
state->data = (char*)realloc(state->data, state->buflen);
}
state->pos += newlen;
while (state->part < P_BODY)
{
char *dataend = state->data + state->pos;
char *lineend = (char*)memchr(state->data, '\n', state->pos);
if (!lineend)
break;
*lineend='\0';
if (lineend != state->data && lineend[-1]=='\r')
lineend[-1]='\0';
if (state->part == P_HEADER_TOP)
{
if (strncmp(state->data, "HTTP/1.", STRLEN_CONST("HTTP/1."))!=0)
{
state->error = true;
state->part = P_ERROR;
state->status = -1;
return true;
}
state->status = (int)strtoul(state->data
+ STRLEN_CONST("HTTP/1.1 "), NULL, 10);
state->part = P_HEADER;
}
else
{
if (string_starts_with_case_insensitive(state->data, "Content-Length:"))
{
char* ptr = state->data + STRLEN_CONST("Content-Length:");
while (ISSPACE(*ptr))
++ptr;
state->bodytype = T_LEN;
state->len = strtol(ptr, NULL, 10);
}
if (string_is_equal_case_insensitive(state->data, "Transfer-Encoding: chunked"))
state->bodytype = T_CHUNK;
/* TODO: save headers somewhere */
if (state->data[0]=='\0')
{
state->part = P_BODY;
if (state->bodytype == T_CHUNK)
state->part = P_BODY_CHUNKLEN;
}
}
memmove(state->data, lineend + 1, dataend-(lineend+1));
state->pos = (dataend-(lineend + 1));
}
if (state->part >= P_BODY)
{
newlen = state->pos;
state->pos = 0;
}
}
if (state->part >= P_BODY && state->part < P_DONE)
{
if (!newlen)
if (state->part < P_BODY)
{
if (state->error)
newlen = -1;
else
{
goto error;
#ifdef HAVE_SSL
if (state->sock_state.ssl && state->sock_state.ssl_ctx)
newlen = ssl_socket_receive_all_nonblocking(
state->sock_state.ssl_ctx,
&state->error,
if (state->sock_state.ssl && state->sock_state.ssl_ctx)
newlen = ssl_socket_receive_all_nonblocking(state->sock_state.ssl_ctx, &state->error,
(uint8_t*)state->data + state->pos,
state->buflen - state->pos);
else
else
#endif
newlen = socket_receive_all_nonblocking(
state->sock_state.fd,
&state->error,
newlen = socket_receive_all_nonblocking(state->sock_state.fd, &state->error,
(uint8_t*)state->data + state->pos,
state->buflen - state->pos);
}
if (newlen < 0)
{
if (state->bodytype != T_FULL)
{
state->error = true;
state->part = P_ERROR;
state->status = -1;
return true;
}
state->part = P_DONE;
state->data = (char*)realloc(state->data, state->len);
newlen = 0;
state->error = true;
goto error;
}
if (state->pos + newlen >= state->buflen - 64)
{
state->buflen *= 2;
state->data = (char*)realloc(state->data, state->buflen);
state->buflen *= 2;
state->data = (char*)realloc(state->data, state->buflen);
}
}
state->pos += newlen;
parse_again:
if (state->bodytype == T_CHUNK)
{
if (state->part == P_BODY_CHUNKLEN)
while (state->part < P_BODY)
{
state->pos += newlen;
char *dataend = state->data + state->pos;
char *lineend = (char*)memchr(state->data, '\n', state->pos);
if (state->pos - state->len >= 2)
if (!lineend)
break;
*lineend = '\0';
if (lineend != state->data && lineend[-1]=='\r')
lineend[-1] = '\0';
if (state->part == P_HEADER_TOP)
{
/*
* len=start of chunk including \r\n
* pos=end of data
*/
char *fullend = state->data + state->pos;
char *end = (char*)memchr(state->data + state->len + 2, '\n',
state->pos - state->len - 2);
if (end)
if (strncmp(state->data, "HTTP/1.", STRLEN_CONST("HTTP/1."))!=0)
{
size_t chunklen = strtoul(state->data+state->len, NULL, 16);
state->pos = state->len;
end++;
state->error = true;
goto error;
}
state->status = (int)strtoul(state->data
+ STRLEN_CONST("HTTP/1.1 "), NULL, 10);
state->part = P_HEADER;
}
else
{
if (string_starts_with_case_insensitive(state->data, "Content-Length:"))
{
char* ptr = state->data + STRLEN_CONST("Content-Length:");
while (ISSPACE(*ptr))
++ptr;
memmove(state->data+state->len, end, fullend-end);
state->len = chunklen;
newlen = (fullend - end);
/*
len=num bytes
newlen=unparsed bytes after \n
pos=start of chunk including \r\n
*/
state->bodytype = T_LEN;
state->len = strtol(ptr, NULL, 10);
}
if (string_is_equal_case_insensitive(state->data, "Transfer-Encoding: chunked"))
state->bodytype = T_CHUNK;
/* TODO: save headers somewhere */
if (state->data[0]=='\0')
{
state->part = P_BODY;
if (state->len == 0)
{
state->part = P_DONE;
state->len = state->pos;
state->data = (char*)realloc(state->data, state->len);
}
goto parse_again;
if (state->bodytype == T_CHUNK)
state->part = P_BODY_CHUNKLEN;
}
}
memmove(state->data, lineend + 1, dataend-(lineend+1));
state->pos = (dataend-(lineend + 1));
}
else if (state->part == P_BODY)
if (state->part >= P_BODY)
{
if ((size_t)newlen >= state->len)
{
state->pos += state->len;
newlen -= state->len;
state->len = state->pos;
state->part = P_BODY_CHUNKLEN;
goto parse_again;
}
state->pos += newlen;
state->len -= newlen;
newlen = state->pos;
state->pos = 0;
}
}
else
if (state->part >= P_BODY && state->part < P_DONE)
{
state->pos += newlen;
if (!newlen)
{
if (state->error)
newlen = -1;
else
{
#ifdef HAVE_SSL
if (state->sock_state.ssl && state->sock_state.ssl_ctx)
newlen = ssl_socket_receive_all_nonblocking(
state->sock_state.ssl_ctx,
&state->error,
(uint8_t*)state->data + state->pos,
state->buflen - state->pos);
else
#endif
newlen = socket_receive_all_nonblocking(
state->sock_state.fd,
&state->error,
(uint8_t*)state->data + state->pos,
state->buflen - state->pos);
}
if (state->pos > state->len)
{
state->error = true;
state->part = P_ERROR;
state->status = -1;
return true;
if (newlen < 0)
{
if (state->bodytype != T_FULL)
{
state->error = true;
goto error;
}
state->part = P_DONE;
state->data = (char*)realloc(state->data, state->len);
newlen = 0;
}
if (state->pos + newlen >= state->buflen - 64)
{
state->buflen *= 2;
state->data = (char*)realloc(state->data, state->buflen);
}
}
else if (state->pos == state->len)
parse_again:
if (state->bodytype == T_CHUNK)
{
state->part = P_DONE;
state->data = (char*)realloc(state->data, state->len);
if (state->part == P_BODY_CHUNKLEN)
{
state->pos += newlen;
if (state->pos - state->len >= 2)
{
/*
* len=start of chunk including \r\n
* pos=end of data
*/
char *fullend = state->data + state->pos;
char *end = (char*)memchr(state->data + state->len + 2, '\n',
state->pos - state->len - 2);
if (end)
{
size_t chunklen = strtoul(state->data+state->len, NULL, 16);
state->pos = state->len;
end++;
memmove(state->data+state->len, end, fullend-end);
state->len = chunklen;
newlen = (fullend - end);
/*
len=num bytes
newlen=unparsed bytes after \n
pos=start of chunk including \r\n
*/
state->part = P_BODY;
if (state->len == 0)
{
state->part = P_DONE;
state->len = state->pos;
state->data = (char*)realloc(state->data, state->len);
}
goto parse_again;
}
}
}
else if (state->part == P_BODY)
{
if ((size_t)newlen >= state->len)
{
state->pos += state->len;
newlen -= state->len;
state->len = state->pos;
state->part = P_BODY_CHUNKLEN;
goto parse_again;
}
state->pos += newlen;
state->len -= newlen;
}
}
else
{
state->pos += newlen;
if (state->pos > state->len)
{
state->error = true;
goto error;
}
else if (state->pos == state->len)
{
state->part = P_DONE;
state->data = (char*)realloc(state->data, state->len);
}
}
}
if (progress)
*progress = state->pos;
if (total)
{
if (state->bodytype == T_LEN)
*total = state->len;
else
*total = 0;
}
if (state->part != P_DONE)
return false;
}
if (progress)
*progress = state->pos;
if (total)
{
if (state->bodytype == T_LEN)
*total = state->len;
else
*total = 0;
}
return (state->part == P_DONE);
return true;
error:
state->part = P_ERROR;
state->status = -1;
return true;
}
/**