From 1ec430513f61d7327368f45a05001fd302ea2b66 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Mon, 11 Jun 2018 21:35:11 +0100 Subject: [PATCH 01/32] t --- CMakeLists.txt | 2 +- src/http_server.c | 49 - src/http_server.h | 41 - src/mongoose.c | 5406 --------------------------------------------- src/mongoose.h | 152 -- src/mpd_client.h | 2 +- src/mympd.c | 39 +- 7 files changed, 23 insertions(+), 5668 deletions(-) delete mode 100644 src/http_server.c delete mode 100644 src/http_server.h delete mode 100644 src/mongoose.c delete mode 100644 src/mongoose.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0819398..94be1bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,7 +47,7 @@ file(GLOB RESOURCES set(SOURCES src/mympd.c src/mpd_client.c - src/mongoose.c + src/mongoose/mongoose.c src/json_encode.c ) diff --git a/src/http_server.c b/src/http_server.c deleted file mode 100644 index cbdf250..0000000 --- a/src/http_server.c +++ /dev/null @@ -1,49 +0,0 @@ -/* myMPD - (c) 2018 Juergen Mang - This project's homepage is: https://github.com/jcorporation/ympd - - myMPD ist fork of: - - ympd - (c) 2013-2014 Andrew Karpow - This project's homepage is: http://www.ympd.org - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#include - -#include "http_server.h" - -int callback_http(struct mg_connection *c) -{ - const struct embedded_file *req_file; - - if(!strcmp(c->uri, "/")) - req_file = find_embedded_file("/index.html"); - else - req_file = find_embedded_file(c->uri); - - if(req_file) - { - mg_send_header(c, "Content-Type", req_file->mimetype); - mg_send_data(c, req_file->data, req_file->size); - - return MG_TRUE; - } - - mg_send_status(c, 404); - mg_printf_data(c, "Not Found"); - return MG_TRUE; -} diff --git a/src/http_server.h b/src/http_server.h deleted file mode 100644 index fb0dbf0..0000000 --- a/src/http_server.h +++ /dev/null @@ -1,41 +0,0 @@ -/* myMPD - (c) 2018 Juergen Mang - This project's homepage is: https://github.com/jcorporation/ympd - - myMPD ist fork of: - - ympd - (c) 2013-2014 Andrew Karpow - This project's homepage is: http://www.ympd.org - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#ifndef __HTTP_SERVER_H__ -#define __HTTP_SERVER_H__ - -#include "mongoose.h" - -struct embedded_file { - const char *name; - const unsigned char *data; - const char *mimetype; - size_t size; -}; - -const struct embedded_file *find_embedded_file(const char *name); -int callback_http(struct mg_connection *c); - -#endif - diff --git a/src/mongoose.c b/src/mongoose.c deleted file mode 100644 index 94d1c68..0000000 --- a/src/mongoose.c +++ /dev/null @@ -1,5406 +0,0 @@ -// Copyright (c) 2004-2013 Sergey Lyubka -// Copyright (c) 2013-2014 Cesanta Software Limited -// All rights reserved -// -// This library is dual-licensed: you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. For the terms of this -// license, see . -// -// You are free to use this library under the terms of the GNU General -// Public License, but WITHOUT ANY WARRANTY; without even the implied -// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU General Public License for more details. -// -// Alternatively, you can license this library under a commercial -// license, as set out in . - -#ifdef NOEMBED_NET_SKELETON -#include "net_skeleton.h" -#else -// net_skeleton start -// Copyright (c) 2014 Cesanta Software Limited -// All rights reserved -// -// This software is dual-licensed: you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. For the terms of this -// license, see . -// -// You are free to use this software under the terms of the GNU General -// Public License, but WITHOUT ANY WARRANTY; without even the implied -// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU General Public License for more details. -// -// Alternatively, you can license this software under a commercial -// license, as set out in . - -#ifndef NS_SKELETON_HEADER_INCLUDED -#define NS_SKELETON_HEADER_INCLUDED - -#define NS_SKELETON_VERSION "2.1.0" - -#undef UNICODE // Use ANSI WinAPI functions -#undef _UNICODE // Use multibyte encoding on Windows -#define _MBCS // Use multibyte encoding on Windows -#define _INTEGRAL_MAX_BITS 64 // Enable _stati64() on Windows -#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005+ -#undef WIN32_LEAN_AND_MEAN // Let windows.h always include winsock2.h -#ifdef __Linux__ -#define _XOPEN_SOURCE 600 // For flockfile() on Linux -#endif -#define __STDC_FORMAT_MACROS // wants this for C++ -#define __STDC_LIMIT_MACROS // C++ wants that for INT64_MAX -#ifndef _LARGEFILE_SOURCE -#define _LARGEFILE_SOURCE // Enable fseeko() and ftello() functions -#endif -#define _FILE_OFFSET_BITS 64 // Enable 64-bit file offsets - -#ifdef _MSC_VER -#pragma warning (disable : 4127) // FD_SET() emits warning, disable it -#pragma warning (disable : 4204) // missing c99 support -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") // Linking with winsock library -#include -typedef SSIZE_T ssize_t; -#endif -#include -#include -#include -#include -#ifndef EINPROGRESS -#define EINPROGRESS WSAEINPROGRESS -#endif -#ifndef EWOULDBLOCK -#define EWOULDBLOCK WSAEWOULDBLOCK -#endif -#ifndef __func__ -#define STRX(x) #x -#define STR(x) STRX(x) -#define __func__ __FILE__ ":" STR(__LINE__) -#endif -#ifndef va_copy -#define va_copy(x,y) x = y -#endif // MINGW #defines va_copy -#define snprintf _snprintf -#define vsnprintf _vsnprintf -#define sleep(x) Sleep((x) * 1000) -#define to64(x) _atoi64(x) -typedef int socklen_t; -typedef unsigned char uint8_t; -typedef unsigned int uint32_t; -typedef unsigned short uint16_t; -typedef unsigned __int64 uint64_t; -typedef __int64 int64_t; -typedef SOCKET sock_t; -typedef struct _stati64 ns_stat_t; -#ifndef S_ISDIR -#define S_ISDIR(x) ((x) & _S_IFDIR) -#endif -#else -#include -#include -#include -#include -#include -#include -#include // For inet_pton() when NS_ENABLE_IPV6 is defined -#include -#include -#include -#define closesocket(x) close(x) -#ifndef __OS2__ -#define __cdecl -#else -#include -typedef int socklen_t; -#endif -#define INVALID_SOCKET (-1) -#define to64(x) strtoll(x, NULL, 10) -typedef int sock_t; -typedef struct stat ns_stat_t; -#endif - -#ifdef NS_ENABLE_DEBUG -#define DBG(x) do { printf("%-20s ", __func__); printf x; putchar('\n'); \ - fflush(stdout); } while(0) -#else -#define DBG(x) -#endif - -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) -#endif - -#ifdef NS_ENABLE_SSL -#ifdef __APPLE__ -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif -#include -#else -typedef void *SSL; -typedef void *SSL_CTX; -#endif - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -union socket_address { - struct sockaddr sa; - struct sockaddr_in sin; -#ifdef NS_ENABLE_IPV6 - struct sockaddr_in6 sin6; -#else - struct sockaddr sin6; -#endif -}; - -// Describes chunk of memory -struct ns_str { - const char *p; - size_t len; -}; - -// IO buffers interface -struct iobuf { - char *buf; - size_t len; - size_t size; -}; - -void iobuf_init(struct iobuf *, size_t initial_size); -void iobuf_free(struct iobuf *); -size_t iobuf_append(struct iobuf *, const void *data, size_t data_size); -void iobuf_remove(struct iobuf *, size_t data_size); -void iobuf_resize(struct iobuf *, size_t new_size); - -// Callback function (event handler) prototype, must be defined by user. -// Net skeleton will call event handler, passing events defined above. -struct ns_connection; -typedef void (*ns_callback_t)(struct ns_connection *, int event_num, void *evp); - -// Events. Meaning of event parameter (evp) is given in the comment. -#define NS_POLL 0 // Sent to each connection on each call to ns_mgr_poll() -#define NS_ACCEPT 1 // New connection accept()-ed. union socket_address *addr -#define NS_CONNECT 2 // connect() succeeded or failed. int *success_status -#define NS_RECV 3 // Data has benn received. int *num_bytes -#define NS_SEND 4 // Data has been written to a socket. int *num_bytes -#define NS_CLOSE 5 // Connection is closed. NULL - - -struct ns_mgr { - struct ns_connection *active_connections; - const char *hexdump_file; // Debug hexdump file path - sock_t ctl[2]; // Socketpair for mg_wakeup() - void *user_data; // User data -}; - - -struct ns_connection { - struct ns_connection *next, *prev; // ns_mgr::active_connections linkage - struct ns_connection *listener; // Set only for accept()-ed connections - struct ns_mgr *mgr; - - sock_t sock; // Socket - union socket_address sa; // Peer address - struct iobuf recv_iobuf; // Received data - struct iobuf send_iobuf; // Data scheduled for sending - SSL *ssl; - SSL_CTX *ssl_ctx; - void *user_data; // User-specific data - void *proto_data; // Application protocol-specific data - time_t last_io_time; // Timestamp of the last socket IO - ns_callback_t callback; // Event handler function - - unsigned int flags; -#define NSF_FINISHED_SENDING_DATA (1 << 0) -#define NSF_BUFFER_BUT_DONT_SEND (1 << 1) -#define NSF_SSL_HANDSHAKE_DONE (1 << 2) -#define NSF_CONNECTING (1 << 3) -#define NSF_CLOSE_IMMEDIATELY (1 << 4) -#define NSF_WANT_READ (1 << 5) -#define NSF_WANT_WRITE (1 << 6) -#define NSF_LISTENING (1 << 7) -#define NSF_UDP (1 << 8) - -#define NSF_USER_1 (1 << 20) -#define NSF_USER_2 (1 << 21) -#define NSF_USER_3 (1 << 22) -#define NSF_USER_4 (1 << 23) -#define NSF_USER_5 (1 << 24) -#define NSF_USER_6 (1 << 25) -}; - -void ns_mgr_init(struct ns_mgr *, void *user_data); -void ns_mgr_free(struct ns_mgr *); -time_t ns_mgr_poll(struct ns_mgr *, int milli); -void ns_broadcast(struct ns_mgr *, ns_callback_t, void *, size_t); - -struct ns_connection *ns_next(struct ns_mgr *, struct ns_connection *); -struct ns_connection *ns_add_sock(struct ns_mgr *, sock_t, - ns_callback_t, void *); -struct ns_connection *ns_bind(struct ns_mgr *, const char *, - ns_callback_t, void *); -struct ns_connection *ns_connect(struct ns_mgr *, const char *, - ns_callback_t, void *); - -int ns_send(struct ns_connection *, const void *buf, size_t len); -int ns_printf(struct ns_connection *, const char *fmt, ...); -int ns_vprintf(struct ns_connection *, const char *fmt, va_list ap); - -// Utility functions -void *ns_start_thread(void *(*f)(void *), void *p); -int ns_socketpair(sock_t [2]); -int ns_socketpair2(sock_t [2], int sock_type); // SOCK_STREAM or SOCK_DGRAM -void ns_set_close_on_exec(sock_t); -void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags); -int ns_hexdump(const void *buf, int len, char *dst, int dst_len); -int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap); -int ns_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len); - -#ifdef __cplusplus -} -#endif // __cplusplus - -#endif // NS_SKELETON_HEADER_INCLUDED -// Copyright (c) 2014 Cesanta Software Limited -// All rights reserved -// -// This software is dual-licensed: you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. For the terms of this -// license, see . -// -// You are free to use this software under the terms of the GNU General -// Public License, but WITHOUT ANY WARRANTY; without even the implied -// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU General Public License for more details. -// -// Alternatively, you can license this software under a commercial -// license, as set out in . -// -// $Date: 2014-09-28 05:04:41 UTC $ - - -#ifndef NS_MALLOC -#define NS_MALLOC malloc -#endif - -#ifndef NS_REALLOC -#define NS_REALLOC realloc -#endif - -#ifndef NS_FREE -#define NS_FREE free -#endif - -#ifndef NS_CALLOC -#define NS_CALLOC calloc -#endif - -#define NS_CTL_MSG_MESSAGE_SIZE (8 * 1024) -#define NS_READ_BUFFER_SIZE 2048 -#define NS_UDP_RECEIVE_BUFFER_SIZE 2000 -#define NS_VPRINTF_BUFFER_SIZE 500 - -struct ctl_msg { - ns_callback_t callback; - char message[NS_CTL_MSG_MESSAGE_SIZE]; -}; - -void iobuf_resize(struct iobuf *io, size_t new_size) { - char *p; - if ((new_size > io->size || (new_size < io->size && new_size >= io->len)) && - (p = (char *) NS_REALLOC(io->buf, new_size)) != NULL) { - io->size = new_size; - io->buf = p; - } -} - -void iobuf_init(struct iobuf *iobuf, size_t initial_size) { - iobuf->len = iobuf->size = 0; - iobuf->buf = NULL; - iobuf_resize(iobuf, initial_size); -} - -void iobuf_free(struct iobuf *iobuf) { - if (iobuf != NULL) { - if (iobuf->buf != NULL) NS_FREE(iobuf->buf); - iobuf_init(iobuf, 0); - } -} - -size_t iobuf_append(struct iobuf *io, const void *buf, size_t len) { - char *p = NULL; - - assert(io != NULL); - assert(io->len <= io->size); - - /* check overflow */ - if (len > ~(size_t)0 - (size_t)(io->buf + io->len)) { - return 0; - } - - if (len <= 0) { - } else if (io->len + len <= io->size) { - memcpy(io->buf + io->len, buf, len); - io->len += len; - } else if ((p = (char *) NS_REALLOC(io->buf, io->len + len)) != NULL) { - io->buf = p; - memcpy(io->buf + io->len, buf, len); - io->len += len; - io->size = io->len; - } else { - len = 0; - } - - return len; -} - -void iobuf_remove(struct iobuf *io, size_t n) { - if (n > 0 && n <= io->len) { - memmove(io->buf, io->buf + n, io->len - n); - io->len -= n; - } -} - -static size_t ns_out(struct ns_connection *nc, const void *buf, size_t len) { - if (nc->flags & NSF_UDP) { - long n = sendto(nc->sock, (const char *) buf, len, 0, &nc->sa.sa, - sizeof(nc->sa.sin)); - DBG(("%p %d send %ld (%d %s)", nc, nc->sock, n, errno, strerror(errno))); - return n < 0 ? 0 : n; - } else { - return iobuf_append(&nc->send_iobuf, buf, len); - } -} - -#ifndef NS_DISABLE_THREADS -void *ns_start_thread(void *(*f)(void *), void *p) { -#ifdef _WIN32 - return (void *) _beginthread((void (__cdecl *)(void *)) f, 0, p); -#else - pthread_t thread_id = (pthread_t) 0; - pthread_attr_t attr; - - (void) pthread_attr_init(&attr); - (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - -#if defined(NS_STACK_SIZE) && NS_STACK_SIZE > 1 - (void) pthread_attr_setstacksize(&attr, NS_STACK_SIZE); -#endif - - pthread_create(&thread_id, &attr, f, p); - pthread_attr_destroy(&attr); - - return (void *) thread_id; -#endif -} -#endif // NS_DISABLE_THREADS - -static void ns_add_conn(struct ns_mgr *mgr, struct ns_connection *c) { - c->next = mgr->active_connections; - mgr->active_connections = c; - c->prev = NULL; - if (c->next != NULL) c->next->prev = c; -} - -static void ns_remove_conn(struct ns_connection *conn) { - if (conn->prev == NULL) conn->mgr->active_connections = conn->next; - if (conn->prev) conn->prev->next = conn->next; - if (conn->next) conn->next->prev = conn->prev; -} - -// Print message to buffer. If buffer is large enough to hold the message, -// return buffer. If buffer is to small, allocate large enough buffer on heap, -// and return allocated buffer. -int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap) { - va_list ap_copy; - int len; - - va_copy(ap_copy, ap); - len = vsnprintf(*buf, size, fmt, ap_copy); - va_end(ap_copy); - - if (len < 0) { - // eCos and Windows are not standard-compliant and return -1 when - // the buffer is too small. Keep allocating larger buffers until we - // succeed or out of memory. - *buf = NULL; - while (len < 0) { - if (*buf) NS_FREE(*buf); - size *= 2; - if ((*buf = (char *) NS_MALLOC(size)) == NULL) break; - va_copy(ap_copy, ap); - len = vsnprintf(*buf, size, fmt, ap_copy); - va_end(ap_copy); - } - } else if (len > (int) size) { - // Standard-compliant code path. Allocate a buffer that is large enough. - if ((*buf = (char *) NS_MALLOC(len + 1)) == NULL) { - len = -1; - } else { - va_copy(ap_copy, ap); - len = vsnprintf(*buf, len + 1, fmt, ap_copy); - va_end(ap_copy); - } - } - - return len; -} - -int ns_vprintf(struct ns_connection *nc, const char *fmt, va_list ap) { - char mem[NS_VPRINTF_BUFFER_SIZE], *buf = mem; - int len; - - if ((len = ns_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) { - ns_out(nc, buf, len); - } - if (buf != mem && buf != NULL) { - NS_FREE(buf); - } - - return len; -} - -int ns_printf(struct ns_connection *conn, const char *fmt, ...) { - int len; - va_list ap; - va_start(ap, fmt); - len = ns_vprintf(conn, fmt, ap); - va_end(ap); - return len; -} - -static void hexdump(struct ns_connection *nc, const char *path, - int num_bytes, int ev) { - const struct iobuf *io = ev == NS_SEND ? &nc->send_iobuf : &nc->recv_iobuf; - FILE *fp; - char *buf, src[60], dst[60]; - int buf_size = num_bytes * 5 + 100; - - if ((fp = fopen(path, "a")) != NULL) { - ns_sock_to_str(nc->sock, src, sizeof(src), 3); - ns_sock_to_str(nc->sock, dst, sizeof(dst), 7); - fprintf(fp, "%lu %p %s %s %s %d\n", (unsigned long) time(NULL), - nc->user_data, src, - ev == NS_RECV ? "<-" : ev == NS_SEND ? "->" : - ev == NS_ACCEPT ? "" : "XX", - dst, num_bytes); - if (num_bytes > 0 && (buf = (char *) NS_MALLOC(buf_size)) != NULL) { - ns_hexdump(io->buf + (ev == NS_SEND ? 0 : io->len) - - (ev == NS_SEND ? 0 : num_bytes), num_bytes, buf, buf_size); - fprintf(fp, "%s", buf); - NS_FREE(buf); - } - fclose(fp); - } -} - -static void ns_call(struct ns_connection *nc, int ev, void *p) { - if (nc->mgr->hexdump_file != NULL && ev != NS_POLL) { - int len = (ev == NS_RECV || ev == NS_SEND) ? * (int *) p : 0; - hexdump(nc, nc->mgr->hexdump_file, len, ev); - } - - nc->callback(nc, ev, p); -} - -static void ns_destroy_conn(struct ns_connection *conn) { - closesocket(conn->sock); - iobuf_free(&conn->recv_iobuf); - iobuf_free(&conn->send_iobuf); -#ifdef NS_ENABLE_SSL - if (conn->ssl != NULL) { - SSL_free(conn->ssl); - } - if (conn->ssl_ctx != NULL) { - SSL_CTX_free(conn->ssl_ctx); - } -#endif - NS_FREE(conn); -} - -static void ns_close_conn(struct ns_connection *conn) { - DBG(("%p %d", conn, conn->flags)); - ns_call(conn, NS_CLOSE, NULL); - ns_remove_conn(conn); - ns_destroy_conn(conn); -} - -void ns_set_close_on_exec(sock_t sock) { -#ifdef _WIN32 - (void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0); -#else - fcntl(sock, F_SETFD, FD_CLOEXEC); -#endif -} - -static void ns_set_non_blocking_mode(sock_t sock) { -#ifdef _WIN32 - unsigned long on = 1; - ioctlsocket(sock, FIONBIO, &on); -#else - int flags = fcntl(sock, F_GETFL, 0); - fcntl(sock, F_SETFL, flags | O_NONBLOCK); -#endif -} - -#ifndef NS_DISABLE_SOCKETPAIR -int ns_socketpair2(sock_t sp[2], int sock_type) { - union socket_address sa; - sock_t sock; - socklen_t len = sizeof(sa.sin); - int ret = 0; - - sp[0] = sp[1] = INVALID_SOCKET; - - (void) memset(&sa, 0, sizeof(sa)); - sa.sin.sin_family = AF_INET; - sa.sin.sin_port = htons(0); - sa.sin.sin_addr.s_addr = htonl(0x7f000001); - - if ((sock = socket(AF_INET, sock_type, 0)) != INVALID_SOCKET && - !bind(sock, &sa.sa, len) && - (sock_type == SOCK_DGRAM || !listen(sock, 1)) && - !getsockname(sock, &sa.sa, &len) && - (sp[0] = socket(AF_INET, sock_type, 0)) != INVALID_SOCKET && - !connect(sp[0], &sa.sa, len) && - (sock_type == SOCK_STREAM || - (!getsockname(sp[0], &sa.sa, &len) && !connect(sock, &sa.sa, len))) && - (sp[1] = (sock_type == SOCK_DGRAM ? sock : - accept(sock, &sa.sa, &len))) != INVALID_SOCKET) { - ns_set_close_on_exec(sp[0]); - ns_set_close_on_exec(sp[1]); - ret = 1; - } else { - if (sp[0] != INVALID_SOCKET) closesocket(sp[0]); - if (sp[1] != INVALID_SOCKET) closesocket(sp[1]); - sp[0] = sp[1] = INVALID_SOCKET; - } - if (sock_type != SOCK_DGRAM) closesocket(sock); - - return ret; -} - -int ns_socketpair(sock_t sp[2]) { - return ns_socketpair2(sp, SOCK_STREAM); -} -#endif // NS_DISABLE_SOCKETPAIR - -// TODO(lsm): use non-blocking resolver -static int ns_resolve2(const char *host, struct in_addr *ina) { -#ifdef NS_ENABLE_GETADDRINFO - int rv = 0; - struct addrinfo hints, *servinfo, *p; - struct sockaddr_in *h = NULL; - char *ip = NS_MALLOC(17); - memset(ip, '\0', 17); - - memset(&hints, 0, sizeof hints); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - - if((rv = getaddrinfo(host, NULL , NULL, &servinfo)) != 0) { - DBG(("getaddrinfo(%s) failed: %s", host, strerror(errno))); - return 0; - } - - for(p = servinfo; p != NULL; p = p->ai_next) { - memcpy(&h, &p->ai_addr, sizeof(struct sockaddr_in *)); - memcpy(ina, &h->sin_addr, sizeof(ina)); - } - - freeaddrinfo(servinfo); - return 1; -#else - struct hostent *he; - if ((he = gethostbyname(host)) == NULL) { - DBG(("gethostbyname(%s) failed: %s", host, strerror(errno))); - } else { - memcpy(ina, he->h_addr_list[0], sizeof(*ina)); - return 1; - } - return 0; -#endif -} - -// Resolve FDQN "host", store IP address in the "ip". -// Return > 0 (IP address length) on success. -int ns_resolve(const char *host, char *buf, size_t n) { - struct in_addr ad; - return ns_resolve2(host, &ad) ? snprintf(buf, n, "%s", inet_ntoa(ad)) : 0; -} - -// Address format: [PROTO://][IP_ADDRESS:]PORT[:CERT][:CA_CERT] -static int ns_parse_address(const char *str, union socket_address *sa, - int *proto, int *use_ssl, char *cert, char *ca) { - unsigned int a, b, c, d, port; - int n = 0, len = 0; - char host[200]; -#ifdef NS_ENABLE_IPV6 - char buf[100]; -#endif - - // MacOS needs that. If we do not zero it, subsequent bind() will fail. - // Also, all-zeroes in the socket address means binding to all addresses - // for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT). - memset(sa, 0, sizeof(*sa)); - sa->sin.sin_family = AF_INET; - - *proto = SOCK_STREAM; - *use_ssl = 0; - cert[0] = ca[0] = '\0'; - - if (strlen(str)>=6) { - if (memcmp(str, "ssl://", 6) == 0) { - str += 6; - *use_ssl = 1; - } else if (memcmp(str, "udp://", 6) == 0) { - str += 6; - *proto = SOCK_DGRAM; - } else if (memcmp(str, "tcp://", 6) == 0) { - str += 6; - } - } - - if (sscanf(str, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) { - // Bind to a specific IPv4 address, e.g. 192.168.1.5:8080 - sa->sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d); - sa->sin.sin_port = htons((uint16_t) port); -#ifdef NS_ENABLE_IPV6 - } else if (sscanf(str, "[%99[^]]]:%u%n", buf, &port, &len) == 2 && - inet_pton(AF_INET6, buf, &sa->sin6.sin6_addr)) { - // IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080 - sa->sin6.sin6_family = AF_INET6; - sa->sin6.sin6_port = htons((uint16_t) port); -#endif - } else if (sscanf(str, "%199[^ :]:%u%n", host, &port, &len) == 2) { - sa->sin.sin_port = htons((uint16_t) port); - ns_resolve2(host, &sa->sin.sin_addr); - } else if (sscanf(str, "%u%n", &port, &len) == 1) { - // If only port is specified, bind to IPv4, INADDR_ANY - sa->sin.sin_port = htons((uint16_t) port); - } - - if (*use_ssl && (sscanf(str + len, ":%99[^:,]:%99[^:,]%n", cert, ca, &n) == 2 || - sscanf(str + len, ":%99[^:,]%n", cert, &n) == 1)) { - len += n; - } - - return port < 0xffff && str[len] == '\0' ? len : 0; -} - -// 'sa' must be an initialized address to bind to -static sock_t ns_open_listening_socket(union socket_address *sa, int proto) { - socklen_t sa_len = (sa->sa.sa_family == AF_INET) ? - sizeof(sa->sin) : sizeof(sa->sin6); - sock_t sock = INVALID_SOCKET; -#ifndef _WIN32 - int on = 1; -#endif - - if ((sock = socket(sa->sa.sa_family, proto, 0)) != INVALID_SOCKET && -#ifndef _WIN32 - // SO_RESUSEADDR is not enabled on Windows because the semantics of - // SO_REUSEADDR on UNIX and Windows is different. On Windows, - // SO_REUSEADDR allows to bind a socket to a port without error even if - // the port is already open by another program. This is not the behavior - // SO_REUSEADDR was designed for, and leads to hard-to-track failure - // scenarios. Therefore, SO_REUSEADDR was disabled on Windows. - !setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) && -#endif - !bind(sock, &sa->sa, sa_len) && - (proto == SOCK_DGRAM || listen(sock, SOMAXCONN) == 0)) { - ns_set_non_blocking_mode(sock); - // In case port was set to 0, get the real port number - (void) getsockname(sock, &sa->sa, &sa_len); - } else if (sock != INVALID_SOCKET) { - closesocket(sock); - sock = INVALID_SOCKET; - } - - return sock; -} - -#ifdef NS_ENABLE_SSL -// Certificate generation script is at -// https://github.com/cesanta/net_skeleton/blob/master/scripts/gen_certs.sh - -static int ns_use_ca_cert(SSL_CTX *ctx, const char *cert) { - if (ctx == NULL) { - return -1; - } else if (cert == NULL || cert[0] == '\0') { - return 0; - } - SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0); - return SSL_CTX_load_verify_locations(ctx, cert, NULL) == 1 ? 0 : -2; -} - -static int ns_use_cert(SSL_CTX *ctx, const char *pem_file) { - if (ctx == NULL) { - return -1; - } else if (pem_file == NULL || pem_file[0] == '\0') { - return 0; - } else if (SSL_CTX_use_certificate_file(ctx, pem_file, 1) == 0 || - SSL_CTX_use_PrivateKey_file(ctx, pem_file, 1) == 0) { - return -2; - } else { - SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - SSL_CTX_use_certificate_chain_file(ctx, pem_file); - return 0; - } -} -#endif // NS_ENABLE_SSL - -struct ns_connection *ns_bind(struct ns_mgr *srv, const char *str, - ns_callback_t callback, void *user_data) { - union socket_address sa; - struct ns_connection *nc = NULL; - int use_ssl, proto; - char cert[100], ca_cert[100]; - sock_t sock; - - ns_parse_address(str, &sa, &proto, &use_ssl, cert, ca_cert); - if (use_ssl && cert[0] == '\0') return NULL; - - if ((sock = ns_open_listening_socket(&sa, proto)) == INVALID_SOCKET) { - } else if ((nc = ns_add_sock(srv, sock, callback, NULL)) == NULL) { - closesocket(sock); - } else { - nc->sa = sa; - nc->flags |= NSF_LISTENING; - nc->user_data = user_data; - nc->callback = callback; - - if (proto == SOCK_DGRAM) { - nc->flags |= NSF_UDP; - } - -#ifdef NS_ENABLE_SSL - if (use_ssl) { - nc->ssl_ctx = SSL_CTX_new(SSLv23_server_method()); - if (ns_use_cert(nc->ssl_ctx, cert) != 0 || - ns_use_ca_cert(nc->ssl_ctx, ca_cert) != 0) { - ns_close_conn(nc); - nc = NULL; - } - } -#endif - - DBG(("%p sock %d/%d ssl %p %p", nc, sock, proto, nc->ssl_ctx, nc->ssl)); - } - - return nc; -} - -static struct ns_connection *accept_conn(struct ns_connection *ls) { - struct ns_connection *c = NULL; - union socket_address sa; - socklen_t len = sizeof(sa); - sock_t sock = INVALID_SOCKET; - - // NOTE(lsm): on Windows, sock is always > FD_SETSIZE - if ((sock = accept(ls->sock, &sa.sa, &len)) == INVALID_SOCKET) { - } else if ((c = ns_add_sock(ls->mgr, sock, ls->callback, - ls->user_data)) == NULL) { - closesocket(sock); -#ifdef NS_ENABLE_SSL - } else if (ls->ssl_ctx != NULL && - ((c->ssl = SSL_new(ls->ssl_ctx)) == NULL || - SSL_set_fd(c->ssl, sock) != 1)) { - DBG(("SSL error")); - ns_close_conn(c); - c = NULL; -#endif - } else { - c->listener = ls; - c->proto_data = ls->proto_data; - ns_call(c, NS_ACCEPT, &sa); - DBG(("%p %d %p %p", c, c->sock, c->ssl_ctx, c->ssl)); - } - - return c; -} - -static int ns_is_error(int n) { - return n == 0 || - (n < 0 && errno != EINTR && errno != EINPROGRESS && - errno != EAGAIN && errno != EWOULDBLOCK -#ifdef _WIN32 - && WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK -#endif - ); -} - -void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags) { - union socket_address sa; - socklen_t slen = sizeof(sa); - - if (buf != NULL && len > 0) { - buf[0] = '\0'; - memset(&sa, 0, sizeof(sa)); - if (flags & 4) { - getpeername(sock, &sa.sa, &slen); - } else { - getsockname(sock, &sa.sa, &slen); - } - if (flags & 1) { -#if defined(NS_ENABLE_IPV6) - inet_ntop(sa.sa.sa_family, sa.sa.sa_family == AF_INET ? - (void *) &sa.sin.sin_addr : - (void *) &sa.sin6.sin6_addr, buf, len); -#elif defined(_WIN32) - // Only Windoze Vista (and newer) have inet_ntop() - strncpy(buf, inet_ntoa(sa.sin.sin_addr), len); -#else - inet_ntop(sa.sa.sa_family, (void *) &sa.sin.sin_addr, buf,(socklen_t)len); -#endif - } - if (flags & 2) { - snprintf(buf + strlen(buf), len - (strlen(buf) + 1), "%s%d", - flags & 1 ? ":" : "", (int) ntohs(sa.sin.sin_port)); - } - } -} - -int ns_hexdump(const void *buf, int len, char *dst, int dst_len) { - const unsigned char *p = (const unsigned char *) buf; - char ascii[17] = ""; - int i, idx, n = 0; - - for (i = 0; i < len; i++) { - idx = i % 16; - if (idx == 0) { - if (i > 0) n += snprintf(dst + n, dst_len - n, " %s\n", ascii); - n += snprintf(dst + n, dst_len - n, "%04x ", i); - } - n += snprintf(dst + n, dst_len - n, " %02x", p[i]); - ascii[idx] = p[i] < 0x20 || p[i] > 0x7e ? '.' : p[i]; - ascii[idx + 1] = '\0'; - } - - while (i++ % 16) n += snprintf(dst + n, dst_len - n, "%s", " "); - n += snprintf(dst + n, dst_len - n, " %s\n\n", ascii); - - return n; -} - -#ifdef NS_ENABLE_SSL -static int ns_ssl_err(struct ns_connection *conn, int res) { - int ssl_err = SSL_get_error(conn->ssl, res); - if (ssl_err == SSL_ERROR_WANT_READ) conn->flags |= NSF_WANT_READ; - if (ssl_err == SSL_ERROR_WANT_WRITE) conn->flags |= NSF_WANT_WRITE; - return ssl_err; -} -#endif - -static void ns_read_from_socket(struct ns_connection *conn) { - char buf[NS_READ_BUFFER_SIZE]; - int n = 0; - - if (conn->flags & NSF_CONNECTING) { - int ok = 1, ret; - socklen_t len = sizeof(ok); - - ret = getsockopt(conn->sock, SOL_SOCKET, SO_ERROR, (char *) &ok, &len); - (void) ret; -#ifdef NS_ENABLE_SSL - if (ret == 0 && ok == 0 && conn->ssl != NULL) { - int res = SSL_connect(conn->ssl); - int ssl_err = ns_ssl_err(conn, res); - if (res == 1) { - conn->flags |= NSF_SSL_HANDSHAKE_DONE; - } else if (ssl_err == SSL_ERROR_WANT_READ || - ssl_err == SSL_ERROR_WANT_WRITE) { - return; // Call us again - } else { - ok = 1; - } - conn->flags &= ~(NSF_WANT_READ | NSF_WANT_WRITE); - } -#endif - conn->flags &= ~NSF_CONNECTING; - DBG(("%p ok=%d", conn, ok)); - if (ok != 0) { - conn->flags |= NSF_CLOSE_IMMEDIATELY; - } - ns_call(conn, NS_CONNECT, &ok); - return; - } - -#ifdef NS_ENABLE_SSL - if (conn->ssl != NULL) { - if (conn->flags & NSF_SSL_HANDSHAKE_DONE) { - // SSL library may have more bytes ready to read then we ask to read. - // Therefore, read in a loop until we read everything. Without the loop, - // we skip to the next select() cycle which can just timeout. - while ((n = SSL_read(conn->ssl, buf, sizeof(buf))) > 0) { - DBG(("%p %d <- %d bytes (SSL)", conn, conn->flags, n)); - iobuf_append(&conn->recv_iobuf, buf, n); - ns_call(conn, NS_RECV, &n); - } - ns_ssl_err(conn, n); - } else { - int res = SSL_accept(conn->ssl); - int ssl_err = ns_ssl_err(conn, res); - if (res == 1) { - conn->flags |= NSF_SSL_HANDSHAKE_DONE; - conn->flags &= ~(NSF_WANT_READ | NSF_WANT_WRITE); - } else if (ssl_err == SSL_ERROR_WANT_READ || - ssl_err == SSL_ERROR_WANT_WRITE) { - return; // Call us again - } else { - conn->flags |= NSF_CLOSE_IMMEDIATELY; - } - return; - } - } else -#endif - { - while ((n = (int) recv(conn->sock, buf, sizeof(buf), 0)) > 0) { - DBG(("%p %d <- %d bytes (PLAIN)", conn, conn->flags, n)); - iobuf_append(&conn->recv_iobuf, buf, n); - ns_call(conn, NS_RECV, &n); - } - } - - if (ns_is_error(n)) { - conn->flags |= NSF_CLOSE_IMMEDIATELY; - } -} - -static void ns_write_to_socket(struct ns_connection *conn) { - struct iobuf *io = &conn->send_iobuf; - int n = 0; - -#ifdef NS_ENABLE_SSL - if (conn->ssl != NULL) { - n = SSL_write(conn->ssl, io->buf, io->len); - if (n <= 0) { - int ssl_err = ns_ssl_err(conn, n); - if (ssl_err == SSL_ERROR_WANT_READ || ssl_err == SSL_ERROR_WANT_WRITE) { - return; // Call us again - } else { - conn->flags |= NSF_CLOSE_IMMEDIATELY; - } - } else { - conn->flags &= ~(NSF_WANT_READ | NSF_WANT_WRITE); - } - } else -#endif - { n = (int) send(conn->sock, io->buf, io->len, 0); } - - DBG(("%p %d -> %d bytes", conn, conn->flags, n)); - - ns_call(conn, NS_SEND, &n); - if (ns_is_error(n)) { - conn->flags |= NSF_CLOSE_IMMEDIATELY; - } else if (n > 0) { - iobuf_remove(io, n); - } -} - -int ns_send(struct ns_connection *conn, const void *buf, size_t len) { - return (int) ns_out(conn, buf, len); -} - -static void ns_handle_udp(struct ns_connection *ls) { - struct ns_connection nc; - char buf[NS_UDP_RECEIVE_BUFFER_SIZE]; - ssize_t n; - socklen_t s_len = sizeof(nc.sa); - - memset(&nc, 0, sizeof(nc)); - n = recvfrom(ls->sock, buf, sizeof(buf), 0, &nc.sa.sa, &s_len); - if (n <= 0) { - DBG(("%p recvfrom: %s", ls, strerror(errno))); - } else { - nc.mgr = ls->mgr; - nc.recv_iobuf.buf = buf; - nc.recv_iobuf.len = nc.recv_iobuf.size = n; - nc.sock = ls->sock; - nc.callback = ls->callback; - nc.user_data = ls->user_data; - nc.proto_data = ls->proto_data; - nc.mgr = ls->mgr; - nc.listener = ls; - nc.flags = NSF_UDP; - DBG(("%p %d bytes received", ls, n)); - ns_call(&nc, NS_RECV, &n); - } -} - -static void ns_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) { - if (sock != INVALID_SOCKET) { - FD_SET(sock, set); - if (*max_fd == INVALID_SOCKET || sock > *max_fd) { - *max_fd = sock; - } - } -} - -time_t ns_mgr_poll(struct ns_mgr *mgr, int milli) { - struct ns_connection *conn, *tmp_conn; - struct timeval tv; - fd_set read_set, write_set; - sock_t max_fd = INVALID_SOCKET; - time_t current_time = time(NULL); - - FD_ZERO(&read_set); - FD_ZERO(&write_set); - ns_add_to_set(mgr->ctl[1], &read_set, &max_fd); - - for (conn = mgr->active_connections; conn != NULL; conn = tmp_conn) { - tmp_conn = conn->next; - if (!(conn->flags & (NSF_LISTENING | NSF_CONNECTING))) { - ns_call(conn, NS_POLL, ¤t_time); - } - if (conn->flags & NSF_CLOSE_IMMEDIATELY) { - ns_close_conn(conn); - } else { - if (!(conn->flags & NSF_WANT_WRITE)) { - //DBG(("%p read_set", conn)); - ns_add_to_set(conn->sock, &read_set, &max_fd); - } - if (((conn->flags & NSF_CONNECTING) && !(conn->flags & NSF_WANT_READ)) || - (conn->send_iobuf.len > 0 && !(conn->flags & NSF_CONNECTING) && - !(conn->flags & NSF_BUFFER_BUT_DONT_SEND))) { - //DBG(("%p write_set", conn)); - ns_add_to_set(conn->sock, &write_set, &max_fd); - } - } - } - - tv.tv_sec = milli / 1000; - tv.tv_usec = (milli % 1000) * 1000; - - if (select((int) max_fd + 1, &read_set, &write_set, NULL, &tv) > 0) { - // select() might have been waiting for a long time, reset current_time - // now to prevent last_io_time being set to the past. - current_time = time(NULL); - - // Read wakeup messages - if (mgr->ctl[1] != INVALID_SOCKET && - FD_ISSET(mgr->ctl[1], &read_set)) { - struct ctl_msg ctl_msg; - int len = (int) recv(mgr->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0); - send(mgr->ctl[1], ctl_msg.message, 1, 0); - if (len >= (int) sizeof(ctl_msg.callback) && ctl_msg.callback != NULL) { - struct ns_connection *c; - for (c = ns_next(mgr, NULL); c != NULL; c = ns_next(mgr, c)) { - ctl_msg.callback(c, NS_POLL, ctl_msg.message); - } - } - } - - for (conn = mgr->active_connections; conn != NULL; conn = tmp_conn) { - tmp_conn = conn->next; - if (FD_ISSET(conn->sock, &read_set)) { - if (conn->flags & NSF_LISTENING) { - if (conn->flags & NSF_UDP) { - ns_handle_udp(conn); - } else { - // We're not looping here, and accepting just one connection at - // a time. The reason is that eCos does not respect non-blocking - // flag on a listening socket and hangs in a loop. - accept_conn(conn); - } - } else { - conn->last_io_time = current_time; - ns_read_from_socket(conn); - } - } - - if (FD_ISSET(conn->sock, &write_set)) { - if (conn->flags & NSF_CONNECTING) { - ns_read_from_socket(conn); - } else if (!(conn->flags & NSF_BUFFER_BUT_DONT_SEND)) { - conn->last_io_time = current_time; - ns_write_to_socket(conn); - } - } - } - } - - for (conn = mgr->active_connections; conn != NULL; conn = tmp_conn) { - tmp_conn = conn->next; - if ((conn->flags & NSF_CLOSE_IMMEDIATELY) || - (conn->send_iobuf.len == 0 && - (conn->flags & NSF_FINISHED_SENDING_DATA))) { - ns_close_conn(conn); - } - } - - return current_time; -} - -struct ns_connection *ns_connect(struct ns_mgr *mgr, const char *address, - ns_callback_t callback, void *user_data) { - sock_t sock = INVALID_SOCKET; - struct ns_connection *nc = NULL; - union socket_address sa; - char cert[100], ca_cert[100]; - int rc, use_ssl, proto; - - ns_parse_address(address, &sa, &proto, &use_ssl, cert, ca_cert); - if ((sock = socket(AF_INET, proto, 0)) == INVALID_SOCKET) { - return NULL; - } - ns_set_non_blocking_mode(sock); - rc = (proto == SOCK_DGRAM) ? 0 : connect(sock, &sa.sa, sizeof(sa.sin)); - - if (rc != 0 && ns_is_error(rc)) { - closesocket(sock); - return NULL; - } else if ((nc = ns_add_sock(mgr, sock, callback, user_data)) == NULL) { - closesocket(sock); - return NULL; - } - - nc->sa = sa; // Important, cause UDP conns will use sendto() - nc->flags = (proto == SOCK_DGRAM) ? NSF_UDP : NSF_CONNECTING; - -#ifdef NS_ENABLE_SSL - if (use_ssl) { - if ((nc->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL || - ns_use_cert(nc->ssl_ctx, cert) != 0 || - ns_use_ca_cert(nc->ssl_ctx, ca_cert) != 0 || - (nc->ssl = SSL_new(nc->ssl_ctx)) == NULL) { - ns_close_conn(nc); - return NULL; - } else { - SSL_set_fd(nc->ssl, sock); - } - } -#endif - - return nc; -} - -struct ns_connection *ns_add_sock(struct ns_mgr *s, sock_t sock, - ns_callback_t callback, void *user_data) { - struct ns_connection *conn; - if ((conn = (struct ns_connection *) NS_MALLOC(sizeof(*conn))) != NULL) { - memset(conn, 0, sizeof(*conn)); - ns_set_non_blocking_mode(sock); - ns_set_close_on_exec(sock); - conn->sock = sock; - conn->user_data = user_data; - conn->callback = callback; - conn->mgr = s; - conn->last_io_time = time(NULL); - ns_add_conn(s, conn); - DBG(("%p %d", conn, sock)); - } - return conn; -} - -struct ns_connection *ns_next(struct ns_mgr *s, struct ns_connection *conn) { - return conn == NULL ? s->active_connections : conn->next; -} - -void ns_broadcast(struct ns_mgr *mgr, ns_callback_t cb,void *data, size_t len) { - struct ctl_msg ctl_msg; - if (mgr->ctl[0] != INVALID_SOCKET && data != NULL && - len < sizeof(ctl_msg.message)) { - ctl_msg.callback = cb; - memcpy(ctl_msg.message, data, len); - send(mgr->ctl[0], (char *) &ctl_msg, - offsetof(struct ctl_msg, message) + len, 0); - recv(mgr->ctl[0], (char *) &len, 1, 0); - } -} - -void ns_mgr_init(struct ns_mgr *s, void *user_data) { - memset(s, 0, sizeof(*s)); - s->ctl[0] = s->ctl[1] = INVALID_SOCKET; - s->user_data = user_data; - -#ifdef _WIN32 - { WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); } -#else - // Ignore SIGPIPE signal, so if client cancels the request, it - // won't kill the whole process. - signal(SIGPIPE, SIG_IGN); -#endif - -#ifndef NS_DISABLE_SOCKETPAIR - do { - ns_socketpair2(s->ctl, SOCK_DGRAM); - } while (s->ctl[0] == INVALID_SOCKET); -#endif - -#ifdef NS_ENABLE_SSL - {static int init_done; if (!init_done) { SSL_library_init(); init_done++; }} -#endif -} - -void ns_mgr_free(struct ns_mgr *s) { - struct ns_connection *conn, *tmp_conn; - - DBG(("%p", s)); - if (s == NULL) return; - // Do one last poll, see https://github.com/cesanta/mongoose/issues/286 - ns_mgr_poll(s, 0); - - if (s->ctl[0] != INVALID_SOCKET) closesocket(s->ctl[0]); - if (s->ctl[1] != INVALID_SOCKET) closesocket(s->ctl[1]); - s->ctl[0] = s->ctl[1] = INVALID_SOCKET; - - for (conn = s->active_connections; conn != NULL; conn = tmp_conn) { - tmp_conn = conn->next; - ns_close_conn(conn); - } -} -// net_skeleton end -#endif // NOEMBED_NET_SKELETON - -#include - -#ifdef _WIN32 //////////////// Windows specific defines and includes -#include // For _lseeki64 -#include // For _mkdir -#ifndef S_ISDIR -#define S_ISDIR(x) ((x) & _S_IFDIR) -#endif -#ifdef stat -#undef stat -#endif -#ifdef lseek -#undef lseek -#endif -#ifdef popen -#undef popen -#endif -#ifdef pclose -#undef pclose -#endif -#define stat(x, y) mg_stat((x), (y)) -#define fopen(x, y) mg_fopen((x), (y)) -#define open(x, y, z) mg_open((x), (y), (z)) -#define close(x) _close(x) -#define fileno(x) _fileno(x) -#define lseek(x, y, z) _lseeki64((x), (y), (z)) -#define read(x, y, z) _read((x), (y), (z)) -#define write(x, y, z) _write((x), (y), (z)) -#define popen(x, y) _popen((x), (y)) -#define pclose(x) _pclose(x) -#define mkdir(x, y) _mkdir(x) -#define rmdir(x) _rmdir(x) -#define strdup(x) _strdup(x) -#ifndef __func__ -#define STRX(x) #x -#define STR(x) STRX(x) -#define __func__ __FILE__ ":" STR(__LINE__) -#endif -#define INT64_FMT "I64d" -#define flockfile(x) ((void) (x)) -#define funlockfile(x) ((void) (x)) -typedef struct _stati64 file_stat_t; -typedef HANDLE process_id_t; - -#else ////////////// UNIX specific defines and includes - -#if !defined(MONGOOSE_NO_FILESYSTEM) &&\ - (!defined(MONGOOSE_NO_DAV) || !defined(MONGOOSE_NO_DIRECTORY_LISTING)) -#include -#endif -#if !defined(MONGOOSE_NO_FILESYSTEM) && !defined(MONGOOSE_NO_DL) -#include -#endif -#include -#include -#if !defined(O_BINARY) -#define O_BINARY 0 -#endif -#define INT64_FMT PRId64 -typedef struct stat file_stat_t; -typedef pid_t process_id_t; -#endif //////// End of platform-specific defines and includes - -#include "mongoose.h" - -#define MAX_REQUEST_SIZE 16384 -#define IOBUF_SIZE 8192 -#define MAX_PATH_SIZE 8192 -#define DEFAULT_CGI_PATTERN "**.cgi$|**.pl$|**.php$" -#define CGI_ENVIRONMENT_SIZE 8192 -#define MAX_CGI_ENVIR_VARS 64 -#define ENV_EXPORT_TO_CGI "MONGOOSE_CGI" -#define PASSWORDS_FILE_NAME ".htpasswd" - -#ifndef MONGOOSE_USE_WEBSOCKET_PING_INTERVAL -#define MONGOOSE_USE_WEBSOCKET_PING_INTERVAL 5 -#endif - -// Extra HTTP headers to send in every static file reply -#if !defined(MONGOOSE_USE_EXTRA_HTTP_HEADERS) -#define MONGOOSE_USE_EXTRA_HTTP_HEADERS "" -#endif - -#ifndef MONGOOSE_POST_SIZE_LIMIT -#define MONGOOSE_POST_SIZE_LIMIT 0 -#endif - -#ifndef MONGOOSE_IDLE_TIMEOUT_SECONDS -#define MONGOOSE_IDLE_TIMEOUT_SECONDS 300 -#endif - -#if defined(NS_DISABLE_SOCKETPAIR) && !defined(MONGOOSE_NO_CGI) -#define MONGOOSE_NO_CGI -#endif - -#ifdef MONGOOSE_NO_FILESYSTEM -#define MONGOOSE_NO_AUTH -#if !defined(MONGOOSE_NO_CGI) -#define MONGOOSE_NO_CGI -#endif -#define MONGOOSE_NO_DAV -#define MONGOOSE_NO_DIRECTORY_LISTING -#define MONGOOSE_NO_LOGGING -#define MONGOOSE_NO_SSI -#define MONGOOSE_NO_DL -#endif - -struct vec { - const char *ptr; - size_t len; -}; - -// For directory listing and WevDAV support -struct dir_entry { - struct connection *conn; - char *file_name; - file_stat_t st; -}; - -// NOTE(lsm): this enum shoulds be in sync with the config_options. -enum { - ACCESS_CONTROL_LIST, -#ifndef MONGOOSE_NO_FILESYSTEM - ACCESS_LOG_FILE, -#ifndef MONGOOSE_NO_AUTH - AUTH_DOMAIN, -#endif -#ifndef MONGOOSE_NO_CGI - CGI_INTERPRETER, - CGI_PATTERN, -#endif - DAV_AUTH_FILE, - DAV_ROOT, - DOCUMENT_ROOT, -#ifndef MONGOOSE_NO_DIRECTORY_LISTING - ENABLE_DIRECTORY_LISTING, -#endif -#endif - ENABLE_PROXY, - EXTRA_MIME_TYPES, -#if !defined(MONGOOSE_NO_FILESYSTEM) && !defined(MONGOOSE_NO_AUTH) - GLOBAL_AUTH_FILE, -#endif -#ifndef MONGOOSE_NO_FILESYSTEM - HIDE_FILES_PATTERN, - HEXDUMP_FILE, - INDEX_FILES, -#endif - LISTENING_PORT, -#ifndef _WIN32 - RUN_AS_USER, -#endif -#ifndef MONGOOSE_NO_SSI - SSI_PATTERN, -#endif - URL_REWRITES, - NUM_OPTIONS -}; - -static const char *static_config_options[] = { - "access_control_list", NULL, -#ifndef MONGOOSE_NO_FILESYSTEM - "access_log_file", NULL, -#ifndef MONGOOSE_NO_AUTH - "auth_domain", "mydomain.com", -#endif -#ifndef MONGOOSE_NO_CGI - "cgi_interpreter", NULL, - "cgi_pattern", DEFAULT_CGI_PATTERN, -#endif - "dav_auth_file", NULL, - "dav_root", NULL, - "document_root", NULL, -#ifndef MONGOOSE_NO_DIRECTORY_LISTING - "enable_directory_listing", "yes", -#endif -#endif - "enable_proxy", NULL, - "extra_mime_types", NULL, -#if !defined(MONGOOSE_NO_FILESYSTEM) && !defined(MONGOOSE_NO_AUTH) - "global_auth_file", NULL, -#endif -#ifndef MONGOOSE_NO_FILESYSTEM - "hide_files_patterns", NULL, - "hexdump_file", NULL, - "index_files","index.html,index.htm,index.shtml,index.cgi,index.php", -#endif - "listening_port", NULL, -#ifndef _WIN32 - "run_as_user", NULL, -#endif -#ifndef MONGOOSE_NO_SSI - "ssi_pattern", "**.shtml$|**.shtm$", -#endif - "url_rewrites", NULL, - NULL -}; - -struct mg_server { - struct ns_mgr ns_mgr; - union socket_address lsa; // Listening socket address - mg_handler_t event_handler; - char *config_options[NUM_OPTIONS]; -}; - -// Local endpoint representation -union endpoint { - int fd; // Opened regular local file - struct ns_connection *nc; // CGI or proxy->target connection -}; - -enum endpoint_type { - EP_NONE, EP_FILE, EP_CGI, EP_USER, EP_PUT, EP_CLIENT, EP_PROXY -}; - -#define MG_HEADERS_SENT NSF_USER_1 -#define MG_USING_CHUNKED_API NSF_USER_2 -#define MG_CGI_CONN NSF_USER_3 -#define MG_PROXY_CONN NSF_USER_4 -#define MG_PROXY_DONT_PARSE NSF_USER_5 - -struct connection { - struct ns_connection *ns_conn; // NOTE(lsm): main.c depends on this order - struct mg_connection mg_conn; - struct mg_server *server; - union endpoint endpoint; - enum endpoint_type endpoint_type; - char *path_info; - char *request; - int64_t num_bytes_recv; // Total number of bytes received - int64_t cl; // Reply content length, for Range support - ssize_t request_len; // Request length, including last \r\n after last header -}; - -#define MG_CONN_2_CONN(c) ((struct connection *) ((char *) (c) - \ - offsetof(struct connection, mg_conn))) - -static void open_local_endpoint(struct connection *conn, int skip_user); -static void close_local_endpoint(struct connection *conn); -static void mg_ev_handler(struct ns_connection *nc, int ev, void *p); - -static const struct { - const char *extension; - size_t ext_len; - const char *mime_type; -} static_builtin_mime_types[] = { - {".html", 5, "text/html"}, - {".htm", 4, "text/html"}, - {".shtm", 5, "text/html"}, - {".shtml", 6, "text/html"}, - {".css", 4, "text/css"}, - {".js", 3, "application/javascript"}, - {".ico", 4, "image/x-icon"}, - {".gif", 4, "image/gif"}, - {".jpg", 4, "image/jpeg"}, - {".jpeg", 5, "image/jpeg"}, - {".png", 4, "image/png"}, - {".svg", 4, "image/svg+xml"}, - {".txt", 4, "text/plain"}, - {".torrent", 8, "application/x-bittorrent"}, - {".wav", 4, "audio/x-wav"}, - {".mp3", 4, "audio/x-mp3"}, - {".mid", 4, "audio/mid"}, - {".m3u", 4, "audio/x-mpegurl"}, - {".ogg", 4, "application/ogg"}, - {".ram", 4, "audio/x-pn-realaudio"}, - {".xml", 4, "text/xml"}, - {".json", 5, "application/json"}, - {".xslt", 5, "application/xml"}, - {".xsl", 4, "application/xml"}, - {".ra", 3, "audio/x-pn-realaudio"}, - {".doc", 4, "application/msword"}, - {".exe", 4, "application/octet-stream"}, - {".zip", 4, "application/x-zip-compressed"}, - {".xls", 4, "application/excel"}, - {".tgz", 4, "application/x-tar-gz"}, - {".tar", 4, "application/x-tar"}, - {".gz", 3, "application/x-gunzip"}, - {".arj", 4, "application/x-arj-compressed"}, - {".rar", 4, "application/x-rar-compressed"}, - {".rtf", 4, "application/rtf"}, - {".pdf", 4, "application/pdf"}, - {".swf", 4, "application/x-shockwave-flash"}, - {".mpg", 4, "video/mpeg"}, - {".webm", 5, "video/webm"}, - {".mpeg", 5, "video/mpeg"}, - {".mov", 4, "video/quicktime"}, - {".mp4", 4, "video/mp4"}, - {".m4v", 4, "video/x-m4v"}, - {".asf", 4, "video/x-ms-asf"}, - {".avi", 4, "video/x-msvideo"}, - {".bmp", 4, "image/bmp"}, - {".ttf", 4, "application/x-font-ttf"}, - {NULL, 0, NULL} -}; - -#ifdef MONGOOSE_ENABLE_THREADS -void *mg_start_thread(void *(*f)(void *), void *p) { - return ns_start_thread(f, p); -} -#endif // MONGOOSE_ENABLE_THREADS - -#ifndef MONGOOSE_NO_MMAP -#ifdef _WIN32 -static void *mmap(void *addr, int64_t len, int prot, int flags, int fd, - int offset) { - HANDLE fh = (HANDLE) _get_osfhandle(fd); - HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0); - void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len); - CloseHandle(mh); - return p; -} -#define munmap(x, y) UnmapViewOfFile(x) -#define MAP_FAILED NULL -#define MAP_PRIVATE 0 -#define PROT_READ 0 -#elif defined(__OS2__) -static void *mmap(void *addr, int64_t len, int prot, int flags, int fd, - int offset) { - void *p; - - int pos = lseek( fd, 0, SEEK_CUR ); /* Get a current position */ - - if (pos == -1) - return NULL; - - /* Seek to offset offset */ - if (lseek( fd, offset, SEEK_SET) == -1) - return NULL; - - p = malloc(len); - - /* Read in a file */ - if (!p || read(fd, p, len) == -1) { - free(p); - p = NULL; - } - - /* Restore the position */ - lseek(fd, pos, SEEK_SET); - - return p; -} -#define munmap(x, y) free(x) -#define MAP_FAILED NULL -#define MAP_PRIVATE 0 -#define PROT_READ 0 -#else -#include -#endif - -void *mg_mmap(FILE *fp, size_t size) { - void *p = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fileno(fp), 0); - return p == MAP_FAILED ? NULL : p; -} - -void mg_munmap(void *p, size_t size) { - munmap(p, size); -} -#endif // MONGOOSE_NO_MMAP - -#if defined(_WIN32) && !defined(MONGOOSE_NO_FILESYSTEM) -// Encode 'path' which is assumed UTF-8 string, into UNICODE string. -// wbuf and wbuf_len is a target buffer and its length. -static void to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) { - char buf[MAX_PATH_SIZE * 2], buf2[MAX_PATH_SIZE * 2], *p; - - strncpy(buf, path, sizeof(buf)); - buf[sizeof(buf) - 1] = '\0'; - - // Trim trailing slashes. Leave backslash for paths like "X:\" - p = buf + strlen(buf) - 1; - while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0'; - - // Convert to Unicode and back. If doubly-converted string does not - // match the original, something is fishy, reject. - memset(wbuf, 0, wbuf_len * sizeof(wchar_t)); - MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len); - WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2), - NULL, NULL); - if (strcmp(buf, buf2) != 0) { - wbuf[0] = L'\0'; - } -} - -static int mg_stat(const char *path, file_stat_t *st) { - wchar_t wpath[MAX_PATH_SIZE]; - to_wchar(path, wpath, ARRAY_SIZE(wpath)); - DBG(("[%ls] -> %d", wpath, _wstati64(wpath, st))); - return _wstati64(wpath, st); -} - -static FILE *mg_fopen(const char *path, const char *mode) { - wchar_t wpath[MAX_PATH_SIZE], wmode[10]; - to_wchar(path, wpath, ARRAY_SIZE(wpath)); - to_wchar(mode, wmode, ARRAY_SIZE(wmode)); - return _wfopen(wpath, wmode); -} - -static int mg_open(const char *path, int flag, int mode) { - wchar_t wpath[MAX_PATH_SIZE]; - to_wchar(path, wpath, ARRAY_SIZE(wpath)); - return _wopen(wpath, flag, mode); -} -#endif // _WIN32 && !MONGOOSE_NO_FILESYSTEM - -// A helper function for traversing a comma separated list of values. -// It returns a list pointer shifted to the next value, or NULL if the end -// of the list found. -// Value is stored in val vector. If value has form "x=y", then eq_val -// vector is initialized to point to the "y" part, and val vector length -// is adjusted to point only to "x". -static const char *next_option(const char *list, struct vec *val, - struct vec *eq_val) { - if (list == NULL || *list == '\0') { - // End of the list - list = NULL; - } else { - val->ptr = list; - if ((list = strchr(val->ptr, ',')) != NULL) { - // Comma found. Store length and shift the list ptr - val->len = list - val->ptr; - list++; - } else { - // This value is the last one - list = val->ptr + strlen(val->ptr); - val->len = list - val->ptr; - } - - if (eq_val != NULL) { - // Value has form "x=y", adjust pointers and lengths - // so that val points to "x", and eq_val points to "y". - eq_val->len = 0; - eq_val->ptr = (const char *) memchr(val->ptr, '=', val->len); - if (eq_val->ptr != NULL) { - eq_val->ptr++; // Skip over '=' character - eq_val->len = val->ptr + val->len - eq_val->ptr; - val->len = (eq_val->ptr - val->ptr) - 1; - } - } - } - - return list; -} - -// Like snprintf(), but never returns negative value, or a value -// that is larger than a supplied buffer. -static int mg_vsnprintf(char *buf, size_t buflen, const char *fmt, va_list ap) { - int n; - if (buflen < 1) return 0; - n = vsnprintf(buf, buflen, fmt, ap); - if (n < 0) { - n = 0; - } else if (n >= (int) buflen) { - n = (int) buflen - 1; - } - buf[n] = '\0'; - return n; -} - -static int mg_snprintf(char *buf, size_t buflen, const char *fmt, ...) { - va_list ap; - int n; - va_start(ap, fmt); - n = mg_vsnprintf(buf, buflen, fmt, ap); - va_end(ap); - return n; -} - -// Check whether full request is buffered. Return: -// -1 if request is malformed -// 0 if request is not yet fully buffered -// >0 actual request length, including last \r\n\r\n -static int get_request_len(const char *s, size_t buf_len) { - const unsigned char *buf = (unsigned char *) s; - size_t i; - - for (i = 0; i < buf_len; i++) { - // Control characters are not allowed but >=128 are. - // Abort scan as soon as one malformed character is found. - if (!isprint(buf[i]) && buf[i] != '\r' && buf[i] != '\n' && buf[i] < 128) { - return -1; - } else if (buf[i] == '\n' && i + 1 < buf_len && buf[i + 1] == '\n') { - return i + 2; - } else if (buf[i] == '\n' && i + 2 < buf_len && buf[i + 1] == '\r' && - buf[i + 2] == '\n') { - return i + 3; - } - } - - return 0; -} - -// Skip the characters until one of the delimiters characters found. -// 0-terminate resulting word. Skip the rest of the delimiters if any. -// Advance pointer to buffer to the next word. Return found 0-terminated word. -static char *skip(char **buf, const char *delimiters) { - char *p, *begin_word, *end_word, *end_delimiters; - - begin_word = *buf; - end_word = begin_word + strcspn(begin_word, delimiters); - end_delimiters = end_word + strspn(end_word, delimiters); - - for (p = end_word; p < end_delimiters; p++) { - *p = '\0'; - } - - *buf = end_delimiters; - - return begin_word; -} - -// Parse HTTP headers from the given buffer, advance buffer to the point -// where parsing stopped. -static void parse_http_headers(char **buf, struct mg_connection *ri) { - size_t i; - - for (i = 0; i < ARRAY_SIZE(ri->http_headers); i++) { - ri->http_headers[i].name = skip(buf, ": "); - ri->http_headers[i].value = skip(buf, "\r\n"); - if (ri->http_headers[i].name[0] == '\0') - break; - ri->num_headers = i + 1; - } -} - -static const char *status_code_to_str(int status_code) { - switch (status_code) { - - case 100: return "Continue"; - case 101: return "Switching Protocols"; - case 102: return "Processing"; - - case 200: return "OK"; - case 201: return "Created"; - case 202: return "Accepted"; - case 203: return "Non-Authoritative Information"; - case 204: return "No Content"; - case 205: return "Reset Content"; - case 206: return "Partial Content"; - case 207: return "Multi-Status"; - case 208: return "Already Reported"; - case 226: return "IM Used"; - - case 300: return "Multiple Choices"; - case 301: return "Moved Permanently"; - case 302: return "Found"; - case 303: return "See Other"; - case 304: return "Not Modified"; - case 305: return "Use Proxy"; - case 306: return "Switch Proxy"; - case 307: return "Temporary Redirect"; - case 308: return "Permanent Redirect"; - - case 400: return "Bad Request"; - case 401: return "Unauthorized"; - case 402: return "Payment Required"; - case 403: return "Forbidden"; - case 404: return "Not Found"; - case 405: return "Method Not Allowed"; - case 406: return "Not Acceptable"; - case 407: return "Proxy Authentication Required"; - case 408: return "Request Timeout"; - case 409: return "Conflict"; - case 410: return "Gone"; - case 411: return "Length Required"; - case 412: return "Precondition Failed"; - case 413: return "Payload Too Large"; - case 414: return "URI Too Long"; - case 415: return "Unsupported Media Type"; - case 416: return "Requested Range Not Satisfiable"; - case 417: return "Expectation Failed"; - case 418: return "I\'m a teapot"; - case 422: return "Unprocessable Entity"; - case 423: return "Locked"; - case 424: return "Failed Dependency"; - case 426: return "Upgrade Required"; - case 428: return "Precondition Required"; - case 429: return "Too Many Requests"; - case 431: return "Request Header Fields Too Large"; - case 451: return "Unavailable For Legal Reasons"; - - case 500: return "Internal Server Error"; - case 501: return "Not Implemented"; - case 502: return "Bad Gateway"; - case 503: return "Service Unavailable"; - case 504: return "Gateway Timeout"; - case 505: return "HTTP Version Not Supported"; - case 506: return "Variant Also Negotiates"; - case 507: return "Insufficient Storage"; - case 508: return "Loop Detected"; - case 510: return "Not Extended"; - case 511: return "Network Authentication Required"; - - default: return "Server Error"; - } -} - -static int call_user(struct connection *conn, enum mg_event ev) { - return conn != NULL && conn->server != NULL && - conn->server->event_handler != NULL ? - conn->server->event_handler(&conn->mg_conn, ev) : MG_FALSE; -} - -static void send_http_error(struct connection *conn, int code, - const char *fmt, ...) { - const char *message = status_code_to_str(code); - const char *rewrites = conn->server->config_options[URL_REWRITES]; - char headers[200], body[200]; - struct vec a, b; - va_list ap; - int body_len, headers_len, match_code; - - conn->mg_conn.status_code = code; - - // Invoke error handler if it is set - if (call_user(conn, MG_HTTP_ERROR) == MG_TRUE) { - close_local_endpoint(conn); - return; - } - - // Handle error code rewrites - while ((rewrites = next_option(rewrites, &a, &b)) != NULL) { - if ((match_code = atoi(a.ptr)) > 0 && match_code == code) { - struct mg_connection *c = &conn->mg_conn; - c->status_code = 302; - mg_printf(c, "HTTP/1.1 %d Moved\r\n" - "Location: %.*s?code=%d&orig_uri=%s&query_string=%s\r\n\r\n", - c->status_code, b.len, b.ptr, code, c->uri, - c->query_string == NULL ? "" : c->query_string); - close_local_endpoint(conn); - return; - } - } - - body_len = mg_snprintf(body, sizeof(body), "%d %s\n", code, message); - if (fmt != NULL) { - va_start(ap, fmt); - body_len += mg_vsnprintf(body + body_len, sizeof(body) - body_len, fmt, ap); - va_end(ap); - } - if ((code >= 300 && code <= 399) || code == 204) { - // 3xx errors do not have body - body_len = 0; - } - headers_len = mg_snprintf(headers, sizeof(headers), - "HTTP/1.1 %d %s\r\nContent-Length: %d\r\n" - "Content-Type: text/plain\r\n\r\n", - code, message, body_len); - ns_send(conn->ns_conn, headers, headers_len); - ns_send(conn->ns_conn, body, body_len); - close_local_endpoint(conn); // This will write to the log file -} - -static void write_chunk(struct connection *conn, const char *buf, int len) { - char chunk_size[50]; - int n = mg_snprintf(chunk_size, sizeof(chunk_size), "%X\r\n", len); - ns_send(conn->ns_conn, chunk_size, n); - ns_send(conn->ns_conn, buf, len); - ns_send(conn->ns_conn, "\r\n", 2); -} - -size_t mg_printf(struct mg_connection *conn, const char *fmt, ...) { - struct connection *c = MG_CONN_2_CONN(conn); - va_list ap; - - va_start(ap, fmt); - ns_vprintf(c->ns_conn, fmt, ap); - va_end(ap); - - return c->ns_conn->send_iobuf.len; -} - -static void ns_forward(struct ns_connection *from, struct ns_connection *to) { - DBG(("%p -> %p %lu bytes", from, to, (unsigned long)from->recv_iobuf.len)); - ns_send(to, from->recv_iobuf.buf, from->recv_iobuf.len); - iobuf_remove(&from->recv_iobuf, from->recv_iobuf.len); -} - -#ifndef MONGOOSE_NO_CGI -#ifdef _WIN32 -struct threadparam { - sock_t s; - HANDLE hPipe; -}; - -static int wait_until_ready(sock_t sock, int for_read) { - fd_set set; - FD_ZERO(&set); - FD_SET(sock, &set); - select(sock + 1, for_read ? &set : 0, for_read ? 0 : &set, 0, 0); - return 1; -} - -static void *push_to_stdin(void *arg) { - struct threadparam *tp = (struct threadparam *)arg; - int n, sent, stop = 0; - DWORD k; - char buf[IOBUF_SIZE]; - - while (!stop && wait_until_ready(tp->s, 1) && - (n = recv(tp->s, buf, sizeof(buf), 0)) > 0) { - if (n == -1 && GetLastError() == WSAEWOULDBLOCK) continue; - for (sent = 0; !stop && sent < n; sent += k) { - if (!WriteFile(tp->hPipe, buf + sent, n - sent, &k, 0)) stop = 1; - } - } - DBG(("%s", "FORWARED EVERYTHING TO CGI")); - CloseHandle(tp->hPipe); - NS_FREE(tp); - _endthread(); - return NULL; -} - -static void *pull_from_stdout(void *arg) { - struct threadparam *tp = (struct threadparam *)arg; - int k = 0, stop = 0; - DWORD n, sent; - char buf[IOBUF_SIZE]; - - while (!stop && ReadFile(tp->hPipe, buf, sizeof(buf), &n, NULL)) { - for (sent = 0; !stop && sent < n; sent += k) { - if (wait_until_ready(tp->s, 0) && - (k = send(tp->s, buf + sent, n - sent, 0)) <= 0) stop = 1; - } - } - DBG(("%s", "EOF FROM CGI")); - CloseHandle(tp->hPipe); - shutdown(tp->s, 2); // Without this, IO thread may get truncated data - closesocket(tp->s); - NS_FREE(tp); - _endthread(); - return NULL; -} - -static void spawn_stdio_thread(sock_t sock, HANDLE hPipe, - void *(*func)(void *)) { - struct threadparam *tp = (struct threadparam *)NS_MALLOC(sizeof(*tp)); - if (tp != NULL) { - tp->s = sock; - tp->hPipe = hPipe; - mg_start_thread(func, tp); - } -} - -static void abs_path(const char *utf8_path, char *abs_path, size_t len) { - wchar_t buf[MAX_PATH_SIZE], buf2[MAX_PATH_SIZE]; - to_wchar(utf8_path, buf, ARRAY_SIZE(buf)); - GetFullPathNameW(buf, ARRAY_SIZE(buf2), buf2, NULL); - WideCharToMultiByte(CP_UTF8, 0, buf2, wcslen(buf2) + 1, abs_path, len, 0, 0); -} - -static process_id_t start_process(char *interp, const char *cmd, - const char *env, const char *envp[], - const char *dir, sock_t sock) { - STARTUPINFOW si; - PROCESS_INFORMATION pi; - HANDLE a[2], b[2], me = GetCurrentProcess(); - wchar_t wcmd[MAX_PATH_SIZE], full_dir[MAX_PATH_SIZE]; - char buf[MAX_PATH_SIZE], buf4[MAX_PATH_SIZE], buf5[MAX_PATH_SIZE], - cmdline[MAX_PATH_SIZE], *p; - DWORD flags = DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS; - FILE *fp; - - memset(&si, 0, sizeof(si)); - memset(&pi, 0, sizeof(pi)); - - si.cb = sizeof(si); - si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; - si.wShowWindow = SW_HIDE; - si.hStdError = GetStdHandle(STD_ERROR_HANDLE); - - CreatePipe(&a[0], &a[1], NULL, 0); - CreatePipe(&b[0], &b[1], NULL, 0); - DuplicateHandle(me, a[0], me, &si.hStdInput, 0, TRUE, flags); - DuplicateHandle(me, b[1], me, &si.hStdOutput, 0, TRUE, flags); - - if (interp == NULL && (fp = fopen(cmd, "r")) != NULL) { - buf[0] = buf[1] = '\0'; - fgets(buf, sizeof(buf), fp); - buf[sizeof(buf) - 1] = '\0'; - if (buf[0] == '#' && buf[1] == '!') { - interp = buf + 2; - for (p = interp + strlen(interp) - 1; - isspace(* (uint8_t *) p) && p > interp; p--) *p = '\0'; - } - fclose(fp); - } - - if (interp != NULL) { - abs_path(interp, buf4, ARRAY_SIZE(buf4)); - interp = buf4; - } - abs_path(dir, buf5, ARRAY_SIZE(buf5)); - to_wchar(dir, full_dir, ARRAY_SIZE(full_dir)); - mg_snprintf(cmdline, sizeof(cmdline), "%s%s\"%s\"", - interp ? interp : "", interp ? " " : "", cmd); - to_wchar(cmdline, wcmd, ARRAY_SIZE(wcmd)); - - if (CreateProcessW(NULL, wcmd, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, - (void *) env, full_dir, &si, &pi) != 0) { - spawn_stdio_thread(sock, a[1], push_to_stdin); - spawn_stdio_thread(sock, b[0], pull_from_stdout); - } else { - CloseHandle(a[1]); - CloseHandle(b[0]); - closesocket(sock); - } - DBG(("CGI command: [%ls] -> %p", wcmd, pi.hProcess)); - - // Not closing a[0] and b[1] because we've used DUPLICATE_CLOSE_SOURCE - CloseHandle(si.hStdOutput); - CloseHandle(si.hStdInput); - //CloseHandle(pi.hThread); - //CloseHandle(pi.hProcess); - - return pi.hProcess; -} -#else -static process_id_t start_process(const char *interp, const char *cmd, - const char *env, const char *envp[], - const char *dir, sock_t sock) { - char buf[500]; - process_id_t pid = fork(); - (void) env; - - if (pid == 0) { - (void) chdir(dir); - (void) dup2(sock, 0); - (void) dup2(sock, 1); - closesocket(sock); - - // After exec, all signal handlers are restored to their default values, - // with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's - // implementation, SIGCHLD's handler will leave unchanged after exec - // if it was set to be ignored. Restore it to default action. - signal(SIGCHLD, SIG_DFL); - - if (interp == NULL) { - execle(cmd, cmd, (char *) 0, envp); // Using (char *) 0 to avoid warning - } else { - execle(interp, interp, cmd, (char *) 0, envp); - } - snprintf(buf, sizeof(buf), "Status: 500\r\n\r\n" - "500 Server Error: %s%s%s: %s", interp == NULL ? "" : interp, - interp == NULL ? "" : " ", cmd, strerror(errno)); - send(1, buf, strlen(buf), 0); - exit(EXIT_FAILURE); // exec call failed - } - - return pid; -} -#endif // _WIN32 - -// This structure helps to create an environment for the spawned CGI program. -// Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings, -// last element must be NULL. -// However, on Windows there is a requirement that all these VARIABLE=VALUE\0 -// strings must reside in a contiguous buffer. The end of the buffer is -// marked by two '\0' characters. -// We satisfy both worlds: we create an envp array (which is vars), all -// entries are actually pointers inside buf. -struct cgi_env_block { - struct mg_connection *conn; - char buf[CGI_ENVIRONMENT_SIZE]; // Environment buffer - const char *vars[MAX_CGI_ENVIR_VARS]; // char *envp[] - int len; // Space taken - int nvars; // Number of variables in envp[] -}; - -// Append VARIABLE=VALUE\0 string to the buffer, and add a respective -// pointer into the vars array. -static char *addenv(struct cgi_env_block *block, const char *fmt, ...) { - int n, space; - char *added; - va_list ap; - - // Calculate how much space is left in the buffer - space = sizeof(block->buf) - block->len - 2; - assert(space >= 0); - - // Make a pointer to the free space int the buffer - added = block->buf + block->len; - - // Copy VARIABLE=VALUE\0 string into the free space - va_start(ap, fmt); - n = mg_vsnprintf(added, (size_t) space, fmt, ap); - va_end(ap); - - // Make sure we do not overflow buffer and the envp array - if (n > 0 && n + 1 < space && - block->nvars < (int) ARRAY_SIZE(block->vars) - 2) { - // Append a pointer to the added string into the envp array - block->vars[block->nvars++] = added; - // Bump up used length counter. Include \0 terminator - block->len += n + 1; - } - - return added; -} - -static void addenv2(struct cgi_env_block *blk, const char *name) { - const char *s; - if ((s = getenv(name)) != NULL) addenv(blk, "%s=%s", name, s); -} - -static void prepare_cgi_environment(struct connection *conn, - const char *prog, - struct cgi_env_block *blk) { - struct mg_connection *ri = &conn->mg_conn; - const char *s, *slash; - char *p, **opts = conn->server->config_options; - int i; - - blk->len = blk->nvars = 0; - blk->conn = ri; - - if ((s = getenv("SERVER_NAME")) != NULL) { - addenv(blk, "SERVER_NAME=%s", s); - } else { - addenv(blk, "SERVER_NAME=%s", ri->local_ip); - } - addenv(blk, "SERVER_ROOT=%s", opts[DOCUMENT_ROOT]); - addenv(blk, "DOCUMENT_ROOT=%s", opts[DOCUMENT_ROOT]); - addenv(blk, "SERVER_SOFTWARE=%s/%s", "Mongoose", MONGOOSE_VERSION); - - // Prepare the environment block - addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1"); - addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1"); - addenv(blk, "%s", "REDIRECT_STATUS=200"); // For PHP - - // TODO(lsm): fix this for IPv6 case - //addenv(blk, "SERVER_PORT=%d", ri->remote_port); - - addenv(blk, "REQUEST_METHOD=%s", ri->request_method); - addenv(blk, "REMOTE_ADDR=%s", ri->remote_ip); - addenv(blk, "REMOTE_PORT=%d", ri->remote_port); - addenv(blk, "REQUEST_URI=%s%s%s", ri->uri, - ri->query_string == NULL ? "" : "?", - ri->query_string == NULL ? "" : ri->query_string); - - // SCRIPT_NAME - if (conn->path_info != NULL) { - addenv(blk, "SCRIPT_NAME=%.*s", - (int) (strlen(ri->uri) - strlen(conn->path_info)), ri->uri); - addenv(blk, "PATH_INFO=%s", conn->path_info); - } else { - s = strrchr(prog, '/'); - slash = strrchr(ri->uri, '/'); - addenv(blk, "SCRIPT_NAME=%.*s%s", - slash == NULL ? 0 : (int) (slash - ri->uri), ri->uri, - s == NULL ? prog : s); - } - - addenv(blk, "SCRIPT_FILENAME=%s", prog); - addenv(blk, "PATH_TRANSLATED=%s", prog); - addenv(blk, "HTTPS=%s", conn->ns_conn->ssl != NULL ? "on" : "off"); - - if ((s = mg_get_header(ri, "Content-Type")) != NULL) - addenv(blk, "CONTENT_TYPE=%s", s); - - if (ri->query_string != NULL) - addenv(blk, "QUERY_STRING=%s", ri->query_string); - - if ((s = mg_get_header(ri, "Content-Length")) != NULL) - addenv(blk, "CONTENT_LENGTH=%s", s); - - addenv2(blk, "PATH"); - addenv2(blk, "TMP"); - addenv2(blk, "TEMP"); - addenv2(blk, "TMPDIR"); - addenv2(blk, "PERLLIB"); - addenv2(blk, ENV_EXPORT_TO_CGI); - -#if defined(_WIN32) - addenv2(blk, "COMSPEC"); - addenv2(blk, "SYSTEMROOT"); - addenv2(blk, "SystemDrive"); - addenv2(blk, "ProgramFiles"); - addenv2(blk, "ProgramFiles(x86)"); - addenv2(blk, "CommonProgramFiles(x86)"); -#else - addenv2(blk, "LD_LIBRARY_PATH"); -#endif // _WIN32 - - // Add all headers as HTTP_* variables - for (i = 0; i < ri->num_headers; i++) { - p = addenv(blk, "HTTP_%s=%s", - ri->http_headers[i].name, ri->http_headers[i].value); - - // Convert variable name into uppercase, and change - to _ - for (; *p != '=' && *p != '\0'; p++) { - if (*p == '-') - *p = '_'; - *p = (char) toupper(* (unsigned char *) p); - } - } - - blk->vars[blk->nvars++] = NULL; - blk->buf[blk->len++] = '\0'; - - assert(blk->nvars < (int) ARRAY_SIZE(blk->vars)); - assert(blk->len > 0); - assert(blk->len < (int) sizeof(blk->buf)); -} - -static const char cgi_status[] = "HTTP/1.1 200 OK\r\n"; - -static void open_cgi_endpoint(struct connection *conn, const char *prog) { - struct cgi_env_block blk; - char dir[MAX_PATH_SIZE]; - const char *p; - sock_t fds[2]; - - prepare_cgi_environment(conn, prog, &blk); - // CGI must be executed in its own directory. 'dir' must point to the - // directory containing executable program, 'p' must point to the - // executable program name relative to 'dir'. - if ((p = strrchr(prog, '/')) == NULL) { - mg_snprintf(dir, sizeof(dir), "%s", "."); - } else { - mg_snprintf(dir, sizeof(dir), "%.*s", (int) (p - prog), prog); - } - - // Try to create socketpair in a loop until success. ns_socketpair() - // can be interrupted by a signal and fail. - // TODO(lsm): use sigaction to restart interrupted syscall - do { - ns_socketpair(fds); - } while (fds[0] == INVALID_SOCKET); - - if (start_process(conn->server->config_options[CGI_INTERPRETER], - prog, blk.buf, blk.vars, dir, fds[1]) != 0) { - conn->endpoint_type = EP_CGI; - conn->endpoint.nc = ns_add_sock(&conn->server->ns_mgr, fds[0], - mg_ev_handler, conn); - conn->endpoint.nc->flags |= MG_CGI_CONN; - ns_send(conn->ns_conn, cgi_status, sizeof(cgi_status) - 1); - conn->mg_conn.status_code = 200; - conn->ns_conn->flags |= NSF_BUFFER_BUT_DONT_SEND; - // Pass POST data to the CGI process - conn->endpoint.nc->send_iobuf = conn->ns_conn->recv_iobuf; - iobuf_init(&conn->ns_conn->recv_iobuf, 0); - } else { - closesocket(fds[0]); - send_http_error(conn, 500, "start_process(%s) failed", prog); - } - -#ifndef _WIN32 - closesocket(fds[1]); // On Windows, CGI stdio thread closes that socket -#endif -} - -static void on_cgi_data(struct ns_connection *nc) { - struct connection *conn = (struct connection *) nc->user_data; - const char *status = "500"; - struct mg_connection c; - - if (!conn) return; - - // Copy CGI data from CGI socket to the client send buffer - ns_forward(nc, conn->ns_conn); - - // If reply has not been parsed yet, parse it - if (conn->ns_conn->flags & NSF_BUFFER_BUT_DONT_SEND) { - struct iobuf *io = &conn->ns_conn->send_iobuf; - size_t s_len = sizeof(cgi_status) - 1; - int len = get_request_len(io->buf + s_len, io->len - s_len); - char buf[MAX_REQUEST_SIZE], *s = buf; - - if (len == 0) return; - - if (len < 0 || len > (int) sizeof(buf)) { - len = io->len; - iobuf_remove(io, io->len); - send_http_error(conn, 500, "CGI program sent malformed headers: [%.*s]", - len, io->buf); - } else { - memset(&c, 0, sizeof(c)); - memcpy(buf, io->buf + s_len, len); - buf[len - 1] = '\0'; - parse_http_headers(&s, &c); - if (mg_get_header(&c, "Location") != NULL) { - status = "302"; - } else if ((status = (char *) mg_get_header(&c, "Status")) == NULL) { - status = "200"; - } - memcpy(io->buf + 9, status, 3); - conn->mg_conn.status_code = atoi(status); - } - conn->ns_conn->flags &= ~NSF_BUFFER_BUT_DONT_SEND; - } -} -#endif // !MONGOOSE_NO_CGI - -static char *mg_strdup(const char *str) { - char *copy = (char *) NS_MALLOC(strlen(str) + 1); - if (copy != NULL) { - strcpy(copy, str); - } - return copy; -} - -static int isbyte(int n) { - return n >= 0 && n <= 255; -} - -static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) { - int n, a, b, c, d, slash = 32, len = 0; - - if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 || - sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) && - isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) && - slash >= 0 && slash < 33) { - len = n; - *net = ((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | d; - *mask = slash ? 0xffffffffU << (32 - slash) : 0; - } - - return len; -} - -// Verify given socket address against the ACL. -// Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed. -static int check_acl(const char *acl, uint32_t remote_ip) { - int allowed, flag; - uint32_t net, mask; - struct vec vec; - - // If any ACL is set, deny by default - allowed = acl == NULL ? '+' : '-'; - - while ((acl = next_option(acl, &vec, NULL)) != NULL) { - flag = vec.ptr[0]; - if ((flag != '+' && flag != '-') || - parse_net(&vec.ptr[1], &net, &mask) == 0) { - return -1; - } - - if (net == (remote_ip & mask)) { - allowed = flag; - } - } - - return allowed == '+'; -} - -// Protect against directory disclosure attack by removing '..', -// excessive '/' and '\' characters -static void remove_double_dots_and_double_slashes(char *s) { - char *p = s; - - while (*s != '\0') { - *p++ = *s++; - if (s[-1] == '/' || s[-1] == '\\') { - // Skip all following slashes, backslashes and double-dots - while (s[0] != '\0') { - if (s[0] == '/' || s[0] == '\\') { s++; } - else if (s[0] == '.' && (s[1] == '/' || s[1] == '\\')) { s += 2; } - else if (s[0] == '.' && s[1] == '.' && s[2] == '\0') { s += 2; } - else if (s[0] == '.' && s[1] == '.' && (s[2] == '/' || s[2] == '\\')) { s += 3; } - else { break; } - } - } - } - *p = '\0'; -} - -int mg_url_decode(const char *src, size_t src_len, char *dst, - size_t dst_len, int is_form_url_encoded) { - size_t i, j = 0; - int a, b; -#define HEXTOI(x) (isdigit(x) ? (x) - '0' : (x) - 'W') - - for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) { - if (src[i] == '%' && i + 2 < src_len && - isxdigit(* (const unsigned char *) (src + i + 1)) && - isxdigit(* (const unsigned char *) (src + i + 2))) { - a = tolower(* (const unsigned char *) (src + i + 1)); - b = tolower(* (const unsigned char *) (src + i + 2)); - dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b)); - i += 2; - } else if (is_form_url_encoded && src[i] == '+') { - dst[j] = ' '; - } else { - dst[j] = src[i]; - } - } - - dst[j] = '\0'; // Null-terminate the destination - - return i >= src_len ? j : -1; -} - -static int is_valid_http_method(const char *s) { - return !strcmp(s, "GET") || !strcmp(s, "POST") || !strcmp(s, "HEAD") || - !strcmp(s, "CONNECT") || !strcmp(s, "PUT") || !strcmp(s, "DELETE") || - !strcmp(s, "OPTIONS") || !strcmp(s, "PROPFIND") || !strcmp(s, "MKCOL"); -} - -// Parse HTTP request, fill in mg_request structure. -// This function modifies the buffer by NUL-terminating -// HTTP request components, header names and header values. -// Note that len must point to the last \n of HTTP headers. -static size_t parse_http_message(char *buf, size_t len, - struct mg_connection *ri) { - int is_request, n; - - // Reset the connection. Make sure that we don't touch fields that are - // set elsewhere: remote_ip, remote_port, server_param - ri->request_method = ri->uri = ri->http_version = ri->query_string = NULL; - ri->num_headers = ri->status_code = ri->is_websocket = ri->content_len = 0; - - if (len < 1) return ~0; - - buf[len - 1] = '\0'; - - // RFC says that all initial whitespaces should be ingored - while (*buf != '\0' && isspace(* (unsigned char *) buf)) { - buf++; - } - ri->request_method = skip(&buf, " "); - ri->uri = skip(&buf, " "); - ri->http_version = skip(&buf, "\r\n"); - - // HTTP message could be either HTTP request or HTTP response, e.g. - // "GET / HTTP/1.0 ...." or "HTTP/1.0 200 OK ..." - is_request = is_valid_http_method(ri->request_method); - if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0) || - (!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) { - len = ~0; - } else { - if (is_request) { - ri->http_version += 5; - } else { - ri->status_code = atoi(ri->uri); - } - parse_http_headers(&buf, ri); - - if ((ri->query_string = strchr(ri->uri, '?')) != NULL) { - *(char *) ri->query_string++ = '\0'; - } - n = (int) strlen(ri->uri); - mg_url_decode(ri->uri, n, (char *) ri->uri, n + 1, 0); - if (*ri->uri == '/' || *ri->uri == '.') { - remove_double_dots_and_double_slashes((char *) ri->uri); - } - } - - return len; -} - -static int lowercase(const char *s) { - return tolower(* (const unsigned char *) s); -} - -static int mg_strcasecmp(const char *s1, const char *s2) { - int diff; - - do { - diff = lowercase(s1++) - lowercase(s2++); - } while (diff == 0 && s1[-1] != '\0'); - - return diff; -} - -static int mg_strncasecmp(const char *s1, const char *s2, size_t len) { - int diff = 0; - - if (len > 0) - do { - diff = lowercase(s1++) - lowercase(s2++); - } while (diff == 0 && s1[-1] != '\0' && --len > 0); - - return diff; -} - -// Return HTTP header value, or NULL if not found. -const char *mg_get_header(const struct mg_connection *ri, const char *s) { - int i; - - for (i = 0; i < ri->num_headers; i++) - if (!mg_strcasecmp(s, ri->http_headers[i].name)) - return ri->http_headers[i].value; - - return NULL; -} - -// Perform case-insensitive match of string against pattern -int mg_match_prefix(const char *pattern, ssize_t pattern_len, const char *str) { - const char *or_str; - int len, res, i = 0, j = 0; - - if ((or_str = (const char *) memchr(pattern, '|', pattern_len)) != NULL) { - res = mg_match_prefix(pattern, or_str - pattern, str); - return res > 0 ? res : mg_match_prefix(or_str + 1, - (pattern + pattern_len) - (or_str + 1), str); - } - - for (; i < pattern_len; i++, j++) { - if (pattern[i] == '?' && str[j] != '\0') { - continue; - } else if (pattern[i] == '$') { - return str[j] == '\0' ? j : -1; - } else if (pattern[i] == '*') { - i++; - if (pattern[i] == '*') { - i++; - len = (int) strlen(str + j); - } else { - len = (int) strcspn(str + j, "/"); - } - if (i == pattern_len) { - return j + len; - } - do { - res = mg_match_prefix(pattern + i, pattern_len - i, str + j + len); - } while (res == -1 && len-- > 0); - return res == -1 ? -1 : j + res + len; - } else if (lowercase(&pattern[i]) != lowercase(&str[j])) { - return -1; - } - } - return j; -} - -// This function prints HTML pages, and expands "{{something}}" blocks -// inside HTML by calling appropriate callback functions. -// Note that {{@path/to/file}} construct outputs embedded file's contents, -// which provides SSI-like functionality. -void mg_template(struct mg_connection *conn, const char *s, - struct mg_expansion *expansions) { - int i, j, pos = 0, inside_marker = 0; - - for (i = 0; s[i] != '\0'; i++) { - if (inside_marker == 0 && !memcmp(&s[i], "{{", 2)) { - if (i > pos) { - mg_send_data(conn, &s[pos], i - pos); - } - pos = i; - inside_marker = 1; - } - if (inside_marker == 1 && !memcmp(&s[i], "}}", 2)) { - for (j = 0; expansions[j].keyword != NULL; j++) { - const char *kw = expansions[j].keyword; - if ((int) strlen(kw) == i - (pos + 2) && - memcmp(kw, &s[pos + 2], i - (pos + 2)) == 0) { - expansions[j].handler(conn); - pos = i + 2; - break; - } - } - inside_marker = 0; - } - } - if (i > pos) { - mg_send_data(conn, &s[pos], i - pos); - } -} - -#ifndef MONGOOSE_NO_FILESYSTEM -static int is_dav_request(const struct connection *conn) { - const char *s = conn->mg_conn.request_method; - return !strcmp(s, "PUT") || !strcmp(s, "DELETE") || - !strcmp(s, "MKCOL") || !strcmp(s, "PROPFIND"); -} - -static int must_hide_file(struct connection *conn, const char *path) { - const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$"; - const char *pattern = conn->server->config_options[HIDE_FILES_PATTERN]; - return mg_match_prefix(pw_pattern, strlen(pw_pattern), path) > 0 || - (pattern != NULL && mg_match_prefix(pattern, strlen(pattern), path) > 0); -} - -// Return 1 if real file has been found, 0 otherwise -static int convert_uri_to_file_name(struct connection *conn, char *buf, - size_t buf_len, file_stat_t *st) { - struct vec a, b; - const char *rewrites = conn->server->config_options[URL_REWRITES]; - const char *root = -#ifndef MONGOOSE_NO_DAV - is_dav_request(conn) && conn->server->config_options[DAV_ROOT] != NULL ? - conn->server->config_options[DAV_ROOT] : -#endif - conn->server->config_options[DOCUMENT_ROOT]; -#ifndef MONGOOSE_NO_CGI - const char *cgi_pat = conn->server->config_options[CGI_PATTERN]; - char *p; -#endif - const char *uri = conn->mg_conn.uri; - const char *domain = mg_get_header(&conn->mg_conn, "Host"); - size_t match_len, root_len = root == NULL ? 0 : strlen(root); - - // Perform virtual hosting rewrites - if (rewrites != NULL && domain != NULL) { - const char *colon = strchr(domain, ':'); - size_t domain_len = colon == NULL ? strlen(domain) : colon - domain; - - while ((rewrites = next_option(rewrites, &a, &b)) != NULL) { - if (a.len > 1 && a.ptr[0] == '@' && a.len == domain_len + 1 && - mg_strncasecmp(a.ptr + 1, domain, domain_len) == 0) { - root = b.ptr; - root_len = b.len; - break; - } - } - } - - // No filesystem access - if (root == NULL || root_len == 0) return 0; - - // Handle URL rewrites - mg_snprintf(buf, buf_len, "%.*s%s", root_len, root, uri); - rewrites = conn->server->config_options[URL_REWRITES]; // Re-initialize! - while ((rewrites = next_option(rewrites, &a, &b)) != NULL) { - if ((match_len = mg_match_prefix(a.ptr, a.len, uri)) > 0) { - mg_snprintf(buf, buf_len, "%.*s%s", (int) b.len, b.ptr, uri + match_len); - break; - } - } - - if (stat(buf, st) == 0) return 1; - -#ifndef MONGOOSE_NO_CGI - // Support PATH_INFO for CGI scripts. - for (p = buf + strlen(root) + 2; *p != '\0'; p++) { - if (*p == '/') { - *p = '\0'; - if (mg_match_prefix(cgi_pat, strlen(cgi_pat), buf) > 0 && - !stat(buf, st)) { - DBG(("!!!! [%s]", buf)); - *p = '/'; - conn->path_info = mg_strdup(p); - *p = '\0'; - return 1; - } - *p = '/'; - } - } -#endif - - return 0; -} -#endif // MONGOOSE_NO_FILESYSTEM - -static int should_keep_alive(const struct mg_connection *conn) { - struct connection *c = MG_CONN_2_CONN(conn); - const char *method = conn->request_method; - const char *http_version = conn->http_version; - const char *header = mg_get_header(conn, "Connection"); - return method != NULL && - (!strcmp(method, "GET") || c->endpoint_type == EP_USER) && - ((header != NULL && !mg_strcasecmp(header, "keep-alive")) || - (header == NULL && http_version && !strcmp(http_version, "1.1"))); -} - -size_t mg_write(struct mg_connection *c, const void *buf, size_t len) { - struct connection *conn = MG_CONN_2_CONN(c); - ns_send(conn->ns_conn, buf, len); - return conn->ns_conn->send_iobuf.len; -} - -void mg_send_status(struct mg_connection *c, int status) { - struct connection *conn = MG_CONN_2_CONN(c); - if (c->status_code == 0) { - c->status_code = status; - mg_printf(c, "HTTP/1.1 %d %s\r\n", status, status_code_to_str(status)); - } - conn->ns_conn->flags |= MG_USING_CHUNKED_API; -} - -void mg_send_header(struct mg_connection *c, const char *name, const char *v) { - struct connection *conn = MG_CONN_2_CONN(c); - if (c->status_code == 0) { - c->status_code = 200; - mg_printf(c, "HTTP/1.1 %d %s\r\n", 200, status_code_to_str(200)); - } - mg_printf(c, "%s: %s\r\n", name, v); - conn->ns_conn->flags |= MG_USING_CHUNKED_API; -} - -static void terminate_headers(struct mg_connection *c) { - struct connection *conn = MG_CONN_2_CONN(c); - if (!(conn->ns_conn->flags & MG_HEADERS_SENT)) { - mg_send_header(c, "Transfer-Encoding", "chunked"); - mg_write(c, "\r\n", 2); - conn->ns_conn->flags |= MG_HEADERS_SENT; - } -} - -size_t mg_send_data(struct mg_connection *c, const void *data, int data_len) { - struct connection *conn = MG_CONN_2_CONN(c); - terminate_headers(c); - write_chunk(MG_CONN_2_CONN(c), (const char *) data, data_len); - return conn->ns_conn->send_iobuf.len; -} - -size_t mg_printf_data(struct mg_connection *c, const char *fmt, ...) { - struct connection *conn = MG_CONN_2_CONN(c); - va_list ap; - int len; - char mem[IOBUF_SIZE], *buf = mem; - - terminate_headers(c); - - va_start(ap, fmt); - len = ns_avprintf(&buf, sizeof(mem), fmt, ap); - va_end(ap); - - if (len >= 0) { - write_chunk((struct connection *) conn, buf, len); - } - if (buf != mem && buf != NULL) { - NS_FREE(buf); - } - return conn->ns_conn->send_iobuf.len; -} - -#if !defined(MONGOOSE_NO_WEBSOCKET) || !defined(MONGOOSE_NO_AUTH) -static int is_big_endian(void) { - static const int n = 1; - return ((char *) &n)[0] == 0; -} -#endif - -#ifndef MONGOOSE_NO_WEBSOCKET -// START OF SHA-1 code -// Copyright(c) By Steve Reid -#define SHA1HANDSOFF -#if defined(__sun) -#include "solarisfixes.h" -#endif - -union char64long16 { unsigned char c[64]; uint32_t l[16]; }; - -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - -static uint32_t blk0(union char64long16 *block, int i) { - // Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN - if (!is_big_endian()) { - block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | - (rol(block->l[i], 8) & 0x00FF00FF); - } - return block->l[i]; -} - -/* Avoid redefine warning (ARM /usr/include/sys/ucontext.h define R0~R4) */ -#undef blk -#undef R0 -#undef R1 -#undef R2 -#undef R3 -#undef R4 - -#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ - ^block->l[(i+2)&15]^block->l[i&15],1)) -#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(block, i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); -#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); -#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); - -typedef struct { - uint32_t state[5]; - uint32_t count[2]; - unsigned char buffer[64]; -} SHA1_CTX; - -static void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) { - uint32_t a, b, c, d, e; - union char64long16 block[1]; - - memcpy(block, buffer, 64); - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); - R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); - R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); - R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); - R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); - R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); - R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); - R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); - R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); - R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); - R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); - R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); - R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); - R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); - R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); - R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); - R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); - R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); - R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); - R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - // Erase working structures. The order of operations is important, - // used to ensure that compiler doesn't optimize those out. - memset(block, 0, sizeof(block)); - a = b = c = d = e = 0; - (void) a; (void) b; (void) c; (void) d; (void) e; -} - -static void SHA1Init(SHA1_CTX *context) { - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; -} - -static void SHA1Update(SHA1_CTX *context, const unsigned char *data, - size_t len) { - size_t i, j; - - j = context->count[0]; - if ((context->count[0] += len << 3) < j) - context->count[1]++; - context->count[1] += (len>>29); - j = (j >> 3) & 63; - if ((j + len) > 63) { - memcpy(&context->buffer[j], data, (i = 64-j)); - SHA1Transform(context->state, context->buffer); - for ( ; i + 63 < len; i += 64) { - SHA1Transform(context->state, &data[i]); - } - j = 0; - } - else i = 0; - memcpy(&context->buffer[j], &data[i], len - i); -} - -static void SHA1Final(unsigned char digest[20], SHA1_CTX *context) { - unsigned i; - unsigned char finalcount[8], c; - - for (i = 0; i < 8; i++) { - finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] - >> ((3-(i & 3)) * 8) ) & 255); - } - c = 0200; - SHA1Update(context, &c, 1); - while ((context->count[0] & 504) != 448) { - c = 0000; - SHA1Update(context, &c, 1); - } - SHA1Update(context, finalcount, 8); - for (i = 0; i < 20; i++) { - digest[i] = (unsigned char) - ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); - } - memset(context, '\0', sizeof(*context)); - memset(&finalcount, '\0', sizeof(finalcount)); -} -// END OF SHA1 CODE - -static void base64_encode(const unsigned char *src, int src_len, char *dst) { - static const char *b64 = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - int i, j, a, b, c; - - for (i = j = 0; i < src_len; i += 3) { - a = src[i]; - b = i + 1 >= src_len ? 0 : src[i + 1]; - c = i + 2 >= src_len ? 0 : src[i + 2]; - - dst[j++] = b64[a >> 2]; - dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; - if (i + 1 < src_len) { - dst[j++] = b64[(b & 15) << 2 | (c >> 6)]; - } - if (i + 2 < src_len) { - dst[j++] = b64[c & 63]; - } - } - while (j % 4 != 0) { - dst[j++] = '='; - } - dst[j++] = '\0'; -} - -static void send_websocket_handshake(struct mg_connection *conn, - const char *key) { - static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - char buf[500], sha[20], b64_sha[sizeof(sha) * 2]; - SHA1_CTX sha_ctx; - - mg_snprintf(buf, sizeof(buf), "%s%s", key, magic); - SHA1Init(&sha_ctx); - SHA1Update(&sha_ctx, (unsigned char *) buf, strlen(buf)); - SHA1Final((unsigned char *) sha, &sha_ctx); - base64_encode((unsigned char *) sha, sizeof(sha), b64_sha); - mg_snprintf(buf, sizeof(buf), "%s%s%s", - "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n"); - - mg_write(conn, buf, strlen(buf)); -} - -static size_t deliver_websocket_frame(struct connection *conn) { - // Having buf unsigned char * is important, as it is used below in arithmetic - unsigned char *buf = (unsigned char *) conn->ns_conn->recv_iobuf.buf; - size_t i, len, buf_len = conn->ns_conn->recv_iobuf.len, frame_len = 0, - mask_len = 0, header_len = 0, data_len = 0, buffered = 0; - - if (buf_len >= 2) { - len = buf[1] & 127; - mask_len = buf[1] & 128 ? 4 : 0; - if (len < 126 && buf_len >= mask_len) { - data_len = len; - header_len = 2 + mask_len; - } else if (len == 126 && buf_len >= 4 + mask_len) { - header_len = 4 + mask_len; - data_len = ((((size_t) buf[2]) << 8) + buf[3]); - } else if (buf_len >= 10 + mask_len) { - header_len = 10 + mask_len; - data_len = (size_t) (((uint64_t) htonl(* (uint32_t *) &buf[2])) << 32) + - htonl(* (uint32_t *) &buf[6]); - } - } - - frame_len = header_len + data_len; - buffered = frame_len > 0 && frame_len <= buf_len; - - if (buffered) { - conn->mg_conn.content_len = data_len; - conn->mg_conn.content = (char *) buf + header_len; - conn->mg_conn.wsbits = buf[0]; - - // Apply mask if necessary - if (mask_len > 0) { - for (i = 0; i < data_len; i++) { - buf[i + header_len] ^= (buf + header_len - mask_len)[i % 4]; - } - } - - // Call the handler and remove frame from the iobuf - if (call_user(conn, MG_REQUEST) == MG_FALSE) { - conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA; - } - iobuf_remove(&conn->ns_conn->recv_iobuf, frame_len); - } - - return buffered; -} - -size_t mg_websocket_write(struct mg_connection *conn, int opcode, - const char *data, size_t data_len) { - unsigned char mem[4192], *copy = mem; - size_t copy_len = 0; - - /* Check overflow */ - if (data_len > ~(size_t)0 - (size_t)10) { - return 0; - } - - if (data_len + 10 > sizeof(mem) && - (copy = (unsigned char *) NS_MALLOC(data_len + 10)) == NULL) { - return 0; - } - - copy[0] = 0x80 + (opcode & 0x0f); - - // Frame format: http://tools.ietf.org/html/rfc6455#section-5.2 - if (data_len < 126) { - // Inline 7-bit length field - copy[1] = data_len; - memcpy(copy + 2, data, data_len); - copy_len = 2 + data_len; - } else if (data_len <= 0xFFFF) { - // 16-bit length field - copy[1] = 126; - * (uint16_t *) (copy + 2) = (uint16_t) htons((uint16_t) data_len); - memcpy(copy + 4, data, data_len); - copy_len = 4 + data_len; - } else { - // 64-bit length field - const uint32_t hi = htonl((uint32_t) ((uint64_t) data_len >> 32)); - const uint32_t lo = htonl(data_len & 0xffffffff); - copy[1] = 127; - memcpy(copy+2,&hi,sizeof(hi)); - memcpy(copy+6,&lo,sizeof(lo)); - memcpy(copy + 10, data, data_len); - copy_len = 10 + data_len; - } - - if (copy_len > 0) { - mg_write(conn, copy, copy_len); - } - if (copy != mem) { - NS_FREE(copy); - } - - // If we send closing frame, schedule a connection to be closed after - // data is drained to the client. - if (opcode == WEBSOCKET_OPCODE_CONNECTION_CLOSE) { - MG_CONN_2_CONN(conn)->ns_conn->flags |= NSF_FINISHED_SENDING_DATA; - } - - return MG_CONN_2_CONN(conn)->ns_conn->send_iobuf.len; -} - -size_t mg_websocket_printf(struct mg_connection *conn, int opcode, - const char *fmt, ...) { - char mem[4192], *buf = mem; - va_list ap; - int len; - - va_start(ap, fmt); - if ((len = ns_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) { - mg_websocket_write(conn, opcode, buf, len); - } - va_end(ap); - - if (buf != mem && buf != NULL) { - NS_FREE(buf); - } - - return MG_CONN_2_CONN(conn)->ns_conn->send_iobuf.len; -} - -static void send_websocket_handshake_if_requested(struct mg_connection *conn) { - const char *ver = mg_get_header(conn, "Sec-WebSocket-Version"), - *key = mg_get_header(conn, "Sec-WebSocket-Key"); - if (ver != NULL && key != NULL) { - conn->is_websocket = 1; - if (call_user(MG_CONN_2_CONN(conn), MG_WS_HANDSHAKE) == MG_FALSE) { - send_websocket_handshake(conn, key); - } - call_user(MG_CONN_2_CONN(conn), MG_WS_CONNECT); - } -} - -static void ping_idle_websocket_connection(struct connection *conn, time_t t) { - if (t - conn->ns_conn->last_io_time > MONGOOSE_USE_WEBSOCKET_PING_INTERVAL) { - mg_websocket_write(&conn->mg_conn, WEBSOCKET_OPCODE_PING, "", 0); - } -} -#else -#define ping_idle_websocket_connection(conn, t) -#endif // !MONGOOSE_NO_WEBSOCKET - -static void write_terminating_chunk(struct connection *conn) { - mg_write(&conn->mg_conn, "0\r\n\r\n", 5); -} - -static int call_request_handler(struct connection *conn) { - int result; - conn->mg_conn.content = conn->ns_conn->recv_iobuf.buf; - if ((result = call_user(conn, MG_REQUEST)) == MG_TRUE) { - if (conn->ns_conn->flags & MG_USING_CHUNKED_API) { - terminate_headers(&conn->mg_conn); - write_terminating_chunk(conn); - } - close_local_endpoint(conn); - } - return result; -} - -const char *mg_get_mime_type(const char *path, const char *default_mime_type) { - const char *ext; - size_t i, path_len; - - path_len = strlen(path); - - for (i = 0; static_builtin_mime_types[i].extension != NULL; i++) { - ext = path + (path_len - static_builtin_mime_types[i].ext_len); - if (path_len > static_builtin_mime_types[i].ext_len && - mg_strcasecmp(ext, static_builtin_mime_types[i].extension) == 0) { - return static_builtin_mime_types[i].mime_type; - } - } - - return default_mime_type; -} - -#ifndef MONGOOSE_NO_FILESYSTEM -// Convert month to the month number. Return -1 on error, or month number -static int get_month_index(const char *s) { - static const char *month_names[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; - int i; - - for (i = 0; i < (int) ARRAY_SIZE(month_names); i++) - if (!strcmp(s, month_names[i])) - return i; - - return -1; -} - -static int num_leap_years(int year) { - return year / 4 - year / 100 + year / 400; -} - -// Parse UTC date-time string, and return the corresponding time_t value. -static time_t parse_date_string(const char *datetime) { - static const unsigned short days_before_month[] = { - 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 - }; - char month_str[32]; - int second, minute, hour, day, month, year, leap_days, days; - time_t result = (time_t) 0; - - if (((sscanf(datetime, "%d/%3s/%d %d:%d:%d", - &day, month_str, &year, &hour, &minute, &second) == 6) || - (sscanf(datetime, "%d %3s %d %d:%d:%d", - &day, month_str, &year, &hour, &minute, &second) == 6) || - (sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d", - &day, month_str, &year, &hour, &minute, &second) == 6) || - (sscanf(datetime, "%d-%3s-%d %d:%d:%d", - &day, month_str, &year, &hour, &minute, &second) == 6)) && - year > 1970 && - (month = get_month_index(month_str)) != -1) { - leap_days = num_leap_years(year) - num_leap_years(1970); - year -= 1970; - days = year * 365 + days_before_month[month] + (day - 1) + leap_days; - result = days * 24 * 3600 + hour * 3600 + minute * 60 + second; - } - - return result; -} - -// Look at the "path" extension and figure what mime type it has. -// Store mime type in the vector. -static void get_mime_type(const struct mg_server *server, const char *path, - struct vec *vec) { - struct vec ext_vec, mime_vec; - const char *list, *ext; - size_t path_len; - - path_len = strlen(path); - - // Scan user-defined mime types first, in case user wants to - // override default mime types. - list = server->config_options[EXTRA_MIME_TYPES]; - while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) { - // ext now points to the path suffix - ext = path + path_len - ext_vec.len; - if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) { - *vec = mime_vec; - return; - } - } - - vec->ptr = mg_get_mime_type(path, "text/plain"); - vec->len = strlen(vec->ptr); -} - -static const char *suggest_connection_header(const struct mg_connection *conn) { - return should_keep_alive(conn) ? "keep-alive" : "close"; -} - -static void construct_etag(char *buf, size_t buf_len, const file_stat_t *st) { - mg_snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"", - (unsigned long) st->st_mtime, (int64_t) st->st_size); -} - -// Return True if we should reply 304 Not Modified. -static int is_not_modified(const struct connection *conn, - const file_stat_t *stp) { - char etag[64]; - const char *ims = mg_get_header(&conn->mg_conn, "If-Modified-Since"); - const char *inm = mg_get_header(&conn->mg_conn, "If-None-Match"); - construct_etag(etag, sizeof(etag), stp); - return (inm != NULL && !mg_strcasecmp(etag, inm)) || - (ims != NULL && stp->st_mtime <= parse_date_string(ims)); -} - -// For given directory path, substitute it to valid index file. -// Return 0 if index file has been found, -1 if not found. -// If the file is found, it's stats is returned in stp. -static int find_index_file(struct connection *conn, char *path, - size_t path_len, file_stat_t *stp) { - const char *list = conn->server->config_options[INDEX_FILES]; - file_stat_t st; - struct vec filename_vec; - size_t n = strlen(path); - int found = 0; - - // The 'path' given to us points to the directory. Remove all trailing - // directory separator characters from the end of the path, and - // then append single directory separator character. - while (n > 0 && path[n - 1] == '/') { - n--; - } - path[n] = '/'; - - // Traverse index files list. For each entry, append it to the given - // path and see if the file exists. If it exists, break the loop - while ((list = next_option(list, &filename_vec, NULL)) != NULL) { - - if (path_len <= n + 2) { - continue; - } - - // Ignore too long entries that may overflow path buffer - if (filename_vec.len > (path_len - (n + 2))) - continue; - - // Prepare full path to the index file - strncpy(path + n + 1, filename_vec.ptr, filename_vec.len); - path[n + 1 + filename_vec.len] = '\0'; - - //DBG(("[%s]", path)); - - // Does it exist? - if (!stat(path, &st)) { - // Yes it does, break the loop - *stp = st; - found = 1; - break; - } - } - - // If no index file exists, restore directory path - if (!found) { - path[n] = '\0'; - } - - return found; -} - -static int parse_range_header(const char *header, int64_t *a, int64_t *b) { - return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b); -} - -static void gmt_time_string(char *buf, size_t buf_len, time_t *t) { - strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t)); -} - -static void open_file_endpoint(struct connection *conn, const char *path, - file_stat_t *st, const char *extra_headers) { - char date[64], lm[64], etag[64], range[64], headers[1000]; - const char *msg = "OK", *hdr; - time_t curtime = time(NULL); - int64_t r1, r2; - struct vec mime_vec; - int n; - - conn->endpoint_type = EP_FILE; - ns_set_close_on_exec(conn->endpoint.fd); - conn->mg_conn.status_code = 200; - - get_mime_type(conn->server, path, &mime_vec); - conn->cl = st->st_size; - range[0] = '\0'; - - // If Range: header specified, act accordingly - r1 = r2 = 0; - hdr = mg_get_header(&conn->mg_conn, "Range"); - if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 && - r1 >= 0 && r2 >= 0) { - conn->mg_conn.status_code = 206; - conn->cl = n == 2 ? (r2 > conn->cl ? conn->cl : r2) - r1 + 1: conn->cl - r1; - mg_snprintf(range, sizeof(range), "Content-Range: bytes " - "%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT "\r\n", - r1, r1 + conn->cl - 1, (int64_t) st->st_size); - msg = "Partial Content"; - lseek(conn->endpoint.fd, r1, SEEK_SET); - } - - // Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to - // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 - gmt_time_string(date, sizeof(date), &curtime); - gmt_time_string(lm, sizeof(lm), &st->st_mtime); - construct_etag(etag, sizeof(etag), st); - - n = mg_snprintf(headers, sizeof(headers), - "HTTP/1.1 %d %s\r\n" - "Date: %s\r\n" - "Last-Modified: %s\r\n" - "Etag: %s\r\n" - "Content-Type: %.*s\r\n" - "Content-Length: %" INT64_FMT "\r\n" - "Connection: %s\r\n" - "Accept-Ranges: bytes\r\n" - "%s%s%s\r\n", - conn->mg_conn.status_code, msg, date, lm, etag, - (int) mime_vec.len, mime_vec.ptr, conn->cl, - suggest_connection_header(&conn->mg_conn), - range, extra_headers == NULL ? "" : extra_headers, - MONGOOSE_USE_EXTRA_HTTP_HEADERS); - ns_send(conn->ns_conn, headers, n); - - if (!strcmp(conn->mg_conn.request_method, "HEAD")) { - conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA; - close(conn->endpoint.fd); - conn->endpoint_type = EP_NONE; - } -} - -void mg_send_file_data(struct mg_connection *c, int fd) { - struct connection *conn = MG_CONN_2_CONN(c); - conn->endpoint_type = EP_FILE; - conn->endpoint.fd = fd; - ns_set_close_on_exec(conn->endpoint.fd); -} -#endif // MONGOOSE_NO_FILESYSTEM - -static void call_request_handler_if_data_is_buffered(struct connection *conn) { -#ifndef MONGOOSE_NO_WEBSOCKET - if (conn->mg_conn.is_websocket) { - do { } while (deliver_websocket_frame(conn)); - } else -#endif - if (conn->num_bytes_recv >= (conn->cl + conn->request_len) && - call_request_handler(conn) == MG_FALSE) { - open_local_endpoint(conn, 1); - } -} - -#if !defined(MONGOOSE_NO_DIRECTORY_LISTING) || !defined(MONGOOSE_NO_DAV) - -#ifdef _WIN32 -struct dirent { - char d_name[MAX_PATH_SIZE]; -}; - -typedef struct DIR { - HANDLE handle; - WIN32_FIND_DATAW info; - struct dirent result; -} DIR; - -// Implementation of POSIX opendir/closedir/readdir for Windows. -static DIR *opendir(const char *name) { - DIR *dir = NULL; - wchar_t wpath[MAX_PATH_SIZE]; - DWORD attrs; - - if (name == NULL) { - SetLastError(ERROR_BAD_ARGUMENTS); - } else if ((dir = (DIR *) NS_MALLOC(sizeof(*dir))) == NULL) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - } else { - to_wchar(name, wpath, ARRAY_SIZE(wpath)); - attrs = GetFileAttributesW(wpath); - if (attrs != 0xFFFFFFFF && - ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) { - (void) wcscat(wpath, L"\\*"); - dir->handle = FindFirstFileW(wpath, &dir->info); - dir->result.d_name[0] = '\0'; - } else { - NS_FREE(dir); - dir = NULL; - } - } - - return dir; -} - -static int closedir(DIR *dir) { - int result = 0; - - if (dir != NULL) { - if (dir->handle != INVALID_HANDLE_VALUE) - result = FindClose(dir->handle) ? 0 : -1; - - NS_FREE(dir); - } else { - result = -1; - SetLastError(ERROR_BAD_ARGUMENTS); - } - - return result; -} - -static struct dirent *readdir(DIR *dir) { - struct dirent *result = 0; - - if (dir) { - if (dir->handle != INVALID_HANDLE_VALUE) { - result = &dir->result; - (void) WideCharToMultiByte(CP_UTF8, 0, - dir->info.cFileName, -1, result->d_name, - sizeof(result->d_name), NULL, NULL); - - if (!FindNextFileW(dir->handle, &dir->info)) { - (void) FindClose(dir->handle); - dir->handle = INVALID_HANDLE_VALUE; - } - - } else { - SetLastError(ERROR_FILE_NOT_FOUND); - } - } else { - SetLastError(ERROR_BAD_ARGUMENTS); - } - - return result; -} -#endif // _WIN32 POSIX opendir/closedir/readdir implementation - -static int scan_directory(struct connection *conn, const char *dir, - struct dir_entry **arr) { - char path[MAX_PATH_SIZE]; - struct dir_entry *p; - struct dirent *dp; - int arr_size = 0, arr_ind = 0, inc = 100; - DIR *dirp; - - *arr = NULL; - if ((dirp = (opendir(dir))) == NULL) return 0; - - while ((dp = readdir(dirp)) != NULL) { - // Do not show current dir and hidden files - if (!strcmp(dp->d_name, ".") || - !strcmp(dp->d_name, "..") || - must_hide_file(conn, dp->d_name)) { - continue; - } - mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name); - - // Resize the array if nesessary - if (arr_ind >= arr_size) { - if ((p = (struct dir_entry *) - NS_REALLOC(*arr, (inc + arr_size) * sizeof(**arr))) != NULL) { - // Memset new chunk to zero, otherwize st_mtime will have garbage which - // can make strftime() segfault, see - // http://code.google.com/p/mongoose/issues/detail?id=79 - memset(p + arr_size, 0, sizeof(**arr) * inc); - - *arr = p; - arr_size += inc; - } - } - - if (arr_ind < arr_size) { - (*arr)[arr_ind].conn = conn; - (*arr)[arr_ind].file_name = strdup(dp->d_name); - stat(path, &(*arr)[arr_ind].st); - arr_ind++; - } - } - closedir(dirp); - - return arr_ind; -} - -size_t mg_url_encode(const char *src, size_t s_len, char *dst, size_t dst_len) { - static const char *dont_escape = "._-$,;~()"; - static const char *hex = "0123456789abcdef"; - size_t i = 0, j = 0; - - for (i = j = 0; dst_len > 0 && i < s_len && j + 2 < dst_len - 1; i++, j++) { - if (isalnum(* (const unsigned char *) (src + i)) || - strchr(dont_escape, * (const unsigned char *) (src + i)) != NULL) { - dst[j] = src[i]; - } else if (j + 3 < dst_len) { - dst[j] = '%'; - dst[j + 1] = hex[(* (const unsigned char *) (src + i)) >> 4]; - dst[j + 2] = hex[(* (const unsigned char *) (src + i)) & 0xf]; - j += 2; - } - } - - dst[j] = '\0'; - return j; -} -#endif // !NO_DIRECTORY_LISTING || !MONGOOSE_NO_DAV - -#ifndef MONGOOSE_NO_DIRECTORY_LISTING - -static void print_dir_entry(const struct dir_entry *de) { - char size[64], mod[64], href[MAX_PATH_SIZE * 3]; - int64_t fsize = de->st.st_size; - int is_dir = S_ISDIR(de->st.st_mode); - const char *slash = is_dir ? "/" : ""; - - if (is_dir) { - mg_snprintf(size, sizeof(size), "%s", "[DIRECTORY]"); - } else { - // We use (signed) cast below because MSVC 6 compiler cannot - // convert unsigned __int64 to double. - if (fsize < 1024) { - mg_snprintf(size, sizeof(size), "%d", (int) fsize); - } else if (fsize < 0x100000) { - mg_snprintf(size, sizeof(size), "%.1fk", (double) fsize / 1024.0); - } else if (fsize < 0x40000000) { - mg_snprintf(size, sizeof(size), "%.1fM", (double) fsize / 1048576); - } else { - mg_snprintf(size, sizeof(size), "%.1fG", (double) fsize / 1073741824); - } - } - strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&de->st.st_mtime)); - mg_url_encode(de->file_name, strlen(de->file_name), href, sizeof(href)); - mg_printf_data(&de->conn->mg_conn, - "%s%s" - " %s  %s\n", - href, slash, de->file_name, slash, mod, size); -} - -// Sort directory entries by size, or name, or modification time. -// On windows, __cdecl specification is needed in case if project is built -// with __stdcall convention. qsort always requires __cdels callback. -static int __cdecl compare_dir_entries(const void *p1, const void *p2) { - const struct dir_entry *a = (const struct dir_entry *) p1, - *b = (const struct dir_entry *) p2; - const char *qs = a->conn->mg_conn.query_string ? - a->conn->mg_conn.query_string : "na"; - int cmp_result = 0; - - if (S_ISDIR(a->st.st_mode) && !S_ISDIR(b->st.st_mode)) { - return -1; // Always put directories on top - } else if (!S_ISDIR(a->st.st_mode) && S_ISDIR(b->st.st_mode)) { - return 1; // Always put directories on top - } else if (*qs == 'n') { - cmp_result = strcmp(a->file_name, b->file_name); - } else if (*qs == 's') { - cmp_result = a->st.st_size == b->st.st_size ? 0 : - a->st.st_size > b->st.st_size ? 1 : -1; - } else if (*qs == 'd') { - cmp_result = a->st.st_mtime == b->st.st_mtime ? 0 : - a->st.st_mtime > b->st.st_mtime ? 1 : -1; - } - - return qs[1] == 'd' ? -cmp_result : cmp_result; -} - -static void send_directory_listing(struct connection *conn, const char *dir) { - struct dir_entry *arr = NULL; - int i, num_entries, sort_direction = conn->mg_conn.query_string != NULL && - conn->mg_conn.query_string[1] == 'd' ? 'a' : 'd'; - - mg_send_header(&conn->mg_conn, "Transfer-Encoding", "chunked"); - mg_send_header(&conn->mg_conn, "Content-Type", "text/html; charset=utf-8"); - - mg_printf_data(&conn->mg_conn, - "Index of %s" - "" - "

Index of %s

"
-              ""
-              ""
-              ""
-              "",
-              conn->mg_conn.uri, conn->mg_conn.uri,
-              sort_direction, sort_direction, sort_direction);
-
-  num_entries = scan_directory(conn, dir, &arr);
-  qsort(arr, num_entries, sizeof(arr[0]), compare_dir_entries);
-  for (i = 0; i < num_entries; i++) {
-    print_dir_entry(&arr[i]);
-    NS_FREE(arr[i].file_name);
-  }
-  NS_FREE(arr);
-
-  write_terminating_chunk(conn);
-  close_local_endpoint(conn);
-}
-#endif  // MONGOOSE_NO_DIRECTORY_LISTING
-
-#ifndef MONGOOSE_NO_DAV
-static void print_props(struct connection *conn, const char *uri,
-                        file_stat_t *stp) {
-  char mtime[64];
-
-  gmt_time_string(mtime, sizeof(mtime), &stp->st_mtime);
-  mg_printf(&conn->mg_conn,
-      ""
-       "%s"
-       ""
-        ""
-         "%s"
-         "%" INT64_FMT ""
-         "%s"
-        ""
-        "HTTP/1.1 200 OK"
-       ""
-      "\n",
-      uri, S_ISDIR(stp->st_mode) ? "" : "",
-      (int64_t) stp->st_size, mtime);
-}
-
-static void handle_propfind(struct connection *conn, const char *path,
-                            file_stat_t *stp, int exists) {
-  static const char header[] = "HTTP/1.1 207 Multi-Status\r\n"
-    "Connection: close\r\n"
-    "Content-Type: text/xml; charset=utf-8\r\n\r\n"
-    ""
-    "\n";
-  static const char footer[] = "";
-  const char *depth = mg_get_header(&conn->mg_conn, "Depth");
-#ifdef MONGOOSE_NO_DIRECTORY_LISTING
-  const char *list_dir = "no";
-#else
-  const char *list_dir = conn->server->config_options[ENABLE_DIRECTORY_LISTING];
-#endif
-
-  conn->mg_conn.status_code = 207;
-
-  // Print properties for the requested resource itself
-  if (!exists) {
-    conn->mg_conn.status_code = 404;
-    mg_printf(&conn->mg_conn, "%s", "HTTP/1.1 404 Not Found\r\n\r\n");
-  } else if (S_ISDIR(stp->st_mode) && mg_strcasecmp(list_dir, "yes") != 0) {
-    conn->mg_conn.status_code = 403;
-    mg_printf(&conn->mg_conn, "%s",
-              "HTTP/1.1 403 Directory Listing Denied\r\n\r\n");
-  } else {
-    ns_send(conn->ns_conn, header, sizeof(header) - 1);
-    print_props(conn, conn->mg_conn.uri, stp);
-
-    if (S_ISDIR(stp->st_mode) &&
-             (depth == NULL || strcmp(depth, "0") != 0)) {
-      struct dir_entry *arr = NULL;
-      int i, num_entries = scan_directory(conn, path, &arr);
-
-      for (i = 0; i < num_entries; i++) {
-        char buf[MAX_PATH_SIZE * 3];
-        struct dir_entry *de = &arr[i];
-        mg_url_encode(de->file_name, strlen(de->file_name), buf, sizeof(buf));
-        print_props(conn, buf, &de->st);
-        NS_FREE(de->file_name);
-      }
-      NS_FREE(arr);
-    }
-    ns_send(conn->ns_conn, footer, sizeof(footer) - 1);
-  }
-
-  close_local_endpoint(conn);
-}
-
-static void handle_mkcol(struct connection *conn, const char *path) {
-  int status_code = 500;
-
-  if (conn->mg_conn.content_len > 0) {
-    status_code = 415;
-  } else if (!mkdir(path, 0755)) {
-    status_code = 201;
-  } else if (errno == EEXIST) {
-    status_code = 405;
-  } else if (errno == EACCES) {
-    status_code = 403;
-  } else if (errno == ENOENT) {
-    status_code = 409;
-  }
-  send_http_error(conn, status_code, NULL);
-}
-
-static int remove_directory(const char *dir) {
-  char path[MAX_PATH_SIZE];
-  struct dirent *dp;
-  file_stat_t st;
-  DIR *dirp;
-
-  if ((dirp = opendir(dir)) == NULL) return 0;
-
-  while ((dp = readdir(dirp)) != NULL) {
-    if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue;
-    mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
-    stat(path, &st);
-    if (S_ISDIR(st.st_mode)) {
-      remove_directory(path);
-    } else {
-      remove(path);
-    }
-  }
-  closedir(dirp);
-  rmdir(dir);
-
-  return 1;
-}
-
-static void handle_delete(struct connection *conn, const char *path) {
-  file_stat_t st;
-
-  if (stat(path, &st) != 0) {
-    send_http_error(conn, 404, NULL);
-  } else if (S_ISDIR(st.st_mode)) {
-    remove_directory(path);
-    send_http_error(conn, 204, NULL);
-  } else if (remove(path) == 0) {
-    send_http_error(conn, 204, NULL);
-  } else {
-    send_http_error(conn, 423, NULL);
-  }
-}
-
-// For a given PUT path, create all intermediate subdirectories
-// for given path. Return 0 if the path itself is a directory,
-// or -1 on error, 1 if OK.
-static int put_dir(const char *path) {
-  char buf[MAX_PATH_SIZE];
-  const char *s, *p;
-  file_stat_t st;
-
-  // Create intermediate directories if they do not exist
-  for (s = p = path + 1; (p = strchr(s, '/')) != NULL; s = ++p) {
-    if (p - path >= (int) sizeof(buf)) return -1; // Buffer overflow
-    memcpy(buf, path, p - path);
-    buf[p - path] = '\0';
-    if (stat(buf, &st) != 0 && mkdir(buf, 0755) != 0) return -1;
-    if (p[1] == '\0') return 0;  // Path is a directory itself
-  }
-
-  return 1;
-}
-
-static void handle_put(struct connection *conn, const char *path) {
-  file_stat_t st;
-  const char *range, *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length");
-  int64_t r1, r2;
-  int rc;
-
-  conn->mg_conn.status_code = !stat(path, &st) ? 200 : 201;
-  if ((rc = put_dir(path)) == 0) {
-    mg_printf(&conn->mg_conn, "HTTP/1.1 %d OK\r\n\r\n",
-              conn->mg_conn.status_code);
-    close_local_endpoint(conn);
-  } else if (rc == -1) {
-    send_http_error(conn, 500, "put_dir: %s", strerror(errno));
-  } else if (cl_hdr == NULL) {
-    send_http_error(conn, 411, NULL);
-  } else if ((conn->endpoint.fd =
-              open(path, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0644)) < 0) {
-    send_http_error(conn, 500, "open(%s): %s", path, strerror(errno));
-  } else {
-    DBG(("PUT [%s] %lu", path, (unsigned long) conn->ns_conn->recv_iobuf.len));
-    conn->endpoint_type = EP_PUT;
-    ns_set_close_on_exec(conn->endpoint.fd);
-    range = mg_get_header(&conn->mg_conn, "Content-Range");
-    conn->cl = to64(cl_hdr);
-    r1 = r2 = 0;
-    if (range != NULL && parse_range_header(range, &r1, &r2) > 0) {
-      conn->mg_conn.status_code = 206;
-      lseek(conn->endpoint.fd, r1, SEEK_SET);
-      conn->cl = r2 > r1 ? r2 - r1 + 1: conn->cl - r1;
-    }
-    mg_printf(&conn->mg_conn, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n",
-              conn->mg_conn.status_code);
-  }
-}
-
-static void forward_put_data(struct connection *conn) {
-  struct iobuf *io = &conn->ns_conn->recv_iobuf;
-  size_t k = conn->cl < (int64_t) io->len ? conn->cl : (int64_t) io->len;   // To write
-  size_t n = write(conn->endpoint.fd, io->buf, k);   // Write them!
-  if (n > 0) {
-    iobuf_remove(io, n);
-    conn->cl -= n;
-  }
-  if (conn->cl <= 0) {
-    close_local_endpoint(conn);
-  }
-}
-#endif //  MONGOOSE_NO_DAV
-
-static void send_options(struct connection *conn) {
-  conn->mg_conn.status_code = 200;
-  mg_printf(&conn->mg_conn, "%s",
-            "HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, CONNECT, PUT, "
-            "DELETE, OPTIONS, PROPFIND, MKCOL\r\nDAV: 1\r\n\r\n");
-  close_local_endpoint(conn);
-}
-
-#ifndef MONGOOSE_NO_AUTH
-void mg_send_digest_auth_request(struct mg_connection *c) {
-  struct connection *conn = MG_CONN_2_CONN(c);
-  c->status_code = 401;
-  mg_printf(c,
-            "HTTP/1.1 401 Unauthorized\r\n"
-            "WWW-Authenticate: Digest qop=\"auth\", "
-            "realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
-            conn->server->config_options[AUTH_DOMAIN],
-            (unsigned long) time(NULL));
-  close_local_endpoint(conn);
-}
-
-// Use the global passwords file, if specified by auth_gpass option,
-// or search for .htpasswd in the requested directory.
-static FILE *open_auth_file(struct connection *conn, const char *path,
-                            int is_directory) {
-  char name[MAX_PATH_SIZE];
-  const char *p, *gpass = conn->server->config_options[GLOBAL_AUTH_FILE];
-  FILE *fp = NULL;
-
-  if (gpass != NULL) {
-    // Use global passwords file
-    fp = fopen(gpass, "r");
-  } else if (is_directory) {
-    mg_snprintf(name, sizeof(name), "%s%c%s", path, '/', PASSWORDS_FILE_NAME);
-    fp = fopen(name, "r");
-  } else {
-    // Try to find .htpasswd in requested directory.
-    if ((p = strrchr(path, '/')) == NULL) p = path;
-    mg_snprintf(name, sizeof(name), "%.*s%c%s",
-                (int) (p - path), path, '/', PASSWORDS_FILE_NAME);
-    fp = fopen(name, "r");
-  }
-
-  return fp;
-}
-
-#if !defined(HAVE_MD5) && !defined(MONGOOSE_NO_AUTH)
-/*
- * This code implements the MD5 message-digest algorithm.
- * The algorithm is due to Ron Rivest.	This code was
- * written by Colin Plumb in 1993, no copyright is claimed.
- * This code is in the public domain; do with it what you wish.
- *
- * Equivalent code is available from RSA Data Security, Inc.
- * This code has been tested against that, and is equivalent,
- * except that you don't need to include two pages of legalese
- * with every copy.
- *
- * To compute the message digest of a chunk of bytes, declare an
- * MD5Context structure, pass it to MD5Init, call MD5Update as
- * needed on buffers full of bytes, and then call MD5Final, which
- * will fill a supplied 16-byte array with the digest.
- */
-
-typedef struct MD5Context {
-  uint32_t buf[4];
-  uint32_t bits[2];
-  unsigned char in[64];
-} MD5_CTX;
-
-static void byteReverse(unsigned char *buf, unsigned longs) {
-  uint32_t t;
-
-  // Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN
-  if (is_big_endian()) {
-    do {
-      t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
-        ((unsigned) buf[1] << 8 | buf[0]);
-      * (uint32_t *) buf = t;
-      buf += 4;
-    } while (--longs);
-  }
-}
-
-#define F1(x, y, z) (z ^ (x & (y ^ z)))
-#define F2(x, y, z) F1(z, x, y)
-#define F3(x, y, z) (x ^ y ^ z)
-#define F4(x, y, z) (y ^ (x | ~z))
-
-#define MD5STEP(f, w, x, y, z, data, s) \
-  ( w += f(x, y, z) + data,  w = w<>(32-s),  w += x )
-
-// Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
-// initialization constants.
-static void MD5Init(MD5_CTX *ctx) {
-  ctx->buf[0] = 0x67452301;
-  ctx->buf[1] = 0xefcdab89;
-  ctx->buf[2] = 0x98badcfe;
-  ctx->buf[3] = 0x10325476;
-
-  ctx->bits[0] = 0;
-  ctx->bits[1] = 0;
-}
-
-static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) {
-  register uint32_t a, b, c, d;
-
-  a = buf[0];
-  b = buf[1];
-  c = buf[2];
-  d = buf[3];
-
-  MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
-  MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
-  MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
-  MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
-  MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
-  MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
-  MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
-  MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
-  MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
-  MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
-  MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
-  MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
-  MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
-  MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
-  MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
-  MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
-
-  MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
-  MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
-  MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
-  MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
-  MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
-  MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
-  MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
-  MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
-  MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
-  MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
-  MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
-  MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
-  MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
-  MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
-  MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
-  MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
-
-  MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
-  MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
-  MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
-  MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
-  MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
-  MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
-  MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
-  MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
-  MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
-  MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
-  MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
-  MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
-  MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
-  MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
-  MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
-  MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
-
-  MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
-  MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
-  MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
-  MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
-  MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
-  MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
-  MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
-  MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
-  MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
-  MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
-  MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
-  MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
-  MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
-  MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
-  MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
-  MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
-
-  buf[0] += a;
-  buf[1] += b;
-  buf[2] += c;
-  buf[3] += d;
-}
-
-static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) {
-  uint32_t t;
-
-  t = ctx->bits[0];
-  if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
-    ctx->bits[1]++;
-  ctx->bits[1] += len >> 29;
-
-  t = (t >> 3) & 0x3f;
-
-  if (t) {
-    unsigned char *p = (unsigned char *) ctx->in + t;
-
-    t = 64 - t;
-    if (len < t) {
-      memcpy(p, buf, len);
-      return;
-    }
-    memcpy(p, buf, t);
-    byteReverse(ctx->in, 16);
-    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
-    buf += t;
-    len -= t;
-  }
-
-  while (len >= 64) {
-    memcpy(ctx->in, buf, 64);
-    byteReverse(ctx->in, 16);
-    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
-    buf += 64;
-    len -= 64;
-  }
-
-  memcpy(ctx->in, buf, len);
-}
-
-static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) {
-  unsigned count;
-  unsigned char *p;
-  uint32_t *a;
-
-  count = (ctx->bits[0] >> 3) & 0x3F;
-
-  p = ctx->in + count;
-  *p++ = 0x80;
-  count = 64 - 1 - count;
-  if (count < 8) {
-    memset(p, 0, count);
-    byteReverse(ctx->in, 16);
-    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
-    memset(ctx->in, 0, 56);
-  } else {
-    memset(p, 0, count - 8);
-  }
-  byteReverse(ctx->in, 14);
-
-  a = (uint32_t *)ctx->in;
-  a[14] = ctx->bits[0];
-  a[15] = ctx->bits[1];
-
-  MD5Transform(ctx->buf, (uint32_t *) ctx->in);
-  byteReverse((unsigned char *) ctx->buf, 4);
-  memcpy(digest, ctx->buf, 16);
-  memset((char *) ctx, 0, sizeof(*ctx));
-}
-#endif // !HAVE_MD5
-
-
-
-// Stringify binary data. Output buffer must be twice as big as input,
-// because each byte takes 2 bytes in string representation
-static void bin2str(char *to, const unsigned char *p, size_t len) {
-  static const char *hex = "0123456789abcdef";
-
-  for (; len--; p++) {
-    *to++ = hex[p[0] >> 4];
-    *to++ = hex[p[0] & 0x0f];
-  }
-  *to = '\0';
-}
-
-// Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
-char *mg_md5(char buf[33], ...) {
-  unsigned char hash[16];
-  const char *p;
-  va_list ap;
-  MD5_CTX ctx;
-
-  MD5Init(&ctx);
-
-  va_start(ap, buf);
-  while ((p = va_arg(ap, const char *)) != NULL) {
-    MD5Update(&ctx, (const unsigned char *) p, (unsigned) strlen(p));
-  }
-  va_end(ap);
-
-  MD5Final(hash, &ctx);
-  bin2str(buf, hash, sizeof(hash));
-  return buf;
-}
-
-// Check the user's password, return 1 if OK
-static int check_password(const char *method, const char *ha1, const char *uri,
-                          const char *nonce, const char *nc, const char *cnonce,
-                          const char *qop, const char *response) {
-  char ha2[32 + 1], expected_response[32 + 1];
-
-#if 0
-  // Check for authentication timeout
-  if ((unsigned long) time(NULL) - (unsigned long) to64(nonce) > 3600 * 2) {
-    return 0;
-  }
-#endif
-
-  mg_md5(ha2, method, ":", uri, NULL);
-  mg_md5(expected_response, ha1, ":", nonce, ":", nc,
-      ":", cnonce, ":", qop, ":", ha2, NULL);
-
-  return mg_strcasecmp(response, expected_response) == 0 ? MG_TRUE : MG_FALSE;
-}
-
-
-// Authorize against the opened passwords file. Return 1 if authorized.
-int mg_authorize_digest(struct mg_connection *c, FILE *fp) {
-  struct connection *conn = MG_CONN_2_CONN(c);
-  const char *hdr;
-  char line[256], f_user[256], ha1[256], f_domain[256], user[100], nonce[100],
-       uri[MAX_REQUEST_SIZE], cnonce[100], resp[100], qop[100], nc[100];
-
-  if (c == NULL || fp == NULL) return 0;
-  if ((hdr = mg_get_header(c, "Authorization")) == NULL ||
-      mg_strncasecmp(hdr, "Digest ", 7) != 0) return 0;
-  if (!mg_parse_header(hdr, "username", user, sizeof(user))) return 0;
-  if (!mg_parse_header(hdr, "cnonce", cnonce, sizeof(cnonce))) return 0;
-  if (!mg_parse_header(hdr, "response", resp, sizeof(resp))) return 0;
-  if (!mg_parse_header(hdr, "uri", uri, sizeof(uri))) return 0;
-  if (!mg_parse_header(hdr, "qop", qop, sizeof(qop))) return 0;
-  if (!mg_parse_header(hdr, "nc", nc, sizeof(nc))) return 0;
-  if (!mg_parse_header(hdr, "nonce", nonce, sizeof(nonce))) return 0;
-
-  while (fgets(line, sizeof(line), fp) != NULL) {
-    if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) == 3 &&
-        !strcmp(user, f_user) &&
-        // NOTE(lsm): due to a bug in MSIE, we do not compare URIs
-        !strcmp(conn->server->config_options[AUTH_DOMAIN], f_domain))
-      return check_password(c->request_method, ha1, uri,
-                            nonce, nc, cnonce, qop, resp);
-  }
-  return MG_FALSE;
-}
-
-
-// Return 1 if request is authorised, 0 otherwise.
-static int is_authorized(struct connection *conn, const char *path,
-                         int is_directory) {
-  FILE *fp;
-  int authorized = MG_TRUE;
-
-  if ((fp = open_auth_file(conn, path, is_directory)) != NULL) {
-    authorized = mg_authorize_digest(&conn->mg_conn, fp);
-    fclose(fp);
-  }
-
-  return authorized;
-}
-
-static int is_authorized_for_dav(struct connection *conn) {
-  const char *auth_file = conn->server->config_options[DAV_AUTH_FILE];
-  const char *method = conn->mg_conn.request_method;
-  FILE *fp;
-  int authorized = MG_FALSE;
-
-  // If dav_auth_file is not set, allow non-authorized PROPFIND
-  if (method != NULL && !strcmp(method, "PROPFIND") && auth_file == NULL) {
-    authorized = MG_TRUE;
-  } else if (auth_file != NULL && (fp = fopen(auth_file, "r")) != NULL) {
-    authorized = mg_authorize_digest(&conn->mg_conn, fp);
-    fclose(fp);
-  }
-
-  return authorized;
-}
-#endif // MONGOOSE_NO_AUTH
-
-static int parse_header(const char *str, size_t str_len, const char *var_name,
-                        char *buf, size_t buf_size) {
-  int ch = ' ', ch1 = ',', len = 0;
-  size_t n = strlen(var_name);
-  const char *p, *end = str + str_len, *s = NULL;
-
-  if (buf != NULL && buf_size > 0) buf[0] = '\0';
-
-  // Find where variable starts
-  for (s = str; s != NULL && s + n < end; s++) {
-    if ((s == str || s[-1] == ch || s[-1] == ch1) && s[n] == '=' &&
-        !memcmp(s, var_name, n)) break;
-  }
-
-  if (s != NULL && &s[n + 1] < end) {
-    s += n + 1;
-    if (*s == '"' || *s == '\'') ch = ch1 = *s++;
-    p = s;
-    while (p < end && p[0] != ch && p[0] != ch1 && len < (int) buf_size) {
-      if (ch == ch1 && p[0] == '\\' && p[1] == ch) p++;
-      buf[len++] = *p++;
-    }
-    if (len >= (int) buf_size || (ch != ' ' && *p != ch)) {
-      len = 0;
-    } else {
-      if (len > 0 && s[len - 1] == ',') len--;
-      if (len > 0 && s[len - 1] == ';') len--;
-      buf[len] = '\0';
-    }
-  }
-
-  return len;
-}
-
-int mg_parse_header(const char *s, const char *var_name, char *buf,
-                    size_t buf_size) {
-  return parse_header(s, s == NULL ? 0 : strlen(s), var_name, buf, buf_size);
-}
-
-#ifndef MONGOOSE_NO_SSI
-static void send_ssi_file(struct mg_connection *, const char *, FILE *, int);
-
-static void send_file_data(struct mg_connection *conn, FILE *fp) {
-  char buf[IOBUF_SIZE];
-  size_t n;
-  while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
-    mg_write(conn, buf, n);
-  }
-}
-
-static void do_ssi_include(struct mg_connection *conn, const char *ssi,
-                           char *tag, int include_level) {
-  char file_name[IOBUF_SIZE], path[MAX_PATH_SIZE], *p;
-  char **opts = (MG_CONN_2_CONN(conn))->server->config_options;
-  FILE *fp;
-
-  // sscanf() is safe here, since send_ssi_file() also uses buffer
-  // of size MG_BUF_LEN to get the tag. So strlen(tag) is always < MG_BUF_LEN.
-  if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) {
-    // File name is relative to the webserver root
-    mg_snprintf(path, sizeof(path), "%s%c%s",
-                opts[DOCUMENT_ROOT], '/', file_name);
-  } else if (sscanf(tag, " abspath=\"%[^\"]\"", file_name) == 1) {
-    // File name is relative to the webserver working directory
-    // or it is absolute system path
-    mg_snprintf(path, sizeof(path), "%s", file_name);
-  } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1 ||
-             sscanf(tag, " \"%[^\"]\"", file_name) == 1) {
-    // File name is relative to the currect document
-    mg_snprintf(path, sizeof(path), "%s", ssi);
-    if ((p = strrchr(path, '/')) != NULL) {
-      p[1] = '\0';
-    }
-    mg_snprintf(path + strlen(path), sizeof(path) - strlen(path), "%s",
-                file_name);
-  } else {
-    mg_printf(conn, "Bad SSI #include: [%s]", tag);
-    return;
-  }
-
-  if ((fp = fopen(path, "rb")) == NULL) {
-    mg_printf(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s",
-              tag, path, strerror(errno));
-  } else {
-    ns_set_close_on_exec(fileno(fp));
-    if (mg_match_prefix(opts[SSI_PATTERN], strlen(opts[SSI_PATTERN]),
-        path) > 0) {
-      send_ssi_file(conn, path, fp, include_level + 1);
-    } else {
-      send_file_data(conn, fp);
-    }
-    fclose(fp);
-  }
-}
-
-#ifndef MONGOOSE_NO_POPEN
-static void do_ssi_exec(struct mg_connection *conn, char *tag) {
-  char cmd[IOBUF_SIZE];
-  FILE *fp;
-
-  if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) {
-    mg_printf(conn, "Bad SSI #exec: [%s]", tag);
-  } else if ((fp = popen(cmd, "r")) == NULL) {
-    mg_printf(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(errno));
-  } else {
-    send_file_data(conn, fp);
-    pclose(fp);
-  }
-}
-#endif // !MONGOOSE_NO_POPEN
-
-static void send_ssi_file(struct mg_connection *conn, const char *path,
-                          FILE *fp, int include_level) {
-  char buf[IOBUF_SIZE];
-  int ch, offset, len, in_ssi_tag;
-
-  if (include_level > 10) {
-    mg_printf(conn, "SSI #include level is too deep (%s)", path);
-    return;
-  }
-
-  in_ssi_tag = len = offset = 0;
-  while ((ch = fgetc(fp)) != EOF) {
-    if (in_ssi_tag && ch == '>') {
-      in_ssi_tag = 0;
-      buf[len++] = (char) ch;
-      buf[len] = '\0';
-      assert(len <= (int) sizeof(buf));
-      if (len < 6 || memcmp(buf, "
+ */
+static void mg_send_ssi_file(struct mg_connection *nc, struct http_message *hm,
+                             const char *path, FILE *fp, int include_level,
+                             const struct mg_serve_http_opts *opts) {
+  static const struct mg_str btag = MG_MK_STR(" */
+      buf[i--] = '\0';
+      while (i > 0 && buf[i] == ' ') {
+        buf[i--] = '\0';
+      }
+
+      /* Handle known SSI directives */
+      if (strncmp(p, d_include.p, d_include.len) == 0) {
+        mg_do_ssi_include(nc, hm, path, p + d_include.len + 1, include_level,
+                          opts);
+      } else if (strncmp(p, d_call.p, d_call.len) == 0) {
+        struct mg_ssi_call_ctx cctx;
+        memset(&cctx, 0, sizeof(cctx));
+        cctx.req = hm;
+        cctx.file = mg_mk_str(path);
+        cctx.arg = mg_mk_str(p + d_call.len + 1);
+        mg_call(nc, NULL, nc->user_data, MG_EV_SSI_CALL,
+                (void *) cctx.arg.p); /* NUL added above */
+        mg_call(nc, NULL, nc->user_data, MG_EV_SSI_CALL_CTX, &cctx);
+#if MG_ENABLE_HTTP_SSI_EXEC
+      } else if (strncmp(p, d_exec.p, d_exec.len) == 0) {
+        do_ssi_exec(nc, p + d_exec.len + 1);
+#endif
+      } else {
+        /* Silently ignore unknown SSI directive. */
+      }
+      len = 0;
+    } else if (ch == '<') {
+      in_ssi_tag = 1;
+      if (len > 0) {
+        mg_send(nc, buf, (size_t) len);
+      }
+      len = 0;
+      buf[len++] = ch & 0xff;
+    } else if (in_ssi_tag) {
+      if (len == (int) btag.len && strncmp(buf, btag.p, btag.len) != 0) {
+        /* Not an SSI tag */
+        in_ssi_tag = 0;
+      } else if (len == (int) sizeof(buf) - 2) {
+        mg_printf(nc, "%s: SSI tag is too large", path);
+        len = 0;
+      }
+      buf[len++] = ch & 0xff;
+    } else {
+      buf[len++] = ch & 0xff;
+      if (len == (int) sizeof(buf)) {
+        mg_send(nc, buf, (size_t) len);
+        len = 0;
+      }
+    }
+  }
+
+  /* Send the rest of buffered data */
+  if (len > 0) {
+    mg_send(nc, buf, (size_t) len);
+  }
+}
+
+MG_INTERNAL void mg_handle_ssi_request(struct mg_connection *nc,
+                                       struct http_message *hm,
+                                       const char *path,
+                                       const struct mg_serve_http_opts *opts) {
+  FILE *fp;
+  struct mg_str mime_type;
+  DBG(("%p %s", nc, path));
+
+  if ((fp = mg_fopen(path, "rb")) == NULL) {
+    mg_http_send_error(nc, 404, NULL);
+  } else {
+    mg_set_close_on_exec((sock_t) fileno(fp));
+
+    mime_type = mg_get_mime_type(path, "text/plain", opts);
+    mg_send_response_line(nc, 200, opts->extra_headers);
+    mg_printf(nc,
+              "Content-Type: %.*s\r\n"
+              "Connection: close\r\n\r\n",
+              (int) mime_type.len, mime_type.p);
+    mg_send_ssi_file(nc, hm, path, fp, 0, opts);
+    fclose(fp);
+    nc->flags |= MG_F_SEND_AND_CLOSE;
+  }
+}
+
+#endif /* MG_ENABLE_HTTP_SSI && MG_ENABLE_HTTP && MG_ENABLE_FILESYSTEM */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_http_webdav.c"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#if MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBDAV
+
+MG_INTERNAL int mg_is_dav_request(const struct mg_str *s) {
+  static const char *methods[] = {
+    "PUT",
+    "DELETE",
+    "MKCOL",
+    "PROPFIND",
+    "MOVE"
+#if MG_ENABLE_FAKE_DAVLOCK
+    ,
+    "LOCK",
+    "UNLOCK"
+#endif
+  };
+  size_t i;
+
+  for (i = 0; i < ARRAY_SIZE(methods); i++) {
+    if (mg_vcmp(s, methods[i]) == 0) {
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+static int mg_mkdir(const char *path, uint32_t mode) {
+#ifndef _WIN32
+  return mkdir(path, mode);
+#else
+  (void) mode;
+  return _mkdir(path);
+#endif
+}
+
+static void mg_print_props(struct mg_connection *nc, const char *name,
+                           cs_stat_t *stp) {
+  char mtime[64];
+  time_t t = stp->st_mtime; /* store in local variable for NDK compile */
+  struct mg_str name_esc = mg_url_encode(mg_mk_str(name));
+  mg_gmt_time_string(mtime, sizeof(mtime), &t);
+  mg_printf(nc,
+            ""
+            "%s"
+            ""
+            ""
+            "%s"
+            "%" INT64_FMT
+            ""
+            "%s"
+            ""
+            "HTTP/1.1 200 OK"
+            ""
+            "\n",
+            name_esc.p, S_ISDIR(stp->st_mode) ? "" : "",
+            (int64_t) stp->st_size, mtime);
+  free((void *) name_esc.p);
+}
+
+MG_INTERNAL void mg_handle_propfind(struct mg_connection *nc, const char *path,
+                                    cs_stat_t *stp, struct http_message *hm,
+                                    struct mg_serve_http_opts *opts) {
+  static const char header[] =
+      "HTTP/1.1 207 Multi-Status\r\n"
+      "Connection: close\r\n"
+      "Content-Type: text/xml; charset=utf-8\r\n\r\n"
+      ""
+      "\n";
+  static const char footer[] = "\n";
+  const struct mg_str *depth = mg_get_http_header(hm, "Depth");
+
+  /* Print properties for the requested resource itself */
+  if (S_ISDIR(stp->st_mode) &&
+      strcmp(opts->enable_directory_listing, "yes") != 0) {
+    mg_printf(nc, "%s", "HTTP/1.1 403 Directory Listing Denied\r\n\r\n");
+  } else {
+    char uri[MG_MAX_PATH];
+    mg_send(nc, header, sizeof(header) - 1);
+    snprintf(uri, sizeof(uri), "%.*s", (int) hm->uri.len, hm->uri.p);
+    mg_print_props(nc, uri, stp);
+    if (S_ISDIR(stp->st_mode) && (depth == NULL || mg_vcmp(depth, "0") != 0)) {
+      mg_scan_directory(nc, path, opts, mg_print_props);
+    }
+    mg_send(nc, footer, sizeof(footer) - 1);
+    nc->flags |= MG_F_SEND_AND_CLOSE;
+  }
+}
+
+#if MG_ENABLE_FAKE_DAVLOCK
+/*
+ * Windows explorer (probably there are another WebDav clients like it)
+ * requires LOCK support in webdav. W/out this, it still works, but fails
+ * to save file: shows error message and offers "Save As".
+ * "Save as" works, but this message is very annoying.
+ * This is fake lock, which doesn't lock something, just returns LOCK token,
+ * UNLOCK always answers "OK".
+ * With this fake LOCK Windows Explorer looks happy and saves file.
+ * NOTE: that is not DAV LOCK imlementation, it is just a way to shut up
+ * Windows native DAV client. This is why FAKE LOCK is not enabed by default
+ */
+MG_INTERNAL void mg_handle_lock(struct mg_connection *nc, const char *path) {
+  static const char *reply =
+      "HTTP/1.1 207 Multi-Status\r\n"
+      "Connection: close\r\n"
+      "Content-Type: text/xml; charset=utf-8\r\n\r\n"
+      ""
+      "\n"
+      "\n"
+      "\n"
+      "\n"
+      "\n"
+      "opaquelocktoken:%s%u"
+      ""
+      ""
+      "\n"
+      ""
+      "\n";
+  mg_printf(nc, reply, path, (unsigned int) mg_time());
+  nc->flags |= MG_F_SEND_AND_CLOSE;
+}
+#endif
+
+MG_INTERNAL void mg_handle_mkcol(struct mg_connection *nc, const char *path,
+                                 struct http_message *hm) {
+  int status_code = 500;
+  if (hm->body.len != (size_t) ~0 && hm->body.len > 0) {
+    status_code = 415;
+  } else if (!mg_mkdir(path, 0755)) {
+    status_code = 201;
+  } else if (errno == EEXIST) {
+    status_code = 405;
+  } else if (errno == EACCES) {
+    status_code = 403;
+  } else if (errno == ENOENT) {
+    status_code = 409;
+  } else {
+    status_code = 500;
+  }
+  mg_http_send_error(nc, status_code, NULL);
+}
+
+static int mg_remove_directory(const struct mg_serve_http_opts *opts,
+                               const char *dir) {
+  char path[MG_MAX_PATH];
+  struct dirent *dp;
+  cs_stat_t st;
+  DIR *dirp;
+
+  if ((dirp = opendir(dir)) == NULL) return 0;
+
+  while ((dp = readdir(dirp)) != NULL) {
+    if (mg_is_file_hidden((const char *) dp->d_name, opts, 1)) {
+      continue;
+    }
+    snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
+    mg_stat(path, &st);
+    if (S_ISDIR(st.st_mode)) {
+      mg_remove_directory(opts, path);
+    } else {
+      remove(path);
+    }
+  }
+  closedir(dirp);
+  rmdir(dir);
+
+  return 1;
+}
+
+MG_INTERNAL void mg_handle_move(struct mg_connection *c,
+                                const struct mg_serve_http_opts *opts,
+                                const char *path, struct http_message *hm) {
+  const struct mg_str *dest = mg_get_http_header(hm, "Destination");
+  if (dest == NULL) {
+    mg_http_send_error(c, 411, NULL);
+  } else {
+    const char *p = (char *) memchr(dest->p, '/', dest->len);
+    if (p != NULL && p[1] == '/' &&
+        (p = (char *) memchr(p + 2, '/', dest->p + dest->len - p)) != NULL) {
+      char buf[MG_MAX_PATH];
+      snprintf(buf, sizeof(buf), "%s%.*s", opts->dav_document_root,
+               (int) (dest->p + dest->len - p), p);
+      if (rename(path, buf) == 0) {
+        mg_http_send_error(c, 200, NULL);
+      } else {
+        mg_http_send_error(c, 418, NULL);
+      }
+    } else {
+      mg_http_send_error(c, 500, NULL);
+    }
+  }
+}
+
+MG_INTERNAL void mg_handle_delete(struct mg_connection *nc,
+                                  const struct mg_serve_http_opts *opts,
+                                  const char *path) {
+  cs_stat_t st;
+  if (mg_stat(path, &st) != 0) {
+    mg_http_send_error(nc, 404, NULL);
+  } else if (S_ISDIR(st.st_mode)) {
+    mg_remove_directory(opts, path);
+    mg_http_send_error(nc, 204, NULL);
+  } else if (remove(path) == 0) {
+    mg_http_send_error(nc, 204, NULL);
+  } else {
+    mg_http_send_error(nc, 423, NULL);
+  }
+}
+
+/* Return -1 on error, 1 on success. */
+static int mg_create_itermediate_directories(const char *path) {
+  const char *s;
+
+  /* Create intermediate directories if they do not exist */
+  for (s = path + 1; *s != '\0'; s++) {
+    if (*s == '/') {
+      char buf[MG_MAX_PATH];
+      cs_stat_t st;
+      snprintf(buf, sizeof(buf), "%.*s", (int) (s - path), path);
+      buf[sizeof(buf) - 1] = '\0';
+      if (mg_stat(buf, &st) != 0 && mg_mkdir(buf, 0755) != 0) {
+        return -1;
+      }
+    }
+  }
+
+  return 1;
+}
+
+MG_INTERNAL void mg_handle_put(struct mg_connection *nc, const char *path,
+                               struct http_message *hm) {
+  struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
+  cs_stat_t st;
+  const struct mg_str *cl_hdr = mg_get_http_header(hm, "Content-Length");
+  int rc, status_code = mg_stat(path, &st) == 0 ? 200 : 201;
+
+  mg_http_free_proto_data_file(&pd->file);
+  if ((rc = mg_create_itermediate_directories(path)) == 0) {
+    mg_printf(nc, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n", status_code);
+  } else if (rc == -1) {
+    mg_http_send_error(nc, 500, NULL);
+  } else if (cl_hdr == NULL) {
+    mg_http_send_error(nc, 411, NULL);
+  } else if ((pd->file.fp = mg_fopen(path, "w+b")) == NULL) {
+    mg_http_send_error(nc, 500, NULL);
+  } else {
+    const struct mg_str *range_hdr = mg_get_http_header(hm, "Content-Range");
+    int64_t r1 = 0, r2 = 0;
+    pd->file.type = DATA_PUT;
+    mg_set_close_on_exec((sock_t) fileno(pd->file.fp));
+    pd->file.cl = to64(cl_hdr->p);
+    if (range_hdr != NULL &&
+        mg_http_parse_range_header(range_hdr, &r1, &r2) > 0) {
+      status_code = 206;
+      fseeko(pd->file.fp, r1, SEEK_SET);
+      pd->file.cl = r2 > r1 ? r2 - r1 + 1 : pd->file.cl - r1;
+    }
+    mg_printf(nc, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n", status_code);
+    /* Remove HTTP request from the mbuf, leave only payload */
+    mbuf_remove(&nc->recv_mbuf, hm->message.len - hm->body.len);
+    mg_http_transfer_file_data(nc);
+  }
+}
+
+#endif /* MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBDAV */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_http_websocket.c"
+#endif
+/*
+ * Copyright (c) 2014 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#if MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBSOCKET
+
+/* Amalgamated: #include "common/cs_sha1.h" */
+
+#ifndef MG_WEBSOCKET_PING_INTERVAL_SECONDS
+#define MG_WEBSOCKET_PING_INTERVAL_SECONDS 5
+#endif
+
+#define FLAGS_MASK_FIN (1 << 7)
+#define FLAGS_MASK_OP 0x0f
+
+static int mg_is_ws_fragment(unsigned char flags) {
+  return (flags & FLAGS_MASK_FIN) == 0 ||
+         (flags & FLAGS_MASK_OP) == WEBSOCKET_OP_CONTINUE;
+}
+
+static int mg_is_ws_first_fragment(unsigned char flags) {
+  return (flags & FLAGS_MASK_FIN) == 0 &&
+         (flags & FLAGS_MASK_OP) != WEBSOCKET_OP_CONTINUE;
+}
+
+static int mg_is_ws_control_frame(unsigned char flags) {
+  unsigned char op = (flags & FLAGS_MASK_OP);
+  return op == WEBSOCKET_OP_CLOSE || op == WEBSOCKET_OP_PING ||
+         op == WEBSOCKET_OP_PONG;
+}
+
+static void mg_handle_incoming_websocket_frame(struct mg_connection *nc,
+                                               struct websocket_message *wsm) {
+  if (wsm->flags & 0x8) {
+    mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_CONTROL_FRAME, wsm);
+  } else {
+    mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_FRAME, wsm);
+  }
+}
+
+static struct mg_ws_proto_data *mg_ws_get_proto_data(struct mg_connection *nc) {
+  struct mg_http_proto_data *htd = mg_http_get_proto_data(nc);
+  return (htd != NULL ? &htd->ws_data : NULL);
+}
+
+/*
+ * Sends a Close websocket frame with the given data, and closes the underlying
+ * connection. If `len` is ~0, strlen(data) is used.
+ */
+static void mg_ws_close(struct mg_connection *nc, const void *data,
+                        size_t len) {
+  if ((int) len == ~0) {
+    len = strlen((const char *) data);
+  }
+  mg_send_websocket_frame(nc, WEBSOCKET_OP_CLOSE, data, len);
+  nc->flags |= MG_F_SEND_AND_CLOSE;
+}
+
+static int mg_deliver_websocket_data(struct mg_connection *nc) {
+  /* Using unsigned char *, cause of integer arithmetic below */
+  uint64_t i, data_len = 0, frame_len = 0, new_data_len = nc->recv_mbuf.len,
+              len, mask_len = 0, header_len = 0;
+  struct mg_ws_proto_data *wsd = mg_ws_get_proto_data(nc);
+  unsigned char *new_data = (unsigned char *) nc->recv_mbuf.buf,
+                *e = (unsigned char *) nc->recv_mbuf.buf + nc->recv_mbuf.len;
+  uint8_t flags;
+  int ok, reass;
+
+  if (wsd->reass_len > 0) {
+    /*
+     * We already have some previously received data which we need to
+     * reassemble and deliver to the client code when we get the final
+     * fragment.
+     *
+     * NOTE: it doesn't mean that the current message must be a continuation:
+     * it might be a control frame (Close, Ping or Pong), which should be
+     * handled without breaking the fragmented message.
+     */
+
+    size_t existing_len = wsd->reass_len;
+    assert(new_data_len >= existing_len);
+
+    new_data += existing_len;
+    new_data_len -= existing_len;
+  }
+
+  flags = new_data[0];
+
+  reass = new_data_len > 0 && mg_is_ws_fragment(flags) &&
+          !(nc->flags & MG_F_WEBSOCKET_NO_DEFRAG);
+
+  if (reass && mg_is_ws_control_frame(flags)) {
+    /*
+     * Control frames can't be fragmented, so if we encounter fragmented
+     * control frame, close connection immediately.
+     */
+    mg_ws_close(nc, "fragmented control frames are illegal", ~0);
+    return 0;
+  } else if (new_data_len > 0 && !reass && !mg_is_ws_control_frame(flags) &&
+             wsd->reass_len > 0) {
+    /*
+     * When in the middle of a fragmented message, only the continuations
+     * and control frames are allowed.
+     */
+    mg_ws_close(nc, "non-continuation in the middle of a fragmented message",
+                ~0);
+    return 0;
+  }
+
+  if (new_data_len >= 2) {
+    len = new_data[1] & 0x7f;
+    mask_len = new_data[1] & FLAGS_MASK_FIN ? 4 : 0;
+    if (len < 126 && new_data_len >= mask_len) {
+      data_len = len;
+      header_len = 2 + mask_len;
+    } else if (len == 126 && new_data_len >= 4 + mask_len) {
+      header_len = 4 + mask_len;
+      data_len = ntohs(*(uint16_t *) &new_data[2]);
+    } else if (new_data_len >= 10 + mask_len) {
+      header_len = 10 + mask_len;
+      data_len = (((uint64_t) ntohl(*(uint32_t *) &new_data[2])) << 32) +
+                 ntohl(*(uint32_t *) &new_data[6]);
+    }
+  }
+
+  frame_len = header_len + data_len;
+  ok = (frame_len > 0 && frame_len <= new_data_len);
+
+  /* Check for overflow */
+  if (frame_len < header_len || frame_len < data_len) {
+    ok = 0;
+    mg_ws_close(nc, "overflowed message", ~0);
+  }
+
+  if (ok) {
+    size_t cleanup_len = 0;
+    struct websocket_message wsm;
+
+    wsm.size = (size_t) data_len;
+    wsm.data = new_data + header_len;
+    wsm.flags = flags;
+
+    /* Apply mask if necessary */
+    if (mask_len > 0) {
+      for (i = 0; i < data_len; i++) {
+        new_data[i + header_len] ^= (new_data + header_len - mask_len)[i % 4];
+      }
+    }
+
+    if (reass) {
+      /* This is a message fragment */
+
+      if (mg_is_ws_first_fragment(flags)) {
+        /*
+         * On the first fragmented frame, skip the first byte (op) and also
+         * reset size to 1 (op), it'll be incremented with the data len below.
+         */
+        new_data += 1;
+        wsd->reass_len = 1 /* op */;
+      }
+
+      /* Append this frame to the reassembled buffer */
+      memmove(new_data, wsm.data, e - wsm.data);
+      wsd->reass_len += wsm.size;
+      nc->recv_mbuf.len -= wsm.data - new_data;
+
+      if (flags & FLAGS_MASK_FIN) {
+        /* On last fragmented frame - call user handler and remove data */
+        wsm.flags = FLAGS_MASK_FIN | nc->recv_mbuf.buf[0];
+        wsm.data = (unsigned char *) nc->recv_mbuf.buf + 1 /* op */;
+        wsm.size = wsd->reass_len - 1 /* op */;
+        cleanup_len = wsd->reass_len;
+        wsd->reass_len = 0;
+
+        /* Pass reassembled message to the client code. */
+        mg_handle_incoming_websocket_frame(nc, &wsm);
+        mbuf_remove(&nc->recv_mbuf, cleanup_len); /* Cleanup frame */
+      }
+    } else {
+      /*
+       * This is a complete message, not a fragment. It might happen in between
+       * of a fragmented message (in this case, WebSocket protocol requires
+       * current message to be a control frame).
+       */
+      cleanup_len = (size_t) frame_len;
+
+      /* First of all, check if we need to react on a control frame. */
+      switch (flags & FLAGS_MASK_OP) {
+        case WEBSOCKET_OP_PING:
+          mg_send_websocket_frame(nc, WEBSOCKET_OP_PONG, wsm.data, wsm.size);
+          break;
+
+        case WEBSOCKET_OP_CLOSE:
+          mg_ws_close(nc, wsm.data, wsm.size);
+          break;
+      }
+
+      /* Pass received message to the client code. */
+      mg_handle_incoming_websocket_frame(nc, &wsm);
+
+      /* Cleanup frame */
+      memmove(nc->recv_mbuf.buf + wsd->reass_len,
+              nc->recv_mbuf.buf + wsd->reass_len + cleanup_len,
+              nc->recv_mbuf.len - wsd->reass_len - cleanup_len);
+      nc->recv_mbuf.len -= cleanup_len;
+    }
+  }
+
+  return ok;
+}
+
+struct ws_mask_ctx {
+  size_t pos; /* zero means unmasked */
+  uint32_t mask;
+};
+
+static uint32_t mg_ws_random_mask(void) {
+  uint32_t mask;
+/*
+ * The spec requires WS client to generate hard to
+ * guess mask keys. From RFC6455, Section 5.3:
+ *
+ * The unpredictability of the masking key is essential to prevent
+ * authors of malicious applications from selecting the bytes that appear on
+ * the wire.
+ *
+ * Hence this feature is essential when the actual end user of this API
+ * is untrusted code that wouldn't have access to a lower level net API
+ * anyway (e.g. web browsers). Hence this feature is low prio for most
+ * mongoose use cases and thus can be disabled, e.g. when porting to a platform
+ * that lacks rand().
+ */
+#if MG_DISABLE_WS_RANDOM_MASK
+  mask = 0xefbeadde; /* generated with a random number generator, I swear */
+#else
+  if (sizeof(long) >= 4) {
+    mask = (uint32_t) rand();
+  } else if (sizeof(long) == 2) {
+    mask = (uint32_t) rand() << 16 | (uint32_t) rand();
+  }
+#endif
+  return mask;
+}
+
+static void mg_send_ws_header(struct mg_connection *nc, int op, size_t len,
+                              struct ws_mask_ctx *ctx) {
+  int header_len;
+  unsigned char header[10];
+
+  header[0] =
+      (op & WEBSOCKET_DONT_FIN ? 0x0 : FLAGS_MASK_FIN) | (op & FLAGS_MASK_OP);
+  if (len < 126) {
+    header[1] = (unsigned char) len;
+    header_len = 2;
+  } else if (len < 65535) {
+    uint16_t tmp = htons((uint16_t) len);
+    header[1] = 126;
+    memcpy(&header[2], &tmp, sizeof(tmp));
+    header_len = 4;
+  } else {
+    uint32_t tmp;
+    header[1] = 127;
+    tmp = htonl((uint32_t)((uint64_t) len >> 32));
+    memcpy(&header[2], &tmp, sizeof(tmp));
+    tmp = htonl((uint32_t)(len & 0xffffffff));
+    memcpy(&header[6], &tmp, sizeof(tmp));
+    header_len = 10;
+  }
+
+  /* client connections enable masking */
+  if (nc->listener == NULL) {
+    header[1] |= 1 << 7; /* set masking flag */
+    mg_send(nc, header, header_len);
+    ctx->mask = mg_ws_random_mask();
+    mg_send(nc, &ctx->mask, sizeof(ctx->mask));
+    ctx->pos = nc->send_mbuf.len;
+  } else {
+    mg_send(nc, header, header_len);
+    ctx->pos = 0;
+  }
+}
+
+static void mg_ws_mask_frame(struct mbuf *mbuf, struct ws_mask_ctx *ctx) {
+  size_t i;
+  if (ctx->pos == 0) return;
+  for (i = 0; i < (mbuf->len - ctx->pos); i++) {
+    mbuf->buf[ctx->pos + i] ^= ((char *) &ctx->mask)[i % 4];
+  }
+}
+
+void mg_send_websocket_frame(struct mg_connection *nc, int op, const void *data,
+                             size_t len) {
+  struct ws_mask_ctx ctx;
+  DBG(("%p %d %d", nc, op, (int) len));
+  mg_send_ws_header(nc, op, len, &ctx);
+  mg_send(nc, data, len);
+
+  mg_ws_mask_frame(&nc->send_mbuf, &ctx);
+
+  if (op == WEBSOCKET_OP_CLOSE) {
+    nc->flags |= MG_F_SEND_AND_CLOSE;
+  }
+}
+
+void mg_send_websocket_framev(struct mg_connection *nc, int op,
+                              const struct mg_str *strv, int strvcnt) {
+  struct ws_mask_ctx ctx;
+  int i;
+  int len = 0;
+  for (i = 0; i < strvcnt; i++) {
+    len += strv[i].len;
+  }
+
+  mg_send_ws_header(nc, op, len, &ctx);
+
+  for (i = 0; i < strvcnt; i++) {
+    mg_send(nc, strv[i].p, strv[i].len);
+  }
+
+  mg_ws_mask_frame(&nc->send_mbuf, &ctx);
+
+  if (op == WEBSOCKET_OP_CLOSE) {
+    nc->flags |= MG_F_SEND_AND_CLOSE;
+  }
+}
+
+void mg_printf_websocket_frame(struct mg_connection *nc, int op,
+                               const char *fmt, ...) {
+  char mem[MG_VPRINTF_BUFFER_SIZE], *buf = mem;
+  va_list ap;
+  int len;
+
+  va_start(ap, fmt);
+  if ((len = mg_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
+    mg_send_websocket_frame(nc, op, buf, len);
+  }
+  va_end(ap);
+
+  if (buf != mem && buf != NULL) {
+    MG_FREE(buf);
+  }
+}
+
+MG_INTERNAL void mg_ws_handler(struct mg_connection *nc, int ev,
+                               void *ev_data MG_UD_ARG(void *user_data)) {
+  mg_call(nc, nc->handler, nc->user_data, ev, ev_data);
+
+  switch (ev) {
+    case MG_EV_RECV:
+      do {
+      } while (mg_deliver_websocket_data(nc));
+      break;
+    case MG_EV_POLL:
+      /* Ping idle websocket connections */
+      {
+        time_t now = *(time_t *) ev_data;
+        if (nc->flags & MG_F_IS_WEBSOCKET &&
+            now > nc->last_io_time + MG_WEBSOCKET_PING_INTERVAL_SECONDS) {
+          mg_send_websocket_frame(nc, WEBSOCKET_OP_PING, "", 0);
+        }
+      }
+      break;
+    default:
+      break;
+  }
+#if MG_ENABLE_CALLBACK_USERDATA
+  (void) user_data;
+#endif
+}
+
+#ifndef MG_EXT_SHA1
+void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[],
+                    const size_t *msg_lens, uint8_t *digest) {
+  size_t i;
+  cs_sha1_ctx sha_ctx;
+  cs_sha1_init(&sha_ctx);
+  for (i = 0; i < num_msgs; i++) {
+    cs_sha1_update(&sha_ctx, msgs[i], msg_lens[i]);
+  }
+  cs_sha1_final(digest, &sha_ctx);
+}
+#else
+extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[],
+                           const size_t *msg_lens, uint8_t *digest);
+#endif
+
+MG_INTERNAL void mg_ws_handshake(struct mg_connection *nc,
+                                 const struct mg_str *key,
+                                 struct http_message *hm) {
+  static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+  const uint8_t *msgs[2] = {(const uint8_t *) key->p, (const uint8_t *) magic};
+  const size_t msg_lens[2] = {key->len, 36};
+  unsigned char sha[20];
+  char b64_sha[30];
+  struct mg_str *s;
+
+  mg_hash_sha1_v(2, msgs, msg_lens, sha);
+  mg_base64_encode(sha, sizeof(sha), b64_sha);
+  mg_printf(nc, "%s",
+            "HTTP/1.1 101 Switching Protocols\r\n"
+            "Upgrade: websocket\r\n"
+            "Connection: Upgrade\r\n");
+
+  s = mg_get_http_header(hm, "Sec-WebSocket-Protocol");
+  if (s != NULL) {
+    mg_printf(nc, "Sec-WebSocket-Protocol: %.*s\r\n", (int) s->len, s->p);
+  }
+  mg_printf(nc, "Sec-WebSocket-Accept: %s%s", b64_sha, "\r\n\r\n");
+
+  DBG(("%p %.*s %s", nc, (int) key->len, key->p, b64_sha));
+}
+
+void mg_send_websocket_handshake2(struct mg_connection *nc, const char *path,
+                                  const char *host, const char *protocol,
+                                  const char *extra_headers) {
+  mg_send_websocket_handshake3(nc, path, host, protocol, extra_headers, NULL,
+                               NULL);
+}
+
+void mg_send_websocket_handshake3(struct mg_connection *nc, const char *path,
+                                  const char *host, const char *protocol,
+                                  const char *extra_headers, const char *user,
+                                  const char *pass) {
+  mg_send_websocket_handshake3v(nc, mg_mk_str(path), mg_mk_str(host),
+                                mg_mk_str(protocol), mg_mk_str(extra_headers),
+                                mg_mk_str(user), mg_mk_str(pass));
+}
+
+void mg_send_websocket_handshake3v(struct mg_connection *nc,
+                                   const struct mg_str path,
+                                   const struct mg_str host,
+                                   const struct mg_str protocol,
+                                   const struct mg_str extra_headers,
+                                   const struct mg_str user,
+                                   const struct mg_str pass) {
+  struct mbuf auth;
+  char key[25];
+  uint32_t nonce[4];
+  nonce[0] = mg_ws_random_mask();
+  nonce[1] = mg_ws_random_mask();
+  nonce[2] = mg_ws_random_mask();
+  nonce[3] = mg_ws_random_mask();
+  mg_base64_encode((unsigned char *) &nonce, sizeof(nonce), key);
+
+  mbuf_init(&auth, 0);
+  if (user.len > 0) {
+    mg_basic_auth_header(user, pass, &auth);
+  }
+
+  /*
+   * NOTE: the  (auth.buf == NULL ? "" : auth.buf) is because cc3200 libc is
+   * broken: it doesn't like zero length to be passed to %.*s
+   * i.e. sprintf("f%.*so", (int)0, NULL), yields `f\0o`.
+   * because it handles NULL specially (and incorrectly).
+   */
+  mg_printf(nc,
+            "GET %.*s HTTP/1.1\r\n"
+            "Upgrade: websocket\r\n"
+            "Connection: Upgrade\r\n"
+            "%.*s"
+            "Sec-WebSocket-Version: 13\r\n"
+            "Sec-WebSocket-Key: %s\r\n",
+            (int) path.len, path.p, (int) auth.len,
+            (auth.buf == NULL ? "" : auth.buf), key);
+
+  /* TODO(mkm): take default hostname from http proto data if host == NULL */
+  if (host.len > 0) {
+    int host_len = (int) (path.p - host.p); /* Account for possible :PORT */
+    mg_printf(nc, "Host: %.*s\r\n", host_len, host.p);
+  }
+  if (protocol.len > 0) {
+    mg_printf(nc, "Sec-WebSocket-Protocol: %.*s\r\n", (int) protocol.len,
+              protocol.p);
+  }
+  if (extra_headers.len > 0) {
+    mg_printf(nc, "%.*s", (int) extra_headers.len, extra_headers.p);
+  }
+  mg_printf(nc, "\r\n");
+
+  mbuf_free(&auth);
+}
+
+void mg_send_websocket_handshake(struct mg_connection *nc, const char *path,
+                                 const char *extra_headers) {
+  struct mg_str null_str = MG_NULL_STR;
+  mg_send_websocket_handshake3v(
+      nc, mg_mk_str(path), null_str /* host */, null_str /* protocol */,
+      mg_mk_str(extra_headers), null_str /* user */, null_str /* pass */);
+}
+
+struct mg_connection *mg_connect_ws_opt(
+    struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
+    struct mg_connect_opts opts, const char *url, const char *protocol,
+    const char *extra_headers) {
+  struct mg_str null_str = MG_NULL_STR;
+  struct mg_str host = MG_NULL_STR, path = MG_NULL_STR, user_info = MG_NULL_STR;
+  struct mg_connection *nc =
+      mg_connect_http_base(mgr, MG_CB(ev_handler, user_data), opts, "http",
+                           "ws", "https", "wss", url, &path, &user_info, &host);
+  if (nc != NULL) {
+    mg_send_websocket_handshake3v(nc, path, host, mg_mk_str(protocol),
+                                  mg_mk_str(extra_headers), user_info,
+                                  null_str);
+  }
+  return nc;
+}
+
+struct mg_connection *mg_connect_ws(
+    struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
+    const char *url, const char *protocol, const char *extra_headers) {
+  struct mg_connect_opts opts;
+  memset(&opts, 0, sizeof(opts));
+  return mg_connect_ws_opt(mgr, MG_CB(ev_handler, user_data), opts, url,
+                           protocol, extra_headers);
+}
+#endif /* MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBSOCKET */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_util.c"
+#endif
+/*
+ * Copyright (c) 2014 Cesanta Software Limited
+ * All rights reserved
+ */
+
+/* Amalgamated: #include "common/cs_base64.h" */
+/* Amalgamated: #include "mg_internal.h" */
+/* Amalgamated: #include "mg_util.h" */
+
+/* For platforms with limited libc */
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+const char *mg_skip(const char *s, const char *end, const char *delims,
+                    struct mg_str *v) {
+  v->p = s;
+  while (s < end && strchr(delims, *(unsigned char *) s) == NULL) s++;
+  v->len = s - v->p;
+  while (s < end && strchr(delims, *(unsigned char *) s) != NULL) s++;
+  return s;
+}
+
+#if MG_ENABLE_FILESYSTEM && !defined(MG_USER_FILE_FUNCTIONS)
+int mg_stat(const char *path, cs_stat_t *st) {
+#ifdef _WIN32
+  wchar_t wpath[MG_MAX_PATH];
+  to_wchar(path, wpath, ARRAY_SIZE(wpath));
+  DBG(("[%ls] -> %d", wpath, _wstati64(wpath, st)));
+  return _wstati64(wpath, st);
+#else
+  return stat(path, st);
+#endif
+}
+
+FILE *mg_fopen(const char *path, const char *mode) {
+#ifdef _WIN32
+  wchar_t wpath[MG_MAX_PATH], wmode[10];
+  to_wchar(path, wpath, ARRAY_SIZE(wpath));
+  to_wchar(mode, wmode, ARRAY_SIZE(wmode));
+  return _wfopen(wpath, wmode);
+#else
+  return fopen(path, mode);
+#endif
+}
+
+int mg_open(const char *path, int flag, int mode) { /* LCOV_EXCL_LINE */
+#if defined(_WIN32) && !defined(WINCE)
+  wchar_t wpath[MG_MAX_PATH];
+  to_wchar(path, wpath, ARRAY_SIZE(wpath));
+  return _wopen(wpath, flag, mode);
+#else
+  return open(path, flag, mode); /* LCOV_EXCL_LINE */
+#endif
+}
+
+size_t mg_fread(void *ptr, size_t size, size_t count, FILE *f) {
+  return fread(ptr, size, count, f);
+}
+
+size_t mg_fwrite(const void *ptr, size_t size, size_t count, FILE *f) {
+  return fwrite(ptr, size, count, f);
+}
+#endif
+
+void mg_base64_encode(const unsigned char *src, int src_len, char *dst) {
+  cs_base64_encode(src, src_len, dst);
+}
+
+int mg_base64_decode(const unsigned char *s, int len, char *dst) {
+  return cs_base64_decode(s, len, dst, NULL);
+}
+
+#if MG_ENABLE_THREADS
+void *mg_start_thread(void *(*f)(void *), void *p) {
+#ifdef WINCE
+  return (void *) CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) f, p, 0, NULL);
+#elif defined(_WIN32)
+  return (void *) _beginthread((void(__cdecl *) (void *) ) f, 0, p);
+#else
+  pthread_t thread_id = (pthread_t) 0;
+  pthread_attr_t attr;
+
+  (void) pthread_attr_init(&attr);
+  (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+#if defined(MG_STACK_SIZE) && MG_STACK_SIZE > 1
+  (void) pthread_attr_setstacksize(&attr, MG_STACK_SIZE);
+#endif
+
+  pthread_create(&thread_id, &attr, f, p);
+  pthread_attr_destroy(&attr);
+
+  return (void *) thread_id;
+#endif
+}
+#endif /* MG_ENABLE_THREADS */
+
+/* Set close-on-exec bit for a given socket. */
+void mg_set_close_on_exec(sock_t sock) {
+#if defined(_WIN32) && !defined(WINCE)
+  (void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0);
+#elif defined(__unix__)
+  fcntl(sock, F_SETFD, FD_CLOEXEC);
+#else
+  (void) sock;
+#endif
+}
+
+int mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len,
+                        int flags) {
+  int is_v6;
+  if (buf == NULL || len <= 0) return 0;
+  memset(buf, 0, len);
+#if MG_ENABLE_IPV6
+  is_v6 = sa->sa.sa_family == AF_INET6;
+#else
+  is_v6 = 0;
+#endif
+  if (flags & MG_SOCK_STRINGIFY_IP) {
+#if MG_ENABLE_IPV6
+    const void *addr = NULL;
+    char *start = buf;
+    socklen_t capacity = len;
+    if (!is_v6) {
+      addr = &sa->sin.sin_addr;
+    } else {
+      addr = (void *) &sa->sin6.sin6_addr;
+      if (flags & MG_SOCK_STRINGIFY_PORT) {
+        *buf = '[';
+        start++;
+        capacity--;
+      }
+    }
+    if (inet_ntop(sa->sa.sa_family, addr, start, capacity) == NULL) {
+      goto cleanup;
+    }
+#elif defined(_WIN32) || MG_LWIP || (MG_NET_IF == MG_NET_IF_PIC32)
+    /* Only Windoze Vista (and newer) have inet_ntop() */
+    char *addr_str = inet_ntoa(sa->sin.sin_addr);
+    if (addr_str != NULL) {
+      strncpy(buf, inet_ntoa(sa->sin.sin_addr), len - 1);
+    } else {
+      goto cleanup;
+    }
+#else
+    if (inet_ntop(AF_INET, (void *) &sa->sin.sin_addr, buf, len - 1) == NULL) {
+      goto cleanup;
+    }
+#endif
+  }
+  if (flags & MG_SOCK_STRINGIFY_PORT) {
+    int port = ntohs(sa->sin.sin_port);
+    if (flags & MG_SOCK_STRINGIFY_IP) {
+      int buf_len = strlen(buf);
+      snprintf(buf + buf_len, len - (buf_len + 1), "%s:%d", (is_v6 ? "]" : ""),
+               port);
+    } else {
+      snprintf(buf, len, "%d", port);
+    }
+  }
+
+  return strlen(buf);
+
+cleanup:
+  *buf = '\0';
+  return 0;
+}
+
+int mg_conn_addr_to_str(struct mg_connection *nc, char *buf, size_t len,
+                        int flags) {
+  union socket_address sa;
+  memset(&sa, 0, sizeof(sa));
+  mg_if_get_conn_addr(nc, flags & MG_SOCK_STRINGIFY_REMOTE, &sa);
+  return mg_sock_addr_to_str(&sa, buf, len, flags);
+}
+
+#if MG_ENABLE_HEXDUMP
+static int mg_hexdump_n(const void *buf, int len, char *dst, int dst_len,
+                        int offset) {
+  const unsigned char *p = (const unsigned char *) buf;
+  char ascii[17] = "";
+  int i, idx, n = 0;
+
+  for (i = 0; i < len; i++) {
+    idx = i % 16;
+    if (idx == 0) {
+      if (i > 0) n += snprintf(dst + n, MAX(dst_len - n, 0), "  %s\n", ascii);
+      n += snprintf(dst + n, MAX(dst_len - n, 0), "%04x ", i + offset);
+    }
+    if (dst_len - n < 0) {
+      return n;
+    }
+    n += snprintf(dst + n, MAX(dst_len - n, 0), " %02x", p[i]);
+    ascii[idx] = p[i] < 0x20 || p[i] > 0x7e ? '.' : p[i];
+    ascii[idx + 1] = '\0';
+  }
+
+  while (i++ % 16) n += snprintf(dst + n, MAX(dst_len - n, 0), "%s", "   ");
+  n += snprintf(dst + n, MAX(dst_len - n, 0), "  %s\n", ascii);
+
+  return n;
+}
+
+int mg_hexdump(const void *buf, int len, char *dst, int dst_len) {
+  return mg_hexdump_n(buf, len, dst, dst_len, 0);
+}
+
+void mg_hexdumpf(FILE *fp, const void *buf, int len) {
+  char tmp[80];
+  int offset = 0, n;
+  while (len > 0) {
+    n = (len < 16 ? len : 16);
+    mg_hexdump_n(((const char *) buf) + offset, n, tmp, sizeof(tmp), offset);
+    fputs(tmp, fp);
+    offset += n;
+    len -= n;
+  }
+}
+
+void mg_hexdump_connection(struct mg_connection *nc, const char *path,
+                           const void *buf, int num_bytes, int ev) {
+  FILE *fp = NULL;
+  char src[60], dst[60];
+  const char *tag = NULL;
+  switch (ev) {
+    case MG_EV_RECV:
+      tag = "<-";
+      break;
+    case MG_EV_SEND:
+      tag = "->";
+      break;
+    case MG_EV_ACCEPT:
+      tag = " 0) {
+    mg_hexdumpf(fp, buf, num_bytes);
+  }
+  if (fp != stdout && fp != stderr) fclose(fp);
+}
+#endif
+
+int mg_is_big_endian(void) {
+  static const int n = 1;
+  /* TODO(mkm) use compiletime check with 4-byte char literal */
+  return ((char *) &n)[0] == 0;
+}
+
+DO_NOT_WARN_UNUSED MG_INTERNAL int mg_get_errno(void) {
+#ifndef WINCE
+  return errno;
+#else
+  /* TODO(alashkin): translate error codes? */
+  return GetLastError();
+#endif
+}
+
+void mg_mbuf_append_base64_putc(char ch, void *user_data) {
+  struct mbuf *mbuf = (struct mbuf *) user_data;
+  mbuf_append(mbuf, &ch, sizeof(ch));
+}
+
+void mg_mbuf_append_base64(struct mbuf *mbuf, const void *data, size_t len) {
+  struct cs_base64_ctx ctx;
+  cs_base64_init(&ctx, mg_mbuf_append_base64_putc, mbuf);
+  cs_base64_update(&ctx, (const char *) data, len);
+  cs_base64_finish(&ctx);
+}
+
+void mg_basic_auth_header(const struct mg_str user, const struct mg_str pass,
+                          struct mbuf *buf) {
+  const char *header_prefix = "Authorization: Basic ";
+  const char *header_suffix = "\r\n";
+
+  struct cs_base64_ctx ctx;
+  cs_base64_init(&ctx, mg_mbuf_append_base64_putc, buf);
+
+  mbuf_append(buf, header_prefix, strlen(header_prefix));
+
+  cs_base64_update(&ctx, user.p, user.len);
+  if (pass.len > 0) {
+    cs_base64_update(&ctx, ":", 1);
+    cs_base64_update(&ctx, pass.p, pass.len);
+  }
+  cs_base64_finish(&ctx);
+  mbuf_append(buf, header_suffix, strlen(header_suffix));
+}
+
+struct mg_str mg_url_encode(const struct mg_str src) {
+  static const char *dont_escape = "._-$,;~()/";
+  static const char *hex = "0123456789abcdef";
+  size_t i = 0;
+  struct mbuf mb;
+  mbuf_init(&mb, src.len);
+
+  for (i = 0; i < src.len; i++) {
+    const unsigned char c = *((const unsigned char *) src.p + i);
+    if (isalnum(c) || strchr(dont_escape, c) != NULL) {
+      mbuf_append(&mb, &c, 1);
+    } else {
+      mbuf_append(&mb, "%", 1);
+      mbuf_append(&mb, &hex[c >> 4], 1);
+      mbuf_append(&mb, &hex[c & 15], 1);
+    }
+  }
+  mbuf_append(&mb, "", 1);
+  mbuf_trim(&mb);
+  return mg_mk_str_n(mb.buf, mb.len - 1);
+}
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_mqtt.c"
+#endif
+/*
+ * Copyright (c) 2014 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#if MG_ENABLE_MQTT
+
+#include 
+
+/* Amalgamated: #include "mg_internal.h" */
+/* Amalgamated: #include "mg_mqtt.h" */
+
+static uint16_t getu16(const char *p) {
+  const uint8_t *up = (const uint8_t *) p;
+  return (up[0] << 8) + up[1];
+}
+
+static const char *scanto(const char *p, struct mg_str *s) {
+  s->len = getu16(p);
+  s->p = p + 2;
+  return s->p + s->len;
+}
+
+MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
+  uint8_t header;
+  size_t len = 0, len_len = 0;
+  const char *p, *end;
+  unsigned char lc = 0;
+  int cmd;
+
+  if (io->len < 2) return MG_MQTT_ERROR_INCOMPLETE_MSG;
+  header = io->buf[0];
+  cmd = header >> 4;
+
+  /* decode mqtt variable length */
+  len = len_len = 0;
+  p = io->buf + 1;
+  while ((size_t)(p - io->buf) < io->len) {
+    lc = *((const unsigned char *) p++);
+    len += (lc & 0x7f) << 7 * len_len;
+    len_len++;
+    if (!(lc & 0x80)) break;
+    if (len_len > 4) return MG_MQTT_ERROR_MALFORMED_MSG;
+  }
+
+  end = p + len;
+  if (lc & 0x80 || len > (io->len - (p - io->buf))) {
+    return MG_MQTT_ERROR_INCOMPLETE_MSG;
+  }
+
+  mm->cmd = cmd;
+  mm->qos = MG_MQTT_GET_QOS(header);
+
+  switch (cmd) {
+    case MG_MQTT_CMD_CONNECT: {
+      p = scanto(p, &mm->protocol_name);
+      if (p > end - 4) return MG_MQTT_ERROR_MALFORMED_MSG;
+      mm->protocol_version = *(uint8_t *) p++;
+      mm->connect_flags = *(uint8_t *) p++;
+      mm->keep_alive_timer = getu16(p);
+      p += 2;
+      if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG;
+      p = scanto(p, &mm->client_id);
+      if (p > end) return MG_MQTT_ERROR_MALFORMED_MSG;
+      if (mm->connect_flags & MG_MQTT_HAS_WILL) {
+        if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG;
+        p = scanto(p, &mm->will_topic);
+      }
+      if (mm->connect_flags & MG_MQTT_HAS_WILL) {
+        if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG;
+        p = scanto(p, &mm->will_message);
+      }
+      if (mm->connect_flags & MG_MQTT_HAS_USER_NAME) {
+        if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG;
+        p = scanto(p, &mm->user_name);
+      }
+      if (mm->connect_flags & MG_MQTT_HAS_PASSWORD) {
+        if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG;
+        p = scanto(p, &mm->password);
+      }
+      if (p != end) return MG_MQTT_ERROR_MALFORMED_MSG;
+
+      LOG(LL_DEBUG,
+          ("%d %2x %d proto [%.*s] client_id [%.*s] will_topic [%.*s] "
+           "will_msg [%.*s] user_name [%.*s] password [%.*s]",
+           (int) len, (int) mm->connect_flags, (int) mm->keep_alive_timer,
+           (int) mm->protocol_name.len, mm->protocol_name.p,
+           (int) mm->client_id.len, mm->client_id.p, (int) mm->will_topic.len,
+           mm->will_topic.p, (int) mm->will_message.len, mm->will_message.p,
+           (int) mm->user_name.len, mm->user_name.p, (int) mm->password.len,
+           mm->password.p));
+      break;
+    }
+    case MG_MQTT_CMD_CONNACK:
+      if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG;
+      mm->connack_ret_code = p[1];
+      break;
+    case MG_MQTT_CMD_PUBACK:
+    case MG_MQTT_CMD_PUBREC:
+    case MG_MQTT_CMD_PUBREL:
+    case MG_MQTT_CMD_PUBCOMP:
+    case MG_MQTT_CMD_SUBACK:
+      mm->message_id = getu16(p);
+      break;
+    case MG_MQTT_CMD_PUBLISH: {
+      p = scanto(p, &mm->topic);
+      if (p > end) return MG_MQTT_ERROR_MALFORMED_MSG;
+      if (mm->qos > 0) {
+        if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG;
+        mm->message_id = getu16(p);
+        p += 2;
+      }
+      mm->payload.p = p;
+      mm->payload.len = end - p;
+      break;
+    }
+    case MG_MQTT_CMD_SUBSCRIBE:
+      if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG;
+      mm->message_id = getu16(p);
+      p += 2;
+      /*
+       * topic expressions are left in the payload and can be parsed with
+       * `mg_mqtt_next_subscribe_topic`
+       */
+      mm->payload.p = p;
+      mm->payload.len = end - p;
+      break;
+    default:
+      /* Unhandled command */
+      break;
+  }
+
+  mm->len = end - io->buf;
+  return mm->len;
+}
+
+static void mqtt_handler(struct mg_connection *nc, int ev,
+                         void *ev_data MG_UD_ARG(void *user_data)) {
+  struct mbuf *io = &nc->recv_mbuf;
+  struct mg_mqtt_message mm;
+  memset(&mm, 0, sizeof(mm));
+
+  nc->handler(nc, ev, ev_data MG_UD_ARG(user_data));
+
+  switch (ev) {
+    case MG_EV_ACCEPT:
+      if (nc->proto_data == NULL) mg_set_protocol_mqtt(nc);
+      break;
+    case MG_EV_RECV: {
+      /* There can be multiple messages in the buffer, process them all. */
+      while (1) {
+        int len = parse_mqtt(io, &mm);
+        if (len < 0) {
+          if (len == MG_MQTT_ERROR_MALFORMED_MSG) {
+            /* Protocol error. */
+            nc->flags |= MG_F_CLOSE_IMMEDIATELY;
+          } else if (len == MG_MQTT_ERROR_INCOMPLETE_MSG) {
+            /* Not fully buffered, let's check if we have a chance to get more
+             * data later */
+            if (nc->recv_mbuf_limit > 0 &&
+                nc->recv_mbuf.len >= nc->recv_mbuf_limit) {
+              LOG(LL_ERROR, ("%p recv buffer (%lu bytes) exceeds the limit "
+                             "%lu bytes, and not drained, closing",
+                             nc, (unsigned long) nc->recv_mbuf.len,
+                             (unsigned long) nc->recv_mbuf_limit));
+              nc->flags |= MG_F_CLOSE_IMMEDIATELY;
+            }
+          } else {
+            /* Should never be here */
+            LOG(LL_ERROR, ("%p invalid len: %d, closing", nc, len));
+            nc->flags |= MG_F_CLOSE_IMMEDIATELY;
+          }
+          break;
+        }
+
+        nc->handler(nc, MG_MQTT_EVENT_BASE + mm.cmd, &mm MG_UD_ARG(user_data));
+        mbuf_remove(io, len);
+      }
+      break;
+    }
+    case MG_EV_POLL: {
+      struct mg_mqtt_proto_data *pd =
+          (struct mg_mqtt_proto_data *) nc->proto_data;
+      double now = mg_time();
+      if (pd->keep_alive > 0 && pd->last_control_time > 0 &&
+          (now - pd->last_control_time) > pd->keep_alive) {
+        LOG(LL_DEBUG, ("Send PINGREQ"));
+        mg_mqtt_ping(nc);
+      }
+      break;
+    }
+  }
+}
+
+static void mg_mqtt_proto_data_destructor(void *proto_data) {
+  MG_FREE(proto_data);
+}
+
+int mg_mqtt_match_topic_expression(struct mg_str exp, struct mg_str topic) {
+  /* TODO(mkm): implement real matching */
+  if (memchr(exp.p, '#', exp.len)) {
+    /* exp `foo/#` will become `foo/` */
+    exp.len -= 1;
+    /*
+     * topic should be longer than the expression: e.g. topic `foo/bar` does
+     * match `foo/#`, but neither `foo` nor `foo/` do.
+     */
+    if (topic.len <= exp.len) {
+      return 0;
+    }
+
+    /* Truncate topic so that it'll pass the next length check */
+    topic.len = exp.len;
+  }
+  if (topic.len != exp.len) {
+    return 0;
+  }
+  return strncmp(topic.p, exp.p, exp.len) == 0;
+}
+
+int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic) {
+  return mg_mqtt_match_topic_expression(mg_mk_str(exp), topic);
+}
+
+void mg_set_protocol_mqtt(struct mg_connection *nc) {
+  nc->proto_handler = mqtt_handler;
+  nc->proto_data = MG_CALLOC(1, sizeof(struct mg_mqtt_proto_data));
+  nc->proto_data_destructor = mg_mqtt_proto_data_destructor;
+}
+
+static void mg_mqtt_prepend_header(struct mg_connection *nc, uint8_t cmd,
+                                   uint8_t flags, size_t len) {
+  struct mg_mqtt_proto_data *pd = (struct mg_mqtt_proto_data *) nc->proto_data;
+  size_t off = nc->send_mbuf.len - len;
+  uint8_t header = cmd << 4 | (uint8_t) flags;
+
+  uint8_t buf[1 + sizeof(size_t)];
+  uint8_t *vlen = &buf[1];
+
+  assert(nc->send_mbuf.len >= len);
+
+  buf[0] = header;
+
+  /* mqtt variable length encoding */
+  do {
+    *vlen = len % 0x80;
+    len /= 0x80;
+    if (len > 0) *vlen |= 0x80;
+    vlen++;
+  } while (len > 0);
+
+  mbuf_insert(&nc->send_mbuf, off, buf, vlen - buf);
+  pd->last_control_time = mg_time();
+}
+
+void mg_send_mqtt_handshake(struct mg_connection *nc, const char *client_id) {
+  static struct mg_send_mqtt_handshake_opts opts;
+  mg_send_mqtt_handshake_opt(nc, client_id, opts);
+}
+
+void mg_send_mqtt_handshake_opt(struct mg_connection *nc, const char *client_id,
+                                struct mg_send_mqtt_handshake_opts opts) {
+  uint16_t hlen, nlen, rem_len = 0;
+  struct mg_mqtt_proto_data *pd = (struct mg_mqtt_proto_data *) nc->proto_data;
+
+  mg_send(nc, "\00\04MQTT\04", 7);
+  rem_len += 7;
+
+  if (opts.user_name != NULL) {
+    opts.flags |= MG_MQTT_HAS_USER_NAME;
+  }
+  if (opts.password != NULL) {
+    opts.flags |= MG_MQTT_HAS_PASSWORD;
+  }
+  if (opts.will_topic != NULL && opts.will_message != NULL) {
+    opts.flags |= MG_MQTT_HAS_WILL;
+  }
+  if (opts.keep_alive == 0) {
+    opts.keep_alive = 60;
+  }
+
+  mg_send(nc, &opts.flags, 1);
+  rem_len += 1;
+
+  nlen = htons(opts.keep_alive);
+  mg_send(nc, &nlen, 2);
+  rem_len += 2;
+
+  hlen = strlen(client_id);
+  nlen = htons((uint16_t) hlen);
+  mg_send(nc, &nlen, 2);
+  mg_send(nc, client_id, hlen);
+  rem_len += 2 + hlen;
+
+  if (opts.flags & MG_MQTT_HAS_WILL) {
+    hlen = strlen(opts.will_topic);
+    nlen = htons((uint16_t) hlen);
+    mg_send(nc, &nlen, 2);
+    mg_send(nc, opts.will_topic, hlen);
+    rem_len += 2 + hlen;
+
+    hlen = strlen(opts.will_message);
+    nlen = htons((uint16_t) hlen);
+    mg_send(nc, &nlen, 2);
+    mg_send(nc, opts.will_message, hlen);
+    rem_len += 2 + hlen;
+  }
+
+  if (opts.flags & MG_MQTT_HAS_USER_NAME) {
+    hlen = strlen(opts.user_name);
+    nlen = htons((uint16_t) hlen);
+    mg_send(nc, &nlen, 2);
+    mg_send(nc, opts.user_name, hlen);
+    rem_len += 2 + hlen;
+  }
+  if (opts.flags & MG_MQTT_HAS_PASSWORD) {
+    hlen = strlen(opts.password);
+    nlen = htons((uint16_t) hlen);
+    mg_send(nc, &nlen, 2);
+    mg_send(nc, opts.password, hlen);
+    rem_len += 2 + hlen;
+  }
+
+  mg_mqtt_prepend_header(nc, MG_MQTT_CMD_CONNECT, 0, rem_len);
+
+  if (pd != NULL) {
+    pd->keep_alive = opts.keep_alive;
+  }
+}
+
+void mg_mqtt_publish(struct mg_connection *nc, const char *topic,
+                     uint16_t message_id, int flags, const void *data,
+                     size_t len) {
+  size_t old_len = nc->send_mbuf.len;
+
+  uint16_t topic_len = htons((uint16_t) strlen(topic));
+  uint16_t message_id_net = htons(message_id);
+
+  mg_send(nc, &topic_len, 2);
+  mg_send(nc, topic, strlen(topic));
+  if (MG_MQTT_GET_QOS(flags) > 0) {
+    mg_send(nc, &message_id_net, 2);
+  }
+  mg_send(nc, data, len);
+
+  mg_mqtt_prepend_header(nc, MG_MQTT_CMD_PUBLISH, flags,
+                         nc->send_mbuf.len - old_len);
+}
+
+void mg_mqtt_subscribe(struct mg_connection *nc,
+                       const struct mg_mqtt_topic_expression *topics,
+                       size_t topics_len, uint16_t message_id) {
+  size_t old_len = nc->send_mbuf.len;
+
+  uint16_t message_id_n = htons(message_id);
+  size_t i;
+
+  mg_send(nc, (char *) &message_id_n, 2);
+  for (i = 0; i < topics_len; i++) {
+    uint16_t topic_len_n = htons((uint16_t) strlen(topics[i].topic));
+    mg_send(nc, &topic_len_n, 2);
+    mg_send(nc, topics[i].topic, strlen(topics[i].topic));
+    mg_send(nc, &topics[i].qos, 1);
+  }
+
+  mg_mqtt_prepend_header(nc, MG_MQTT_CMD_SUBSCRIBE, MG_MQTT_QOS(1),
+                         nc->send_mbuf.len - old_len);
+}
+
+int mg_mqtt_next_subscribe_topic(struct mg_mqtt_message *msg,
+                                 struct mg_str *topic, uint8_t *qos, int pos) {
+  unsigned char *buf = (unsigned char *) msg->payload.p + pos;
+  int new_pos;
+
+  if ((size_t) pos >= msg->payload.len) return -1;
+
+  topic->len = buf[0] << 8 | buf[1];
+  topic->p = (char *) buf + 2;
+  new_pos = pos + 2 + topic->len + 1;
+  if ((size_t) new_pos > msg->payload.len) return -1;
+  *qos = buf[2 + topic->len];
+  return new_pos;
+}
+
+void mg_mqtt_unsubscribe(struct mg_connection *nc, char **topics,
+                         size_t topics_len, uint16_t message_id) {
+  size_t old_len = nc->send_mbuf.len;
+
+  uint16_t message_id_n = htons(message_id);
+  size_t i;
+
+  mg_send(nc, (char *) &message_id_n, 2);
+  for (i = 0; i < topics_len; i++) {
+    uint16_t topic_len_n = htons((uint16_t) strlen(topics[i]));
+    mg_send(nc, &topic_len_n, 2);
+    mg_send(nc, topics[i], strlen(topics[i]));
+  }
+
+  mg_mqtt_prepend_header(nc, MG_MQTT_CMD_UNSUBSCRIBE, MG_MQTT_QOS(1),
+                         nc->send_mbuf.len - old_len);
+}
+
+void mg_mqtt_connack(struct mg_connection *nc, uint8_t return_code) {
+  uint8_t unused = 0;
+  mg_send(nc, &unused, 1);
+  mg_send(nc, &return_code, 1);
+  mg_mqtt_prepend_header(nc, MG_MQTT_CMD_CONNACK, 0, 2);
+}
+
+/*
+ * Sends a command which contains only a `message_id` and a QoS level of 1.
+ *
+ * Helper function.
+ */
+static void mg_send_mqtt_short_command(struct mg_connection *nc, uint8_t cmd,
+                                       uint16_t message_id) {
+  uint16_t message_id_net = htons(message_id);
+  uint8_t flags = (cmd == MG_MQTT_CMD_PUBREL ? 2 : 0);
+  mg_send(nc, &message_id_net, 2);
+  mg_mqtt_prepend_header(nc, cmd, flags, 2 /* len */);
+}
+
+void mg_mqtt_puback(struct mg_connection *nc, uint16_t message_id) {
+  mg_send_mqtt_short_command(nc, MG_MQTT_CMD_PUBACK, message_id);
+}
+
+void mg_mqtt_pubrec(struct mg_connection *nc, uint16_t message_id) {
+  mg_send_mqtt_short_command(nc, MG_MQTT_CMD_PUBREC, message_id);
+}
+
+void mg_mqtt_pubrel(struct mg_connection *nc, uint16_t message_id) {
+  mg_send_mqtt_short_command(nc, MG_MQTT_CMD_PUBREL, message_id);
+}
+
+void mg_mqtt_pubcomp(struct mg_connection *nc, uint16_t message_id) {
+  mg_send_mqtt_short_command(nc, MG_MQTT_CMD_PUBCOMP, message_id);
+}
+
+void mg_mqtt_suback(struct mg_connection *nc, uint8_t *qoss, size_t qoss_len,
+                    uint16_t message_id) {
+  size_t i;
+  uint16_t message_id_net = htons(message_id);
+  mg_send(nc, &message_id_net, 2);
+  for (i = 0; i < qoss_len; i++) {
+    mg_send(nc, &qoss[i], 1);
+  }
+  mg_mqtt_prepend_header(nc, MG_MQTT_CMD_SUBACK, MG_MQTT_QOS(1), 2 + qoss_len);
+}
+
+void mg_mqtt_unsuback(struct mg_connection *nc, uint16_t message_id) {
+  mg_send_mqtt_short_command(nc, MG_MQTT_CMD_UNSUBACK, message_id);
+}
+
+void mg_mqtt_ping(struct mg_connection *nc) {
+  mg_mqtt_prepend_header(nc, MG_MQTT_CMD_PINGREQ, 0, 0);
+}
+
+void mg_mqtt_pong(struct mg_connection *nc) {
+  mg_mqtt_prepend_header(nc, MG_MQTT_CMD_PINGRESP, 0, 0);
+}
+
+void mg_mqtt_disconnect(struct mg_connection *nc) {
+  mg_mqtt_prepend_header(nc, MG_MQTT_CMD_DISCONNECT, 0, 0);
+}
+
+#endif /* MG_ENABLE_MQTT */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_mqtt_server.c"
+#endif
+/*
+ * Copyright (c) 2014 Cesanta Software Limited
+ * All rights reserved
+ */
+
+/* Amalgamated: #include "mg_internal.h" */
+/* Amalgamated: #include "mg_mqtt_server.h" */
+
+#if MG_ENABLE_MQTT_BROKER
+
+static void mg_mqtt_session_init(struct mg_mqtt_broker *brk,
+                                 struct mg_mqtt_session *s,
+                                 struct mg_connection *nc) {
+  s->brk = brk;
+  s->subscriptions = NULL;
+  s->num_subscriptions = 0;
+  s->nc = nc;
+}
+
+static void mg_mqtt_add_session(struct mg_mqtt_session *s) {
+  LIST_INSERT_HEAD(&s->brk->sessions, s, link);
+}
+
+static void mg_mqtt_remove_session(struct mg_mqtt_session *s) {
+  LIST_REMOVE(s, link);
+}
+
+static void mg_mqtt_destroy_session(struct mg_mqtt_session *s) {
+  size_t i;
+  for (i = 0; i < s->num_subscriptions; i++) {
+    MG_FREE((void *) s->subscriptions[i].topic);
+  }
+  MG_FREE(s->subscriptions);
+  MG_FREE(s);
+}
+
+static void mg_mqtt_close_session(struct mg_mqtt_session *s) {
+  mg_mqtt_remove_session(s);
+  mg_mqtt_destroy_session(s);
+}
+
+void mg_mqtt_broker_init(struct mg_mqtt_broker *brk, void *user_data) {
+  LIST_INIT(&brk->sessions);
+  brk->user_data = user_data;
+}
+
+static void mg_mqtt_broker_handle_connect(struct mg_mqtt_broker *brk,
+                                          struct mg_connection *nc) {
+  struct mg_mqtt_session *s =
+      (struct mg_mqtt_session *) MG_CALLOC(1, sizeof *s);
+  if (s == NULL) {
+    /* LCOV_EXCL_START */
+    mg_mqtt_connack(nc, MG_EV_MQTT_CONNACK_SERVER_UNAVAILABLE);
+    return;
+    /* LCOV_EXCL_STOP */
+  }
+
+  /* TODO(mkm): check header (magic and version) */
+
+  mg_mqtt_session_init(brk, s, nc);
+  nc->priv_2 = s;
+  mg_mqtt_add_session(s);
+
+  mg_mqtt_connack(nc, MG_EV_MQTT_CONNACK_ACCEPTED);
+}
+
+static void mg_mqtt_broker_handle_subscribe(struct mg_connection *nc,
+                                            struct mg_mqtt_message *msg) {
+  struct mg_mqtt_session *ss = (struct mg_mqtt_session *) nc->priv_2;
+  uint8_t qoss[MG_MQTT_MAX_SESSION_SUBSCRIPTIONS];
+  size_t num_subs = 0;
+  struct mg_str topic;
+  uint8_t qos;
+  int pos;
+  struct mg_mqtt_topic_expression *te;
+
+  for (pos = 0;
+       (pos = mg_mqtt_next_subscribe_topic(msg, &topic, &qos, pos)) != -1;) {
+    if (num_subs >= sizeof(MG_MQTT_MAX_SESSION_SUBSCRIPTIONS) ||
+        (ss->num_subscriptions + num_subs >=
+         MG_MQTT_MAX_SESSION_SUBSCRIPTIONS)) {
+      nc->flags |= MG_F_CLOSE_IMMEDIATELY;
+      return;
+    }
+    qoss[num_subs++] = qos;
+  }
+
+  if (num_subs > 0) {
+    te = (struct mg_mqtt_topic_expression *) MG_REALLOC(
+        ss->subscriptions,
+        sizeof(*ss->subscriptions) * (ss->num_subscriptions + num_subs));
+    if (te == NULL) {
+      nc->flags |= MG_F_CLOSE_IMMEDIATELY;
+      return;
+    }
+    ss->subscriptions = te;
+    for (pos = 0;
+         pos < (int) msg->payload.len &&
+             (pos = mg_mqtt_next_subscribe_topic(msg, &topic, &qos, pos)) != -1;
+         ss->num_subscriptions++) {
+      te = &ss->subscriptions[ss->num_subscriptions];
+      te->topic = (char *) MG_MALLOC(topic.len + 1);
+      te->qos = qos;
+      memcpy((char *) te->topic, topic.p, topic.len);
+      ((char *) te->topic)[topic.len] = '\0';
+    }
+  }
+
+  if (pos == (int) msg->payload.len) {
+    mg_mqtt_suback(nc, qoss, num_subs, msg->message_id);
+  } else {
+    /* We did not fully parse the payload, something must be wrong. */
+    nc->flags |= MG_F_CLOSE_IMMEDIATELY;
+  }
+}
+
+static void mg_mqtt_broker_handle_publish(struct mg_mqtt_broker *brk,
+                                          struct mg_mqtt_message *msg) {
+  struct mg_mqtt_session *s;
+  size_t i;
+
+  for (s = mg_mqtt_next(brk, NULL); s != NULL; s = mg_mqtt_next(brk, s)) {
+    for (i = 0; i < s->num_subscriptions; i++) {
+      if (mg_mqtt_vmatch_topic_expression(s->subscriptions[i].topic,
+                                          msg->topic)) {
+        char buf[100], *p = buf;
+        mg_asprintf(&p, sizeof(buf), "%.*s", (int) msg->topic.len,
+                    msg->topic.p);
+        if (p == NULL) {
+          return;
+        }
+        mg_mqtt_publish(s->nc, p, 0, 0, msg->payload.p, msg->payload.len);
+        if (p != buf) {
+          MG_FREE(p);
+        }
+        break;
+      }
+    }
+  }
+}
+
+void mg_mqtt_broker(struct mg_connection *nc, int ev, void *data) {
+  struct mg_mqtt_message *msg = (struct mg_mqtt_message *) data;
+  struct mg_mqtt_broker *brk;
+
+  if (nc->listener) {
+    brk = (struct mg_mqtt_broker *) nc->listener->priv_2;
+  } else {
+    brk = (struct mg_mqtt_broker *) nc->priv_2;
+  }
+
+  switch (ev) {
+    case MG_EV_ACCEPT:
+      if (nc->proto_data == NULL) mg_set_protocol_mqtt(nc);
+      nc->priv_2 = NULL; /* Clear up the inherited pointer to broker */
+      break;
+    case MG_EV_MQTT_CONNECT:
+      if (nc->priv_2 == NULL) {
+        mg_mqtt_broker_handle_connect(brk, nc);
+      } else {
+        /* Repeated CONNECT */
+        nc->flags |= MG_F_CLOSE_IMMEDIATELY;
+      }
+      break;
+    case MG_EV_MQTT_SUBSCRIBE:
+      if (nc->priv_2 != NULL) {
+        mg_mqtt_broker_handle_subscribe(nc, msg);
+      } else {
+        /* Subscribe before CONNECT */
+        nc->flags |= MG_F_CLOSE_IMMEDIATELY;
+      }
+      break;
+    case MG_EV_MQTT_PUBLISH:
+      if (nc->priv_2 != NULL) {
+        mg_mqtt_broker_handle_publish(brk, msg);
+      } else {
+        /* Publish before CONNECT */
+        nc->flags |= MG_F_CLOSE_IMMEDIATELY;
+      }
+      break;
+    case MG_EV_CLOSE:
+      if (nc->listener && nc->priv_2 != NULL) {
+        mg_mqtt_close_session((struct mg_mqtt_session *) nc->priv_2);
+      }
+      break;
+  }
+}
+
+struct mg_mqtt_session *mg_mqtt_next(struct mg_mqtt_broker *brk,
+                                     struct mg_mqtt_session *s) {
+  return s == NULL ? LIST_FIRST(&brk->sessions) : LIST_NEXT(s, link);
+}
+
+#endif /* MG_ENABLE_MQTT_BROKER */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_dns.c"
+#endif
+/*
+ * Copyright (c) 2014 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#if MG_ENABLE_DNS
+
+/* Amalgamated: #include "mg_internal.h" */
+/* Amalgamated: #include "mg_dns.h" */
+
+static int mg_dns_tid = 0xa0;
+
+struct mg_dns_header {
+  uint16_t transaction_id;
+  uint16_t flags;
+  uint16_t num_questions;
+  uint16_t num_answers;
+  uint16_t num_authority_prs;
+  uint16_t num_other_prs;
+};
+
+struct mg_dns_resource_record *mg_dns_next_record(
+    struct mg_dns_message *msg, int query,
+    struct mg_dns_resource_record *prev) {
+  struct mg_dns_resource_record *rr;
+
+  for (rr = (prev == NULL ? msg->answers : prev + 1);
+       rr - msg->answers < msg->num_answers; rr++) {
+    if (rr->rtype == query) {
+      return rr;
+    }
+  }
+  return NULL;
+}
+
+int mg_dns_parse_record_data(struct mg_dns_message *msg,
+                             struct mg_dns_resource_record *rr, void *data,
+                             size_t data_len) {
+  switch (rr->rtype) {
+    case MG_DNS_A_RECORD:
+      if (data_len < sizeof(struct in_addr)) {
+        return -1;
+      }
+      if (rr->rdata.p + data_len > msg->pkt.p + msg->pkt.len) {
+        return -1;
+      }
+      memcpy(data, rr->rdata.p, data_len);
+      return 0;
+#if MG_ENABLE_IPV6
+    case MG_DNS_AAAA_RECORD:
+      if (data_len < sizeof(struct in6_addr)) {
+        return -1; /* LCOV_EXCL_LINE */
+      }
+      memcpy(data, rr->rdata.p, data_len);
+      return 0;
+#endif
+    case MG_DNS_CNAME_RECORD:
+      mg_dns_uncompress_name(msg, &rr->rdata, (char *) data, data_len);
+      return 0;
+  }
+
+  return -1;
+}
+
+int mg_dns_insert_header(struct mbuf *io, size_t pos,
+                         struct mg_dns_message *msg) {
+  struct mg_dns_header header;
+
+  memset(&header, 0, sizeof(header));
+  header.transaction_id = msg->transaction_id;
+  header.flags = htons(msg->flags);
+  header.num_questions = htons(msg->num_questions);
+  header.num_answers = htons(msg->num_answers);
+
+  return mbuf_insert(io, pos, &header, sizeof(header));
+}
+
+int mg_dns_copy_questions(struct mbuf *io, struct mg_dns_message *msg) {
+  unsigned char *begin, *end;
+  struct mg_dns_resource_record *last_q;
+  if (msg->num_questions <= 0) return 0;
+  begin = (unsigned char *) msg->pkt.p + sizeof(struct mg_dns_header);
+  last_q = &msg->questions[msg->num_questions - 1];
+  end = (unsigned char *) last_q->name.p + last_q->name.len + 4;
+  return mbuf_append(io, begin, end - begin);
+}
+
+int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len) {
+  const char *s;
+  unsigned char n;
+  size_t pos = io->len;
+
+  do {
+    if ((s = strchr(name, '.')) == NULL) {
+      s = name + len;
+    }
+
+    if (s - name > 127) {
+      return -1; /* TODO(mkm) cover */
+    }
+    n = s - name;           /* chunk length */
+    mbuf_append(io, &n, 1); /* send length */
+    mbuf_append(io, name, n);
+
+    if (*s == '.') {
+      n++;
+    }
+
+    name += n;
+    len -= n;
+  } while (*s != '\0');
+  mbuf_append(io, "\0", 1); /* Mark end of host name */
+
+  return io->len - pos;
+}
+
+int mg_dns_encode_record(struct mbuf *io, struct mg_dns_resource_record *rr,
+                         const char *name, size_t nlen, const void *rdata,
+                         size_t rlen) {
+  size_t pos = io->len;
+  uint16_t u16;
+  uint32_t u32;
+
+  if (rr->kind == MG_DNS_INVALID_RECORD) {
+    return -1; /* LCOV_EXCL_LINE */
+  }
+
+  if (mg_dns_encode_name(io, name, nlen) == -1) {
+    return -1;
+  }
+
+  u16 = htons(rr->rtype);
+  mbuf_append(io, &u16, 2);
+  u16 = htons(rr->rclass);
+  mbuf_append(io, &u16, 2);
+
+  if (rr->kind == MG_DNS_ANSWER) {
+    u32 = htonl(rr->ttl);
+    mbuf_append(io, &u32, 4);
+
+    if (rr->rtype == MG_DNS_CNAME_RECORD) {
+      int clen;
+      /* fill size after encoding */
+      size_t off = io->len;
+      mbuf_append(io, &u16, 2);
+      if ((clen = mg_dns_encode_name(io, (const char *) rdata, rlen)) == -1) {
+        return -1;
+      }
+      u16 = clen;
+      io->buf[off] = u16 >> 8;
+      io->buf[off + 1] = u16 & 0xff;
+    } else {
+      u16 = htons((uint16_t) rlen);
+      mbuf_append(io, &u16, 2);
+      mbuf_append(io, rdata, rlen);
+    }
+  }
+
+  return io->len - pos;
+}
+
+void mg_send_dns_query(struct mg_connection *nc, const char *name,
+                       int query_type) {
+  struct mg_dns_message *msg =
+      (struct mg_dns_message *) MG_CALLOC(1, sizeof(*msg));
+  struct mbuf pkt;
+  struct mg_dns_resource_record *rr = &msg->questions[0];
+
+  DBG(("%s %d", name, query_type));
+
+  mbuf_init(&pkt, 64 /* Start small, it'll grow as needed. */);
+
+  msg->transaction_id = ++mg_dns_tid;
+  msg->flags = 0x100;
+  msg->num_questions = 1;
+
+  mg_dns_insert_header(&pkt, 0, msg);
+
+  rr->rtype = query_type;
+  rr->rclass = 1; /* Class: inet */
+  rr->kind = MG_DNS_QUESTION;
+
+  if (mg_dns_encode_record(&pkt, rr, name, strlen(name), NULL, 0) == -1) {
+    /* TODO(mkm): return an error code */
+    goto cleanup; /* LCOV_EXCL_LINE */
+  }
+
+  /* TCP DNS requires messages to be prefixed with len */
+  if (!(nc->flags & MG_F_UDP)) {
+    uint16_t len = htons((uint16_t) pkt.len);
+    mbuf_insert(&pkt, 0, &len, 2);
+  }
+
+  mg_send(nc, pkt.buf, pkt.len);
+  mbuf_free(&pkt);
+
+cleanup:
+  MG_FREE(msg);
+}
+
+static unsigned char *mg_parse_dns_resource_record(
+    unsigned char *data, unsigned char *end, struct mg_dns_resource_record *rr,
+    int reply) {
+  unsigned char *name = data;
+  int chunk_len, data_len;
+
+  while (data < end && (chunk_len = *data)) {
+    if (((unsigned char *) data)[0] & 0xc0) {
+      data += 1;
+      break;
+    }
+    data += chunk_len + 1;
+  }
+
+  if (data > end - 5) {
+    return NULL;
+  }
+
+  rr->name.p = (char *) name;
+  rr->name.len = data - name + 1;
+  data++;
+
+  rr->rtype = data[0] << 8 | data[1];
+  data += 2;
+
+  rr->rclass = data[0] << 8 | data[1];
+  data += 2;
+
+  rr->kind = reply ? MG_DNS_ANSWER : MG_DNS_QUESTION;
+  if (reply) {
+    if (data >= end - 6) {
+      return NULL;
+    }
+
+    rr->ttl = (uint32_t) data[0] << 24 | (uint32_t) data[1] << 16 |
+              data[2] << 8 | data[3];
+    data += 4;
+
+    data_len = *data << 8 | *(data + 1);
+    data += 2;
+
+    rr->rdata.p = (char *) data;
+    rr->rdata.len = data_len;
+    data += data_len;
+  }
+  return data;
+}
+
+int mg_parse_dns(const char *buf, int len, struct mg_dns_message *msg) {
+  struct mg_dns_header *header = (struct mg_dns_header *) buf;
+  unsigned char *data = (unsigned char *) buf + sizeof(*header);
+  unsigned char *end = (unsigned char *) buf + len;
+  int i;
+
+  memset(msg, 0, sizeof(*msg));
+  msg->pkt.p = buf;
+  msg->pkt.len = len;
+
+  if (len < (int) sizeof(*header)) return -1;
+
+  msg->transaction_id = header->transaction_id;
+  msg->flags = ntohs(header->flags);
+  msg->num_questions = ntohs(header->num_questions);
+  if (msg->num_questions > (int) ARRAY_SIZE(msg->questions)) {
+    msg->num_questions = (int) ARRAY_SIZE(msg->questions);
+  }
+  msg->num_answers = ntohs(header->num_answers);
+  if (msg->num_answers > (int) ARRAY_SIZE(msg->answers)) {
+    msg->num_answers = (int) ARRAY_SIZE(msg->answers);
+  }
+
+  for (i = 0; i < msg->num_questions; i++) {
+    data = mg_parse_dns_resource_record(data, end, &msg->questions[i], 0);
+    if (data == NULL) return -1;
+  }
+
+  for (i = 0; i < msg->num_answers; i++) {
+    data = mg_parse_dns_resource_record(data, end, &msg->answers[i], 1);
+    if (data == NULL) return -1;
+  }
+
+  return 0;
+}
+
+size_t mg_dns_uncompress_name(struct mg_dns_message *msg, struct mg_str *name,
+                              char *dst, int dst_len) {
+  int chunk_len, num_ptrs = 0;
+  char *old_dst = dst;
+  const unsigned char *data = (unsigned char *) name->p;
+  const unsigned char *end = (unsigned char *) msg->pkt.p + msg->pkt.len;
+
+  if (data >= end) {
+    return 0;
+  }
+
+  while ((chunk_len = *data++)) {
+    int leeway = dst_len - (dst - old_dst);
+    if (data >= end) {
+      return 0;
+    }
+
+    if ((chunk_len & 0xc0) == 0xc0) {
+      uint16_t off = (data[-1] & (~0xc0)) << 8 | data[0];
+      if (off >= msg->pkt.len) {
+        return 0;
+      }
+      /* Basic circular loop avoidance: allow up to 16 pointer hops. */
+      if (++num_ptrs > 15) {
+        return 0;
+      }
+      data = (unsigned char *) msg->pkt.p + off;
+      continue;
+    }
+    if (chunk_len > 63) {
+      return 0;
+    }
+    if (chunk_len > leeway) {
+      chunk_len = leeway;
+    }
+
+    if (data + chunk_len >= end) {
+      return 0;
+    }
+
+    memcpy(dst, data, chunk_len);
+    data += chunk_len;
+    dst += chunk_len;
+    leeway -= chunk_len;
+    if (leeway == 0) {
+      return dst - old_dst;
+    }
+    *dst++ = '.';
+  }
+
+  if (dst != old_dst) {
+    *--dst = 0;
+  }
+  return dst - old_dst;
+}
+
+static void dns_handler(struct mg_connection *nc, int ev,
+                        void *ev_data MG_UD_ARG(void *user_data)) {
+  struct mbuf *io = &nc->recv_mbuf;
+  struct mg_dns_message msg;
+
+  /* Pass low-level events to the user handler */
+  nc->handler(nc, ev, ev_data MG_UD_ARG(user_data));
+
+  switch (ev) {
+    case MG_EV_RECV:
+      if (!(nc->flags & MG_F_UDP)) {
+        mbuf_remove(&nc->recv_mbuf, 2);
+      }
+      if (mg_parse_dns(nc->recv_mbuf.buf, nc->recv_mbuf.len, &msg) == -1) {
+        /* reply + recursion allowed + format error */
+        memset(&msg, 0, sizeof(msg));
+        msg.flags = 0x8081;
+        mg_dns_insert_header(io, 0, &msg);
+        if (!(nc->flags & MG_F_UDP)) {
+          uint16_t len = htons((uint16_t) io->len);
+          mbuf_insert(io, 0, &len, 2);
+        }
+        mg_send(nc, io->buf, io->len);
+      } else {
+        /* Call user handler with parsed message */
+        nc->handler(nc, MG_DNS_MESSAGE, &msg MG_UD_ARG(user_data));
+      }
+      mbuf_remove(io, io->len);
+      break;
+  }
+}
+
+void mg_set_protocol_dns(struct mg_connection *nc) {
+  nc->proto_handler = dns_handler;
+}
+
+#endif /* MG_ENABLE_DNS */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_dns_server.c"
+#endif
+/*
+ * Copyright (c) 2014 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#if MG_ENABLE_DNS_SERVER
+
+/* Amalgamated: #include "mg_internal.h" */
+/* Amalgamated: #include "dns-server.h" */
+
+struct mg_dns_reply mg_dns_create_reply(struct mbuf *io,
+                                        struct mg_dns_message *msg) {
+  struct mg_dns_reply rep;
+  rep.msg = msg;
+  rep.io = io;
+  rep.start = io->len;
+
+  /* reply + recursion allowed */
+  msg->flags |= 0x8080;
+  mg_dns_copy_questions(io, msg);
+
+  msg->num_answers = 0;
+  return rep;
+}
+
+void mg_dns_send_reply(struct mg_connection *nc, struct mg_dns_reply *r) {
+  size_t sent = r->io->len - r->start;
+  mg_dns_insert_header(r->io, r->start, r->msg);
+  if (!(nc->flags & MG_F_UDP)) {
+    uint16_t len = htons((uint16_t) sent);
+    mbuf_insert(r->io, r->start, &len, 2);
+  }
+
+  if (&nc->send_mbuf != r->io) {
+    mg_send(nc, r->io->buf + r->start, r->io->len - r->start);
+    r->io->len = r->start;
+  }
+}
+
+int mg_dns_reply_record(struct mg_dns_reply *reply,
+                        struct mg_dns_resource_record *question,
+                        const char *name, int rtype, int ttl, const void *rdata,
+                        size_t rdata_len) {
+  struct mg_dns_message *msg = (struct mg_dns_message *) reply->msg;
+  char rname[512];
+  struct mg_dns_resource_record *ans = &msg->answers[msg->num_answers];
+  if (msg->num_answers >= MG_MAX_DNS_ANSWERS) {
+    return -1; /* LCOV_EXCL_LINE */
+  }
+
+  if (name == NULL) {
+    name = rname;
+    rname[511] = 0;
+    mg_dns_uncompress_name(msg, &question->name, rname, sizeof(rname) - 1);
+  }
+
+  *ans = *question;
+  ans->kind = MG_DNS_ANSWER;
+  ans->rtype = rtype;
+  ans->ttl = ttl;
+
+  if (mg_dns_encode_record(reply->io, ans, name, strlen(name), rdata,
+                           rdata_len) == -1) {
+    return -1; /* LCOV_EXCL_LINE */
+  };
+
+  msg->num_answers++;
+  return 0;
+}
+
+#endif /* MG_ENABLE_DNS_SERVER */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_resolv.c"
+#endif
+/*
+ * Copyright (c) 2014 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#if MG_ENABLE_ASYNC_RESOLVER
+
+/* Amalgamated: #include "mg_internal.h" */
+/* Amalgamated: #include "mg_resolv.h" */
+
+#ifndef MG_DEFAULT_NAMESERVER
+#define MG_DEFAULT_NAMESERVER "8.8.8.8"
+#endif
+
+struct mg_resolve_async_request {
+  char name[1024];
+  int query;
+  mg_resolve_callback_t callback;
+  void *data;
+  time_t timeout;
+  int max_retries;
+  enum mg_resolve_err err;
+
+  /* state */
+  time_t last_time;
+  int retries;
+};
+
+/*
+ * Find what nameserver to use.
+ *
+ * Return 0 if OK, -1 if error
+ */
+static int mg_get_ip_address_of_nameserver(char *name, size_t name_len) {
+  int ret = -1;
+
+#ifdef _WIN32
+  int i;
+  LONG err;
+  HKEY hKey, hSub;
+  wchar_t subkey[512], value[128],
+      *key = L"SYSTEM\\ControlSet001\\Services\\Tcpip\\Parameters\\Interfaces";
+
+  if ((err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hKey)) !=
+      ERROR_SUCCESS) {
+    fprintf(stderr, "cannot open reg key %S: %ld\n", key, err);
+    ret = -1;
+  } else {
+    for (ret = -1, i = 0; 1; i++) {
+      DWORD subkey_size = sizeof(subkey), type, len = sizeof(value);
+      if (RegEnumKeyExW(hKey, i, subkey, &subkey_size, NULL, NULL, NULL,
+                        NULL) != ERROR_SUCCESS) {
+        break;
+      }
+      if (RegOpenKeyExW(hKey, subkey, 0, KEY_READ, &hSub) == ERROR_SUCCESS &&
+          ((RegQueryValueExW(hSub, L"NameServer", 0, &type, (void *) value,
+                             &len) == ERROR_SUCCESS &&
+            value[0] != '\0') ||
+           (RegQueryValueExW(hSub, L"DhcpNameServer", 0, &type, (void *) value,
+                             &len) == ERROR_SUCCESS &&
+            value[0] != '\0'))) {
+        /*
+         * See https://github.com/cesanta/mongoose/issues/176
+         * The value taken from the registry can be empty, a single
+         * IP address, or multiple IP addresses separated by comma.
+         * If it's empty, check the next interface.
+         * If it's multiple IP addresses, take the first one.
+         */
+        wchar_t *comma = wcschr(value, ',');
+        if (comma != NULL) {
+          *comma = '\0';
+        }
+        /* %S will convert wchar_t -> char */
+        snprintf(name, name_len, "%S", value);
+        ret = 0;
+        RegCloseKey(hSub);
+        break;
+      }
+    }
+    RegCloseKey(hKey);
+  }
+#elif MG_ENABLE_FILESYSTEM && defined(MG_RESOLV_CONF_FILE_NAME)
+  FILE *fp;
+  char line[512];
+
+  if ((fp = mg_fopen(MG_RESOLV_CONF_FILE_NAME, "r")) == NULL) {
+    ret = -1;
+  } else {
+    /* Try to figure out what nameserver to use */
+    for (ret = -1; fgets(line, sizeof(line), fp) != NULL;) {
+      unsigned int a, b, c, d;
+      if (sscanf(line, "nameserver %u.%u.%u.%u", &a, &b, &c, &d) == 4) {
+        snprintf(name, name_len, "%u.%u.%u.%u", a, b, c, d);
+        ret = 0;
+        break;
+      }
+    }
+    (void) fclose(fp);
+  }
+#else
+  snprintf(name, name_len, "%s", MG_DEFAULT_NAMESERVER);
+#endif /* _WIN32 */
+
+  return ret;
+}
+
+int mg_resolve_from_hosts_file(const char *name, union socket_address *usa) {
+#if MG_ENABLE_FILESYSTEM && defined(MG_HOSTS_FILE_NAME)
+  /* TODO(mkm) cache /etc/hosts */
+  FILE *fp;
+  char line[1024];
+  char *p;
+  char alias[256];
+  unsigned int a, b, c, d;
+  int len = 0;
+
+  if ((fp = mg_fopen(MG_HOSTS_FILE_NAME, "r")) == NULL) {
+    return -1;
+  }
+
+  for (; fgets(line, sizeof(line), fp) != NULL;) {
+    if (line[0] == '#') continue;
+
+    if (sscanf(line, "%u.%u.%u.%u%n", &a, &b, &c, &d, &len) == 0) {
+      /* TODO(mkm): handle ipv6 */
+      continue;
+    }
+    for (p = line + len; sscanf(p, "%s%n", alias, &len) == 1; p += len) {
+      if (strcmp(alias, name) == 0) {
+        usa->sin.sin_addr.s_addr = htonl(a << 24 | b << 16 | c << 8 | d);
+        fclose(fp);
+        return 0;
+      }
+    }
+  }
+
+  fclose(fp);
+#else
+  (void) name;
+  (void) usa;
+#endif
+
+  return -1;
+}
+
+static void mg_resolve_async_eh(struct mg_connection *nc, int ev,
+                                void *data MG_UD_ARG(void *user_data)) {
+  time_t now = (time_t) mg_time();
+  struct mg_resolve_async_request *req;
+  struct mg_dns_message *msg;
+  int first = 0;
+#if !MG_ENABLE_CALLBACK_USERDATA
+  void *user_data = nc->user_data;
+#endif
+
+  if (ev != MG_EV_POLL) DBG(("ev=%d user_data=%p", ev, user_data));
+
+  req = (struct mg_resolve_async_request *) user_data;
+
+  if (req == NULL) {
+    return;
+  }
+
+  switch (ev) {
+    case MG_EV_CONNECT:
+      /* don't depend on timer not being at epoch for sending out first req */
+      first = 1;
+    /* fallthrough */
+    case MG_EV_POLL:
+      if (req->retries > req->max_retries) {
+        req->err = MG_RESOLVE_EXCEEDED_RETRY_COUNT;
+        nc->flags |= MG_F_CLOSE_IMMEDIATELY;
+        break;
+      }
+      if (first || now - req->last_time >= req->timeout) {
+        mg_send_dns_query(nc, req->name, req->query);
+        req->last_time = now;
+        req->retries++;
+      }
+      break;
+    case MG_EV_RECV:
+      msg = (struct mg_dns_message *) MG_MALLOC(sizeof(*msg));
+      if (mg_parse_dns(nc->recv_mbuf.buf, *(int *) data, msg) == 0 &&
+          msg->num_answers > 0) {
+        req->callback(msg, req->data, MG_RESOLVE_OK);
+        nc->user_data = NULL;
+        MG_FREE(req);
+      } else {
+        req->err = MG_RESOLVE_NO_ANSWERS;
+      }
+      MG_FREE(msg);
+      nc->flags |= MG_F_CLOSE_IMMEDIATELY;
+      break;
+    case MG_EV_SEND:
+      /*
+       * If a send error occurs, prevent closing of the connection by the core.
+       * We will retry after timeout.
+       */
+      nc->flags &= ~MG_F_CLOSE_IMMEDIATELY;
+      mbuf_remove(&nc->send_mbuf, nc->send_mbuf.len);
+      break;
+    case MG_EV_TIMER:
+      req->err = MG_RESOLVE_TIMEOUT;
+      nc->flags |= MG_F_CLOSE_IMMEDIATELY;
+      break;
+    case MG_EV_CLOSE:
+      /* If we got here with request still not done, fire an error callback. */
+      if (req != NULL) {
+        char addr[32];
+        mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr), MG_SOCK_STRINGIFY_IP);
+#ifdef MG_LOG_DNS_FAILURES
+        LOG(LL_ERROR, ("Failed to resolve '%s', server %s", req->name, addr));
+#endif
+        req->callback(NULL, req->data, req->err);
+        nc->user_data = NULL;
+        MG_FREE(req);
+      }
+      break;
+  }
+}
+
+int mg_resolve_async(struct mg_mgr *mgr, const char *name, int query,
+                     mg_resolve_callback_t cb, void *data) {
+  struct mg_resolve_async_opts opts;
+  memset(&opts, 0, sizeof(opts));
+  return mg_resolve_async_opt(mgr, name, query, cb, data, opts);
+}
+
+int mg_resolve_async_opt(struct mg_mgr *mgr, const char *name, int query,
+                         mg_resolve_callback_t cb, void *data,
+                         struct mg_resolve_async_opts opts) {
+  struct mg_resolve_async_request *req;
+  struct mg_connection *dns_nc;
+  const char *nameserver = opts.nameserver;
+  char dns_server_buff[17], nameserver_url[26];
+
+  if (nameserver == NULL) {
+    nameserver = mgr->nameserver;
+  }
+
+  DBG(("%s %d %p", name, query, opts.dns_conn));
+
+  /* resolve with DNS */
+  req = (struct mg_resolve_async_request *) MG_CALLOC(1, sizeof(*req));
+  if (req == NULL) {
+    return -1;
+  }
+
+  strncpy(req->name, name, sizeof(req->name));
+  req->name[sizeof(req->name) - 1] = '\0';
+
+  req->query = query;
+  req->callback = cb;
+  req->data = data;
+  /* TODO(mkm): parse defaults out of resolve.conf */
+  req->max_retries = opts.max_retries ? opts.max_retries : 2;
+  req->timeout = opts.timeout ? opts.timeout : 5;
+
+  /* Lazily initialize dns server */
+  if (nameserver == NULL) {
+    if (mg_get_ip_address_of_nameserver(dns_server_buff,
+                                        sizeof(dns_server_buff)) != -1) {
+      nameserver = dns_server_buff;
+    } else {
+      nameserver = MG_DEFAULT_NAMESERVER;
+    }
+  }
+
+  snprintf(nameserver_url, sizeof(nameserver_url), "udp://%s:53", nameserver);
+
+  dns_nc = mg_connect(mgr, nameserver_url, MG_CB(mg_resolve_async_eh, NULL));
+  if (dns_nc == NULL) {
+    MG_FREE(req);
+    return -1;
+  }
+  dns_nc->user_data = req;
+  if (opts.dns_conn != NULL) {
+    *opts.dns_conn = dns_nc;
+  }
+
+  return 0;
+}
+
+void mg_set_nameserver(struct mg_mgr *mgr, const char *nameserver) {
+  MG_FREE((char *) mgr->nameserver);
+  mgr->nameserver = NULL;
+  if (nameserver != NULL) {
+    mgr->nameserver = strdup(nameserver);
+  }
+}
+
+#endif /* MG_ENABLE_ASYNC_RESOLVER */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_coap.c"
+#endif
+/*
+ * Copyright (c) 2015 Cesanta Software Limited
+ * All rights reserved
+ * This software is dual-licensed: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. For the terms of this
+ * license, see .
+ *
+ * You are free to use this software under the terms of the GNU General
+ * Public License, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * Alternatively, you can license this software under a commercial
+ * license, as set out in .
+ */
+
+/* Amalgamated: #include "mg_internal.h" */
+/* Amalgamated: #include "mg_coap.h" */
+
+#if MG_ENABLE_COAP
+
+void mg_coap_free_options(struct mg_coap_message *cm) {
+  while (cm->options != NULL) {
+    struct mg_coap_option *next = cm->options->next;
+    MG_FREE(cm->options);
+    cm->options = next;
+  }
+}
+
+struct mg_coap_option *mg_coap_add_option(struct mg_coap_message *cm,
+                                          uint32_t number, char *value,
+                                          size_t len) {
+  struct mg_coap_option *new_option =
+      (struct mg_coap_option *) MG_CALLOC(1, sizeof(*new_option));
+
+  new_option->number = number;
+  new_option->value.p = value;
+  new_option->value.len = len;
+
+  if (cm->options == NULL) {
+    cm->options = cm->optiomg_tail = new_option;
+  } else {
+    /*
+     * A very simple attention to help clients to compose options:
+     * CoAP wants to see options ASC ordered.
+     * Could be change by using sort in coap_compose
+     */
+    if (cm->optiomg_tail->number <= new_option->number) {
+      /* if option is already ordered just add it */
+      cm->optiomg_tail = cm->optiomg_tail->next = new_option;
+    } else {
+      /* looking for appropriate position */
+      struct mg_coap_option *current_opt = cm->options;
+      struct mg_coap_option *prev_opt = 0;
+
+      while (current_opt != NULL) {
+        if (current_opt->number > new_option->number) {
+          break;
+        }
+        prev_opt = current_opt;
+        current_opt = current_opt->next;
+      }
+
+      if (prev_opt != NULL) {
+        prev_opt->next = new_option;
+        new_option->next = current_opt;
+      } else {
+        /* insert new_option to the beginning */
+        new_option->next = cm->options;
+        cm->options = new_option;
+      }
+    }
+  }
+
+  return new_option;
+}
+
+/*
+ * Fills CoAP header in mg_coap_message.
+ *
+ * Helper function.
+ */
+static char *coap_parse_header(char *ptr, struct mbuf *io,
+                               struct mg_coap_message *cm) {
+  if (io->len < sizeof(uint32_t)) {
+    cm->flags |= MG_COAP_NOT_ENOUGH_DATA;
+    return NULL;
+  }
+
+  /*
+   * Version (Ver):  2-bit unsigned integer.  Indicates the CoAP version
+   * number.  Implementations of this specification MUST set this field
+   * to 1 (01 binary).  Other values are reserved for future versions.
+   * Messages with unknown version numbers MUST be silently ignored.
+   */
+  if (((uint8_t) *ptr >> 6) != 1) {
+    cm->flags |= MG_COAP_IGNORE;
+    return NULL;
+  }
+
+  /*
+   * Type (T):  2-bit unsigned integer.  Indicates if this message is of
+   * type Confirmable (0), Non-confirmable (1), Acknowledgement (2), or
+   * Reset (3).
+   */
+  cm->msg_type = ((uint8_t) *ptr & 0x30) >> 4;
+  cm->flags |= MG_COAP_MSG_TYPE_FIELD;
+
+  /*
+   * Token Length (TKL):  4-bit unsigned integer.  Indicates the length of
+   * the variable-length Token field (0-8 bytes).  Lengths 9-15 are
+   * reserved, MUST NOT be sent, and MUST be processed as a message
+   * format error.
+   */
+  cm->token.len = *ptr & 0x0F;
+  if (cm->token.len > 8) {
+    cm->flags |= MG_COAP_FORMAT_ERROR;
+    return NULL;
+  }
+
+  ptr++;
+
+  /*
+   * Code:  8-bit unsigned integer, split into a 3-bit class (most
+   * significant bits) and a 5-bit detail (least significant bits)
+   */
+  cm->code_class = (uint8_t) *ptr >> 5;
+  cm->code_detail = *ptr & 0x1F;
+  cm->flags |= (MG_COAP_CODE_CLASS_FIELD | MG_COAP_CODE_DETAIL_FIELD);
+
+  ptr++;
+
+  /* Message ID:  16-bit unsigned integer in network byte order. */
+  cm->msg_id = (uint8_t) *ptr << 8 | (uint8_t) * (ptr + 1);
+  cm->flags |= MG_COAP_MSG_ID_FIELD;
+
+  ptr += 2;
+
+  return ptr;
+}
+
+/*
+ * Fills token information in mg_coap_message.
+ *
+ * Helper function.
+ */
+static char *coap_get_token(char *ptr, struct mbuf *io,
+                            struct mg_coap_message *cm) {
+  if (cm->token.len != 0) {
+    if (ptr + cm->token.len > io->buf + io->len) {
+      cm->flags |= MG_COAP_NOT_ENOUGH_DATA;
+      return NULL;
+    } else {
+      cm->token.p = ptr;
+      ptr += cm->token.len;
+      cm->flags |= MG_COAP_TOKEN_FIELD;
+    }
+  }
+
+  return ptr;
+}
+
+/*
+ * Returns Option Delta or Length.
+ *
+ * Helper function.
+ */
+static int coap_get_ext_opt(char *ptr, struct mbuf *io, uint16_t *opt_info) {
+  int ret = 0;
+
+  if (*opt_info == 13) {
+    /*
+     * 13:  An 8-bit unsigned integer follows the initial byte and
+     * indicates the Option Delta/Length minus 13.
+     */
+    if (ptr < io->buf + io->len) {
+      *opt_info = (uint8_t) *ptr + 13;
+      ret = sizeof(uint8_t);
+    } else {
+      ret = -1; /* LCOV_EXCL_LINE */
+    }
+  } else if (*opt_info == 14) {
+    /*
+     * 14:  A 16-bit unsigned integer in network byte order follows the
+     * initial byte and indicates the Option Delta/Length minus 269.
+     */
+    if (ptr + sizeof(uint8_t) < io->buf + io->len) {
+      *opt_info = ((uint8_t) *ptr << 8 | (uint8_t) * (ptr + 1)) + 269;
+      ret = sizeof(uint16_t);
+    } else {
+      ret = -1; /* LCOV_EXCL_LINE */
+    }
+  }
+
+  return ret;
+}
+
+/*
+ * Fills options in mg_coap_message.
+ *
+ * Helper function.
+ *
+ * General options format:
+ * +---------------+---------------+
+ * | Option Delta  | Option Length |  1 byte
+ * +---------------+---------------+
+ * \    Option Delta (extended)    \  0-2 bytes
+ * +-------------------------------+
+ * / Option Length  (extended)     \  0-2 bytes
+ * +-------------------------------+
+ * \         Option Value          \  0 or more bytes
+ * +-------------------------------+
+ */
+static char *coap_get_options(char *ptr, struct mbuf *io,
+                              struct mg_coap_message *cm) {
+  uint16_t prev_opt = 0;
+
+  if (ptr == io->buf + io->len) {
+    /* end of packet, ok */
+    return NULL;
+  }
+
+  /* 0xFF is payload marker */
+  while (ptr < io->buf + io->len && (uint8_t) *ptr != 0xFF) {
+    uint16_t option_delta, option_lenght;
+    int optinfo_len;
+
+    /* Option Delta:  4-bit unsigned integer */
+    option_delta = ((uint8_t) *ptr & 0xF0) >> 4;
+    /* Option Length:  4-bit unsigned integer */
+    option_lenght = *ptr & 0x0F;
+
+    if (option_delta == 15 || option_lenght == 15) {
+      /*
+       * 15:  Reserved for future use.  If the field is set to this value,
+       * it MUST be processed as a message format error
+       */
+      cm->flags |= MG_COAP_FORMAT_ERROR;
+      break;
+    }
+
+    ptr++;
+
+    /* check for extended option delta */
+    optinfo_len = coap_get_ext_opt(ptr, io, &option_delta);
+    if (optinfo_len == -1) {
+      cm->flags |= MG_COAP_NOT_ENOUGH_DATA; /* LCOV_EXCL_LINE */
+      break;                                /* LCOV_EXCL_LINE */
+    }
+
+    ptr += optinfo_len;
+
+    /* check or extended option lenght */
+    optinfo_len = coap_get_ext_opt(ptr, io, &option_lenght);
+    if (optinfo_len == -1) {
+      cm->flags |= MG_COAP_NOT_ENOUGH_DATA; /* LCOV_EXCL_LINE */
+      break;                                /* LCOV_EXCL_LINE */
+    }
+
+    ptr += optinfo_len;
+
+    /*
+     * Instead of specifying the Option Number directly, the instances MUST
+     * appear in order of their Option Numbers and a delta encoding is used
+     * between them.
+     */
+    option_delta += prev_opt;
+
+    mg_coap_add_option(cm, option_delta, ptr, option_lenght);
+
+    prev_opt = option_delta;
+
+    if (ptr + option_lenght > io->buf + io->len) {
+      cm->flags |= MG_COAP_NOT_ENOUGH_DATA; /* LCOV_EXCL_LINE */
+      break;                                /* LCOV_EXCL_LINE */
+    }
+
+    ptr += option_lenght;
+  }
+
+  if ((cm->flags & MG_COAP_ERROR) != 0) {
+    mg_coap_free_options(cm);
+    return NULL;
+  }
+
+  cm->flags |= MG_COAP_OPTIOMG_FIELD;
+
+  if (ptr == io->buf + io->len) {
+    /* end of packet, ok */
+    return NULL;
+  }
+
+  ptr++;
+
+  return ptr;
+}
+
+uint32_t mg_coap_parse(struct mbuf *io, struct mg_coap_message *cm) {
+  char *ptr;
+
+  memset(cm, 0, sizeof(*cm));
+
+  if ((ptr = coap_parse_header(io->buf, io, cm)) == NULL) {
+    return cm->flags;
+  }
+
+  if ((ptr = coap_get_token(ptr, io, cm)) == NULL) {
+    return cm->flags;
+  }
+
+  if ((ptr = coap_get_options(ptr, io, cm)) == NULL) {
+    return cm->flags;
+  }
+
+  /* the rest is payload */
+  cm->payload.len = io->len - (ptr - io->buf);
+  if (cm->payload.len != 0) {
+    cm->payload.p = ptr;
+    cm->flags |= MG_COAP_PAYLOAD_FIELD;
+  }
+
+  return cm->flags;
+}
+
+/*
+ * Calculates extended size of given Opt Number/Length in coap message.
+ *
+ * Helper function.
+ */
+static size_t coap_get_ext_opt_size(uint32_t value) {
+  int ret = 0;
+
+  if (value >= 13 && value <= 0xFF + 13) {
+    ret = sizeof(uint8_t);
+  } else if (value > 0xFF + 13 && value <= 0xFFFF + 269) {
+    ret = sizeof(uint16_t);
+  }
+
+  return ret;
+}
+
+/*
+ * Splits given Opt Number/Length into base and ext values.
+ *
+ * Helper function.
+ */
+static int coap_split_opt(uint32_t value, uint8_t *base, uint16_t *ext) {
+  int ret = 0;
+
+  if (value < 13) {
+    *base = value;
+  } else if (value >= 13 && value <= 0xFF + 13) {
+    *base = 13;
+    *ext = value - 13;
+    ret = sizeof(uint8_t);
+  } else if (value > 0xFF + 13 && value <= 0xFFFF + 269) {
+    *base = 14;
+    *ext = value - 269;
+    ret = sizeof(uint16_t);
+  }
+
+  return ret;
+}
+
+/*
+ * Puts uint16_t (in network order) into given char stream.
+ *
+ * Helper function.
+ */
+static char *coap_add_uint16(char *ptr, uint16_t val) {
+  *ptr = val >> 8;
+  ptr++;
+  *ptr = val & 0x00FF;
+  ptr++;
+  return ptr;
+}
+
+/*
+ * Puts extended value of Opt Number/Length into given char stream.
+ *
+ * Helper function.
+ */
+static char *coap_add_opt_info(char *ptr, uint16_t val, size_t len) {
+  if (len == sizeof(uint8_t)) {
+    *ptr = (char) val;
+    ptr++;
+  } else if (len == sizeof(uint16_t)) {
+    ptr = coap_add_uint16(ptr, val);
+  }
+
+  return ptr;
+}
+
+/*
+ * Verifies given mg_coap_message and calculates message size for it.
+ *
+ * Helper function.
+ */
+static uint32_t coap_calculate_packet_size(struct mg_coap_message *cm,
+                                           size_t *len) {
+  struct mg_coap_option *opt;
+  uint32_t prev_opt_number;
+
+  *len = 4; /* header */
+  if (cm->msg_type > MG_COAP_MSG_MAX) {
+    return MG_COAP_ERROR | MG_COAP_MSG_TYPE_FIELD;
+  }
+  if (cm->token.len > 8) {
+    return MG_COAP_ERROR | MG_COAP_TOKEN_FIELD;
+  }
+  if (cm->code_class > 7) {
+    return MG_COAP_ERROR | MG_COAP_CODE_CLASS_FIELD;
+  }
+  if (cm->code_detail > 31) {
+    return MG_COAP_ERROR | MG_COAP_CODE_DETAIL_FIELD;
+  }
+
+  *len += cm->token.len;
+  if (cm->payload.len != 0) {
+    *len += cm->payload.len + 1; /* ... + 1; add payload marker */
+  }
+
+  opt = cm->options;
+  prev_opt_number = 0;
+  while (opt != NULL) {
+    *len += 1; /* basic delta/length */
+    *len += coap_get_ext_opt_size(opt->number - prev_opt_number);
+    *len += coap_get_ext_opt_size((uint32_t) opt->value.len);
+    /*
+     * Current implementation performs check if
+     * option_number > previous option_number and produces an error
+     * TODO(alashkin): write design doc with limitations
+     * May be resorting is more suitable solution.
+     */
+    if ((opt->next != NULL && opt->number > opt->next->number) ||
+        opt->value.len > 0xFFFF + 269 ||
+        opt->number - prev_opt_number > 0xFFFF + 269) {
+      return MG_COAP_ERROR | MG_COAP_OPTIOMG_FIELD;
+    }
+    *len += opt->value.len;
+    prev_opt_number = opt->number;
+    opt = opt->next;
+  }
+
+  return 0;
+}
+
+uint32_t mg_coap_compose(struct mg_coap_message *cm, struct mbuf *io) {
+  struct mg_coap_option *opt;
+  uint32_t res, prev_opt_number;
+  size_t prev_io_len, packet_size;
+  char *ptr;
+
+  res = coap_calculate_packet_size(cm, &packet_size);
+  if (res != 0) {
+    return res;
+  }
+
+  /* saving previous lenght to handle non-empty mbuf */
+  prev_io_len = io->len;
+  if (mbuf_append(io, NULL, packet_size) == 0) return MG_COAP_ERROR;
+  ptr = io->buf + prev_io_len;
+
+  /*
+   * since cm is verified, it is possible to use bits shift operator
+   * without additional zeroing of unused bits
+   */
+
+  /* ver: 2 bits, msg_type: 2 bits, toklen: 4 bits */
+  *ptr = (1 << 6) | (cm->msg_type << 4) | (uint8_t)(cm->token.len);
+  ptr++;
+
+  /* code class: 3 bits, code detail: 5 bits */
+  *ptr = (cm->code_class << 5) | (cm->code_detail);
+  ptr++;
+
+  ptr = coap_add_uint16(ptr, cm->msg_id);
+
+  if (cm->token.len != 0) {
+    memcpy(ptr, cm->token.p, cm->token.len);
+    ptr += cm->token.len;
+  }
+
+  opt = cm->options;
+  prev_opt_number = 0;
+  while (opt != NULL) {
+    uint8_t delta_base = 0, length_base = 0;
+    uint16_t delta_ext = 0, length_ext = 0;
+
+    size_t opt_delta_len =
+        coap_split_opt(opt->number - prev_opt_number, &delta_base, &delta_ext);
+    size_t opt_lenght_len =
+        coap_split_opt((uint32_t) opt->value.len, &length_base, &length_ext);
+
+    *ptr = (delta_base << 4) | length_base;
+    ptr++;
+
+    ptr = coap_add_opt_info(ptr, delta_ext, opt_delta_len);
+    ptr = coap_add_opt_info(ptr, length_ext, opt_lenght_len);
+
+    if (opt->value.len != 0) {
+      memcpy(ptr, opt->value.p, opt->value.len);
+      ptr += opt->value.len;
+    }
+
+    prev_opt_number = opt->number;
+    opt = opt->next;
+  }
+
+  if (cm->payload.len != 0) {
+    *ptr = (char) -1;
+    ptr++;
+    memcpy(ptr, cm->payload.p, cm->payload.len);
+  }
+
+  return 0;
+}
+
+uint32_t mg_coap_send_message(struct mg_connection *nc,
+                              struct mg_coap_message *cm) {
+  struct mbuf packet_out;
+  uint32_t compose_res;
+
+  mbuf_init(&packet_out, 0);
+  compose_res = mg_coap_compose(cm, &packet_out);
+  if (compose_res != 0) {
+    return compose_res; /* LCOV_EXCL_LINE */
+  }
+
+  mg_send(nc, packet_out.buf, (int) packet_out.len);
+  mbuf_free(&packet_out);
+
+  return 0;
+}
+
+uint32_t mg_coap_send_ack(struct mg_connection *nc, uint16_t msg_id) {
+  struct mg_coap_message cm;
+  memset(&cm, 0, sizeof(cm));
+  cm.msg_type = MG_COAP_MSG_ACK;
+  cm.msg_id = msg_id;
+
+  return mg_coap_send_message(nc, &cm);
+}
+
+static void coap_handler(struct mg_connection *nc, int ev,
+                         void *ev_data MG_UD_ARG(void *user_data)) {
+  struct mbuf *io = &nc->recv_mbuf;
+  struct mg_coap_message cm;
+  uint32_t parse_res;
+
+  memset(&cm, 0, sizeof(cm));
+
+  nc->handler(nc, ev, ev_data MG_UD_ARG(user_data));
+
+  switch (ev) {
+    case MG_EV_RECV:
+      parse_res = mg_coap_parse(io, &cm);
+      if ((parse_res & MG_COAP_IGNORE) == 0) {
+        if ((cm.flags & MG_COAP_NOT_ENOUGH_DATA) != 0) {
+          /*
+           * Since we support UDP only
+           * MG_COAP_NOT_ENOUGH_DATA == MG_COAP_FORMAT_ERROR
+           */
+          cm.flags |= MG_COAP_FORMAT_ERROR; /* LCOV_EXCL_LINE */
+        }                                   /* LCOV_EXCL_LINE */
+        nc->handler(nc, MG_COAP_EVENT_BASE + cm.msg_type,
+                    &cm MG_UD_ARG(user_data));
+      }
+
+      mg_coap_free_options(&cm);
+      mbuf_remove(io, io->len);
+      break;
+  }
+}
+/*
+ * Attach built-in CoAP event handler to the given connection.
+ *
+ * The user-defined event handler will receive following extra events:
+ *
+ * - MG_EV_COAP_CON
+ * - MG_EV_COAP_NOC
+ * - MG_EV_COAP_ACK
+ * - MG_EV_COAP_RST
+ */
+int mg_set_protocol_coap(struct mg_connection *nc) {
+  /* supports UDP only */
+  if ((nc->flags & MG_F_UDP) == 0) {
+    return -1;
+  }
+
+  nc->proto_handler = coap_handler;
+
+  return 0;
+}
+
+#endif /* MG_ENABLE_COAP */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_sntp.c"
+#endif
+/*
+ * Copyright (c) 2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+/* Amalgamated: #include "mg_internal.h" */
+/* Amalgamated: #include "mg_sntp.h" */
+/* Amalgamated: #include "mg_util.h" */
+
+#if MG_ENABLE_SNTP
+
+#define SNTP_TIME_OFFSET 2208988800
+
+#ifndef SNTP_TIMEOUT
+#define SNTP_TIMEOUT 10
+#endif
+
+#ifndef SNTP_ATTEMPTS
+#define SNTP_ATTEMPTS 3
+#endif
+
+static uint64_t mg_get_sec(uint64_t val) {
+  return (val & 0xFFFFFFFF00000000) >> 32;
+}
+
+static uint64_t mg_get_usec(uint64_t val) {
+  uint64_t tmp = (val & 0x00000000FFFFFFFF);
+  tmp *= 1000000;
+  tmp >>= 32;
+  return tmp;
+}
+
+static void mg_ntp_to_tv(uint64_t val, struct timeval *tv) {
+  uint64_t tmp;
+  tmp = mg_get_sec(val);
+  tmp -= SNTP_TIME_OFFSET;
+  tv->tv_sec = tmp;
+  tv->tv_usec = mg_get_usec(val);
+}
+
+static void mg_get_ntp_ts(const char *ntp, uint64_t *val) {
+  uint32_t tmp;
+  memcpy(&tmp, ntp, sizeof(tmp));
+  tmp = ntohl(tmp);
+  *val = (uint64_t) tmp << 32;
+  memcpy(&tmp, ntp + 4, sizeof(tmp));
+  tmp = ntohl(tmp);
+  *val |= tmp;
+}
+
+void mg_sntp_send_request(struct mg_connection *c) {
+  uint8_t buf[48] = {0};
+  /*
+   * header - 8 bit:
+   * LI (2 bit) - 3 (not in sync), VN (3 bit) - 4 (version),
+   * mode (3 bit) - 3 (client)
+   */
+  buf[0] = (3 << 6) | (4 << 3) | 3;
+
+/*
+ * Next fields should be empty in client request
+ * stratum, 8 bit
+ * poll interval, 8 bit
+ * rrecision, 8 bit
+ * root delay, 32 bit
+ * root dispersion, 32 bit
+ * ref id, 32 bit
+ * ref timestamp, 64 bit
+ * originate Timestamp, 64 bit
+ * receive Timestamp, 64 bit
+*/
+
+/*
+ * convert time to sntp format (sntp starts from 00:00:00 01.01.1900)
+ * according to rfc868 it is 2208988800L sec
+ * this information is used to correct roundtrip delay
+ * but if local clock is absolutely broken (and doesn't work even
+ * as simple timer), it is better to disable it
+*/
+#ifndef MG_SNTP_NO_DELAY_CORRECTION
+  uint32_t sec;
+  sec = htonl((uint32_t)(mg_time() + SNTP_TIME_OFFSET));
+  memcpy(&buf[40], &sec, sizeof(sec));
+#endif
+
+  mg_send(c, buf, sizeof(buf));
+}
+
+#ifndef MG_SNTP_NO_DELAY_CORRECTION
+static uint64_t mg_calculate_delay(uint64_t t1, uint64_t t2, uint64_t t3) {
+  /* roundloop delay = (T4 - T1) - (T3 - T2) */
+  uint64_t d1 = ((mg_time() + SNTP_TIME_OFFSET) * 1000000) -
+                (mg_get_sec(t1) * 1000000 + mg_get_usec(t1));
+  uint64_t d2 = (mg_get_sec(t3) * 1000000 + mg_get_usec(t3)) -
+                (mg_get_sec(t2) * 1000000 + mg_get_usec(t2));
+
+  return (d1 > d2) ? d1 - d2 : 0;
+}
+#endif
+
+MG_INTERNAL int mg_sntp_parse_reply(const char *buf, int len,
+                                    struct mg_sntp_message *msg) {
+  uint8_t hdr;
+  uint64_t trsm_ts_T3, delay = 0;
+  int mode;
+  struct timeval tv;
+
+  if (len < 48) {
+    return -1;
+  }
+
+  hdr = buf[0];
+
+  if ((hdr & 0x38) >> 3 != 4) {
+    /* Wrong version */
+    return -1;
+  }
+
+  mode = hdr & 0x7;
+  if (mode != 4 && mode != 5) {
+    /* Not a server reply */
+    return -1;
+  }
+
+  memset(msg, 0, sizeof(*msg));
+
+  msg->kiss_of_death = (buf[1] == 0); /* Server asks to not send requests */
+
+  mg_get_ntp_ts(&buf[40], &trsm_ts_T3);
+
+#ifndef MG_SNTP_NO_DELAY_CORRECTION
+  {
+    uint64_t orig_ts_T1, recv_ts_T2;
+    mg_get_ntp_ts(&buf[24], &orig_ts_T1);
+    mg_get_ntp_ts(&buf[32], &recv_ts_T2);
+    delay = mg_calculate_delay(orig_ts_T1, recv_ts_T2, trsm_ts_T3);
+  }
+#endif
+
+  mg_ntp_to_tv(trsm_ts_T3, &tv);
+
+  msg->time = (double) tv.tv_sec + (((double) tv.tv_usec + delay) / 1000000.0);
+
+  return 0;
+}
+
+static void mg_sntp_handler(struct mg_connection *c, int ev,
+                            void *ev_data MG_UD_ARG(void *user_data)) {
+  struct mbuf *io = &c->recv_mbuf;
+  struct mg_sntp_message msg;
+
+  c->handler(c, ev, ev_data MG_UD_ARG(user_data));
+
+  switch (ev) {
+    case MG_EV_RECV: {
+      if (mg_sntp_parse_reply(io->buf, io->len, &msg) < 0) {
+        DBG(("Invalid SNTP packet received (%d)", (int) io->len));
+        c->handler(c, MG_SNTP_MALFORMED_REPLY, NULL MG_UD_ARG(user_data));
+      } else {
+        c->handler(c, MG_SNTP_REPLY, (void *) &msg MG_UD_ARG(user_data));
+      }
+
+      mbuf_remove(io, io->len);
+      break;
+    }
+  }
+}
+
+int mg_set_protocol_sntp(struct mg_connection *c) {
+  if ((c->flags & MG_F_UDP) == 0) {
+    return -1;
+  }
+
+  c->proto_handler = mg_sntp_handler;
+
+  return 0;
+}
+
+struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr,
+                                      MG_CB(mg_event_handler_t event_handler,
+                                            void *user_data),
+                                      const char *sntp_server_name) {
+  struct mg_connection *c = NULL;
+  char url[100], *p_url = url;
+  const char *proto = "", *port = "", *tmp;
+
+  /* If port is not specified, use default (123) */
+  tmp = strchr(sntp_server_name, ':');
+  if (tmp != NULL && *(tmp + 1) == '/') {
+    tmp = strchr(tmp + 1, ':');
+  }
+
+  if (tmp == NULL) {
+    port = ":123";
+  }
+
+  /* Add udp:// if needed */
+  if (strncmp(sntp_server_name, "udp://", 6) != 0) {
+    proto = "udp://";
+  }
+
+  mg_asprintf(&p_url, sizeof(url), "%s%s%s", proto, sntp_server_name, port);
+
+  c = mg_connect(mgr, p_url, event_handler MG_UD_ARG(user_data));
+
+  if (c == NULL) {
+    goto cleanup;
+  }
+
+  mg_set_protocol_sntp(c);
+
+cleanup:
+  if (p_url != url) {
+    MG_FREE(p_url);
+  }
+
+  return c;
+}
+
+struct sntp_data {
+  mg_event_handler_t hander;
+  int count;
+};
+
+static void mg_sntp_util_ev_handler(struct mg_connection *c, int ev,
+                                    void *ev_data MG_UD_ARG(void *user_data)) {
+#if !MG_ENABLE_CALLBACK_USERDATA
+  void *user_data = c->user_data;
+#endif
+  struct sntp_data *sd = (struct sntp_data *) user_data;
+
+  switch (ev) {
+    case MG_EV_CONNECT:
+      if (*(int *) ev_data != 0) {
+        mg_call(c, sd->hander, c->user_data, MG_SNTP_FAILED, NULL);
+        break;
+      }
+    /* fallthrough */
+    case MG_EV_TIMER:
+      if (sd->count <= SNTP_ATTEMPTS) {
+        mg_sntp_send_request(c);
+        mg_set_timer(c, mg_time() + 10);
+        sd->count++;
+      } else {
+        mg_call(c, sd->hander, c->user_data, MG_SNTP_FAILED, NULL);
+        c->flags |= MG_F_CLOSE_IMMEDIATELY;
+      }
+      break;
+    case MG_SNTP_MALFORMED_REPLY:
+      mg_call(c, sd->hander, c->user_data, MG_SNTP_FAILED, NULL);
+      c->flags |= MG_F_CLOSE_IMMEDIATELY;
+      break;
+    case MG_SNTP_REPLY:
+      mg_call(c, sd->hander, c->user_data, MG_SNTP_REPLY, ev_data);
+      c->flags |= MG_F_CLOSE_IMMEDIATELY;
+      break;
+    case MG_EV_CLOSE:
+      MG_FREE(user_data);
+      c->user_data = NULL;
+      break;
+  }
+}
+
+struct mg_connection *mg_sntp_get_time(struct mg_mgr *mgr,
+                                       mg_event_handler_t event_handler,
+                                       const char *sntp_server_name) {
+  struct mg_connection *c;
+  struct sntp_data *sd = (struct sntp_data *) MG_CALLOC(1, sizeof(*sd));
+  if (sd == NULL) {
+    return NULL;
+  }
+
+  c = mg_sntp_connect(mgr, MG_CB(mg_sntp_util_ev_handler, sd),
+                      sntp_server_name);
+  if (c == NULL) {
+    MG_FREE(sd);
+    return NULL;
+  }
+
+  sd->hander = event_handler;
+#if !MG_ENABLE_CALLBACK_USERDATA
+  c->user_data = sd;
+#endif
+
+  return c;
+}
+
+#endif /* MG_ENABLE_SNTP */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_socks.c"
+#endif
+/*
+ * Copyright (c) 2017 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#if MG_ENABLE_SOCKS
+
+/* Amalgamated: #include "mg_socks.h" */
+/* Amalgamated: #include "mg_internal.h" */
+
+/*
+ *  https://www.ietf.org/rfc/rfc1928.txt paragraph 3, handle client handshake
+ *
+ *  +----+----------+----------+
+ *  |VER | NMETHODS | METHODS  |
+ *  +----+----------+----------+
+ *  | 1  |    1     | 1 to 255 |
+ *  +----+----------+----------+
+ */
+static void mg_socks5_handshake(struct mg_connection *c) {
+  struct mbuf *r = &c->recv_mbuf;
+  if (r->buf[0] != MG_SOCKS_VERSION) {
+    c->flags |= MG_F_CLOSE_IMMEDIATELY;
+  } else if (r->len > 2 && (size_t) r->buf[1] + 2 <= r->len) {
+    /* https://www.ietf.org/rfc/rfc1928.txt paragraph 3 */
+    unsigned char reply[2] = {MG_SOCKS_VERSION, MG_SOCKS_HANDSHAKE_FAILURE};
+    int i;
+    for (i = 2; i < r->buf[1] + 2; i++) {
+      /* TODO(lsm): support other auth methods */
+      if (r->buf[i] == MG_SOCKS_HANDSHAKE_NOAUTH) reply[1] = r->buf[i];
+    }
+    mbuf_remove(r, 2 + r->buf[1]);
+    mg_send(c, reply, sizeof(reply));
+    c->flags |= MG_SOCKS_HANDSHAKE_DONE; /* Mark handshake done */
+  }
+}
+
+static void disband(struct mg_connection *c) {
+  struct mg_connection *c2 = (struct mg_connection *) c->user_data;
+  if (c2 != NULL) {
+    c2->flags |= MG_F_SEND_AND_CLOSE;
+    c2->user_data = NULL;
+  }
+  c->flags |= MG_F_SEND_AND_CLOSE;
+  c->user_data = NULL;
+}
+
+static void relay_data(struct mg_connection *c) {
+  struct mg_connection *c2 = (struct mg_connection *) c->user_data;
+  if (c2 != NULL) {
+    mg_send(c2, c->recv_mbuf.buf, c->recv_mbuf.len);
+    mbuf_remove(&c->recv_mbuf, c->recv_mbuf.len);
+  } else {
+    c->flags |= MG_F_SEND_AND_CLOSE;
+  }
+}
+
+static void serv_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
+  if (ev == MG_EV_CLOSE) {
+    disband(c);
+  } else if (ev == MG_EV_RECV) {
+    relay_data(c);
+  } else if (ev == MG_EV_CONNECT) {
+    int res = *(int *) ev_data;
+    if (res != 0) LOG(LL_ERROR, ("connect error: %d", res));
+  }
+}
+
+static void mg_socks5_connect(struct mg_connection *c, const char *addr) {
+  struct mg_connection *serv = mg_connect(c->mgr, addr, serv_ev_handler);
+  serv->user_data = c;
+  c->user_data = serv;
+}
+
+/*
+ *  Request, https://www.ietf.org/rfc/rfc1928.txt paragraph 4
+ *
+ *  +----+-----+-------+------+----------+----------+
+ *  |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
+ *  +----+-----+-------+------+----------+----------+
+ *  | 1  |  1  | X'00' |  1   | Variable |    2     |
+ *  +----+-----+-------+------+----------+----------+
+ */
+static void mg_socks5_handle_request(struct mg_connection *c) {
+  struct mbuf *r = &c->recv_mbuf;
+  unsigned char *p = (unsigned char *) r->buf;
+  unsigned char addr_len = 4, reply = MG_SOCKS_SUCCESS;
+  int ver, cmd, atyp;
+  char addr[300];
+
+  if (r->len < 8) return; /* return if not fully buffered. min DST.ADDR is 2 */
+  ver = p[0];
+  cmd = p[1];
+  atyp = p[3];
+
+  /* TODO(lsm): support other commands */
+  if (ver != MG_SOCKS_VERSION || cmd != MG_SOCKS_CMD_CONNECT) {
+    reply = MG_SOCKS_CMD_NOT_SUPPORTED;
+  } else if (atyp == MG_SOCKS_ADDR_IPV4) {
+    addr_len = 4;
+    if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */
+    snprintf(addr, sizeof(addr), "%d.%d.%d.%d:%d", p[4], p[5], p[6], p[7],
+             p[8] << 8 | p[9]);
+    mg_socks5_connect(c, addr);
+  } else if (atyp == MG_SOCKS_ADDR_IPV6) {
+    addr_len = 16;
+    if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */
+    snprintf(addr, sizeof(addr), "[%x:%x:%x:%x:%x:%x:%x:%x]:%d",
+             p[4] << 8 | p[5], p[6] << 8 | p[7], p[8] << 8 | p[9],
+             p[10] << 8 | p[11], p[12] << 8 | p[13], p[14] << 8 | p[15],
+             p[16] << 8 | p[17], p[18] << 8 | p[19], p[20] << 8 | p[21]);
+    mg_socks5_connect(c, addr);
+  } else if (atyp == MG_SOCKS_ADDR_DOMAIN) {
+    addr_len = p[4] + 1;
+    if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */
+    snprintf(addr, sizeof(addr), "%.*s:%d", p[4], p + 5,
+             p[4 + addr_len] << 8 | p[4 + addr_len + 1]);
+    mg_socks5_connect(c, addr);
+  } else {
+    reply = MG_SOCKS_ADDR_NOT_SUPPORTED;
+  }
+
+  /*
+   *  Reply, https://www.ietf.org/rfc/rfc1928.txt paragraph 5
+   *
+   *  +----+-----+-------+------+----------+----------+
+   *  |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
+   *  +----+-----+-------+------+----------+----------+
+   *  | 1  |  1  | X'00' |  1   | Variable |    2     |
+   *  +----+-----+-------+------+----------+----------+
+   */
+  {
+    unsigned char buf[] = {MG_SOCKS_VERSION, reply, 0};
+    mg_send(c, buf, sizeof(buf));
+  }
+  mg_send(c, r->buf + 3, addr_len + 1 + 2);
+
+  mbuf_remove(r, 6 + addr_len);      /* Remove request from the input stream */
+  c->flags |= MG_SOCKS_CONNECT_DONE; /* Mark ourselves as connected */
+}
+
+static void socks_handler(struct mg_connection *c, int ev, void *ev_data) {
+  if (ev == MG_EV_RECV) {
+    if (!(c->flags & MG_SOCKS_HANDSHAKE_DONE)) mg_socks5_handshake(c);
+    if (c->flags & MG_SOCKS_HANDSHAKE_DONE &&
+        !(c->flags & MG_SOCKS_CONNECT_DONE)) {
+      mg_socks5_handle_request(c);
+    }
+    if (c->flags & MG_SOCKS_CONNECT_DONE) relay_data(c);
+  } else if (ev == MG_EV_CLOSE) {
+    disband(c);
+  }
+  (void) ev_data;
+}
+
+void mg_set_protocol_socks(struct mg_connection *c) {
+  c->proto_handler = socks_handler;
+}
+#endif
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/cc3200/cc3200_libc.c"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#if CS_PLATFORM == CS_P_CC3200
+
+/* Amalgamated: #include "common/mg_mem.h" */
+#include 
+#include 
+
+#ifndef __TI_COMPILER_VERSION__
+#include 
+#include 
+#include 
+#include 
+#endif
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define CONSOLE_UART UARTA0_BASE
+
+#ifdef __TI_COMPILER_VERSION__
+int asprintf(char **strp, const char *fmt, ...) {
+  va_list ap;
+  int len;
+
+  *strp = MG_MALLOC(BUFSIZ);
+  if (*strp == NULL) return -1;
+
+  va_start(ap, fmt);
+  len = vsnprintf(*strp, BUFSIZ, fmt, ap);
+  va_end(ap);
+
+  if (len > 0) {
+    *strp = MG_REALLOC(*strp, len + 1);
+    if (*strp == NULL) return -1;
+  }
+
+  if (len >= BUFSIZ) {
+    va_start(ap, fmt);
+    len = vsnprintf(*strp, len + 1, fmt, ap);
+    va_end(ap);
+  }
+
+  return len;
+}
+
+#if MG_TI_NO_HOST_INTERFACE
+time_t HOSTtime() {
+  struct timeval tp;
+  gettimeofday(&tp, NULL);
+  return tp.tv_sec;
+}
+#endif
+
+#endif /* __TI_COMPILER_VERSION__ */
+
+void fprint_str(FILE *fp, const char *str) {
+  while (*str != '\0') {
+    if (*str == '\n') MAP_UARTCharPut(CONSOLE_UART, '\r');
+    MAP_UARTCharPut(CONSOLE_UART, *str++);
+  }
+}
+
+void _exit(int status) {
+  fprint_str(stderr, "_exit\n");
+  /* cause an unaligned access exception, that will drop you into gdb */
+  *(int *) 1 = status;
+  while (1)
+    ; /* avoid gcc warning because stdlib abort() has noreturn attribute */
+}
+
+void _not_implemented(const char *what) {
+  fprint_str(stderr, what);
+  fprint_str(stderr, " is not implemented\n");
+  _exit(42);
+}
+
+int _kill(int pid, int sig) {
+  (void) pid;
+  (void) sig;
+  _not_implemented("_kill");
+  return -1;
+}
+
+int _getpid() {
+  fprint_str(stderr, "_getpid is not implemented\n");
+  return 42;
+}
+
+int _isatty(int fd) {
+  /* 0, 1 and 2 are TTYs. */
+  return fd < 2;
+}
+
+#endif /* CS_PLATFORM == CS_P_CC3200 */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/msp432/msp432_libc.c"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#if CS_PLATFORM == CS_P_MSP432
+
+#include 
+#include 
+
+int gettimeofday(struct timeval *tp, void *tzp) {
+  uint32_t ticks = Clock_getTicks();
+  tp->tv_sec = ticks / 1000;
+  tp->tv_usec = (ticks % 1000) * 1000;
+  return 0;
+}
+
+#endif /* CS_PLATFORM == CS_P_MSP432 */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/nrf5/nrf5_libc.c"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#if (CS_PLATFORM == CS_P_NRF51 || CS_PLATFORM == CS_P_NRF52) && \
+    defined(__ARMCC_VERSION)
+int gettimeofday(struct timeval *tp, void *tzp) {
+  /* TODO */
+  tp->tv_sec = 0;
+  tp->tv_usec = 0;
+  return 0;
+}
+#endif
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/simplelink/sl_fs_slfs.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_PLATFORMS_SIMPLELINK_SL_FS_SLFS_H_
+#define CS_COMMON_PLATFORMS_SIMPLELINK_SL_FS_SLFS_H_
+
+#if defined(MG_FS_SLFS)
+
+#include 
+#ifndef __TI_COMPILER_VERSION__
+#include 
+#include 
+#endif
+
+#define MAX_OPEN_SLFS_FILES 8
+
+/* Indirect libc interface - same functions, different names. */
+int fs_slfs_open(const char *pathname, int flags, mode_t mode);
+int fs_slfs_close(int fd);
+ssize_t fs_slfs_read(int fd, void *buf, size_t count);
+ssize_t fs_slfs_write(int fd, const void *buf, size_t count);
+int fs_slfs_stat(const char *pathname, struct stat *s);
+int fs_slfs_fstat(int fd, struct stat *s);
+off_t fs_slfs_lseek(int fd, off_t offset, int whence);
+int fs_slfs_unlink(const char *filename);
+int fs_slfs_rename(const char *from, const char *to);
+
+void fs_slfs_set_new_file_size(const char *name, size_t size);
+
+#endif /* defined(MG_FS_SLFS) */
+
+#endif /* CS_COMMON_PLATFORMS_SIMPLELINK_SL_FS_SLFS_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/simplelink/sl_fs_slfs.c"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+/* Standard libc interface to TI SimpleLink FS. */
+
+#if defined(MG_FS_SLFS) || defined(CC3200_FS_SLFS)
+
+/* Amalgamated: #include "common/platforms/simplelink/sl_fs_slfs.h" */
+
+#include 
+
+#if CS_PLATFORM == CS_P_CC3200
+#include 
+#endif
+
+/* Amalgamated: #include "common/cs_dbg.h" */
+/* Amalgamated: #include "common/mg_mem.h" */
+
+#if SL_MAJOR_VERSION_NUM < 2
+int slfs_open(const unsigned char *fname, uint32_t flags) {
+  _i32 fh;
+  _i32 r = sl_FsOpen(fname, flags, NULL /* token */, &fh);
+  return (r < 0 ? r : fh);
+}
+#else /* SL_MAJOR_VERSION_NUM >= 2 */
+int slfs_open(const unsigned char *fname, uint32_t flags) {
+  return sl_FsOpen(fname, flags, NULL /* token */);
+}
+#endif
+
+/* From sl_fs.c */
+int set_errno(int e);
+const char *drop_dir(const char *fname, bool *is_slfs);
+
+/*
+ * With SLFS, you have to pre-declare max file size. Yes. Really.
+ * 64K should be enough for everyone. Right?
+ */
+#ifndef FS_SLFS_MAX_FILE_SIZE
+#define FS_SLFS_MAX_FILE_SIZE (64 * 1024)
+#endif
+
+struct sl_file_size_hint {
+  char *name;
+  size_t size;
+};
+
+struct sl_fd_info {
+  _i32 fh;
+  _off_t pos;
+  size_t size;
+};
+
+static struct sl_fd_info s_sl_fds[MAX_OPEN_SLFS_FILES];
+static struct sl_file_size_hint s_sl_file_size_hints[MAX_OPEN_SLFS_FILES];
+
+static int sl_fs_to_errno(_i32 r) {
+  DBG(("SL error: %d", (int) r));
+  switch (r) {
+    case SL_FS_OK:
+      return 0;
+    case SL_ERROR_FS_FILE_NAME_EXIST:
+      return EEXIST;
+    case SL_ERROR_FS_WRONG_FILE_NAME:
+      return EINVAL;
+    case SL_ERROR_FS_NO_AVAILABLE_NV_INDEX:
+    case SL_ERROR_FS_NOT_ENOUGH_STORAGE_SPACE:
+      return ENOSPC;
+    case SL_ERROR_FS_FAILED_TO_ALLOCATE_MEM:
+      return ENOMEM;
+    case SL_ERROR_FS_FILE_NOT_EXISTS:
+      return ENOENT;
+    case SL_ERROR_FS_NOT_SUPPORTED:
+      return ENOTSUP;
+  }
+  return ENXIO;
+}
+
+int fs_slfs_open(const char *pathname, int flags, mode_t mode) {
+  int fd;
+  for (fd = 0; fd < MAX_OPEN_SLFS_FILES; fd++) {
+    if (s_sl_fds[fd].fh <= 0) break;
+  }
+  if (fd >= MAX_OPEN_SLFS_FILES) return set_errno(ENOMEM);
+  struct sl_fd_info *fi = &s_sl_fds[fd];
+
+  /*
+   * Apply path manipulations again, in case we got here directly
+   * (via TI libc's "add_device").
+   */
+  pathname = drop_dir(pathname, NULL);
+
+  _u32 am = 0;
+  fi->size = (size_t) -1;
+  int rw = (flags & 3);
+  size_t new_size = FS_SLFS_MAX_FILE_SIZE;
+  if (rw == O_RDONLY) {
+    SlFsFileInfo_t sl_fi;
+    _i32 r = sl_FsGetInfo((const _u8 *) pathname, 0, &sl_fi);
+    if (r == SL_FS_OK) {
+      fi->size = SL_FI_FILE_SIZE(sl_fi);
+    }
+    am = SL_FS_READ;
+  } else {
+    if (!(flags & O_TRUNC) || (flags & O_APPEND)) {
+      // FailFS files cannot be opened for append and will be truncated
+      // when opened for write.
+      return set_errno(ENOTSUP);
+    }
+    if (flags & O_CREAT) {
+      size_t i;
+      for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) {
+        if (s_sl_file_size_hints[i].name != NULL &&
+            strcmp(s_sl_file_size_hints[i].name, pathname) == 0) {
+          new_size = s_sl_file_size_hints[i].size;
+          MG_FREE(s_sl_file_size_hints[i].name);
+          s_sl_file_size_hints[i].name = NULL;
+          break;
+        }
+      }
+      am = FS_MODE_OPEN_CREATE(new_size, 0);
+    } else {
+      am = SL_FS_WRITE;
+    }
+  }
+  fi->fh = slfs_open((_u8 *) pathname, am);
+  LOG(LL_DEBUG, ("sl_FsOpen(%s, 0x%x) sz %u = %d", pathname, (int) am,
+                 (unsigned int) new_size, (int) fi->fh));
+  int r;
+  if (fi->fh >= 0) {
+    fi->pos = 0;
+    r = fd;
+  } else {
+    r = set_errno(sl_fs_to_errno(fi->fh));
+  }
+  return r;
+}
+
+int fs_slfs_close(int fd) {
+  struct sl_fd_info *fi = &s_sl_fds[fd];
+  if (fi->fh <= 0) return set_errno(EBADF);
+  _i32 r = sl_FsClose(fi->fh, NULL, NULL, 0);
+  LOG(LL_DEBUG, ("sl_FsClose(%d) = %d", (int) fi->fh, (int) r));
+  s_sl_fds[fd].fh = -1;
+  return set_errno(sl_fs_to_errno(r));
+}
+
+ssize_t fs_slfs_read(int fd, void *buf, size_t count) {
+  struct sl_fd_info *fi = &s_sl_fds[fd];
+  if (fi->fh <= 0) return set_errno(EBADF);
+  /* Simulate EOF. sl_FsRead @ file_size return SL_FS_ERR_OFFSET_OUT_OF_RANGE.
+   */
+  if (fi->pos == fi->size) return 0;
+  _i32 r = sl_FsRead(fi->fh, fi->pos, buf, count);
+  DBG(("sl_FsRead(%d, %d, %d) = %d", (int) fi->fh, (int) fi->pos, (int) count,
+       (int) r));
+  if (r >= 0) {
+    fi->pos += r;
+    return r;
+  }
+  return set_errno(sl_fs_to_errno(r));
+}
+
+ssize_t fs_slfs_write(int fd, const void *buf, size_t count) {
+  struct sl_fd_info *fi = &s_sl_fds[fd];
+  if (fi->fh <= 0) return set_errno(EBADF);
+  _i32 r = sl_FsWrite(fi->fh, fi->pos, (_u8 *) buf, count);
+  DBG(("sl_FsWrite(%d, %d, %d) = %d", (int) fi->fh, (int) fi->pos, (int) count,
+       (int) r));
+  if (r >= 0) {
+    fi->pos += r;
+    return r;
+  }
+  return set_errno(sl_fs_to_errno(r));
+}
+
+int fs_slfs_stat(const char *pathname, struct stat *s) {
+  SlFsFileInfo_t sl_fi;
+  /*
+   * Apply path manipulations again, in case we got here directly
+   * (via TI libc's "add_device").
+   */
+  pathname = drop_dir(pathname, NULL);
+  _i32 r = sl_FsGetInfo((const _u8 *) pathname, 0, &sl_fi);
+  if (r == SL_FS_OK) {
+    s->st_mode = S_IFREG | 0666;
+    s->st_nlink = 1;
+    s->st_size = SL_FI_FILE_SIZE(sl_fi);
+    return 0;
+  }
+  return set_errno(sl_fs_to_errno(r));
+}
+
+int fs_slfs_fstat(int fd, struct stat *s) {
+  struct sl_fd_info *fi = &s_sl_fds[fd];
+  if (fi->fh <= 0) return set_errno(EBADF);
+  s->st_mode = 0666;
+  s->st_mode = S_IFREG | 0666;
+  s->st_nlink = 1;
+  s->st_size = fi->size;
+  return 0;
+}
+
+off_t fs_slfs_lseek(int fd, off_t offset, int whence) {
+  if (s_sl_fds[fd].fh <= 0) return set_errno(EBADF);
+  switch (whence) {
+    case SEEK_SET:
+      s_sl_fds[fd].pos = offset;
+      break;
+    case SEEK_CUR:
+      s_sl_fds[fd].pos += offset;
+      break;
+    case SEEK_END:
+      return set_errno(ENOTSUP);
+  }
+  return 0;
+}
+
+int fs_slfs_unlink(const char *pathname) {
+  /*
+   * Apply path manipulations again, in case we got here directly
+   * (via TI libc's "add_device").
+   */
+  pathname = drop_dir(pathname, NULL);
+  return set_errno(sl_fs_to_errno(sl_FsDel((const _u8 *) pathname, 0)));
+}
+
+int fs_slfs_rename(const char *from, const char *to) {
+  return set_errno(ENOTSUP);
+}
+
+void fs_slfs_set_new_file_size(const char *name, size_t size) {
+  int i;
+  for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) {
+    if (s_sl_file_size_hints[i].name == NULL) {
+      DBG(("File size hint: %s %d", name, (int) size));
+      s_sl_file_size_hints[i].name = strdup(name);
+      s_sl_file_size_hints[i].size = size;
+      break;
+    }
+  }
+}
+
+#endif /* defined(MG_FS_SLFS) || defined(CC3200_FS_SLFS) */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/simplelink/sl_fs.c"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#if MG_NET_IF == MG_NET_IF_SIMPLELINK && \
+    (defined(MG_FS_SLFS) || defined(MG_FS_SPIFFS))
+
+int set_errno(int e) {
+  errno = e;
+  return (e == 0 ? 0 : -1);
+}
+
+const char *drop_dir(const char *fname, bool *is_slfs) {
+  if (is_slfs != NULL) {
+    *is_slfs = (strncmp(fname, "SL:", 3) == 0);
+    if (*is_slfs) fname += 3;
+  }
+  /* Drop "./", if any */
+  if (fname[0] == '.' && fname[1] == '/') {
+    fname += 2;
+  }
+  /*
+   * Drop / if it is the only one in the path.
+   * This allows use of /pretend/directories but serves /file.txt as normal.
+   */
+  if (fname[0] == '/' && strchr(fname + 1, '/') == NULL) {
+    fname++;
+  }
+  return fname;
+}
+
+#if !defined(MG_FS_NO_VFS)
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#ifdef __TI_COMPILER_VERSION__
+#include 
+#endif
+
+/* Amalgamated: #include "common/cs_dbg.h" */
+/* Amalgamated: #include "common/platform.h" */
+
+#ifdef CC3200_FS_SPIFFS
+/* Amalgamated: #include "cc3200_fs_spiffs.h" */
+#endif
+
+#ifdef MG_FS_SLFS
+/* Amalgamated: #include "sl_fs_slfs.h" */
+#endif
+
+#define NUM_SYS_FDS 3
+#define SPIFFS_FD_BASE 10
+#define SLFS_FD_BASE 100
+
+#if !defined(MG_UART_CHAR_PUT) && !defined(MG_UART_WRITE)
+#if CS_PLATFORM == CS_P_CC3200
+#include 
+#include 
+#include 
+#include 
+#include 
+#define MG_UART_CHAR_PUT(fd, c) MAP_UARTCharPut(UARTA0_BASE, c);
+#else
+#define MG_UART_WRITE(fd, buf, len)
+#endif /* CS_PLATFORM == CS_P_CC3200 */
+#endif /* !MG_UART_CHAR_PUT */
+
+enum fd_type {
+  FD_INVALID,
+  FD_SYS,
+#ifdef CC3200_FS_SPIFFS
+  FD_SPIFFS,
+#endif
+#ifdef MG_FS_SLFS
+  FD_SLFS
+#endif
+};
+static int fd_type(int fd) {
+  if (fd >= 0 && fd < NUM_SYS_FDS) return FD_SYS;
+#ifdef CC3200_FS_SPIFFS
+  if (fd >= SPIFFS_FD_BASE && fd < SPIFFS_FD_BASE + MAX_OPEN_SPIFFS_FILES) {
+    return FD_SPIFFS;
+  }
+#endif
+#ifdef MG_FS_SLFS
+  if (fd >= SLFS_FD_BASE && fd < SLFS_FD_BASE + MAX_OPEN_SLFS_FILES) {
+    return FD_SLFS;
+  }
+#endif
+  return FD_INVALID;
+}
+
+#if MG_TI_NO_HOST_INTERFACE
+int open(const char *pathname, unsigned flags, int mode) {
+#else
+int _open(const char *pathname, int flags, mode_t mode) {
+#endif
+  int fd = -1;
+  bool is_sl;
+  const char *fname = drop_dir(pathname, &is_sl);
+  if (is_sl) {
+#ifdef MG_FS_SLFS
+    fd = fs_slfs_open(fname, flags, mode);
+    if (fd >= 0) fd += SLFS_FD_BASE;
+#endif
+  } else {
+#ifdef CC3200_FS_SPIFFS
+    fd = fs_spiffs_open(fname, flags, mode);
+    if (fd >= 0) fd += SPIFFS_FD_BASE;
+#endif
+  }
+  LOG(LL_DEBUG,
+      ("open(%s, 0x%x) = %d, fname = %s", pathname, flags, fd, fname));
+  return fd;
+}
+
+int _stat(const char *pathname, struct stat *st) {
+  int res = -1;
+  bool is_sl;
+  const char *fname = drop_dir(pathname, &is_sl);
+  memset(st, 0, sizeof(*st));
+  /* Simulate statting the root directory. */
+  if (fname[0] == '\0' || strcmp(fname, ".") == 0) {
+    st->st_ino = 0;
+    st->st_mode = S_IFDIR | 0777;
+    st->st_nlink = 1;
+    st->st_size = 0;
+    return 0;
+  }
+  if (is_sl) {
+#ifdef MG_FS_SLFS
+    res = fs_slfs_stat(fname, st);
+#endif
+  } else {
+#ifdef CC3200_FS_SPIFFS
+    res = fs_spiffs_stat(fname, st);
+#endif
+  }
+  LOG(LL_DEBUG, ("stat(%s) = %d; fname = %s", pathname, res, fname));
+  return res;
+}
+
+#if MG_TI_NO_HOST_INTERFACE
+int close(int fd) {
+#else
+int _close(int fd) {
+#endif
+  int r = -1;
+  switch (fd_type(fd)) {
+    case FD_INVALID:
+      r = set_errno(EBADF);
+      break;
+    case FD_SYS:
+      r = set_errno(EACCES);
+      break;
+#ifdef CC3200_FS_SPIFFS
+    case FD_SPIFFS:
+      r = fs_spiffs_close(fd - SPIFFS_FD_BASE);
+      break;
+#endif
+#ifdef MG_FS_SLFS
+    case FD_SLFS:
+      r = fs_slfs_close(fd - SLFS_FD_BASE);
+      break;
+#endif
+  }
+  DBG(("close(%d) = %d", fd, r));
+  return r;
+}
+
+#if MG_TI_NO_HOST_INTERFACE
+off_t lseek(int fd, off_t offset, int whence) {
+#else
+off_t _lseek(int fd, off_t offset, int whence) {
+#endif
+  int r = -1;
+  switch (fd_type(fd)) {
+    case FD_INVALID:
+      r = set_errno(EBADF);
+      break;
+    case FD_SYS:
+      r = set_errno(ESPIPE);
+      break;
+#ifdef CC3200_FS_SPIFFS
+    case FD_SPIFFS:
+      r = fs_spiffs_lseek(fd - SPIFFS_FD_BASE, offset, whence);
+      break;
+#endif
+#ifdef MG_FS_SLFS
+    case FD_SLFS:
+      r = fs_slfs_lseek(fd - SLFS_FD_BASE, offset, whence);
+      break;
+#endif
+  }
+  DBG(("lseek(%d, %d, %d) = %d", fd, (int) offset, whence, r));
+  return r;
+}
+
+int _fstat(int fd, struct stat *s) {
+  int r = -1;
+  memset(s, 0, sizeof(*s));
+  switch (fd_type(fd)) {
+    case FD_INVALID:
+      r = set_errno(EBADF);
+      break;
+    case FD_SYS: {
+      /* Create barely passable stats for STD{IN,OUT,ERR}. */
+      memset(s, 0, sizeof(*s));
+      s->st_ino = fd;
+      s->st_mode = S_IFCHR | 0666;
+      r = 0;
+      break;
+    }
+#ifdef CC3200_FS_SPIFFS
+    case FD_SPIFFS:
+      r = fs_spiffs_fstat(fd - SPIFFS_FD_BASE, s);
+      break;
+#endif
+#ifdef MG_FS_SLFS
+    case FD_SLFS:
+      r = fs_slfs_fstat(fd - SLFS_FD_BASE, s);
+      break;
+#endif
+  }
+  DBG(("fstat(%d) = %d", fd, r));
+  return r;
+}
+
+#if MG_TI_NO_HOST_INTERFACE
+int read(int fd, char *buf, unsigned count) {
+#else
+ssize_t _read(int fd, void *buf, size_t count) {
+#endif
+  int r = -1;
+  switch (fd_type(fd)) {
+    case FD_INVALID:
+      r = set_errno(EBADF);
+      break;
+    case FD_SYS: {
+      if (fd != 0) {
+        r = set_errno(EACCES);
+        break;
+      }
+      /* Should we allow reading from stdin = uart? */
+      r = set_errno(ENOTSUP);
+      break;
+    }
+#ifdef CC3200_FS_SPIFFS
+    case FD_SPIFFS:
+      r = fs_spiffs_read(fd - SPIFFS_FD_BASE, buf, count);
+      break;
+#endif
+#ifdef MG_FS_SLFS
+    case FD_SLFS:
+      r = fs_slfs_read(fd - SLFS_FD_BASE, buf, count);
+      break;
+#endif
+  }
+  DBG(("read(%d, %u) = %d", fd, count, r));
+  return r;
+}
+
+#if MG_TI_NO_HOST_INTERFACE
+int write(int fd, const char *buf, unsigned count) {
+#else
+ssize_t _write(int fd, const void *buf, size_t count) {
+#endif
+  int r = -1;
+  switch (fd_type(fd)) {
+    case FD_INVALID:
+      r = set_errno(EBADF);
+      break;
+    case FD_SYS: {
+      if (fd == 0) {
+        r = set_errno(EACCES);
+        break;
+      }
+#ifdef MG_UART_WRITE
+      MG_UART_WRITE(fd, buf, count);
+#elif defined(MG_UART_CHAR_PUT)
+      {
+        size_t i;
+        for (i = 0; i < count; i++) {
+          const char c = ((const char *) buf)[i];
+          if (c == '\n') MG_UART_CHAR_PUT(fd, '\r');
+          MG_UART_CHAR_PUT(fd, c);
+        }
+      }
+#endif
+      r = count;
+      break;
+    }
+#ifdef CC3200_FS_SPIFFS
+    case FD_SPIFFS:
+      r = fs_spiffs_write(fd - SPIFFS_FD_BASE, buf, count);
+      break;
+#endif
+#ifdef MG_FS_SLFS
+    case FD_SLFS:
+      r = fs_slfs_write(fd - SLFS_FD_BASE, buf, count);
+      break;
+#endif
+  }
+  return r;
+}
+
+/*
+ * On Newlib we override rename directly too, because the default
+ * implementation using _link and _unlink doesn't work for us.
+ */
+#if MG_TI_NO_HOST_INTERFACE || defined(_NEWLIB_VERSION)
+int rename(const char *frompath, const char *topath) {
+  int r = -1;
+  bool is_sl_from, is_sl_to;
+  const char *from = drop_dir(frompath, &is_sl_from);
+  const char *to = drop_dir(topath, &is_sl_to);
+  if (is_sl_from || is_sl_to) {
+    set_errno(ENOTSUP);
+  } else {
+#ifdef CC3200_FS_SPIFFS
+    r = fs_spiffs_rename(from, to);
+#endif
+  }
+  DBG(("rename(%s, %s) = %d", from, to, r));
+  return r;
+}
+#endif /* MG_TI_NO_HOST_INTERFACE || defined(_NEWLIB_VERSION) */
+
+#if MG_TI_NO_HOST_INTERFACE
+int unlink(const char *pathname) {
+#else
+int _unlink(const char *pathname) {
+#endif
+  int r = -1;
+  bool is_sl;
+  const char *fname = drop_dir(pathname, &is_sl);
+  if (is_sl) {
+#ifdef MG_FS_SLFS
+    r = fs_slfs_unlink(fname);
+#endif
+  } else {
+#ifdef CC3200_FS_SPIFFS
+    r = fs_spiffs_unlink(fname);
+#endif
+  }
+  DBG(("unlink(%s) = %d, fname = %s", pathname, r, fname));
+  return r;
+}
+
+#ifdef CC3200_FS_SPIFFS /* FailFS does not support listing files. */
+DIR *opendir(const char *dir_name) {
+  DIR *r = NULL;
+  bool is_sl;
+  drop_dir(dir_name, &is_sl);
+  if (is_sl) {
+    r = NULL;
+    set_errno(ENOTSUP);
+  } else {
+    r = fs_spiffs_opendir(dir_name);
+  }
+  DBG(("opendir(%s) = %p", dir_name, r));
+  return r;
+}
+
+struct dirent *readdir(DIR *dir) {
+  struct dirent *res = fs_spiffs_readdir(dir);
+  DBG(("readdir(%p) = %p", dir, res));
+  return res;
+}
+
+int closedir(DIR *dir) {
+  int res = fs_spiffs_closedir(dir);
+  DBG(("closedir(%p) = %d", dir, res));
+  return res;
+}
+
+int rmdir(const char *path) {
+  return fs_spiffs_rmdir(path);
+}
+
+int mkdir(const char *path, mode_t mode) {
+  (void) path;
+  (void) mode;
+  /* for spiffs supports only root dir, which comes from mongoose as '.' */
+  return (strlen(path) == 1 && *path == '.') ? 0 : ENOTDIR;
+}
+#endif
+
+int sl_fs_init(void) {
+  int ret = 1;
+#ifdef __TI_COMPILER_VERSION__
+#ifdef MG_FS_SLFS
+#pragma diag_push
+#pragma diag_suppress 169 /* Nothing we can do about the prototype mismatch. \
+                             */
+  ret = (add_device("SL", _MSA, fs_slfs_open, fs_slfs_close, fs_slfs_read,
+                    fs_slfs_write, fs_slfs_lseek, fs_slfs_unlink,
+                    fs_slfs_rename) == 0);
+#pragma diag_pop
+#endif
+#endif
+  return ret;
+}
+
+#endif /* !defined(MG_FS_NO_VFS) */
+#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK && (defined(MG_FS_SLFS) || \
+          defined(MG_FS_SPIFFS)) */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/simplelink/sl_socket.c"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#if MG_NET_IF == MG_NET_IF_SIMPLELINK
+
+#include 
+#include 
+
+/* Amalgamated: #include "common/platform.h" */
+
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) {
+  int res;
+  struct in_addr *in = (struct in_addr *) src;
+  if (af != AF_INET) {
+    errno = ENOTSUP;
+    return NULL;
+  }
+  res = snprintf(dst, size, "%lu.%lu.%lu.%lu", SL_IPV4_BYTE(in->s_addr, 0),
+                 SL_IPV4_BYTE(in->s_addr, 1), SL_IPV4_BYTE(in->s_addr, 2),
+                 SL_IPV4_BYTE(in->s_addr, 3));
+  return res > 0 ? dst : NULL;
+}
+
+char *inet_ntoa(struct in_addr n) {
+  static char a[16];
+  return (char *) inet_ntop(AF_INET, &n, a, sizeof(a));
+}
+
+int inet_pton(int af, const char *src, void *dst) {
+  uint32_t a0, a1, a2, a3;
+  uint8_t *db = (uint8_t *) dst;
+  if (af != AF_INET) {
+    errno = ENOTSUP;
+    return 0;
+  }
+  if (sscanf(src, "%lu.%lu.%lu.%lu", &a0, &a1, &a2, &a3) != 4) {
+    return 0;
+  }
+  *db = a3;
+  *(db + 1) = a2;
+  *(db + 2) = a1;
+  *(db + 3) = a0;
+  return 1;
+}
+
+#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/simplelink/sl_mg_task.c"
+#endif
+#if MG_NET_IF == MG_NET_IF_SIMPLELINK && !defined(MG_SIMPLELINK_NO_OSI)
+
+/* Amalgamated: #include "mg_task.h" */
+
+#include 
+
+enum mg_q_msg_type {
+  MG_Q_MSG_CB,
+};
+struct mg_q_msg {
+  enum mg_q_msg_type type;
+  void (*cb)(struct mg_mgr *mgr, void *arg);
+  void *arg;
+};
+static OsiMsgQ_t s_mg_q;
+static void mg_task(void *arg);
+
+bool mg_start_task(int priority, int stack_size, mg_init_cb mg_init) {
+  if (osi_MsgQCreate(&s_mg_q, "MG", sizeof(struct mg_q_msg), 16) != OSI_OK) {
+    return false;
+  }
+  if (osi_TaskCreate(mg_task, (const signed char *) "MG", stack_size,
+                     (void *) mg_init, priority, NULL) != OSI_OK) {
+    return false;
+  }
+  return true;
+}
+
+static void mg_task(void *arg) {
+  struct mg_mgr mgr;
+  mg_init_cb mg_init = (mg_init_cb) arg;
+  mg_mgr_init(&mgr, NULL);
+  mg_init(&mgr);
+  while (1) {
+    struct mg_q_msg msg;
+    mg_mgr_poll(&mgr, 1);
+    if (osi_MsgQRead(&s_mg_q, &msg, 1) != OSI_OK) continue;
+    switch (msg.type) {
+      case MG_Q_MSG_CB: {
+        msg.cb(&mgr, msg.arg);
+      }
+    }
+  }
+}
+
+void mg_run_in_task(void (*cb)(struct mg_mgr *mgr, void *arg), void *cb_arg) {
+  struct mg_q_msg msg = {MG_Q_MSG_CB, cb, cb_arg};
+  osi_MsgQWrite(&s_mg_q, &msg, OSI_NO_WAIT);
+}
+
+#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK && !defined(MG_SIMPLELINK_NO_OSI) \
+          */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/simplelink/sl_net_if.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_PLATFORMS_SIMPLELINK_SL_NET_IF_H_
+#define CS_COMMON_PLATFORMS_SIMPLELINK_SL_NET_IF_H_
+
+/* Amalgamated: #include "mongoose/src/net_if.h" */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifndef MG_ENABLE_NET_IF_SIMPLELINK
+#define MG_ENABLE_NET_IF_SIMPLELINK MG_NET_IF == MG_NET_IF_SIMPLELINK
+#endif
+
+extern const struct mg_iface_vtable mg_simplelink_iface_vtable;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CS_COMMON_PLATFORMS_SIMPLELINK_SL_NET_IF_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/simplelink/sl_net_if.c"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+/* Amalgamated: #include "common/platforms/simplelink/sl_net_if.h" */
+
+#if MG_ENABLE_NET_IF_SIMPLELINK
+
+/* Amalgamated: #include "mongoose/src/internal.h" */
+/* Amalgamated: #include "mongoose/src/util.h" */
+
+#define MG_TCP_RECV_BUFFER_SIZE 1024
+#define MG_UDP_RECV_BUFFER_SIZE 1500
+
+static sock_t mg_open_listening_socket(struct mg_connection *nc,
+                                       union socket_address *sa, int type,
+                                       int proto);
+
+void mg_set_non_blocking_mode(sock_t sock) {
+  SlSockNonblocking_t opt;
+#if SL_MAJOR_VERSION_NUM < 2
+  opt.NonblockingEnabled = 1;
+#else
+  opt.NonBlockingEnabled = 1;
+#endif
+  sl_SetSockOpt(sock, SL_SOL_SOCKET, SL_SO_NONBLOCKING, &opt, sizeof(opt));
+}
+
+static int mg_is_error(int n) {
+  return (n < 0 && n != SL_ERROR_BSD_EALREADY && n != SL_ERROR_BSD_EAGAIN);
+}
+
+void mg_sl_if_connect_tcp(struct mg_connection *nc,
+                          const union socket_address *sa) {
+  int proto = 0;
+  if (nc->flags & MG_F_SSL) proto = SL_SEC_SOCKET;
+  sock_t sock = sl_Socket(AF_INET, SOCK_STREAM, proto);
+  if (sock < 0) {
+    nc->err = sock;
+    goto out;
+  }
+  mg_sock_set(nc, sock);
+#if MG_ENABLE_SSL
+  nc->err = sl_set_ssl_opts(sock, nc);
+  if (nc->err != 0) goto out;
+#endif
+  nc->err = sl_Connect(sock, &sa->sa, sizeof(sa->sin));
+out:
+  DBG(("%p to %s:%d sock %d %d err %d", nc, inet_ntoa(sa->sin.sin_addr),
+       ntohs(sa->sin.sin_port), nc->sock, proto, nc->err));
+}
+
+void mg_sl_if_connect_udp(struct mg_connection *nc) {
+  sock_t sock = sl_Socket(AF_INET, SOCK_DGRAM, 0);
+  if (sock < 0) {
+    nc->err = sock;
+    return;
+  }
+  mg_sock_set(nc, sock);
+  nc->err = 0;
+}
+
+int mg_sl_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) {
+  int proto = 0;
+  if (nc->flags & MG_F_SSL) proto = SL_SEC_SOCKET;
+  sock_t sock = mg_open_listening_socket(nc, sa, SOCK_STREAM, proto);
+  if (sock < 0) return sock;
+  mg_sock_set(nc, sock);
+  return 0;
+}
+
+int mg_sl_if_listen_udp(struct mg_connection *nc, union socket_address *sa) {
+  sock_t sock = mg_open_listening_socket(nc, sa, SOCK_DGRAM, 0);
+  if (sock == INVALID_SOCKET) return (errno ? errno : 1);
+  mg_sock_set(nc, sock);
+  return 0;
+}
+
+void mg_sl_if_tcp_send(struct mg_connection *nc, const void *buf, size_t len) {
+  mbuf_append(&nc->send_mbuf, buf, len);
+}
+
+void mg_sl_if_udp_send(struct mg_connection *nc, const void *buf, size_t len) {
+  mbuf_append(&nc->send_mbuf, buf, len);
+}
+
+void mg_sl_if_recved(struct mg_connection *nc, size_t len) {
+  (void) nc;
+  (void) len;
+}
+
+int mg_sl_if_create_conn(struct mg_connection *nc) {
+  (void) nc;
+  return 1;
+}
+
+void mg_sl_if_destroy_conn(struct mg_connection *nc) {
+  if (nc->sock == INVALID_SOCKET) return;
+  /* For UDP, only close outgoing sockets or listeners. */
+  if (!(nc->flags & MG_F_UDP) || nc->listener == NULL) {
+    sl_Close(nc->sock);
+  }
+  nc->sock = INVALID_SOCKET;
+}
+
+static int mg_accept_conn(struct mg_connection *lc) {
+  struct mg_connection *nc;
+  union socket_address sa;
+  socklen_t sa_len = sizeof(sa);
+  sock_t sock = sl_Accept(lc->sock, &sa.sa, &sa_len);
+  if (sock < 0) {
+    DBG(("%p: failed to accept: %d", lc, sock));
+    return 0;
+  }
+  nc = mg_if_accept_new_conn(lc);
+  if (nc == NULL) {
+    sl_Close(sock);
+    return 0;
+  }
+  DBG(("%p conn from %s:%d", nc, inet_ntoa(sa.sin.sin_addr),
+       ntohs(sa.sin.sin_port)));
+  mg_sock_set(nc, sock);
+  if (nc->flags & MG_F_SSL) nc->flags |= MG_F_SSL_HANDSHAKE_DONE;
+  mg_if_accept_tcp_cb(nc, &sa, sa_len);
+  return 1;
+}
+
+/* 'sa' must be an initialized address to bind to */
+static sock_t mg_open_listening_socket(struct mg_connection *nc,
+                                       union socket_address *sa, int type,
+                                       int proto) {
+  int r;
+  socklen_t sa_len =
+      (sa->sa.sa_family == AF_INET) ? sizeof(sa->sin) : sizeof(sa->sin6);
+  sock_t sock = sl_Socket(sa->sa.sa_family, type, proto);
+  if (sock < 0) return sock;
+  if ((r = sl_Bind(sock, &sa->sa, sa_len)) < 0) goto clean;
+  if (type != SOCK_DGRAM) {
+#if MG_ENABLE_SSL
+    if ((r = sl_set_ssl_opts(sock, nc)) < 0) goto clean;
+#endif
+    if ((r = sl_Listen(sock, SOMAXCONN)) < 0) goto clean;
+  }
+  mg_set_non_blocking_mode(sock);
+clean:
+  if (r < 0) {
+    sl_Close(sock);
+    sock = r;
+  }
+  return sock;
+}
+
+static void mg_write_to_socket(struct mg_connection *nc) {
+  struct mbuf *io = &nc->send_mbuf;
+  int n = 0;
+
+  if (nc->flags & MG_F_UDP) {
+    n = sl_SendTo(nc->sock, io->buf, io->len, 0, &nc->sa.sa,
+                  sizeof(nc->sa.sin));
+    DBG(("%p %d %d %d %s:%hu", nc, nc->sock, n, errno,
+         inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port)));
+  } else {
+    n = (int) sl_Send(nc->sock, io->buf, io->len, 0);
+    DBG(("%p %d bytes -> %d", nc, n, nc->sock));
+  }
+
+  if (n > 0) {
+    mg_if_sent_cb(nc, n);
+  } else if (n < 0 && mg_is_error(n)) {
+    /* Something went wrong, drop the connection. */
+    nc->flags |= MG_F_CLOSE_IMMEDIATELY;
+  }
+}
+
+MG_INTERNAL size_t recv_avail_size(struct mg_connection *conn, size_t max) {
+  size_t avail;
+  if (conn->recv_mbuf_limit < conn->recv_mbuf.len) return 0;
+  avail = conn->recv_mbuf_limit - conn->recv_mbuf.len;
+  return avail > max ? max : avail;
+}
+
+static void mg_handle_tcp_read(struct mg_connection *conn) {
+  int n = 0;
+  char *buf = (char *) MG_MALLOC(MG_TCP_RECV_BUFFER_SIZE);
+
+  if (buf == NULL) {
+    DBG(("OOM"));
+    return;
+  }
+
+  n = (int) sl_Recv(conn->sock, buf,
+                    recv_avail_size(conn, MG_TCP_RECV_BUFFER_SIZE), 0);
+  DBG(("%p %d bytes <- %d", conn, n, conn->sock));
+  if (n > 0) {
+    mg_if_recv_tcp_cb(conn, buf, n, 1 /* own */);
+  } else {
+    MG_FREE(buf);
+  }
+  if (n == 0) {
+    /* Orderly shutdown of the socket, try flushing output. */
+    conn->flags |= MG_F_SEND_AND_CLOSE;
+  } else if (mg_is_error(n)) {
+    conn->flags |= MG_F_CLOSE_IMMEDIATELY;
+  }
+}
+
+static void mg_handle_udp_read(struct mg_connection *nc) {
+  char *buf = (char *) MG_MALLOC(MG_UDP_RECV_BUFFER_SIZE);
+  if (buf == NULL) return;
+  union socket_address sa;
+  socklen_t sa_len = sizeof(sa);
+  int n = sl_RecvFrom(nc->sock, buf, MG_UDP_RECV_BUFFER_SIZE, 0,
+                      (SlSockAddr_t *) &sa, &sa_len);
+  DBG(("%p %d bytes from %s:%d", nc, n, inet_ntoa(nc->sa.sin.sin_addr),
+       ntohs(nc->sa.sin.sin_port)));
+  if (n > 0) {
+    mg_if_recv_udp_cb(nc, buf, n, &sa, sa_len);
+  } else {
+    MG_FREE(buf);
+  }
+}
+
+#define _MG_F_FD_CAN_READ 1
+#define _MG_F_FD_CAN_WRITE 1 << 1
+#define _MG_F_FD_ERROR 1 << 2
+
+void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) {
+  DBG(("%p fd=%d fd_flags=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock,
+       fd_flags, nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len));
+
+  if (nc->flags & MG_F_CONNECTING) {
+    if (nc->flags & MG_F_UDP || nc->err != SL_ERROR_BSD_EALREADY) {
+      mg_if_connect_cb(nc, nc->err);
+    } else {
+      /* In SimpleLink, to get status of non-blocking connect() we need to wait
+       * until socket is writable and repeat the call to sl_Connect again,
+       * which will now return the real status. */
+      if (fd_flags & _MG_F_FD_CAN_WRITE) {
+        nc->err = sl_Connect(nc->sock, &nc->sa.sa, sizeof(nc->sa.sin));
+        DBG(("%p conn res=%d", nc, nc->err));
+        if (nc->err == SL_ERROR_BSD_ESECSNOVERIFY ||
+            /* TODO(rojer): Provide API to set the date for verification. */
+            nc->err == SL_ERROR_BSD_ESECDATEERROR
+#if SL_MAJOR_VERSION_NUM >= 2
+            /* Per SWRU455, this error does not mean verification failed,
+             * it only means that the cert used is not present in the trusted
+             * root CA catalog. Which is perfectly fine. */
+            ||
+            nc->err == SL_ERROR_BSD_ESECUNKNOWNROOTCA
+#endif
+            ) {
+          nc->err = 0;
+        }
+        if (nc->flags & MG_F_SSL && nc->err == 0) {
+          nc->flags |= MG_F_SSL_HANDSHAKE_DONE;
+        }
+        mg_if_connect_cb(nc, nc->err);
+      }
+    }
+    /* Ignore read/write in further processing, we've handled it. */
+    fd_flags &= ~(_MG_F_FD_CAN_READ | _MG_F_FD_CAN_WRITE);
+  }
+
+  if (fd_flags & _MG_F_FD_CAN_READ) {
+    if (nc->flags & MG_F_UDP) {
+      mg_handle_udp_read(nc);
+    } else {
+      if (nc->flags & MG_F_LISTENING) {
+        mg_accept_conn(nc);
+      } else {
+        mg_handle_tcp_read(nc);
+      }
+    }
+  }
+
+  if (!(nc->flags & MG_F_CLOSE_IMMEDIATELY)) {
+    if ((fd_flags & _MG_F_FD_CAN_WRITE) && nc->send_mbuf.len > 0) {
+      mg_write_to_socket(nc);
+    }
+
+    if (!(fd_flags & (_MG_F_FD_CAN_READ | _MG_F_FD_CAN_WRITE))) {
+      mg_if_poll(nc, now);
+    }
+    mg_if_timer(nc, now);
+  }
+
+  DBG(("%p after fd=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, nc->flags,
+       (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len));
+}
+
+/* Associate a socket to a connection. */
+void mg_sl_if_sock_set(struct mg_connection *nc, sock_t sock) {
+  mg_set_non_blocking_mode(sock);
+  nc->sock = sock;
+  DBG(("%p %d", nc, sock));
+}
+
+void mg_sl_if_init(struct mg_iface *iface) {
+  (void) iface;
+  DBG(("%p using sl_Select()", iface->mgr));
+}
+
+void mg_sl_if_free(struct mg_iface *iface) {
+  (void) iface;
+}
+
+void mg_sl_if_add_conn(struct mg_connection *nc) {
+  (void) nc;
+}
+
+void mg_sl_if_remove_conn(struct mg_connection *nc) {
+  (void) nc;
+}
+
+time_t mg_sl_if_poll(struct mg_iface *iface, int timeout_ms) {
+  struct mg_mgr *mgr = iface->mgr;
+  double now = mg_time();
+  double min_timer;
+  struct mg_connection *nc, *tmp;
+  struct SlTimeval_t tv;
+  SlFdSet_t read_set, write_set, err_set;
+  sock_t max_fd = INVALID_SOCKET;
+  int num_fds, num_ev = 0, num_timers = 0;
+
+  SL_SOCKET_FD_ZERO(&read_set);
+  SL_SOCKET_FD_ZERO(&write_set);
+  SL_SOCKET_FD_ZERO(&err_set);
+
+  /*
+   * Note: it is ok to have connections with sock == INVALID_SOCKET in the list,
+   * e.g. timer-only "connections".
+   */
+  min_timer = 0;
+  for (nc = mgr->active_connections, num_fds = 0; nc != NULL; nc = tmp) {
+    tmp = nc->next;
+
+    if (nc->sock != INVALID_SOCKET) {
+      num_fds++;
+
+      if (!(nc->flags & MG_F_WANT_WRITE) &&
+          nc->recv_mbuf.len < nc->recv_mbuf_limit &&
+          (!(nc->flags & MG_F_UDP) || nc->listener == NULL)) {
+        SL_SOCKET_FD_SET(nc->sock, &read_set);
+        if (max_fd == INVALID_SOCKET || nc->sock > max_fd) max_fd = nc->sock;
+      }
+
+      if (((nc->flags & MG_F_CONNECTING) && !(nc->flags & MG_F_WANT_READ)) ||
+          (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING))) {
+        SL_SOCKET_FD_SET(nc->sock, &write_set);
+        SL_SOCKET_FD_SET(nc->sock, &err_set);
+        if (max_fd == INVALID_SOCKET || nc->sock > max_fd) max_fd = nc->sock;
+      }
+    }
+
+    if (nc->ev_timer_time > 0) {
+      if (num_timers == 0 || nc->ev_timer_time < min_timer) {
+        min_timer = nc->ev_timer_time;
+      }
+      num_timers++;
+    }
+  }
+
+  /*
+   * If there is a timer to be fired earlier than the requested timeout,
+   * adjust the timeout.
+   */
+  if (num_timers > 0) {
+    double timer_timeout_ms = (min_timer - mg_time()) * 1000 + 1 /* rounding */;
+    if (timer_timeout_ms < timeout_ms) {
+      timeout_ms = timer_timeout_ms;
+    }
+  }
+  if (timeout_ms < 0) timeout_ms = 0;
+
+  tv.tv_sec = timeout_ms / 1000;
+  tv.tv_usec = (timeout_ms % 1000) * 1000;
+
+  if (num_fds > 0) {
+    num_ev = sl_Select((int) max_fd + 1, &read_set, &write_set, &err_set, &tv);
+  }
+
+  now = mg_time();
+  DBG(("sl_Select @ %ld num_ev=%d of %d, timeout=%d", (long) now, num_ev,
+       num_fds, timeout_ms));
+
+  for (nc = mgr->active_connections; nc != NULL; nc = tmp) {
+    int fd_flags = 0;
+    if (nc->sock != INVALID_SOCKET) {
+      if (num_ev > 0) {
+        fd_flags =
+            (SL_SOCKET_FD_ISSET(nc->sock, &read_set) &&
+                     (!(nc->flags & MG_F_UDP) || nc->listener == NULL)
+                 ? _MG_F_FD_CAN_READ
+                 : 0) |
+            (SL_SOCKET_FD_ISSET(nc->sock, &write_set) ? _MG_F_FD_CAN_WRITE
+                                                      : 0) |
+            (SL_SOCKET_FD_ISSET(nc->sock, &err_set) ? _MG_F_FD_ERROR : 0);
+      }
+      /* SimpleLink does not report UDP sockets as writable. */
+      if (nc->flags & MG_F_UDP && nc->send_mbuf.len > 0) {
+        fd_flags |= _MG_F_FD_CAN_WRITE;
+      }
+    }
+    tmp = nc->next;
+    mg_mgr_handle_conn(nc, fd_flags, now);
+  }
+
+  for (nc = mgr->active_connections; nc != NULL; nc = tmp) {
+    tmp = nc->next;
+    if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) ||
+        (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) {
+      mg_close_conn(nc);
+    }
+  }
+
+  return now;
+}
+
+void mg_sl_if_get_conn_addr(struct mg_connection *nc, int remote,
+                            union socket_address *sa) {
+  /* SimpleLink does not provide a way to get socket's peer address after
+   * accept or connect. Address should have been preserved in the connection,
+   * so we do our best here by using it. */
+  if (remote) memcpy(sa, &nc->sa, sizeof(*sa));
+}
+
+void sl_restart_cb(struct mg_mgr *mgr) {
+  /*
+   * SimpleLink has been restarted, meaning all sockets have been invalidated.
+   * We try our best - we'll restart the listeners, but for outgoing
+   * connections we have no option but to terminate.
+   */
+  struct mg_connection *nc;
+  for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) {
+    if (nc->sock == INVALID_SOCKET) continue; /* Could be a timer */
+    if (nc->flags & MG_F_LISTENING) {
+      DBG(("restarting %p %s:%d", nc, inet_ntoa(nc->sa.sin.sin_addr),
+           ntohs(nc->sa.sin.sin_port)));
+      int res = (nc->flags & MG_F_UDP ? mg_sl_if_listen_udp(nc, &nc->sa)
+                                      : mg_sl_if_listen_tcp(nc, &nc->sa));
+      if (res == 0) continue;
+      /* Well, we tried and failed. Fall through to closing. */
+    }
+    nc->sock = INVALID_SOCKET;
+    DBG(("terminating %p %s:%d", nc, inet_ntoa(nc->sa.sin.sin_addr),
+         ntohs(nc->sa.sin.sin_port)));
+    /* TODO(rojer): Outgoing UDP? */
+    nc->flags |= MG_F_CLOSE_IMMEDIATELY;
+  }
+}
+
+/* clang-format off */
+#define MG_SL_IFACE_VTABLE                                              \
+  {                                                                     \
+    mg_sl_if_init,                                                      \
+    mg_sl_if_free,                                                      \
+    mg_sl_if_add_conn,                                                  \
+    mg_sl_if_remove_conn,                                               \
+    mg_sl_if_poll,                                                      \
+    mg_sl_if_listen_tcp,                                                \
+    mg_sl_if_listen_udp,                                                \
+    mg_sl_if_connect_tcp,                                               \
+    mg_sl_if_connect_udp,                                               \
+    mg_sl_if_tcp_send,                                                  \
+    mg_sl_if_udp_send,                                                  \
+    mg_sl_if_recved,                                                    \
+    mg_sl_if_create_conn,                                               \
+    mg_sl_if_destroy_conn,                                              \
+    mg_sl_if_sock_set,                                                  \
+    mg_sl_if_get_conn_addr,                                             \
+  }
+/* clang-format on */
+
+const struct mg_iface_vtable mg_simplelink_iface_vtable = MG_SL_IFACE_VTABLE;
+#if MG_NET_IF == MG_NET_IF_SIMPLELINK
+const struct mg_iface_vtable mg_default_iface_vtable = MG_SL_IFACE_VTABLE;
+#endif
+
+#endif /* MG_ENABLE_NET_IF_SIMPLELINK */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/simplelink/sl_ssl_if.c"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_SIMPLELINK
+
+/* Amalgamated: #include "common/mg_mem.h" */
+
+#ifndef MG_SSL_IF_SIMPLELINK_SLFS_PREFIX
+#define MG_SSL_IF_SIMPLELINK_SLFS_PREFIX "SL:"
+#endif
+
+#define MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN \
+  (sizeof(MG_SSL_IF_SIMPLELINK_SLFS_PREFIX) - 1)
+
+struct mg_ssl_if_ctx {
+  char *ssl_cert;
+  char *ssl_key;
+  char *ssl_ca_cert;
+  char *ssl_server_name;
+};
+
+void mg_ssl_if_init() {
+}
+
+enum mg_ssl_if_result mg_ssl_if_conn_init(
+    struct mg_connection *nc, const struct mg_ssl_if_conn_params *params,
+    const char **err_msg) {
+  struct mg_ssl_if_ctx *ctx =
+      (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx));
+  if (ctx == NULL) {
+    MG_SET_PTRPTR(err_msg, "Out of memory");
+    return MG_SSL_ERROR;
+  }
+  nc->ssl_if_data = ctx;
+
+  if (params->cert != NULL || params->key != NULL) {
+    if (params->cert != NULL && params->key != NULL) {
+      ctx->ssl_cert = strdup(params->cert);
+      ctx->ssl_key = strdup(params->key);
+    } else {
+      MG_SET_PTRPTR(err_msg, "Both cert and key are required.");
+      return MG_SSL_ERROR;
+    }
+  }
+  if (params->ca_cert != NULL && strcmp(params->ca_cert, "*") != 0) {
+    ctx->ssl_ca_cert = strdup(params->ca_cert);
+  }
+  /* TODO(rojer): cipher_suites. */
+  if (params->server_name != NULL) {
+    ctx->ssl_server_name = strdup(params->server_name);
+  }
+  return MG_SSL_OK;
+}
+
+void mg_ssl_if_conn_close_notify(struct mg_connection *nc) {
+  /* Nothing to do */
+  (void) nc;
+}
+
+void mg_ssl_if_conn_free(struct mg_connection *nc) {
+  struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data;
+  if (ctx == NULL) return;
+  nc->ssl_if_data = NULL;
+  MG_FREE(ctx->ssl_cert);
+  MG_FREE(ctx->ssl_key);
+  MG_FREE(ctx->ssl_ca_cert);
+  MG_FREE(ctx->ssl_server_name);
+  memset(ctx, 0, sizeof(*ctx));
+  MG_FREE(ctx);
+}
+
+bool pem_to_der(const char *pem_file, const char *der_file) {
+  bool ret = false;
+  FILE *pf = NULL, *df = NULL;
+  bool writing = false;
+  pf = fopen(pem_file, "r");
+  if (pf == NULL) goto clean;
+  remove(der_file);
+  fs_slfs_set_new_file_size(der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN,
+                            2048);
+  df = fopen(der_file, "w");
+  if (df == NULL) goto clean;
+  while (1) {
+    char pem_buf[70];
+    char der_buf[48];
+    if (!fgets(pem_buf, sizeof(pem_buf), pf)) break;
+    if (writing) {
+      if (strstr(pem_buf, "-----END ") != NULL) {
+        ret = true;
+        break;
+      }
+      int l = 0;
+      while (!isspace((unsigned int) pem_buf[l])) l++;
+      int der_len = 0;
+      cs_base64_decode((const unsigned char *) pem_buf, sizeof(pem_buf),
+                       der_buf, &der_len);
+      if (der_len <= 0) break;
+      if (fwrite(der_buf, 1, der_len, df) != der_len) break;
+    } else if (strstr(pem_buf, "-----BEGIN ") != NULL) {
+      writing = true;
+    }
+  }
+
+clean:
+  if (pf != NULL) fclose(pf);
+  if (df != NULL) {
+    fclose(df);
+    if (!ret) remove(der_file);
+  }
+  return ret;
+}
+
+#if MG_ENABLE_FILESYSTEM && defined(MG_FS_SLFS)
+/* If the file's extension is .pem, convert it to DER format and put on SLFS. */
+static char *sl_pem2der(const char *pem_file) {
+  const char *pem_ext = strstr(pem_file, ".pem");
+  if (pem_ext == NULL || *(pem_ext + 4) != '\0') {
+    return strdup(pem_file);
+  }
+  char *der_file = NULL;
+  /* DER file must be located on SLFS, add prefix. */
+  int l = mg_asprintf(&der_file, 0, MG_SSL_IF_SIMPLELINK_SLFS_PREFIX "%.*s.der",
+                      (int) (pem_ext - pem_file), pem_file);
+  if (der_file == NULL) return NULL;
+  bool result = false;
+  cs_stat_t st;
+  if (mg_stat(der_file, &st) != 0) {
+    result = pem_to_der(pem_file, der_file);
+    LOG(LL_DEBUG, ("%s -> %s = %d", pem_file, der_file, result));
+  } else {
+    /* File exists, assume it's already been converted. */
+    result = true;
+  }
+  if (result) {
+    /* Strip the SL: prefix we added since NWP does not expect it. */
+    memmove(der_file, der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN,
+            l - 2 /* including \0 */);
+  } else {
+    MG_FREE(der_file);
+    der_file = NULL;
+  }
+  return der_file;
+}
+#else
+static char *sl_pem2der(const char *pem_file) {
+  return strdup(pem_file);
+}
+#endif
+
+int sl_set_ssl_opts(int sock, struct mg_connection *nc) {
+  int err;
+  const struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data;
+  DBG(("%p ssl ctx: %p", nc, ctx));
+
+  if (ctx != NULL) {
+    DBG(("%p %s,%s,%s,%s", nc, (ctx->ssl_cert ? ctx->ssl_cert : "-"),
+         (ctx->ssl_key ? ctx->ssl_cert : "-"),
+         (ctx->ssl_ca_cert ? ctx->ssl_ca_cert : "-"),
+         (ctx->ssl_server_name ? ctx->ssl_server_name : "-")));
+    if (ctx->ssl_cert != NULL && ctx->ssl_key != NULL) {
+      char *ssl_cert = sl_pem2der(ctx->ssl_cert);
+      char *ssl_key = sl_pem2der(ctx->ssl_key);
+      if (ssl_cert != NULL && ssl_key != NULL) {
+        err = sl_SetSockOpt(sock, SL_SOL_SOCKET,
+                            SL_SO_SECURE_FILES_CERTIFICATE_FILE_NAME, ssl_cert,
+                            strlen(ssl_cert));
+        LOG(LL_INFO, ("CERTIFICATE_FILE_NAME %s -> %d", ssl_cert, err));
+        err = sl_SetSockOpt(sock, SL_SOL_SOCKET,
+                            SL_SO_SECURE_FILES_PRIVATE_KEY_FILE_NAME, ssl_key,
+                            strlen(ssl_key));
+        LOG(LL_INFO, ("PRIVATE_KEY_FILE_NAME %s -> %d", ssl_key, err));
+      } else {
+        err = -1;
+      }
+      MG_FREE(ssl_cert);
+      MG_FREE(ssl_key);
+      if (err != 0) return err;
+    }
+    if (ctx->ssl_ca_cert != NULL) {
+      if (ctx->ssl_ca_cert[0] != '\0') {
+        char *ssl_ca_cert = sl_pem2der(ctx->ssl_ca_cert);
+        if (ssl_ca_cert != NULL) {
+          err = sl_SetSockOpt(sock, SL_SOL_SOCKET,
+                              SL_SO_SECURE_FILES_CA_FILE_NAME, ssl_ca_cert,
+                              strlen(ssl_ca_cert));
+          LOG(LL_INFO, ("CA_FILE_NAME %s -> %d", ssl_ca_cert, err));
+        } else {
+          err = -1;
+        }
+        MG_FREE(ssl_ca_cert);
+        if (err != 0) return err;
+      }
+    }
+    if (ctx->ssl_server_name != NULL) {
+      err = sl_SetSockOpt(sock, SL_SOL_SOCKET,
+                          SL_SO_SECURE_DOMAIN_NAME_VERIFICATION,
+                          ctx->ssl_server_name, strlen(ctx->ssl_server_name));
+      DBG(("DOMAIN_NAME_VERIFICATION %s -> %d", ctx->ssl_server_name, err));
+      /* Domain name verificationw as added in a NWP service pack, older
+       * versions return SL_ERROR_BSD_ENOPROTOOPT. There isn't much we can do
+       * about it,
+       * so we ignore the error. */
+      if (err != 0 && err != SL_ERROR_BSD_ENOPROTOOPT) return err;
+    }
+  }
+  return 0;
+}
+
+#endif /* MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_SIMPLELINK */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/lwip/mg_lwip_net_if.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_PLATFORMS_LWIP_MG_NET_IF_LWIP_H_
+#define CS_COMMON_PLATFORMS_LWIP_MG_NET_IF_LWIP_H_
+
+#ifndef MG_ENABLE_NET_IF_LWIP_LOW_LEVEL
+#define MG_ENABLE_NET_IF_LWIP_LOW_LEVEL MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL
+#endif
+
+#if MG_ENABLE_NET_IF_LWIP_LOW_LEVEL
+
+#include 
+
+extern const struct mg_iface_vtable mg_lwip_iface_vtable;
+
+struct mg_lwip_conn_state {
+  struct mg_connection *nc;
+  struct mg_connection *lc;
+  union {
+    struct tcp_pcb *tcp;
+    struct udp_pcb *udp;
+  } pcb;
+  err_t err;
+  size_t num_sent; /* Number of acknowledged bytes to be reported to the core */
+  struct pbuf *rx_chain; /* Chain of incoming data segments. */
+  size_t rx_offset; /* Offset within the first pbuf (if partially consumed) */
+  /* Last SSL write size, for retries. */
+  int last_ssl_write_size;
+  /* Whether MG_SIG_RECV is already pending for this connection */
+  int recv_pending : 1;
+  /* Whether the connection is about to close, just `rx_chain` needs to drain */
+  int draining_rx_chain : 1;
+};
+
+enum mg_sig_type {
+  MG_SIG_CONNECT_RESULT = 1,
+  MG_SIG_RECV = 2,
+  MG_SIG_CLOSE_CONN = 3,
+  MG_SIG_TOMBSTONE = 4,
+  MG_SIG_ACCEPT = 5,
+};
+
+void mg_lwip_post_signal(enum mg_sig_type sig, struct mg_connection *nc);
+
+/* To be implemented by the platform. */
+void mg_lwip_mgr_schedule_poll(struct mg_mgr *mgr);
+
+#endif /* MG_ENABLE_NET_IF_LWIP_LOW_LEVEL */
+
+#endif /* CS_COMMON_PLATFORMS_LWIP_MG_NET_IF_LWIP_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/lwip/mg_lwip_net_if.c"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#if MG_ENABLE_NET_IF_LWIP_LOW_LEVEL
+
+/* Amalgamated: #include "common/mg_mem.h" */
+
+#include 
+#include 
+#include 
+#include 
+#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105
+#include  /* For tcp_seg */
+#else
+#include 
+#endif
+#include 
+
+/* Amalgamated: #include "common/cs_dbg.h" */
+
+/*
+ * Newest versions of LWIP have ip_2_ip4, older have ipX_2_ip,
+ * even older have nothing.
+ */
+#ifndef ip_2_ip4
+#ifdef ipX_2_ip
+#define ip_2_ip4(addr) ipX_2_ip(addr)
+#else
+#define ip_2_ip4(addr) (addr)
+#endif
+#endif
+
+/*
+ * Depending on whether Mongoose is compiled with ipv6 support, use right
+ * lwip functions
+ */
+#if MG_ENABLE_IPV6
+#define TCP_NEW tcp_new_ip6
+#define TCP_BIND tcp_bind_ip6
+#define UDP_BIND udp_bind_ip6
+#define IPADDR_NTOA(x) ip6addr_ntoa((const ip6_addr_t *)(x))
+#define SET_ADDR(dst, src)                               \
+  memcpy((dst)->sin6.sin6_addr.s6_addr, (src)->ip6.addr, \
+         sizeof((dst)->sin6.sin6_addr.s6_addr))
+#else
+#define TCP_NEW tcp_new
+#define TCP_BIND tcp_bind
+#define UDP_BIND udp_bind
+#define IPADDR_NTOA ipaddr_ntoa
+#define SET_ADDR(dst, src) (dst)->sin.sin_addr.s_addr = ip_2_ip4(src)->addr
+#endif
+
+#if NO_SYS
+#define tcpip_callback(fn, arg) (fn)(arg)
+typedef void (*tcpip_callback_fn)(void *arg);
+#endif
+
+void mg_lwip_ssl_do_hs(struct mg_connection *nc);
+void mg_lwip_ssl_send(struct mg_connection *nc);
+void mg_lwip_ssl_recv(struct mg_connection *nc);
+
+void mg_lwip_if_init(struct mg_iface *iface);
+void mg_lwip_if_free(struct mg_iface *iface);
+void mg_lwip_if_add_conn(struct mg_connection *nc);
+void mg_lwip_if_remove_conn(struct mg_connection *nc);
+time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms);
+
+#if defined(RTOS_SDK) || defined(ESP_PLATFORM)
+extern void mgos_lock();
+extern void mgos_unlock();
+#else
+#define mgos_lock()
+#define mgos_unlock()
+#endif
+
+static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p);
+
+#if LWIP_TCP_KEEPALIVE
+void mg_lwip_set_keepalive_params(struct mg_connection *nc, int idle,
+                                  int interval, int count) {
+  if (nc->sock == INVALID_SOCKET || nc->flags & MG_F_UDP) {
+    return;
+  }
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  struct tcp_pcb *tpcb = cs->pcb.tcp;
+  if (idle > 0 && interval > 0 && count > 0) {
+    tpcb->keep_idle = idle * 1000;
+    tpcb->keep_intvl = interval * 1000;
+    tpcb->keep_cnt = count;
+    tpcb->so_options |= SOF_KEEPALIVE;
+  } else {
+    tpcb->so_options &= ~SOF_KEEPALIVE;
+  }
+}
+#elif !defined(MG_NO_LWIP_TCP_KEEPALIVE)
+#warning LWIP TCP keepalive is disabled. Please consider enabling it.
+#endif /* LWIP_TCP_KEEPALIVE */
+
+static err_t mg_lwip_tcp_conn_cb(void *arg, struct tcp_pcb *tpcb, err_t err) {
+  struct mg_connection *nc = (struct mg_connection *) arg;
+  DBG(("%p connect to %s:%u = %d", nc, IPADDR_NTOA(ipX_2_ip(&tpcb->remote_ip)),
+       tpcb->remote_port, err));
+  if (nc == NULL) {
+    tcp_abort(tpcb);
+    return ERR_ARG;
+  }
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  cs->err = err;
+#if LWIP_TCP_KEEPALIVE
+  if (err == 0) mg_lwip_set_keepalive_params(nc, 60, 10, 6);
+#endif
+  mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc);
+  return ERR_OK;
+}
+
+static void mg_lwip_tcp_error_cb(void *arg, err_t err) {
+  struct mg_connection *nc = (struct mg_connection *) arg;
+  DBG(("%p conn error %d", nc, err));
+  if (nc == NULL) return;
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  cs->pcb.tcp = NULL; /* Has already been deallocated */
+  if (nc->flags & MG_F_CONNECTING) {
+    cs->err = err;
+    mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc);
+  } else {
+    mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
+  }
+}
+
+static err_t mg_lwip_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb,
+                                 struct pbuf *p, err_t err) {
+  struct mg_connection *nc = (struct mg_connection *) arg;
+  DBG(("%p %p %u %d", nc, tpcb, (p != NULL ? p->tot_len : 0), err));
+  if (p == NULL) {
+    if (nc != NULL && !(nc->flags & MG_F_CLOSE_IMMEDIATELY)) {
+      struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+      if (cs->rx_chain != NULL) {
+        /*
+         * rx_chain still contains non-consumed data, don't close the
+         * connection
+         */
+        cs->draining_rx_chain = 1;
+      } else {
+        mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
+      }
+    } else {
+      /* Tombstoned connection, do nothing. */
+    }
+    return ERR_OK;
+  } else if (nc == NULL) {
+    tcp_abort(tpcb);
+    return ERR_ARG;
+  }
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  /*
+   * If we get a chain of more than one segment at once, we need to bump
+   * refcount on the subsequent bufs to make them independent.
+   */
+  if (p->next != NULL) {
+    struct pbuf *q = p->next;
+    for (; q != NULL; q = q->next) pbuf_ref(q);
+  }
+  mgos_lock();
+  if (cs->rx_chain == NULL) {
+    cs->rx_offset = 0;
+  } else if (pbuf_clen(cs->rx_chain) >= 4) {
+    /* ESP SDK has a limited pool of 5 pbufs. We must not hog them all or RX
+     * will be completely blocked. We already have at least 4 in the chain,
+     * this one is, so we have to make a copy and release this one. */
+    struct pbuf *np = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+    if (np != NULL) {
+      pbuf_copy(np, p);
+      pbuf_free(p);
+      p = np;
+    }
+  }
+  mgos_unlock();
+  mg_lwip_recv_common(nc, p);
+  return ERR_OK;
+}
+
+static void mg_lwip_consume_rx_chain_tcp(struct mg_connection *nc) {
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  if (cs->rx_chain == NULL) return;
+#if MG_ENABLE_SSL
+  if (nc->flags & MG_F_SSL) {
+    if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) {
+      mg_lwip_ssl_recv(nc);
+    } else {
+      mg_lwip_ssl_do_hs(nc);
+    }
+    return;
+  }
+#endif
+  mgos_lock();
+  while (cs->rx_chain != NULL && nc->recv_mbuf.len < nc->recv_mbuf_limit) {
+    struct pbuf *seg = cs->rx_chain;
+
+    size_t seg_len = (seg->len - cs->rx_offset);
+    size_t buf_avail = (nc->recv_mbuf_limit - nc->recv_mbuf.len);
+    size_t len = MIN(seg_len, buf_avail);
+
+    char *data = (char *) MG_MALLOC(len);
+    if (data == NULL) {
+      mgos_unlock();
+      DBG(("OOM"));
+      return;
+    }
+    pbuf_copy_partial(seg, data, len, cs->rx_offset);
+    cs->rx_offset += len;
+    if (cs->rx_offset == cs->rx_chain->len) {
+      cs->rx_chain = pbuf_dechain(cs->rx_chain);
+      pbuf_free(seg);
+      cs->rx_offset = 0;
+    }
+    mgos_unlock();
+    mg_if_recv_tcp_cb(nc, data, len, 1 /* own */);
+    mgos_lock();
+  }
+  mgos_unlock();
+}
+
+static void mg_lwip_handle_recv_tcp(struct mg_connection *nc) {
+  mg_lwip_consume_rx_chain_tcp(nc);
+
+  if (nc->send_mbuf.len > 0) {
+    mg_lwip_mgr_schedule_poll(nc->mgr);
+  }
+}
+
+static err_t mg_lwip_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb,
+                                 u16_t num_sent) {
+  struct mg_connection *nc = (struct mg_connection *) arg;
+  DBG(("%p %p %u %p %p", nc, tpcb, num_sent, tpcb->unsent, tpcb->unacked));
+  if (nc == NULL) return ERR_OK;
+  if ((nc->flags & MG_F_SEND_AND_CLOSE) && !(nc->flags & MG_F_WANT_WRITE) &&
+      nc->send_mbuf.len == 0 && tpcb->unsent == NULL && tpcb->unacked == NULL) {
+    mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
+  }
+  return ERR_OK;
+}
+
+struct mg_lwip_if_connect_tcp_ctx {
+  struct mg_connection *nc;
+  const union socket_address *sa;
+};
+
+static void mg_lwip_if_connect_tcp_tcpip(void *arg) {
+  struct mg_lwip_if_connect_tcp_ctx *ctx =
+      (struct mg_lwip_if_connect_tcp_ctx *) arg;
+  struct mg_connection *nc = ctx->nc;
+  const union socket_address *sa = ctx->sa;
+
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  struct tcp_pcb *tpcb = TCP_NEW();
+  cs->pcb.tcp = tpcb;
+  ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr;
+  u16_t port = ntohs(sa->sin.sin_port);
+  tcp_arg(tpcb, nc);
+  tcp_err(tpcb, mg_lwip_tcp_error_cb);
+  tcp_sent(tpcb, mg_lwip_tcp_sent_cb);
+  tcp_recv(tpcb, mg_lwip_tcp_recv_cb);
+  cs->err = TCP_BIND(tpcb, IP_ADDR_ANY, 0 /* any port */);
+  DBG(("%p tcp_bind = %d", nc, cs->err));
+  if (cs->err != ERR_OK) {
+    mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc);
+    return;
+  }
+  cs->err = tcp_connect(tpcb, ip, port, mg_lwip_tcp_conn_cb);
+  DBG(("%p tcp_connect %p = %d", nc, tpcb, cs->err));
+  if (cs->err != ERR_OK) {
+    mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc);
+    return;
+  }
+}
+
+void mg_lwip_if_connect_tcp(struct mg_connection *nc,
+                            const union socket_address *sa) {
+  struct mg_lwip_if_connect_tcp_ctx ctx = {.nc = nc, .sa = sa};
+  tcpip_callback(mg_lwip_if_connect_tcp_tcpip, &ctx);
+}
+
+/*
+ * Lwip included in the SDKs for nRF5x chips has different type for the
+ * callback of `udp_recv()`
+ */
+#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105
+static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p,
+                                const ip_addr_t *addr, u16_t port)
+#else
+static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p,
+                                ip_addr_t *addr, u16_t port)
+#endif
+{
+  struct mg_connection *nc = (struct mg_connection *) arg;
+  DBG(("%p %s:%u %p %u %u", nc, IPADDR_NTOA(addr), port, p, p->ref, p->len));
+  /* Put address in a separate pbuf and tack it onto the packet. */
+  struct pbuf *sap =
+      pbuf_alloc(PBUF_RAW, sizeof(union socket_address), PBUF_RAM);
+  if (sap == NULL) {
+    pbuf_free(p);
+    return;
+  }
+  union socket_address *sa = (union socket_address *) sap->payload;
+#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105
+  sa->sin.sin_addr.s_addr = ip_2_ip4(addr)->addr;
+#else
+  sa->sin.sin_addr.s_addr = addr->addr;
+#endif
+  sa->sin.sin_port = htons(port);
+  /* Logic in the recv handler requires that there be exactly one data pbuf. */
+  p = pbuf_coalesce(p, PBUF_RAW);
+  pbuf_chain(sap, p);
+  mg_lwip_recv_common(nc, sap);
+  (void) pcb;
+}
+
+static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p) {
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  mgos_lock();
+  if (cs->rx_chain == NULL) {
+    cs->rx_chain = p;
+  } else {
+    pbuf_chain(cs->rx_chain, p);
+  }
+  if (!cs->recv_pending) {
+    cs->recv_pending = 1;
+    mg_lwip_post_signal(MG_SIG_RECV, nc);
+  }
+  mgos_unlock();
+}
+
+static void mg_lwip_handle_recv_udp(struct mg_connection *nc) {
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  /*
+   * For UDP, RX chain consists of interleaved address and packet bufs:
+   * Address pbuf followed by exactly one data pbuf (recv_cb took care of that).
+   */
+  while (cs->rx_chain != NULL) {
+    struct pbuf *sap = cs->rx_chain;
+    struct pbuf *p = sap->next;
+    cs->rx_chain = pbuf_dechain(p);
+    size_t data_len = p->len;
+    char *data = (char *) MG_MALLOC(data_len);
+    if (data != NULL) {
+      pbuf_copy_partial(p, data, data_len, 0);
+      pbuf_free(p);
+      mg_if_recv_udp_cb(nc, data, data_len,
+                        (union socket_address *) sap->payload, sap->len);
+      pbuf_free(sap);
+    } else {
+      pbuf_free(p);
+      pbuf_free(sap);
+    }
+  }
+}
+
+static void mg_lwip_if_connect_udp_tcpip(void *arg) {
+  struct mg_connection *nc = (struct mg_connection *) arg;
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  struct udp_pcb *upcb = udp_new();
+  cs->err = UDP_BIND(upcb, IP_ADDR_ANY, 0 /* any port */);
+  DBG(("%p udp_bind %p = %d", nc, upcb, cs->err));
+  if (cs->err == ERR_OK) {
+    udp_recv(upcb, mg_lwip_udp_recv_cb, nc);
+    cs->pcb.udp = upcb;
+  } else {
+    udp_remove(upcb);
+  }
+  mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc);
+}
+
+void mg_lwip_if_connect_udp(struct mg_connection *nc) {
+  tcpip_callback(mg_lwip_if_connect_udp_tcpip, nc);
+}
+
+void mg_lwip_accept_conn(struct mg_connection *nc, struct tcp_pcb *tpcb) {
+  union socket_address sa;
+  SET_ADDR(&sa, &tpcb->remote_ip);
+  sa.sin.sin_port = htons(tpcb->remote_port);
+  mg_if_accept_tcp_cb(nc, &sa, sizeof(sa.sin));
+}
+
+static void tcp_close_tcpip(void *arg) {
+  tcp_close((struct tcp_pcb *) arg);
+}
+
+void mg_lwip_handle_accept(struct mg_connection *nc) {
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  if (cs->pcb.tcp == NULL) return;
+#if MG_ENABLE_SSL
+  if (cs->lc->flags & MG_F_SSL) {
+    if (mg_ssl_if_conn_accept(nc, cs->lc) != MG_SSL_OK) {
+      LOG(LL_ERROR, ("SSL error"));
+      tcpip_callback(tcp_close_tcpip, cs->pcb.tcp);
+    }
+  } else
+#endif
+  {
+    mg_lwip_accept_conn(nc, cs->pcb.tcp);
+  }
+}
+
+static err_t mg_lwip_accept_cb(void *arg, struct tcp_pcb *newtpcb, err_t err) {
+  struct mg_connection *lc = (struct mg_connection *) arg, *nc;
+  struct mg_lwip_conn_state *lcs, *cs;
+  struct tcp_pcb_listen *lpcb;
+  LOG(LL_DEBUG,
+      ("%p conn %p from %s:%u", lc, newtpcb,
+       IPADDR_NTOA(ipX_2_ip(&newtpcb->remote_ip)), newtpcb->remote_port));
+  if (lc == NULL) {
+    tcp_abort(newtpcb);
+    return ERR_ABRT;
+  }
+  lcs = (struct mg_lwip_conn_state *) lc->sock;
+  lpcb = (struct tcp_pcb_listen *) lcs->pcb.tcp;
+#if TCP_LISTEN_BACKLOG
+  tcp_accepted(lpcb);
+#endif
+  nc = mg_if_accept_new_conn(lc);
+  if (nc == NULL) {
+    tcp_abort(newtpcb);
+    return ERR_ABRT;
+  }
+  cs = (struct mg_lwip_conn_state *) nc->sock;
+  cs->lc = lc;
+  cs->pcb.tcp = newtpcb;
+  /* We need to set up callbacks before returning because data may start
+   * arriving immediately. */
+  tcp_arg(newtpcb, nc);
+  tcp_err(newtpcb, mg_lwip_tcp_error_cb);
+  tcp_sent(newtpcb, mg_lwip_tcp_sent_cb);
+  tcp_recv(newtpcb, mg_lwip_tcp_recv_cb);
+#if LWIP_TCP_KEEPALIVE
+  mg_lwip_set_keepalive_params(nc, 60, 10, 6);
+#endif
+  mg_lwip_post_signal(MG_SIG_ACCEPT, nc);
+  (void) err;
+  (void) lpcb;
+  return ERR_OK;
+}
+
+struct mg_lwip_if_listen_ctx {
+  struct mg_connection *nc;
+  union socket_address *sa;
+  int ret;
+};
+
+static void mg_lwip_if_listen_tcp_tcpip(void *arg) {
+  struct mg_lwip_if_listen_ctx *ctx = (struct mg_lwip_if_listen_ctx *) arg;
+  struct mg_connection *nc = ctx->nc;
+  union socket_address *sa = ctx->sa;
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  struct tcp_pcb *tpcb = TCP_NEW();
+  ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr;
+  u16_t port = ntohs(sa->sin.sin_port);
+  cs->err = TCP_BIND(tpcb, ip, port);
+  DBG(("%p tcp_bind(%s:%u) = %d", nc, IPADDR_NTOA(ip), port, cs->err));
+  if (cs->err != ERR_OK) {
+    tcp_close(tpcb);
+    ctx->ret = -1;
+    return;
+  }
+  tcp_arg(tpcb, nc);
+  tpcb = tcp_listen(tpcb);
+  cs->pcb.tcp = tpcb;
+  tcp_accept(tpcb, mg_lwip_accept_cb);
+  ctx->ret = 0;
+}
+
+int mg_lwip_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) {
+  struct mg_lwip_if_listen_ctx ctx = {.nc = nc, .sa = sa};
+  tcpip_callback(mg_lwip_if_listen_tcp_tcpip, &ctx);
+  return ctx.ret;
+}
+
+static void mg_lwip_if_listen_udp_tcpip(void *arg) {
+  struct mg_lwip_if_listen_ctx *ctx = (struct mg_lwip_if_listen_ctx *) arg;
+  struct mg_connection *nc = ctx->nc;
+  union socket_address *sa = ctx->sa;
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  struct udp_pcb *upcb = udp_new();
+  ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr;
+  u16_t port = ntohs(sa->sin.sin_port);
+  cs->err = UDP_BIND(upcb, ip, port);
+  DBG(("%p udb_bind(%s:%u) = %d", nc, IPADDR_NTOA(ip), port, cs->err));
+  if (cs->err != ERR_OK) {
+    udp_remove(upcb);
+    ctx->ret = -1;
+  } else {
+    udp_recv(upcb, mg_lwip_udp_recv_cb, nc);
+    cs->pcb.udp = upcb;
+    ctx->ret = 0;
+  }
+}
+
+int mg_lwip_if_listen_udp(struct mg_connection *nc, union socket_address *sa) {
+  struct mg_lwip_if_listen_ctx ctx = {.nc = nc, .sa = sa};
+  tcpip_callback(mg_lwip_if_listen_udp_tcpip, &ctx);
+  return ctx.ret;
+}
+
+struct mg_lwip_tcp_write_ctx {
+  struct mg_connection *nc;
+  const void *data;
+  uint16_t len;
+  int ret;
+};
+
+static void tcp_output_tcpip(void *arg) {
+  tcp_output((struct tcp_pcb *) arg);
+}
+
+static void mg_lwip_tcp_write_tcpip(void *arg) {
+  struct mg_lwip_tcp_write_ctx *ctx = (struct mg_lwip_tcp_write_ctx *) arg;
+  struct mg_connection *nc = ctx->nc;
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  struct tcp_pcb *tpcb = cs->pcb.tcp;
+  size_t len = MIN(tpcb->mss, MIN(ctx->len, tpcb->snd_buf));
+  size_t unsent, unacked;
+  if (len == 0) {
+    DBG(("%p no buf avail %u %u %p %p", tpcb, tpcb->snd_buf, tpcb->snd_queuelen,
+         tpcb->unsent, tpcb->unacked));
+    tcpip_callback(tcp_output_tcpip, tpcb);
+    ctx->ret = 0;
+    return;
+  }
+  unsent = (tpcb->unsent != NULL ? tpcb->unsent->len : 0);
+  unacked = (tpcb->unacked != NULL ? tpcb->unacked->len : 0);
+/*
+ * On ESP8266 we only allow one TCP segment in flight at any given time.
+ * This may increase latency and reduce efficiency of tcp windowing,
+ * but memory is scarce and precious on that platform so we do this to
+ * reduce footprint.
+ */
+#if CS_PLATFORM == CS_P_ESP8266
+  if (unacked > 0) {
+    ctx->ret = 0;
+    return;
+  }
+  len = MIN(len, (TCP_MSS - unsent));
+#endif
+  cs->err = tcp_write(tpcb, ctx->data, len, TCP_WRITE_FLAG_COPY);
+  unsent = (tpcb->unsent != NULL ? tpcb->unsent->len : 0);
+  unacked = (tpcb->unacked != NULL ? tpcb->unacked->len : 0);
+  DBG(("%p tcp_write %u = %d, %u %u", tpcb, len, cs->err, unsent, unacked));
+  if (cs->err != ERR_OK) {
+    /*
+     * We ignore ERR_MEM because memory will be freed up when the data is sent
+     * and we'll retry.
+     */
+    ctx->ret = (cs->err == ERR_MEM ? 0 : -1);
+    return;
+  }
+  ctx->ret = len;
+}
+
+static int mg_lwip_tcp_write(struct mg_connection *nc, const void *data,
+                             uint16_t len) {
+  struct mg_lwip_tcp_write_ctx ctx = {.nc = nc, .data = data, .len = len};
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  struct tcp_pcb *tpcb = cs->pcb.tcp;
+  if (tpcb == NULL) {
+    return -1;
+  }
+  tcpip_callback(mg_lwip_tcp_write_tcpip, &ctx);
+  return ctx.ret;
+}
+
+struct udp_sendto_ctx {
+  struct udp_pcb *upcb;
+  struct pbuf *p;
+  ip_addr_t *ip;
+  uint16_t port;
+  int ret;
+};
+
+static void udp_sendto_tcpip(void *arg) {
+  struct udp_sendto_ctx *ctx = (struct udp_sendto_ctx *) arg;
+  ctx->ret = udp_sendto(ctx->upcb, ctx->p, ctx->ip, ctx->port);
+}
+
+static int mg_lwip_udp_send(struct mg_connection *nc, const void *data,
+                            uint16_t len) {
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  if (cs->pcb.udp == NULL) {
+    /*
+     * In case of UDP, this usually means, what
+     * async DNS resolve is still in progress and connection
+     * is not ready yet
+     */
+    DBG(("%p socket is not connected", nc));
+    return -1;
+  }
+  struct udp_pcb *upcb = cs->pcb.udp;
+  struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
+#if defined(LWIP_IPV4) && LWIP_IPV4 && defined(LWIP_IPV6) && LWIP_IPV6
+  ip_addr_t ip = {.u_addr.ip4.addr = nc->sa.sin.sin_addr.s_addr, .type = 0};
+#else
+  ip_addr_t ip = {.addr = nc->sa.sin.sin_addr.s_addr};
+#endif
+  u16_t port = ntohs(nc->sa.sin.sin_port);
+  if (p == NULL) {
+    DBG(("OOM"));
+    return 0;
+  }
+  memcpy(p->payload, data, len);
+  struct udp_sendto_ctx ctx = {.upcb = upcb, .p = p, .ip = &ip, .port = port};
+  tcpip_callback(udp_sendto_tcpip, &ctx);
+  cs->err = ctx.ret;
+  pbuf_free(p);
+  return (cs->err == ERR_OK ? len : -1);
+}
+
+static void mg_lwip_send_more(struct mg_connection *nc) {
+  int num_sent = 0;
+  if (nc->sock == INVALID_SOCKET) return;
+  if (nc->flags & MG_F_UDP) {
+    num_sent = mg_lwip_udp_send(nc, nc->send_mbuf.buf, nc->send_mbuf.len);
+    DBG(("%p mg_lwip_udp_send %u = %d", nc, nc->send_mbuf.len, num_sent));
+  } else {
+    num_sent = mg_lwip_tcp_write(nc, nc->send_mbuf.buf, nc->send_mbuf.len);
+    DBG(("%p mg_lwip_tcp_write %u = %d", nc, nc->send_mbuf.len, num_sent));
+  }
+  if (num_sent == 0) return;
+  if (num_sent > 0) {
+    mg_if_sent_cb(nc, num_sent);
+  } else {
+    mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
+  }
+}
+
+void mg_lwip_if_tcp_send(struct mg_connection *nc, const void *buf,
+                         size_t len) {
+  mbuf_append(&nc->send_mbuf, buf, len);
+  mg_lwip_mgr_schedule_poll(nc->mgr);
+}
+
+void mg_lwip_if_udp_send(struct mg_connection *nc, const void *buf,
+                         size_t len) {
+  mbuf_append(&nc->send_mbuf, buf, len);
+  mg_lwip_mgr_schedule_poll(nc->mgr);
+}
+
+struct tcp_recved_ctx {
+  struct tcp_pcb *tpcb;
+  size_t len;
+};
+
+void tcp_recved_tcpip(void *arg) {
+  struct tcp_recved_ctx *ctx = (struct tcp_recved_ctx *) arg;
+  tcp_recved(ctx->tpcb, ctx->len);
+}
+
+void mg_lwip_if_recved(struct mg_connection *nc, size_t len) {
+  if (nc->flags & MG_F_UDP) return;
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  if (nc->sock == INVALID_SOCKET || cs->pcb.tcp == NULL) {
+    DBG(("%p invalid socket", nc));
+    return;
+  }
+  DBG(("%p %p %u %u", nc, cs->pcb.tcp, len,
+       (cs->rx_chain ? cs->rx_chain->tot_len : 0)));
+  struct tcp_recved_ctx ctx = {.tpcb = cs->pcb.tcp, .len = len};
+#if MG_ENABLE_SSL
+  if (!(nc->flags & MG_F_SSL)) {
+    tcpip_callback(tcp_recved_tcpip, &ctx);
+  } else {
+    /* Currently SSL acknowledges data immediately.
+     * TODO(rojer): Find a way to propagate mg_lwip_if_recved. */
+  }
+#else
+  tcpip_callback(tcp_recved_tcpip, &ctx);
+#endif
+  mbuf_trim(&nc->recv_mbuf);
+}
+
+int mg_lwip_if_create_conn(struct mg_connection *nc) {
+  struct mg_lwip_conn_state *cs =
+      (struct mg_lwip_conn_state *) MG_CALLOC(1, sizeof(*cs));
+  if (cs == NULL) return 0;
+  cs->nc = nc;
+  nc->sock = (intptr_t) cs;
+  return 1;
+}
+
+static void udp_remove_tcpip(void *arg) {
+  udp_remove((struct udp_pcb *) arg);
+}
+
+void mg_lwip_if_destroy_conn(struct mg_connection *nc) {
+  if (nc->sock == INVALID_SOCKET) return;
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  if (!(nc->flags & MG_F_UDP)) {
+    struct tcp_pcb *tpcb = cs->pcb.tcp;
+    if (tpcb != NULL) {
+      tcp_arg(tpcb, NULL);
+      DBG(("%p tcp_close %p", nc, tpcb));
+      tcp_arg(tpcb, NULL);
+      tcpip_callback(tcp_close_tcpip, tpcb);
+    }
+    while (cs->rx_chain != NULL) {
+      struct pbuf *seg = cs->rx_chain;
+      cs->rx_chain = pbuf_dechain(cs->rx_chain);
+      pbuf_free(seg);
+    }
+    memset(cs, 0, sizeof(*cs));
+    MG_FREE(cs);
+  } else if (nc->listener == NULL) {
+    /* Only close outgoing UDP pcb or listeners. */
+    struct udp_pcb *upcb = cs->pcb.udp;
+    if (upcb != NULL) {
+      DBG(("%p udp_remove %p", nc, upcb));
+      tcpip_callback(udp_remove_tcpip, upcb);
+    }
+    memset(cs, 0, sizeof(*cs));
+    MG_FREE(cs);
+  }
+  nc->sock = INVALID_SOCKET;
+}
+
+void mg_lwip_if_get_conn_addr(struct mg_connection *nc, int remote,
+                              union socket_address *sa) {
+  memset(sa, 0, sizeof(*sa));
+  if (nc == NULL || nc->sock == INVALID_SOCKET) return;
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  if (nc->flags & MG_F_UDP) {
+    struct udp_pcb *upcb = cs->pcb.udp;
+    if (remote) {
+      memcpy(sa, &nc->sa, sizeof(*sa));
+    } else if (upcb != NULL) {
+      sa->sin.sin_port = htons(upcb->local_port);
+      SET_ADDR(sa, &upcb->local_ip);
+    }
+  } else {
+    struct tcp_pcb *tpcb = cs->pcb.tcp;
+    if (remote) {
+      memcpy(sa, &nc->sa, sizeof(*sa));
+    } else if (tpcb != NULL) {
+      sa->sin.sin_port = htons(tpcb->local_port);
+      SET_ADDR(sa, &tpcb->local_ip);
+    }
+  }
+}
+
+void mg_lwip_if_sock_set(struct mg_connection *nc, sock_t sock) {
+  nc->sock = sock;
+}
+
+/* clang-format off */
+#define MG_LWIP_IFACE_VTABLE                                          \
+  {                                                                   \
+    mg_lwip_if_init,                                                  \
+    mg_lwip_if_free,                                                  \
+    mg_lwip_if_add_conn,                                              \
+    mg_lwip_if_remove_conn,                                           \
+    mg_lwip_if_poll,                                                  \
+    mg_lwip_if_listen_tcp,                                            \
+    mg_lwip_if_listen_udp,                                            \
+    mg_lwip_if_connect_tcp,                                           \
+    mg_lwip_if_connect_udp,                                           \
+    mg_lwip_if_tcp_send,                                              \
+    mg_lwip_if_udp_send,                                              \
+    mg_lwip_if_recved,                                                \
+    mg_lwip_if_create_conn,                                           \
+    mg_lwip_if_destroy_conn,                                          \
+    mg_lwip_if_sock_set,                                              \
+    mg_lwip_if_get_conn_addr,                                         \
+  }
+/* clang-format on */
+
+const struct mg_iface_vtable mg_lwip_iface_vtable = MG_LWIP_IFACE_VTABLE;
+#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL
+const struct mg_iface_vtable mg_default_iface_vtable = MG_LWIP_IFACE_VTABLE;
+#endif
+
+#endif /* MG_ENABLE_NET_IF_LWIP_LOW_LEVEL */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/lwip/mg_lwip_ev_mgr.c"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL
+
+#ifndef MG_SIG_QUEUE_LEN
+#define MG_SIG_QUEUE_LEN 32
+#endif
+
+struct mg_ev_mgr_lwip_signal {
+  int sig;
+  struct mg_connection *nc;
+};
+
+struct mg_ev_mgr_lwip_data {
+  struct mg_ev_mgr_lwip_signal sig_queue[MG_SIG_QUEUE_LEN];
+  int sig_queue_len;
+  int start_index;
+};
+
+void mg_lwip_post_signal(enum mg_sig_type sig, struct mg_connection *nc) {
+  struct mg_ev_mgr_lwip_data *md =
+      (struct mg_ev_mgr_lwip_data *) nc->iface->data;
+  mgos_lock();
+  if (md->sig_queue_len >= MG_SIG_QUEUE_LEN) {
+    mgos_unlock();
+    return;
+  }
+  int end_index = (md->start_index + md->sig_queue_len) % MG_SIG_QUEUE_LEN;
+  md->sig_queue[end_index].sig = sig;
+  md->sig_queue[end_index].nc = nc;
+  md->sig_queue_len++;
+  mg_lwip_mgr_schedule_poll(nc->mgr);
+  mgos_unlock();
+}
+
+void mg_ev_mgr_lwip_process_signals(struct mg_mgr *mgr) {
+  struct mg_ev_mgr_lwip_data *md =
+      (struct mg_ev_mgr_lwip_data *) mgr->ifaces[MG_MAIN_IFACE]->data;
+  while (md->sig_queue_len > 0) {
+    mgos_lock();
+    int sig = md->sig_queue[md->start_index].sig;
+    struct mg_connection *nc = md->sig_queue[md->start_index].nc;
+    struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+    md->start_index = (md->start_index + 1) % MG_SIG_QUEUE_LEN;
+    md->sig_queue_len--;
+    mgos_unlock();
+    if (nc->iface == NULL || nc->mgr == NULL) continue;
+    switch (sig) {
+      case MG_SIG_CONNECT_RESULT: {
+#if MG_ENABLE_SSL
+        if (cs->err == 0 && (nc->flags & MG_F_SSL) &&
+            !(nc->flags & MG_F_SSL_HANDSHAKE_DONE)) {
+          mg_lwip_ssl_do_hs(nc);
+        } else
+#endif
+        {
+          mg_if_connect_cb(nc, cs->err);
+        }
+        break;
+      }
+      case MG_SIG_CLOSE_CONN: {
+        nc->flags |= MG_F_SEND_AND_CLOSE;
+        mg_close_conn(nc);
+        break;
+      }
+      case MG_SIG_RECV: {
+        cs->recv_pending = 0;
+        if (nc->flags & MG_F_UDP) {
+          mg_lwip_handle_recv_udp(nc);
+        } else {
+          mg_lwip_handle_recv_tcp(nc);
+        }
+        break;
+      }
+      case MG_SIG_TOMBSTONE: {
+        break;
+      }
+      case MG_SIG_ACCEPT: {
+        mg_lwip_handle_accept(nc);
+        break;
+      }
+    }
+  }
+}
+
+void mg_lwip_if_init(struct mg_iface *iface) {
+  LOG(LL_INFO, ("%p Mongoose init", iface));
+  iface->data = MG_CALLOC(1, sizeof(struct mg_ev_mgr_lwip_data));
+}
+
+void mg_lwip_if_free(struct mg_iface *iface) {
+  MG_FREE(iface->data);
+  iface->data = NULL;
+}
+
+void mg_lwip_if_add_conn(struct mg_connection *nc) {
+  (void) nc;
+}
+
+void mg_lwip_if_remove_conn(struct mg_connection *nc) {
+  struct mg_ev_mgr_lwip_data *md =
+      (struct mg_ev_mgr_lwip_data *) nc->iface->data;
+  /* Walk the queue and null-out further signals for this conn. */
+  for (int i = 0; i < MG_SIG_QUEUE_LEN; i++) {
+    if (md->sig_queue[i].nc == nc) {
+      md->sig_queue[i].sig = MG_SIG_TOMBSTONE;
+    }
+  }
+}
+
+time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms) {
+  struct mg_mgr *mgr = iface->mgr;
+  int n = 0;
+  double now = mg_time();
+  struct mg_connection *nc, *tmp;
+  double min_timer = 0;
+  int num_timers = 0;
+#if 0
+  DBG(("begin poll @%u", (unsigned int) (now * 1000)));
+#endif
+  mg_ev_mgr_lwip_process_signals(mgr);
+  for (nc = mgr->active_connections; nc != NULL; nc = tmp) {
+    struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+    tmp = nc->next;
+    n++;
+    if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) ||
+        ((nc->flags & MG_F_SEND_AND_CLOSE) && (nc->flags & MG_F_UDP) &&
+         (nc->send_mbuf.len == 0))) {
+      mg_close_conn(nc);
+      continue;
+    }
+    mg_if_poll(nc, now);
+    mg_if_timer(nc, now);
+#if MG_ENABLE_SSL
+    if ((nc->flags & MG_F_SSL) && cs != NULL && cs->pcb.tcp != NULL &&
+        cs->pcb.tcp->state == ESTABLISHED) {
+      if (((nc->flags & MG_F_WANT_WRITE) ||
+           ((nc->send_mbuf.len > 0) &&
+            (nc->flags & MG_F_SSL_HANDSHAKE_DONE))) &&
+          cs->pcb.tcp->snd_buf > 0) {
+        /* Can write more. */
+        if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) {
+          if (!(nc->flags & MG_F_CONNECTING)) mg_lwip_ssl_send(nc);
+        } else {
+          mg_lwip_ssl_do_hs(nc);
+        }
+      }
+      if (cs->rx_chain != NULL || (nc->flags & MG_F_WANT_READ)) {
+        if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) {
+          if (!(nc->flags & MG_F_CONNECTING)) mg_lwip_ssl_recv(nc);
+        } else {
+          mg_lwip_ssl_do_hs(nc);
+        }
+      }
+    } else
+#endif /* MG_ENABLE_SSL */
+    {
+      if (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING)) {
+        mg_lwip_send_more(nc);
+      }
+    }
+    if (nc->sock != INVALID_SOCKET &&
+        !(nc->flags & (MG_F_UDP | MG_F_LISTENING)) && cs->pcb.tcp != NULL &&
+        cs->pcb.tcp->unsent != NULL) {
+      tcpip_callback(tcp_output_tcpip, cs->pcb.tcp);
+    }
+    if (nc->ev_timer_time > 0) {
+      if (num_timers == 0 || nc->ev_timer_time < min_timer) {
+        min_timer = nc->ev_timer_time;
+      }
+      num_timers++;
+    }
+
+    if (nc->sock != INVALID_SOCKET) {
+      /* Try to consume data from cs->rx_chain */
+      mg_lwip_consume_rx_chain_tcp(nc);
+
+      /*
+       * If the connection is about to close, and rx_chain is finally empty,
+       * send the MG_SIG_CLOSE_CONN signal
+       */
+      if (cs->draining_rx_chain && cs->rx_chain == NULL) {
+        mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
+      }
+    }
+  }
+#if 0
+  DBG(("end poll @%u, %d conns, %d timers (min %u), next in %d ms",
+       (unsigned int) (now * 1000), n, num_timers,
+       (unsigned int) (min_timer * 1000), timeout_ms));
+#endif
+  (void) timeout_ms;
+  return now;
+}
+
+uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr) {
+  struct mg_connection *nc;
+  double now;
+  double min_timer = 0;
+  int num_timers = 0;
+  mg_ev_mgr_lwip_process_signals(mgr);
+  for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) {
+    struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+    if (nc->ev_timer_time > 0) {
+      if (num_timers == 0 || nc->ev_timer_time < min_timer) {
+        min_timer = nc->ev_timer_time;
+      }
+      num_timers++;
+    }
+    if (nc->send_mbuf.len > 0
+#if MG_ENABLE_SSL
+        || (nc->flags & MG_F_WANT_WRITE)
+#endif
+            ) {
+      int can_send = 0;
+      /* We have stuff to send, but can we? */
+      if (nc->flags & MG_F_UDP) {
+        /* UDP is always ready for sending. */
+        can_send = (cs->pcb.udp != NULL);
+      } else {
+        can_send = (cs->pcb.tcp != NULL && cs->pcb.tcp->snd_buf > 0);
+      }
+      /* We want and can send, request a poll immediately. */
+      if (can_send) return 0;
+    }
+  }
+  uint32_t timeout_ms = ~0;
+  now = mg_time();
+  if (num_timers > 0) {
+    /* If we have a timer that is past due, do a poll ASAP. */
+    if (min_timer < now) return 0;
+    double timer_timeout_ms = (min_timer - now) * 1000 + 1 /* rounding */;
+    if (timer_timeout_ms < timeout_ms) {
+      timeout_ms = timer_timeout_ms;
+    }
+  }
+  return timeout_ms;
+}
+
+#endif /* MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/lwip/mg_lwip_ssl_if.c"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#if MG_ENABLE_SSL && MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL
+
+/* Amalgamated: #include "common/mg_mem.h" */
+/* Amalgamated: #include "common/cs_dbg.h" */
+
+#include 
+#include 
+
+#ifndef MG_LWIP_SSL_IO_SIZE
+#define MG_LWIP_SSL_IO_SIZE 1024
+#endif
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+void mg_lwip_ssl_do_hs(struct mg_connection *nc) {
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  int server_side = (nc->listener != NULL);
+  enum mg_ssl_if_result res;
+  if (nc->flags & MG_F_CLOSE_IMMEDIATELY) return;
+  res = mg_ssl_if_handshake(nc);
+  DBG(("%p %lu %d %d", nc, nc->flags, server_side, res));
+  if (res != MG_SSL_OK) {
+    if (res == MG_SSL_WANT_WRITE) {
+      nc->flags |= MG_F_WANT_WRITE;
+      cs->err = 0;
+    } else if (res == MG_SSL_WANT_READ) {
+      /*
+       * Nothing to do in particular, we are callback-driven.
+       * What we definitely do not need anymore is SSL reading (nothing left).
+       */
+      nc->flags &= ~MG_F_WANT_READ;
+      cs->err = 0;
+    } else {
+      cs->err = res;
+      if (server_side) {
+        mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
+      } else {
+        mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc);
+      }
+    }
+  } else {
+    cs->err = 0;
+    nc->flags &= ~MG_F_WANT_WRITE;
+    /*
+     * Handshake is done. Schedule a read immediately to consume app data
+     * which may already be waiting.
+     */
+    nc->flags |= (MG_F_SSL_HANDSHAKE_DONE | MG_F_WANT_READ);
+    if (server_side) {
+      mg_lwip_accept_conn(nc, cs->pcb.tcp);
+    } else {
+      mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc);
+    }
+  }
+}
+
+void mg_lwip_ssl_send(struct mg_connection *nc) {
+  if (nc->sock == INVALID_SOCKET) {
+    DBG(("%p invalid socket", nc));
+    return;
+  }
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  /* It's ok if the buffer is empty. Return value of 0 may also be valid. */
+  int len = cs->last_ssl_write_size;
+  if (len == 0) {
+    len = MIN(MG_LWIP_SSL_IO_SIZE, nc->send_mbuf.len);
+  }
+  int ret = mg_ssl_if_write(nc, nc->send_mbuf.buf, len);
+  DBG(("%p SSL_write %u = %d", nc, len, ret));
+  if (ret > 0) {
+    mg_if_sent_cb(nc, ret);
+    cs->last_ssl_write_size = 0;
+  } else if (ret < 0) {
+    /* This is tricky. We must remember the exact data we were sending to retry
+     * exactly the same send next time. */
+    cs->last_ssl_write_size = len;
+  }
+  if (ret == len) {
+    nc->flags &= ~MG_F_WANT_WRITE;
+  } else if (ret == MG_SSL_WANT_WRITE) {
+    nc->flags |= MG_F_WANT_WRITE;
+  } else {
+    mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
+  }
+}
+
+void mg_lwip_ssl_recv(struct mg_connection *nc) {
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  /* Don't deliver data before connect callback */
+  if (nc->flags & MG_F_CONNECTING) return;
+  while (nc->recv_mbuf.len < nc->recv_mbuf_limit) {
+    char *buf = (char *) MG_MALLOC(MG_LWIP_SSL_IO_SIZE);
+    if (buf == NULL) return;
+    int ret = mg_ssl_if_read(nc, buf, MG_LWIP_SSL_IO_SIZE);
+    DBG(("%p %p SSL_read %u = %d", nc, cs->rx_chain, MG_LWIP_SSL_IO_SIZE, ret));
+    if (ret <= 0) {
+      MG_FREE(buf);
+      if (ret == MG_SSL_WANT_WRITE) {
+        nc->flags |= MG_F_WANT_WRITE;
+        return;
+      } else if (ret == MG_SSL_WANT_READ) {
+        /*
+         * Nothing to do in particular, we are callback-driven.
+         * What we definitely do not need anymore is SSL reading (nothing left).
+         */
+        nc->flags &= ~MG_F_WANT_READ;
+        cs->err = 0;
+        return;
+      } else {
+        mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
+        return;
+      }
+    } else {
+      mg_if_recv_tcp_cb(nc, buf, ret, 1 /* own */);
+    }
+  }
+}
+
+#ifdef KR_VERSION
+
+ssize_t kr_send(int fd, const void *buf, size_t len) {
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) fd;
+  int ret = mg_lwip_tcp_write(cs->nc, buf, len);
+  DBG(("%p mg_lwip_tcp_write %u = %d", cs->nc, len, ret));
+  if (ret == 0) ret = KR_IO_WOULDBLOCK;
+  return ret;
+}
+
+ssize_t kr_recv(int fd, void *buf, size_t len) {
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) fd;
+  struct pbuf *seg = cs->rx_chain;
+  if (seg == NULL) {
+    DBG(("%u - nothing to read", len));
+    return KR_IO_WOULDBLOCK;
+  }
+  size_t seg_len = (seg->len - cs->rx_offset);
+  DBG(("%u %u %u %u", len, cs->rx_chain->len, seg_len, cs->rx_chain->tot_len));
+  len = MIN(len, seg_len);
+  pbuf_copy_partial(seg, buf, len, cs->rx_offset);
+  cs->rx_offset += len;
+  tcp_recved(cs->pcb.tcp, len);
+  if (cs->rx_offset == cs->rx_chain->len) {
+    cs->rx_chain = pbuf_dechain(cs->rx_chain);
+    pbuf_free(seg);
+    cs->rx_offset = 0;
+  }
+  return len;
+}
+
+#elif MG_SSL_IF == MG_SSL_IF_MBEDTLS
+
+int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len) {
+  struct mg_connection *nc = (struct mg_connection *) ctx;
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  int ret = mg_lwip_tcp_write(cs->nc, buf, len);
+  if (ret == 0) ret = MBEDTLS_ERR_SSL_WANT_WRITE;
+  LOG(LL_DEBUG, ("%p %d -> %d", nc, len, ret));
+  return ret;
+}
+
+int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len) {
+  struct mg_connection *nc = (struct mg_connection *) ctx;
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  struct pbuf *seg = cs->rx_chain;
+  if (seg == NULL) {
+    DBG(("%u - nothing to read", len));
+    return MBEDTLS_ERR_SSL_WANT_READ;
+  }
+  size_t seg_len = (seg->len - cs->rx_offset);
+  DBG(("%u %u %u %u", len, cs->rx_chain->len, seg_len, cs->rx_chain->tot_len));
+  mgos_lock();
+  len = MIN(len, seg_len);
+  pbuf_copy_partial(seg, buf, len, cs->rx_offset);
+  cs->rx_offset += len;
+  /* TCP PCB may be NULL if connection has already been closed
+   * but we still have data to deliver to SSL. */
+  if (cs->pcb.tcp != NULL) tcp_recved(cs->pcb.tcp, len);
+  if (cs->rx_offset == cs->rx_chain->len) {
+    cs->rx_chain = pbuf_dechain(cs->rx_chain);
+    pbuf_free(seg);
+    cs->rx_offset = 0;
+  }
+  mgos_unlock();
+  LOG(LL_DEBUG, ("%p <- %d", nc, (int) len));
+  return len;
+}
+
+#endif
+
+#endif /* MG_ENABLE_SSL && MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/wince/wince_libc.c"
+#endif
+/*
+ * Copyright (c) 2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifdef WINCE
+
+const char *strerror(int err) {
+  /*
+   * TODO(alashkin): there is no strerror on WinCE;
+   * look for similar wce_xxxx function
+   */
+  static char buf[10];
+  snprintf(buf, sizeof(buf), "%d", err);
+  return buf;
+}
+
+int open(const char *filename, int oflag, int pmode) {
+  /*
+   * TODO(alashkin): mg_open function is not used in mongoose
+   * but exists in documentation as utility function
+   * Shall we delete it at all or implement for WinCE as well?
+   */
+  DebugBreak();
+  return 0; /* for compiler */
+}
+
+int _wstati64(const wchar_t *path, cs_stat_t *st) {
+  DWORD fa = GetFileAttributesW(path);
+  if (fa == INVALID_FILE_ATTRIBUTES) {
+    return -1;
+  }
+  memset(st, 0, sizeof(*st));
+  if ((fa & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+    HANDLE h;
+    FILETIME ftime;
+    st->st_mode |= _S_IFREG;
+    h = CreateFileW(path, GENERIC_READ, 0, NULL, OPEN_EXISTING,
+                    FILE_ATTRIBUTE_NORMAL, NULL);
+    if (h == INVALID_HANDLE_VALUE) {
+      return -1;
+    }
+    st->st_size = GetFileSize(h, NULL);
+    GetFileTime(h, NULL, NULL, &ftime);
+    st->st_mtime = (uint32_t)((((uint64_t) ftime.dwLowDateTime +
+                                ((uint64_t) ftime.dwHighDateTime << 32)) /
+                               10000000.0) -
+                              11644473600);
+    CloseHandle(h);
+  } else {
+    st->st_mode |= _S_IFDIR;
+  }
+  return 0;
+}
+
+/* Windows CE doesn't have neither gmtime nor strftime */
+static void mg_gmt_time_string(char *buf, size_t buf_len, time_t *t) {
+  FILETIME ft;
+  SYSTEMTIME systime;
+  if (t != NULL) {
+    uint64_t filetime = (*t + 11644473600) * 10000000;
+    ft.dwLowDateTime = filetime & 0xFFFFFFFF;
+    ft.dwHighDateTime = (filetime & 0xFFFFFFFF00000000) >> 32;
+    FileTimeToSystemTime(&ft, &systime);
+  } else {
+    GetSystemTime(&systime);
+  }
+  /* There is no PRIu16 in WinCE SDK */
+  snprintf(buf, buf_len, "%d.%d.%d %d:%d:%d GMT", (int) systime.wYear,
+           (int) systime.wMonth, (int) systime.wDay, (int) systime.wHour,
+           (int) systime.wMinute, (int) systime.wSecond);
+}
+
+#endif
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/pic32/pic32_net_if.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_PLATFORMS_PIC32_NET_IF_H_
+#define CS_COMMON_PLATFORMS_PIC32_NET_IF_H_
+
+/* Amalgamated: #include "mongoose/src/net_if.h" */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifndef MG_ENABLE_NET_IF_PIC32
+#define MG_ENABLE_NET_IF_PIC32 MG_NET_IF == MG_NET_IF_PIC32
+#endif
+
+extern const struct mg_iface_vtable mg_pic32_iface_vtable;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CS_COMMON_PLATFORMS_PIC32_NET_IF_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/pic32/pic32_net_if.c"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#if MG_ENABLE_NET_IF_PIC32
+
+int mg_pic32_if_create_conn(struct mg_connection *nc) {
+  (void) nc;
+  return 1;
+}
+
+void mg_pic32_if_recved(struct mg_connection *nc, size_t len) {
+  (void) nc;
+  (void) len;
+}
+
+void mg_pic32_if_add_conn(struct mg_connection *nc) {
+  (void) nc;
+}
+
+void mg_pic32_if_init(struct mg_iface *iface) {
+  (void) iface;
+  (void) mg_get_errno(); /* Shutup compiler */
+}
+
+void mg_pic32_if_free(struct mg_iface *iface) {
+  (void) iface;
+}
+
+void mg_pic32_if_remove_conn(struct mg_connection *nc) {
+  (void) nc;
+}
+
+void mg_pic32_if_destroy_conn(struct mg_connection *nc) {
+  if (nc->sock == INVALID_SOCKET) return;
+  /* For UDP, only close outgoing sockets or listeners. */
+  if (!(nc->flags & MG_F_UDP)) {
+    /* Close TCP */
+    TCPIP_TCP_Close((TCP_SOCKET) nc->sock);
+  } else if (nc->listener == NULL) {
+    /* Only close outgoing UDP or listeners. */
+    TCPIP_UDP_Close((UDP_SOCKET) nc->sock);
+  }
+
+  nc->sock = INVALID_SOCKET;
+}
+
+int mg_pic32_if_listen_udp(struct mg_connection *nc, union socket_address *sa) {
+  nc->sock = TCPIP_UDP_ServerOpen(
+      sa->sin.sin_family == AF_INET ? IP_ADDRESS_TYPE_IPV4
+                                    : IP_ADDRESS_TYPE_IPV6,
+      ntohs(sa->sin.sin_port),
+      sa->sin.sin_addr.s_addr == 0 ? 0 : (IP_MULTI_ADDRESS *) &sa->sin);
+  if (nc->sock == INVALID_SOCKET) {
+    return -1;
+  }
+  return 0;
+}
+
+void mg_pic32_if_udp_send(struct mg_connection *nc, const void *buf,
+                          size_t len) {
+  mbuf_append(&nc->send_mbuf, buf, len);
+}
+
+void mg_pic32_if_tcp_send(struct mg_connection *nc, const void *buf,
+                          size_t len) {
+  mbuf_append(&nc->send_mbuf, buf, len);
+}
+
+int mg_pic32_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) {
+  nc->sock = TCPIP_TCP_ServerOpen(
+      sa->sin.sin_family == AF_INET ? IP_ADDRESS_TYPE_IPV4
+                                    : IP_ADDRESS_TYPE_IPV6,
+      ntohs(sa->sin.sin_port),
+      sa->sin.sin_addr.s_addr == 0 ? 0 : (IP_MULTI_ADDRESS *) &sa->sin);
+  memcpy(&nc->sa, sa, sizeof(*sa));
+  if (nc->sock == INVALID_SOCKET) {
+    return -1;
+  }
+  return 0;
+}
+
+static int mg_accept_conn(struct mg_connection *lc) {
+  struct mg_connection *nc;
+  TCP_SOCKET_INFO si;
+  union socket_address sa;
+
+  nc = mg_if_accept_new_conn(lc);
+
+  if (nc == NULL) {
+    return 0;
+  }
+
+  nc->sock = lc->sock;
+  nc->flags &= ~MG_F_LISTENING;
+
+  if (!TCPIP_TCP_SocketInfoGet((TCP_SOCKET) nc->sock, &si)) {
+    return 0;
+  }
+
+  if (si.addressType == IP_ADDRESS_TYPE_IPV4) {
+    sa.sin.sin_family = AF_INET;
+    sa.sin.sin_port = htons(si.remotePort);
+    sa.sin.sin_addr.s_addr = si.remoteIPaddress.v4Add.Val;
+  } else {
+    /* TODO(alashkin): do something with _potential_ IPv6 */
+    memset(&sa, 0, sizeof(sa));
+  }
+
+  mg_if_accept_tcp_cb(nc, (union socket_address *) &sa, sizeof(sa));
+
+  return mg_pic32_if_listen_tcp(lc, &lc->sa) >= 0;
+}
+
+char *inet_ntoa(struct in_addr in) {
+  static char addr[17];
+  snprintf(addr, sizeof(addr), "%d.%d.%d.%d", (int) in.S_un.S_un_b.s_b1,
+           (int) in.S_un.S_un_b.s_b2, (int) in.S_un.S_un_b.s_b3,
+           (int) in.S_un.S_un_b.s_b4);
+  return addr;
+}
+
+static void mg_handle_send(struct mg_connection *nc) {
+  uint16_t bytes_written = 0;
+  if (nc->flags & MG_F_UDP) {
+    if (!TCPIP_UDP_RemoteBind(
+            (UDP_SOCKET) nc->sock,
+            nc->sa.sin.sin_family == AF_INET ? IP_ADDRESS_TYPE_IPV4
+                                             : IP_ADDRESS_TYPE_IPV6,
+            ntohs(nc->sa.sin.sin_port), (IP_MULTI_ADDRESS *) &nc->sa.sin)) {
+      nc->flags |= MG_F_CLOSE_IMMEDIATELY;
+      return;
+    }
+    bytes_written = TCPIP_UDP_TxPutIsReady((UDP_SOCKET) nc->sock, 0);
+    if (bytes_written >= nc->send_mbuf.len) {
+      if (TCPIP_UDP_ArrayPut((UDP_SOCKET) nc->sock,
+                             (uint8_t *) nc->send_mbuf.buf,
+                             nc->send_mbuf.len) != nc->send_mbuf.len) {
+        nc->flags |= MG_F_CLOSE_IMMEDIATELY;
+        bytes_written = 0;
+      }
+    }
+  } else {
+    bytes_written = TCPIP_TCP_FifoTxFreeGet((TCP_SOCKET) nc->sock);
+    if (bytes_written != 0) {
+      if (bytes_written > nc->send_mbuf.len) {
+        bytes_written = nc->send_mbuf.len;
+      }
+      if (TCPIP_TCP_ArrayPut((TCP_SOCKET) nc->sock,
+                             (uint8_t *) nc->send_mbuf.buf,
+                             bytes_written) != bytes_written) {
+        nc->flags |= MG_F_CLOSE_IMMEDIATELY;
+        bytes_written = 0;
+      }
+    }
+  }
+
+  mg_if_sent_cb(nc, bytes_written);
+}
+
+static void mg_handle_recv(struct mg_connection *nc) {
+  uint16_t bytes_read = 0;
+  uint8_t *buf = NULL;
+  if (nc->flags & MG_F_UDP) {
+    bytes_read = TCPIP_UDP_GetIsReady((UDP_SOCKET) nc->sock);
+    if (bytes_read != 0 &&
+        (nc->recv_mbuf_limit == -1 ||
+         nc->recv_mbuf.len + bytes_read < nc->recv_mbuf_limit)) {
+      buf = (uint8_t *) MG_MALLOC(bytes_read);
+      if (TCPIP_UDP_ArrayGet((UDP_SOCKET) nc->sock, buf, bytes_read) !=
+          bytes_read) {
+        nc->flags |= MG_F_CLOSE_IMMEDIATELY;
+        bytes_read = 0;
+        MG_FREE(buf);
+      }
+    }
+  } else {
+    bytes_read = TCPIP_TCP_GetIsReady((TCP_SOCKET) nc->sock);
+    if (bytes_read != 0) {
+      if (nc->recv_mbuf_limit != -1 &&
+          nc->recv_mbuf_limit - nc->recv_mbuf.len > bytes_read) {
+        bytes_read = nc->recv_mbuf_limit - nc->recv_mbuf.len;
+      }
+      buf = (uint8_t *) MG_MALLOC(bytes_read);
+      if (TCPIP_TCP_ArrayGet((TCP_SOCKET) nc->sock, buf, bytes_read) !=
+          bytes_read) {
+        nc->flags |= MG_F_CLOSE_IMMEDIATELY;
+        MG_FREE(buf);
+        bytes_read = 0;
+      }
+    }
+  }
+
+  if (bytes_read != 0) {
+    mg_if_recv_tcp_cb(nc, buf, bytes_read, 1 /* own */);
+  }
+}
+
+time_t mg_pic32_if_poll(struct mg_iface *iface, int timeout_ms) {
+  struct mg_mgr *mgr = iface->mgr;
+  double now = mg_time();
+  struct mg_connection *nc, *tmp;
+
+  for (nc = mgr->active_connections; nc != NULL; nc = tmp) {
+    tmp = nc->next;
+
+    if (nc->flags & MG_F_CONNECTING) {
+      /* processing connections */
+      if (nc->flags & MG_F_UDP ||
+          TCPIP_TCP_IsConnected((TCP_SOCKET) nc->sock)) {
+        mg_if_connect_cb(nc, 0);
+      }
+    } else if (nc->flags & MG_F_LISTENING) {
+      if (TCPIP_TCP_IsConnected((TCP_SOCKET) nc->sock)) {
+        /* accept new connections */
+        mg_accept_conn(nc);
+      }
+    } else {
+      if (nc->send_mbuf.len != 0) {
+        mg_handle_send(nc);
+      }
+
+      if (nc->recv_mbuf_limit == -1 ||
+          nc->recv_mbuf.len < nc->recv_mbuf_limit) {
+        mg_handle_recv(nc);
+      }
+    }
+  }
+
+  for (nc = mgr->active_connections; nc != NULL; nc = tmp) {
+    tmp = nc->next;
+    if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) ||
+        (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) {
+      mg_close_conn(nc);
+    }
+  }
+
+  return now;
+}
+
+void mg_pic32_if_sock_set(struct mg_connection *nc, sock_t sock) {
+  nc->sock = sock;
+}
+
+void mg_pic32_if_get_conn_addr(struct mg_connection *nc, int remote,
+                               union socket_address *sa) {
+  /* TODO(alaskin): not implemented yet */
+}
+
+void mg_pic32_if_connect_tcp(struct mg_connection *nc,
+                             const union socket_address *sa) {
+  nc->sock = TCPIP_TCP_ClientOpen(
+      sa->sin.sin_family == AF_INET ? IP_ADDRESS_TYPE_IPV4
+                                    : IP_ADDRESS_TYPE_IPV6,
+      ntohs(sa->sin.sin_port), (IP_MULTI_ADDRESS *) &sa->sin);
+  nc->err = (nc->sock == INVALID_SOCKET) ? -1 : 0;
+}
+
+void mg_pic32_if_connect_udp(struct mg_connection *nc) {
+  nc->sock = TCPIP_UDP_ClientOpen(IP_ADDRESS_TYPE_ANY, 0, NULL);
+  nc->err = (nc->sock == INVALID_SOCKET) ? -1 : 0;
+}
+
+/* clang-format off */
+#define MG_PIC32_IFACE_VTABLE                                   \
+  {                                                             \
+    mg_pic32_if_init,                                           \
+    mg_pic32_if_free,                                           \
+    mg_pic32_if_add_conn,                                       \
+    mg_pic32_if_remove_conn,                                    \
+    mg_pic32_if_poll,                                           \
+    mg_pic32_if_listen_tcp,                                     \
+    mg_pic32_if_listen_udp,                                     \
+    mg_pic32_if_connect_tcp,                                    \
+    mg_pic32_if_connect_udp,                                    \
+    mg_pic32_if_tcp_send,                                       \
+    mg_pic32_if_udp_send,                                       \
+    mg_pic32_if_recved,                                         \
+    mg_pic32_if_create_conn,                                    \
+    mg_pic32_if_destroy_conn,                                   \
+    mg_pic32_if_sock_set,                                       \
+    mg_pic32_if_get_conn_addr,                                  \
+  }
+/* clang-format on */
+
+const struct mg_iface_vtable mg_pic32_iface_vtable = MG_PIC32_IFACE_VTABLE;
+#if MG_NET_IF == MG_NET_IF_PIC32
+const struct mg_iface_vtable mg_default_iface_vtable = MG_PIC32_IFACE_VTABLE;
+#endif
+
+#endif /* MG_ENABLE_NET_IF_PIC32 */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/windows/windows_direct.c"
+#endif
+/*
+ * Copyright (c) 2017 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifdef _WIN32
+
+int rmdir(const char *dirname) {
+  return _rmdir(dirname);
+}
+
+unsigned int sleep(unsigned int seconds) {
+  Sleep(seconds * 1000);
+  return 0;
+}
+
+#endif /* _WIN32 */
diff --git a/src/mongoose-6.11/mongoose.h b/src/mongoose-6.11/mongoose.h
new file mode 100644
index 0000000..eecbbef
--- /dev/null
+++ b/src/mongoose-6.11/mongoose.h
@@ -0,0 +1,6222 @@
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_common.h"
+#endif
+/*
+ * Copyright (c) 2004-2013 Sergey Lyubka
+ * Copyright (c) 2013-2015 Cesanta Software Limited
+ * All rights reserved
+ *
+ * This software is dual-licensed: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. For the terms of this
+ * license, see .
+ *
+ * You are free to use this software under the terms of the GNU General
+ * Public License, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * Alternatively, you can license this software under a commercial
+ * license, as set out in .
+ */
+
+#ifndef CS_MONGOOSE_SRC_COMMON_H_
+#define CS_MONGOOSE_SRC_COMMON_H_
+
+#define MG_VERSION "6.11"
+
+/* Local tweaks, applied before any of Mongoose's own headers. */
+#ifdef MG_LOCALS
+#include 
+#endif
+
+#endif /* CS_MONGOOSE_SRC_COMMON_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platform.h"
+#endif
+#ifndef CS_COMMON_PLATFORM_H_
+#define CS_COMMON_PLATFORM_H_
+
+/*
+ * For the "custom" platform, includes and dependencies can be
+ * provided through mg_locals.h.
+ */
+#define CS_P_CUSTOM 0
+#define CS_P_UNIX 1
+#define CS_P_WINDOWS 2
+#define CS_P_ESP32 15
+#define CS_P_ESP8266 3
+#define CS_P_CC3100 6
+#define CS_P_CC3200 4
+#define CS_P_CC3220 17
+#define CS_P_MSP432 5
+#define CS_P_TM4C129 14
+#define CS_P_MBED 7
+#define CS_P_WINCE 8
+#define CS_P_NXP_LPC 13
+#define CS_P_NXP_KINETIS 9
+#define CS_P_NRF51 12
+#define CS_P_NRF52 10
+#define CS_P_PIC32 11
+#define CS_P_STM32 16
+/* Next id: 18 */
+
+/* If not specified explicitly, we guess platform by defines. */
+#ifndef CS_PLATFORM
+
+#if defined(TARGET_IS_MSP432P4XX) || defined(__MSP432P401R__)
+#define CS_PLATFORM CS_P_MSP432
+#elif defined(cc3200) || defined(TARGET_IS_CC3200)
+#define CS_PLATFORM CS_P_CC3200
+#elif defined(cc3220) || defined(TARGET_IS_CC3220)
+#define CS_PLATFORM CS_P_CC3220
+#elif defined(__unix__) || defined(__APPLE__)
+#define CS_PLATFORM CS_P_UNIX
+#elif defined(WINCE)
+#define CS_PLATFORM CS_P_WINCE
+#elif defined(_WIN32)
+#define CS_PLATFORM CS_P_WINDOWS
+#elif defined(__MBED__)
+#define CS_PLATFORM CS_P_MBED
+#elif defined(__USE_LPCOPEN)
+#define CS_PLATFORM CS_P_NXP_LPC
+#elif defined(FRDM_K64F) || defined(FREEDOM)
+#define CS_PLATFORM CS_P_NXP_KINETIS
+#elif defined(PIC32)
+#define CS_PLATFORM CS_P_PIC32
+#elif defined(ESP_PLATFORM)
+#define CS_PLATFORM CS_P_ESP32
+#elif defined(ICACHE_FLASH)
+#define CS_PLATFORM CS_P_ESP8266
+#elif defined(TARGET_IS_TM4C129_RA0) || defined(TARGET_IS_TM4C129_RA1) || \
+    defined(TARGET_IS_TM4C129_RA2)
+#define CS_PLATFORM CS_P_TM4C129
+#elif defined(STM32)
+#define CS_PLATFORM CS_P_STM32
+#endif
+
+#ifndef CS_PLATFORM
+#error "CS_PLATFORM is not specified and we couldn't guess it."
+#endif
+
+#endif /* !defined(CS_PLATFORM) */
+
+#define MG_NET_IF_SOCKET 1
+#define MG_NET_IF_SIMPLELINK 2
+#define MG_NET_IF_LWIP_LOW_LEVEL 3
+#define MG_NET_IF_PIC32 4
+
+#define MG_SSL_IF_OPENSSL 1
+#define MG_SSL_IF_MBEDTLS 2
+#define MG_SSL_IF_SIMPLELINK 3
+
+/* Amalgamated: #include "common/platforms/platform_unix.h" */
+/* Amalgamated: #include "common/platforms/platform_windows.h" */
+/* Amalgamated: #include "common/platforms/platform_esp32.h" */
+/* Amalgamated: #include "common/platforms/platform_esp8266.h" */
+/* Amalgamated: #include "common/platforms/platform_cc3100.h" */
+/* Amalgamated: #include "common/platforms/platform_cc3200.h" */
+/* Amalgamated: #include "common/platforms/platform_cc3220.h" */
+/* Amalgamated: #include "common/platforms/platform_mbed.h" */
+/* Amalgamated: #include "common/platforms/platform_nrf51.h" */
+/* Amalgamated: #include "common/platforms/platform_nrf52.h" */
+/* Amalgamated: #include "common/platforms/platform_wince.h" */
+/* Amalgamated: #include "common/platforms/platform_nxp_lpc.h" */
+/* Amalgamated: #include "common/platforms/platform_nxp_kinetis.h" */
+/* Amalgamated: #include "common/platforms/platform_pic32.h" */
+/* Amalgamated: #include "common/platforms/platform_stm32.h" */
+
+/* Common stuff */
+
+#if !defined(WEAK)
+#if (defined(__GNUC__) || defined(__TI_COMPILER_VERSION__)) && !defined(_WIN32)
+#define WEAK __attribute__((weak))
+#else
+#define WEAK
+#endif
+#endif
+
+#ifdef __GNUC__
+#define NORETURN __attribute__((noreturn))
+#define NOINLINE __attribute__((noinline))
+#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#define NOINSTR __attribute__((no_instrument_function))
+#define DO_NOT_WARN_UNUSED __attribute__((unused))
+#else
+#define NORETURN
+#define NOINLINE
+#define WARN_UNUSED_RESULT
+#define NOINSTR
+#define DO_NOT_WARN_UNUSED
+#endif /* __GNUC__ */
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
+#endif
+
+#endif /* CS_COMMON_PLATFORM_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/platform_windows.h"
+#endif
+#ifndef CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_
+#define CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_
+#if CS_PLATFORM == CS_P_WINDOWS
+
+/*
+ * MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
+ * MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
+ * MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
+ * MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
+ * MSVC++ 9.0  _MSC_VER == 1500 (Visual Studio 2008)
+ * MSVC++ 8.0  _MSC_VER == 1400 (Visual Studio 2005)
+ * MSVC++ 7.1  _MSC_VER == 1310 (Visual Studio 2003)
+ * MSVC++ 7.0  _MSC_VER == 1300
+ * MSVC++ 6.0  _MSC_VER == 1200
+ * MSVC++ 5.0  _MSC_VER == 1100
+ */
+#ifdef _MSC_VER
+#pragma warning(disable : 4127) /* FD_SET() emits warning, disable it */
+#pragma warning(disable : 4204) /* missing c99 support */
+#endif
+
+#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
+#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
+#endif
+
+#ifndef _CRT_SECURE_NO_WARNINGS
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#ifdef _MSC_VER
+#pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */
+#endif
+
+#include 
+#include 
+#include 
+#include 
+
+#if _MSC_VER < 1700
+typedef int bool;
+#else
+#include 
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER >= 1800
+#define strdup _strdup
+#endif
+
+#ifndef EINPROGRESS
+#define EINPROGRESS WSAEINPROGRESS
+#endif
+#ifndef EWOULDBLOCK
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#endif
+#ifndef __func__
+#define STRX(x) #x
+#define STR(x) STRX(x)
+#define __func__ __FILE__ ":" STR(__LINE__)
+#endif
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#define to64(x) _atoi64(x)
+#if !defined(__MINGW32__) && !defined(__MINGW64__)
+#define popen(x, y) _popen((x), (y))
+#define pclose(x) _pclose(x)
+#define fileno _fileno
+#endif
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+#define fseeko(x, y, z) _fseeki64((x), (y), (z))
+#else
+#define fseeko(x, y, z) fseek((x), (y), (z))
+#endif
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+typedef unsigned long uintptr_t;
+typedef long intptr_t;
+#endif
+typedef int socklen_t;
+#if _MSC_VER >= 1700
+#include 
+#else
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#endif
+typedef SOCKET sock_t;
+typedef uint32_t in_addr_t;
+#ifndef UINT16_MAX
+#define UINT16_MAX 65535
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX 4294967295
+#endif
+#ifndef pid_t
+#define pid_t HANDLE
+#endif
+#define INT64_FMT "I64d"
+#define INT64_X_FMT "I64x"
+#define SIZE_T_FMT "Iu"
+typedef struct _stati64 cs_stat_t;
+#ifndef S_ISDIR
+#define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR)
+#endif
+#ifndef S_ISREG
+#define S_ISREG(x) (((x) &_S_IFMT) == _S_IFREG)
+#endif
+#define DIRSEP '\\'
+#define CS_DEFINE_DIRENT
+
+#ifndef va_copy
+#ifdef __va_copy
+#define va_copy __va_copy
+#else
+#define va_copy(x, y) (x) = (y)
+#endif
+#endif
+
+#ifndef MG_MAX_HTTP_REQUEST_SIZE
+#define MG_MAX_HTTP_REQUEST_SIZE 8192
+#endif
+
+#ifndef MG_MAX_HTTP_SEND_MBUF
+#define MG_MAX_HTTP_SEND_MBUF 4096
+#endif
+
+#ifndef MG_MAX_HTTP_HEADERS
+#define MG_MAX_HTTP_HEADERS 40
+#endif
+
+#ifndef CS_ENABLE_STDIO
+#define CS_ENABLE_STDIO 1
+#endif
+
+#ifndef MG_ENABLE_BROADCAST
+#define MG_ENABLE_BROADCAST 1
+#endif
+
+#ifndef MG_ENABLE_DIRECTORY_LISTING
+#define MG_ENABLE_DIRECTORY_LISTING 1
+#endif
+
+#ifndef MG_ENABLE_FILESYSTEM
+#define MG_ENABLE_FILESYSTEM 1
+#endif
+
+#ifndef MG_ENABLE_HTTP_CGI
+#define MG_ENABLE_HTTP_CGI MG_ENABLE_FILESYSTEM
+#endif
+
+#ifndef MG_NET_IF
+#define MG_NET_IF MG_NET_IF_SOCKET
+#endif
+
+unsigned int sleep(unsigned int seconds);
+
+/* https://stackoverflow.com/questions/16647819/timegm-cross-platform */
+#define timegm _mkgmtime
+
+#define gmtime_r(a, b) \
+  do {                 \
+    *(b) = *gmtime(a); \
+  } while (0)
+
+#endif /* CS_PLATFORM == CS_P_WINDOWS */
+#endif /* CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/platform_unix.h"
+#endif
+#ifndef CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_
+#define CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_
+#if CS_PLATFORM == CS_P_UNIX
+
+#ifndef _XOPEN_SOURCE
+#define _XOPEN_SOURCE 600
+#endif
+
+/*  wants this for C++ */
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS
+#endif
+
+/* C++ wants that for INT64_MAX */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS
+#endif
+
+/* Enable fseeko() and ftello() functions */
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+
+/* Enable 64-bit file offsets */
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 64
+#endif
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#ifdef __APPLE__
+#include 
+#ifndef BYTE_ORDER
+#define LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN
+#define BIG_ENDIAN __DARWIN_BIG_ENDIAN
+#define PDP_ENDIAN __DARWIN_PDP_ENDIAN
+#define BYTE_ORDER __DARWIN_BYTE_ORDER
+#endif
+#endif
+
+/*
+ * osx correctly avoids defining strtoll when compiling in strict ansi mode.
+ * c++ 11 standard defines strtoll as well.
+ * We require strtoll, and if your embedded pre-c99 compiler lacks one, please
+ * implement a shim.
+ */
+#if !(defined(__cplusplus) && __cplusplus >= 201103L) && \
+    !(defined(__DARWIN_C_LEVEL) && __DARWIN_C_LEVEL >= 200809L)
+long long strtoll(const char *, char **, int);
+#endif
+
+typedef int sock_t;
+#define INVALID_SOCKET (-1)
+#define SIZE_T_FMT "zu"
+typedef struct stat cs_stat_t;
+#define DIRSEP '/'
+#define to64(x) strtoll(x, NULL, 10)
+#define INT64_FMT PRId64
+#define INT64_X_FMT PRIx64
+
+#ifndef __cdecl
+#define __cdecl
+#endif
+
+#ifndef va_copy
+#ifdef __va_copy
+#define va_copy __va_copy
+#else
+#define va_copy(x, y) (x) = (y)
+#endif
+#endif
+
+#define closesocket(x) close(x)
+
+#ifndef MG_MAX_HTTP_REQUEST_SIZE
+#define MG_MAX_HTTP_REQUEST_SIZE 8192
+#endif
+
+#ifndef MG_MAX_HTTP_SEND_MBUF
+#define MG_MAX_HTTP_SEND_MBUF 4096
+#endif
+
+#ifndef MG_MAX_HTTP_HEADERS
+#define MG_MAX_HTTP_HEADERS 40
+#endif
+
+#ifndef CS_ENABLE_STDIO
+#define CS_ENABLE_STDIO 1
+#endif
+
+#ifndef MG_ENABLE_BROADCAST
+#define MG_ENABLE_BROADCAST 1
+#endif
+
+#ifndef MG_ENABLE_DIRECTORY_LISTING
+#define MG_ENABLE_DIRECTORY_LISTING 1
+#endif
+
+#ifndef MG_ENABLE_FILESYSTEM
+#define MG_ENABLE_FILESYSTEM 1
+#endif
+
+#ifndef MG_ENABLE_HTTP_CGI
+#define MG_ENABLE_HTTP_CGI MG_ENABLE_FILESYSTEM
+#endif
+
+#ifndef MG_NET_IF
+#define MG_NET_IF MG_NET_IF_SOCKET
+#endif
+
+#ifndef MG_HOSTS_FILE_NAME
+#define MG_HOSTS_FILE_NAME "/etc/hosts"
+#endif
+
+#ifndef MG_RESOLV_CONF_FILE_NAME
+#define MG_RESOLV_CONF_FILE_NAME "/etc/resolv.conf"
+#endif
+
+#endif /* CS_PLATFORM == CS_P_UNIX */
+#endif /* CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/platform_esp32.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_
+#define CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_
+#if CS_PLATFORM == CS_P_ESP32
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define SIZE_T_FMT "u"
+typedef struct stat cs_stat_t;
+#define DIRSEP '/'
+#define to64(x) strtoll(x, NULL, 10)
+#define INT64_FMT PRId64
+#define INT64_X_FMT PRIx64
+#define __cdecl
+#define _FILE_OFFSET_BITS 32
+
+#define MG_LWIP 1
+
+#ifndef MG_NET_IF
+#define MG_NET_IF MG_NET_IF_SOCKET
+#endif
+
+#ifndef CS_ENABLE_STDIO
+#define CS_ENABLE_STDIO 1
+#endif
+
+#endif /* CS_PLATFORM == CS_P_ESP32 */
+#endif /* CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/platform_esp8266.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_
+#define CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_
+#if CS_PLATFORM == CS_P_ESP8266
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define SIZE_T_FMT "u"
+typedef struct stat cs_stat_t;
+#define DIRSEP '/'
+#if !defined(MGOS_VFS_DEFINE_DIRENT)
+#define CS_DEFINE_DIRENT
+#endif
+
+#define to64(x) strtoll(x, NULL, 10)
+#define INT64_FMT PRId64
+#define INT64_X_FMT PRIx64
+#define __cdecl
+#define _FILE_OFFSET_BITS 32
+
+#if !defined(RTOS_SDK) && !defined(__cplusplus)
+#define fileno(x) -1
+#endif
+
+#define MG_LWIP 1
+
+/* struct timeval is defined in sys/time.h. */
+#define LWIP_TIMEVAL_PRIVATE 0
+
+#ifndef MG_NET_IF
+#include 
+#if LWIP_SOCKET /* RTOS SDK has LWIP sockets */
+#define MG_NET_IF MG_NET_IF_SOCKET
+#else
+#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL
+#endif
+#endif
+
+#ifndef CS_ENABLE_STDIO
+#define CS_ENABLE_STDIO 1
+#endif
+
+#define inet_ntop(af, src, dst, size)                                          \
+  (((af) == AF_INET) ? ipaddr_ntoa_r((const ip_addr_t *) (src), (dst), (size)) \
+                     : NULL)
+#define inet_pton(af, src, dst) \
+  (((af) == AF_INET) ? ipaddr_aton((src), (ip_addr_t *) (dst)) : 0)
+
+#endif /* CS_PLATFORM == CS_P_ESP8266 */
+#endif /* CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/platform_cc3100.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_
+#define CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_
+#if CS_PLATFORM == CS_P_CC3100
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define MG_NET_IF MG_NET_IF_SIMPLELINK
+#define MG_SSL_IF MG_SSL_IF_SIMPLELINK
+
+/*
+ * CC3100 SDK and STM32 SDK include headers w/out path, just like
+ * #include "simplelink.h". As result, we have to add all required directories
+ * into Makefile IPATH and do the same thing (include w/out path)
+ */
+
+#include 
+#include 
+#undef timeval
+
+typedef int sock_t;
+#define INVALID_SOCKET (-1)
+
+#define to64(x) strtoll(x, NULL, 10)
+#define INT64_FMT PRId64
+#define INT64_X_FMT PRIx64
+#define SIZE_T_FMT "u"
+
+#define SOMAXCONN 8
+
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
+char *inet_ntoa(struct in_addr in);
+int inet_pton(int af, const char *src, void *dst);
+
+#endif /* CS_PLATFORM == CS_P_CC3100 */
+#endif /* CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/platform_cc3200.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_
+#define CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_
+#if CS_PLATFORM == CS_P_CC3200
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#ifndef __TI_COMPILER_VERSION__
+#include 
+#include 
+#endif
+
+#define MG_NET_IF MG_NET_IF_SIMPLELINK
+#define MG_SSL_IF MG_SSL_IF_SIMPLELINK
+
+/* Only SPIFFS supports directories, SLFS does not. */
+#if defined(CC3200_FS_SPIFFS) && !defined(MG_ENABLE_DIRECTORY_LISTING)
+#define MG_ENABLE_DIRECTORY_LISTING 1
+#endif
+
+/* Amalgamated: #include "common/platforms/simplelink/cs_simplelink.h" */
+
+typedef int sock_t;
+#define INVALID_SOCKET (-1)
+#define SIZE_T_FMT "u"
+typedef struct stat cs_stat_t;
+#define DIRSEP '/'
+#define to64(x) strtoll(x, NULL, 10)
+#define INT64_FMT PRId64
+#define INT64_X_FMT PRIx64
+#define __cdecl
+
+#define fileno(x) -1
+
+/* Some functions we implement for Mongoose. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __TI_COMPILER_VERSION__
+struct SlTimeval_t;
+#define timeval SlTimeval_t
+int gettimeofday(struct timeval *t, void *tz);
+int settimeofday(const struct timeval *tv, const void *tz);
+
+int asprintf(char **strp, const char *fmt, ...);
+
+#endif
+
+/* TI's libc does not have stat & friends, add them. */
+#ifdef __TI_COMPILER_VERSION__
+
+#include 
+
+typedef unsigned int mode_t;
+typedef size_t _off_t;
+typedef long ssize_t;
+
+struct stat {
+  int st_ino;
+  mode_t st_mode;
+  int st_nlink;
+  time_t st_mtime;
+  off_t st_size;
+};
+
+int _stat(const char *pathname, struct stat *st);
+int stat(const char *pathname, struct stat *st);
+
+#define __S_IFMT 0170000
+
+#define __S_IFDIR 0040000
+#define __S_IFCHR 0020000
+#define __S_IFREG 0100000
+
+#define __S_ISTYPE(mode, mask) (((mode) &__S_IFMT) == (mask))
+
+#define S_IFDIR __S_IFDIR
+#define S_IFCHR __S_IFCHR
+#define S_IFREG __S_IFREG
+#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR)
+#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG)
+
+/* 5.x series compilers don't have va_copy, 16.x do. */
+#if __TI_COMPILER_VERSION__ < 16000000
+#define va_copy(apc, ap) ((apc) = (ap))
+#endif
+
+#endif /* __TI_COMPILER_VERSION__ */
+
+#ifdef CC3200_FS_SLFS
+#define MG_FS_SLFS
+#endif
+
+#if (defined(CC3200_FS_SPIFFS) || defined(CC3200_FS_SLFS)) && \
+    !defined(MG_ENABLE_FILESYSTEM)
+#define MG_ENABLE_FILESYSTEM 1
+#define CS_DEFINE_DIRENT
+#endif
+
+#ifndef CS_ENABLE_STDIO
+#define CS_ENABLE_STDIO 1
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CS_PLATFORM == CS_P_CC3200 */
+#endif /* CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/platform_msp432.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_PLATFORMS_PLATFORM_MSP432_H_
+#define CS_COMMON_PLATFORMS_PLATFORM_MSP432_H_
+#if CS_PLATFORM == CS_P_MSP432
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#ifndef __TI_COMPILER_VERSION__
+#include 
+#include 
+#endif
+
+#define MG_NET_IF MG_NET_IF_SIMPLELINK
+#define MG_SSL_IF MG_SSL_IF_SIMPLELINK
+
+/* Amalgamated: #include "common/platforms/simplelink/cs_simplelink.h" */
+
+typedef int sock_t;
+#define INVALID_SOCKET (-1)
+#define SIZE_T_FMT "u"
+typedef struct stat cs_stat_t;
+#define DIRSEP '/'
+#define to64(x) strtoll(x, NULL, 10)
+#define INT64_FMT PRId64
+#define INT64_X_FMT PRIx64
+#define __cdecl
+
+#define fileno(x) -1
+
+/* Some functions we implement for Mongoose. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __TI_COMPILER_VERSION__
+struct SlTimeval_t;
+#define timeval SlTimeval_t
+int gettimeofday(struct timeval *t, void *tz);
+#endif
+
+/* TI's libc does not have stat & friends, add them. */
+#ifdef __TI_COMPILER_VERSION__
+
+#include 
+
+typedef unsigned int mode_t;
+typedef size_t _off_t;
+typedef long ssize_t;
+
+struct stat {
+  int st_ino;
+  mode_t st_mode;
+  int st_nlink;
+  time_t st_mtime;
+  off_t st_size;
+};
+
+int _stat(const char *pathname, struct stat *st);
+#define stat(a, b) _stat(a, b)
+
+#define __S_IFMT 0170000
+
+#define __S_IFDIR 0040000
+#define __S_IFCHR 0020000
+#define __S_IFREG 0100000
+
+#define __S_ISTYPE(mode, mask) (((mode) &__S_IFMT) == (mask))
+
+#define S_IFDIR __S_IFDIR
+#define S_IFCHR __S_IFCHR
+#define S_IFREG __S_IFREG
+#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR)
+#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG)
+
+/* As of 5.2.7, TI compiler does not support va_copy() yet. */
+#define va_copy(apc, ap) ((apc) = (ap))
+
+#endif /* __TI_COMPILER_VERSION__ */
+
+#ifndef CS_ENABLE_STDIO
+#define CS_ENABLE_STDIO 1
+#endif
+
+#if (defined(CC3200_FS_SPIFFS) || defined(CC3200_FS_SLFS)) && \
+    !defined(MG_ENABLE_FILESYSTEM)
+#define MG_ENABLE_FILESYSTEM 1
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CS_PLATFORM == CS_P_MSP432 */
+#endif /* CS_COMMON_PLATFORMS_PLATFORM_MSP432_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/platform_tm4c129.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_PLATFORMS_PLATFORM_TM4C129_H_
+#define CS_COMMON_PLATFORMS_PLATFORM_TM4C129_H_
+#if CS_PLATFORM == CS_P_TM4C129
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#ifndef __TI_COMPILER_VERSION__
+#include 
+#include 
+#endif
+
+#define SIZE_T_FMT "u"
+typedef struct stat cs_stat_t;
+#define DIRSEP '/'
+#define to64(x) strtoll(x, NULL, 10)
+#define INT64_FMT PRId64
+#define INT64_X_FMT PRIx64
+#define __cdecl
+
+#ifndef MG_NET_IF
+#include 
+#if LWIP_SOCKET
+#define MG_NET_IF MG_NET_IF_SOCKET
+#else
+#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL
+#endif
+#define MG_LWIP 1
+#elif MG_NET_IF == MG_NET_IF_SIMPLELINK
+/* Amalgamated: #include "common/platforms/simplelink/cs_simplelink.h" */
+#endif
+
+#ifndef CS_ENABLE_STDIO
+#define CS_ENABLE_STDIO 1
+#endif
+
+#ifdef __TI_COMPILER_VERSION__
+/* As of 5.2.8, TI compiler does not support va_copy() yet. */
+#define va_copy(apc, ap) ((apc) = (ap))
+#endif /* __TI_COMPILER_VERSION__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CS_PLATFORM == CS_P_TM4C129 */
+#endif /* CS_COMMON_PLATFORMS_PLATFORM_TM4C129_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/platform_mbed.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_PLATFORMS_PLATFORM_MBED_H_
+#define CS_COMMON_PLATFORMS_PLATFORM_MBED_H_
+#if CS_PLATFORM == CS_P_MBED
+
+/*
+ * mbed.h contains C++ code (e.g. templates), thus, it should be processed
+ * only if included directly to startup file (ex: main.cpp)
+ */
+#ifdef __cplusplus
+/* Amalgamated: #include "mbed.h" */
+#endif /* __cplusplus */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+typedef struct stat cs_stat_t;
+#define DIRSEP '/'
+
+#ifndef CS_ENABLE_STDIO
+#define CS_ENABLE_STDIO 1
+#endif
+
+/*
+ * mbed can be compiled with the ARM compiler which
+ * just doesn't come with a gettimeofday shim
+ * because it's a BSD API and ARM targets embedded
+ * non-unix platforms.
+ */
+#if defined(__ARMCC_VERSION) || defined(__ICCARM__)
+#define _TIMEVAL_DEFINED
+#define gettimeofday _gettimeofday
+
+/* copied from GCC on ARM; for some reason useconds are signed */
+typedef long suseconds_t; /* microseconds (signed) */
+struct timeval {
+  time_t tv_sec;       /* seconds */
+  suseconds_t tv_usec; /* and microseconds */
+};
+
+#endif
+
+#if MG_NET_IF == MG_NET_IF_SIMPLELINK
+
+#define MG_SIMPLELINK_NO_OSI 1
+
+#include 
+
+typedef int sock_t;
+#define INVALID_SOCKET (-1)
+
+#define to64(x) strtoll(x, NULL, 10)
+#define INT64_FMT PRId64
+#define INT64_X_FMT PRIx64
+#define SIZE_T_FMT "u"
+
+#define SOMAXCONN 8
+
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
+char *inet_ntoa(struct in_addr in);
+int inet_pton(int af, const char *src, void *dst);
+int inet_aton(const char *cp, struct in_addr *inp);
+in_addr_t inet_addr(const char *cp);
+
+#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */
+
+#endif /* CS_PLATFORM == CS_P_MBED */
+#endif /* CS_COMMON_PLATFORMS_PLATFORM_MBED_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/platform_nrf51.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+#ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_
+#define CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_
+#if CS_PLATFORM == CS_P_NRF51
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define to64(x) strtoll(x, NULL, 10)
+
+#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL
+#define MG_LWIP 1
+#define MG_ENABLE_IPV6 1
+
+/*
+ * For ARM C Compiler, make lwip to export `struct timeval`; for other
+ * compilers, suppress it.
+ */
+#if !defined(__ARMCC_VERSION)
+#define LWIP_TIMEVAL_PRIVATE 0
+#else
+struct timeval;
+int gettimeofday(struct timeval *tp, void *tzp);
+#endif
+
+#define INT64_FMT PRId64
+#define SIZE_T_FMT "u"
+
+/*
+ * ARM C Compiler doesn't have strdup, so we provide it
+ */
+#define CS_ENABLE_STRDUP defined(__ARMCC_VERSION)
+
+#endif /* CS_PLATFORM == CS_P_NRF51 */
+#endif /* CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/platform_nrf52.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+#ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_
+#define CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_
+#if CS_PLATFORM == CS_P_NRF52
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define to64(x) strtoll(x, NULL, 10)
+
+#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL
+#define MG_LWIP 1
+#define MG_ENABLE_IPV6 1
+
+#if !defined(ENOSPC)
+#define ENOSPC 28 /* No space left on device */
+#endif
+
+/*
+ * For ARM C Compiler, make lwip to export `struct timeval`; for other
+ * compilers, suppress it.
+ */
+#if !defined(__ARMCC_VERSION)
+#define LWIP_TIMEVAL_PRIVATE 0
+#endif
+
+#define INT64_FMT PRId64
+#define SIZE_T_FMT "u"
+
+/*
+ * ARM C Compiler doesn't have strdup, so we provide it
+ */
+#define CS_ENABLE_STRDUP defined(__ARMCC_VERSION)
+
+#endif /* CS_PLATFORM == CS_P_NRF52 */
+#endif /* CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/simplelink/cs_simplelink.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_
+#define CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_
+
+#if defined(MG_NET_IF) && MG_NET_IF == MG_NET_IF_SIMPLELINK
+
+/* If simplelink.h is already included, all bets are off. */
+#if !defined(__SIMPLELINK_H__)
+
+#include 
+
+#ifndef __TI_COMPILER_VERSION__
+#undef __CONCAT
+#undef FD_CLR
+#undef FD_ISSET
+#undef FD_SET
+#undef FD_SETSIZE
+#undef FD_ZERO
+#undef fd_set
+#endif
+
+#if CS_PLATFORM == CS_P_CC3220
+#include 
+#include 
+#include 
+#include 
+#else
+/* We want to disable SL_INC_STD_BSD_API_NAMING, so we include user.h ourselves
+ * and undef it. */
+#define PROVISIONING_API_H_
+#include 
+#undef PROVISIONING_API_H_
+#undef SL_INC_STD_BSD_API_NAMING
+
+#include 
+#include 
+#endif /* CS_PLATFORM == CS_P_CC3220 */
+
+/* Now define only the subset of the BSD API that we use.
+ * Notably, close(), read() and write() are not defined. */
+#define AF_INET SL_AF_INET
+
+#define socklen_t SlSocklen_t
+#define sockaddr SlSockAddr_t
+#define sockaddr_in SlSockAddrIn_t
+#define in_addr SlInAddr_t
+
+#define SOCK_STREAM SL_SOCK_STREAM
+#define SOCK_DGRAM SL_SOCK_DGRAM
+
+#define htonl sl_Htonl
+#define ntohl sl_Ntohl
+#define htons sl_Htons
+#define ntohs sl_Ntohs
+
+#ifndef EACCES
+#define EACCES SL_EACCES
+#endif
+#ifndef EAFNOSUPPORT
+#define EAFNOSUPPORT SL_EAFNOSUPPORT
+#endif
+#ifndef EAGAIN
+#define EAGAIN SL_EAGAIN
+#endif
+#ifndef EBADF
+#define EBADF SL_EBADF
+#endif
+#ifndef EINVAL
+#define EINVAL SL_EINVAL
+#endif
+#ifndef ENOMEM
+#define ENOMEM SL_ENOMEM
+#endif
+#ifndef EWOULDBLOCK
+#define EWOULDBLOCK SL_EWOULDBLOCK
+#endif
+
+#define SOMAXCONN 8
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
+char *inet_ntoa(struct in_addr in);
+int inet_pton(int af, const char *src, void *dst);
+
+struct mg_mgr;
+struct mg_connection;
+
+typedef void (*mg_init_cb)(struct mg_mgr *mgr);
+bool mg_start_task(int priority, int stack_size, mg_init_cb mg_init);
+
+void mg_run_in_task(void (*cb)(struct mg_mgr *mgr, void *arg), void *cb_arg);
+
+int sl_fs_init(void);
+
+void sl_restart_cb(struct mg_mgr *mgr);
+
+int sl_set_ssl_opts(int sock, struct mg_connection *nc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !defined(__SIMPLELINK_H__) */
+
+/* Compatibility with older versions of SimpleLink */
+#if SL_MAJOR_VERSION_NUM < 2
+
+#define SL_ERROR_BSD_EAGAIN SL_EAGAIN
+#define SL_ERROR_BSD_EALREADY SL_EALREADY
+#define SL_ERROR_BSD_ENOPROTOOPT SL_ENOPROTOOPT
+#define SL_ERROR_BSD_ESECDATEERROR SL_ESECDATEERROR
+#define SL_ERROR_BSD_ESECSNOVERIFY SL_ESECSNOVERIFY
+#define SL_ERROR_FS_FAILED_TO_ALLOCATE_MEM SL_FS_ERR_FAILED_TO_ALLOCATE_MEM
+#define SL_ERROR_FS_FILE_HAS_NOT_BEEN_CLOSE_CORRECTLY \
+  SL_FS_FILE_HAS_NOT_BEEN_CLOSE_CORRECTLY
+#define SL_ERROR_FS_FILE_NAME_EXIST SL_FS_FILE_NAME_EXIST
+#define SL_ERROR_FS_FILE_NOT_EXISTS SL_FS_ERR_FILE_NOT_EXISTS
+#define SL_ERROR_FS_NO_AVAILABLE_NV_INDEX SL_FS_ERR_NO_AVAILABLE_NV_INDEX
+#define SL_ERROR_FS_NOT_ENOUGH_STORAGE_SPACE SL_FS_ERR_NO_AVAILABLE_BLOCKS
+#define SL_ERROR_FS_NOT_SUPPORTED SL_FS_ERR_NOT_SUPPORTED
+#define SL_ERROR_FS_WRONG_FILE_NAME SL_FS_WRONG_FILE_NAME
+#define SL_ERROR_FS_INVALID_HANDLE SL_FS_ERR_INVALID_HANDLE
+#define SL_NETCFG_MAC_ADDRESS_GET SL_MAC_ADDRESS_GET
+#define SL_SOCKET_FD_ZERO SL_FD_ZERO
+#define SL_SOCKET_FD_SET SL_FD_SET
+#define SL_SOCKET_FD_ISSET SL_FD_ISSET
+#define SL_SO_SECURE_DOMAIN_NAME_VERIFICATION SO_SECURE_DOMAIN_NAME_VERIFICATION
+
+#define SL_FS_READ FS_MODE_OPEN_READ
+#define SL_FS_WRITE FS_MODE_OPEN_WRITE
+
+#define SL_FI_FILE_SIZE(fi) ((fi).FileLen)
+#define SL_FI_FILE_MAX_SIZE(fi) ((fi).AllocatedLen)
+
+#define SlDeviceVersion_t SlVersionFull
+#define sl_DeviceGet sl_DevGet
+#define SL_DEVICE_GENERAL SL_DEVICE_GENERAL_CONFIGURATION
+#define SL_LEN_TYPE _u8
+#define SL_OPT_TYPE _u8
+
+#else /* SL_MAJOR_VERSION_NUM >= 2 */
+
+#define FS_MODE_OPEN_CREATE(max_size, flag) \
+  (SL_FS_CREATE | SL_FS_CREATE_MAX_SIZE(max_size))
+#define SL_FI_FILE_SIZE(fi) ((fi).Len)
+#define SL_FI_FILE_MAX_SIZE(fi) ((fi).MaxSize)
+
+#define SL_LEN_TYPE _u16
+#define SL_OPT_TYPE _u16
+
+#endif /* SL_MAJOR_VERSION_NUM < 2 */
+
+int slfs_open(const unsigned char *fname, uint32_t flags);
+
+#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */
+
+#endif /* CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/platform_wince.h"
+#endif
+#ifndef CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_
+#define CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_
+
+#if CS_PLATFORM == CS_P_WINCE
+
+/*
+ * MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
+ * MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
+ * MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
+ * MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
+ * MSVC++ 9.0  _MSC_VER == 1500 (Visual Studio 2008)
+ * MSVC++ 8.0  _MSC_VER == 1400 (Visual Studio 2005)
+ * MSVC++ 7.1  _MSC_VER == 1310 (Visual Studio 2003)
+ * MSVC++ 7.0  _MSC_VER == 1300
+ * MSVC++ 6.0  _MSC_VER == 1200
+ * MSVC++ 5.0  _MSC_VER == 1100
+ */
+#pragma warning(disable : 4127) /* FD_SET() emits warning, disable it */
+#pragma warning(disable : 4204) /* missing c99 support */
+
+#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
+#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
+#endif
+
+#ifndef _CRT_SECURE_NO_WARNINGS
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#pragma comment(lib, "ws2.lib") /* Linking with WinCE winsock library */
+
+#include 
+#include 
+#include 
+
+#define strdup _strdup
+
+#ifndef EINPROGRESS
+#define EINPROGRESS WSAEINPROGRESS
+#endif
+
+#ifndef EWOULDBLOCK
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#endif
+
+#ifndef EAGAIN
+#define EAGAIN EWOULDBLOCK
+#endif
+
+#ifndef __func__
+#define STRX(x) #x
+#define STR(x) STRX(x)
+#define __func__ __FILE__ ":" STR(__LINE__)
+#endif
+
+#define snprintf _snprintf
+#define fileno _fileno
+#define vsnprintf _vsnprintf
+#define sleep(x) Sleep((x) *1000)
+#define to64(x) _atoi64(x)
+#define rmdir _rmdir
+
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+#define fseeko(x, y, z) _fseeki64((x), (y), (z))
+#else
+#define fseeko(x, y, z) fseek((x), (y), (z))
+#endif
+
+typedef int socklen_t;
+
+#if _MSC_VER >= 1700
+#include 
+#else
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#endif
+
+typedef SOCKET sock_t;
+typedef uint32_t in_addr_t;
+
+#ifndef UINT16_MAX
+#define UINT16_MAX 65535
+#endif
+
+#ifndef UINT32_MAX
+#define UINT32_MAX 4294967295
+#endif
+
+#ifndef pid_t
+#define pid_t HANDLE
+#endif
+
+#define INT64_FMT "I64d"
+#define INT64_X_FMT "I64x"
+/* TODO(alashkin): check if this is correct */
+#define SIZE_T_FMT "u"
+
+#define DIRSEP '\\'
+#define CS_DEFINE_DIRENT
+
+#ifndef va_copy
+#ifdef __va_copy
+#define va_copy __va_copy
+#else
+#define va_copy(x, y) (x) = (y)
+#endif
+#endif
+
+#ifndef MG_MAX_HTTP_REQUEST_SIZE
+#define MG_MAX_HTTP_REQUEST_SIZE 8192
+#endif
+
+#ifndef MG_MAX_HTTP_SEND_MBUF
+#define MG_MAX_HTTP_SEND_MBUF 4096
+#endif
+
+#ifndef MG_MAX_HTTP_HEADERS
+#define MG_MAX_HTTP_HEADERS 40
+#endif
+
+#ifndef CS_ENABLE_STDIO
+#define CS_ENABLE_STDIO 1
+#endif
+
+#define abort() DebugBreak();
+
+#ifndef BUFSIZ
+#define BUFSIZ 4096
+#endif
+/*
+ * Explicitly disabling MG_ENABLE_THREADS for WinCE
+ * because they are enabled for _WIN32 by default
+ */
+#ifndef MG_ENABLE_THREADS
+#define MG_ENABLE_THREADS 0
+#endif
+
+#ifndef MG_ENABLE_FILESYSTEM
+#define MG_ENABLE_FILESYSTEM 1
+#endif
+
+#ifndef MG_NET_IF
+#define MG_NET_IF MG_NET_IF_SOCKET
+#endif
+
+typedef struct _stati64 {
+  uint32_t st_mtime;
+  uint32_t st_size;
+  uint32_t st_mode;
+} cs_stat_t;
+
+/*
+ * WinCE 6.0 has a lot of useful definitions in ATL (not windows.h) headers
+ * use #ifdefs to avoid conflicts
+ */
+
+#ifndef ENOENT
+#define ENOENT ERROR_PATH_NOT_FOUND
+#endif
+
+#ifndef EACCES
+#define EACCES ERROR_ACCESS_DENIED
+#endif
+
+#ifndef ENOMEM
+#define ENOMEM ERROR_NOT_ENOUGH_MEMORY
+#endif
+
+#ifndef _UINTPTR_T_DEFINED
+typedef unsigned int *uintptr_t;
+#endif
+
+#define _S_IFREG 2
+#define _S_IFDIR 4
+
+#ifndef S_ISDIR
+#define S_ISDIR(x) (((x) &_S_IFDIR) != 0)
+#endif
+
+#ifndef S_ISREG
+#define S_ISREG(x) (((x) &_S_IFREG) != 0)
+#endif
+
+int open(const char *filename, int oflag, int pmode);
+int _wstati64(const wchar_t *path, cs_stat_t *st);
+const char *strerror();
+
+#endif /* CS_PLATFORM == CS_P_WINCE */
+#endif /* CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/platform_nxp_lpc.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_
+#define CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_
+
+#if CS_PLATFORM == CS_P_NXP_LPC
+
+#include 
+#include 
+#include 
+
+#define SIZE_T_FMT "u"
+typedef struct stat cs_stat_t;
+#define INT64_FMT "lld"
+#define INT64_X_FMT "llx"
+#define __cdecl
+
+#define MG_LWIP 1
+
+#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL
+
+/*
+ * LPCXpress comes with 3 C library implementations: Newlib, NewlibNano and
+ *Redlib.
+ * See https://community.nxp.com/message/630860 for more details.
+ *
+ * Redlib is the default and lacks certain things, so we provide them.
+ */
+#ifdef __REDLIB_INTERFACE_VERSION__
+
+/* Let LWIP define timeval for us. */
+#define LWIP_TIMEVAL_PRIVATE 1
+
+#define va_copy(d, s) __builtin_va_copy(d, s)
+
+#define CS_ENABLE_TO64 1
+#define to64(x) cs_to64(x)
+
+#define CS_ENABLE_STRDUP 1
+
+#else
+
+#include 
+#define LWIP_TIMEVAL_PRIVATE 0
+#define to64(x) strtoll(x, NULL, 10)
+
+#endif
+
+#endif /* CS_PLATFORM == CS_P_NXP_LPC */
+#endif /* CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/platform_nxp_kinetis.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_
+#define CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_
+
+#if CS_PLATFORM == CS_P_NXP_KINETIS
+
+#include 
+#include 
+#include 
+#include 
+
+#define SIZE_T_FMT "u"
+typedef struct stat cs_stat_t;
+#define to64(x) strtoll(x, NULL, 10)
+#define INT64_FMT "lld"
+#define INT64_X_FMT "llx"
+#define __cdecl
+
+#define MG_LWIP 1
+
+#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL
+
+/* struct timeval is defined in sys/time.h. */
+#define LWIP_TIMEVAL_PRIVATE 0
+
+#endif /* CS_PLATFORM == CS_P_NXP_KINETIS */
+#endif /* CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/platform_pic32.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_
+#define CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_
+
+#if CS_PLATFORM == CS_P_PIC32
+
+#define MG_NET_IF MG_NET_IF_PIC32
+
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+
+#include 
+
+typedef TCP_SOCKET sock_t;
+#define to64(x) strtoll(x, NULL, 10)
+
+#define SIZE_T_FMT "lu"
+#define INT64_FMT "lld"
+
+#ifndef CS_ENABLE_STDIO
+#define CS_ENABLE_STDIO 1
+#endif
+
+char *inet_ntoa(struct in_addr in);
+
+#endif /* CS_PLATFORM == CS_P_PIC32 */
+
+#endif /* CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/platform_stm32.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_PLATFORMS_PLATFORM_STM32_H_
+#define CS_COMMON_PLATFORMS_PLATFORM_STM32_H_
+#if CS_PLATFORM == CS_P_STM32
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#define to64(x) strtoll(x, NULL, 10)
+#define INT64_FMT PRId64
+#define SIZE_T_FMT "u"
+typedef struct stat cs_stat_t;
+#define DIRSEP '/'
+
+#ifndef CS_ENABLE_STDIO
+#define CS_ENABLE_STDIO 1
+#endif
+
+#ifndef MG_ENABLE_FILESYSTEM
+#define MG_ENABLE_FILESYSTEM 1
+#endif
+
+#endif /* CS_PLATFORM == CS_P_STM32 */
+#endif /* CS_COMMON_PLATFORMS_PLATFORM_STM32_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/platforms/lwip/mg_lwip.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_
+#define CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_
+
+#ifndef MG_LWIP
+#define MG_LWIP 0
+#endif
+
+#if MG_LWIP
+
+/*
+ * When compiling for nRF5x chips with arm-none-eabi-gcc, it has BYTE_ORDER
+ * already defined, so in order to avoid warnings in lwip, we have to undefine
+ * it.
+ *
+ * TODO: Check if in the future versions of nRF5 SDK that changes.
+ *       Current version of nRF51 SDK: 0.8.0
+ *                          nRF5 SDK:  0.9.0
+ */
+#if CS_PLATFORM == CS_P_NRF51 || CS_PLATFORM == CS_P_NRF52
+#undef BYTE_ORDER
+#endif
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#ifndef LWIP_PROVIDE_ERRNO
+#include 
+#endif
+
+#if LWIP_SOCKET
+#include 
+#else
+/* We really need the definitions from sockets.h. */
+#undef LWIP_SOCKET
+#define LWIP_SOCKET 1
+#include 
+#undef LWIP_SOCKET
+#define LWIP_SOCKET 0
+#endif
+
+#define INVALID_SOCKET (-1)
+#define SOMAXCONN 10
+typedef int sock_t;
+
+#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL
+struct mg_mgr;
+struct mg_connection;
+uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr);
+void mg_lwip_set_keepalive_params(struct mg_connection *nc, int idle,
+                                  int interval, int count);
+#endif
+
+/* For older version of LWIP */
+#ifndef ipX_2_ip
+#define ipX_2_ip(x) (x)
+#endif
+
+#endif /* MG_LWIP */
+
+#endif /* CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/cs_md5.h"
+#endif
+/*
+ * Copyright (c) 2014 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_MD5_H_
+#define CS_COMMON_MD5_H_
+
+/* Amalgamated: #include "common/platform.h" */
+
+#ifndef CS_DISABLE_MD5
+#define CS_DISABLE_MD5 0
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct {
+  uint32_t buf[4];
+  uint32_t bits[2];
+  unsigned char in[64];
+} cs_md5_ctx;
+
+void cs_md5_init(cs_md5_ctx *c);
+void cs_md5_update(cs_md5_ctx *c, const unsigned char *data, size_t len);
+void cs_md5_final(unsigned char *md, cs_md5_ctx *c);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CS_COMMON_MD5_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/cs_sha1.h"
+#endif
+/*
+ * Copyright (c) 2014 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_SHA1_H_
+#define CS_COMMON_SHA1_H_
+
+#ifndef CS_DISABLE_SHA1
+#define CS_DISABLE_SHA1 0
+#endif
+
+#if !CS_DISABLE_SHA1
+
+/* Amalgamated: #include "common/platform.h" */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct {
+  uint32_t state[5];
+  uint32_t count[2];
+  unsigned char buffer[64];
+} cs_sha1_ctx;
+
+void cs_sha1_init(cs_sha1_ctx *);
+void cs_sha1_update(cs_sha1_ctx *, const unsigned char *data, uint32_t len);
+void cs_sha1_final(unsigned char digest[20], cs_sha1_ctx *);
+void cs_hmac_sha1(const unsigned char *key, size_t key_len,
+                  const unsigned char *text, size_t text_len,
+                  unsigned char out[20]);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CS_DISABLE_SHA1 */
+
+#endif /* CS_COMMON_SHA1_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/cs_time.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_CS_TIME_H_
+#define CS_COMMON_CS_TIME_H_
+
+#include 
+
+/* Amalgamated: #include "common/platform.h" */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Sub-second granularity time(). */
+double cs_time(void);
+
+/*
+ * Similar to (non-standard) timegm, converts broken-down time into the number
+ * of seconds since Unix Epoch.
+ */
+double cs_timegm(const struct tm *tm);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CS_COMMON_CS_TIME_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/mg_str.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_MG_STR_H_
+#define CS_COMMON_MG_STR_H_
+
+#include 
+
+/* Amalgamated: #include "common/platform.h" */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Describes chunk of memory */
+struct mg_str {
+  const char *p; /* Memory chunk pointer */
+  size_t len;    /* Memory chunk length */
+};
+
+/*
+ * Helper function for creating mg_str struct from plain C string.
+ * `NULL` is allowed and becomes `{NULL, 0}`.
+ */
+struct mg_str mg_mk_str(const char *s);
+
+/*
+ * Like `mg_mk_str`, but takes string length explicitly.
+ */
+struct mg_str mg_mk_str_n(const char *s, size_t len);
+
+/* Macro for initializing mg_str. */
+#define MG_MK_STR(str_literal) \
+  { str_literal, sizeof(str_literal) - 1 }
+#define MG_NULL_STR \
+  { NULL, 0 }
+
+/*
+ * Cross-platform version of `strcmp()` where where first string is
+ * specified by `struct mg_str`.
+ */
+int mg_vcmp(const struct mg_str *str2, const char *str1);
+
+/*
+ * Cross-platform version of `strncasecmp()` where first string is
+ * specified by `struct mg_str`.
+ */
+int mg_vcasecmp(const struct mg_str *str2, const char *str1);
+
+/* Creates a copy of s (heap-allocated). */
+struct mg_str mg_strdup(const struct mg_str s);
+
+/*
+ * Creates a copy of s (heap-allocated).
+ * Resulting string is NUL-terminated (but NUL is not included in len).
+ */
+struct mg_str mg_strdup_nul(const struct mg_str s);
+
+/*
+ * Locates character in a string.
+ */
+const char *mg_strchr(const struct mg_str s, int c);
+
+/*
+ * Compare two `mg_str`s; return value is the same as `strcmp`.
+ */
+int mg_strcmp(const struct mg_str str1, const struct mg_str str2);
+
+/*
+ * Like `mg_strcmp`, but compares at most `n` characters.
+ */
+int mg_strncmp(const struct mg_str str1, const struct mg_str str2, size_t n);
+
+/*
+ * Finds the first occurrence of a substring `needle` in the `haystack`.
+ */
+const char *mg_strstr(const struct mg_str haystack, const struct mg_str needle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CS_COMMON_MG_STR_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/mbuf.h"
+#endif
+/*
+ * Copyright (c) 2015 Cesanta Software Limited
+ * All rights reserved
+ */
+
+/*
+ * === Memory Buffers
+ *
+ * Mbufs are mutable/growing memory buffers, like C++ strings.
+ * Mbuf can append data to the end of a buffer or insert data into arbitrary
+ * position in the middle of a buffer. The buffer grows automatically when
+ * needed.
+ */
+
+#ifndef CS_COMMON_MBUF_H_
+#define CS_COMMON_MBUF_H_
+
+#include 
+/* Amalgamated: #include "common/platform.h" */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#ifndef MBUF_SIZE_MULTIPLIER
+#define MBUF_SIZE_MULTIPLIER 1.5
+#endif
+
+/* Memory buffer descriptor */
+struct mbuf {
+  char *buf;   /* Buffer pointer */
+  size_t len;  /* Data length. Data is located between offset 0 and len. */
+  size_t size; /* Buffer size allocated by realloc(1). Must be >= len */
+};
+
+/*
+ * Initialises an Mbuf.
+ * `initial_capacity` specifies the initial capacity of the mbuf.
+ */
+void mbuf_init(struct mbuf *, size_t initial_capacity);
+
+/* Frees the space allocated for the mbuffer and resets the mbuf structure. */
+void mbuf_free(struct mbuf *);
+
+/*
+ * Appends data to the Mbuf.
+ *
+ * Returns the number of bytes appended or 0 if out of memory.
+ */
+size_t mbuf_append(struct mbuf *, const void *data, size_t data_size);
+
+/*
+ * Inserts data at a specified offset in the Mbuf.
+ *
+ * Existing data will be shifted forwards and the buffer will
+ * be grown if necessary.
+ * Returns the number of bytes inserted.
+ */
+size_t mbuf_insert(struct mbuf *, size_t, const void *, size_t);
+
+/* Removes `data_size` bytes from the beginning of the buffer. */
+void mbuf_remove(struct mbuf *, size_t data_size);
+
+/*
+ * Resizes an Mbuf.
+ *
+ * If `new_size` is smaller than buffer's `len`, the
+ * resize is not performed.
+ */
+void mbuf_resize(struct mbuf *, size_t new_size);
+
+/* Shrinks an Mbuf by resizing its `size` to `len`. */
+void mbuf_trim(struct mbuf *);
+
+#if defined(__cplusplus)
+}
+#endif /* __cplusplus */
+
+#endif /* CS_COMMON_MBUF_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/cs_base64.h"
+#endif
+/*
+ * Copyright (c) 2014 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_CS_BASE64_H_
+#define CS_COMMON_CS_BASE64_H_
+
+#ifndef DISABLE_BASE64
+#define DISABLE_BASE64 0
+#endif
+
+#if !DISABLE_BASE64
+
+#include 
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void (*cs_base64_putc_t)(char, void *);
+
+struct cs_base64_ctx {
+  /* cannot call it putc because it's a macro on some environments */
+  cs_base64_putc_t b64_putc;
+  unsigned char chunk[3];
+  int chunk_size;
+  void *user_data;
+};
+
+void cs_base64_init(struct cs_base64_ctx *ctx, cs_base64_putc_t putc,
+                    void *user_data);
+void cs_base64_update(struct cs_base64_ctx *ctx, const char *str, size_t len);
+void cs_base64_finish(struct cs_base64_ctx *ctx);
+
+void cs_base64_encode(const unsigned char *src, int src_len, char *dst);
+void cs_fprint_base64(FILE *f, const unsigned char *src, int src_len);
+int cs_base64_decode(const unsigned char *s, int len, char *dst, int *dec_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DISABLE_BASE64 */
+
+#endif /* CS_COMMON_CS_BASE64_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/str_util.h"
+#endif
+/*
+ * Copyright (c) 2015 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_STR_UTIL_H_
+#define CS_COMMON_STR_UTIL_H_
+
+#include 
+#include 
+
+/* Amalgamated: #include "common/mg_str.h" */
+/* Amalgamated: #include "common/platform.h" */
+
+#ifndef CS_ENABLE_STRDUP
+#define CS_ENABLE_STRDUP 0
+#endif
+
+#ifndef CS_ENABLE_TO64
+#define CS_ENABLE_TO64 0
+#endif
+
+/*
+ * Expands to a string representation of its argument: e.g.
+ * `CS_STRINGIFY_LIT(5) expands to "5"`
+ */
+#define CS_STRINGIFY_LIT(x) #x
+
+/*
+ * Expands to a string representation of its argument, which is allowed
+ * to be a macro: e.g.
+ *
+ * #define FOO 123
+ * CS_STRINGIFY_MACRO(FOO)
+ *
+ * expands to 123.
+ */
+#define CS_STRINGIFY_MACRO(x) CS_STRINGIFY_LIT(x)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Equivalent of standard `strnlen()`.
+ */
+size_t c_strnlen(const char *s, size_t maxlen);
+
+/*
+ * Equivalent of standard `snprintf()`.
+ */
+int c_snprintf(char *buf, size_t buf_size, const char *format, ...);
+
+/*
+ * Equivalent of standard `vsnprintf()`.
+ */
+int c_vsnprintf(char *buf, size_t buf_size, const char *format, va_list ap);
+
+/*
+ * Find the first occurrence of find in s, where the search is limited to the
+ * first slen characters of s.
+ */
+const char *c_strnstr(const char *s, const char *find, size_t slen);
+
+/*
+ * Stringify binary data. Output buffer size must be 2 * size_of_input + 1
+ * because each byte of input takes 2 bytes in string representation
+ * plus 1 byte for the terminating \0 character.
+ */
+void cs_to_hex(char *to, const unsigned char *p, size_t len);
+
+/*
+ * Convert stringified binary data back to binary.
+ * Does the reverse of `cs_to_hex()`.
+ */
+void cs_from_hex(char *to, const char *p, size_t len);
+
+#if CS_ENABLE_STRDUP
+/*
+ * Equivalent of standard `strdup()`, defined if only `CS_ENABLE_STRDUP` is 1.
+ */
+char *strdup(const char *src);
+#endif
+
+#if CS_ENABLE_TO64
+#include 
+/*
+ * Simple string -> int64 conversion routine.
+ */
+int64_t cs_to64(const char *s);
+#endif
+
+/*
+ * Cross-platform version of `strncasecmp()`.
+ */
+int mg_ncasecmp(const char *s1, const char *s2, size_t len);
+
+/*
+ * Cross-platform version of `strcasecmp()`.
+ */
+int mg_casecmp(const char *s1, const char *s2);
+
+/*
+ * Prints message to the buffer. If the buffer is large enough to hold the
+ * message, it returns buffer. If buffer is to small, it allocates a large
+ * enough buffer on heap and returns allocated buffer.
+ * This is a supposed use case:
+ *
+ * ```c
+ *    char buf[5], *p = buf;
+ *    mg_avprintf(&p, sizeof(buf), "%s", "hi there");
+ *    use_p_somehow(p);
+ *    if (p != buf) {
+ *      free(p);
+ *    }
+ * ```
+ *
+ * The purpose of this is to avoid malloc-ing if generated strings are small.
+ */
+int mg_asprintf(char **buf, size_t size, const char *fmt, ...);
+
+/* Same as mg_asprintf, but takes varargs list. */
+int mg_avprintf(char **buf, size_t size, const char *fmt, va_list ap);
+
+/*
+ * A helper function for traversing a comma separated list of values.
+ * It returns a list pointer shifted to the next value or NULL if the end
+ * of the list found.
+ * The value is stored in a val vector. If the value has a form "x=y", then
+ * eq_val vector is initialised to point to the "y" part, and val vector length
+ * is adjusted to point only to "x".
+ * If the list is just a comma separated list of entries, like "aa,bb,cc" then
+ * `eq_val` will contain zero-length string.
+ *
+ * The purpose of this function is to parse comma separated string without
+ * any copying/memory allocation.
+ */
+const char *mg_next_comma_list_entry(const char *list, struct mg_str *val,
+                                     struct mg_str *eq_val);
+
+/*
+ * Like `mg_next_comma_list_entry()`, but takes `list` as `struct mg_str`.
+ */
+struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val,
+                                         struct mg_str *eq_val);
+
+/*
+ * Matches 0-terminated string (mg_match_prefix) or string with given length
+ * mg_match_prefix_n against a glob pattern. Glob syntax:
+ * ```
+ * - * matches zero or more characters until a slash character /
+ * - ** matches zero or more characters
+ * - ? Matches exactly one character which is not a slash /
+ * - | or ,  divides alternative patterns
+ * - any other character matches itself
+ * ```
+ * Match is case-insensitive. Return number of bytes matched.
+ * Examples:
+ * ```
+ * mg_match_prefix("a*f", len, "abcdefgh") == 6
+ * mg_match_prefix("a*f", len, "abcdexgh") == 0
+ * mg_match_prefix("a*f|de*,xy", len, "defgh") == 5
+ * mg_match_prefix("?*", len, "abc") == 3
+ * mg_match_prefix("?*", len, "") == 0
+ * ```
+ */
+size_t mg_match_prefix(const char *pattern, int pattern_len, const char *str);
+
+/*
+ * Like `mg_match_prefix()`, but takes `pattern` and `str` as `struct mg_str`.
+ */
+size_t mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CS_COMMON_STR_UTIL_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "common/queue.h"
+#endif
+/* clang-format off */
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)queue.h	8.5 (Berkeley) 8/20/94
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define	_SYS_QUEUE_H_
+
+/*
+ * This file defines four types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists and tail queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction.  Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may be traversed in either direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ *
+ *				SLIST	LIST	STAILQ	TAILQ
+ * _HEAD			+	+	+	+
+ * _CLASS_HEAD			+	+	+	+
+ * _HEAD_INITIALIZER		+	+	+	+
+ * _ENTRY			+	+	+	+
+ * _CLASS_ENTRY			+	+	+	+
+ * _INIT			+	+	+	+
+ * _EMPTY			+	+	+	+
+ * _FIRST			+	+	+	+
+ * _NEXT			+	+	+	+
+ * _PREV			-	+	-	+
+ * _LAST			-	-	+	+
+ * _FOREACH			+	+	+	+
+ * _FOREACH_FROM		+	+	+	+
+ * _FOREACH_SAFE		+	+	+	+
+ * _FOREACH_FROM_SAFE		+	+	+	+
+ * _FOREACH_REVERSE		-	-	-	+
+ * _FOREACH_REVERSE_FROM	-	-	-	+
+ * _FOREACH_REVERSE_SAFE	-	-	-	+
+ * _FOREACH_REVERSE_FROM_SAFE	-	-	-	+
+ * _INSERT_HEAD			+	+	+	+
+ * _INSERT_BEFORE		-	+	-	+
+ * _INSERT_AFTER		+	+	+	+
+ * _INSERT_TAIL			-	-	+	+
+ * _CONCAT			-	-	+	+
+ * _REMOVE_AFTER		+	-	+	-
+ * _REMOVE_HEAD			+	-	+	-
+ * _REMOVE			+	+	+	+
+ * _SWAP			+	+	+	+
+ *
+ */
+#ifdef QUEUE_MACRO_DEBUG
+/* Store the last 2 places the queue element or head was altered */
+struct qm_trace {
+	unsigned long	 lastline;
+	unsigned long	 prevline;
+	const char	*lastfile;
+	const char	*prevfile;
+};
+
+#define	TRACEBUF	struct qm_trace trace;
+#define	TRACEBUF_INITIALIZER	{ __LINE__, 0, __FILE__, NULL } ,
+#define	TRASHIT(x)	do {(x) = (void *)-1;} while (0)
+#define	QMD_SAVELINK(name, link)	void **name = (void *)&(link)
+
+#define	QMD_TRACE_HEAD(head) do {					\
+	(head)->trace.prevline = (head)->trace.lastline;		\
+	(head)->trace.prevfile = (head)->trace.lastfile;		\
+	(head)->trace.lastline = __LINE__;				\
+	(head)->trace.lastfile = __FILE__;				\
+} while (0)
+
+#define	QMD_TRACE_ELEM(elem) do {					\
+	(elem)->trace.prevline = (elem)->trace.lastline;		\
+	(elem)->trace.prevfile = (elem)->trace.lastfile;		\
+	(elem)->trace.lastline = __LINE__;				\
+	(elem)->trace.lastfile = __FILE__;				\
+} while (0)
+
+#else
+#define	QMD_TRACE_ELEM(elem)
+#define	QMD_TRACE_HEAD(head)
+#define	QMD_SAVELINK(name, link)
+#define	TRACEBUF
+#define	TRACEBUF_INITIALIZER
+#define	TRASHIT(x)
+#endif	/* QUEUE_MACRO_DEBUG */
+
+#ifdef __cplusplus
+/*
+ * In C++ there can be structure lists and class lists:
+ */
+#define	QUEUE_TYPEOF(type) type
+#else
+#define	QUEUE_TYPEOF(type) struct type
+#endif
+
+/*
+ * Singly-linked List declarations.
+ */
+#define	SLIST_HEAD(name, type)						\
+struct name {								\
+	struct type *slh_first;	/* first element */			\
+}
+
+#define	SLIST_CLASS_HEAD(name, type)					\
+struct name {								\
+	class type *slh_first;	/* first element */			\
+}
+
+#define	SLIST_HEAD_INITIALIZER(head)					\
+	{ NULL }
+
+#define	SLIST_ENTRY(type)						\
+struct {								\
+	struct type *sle_next;	/* next element */			\
+}
+
+#define	SLIST_CLASS_ENTRY(type)						\
+struct {								\
+	class type *sle_next;		/* next element */		\
+}
+
+/*
+ * Singly-linked List functions.
+ */
+#define	SLIST_EMPTY(head)	((head)->slh_first == NULL)
+
+#define	SLIST_FIRST(head)	((head)->slh_first)
+
+#define	SLIST_FOREACH(var, head, field)					\
+	for ((var) = SLIST_FIRST((head));				\
+	    (var);							\
+	    (var) = SLIST_NEXT((var), field))
+
+#define	SLIST_FOREACH_FROM(var, head, field)				\
+	for ((var) = ((var) ? (var) : SLIST_FIRST((head)));		\
+	    (var);							\
+	    (var) = SLIST_NEXT((var), field))
+
+#define	SLIST_FOREACH_SAFE(var, head, field, tvar)			\
+	for ((var) = SLIST_FIRST((head));				\
+	    (var) && ((tvar) = SLIST_NEXT((var), field), 1);		\
+	    (var) = (tvar))
+
+#define	SLIST_FOREACH_FROM_SAFE(var, head, field, tvar)			\
+	for ((var) = ((var) ? (var) : SLIST_FIRST((head)));		\
+	    (var) && ((tvar) = SLIST_NEXT((var), field), 1);		\
+	    (var) = (tvar))
+
+#define	SLIST_FOREACH_PREVPTR(var, varp, head, field)			\
+	for ((varp) = &SLIST_FIRST((head));				\
+	    ((var) = *(varp)) != NULL;					\
+	    (varp) = &SLIST_NEXT((var), field))
+
+#define	SLIST_INIT(head) do {						\
+	SLIST_FIRST((head)) = NULL;					\
+} while (0)
+
+#define	SLIST_INSERT_AFTER(slistelm, elm, field) do {			\
+	SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field);	\
+	SLIST_NEXT((slistelm), field) = (elm);				\
+} while (0)
+
+#define	SLIST_INSERT_HEAD(head, elm, field) do {			\
+	SLIST_NEXT((elm), field) = SLIST_FIRST((head));			\
+	SLIST_FIRST((head)) = (elm);					\
+} while (0)
+
+#define	SLIST_NEXT(elm, field)	((elm)->field.sle_next)
+
+#define	SLIST_REMOVE(head, elm, type, field) do {			\
+	QMD_SAVELINK(oldnext, (elm)->field.sle_next);			\
+	if (SLIST_FIRST((head)) == (elm)) {				\
+		SLIST_REMOVE_HEAD((head), field);			\
+	}								\
+	else {								\
+		QUEUE_TYPEOF(type) *curelm = SLIST_FIRST(head);		\
+		while (SLIST_NEXT(curelm, field) != (elm))		\
+			curelm = SLIST_NEXT(curelm, field);		\
+		SLIST_REMOVE_AFTER(curelm, field);			\
+	}								\
+	TRASHIT(*oldnext);						\
+} while (0)
+
+#define SLIST_REMOVE_AFTER(elm, field) do {				\
+	SLIST_NEXT(elm, field) =					\
+	    SLIST_NEXT(SLIST_NEXT(elm, field), field);			\
+} while (0)
+
+#define	SLIST_REMOVE_HEAD(head, field) do {				\
+	SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field);	\
+} while (0)
+
+#define SLIST_SWAP(head1, head2, type) do {				\
+	QUEUE_TYPEOF(type) *swap_first = SLIST_FIRST(head1);		\
+	SLIST_FIRST(head1) = SLIST_FIRST(head2);			\
+	SLIST_FIRST(head2) = swap_first;				\
+} while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define	STAILQ_HEAD(name, type)						\
+struct name {								\
+	struct type *stqh_first;/* first element */			\
+	struct type **stqh_last;/* addr of last next element */		\
+}
+
+#define	STAILQ_CLASS_HEAD(name, type)					\
+struct name {								\
+	class type *stqh_first;	/* first element */			\
+	class type **stqh_last;	/* addr of last next element */		\
+}
+
+#define	STAILQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).stqh_first }
+
+#define	STAILQ_ENTRY(type)						\
+struct {								\
+	struct type *stqe_next;	/* next element */			\
+}
+
+#define	STAILQ_CLASS_ENTRY(type)					\
+struct {								\
+	class type *stqe_next;	/* next element */			\
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define	STAILQ_CONCAT(head1, head2) do {				\
+	if (!STAILQ_EMPTY((head2))) {					\
+		*(head1)->stqh_last = (head2)->stqh_first;		\
+		(head1)->stqh_last = (head2)->stqh_last;		\
+		STAILQ_INIT((head2));					\
+	}								\
+} while (0)
+
+#define	STAILQ_EMPTY(head)	((head)->stqh_first == NULL)
+
+#define	STAILQ_FIRST(head)	((head)->stqh_first)
+
+#define	STAILQ_FOREACH(var, head, field)				\
+	for((var) = STAILQ_FIRST((head));				\
+	   (var);							\
+	   (var) = STAILQ_NEXT((var), field))
+
+#define	STAILQ_FOREACH_FROM(var, head, field)				\
+	for ((var) = ((var) ? (var) : STAILQ_FIRST((head)));		\
+	   (var);							\
+	   (var) = STAILQ_NEXT((var), field))
+
+#define	STAILQ_FOREACH_SAFE(var, head, field, tvar)			\
+	for ((var) = STAILQ_FIRST((head));				\
+	    (var) && ((tvar) = STAILQ_NEXT((var), field), 1);		\
+	    (var) = (tvar))
+
+#define	STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar)		\
+	for ((var) = ((var) ? (var) : STAILQ_FIRST((head)));		\
+	    (var) && ((tvar) = STAILQ_NEXT((var), field), 1);		\
+	    (var) = (tvar))
+
+#define	STAILQ_INIT(head) do {						\
+	STAILQ_FIRST((head)) = NULL;					\
+	(head)->stqh_last = &STAILQ_FIRST((head));			\
+} while (0)
+
+#define	STAILQ_INSERT_AFTER(head, tqelm, elm, field) do {		\
+	if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
+		(head)->stqh_last = &STAILQ_NEXT((elm), field);		\
+	STAILQ_NEXT((tqelm), field) = (elm);				\
+} while (0)
+
+#define	STAILQ_INSERT_HEAD(head, elm, field) do {			\
+	if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL)	\
+		(head)->stqh_last = &STAILQ_NEXT((elm), field);		\
+	STAILQ_FIRST((head)) = (elm);					\
+} while (0)
+
+#define	STAILQ_INSERT_TAIL(head, elm, field) do {			\
+	STAILQ_NEXT((elm), field) = NULL;				\
+	*(head)->stqh_last = (elm);					\
+	(head)->stqh_last = &STAILQ_NEXT((elm), field);			\
+} while (0)
+
+#define	STAILQ_LAST(head, type, field)				\
+	(STAILQ_EMPTY((head)) ? NULL :				\
+	    __containerof((head)->stqh_last,			\
+	    QUEUE_TYPEOF(type), field.stqe_next))
+
+#define	STAILQ_NEXT(elm, field)	((elm)->field.stqe_next)
+
+#define	STAILQ_REMOVE(head, elm, type, field) do {			\
+	QMD_SAVELINK(oldnext, (elm)->field.stqe_next);			\
+	if (STAILQ_FIRST((head)) == (elm)) {				\
+		STAILQ_REMOVE_HEAD((head), field);			\
+	}								\
+	else {								\
+		QUEUE_TYPEOF(type) *curelm = STAILQ_FIRST(head);	\
+		while (STAILQ_NEXT(curelm, field) != (elm))		\
+			curelm = STAILQ_NEXT(curelm, field);		\
+		STAILQ_REMOVE_AFTER(head, curelm, field);		\
+	}								\
+	TRASHIT(*oldnext);						\
+} while (0)
+
+#define STAILQ_REMOVE_AFTER(head, elm, field) do {			\
+	if ((STAILQ_NEXT(elm, field) =					\
+	     STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL)	\
+		(head)->stqh_last = &STAILQ_NEXT((elm), field);		\
+} while (0)
+
+#define	STAILQ_REMOVE_HEAD(head, field) do {				\
+	if ((STAILQ_FIRST((head)) =					\
+	     STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL)		\
+		(head)->stqh_last = &STAILQ_FIRST((head));		\
+} while (0)
+
+#define STAILQ_SWAP(head1, head2, type) do {				\
+	QUEUE_TYPEOF(type) *swap_first = STAILQ_FIRST(head1);		\
+	QUEUE_TYPEOF(type) **swap_last = (head1)->stqh_last;		\
+	STAILQ_FIRST(head1) = STAILQ_FIRST(head2);			\
+	(head1)->stqh_last = (head2)->stqh_last;			\
+	STAILQ_FIRST(head2) = swap_first;				\
+	(head2)->stqh_last = swap_last;					\
+	if (STAILQ_EMPTY(head1))					\
+		(head1)->stqh_last = &STAILQ_FIRST(head1);		\
+	if (STAILQ_EMPTY(head2))					\
+		(head2)->stqh_last = &STAILQ_FIRST(head2);		\
+} while (0)
+
+
+/*
+ * List declarations.
+ */
+#define	LIST_HEAD(name, type)						\
+struct name {								\
+	struct type *lh_first;	/* first element */			\
+}
+
+#define	LIST_CLASS_HEAD(name, type)					\
+struct name {								\
+	class type *lh_first;	/* first element */			\
+}
+
+#define	LIST_HEAD_INITIALIZER(head)					\
+	{ NULL }
+
+#define	LIST_ENTRY(type)						\
+struct {								\
+	struct type *le_next;	/* next element */			\
+	struct type **le_prev;	/* address of previous next element */	\
+}
+
+#define	LIST_CLASS_ENTRY(type)						\
+struct {								\
+	class type *le_next;	/* next element */			\
+	class type **le_prev;	/* address of previous next element */	\
+}
+
+/*
+ * List functions.
+ */
+
+#if (defined(_KERNEL) && defined(INVARIANTS))
+#define	QMD_LIST_CHECK_HEAD(head, field) do {				\
+	if (LIST_FIRST((head)) != NULL &&				\
+	    LIST_FIRST((head))->field.le_prev !=			\
+	     &LIST_FIRST((head)))					\
+		panic("Bad list head %p first->prev != head", (head));	\
+} while (0)
+
+#define	QMD_LIST_CHECK_NEXT(elm, field) do {				\
+	if (LIST_NEXT((elm), field) != NULL &&				\
+	    LIST_NEXT((elm), field)->field.le_prev !=			\
+	     &((elm)->field.le_next))					\
+	     	panic("Bad link elm %p next->prev != elm", (elm));	\
+} while (0)
+
+#define	QMD_LIST_CHECK_PREV(elm, field) do {				\
+	if (*(elm)->field.le_prev != (elm))				\
+		panic("Bad link elm %p prev->next != elm", (elm));	\
+} while (0)
+#else
+#define	QMD_LIST_CHECK_HEAD(head, field)
+#define	QMD_LIST_CHECK_NEXT(elm, field)
+#define	QMD_LIST_CHECK_PREV(elm, field)
+#endif /* (_KERNEL && INVARIANTS) */
+
+#define	LIST_EMPTY(head)	((head)->lh_first == NULL)
+
+#define	LIST_FIRST(head)	((head)->lh_first)
+
+#define	LIST_FOREACH(var, head, field)					\
+	for ((var) = LIST_FIRST((head));				\
+	    (var);							\
+	    (var) = LIST_NEXT((var), field))
+
+#define	LIST_FOREACH_FROM(var, head, field)				\
+	for ((var) = ((var) ? (var) : LIST_FIRST((head)));		\
+	    (var);							\
+	    (var) = LIST_NEXT((var), field))
+
+#define	LIST_FOREACH_SAFE(var, head, field, tvar)			\
+	for ((var) = LIST_FIRST((head));				\
+	    (var) && ((tvar) = LIST_NEXT((var), field), 1);		\
+	    (var) = (tvar))
+
+#define	LIST_FOREACH_FROM_SAFE(var, head, field, tvar)			\
+	for ((var) = ((var) ? (var) : LIST_FIRST((head)));		\
+	    (var) && ((tvar) = LIST_NEXT((var), field), 1);		\
+	    (var) = (tvar))
+
+#define	LIST_INIT(head) do {						\
+	LIST_FIRST((head)) = NULL;					\
+} while (0)
+
+#define	LIST_INSERT_AFTER(listelm, elm, field) do {			\
+	QMD_LIST_CHECK_NEXT(listelm, field);				\
+	if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
+		LIST_NEXT((listelm), field)->field.le_prev =		\
+		    &LIST_NEXT((elm), field);				\
+	LIST_NEXT((listelm), field) = (elm);				\
+	(elm)->field.le_prev = &LIST_NEXT((listelm), field);		\
+} while (0)
+
+#define	LIST_INSERT_BEFORE(listelm, elm, field) do {			\
+	QMD_LIST_CHECK_PREV(listelm, field);				\
+	(elm)->field.le_prev = (listelm)->field.le_prev;		\
+	LIST_NEXT((elm), field) = (listelm);				\
+	*(listelm)->field.le_prev = (elm);				\
+	(listelm)->field.le_prev = &LIST_NEXT((elm), field);		\
+} while (0)
+
+#define	LIST_INSERT_HEAD(head, elm, field) do {				\
+	QMD_LIST_CHECK_HEAD((head), field);				\
+	if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL)	\
+		LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
+	LIST_FIRST((head)) = (elm);					\
+	(elm)->field.le_prev = &LIST_FIRST((head));			\
+} while (0)
+
+#define	LIST_NEXT(elm, field)	((elm)->field.le_next)
+
+#define	LIST_PREV(elm, head, type, field)			\
+	((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL :	\
+	    __containerof((elm)->field.le_prev,			\
+	    QUEUE_TYPEOF(type), field.le_next))
+
+#define	LIST_REMOVE(elm, field) do {					\
+	QMD_SAVELINK(oldnext, (elm)->field.le_next);			\
+	QMD_SAVELINK(oldprev, (elm)->field.le_prev);			\
+	QMD_LIST_CHECK_NEXT(elm, field);				\
+	QMD_LIST_CHECK_PREV(elm, field);				\
+	if (LIST_NEXT((elm), field) != NULL)				\
+		LIST_NEXT((elm), field)->field.le_prev = 		\
+		    (elm)->field.le_prev;				\
+	*(elm)->field.le_prev = LIST_NEXT((elm), field);		\
+	TRASHIT(*oldnext);						\
+	TRASHIT(*oldprev);						\
+} while (0)
+
+#define LIST_SWAP(head1, head2, type, field) do {			\
+	QUEUE_TYPEOF(type) *swap_tmp = LIST_FIRST(head1);		\
+	LIST_FIRST((head1)) = LIST_FIRST((head2));			\
+	LIST_FIRST((head2)) = swap_tmp;					\
+	if ((swap_tmp = LIST_FIRST((head1))) != NULL)			\
+		swap_tmp->field.le_prev = &LIST_FIRST((head1));		\
+	if ((swap_tmp = LIST_FIRST((head2))) != NULL)			\
+		swap_tmp->field.le_prev = &LIST_FIRST((head2));		\
+} while (0)
+
+/*
+ * Tail queue declarations.
+ */
+#define	TAILQ_HEAD(name, type)						\
+struct name {								\
+	struct type *tqh_first;	/* first element */			\
+	struct type **tqh_last;	/* addr of last next element */		\
+	TRACEBUF							\
+}
+
+#define	TAILQ_CLASS_HEAD(name, type)					\
+struct name {								\
+	class type *tqh_first;	/* first element */			\
+	class type **tqh_last;	/* addr of last next element */		\
+	TRACEBUF							\
+}
+
+#define	TAILQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).tqh_first, TRACEBUF_INITIALIZER }
+
+#define	TAILQ_ENTRY(type)						\
+struct {								\
+	struct type *tqe_next;	/* next element */			\
+	struct type **tqe_prev;	/* address of previous next element */	\
+	TRACEBUF							\
+}
+
+#define	TAILQ_CLASS_ENTRY(type)						\
+struct {								\
+	class type *tqe_next;	/* next element */			\
+	class type **tqe_prev;	/* address of previous next element */	\
+	TRACEBUF							\
+}
+
+/*
+ * Tail queue functions.
+ */
+#if (defined(_KERNEL) && defined(INVARIANTS))
+#define	QMD_TAILQ_CHECK_HEAD(head, field) do {				\
+	if (!TAILQ_EMPTY(head) &&					\
+	    TAILQ_FIRST((head))->field.tqe_prev !=			\
+	     &TAILQ_FIRST((head)))					\
+		panic("Bad tailq head %p first->prev != head", (head));	\
+} while (0)
+
+#define	QMD_TAILQ_CHECK_TAIL(head, field) do {				\
+	if (*(head)->tqh_last != NULL)					\
+	    	panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); 	\
+} while (0)
+
+#define	QMD_TAILQ_CHECK_NEXT(elm, field) do {				\
+	if (TAILQ_NEXT((elm), field) != NULL &&				\
+	    TAILQ_NEXT((elm), field)->field.tqe_prev !=			\
+	     &((elm)->field.tqe_next))					\
+		panic("Bad link elm %p next->prev != elm", (elm));	\
+} while (0)
+
+#define	QMD_TAILQ_CHECK_PREV(elm, field) do {				\
+	if (*(elm)->field.tqe_prev != (elm))				\
+		panic("Bad link elm %p prev->next != elm", (elm));	\
+} while (0)
+#else
+#define	QMD_TAILQ_CHECK_HEAD(head, field)
+#define	QMD_TAILQ_CHECK_TAIL(head, headname)
+#define	QMD_TAILQ_CHECK_NEXT(elm, field)
+#define	QMD_TAILQ_CHECK_PREV(elm, field)
+#endif /* (_KERNEL && INVARIANTS) */
+
+#define	TAILQ_CONCAT(head1, head2, field) do {				\
+	if (!TAILQ_EMPTY(head2)) {					\
+		*(head1)->tqh_last = (head2)->tqh_first;		\
+		(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last;	\
+		(head1)->tqh_last = (head2)->tqh_last;			\
+		TAILQ_INIT((head2));					\
+		QMD_TRACE_HEAD(head1);					\
+		QMD_TRACE_HEAD(head2);					\
+	}								\
+} while (0)
+
+#define	TAILQ_EMPTY(head)	((head)->tqh_first == NULL)
+
+#define	TAILQ_FIRST(head)	((head)->tqh_first)
+
+#define	TAILQ_FOREACH(var, head, field)					\
+	for ((var) = TAILQ_FIRST((head));				\
+	    (var);							\
+	    (var) = TAILQ_NEXT((var), field))
+
+#define	TAILQ_FOREACH_FROM(var, head, field)				\
+	for ((var) = ((var) ? (var) : TAILQ_FIRST((head)));		\
+	    (var);							\
+	    (var) = TAILQ_NEXT((var), field))
+
+#define	TAILQ_FOREACH_SAFE(var, head, field, tvar)			\
+	for ((var) = TAILQ_FIRST((head));				\
+	    (var) && ((tvar) = TAILQ_NEXT((var), field), 1);		\
+	    (var) = (tvar))
+
+#define	TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar)			\
+	for ((var) = ((var) ? (var) : TAILQ_FIRST((head)));		\
+	    (var) && ((tvar) = TAILQ_NEXT((var), field), 1);		\
+	    (var) = (tvar))
+
+#define	TAILQ_FOREACH_REVERSE(var, head, headname, field)		\
+	for ((var) = TAILQ_LAST((head), headname);			\
+	    (var);							\
+	    (var) = TAILQ_PREV((var), headname, field))
+
+#define	TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field)		\
+	for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname));	\
+	    (var);							\
+	    (var) = TAILQ_PREV((var), headname, field))
+
+#define	TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar)	\
+	for ((var) = TAILQ_LAST((head), headname);			\
+	    (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1);	\
+	    (var) = (tvar))
+
+#define	TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
+	for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname));	\
+	    (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1);	\
+	    (var) = (tvar))
+
+#define	TAILQ_INIT(head) do {						\
+	TAILQ_FIRST((head)) = NULL;					\
+	(head)->tqh_last = &TAILQ_FIRST((head));			\
+	QMD_TRACE_HEAD(head);						\
+} while (0)
+
+#define	TAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	QMD_TAILQ_CHECK_NEXT(listelm, field);				\
+	if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
+		TAILQ_NEXT((elm), field)->field.tqe_prev = 		\
+		    &TAILQ_NEXT((elm), field);				\
+	else {								\
+		(head)->tqh_last = &TAILQ_NEXT((elm), field);		\
+		QMD_TRACE_HEAD(head);					\
+	}								\
+	TAILQ_NEXT((listelm), field) = (elm);				\
+	(elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field);		\
+	QMD_TRACE_ELEM(&(elm)->field);					\
+	QMD_TRACE_ELEM(&(listelm)->field);				\
+} while (0)
+
+#define	TAILQ_INSERT_BEFORE(listelm, elm, field) do {			\
+	QMD_TAILQ_CHECK_PREV(listelm, field);				\
+	(elm)->field.tqe_prev = (listelm)->field.tqe_prev;		\
+	TAILQ_NEXT((elm), field) = (listelm);				\
+	*(listelm)->field.tqe_prev = (elm);				\
+	(listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field);		\
+	QMD_TRACE_ELEM(&(elm)->field);					\
+	QMD_TRACE_ELEM(&(listelm)->field);				\
+} while (0)
+
+#define	TAILQ_INSERT_HEAD(head, elm, field) do {			\
+	QMD_TAILQ_CHECK_HEAD(head, field);				\
+	if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL)	\
+		TAILQ_FIRST((head))->field.tqe_prev =			\
+		    &TAILQ_NEXT((elm), field);				\
+	else								\
+		(head)->tqh_last = &TAILQ_NEXT((elm), field);		\
+	TAILQ_FIRST((head)) = (elm);					\
+	(elm)->field.tqe_prev = &TAILQ_FIRST((head));			\
+	QMD_TRACE_HEAD(head);						\
+	QMD_TRACE_ELEM(&(elm)->field);					\
+} while (0)
+
+#define	TAILQ_INSERT_TAIL(head, elm, field) do {			\
+	QMD_TAILQ_CHECK_TAIL(head, field);				\
+	TAILQ_NEXT((elm), field) = NULL;				\
+	(elm)->field.tqe_prev = (head)->tqh_last;			\
+	*(head)->tqh_last = (elm);					\
+	(head)->tqh_last = &TAILQ_NEXT((elm), field);			\
+	QMD_TRACE_HEAD(head);						\
+	QMD_TRACE_ELEM(&(elm)->field);					\
+} while (0)
+
+#define	TAILQ_LAST(head, headname)					\
+	(*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define	TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define	TAILQ_PREV(elm, headname, field)				\
+	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define	TAILQ_REMOVE(head, elm, field) do {				\
+	QMD_SAVELINK(oldnext, (elm)->field.tqe_next);			\
+	QMD_SAVELINK(oldprev, (elm)->field.tqe_prev);			\
+	QMD_TAILQ_CHECK_NEXT(elm, field);				\
+	QMD_TAILQ_CHECK_PREV(elm, field);				\
+	if ((TAILQ_NEXT((elm), field)) != NULL)				\
+		TAILQ_NEXT((elm), field)->field.tqe_prev = 		\
+		    (elm)->field.tqe_prev;				\
+	else {								\
+		(head)->tqh_last = (elm)->field.tqe_prev;		\
+		QMD_TRACE_HEAD(head);					\
+	}								\
+	*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field);		\
+	TRASHIT(*oldnext);						\
+	TRASHIT(*oldprev);						\
+	QMD_TRACE_ELEM(&(elm)->field);					\
+} while (0)
+
+#define TAILQ_SWAP(head1, head2, type, field) do {			\
+	QUEUE_TYPEOF(type) *swap_first = (head1)->tqh_first;		\
+	QUEUE_TYPEOF(type) **swap_last = (head1)->tqh_last;		\
+	(head1)->tqh_first = (head2)->tqh_first;			\
+	(head1)->tqh_last = (head2)->tqh_last;				\
+	(head2)->tqh_first = swap_first;				\
+	(head2)->tqh_last = swap_last;					\
+	if ((swap_first = (head1)->tqh_first) != NULL)			\
+		swap_first->field.tqe_prev = &(head1)->tqh_first;	\
+	else								\
+		(head1)->tqh_last = &(head1)->tqh_first;		\
+	if ((swap_first = (head2)->tqh_first) != NULL)			\
+		swap_first->field.tqe_prev = &(head2)->tqh_first;	\
+	else								\
+		(head2)->tqh_last = &(head2)->tqh_first;		\
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_features.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_MONGOOSE_SRC_FEATURES_H_
+#define CS_MONGOOSE_SRC_FEATURES_H_
+
+#ifndef MG_DISABLE_HTTP_DIGEST_AUTH
+#define MG_DISABLE_HTTP_DIGEST_AUTH 0
+#endif
+
+#ifndef MG_DISABLE_HTTP_KEEP_ALIVE
+#define MG_DISABLE_HTTP_KEEP_ALIVE 0
+#endif
+
+#ifndef MG_DISABLE_PFS
+#define MG_DISABLE_PFS 0
+#endif
+
+#ifndef MG_DISABLE_WS_RANDOM_MASK
+#define MG_DISABLE_WS_RANDOM_MASK 0
+#endif
+
+#ifndef MG_ENABLE_ASYNC_RESOLVER
+#define MG_ENABLE_ASYNC_RESOLVER 1
+#endif
+
+#ifndef MG_ENABLE_BROADCAST
+#define MG_ENABLE_BROADCAST 0
+#endif
+
+#ifndef MG_ENABLE_COAP
+#define MG_ENABLE_COAP 0
+#endif
+
+#ifndef MG_ENABLE_DEBUG
+#define MG_ENABLE_DEBUG 0
+#endif
+
+#ifndef MG_ENABLE_DIRECTORY_LISTING
+#define MG_ENABLE_DIRECTORY_LISTING 0
+#endif
+
+#ifndef MG_ENABLE_DNS
+#define MG_ENABLE_DNS 1
+#endif
+
+#ifndef MG_ENABLE_DNS_SERVER
+#define MG_ENABLE_DNS_SERVER 0
+#endif
+
+#ifndef MG_ENABLE_FAKE_DAVLOCK
+#define MG_ENABLE_FAKE_DAVLOCK 0
+#endif
+
+#ifndef MG_ENABLE_FILESYSTEM
+#define MG_ENABLE_FILESYSTEM 0
+#endif
+
+#ifndef MG_ENABLE_GETADDRINFO
+#define MG_ENABLE_GETADDRINFO 0
+#endif
+
+#ifndef MG_ENABLE_HEXDUMP
+#define MG_ENABLE_HEXDUMP CS_ENABLE_STDIO
+#endif
+
+#ifndef MG_ENABLE_HTTP
+#define MG_ENABLE_HTTP 1
+#endif
+
+#ifndef MG_ENABLE_HTTP_CGI
+#define MG_ENABLE_HTTP_CGI 0
+#endif
+
+#ifndef MG_ENABLE_HTTP_SSI
+#define MG_ENABLE_HTTP_SSI MG_ENABLE_FILESYSTEM
+#endif
+
+#ifndef MG_ENABLE_HTTP_SSI_EXEC
+#define MG_ENABLE_HTTP_SSI_EXEC 0
+#endif
+
+#ifndef MG_ENABLE_HTTP_STREAMING_MULTIPART
+#define MG_ENABLE_HTTP_STREAMING_MULTIPART 0
+#endif
+
+#ifndef MG_ENABLE_HTTP_WEBDAV
+#define MG_ENABLE_HTTP_WEBDAV 0
+#endif
+
+#ifndef MG_ENABLE_HTTP_WEBSOCKET
+#define MG_ENABLE_HTTP_WEBSOCKET MG_ENABLE_HTTP
+#endif
+
+#ifndef MG_ENABLE_IPV6
+#define MG_ENABLE_IPV6 0
+#endif
+
+#ifndef MG_ENABLE_MQTT
+#define MG_ENABLE_MQTT 1
+#endif
+
+#ifndef MG_ENABLE_SOCKS
+#define MG_ENABLE_SOCKS 0
+#endif
+
+#ifndef MG_ENABLE_MQTT_BROKER
+#define MG_ENABLE_MQTT_BROKER 0
+#endif
+
+#ifndef MG_ENABLE_SSL
+#define MG_ENABLE_SSL 0
+#endif
+
+#ifndef MG_ENABLE_SYNC_RESOLVER
+#define MG_ENABLE_SYNC_RESOLVER 0
+#endif
+
+#ifndef MG_ENABLE_STDIO
+#define MG_ENABLE_STDIO CS_ENABLE_STDIO
+#endif
+
+#ifndef MG_NET_IF
+#define MG_NET_IF MG_NET_IF_SOCKET
+#endif
+
+#ifndef MG_SSL_IF
+#define MG_SSL_IF MG_SSL_IF_OPENSSL
+#endif
+
+#ifndef MG_ENABLE_THREADS /* ifdef-ok */
+#ifdef _WIN32
+#define MG_ENABLE_THREADS 1
+#else
+#define MG_ENABLE_THREADS 0
+#endif
+#endif
+
+#if MG_ENABLE_DEBUG && !defined(CS_ENABLE_DEBUG)
+#define CS_ENABLE_DEBUG 1
+#endif
+
+/* MQTT broker requires MQTT */
+#if MG_ENABLE_MQTT_BROKER && !MG_ENABLE_MQTT
+#undef MG_ENABLE_MQTT
+#define MG_ENABLE_MQTT 1
+#endif
+
+#ifndef MG_ENABLE_HTTP_URL_REWRITES
+#define MG_ENABLE_HTTP_URL_REWRITES \
+  (CS_PLATFORM == CS_P_WINDOWS || CS_PLATFORM == CS_P_UNIX)
+#endif
+
+#ifndef MG_ENABLE_SNTP
+#define MG_ENABLE_SNTP 0
+#endif
+
+#ifndef MG_ENABLE_EXTRA_ERRORS_DESC
+#define MG_ENABLE_EXTRA_ERRORS_DESC 0
+#endif
+
+#ifndef MG_ENABLE_CALLBACK_USERDATA
+#define MG_ENABLE_CALLBACK_USERDATA 0
+#endif
+
+#if MG_ENABLE_CALLBACK_USERDATA
+#define MG_UD_ARG(ud) , ud
+#define MG_CB(cb, ud) cb, ud
+#else
+#define MG_UD_ARG(ud)
+#define MG_CB(cb, ud) cb
+#endif
+
+#endif /* CS_MONGOOSE_SRC_FEATURES_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_net_if.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_MONGOOSE_SRC_NET_IF_H_
+#define CS_MONGOOSE_SRC_NET_IF_H_
+
+/* Amalgamated: #include "common/platform.h" */
+
+/*
+ * Internal async networking core interface.
+ * Consists of calls made by the core, which should not block,
+ * and callbacks back into the core ("..._cb").
+ * Callbacks may (will) cause methods to be invoked from within,
+ * but methods are not allowed to invoke callbacks inline.
+ *
+ * Implementation must ensure that only one callback is invoked at any time.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define MG_MAIN_IFACE 0
+
+struct mg_mgr;
+struct mg_connection;
+union socket_address;
+
+struct mg_iface_vtable;
+
+struct mg_iface {
+  struct mg_mgr *mgr;
+  void *data; /* Implementation-specific data */
+  const struct mg_iface_vtable *vtable;
+};
+
+struct mg_iface_vtable {
+  void (*init)(struct mg_iface *iface);
+  void (*free)(struct mg_iface *iface);
+  void (*add_conn)(struct mg_connection *nc);
+  void (*remove_conn)(struct mg_connection *nc);
+  time_t (*poll)(struct mg_iface *iface, int timeout_ms);
+
+  /* Set up a listening TCP socket on a given address. rv = 0 -> ok. */
+  int (*listen_tcp)(struct mg_connection *nc, union socket_address *sa);
+  /* Request that a "listening" UDP socket be created. */
+  int (*listen_udp)(struct mg_connection *nc, union socket_address *sa);
+
+  /* Request that a TCP connection is made to the specified address. */
+  void (*connect_tcp)(struct mg_connection *nc, const union socket_address *sa);
+  /* Open a UDP socket. Doesn't actually connect anything. */
+  void (*connect_udp)(struct mg_connection *nc);
+
+  /* Send functions for TCP and UDP. Sent data is copied before return. */
+  void (*tcp_send)(struct mg_connection *nc, const void *buf, size_t len);
+  void (*udp_send)(struct mg_connection *nc, const void *buf, size_t len);
+
+  void (*recved)(struct mg_connection *nc, size_t len);
+
+  /* Perform interface-related connection initialization. Return 1 on ok. */
+  int (*create_conn)(struct mg_connection *nc);
+  /* Perform interface-related cleanup on connection before destruction. */
+  void (*destroy_conn)(struct mg_connection *nc);
+
+  /* Associate a socket to a connection. */
+  void (*sock_set)(struct mg_connection *nc, sock_t sock);
+
+  /* Put connection's address into *sa, local (remote = 0) or remote. */
+  void (*get_conn_addr)(struct mg_connection *nc, int remote,
+                        union socket_address *sa);
+};
+
+extern const struct mg_iface_vtable *mg_ifaces[];
+extern int mg_num_ifaces;
+
+/* Creates a new interface instance. */
+struct mg_iface *mg_if_create_iface(const struct mg_iface_vtable *vtable,
+                                    struct mg_mgr *mgr);
+
+/*
+ * Find an interface with a given implementation. The search is started from
+ * interface `from`, exclusive. Returns NULL if none is found.
+ */
+struct mg_iface *mg_find_iface(struct mg_mgr *mgr,
+                               const struct mg_iface_vtable *vtable,
+                               struct mg_iface *from);
+/*
+ * Deliver a new TCP connection. Returns NULL in case on error (unable to
+ * create connection, in which case interface state should be discarded.
+ * This is phase 1 of the two-phase process - MG_EV_ACCEPT will be delivered
+ * when mg_if_accept_tcp_cb is invoked.
+ */
+struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc);
+void mg_if_accept_tcp_cb(struct mg_connection *nc, union socket_address *sa,
+                         size_t sa_len);
+
+/* Callback invoked by connect methods. err = 0 -> ok, != 0 -> error. */
+void mg_if_connect_cb(struct mg_connection *nc, int err);
+/* Callback that reports that data has been put on the wire. */
+void mg_if_sent_cb(struct mg_connection *nc, int num_sent);
+/*
+ * Receive callback.
+ * if `own` is true, buf must be heap-allocated and ownership is transferred
+ * to the core.
+ * Core will acknowledge consumption by calling iface::recved.
+ */
+void mg_if_recv_tcp_cb(struct mg_connection *nc, void *buf, int len, int own);
+/*
+ * Receive callback.
+ * buf must be heap-allocated and ownership is transferred to the core.
+ * Core will acknowledge consumption by calling iface::recved.
+ */
+void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len,
+                       union socket_address *sa, size_t sa_len);
+
+/* void mg_if_close_conn(struct mg_connection *nc); */
+
+/* Deliver a POLL event to the connection. */
+void mg_if_poll(struct mg_connection *nc, time_t now);
+
+/* Deliver a TIMER event to the connection. */
+void mg_if_timer(struct mg_connection *c, double now);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CS_MONGOOSE_SRC_NET_IF_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_ssl_if.h"
+#endif
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_MONGOOSE_SRC_SSL_IF_H_
+#define CS_MONGOOSE_SRC_SSL_IF_H_
+
+#if MG_ENABLE_SSL
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+struct mg_ssl_if_ctx;
+struct mg_connection;
+
+void mg_ssl_if_init();
+
+enum mg_ssl_if_result {
+  MG_SSL_OK = 0,
+  MG_SSL_WANT_READ = -1,
+  MG_SSL_WANT_WRITE = -2,
+  MG_SSL_ERROR = -3,
+};
+
+struct mg_ssl_if_conn_params {
+  const char *cert;
+  const char *key;
+  const char *ca_cert;
+  const char *server_name;
+  const char *cipher_suites;
+  const char *psk_identity;
+  const char *psk_key;
+};
+
+enum mg_ssl_if_result mg_ssl_if_conn_init(
+    struct mg_connection *nc, const struct mg_ssl_if_conn_params *params,
+    const char **err_msg);
+enum mg_ssl_if_result mg_ssl_if_conn_accept(struct mg_connection *nc,
+                                            struct mg_connection *lc);
+void mg_ssl_if_conn_close_notify(struct mg_connection *nc);
+void mg_ssl_if_conn_free(struct mg_connection *nc);
+
+enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc);
+int mg_ssl_if_read(struct mg_connection *nc, void *buf, size_t buf_size);
+int mg_ssl_if_write(struct mg_connection *nc, const void *data, size_t len);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* MG_ENABLE_SSL */
+
+#endif /* CS_MONGOOSE_SRC_SSL_IF_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_net.h"
+#endif
+/*
+ * Copyright (c) 2014 Cesanta Software Limited
+ * All rights reserved
+ * This software is dual-licensed: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. For the terms of this
+ * license, see .
+ *
+ * You are free to use this software under the terms of the GNU General
+ * Public License, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * Alternatively, you can license this software under a commercial
+ * license, as set out in .
+ */
+
+/*
+ * === Core API: TCP/UDP/SSL
+ *
+ * NOTE: Mongoose manager is single threaded. It does not protect
+ * its data structures by mutexes, therefore all functions that are dealing
+ * with a particular event manager should be called from the same thread,
+ * with exception of the `mg_broadcast()` function. It is fine to have different
+ * event managers handled by different threads.
+ */
+
+#ifndef CS_MONGOOSE_SRC_NET_H_
+#define CS_MONGOOSE_SRC_NET_H_
+
+/* Amalgamated: #include "mg_common.h" */
+/* Amalgamated: #include "mg_net_if.h" */
+/* Amalgamated: #include "common/mbuf.h" */
+
+#ifndef MG_VPRINTF_BUFFER_SIZE
+#define MG_VPRINTF_BUFFER_SIZE 100
+#endif
+
+#ifdef MG_USE_READ_WRITE
+#define MG_RECV_FUNC(s, b, l, f) read(s, b, l)
+#define MG_SEND_FUNC(s, b, l, f) write(s, b, l)
+#else
+#define MG_RECV_FUNC(s, b, l, f) recv(s, b, l, f)
+#define MG_SEND_FUNC(s, b, l, f) send(s, b, l, f)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+union socket_address {
+  struct sockaddr sa;
+  struct sockaddr_in sin;
+#if MG_ENABLE_IPV6
+  struct sockaddr_in6 sin6;
+#else
+  struct sockaddr sin6;
+#endif
+};
+
+struct mg_connection;
+
+/*
+ * Callback function (event handler) prototype. Must be defined by the user.
+ * Mongoose calls the event handler, passing the events defined below.
+ */
+typedef void (*mg_event_handler_t)(struct mg_connection *nc, int ev,
+                                   void *ev_data MG_UD_ARG(void *user_data));
+
+/* Events. Meaning of event parameter (evp) is given in the comment. */
+#define MG_EV_POLL 0    /* Sent to each connection on each mg_mgr_poll() call */
+#define MG_EV_ACCEPT 1  /* New connection accepted. union socket_address * */
+#define MG_EV_CONNECT 2 /* connect() succeeded or failed. int *  */
+#define MG_EV_RECV 3    /* Data has been received. int *num_bytes */
+#define MG_EV_SEND 4    /* Data has been written to a socket. int *num_bytes */
+#define MG_EV_CLOSE 5   /* Connection is closed. NULL */
+#define MG_EV_TIMER 6   /* now >= conn->ev_timer_time. double * */
+
+/*
+ * Mongoose event manager.
+ */
+struct mg_mgr {
+  struct mg_connection *active_connections;
+#if MG_ENABLE_HEXDUMP
+  const char *hexdump_file; /* Debug hexdump file path */
+#endif
+#if MG_ENABLE_BROADCAST
+  sock_t ctl[2]; /* Socketpair for mg_broadcast() */
+#endif
+  void *user_data; /* User data */
+  int num_ifaces;
+  struct mg_iface **ifaces; /* network interfaces */
+  const char *nameserver;   /* DNS server to use */
+};
+
+/*
+ * Mongoose connection.
+ */
+struct mg_connection {
+  struct mg_connection *next, *prev; /* mg_mgr::active_connections linkage */
+  struct mg_connection *listener;    /* Set only for accept()-ed connections */
+  struct mg_mgr *mgr;                /* Pointer to containing manager */
+
+  sock_t sock; /* Socket to the remote peer */
+  int err;
+  union socket_address sa; /* Remote peer address */
+  size_t recv_mbuf_limit;  /* Max size of recv buffer */
+  struct mbuf recv_mbuf;   /* Received data */
+  struct mbuf send_mbuf;   /* Data scheduled for sending */
+  time_t last_io_time;     /* Timestamp of the last socket IO */
+  double ev_timer_time;    /* Timestamp of the future MG_EV_TIMER */
+#if MG_ENABLE_SSL
+  void *ssl_if_data; /* SSL library data. */
+#endif
+  mg_event_handler_t proto_handler; /* Protocol-specific event handler */
+  void *proto_data;                 /* Protocol-specific data */
+  void (*proto_data_destructor)(void *proto_data);
+  mg_event_handler_t handler; /* Event handler function */
+  void *user_data;            /* User-specific data */
+  union {
+    void *v;
+    /*
+     * the C standard is fussy about fitting function pointers into
+     * void pointers, since some archs might have fat pointers for functions.
+     */
+    mg_event_handler_t f;
+  } priv_1;
+  void *priv_2;
+  void *mgr_data; /* Implementation-specific event manager's data. */
+  struct mg_iface *iface;
+  unsigned long flags;
+/* Flags set by Mongoose */
+#define MG_F_LISTENING (1 << 0)          /* This connection is listening */
+#define MG_F_UDP (1 << 1)                /* This connection is UDP */
+#define MG_F_RESOLVING (1 << 2)          /* Waiting for async resolver */
+#define MG_F_CONNECTING (1 << 3)         /* connect() call in progress */
+#define MG_F_SSL (1 << 4)                /* SSL is enabled on the connection */
+#define MG_F_SSL_HANDSHAKE_DONE (1 << 5) /* SSL hanshake has completed */
+#define MG_F_WANT_READ (1 << 6)          /* SSL specific */
+#define MG_F_WANT_WRITE (1 << 7)         /* SSL specific */
+#define MG_F_IS_WEBSOCKET (1 << 8)       /* Websocket specific */
+
+/* Flags that are settable by user */
+#define MG_F_SEND_AND_CLOSE (1 << 10)      /* Push remaining data and close  */
+#define MG_F_CLOSE_IMMEDIATELY (1 << 11)   /* Disconnect */
+#define MG_F_WEBSOCKET_NO_DEFRAG (1 << 12) /* Websocket specific */
+#define MG_F_DELETE_CHUNK (1 << 13)        /* HTTP specific */
+#define MG_F_ENABLE_BROADCAST (1 << 14)    /* Allow broadcast address usage */
+
+#define MG_F_USER_1 (1 << 20) /* Flags left for application */
+#define MG_F_USER_2 (1 << 21)
+#define MG_F_USER_3 (1 << 22)
+#define MG_F_USER_4 (1 << 23)
+#define MG_F_USER_5 (1 << 24)
+#define MG_F_USER_6 (1 << 25)
+};
+
+/*
+ * Initialise Mongoose manager. Side effect: ignores SIGPIPE signal.
+ * `mgr->user_data` field will be initialised with a `user_data` parameter.
+ * That is an arbitrary pointer, where the user code can associate some data
+ * with the particular Mongoose manager. For example, a C++ wrapper class
+ * could be written in which case `user_data` can hold a pointer to the
+ * class instance.
+ */
+void mg_mgr_init(struct mg_mgr *mgr, void *user_data);
+
+/*
+ * Optional parameters to `mg_mgr_init_opt()`.
+ *
+ * If `main_iface` is not NULL, it will be used as the main interface in the
+ * default interface set. The pointer will be free'd by `mg_mgr_free`.
+ * Otherwise, the main interface will be autodetected based on the current
+ * platform.
+ *
+ * If `num_ifaces` is 0 and `ifaces` is NULL, the default interface set will be
+ * used.
+ * This is an advanced option, as it requires you to construct a full interface
+ * set, including special networking interfaces required by some optional
+ * features such as TCP tunneling. Memory backing `ifaces` and each of the
+ * `num_ifaces` pointers it contains will be reclaimed by `mg_mgr_free`.
+ */
+struct mg_mgr_init_opts {
+  const struct mg_iface_vtable *main_iface;
+  int num_ifaces;
+  const struct mg_iface_vtable **ifaces;
+  const char *nameserver;
+};
+
+/*
+ * Like `mg_mgr_init` but with more options.
+ *
+ * Notably, this allows you to create a manger and choose
+ * dynamically which networking interface implementation to use.
+ */
+void mg_mgr_init_opt(struct mg_mgr *mgr, void *user_data,
+                     struct mg_mgr_init_opts opts);
+
+/*
+ * De-initialises Mongoose manager.
+ *
+ * Closes and deallocates all active connections.
+ */
+void mg_mgr_free(struct mg_mgr *);
+
+/*
+ * This function performs the actual IO and must be called in a loop
+ * (an event loop). It returns the current timestamp.
+ * `milli` is the maximum number of milliseconds to sleep.
+ * `mg_mgr_poll()` checks all connections for IO readiness. If at least one
+ * of the connections is IO-ready, `mg_mgr_poll()` triggers the respective
+ * event handlers and returns.
+ */
+time_t mg_mgr_poll(struct mg_mgr *, int milli);
+
+#if MG_ENABLE_BROADCAST
+/*
+ * Passes a message of a given length to all connections.
+ *
+ * Must be called from a thread that does NOT call `mg_mgr_poll()`.
+ * Note that `mg_broadcast()` is the only function
+ * that can be, and must be, called from a different (non-IO) thread.
+ *
+ * `func` callback function will be called by the IO thread for each
+ * connection. When called, the event will be `MG_EV_POLL`, and a message will
+ * be passed as the `ev_data` pointer. Maximum message size is capped
+ * by `MG_CTL_MSG_MESSAGE_SIZE` which is set to 8192 bytes.
+ */
+void mg_broadcast(struct mg_mgr *mgr, mg_event_handler_t cb, void *data,
+                  size_t len);
+#endif
+
+/*
+ * Iterates over all active connections.
+ *
+ * Returns the next connection from the list
+ * of active connections or `NULL` if there are no more connections. Below
+ * is the iteration idiom:
+ *
+ * ```c
+ * for (c = mg_next(srv, NULL); c != NULL; c = mg_next(srv, c)) {
+ *   // Do something with connection `c`
+ * }
+ * ```
+ */
+struct mg_connection *mg_next(struct mg_mgr *mgr, struct mg_connection *c);
+
+/*
+ * Optional parameters to `mg_add_sock_opt()`.
+ *
+ * `flags` is an initial `struct mg_connection::flags` bitmask to set,
+ * see `MG_F_*` flags definitions.
+ */
+struct mg_add_sock_opts {
+  void *user_data;           /* Initial value for connection's user_data */
+  unsigned int flags;        /* Initial connection flags */
+  const char **error_string; /* Placeholder for the error string */
+  struct mg_iface *iface;    /* Interface instance */
+};
+
+/*
+ * Creates a connection, associates it with the given socket and event handler
+ * and adds it to the manager.
+ *
+ * For more options see the `mg_add_sock_opt` variant.
+ */
+struct mg_connection *mg_add_sock(struct mg_mgr *mgr, sock_t sock,
+                                  MG_CB(mg_event_handler_t handler,
+                                        void *user_data));
+
+/*
+ * Creates a connection, associates it with the given socket and event handler
+ * and adds to the manager.
+ *
+ * See the `mg_add_sock_opts` structure for a description of the options.
+ */
+struct mg_connection *mg_add_sock_opt(struct mg_mgr *mgr, sock_t sock,
+                                      MG_CB(mg_event_handler_t handler,
+                                            void *user_data),
+                                      struct mg_add_sock_opts opts);
+
+/*
+ * Optional parameters to `mg_bind_opt()`.
+ *
+ * `flags` is an initial `struct mg_connection::flags` bitmask to set,
+ * see `MG_F_*` flags definitions.
+ */
+struct mg_bind_opts {
+  void *user_data;           /* Initial value for connection's user_data */
+  unsigned int flags;        /* Extra connection flags */
+  const char **error_string; /* Placeholder for the error string */
+  struct mg_iface *iface;    /* Interface instance */
+#if MG_ENABLE_SSL
+  /*
+   * SSL settings.
+   *
+   * Server certificate to present to clients or client certificate to
+   * present to tunnel dispatcher (for tunneled connections).
+   */
+  const char *ssl_cert;
+  /* Private key corresponding to the certificate. If ssl_cert is set but
+   * ssl_key is not, ssl_cert is used. */
+  const char *ssl_key;
+  /* CA bundle used to verify client certificates or tunnel dispatchers. */
+  const char *ssl_ca_cert;
+  /* Colon-delimited list of acceptable cipher suites.
+   * Names depend on the library used, for example:
+   *
+   * ECDH-ECDSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256 (OpenSSL)
+   * TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256
+   *   (mbedTLS)
+   *
+   * For OpenSSL the list can be obtained by running "openssl ciphers".
+   * For mbedTLS, names can be found in library/ssl_ciphersuites.c
+   * If NULL, a reasonable default is used.
+   */
+  const char *ssl_cipher_suites;
+#endif
+};
+
+/*
+ * Creates a listening connection.
+ *
+ * See `mg_bind_opt` for full documentation.
+ */
+struct mg_connection *mg_bind(struct mg_mgr *mgr, const char *address,
+                              MG_CB(mg_event_handler_t handler,
+                                    void *user_data));
+/*
+ * Creates a listening connection.
+ *
+ * The `address` parameter specifies which address to bind to. It's format is
+ * the same as for the `mg_connect()` call, where `HOST` part is optional.
+ * `address` can be just a port number, e.g. `:8000`. To bind to a specific
+ * interface, an IP address can be specified, e.g. `1.2.3.4:8000`. By default,
+ * a TCP connection is created. To create UDP connection, prepend `udp://`
+ * prefix, e.g. `udp://:8000`. To summarize, `address` parameter has following
+ * format: `[PROTO://][IP_ADDRESS]:PORT`, where `PROTO` could be `tcp` or
+ * `udp`.
+ *
+ * See the `mg_bind_opts` structure for a description of the optional
+ * parameters.
+ *
+ * Returns a new listening connection or `NULL` on error.
+ * NOTE: The connection remains owned by the manager, do not free().
+ */
+struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address,
+                                  MG_CB(mg_event_handler_t handler,
+                                        void *user_data),
+                                  struct mg_bind_opts opts);
+
+/* Optional parameters to `mg_connect_opt()` */
+struct mg_connect_opts {
+  void *user_data;           /* Initial value for connection's user_data */
+  unsigned int flags;        /* Extra connection flags */
+  const char **error_string; /* Placeholder for the error string */
+  struct mg_iface *iface;    /* Interface instance */
+  const char *nameserver;    /* DNS server to use, NULL for default */
+#if MG_ENABLE_SSL
+  /*
+   * SSL settings.
+   * Client certificate to present to the server.
+   */
+  const char *ssl_cert;
+  /*
+   * Private key corresponding to the certificate.
+   * If ssl_cert is set but ssl_key is not, ssl_cert is used.
+   */
+  const char *ssl_key;
+  /*
+   * Verify server certificate using this CA bundle. If set to "*", then SSL
+   * is enabled but no cert verification is performed.
+   */
+  const char *ssl_ca_cert;
+  /* Colon-delimited list of acceptable cipher suites.
+   * Names depend on the library used, for example:
+   *
+   * ECDH-ECDSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256 (OpenSSL)
+   * TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256
+   *   (mbedTLS)
+   *
+   * For OpenSSL the list can be obtained by running "openssl ciphers".
+   * For mbedTLS, names can be found in library/ssl_ciphersuites.c
+   * If NULL, a reasonable default is used.
+   */
+  const char *ssl_cipher_suites;
+  /*
+   * Server name verification. If ssl_ca_cert is set and the certificate has
+   * passed verification, its subject will be verified against this string.
+   * By default (if ssl_server_name is NULL) hostname part of the address will
+   * be used. Wildcard matching is supported. A special value of "*" disables
+   * name verification.
+   */
+  const char *ssl_server_name;
+  /*
+   * PSK identity and key. Identity is a NUL-terminated string and key is a hex
+   * string. Key must be either 16 or 32 bytes (32 or 64 hex digits) for AES-128
+   * or AES-256 respectively.
+   * Note: Default list of cipher suites does not include PSK suites, if you
+   * want to use PSK you will need to set ssl_cipher_suites as well.
+   */
+  const char *ssl_psk_identity;
+  const char *ssl_psk_key;
+#endif
+};
+
+/*
+ * Connects to a remote host.
+ *
+ * See `mg_connect_opt()` for full documentation.
+ */
+struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *address,
+                                 MG_CB(mg_event_handler_t handler,
+                                       void *user_data));
+
+/*
+ * Connects to a remote host.
+ *
+ * The `address` format is `[PROTO://]HOST:PORT`. `PROTO` could be `tcp` or
+ * `udp`. `HOST` could be an IP address,
+ * IPv6 address (if Mongoose is compiled with `-DMG_ENABLE_IPV6`) or a host
+ * name. If `HOST` is a name, Mongoose will resolve it asynchronously. Examples
+ * of valid addresses: `google.com:80`, `udp://1.2.3.4:53`, `10.0.0.1:443`,
+ * `[::1]:80`
+ *
+ * See the `mg_connect_opts` structure for a description of the optional
+ * parameters.
+ *
+ * Returns a new outbound connection or `NULL` on error.
+ *
+ * NOTE: The connection remains owned by the manager, do not free().
+ *
+ * NOTE: To enable IPv6 addresses `-DMG_ENABLE_IPV6` should be specified
+ * in the compilation flags.
+ *
+ * NOTE: The new connection will receive `MG_EV_CONNECT` as its first event
+ * which will report the connect success status.
+ * If the asynchronous resolution fails or the `connect()` syscall fails for
+ * whatever reason (e.g. with `ECONNREFUSED` or `ENETUNREACH`), then
+ * `MG_EV_CONNECT` event will report failure. Code example below:
+ *
+ * ```c
+ * static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
+ *   int connect_status;
+ *
+ *   switch (ev) {
+ *     case MG_EV_CONNECT:
+ *       connect_status = * (int *) ev_data;
+ *       if (connect_status == 0) {
+ *         // Success
+ *       } else  {
+ *         // Error
+ *         printf("connect() error: %s\n", strerror(connect_status));
+ *       }
+ *       break;
+ *     ...
+ *   }
+ * }
+ *
+ *   ...
+ *   mg_connect(mgr, "my_site.com:80", ev_handler);
+ * ```
+ */
+struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address,
+                                     MG_CB(mg_event_handler_t handler,
+                                           void *user_data),
+                                     struct mg_connect_opts opts);
+
+#if MG_ENABLE_SSL && MG_NET_IF != MG_NET_IF_SIMPLELINK
+/*
+ * Note: This function is deprecated. Please, use SSL options in
+ * mg_connect_opt.
+ *
+ * Enables SSL for a given connection.
+ * `cert` is a server certificate file name for a listening connection
+ * or a client certificate file name for an outgoing connection.
+ * The certificate files must be in PEM format. The server certificate file
+ * must contain a certificate, concatenated with a private key, optionally
+ * concatenated with DH parameters.
+ * `ca_cert` is a CA certificate or NULL if peer verification is not
+ * required.
+ * Return: NULL on success or error message on error.
+ */
+const char *mg_set_ssl(struct mg_connection *nc, const char *cert,
+                       const char *ca_cert);
+#endif
+
+/*
+ * Sends data to the connection.
+ *
+ * Note that sending functions do not actually push data to the socket.
+ * They just append data to the output buffer. MG_EV_SEND will be delivered when
+ * the data has actually been pushed out.
+ */
+void mg_send(struct mg_connection *, const void *buf, int len);
+
+/* Enables format string warnings for mg_printf */
+#if defined(__GNUC__)
+__attribute__((format(printf, 2, 3)))
+#endif
+/* don't separate from mg_printf declaration */
+
+/*
+ * Sends `printf`-style formatted data to the connection.
+ *
+ * See `mg_send` for more details on send semantics.
+ */
+int mg_printf(struct mg_connection *, const char *fmt, ...);
+
+/* Same as `mg_printf()`, but takes `va_list ap` as an argument. */
+int mg_vprintf(struct mg_connection *, const char *fmt, va_list ap);
+
+/*
+ * Creates a socket pair.
+ * `sock_type` can be either `SOCK_STREAM` or `SOCK_DGRAM`.
+ * Returns 0 on failure and 1 on success.
+ */
+int mg_socketpair(sock_t[2], int sock_type);
+
+#if MG_ENABLE_SYNC_RESOLVER
+/*
+ * Convert domain name into IP address.
+ *
+ * This is a utility function. If compilation flags have
+ * `-DMG_ENABLE_GETADDRINFO`, then `getaddrinfo()` call is used for name
+ * resolution. Otherwise, `gethostbyname()` is used.
+ *
+ * CAUTION: this function can block.
+ * Return 1 on success, 0 on failure.
+ */
+int mg_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len);
+#endif
+
+/*
+ * Verify given IP address against the ACL.
+ *
+ * `remote_ip` - an IPv4 address to check, in host byte order
+ * `acl` - a comma separated list of IP subnets: `x.x.x.x/x` or `x.x.x.x`.
+ * Each subnet is
+ * prepended by either a - or a + sign. A plus sign means allow, where a
+ * minus sign means deny. If a subnet mask is omitted, such as `-1.2.3.4`,
+ * it means that only that single IP address is denied.
+ * Subnet masks may vary from 0 to 32, inclusive. The default setting
+ * is to allow all access. On each request the full list is traversed,
+ * and the last match wins. Example:
+ *
+ * `-0.0.0.0/0,+192.168/16` - deny all accesses, only allow 192.168/16 subnet
+ *
+ * To learn more about subnet masks, see this
+ * link:https://en.wikipedia.org/wiki/Subnetwork[Wikipedia page on Subnetwork].
+ *
+ * Returns -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
+ */
+int mg_check_ip_acl(const char *acl, uint32_t remote_ip);
+
+/*
+ * Schedules an MG_EV_TIMER event to be delivered at `timestamp` time.
+ * `timestamp` is UNIX time (the number of seconds since Epoch). It is
+ * `double` instead of `time_t` to allow for sub-second precision.
+ * Returns the old timer value.
+ *
+ * Example: set the connect timeout to 1.5 seconds:
+ *
+ * ```
+ *  c = mg_connect(&mgr, "cesanta.com", ev_handler);
+ *  mg_set_timer(c, mg_time() + 1.5);
+ *  ...
+ *
+ *  void ev_handler(struct mg_connection *c, int ev, void *ev_data) {
+ *  switch (ev) {
+ *    case MG_EV_CONNECT:
+ *      mg_set_timer(c, 0);  // Clear connect timer
+ *      break;
+ *    case MG_EV_TIMER:
+ *      log("Connect timeout");
+ *      c->flags |= MG_F_CLOSE_IMMEDIATELY;
+ *      break;
+ * ```
+ */
+double mg_set_timer(struct mg_connection *c, double timestamp);
+
+/*
+ * A sub-second precision version of time().
+ */
+double mg_time(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CS_MONGOOSE_SRC_NET_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_uri.h"
+#endif
+/*
+ * Copyright (c) 2014 Cesanta Software Limited
+ * All rights reserved
+ */
+
+/*
+ * === URI
+ */
+
+#ifndef CS_MONGOOSE_SRC_URI_H_
+#define CS_MONGOOSE_SRC_URI_H_
+
+/* Amalgamated: #include "mg_net.h" */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Parses an URI and fills string chunks with locations of the respective
+ * uri components within the input uri string. NULL pointers will be
+ * ignored.
+ *
+ * General syntax:
+ *
+ *     [scheme://[user_info@]]host[:port][/path][?query][#fragment]
+ *
+ * Example:
+ *
+ *     foo.com:80
+ *     tcp://foo.com:1234
+ *     http://foo.com:80/bar?baz=1
+ *     https://user:pw@foo.com:443/blah
+ *
+ * `path` will include the leading slash. `query` won't include the leading `?`.
+ * `host` can contain embedded colons if surrounded by square brackets in order
+ * to support IPv6 literal addresses.
+ *
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int mg_parse_uri(const struct mg_str uri, struct mg_str *scheme,
+                 struct mg_str *user_info, struct mg_str *host,
+                 unsigned int *port, struct mg_str *path, struct mg_str *query,
+                 struct mg_str *fragment);
+
+/*
+ * Assemble URI from parts. Any of the inputs can be NULL or zero-length mg_str.
+ *
+ * If normalize_path is true, path is normalized by resolving relative refs.
+ *
+ * Result is a heap-allocated string (uri->p must be free()d after use).
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int mg_assemble_uri(const struct mg_str *scheme, const struct mg_str *user_info,
+                    const struct mg_str *host, unsigned int port,
+                    const struct mg_str *path, const struct mg_str *query,
+                    const struct mg_str *fragment, int normalize_path,
+                    struct mg_str *uri);
+
+int mg_normalize_uri_path(const struct mg_str *in, struct mg_str *out);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* CS_MONGOOSE_SRC_URI_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_util.h"
+#endif
+/*
+ * Copyright (c) 2014 Cesanta Software Limited
+ * All rights reserved
+ */
+
+/*
+ * === Utility API
+ */
+
+#ifndef CS_MONGOOSE_SRC_UTIL_H_
+#define CS_MONGOOSE_SRC_UTIL_H_
+
+#include 
+
+/* Amalgamated: #include "mg_common.h" */
+/* Amalgamated: #include "mg_net_if.h" */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifndef MG_MAX_PATH
+#ifdef PATH_MAX
+#define MG_MAX_PATH PATH_MAX
+#else
+#define MG_MAX_PATH 256
+#endif
+#endif
+
+/*
+ * Fetches substring from input string `s`, `end` into `v`.
+ * Skips initial delimiter characters. Records first non-delimiter character
+ * at the beginning of substring `v`. Then scans the rest of the string
+ * until a delimiter character or end-of-string is found.
+ * `delimiters` is a 0-terminated string containing delimiter characters.
+ * Either one of `delimiters` or `end_string` terminates the search.
+ * Returns an `s` pointer, advanced forward where parsing has stopped.
+ */
+const char *mg_skip(const char *s, const char *end_string,
+                    const char *delimiters, struct mg_str *v);
+
+/*
+ * Decodes base64-encoded string `s`, `len` into the destination `dst`.
+ * The destination has to have enough space to hold the decoded buffer.
+ * Decoding stops either when all strings have been decoded or invalid an
+ * character appeared.
+ * Destination is '\0'-terminated.
+ * Returns the number of decoded characters. On success, that should be equal
+ * to `len`. On error (invalid character) the return value is smaller then
+ * `len`.
+ */
+int mg_base64_decode(const unsigned char *s, int len, char *dst);
+
+/*
+ * Base64-encode chunk of memory `src`, `src_len` into the destination `dst`.
+ * Destination has to have enough space to hold encoded buffer.
+ * Destination is '\0'-terminated.
+ */
+void mg_base64_encode(const unsigned char *src, int src_len, char *dst);
+
+#if MG_ENABLE_FILESYSTEM
+/*
+ * Performs a 64-bit `stat()` call against a given file.
+ *
+ * `path` should be UTF8 encoded.
+ *
+ * Return value is the same as for `stat()` syscall.
+ */
+int mg_stat(const char *path, cs_stat_t *st);
+
+/*
+ * Opens the given file and returns a file stream.
+ *
+ * `path` and `mode` should be UTF8 encoded.
+ *
+ * Return value is the same as for the `fopen()` call.
+ */
+FILE *mg_fopen(const char *path, const char *mode);
+
+/*
+ * Opens the given file and returns a file stream.
+ *
+ * `path` should be UTF8 encoded.
+ *
+ * Return value is the same as for the `open()` syscall.
+ */
+int mg_open(const char *path, int flag, int mode);
+
+/*
+ * Reads data from the given file stream.
+ *
+ * Return value is a number of bytes readen.
+ */
+size_t mg_fread(void *ptr, size_t size, size_t count, FILE *f);
+
+/*
+ * Writes data to the given file stream.
+ *
+ * Return value is a number of bytes wtitten.
+ */
+size_t mg_fwrite(const void *ptr, size_t size, size_t count, FILE *f);
+
+#endif /* MG_ENABLE_FILESYSTEM */
+
+#if MG_ENABLE_THREADS
+/*
+ * Starts a new detached thread.
+ * Arguments and semantics are the same as pthead's `pthread_create()`.
+ * `thread_func` is a thread function, `thread_func_param` is a parameter
+ * that is passed to the thread function.
+ */
+void *mg_start_thread(void *(*thread_func)(void *), void *thread_func_param);
+#endif
+
+void mg_set_close_on_exec(sock_t);
+
+#define MG_SOCK_STRINGIFY_IP 1
+#define MG_SOCK_STRINGIFY_PORT 2
+#define MG_SOCK_STRINGIFY_REMOTE 4
+/*
+ * Converts a connection's local or remote address into string.
+ *
+ * The `flags` parameter is a bit mask that controls the behaviour,
+ * see `MG_SOCK_STRINGIFY_*` definitions.
+ *
+ * - MG_SOCK_STRINGIFY_IP - print IP address
+ * - MG_SOCK_STRINGIFY_PORT - print port number
+ * - MG_SOCK_STRINGIFY_REMOTE - print remote peer's IP/port, not local address
+ *
+ * If both port number and IP address are printed, they are separated by `:`.
+ * If compiled with `-DMG_ENABLE_IPV6`, IPv6 addresses are supported.
+ * Return length of the stringified address.
+ */
+int mg_conn_addr_to_str(struct mg_connection *c, char *buf, size_t len,
+                        int flags);
+#if MG_NET_IF == MG_NET_IF_SOCKET
+/* Legacy interface. */
+void mg_sock_to_str(sock_t sock, char *buf, size_t len, int flags);
+#endif
+
+/*
+ * Convert the socket's address into string.
+ *
+ * `flags` is MG_SOCK_STRINGIFY_IP and/or MG_SOCK_STRINGIFY_PORT.
+ */
+int mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len,
+                        int flags);
+
+#if MG_ENABLE_HEXDUMP
+/*
+ * Generates a human-readable hexdump of memory chunk.
+ *
+ * Takes a memory buffer `buf` of length `len` and creates a hex dump of that
+ * buffer in `dst`. The generated output is a-la hexdump(1).
+ * Returns the length of generated string, excluding terminating `\0`. If
+ * returned length is bigger than `dst_len`, the overflow bytes are discarded.
+ */
+int mg_hexdump(const void *buf, int len, char *dst, int dst_len);
+
+/* Same as mg_hexdump, but with output going to file instead of a buffer. */
+void mg_hexdumpf(FILE *fp, const void *buf, int len);
+
+/*
+ * Generates human-readable hexdump of the data sent or received by the
+ * connection. `path` is a file name where hexdump should be written.
+ * `num_bytes` is a number of bytes sent/received. `ev` is one of the `MG_*`
+ * events sent to an event handler. This function is supposed to be called from
+ * the event handler.
+ */
+void mg_hexdump_connection(struct mg_connection *nc, const char *path,
+                           const void *buf, int num_bytes, int ev);
+#endif
+
+/*
+ * Returns true if target platform is big endian.
+ */
+int mg_is_big_endian(void);
+
+/*
+ * Use with cs_base64_init/update/finish in order to write out base64 in chunks.
+ */
+void mg_mbuf_append_base64_putc(char ch, void *user_data);
+
+/*
+ * Encode `len` bytes starting at `data` as base64 and append them to an mbuf.
+ */
+void mg_mbuf_append_base64(struct mbuf *mbuf, const void *data, size_t len);
+
+/*
+ * Generate a Basic Auth header and appends it to buf.
+ * If pass is NULL, then user is expected to contain the credentials pair
+ * already encoded as `user:pass`.
+ */
+void mg_basic_auth_header(const struct mg_str user, const struct mg_str pass,
+                          struct mbuf *buf);
+
+/*
+ * URL-escape the specified string.
+ * All non-printable characters are escaped, plus `._-$,;~()/`.
+ * Input need not be NUL-terminated, but the returned string is.
+ * Returned string is heap-allocated and must be free()'d.
+ */
+struct mg_str mg_url_encode(const struct mg_str src);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* CS_MONGOOSE_SRC_UTIL_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_http.h"
+#endif
+/*
+ * Copyright (c) 2014 Cesanta Software Limited
+ * All rights reserved
+ */
+
+/*
+ * === Common API reference
+ */
+
+#ifndef CS_MONGOOSE_SRC_HTTP_H_
+#define CS_MONGOOSE_SRC_HTTP_H_
+
+#if MG_ENABLE_HTTP
+
+/* Amalgamated: #include "mg_net.h" */
+/* Amalgamated: #include "common/mg_str.h" */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifndef MG_MAX_HTTP_HEADERS
+#define MG_MAX_HTTP_HEADERS 20
+#endif
+
+#ifndef MG_MAX_HTTP_REQUEST_SIZE
+#define MG_MAX_HTTP_REQUEST_SIZE 1024
+#endif
+
+#ifndef MG_MAX_HTTP_SEND_MBUF
+#define MG_MAX_HTTP_SEND_MBUF 1024
+#endif
+
+#ifndef MG_CGI_ENVIRONMENT_SIZE
+#define MG_CGI_ENVIRONMENT_SIZE 8192
+#endif
+
+/* HTTP message */
+struct http_message {
+  struct mg_str message; /* Whole message: request line + headers + body */
+  struct mg_str body;    /* Message body. 0-length for requests with no body */
+
+  /* HTTP Request line (or HTTP response line) */
+  struct mg_str method; /* "GET" */
+  struct mg_str uri;    /* "/my_file.html" */
+  struct mg_str proto;  /* "HTTP/1.1" -- for both request and response */
+
+  /* For responses, code and response status message are set */
+  int resp_code;
+  struct mg_str resp_status_msg;
+
+  /*
+   * Query-string part of the URI. For example, for HTTP request
+   *    GET /foo/bar?param1=val1¶m2=val2
+   *    |    uri    |     query_string     |
+   *
+   * Note that question mark character doesn't belong neither to the uri,
+   * nor to the query_string
+   */
+  struct mg_str query_string;
+
+  /* Headers */
+  struct mg_str header_names[MG_MAX_HTTP_HEADERS];
+  struct mg_str header_values[MG_MAX_HTTP_HEADERS];
+};
+
+#if MG_ENABLE_HTTP_WEBSOCKET
+/* WebSocket message */
+struct websocket_message {
+  unsigned char *data;
+  size_t size;
+  unsigned char flags;
+};
+#endif
+
+/* HTTP multipart part */
+struct mg_http_multipart_part {
+  const char *file_name;
+  const char *var_name;
+  struct mg_str data;
+  int status; /* <0 on error */
+  void *user_data;
+};
+
+/* SSI call context */
+struct mg_ssi_call_ctx {
+  struct http_message *req; /* The request being processed. */
+  struct mg_str file;       /* Filesystem path of the file being processed. */
+  struct mg_str arg; /* The argument passed to the tag: . */
+};
+
+/* HTTP and websocket events. void *ev_data is described in a comment. */
+#define MG_EV_HTTP_REQUEST 100 /* struct http_message * */
+#define MG_EV_HTTP_REPLY 101   /* struct http_message * */
+#define MG_EV_HTTP_CHUNK 102   /* struct http_message * */
+#define MG_EV_SSI_CALL 105     /* char * */
+#define MG_EV_SSI_CALL_CTX 106 /* struct mg_ssi_call_ctx * */
+
+#if MG_ENABLE_HTTP_WEBSOCKET
+#define MG_EV_WEBSOCKET_HANDSHAKE_REQUEST 111 /* struct http_message * */
+#define MG_EV_WEBSOCKET_HANDSHAKE_DONE 112    /* NULL */
+#define MG_EV_WEBSOCKET_FRAME 113             /* struct websocket_message * */
+#define MG_EV_WEBSOCKET_CONTROL_FRAME 114     /* struct websocket_message * */
+#endif
+
+#if MG_ENABLE_HTTP_STREAMING_MULTIPART
+#define MG_EV_HTTP_MULTIPART_REQUEST 121 /* struct http_message */
+#define MG_EV_HTTP_PART_BEGIN 122        /* struct mg_http_multipart_part */
+#define MG_EV_HTTP_PART_DATA 123         /* struct mg_http_multipart_part */
+#define MG_EV_HTTP_PART_END 124          /* struct mg_http_multipart_part */
+/* struct mg_http_multipart_part */
+#define MG_EV_HTTP_MULTIPART_REQUEST_END 125
+#endif
+
+/*
+ * Attaches a built-in HTTP event handler to the given connection.
+ * The user-defined event handler will receive following extra events:
+ *
+ * - MG_EV_HTTP_REQUEST: HTTP request has arrived. Parsed HTTP request
+ *  is passed as
+ *   `struct http_message` through the handler's `void *ev_data` pointer.
+ * - MG_EV_HTTP_REPLY: The HTTP reply has arrived. The parsed HTTP reply is
+ *   passed as `struct http_message` through the handler's `void *ev_data`
+ *   pointer.
+ * - MG_EV_HTTP_CHUNK: The HTTP chunked-encoding chunk has arrived.
+ *   The parsed HTTP reply is passed as `struct http_message` through the
+ *   handler's `void *ev_data` pointer. `http_message::body` would contain
+ *   incomplete, reassembled HTTP body.
+ *   It will grow with every new chunk that arrives, and it can
+ *   potentially consume a lot of memory. An event handler may process
+ *   the body as chunks are coming, and signal Mongoose to delete processed
+ *   body by setting `MG_F_DELETE_CHUNK` in `mg_connection::flags`. When
+ *   the last zero chunk is received,
+ *   Mongoose sends `MG_EV_HTTP_REPLY` event with
+ *   full reassembled body (if handler did not signal to delete chunks) or
+ *   with empty body (if handler did signal to delete chunks).
+ * - MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: server has received the WebSocket
+ *   handshake request. `ev_data` contains parsed HTTP request.
+ * - MG_EV_WEBSOCKET_HANDSHAKE_DONE: server has completed the WebSocket
+ *   handshake. `ev_data` is `NULL`.
+ * - MG_EV_WEBSOCKET_FRAME: new WebSocket frame has arrived. `ev_data` is
+ *   `struct websocket_message *`
+ *
+ * When compiled with MG_ENABLE_HTTP_STREAMING_MULTIPART, Mongoose parses
+ * multipart requests and splits them into separate events:
+ * - MG_EV_HTTP_MULTIPART_REQUEST: Start of the request.
+ *   This event is sent before body is parsed. After this, the user
+ *   should expect a sequence of PART_BEGIN/DATA/END requests.
+ *   This is also the last time when headers and other request fields are
+ *   accessible.
+ * - MG_EV_HTTP_PART_BEGIN: Start of a part of a multipart message.
+ *   Argument: mg_http_multipart_part with var_name and file_name set
+ *   (if present). No data is passed in this message.
+ * - MG_EV_HTTP_PART_DATA: new portion of data from the multipart message.
+ *   Argument: mg_http_multipart_part. var_name and file_name are preserved,
+ *   data is available in mg_http_multipart_part.data.
+ * - MG_EV_HTTP_PART_END: End of the current part. var_name, file_name are
+ *   the same, no data in the message. If status is 0, then the part is
+ *   properly terminated with a boundary, status < 0 means that connection
+ *   was terminated.
+ * - MG_EV_HTTP_MULTIPART_REQUEST_END: End of the multipart request.
+ *   Argument: mg_http_multipart_part, var_name and file_name are NULL,
+ *   status = 0 means request was properly closed, < 0 means connection
+ *   was terminated (note: in this case both PART_END and REQUEST_END are
+ *   delivered).
+ */
+void mg_set_protocol_http_websocket(struct mg_connection *nc);
+
+#if MG_ENABLE_HTTP_WEBSOCKET
+/*
+ * Send websocket handshake to the server.
+ *
+ * `nc` must be a valid connection, connected to a server. `uri` is an URI
+ * to fetch, extra_headers` is extra HTTP headers to send or `NULL`.
+ *
+ * This function is intended to be used by websocket client.
+ *
+ * Note that the Host header is mandatory in HTTP/1.1 and must be
+ * included in `extra_headers`. `mg_send_websocket_handshake2` offers
+ * a better API for that.
+ *
+ * Deprecated in favour of `mg_send_websocket_handshake2`
+ */
+void mg_send_websocket_handshake(struct mg_connection *nc, const char *uri,
+                                 const char *extra_headers);
+
+/*
+ * Send websocket handshake to the server.
+ *
+ * `nc` must be a valid connection, connected to a server. `uri` is an URI
+ * to fetch, `host` goes into the `Host` header, `protocol` goes into the
+ * `Sec-WebSocket-Proto` header (NULL to omit), extra_headers` is extra HTTP
+ * headers to send or `NULL`.
+ *
+ * This function is intended to be used by websocket client.
+ */
+void mg_send_websocket_handshake2(struct mg_connection *nc, const char *path,
+                                  const char *host, const char *protocol,
+                                  const char *extra_headers);
+
+/* Like mg_send_websocket_handshake2 but also passes basic auth header */
+void mg_send_websocket_handshake3(struct mg_connection *nc, const char *path,
+                                  const char *host, const char *protocol,
+                                  const char *extra_headers, const char *user,
+                                  const char *pass);
+
+/* Same as mg_send_websocket_handshake3 but with strings not necessarily
+ * NUL-temrinated */
+void mg_send_websocket_handshake3v(struct mg_connection *nc,
+                                   const struct mg_str path,
+                                   const struct mg_str host,
+                                   const struct mg_str protocol,
+                                   const struct mg_str extra_headers,
+                                   const struct mg_str user,
+                                   const struct mg_str pass);
+
+/*
+ * Helper function that creates an outbound WebSocket connection.
+ *
+ * `url` is a URL to connect to. It must be properly URL-encoded, e.g. have
+ * no spaces, etc. By default, `mg_connect_ws()` sends Connection and
+ * Host headers. `extra_headers` is an extra HTTP header to send, e.g.
+ * `"User-Agent: my-app\r\n"`.
+ * If `protocol` is not NULL, then a `Sec-WebSocket-Protocol` header is sent.
+ *
+ * Examples:
+ *
+ * ```c
+ *   nc1 = mg_connect_ws(mgr, ev_handler_1, "ws://echo.websocket.org", NULL,
+ *                       NULL);
+ *   nc2 = mg_connect_ws(mgr, ev_handler_1, "wss://echo.websocket.org", NULL,
+ *                       NULL);
+ *   nc3 = mg_connect_ws(mgr, ev_handler_1, "ws://api.cesanta.com",
+ *                       "clubby.cesanta.com", NULL);
+ * ```
+ */
+struct mg_connection *mg_connect_ws(struct mg_mgr *mgr,
+                                    MG_CB(mg_event_handler_t event_handler,
+                                          void *user_data),
+                                    const char *url, const char *protocol,
+                                    const char *extra_headers);
+
+/*
+ * Helper function that creates an outbound WebSocket connection
+ *
+ * Mostly identical to `mg_connect_ws`, but allows to provide extra parameters
+ * (for example, SSL parameters)
+ */
+struct mg_connection *mg_connect_ws_opt(
+    struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
+    struct mg_connect_opts opts, const char *url, const char *protocol,
+    const char *extra_headers);
+
+/*
+ * Send WebSocket frame to the remote end.
+ *
+ * `op_and_flags` specifies the frame's type. It's one of:
+ *
+ * - WEBSOCKET_OP_CONTINUE
+ * - WEBSOCKET_OP_TEXT
+ * - WEBSOCKET_OP_BINARY
+ * - WEBSOCKET_OP_CLOSE
+ * - WEBSOCKET_OP_PING
+ * - WEBSOCKET_OP_PONG
+ *
+ * Orred with one of the flags:
+ *
+ * - WEBSOCKET_DONT_FIN: Don't set the FIN flag on the frame to be sent.
+ *
+ * `data` and `data_len` contain frame data.
+ */
+void mg_send_websocket_frame(struct mg_connection *nc, int op_and_flags,
+                             const void *data, size_t data_len);
+
+/*
+ * Like `mg_send_websocket_frame()`, but composes a single frame from multiple
+ * buffers.
+ */
+void mg_send_websocket_framev(struct mg_connection *nc, int op_and_flags,
+                              const struct mg_str *strings, int num_strings);
+
+/*
+ * Sends WebSocket frame to the remote end.
+ *
+ * Like `mg_send_websocket_frame()`, but allows to create formatted messages
+ * with `printf()`-like semantics.
+ */
+void mg_printf_websocket_frame(struct mg_connection *nc, int op_and_flags,
+                               const char *fmt, ...);
+
+/* Websocket opcodes, from http://tools.ietf.org/html/rfc6455 */
+#define WEBSOCKET_OP_CONTINUE 0
+#define WEBSOCKET_OP_TEXT 1
+#define WEBSOCKET_OP_BINARY 2
+#define WEBSOCKET_OP_CLOSE 8
+#define WEBSOCKET_OP_PING 9
+#define WEBSOCKET_OP_PONG 10
+
+/*
+ * If set causes the FIN flag to not be set on outbound
+ * frames. This enables sending multiple fragments of a single
+ * logical message.
+ *
+ * The WebSocket protocol mandates that if the FIN flag of a data
+ * frame is not set, the next frame must be a WEBSOCKET_OP_CONTINUE.
+ * The last frame must have the FIN bit set.
+ *
+ * Note that mongoose will automatically defragment incoming messages,
+ * so this flag is used only on outbound messages.
+ */
+#define WEBSOCKET_DONT_FIN 0x100
+
+#endif /* MG_ENABLE_HTTP_WEBSOCKET */
+
+/*
+ * Decodes a URL-encoded string.
+ *
+ * Source string is specified by (`src`, `src_len`), and destination is
+ * (`dst`, `dst_len`). If `is_form_url_encoded` is non-zero, then
+ * `+` character is decoded as a blank space character. This function
+ * guarantees to NUL-terminate the destination. If destination is too small,
+ * then the source string is partially decoded and `-1` is returned.
+ *Otherwise,
+ * a length of the decoded string is returned, not counting final NUL.
+ */
+int mg_url_decode(const char *src, int src_len, char *dst, int dst_len,
+                  int is_form_url_encoded);
+
+extern void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[],
+                          const size_t *msg_lens, uint8_t *digest);
+extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[],
+                           const size_t *msg_lens, uint8_t *digest);
+
+/*
+ * Flags for `mg_http_is_authorized()`.
+ */
+#define MG_AUTH_FLAG_IS_DIRECTORY (1 << 0)
+#define MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE (1 << 1)
+#define MG_AUTH_FLAG_ALLOW_MISSING_FILE (1 << 2)
+
+/*
+ * Checks whether an http request is authorized. `domain` is the authentication
+ * realm, `passwords_file` is a htdigest file (can be created e.g. with
+ * `htdigest` utility). If either `domain` or `passwords_file` is NULL, this
+ * function always returns 1; otherwise checks the authentication in the
+ * http request and returns 1 only if there is a match; 0 otherwise.
+ */
+int mg_http_is_authorized(struct http_message *hm, struct mg_str path,
+                          const char *domain, const char *passwords_file,
+                          int flags);
+
+/*
+ * Sends 401 Unauthorized response.
+ */
+void mg_http_send_digest_auth_request(struct mg_connection *c,
+                                      const char *domain);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* MG_ENABLE_HTTP */
+
+#endif /* CS_MONGOOSE_SRC_HTTP_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_http_server.h"
+#endif
+/*
+ * === Server API reference
+ */
+
+#ifndef CS_MONGOOSE_SRC_HTTP_SERVER_H_
+#define CS_MONGOOSE_SRC_HTTP_SERVER_H_
+
+#if MG_ENABLE_HTTP
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Parses a HTTP message.
+ *
+ * `is_req` should be set to 1 if parsing a request, 0 if reply.
+ *
+ * Returns the number of bytes parsed. If HTTP message is
+ * incomplete `0` is returned. On parse error, a negative number is returned.
+ */
+int mg_parse_http(const char *s, int n, struct http_message *hm, int is_req);
+
+/*
+ * Searches and returns the header `name` in parsed HTTP message `hm`.
+ * If header is not found, NULL is returned. Example:
+ *
+ *     struct mg_str *host_hdr = mg_get_http_header(hm, "Host");
+ */
+struct mg_str *mg_get_http_header(struct http_message *hm, const char *name);
+
+/*
+ * Parses the HTTP header `hdr`. Finds variable `var_name` and stores its value
+ * in the buffer `*buf`, `buf_size`. If the buffer size is not enough,
+ * allocates a buffer of required size and writes it to `*buf`, similar to
+ * asprintf(). The caller should always check whether the buffer was updated,
+ * and free it if so.
+ *
+ * This function is supposed to parse cookies, authentication headers, etc.
+ * Example (error handling omitted):
+ *
+ *     char user_buf[20];
+ *     char *user = user_buf;
+ *     struct mg_str *hdr = mg_get_http_header(hm, "Authorization");
+ *     mg_http_parse_header2(hdr, "username", &user, sizeof(user_buf));
+ *     // ... do something useful with user
+ *     if (user != user_buf) {
+ *       free(user);
+ *     }
+ *
+ * Returns the length of the variable's value. If variable is not found, 0 is
+ * returned.
+ */
+int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf,
+                          size_t buf_size);
+
+/*
+ * DEPRECATED: use mg_http_parse_header2() instead.
+ *
+ * Same as mg_http_parse_header2(), but takes buffer as a `char *` (instead of
+ * `char **`), and thus it cannot allocate a new buffer if the provided one
+ * is not enough, and just returns 0 in that case.
+ */
+int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
+                         size_t buf_size)
+#ifdef __GNUC__
+    __attribute__((deprecated));
+#endif
+;
+
+/*
+ * Gets and parses the Authorization: Basic header
+ * Returns -1 if no Authorization header is found, or if
+ * mg_parse_http_basic_auth
+ * fails parsing the resulting header.
+ */
+int mg_get_http_basic_auth(struct http_message *hm, char *user, size_t user_len,
+                           char *pass, size_t pass_len);
+
+/*
+ * Parses the Authorization: Basic header
+ * Returns -1 iif the authorization type is not "Basic" or any other error such
+ * as incorrectly encoded base64 user password pair.
+ */
+int mg_parse_http_basic_auth(struct mg_str *hdr, char *user, size_t user_len,
+                             char *pass, size_t pass_len);
+
+/*
+ * Parses the buffer `buf`, `buf_len` that contains multipart form data chunks.
+ * Stores the chunk name in a `var_name`, `var_name_len` buffer.
+ * If a chunk is an uploaded file, then `file_name`, `file_name_len` is
+ * filled with an uploaded file name. `chunk`, `chunk_len`
+ * points to the chunk data.
+ *
+ * Return: number of bytes to skip to the next chunk or 0 if there are
+ *         no more chunks.
+ *
+ * Usage example:
+ *
+ * ```c
+ *    static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
+ *      switch(ev) {
+ *        case MG_EV_HTTP_REQUEST: {
+ *          struct http_message *hm = (struct http_message *) ev_data;
+ *          char var_name[100], file_name[100];
+ *          const char *chunk;
+ *          size_t chunk_len, n1, n2;
+ *
+ *          n1 = n2 = 0;
+ *          while ((n2 = mg_parse_multipart(hm->body.p + n1,
+ *                                          hm->body.len - n1,
+ *                                          var_name, sizeof(var_name),
+ *                                          file_name, sizeof(file_name),
+ *                                          &chunk, &chunk_len)) > 0) {
+ *            printf("var: %s, file_name: %s, size: %d, chunk: [%.*s]\n",
+ *                   var_name, file_name, (int) chunk_len,
+ *                   (int) chunk_len, chunk);
+ *            n1 += n2;
+ *          }
+ *        }
+ *        break;
+ * ```
+ */
+size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name,
+                          size_t var_name_len, char *file_name,
+                          size_t file_name_len, const char **chunk,
+                          size_t *chunk_len);
+
+/*
+ * Fetches a HTTP form variable.
+ *
+ * Fetches a variable `name` from a `buf` into a buffer specified by `dst`,
+ * `dst_len`. The destination is always zero-terminated. Returns the length of
+ * a fetched variable. If not found, 0 is returned. `buf` must be valid
+ * url-encoded buffer. If destination is too small or an error occured,
+ * negative number is returned.
+ */
+int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst,
+                    size_t dst_len);
+
+#if MG_ENABLE_FILESYSTEM
+/*
+ * This structure defines how `mg_serve_http()` works.
+ * Best practice is to set only required settings, and leave the rest as NULL.
+ */
+struct mg_serve_http_opts {
+  /* Path to web root directory */
+  const char *document_root;
+
+  /* List of index files. Default is "" */
+  const char *index_files;
+
+  /*
+   * Leave as NULL to disable authentication.
+   * To enable directory protection with authentication, set this to ".htpasswd"
+   * Then, creating ".htpasswd" file in any directory automatically protects
+   * it with digest authentication.
+   * Use `mongoose` web server binary, or `htdigest` Apache utility to
+   * create/manipulate passwords file.
+   * Make sure `auth_domain` is set to a valid domain name.
+   */
+  const char *per_directory_auth_file;
+
+  /* Authorization domain (domain name of this web server) */
+  const char *auth_domain;
+
+  /*
+   * Leave as NULL to disable authentication.
+   * Normally, only selected directories in the document root are protected.
+   * If absolutely every access to the web server needs to be authenticated,
+   * regardless of the URI, set this option to the path to the passwords file.
+   * Format of that file is the same as ".htpasswd" file. Make sure that file
+   * is located outside document root to prevent people fetching it.
+   */
+  const char *global_auth_file;
+
+  /* Set to "no" to disable directory listing. Enabled by default. */
+  const char *enable_directory_listing;
+
+  /*
+   * SSI files pattern. If not set, "**.shtml$|**.shtm$" is used.
+   *
+   * All files that match ssi_pattern are treated as SSI.
+   *
+   * Server Side Includes (SSI) is a simple interpreted server-side scripting
+   * language which is most commonly used to include the contents of a file
+   * into a web page. It can be useful when it is desirable to include a common
+   * piece of code throughout a website, for example, headers and footers.
+   *
+   * In order for a webpage to recognize an SSI-enabled HTML file, the
+   * filename should end with a special extension, by default the extension
+   * should be either .shtml or .shtm
+   *
+   * Unknown SSI directives are silently ignored by Mongoose. Currently,
+   * the following SSI directives are supported:
+   *    <!--#include FILE_TO_INCLUDE -->
+   *    <!--#exec "COMMAND_TO_EXECUTE" -->
+   *    <!--#call COMMAND -->
+   *
+   * Note that <!--#include ...> directive supports three path
+   *specifications:
+   *
+   * <!--#include virtual="path" -->  Path is relative to web server root
+   * <!--#include abspath="path" -->  Path is absolute or relative to the
+   *                                  web server working dir
+   * <!--#include file="path" -->,    Path is relative to current document
+   * <!--#include "path" -->
+   *
+   * The include directive may be used to include the contents of a file or
+   * the result of running a CGI script.
+   *
+   * The exec directive is used to execute
+   * a command on a server, and show command's output. Example:
+   *
+   * <!--#exec "ls -l" -->
+   *
+   * The call directive is a way to invoke a C handler from the HTML page.
+   * On each occurence of <!--#call COMMAND OPTIONAL_PARAMS> directive,
+   * Mongoose calls a registered event handler with MG_EV_SSI_CALL event,
+   * and event parameter will point to the COMMAND OPTIONAL_PARAMS string.
+   * An event handler can output any text, for example by calling
+   * `mg_printf()`. This is a flexible way of generating a web page on
+   * server side by calling a C event handler. Example:
+   *
+   * <!--#call foo --> ... <!--#call bar -->
+   *
+   * In the event handler:
+   *    case MG_EV_SSI_CALL: {
+   *      const char *param = (const char *) ev_data;
+   *      if (strcmp(param, "foo") == 0) {
+   *        mg_printf(c, "hello from foo");
+   *      } else if (strcmp(param, "bar") == 0) {
+   *        mg_printf(c, "hello from bar");
+   *      }
+   *      break;
+   *    }
+   */
+  const char *ssi_pattern;
+
+  /* IP ACL. By default, NULL, meaning all IPs are allowed to connect */
+  const char *ip_acl;
+
+#if MG_ENABLE_HTTP_URL_REWRITES
+  /* URL rewrites.
+   *
+   * Comma-separated list of `uri_pattern=url_file_or_directory_path` rewrites.
+   * When HTTP request is received, Mongoose constructs a file name from the
+   * requested URI by combining `document_root` and the URI. However, if the
+   * rewrite option is used and `uri_pattern` matches requested URI, then
+   * `document_root` is ignored. Instead, `url_file_or_directory_path` is used,
+   * which should be a full path name or a path relative to the web server's
+   * current working directory. It can also be an URI (http:// or https://)
+   * in which case mongoose will behave as a reverse proxy for that destination.
+   *
+   * Note that `uri_pattern`, as all Mongoose patterns, is a prefix pattern.
+   *
+   * If uri_pattern starts with `@` symbol, then Mongoose compares it with the
+   * HOST header of the request. If they are equal, Mongoose sets document root
+   * to `file_or_directory_path`, implementing virtual hosts support.
+   * Example: `@foo.com=/document/root/for/foo.com`
+   *
+   * If `uri_pattern` starts with `%` symbol, then Mongoose compares it with
+   * the listening port. If they match, then Mongoose issues a 301 redirect.
+   * For example, to redirect all HTTP requests to the
+   * HTTPS port, do `%80=https://my.site.com`. Note that the request URI is
+   * automatically appended to the redirect location.
+   */
+  const char *url_rewrites;
+#endif
+
+  /* DAV document root. If NULL, DAV requests are going to fail. */
+  const char *dav_document_root;
+
+  /*
+   * DAV passwords file. If NULL, DAV requests are going to fail.
+   * If passwords file is set to "-", then DAV auth is disabled.
+   */
+  const char *dav_auth_file;
+
+  /* Glob pattern for the files to hide. */
+  const char *hidden_file_pattern;
+
+  /* Set to non-NULL to enable CGI, e.g. **.cgi$|**.php$" */
+  const char *cgi_file_pattern;
+
+  /* If not NULL, ignore CGI script hashbang and use this interpreter */
+  const char *cgi_interpreter;
+
+  /*
+   * Comma-separated list of Content-Type overrides for path suffixes, e.g.
+   * ".txt=text/plain; charset=utf-8,.c=text/plain"
+   */
+  const char *custom_mime_types;
+
+  /*
+   * Extra HTTP headers to add to each server response.
+   * Example: to enable CORS, set this to "Access-Control-Allow-Origin: *".
+   */
+  const char *extra_headers;
+};
+
+/*
+ * Serves given HTTP request according to the `options`.
+ *
+ * Example code snippet:
+ *
+ * ```c
+ * static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
+ *   struct http_message *hm = (struct http_message *) ev_data;
+ *   struct mg_serve_http_opts opts = { .document_root = "/var/www" };  // C99
+ *
+ *   switch (ev) {
+ *     case MG_EV_HTTP_REQUEST:
+ *       mg_serve_http(nc, hm, opts);
+ *       break;
+ *     default:
+ *       break;
+ *   }
+ * }
+ * ```
+ */
+void mg_serve_http(struct mg_connection *nc, struct http_message *hm,
+                   struct mg_serve_http_opts opts);
+
+/*
+ * Serves a specific file with a given MIME type and optional extra headers.
+ *
+ * Example code snippet:
+ *
+ * ```c
+ * static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
+ *   switch (ev) {
+ *     case MG_EV_HTTP_REQUEST: {
+ *       struct http_message *hm = (struct http_message *) ev_data;
+ *       mg_http_serve_file(nc, hm, "file.txt",
+ *                          mg_mk_str("text/plain"), mg_mk_str(""));
+ *       break;
+ *     }
+ *     ...
+ *   }
+ * }
+ * ```
+ */
+void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm,
+                        const char *path, const struct mg_str mime_type,
+                        const struct mg_str extra_headers);
+
+#if MG_ENABLE_HTTP_STREAMING_MULTIPART
+
+/* Callback prototype for `mg_file_upload_handler()`. */
+typedef struct mg_str (*mg_fu_fname_fn)(struct mg_connection *nc,
+                                        struct mg_str fname);
+
+/*
+ * File upload handler.
+ * This handler can be used to implement file uploads with minimum code.
+ * This handler will process MG_EV_HTTP_PART_* events and store file data into
+ * a local file.
+ * `local_name_fn` will be invoked with whatever name was provided by the client
+ * and will expect the name of the local file to open. A return value of NULL
+ * will abort file upload (client will get a "403 Forbidden" response). If
+ * non-null, the returned string must be heap-allocated and will be freed by
+ * the caller.
+ * Exception: it is ok to return the same string verbatim.
+ *
+ * Example:
+ *
+ * ```c
+ * struct mg_str upload_fname(struct mg_connection *nc, struct mg_str fname) {
+ *   // Just return the same filename. Do not actually do this except in test!
+ *   // fname is user-controlled and needs to be sanitized.
+ *   return fname;
+ * }
+ * void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
+ *   switch (ev) {
+ *     ...
+ *     case MG_EV_HTTP_PART_BEGIN:
+ *     case MG_EV_HTTP_PART_DATA:
+ *     case MG_EV_HTTP_PART_END:
+ *       mg_file_upload_handler(nc, ev, ev_data, upload_fname);
+ *       break;
+ *   }
+ * }
+ * ```
+ */
+void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data,
+                            mg_fu_fname_fn local_name_fn
+                                MG_UD_ARG(void *user_data));
+#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */
+#endif /* MG_ENABLE_FILESYSTEM */
+
+/*
+ * Registers a callback for a specified http endpoint
+ * Note: if callback is registered it is called instead of the
+ * callback provided in mg_bind
+ *
+ * Example code snippet:
+ *
+ * ```c
+ * static void handle_hello1(struct mg_connection *nc, int ev, void *ev_data) {
+ *   (void) ev; (void) ev_data;
+ *   mg_printf(nc, "HTTP/1.0 200 OK\r\n\r\n[I am Hello1]");
+ *  nc->flags |= MG_F_SEND_AND_CLOSE;
+ * }
+ *
+ * static void handle_hello2(struct mg_connection *nc, int ev, void *ev_data) {
+ *  (void) ev; (void) ev_data;
+ *   mg_printf(nc, "HTTP/1.0 200 OK\r\n\r\n[I am Hello2]");
+ *  nc->flags |= MG_F_SEND_AND_CLOSE;
+ * }
+ *
+ * void init() {
+ *   nc = mg_bind(&mgr, local_addr, cb1);
+ *   mg_register_http_endpoint(nc, "/hello1", handle_hello1);
+ *   mg_register_http_endpoint(nc, "/hello1/hello2", handle_hello2);
+ * }
+ * ```
+ */
+void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path,
+                               MG_CB(mg_event_handler_t handler,
+                                     void *user_data));
+
+struct mg_http_endpoint_opts {
+  void *user_data;
+  /* Authorization domain (realm) */
+  const char *auth_domain;
+  const char *auth_file;
+};
+
+void mg_register_http_endpoint_opt(struct mg_connection *nc,
+                                   const char *uri_path,
+                                   mg_event_handler_t handler,
+                                   struct mg_http_endpoint_opts opts);
+
+/*
+ * Authenticates a HTTP request against an opened password file.
+ * Returns 1 if authenticated, 0 otherwise.
+ */
+int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
+                              FILE *fp);
+
+/*
+ * Authenticates given response params against an opened password file.
+ * Returns 1 if authenticated, 0 otherwise.
+ *
+ * It's used by mg_http_check_digest_auth().
+ */
+int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
+                         struct mg_str username, struct mg_str cnonce,
+                         struct mg_str response, struct mg_str qop,
+                         struct mg_str nc, struct mg_str nonce,
+                         struct mg_str auth_domain, FILE *fp);
+
+/*
+ * Sends buffer `buf` of size `len` to the client using chunked HTTP encoding.
+ * This function sends the buffer size as hex number + newline first, then
+ * the buffer itself, then the newline. For example,
+ * `mg_send_http_chunk(nc, "foo", 3)` will append the `3\r\nfoo\r\n` string
+ * to the `nc->send_mbuf` output IO buffer.
+ *
+ * NOTE: The HTTP header "Transfer-Encoding: chunked" should be sent prior to
+ * using this function.
+ *
+ * NOTE: do not forget to send an empty chunk at the end of the response,
+ * to tell the client that everything was sent. Example:
+ *
+ * ```
+ *   mg_printf_http_chunk(nc, "%s", "my response!");
+ *   mg_send_http_chunk(nc, "", 0); // Tell the client we're finished
+ * ```
+ */
+void mg_send_http_chunk(struct mg_connection *nc, const char *buf, size_t len);
+
+/*
+ * Sends a printf-formatted HTTP chunk.
+ * Functionality is similar to `mg_send_http_chunk()`.
+ */
+void mg_printf_http_chunk(struct mg_connection *nc, const char *fmt, ...);
+
+/*
+ * Sends the response status line.
+ * If `extra_headers` is not NULL, then `extra_headers` are also sent
+ * after the response line. `extra_headers` must NOT end end with new line.
+ * Example:
+ *
+ *      mg_send_response_line(nc, 200, "Access-Control-Allow-Origin: *");
+ *
+ * Will result in:
+ *
+ *      HTTP/1.1 200 OK\r\n
+ *      Access-Control-Allow-Origin: *\r\n
+ */
+void mg_send_response_line(struct mg_connection *nc, int status_code,
+                           const char *extra_headers);
+
+/*
+ * Sends an error response. If reason is NULL, the message will be inferred
+ * from the error code (if supported).
+ */
+void mg_http_send_error(struct mg_connection *nc, int code, const char *reason);
+
+/*
+ * Sends a redirect response.
+ * `status_code` should be either 301 or 302 and `location` point to the
+ * new location.
+ * If `extra_headers` is not empty, then `extra_headers` are also sent
+ * after the response line. `extra_headers` must NOT end end with new line.
+ *
+ * Example:
+ *
+ *      mg_http_send_redirect(nc, 302, mg_mk_str("/login"), mg_mk_str(NULL));
+ */
+void mg_http_send_redirect(struct mg_connection *nc, int status_code,
+                           const struct mg_str location,
+                           const struct mg_str extra_headers);
+
+/*
+ * Sends the response line and headers.
+ * This function sends the response line with the `status_code`, and
+ * automatically
+ * sends one header: either "Content-Length" or "Transfer-Encoding".
+ * If `content_length` is negative, then "Transfer-Encoding: chunked" header
+ * is sent, otherwise, "Content-Length" header is sent.
+ *
+ * NOTE: If `Transfer-Encoding` is `chunked`, then message body must be sent
+ * using `mg_send_http_chunk()` or `mg_printf_http_chunk()` functions.
+ * Otherwise, `mg_send()` or `mg_printf()` must be used.
+ * Extra headers could be set through `extra_headers`. Note `extra_headers`
+ * must NOT be terminated by a new line.
+ */
+void mg_send_head(struct mg_connection *n, int status_code,
+                  int64_t content_length, const char *extra_headers);
+
+/*
+ * Sends a printf-formatted HTTP chunk, escaping HTML tags.
+ */
+void mg_printf_html_escape(struct mg_connection *nc, const char *fmt, ...);
+
+#if MG_ENABLE_HTTP_URL_REWRITES
+/*
+ * Proxies a given request to a given upstream http server. The path prefix
+ * in `mount` will be stripped of the path requested to the upstream server,
+ * e.g. if mount is /api and upstream is http://localhost:8001/foo
+ * then an incoming request to /api/bar will cause a request to
+ * http://localhost:8001/foo/bar
+ *
+ * EXPERIMENTAL API. Please use http_serve_http + url_rewrites if a static
+ * mapping is good enough.
+ */
+void mg_http_reverse_proxy(struct mg_connection *nc,
+                           const struct http_message *hm, struct mg_str mount,
+                           struct mg_str upstream);
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* MG_ENABLE_HTTP */
+
+#endif /* CS_MONGOOSE_SRC_HTTP_SERVER_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_http_client.h"
+#endif
+/*
+ * === Client API reference
+ */
+
+#ifndef CS_MONGOOSE_SRC_HTTP_CLIENT_H_
+#define CS_MONGOOSE_SRC_HTTP_CLIENT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Helper function that creates an outbound HTTP connection.
+ *
+ * `url` is the URL to fetch. It must be properly URL-encoded, e.g. have
+ * no spaces, etc. By default, `mg_connect_http()` sends the Connection and
+ * Host headers. `extra_headers` is an extra HTTP header to send, e.g.
+ * `"User-Agent: my-app\r\n"`.
+ * If `post_data` is NULL, then a GET request is created. Otherwise, a POST
+ * request is created with the specified POST data. Note that if the data being
+ * posted is a form submission, the `Content-Type` header should be set
+ * accordingly (see example below).
+ *
+ * Examples:
+ *
+ * ```c
+ *   nc1 = mg_connect_http(mgr, ev_handler_1, "http://www.google.com", NULL,
+ *                         NULL);
+ *   nc2 = mg_connect_http(mgr, ev_handler_1, "https://github.com", NULL, NULL);
+ *   nc3 = mg_connect_http(
+ *       mgr, ev_handler_1, "my_server:8000/form_submit/",
+ *       "Content-Type: application/x-www-form-urlencoded\r\n",
+ *       "var_1=value_1&var_2=value_2");
+ * ```
+ */
+struct mg_connection *mg_connect_http(
+    struct mg_mgr *mgr,
+    MG_CB(mg_event_handler_t event_handler, void *user_data), const char *url,
+    const char *extra_headers, const char *post_data);
+
+/*
+ * Helper function that creates an outbound HTTP connection.
+ *
+ * Mostly identical to mg_connect_http, but allows you to provide extra
+ *parameters
+ * (for example, SSL parameters)
+ */
+struct mg_connection *mg_connect_http_opt(
+    struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
+    struct mg_connect_opts opts, const char *url, const char *extra_headers,
+    const char *post_data);
+
+/* Creates digest authentication header for a client request. */
+int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
+                                      const char *method, const char *uri,
+                                      const char *auth_domain, const char *user,
+                                      const char *passwd, const char *nonce);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* CS_MONGOOSE_SRC_HTTP_CLIENT_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_mqtt.h"
+#endif
+/*
+ * Copyright (c) 2014 Cesanta Software Limited
+ * All rights reserved
+ * This software is dual-licensed: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. For the terms of this
+ * license, see .
+ *
+ * You are free to use this software under the terms of the GNU General
+ * Public License, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * Alternatively, you can license this software under a commercial
+ * license, as set out in .
+ */
+
+/*
+ * === MQTT API reference
+ */
+
+#ifndef CS_MONGOOSE_SRC_MQTT_H_
+#define CS_MONGOOSE_SRC_MQTT_H_
+
+/* Amalgamated: #include "mg_net.h" */
+
+struct mg_mqtt_message {
+  int cmd;
+  int qos;
+  int len; /* message length in the IO buffer */
+  struct mg_str topic;
+  struct mg_str payload;
+
+  uint8_t connack_ret_code; /* connack */
+  uint16_t message_id;      /* puback */
+
+  /* connect */
+  uint8_t protocol_version;
+  uint8_t connect_flags;
+  uint16_t keep_alive_timer;
+  struct mg_str protocol_name;
+  struct mg_str client_id;
+  struct mg_str will_topic;
+  struct mg_str will_message;
+  struct mg_str user_name;
+  struct mg_str password;
+};
+
+struct mg_mqtt_topic_expression {
+  const char *topic;
+  uint8_t qos;
+};
+
+struct mg_send_mqtt_handshake_opts {
+  unsigned char flags; /* connection flags */
+  uint16_t keep_alive;
+  const char *will_topic;
+  const char *will_message;
+  const char *user_name;
+  const char *password;
+};
+
+/* mg_mqtt_proto_data should be in header to allow external access to it */
+struct mg_mqtt_proto_data {
+  uint16_t keep_alive;
+  double last_control_time;
+};
+
+/* Message types */
+#define MG_MQTT_CMD_CONNECT 1
+#define MG_MQTT_CMD_CONNACK 2
+#define MG_MQTT_CMD_PUBLISH 3
+#define MG_MQTT_CMD_PUBACK 4
+#define MG_MQTT_CMD_PUBREC 5
+#define MG_MQTT_CMD_PUBREL 6
+#define MG_MQTT_CMD_PUBCOMP 7
+#define MG_MQTT_CMD_SUBSCRIBE 8
+#define MG_MQTT_CMD_SUBACK 9
+#define MG_MQTT_CMD_UNSUBSCRIBE 10
+#define MG_MQTT_CMD_UNSUBACK 11
+#define MG_MQTT_CMD_PINGREQ 12
+#define MG_MQTT_CMD_PINGRESP 13
+#define MG_MQTT_CMD_DISCONNECT 14
+
+/* MQTT event types */
+#define MG_MQTT_EVENT_BASE 200
+#define MG_EV_MQTT_CONNECT (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_CONNECT)
+#define MG_EV_MQTT_CONNACK (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_CONNACK)
+#define MG_EV_MQTT_PUBLISH (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBLISH)
+#define MG_EV_MQTT_PUBACK (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBACK)
+#define MG_EV_MQTT_PUBREC (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBREC)
+#define MG_EV_MQTT_PUBREL (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBREL)
+#define MG_EV_MQTT_PUBCOMP (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBCOMP)
+#define MG_EV_MQTT_SUBSCRIBE (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_SUBSCRIBE)
+#define MG_EV_MQTT_SUBACK (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_SUBACK)
+#define MG_EV_MQTT_UNSUBSCRIBE (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_UNSUBSCRIBE)
+#define MG_EV_MQTT_UNSUBACK (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_UNSUBACK)
+#define MG_EV_MQTT_PINGREQ (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PINGREQ)
+#define MG_EV_MQTT_PINGRESP (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PINGRESP)
+#define MG_EV_MQTT_DISCONNECT (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_DISCONNECT)
+
+/* Message flags */
+#define MG_MQTT_RETAIN 0x1
+#define MG_MQTT_DUP 0x4
+#define MG_MQTT_QOS(qos) ((qos) << 1)
+#define MG_MQTT_GET_QOS(flags) (((flags) &0x6) >> 1)
+#define MG_MQTT_SET_QOS(flags, qos) (flags) = ((flags) & ~0x6) | ((qos) << 1)
+
+/* Connection flags */
+#define MG_MQTT_CLEAN_SESSION 0x02
+#define MG_MQTT_HAS_WILL 0x04
+#define MG_MQTT_WILL_RETAIN 0x20
+#define MG_MQTT_HAS_PASSWORD 0x40
+#define MG_MQTT_HAS_USER_NAME 0x80
+#define MG_MQTT_GET_WILL_QOS(flags) (((flags) &0x18) >> 3)
+#define MG_MQTT_SET_WILL_QOS(flags, qos) \
+  (flags) = ((flags) & ~0x18) | ((qos) << 3)
+
+/* CONNACK return codes */
+#define MG_EV_MQTT_CONNACK_ACCEPTED 0
+#define MG_EV_MQTT_CONNACK_UNACCEPTABLE_VERSION 1
+#define MG_EV_MQTT_CONNACK_IDENTIFIER_REJECTED 2
+#define MG_EV_MQTT_CONNACK_SERVER_UNAVAILABLE 3
+#define MG_EV_MQTT_CONNACK_BAD_AUTH 4
+#define MG_EV_MQTT_CONNACK_NOT_AUTHORIZED 5
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Attaches a built-in MQTT event handler to the given connection.
+ *
+ * The user-defined event handler will receive following extra events:
+ *
+ * - MG_EV_MQTT_CONNACK
+ * - MG_EV_MQTT_PUBLISH
+ * - MG_EV_MQTT_PUBACK
+ * - MG_EV_MQTT_PUBREC
+ * - MG_EV_MQTT_PUBREL
+ * - MG_EV_MQTT_PUBCOMP
+ * - MG_EV_MQTT_SUBACK
+ */
+void mg_set_protocol_mqtt(struct mg_connection *nc);
+
+/* Sends an MQTT handshake. */
+void mg_send_mqtt_handshake(struct mg_connection *nc, const char *client_id);
+
+/* Sends an MQTT handshake with optional parameters. */
+void mg_send_mqtt_handshake_opt(struct mg_connection *nc, const char *client_id,
+                                struct mg_send_mqtt_handshake_opts);
+
+/* Publishes a message to a given topic. */
+void mg_mqtt_publish(struct mg_connection *nc, const char *topic,
+                     uint16_t message_id, int flags, const void *data,
+                     size_t len);
+
+/* Subscribes to a bunch of topics. */
+void mg_mqtt_subscribe(struct mg_connection *nc,
+                       const struct mg_mqtt_topic_expression *topics,
+                       size_t topics_len, uint16_t message_id);
+
+/* Unsubscribes from a bunch of topics. */
+void mg_mqtt_unsubscribe(struct mg_connection *nc, char **topics,
+                         size_t topics_len, uint16_t message_id);
+
+/* Sends a DISCONNECT command. */
+void mg_mqtt_disconnect(struct mg_connection *nc);
+
+/* Sends a CONNACK command with a given `return_code`. */
+void mg_mqtt_connack(struct mg_connection *nc, uint8_t return_code);
+
+/* Sends a PUBACK command with a given `message_id`. */
+void mg_mqtt_puback(struct mg_connection *nc, uint16_t message_id);
+
+/* Sends a PUBREC command with a given `message_id`. */
+void mg_mqtt_pubrec(struct mg_connection *nc, uint16_t message_id);
+
+/* Sends a PUBREL command with a given `message_id`. */
+void mg_mqtt_pubrel(struct mg_connection *nc, uint16_t message_id);
+
+/* Sends a PUBCOMP command with a given `message_id`. */
+void mg_mqtt_pubcomp(struct mg_connection *nc, uint16_t message_id);
+
+/*
+ * Sends a SUBACK command with a given `message_id`
+ * and a sequence of granted QoSs.
+ */
+void mg_mqtt_suback(struct mg_connection *nc, uint8_t *qoss, size_t qoss_len,
+                    uint16_t message_id);
+
+/* Sends a UNSUBACK command with a given `message_id`. */
+void mg_mqtt_unsuback(struct mg_connection *nc, uint16_t message_id);
+
+/* Sends a PINGREQ command. */
+void mg_mqtt_ping(struct mg_connection *nc);
+
+/* Sends a PINGRESP command. */
+void mg_mqtt_pong(struct mg_connection *nc);
+
+/*
+ * Extracts the next topic expression from a SUBSCRIBE command payload.
+ *
+ * The topic expression name will point to a string in the payload buffer.
+ * Returns the pos of the next topic expression or -1 when the list
+ * of topics is exhausted.
+ */
+int mg_mqtt_next_subscribe_topic(struct mg_mqtt_message *msg,
+                                 struct mg_str *topic, uint8_t *qos, int pos);
+
+/*
+ * Matches a topic against a topic expression
+ *
+ * Returns 1 if it matches; 0 otherwise.
+ */
+int mg_mqtt_match_topic_expression(struct mg_str exp, struct mg_str topic);
+
+/*
+ * Same as `mg_mqtt_match_topic_expression()`, but takes `exp` as a
+ * NULL-terminated string.
+ */
+int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CS_MONGOOSE_SRC_MQTT_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_mqtt_server.h"
+#endif
+/*
+ * Copyright (c) 2014 Cesanta Software Limited
+ * All rights reserved
+ * This software is dual-licensed: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. For the terms of this
+ * license, see .
+ *
+ * You are free to use this software under the terms of the GNU General
+ * Public License, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * Alternatively, you can license this software under a commercial
+ * license, as set out in .
+ */
+
+/*
+ * === MQTT Server API reference
+ */
+
+#ifndef CS_MONGOOSE_SRC_MQTT_BROKER_H_
+#define CS_MONGOOSE_SRC_MQTT_BROKER_H_
+
+#if MG_ENABLE_MQTT_BROKER
+
+/* Amalgamated: #include "common/queue.h" */
+/* Amalgamated: #include "mg_mqtt.h" */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifndef MG_MQTT_MAX_SESSION_SUBSCRIPTIONS
+#define MG_MQTT_MAX_SESSION_SUBSCRIPTIONS 512
+#endif
+
+struct mg_mqtt_broker;
+
+/* MQTT session (Broker side). */
+struct mg_mqtt_session {
+  struct mg_mqtt_broker *brk;       /* Broker */
+  LIST_ENTRY(mg_mqtt_session) link; /* mg_mqtt_broker::sessions linkage */
+  struct mg_connection *nc;         /* Connection with the client */
+  size_t num_subscriptions;         /* Size of `subscriptions` array */
+  void *user_data;                  /* User data */
+  struct mg_mqtt_topic_expression *subscriptions;
+};
+
+/* MQTT broker. */
+struct mg_mqtt_broker {
+  LIST_HEAD(_mg_sesshead, mg_mqtt_session) sessions; /* Session list */
+  void *user_data;                                   /* User data */
+};
+
+/* Initialises a MQTT broker. */
+void mg_mqtt_broker_init(struct mg_mqtt_broker *brk, void *user_data);
+
+/*
+ * Processes a MQTT broker message.
+ *
+ * The listening connection expects a pointer to an initialised
+ * `mg_mqtt_broker` structure in the `user_data` field.
+ *
+ * Basic usage:
+ *
+ * ```c
+ * mg_mqtt_broker_init(&brk, NULL);
+ *
+ * if ((nc = mg_bind(&mgr, address, mg_mqtt_broker)) == NULL) {
+ *   // fail;
+ * }
+ * nc->user_data = &brk;
+ * ```
+ *
+ * New incoming connections will receive a `mg_mqtt_session` structure
+ * in the connection `user_data`. The original `user_data` will be stored
+ * in the `user_data` field of the session structure. This allows the user
+ * handler to store user data before `mg_mqtt_broker` creates the session.
+ *
+ * Since only the MG_EV_ACCEPT message is processed by the listening socket,
+ * for most events the `user_data` will thus point to a `mg_mqtt_session`.
+ */
+void mg_mqtt_broker(struct mg_connection *brk, int ev, void *data);
+
+/*
+ * Iterates over all MQTT session connections. Example:
+ *
+ * ```c
+ * struct mg_mqtt_session *s;
+ * for (s = mg_mqtt_next(brk, NULL); s != NULL; s = mg_mqtt_next(brk, s)) {
+ *   // Do something
+ * }
+ * ```
+ */
+struct mg_mqtt_session *mg_mqtt_next(struct mg_mqtt_broker *brk,
+                                     struct mg_mqtt_session *s);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* MG_ENABLE_MQTT_BROKER */
+#endif /* CS_MONGOOSE_SRC_MQTT_BROKER_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_dns.h"
+#endif
+/*
+ * Copyright (c) 2014 Cesanta Software Limited
+ * All rights reserved
+ */
+
+/*
+ * === DNS API reference
+ */
+
+#ifndef CS_MONGOOSE_SRC_DNS_H_
+#define CS_MONGOOSE_SRC_DNS_H_
+
+/* Amalgamated: #include "mg_net.h" */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define MG_DNS_A_RECORD 0x01     /* Lookup IP address */
+#define MG_DNS_CNAME_RECORD 0x05 /* Lookup CNAME */
+#define MG_DNS_PTR_RECORD 0x0c   /* Lookup PTR */
+#define MG_DNS_TXT_RECORD 0x10   /* Lookup TXT */
+#define MG_DNS_AAAA_RECORD 0x1c  /* Lookup IPv6 address */
+#define MG_DNS_SRV_RECORD 0x21   /* Lookup SRV */
+#define MG_DNS_MX_RECORD 0x0f    /* Lookup mail server for domain */
+#define MG_DNS_ANY_RECORD 0xff
+#define MG_DNS_NSEC_RECORD 0x2f
+
+#define MG_MAX_DNS_QUESTIONS 32
+#define MG_MAX_DNS_ANSWERS 32
+
+#define MG_DNS_MESSAGE 100 /* High-level DNS message event */
+
+enum mg_dns_resource_record_kind {
+  MG_DNS_INVALID_RECORD = 0,
+  MG_DNS_QUESTION,
+  MG_DNS_ANSWER
+};
+
+/* DNS resource record. */
+struct mg_dns_resource_record {
+  struct mg_str name; /* buffer with compressed name */
+  int rtype;
+  int rclass;
+  int ttl;
+  enum mg_dns_resource_record_kind kind;
+  struct mg_str rdata; /* protocol data (can be a compressed name) */
+};
+
+/* DNS message (request and response). */
+struct mg_dns_message {
+  struct mg_str pkt; /* packet body */
+  uint16_t flags;
+  uint16_t transaction_id;
+  int num_questions;
+  int num_answers;
+  struct mg_dns_resource_record questions[MG_MAX_DNS_QUESTIONS];
+  struct mg_dns_resource_record answers[MG_MAX_DNS_ANSWERS];
+};
+
+struct mg_dns_resource_record *mg_dns_next_record(
+    struct mg_dns_message *msg, int query, struct mg_dns_resource_record *prev);
+
+/*
+ * Parses the record data from a DNS resource record.
+ *
+ *  - A:     struct in_addr *ina
+ *  - AAAA:  struct in6_addr *ina
+ *  - CNAME: char buffer
+ *
+ * Returns -1 on error.
+ *
+ * TODO(mkm): MX
+ */
+int mg_dns_parse_record_data(struct mg_dns_message *msg,
+                             struct mg_dns_resource_record *rr, void *data,
+                             size_t data_len);
+
+/*
+ * Sends a DNS query to the remote end.
+ */
+void mg_send_dns_query(struct mg_connection *nc, const char *name,
+                       int query_type);
+
+/*
+ * Inserts a DNS header to an IO buffer.
+ *
+ * Returns the number of bytes inserted.
+ */
+int mg_dns_insert_header(struct mbuf *io, size_t pos,
+                         struct mg_dns_message *msg);
+
+/*
+ * Appends already encoded questions from an existing message.
+ *
+ * This is useful when generating a DNS reply message which includes
+ * all question records.
+ *
+ * Returns the number of appended bytes.
+ */
+int mg_dns_copy_questions(struct mbuf *io, struct mg_dns_message *msg);
+
+/*
+ * Encodes and appends a DNS resource record to an IO buffer.
+ *
+ * The record metadata is taken from the `rr` parameter, while the name and data
+ * are taken from the parameters, encoded in the appropriate format depending on
+ * record type and stored in the IO buffer. The encoded values might contain
+ * offsets within the IO buffer. It's thus important that the IO buffer doesn't
+ * get trimmed while a sequence of records are encoded while preparing a DNS
+ * reply.
+ *
+ * This function doesn't update the `name` and `rdata` pointers in the `rr`
+ * struct because they might be invalidated as soon as the IO buffer grows
+ * again.
+ *
+ * Returns the number of bytes appended or -1 in case of error.
+ */
+int mg_dns_encode_record(struct mbuf *io, struct mg_dns_resource_record *rr,
+                         const char *name, size_t nlen, const void *rdata,
+                         size_t rlen);
+
+/*
+ * Encodes a DNS name.
+ */
+int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len);
+
+/* Low-level: parses a DNS response. */
+int mg_parse_dns(const char *buf, int len, struct mg_dns_message *msg);
+
+/*
+ * Uncompresses a DNS compressed name.
+ *
+ * The containing DNS message is required because of the compressed encoding
+ * and reference suffixes present elsewhere in the packet.
+ *
+ * If the name is less than `dst_len` characters long, the remainder
+ * of `dst` is terminated with `\0` characters. Otherwise, `dst` is not
+ * terminated.
+ *
+ * If `dst_len` is 0 `dst` can be NULL.
+ * Returns the uncompressed name length.
+ */
+size_t mg_dns_uncompress_name(struct mg_dns_message *msg, struct mg_str *name,
+                              char *dst, int dst_len);
+
+/*
+ * Attaches a built-in DNS event handler to the given listening connection.
+ *
+ * The DNS event handler parses the incoming UDP packets, treating them as DNS
+ * requests. If an incoming packet gets successfully parsed by the DNS event
+ * handler, a user event handler will receive an `MG_DNS_REQUEST` event, with
+ * `ev_data` pointing to the parsed `struct mg_dns_message`.
+ *
+ * See
+ * [captive_dns_server](https://github.com/cesanta/mongoose/tree/master/examples/captive_dns_server)
+ * example on how to handle DNS request and send DNS reply.
+ */
+void mg_set_protocol_dns(struct mg_connection *nc);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* CS_MONGOOSE_SRC_DNS_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_dns_server.h"
+#endif
+/*
+ * Copyright (c) 2014 Cesanta Software Limited
+ * All rights reserved
+ */
+
+/*
+ * === DNS server API reference
+ *
+ * Disabled by default; enable with `-DMG_ENABLE_DNS_SERVER`.
+ */
+
+#ifndef CS_MONGOOSE_SRC_DNS_SERVER_H_
+#define CS_MONGOOSE_SRC_DNS_SERVER_H_
+
+#if MG_ENABLE_DNS_SERVER
+
+/* Amalgamated: #include "mg_dns.h" */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define MG_DNS_SERVER_DEFAULT_TTL 3600
+
+struct mg_dns_reply {
+  struct mg_dns_message *msg;
+  struct mbuf *io;
+  size_t start;
+};
+
+/*
+ * Creates a DNS reply.
+ *
+ * The reply will be based on an existing query message `msg`.
+ * The query body will be appended to the output buffer.
+ * "reply + recursion allowed" will be added to the message flags and the
+ * message's num_answers will be set to 0.
+ *
+ * Answer records can be appended with `mg_dns_send_reply` or by lower
+ * level function defined in the DNS API.
+ *
+ * In order to send a reply use `mg_dns_send_reply`.
+ * It's possible to use a connection's send buffer as reply buffer,
+ * and it will work for both UDP and TCP connections.
+ *
+ * Example:
+ *
+ * ```c
+ * reply = mg_dns_create_reply(&nc->send_mbuf, msg);
+ * for (i = 0; i < msg->num_questions; i++) {
+ *   rr = &msg->questions[i];
+ *   if (rr->rtype == MG_DNS_A_RECORD) {
+ *     mg_dns_reply_record(&reply, rr, 3600, &dummy_ip_addr, 4);
+ *   }
+ * }
+ * mg_dns_send_reply(nc, &reply);
+ * ```
+ */
+struct mg_dns_reply mg_dns_create_reply(struct mbuf *io,
+                                        struct mg_dns_message *msg);
+
+/*
+ * Appends a DNS reply record to the IO buffer and to the DNS message.
+ *
+ * The message's num_answers field will be incremented. It's the caller's duty
+ * to ensure num_answers is properly initialised.
+ *
+ * Returns -1 on error.
+ */
+int mg_dns_reply_record(struct mg_dns_reply *reply,
+                        struct mg_dns_resource_record *question,
+                        const char *name, int rtype, int ttl, const void *rdata,
+                        size_t rdata_len);
+
+/*
+ * Sends a DNS reply through a connection.
+ *
+ * The DNS data is stored in an IO buffer pointed by reply structure in `r`.
+ * This function mutates the content of that buffer in order to ensure that
+ * the DNS header reflects the size and flags of the message, that might have
+ * been updated either with `mg_dns_reply_record` or by direct manipulation of
+ * `r->message`.
+ *
+ * Once sent, the IO buffer will be trimmed unless the reply IO buffer
+ * is the connection's send buffer and the connection is not in UDP mode.
+ */
+void mg_dns_send_reply(struct mg_connection *nc, struct mg_dns_reply *r);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* MG_ENABLE_DNS_SERVER */
+#endif /* CS_MONGOOSE_SRC_DNS_SERVER_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_resolv.h"
+#endif
+/*
+ * Copyright (c) 2014 Cesanta Software Limited
+ * All rights reserved
+ */
+
+/*
+ * === API reference
+ */
+
+#ifndef CS_MONGOOSE_SRC_RESOLV_H_
+#define CS_MONGOOSE_SRC_RESOLV_H_
+
+/* Amalgamated: #include "mg_dns.h" */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+enum mg_resolve_err {
+  MG_RESOLVE_OK = 0,
+  MG_RESOLVE_NO_ANSWERS = 1,
+  MG_RESOLVE_EXCEEDED_RETRY_COUNT = 2,
+  MG_RESOLVE_TIMEOUT = 3
+};
+
+typedef void (*mg_resolve_callback_t)(struct mg_dns_message *dns_message,
+                                      void *user_data, enum mg_resolve_err);
+
+/* Options for `mg_resolve_async_opt`. */
+struct mg_resolve_async_opts {
+  const char *nameserver;
+  int max_retries;    /* defaults to 2 if zero */
+  int timeout;        /* in seconds; defaults to 5 if zero */
+  int accept_literal; /* pseudo-resolve literal ipv4 and ipv6 addrs */
+  int only_literal;   /* only resolves literal addrs; sync cb invocation */
+  struct mg_connection **dns_conn; /* return DNS connection */
+};
+
+/* See `mg_resolve_async_opt()` */
+int mg_resolve_async(struct mg_mgr *mgr, const char *name, int query,
+                     mg_resolve_callback_t cb, void *data);
+
+/* Set default DNS server */
+void mg_set_nameserver(struct mg_mgr *mgr, const char *nameserver);
+
+/*
+ * Resolved a DNS name asynchronously.
+ *
+ * Upon successful resolution, the user callback will be invoked
+ * with the full DNS response message and a pointer to the user's
+ * context `data`.
+ *
+ * In case of timeout while performing the resolution the callback
+ * will receive a NULL `msg`.
+ *
+ * The DNS answers can be extracted with `mg_next_record` and
+ * `mg_dns_parse_record_data`:
+ *
+ * [source,c]
+ * ----
+ * struct in_addr ina;
+ * struct mg_dns_resource_record *rr = mg_next_record(msg, MG_DNS_A_RECORD,
+ *   NULL);
+ * mg_dns_parse_record_data(msg, rr, &ina, sizeof(ina));
+ * ----
+ */
+int mg_resolve_async_opt(struct mg_mgr *mgr, const char *name, int query,
+                         mg_resolve_callback_t cb, void *data,
+                         struct mg_resolve_async_opts opts);
+
+/*
+ * Resolve a name from `/etc/hosts`.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int mg_resolve_from_hosts_file(const char *host, union socket_address *usa);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* CS_MONGOOSE_SRC_RESOLV_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_coap.h"
+#endif
+/*
+ * Copyright (c) 2015 Cesanta Software Limited
+ * All rights reserved
+ * This software is dual-licensed: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. For the terms of this
+ * license, see .
+ *
+ * You are free to use this software under the terms of the GNU General
+ * Public License, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * Alternatively, you can license this software under a commercial
+ * license, as set out in .
+ */
+
+/*
+ * === CoAP API reference
+ *
+ * CoAP message format:
+ *
+ * ```
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+ * |Ver| T | TKL | Code | Message ID | Token (if any, TKL bytes) ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+ * | Options (if any) ...            |1 1 1 1 1 1 1 1| Payload (if any) ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+ * ```
+ */
+
+#ifndef CS_MONGOOSE_SRC_COAP_H_
+#define CS_MONGOOSE_SRC_COAP_H_
+
+#if MG_ENABLE_COAP
+
+#define MG_COAP_MSG_TYPE_FIELD 0x2
+#define MG_COAP_CODE_CLASS_FIELD 0x4
+#define MG_COAP_CODE_DETAIL_FIELD 0x8
+#define MG_COAP_MSG_ID_FIELD 0x10
+#define MG_COAP_TOKEN_FIELD 0x20
+#define MG_COAP_OPTIOMG_FIELD 0x40
+#define MG_COAP_PAYLOAD_FIELD 0x80
+
+#define MG_COAP_ERROR 0x10000
+#define MG_COAP_FORMAT_ERROR (MG_COAP_ERROR | 0x20000)
+#define MG_COAP_IGNORE (MG_COAP_ERROR | 0x40000)
+#define MG_COAP_NOT_ENOUGH_DATA (MG_COAP_ERROR | 0x80000)
+#define MG_COAP_NETWORK_ERROR (MG_COAP_ERROR | 0x100000)
+
+#define MG_COAP_MSG_CON 0
+#define MG_COAP_MSG_NOC 1
+#define MG_COAP_MSG_ACK 2
+#define MG_COAP_MSG_RST 3
+#define MG_COAP_MSG_MAX 3
+
+#define MG_COAP_CODECLASS_REQUEST 0
+#define MG_COAP_CODECLASS_RESP_OK 2
+#define MG_COAP_CODECLASS_CLIENT_ERR 4
+#define MG_COAP_CODECLASS_SRV_ERR 5
+
+#define MG_COAP_EVENT_BASE 300
+#define MG_EV_COAP_CON (MG_COAP_EVENT_BASE + MG_COAP_MSG_CON)
+#define MG_EV_COAP_NOC (MG_COAP_EVENT_BASE + MG_COAP_MSG_NOC)
+#define MG_EV_COAP_ACK (MG_COAP_EVENT_BASE + MG_COAP_MSG_ACK)
+#define MG_EV_COAP_RST (MG_COAP_EVENT_BASE + MG_COAP_MSG_RST)
+
+/*
+ * CoAP options.
+ * Use mg_coap_add_option and mg_coap_free_options
+ * for creation and destruction.
+ */
+struct mg_coap_option {
+  struct mg_coap_option *next;
+  uint32_t number;
+  struct mg_str value;
+};
+
+/* CoAP message. See RFC 7252 for details. */
+struct mg_coap_message {
+  uint32_t flags;
+  uint8_t msg_type;
+  uint8_t code_class;
+  uint8_t code_detail;
+  uint16_t msg_id;
+  struct mg_str token;
+  struct mg_coap_option *options;
+  struct mg_str payload;
+  struct mg_coap_option *optiomg_tail;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Sets CoAP protocol handler - triggers CoAP specific events. */
+int mg_set_protocol_coap(struct mg_connection *nc);
+
+/*
+ * Adds a new option to mg_coap_message structure.
+ * Returns pointer to the newly created option.
+ * Note: options must be freed by using mg_coap_free_options
+ */
+struct mg_coap_option *mg_coap_add_option(struct mg_coap_message *cm,
+                                          uint32_t number, char *value,
+                                          size_t len);
+
+/*
+ * Frees the memory allocated for options.
+ * If the cm parameter doesn't contain any option it does nothing.
+ */
+void mg_coap_free_options(struct mg_coap_message *cm);
+
+/*
+ * Composes a CoAP message from `mg_coap_message`
+ * and sends it into `nc` connection.
+ * Returns 0 on success. On error, it is a bitmask:
+ *
+ * - `#define MG_COAP_ERROR 0x10000`
+ * - `#define MG_COAP_FORMAT_ERROR (MG_COAP_ERROR | 0x20000)`
+ * - `#define MG_COAP_IGNORE (MG_COAP_ERROR | 0x40000)`
+ * - `#define MG_COAP_NOT_ENOUGH_DATA (MG_COAP_ERROR | 0x80000)`
+ * - `#define MG_COAP_NETWORK_ERROR (MG_COAP_ERROR | 0x100000)`
+ */
+uint32_t mg_coap_send_message(struct mg_connection *nc,
+                              struct mg_coap_message *cm);
+
+/*
+ * Composes CoAP acknowledgement from `mg_coap_message`
+ * and sends it into `nc` connection.
+ * Return value: see `mg_coap_send_message()`
+ */
+uint32_t mg_coap_send_ack(struct mg_connection *nc, uint16_t msg_id);
+
+/*
+ * Parses CoAP message and fills mg_coap_message and returns cm->flags.
+ * This is a helper function.
+ *
+ * NOTE: usually CoAP works over UDP, so lack of data means format error.
+ * But, in theory, it is possible to use CoAP over TCP (according to RFC)
+ *
+ * The caller has to check results and treat COAP_NOT_ENOUGH_DATA according to
+ * underlying protocol:
+ *
+ * - in case of UDP COAP_NOT_ENOUGH_DATA means COAP_FORMAT_ERROR,
+ * - in case of TCP client can try to receive more data
+ *
+ * Return value: see `mg_coap_send_message()`
+ */
+uint32_t mg_coap_parse(struct mbuf *io, struct mg_coap_message *cm);
+
+/*
+ * Composes CoAP message from mg_coap_message structure.
+ * This is a helper function.
+ * Return value: see `mg_coap_send_message()`
+ */
+uint32_t mg_coap_compose(struct mg_coap_message *cm, struct mbuf *io);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* MG_ENABLE_COAP */
+
+#endif /* CS_MONGOOSE_SRC_COAP_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_sntp.h"
+#endif
+/*
+ * Copyright (c) 2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_MONGOOSE_SRC_SNTP_H_
+#define CS_MONGOOSE_SRC_SNTP_H_
+
+#if MG_ENABLE_SNTP
+
+#define MG_SNTP_EVENT_BASE 500
+
+/*
+ * Received reply from time server. Event handler parameter contains
+ * pointer to mg_sntp_message structure
+ */
+#define MG_SNTP_REPLY (MG_SNTP_EVENT_BASE + 1)
+
+/* Received malformed SNTP packet */
+#define MG_SNTP_MALFORMED_REPLY (MG_SNTP_EVENT_BASE + 2)
+
+/* Failed to get time from server (timeout etc) */
+#define MG_SNTP_FAILED (MG_SNTP_EVENT_BASE + 3)
+
+struct mg_sntp_message {
+  /* if server sends this flags, user should not send requests to it */
+  int kiss_of_death;
+  /* usual mg_time */
+  double time;
+};
+
+/* Establishes connection to given sntp server */
+struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr,
+                                      MG_CB(mg_event_handler_t event_handler,
+                                            void *user_data),
+                                      const char *sntp_server_name);
+
+/* Sends time request to given connection */
+void mg_sntp_send_request(struct mg_connection *c);
+
+/*
+ * Helper function
+ * Establishes connection to time server, tries to send request
+ * repeats sending SNTP_ATTEMPTS times every SNTP_TIMEOUT sec
+ * (if needed)
+ * See sntp_client example
+ */
+struct mg_connection *mg_sntp_get_time(struct mg_mgr *mgr,
+                                       mg_event_handler_t event_handler,
+                                       const char *sntp_server_name);
+
+#endif
+
+#endif /* CS_MONGOOSE_SRC_SNTP_H_ */
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_socks.h"
+#endif
+/*
+ * Copyright (c) 2017 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_MONGOOSE_SRC_SOCKS_H_
+#define CS_MONGOOSE_SRC_SOCKS_H_
+
+#if MG_ENABLE_SOCKS
+
+#define MG_SOCKS_VERSION 5
+
+#define MG_SOCKS_HANDSHAKE_DONE MG_F_USER_1
+#define MG_SOCKS_CONNECT_DONE MG_F_USER_2
+
+/* SOCKS5 handshake methods */
+enum mg_socks_handshake_method {
+  MG_SOCKS_HANDSHAKE_NOAUTH = 0,     /* Handshake method - no authentication */
+  MG_SOCKS_HANDSHAKE_GSSAPI = 1,     /* Handshake method - GSSAPI auth */
+  MG_SOCKS_HANDSHAKE_USERPASS = 2,   /* Handshake method - user/password auth */
+  MG_SOCKS_HANDSHAKE_FAILURE = 0xff, /* Handshake method - failure */
+};
+
+/* SOCKS5 commands */
+enum mg_socks_command {
+  MG_SOCKS_CMD_CONNECT = 1,       /* Command: CONNECT */
+  MG_SOCKS_CMD_BIND = 2,          /* Command: BIND */
+  MG_SOCKS_CMD_UDP_ASSOCIATE = 3, /* Command: UDP ASSOCIATE */
+};
+
+/* SOCKS5 address types */
+enum mg_socks_address_type {
+  MG_SOCKS_ADDR_IPV4 = 1,   /* Address type: IPv4 */
+  MG_SOCKS_ADDR_DOMAIN = 3, /* Address type: Domain name */
+  MG_SOCKS_ADDR_IPV6 = 4,   /* Address type: IPv6 */
+};
+
+/* SOCKS5 response codes */
+enum mg_socks_response {
+  MG_SOCKS_SUCCESS = 0,            /* Response: success */
+  MG_SOCKS_FAILURE = 1,            /* Response: failure */
+  MG_SOCKS_NOT_ALLOWED = 2,        /* Response: connection not allowed */
+  MG_SOCKS_NET_UNREACHABLE = 3,    /* Response: network unreachable */
+  MG_SOCKS_HOST_UNREACHABLE = 4,   /* Response: network unreachable */
+  MG_SOCKS_CONN_REFUSED = 5,       /* Response: network unreachable */
+  MG_SOCKS_TTL_EXPIRED = 6,        /* Response: network unreachable */
+  MG_SOCKS_CMD_NOT_SUPPORTED = 7,  /* Response: network unreachable */
+  MG_SOCKS_ADDR_NOT_SUPPORTED = 8, /* Response: network unreachable */
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Turn the connection into the SOCKS server */
+void mg_set_protocol_socks(struct mg_connection *c);
+
+/* Create socks tunnel for the client connection */
+struct mg_iface *mg_socks_mk_iface(struct mg_mgr *, const char *proxy_addr);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+#endif
diff --git a/src/mongoose-6.11/src/CPPLINT.cfg b/src/mongoose-6.11/src/CPPLINT.cfg
new file mode 100644
index 0000000..6809e55
--- /dev/null
+++ b/src/mongoose-6.11/src/CPPLINT.cfg
@@ -0,0 +1 @@
+exclude_files=sha1\.c
diff --git a/src/mongoose-6.11/src/common/cs_dbg.c b/src/mongoose-6.11/src/common/cs_dbg.c
new file mode 100644
index 0000000..da1bc2e
--- /dev/null
+++ b/src/mongoose-6.11/src/common/cs_dbg.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#include "common/cs_dbg.h"
+
+#include 
+#include 
+#include 
+
+#include "common/cs_time.h"
+#include "common/str_util.h"
+
+enum cs_log_level cs_log_threshold WEAK =
+#if CS_ENABLE_DEBUG
+    LL_VERBOSE_DEBUG;
+#else
+    LL_ERROR;
+#endif
+
+static char *s_filter_pattern = NULL;
+static size_t s_filter_pattern_len;
+
+void cs_log_set_filter(const char *pattern) WEAK;
+
+#if CS_ENABLE_STDIO
+
+FILE *cs_log_file WEAK = NULL;
+
+#if CS_LOG_ENABLE_TS_DIFF
+double cs_log_ts WEAK;
+#endif
+
+enum cs_log_level cs_log_cur_msg_level WEAK = LL_NONE;
+
+void cs_log_set_filter(const char *pattern) {
+  free(s_filter_pattern);
+  if (pattern != NULL) {
+    s_filter_pattern = strdup(pattern);
+    s_filter_pattern_len = strlen(pattern);
+  } else {
+    s_filter_pattern = NULL;
+    s_filter_pattern_len = 0;
+  }
+}
+
+int cs_log_print_prefix(enum cs_log_level, const char *, const char *) WEAK;
+int cs_log_print_prefix(enum cs_log_level level, const char *func,
+                        const char *filename) {
+  char prefix[21];
+
+  if (level > cs_log_threshold) return 0;
+  if (s_filter_pattern != NULL &&
+      mg_match_prefix(s_filter_pattern, s_filter_pattern_len, func) == 0 &&
+      mg_match_prefix(s_filter_pattern, s_filter_pattern_len, filename) == 0) {
+    return 0;
+  }
+
+  strncpy(prefix, func, 20);
+  prefix[20] = '\0';
+  if (cs_log_file == NULL) cs_log_file = stderr;
+  cs_log_cur_msg_level = level;
+  fprintf(cs_log_file, "%-20s ", prefix);
+#if CS_LOG_ENABLE_TS_DIFF
+  {
+    double now = cs_time();
+    fprintf(cs_log_file, "%7u ", (unsigned int) ((now - cs_log_ts) * 1000000));
+    cs_log_ts = now;
+  }
+#endif
+  return 1;
+}
+
+void cs_log_printf(const char *fmt, ...) WEAK;
+void cs_log_printf(const char *fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  vfprintf(cs_log_file, fmt, ap);
+  va_end(ap);
+  fputc('\n', cs_log_file);
+  fflush(cs_log_file);
+  cs_log_cur_msg_level = LL_NONE;
+}
+
+void cs_log_set_file(FILE *file) WEAK;
+void cs_log_set_file(FILE *file) {
+  cs_log_file = file;
+}
+
+#else
+
+void cs_log_set_filter(const char *pattern) {
+  (void) pattern;
+}
+
+#endif /* CS_ENABLE_STDIO */
+
+void cs_log_set_level(enum cs_log_level level) WEAK;
+void cs_log_set_level(enum cs_log_level level) {
+  cs_log_threshold = level;
+#if CS_LOG_ENABLE_TS_DIFF && CS_ENABLE_STDIO
+  cs_log_ts = cs_time();
+#endif
+}
diff --git a/src/mongoose-6.11/src/common/cs_dbg.h b/src/mongoose-6.11/src/common/cs_dbg.h
new file mode 100644
index 0000000..24a7022
--- /dev/null
+++ b/src/mongoose-6.11/src/common/cs_dbg.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_CS_DBG_H_
+#define CS_COMMON_CS_DBG_H_
+
+#include "common/platform.h"
+
+#if CS_ENABLE_STDIO
+#include 
+#endif
+
+#ifndef CS_ENABLE_DEBUG
+#define CS_ENABLE_DEBUG 0
+#endif
+
+#ifndef CS_LOG_ENABLE_TS_DIFF
+#define CS_LOG_ENABLE_TS_DIFF 0
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Log level; `LL_INFO` is the default. Use `cs_log_set_level()` to change it.
+ */
+enum cs_log_level {
+  LL_NONE = -1,
+  LL_ERROR = 0,
+  LL_WARN = 1,
+  LL_INFO = 2,
+  LL_DEBUG = 3,
+  LL_VERBOSE_DEBUG = 4,
+
+  _LL_MIN = -2,
+  _LL_MAX = 5,
+};
+
+/*
+ * Set max log level to print; messages with the level above the given one will
+ * not be printed.
+ */
+void cs_log_set_level(enum cs_log_level level);
+
+/*
+ * Set log filter. NULL (a default) logs everything.
+ * Otherwise, function name and file name will be tested against the given
+ * pattern, and only matching messages will be printed.
+ *
+ * For the pattern syntax, refer to `mg_match_prefix()` in `str_util.h`.
+ *
+ * Example:
+ * ```c
+ * void foo(void) {
+ *   LOG(LL_INFO, ("hello from foo"));
+ * }
+ *
+ * void bar(void) {
+ *   LOG(LL_INFO, ("hello from bar"));
+ * }
+ *
+ * void test(void) {
+ *   cs_log_set_filter(NULL);
+ *   foo();
+ *   bar();
+ *
+ *   cs_log_set_filter("f*");
+ *   foo();
+ *   bar(); // Will NOT print anything
+ *
+ *   cs_log_set_filter("bar");
+ *   foo(); // Will NOT print anything
+ *   bar();
+ * }
+ * ```
+ */
+void cs_log_set_filter(const char *pattern);
+
+/*
+ * Helper function which prints message prefix with the given `level`, function
+ * name `func` and `filename`. If message should be printed (accordingly to the
+ * current log level and filter), prints the prefix and returns 1, otherwise
+ * returns 0.
+ *
+ * Clients should typically just use `LOG()` macro.
+ */
+int cs_log_print_prefix(enum cs_log_level level, const char *func,
+                        const char *filename);
+
+extern enum cs_log_level cs_log_threshold;
+
+#if CS_ENABLE_STDIO
+
+/*
+ * Set file to write logs into. If `NULL`, logs go to `stderr`.
+ */
+void cs_log_set_file(FILE *file);
+
+/*
+ * Prints log to the current log file, appends "\n" in the end and flushes the
+ * stream.
+ */
+void cs_log_printf(const char *fmt, ...)
+#ifdef __GNUC__
+    __attribute__((format(printf, 1, 2)))
+#endif
+    ;
+
+/*
+ * Format and print message `x` with the given level `l`. Example:
+ *
+ * ```c
+ * LOG(LL_INFO, ("my info message: %d", 123));
+ * LOG(LL_DEBUG, ("my debug message: %d", 123));
+ * ```
+ */
+#define LOG(l, x)                                                    \
+  do {                                                               \
+    if (cs_log_print_prefix(l, __func__, __FILE__)) cs_log_printf x; \
+  } while (0)
+
+#ifndef CS_NDEBUG
+
+/*
+ * Shortcut for `LOG(LL_VERBOSE_DEBUG, (...))`
+ */
+#define DBG(x) LOG(LL_VERBOSE_DEBUG, x)
+
+#else /* NDEBUG */
+
+#define DBG(x)
+
+#endif
+
+#else /* CS_ENABLE_STDIO */
+
+#define LOG(l, x)
+#define DBG(x)
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CS_COMMON_CS_DBG_H_ */
diff --git a/src/mongoose-6.11/src/common/cs_md5.c b/src/mongoose-6.11/src/common/cs_md5.c
new file mode 100644
index 0000000..f8c8b68
--- /dev/null
+++ b/src/mongoose-6.11/src/common/cs_md5.c
@@ -0,0 +1,211 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#include "common/cs_md5.h"
+#include "common/str_util.h"
+
+#if !defined(EXCLUDE_COMMON)
+#if !CS_DISABLE_MD5
+
+#include "common/cs_endian.h"
+
+static void byteReverse(unsigned char *buf, unsigned longs) {
+/* Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN */
+#if BYTE_ORDER == BIG_ENDIAN
+  do {
+    uint32_t t = (uint32_t)((unsigned) buf[3] << 8 | buf[2]) << 16 |
+                 ((unsigned) buf[1] << 8 | buf[0]);
+    *(uint32_t *) buf = t;
+    buf += 4;
+  } while (--longs);
+#else
+  (void) buf;
+  (void) longs;
+#endif
+}
+
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+#define MD5STEP(f, w, x, y, z, data, s) \
+  (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x)
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void cs_md5_init(cs_md5_ctx *ctx) {
+  ctx->buf[0] = 0x67452301;
+  ctx->buf[1] = 0xefcdab89;
+  ctx->buf[2] = 0x98badcfe;
+  ctx->buf[3] = 0x10325476;
+
+  ctx->bits[0] = 0;
+  ctx->bits[1] = 0;
+}
+
+static void cs_md5_transform(uint32_t buf[4], uint32_t const in[16]) {
+  register uint32_t a, b, c, d;
+
+  a = buf[0];
+  b = buf[1];
+  c = buf[2];
+  d = buf[3];
+
+  MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+  MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+  MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+  MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+  MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+  MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+  MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+  MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+  MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+  MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+  MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+  MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+  MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+  MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+  MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+  MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+  MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+  MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+  MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+  MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+  MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+  MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+  MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+  MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+  MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+  MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+  MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+  MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+  MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+  MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+  MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+  MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+  MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+  MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+  MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+  MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+  MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+  MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+  MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+  MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+  MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+  MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+  MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+  MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+  MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+  MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+  MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+  MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+  MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+  MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+  MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+  MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+  MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+  MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+  MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+  MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+  MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+  MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+  MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+  MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+  MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+  MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+  MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+  MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+  buf[0] += a;
+  buf[1] += b;
+  buf[2] += c;
+  buf[3] += d;
+}
+
+void cs_md5_update(cs_md5_ctx *ctx, const unsigned char *buf, size_t len) {
+  uint32_t t;
+
+  t = ctx->bits[0];
+  if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) ctx->bits[1]++;
+  ctx->bits[1] += (uint32_t) len >> 29;
+
+  t = (t >> 3) & 0x3f;
+
+  if (t) {
+    unsigned char *p = (unsigned char *) ctx->in + t;
+
+    t = 64 - t;
+    if (len < t) {
+      memcpy(p, buf, len);
+      return;
+    }
+    memcpy(p, buf, t);
+    byteReverse(ctx->in, 16);
+    cs_md5_transform(ctx->buf, (uint32_t *) ctx->in);
+    buf += t;
+    len -= t;
+  }
+
+  while (len >= 64) {
+    memcpy(ctx->in, buf, 64);
+    byteReverse(ctx->in, 16);
+    cs_md5_transform(ctx->buf, (uint32_t *) ctx->in);
+    buf += 64;
+    len -= 64;
+  }
+
+  memcpy(ctx->in, buf, len);
+}
+
+void cs_md5_final(unsigned char digest[16], cs_md5_ctx *ctx) {
+  unsigned count;
+  unsigned char *p;
+  uint32_t *a;
+
+  count = (ctx->bits[0] >> 3) & 0x3F;
+
+  p = ctx->in + count;
+  *p++ = 0x80;
+  count = 64 - 1 - count;
+  if (count < 8) {
+    memset(p, 0, count);
+    byteReverse(ctx->in, 16);
+    cs_md5_transform(ctx->buf, (uint32_t *) ctx->in);
+    memset(ctx->in, 0, 56);
+  } else {
+    memset(p, 0, count - 8);
+  }
+  byteReverse(ctx->in, 14);
+
+  a = (uint32_t *) ctx->in;
+  a[14] = ctx->bits[0];
+  a[15] = ctx->bits[1];
+
+  cs_md5_transform(ctx->buf, (uint32_t *) ctx->in);
+  byteReverse((unsigned char *) ctx->buf, 4);
+  memcpy(digest, ctx->buf, 16);
+  memset((char *) ctx, 0, sizeof(*ctx));
+}
+
+#endif /* CS_DISABLE_MD5 */
+#endif /* EXCLUDE_COMMON */
diff --git a/src/mongoose-6.11/src/common/cs_md5.h b/src/mongoose-6.11/src/common/cs_md5.h
new file mode 100644
index 0000000..1450fd2
--- /dev/null
+++ b/src/mongoose-6.11/src/common/cs_md5.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_MD5_H_
+#define CS_COMMON_MD5_H_
+
+#include "common/platform.h"
+
+#ifndef CS_DISABLE_MD5
+#define CS_DISABLE_MD5 0
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct {
+  uint32_t buf[4];
+  uint32_t bits[2];
+  unsigned char in[64];
+} cs_md5_ctx;
+
+void cs_md5_init(cs_md5_ctx *c);
+void cs_md5_update(cs_md5_ctx *c, const unsigned char *data, size_t len);
+void cs_md5_final(unsigned char *md, cs_md5_ctx *c);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CS_COMMON_MD5_H_ */
diff --git a/src/mongoose-6.11/src/common/cs_time.c b/src/mongoose-6.11/src/common/cs_time.c
new file mode 100644
index 0000000..f21845c
--- /dev/null
+++ b/src/mongoose-6.11/src/common/cs_time.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#include "common/cs_time.h"
+
+#ifndef _WIN32
+#include 
+/*
+ * There is no sys/time.h on ARMCC.
+ */
+#if !(defined(__ARMCC_VERSION) || defined(__ICCARM__)) && \
+    !defined(__TI_COMPILER_VERSION__) &&                  \
+    (!defined(CS_PLATFORM) || CS_PLATFORM != CS_P_NXP_LPC)
+#include 
+#endif
+#else
+#include 
+#endif
+
+double cs_time(void) WEAK;
+double cs_time(void) {
+  double now;
+#ifndef _WIN32
+  struct timeval tv;
+  if (gettimeofday(&tv, NULL /* tz */) != 0) return 0;
+  now = (double) tv.tv_sec + (((double) tv.tv_usec) / 1000000.0);
+#else
+  SYSTEMTIME sysnow;
+  FILETIME ftime;
+  GetLocalTime(&sysnow);
+  SystemTimeToFileTime(&sysnow, &ftime);
+  /*
+   * 1. VC 6.0 doesn't support conversion uint64 -> double, so, using int64
+   * This should not cause a problems in this (21th) century
+   * 2. Windows FILETIME is a number of 100-nanosecond intervals since January
+   * 1, 1601 while time_t is a number of _seconds_ since January 1, 1970 UTC,
+   * thus, we need to convert to seconds and adjust amount (subtract 11644473600
+   * seconds)
+   */
+  now = (double) (((int64_t) ftime.dwLowDateTime +
+                   ((int64_t) ftime.dwHighDateTime << 32)) /
+                  10000000.0) -
+        11644473600;
+#endif /* _WIN32 */
+  return now;
+}
+
+double cs_timegm(const struct tm *tm) {
+  /* Month-to-day offset for non-leap-years. */
+  static const int month_day[12] = {0,   31,  59,  90,  120, 151,
+                                    181, 212, 243, 273, 304, 334};
+
+  /* Most of the calculation is easy; leap years are the main difficulty. */
+  int month = tm->tm_mon % 12;
+  int year = tm->tm_year + tm->tm_mon / 12;
+  int year_for_leap;
+  int64_t rt;
+
+  if (month < 0) { /* Negative values % 12 are still negative. */
+    month += 12;
+    --year;
+  }
+
+  /* This is the number of Februaries since 1900. */
+  year_for_leap = (month > 1) ? year + 1 : year;
+
+  rt =
+      tm->tm_sec /* Seconds */
+      +
+      60 *
+          (tm->tm_min /* Minute = 60 seconds */
+           +
+           60 * (tm->tm_hour /* Hour = 60 minutes */
+                 +
+                 24 * (month_day[month] + tm->tm_mday - 1 /* Day = 24 hours */
+                       + 365 * (year - 70)                /* Year = 365 days */
+                       + (year_for_leap - 69) / 4 /* Every 4 years is leap... */
+                       - (year_for_leap - 1) / 100 /* Except centuries... */
+                       + (year_for_leap + 299) / 400))); /* Except 400s. */
+  return rt < 0 ? -1 : (double) rt;
+}
diff --git a/src/mongoose-6.11/src/common/cs_time.h b/src/mongoose-6.11/src/common/cs_time.h
new file mode 100644
index 0000000..3b59073
--- /dev/null
+++ b/src/mongoose-6.11/src/common/cs_time.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_CS_TIME_H_
+#define CS_COMMON_CS_TIME_H_
+
+#include 
+
+#include "common/platform.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Sub-second granularity time(). */
+double cs_time(void);
+
+/*
+ * Similar to (non-standard) timegm, converts broken-down time into the number
+ * of seconds since Unix Epoch.
+ */
+double cs_timegm(const struct tm *tm);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CS_COMMON_CS_TIME_H_ */
diff --git a/src/mongoose-6.11/src/common/mg_mem.h b/src/mongoose-6.11/src/common/mg_mem.h
new file mode 100644
index 0000000..65d71c7
--- /dev/null
+++ b/src/mongoose-6.11/src/common/mg_mem.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#ifndef CS_COMMON_MG_MEM_H_
+#define CS_COMMON_MG_MEM_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef MG_MALLOC
+#define MG_MALLOC malloc
+#endif
+
+#ifndef MG_CALLOC
+#define MG_CALLOC calloc
+#endif
+
+#ifndef MG_REALLOC
+#define MG_REALLOC realloc
+#endif
+
+#ifndef MG_FREE
+#define MG_FREE free
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CS_COMMON_MG_MEM_H_ */
diff --git a/src/mongoose-6.11/src/common/platform.h b/src/mongoose-6.11/src/common/platform.h
new file mode 100644
index 0000000..b517cab
--- /dev/null
+++ b/src/mongoose-6.11/src/common/platform.h
@@ -0,0 +1,121 @@
+#ifndef CS_COMMON_PLATFORM_H_
+#define CS_COMMON_PLATFORM_H_
+
+/*
+ * For the "custom" platform, includes and dependencies can be
+ * provided through mg_locals.h.
+ */
+#define CS_P_CUSTOM 0
+#define CS_P_UNIX 1
+#define CS_P_WINDOWS 2
+#define CS_P_ESP32 15
+#define CS_P_ESP8266 3
+#define CS_P_CC3100 6
+#define CS_P_CC3200 4
+#define CS_P_CC3220 17
+#define CS_P_MSP432 5
+#define CS_P_TM4C129 14
+#define CS_P_MBED 7
+#define CS_P_WINCE 8
+#define CS_P_NXP_LPC 13
+#define CS_P_NXP_KINETIS 9
+#define CS_P_NRF51 12
+#define CS_P_NRF52 10
+#define CS_P_PIC32 11
+#define CS_P_STM32 16
+/* Next id: 18 */
+
+/* If not specified explicitly, we guess platform by defines. */
+#ifndef CS_PLATFORM
+
+#if defined(TARGET_IS_MSP432P4XX) || defined(__MSP432P401R__)
+#define CS_PLATFORM CS_P_MSP432
+#elif defined(cc3200) || defined(TARGET_IS_CC3200)
+#define CS_PLATFORM CS_P_CC3200
+#elif defined(cc3220) || defined(TARGET_IS_CC3220)
+#define CS_PLATFORM CS_P_CC3220
+#elif defined(__unix__) || defined(__APPLE__)
+#define CS_PLATFORM CS_P_UNIX
+#elif defined(WINCE)
+#define CS_PLATFORM CS_P_WINCE
+#elif defined(_WIN32)
+#define CS_PLATFORM CS_P_WINDOWS
+#elif defined(__MBED__)
+#define CS_PLATFORM CS_P_MBED
+#elif defined(__USE_LPCOPEN)
+#define CS_PLATFORM CS_P_NXP_LPC
+#elif defined(FRDM_K64F) || defined(FREEDOM)
+#define CS_PLATFORM CS_P_NXP_KINETIS
+#elif defined(PIC32)
+#define CS_PLATFORM CS_P_PIC32
+#elif defined(ESP_PLATFORM)
+#define CS_PLATFORM CS_P_ESP32
+#elif defined(ICACHE_FLASH)
+#define CS_PLATFORM CS_P_ESP8266
+#elif defined(TARGET_IS_TM4C129_RA0) || defined(TARGET_IS_TM4C129_RA1) || \
+    defined(TARGET_IS_TM4C129_RA2)
+#define CS_PLATFORM CS_P_TM4C129
+#elif defined(STM32)
+#define CS_PLATFORM CS_P_STM32
+#endif
+
+#ifndef CS_PLATFORM
+#error "CS_PLATFORM is not specified and we couldn't guess it."
+#endif
+
+#endif /* !defined(CS_PLATFORM) */
+
+#define MG_NET_IF_SOCKET 1
+#define MG_NET_IF_SIMPLELINK 2
+#define MG_NET_IF_LWIP_LOW_LEVEL 3
+#define MG_NET_IF_PIC32 4
+
+#define MG_SSL_IF_OPENSSL 1
+#define MG_SSL_IF_MBEDTLS 2
+#define MG_SSL_IF_SIMPLELINK 3
+
+#include "common/platforms/platform_unix.h"
+#include "common/platforms/platform_windows.h"
+#include "common/platforms/platform_esp32.h"
+#include "common/platforms/platform_esp8266.h"
+#include "common/platforms/platform_cc3100.h"
+#include "common/platforms/platform_cc3200.h"
+#include "common/platforms/platform_cc3220.h"
+#include "common/platforms/platform_mbed.h"
+#include "common/platforms/platform_nrf51.h"
+#include "common/platforms/platform_nrf52.h"
+#include "common/platforms/platform_wince.h"
+#include "common/platforms/platform_nxp_lpc.h"
+#include "common/platforms/platform_nxp_kinetis.h"
+#include "common/platforms/platform_pic32.h"
+#include "common/platforms/platform_stm32.h"
+
+/* Common stuff */
+
+#if !defined(WEAK)
+#if (defined(__GNUC__) || defined(__TI_COMPILER_VERSION__)) && !defined(_WIN32)
+#define WEAK __attribute__((weak))
+#else
+#define WEAK
+#endif
+#endif
+
+#ifdef __GNUC__
+#define NORETURN __attribute__((noreturn))
+#define NOINLINE __attribute__((noinline))
+#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#define NOINSTR __attribute__((no_instrument_function))
+#define DO_NOT_WARN_UNUSED __attribute__((unused))
+#else
+#define NORETURN
+#define NOINLINE
+#define WARN_UNUSED_RESULT
+#define NOINSTR
+#define DO_NOT_WARN_UNUSED
+#endif /* __GNUC__ */
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
+#endif
+
+#endif /* CS_COMMON_PLATFORM_H_ */
diff --git a/src/mongoose-6.11/src/common/platforms/arm/arm_exc.c b/src/mongoose-6.11/src/common/platforms/arm/arm_exc.c
new file mode 100644
index 0000000..60b50c8
--- /dev/null
+++ b/src/mongoose-6.11/src/common/platforms/arm/arm_exc.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2014-2017 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#include 
+#include 
+#include 
+#include 
+
+#include "FreeRTOS.h"
+
+#include "common/platform.h"
+
+#include "mgos_core_dump.h"
+#include "mgos_hal.h"
+
+struct arm_exc_frame {
+  uint32_t r0;
+  uint32_t r1;
+  uint32_t r2;
+  uint32_t r3;
+  uint32_t r12;
+  uint32_t lr;
+  uint32_t pc;
+  uint32_t xpsr;
+#ifdef ARM_HAVE_FPU
+  uint32_t s[16];
+  uint32_t fpscr;
+  uint32_t reserved;
+#endif
+} __attribute__((packed));
+
+struct arm_gdb_reg_file {
+  uint32_t r[13];
+  uint32_t sp;
+  uint32_t lr;
+  uint32_t pc;
+  uint32_t cpsr;
+  uint64_t d[16];
+  uint32_t fpscr;
+} __attribute__((packed));
+
+#if ARM_HAVE_FPU
+static void save_s16_s31(uint32_t *dst) {
+  __asm volatile(
+      "\
+      vmov r1, s16\n str  r1, [%0, 0]\n\
+      vmov r1, s17\n str  r1, [%0, 4]\n\
+      vmov r1, s18\n str  r1, [%0, 8]\n\
+      vmov r1, s19\n str  r1, [%0, 12]\n\
+      vmov r1, s20\n str  r1, [%0, 16]\n\
+      vmov r1, s21\n str  r1, [%0, 20]\n\
+      vmov r1, s22\n str  r1, [%0, 24]\n\
+      vmov r1, s23\n str  r1, [%0, 28]\n\
+      vmov r1, s24\n str  r1, [%0, 32]\n\
+      vmov r1, s25\n str  r1, [%0, 36]\n\
+      vmov r1, s26\n str  r1, [%0, 40]\n\
+      vmov r1, s27\n str  r1, [%0, 44]\n\
+      vmov r1, s28\n str  r1, [%0, 48]\n\
+      vmov r1, s29\n str  r1, [%0, 52]\n\
+      vmov r1, s30\n str  r1, [%0, 56]\n\
+      vmov r1, s31\n str  r1, [%0, 60]\n\
+      "
+      :
+      : "r"(dst)
+      : "r1");
+}
+
+static void print_fpu_regs(const uint32_t *regs, int off, int n) {
+  for (int i = 0, j = off; i < n; i++, j++) {
+    if (j % 4 == 0) mgos_cd_putc('\n');
+    mgos_cd_printf("  S%d: %s0x%08lx", j, (j < 10 ? " " : ""), regs[i]);
+  }
+}
+#endif
+
+void arm_exc_handler_bottom(uint8_t isr_no, struct arm_exc_frame *ef,
+                            struct arm_gdb_reg_file *rf) {
+  char buf[8];
+  const char *name;
+  portDISABLE_INTERRUPTS();
+  switch (isr_no) {
+    case 0:
+      name = "ThreadMode";
+      break;
+
+    case 1:
+    case 7:
+    case 8:
+    case 9:
+    case 10:
+    case 13:
+      name = "Reserved";
+      break;
+
+    case 2:
+      name = "NMI";
+      break;
+    case 3:
+      name = "HardFault";
+      break;
+    case 4:
+      name = "MemManage";
+      break;
+    case 5:
+      name = "BusFault";
+      break;
+    case 6:
+      name = "UsageFault";
+      break;
+    case 11:
+      name = "SVCall";
+      break;
+    case 12:
+      name = "ReservedDebug";
+      break;
+    case 14:
+      name = "PendSV";
+      break;
+    case 15:
+      name = "SysTick";
+      break;
+    default: {
+      sprintf(buf, "IRQ%u", isr_no - 16);
+      name = buf;
+    }
+  }
+  mgos_cd_printf("\n\n--- Exception %u (%s) ---\n", isr_no, name);
+  if (rf != NULL) {
+    mgos_cd_printf("  R0:  0x%08lx  R1:  0x%08lx  R2:  0x%08lx  R3:  0x%08lx\n",
+                   rf->r[0], rf->r[1], rf->r[2], rf->r[3]);
+    mgos_cd_printf("  R4:  0x%08lx  R5:  0x%08lx  R6:  0x%08lx  R7:  0x%08lx\n",
+                   rf->r[4], rf->r[5], rf->r[6], rf->r[7]);
+    mgos_cd_printf("  R8:  0x%08lx  R9:  0x%08lx  R10: 0x%08lx  R11: 0x%08lx\n",
+                   rf->r[8], rf->r[9], rf->r[10], rf->r[11]);
+    mgos_cd_printf("  R12: 0x%08lx  SP:  0x%08lx  LR:  0x%08lx  PC:  0x%08lx\n",
+                   rf->r[12], rf->sp, rf->lr, rf->pc);
+    mgos_cd_printf("  PSR: 0x%08lx\n", rf->cpsr);
+  }
+  memset(rf->d, 0, sizeof(rf->d));
+#if ARM_HAVE_FPU
+  rf->fpscr = ef->fpscr;
+  memcpy((uint8_t *) rf->d, ef->s, sizeof(ef->s));
+  print_fpu_regs((uint32_t *) rf->d, 0, ARRAY_SIZE(ef->s));
+  save_s16_s31(ef->s);
+  memcpy(((uint8_t *) rf->d) + sizeof(ef->s), ef->s, sizeof(ef->s));
+  print_fpu_regs((uint32_t *) (((uint8_t *) rf->d) + sizeof(ef->s)), 16,
+                 ARRAY_SIZE(ef->s));
+  mgos_cd_putc('\n');
+  mgos_cd_printf("FPSCR: 0x%08lx\n", rf->fpscr);
+#else
+  rf->fpscr = 0;
+#endif
+  mgos_cd_emit_header();
+  mgos_cd_emit_section(MGOS_CORE_DUMP_SECTION_REGS, rf, sizeof(*rf));
+  mgos_cd_emit_section("SRAM", (void *) SRAM_BASE_ADDR, SRAM_SIZE);
+  mgos_cd_emit_footer();
+#ifdef MGOS_HALT_ON_EXCEPTION
+  mgos_cd_printf("Halting\n");
+  while (1) {
+    mgos_wdt_feed();
+  }
+#else
+  mgos_cd_printf("Rebooting\n");
+  mgos_dev_system_restart();
+#endif
+}
diff --git a/src/mongoose-6.11/src/common/platforms/arm/arm_exc_top.S b/src/mongoose-6.11/src/common/platforms/arm/arm_exc_top.S
new file mode 100644
index 0000000..9ffade4
--- /dev/null
+++ b/src/mongoose-6.11/src/common/platforms/arm/arm_exc_top.S
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2014-2017 Cesanta Software Limited
+ * All rights reserved
+ */
+
+.arch armv7e-m
+.syntax unified
+.thumb
+
+/* These are required to satisfy TI linker. */
+.eabi_attribute Tag_ABI_align_needed, 1
+.eabi_attribute Tag_ABI_align_preserved, 1
+
+.global arm_exc_handler_top
+.global arm_exc_handler_bottom
+
+/*
+ * Determines the stack pointer, populates most of the GDB frame 
+ * and hands off to the C routine.
+ */
+.section .text.arm_exc_handler_top
+.type arm_exc_handler_top, %function
+.align 8
+
+arm_exc_handler_top:
+  tst lr, #4
+  ite eq
+  mrseq r1, msp
+  mrsne r1, psp
+  // r1 -> arm_exc_frame prepared for us by the CPU
+#if ARM_HAVE_FPU
+  add r0, r1, #104  // sizeof(arm_exc_frame)
+  sub sp, #328      // sizeof(arm_gdb_reg_file)
+#else
+  add r0, r1, #32   // sizeof(arm_exc_frame)
+  sub sp, #328      // sizeof(arm_gdb_reg_file)
+#endif
+  mov r2, sp
+  // r0 -> original sp, r2 -> arm_gdb_reg_file to fill
+  // r3 - scratch
+  ldr r3,  [r1, #0]   // r0
+  str r3,  [r2, #0]
+  ldr r3,  [r1, #4]   // r2
+  str r3,  [r2, #4]
+  ldr r3,  [r1, #8]   // r1
+  str r3,  [r2, #8]
+  ldr r3,  [r1, #12]  // r3
+  str r3,  [r2, #12]
+  str r4,  [r2, #16]  // r4
+  str r5,  [r2, #20]  // r5
+  str r6,  [r2, #24]  // r6
+  str r7,  [r2, #28]  // r7
+  str r8,  [r2, #32]  // r8
+  str r9,  [r2, #36]  // r9
+  str r10, [r2, #40]  // r10
+  str r11, [r2, #44]  // r11
+  ldr r3,  [r1, #16]  // r12
+  str r3,  [r2, #48]
+  str r0,  [r2, #52]  // sp
+  ldr r3,  [r1, #20]  // lr
+  str r3,  [r2, #56]
+  ldr r3,  [r1, #24]  // pc
+  str r3,  [r2, #60]
+  ldr r3,  [r1, #28]  // xpsr
+  str r3,  [r2, #64]
+
+  mrs r0, ipsr
+  b arm_exc_handler_bottom
+.size arm_exc_handler_top, . - arm_exc_handler_top
diff --git a/src/mongoose-6.11/src/common/platforms/cc3200/cc3200.ld b/src/mongoose-6.11/src/common/platforms/cc3200/cc3200.ld
new file mode 100644
index 0000000..a2df648
--- /dev/null
+++ b/src/mongoose-6.11/src/common/platforms/cc3200/cc3200.ld
@@ -0,0 +1,92 @@
+/*****************************************************************************
+*
+* GCC Linker script for CC3200. Based on TI's example "blinky.ld".
+*
+* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ 
+* 
+* 
+*  Redistribution and use in source and binary forms, with or without 
+*  modification, are permitted provided that the following conditions 
+*  are met:
+*
+*    Redistributions of source code must retain the above copyright 
+*    notice, this list of conditions and the following disclaimer.
+*
+*    Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the 
+*    documentation and/or other materials provided with the   
+*    distribution.
+*
+*    Neither the name of Texas Instruments Incorporated nor the names of
+*    its contributors may be used to endorse or promote products derived
+*    from this software without specific prior written permission.
+*
+*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+*  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+*  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+*  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+*  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+*  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+*  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+*  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************/
+
+ORG = DEFINED(ORG) ? ORG : 0x20004000;
+RAM_SIZE = DEFINED(RAM_SIZE) ? RAM_SIZE : 0x3C000;
+
+MEMORY
+{
+    /* SRAM size of 240KB for cc3200 ES 1.33 device onward */
+    SRAM (rwx) : ORIGIN = ORG, LENGTH = RAM_SIZE
+}
+
+SECTIONS
+{
+    .text :
+    {
+        _text = .;
+        KEEP(*(.intvecs))
+        *(.text*)
+        *(.rodata*)
+        *(.ARM.extab* .gnu.linkonce.armextab.*)
+        . = ALIGN(8);
+        _etext = .;
+    } > SRAM
+
+    .ARM : {
+    __exidx_start = .;
+      *(.ARM.exidx*)
+      __exidx_end = .;
+    } > SRAM
+
+    __init_data = .;
+
+    .data : AT(__init_data)
+    {
+        _data = .;
+        *(.data*)
+        . = ALIGN (8);
+        _edata = .;
+    } > SRAM
+
+    .bss :
+    {
+        _bss = .;
+        *(.bss*)
+        *(COMMON)
+        _ebss = .;
+    } > SRAM
+
+    .heap :
+    {
+        _heap = .;
+        . = . + (LENGTH(SRAM) - SIZEOF(.text) - SIZEOF(.ARM) - SIZEOF(.data) - SIZEOF(.bss));
+        . = ALIGN(8);
+        _eheap = .;
+
+    } > SRAM
+}
diff --git a/src/mongoose-6.11/src/common/platforms/cc3200/cc3200_libc.c b/src/mongoose-6.11/src/common/platforms/cc3200/cc3200_libc.c
new file mode 100644
index 0000000..f488725
--- /dev/null
+++ b/src/mongoose-6.11/src/common/platforms/cc3200/cc3200_libc.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2014-2016 Cesanta Software Limited
+ * All rights reserved
+ */
+
+#if CS_PLATFORM == CS_P_CC3200
+
+#include "common/mg_mem.h"
+#include 
+#include 
+
+#ifndef __TI_COMPILER_VERSION__
+#include 
+#include 
+#include 
+#include 
+#endif
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define CONSOLE_UART UARTA0_BASE
+
+#ifdef __TI_COMPILER_VERSION__
+int asprintf(char **strp, const char *fmt, ...) {
+  va_list ap;
+  int len;
+
+  *strp = MG_MALLOC(BUFSIZ);
+  if (*strp == NULL) return -1;
+
+  va_start(ap, fmt);
+  len = vsnprintf(*strp, BUFSIZ, fmt, ap);
+  va_end(ap);
+
+  if (len > 0) {
+    *strp = MG_REALLOC(*strp, len + 1);
+    if (*strp == NULL) return -1;
+  }
+
+  if (len >= BUFSIZ) {
+    va_start(ap, fmt);
+    len = vsnprintf(*strp, len + 1, fmt, ap);
+    va_end(ap);
+  }
+
+  return len;
+}
+
+#if MG_TI_NO_HOST_INTERFACE
+time_t HOSTtime() {
+  struct timeval tp;
+  gettimeofday(&tp, NULL);
+  return tp.tv_sec;
+}
+#endif
+
+#endif /* __TI_COMPILER_VERSION__ */
+
+void fprint_str(FILE *fp, const char *str) {
+  while (*str != '\0') {
+    if (*str == '\n') MAP_UARTCharPut(CONSOLE_UART, '\r');
+    MAP_UARTCharPut(CONSOLE_UART, *str++);
+  }
+}
+
+void _exit(int status) {
+  fprint_str(stderr, "_exit\n");
+  /* cause an unaligned access exception, that will drop you into gdb */
+  *(int *) 1 = status;
+  while (1)
+    ; /* avoid gcc warning because stdlib abort() has noreturn attribute */
+}
+
+void _not_implemented(const char *what) {
+  fprint_str(stderr, what);
+  fprint_str(stderr, " is not implemented\n");
+  _exit(42);
+}
+
+int _kill(int pid, int sig) {
+  (void) pid;
+  (void) sig;
+  _not_implemented("_kill");
+  return -1;
+}
+
+int _getpid() {
+  fprint_str(stderr, "_getpid is not implemented\n");
+  return 42;
+}
+
+int _isatty(int fd) {
+  /* 0, 1 and 2 are TTYs. */
+  return fd < 2;
+}
+
+#endif /* CS_PLATFORM == CS_P_CC3200 */
diff --git a/src/mongoose-6.11/src/common/platforms/cc3200/cc3200v1p32.cmd b/src/mongoose-6.11/src/common/platforms/cc3200/cc3200v1p32.cmd
new file mode 100644
index 0000000..3b17d07
--- /dev/null
+++ b/src/mongoose-6.11/src/common/platforms/cc3200/cc3200v1p32.cmd
@@ -0,0 +1,82 @@
+//*****************************************************************************
+// cc3200v1p32.cmd
+//
+// CCS linker configuration file for cc3200 ES 1.32.
+//
+// Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ 
+// 
+// 
+//  Redistribution and use in source and binary forms, with or without 
+//  modification, are permitted provided that the following conditions 
+//  are met:
+//
+//    Redistributions of source code must retain the above copyright 
+//    notice, this list of conditions and the following disclaimer.
+//
+//    Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the 
+//    documentation and/or other materials provided with the   
+//    distribution.
+//
+//    Neither the name of Texas Instruments Incorporated nor the names of
+//    its contributors may be used to endorse or promote products derived
+//    from this software without specific prior written permission.
+//
+//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+//  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+//  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+//  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+//  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+//  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+//  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+//  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+//  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+//  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+//  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+//*****************************************************************************
+
+
+--retain=g_pfnVectors
+
+//*****************************************************************************
+// The following command line options are set as part of the CCS project.  
+// If you are building using the command line, or for some reason want to    
+// define them here, you can uncomment and modify these lines as needed.     
+// If you are using CCS for building, it is probably better to make any such 
+// modifications in your CCS project and leave this file alone.              
+//*****************************************************************************
+
+
+//*****************************************************************************
+// The starting address of the application.  Normally the interrupt vectors  
+// must be located at the beginning of the application.                      
+//*****************************************************************************
+#define RAM_BASE 0x20004000
+
+/* System memory map */
+
+MEMORY
+{
+    /* Application uses internal RAM for program and data */
+    SRAM_CODE (RWX) : origin = 0x20004000, length = 0x2F000
+    SRAM_DATA (RWX) : origin = 0x20033000, length = 0xD000
+}
+
+/* Section allocation in memory */
+
+SECTIONS
+{
+    .intvecs:   > RAM_BASE
+    .init_array : > SRAM_CODE
+    .vtable :   > SRAM_CODE
+    .text   :   > SRAM_CODE
+    .const  :   > SRAM_CODE
+    .cinit  :   > SRAM_CODE
+    .pinit  :   > SRAM_CODE
+    .data   :   > SRAM_DATA
+    .bss    :   > SRAM_DATA
+    .sysmem :   > SRAM_DATA
+    .stack  :   > SRAM_DATA(HIGH)
+}
+
diff --git a/src/mongoose-6.11/src/common/platforms/cc3200/gcc.mk b/src/mongoose-6.11/src/common/platforms/cc3200/gcc.mk
new file mode 100644
index 0000000..ec98b75
--- /dev/null
+++ b/src/mongoose-6.11/src/common/platforms/cc3200/gcc.mk
@@ -0,0 +1,52 @@
+APP_LDFLAGS ?=
+CC_WRAPPER ?=
+GENFILES_LIST ?=
+CC = arm-none-eabi-gcc
+CXX = arm-none-eabi-g++
+AR = arm-none-eabi-ar
+NM = arm-none-eabi-nm
+
+IPATH += $(SDK_PATH)/third_party/FreeRTOS/source/portable/GCC/ARM_CM4
+VPATH += $(SDK_PATH)/third_party/FreeRTOS/source/portable/GCC/ARM_CM4
+
+C_CXX_FLAGS = -mthumb -mcpu=cortex-m4 -ffunction-sections -fdata-sections \
+              -MD -Os -ggdb -Wall -Werror -Dgcc
+CFLAGS += -std=c99 $(C_CXX_FLAGS)
+CXXFLAGS += -std=g++11 $(C_CXX_FLAGS)
+
+AR = arm-none-eabi-ar
+LD = arm-none-eabi-ld
+OBJCOPY = arm-none-eabi-objcopy
+LIBGCC := ${shell ${CC} -mthumb ${CFLAGS} -print-libgcc-file-name}
+LIBC := ${shell ${CC} ${CFLAGS} -print-file-name=libc.a}
+LIBM := ${shell ${CC} ${CFLAGS} -print-file-name=libm.a}
+
+# Disable certain warnings on SDK sources, we have no control over them anyway.
+# We also force-include platform.h which resolves some symbol conflicts
+# between system includes and simplelink.h
+$(SDK_OBJS): CFLAGS += -Wno-missing-braces -Wno-strict-aliasing -Wno-parentheses -Wno-unused-variable -Wno-builtin-macro-redefined
+$(SDK_OBJS): CFLAGS += -include common/platform.h
+
+# cc flags,file
+define cc
+	$(vecho) "GCC   $2 -> $@"
+	$(Q) $(CC_WRAPPER) $(CC) -c $1 -o $@ $2
+endef
+define cxx
+	$(vecho) "G++   $2 -> $@"
+	$(Q) $(CC_WRAPPER) $(CXX) -c $1 -o $@ $2
+endef
+
+# ar files
+define ar
+	$(vecho) "AR    $@"
+	$(Q) $(AR) cru $@ $1
+endef
+
+# link script,flags,objs
+define link
+	$(vecho) "LD    $@"
+	$(Q) $(CC_WRAPPER) $(LD) \
+	  --gc-sections -o $@ -T $1 $2 $3 \
+	  $(LIBM) $(LIBC) $(LIBGCC)
+endef
diff --git a/src/mongoose-6.11/src/common/platforms/cc3200/ti.mk b/src/mongoose-6.11/src/common/platforms/cc3200/ti.mk
new file mode 100644
index 0000000..b50407f
--- /dev/null
+++ b/src/mongoose-6.11/src/common/platforms/cc3200/ti.mk
@@ -0,0 +1,52 @@
+IPATH += $(SDK_PATH)/third_party/FreeRTOS/source/portable/CCS/ARM_CM3
+VPATH += $(SDK_PATH)/third_party/FreeRTOS/source/portable/CCS/ARM_CM3
+
+CC_WRAPPER ?=
+CC = $(TOOLCHAIN)/bin/armcl
+AR = $(TOOLCHAIN)/bin/armar
+NM = nm
+GENFILES_LIST ?=
+
+C_CXX_FLAGS = -Dccs -I$(TOOLCHAIN)/include
+TI_C_CXX_FLAGS = -mv7M4 --little_endian --code_state=16 --float_support=vfplib --abi=eabi \
+                 -O4 --opt_for_speed=0 --unaligned_access=on --small_enum \
+                 --gen_func_subsections=on --diag_wrap=off --display_error_number \
+                 --emit_warnings_as_errors
+CFLAGS += --c99 $(TI_C_CXX_FLAGS) $(C_CXX_FLAGS)
+CXXFLAGS += $(TI_C_CXX_FLAGS) $(C_CXX_FLAGS)
+
+# cc flags,file
+define cc
+	$(vecho) "TICC  $2 -> $@"
+	$(Q) $(CC_WRAPPER) $(CC) -c --preproc_with_compile -ppd=$@.d $1 --output_file=$@ $2
+endef
+define cxx
+	$(vecho) "TICXX $2 -> $@"
+	$(Q) $(CC_WRAPPER) $(CC) -c --preproc_with_compile -ppd=$@.d $1 --output_file=$@ $2
+endef
+
+# asm flags,file
+define asm
+	$(vecho) "TIASM $2 -> $@"
+	$(Q) $(CC_WRAPPER) $(CC) -c $1 --output_file=$@ $2
+endef
+
+# ar files
+define ar
+	$(vecho) "TIAR  $@"
+	$(Q) $(AR) qru $@ $1
+endef
+
+# link script,flags,objs
+define link
+	$(vecho) "TILD  $@"
+	$(Q) $(CC_WRAPPER) $(CC) \
+	  -mv7M4 --code_state=16 --float_support=vfplib --abi=eabi --little_endian \
+	  --run_linker \
+	  --generate_dead_funcs_list=$@.garbage.xml \
+	  -i $(TOOLCHAIN)/lib \
+	  --reread_libs --warn_sections --display_error_number \
+	  --unused_section_elimination=on \
+	  -o $@ --map_file=$@.map --xml_link_info=$@.map.xml \
+	  $2 $1 $3
+endef
diff --git a/src/mongoose-6.11/src/common/platforms/esp/esptool.py b/src/mongoose-6.11/src/common/platforms/esp/esptool.py
new file mode 100755
index 0000000..9592f6d
--- /dev/null
+++ b/src/mongoose-6.11/src/common/platforms/esp/esptool.py
@@ -0,0 +1,2133 @@
+#!/usr/bin/env python
+# NB: Before sending a PR to change the above line to '#!/usr/bin/env python2', please read https://github.com/themadinventor/esptool/issues/21
+#
+# ESP8266 & ESP32 ROM Bootloader Utility
+# https://github.com/themadinventor/esptool
+#
+# Copyright (C) 2014-2016 Fredrik Ahlberg, Angus Gratton, Espressif Systems (Shanghai) PTE LTD, other contributors as noted.
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
+# Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import argparse
+import hashlib
+import inspect
+import json
+import os
+import serial
+import struct
+import subprocess
+import sys
+import time
+import base64
+import zlib
+
+__version__ = "2.0-dev"
+
+
+MAX_UINT32 = 0xffffffff
+MAX_UINT24 = 0xffffff
+
+
+def check_supported_function(func, check_func):
+    """
+    Decorator implementation that wraps a check around an ESPLoader
+    bootloader function to check if it's supported.
+
+    This is used to capture the multidimensional differences in
+    functionality between the ESP8266 & ESP32 ROM loaders, and the
+    software stub that runs on both. Not possible to do this cleanly
+    via inheritance alone.
+    """
+    def inner(*args, **kwargs):
+        obj = args[0]
+        if check_func(obj):
+            return func(*args, **kwargs)
+        else:
+            raise NotImplementedInROMError(obj)
+    return inner
+
+
+def stub_function_only(func):
+    """ Attribute for a function only supported in the software stub loader """
+    return check_supported_function(func, lambda o: o.IS_STUB)
+
+
+def stub_and_esp32_function_only(func):
+    """ Attribute for a function only supported by software stubs or ESP32 ROM """
+    return check_supported_function(func, lambda o: o.IS_STUB or o.CHIP_NAME == "ESP32")
+
+
+def esp8266_function_only(func):
+    """ Attribute for a function only supported on ESP8266 """
+    return check_supported_function(func, lambda o: o.CHIP_NAME == "ESP8266")
+
+
+class ESPLoader(object):
+    """ Base class providing access to ESP ROM & softtware stub bootloaders.
+    Subclasses provide ESP8266 & ESP32 specific functionality.
+
+    Don't instantiate this base class directly, either instantiate a subclass or
+    call ESPLoader.detect_chip() which will interrogate the chip and return the
+    appropriate subclass instance.
+
+    """
+    CHIP_NAME = "Espressif device"
+    IS_STUB = False
+
+    DEFAULT_PORT = "/dev/ttyUSB0"
+
+    # Commands supported by ESP8266 ROM bootloader
+    ESP_FLASH_BEGIN = 0x02
+    ESP_FLASH_DATA  = 0x03
+    ESP_FLASH_END   = 0x04
+    ESP_MEM_BEGIN   = 0x05
+    ESP_MEM_END     = 0x06
+    ESP_MEM_DATA    = 0x07
+    ESP_SYNC        = 0x08
+    ESP_WRITE_REG   = 0x09
+    ESP_READ_REG    = 0x0a
+
+    # Some comands supported by ESP32 ROM bootloader (or -8266 w/ stub)
+    ESP_SPI_SET_PARAMS = 0x0B
+    ESP_SPI_ATTACH     = 0x0D
+    ESP_CHANGE_BAUDRATE = 0x0F
+    ESP_FLASH_DEFL_BEGIN = 0x10
+    ESP_FLASH_DEFL_DATA  = 0x11
+    ESP_FLASH_DEFL_END   = 0x12
+    ESP_SPI_FLASH_MD5    = 0x13
+
+    # Some commands supported by stub only
+    ESP_ERASE_FLASH = 0xD0
+    ESP_ERASE_REGION = 0xD1
+    ESP_READ_FLASH = 0xD2
+    ESP_GET_FLASH_ID = 0xD3
+
+    # Maximum block sized for RAM and Flash writes, respectively.
+    ESP_RAM_BLOCK   = 0x1800
+    ESP_FLASH_BLOCK = 0x400
+
+    FLASH_WRITE_SIZE = ESP_FLASH_BLOCK
+
+    # Default baudrate. The ROM auto-bauds, so we can use more or less whatever we want.
+    ESP_ROM_BAUD    = 115200
+
+    # First byte of the application image
+    ESP_IMAGE_MAGIC = 0xe9
+
+    # Initial state for the checksum routine
+    ESP_CHECKSUM_MAGIC = 0xef
+
+    # Flash sector size, minimum unit of erase.
+    ESP_FLASH_SECTOR = 0x1000
+
+    UART_DATA_REG_ADDR = 0x60000078
+
+    # Memory addresses
+    IROM_MAP_START = 0x40200000
+    IROM_MAP_END = 0x40300000
+
+    # The number of bytes in the UART response that signify command status
+    STATUS_BYTES_LENGTH = 2
+
+    def __init__(self, port=DEFAULT_PORT, baud=ESP_ROM_BAUD, do_connect=True):
+        """Base constructor for ESPLoader bootloader interaction
+
+        Don't call this constructor, either instantiate ESP8266ROM
+        or ESP32ROM, or use ESPLoader.detect_chip().
+
+        This base class has all of the instance methods for bootloader
+        functionality supported across various chips & stub
+        loaders. Subclasses replace the functions they don't support
+        with ones which throw NotImplementedInROMError().
+
+        """
+        if isinstance(port, serial.Serial):
+            self._port = port
+        else:
+            self._port = serial.Serial(port)
+        self._slip_reader = slip_reader(self._port)
+        # setting baud rate in a separate step is a workaround for
+        # CH341 driver on some Linux versions (this opens at 9600 then
+        # sets), shouldn't matter for other platforms/drivers. See
+        # https://github.com/themadinventor/esptool/issues/44#issuecomment-107094446
+        self._port.baudrate = baud
+        if do_connect:
+            self.connect()
+
+    @staticmethod
+    def detect_chip(port=DEFAULT_PORT, baud=ESP_ROM_BAUD):
+        """Use serial access to detect the chip type.
+
+        We use the UART's datecode register for this, it's mapped at
+        the same address on ESP8266 & ESP32 so we can use one
+        memory read and compare to the datecode register for each chip
+        type.
+
+        """
+        detect_port = ESPLoader(port, baud, True)
+        sys.stdout.write('Detecting chip type... ')
+        date_reg = detect_port.read_reg(ESPLoader.UART_DATA_REG_ADDR)
+
+        for cls in [ESP8266ROM, ESP32ROM]:
+            if date_reg == cls.DATE_REG_VALUE:
+                # don't connect a second time
+                inst = cls(detect_port._port, baud, False)
+                print '%s' % inst.CHIP_NAME
+                return inst
+        print ''
+        raise FatalError("Unexpected UART datecode value 0x%08x. Failed to autodetect chip type." % date_reg)
+
+    """ Read a SLIP packet from the serial port """
+    def read(self):
+        r = self._slip_reader.next()
+        return r
+
+    """ Write bytes to the serial port while performing SLIP escaping """
+    def write(self, packet):
+        buf = '\xc0' \
+              + (packet.replace('\xdb','\xdb\xdd').replace('\xc0','\xdb\xdc')) \
+              + '\xc0'
+        self._port.write(buf)
+
+    """ Calculate checksum of a blob, as it is defined by the ROM """
+    @staticmethod
+    def checksum(data, state=ESP_CHECKSUM_MAGIC):
+        for b in data:
+            state ^= ord(b)
+        return state
+
+    """ Send a request and read the response """
+    def command(self, op=None, data="", chk=0):
+        if op is not None:
+            pkt = struct.pack(' self.STATUS_BYTES_LENGTH:
+            return data[:-self.STATUS_BYTES_LENGTH]
+        else:  # otherwise, just return the 'val' field which comes from the reply header (this is used by read_reg)
+            return val
+
+    def flush_input(self):
+        self._port.flushInput()
+        self._slip_reader = slip_reader(self._port)
+
+    def sync(self):
+        """ Perform a connection test """
+        self.command(self.ESP_SYNC, '\x07\x07\x12\x20' + 32 * '\x55')
+        for i in xrange(7):
+            self.command()
+
+    def connect(self):
+        """ Try connecting repeatedly until successful, or giving up """
+        print 'Connecting...'
+
+        for _ in xrange(10):
+            # issue reset-to-bootloader:
+            # RTS = either CH_PD or nRESET (both active low = chip in reset)
+            # DTR = GPIO0 (active low = boot to flasher)
+            self._port.setDTR(False)
+            self._port.setRTS(True)
+            time.sleep(0.05)
+            self._port.setDTR(True)
+            self._port.setRTS(False)
+            time.sleep(0.05)
+            self._port.setDTR(False)
+
+            self._port.timeout = 0.1
+            last_exception = None
+            for _ in xrange(4):
+                try:
+                    self.flush_input()
+                    self._port.flushOutput()
+                    self.sync()
+                    self._port.timeout = 5
+                    return
+                except FatalError as e:
+                    last_exception = e
+                    time.sleep(0.05)
+        raise FatalError('Failed to connect to %s: %s' % (self.CHIP_NAME, last_exception))
+
+    """ Read memory address in target """
+    def read_reg(self, addr):
+        # we don't call check_command here because read_reg() function is called
+        # when detecting chip type, and the way we check for success (STATUS_BYTES_LENGTH) is different
+        # for different chip types (!)
+        val, data = self.command(self.ESP_READ_REG, struct.pack('>sys.stderr, 'Stub executed, reading response:'
+            while True:
+                p = self.read()
+                print hexify(p)
+                if p == '':
+                    return
+
+    def run_stub(self, stub=None):
+        if stub is None:
+            if self.IS_STUB:
+                raise FatalError("Not possible for a stub to load another stub (memory likely to overlap.)")
+            stub = self.STUB_CODE
+
+        # Upload
+        print "Uploading stub..."
+        for field in ['text', 'data']:
+            if field in stub:
+                self._upload_ram(stub[field + "_start"], stub[field])
+        print "Running stub..."
+        self.mem_finish(stub['entry'])
+
+        p = self.read()
+        if p != 'OHAI':
+            raise FatalError("Failed to start stub. Unexpected response: %s" % p)
+        print "Stub running..."
+        return self.STUB_CLASS(self)
+
+    @stub_and_esp32_function_only
+    def flash_defl_begin(self, size, compsize, offset):
+        """ Start downloading compressed data to Flash (performs an erase) """
+        old_tmo = self._port.timeout
+        num_blocks = (compsize + self.ESP_FLASH_BLOCK - 1) / self.ESP_FLASH_BLOCK
+        erase_blocks = (size + self.ESP_FLASH_BLOCK - 1) / self.ESP_FLASH_BLOCK
+
+        self._port.timeout = 20
+        t = time.time()
+        print "Compressed %d bytes to %d..." % (size, compsize)
+        self.check_command("enter compressed flash mode", self.ESP_FLASH_DEFL_BEGIN,
+                           struct.pack(' length:
+            raise FatalError('Read more than expected')
+        digest_frame = self.read()
+        if len(digest_frame) != 16:
+            raise FatalError('Expected digest, got: %s' % hexify(digest_frame))
+        expected_digest = hexify(digest_frame).upper()
+        digest = hashlib.md5(data).hexdigest().upper()
+        if digest != expected_digest:
+            raise FatalError('Digest mismatch: expected %s, got %s' % (expected_digest, digest))
+        return data
+
+    def flash_spi_attach(self,is_hspi,is_legacy):
+        """Send SPI attach command to enable the SPI flash pins
+
+        ESP8266 ROM does this when you send flash_begin, ESP32 ROM
+        has it as a SPI command.
+        """
+        # last 3 bytes in ESP_SPI_ATTACH argument are reserved values
+        arg = struct.pack(' 0:
+                    self.write_reg(SPI_MOSI_DLEN_REG, mosi_bits - 1)
+                if miso_bits > 0:
+                    self.write_reg(SPI_MISO_DLEN_REG, miso_bits - 1)
+        else:
+
+            def set_data_lengths(mosi_bits, miso_bits):
+                SPI_DATA_LEN_REG = SPI_USR1_REG
+                SPI_MOSI_BITLEN_S = 17
+                SPI_MISO_BITLEN_S = 8
+                mosi_mask = 0 if (mosi_bits == 0) else (mosi_bits - 1)
+                miso_mask = 0 if (miso_bits == 0) else (miso_bits - 1)
+                self.write_reg(SPI_DATA_LEN_REG,
+                               (miso_mask << SPI_MISO_BITLEN_S) | (
+                                   mosi_mask << SPI_MOSI_BITLEN_S))
+
+        # SPI peripheral "command" bitmasks for SPI_CMD_REG
+        SPI_CMD_USR  = (1 << 18)
+
+        # shift values
+        SPI_USR2_DLEN_SHIFT = 28
+
+        if read_bits > 32:
+            raise FatalError("Reading more than 32 bits back from a SPI flash operation is unsupported")
+        if len(data) > 64:
+            raise FatalError("Writing more than 64 bytes of data with one SPI command is unsupported")
+
+        data_bits = len(data) * 8
+        flags = SPI_USR_COMMAND
+        if read_bits > 0:
+            flags |= SPI_USR_MISO
+        if data_bits > 0:
+            flags |= SPI_USR_MOSI
+        set_data_lengths(data_bits, read_bits)
+        self.write_reg(SPI_USR_REG, flags)
+        self.write_reg(SPI_USR2_REG,
+                       (7 << SPI_USR2_DLEN_SHIFT) | spiflash_command)
+        if data_bits == 0:
+            self.write_reg(SPI_W0_REG, 0)  # clear data register before we read it
+        else:
+            if len(data) % 4 != 0:  # pad to 32-bit multiple
+                data += b'\0' * (4 - (len(data) % 4))
+            words = struct.unpack("I" * (len(data) / 4), data)
+            next_reg = SPI_W0_REG
+            for word in words:
+                self.write_reg(next_reg, word)
+                next_reg += 4
+        self.write_reg(SPI_CMD_REG, SPI_CMD_USR)
+
+        def wait_done():
+            for _ in xrange(10):
+                if (self.read_reg(SPI_CMD_REG) & SPI_CMD_USR) == 0:
+                    return
+            raise FatalError("SPI command did not complete in time")
+        wait_done()
+
+        status = self.read_reg(SPI_W0_REG)
+        return status
+
+    def read_status(self, num_bytes=2):
+        """Read up to 24 bits (num_bytes) of SPI flash status register contents
+        via RDSR, RDSR2, RDSR3 commands
+
+        Not all SPI flash supports all three commands. The upper 1 or 2
+        bytes may be 0xFF.
+        """
+        SPIFLASH_RDSR  = 0x05
+        SPIFLASH_RDSR2 = 0x35
+        SPIFLASH_RDSR3 = 0x15
+
+        status = 0
+        shift = 0
+        for cmd in [SPIFLASH_RDSR, SPIFLASH_RDSR2, SPIFLASH_RDSR3][0:num_bytes]:
+            status += self.run_spiflash_command(cmd, read_bits=8) << shift
+            shift += 8
+        return status
+
+    def write_status(self, new_status, num_bytes=2, set_non_volatile=False):
+        """Write up to 24 bits (num_bytes) of new status register
+
+        num_bytes can be 1, 2 or 3.
+
+        Not all flash supports the additional commands to write the
+        second and third byte of the status register. When writing 2
+        bytes, esptool also sends a 16-byte WRSR command (as some
+        flash types use this instead of WRSR2.)
+
+        If the set_non_volatile flag is set, non-volatile bits will
+        be set as well as volatile ones (WREN used instead of WEVSR).
+
+        """
+        SPIFLASH_WRSR = 0x01
+        SPIFLASH_WRSR2 = 0x31
+        SPIFLASH_WRSR3 = 0x11
+        SPIFLASH_WEVSR = 0x50
+        SPIFLASH_WREN = 0x06
+        SPIFLASH_WRDI = 0x04
+
+        enable_cmd = SPIFLASH_WREN if set_non_volatile else SPIFLASH_WEVSR
+
+        # try using a 16-bit WRSR (not supported by all chips)
+        # this may be redundant, but shouldn't hurt
+        if num_bytes == 2:
+            self.run_spiflash_command(enable_cmd)
+            self.run_spiflash_command(SPIFLASH_WRSR, struct.pack(">= 8
+
+        self.run_spiflash_command(SPIFLASH_WRDI)
+
+
+class ESP8266ROM(ESPLoader):
+    """ Access class for ESP8266 ROM bootloader
+    """
+    CHIP_NAME = "ESP8266"
+    IS_STUB = False
+
+    DATE_REG_VALUE = 0x00062000
+
+    # OTP ROM addresses
+    ESP_OTP_MAC0    = 0x3ff00050
+    ESP_OTP_MAC1    = 0x3ff00054
+    ESP_OTP_MAC3    = 0x3ff0005c
+
+    SPI_REG_BASE    = 0x60000200
+    SPI_W0_OFFS     = 0x40
+    SPI_HAS_MOSI_DLEN_REG = False
+
+    FLASH_SIZES = {
+        '512KB':0x00,
+        '256KB':0x10,
+        '1MB':0x20,
+        '2MB':0x30,
+        '4MB':0x40,
+        '2MB-c1': 0x50,
+        '4MB-c1':0x60,
+        '4MB-c2':0x70}
+
+    def flash_spi_attach(self, is_spi, is_legacy):
+        pass  # not implemented in ROM, but OK to silently skip
+
+    def flash_set_parameters(self, size):
+        pass  # not implemented in ROM, but OK to silently skip
+
+    def chip_id(self):
+        """ Read Chip ID from OTP ROM - see http://esp8266-re.foogod.com/wiki/System_get_chip_id_%28IoT_RTOS_SDK_0.9.9%29 """
+        id0 = self.read_reg(self.ESP_OTP_MAC0)
+        id1 = self.read_reg(self.ESP_OTP_MAC1)
+        return (id0 >> 24) | ((id1 & MAX_UINT24) << 8)
+
+    def read_mac(self):
+        """ Read MAC from OTP ROM """
+        mac0 = self.read_reg(self.ESP_OTP_MAC0)
+        mac1 = self.read_reg(self.ESP_OTP_MAC1)
+        mac3 = self.read_reg(self.ESP_OTP_MAC3)
+        if (mac3 != 0):
+            oui = ((mac3 >> 16) & 0xff, (mac3 >> 8) & 0xff, mac3 & 0xff)
+        elif ((mac1 >> 16) & 0xff) == 0:
+            oui = (0x18, 0xfe, 0x34)
+        elif ((mac1 >> 16) & 0xff) == 1:
+            oui = (0xac, 0xd0, 0x74)
+        else:
+            raise FatalError("Unknown OUI")
+        return oui + ((mac1 >> 8) & 0xff, mac1 & 0xff, (mac0 >> 24) & 0xff)
+
+    def get_erase_size(self, offset, size):
+        """ Calculate an erase size given a specific size in bytes.
+
+        Provides a workaround for the bootloader erase bug."""
+
+        sectors_per_block = 16
+        sector_size = self.ESP_FLASH_SECTOR
+        num_sectors = (size + sector_size - 1) / sector_size
+        start_sector = offset / sector_size
+
+        head_sectors = sectors_per_block - (start_sector % sectors_per_block)
+        if num_sectors < head_sectors:
+            head_sectors = num_sectors
+
+        if num_sectors < 2 * head_sectors:
+            return (num_sectors + 1) / 2 * sector_size
+        else:
+            return (num_sectors - head_sectors) * sector_size
+
+
+class ESP8266StubLoader(ESP8266ROM):
+    """ Access class for ESP8266 stub loader, runs on top of ROM.
+    """
+    FLASH_WRITE_SIZE = 0x4000  # matches MAX_WRITE_BLOCK in stub_loader.c
+    IS_STUB = True
+
+    def __init__(self, rom_loader):
+        self._port = rom_loader._port
+        self.flush_input()  # resets _slip_reader
+
+ESP8266ROM.STUB_CLASS = ESP8266StubLoader
+
+
+class ESP32ROM(ESPLoader):
+    """Access class for ESP32 ROM bootloader
+
+    """
+    CHIP_NAME = "ESP32"
+    IS_STUB = False
+
+    DATE_REG_VALUE = 0x15122500
+
+    IROM_MAP_START = 0x400d0000
+    IROM_MAP_END   = 0x40400000
+    DROM_MAP_START = 0x3F400000
+    DROM_MAP_END   = 0x3F700000
+
+    # ESP32 uses a 4 byte status reply
+    STATUS_BYTES_LENGTH = 4
+
+    SPI_REG_BASE   = 0x60002000
+    EFUSE_REG_BASE = 0x6001a000
+
+    SPI_W0_OFFS = 0x80
+    SPI_HAS_MOSI_DLEN_REG = True
+
+    FLASH_SIZES = {
+        '1MB':0x00,
+        '2MB':0x10,
+        '4MB':0x20,
+        '8MB':0x30,
+        '16MB':0x40
+    }
+
+    def read_efuse(self, n):
+        """ Read the nth word of the ESP3x EFUSE region. """
+        return self.read_reg(self.EFUSE_REG_BASE + (4 * n))
+
+    def chip_id(self):
+        word16 = self.read_efuse(16)
+        word17 = self.read_efuse(17)
+        return ((word17 & MAX_UINT24) << 24) | (word16 >> 8) & MAX_UINT24
+
+    def read_mac(self):
+        """ Read MAC from EFUSE region """
+        word16 = self.read_efuse(16)
+        word17 = self.read_efuse(17)
+        word18 = self.read_efuse(18)
+        word19 = self.read_efuse(19)
+        wifi_mac = (((word17 >> 16) & 0xff), ((word17 >> 8) & 0xff), ((word17 >> 0) & 0xff),
+                    ((word16 >> 24) & 0xff), ((word16 >> 16) & 0xff), ((word16 >> 8) & 0xff))
+        bt_mac = (((word19 >> 16) & 0xff), ((word19 >> 8) & 0xff), ((word19 >> 0) & 0xff),
+                  ((word18 >> 24) & 0xff), ((word18 >> 16) & 0xff), ((word18 >> 8) & 0xff))
+        return (wifi_mac,bt_mac)
+
+    def get_erase_size(self, offset, size):
+        return size
+
+
+class ESP32StubLoader(ESP32ROM):
+    """ Access class for ESP32 stub loader, runs on top of ROM.
+    """
+    FLASH_WRITE_SIZE = 0x4000  # matches MAX_WRITE_BLOCK in stub_loader.c
+    STATUS_BYTES_LENGTH = 2  # same as ESP8266, different to ESP32 ROM
+    IS_STUB = True
+
+    def __init__(self, rom_loader):
+        self._port = rom_loader._port
+        self.flush_input()  # resets _slip_reader
+
+ESP32ROM.STUB_CLASS = ESP32StubLoader
+
+
+class ESPBOOTLOADER(object):
+    """ These are constants related to software ESP bootloader, working with 'v2' image files """
+
+    # First byte of the "v2" application image
+    IMAGE_V2_MAGIC = 0xea
+
+    # First 'segment' value in a "v2" application image, appears to be a constant version value?
+    IMAGE_V2_SEGMENT = 4
+
+
+def LoadFirmwareImage(chip, filename):
+    """ Load a firmware image. Can be for ESP8266 or ESP32. ESP8266 images will be examined to determine if they are
+        original ROM firmware images (ESPFirmwareImage) or "v2" OTA bootloader images.
+
+        Returns a BaseFirmwareImage subclass, either ESPFirmwareImage (v1) or OTAFirmwareImage (v2).
+    """
+    with open(filename, 'rb') as f:
+        if chip == 'esp32':
+            return ESP32FirmwareImage(f)
+        else:  # Otherwise, ESP8266 so look at magic to determine the image type
+            magic = ord(f.read(1))
+            f.seek(0)
+            if magic == ESPLoader.ESP_IMAGE_MAGIC:
+                return ESPFirmwareImage(f)
+            elif magic == ESPBOOTLOADER.IMAGE_V2_MAGIC:
+                return OTAFirmwareImage(f)
+            else:
+                raise FatalError("Invalid image magic number: %d" % magic)
+
+
+class ImageSegment(object):
+    """ Wrapper class for a segment in an ESP image
+    (very similar to a section in an ELFImage also) """
+    def __init__(self, addr, data, file_offs=None):
+        self.addr = addr
+        # pad all ImageSegments to at least 4 bytes length
+        pad_mod = len(data) % 4
+        if pad_mod != 0:
+            data += "\x00" * (4 - pad_mod)
+        self.data = data
+        self.file_offs = file_offs
+        self.include_in_checksum = True
+
+    def copy_with_new_addr(self, new_addr):
+        """ Return a new ImageSegment with same data, but mapped at
+        a new address. """
+        return ImageSegment(new_addr, self.data, 0)
+
+    def __repr__(self):
+        r = "len 0x%05x load 0x%08x" % (len(self.data), self.addr)
+        if self.file_offs is not None:
+            r += " file_offs 0x%08x" % (self.file_offs)
+        return r
+
+
+class ELFSection(ImageSegment):
+    """ Wrapper class for a section in an ELF image, has a section
+    name as well as the common properties of an ImageSegment. """
+    def __init__(self, name, addr, data):
+        super(ELFSection, self).__init__(addr, data)
+        self.name = name
+
+    def __repr__(self):
+        return "%s %s" % (self.name, super(ELFSection, self).__repr__())
+
+
+class BaseFirmwareImage(object):
+    SEG_HEADER_LEN = 8
+
+    """ Base class with common firmware image functions """
+    def __init__(self):
+        self.segments = []
+        self.entrypoint = 0
+
+    def load_common_header(self, load_file, expected_magic):
+            (magic, segments, self.flash_mode, self.flash_size_freq, self.entrypoint) = struct.unpack(' 16:
+                raise FatalError('Invalid firmware image magic=%d segments=%d' % (magic, segments))
+            return segments
+
+    def load_segment(self, f, is_irom_segment=False):
+        """ Load the next segment from the image file """
+        file_offs = f.tell()
+        (offset, size) = struct.unpack(' 0x40200000 or offset < 0x3ffe0000 or size > 65536:
+                print('WARNING: Suspicious segment 0x%x, length %d' % (offset, size))
+
+    def save_segment(self, f, segment, checksum=None):
+        """ Save the next segment to the image file, return next checksum value if provided """
+        f.write(struct.pack(' 0:
+                if len(irom_segments) != 1:
+                    raise FatalError('Found %d segments that could be irom0. Bad ELF file?' % len(irom_segments))
+                return irom_segments[0]
+            return None
+
+    def get_non_irom_segments(self):
+        irom_segment = self.get_irom_segment()
+        return [s for s in self.segments if s != irom_segment]
+
+
+class ESPFirmwareImage(BaseFirmwareImage):
+    """ 'Version 1' firmware image, segments loaded directly by the ROM bootloader. """
+
+    ROM_LOADER = ESP8266ROM
+
+    def __init__(self, load_file=None):
+        super(ESPFirmwareImage, self).__init__()
+        self.flash_mode = 0
+        self.flash_size_freq = 0
+        self.version = 1
+
+        if load_file is not None:
+            segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC)
+
+            for _ in xrange(segments):
+                self.load_segment(load_file)
+            self.checksum = self.read_checksum(load_file)
+
+    def default_output_name(self, input_file):
+        """ Derive a default output name from the ELF name. """
+        return input_file + '-'
+
+    def save(self, basename):
+        """ Save a set of V1 images for flashing. Parameter is a base filename. """
+        # IROM data goes in its own plain binary file
+        irom_segment = self.get_irom_segment()
+        if irom_segment is not None:
+            with open("%s0x%05x.bin" % (basename, irom_segment.addr - ESP8266ROM.IROM_MAP_START), "wb") as f:
+                f.write(irom_segment.data)
+
+        # everything but IROM goes at 0x00000 in an image file
+        normal_segments = self.get_non_irom_segments()
+        with open("%s0x00000.bin" % basename, 'wb') as f:
+            self.write_common_header(f, normal_segments)
+            checksum = ESPLoader.ESP_CHECKSUM_MAGIC
+            for segment in normal_segments:
+                checksum = self.save_segment(f, segment, checksum)
+            self.append_checksum(f, checksum)
+
+
+class OTAFirmwareImage(BaseFirmwareImage):
+    """ 'Version 2' firmware image, segments loaded by software bootloader stub
+        (ie Espressif bootloader or rboot)
+    """
+
+    ROM_LOADER = ESP8266ROM
+
+    def __init__(self, load_file=None):
+        super(OTAFirmwareImage, self).__init__()
+        self.version = 2
+        if load_file is not None:
+            segments = self.load_common_header(load_file, ESPBOOTLOADER.IMAGE_V2_MAGIC)
+            if segments != ESPBOOTLOADER.IMAGE_V2_SEGMENT:
+                # segment count is not really segment count here, but we expect to see '4'
+                print 'Warning: V2 header has unexpected "segment" count %d (usually 4)' % segments
+
+            # irom segment comes before the second header
+            #
+            # the file is saved in the image with a zero load address
+            # in the header, so we need to calculate a load address
+            irom_segment = self.load_segment(load_file, True)
+            # for actual mapped addr, add ESP8266ROM.IROM_MAP_START + flashing_Addr + 8
+            irom_segment.addr = 0
+            irom_segment.include_in_checksum = False
+
+            first_flash_mode = self.flash_mode
+            first_flash_size_freq = self.flash_size_freq
+            first_entrypoint = self.entrypoint
+            # load the second header
+
+            segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC)
+
+            if first_flash_mode != self.flash_mode:
+                print('WARNING: Flash mode value in first header (0x%02x) disagrees with second (0x%02x). Using second value.'
+                      % (first_flash_mode, self.flash_mode))
+            if first_flash_size_freq != self.flash_size_freq:
+                print('WARNING: Flash size/freq value in first header (0x%02x) disagrees with second (0x%02x). Using second value.'
+                      % (first_flash_size_freq, self.flash_size_freq))
+            if first_entrypoint != self.entrypoint:
+                print('WARNING: Entrypoint address in first header (0x%08x) disagrees with second header (0x%08x). Using second value.'
+                      % (first_entrypoint, self.entrypoint))
+
+            # load all the usual segments
+            for _ in xrange(segments):
+                self.load_segment(load_file)
+            self.checksum = self.read_checksum(load_file)
+
+    def default_output_name(self, input_file):
+        """ Derive a default output name from the ELF name. """
+        irom_segment = self.get_irom_segment()
+        if irom_segment is not None:
+            irom_offs = irom_segment.addr - ESP8266ROM.IROM_MAP_START
+        else:
+            irom_offs = 0
+        return "%s-0x%05x.bin" % (os.path.splitext(input_file)[0],
+                                  irom_offs & ~(ESPLoader.ESP_FLASH_SECTOR - 1))
+
+    def save(self, filename):
+        with open(filename, 'wb') as f:
+            # Save first header for irom0 segment
+            f.write(struct.pack(' 0:
+                        null = ImageSegment(0, '\x00' * pad_len, f.tell())
+                        checksum = self.save_segment(f, null, checksum)
+                        padding_segments += 1
+                    # verify that after the 8 byte header is added, were are at the correct offset relative to the segment's vaddr
+                    assert (f.tell() + 8) % IROM_ALIGN == segment.addr % IROM_ALIGN
+                checksum = self.save_segment(f, segment, checksum)
+            self.append_checksum(f, checksum)
+            # kinda hacky: go back to the initial header and write the new segment count
+            # that includes padding segments. Luckily(?) this header is not checksummed
+            f.seek(1)
+            f.write(chr(len(self.segments) + padding_segments))
+
+
+class ELFFile(object):
+    SEC_TYPE_PROGBITS = 0x01
+    SEC_TYPE_STRTAB = 0x03
+
+    def __init__(self, name):
+        # Load sections from the ELF file
+        self.name = name
+        self.symbols = None
+        with open(self.name, 'rb') as f:
+            self._read_elf_file(f)
+
+    def get_section(self, section_name):
+        for s in self.sections:
+            if s.name == section_name:
+                return s
+        raise ValueError("No section %s in ELF file" % section_name)
+
+    def _read_elf_file(self, f):
+        # read the ELF file header
+        LEN_FILE_HEADER = 0x34
+        try:
+            (ident,_type,machine,_version,
+             self.entrypoint,_phoff,shoff,_flags,
+             _ehsize, _phentsize,_phnum,_shentsize,
+             _shnum,shstrndx) = struct.unpack("<16sHHLLLLLHHHHHH", f.read(LEN_FILE_HEADER))
+        except struct.error as e:
+            raise FatalError("Failed to read a valid ELF header from %s: %s" % (self.name, e))
+
+        if ident[0] != '\x7f' or ident[1:4] != 'ELF':
+            raise FatalError("%s has invalid ELF magic header" % self.name)
+        if machine != 0x5e:
+            raise FatalError("%s does not appear to be an Xtensa ELF file. e_machine=%04x" % (self.name, machine))
+        self._read_sections(f, shoff, shstrndx)
+
+    def _read_sections(self, f, section_header_offs, shstrndx):
+        f.seek(section_header_offs)
+        section_header = f.read()
+        LEN_SEC_HEADER = 0x28
+        if len(section_header) == 0:
+            raise FatalError("No section header found at offset %04x in ELF file." % section_header_offs)
+        if len(section_header) % LEN_SEC_HEADER != 0:
+            print 'WARNING: Unexpected ELF section header length %04x is not mod-%02x' % (len(section_header),LEN_SEC_HEADER)
+
+        # walk through the section header and extract all sections
+        section_header_offsets = range(0, len(section_header), LEN_SEC_HEADER)
+
+        def read_section_header(offs):
+            name_offs,sec_type,_flags,lma,sec_offs,size = struct.unpack_from(", ) or a single 
+# argument.
+
+
+def load_ram(esp, args):
+    image = LoadFirmwareImage(esp, args.filename)
+
+    print 'RAM boot...'
+    for (offset, size, data) in image.segments:
+        print 'Downloading %d bytes at %08x...' % (size, offset),
+        sys.stdout.flush()
+        esp.mem_begin(size, div_roundup(size, esp.ESP_RAM_BLOCK), esp.ESP_RAM_BLOCK, offset)
+
+        seq = 0
+        while len(data) > 0:
+            esp.mem_block(data[0:esp.ESP_RAM_BLOCK], seq)
+            data = data[esp.ESP_RAM_BLOCK:]
+            seq += 1
+        print 'done!'
+
+    print 'All segments done, executing at %08x' % image.entrypoint
+    esp.mem_finish(image.entrypoint)
+
+
+def read_mem(esp, args):
+    print '0x%08x = 0x%08x' % (args.address, esp.read_reg(args.address))
+
+
+def write_mem(esp, args):
+    esp.write_reg(args.address, args.value, args.mask, 0)
+    print 'Wrote %08x, mask %08x to %08x' % (args.value, args.mask, args.address)
+
+
+def dump_mem(esp, args):
+    f = file(args.filename, 'wb')
+    for i in xrange(args.size / 4):
+        d = esp.read_reg(args.address + (i * 4))
+        f.write(struct.pack(' flash_end:
+            raise FatalError(("File %s (length %d) at offset %d will not fit in %d bytes of flash. " +
+                             "Use --flash-size argument, or change flashing address.")
+                             % (argfile.name, argfile.tell(), address, flash_end))
+        argfile.seek(0)
+
+    for address, argfile in args.addr_filename:
+        print 'Erasing flash...'
+        image = argfile.read()
+        # Update header with flash parameters
+        if address == 0 and image[0] == '\xe9':
+            image = image[0:2] + flash_info + image[4:]
+        calcmd5 = hashlib.md5(image).hexdigest()
+        uncsize = len(image)
+        if args.compress:
+            uncimage = image
+            image = zlib.compress(uncimage, 9)
+            blocks = div_roundup(len(image), esp.FLASH_WRITE_SIZE)
+            esp.flash_defl_begin(len(uncimage),len(image), address)
+        else:
+            blocks = div_roundup(len(image), esp.FLASH_WRITE_SIZE)
+            esp.flash_begin(blocks * esp.FLASH_WRITE_SIZE, address)
+        argfile.seek(0)  # in case we need it again
+        seq = 0
+        written = 0
+        t = time.time()
+        header_block = None
+        while len(image) > 0:
+            print '\rWriting at 0x%08x... (%d %%)' % (address + seq * esp.FLASH_WRITE_SIZE, 100 * (seq + 1) / blocks),
+            sys.stdout.flush()
+            block = image[0:esp.FLASH_WRITE_SIZE]
+            if args.compress:
+                esp.flash_defl_block(block, seq)
+            else:
+                # Pad the last block
+                block = block + '\xff' * (esp.FLASH_WRITE_SIZE - len(block))
+                esp.flash_block(block, seq)
+            image = image[esp.FLASH_WRITE_SIZE:]
+            seq += 1
+            written += len(block)
+        t = time.time() - t
+        speed_msg = ""
+        if args.compress:
+            if t > 0.0:
+                speed_msg = " (effective %.1f kbit/s)" % (uncsize / t * 8 / 1000)
+            print '\rWrote %d bytes (%d compressed) at 0x%08x in %.1f seconds%s...' % (uncsize, written, address, t, speed_msg)
+        else:
+            if t > 0.0:
+                speed_msg = " (%.1f kbit/s)" % (written / t * 8 / 1000)
+            print '\rWrote %d bytes at 0x%08x in %.1f seconds%s...' % (written, address, t, speed_msg)
+        res = esp.flash_md5sum(address, uncsize)
+        if res != calcmd5:
+            print 'File  md5: %s' % calcmd5
+            print 'Flash md5: %s' % res
+            raise FatalError("MD5 of file does not match data in flash!")
+        else:
+            print 'Hash of data verified.'
+    print '\nLeaving...'
+    if args.flash_mode == 'dio' and esp.CHIP_NAME == "ESP8266":
+        esp.flash_unlock_dio()
+    else:
+        esp.flash_begin(0, 0)
+        if args.compress:
+            esp.flash_defl_finish(False)
+        else:
+            esp.flash_finish(False)
+    if args.verify:
+        print 'Verifying just-written flash...'
+        verify_flash(esp, args, header_block)
+
+
+def image_info(args):
+    image = LoadFirmwareImage(args.chip, args.filename)
+    print('Image version: %d' % image.version)
+    print('Entry point: %08x' % image.entrypoint) if image.entrypoint != 0 else 'Entry point not set'
+    print '%d segments' % len(image.segments)
+    print
+    idx = 0
+    for seg in image.segments:
+        idx += 1
+        print 'Segment %d: %r' % (idx, seg)
+    calc_checksum = image.calculate_checksum()
+    print 'Checksum: %02x (%s)' % (image.checksum,
+                                   'valid' if image.checksum == calc_checksum else 'invalid - calculated %02x' % calc_checksum)
+
+
+def make_image(args):
+    image = ESPFirmwareImage()
+    if len(args.segfile) == 0:
+        raise FatalError('No segments specified')
+    if len(args.segfile) != len(args.segaddr):
+        raise FatalError('Number of specified files does not match number of specified addresses')
+    for (seg, addr) in zip(args.segfile, args.segaddr):
+        data = file(seg, 'rb').read()
+        image.segments.append(ImageSegment(addr, data))
+    image.entrypoint = args.entrypoint
+    image.save(args.output)
+
+
+def elf2image(args):
+    e = ELFFile(args.input)
+    if args.chip == 'auto':  # Default to ESP8266 for backwards compatibility
+        print "Creating image for ESP8266..."
+        args.chip == 'esp8266'
+
+    if args.chip == 'esp32':
+        image = ESP32FirmwareImage()
+    elif args.version == '1':  # ESP8266
+        image = ESPFirmwareImage()
+    else:
+        image = OTAFirmwareImage()
+    image.entrypoint = e.entrypoint
+    image.segments = e.sections  # ELFSection is a subclass of ImageSegment
+    image.flash_mode = {'qio':0, 'qout':1, 'dio':2, 'dout': 3}[args.flash_mode]
+    image.flash_size_freq = image.ROM_LOADER.FLASH_SIZES[args.flash_size]
+    image.flash_size_freq += {'40m':0, '26m':1, '20m':2, '80m': 0xf}[args.flash_freq]
+
+    if args.output is None:
+        args.output = image.default_output_name(args.input)
+    image.save(args.output)
+
+
+def read_mac(esp, args):
+    mac = esp.read_mac()
+    print 'MAC: %s' % ':'.join(map(lambda x: '%02x' % x, mac))
+
+
+def chip_id(esp, args):
+    chipid = esp.chip_id()
+    print 'Chip ID: 0x%08x' % chipid
+
+
+def erase_flash(esp, args):
+    print 'Erasing flash (this may take a while)...'
+    t = time.time()
+    esp.erase_flash()
+    print 'Chip erase completed successfully in %.1fs' % (time.time() - t)
+
+
+def erase_region(esp, args):
+    print 'Erasing region (may be slow depending on size)...'
+    t = time.time()
+    esp.erase_region(args.address, args.size)
+    print 'Erase completed successfully in %.1f seconds.' % (time.time() - t)
+
+
+def run(esp, args):
+    esp.run()
+
+
+def flash_id(esp, args):
+    flash_id = esp.flash_id()
+    print 'Manufacturer: %02x' % (flash_id & 0xff)
+    print 'Device: %02x%02x' % ((flash_id >> 8) & 0xff, (flash_id >> 16) & 0xff)
+
+
+def read_flash(esp, args):
+    if args.no_progress:
+        flash_progress = None
+    else:
+        def flash_progress(progress, length):
+            msg = '%d (%d %%)' % (progress, progress * 100.0 / length)
+            padding = '\b' * len(msg)
+            if progress == length:
+                padding = '\n'
+            sys.stdout.write(msg + padding)
+            sys.stdout.flush()
+    t = time.time()
+    data = esp.read_flash(args.address, args.size, flash_progress)
+    t = time.time() - t
+    print ('\rRead %d bytes at 0x%x in %.1f seconds (%.1f kbit/s)...'
+           % (len(data), args.address, t, len(data) / t * 8 / 1000))
+    file(args.filename, 'wb').write(data)
+
+
+def verify_flash(esp, args, flash_params=None):
+    differences = False
+    for address, argfile in args.addr_filename:
+        image = argfile.read()
+        argfile.seek(0)  # rewind in case we need it again
+        if address == 0 and image[0] == '\xe9' and flash_params is not None:
+            image = image[0:2] + flash_params + image[4:]
+        image_size = len(image)
+        print 'Verifying 0x%x (%d) bytes @ 0x%08x in flash against %s...' % (image_size, image_size, address, argfile.name)
+        # Try digest first, only read if there are differences.
+        digest = esp.flash_md5sum(address, image_size)
+        expected_digest = hashlib.md5(image).hexdigest()
+        if digest == expected_digest:
+            print '-- verify OK (digest matched)'
+            continue
+        else:
+            differences = True
+            if getattr(args, 'diff', 'no') != 'yes':
+                print '-- verify FAILED (digest mismatch)'
+                continue
+
+        flash = esp.read_flash(address, image_size)
+        assert flash != image
+        diff = [i for i in xrange(image_size) if flash[i] != image[i]]
+        print '-- verify FAILED: %d differences, first @ 0x%08x' % (len(diff), address + diff[0])
+        for d in diff:
+            print '   %08x %02x %02x' % (address + d, ord(flash[d]), ord(image[d]))
+    if differences:
+        raise FatalError("Verify failed.")
+
+
+def read_flash_status(esp, args):
+    print ('Status value: 0x%04x' % esp.read_status(args.bytes))
+
+
+def write_flash_status(esp, args):
+    fmt = "0x%%0%dx" % (args.bytes * 2)
+    args.value = args.value & ((1 << (args.bytes * 8)) - 1)
+    print (('Initial flash status: ' + fmt) % esp.read_status(args.bytes))
+    print (('Setting flash status: ' + fmt) % args.value)
+    esp.write_status(args.value, args.bytes, args.non_volatile)
+    print (('After flash status:   ' + fmt) % esp.read_status(args.bytes))
+
+
+def version(args):
+    print __version__
+
+
+def wrap_stub(args):
+    e = ELFFile(args.input)
+
+    stub = {
+        'params_start': e.get_symbol_addr('_params_start'),
+        'code': e.get_section('.text').data,
+        'code_start': e.get_symbol_addr('_code_start'),
+        'entry': e.get_symbol_addr(args.entry),
+    }
+    try:
+        stub['data'] = e.get_section('.data').data
+        stub['data_start'] = e.get_symbol_addr('_data_start')
+    except ValueError:
+        pass
+        # No data section, that's fine
+    bss_size, bss_start = 0, 0
+    try:
+        bss_start = e.get_symbol_addr('_bss_start')
+        bss_size = e.get_symbol_addr('_bss_end') - bss_start
+    except ValueError:
+        pass
+    params_len = e.get_symbol_addr('_params_end') - stub['params_start']
+    if params_len % 4 != 0:
+        raise FatalError('Params must be dwords')
+    stub['num_params'] = params_len / 4
+
+    # Pad code with NOPs to mod 4.
+    if len(stub['code']) % 4 != 0:
+        stub['code'] += (4 - (len(stub['code']) % 4)) * '\0'
+
+    print >>sys.stderr, (
+            'Stub params: %d @ 0x%08x, code: %d @ 0x%08x, bss: %d @ 0x%08x, data: %d @ 0x%08x, entry: %s @ 0x%x' % (
+            params_len, stub['params_start'],
+            len(stub['code']), stub['code_start'],
+            bss_size, bss_start,
+            len(stub.get('data', '')), stub.get('data_start', 0),
+            args.entry, stub['entry']))
+
+    jstub = dict(stub)
+    jstub['code'] = hexify(stub['code'])
+    if 'data' in stub:
+        jstub['data'] = hexify(stub['data'])
+    print json.dumps(jstub)
+
+
+def run_stub(esp, args):
+    esp.run_custom_stub(json.load(open(args.input)), args.params, read_output=True)
+
+#
+# End of operations functions
+#
+
+
+def main():
+    parser = argparse.ArgumentParser(description='esptool.py v%s - ESP8266 ROM Bootloader Utility' % __version__, prog='esptool')
+
+    parser.add_argument('--chip', '-c',
+                        help='Target chip type',
+                        choices=['auto', 'esp8266', 'esp31', 'esp32'],
+                        default=os.environ.get('ESPTOOL_CHIP', 'auto'))
+
+    parser.add_argument(
+        '--port', '-p',
+        help='Serial port device',
+        default=os.environ.get('ESPTOOL_PORT', ESPLoader.DEFAULT_PORT))
+
+    parser.add_argument(
+        '--baud', '-b',
+        help='Serial port baud rate used when flashing/reading',
+        type=arg_auto_int,
+        default=os.environ.get('ESPTOOL_BAUD', ESPLoader.ESP_ROM_BAUD))
+
+    parser.add_argument(
+        '--no-stub',
+        help="Disable launching the flasher stub, only talk to ROM bootloader. Some features will not be available.",
+        action='store_true')
+
+    subparsers = parser.add_subparsers(
+        dest='operation',
+        help='Run esptool {command} -h for additional help')
+
+    parser_load_ram = subparsers.add_parser(
+        'load_ram',
+        help='Download an image to RAM and execute')
+    parser_load_ram.add_argument('filename', help='Firmware image')
+
+    parser_dump_mem = subparsers.add_parser(
+        'dump_mem',
+        help='Dump arbitrary memory to disk')
+    parser_dump_mem.add_argument('address', help='Base address', type=arg_auto_int)
+    parser_dump_mem.add_argument('size', help='Size of region to dump', type=arg_auto_int)
+    parser_dump_mem.add_argument('filename', help='Name of binary dump')
+
+    parser_read_mem = subparsers.add_parser(
+        'read_mem',
+        help='Read arbitrary memory location')
+    parser_read_mem.add_argument('address', help='Address to read', type=arg_auto_int)
+
+    parser_write_mem = subparsers.add_parser(
+        'write_mem',
+        help='Read-modify-write to arbitrary memory location')
+    parser_write_mem.add_argument('address', help='Address to write', type=arg_auto_int)
+    parser_write_mem.add_argument('value', help='Value', type=arg_auto_int)
+    parser_write_mem.add_argument('mask', help='Mask of bits to write', type=arg_auto_int)
+
+    def add_spi_flash_subparsers(parent):
+        """ Add common parser arguments for SPI flash properties """
+        parent.add_argument('--flash_freq', '-ff', help='SPI Flash frequency',
+                            choices=['40m', '26m', '20m', '80m'],
+                            default=os.environ.get('ESPTOOL_FF', '40m'))
+        parent.add_argument('--flash_mode', '-fm', help='SPI Flash mode',
+                            choices=['qio', 'qout', 'dio', 'dout'],
+                            default=os.environ.get('ESPTOOL_FM', 'qio'))
+        parent.add_argument('--flash_size', '-fs', help='SPI Flash size in MegaBytes (1MB, 2MB, 4MB, 8MB, 16M)'
+                            ' plus ESP8266-only (256KB, 512KB, 2MB-c1, 4MB-c1, 4MB-2)',
+                            action=FlashSizeAction,
+                            default=os.environ.get('ESPTOOL_FS', '1MB'))
+        parent.add_argument('--ucIsHspi', '-ih', help='Config SPI PORT/PINS (Espressif internal feature)',action='store_true')
+        parent.add_argument('--ucIsLegacy', '-il', help='Config SPI LEGACY (Espressif internal feature)',action='store_true')
+
+    parser_write_flash = subparsers.add_parser(
+        'write_flash',
+        help='Write a binary blob to flash')
+    parser_write_flash.add_argument('addr_filename', metavar='
', help='Address followed by binary filename, separated by space', + action=AddrFilenamePairAction) + add_spi_flash_subparsers(parser_write_flash) + parser_write_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") + parser_write_flash.add_argument('--verify', help='Verify just-written data (only necessary if very cautious, data is already CRCed', action='store_true') + parser_write_flash.add_argument('--compress', '-z', help='Compress data in transfer',action="store_true") + + subparsers.add_parser( + 'run', + help='Run application code in flash') + + parser_image_info = subparsers.add_parser( + 'image_info', + help='Dump headers from an application image') + parser_image_info.add_argument('filename', help='Image file to parse') + + parser_make_image = subparsers.add_parser( + 'make_image', + help='Create an application image from binary files') + parser_make_image.add_argument('output', help='Output image file') + parser_make_image.add_argument('--segfile', '-f', action='append', help='Segment input file') + parser_make_image.add_argument('--segaddr', '-a', action='append', help='Segment base address', type=arg_auto_int) + parser_make_image.add_argument('--entrypoint', '-e', help='Address of entry point', type=arg_auto_int, default=0) + + parser_elf2image = subparsers.add_parser( + 'elf2image', + help='Create an application image from ELF file') + parser_elf2image.add_argument('input', help='Input ELF file') + parser_elf2image.add_argument('--output', '-o', help='Output filename prefix (for version 1 image), or filename (for version 2 single image)', type=str) + parser_elf2image.add_argument('--version', '-e', help='Output image version', choices=['1','2'], default='1') + add_spi_flash_subparsers(parser_elf2image) + + subparsers.add_parser( + 'read_mac', + help='Read MAC address from OTP ROM') + + subparsers.add_parser( + 'chip_id', + help='Read Chip ID from OTP ROM') + + subparsers.add_parser( + 'flash_id', + help='Read SPI flash manufacturer and device ID') + + parser_read_status = subparsers.add_parser( + 'read_flash_status', + help='Read SPI flash status register') + + parser_read_status.add_argument('--bytes', help='Number of bytes to read (1-3)', type=int, choices=[1,2,3], default=2) + + parser_write_status = subparsers.add_parser( + 'write_flash_status', + help='Write SPI flash status register') + + parser_write_status.add_argument('--non-volatile', help='Write non-volatile bits (use with caution)', action='store_true') + parser_write_status.add_argument('--bytes', help='Number of status bytes to write (1-3)', type=int, choices=[1,2,3], default=2) + parser_write_status.add_argument('value', help='New value', type=arg_auto_int) + + parser_read_flash = subparsers.add_parser( + 'read_flash', + help='Read SPI flash content') + parser_read_flash.add_argument('address', help='Start address', type=arg_auto_int) + parser_read_flash.add_argument('size', help='Size of region to dump', type=arg_auto_int) + parser_read_flash.add_argument('filename', help='Name of binary dump') + parser_read_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") + + parser_verify_flash = subparsers.add_parser( + 'verify_flash', + help='Verify a binary blob against flash') + parser_verify_flash.add_argument('addr_filename', help='Address and binary file to verify there, separated by space', + action=AddrFilenamePairAction) + parser_verify_flash.add_argument('--diff', '-d', help='Show differences', + choices=['no', 'yes'], default='no') + + subparsers.add_parser( + 'erase_flash', + help='Perform Chip Erase on SPI flash') + + parser_erase_region = subparsers.add_parser( + 'erase_region', + help='Erase a region of the flash') + parser_erase_region.add_argument('address', help='Start address (must be multiple of 4096)', type=arg_auto_int) + parser_erase_region.add_argument('size', help='Size of region to erase (must be multiple of 4096)', type=arg_auto_int) + + subparsers.add_parser( + 'version', help='Print esptool version') + + parser_wrap_stub = subparsers.add_parser('wrap_stub', help='Wrap stub and output a JSON object') + parser_wrap_stub.add_argument('input') + parser_wrap_stub.add_argument('--entry', default='stub_main') + + parser_run_stub = subparsers.add_parser('run_stub', help='Run stub on a device') + parser_run_stub.add_argument('--entry', default='stub_main') + parser_run_stub.add_argument('input') + parser_run_stub.add_argument('params', nargs='*', type=arg_auto_int) + + # internal sanity check - every operation matches a module function of the same name + for operation in subparsers.choices.keys(): + assert operation in globals(), "%s should be a module function" % operation + + args = parser.parse_args() + + # operation function can take 1 arg (args), 2 args (esp, arg) + # or be a member function of the ESPLoader class. + + operation_func = globals()[args.operation] + operation_args,_,_,_ = inspect.getargspec(operation_func) + if operation_args[0] == 'esp': # operation function takes an ESPLoader connection object + initial_baud = min(ESPLoader.ESP_ROM_BAUD, args.baud) # don't sync faster than the default baud rate + chip_constructor_fun = { + 'auto': ESPLoader.detect_chip, + 'esp8266': ESP8266ROM, + 'esp32': ESP32ROM, + }[args.chip] + esp = chip_constructor_fun(args.port, initial_baud) + + if not args.no_stub and args.operation != 'run_stub': + esp = esp.run_stub() + + if args.baud > initial_baud: + esp.change_baud(args.baud) + # TODO: handle a NotImplementedInROMError + + # override common SPI flash parameter stuff as required + if hasattr(args, "ucIsHspi"): + print "Attaching SPI flash..." + esp.flash_spi_attach(args.ucIsHspi,args.ucIsLegacy) + else: + esp.flash_spi_attach(0, 0) + if hasattr(args, "flash_size"): + print "Configuring flash size..." + esp.flash_set_parameters(flash_size_bytes(args.flash_size)) + + operation_func(esp, args) + else: + operation_func(args) + + +class FlashSizeAction(argparse.Action): + """ Custom flash size parser class to support backwards compatibility with megabit size arguments. + + (At next major relase, remove deprecated sizes and this can become a 'normal' choices= argument again.) + """ + def __init__(self, option_strings, dest, nargs=1, **kwargs): + super(FlashSizeAction, self).__init__(option_strings, dest, nargs, **kwargs) + + def __call__(self, parser, namespace, values, option_string=None): + try: + value = { + '2m': '256KB', + '4m': '512KB', + '8m': '1MB', + '16m': '2MB', + '32m': '4MB', + '16m-c1': '2MB-c1', + '32m-c1': '4MB-c1', + '32m-c2': '4MB-c2' + }[values[0]] + print("WARNING: Flash size arguments in megabits like '%s' are deprecated." % (values[0])) + print("Please use the equivalent size '%s'." % (value)) + print("Megabit arguments may be removed in a future release.") + except KeyError: + value = values[0] + + known_sizes = dict(ESP8266ROM.FLASH_SIZES) + known_sizes.update(ESP32ROM.FLASH_SIZES) + if value not in known_sizes: + raise argparse.ArgumentError(self, '%s is not a known flash size. Known sizes: %s' % (value, ", ".join(known_sizes.keys()))) + setattr(namespace, self.dest, value) + + +class AddrFilenamePairAction(argparse.Action): + """ Custom parser class for the address/filename pairs passed as arguments """ + def __init__(self, option_strings, dest, nargs='+', **kwargs): + super(AddrFilenamePairAction, self).__init__(option_strings, dest, nargs, **kwargs) + + def __call__(self, parser, namespace, values, option_string=None): + # validate pair arguments + pairs = [] + for i in range(0,len(values),2): + try: + address = int(values[i],0) + except ValueError as e: + raise argparse.ArgumentError(self,'Address "%s" must be a number' % values[i]) + try: + argfile = open(values[i + 1], 'rb') + except IOError as e: + raise argparse.ArgumentError(self, e) + except IndexError: + raise argparse.ArgumentError(self,'Must be pairs of an address and the binary filename to write there') + pairs.append((address, argfile)) + setattr(namespace, self.dest, pairs) + +# Binary stub code (see flasher_stub dir for source & details) +ESP8266ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" +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\ +"""))) +ESP32ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" +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\ +"""))) + +if __name__ == '__main__': + try: + main() + except FatalError as e: + print '\nA fatal error occurred: %s' % e + sys.exit(2) diff --git a/src/mongoose-6.11/src/common/platforms/esp/slip.c b/src/mongoose-6.11/src/common/platforms/esp/slip.c new file mode 100644 index 0000000..13b980e --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp/slip.c @@ -0,0 +1,35 @@ +#include "rom_functions.h" + +void SLIP_send(uint8_t *pkt, uint32_t size) { + send_packet(pkt, size); +} + +uint32_t SLIP_recv(void *pkt, uint32_t max_len) { + uint8_t c; + uint32_t len = 0; + uint8_t *p = (uint8_t *) pkt; + do { + c = uart_rx_one_char_block(); + } while (c != '\xc0'); + while (len < max_len) { + c = uart_rx_one_char_block(); + if (c == '\xc0') return len; + if (c == '\xdb') { + c = uart_rx_one_char_block(); + if (c == '\xdc') { + c = '\xc0'; + } else if (c == '\xdd') { + c = '\xdb'; + } else { + len = 0; + break; /* Bad esc sequence. */ + } + } + *p++ = c; + len++; + } + do { + c = uart_rx_one_char_block(); + } while (c != '\xc0'); + return len; +} diff --git a/src/mongoose-6.11/src/common/platforms/esp/slip.h b/src/mongoose-6.11/src/common/platforms/esp/slip.h new file mode 100644 index 0000000..53100a0 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp/slip.h @@ -0,0 +1,9 @@ +#ifndef CS_COMMON_PLATFORMS_ESP8266_STUBS_SLIP_H_ +#define CS_COMMON_PLATFORMS_ESP8266_STUBS_SLIP_H_ + +#include + +void SLIP_send(const void *pkt, uint32_t size); +uint32_t SLIP_recv(void *pkt, uint32_t max_len); + +#endif /* CS_COMMON_PLATFORMS_ESP8266_STUBS_SLIP_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp/stub_flasher.c b/src/mongoose-6.11/src/common/platforms/esp/stub_flasher.c new file mode 100644 index 0000000..be7398c --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp/stub_flasher.c @@ -0,0 +1,559 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + * + * Spiffy flasher. Implements strong checksums (MD5) and can use higher + * baud rates. Actual max baud rate will differe from device to device, + * but 921K seems to be common. + * + * SLIP protocol is used for communication. + * First packet is a single byte - command number. + * After that, a packet with a variable number of 32-bit (LE) arguments, + * depending on command. + * + * Then command produces variable number of packets of output, but first + * packet of length 1 is the response code: 0 for success, non-zero - error. + * + * See individual command description below. + */ + +#include "stub_flasher.h" + +#include +#include + +#include "rom_functions.h" + +#if defined(ESP8266) +#include "eagle_soc.h" +#include "ets_sys.h" +#include "../../../miniz.c" +#elif defined(ESP32) +#include "rom/efuse.h" +#include "rom/miniz.h" +#include "rom/spi_flash.h" +#include "soc/uart_reg.h" +#include "led.h" +#endif + +#include "slip.h" +#include "uart.h" + +/* Param: baud rate. */ +uint32_t params[1] __attribute__((section(".params"))); + +#define FLASH_BLOCK_SIZE 65536 +#define FLASH_SECTOR_SIZE 4096 +#define FLASH_PAGE_SIZE 256 + +/* These consts should be in sync with flasher_client.go */ +#define NUM_BUFS 4 +#define BUF_SIZE 4096 +#define FLASH_WRITE_SIZE BUF_SIZE + +#define UART_RX_INTS (UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA) + +extern uint32_t _bss_start, _bss_end; + +#ifdef ESP8266 +#define REG_SPI_BASE(i) (0x60000200 - i * 0x100) + +#define SPI_CMD_REG(i) (REG_SPI_BASE(i) + 0x0) +#define SPI_FLASH_WREN (BIT(30)) +#define SPI_FLASH_RDID (BIT(28)) +#define SPI_FLASH_SE (BIT(24)) +#define SPI_FLASH_BE (BIT(23)) + +#define SPI_ADDR_REG(i) (REG_SPI_BASE(i) + 0x4) + +#define SPI_USER_REG(i) (REG_SPI_BASE(i) + 0x1C) + +#define SPI_W0_REG(i) (REG_SPI_BASE(i) + 0x40) +#endif + +enum read_state { + READ_WAIT_START = 0, + READ_FLAGS, + READ_DATA, + READ_UNESCAPE, + READ_ERROR, +}; + +struct data_buf { + uint32_t len; + uint8_t data[BUF_SIZE]; + uint8_t flags; +}; + +#define FLAG_COMPRESSED 1 + +struct uart_buf { + enum read_state state; + struct data_buf bufs[NUM_BUFS]; + uint32_t bri, bwi; + uint32_t ps; +}; + +static inline uint32_t ccount(void) { + uint32_t r; + __asm volatile("rsr.ccount %0" : "=a"(r)); + return r; +} + +struct write_progress { + uint32_t num_written; + uint32_t buf_level; + uint8_t digest[16]; +}; + +struct write_result { + uint32_t wait_time; + uint32_t decomp_time; + uint32_t write_time; + uint32_t erase_time; + uint32_t total_time; + uint8_t digest[16]; +}; + +static struct uart_buf ub; +static uint32_t inflate_buf[TINFL_LZ_DICT_SIZE / sizeof(uint32_t)]; + +static void next_write_buf(void) { + ub.bwi++; + if (ub.bwi == NUM_BUFS) ub.bwi = 0; + ub.bufs[ub.bwi].len = 0; + ub.bufs[ub.bwi].flags = 0; +} + +static void add_byte(uint8_t byte) { + struct data_buf *buf = &ub.bufs[ub.bwi]; + if (buf->len < BUF_SIZE) { + buf->data[buf->len++] = byte; + ub.ps++; + } +} + +void uart_isr(void *arg) { + uint32_t int_st = READ_PERI_REG(UART_INT_ST_REG(0)); + uint8_t fifo_len, i; + while ((fifo_len = READ_PERI_REG(UART_STATUS_REG(0))) > 0) { + for (i = 0; i < fifo_len; i++) { + uint8_t byte = READ_PERI_REG(UART_FIFO_REG(0)); + switch (ub.state) { + case READ_WAIT_START: { + if (byte == 0xc0) { + ub.state = READ_FLAGS; + ub.ps = 0; + } + break; + } + case READ_FLAGS: + case READ_DATA: { + struct data_buf *buf = &ub.bufs[ub.bwi]; + if (byte == 0xdb) { + ub.state = READ_UNESCAPE; + } else if (byte == 0xc0) { + next_write_buf(); + if (ub.ps == 0) { + /* Empty packet, sender aborted. */ + ub.state = READ_ERROR; + SET_PERI_REG_MASK(UART_INT_ENA_REG(0), 0); + goto out; + } else { + ub.state = READ_WAIT_START; + } + } else { + if (ub.state == READ_FLAGS) { + buf->flags = byte; + ub.state = READ_DATA; + } else { + add_byte(byte); + } + } + break; + } + case READ_UNESCAPE: { + if (byte == 0xdc) { + byte = 0xc0; + } else if (byte == 0xdd) { + byte = 0xdb; + } else { + ub.state = READ_ERROR; + SET_PERI_REG_MASK(UART_INT_ENA_REG(0), 0); + goto out; + } + add_byte(byte); + ub.state = READ_DATA; + break; + } + case READ_ERROR: { + goto out; + } + } + } + } +out: + WRITE_PERI_REG(UART_INT_CLR_REG(0), int_st); + (void) arg; +} + +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, + const void *pSrc_buf, size_t src_buf_len, + int flags); +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +#define TINFL_FLAG_PARSE_ZLIB_HEADER 1 + +#if defined(ESP8266) +int esp_rom_spiflash_erase_start(uint32_t addr, uint32_t cmd) { + SPI_write_enable(flashchip); + WRITE_PERI_REG(SPI_ADDR_REG(0), addr); + WRITE_PERI_REG(SPI_CMD_REG(0), cmd); + while (READ_PERI_REG(SPI_CMD_REG(0)) & cmd) { + } + return 0; +} +#elif defined(ESP32) +extern esp_rom_spiflash_chip_t g_rom_spiflash_chip; + +esp_rom_spiflash_result_t esp_rom_spiflash_erase_start(uint32_t addr, + uint32_t cmd) { + esp_rom_spiflash_chip_t *spi = &g_rom_spiflash_chip; + esp_rom_spiflash_wait_idle(spi); + + REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_DUMMY); + REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_ADDR_BITLEN, + ESP_ROM_SPIFLASH_W_SIO_ADDR_BITSLEN); + + WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, SPI_FLASH_WREN); + while (READ_PERI_REG(PERIPHS_SPI_FLASH_CMD) != 0) { + } + + WRITE_PERI_REG(PERIPHS_SPI_FLASH_ADDR, addr); + WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, cmd); + + return ESP_ROM_SPIFLASH_RESULT_OK; +} +#endif + +int do_flash_write(uint32_t addr, uint32_t len, uint32_t erase) { + int ret = 0; + uint32_t num_erased = 0; + struct MD5Context ctx; + MD5Init(&ctx); + + if (addr % FLASH_SECTOR_SIZE != 0) return 0x32; + if (len % FLASH_SECTOR_SIZE != 0) return 0x33; + if (esp_rom_spiflash_unlock() != 0) return 0x34; + + memset(&ub, 0, sizeof(ub)); + memset(&inflate_buf, 0, sizeof(inflate_buf)); + ets_isr_attach(ETS_UART0_INUM, uart_isr, &ub); + uint32_t saved_conf1 = READ_PERI_REG(UART_CONF1_REG(0)); + /* Reduce frequency of UART interrupts */ + WRITE_PERI_REG(UART_CONF1_REG(0), UART_RX_TOUT_EN | + (20 << UART_RX_TOUT_THRHD_S) | + (100 << UART_RXFIFO_FULL_THRHD_S)); + SET_PERI_REG_MASK(UART_INT_ENA_REG(0), UART_RX_INTS); + ets_isr_unmask(1 << ETS_UART0_INUM); + + struct write_result wr; + memset(&wr, 0, sizeof(wr)); + + struct write_progress wp = {.num_written = 0, .buf_level = 0}; + wp.buf_level = (uint32_t) &addr; + SLIP_send(&wp, sizeof(wp)); + wr.total_time = ccount(); + while (wp.num_written < len) { + /* Prepare the space ahead. */ + uint32_t start_count = ccount(); + while (erase && num_erased < wp.num_written + FLASH_WRITE_SIZE) { + const uint32_t num_left = (len - num_erased); + if (num_left >= FLASH_BLOCK_SIZE && addr % FLASH_BLOCK_SIZE == 0) { + if (esp_rom_spiflash_erase_start(addr, SPI_FLASH_BE) != 0) { + ret = 0x35; + goto out; + } + num_erased += FLASH_BLOCK_SIZE; + } else { + /* len % FLASH_SECTOR_SIZE == 0 is enforced, no further checks needed */ + if (esp_rom_spiflash_erase_start(addr, SPI_FLASH_SE) != 0) { + ret = 0x36; + goto out; + } + num_erased += FLASH_SECTOR_SIZE; + } + } + wr.erase_time += ccount() - start_count; + start_count = ccount(); + /* Wait for data to arrive. */ + wp.buf_level = 0; + for (int i = 0; i < NUM_BUFS; i++) wp.buf_level += ub.bufs[i].len; + volatile uint32_t *bwi = &ub.bwi; + while (*bwi == ub.bri && ub.state != READ_ERROR) { + } + struct data_buf *buf = &ub.bufs[ub.bri]; + if (ub.state == READ_ERROR) { + ret = 0x37; + goto out; + } + wr.wait_time += ccount() - start_count; + start_count = ccount(); + uint32_t *data = (uint32_t *) buf->data; + uint32_t write_len = buf->len; + if (buf->flags & FLAG_COMPRESSED) { + data = inflate_buf; + write_len = tinfl_decompress_mem_to_mem( + &inflate_buf[0], sizeof(inflate_buf), buf->data, write_len, + TINFL_FLAG_PARSE_ZLIB_HEADER); + if (write_len == TINFL_DECOMPRESS_MEM_TO_MEM_FAILED) { + ret = 0x40; + goto out; + } + } + wr.decomp_time += ccount() - start_count; + MD5Update(&ctx, (uint8_t *) data, write_len); + start_count = ccount(); + wr.erase_time += ccount() - start_count; + start_count = ccount(); + if (esp_rom_spiflash_write(addr, data, write_len) != 0) { + ret = 0x38; + goto out; + } + wr.write_time += ccount() - start_count; + buf->len = 0; + ub.bri++; + if (ub.bri == NUM_BUFS) ub.bri = 0; + addr += write_len; + wp.num_written += write_len; + struct MD5Context ctx2; + memcpy(&ctx2, &ctx, sizeof(ctx)); + MD5Final(wp.digest, &ctx2); + SLIP_send(&wp, sizeof(wp)); + } + + MD5Final(wr.digest, &ctx); + + wr.total_time = ccount() - wr.total_time; + SLIP_send(&wr, sizeof(wr)); + +out: + WRITE_PERI_REG(UART_CONF1_REG(0), saved_conf1); + ets_isr_mask(1 << ETS_UART0_INUM); + return ret; +} + +int do_flash_read(uint32_t addr, uint32_t len, uint32_t block_size, + uint32_t max_in_flight) { + uint8_t buf[FLASH_SECTOR_SIZE]; + uint8_t digest[16]; + struct MD5Context ctx; + uint32_t num_sent = 0, num_acked = 0; + if (block_size > sizeof(buf)) return 0x52; + MD5Init(&ctx); + while (num_acked < len) { + while (num_sent < len && num_sent - num_acked < max_in_flight) { + uint32_t n = len - num_sent; + if (n > block_size) n = block_size; + if (esp_rom_spiflash_read(addr, (uint32_t *) buf, n) != 0) return 0x53; + send_packet(buf, n); + MD5Update(&ctx, buf, n); + addr += n; + num_sent += n; + } + { + if (SLIP_recv(&num_acked, sizeof(num_acked)) != 4) return 0x54; + if (num_acked > num_sent) return 0x55; + } + } + MD5Final(digest, &ctx); + send_packet(digest, sizeof(digest)); + return 0; +} + +int do_flash_digest(uint32_t addr, uint32_t len, uint32_t digest_block_size) { + uint8_t buf[FLASH_SECTOR_SIZE]; + uint8_t digest[16]; + uint32_t read_block_size = + digest_block_size ? digest_block_size : sizeof(buf); + struct MD5Context ctx; + if (digest_block_size > sizeof(buf)) return 0x62; + MD5Init(&ctx); + while (len > 0) { + uint32_t n = len; + struct MD5Context block_ctx; + MD5Init(&block_ctx); + if (n > read_block_size) n = read_block_size; + if (esp_rom_spiflash_read(addr, (uint32_t *) buf, n) != 0) return 0x63; + MD5Update(&ctx, buf, n); + if (digest_block_size > 0) { + MD5Update(&block_ctx, buf, n); + MD5Final(digest, &block_ctx); + send_packet(digest, sizeof(digest)); + } + addr += n; + len -= n; + } + MD5Final(digest, &ctx); + send_packet(digest, sizeof(digest)); + return 0; +} + +int do_flash_read_chip_id(void) { + uint32_t chip_id = 0; + WRITE_PERI_REG(SPI_CMD_REG(0), SPI_FLASH_RDID); + while (READ_PERI_REG(SPI_CMD_REG(0)) & SPI_FLASH_RDID) { + } + chip_id = READ_PERI_REG(SPI_W0_REG(0)) & 0xFFFFFF; + send_packet((uint8_t *) &chip_id, sizeof(chip_id)); + return 0; +} + +uint8_t cmd_loop(void) { + uint8_t cmd = 0x55; + do { + /* Reset FIFO to re-sync */ + SET_PERI_REG_MASK(UART_CONF0_REG(0), UART_RXFIFO_RST); + CLEAR_PERI_REG_MASK(UART_CONF0_REG(0), UART_RXFIFO_RST); + uint32_t args[4]; + uint32_t len = SLIP_recv(&cmd, 1); + if (len != 1) { + continue; + } + uint8_t resp = 0xff; + switch (cmd) { + case CMD_FLASH_WRITE: { + len = SLIP_recv(args, sizeof(args)); + if (len == 12) { + resp = do_flash_write(args[0] /* addr */, args[1] /* len */, + args[2] /* erase */); + } else { + resp = 0x41; + } + break; + } + case CMD_FLASH_READ: { + len = SLIP_recv(args, sizeof(args)); + if (len == 16) { + resp = do_flash_read(args[0] /* addr */, args[1], /* len */ + args[2] /* block_size */, + args[3] /* max_in_flight */); + } else { + resp = 0x51; + } + break; + } + case CMD_FLASH_DIGEST: { + len = SLIP_recv(args, sizeof(args)); + if (len == 12) { + resp = do_flash_digest(args[0] /* addr */, args[1], /* len */ + args[2] /* digest_block_size */); + } else { + resp = 0x61; + } + break; + } + case CMD_FLASH_READ_CHIP_ID: { + resp = do_flash_read_chip_id(); + break; + } + case CMD_FLASH_ERASE_CHIP: { + resp = esp_rom_spiflash_erase_chip(); + break; + } + case CMD_BOOT_FW: + case CMD_REBOOT: { + resp = 0; + SLIP_send(&resp, 1); + return cmd; + } + case CMD_ECHO: { + len = SLIP_recv(args, sizeof(args)); + SLIP_send(args, len); + resp = 0; + break; + } + } + SLIP_send(&resp, 1); + } while (cmd != CMD_BOOT_FW && cmd != CMD_REBOOT); + return cmd; +} + +void stub_main1(void) { + uint32_t baud_rate = params[0]; + uint32_t greeting = 0x4941484f; /* OHAI */ + uint8_t last_cmd; + + /* This points at us right now, reset for next boot. */ + ets_set_user_start(0); + +/* Selects SPI functions for flash pins. */ +#if defined(ESP8266) + SelectSpiFunction(); + SET_PERI_REG_MASK(0x3FF00014, 1); /* Switch to 160 MHz */ +#elif defined(ESP32) + esp_rom_spiflash_attach(ets_efuse_get_spiconfig(), 0 /* legacy */); +#endif + + esp_rom_spiflash_config_param( + 0 /* deviceId */, 16 * 1024 * 1024 /* chip_size */, FLASH_BLOCK_SIZE, + FLASH_SECTOR_SIZE, FLASH_PAGE_SIZE, 0xffff /* status_mask */); + + if (baud_rate > 0) { + ets_delay_us(10000); + set_baud_rate(0, baud_rate); + } + + /* Give host time to get ready too. */ + ets_delay_us(50000); + +#ifdef BAUD_TEST + while (1) { + WRITE_PERI_REG(UART_FIFO_REG(0), 0x55); + } +#else + SLIP_send(&greeting, 4); +#endif + + last_cmd = cmd_loop(); + + ets_delay_us(10000); + + if (last_cmd == CMD_BOOT_FW) { +#if defined(ESP8266) + /* + * Find the return address in our own stack and change it. + * "flash_finish" it gets to the same point, except it doesn't need to + * patch up its RA: it returns from UartDwnLdProc, then from f_400011ac, + * then jumps to 0x4000108a, then checks strapping bits again (which will + * not have changed), and then proceeds to 0x400010a8. + */ + volatile uint32_t *sp = &baud_rate; + while (*sp != (uint32_t) 0x40001100) sp++; + *sp = 0x400010a8; + /* + * The following dummy asm fragment acts as a barrier, to make sure function + * epilogue, including return address loading, is added after our stack + * patching. + */ + __asm volatile("nop.n"); + return; /* To 0x400010a8 */ +#elif defined(ESP32) +/* TODO(rojer) */ +#endif + } else { + software_reset(); + } + /* Not reached */ +} + +/* miniz requires at least 12K of stack */ +uint32_t stack[3071]; +uint32_t stack_end; + +void stub_main(void) { + memset(&_bss_start, 0, (&_bss_end - &_bss_start)); + __asm volatile("movi a1, stack_end\n"); + stub_main1(); + // Keep the stack vars alive. + stack[0] = stack_end = 0xff; +} diff --git a/src/mongoose-6.11/src/common/platforms/esp/stub_flasher.h b/src/mongoose-6.11/src/common/platforms/esp/stub_flasher.h new file mode 100644 index 0000000..4bae4c6 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp/stub_flasher.h @@ -0,0 +1,96 @@ +#ifndef CS_COMMON_PLATFORMS_ESP8266_STUBS_STUB_FLASHER_H_ +#define CS_COMMON_PLATFORMS_ESP8266_STUBS_STUB_FLASHER_H_ + +enum stub_cmd { + /* + * Write to the SPI flash. + * + * Args: addr, len, erase; addr and len must be SECTOR_SIZE-aligned. + * If erase != 0, perform erase before writing. + * Input: Stream of data to be written, note: no SLIP encapsulation here. + * Output: SLIP packets with number of bytes written after every write. + * This can (and should) be used for flow control. Flasher will + * write in 1K chunks but will buffer up to 4K of data + * Use this feedback to keep buffer above 1K but below 4K. + * Final packet will contain MD5 digest of the data written. + */ + CMD_FLASH_WRITE = 1, + + /* + * Read from the SPI flash. + * + * Args: addr, len, block_size; no alignment requirements, block_size <= 4K. + * Input: None. + * Output: Packets of up to block_size with data. An acknowledgement is + *expected + * after every packet, in the form of a packet with total number of + * bytes received so far. + * Last packet is the MD5 digest of the data sent. + * + * Note: No flow control is performed, it is assumed that the host can cope + * with the inbound stream. + */ + CMD_FLASH_READ = 2, + + /* + * Compute MD5 digest of the specified flash region. + * + * Args: addr, len, digest_block_size; no alignment requirements. + * Input: None. + * Output: If block digests are not enabled (digest_block_size == 0), + * only overall digest is produced. + * Otherwise, there will be a separate digest for each block, + * the remainder (if any) and the overall digest at the end. + */ + CMD_FLASH_DIGEST = 3, + + /* + * Read flash chip ID. + * This is the JEDEC ID, containinf manufactirer, SPI mode and capacity. + * + * Args: None. + * Input: None. + * Output: 32 bit chip id (only 24 bits are meaningful). + */ + CMD_FLASH_READ_CHIP_ID = 4, + + /* + * Zap the whole chip at once. + * + * Args: None. + * Input: None. + * Output: None. + */ + CMD_FLASH_ERASE_CHIP = 5, + + /* + * Boots the firmware from flash. + * + * Args: None. + * Input: None. + * Output: None. + */ + CMD_BOOT_FW = 6, + + /* + * Reboot the CPU. + * Since strapping settings are not reset, this will reboot into whatever mode + * got us here, most likely UART loader. + * + * Args: None. + * Input: None. + * Output: None. + */ + CMD_REBOOT = 7, + + /* + * Echo the arguments back to the host. + * + * Args: variable. + * Input: None. + * Output: arguments. + */ + CMD_ECHO = 8, +}; + +#endif /* CS_COMMON_PLATFORMS_ESP8266_STUBS_STUB_FLASHER_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/.gitattributes b/src/mongoose-6.11/src/common/platforms/esp31/rom/.gitattributes new file mode 100644 index 0000000..1260bec --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp31/rom/.gitattributes @@ -0,0 +1 @@ +rom.bin -nodiff diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/ESP31B_ROM.txt b/src/mongoose-6.11/src/common/platforms/esp31/rom/ESP31B_ROM.txt new file mode 100644 index 0000000..f4640cc --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp31/rom/ESP31B_ROM.txt @@ -0,0 +1,11917 @@ + +rom.elf: file format elf32-xtensa-le + + +Disassembly of section .text: + +40000000 <_start>: +40000000: 49c500 s32e a0, a5, -16 +40000003: 49d510 s32e a1, a5, -12 +40000006: 49e520 s32e a2, a5, -8 +40000009: 49f530 s32e a3, a5, -4 +4000000c: 003400 rfwo +4000000f: 412800 srli a2, a0, 8 +40000012: 5138 l32i.n a3, a1, 20 +40000014: 6148 l32i.n a4, a1, 24 +40000016: 01d112 addmi a1, a1, 0x100 +40000019: 13d100 wsr.excsave1 a0 +4000001c: 034800 rsr.windowbase a0 +4000001f: 4080f0 rotw -1 +40000022: 03e620 rsr.ps a2 +40000025: 343820 extui a3, a2, 8, 4 +40000028: 303340 xor a3, a3, a4 +4000002b: 000846 j 40000050 <_XX_ExcVec50> + ... + +40000040 <_XX_Vec40>: +40000040: 09c500 l32e a0, a5, -16 +40000043: 09d510 l32e a1, a5, -12 +40000046: 09e520 l32e a2, a5, -8 +40000049: 09f530 l32e a3, a5, -4 +4000004c: 003500 rfwu + ... + +40000050 <_XX_ExcVec50>: +40000050: 03d140 rsr.excsave1 a4 +40000053: 113380 slli a3, a3, 8 +40000056: 302230 xor a2, a2, a3 +40000059: 13e620 wsr.ps a2 +4000005c: 002010 rsync +4000005f: dd74f7 bbci a4, 31, 40000040 <_XX_Vec40> +40000062: 4080f0 rotw -1 +40000065: 5778e7 bbci a8, 30, 400000c0 <_XX_ExcVec80+0x40> +40000068: 4080f0 rotw -1 +4000006b: 003446 j 40000140 <_WindowUnderflowHandler> + ... + +40000080 <_XX_ExcVec80>: +40000080: 49c900 s32e a0, a9, -16 +40000083: 09d100 l32e a0, a1, -12 +40000086: 49d910 s32e a1, a9, -12 +40000089: 49e920 s32e a2, a9, -8 +4000008c: 49f930 s32e a3, a9, -4 +4000008f: 498040 s32e a4, a0, -32 +40000092: 499050 s32e a5, a0, -28 +40000095: 49a060 s32e a6, a0, -24 +40000098: 49b070 s32e a7, a0, -20 +4000009b: 003400 rfwo + ... +400000be: 000000 ill +400000c1: 09c9 s32i.n a12, a9, 0 +400000c3: 09d910 l32e a1, a9, -12 +400000c6: 09e920 l32e a2, a9, -8 +400000c9: 09d170 l32e a7, a1, -12 +400000cc: 09f930 l32e a3, a9, -4 +400000cf: 098740 l32e a4, a7, -32 +400000d2: 099750 l32e a5, a7, -28 +400000d5: 09a760 l32e a6, a7, -24 +400000d8: 09b770 l32e a7, a7, -20 +400000db: 003500 rfwu + ... + +40000100 <_WindowOverflowHandler>: +40000100: 49cd00 s32e a0, a13, -16 +40000103: 09d100 l32e a0, a1, -12 +40000106: 49dd10 s32e a1, a13, -12 +40000109: 49ed20 s32e a2, a13, -8 +4000010c: 49fd30 s32e a3, a13, -4 +4000010f: 494040 s32e a4, a0, -48 +40000112: 495050 s32e a5, a0, -44 +40000115: 496060 s32e a6, a0, -40 +40000118: 497070 s32e a7, a0, -36 +4000011b: 498080 s32e a8, a0, -32 +4000011e: 499090 s32e a9, a0, -28 +40000121: 49a0a0 s32e a10, a0, -24 +40000124: 49b0b0 s32e a11, a0, -20 +40000127: 003400 rfwo + ... + +40000140 <_WindowUnderflowHandler>: +40000140: 09cd00 l32e a0, a13, -16 +40000143: 09dd10 l32e a1, a13, -12 +40000146: 09ed20 l32e a2, a13, -8 +40000149: 09d1b0 l32e a11, a1, -12 +4000014c: 09fd30 l32e a3, a13, -4 +4000014f: 094b40 l32e a4, a11, -48 +40000152: 095b50 l32e a5, a11, -44 +40000155: 096b60 l32e a6, a11, -40 +40000158: 097b70 l32e a7, a11, -36 +4000015b: 098b80 l32e a8, a11, -32 +4000015e: 099b90 l32e a9, a11, -28 +40000161: 09aba0 l32e a10, a11, -24 +40000164: 09bbb0 l32e a11, a11, -20 +40000167: 003500 rfwu + ... +4000017e: 200000 or a0, a0, a0 +40000181: a061d2 s32i a13, a1, 0x280 +40000184: 000002 l8ui a0, a0, 0 + ... +400001bf: d32000 movt a2, a0, b0 +400001c2: 02a061 l32r a6, 3ffc0c44 <_start-0x3f3bc> + ... +400001fd: 000000 ill +40000200: 61d420 xsr.excsave4 a2 +40000203: 0002a0 jx a2 + ... +4000023e: 200000 or a0, a0, a0 +40000241: a061d5 call4 3ffa0860 <_start-0x5f7a0> +40000244: 000002 l8ui a0, a0, 0 + ... +4000027f: d62000 excw +40000282: 827c13 excw +40000285: 005100 simcall +40000288: ffff06 j 40000288 <_WindowUnderflowHandler+0x148> + ... +400002bf: 371000 excw + ... + +40000300 <_XX_Vec400>: +40000300: 004100 break 1, 0 +40000303: fffe46 j 40000300 <_XX_Vec400> + ... +4000033e: 120000 andbc b0, b0, b0 +40000341: 29ffd1 l32r a13, 3ffcab40 <_start-0x354c0> +40000344: 513941 l32r a4, 3ffd4828 <_start-0x2b7d8> +40000347: f27e31 l32r a3, 3fffcd40 <_start-0x32c0> +4000034a: 03e820 rsr.exccause a2 +4000034d: a03230 addx4 a3, a2, a3 +40000350: 0338 l32i.n a3, a3, 0 +40000352: 6149 s32i.n a4, a1, 24 +40000354: 0003a0 jx a3 + ... +400003bf: 414000 srli a4, a0, 0 +400003c2: fe4600 excw +400003c5: ff .byte 0xff + ... +400003fe: 7c0000 excw +40000401: 6100d0 xsr.lbeg a13 +40000404: 0dc613 excw + ... +4000040f: 211f40 srai a1, a4, 15 +40000412: 002222 l32i a2, a2, 0 +40000415: e00000 subx4 a0, a0, a0 +40000418: 000494 excw +4000041b: 70d440 excw +4000041e: dc4000 excw +40000421: 0009 s32i.n a0, a0, 0 +40000423: 0ab040 depbits a4, a0, 0, 12 +40000426: 804000 add a4, a0, a0 +40000429: 000b addi.n a0, a0, -1 +4000042b: c20840 quou a0, a8, a4 +4000042e: ff .byte 0xff +4000042f: 3f .byte 0x3f +40000430: 000cc0 callx0 a12 +40000433: c20c40 quou a0, a12, a4 +40000436: ff .byte 0xff +40000437: 3f .byte 0x3f + ... +40000440: 000c movi.n a0, 0 +40000442: 13e400 wsr.intenable a0 +40000445: fff021 l32r a2, 40000408 <_XX_Vec400+0x108> +40000448: 03eb30 rsr.prid a3 +4000044b: 743030 extui a3, a3, 0, 8 +4000044e: 228c beqz.n a2, 40000454 <_XX_Vec400+0x154> +40000450: 03cc bnez.n a3, 40000454 <_XX_Vec400+0x154> +40000452: 0209 s32i.n a0, a2, 0 +40000454: ffee21 l32r a2, 4000040c <_XX_Vec400+0x10c> +40000457: 13e720 wsr.vecbase a2 +4000045a: 531c movi.n a3, 21 +4000045c: 136330 wsr.atomctl a3 +4000045f: 006120 rsil a2, 1 +40000462: ffeb21 l32r a2, 40000410 <_XX_Vec400+0x110> +40000465: ffeb51 l32r a5, 40000414 <_XX_Vec400+0x114> +40000468: ffec61 l32r a6, 40000418 <_XX_Vec400+0x118> +4000046b: 030c movi.n a3, 0 +4000046d: 027d mov.n a7, a2 +4000046f: 106650 and a6, a6, a5 +40000472: 000786 j 40000494 <_XX_Vec400+0x194> + ... +4000047d: 000000 ill +40000480: 506340 witlb a4, a3 +40000483: 002000 isync +40000486: f03d nop.n +40000488: 0020f0 nop +4000048b: c03350 sub a3, a3, a5 +4000048e: 14b3b6 bltui a3, 16, 400004a6 <_XX_Vec400+0x1a6> +40000491: 417470 srli a7, a7, 4 +40000494: 344070 extui a4, a7, 0, 4 +40000497: e51367 beq a3, a6, 40000480 <_XX_Vec400+0x180> +4000049a: 506340 witlb a4, a3 +4000049d: c03350 sub a3, a3, a5 +400004a0: edb3f6 bgeui a3, 16, 40000491 <_XX_Vec400+0x191> +400004a3: 002000 isync +400004a6: ffdb51 l32r a5, 40000414 <_XX_Vec400+0x114> +400004a9: 030c movi.n a3, 0 +400004ab: 027d mov.n a7, a2 +400004ad: 344070 extui a4, a7, 0, 4 +400004b0: 50e340 wdtlb a4, a3 +400004b3: c03350 sub a3, a3, a5 +400004b6: 417470 srli a7, a7, 4 +400004b9: f0b3f6 bgeui a3, 16, 400004ad <_XX_Vec400+0x1ad> +400004bc: 002030 dsync +400004bf: 03a032 movi a3, 3 +400004c2: 036120 rsr.memctl a2 +400004c5: 202230 or a2, a2, a3 +400004c8: 136120 wsr.memctl a2 +400004cb: ffd421 l32r a2, 4000041c <_XX_Vec400+0x11c> +400004ce: 02ac beqz.n a2, 400004f2 <_XX_Vec400+0x1f2> +400004d0: 0238 l32i.n a3, a2, 0 +400004d2: 1248 l32i.n a4, a2, 4 +400004d4: 2258 l32i.n a5, a2, 8 +400004d6: 0cc222 addi a2, a2, 12 +400004d9: 0fb347 bgeu a3, a4, 400004ec <_XX_Vec400+0x1ec> +400004dc: 0568 l32i.n a6, a5, 0 +400004de: 554b addi.n a5, a5, 4 +400004e0: 0369 s32i.n a6, a3, 0 +400004e2: 334b addi.n a3, a3, 4 +400004e4: f43347 bltu a3, a4, 400004dc <_XX_Vec400+0x1dc> +400004e7: fff946 j 400004d0 <_XX_Vec400+0x1d0> +400004ea: 560000 excw +400004ed: 56fe03 excw +400004f0: 00fdd5 call4 400014d0 <_l_strap_0x0x11_loader+0x7> +400004f3: 0c0020 excw +400004f6: 491011 l32r a1, 3ffd2938 <_start-0x2d6c8> +400004f9: 480013 excw +400004fc: 201013 excw + ... + +40000500 <_X_ResetVector>: +40000500: 000c movi.n a0, 0 +40000502: ffc741 l32r a4, 40000420 <_XX_Vec400+0x120> +40000505: 13d240 wsr.excsave2 a4 +40000508: ffc741 l32r a4, 40000424 <_XX_Vec400+0x124> +4000050b: 13d340 wsr.excsave3 a4 +4000050e: ffc641 l32r a4, 40000428 <_XX_Vec400+0x128> +40000511: 13d440 wsr.excsave4 a4 +40000514: ffc651 l32r a5, 4000042c <_XX_Vec400+0x12c> +40000517: 006542 s32i a4, a5, 0 +4000051a: ffc541 l32r a4, 40000430 <_XX_Vec400+0x130> +4000051d: 13d540 wsr.excsave5 a4 +40000520: ffc551 l32r a5, 40000434 <_XX_Vec400+0x134> +40000523: 006542 s32i a4, a5, 0 +40000526: 002845 call0 400007ac <_X_start> + ... + +40000700 <_c_stack>: +40000700: 000000 ill +40000703: 002040 excw +40000706: 480004 mula.da.ll.ldinc m0, a0, m0, a0 + +40000708 <_c_bss_start>: +40000708: cd48 l32i.n a4, a13, 48 +4000070a: ff .byte 0xff +4000070b: 3f .byte 0x3f + +4000070c <_c_bss_end>: +4000070c: ffe1d0 excw +4000070f: 3f .byte 0x3f +40000710: 6668 l32i.n a6, a6, 24 +40000712: 084000 excw +40000715: 3fffc1 l32r a12, 3ffd0714 <_start-0x2f8ec> +40000718: 000e40 excw +4000071b: c00840 sub a0, a8, a4 +4000071e: ff .byte 0xff +4000071f: 3f .byte 0x3f +40000720: 66b8 l32i.n a11, a6, 24 +40000722: 234000 sext a4, a0, 7 +40000725: 000500 excw +40000728: 000007 bnone a0, a0, 4000072c <_c_bss_end+0x20> +4000072b: 000580 ret +4000072e: 000000 ill +40000731: c00000 sub a0, a0, a0 + +40000734 <_c_0x3fffc210>: +40000734: ffc210 excw +40000737: 3f .byte 0x3f + +40000738 <_c_0x80000000>: +40000738: 000000 ill +4000073b: c21880 quou a1, a8, a8 +4000073e: ff .byte 0xff +4000073f: 3f .byte 0x3f +40000740: 66c8 l32i.n a12, a6, 24 +40000742: 004000 break 0, 0 +40000745: 078000 excw +40000748: 000aa1 l32r a10, 3ffc0770 <_start-0x3f890> +4000074b: 800040 add a0, a0, a4 +4000074e: 723800 excw +40000751: 000b addi.n a0, a0, -1 +40000753: d15040 mul16s a5, a0, a4 +40000756: ff .byte 0xff +40000757: 3f .byte 0x3f +40000758: c208 l32i.n a0, a2, 48 +4000075a: ff .byte 0xff +4000075b: 3f .byte 0x3f +4000075c: 050024 excw + ... + +40000760 <_c_0x40000000>: +40000760: 000000 ill +40000763: d6b040 excw +40000766: ff .byte 0xff +40000767: 3f .byte 0x3f +40000768: c20c movi.n a2, 12 +4000076a: ff .byte 0xff +4000076b: 3f .byte 0x3f +4000076c: 050025 call8 40005770 <_XX_unk5734+0x3c> +4000076f: 000000 ill +40000772: 408001 l32r a0, 3ffd0974 <_start-0x2f68c> +40000775: ffc8 l32i.n a12, a15, 60 +40000777: 3f .byte 0x3f +40000778: f42d break.n 4 +4000077a: 2d5851 l32r a5, 3ffcbcdc <_start-0x34324> +4000077d: 7f .byte 0x7f +4000077e: ff4c95 call4 3ffffc48 <_start-0x3b8> + +40000780 <_c_0x7fffffff>: +40000780: ff .byte 0xff +40000781: ff .byte 0xff +40000782: ff .byte 0xff +40000783: 7f .byte 0x7f +40000784: ffca30 excw +40000787: 3f .byte 0x3f +40000788: 0066b0 rsil a11, 6 +4000078b: fff040 excw +4000078e: fffb addi.n a15, a15, 15 +40000790: ff .byte 0xff +40000791: 000000 ill +40000794: 00ff00 excw + ... + +40000798 <_c_0x00ff0000>: +40000798: ff0000 excw +4000079b: 000000 ill +4000079e: 40ff00 nsau a0, a15 +400007a1: 404040 excw +400007a4: 808080 add a8, a0, a8 +400007a7: feff80 excw +400007aa: fe .byte 0xfe +400007ab: fe .byte 0xfe + +400007ac <_X_start>: +400007ac: 00a002 movi a0, 0 +400007af: ffd411 l32r a1, 40000700 <_c_stack> +400007b2: ffd431 l32r a3, 40000704 <_c_stack+0x4> +400007b5: f03d nop.n +400007b7: 13e630 wsr.ps a3 +400007ba: 002010 rsync +400007bd: ffd261 l32r a6, 40000708 <_c_bss_start> +400007c0: ffd371 l32r a7, 4000070c <_c_bss_end> +400007c3: 06b677 bgeu a6, a7, 400007cd <_X_start+0x21> +400007c6: 0609 s32i.n a0, a6, 0 +400007c8: 664b addi.n a6, a6, 4 +400007ca: f83677 bltu a6, a7, 400007c6 <_X_start+0x1a> +400007cd: 0098d5 call4 4000115c <_X_main> +400007d0: 120c movi.n a2, 1 +400007d2: 005100 simcall +400007d5: 0041f0 break 1, 15 +400007d8: fffd06 j 400007d0 <_X_start+0x24> +400007db: 413600 srli a3, a0, 6 +400007de: d2f600 quos a15, a6, a0 +400007e1: 2e .byte 0x2e +400007e2: 090c movi.n a9, 0 +400007e4: ffcd61 l32r a6, 40000718 <_c_bss_end+0xc> +400007e7: ffce41 l32r a4, 40000720 <_c_bss_end+0x14> +400007ea: ffcc81 l32r a8, 4000071c <_c_bss_end+0x10> +400007ed: ffc8a1 l32r a10, 40000710 <_c_bss_end+0x4> +400007f0: ffc9b1 l32r a11, 40000714 <_c_bss_end+0x8> +400007f3: 1152e0 slli a5, a2, 2 +400007f6: b5ba add.n a11, a5, a11 +400007f8: 833a30 moveqz a3, a10, a3 +400007fb: 558a add.n a5, a5, a8 +400007fd: 0b28 l32i.n a2, a11, 0 +400007ff: c073a0 sub a7, a3, a10 +40000802: 934670 movnez a4, a6, a7 +40000805: 0b39 s32i.n a3, a11, 0 +40000807: 0549 s32i.n a4, a5, 0 +40000809: c0a2a0 sub a10, a2, a10 +4000080c: 8329a0 moveqz a2, a9, a10 +4000080f: f01d retw.n +40000811: 020c movi.n a2, 0 +40000813: f01d retw.n +40000815: 000000 ill +40000818: 03b130 rsr.epc1 a3 +4000081b: 030120 rsr.lend a2 +4000081e: 333b addi.n a3, a3, 3 +40000820: 0c9237 bne a2, a3, 40000830 <_X_start+0x84> +40000823: 030220 rsr.lcount a2 +40000826: 628c beqz.n a2, 40000830 <_X_start+0x84> +40000828: 220b addi.n a2, a2, -1 +4000082a: 130220 wsr.lcount a2 +4000082d: 030030 rsr.lbeg a3 +40000830: 4128 l32i.n a2, a1, 16 +40000832: 13b130 wsr.epc1 a3 +40000835: 5138 l32i.n a3, a1, 20 +40000837: 056256 bnez a2, 40000891 <_X_start+0xe5> +4000083a: 396132 s32i a3, a1, 228 +4000083d: 3a6142 s32i a4, a1, 232 +40000840: 3b6152 s32i a5, a1, 236 +40000843: ffb831 l32r a3, 40000724 <_c_bss_end+0x18> +40000846: 03e620 rsr.ps a2 +40000849: 13e630 wsr.ps a3 +4000084c: 03b130 rsr.epc1 a3 +4000084f: 01d112 addmi a1, a1, 0x100 +40000852: 002010 rsync +40000855: 040c movi.n a4, 0 +40000857: 008136 entry a1, 64 +4000085a: 0001f5 call12 40000878 <_X_start+0xcc> +4000085d: ffb2e1 l32r a14, 40000728 <_c_bss_end+0x1c> +40000860: 0cea add.n a0, a12, a14 +40000862: f01d retw.n +40000864: 13e620 wsr.ps a2 +40000867: 020c movi.n a2, 0 +40000869: a548 l32i.n a4, a5, 40 +4000086b: 002010 rsync +4000086e: 13b130 wsr.epc1 a3 +40000871: 9538 l32i.n a3, a5, 36 +40000873: b558 l32i.n a5, a5, 44 +40000875: 003000 rfe +40000878: 006136 entry a1, 48 +4000087b: ffacc1 l32r a12, 4000072c <_c_bss_end+0x20> +4000087e: c0ca add.n a12, a0, a12 +40000880: 006136 entry a1, 48 +40000883: 00cd mov.n a12, a0 +40000885: 006136 entry a1, 48 +40000888: 00cd mov.n a12, a0 +4000088a: 002136 entry a1, 16 +4000088d: 0bbd mov.n a11, a11 +4000088f: f01d retw.n +40000891: f27c movi.n a2, -1 +40000893: 01d112 addmi a1, a1, 0x100 +40000896: 003000 rfe +40000899: 000000 ill +4000089c: 7159 s32i.n a5, a1, 28 +4000089e: ffa121 l32r a2, 40000724 <_c_bss_end+0x18> +400008a1: 03b130 rsr.epc1 a3 +400008a4: 61e620 xsr.ps a2 +400008a7: 0139 s32i.n a3, a1, 0 +400008a9: 1129 s32i.n a2, a1, 4 +400008ab: 3b2122 l32i a2, a1, 236 +400008ae: 01d112 addmi a1, a1, 0x100 +400008b1: 002010 rsync +400008b4: ff9f41 l32r a4, 40000730 <_c_bss_end+0x24> +400008b7: 203430 or a3, a4, a3 +400008ba: 904430 addx2 a4, a4, a3 +400008bd: 020136 entry a1, 0x100 +400008c0: 0061f0 rsil a15, 1 +400008c3: ff9ce1 l32r a14, 40000734 <_c_0x3fffc210> +400008c6: 03e2f0 rsr.interrupt a15 +400008c9: 03e4c0 rsr.intenable a12 +400008cc: 1ed8 l32i.n a13, a14, 4 +400008ce: 10ffc0 and a15, a15, a12 +400008d1: 0303c0 rsr.sar a12 +400008d4: 31d9 s32i.n a13, a1, 12 +400008d6: 092f16 beqz a15, 4000096c <_X_start+0x1c0> +400008d9: 21c9 s32i.n a12, a1, 8 +400008db: 8129 s32i.n a2, a1, 32 +400008dd: ffaf22 movi a2, -1 +400008e0: 40ffc0 nsau a12, a15 +400008e3: ff95f1 l32r a15, 40000738 <_c_0x80000000> +400008e6: 400c00 ssr a12 +400008e9: 91f0f0 srl a15, a15 +400008ec: 3022f0 xor a2, a2, a15 +400008ef: ff93d1 l32r a13, 4000073c <_c_0x80000000+0x4> +400008f2: 13e3f0 wsr.intclear a15 +400008f5: b0ccd0 addx8 a12, a12, a13 +400008f8: 402cd2 l32i a13, a12, 0x100 +400008fb: 0066f0 rsil a15, 6 +400008fe: 0ef8 l32i.n a15, a14, 0 +40000900: 1ed9 s32i.n a13, a14, 4 +40000902: 10ffd0 and a15, a15, a13 +40000905: 13e4f0 wsr.intenable a15 +40000908: 002010 rsync +4000090b: 0060f0 rsil a15, 0 +4000090e: 0302d0 rsr.lcount a13 +40000911: 0300f0 rsr.lbeg a15 +40000914: 91d9 s32i.n a13, a1, 36 +40000916: 0301d0 rsr.lend a13 +40000919: a1f9 s32i.n a15, a1, 40 +4000091b: b1d9 s32i.n a13, a1, 44 +4000091d: 0310d0 rsr.acclo a13 +40000920: 0311f0 rsr.acchi a15 +40000923: c1d9 s32i.n a13, a1, 48 +40000925: d1f9 s32i.n a15, a1, 52 +40000927: 0cd8 l32i.n a13, a12, 0 +40000929: 01fd mov.n a15, a1 +4000092b: 012ce2 l32i a14, a12, 4 +4000092e: 000df0 callx12 a13 +40000931: 91d8 l32i.n a13, a1, 36 +40000933: a1e8 l32i.n a14, a1, 40 +40000935: b1f8 l32i.n a15, a1, 44 +40000937: 1302d0 wsr.lcount a13 +4000093a: 1300e0 wsr.lbeg a14 +4000093d: 1301f0 wsr.lend a15 +40000940: c1d8 l32i.n a13, a1, 48 +40000942: d1e8 l32i.n a14, a1, 52 +40000944: 1310d0 wsr.acclo a13 +40000947: 1311e0 wsr.acchi a14 +4000094a: ff7ae1 l32r a14, 40000734 <_c_0x3fffc210> +4000094d: 03e2f0 rsr.interrupt a15 +40000950: 0066d0 rsil a13, 6 +40000953: 0ed8 l32i.n a13, a14, 0 +40000955: 31c8 l32i.n a12, a1, 12 +40000957: 10ddc0 and a13, a13, a12 +4000095a: 10ffd0 and a15, a15, a13 +4000095d: 01ff56 bnez a15, 40000980 <_X_start+0x1d4> +40000960: 8128 l32i.n a2, a1, 32 +40000962: 1ec9 s32i.n a12, a14, 4 +40000964: 21c8 l32i.n a12, a1, 8 +40000966: 13e4d0 wsr.intenable a13 +40000969: 0063d0 rsil a13, 3 +4000096c: ff7501 l32r a0, 40000740 <_c_0x80000000+0x8> +4000096f: ff70d1 l32r a13, 40000730 <_c_bss_end+0x24> +40000972: 1303c0 wsr.sar a12 +40000975: 2000d0 or a0, a0, a13 +40000978: 900d00 addx2 a0, a13, a0 +4000097b: 0063d0 rsil a13, 3 +4000097e: f01d retw.n +40000980: 40ffd0 nsau a13, a15 +40000983: ff6ec1 l32r a12, 4000073c <_c_0x80000000+0x4> +40000986: b0cdc0 addx8 a12, a13, a12 +40000989: 412ce2 l32i a14, a12, 0x104 +4000098c: 10ff20 and a15, a15, a2 +4000098f: 10ffe0 and a15, a15, a14 +40000992: ff68e1 l32r a14, 40000734 <_c_0x3fffc210> +40000995: f47f56 bnez a15, 400008e0 <_X_start+0x134> +40000998: ff68f1 l32r a15, 40000738 <_c_0x80000000> +4000099b: 400d00 ssr a13 +4000099e: 91d0f0 srl a13, a15 +400009a1: 412cf2 l32i a15, a12, 0x104 +400009a4: 13e3d0 wsr.intclear a13 +400009a7: 2022f0 or a2, a2, a15 +400009aa: 3022d0 xor a2, a2, a13 +400009ad: ffd1c6 j 400008f8 <_X_start+0x14c> +400009b0: 002136 entry a1, 16 +400009b3: 13e620 wsr.ps a2 +400009b6: 002010 rsync +400009b9: f01d retw.n +400009bb: 213600 srai a3, a0, 6 +400009be: 023d00 andb b3, b13, b0 +400009c1: ff5c41 l32r a4, 40000734 <_c_0x3fffc210> +400009c4: 006670 rsil a7, 6 +400009c7: 1428 l32i.n a2, a4, 4 +400009c9: 0458 l32i.n a5, a4, 0 +400009cb: 1439 s32i.n a3, a4, 4 +400009cd: 105530 and a5, a5, a3 +400009d0: 13e450 wsr.intenable a5 +400009d3: 13e670 wsr.ps a7 +400009d6: 002010 rsync +400009d9: f01d retw.n +400009db: d22000 quos a2, a0, a0 +400009de: d11261 l32r a6, 3fff4e28 <_start-0xb1d8> +400009e1: ff .byte 0xff +400009e2: 4129 s32i.n a2, a1, 16 +400009e4: ff5021 l32r a2, 40000724 <_c_bss_end+0x18> +400009e7: 6149 s32i.n a4, a1, 24 +400009e9: 7159 s32i.n a5, a1, 28 +400009eb: 13e620 wsr.ps a2 +400009ee: 002010 rsync +400009f1: 3b2122 l32i a2, a1, 236 +400009f4: 01d112 addmi a1, a1, 0x100 +400009f7: 03b240 rsr.epc2 a4 +400009fa: ff4d51 l32r a5, 40000730 <_c_bss_end+0x24> +400009fd: 204540 or a4, a5, a4 +40000a00: 904540 addx2 a4, a5, a4 +40000a03: 020136 entry a1, 0x100 +40000a06: 0062f0 rsil a15, 2 +40000a09: 03e2f0 rsr.interrupt a15 +40000a0c: 03e4c0 rsr.intenable a12 +40000a0f: ff4dd1 l32r a13, 40000744 <_c_0x80000000+0xc> +40000a12: 10ffc0 and a15, a15, a12 +40000a15: 10ffd0 and a15, a15, a13 +40000a18: 0303e0 rsr.sar a14 +40000a1b: 06ef16 beqz a15, 40000a8d <_X_start+0x2e1> +40000a1e: 21e9 s32i.n a14, a1, 8 +40000a20: 0302d0 rsr.lcount a13 +40000a23: 0300e0 rsr.lbeg a14 +40000a26: 91d9 s32i.n a13, a1, 36 +40000a28: 0301d0 rsr.lend a13 +40000a2b: a1e9 s32i.n a14, a1, 40 +40000a2d: b1d9 s32i.n a13, a1, 44 +40000a2f: 0310d0 rsr.acclo a13 +40000a32: 0311e0 rsr.acchi a14 +40000a35: c1d9 s32i.n a13, a1, 48 +40000a37: d1e9 s32i.n a14, a1, 52 +40000a39: 60c0f0 neg a12, a15 +40000a3c: 10ccf0 and a12, a12, a15 +40000a3f: 13e3c0 wsr.intclear a12 +40000a42: ff3ed1 l32r a13, 4000073c <_c_0x80000000+0x4> +40000a45: fe1c movi.n a14, 31 +40000a47: 40fcc0 nsau a12, a12 +40000a4a: c0fec0 sub a15, a14, a12 +40000a4d: 60f0f0 neg a15, a15 +40000a50: 1fcff2 addi a15, a15, 31 +40000a53: b0cfd0 addx8 a12, a15, a13 +40000a56: 0cd8 l32i.n a13, a12, 0 +40000a58: 1ce8 l32i.n a14, a12, 4 +40000a5a: 20f110 or a15, a1, a1 +40000a5d: 000df0 callx12 a13 +40000a60: 03e2f0 rsr.interrupt a15 +40000a63: 03e4c0 rsr.intenable a12 +40000a66: ff37d1 l32r a13, 40000744 <_c_0x80000000+0xc> +40000a69: 10ffc0 and a15, a15, a12 +40000a6c: 10ffd0 and a15, a15, a13 +40000a6f: fc6f56 bnez a15, 40000a39 <_X_start+0x28d> +40000a72: 91d8 l32i.n a13, a1, 36 +40000a74: a1e8 l32i.n a14, a1, 40 +40000a76: b1f8 l32i.n a15, a1, 44 +40000a78: 1302d0 wsr.lcount a13 +40000a7b: 1300e0 wsr.lbeg a14 +40000a7e: 1301f0 wsr.lend a15 +40000a81: c1d8 l32i.n a13, a1, 48 +40000a83: d1e8 l32i.n a14, a1, 52 +40000a85: 1310d0 wsr.acclo a13 +40000a88: 1311e0 wsr.acchi a14 +40000a8b: 21e8 l32i.n a14, a1, 8 +40000a8d: ff2e01 l32r a0, 40000748 <_c_0x80000000+0x10> +40000a90: ff28d1 l32r a13, 40000730 <_c_bss_end+0x24> +40000a93: 1303e0 wsr.sar a14 +40000a96: 2000d0 or a0, a0, a13 +40000a99: 900d00 addx2 a0, a13, a0 +40000a9c: 0063e0 rsil a14, 3 +40000a9f: f01d retw.n +40000aa1: 3b6522 s32i a2, a5, 236 +40000aa4: 4528 l32i.n a2, a5, 16 +40000aa6: 6548 l32i.n a4, a5, 24 +40000aa8: 7558 l32i.n a5, a5, 28 +40000aaa: 003210 rfi 2 +40000aad: 000000 ill +40000ab0: 61d320 xsr.excsave3 a2 +40000ab3: ffd112 addmi a1, a1, 0xffffff00 +40000ab6: 4129 s32i.n a2, a1, 16 +40000ab8: ff1b21 l32r a2, 40000724 <_c_bss_end+0x18> +40000abb: 6149 s32i.n a4, a1, 24 +40000abd: 7159 s32i.n a5, a1, 28 +40000abf: 13e620 wsr.ps a2 +40000ac2: 002010 rsync +40000ac5: 3b2122 l32i a2, a1, 236 +40000ac8: 01d112 addmi a1, a1, 0x100 +40000acb: 03b340 rsr.epc3 a4 +40000ace: ff1851 l32r a5, 40000730 <_c_bss_end+0x24> +40000ad1: 204540 or a4, a5, a4 +40000ad4: 904540 addx2 a4, a5, a4 +40000ad7: 020136 entry a1, 0x100 +40000ada: 0063f0 rsil a15, 3 +40000add: 03e2f0 rsr.interrupt a15 +40000ae0: 03e4c0 rsr.intenable a12 +40000ae3: ff1ad1 l32r a13, 4000074c <_c_0x80000000+0x14> +40000ae6: 10ffc0 and a15, a15, a12 +40000ae9: 10ffd0 and a15, a15, a13 +40000aec: 0303e0 rsr.sar a14 +40000aef: 06ef16 beqz a15, 40000b61 <_X_start+0x3b5> +40000af2: 21e9 s32i.n a14, a1, 8 +40000af4: 0302d0 rsr.lcount a13 +40000af7: 0300e0 rsr.lbeg a14 +40000afa: 91d9 s32i.n a13, a1, 36 +40000afc: 0301d0 rsr.lend a13 +40000aff: a1e9 s32i.n a14, a1, 40 +40000b01: b1d9 s32i.n a13, a1, 44 +40000b03: 0310d0 rsr.acclo a13 +40000b06: 0311e0 rsr.acchi a14 +40000b09: c1d9 s32i.n a13, a1, 48 +40000b0b: d1e9 s32i.n a14, a1, 52 +40000b0d: 60c0f0 neg a12, a15 +40000b10: 10ccf0 and a12, a12, a15 +40000b13: 13e3c0 wsr.intclear a12 +40000b16: ff09d1 l32r a13, 4000073c <_c_0x80000000+0x4> +40000b19: fe1c movi.n a14, 31 +40000b1b: 40fcc0 nsau a12, a12 +40000b1e: c0fec0 sub a15, a14, a12 +40000b21: 60f0f0 neg a15, a15 +40000b24: 1fcff2 addi a15, a15, 31 +40000b27: b0cfd0 addx8 a12, a15, a13 +40000b2a: 0cd8 l32i.n a13, a12, 0 +40000b2c: 1ce8 l32i.n a14, a12, 4 +40000b2e: 20f110 or a15, a1, a1 +40000b31: 000df0 callx12 a13 +40000b34: 03e2f0 rsr.interrupt a15 +40000b37: 03e4c0 rsr.intenable a12 +40000b3a: ff04d1 l32r a13, 4000074c <_c_0x80000000+0x14> +40000b3d: 10ffc0 and a15, a15, a12 +40000b40: 10ffd0 and a15, a15, a13 +40000b43: fc6f56 bnez a15, 40000b0d <_X_start+0x361> +40000b46: 91d8 l32i.n a13, a1, 36 +40000b48: a1e8 l32i.n a14, a1, 40 +40000b4a: b1f8 l32i.n a15, a1, 44 +40000b4c: 1302d0 wsr.lcount a13 +40000b4f: 1300e0 wsr.lbeg a14 +40000b52: 1301f0 wsr.lend a15 +40000b55: c1d8 l32i.n a13, a1, 48 +40000b57: d1e8 l32i.n a14, a1, 52 +40000b59: 1310d0 wsr.acclo a13 +40000b5c: 1311e0 wsr.acchi a14 +40000b5f: 21e8 l32i.n a14, a1, 8 +40000b61: fefb01 l32r a0, 40000750 <_c_0x80000000+0x18> +40000b64: fef3d1 l32r a13, 40000730 <_c_bss_end+0x24> +40000b67: 1303e0 wsr.sar a14 +40000b6a: 2000d0 or a0, a0, a13 +40000b6d: 900d00 addx2 a0, a13, a0 +40000b70: f01d retw.n +40000b72: 3b6522 s32i a2, a5, 236 +40000b75: 4528 l32i.n a2, a5, 16 +40000b77: 6548 l32i.n a4, a5, 24 +40000b79: 7558 l32i.n a5, a5, 28 +40000b7b: 003310 rfi 3 +40000b7e: 210000 srai a0, a0, 0 +40000b81: 09fef5 call12 4000ab70 <__umoddi3+0x3d0c> +40000b84: f401d2 l8ui a13, a1, 244 +40000b87: fe .byte 0xfe +40000b88: e219 s32i.n a1, a2, 56 +40000b8a: 0008 l32i.n a0, a0, 0 +40000b8c: 106232 s32i a3, a2, 64 +40000b8f: 61d400 xsr.excsave4 a0 +40000b92: 116242 s32i a4, a2, 68 +40000b95: f209 s32i.n a0, a2, 60 +40000b97: 03b130 rsr.epc1 a3 +40000b9a: 03e840 rsr.exccause a4 +40000b9d: 3239 s32i.n a3, a2, 12 +40000b9f: 4249 s32i.n a4, a2, 16 +40000ba1: 03ee30 rsr.excvaddr a3 +40000ba4: 056232 s32i a3, a2, 20 +40000ba7: 03d140 rsr.excsave1 a4 +40000baa: 066242 s32i a4, a2, 24 +40000bad: 803c movi.n a0, 56 +40000baf: 126252 s32i a5, a2, 72 +40000bb2: 136262 s32i a6, a2, 76 +40000bb5: 146272 s32i a7, a2, 80 +40000bb8: 156282 s32i a8, a2, 84 +40000bbb: 166292 s32i a9, a2, 88 +40000bbe: 1762a2 s32i a10, a2, 92 +40000bc1: 1862b2 s32i a11, a2, 96 +40000bc4: 1962c2 s32i a12, a2, 100 +40000bc7: 1a62d2 s32i a13, a2, 104 +40000bca: 1b62e2 s32i a14, a2, 108 +40000bcd: 1c62f2 s32i a15, a2, 112 +40000bd0: f8c082 addi a8, a0, -8 +40000bd3: 20c2a2 addi a10, a2, 32 +40000bd6: 408020 rotw 2 +40000bd9: fdb056 bnez a0, 40000bb8 <_X_start+0x40c> +40000bdc: 408020 rotw 2 +40000bdf: 034930 rsr.windowstart a3 +40000be2: 034840 rsr.windowbase a4 +40000be5: 1239 s32i.n a3, a2, 4 +40000be7: 2249 s32i.n a4, a2, 8 +40000be9: 130c movi.n a3, 1 +40000beb: 040c movi.n a4, 0 +40000bed: 134930 wsr.windowstart a3 +40000bf0: 134840 wsr.windowbase a4 +40000bf3: 002010 rsync +40000bf6: fed711 l32r a1, 40000754 <_c_0x80000000+0x1c> +40000bf9: 000c movi.n a0, 0 +40000bfb: fed821 l32r a2, 4000075c <_c_0x80000000+0x24> +40000bfe: 13e620 wsr.ps a2 +40000c01: 002010 rsync +40000c04: 0303e0 rsr.sar a14 +40000c07: 01e9 s32i.n a14, a1, 0 +40000c09: 0302e0 rsr.lcount a14 +40000c0c: a1e9 s32i.n a14, a1, 40 +40000c0e: 0300e0 rsr.lbeg a14 +40000c11: b1e9 s32i.n a14, a1, 44 +40000c13: 0301e0 rsr.lend a14 +40000c16: c1e9 s32i.n a14, a1, 48 +40000c18: 0310e0 rsr.acclo a14 +40000c1b: 81e9 s32i.n a14, a1, 32 +40000c1d: 0311e0 rsr.acchi a14 +40000c20: 91e9 s32i.n a14, a1, 36 +40000c22: fecfd1 l32r a13, 40000760 <_c_0x40000000> +40000c25: fec5c1 l32r a12, 4000073c <_c_0x80000000+0x4> +40000c28: 13e3d0 wsr.intclear a13 +40000c2b: 2cd8 l32i.n a13, a12, 8 +40000c2d: 3c68 l32i.n a6, a12, 12 +40000c2f: 000dd0 callx4 a13 +40000c32: a1d8 l32i.n a13, a1, 40 +40000c34: b1e8 l32i.n a14, a1, 44 +40000c36: c1f8 l32i.n a15, a1, 48 +40000c38: 1302d0 wsr.lcount a13 +40000c3b: 1300e0 wsr.lbeg a14 +40000c3e: 1301f0 wsr.lend a15 +40000c41: 81d8 l32i.n a13, a1, 32 +40000c43: 91e8 l32i.n a14, a1, 36 +40000c45: 1310d0 wsr.acclo a13 +40000c48: 1311e0 wsr.acchi a14 +40000c4b: 01f8 l32i.n a15, a1, 0 +40000c4d: 1303f0 wsr.sar a15 +40000c50: 423c movi.n a2, 52 +40000c52: 13e620 wsr.ps a2 +40000c55: 002010 rsync +40000c58: 1128 l32i.n a2, a1, 4 +40000c5a: 2138 l32i.n a3, a1, 8 +40000c5c: 134920 wsr.windowstart a2 +40000c5f: 134830 wsr.windowbase a3 +40000c62: 002010 rsync +40000c65: febb11 l32r a1, 40000754 <_c_0x80000000+0x1c> +40000c68: 863c movi.n a6, 56 +40000c6a: e0c172 addi a7, a1, -32 +40000c6d: f8c6e2 addi a14, a6, -8 +40000c70: 20c7f2 addi a15, a7, 32 +40000c73: 112f42 l32i a4, a15, 68 +40000c76: 122f52 l32i a5, a15, 72 +40000c79: 132f62 l32i a6, a15, 76 +40000c7c: 142f72 l32i a7, a15, 80 +40000c7f: 152f82 l32i a8, a15, 84 +40000c82: 162f92 l32i a9, a15, 88 +40000c85: 172fa2 l32i a10, a15, 92 +40000c88: 182fb2 l32i a11, a15, 96 +40000c8b: 408020 rotw 2 +40000c8e: fdb656 bnez a6, 40000c6d <_X_start+0x4c1> +40000c91: 192742 l32i a4, a7, 100 +40000c94: 1a2752 l32i a5, a7, 104 +40000c97: 1b2762 l32i a6, a7, 108 +40000c9a: 1c2772 l32i a7, a7, 112 +40000c9d: 408020 rotw 2 +40000ca0: 3128 l32i.n a2, a1, 12 +40000ca2: 4138 l32i.n a3, a1, 16 +40000ca4: 13b120 wsr.epc1 a2 +40000ca7: 13e830 wsr.exccause a3 +40000caa: 5128 l32i.n a2, a1, 20 +40000cac: 13ee20 wsr.excvaddr a2 +40000caf: 6138 l32i.n a3, a1, 24 +40000cb1: 13d130 wsr.excsave1 a3 +40000cb4: d108 l32i.n a0, a1, 52 +40000cb6: f128 l32i.n a2, a1, 60 +40000cb8: 102132 l32i a3, a1, 64 +40000cbb: e118 l32i.n a1, a1, 56 +40000cbd: 003410 rfi 4 +40000cc0: fea921 l32r a2, 40000764 <_c_0x40000000+0x4> +40000cc3: d209 s32i.n a0, a2, 52 +40000cc5: fea801 l32r a0, 40000768 <_c_0x40000000+0x8> +40000cc8: e219 s32i.n a1, a2, 56 +40000cca: 0008 l32i.n a0, a0, 0 +40000ccc: 106232 s32i a3, a2, 64 +40000ccf: 61d500 xsr.excsave5 a0 +40000cd2: 116242 s32i a4, a2, 68 +40000cd5: f209 s32i.n a0, a2, 60 +40000cd7: 03b130 rsr.epc1 a3 +40000cda: 03e840 rsr.exccause a4 +40000cdd: 3239 s32i.n a3, a2, 12 +40000cdf: 4249 s32i.n a4, a2, 16 +40000ce1: 03ee30 rsr.excvaddr a3 +40000ce4: 056232 s32i a3, a2, 20 +40000ce7: 03d140 rsr.excsave1 a4 +40000cea: 6249 s32i.n a4, a2, 24 +40000cec: 38a002 movi a0, 56 +40000cef: 126252 s32i a5, a2, 72 +40000cf2: 136262 s32i a6, a2, 76 +40000cf5: 146272 s32i a7, a2, 80 +40000cf8: 156282 s32i a8, a2, 84 +40000cfb: 166292 s32i a9, a2, 88 +40000cfe: 1762a2 s32i a10, a2, 92 +40000d01: 1862b2 s32i a11, a2, 96 +40000d04: 1962c2 s32i a12, a2, 100 +40000d07: 1a62d2 s32i a13, a2, 104 +40000d0a: 1b62e2 s32i a14, a2, 108 +40000d0d: 1c62f2 s32i a15, a2, 112 +40000d10: f8c082 addi a8, a0, -8 +40000d13: 20c2a2 addi a10, a2, 32 +40000d16: 408020 rotw 2 +40000d19: fdb056 bnez a0, 40000cf8 <_X_start+0x54c> +40000d1c: 408020 rotw 2 +40000d1f: 034930 rsr.windowstart a3 +40000d22: 034840 rsr.windowbase a4 +40000d25: 1239 s32i.n a3, a2, 4 +40000d27: 2249 s32i.n a4, a2, 8 +40000d29: 130c movi.n a3, 1 +40000d2b: 040c movi.n a4, 0 +40000d2d: 134930 wsr.windowstart a3 +40000d30: 134840 wsr.windowbase a4 +40000d33: 002010 rsync +40000d36: fe8b11 l32r a1, 40000764 <_c_0x40000000+0x4> +40000d39: 000c movi.n a0, 0 +40000d3b: fe8c21 l32r a2, 4000076c <_c_0x40000000+0xc> +40000d3e: 13e620 wsr.ps a2 +40000d41: 002010 rsync +40000d44: 0303e0 rsr.sar a14 +40000d47: 01e9 s32i.n a14, a1, 0 +40000d49: 0302e0 rsr.lcount a14 +40000d4c: a1e9 s32i.n a14, a1, 40 +40000d4e: 0300e0 rsr.lbeg a14 +40000d51: b1e9 s32i.n a14, a1, 44 +40000d53: 0301e0 rsr.lend a14 +40000d56: c1e9 s32i.n a14, a1, 48 +40000d58: 0310e0 rsr.acclo a14 +40000d5b: 81e9 s32i.n a14, a1, 32 +40000d5d: 0311e0 rsr.acchi a14 +40000d60: 91e9 s32i.n a14, a1, 36 +40000d62: 03e2f0 rsr.interrupt a15 +40000d65: 03e4c0 rsr.intenable a12 +40000d68: fe82d1 l32r a13, 40000770 <_c_0x40000000+0x10> +40000d6b: 10ffc0 and a15, a15, a12 +40000d6e: 10ffd0 and a15, a15, a13 +40000d71: 02af16 beqz a15, 40000d9f <_X_start+0x5f3> +40000d74: 60e0f0 neg a14, a15 +40000d77: 10eef0 and a14, a14, a15 +40000d7a: 13e3e0 wsr.intclear a14 +40000d7d: fe6fc1 l32r a12, 4000073c <_c_0x80000000+0x4> +40000d80: 40fee0 nsau a14, a14 +40000d83: b0cec0 addx8 a12, a14, a12 +40000d86: 0cd8 l32i.n a13, a12, 0 +40000d88: 1c68 l32i.n a6, a12, 4 +40000d8a: 000dd0 callx4 a13 +40000d8d: 03e2f0 rsr.interrupt a15 +40000d90: 03e4c0 rsr.intenable a12 +40000d93: fe77d1 l32r a13, 40000770 <_c_0x40000000+0x10> +40000d96: 10ffc0 and a15, a15, a12 +40000d99: 10ffd0 and a15, a15, a13 +40000d9c: fd4f56 bnez a15, 40000d74 <_X_start+0x5c8> +40000d9f: a1d8 l32i.n a13, a1, 40 +40000da1: b1e8 l32i.n a14, a1, 44 +40000da3: c1f8 l32i.n a15, a1, 48 +40000da5: 1302d0 wsr.lcount a13 +40000da8: 1300e0 wsr.lbeg a14 +40000dab: 1301f0 wsr.lend a15 +40000dae: 81d8 l32i.n a13, a1, 32 +40000db0: 91e8 l32i.n a14, a1, 36 +40000db2: 1310d0 wsr.acclo a13 +40000db5: 1311e0 wsr.acchi a14 +40000db8: 01f8 l32i.n a15, a1, 0 +40000dba: 1303f0 wsr.sar a15 +40000dbd: 523c movi.n a2, 53 +40000dbf: 13e620 wsr.ps a2 +40000dc2: 002010 rsync +40000dc5: 1128 l32i.n a2, a1, 4 +40000dc7: 022132 l32i a3, a1, 8 +40000dca: 134920 wsr.windowstart a2 +40000dcd: 134830 wsr.windowbase a3 +40000dd0: 002010 rsync +40000dd3: fe6411 l32r a1, 40000764 <_c_0x40000000+0x4> +40000dd6: 38a062 movi a6, 56 +40000dd9: e0c172 addi a7, a1, -32 +40000ddc: f8c6e2 addi a14, a6, -8 +40000ddf: 20c7f2 addi a15, a7, 32 +40000de2: 112f42 l32i a4, a15, 68 +40000de5: 122f52 l32i a5, a15, 72 +40000de8: 132f62 l32i a6, a15, 76 +40000deb: 142f72 l32i a7, a15, 80 +40000dee: 152f82 l32i a8, a15, 84 +40000df1: 162f92 l32i a9, a15, 88 +40000df4: 172fa2 l32i a10, a15, 92 +40000df7: 182fb2 l32i a11, a15, 96 +40000dfa: 408020 rotw 2 +40000dfd: fdb656 bnez a6, 40000ddc <_X_start+0x630> +40000e00: 192742 l32i a4, a7, 100 +40000e03: 1a2752 l32i a5, a7, 104 +40000e06: 1b2762 l32i a6, a7, 108 +40000e09: 1c2772 l32i a7, a7, 112 +40000e0c: 408020 rotw 2 +40000e0f: 3128 l32i.n a2, a1, 12 +40000e11: 4138 l32i.n a3, a1, 16 +40000e13: 13b120 wsr.epc1 a2 +40000e16: 13e830 wsr.exccause a3 +40000e19: 5128 l32i.n a2, a1, 20 +40000e1b: 13ee20 wsr.excvaddr a2 +40000e1e: 6138 l32i.n a3, a1, 24 +40000e20: 13d130 wsr.excsave1 a3 +40000e23: d108 l32i.n a0, a1, 52 +40000e25: f128 l32i.n a2, a1, 60 +40000e27: 102132 l32i a3, a1, 64 +40000e2a: e118 l32i.n a1, a1, 56 +40000e2c: 003510 rfi 5 +40000e2f: 320c00 orbc b0, b12, b0 +40000e32: 000286 j 40000e40 <_X_start+0x694> +40000e35: 000000 ill +40000e38: fdc332 addi a3, a3, -3 +40000e3b: 332a add.n a3, a3, a2 +40000e3d: 0004c6 j 40000e54 <_X_start+0x6a8> +40000e40: 7159 s32i.n a5, a1, 28 +40000e42: 086122 s32i a2, a1, 32 +40000e45: 03e620 rsr.ps a2 +40000e48: fe3731 l32r a3, 40000724 <_c_bss_end+0x18> +40000e4b: 016122 s32i a2, a1, 4 +40000e4e: 342020 extui a2, a2, 0, 4 +40000e51: e342f6 bgeui a2, 4, 40000e38 <_X_start+0x68c> +40000e54: 03b120 rsr.epc1 a2 +40000e57: 13e630 wsr.ps a3 +40000e5a: 0129 s32i.n a2, a1, 0 +40000e5c: 3b2122 l32i a2, a1, 236 +40000e5f: 01d112 addmi a1, a1, 0x100 +40000e62: 002010 rsync +40000e65: fe3241 l32r a4, 40000730 <_c_bss_end+0x24> +40000e68: 203430 or a3, a4, a3 +40000e6b: 904430 addx2 a4, a4, a3 +40000e6e: 020136 entry a1, 0x100 +40000e71: 0303c0 rsr.sar a12 +40000e74: 11d8 l32i.n a13, a1, 4 +40000e76: 03e6e0 rsr.ps a14 +40000e79: 34d0d0 extui a13, a13, 0, 4 +40000e7c: 34f0e0 extui a15, a14, 0, 4 +40000e7f: 30eef0 xor a14, a14, a15 +40000e82: 30eed0 xor a14, a14, a13 +40000e85: 13e6e0 wsr.ps a14 +40000e88: fe23d1 l32r a13, 40000714 <_c_bss_end+0x8> +40000e8b: 81f8 l32i.n a15, a1, 32 +40000e8d: 21c9 s32i.n a12, a1, 8 +40000e8f: 0302c0 rsr.lcount a12 +40000e92: 0300e0 rsr.lbeg a14 +40000e95: 91c9 s32i.n a12, a1, 36 +40000e97: 0301c0 rsr.lend a12 +40000e9a: a1e9 s32i.n a14, a1, 40 +40000e9c: b1c9 s32i.n a12, a1, 44 +40000e9e: 0310c0 rsr.acclo a12 +40000ea1: 0311e0 rsr.acchi a14 +40000ea4: c1c9 s32i.n a12, a1, 48 +40000ea6: d1e9 s32i.n a14, a1, 52 +40000ea8: a0cfd0 addx4 a12, a15, a13 +40000eab: 0cc8 l32i.n a12, a12, 0 +40000ead: 01ed mov.n a14, a1 +40000eaf: 1c8c beqz.n a12, 40000eb4 <_X_start+0x708> +40000eb1: 000cf0 callx12 a12 +40000eb4: 91d8 l32i.n a13, a1, 36 +40000eb6: a1e8 l32i.n a14, a1, 40 +40000eb8: b1f8 l32i.n a15, a1, 44 +40000eba: 1302d0 wsr.lcount a13 +40000ebd: 1300e0 wsr.lbeg a14 +40000ec0: 1301f0 wsr.lend a15 +40000ec3: c1d8 l32i.n a13, a1, 48 +40000ec5: d1e8 l32i.n a14, a1, 52 +40000ec7: 1310d0 wsr.acclo a13 +40000eca: 1311e0 wsr.acchi a14 +40000ecd: 21e8 l32i.n a14, a1, 8 +40000ecf: 0063c0 rsil a12, 3 +40000ed2: 1303e0 wsr.sar a14 +40000ed5: fe1a01 l32r a0, 40000740 <_c_0x80000000+0x8> +40000ed8: fe16d1 l32r a13, 40000730 <_c_bss_end+0x24> +40000edb: 2000d0 or a0, a0, a13 +40000ede: 900d00 addx2 a0, a13, a0 +40000ee1: f01d retw.n +40000ee3: 413600 srli a3, a0, 6 +40000ee6: 234100 sext a4, a1, 7 +40000ee9: fe .byte 0xfe +40000eea: 0448 l32i.n a4, a4, 0 +40000eec: 030c movi.n a3, 0 +40000eee: 2a6422 s32i a2, a4, 168 +40000ef1: 2b6432 s32i a3, a4, 172 +40000ef4: f01d retw.n +40000ef6: 360000 excw +40000ef9: 610041 l32r a4, 3ffd92fc <_start-0x26d04> +40000efc: 1e .byte 0x1e +40000efd: fe .byte 0xfe +40000efe: fe1e81 l32r a8, 40000778 <_c_0x40000000+0x18> +40000f01: 0668 l32i.n a6, a6, 0 +40000f03: fe1e51 l32r a5, 4000077c <_c_0x40000000+0x1c> +40000f06: 2b2672 l32i a7, a6, 172 +40000f09: 2a2642 l32i a4, a6, 168 +40000f0c: 827750 mull a7, a7, a5 +40000f0f: 828480 mull a8, a4, a8 +40000f12: 822450 mull a2, a4, a5 +40000f15: a24450 muluh a4, a4, a5 +40000f18: 521b addi.n a5, a2, 1 +40000f1a: 448a add.n a4, a4, a8 +40000f1c: 447a add.n a4, a4, a7 +40000f1e: 0fb527 bgeu a5, a2, 40000f31 <_X_start+0x785> +40000f21: 2a6652 s32i a5, a6, 168 +40000f24: 241b addi.n a2, a4, 1 +40000f26: fe1631 l32r a3, 40000780 <_c_0x7fffffff> +40000f29: 2b6622 s32i a2, a6, 172 +40000f2c: 102230 and a2, a2, a3 +40000f2f: f01d retw.n +40000f31: 2a6652 s32i a5, a6, 168 +40000f34: fe1321 l32r a2, 40000780 <_c_0x7fffffff> +40000f37: 2b6642 s32i a4, a6, 172 +40000f3a: 102420 and a2, a4, a2 +40000f3d: f01d retw.n +40000f3f: d81000 excw +40000f42: ff .byte 0xff +40000f43: 3f .byte 0x3f +40000f44: 004136 entry a1, 32 +40000f47: fffe41 l32r a4, 40000f40 <_X_start+0x794> +40000f4a: 1439 s32i.n a3, a4, 4 +40000f4c: 0429 s32i.n a2, a4, 0 +40000f4e: f01d retw.n + +40000f50 <_c_0x3fffd820>: +40000f50: ffd820 excw +40000f53: 3f .byte 0x3f + +40000f54 <_X_ets_task>: +40000f54: 004136 entry a1, 32 +40000f57: 160c movi.n a6, 1 +40000f59: 090c movi.n a9, 0 +40000f5b: fffd71 l32r a7, 40000f50 <_c_0x3fffd820> +40000f5e: 1183c0 slli a8, a3, 4 +40000f61: 778a add.n a7, a7, a8 +40000f63: 830b addi.n a8, a3, -1 +40000f65: f0c772 addi a7, a7, -16 +40000f68: 0729 s32i.n a2, a7, 0 +40000f6a: 1749 s32i.n a4, a7, 4 +40000f6c: 084752 s8i a5, a7, 8 +40000f6f: 094792 s8i a9, a7, 9 +40000f72: 0a4792 s8i a9, a7, 10 +40000f75: 0b4792 s8i a9, a7, 11 +40000f78: 401800 ssl a8 +40000f7b: a16600 sll a6, a6 +40000f7e: 3769 s32i.n a6, a7, 12 +40000f80: f01d retw.n + ... + +40000f84 <_XX_unk0f84>: +40000f84: 004136 entry a1, 32 +40000f87: f01d retw.n +40000f89: 000000 ill +40000f8c: 004136 entry a1, 32 +40000f8f: ffff65 call8 40000f84 <_XX_unk0f84> +40000f92: 0008a5 call8 4000101c <_XX_unk0f96+0x84> +40000f95: f01d retw.n + ... + +40000f98 <_XX_unk0f96>: +40000f98: 004136 entry a1, 32 +40000f9b: 0010e5 call8 400010a8 <_X_ets_enter_critical> +40000f9e: ffeca1 l32r a10, 40000f50 <_c_0x3fffd820> +40000fa1: 11b2c0 slli a11, a2, 4 +40000fa4: aaba add.n a10, a10, a11 +40000fa6: ffdac2 addmi a12, a10, 0xffffff00 +40000fa9: f80cd2 l8ui a13, a12, 248 +40000fac: fb0c82 l8ui a8, a12, 251 +40000faf: f0cab2 addi a11, a10, -16 +40000fb2: 4eb8d7 bgeu a8, a13, 40001004 <_XX_unk0f96+0x6c> +40000fb5: 090b82 l8ui a8, a11, 9 +40000fb8: 1b98 l32i.n a9, a11, 4 +40000fba: f81b addi.n a15, a8, 1 +40000fbc: b08890 addx8 a8, a8, a9 +40000fbf: 0839 s32i.n a3, a8, 0 +40000fc1: 1849 s32i.n a4, a8, 4 +40000fc3: 094bf2 s8i a15, a11, 9 +40000fc6: f90ce2 l8ui a14, a12, 249 +40000fc9: ffdaa2 addmi a10, a10, 0xffffff00 +40000fcc: 049de7 bne a13, a14, 40000fd4 <_XX_unk0f96+0x3c> +40000fcf: 0c0c movi.n a12, 0 +40000fd1: 094bc2 s8i a12, a11, 9 +40000fd4: 0b0be2 l8ui a14, a11, 11 +40000fd7: 01cee2 addi a14, a14, 1 +40000fda: 0b4be2 s8i a14, a11, 11 +40000fdd: fb0ad2 l8ui a13, a10, 251 +40000fe0: 091d26 beqi a13, 1, 40000fed <_XX_unk0f96+0x55> +40000fe3: 000da5 call8 400010bc <_X_ets_exit_critical> +40000fe6: 00a022 movi a2, 0 +40000fe9: 000090 retw +40000fec: d48100 extui a8, a0, 1, 14 +40000fef: ff .byte 0xff +40000ff0: 3f2af2 l32i a15, a10, 252 +40000ff3: 022892 l32i a9, a8, 8 +40000ff6: 20ff90 or a15, a15, a9 +40000ff9: 0268f2 s32i a15, a8, 8 +40000ffc: 000be5 call8 400010bc <_X_ets_exit_critical> +40000fff: 00a022 movi a2, 0 +40001002: f01d retw.n +40001004: 000b65 call8 400010bc <_X_ets_exit_critical> +40001007: 120c movi.n a2, 1 +40001009: f01d retw.n +4000100b: 413600 srli a3, a0, 6 +4000100e: f23000 rems a3, a0, a0 +40001011: 022c40 andb b2, b12, b4 +40001014: c02230 sub a2, a2, a3 +40001017: 742020 extui a2, a2, 0, 8 +4000101a: f01d retw.n +4000101c: 004136 entry a1, 32 +4000101f: f77c movi.n a7, -1 +40001021: ffc731 l32r a3, 40000f40 <_X_start+0x794> +40001024: 060c movi.n a6, 0 +40001026: 10c352 addi a5, a3, 16 +40001029: 000406 j 4000103d <_XX_unk0f96+0xa5> +4000102c: 201110 or a1, a1, a1 +4000102f: 0008e5 call8 400010bc <_X_ets_exit_critical> +40001032: ffd282 addmi a8, a2, 0xffffff00 +40001035: 3c2882 l32i a8, a8, 240 +40001038: 04ad mov.n a10, a4 +4000103a: 0008e0 callx8 a8 +4000103d: 0006a5 call8 400010a8 <_X_ets_enter_critical> +40001040: 23a8 l32i.n a10, a3, 8 +40001042: fffca5 call8 4000100c <_XX_unk0f96+0x74> +40001045: 048a16 beqz a10, 40001091 <_XX_unk0f96+0xf9> +40001048: 112ac0 slli a2, a10, 4 +4000104b: 252a add.n a2, a5, a2 +4000104d: f0c2a2 addi a10, a2, -16 +40001050: ffd2b2 addmi a11, a2, 0xffffff00 +40001053: f80b92 l8ui a9, a11, 248 +40001056: 3d2b82 l32i a8, a11, 244 +40001059: 0a0ac2 l8ui a12, a10, 10 +4000105c: fa0b42 l8ui a4, a11, 250 +4000105f: cc1b addi.n a12, a12, 1 +40001061: 0a4ac2 s8i a12, a10, 10 +40001064: fa0bb2 l8ui a11, a11, 250 +40001067: b04480 addx8 a4, a4, a8 +4000106a: 0299b7 bne a9, a11, 40001070 <_XX_unk0f96+0xd8> +4000106d: 0a4a62 s8i a6, a10, 10 +40001070: 0b0ab2 l8ui a11, a10, 11 +40001073: ffd292 addmi a9, a2, 0xffffff00 +40001076: bb0b addi.n a11, a11, -1 +40001078: 0b4ab2 s8i a11, a10, 11 +4000107b: fb0982 l8ui a8, a9, 251 +4000107e: 3f29d2 l32i a13, a9, 252 +40001081: fa7856 bnez a8, 4000102c <_XX_unk0f96+0x94> +40001084: 23c8 l32i.n a12, a3, 8 +40001086: 30dd70 xor a13, a13, a7 +40001089: 10ccd0 and a12, a12, a13 +4000108c: 23c9 s32i.n a12, a3, 8 +4000108e: ffe686 j 4000102c <_XX_unk0f96+0x94> +40001091: 03b8 l32i.n a11, a3, 0 +40001093: 005b16 beqz a11, 4000109c <_XX_unk0f96+0x104> +40001096: 0123a2 l32i a10, a3, 4 +40001099: 000be0 callx8 a11 +4000109c: 000365 call8 400010d4 <_X_ets_exit_critical_and_wait_int> +4000109f: ffe686 j 4000103d <_XX_unk0f96+0xa5> + ... + +400010a4 <_c_ets_critical_level>: +400010a4: ffda20 excw +400010a7: 3f .byte 0x3f + +400010a8 <_X_ets_enter_critical>: +400010a8: 004136 entry a1, 32 +400010ab: fffe31 l32r a3, 400010a4 <_c_ets_critical_level> +400010ae: 006220 rsil a2, 2 +400010b1: 0388 l32i.n a8, a3, 0 +400010b3: 881b addi.n a8, a8, 1 +400010b5: 0389 s32i.n a8, a3, 0 +400010b7: f01d retw.n +400010b9: 000000 ill + +400010bc <_X_ets_exit_critical>: +400010bc: 004136 entry a1, 32 +400010bf: fff931 l32r a3, 400010a4 <_c_ets_critical_level> +400010c2: 0328 l32i.n a2, a3, 0 +400010c4: 220b addi.n a2, a2, -1 +400010c6: 0329 s32i.n a2, a3, 0 +400010c8: 028c beqz.n a2, 400010cc <_X_ets_exit_critical+0x10> +400010ca: f01d retw.n +400010cc: 006080 rsil a8, 0 +400010cf: f01d retw.n +400010d1: 000000 ill + +400010d4 <_X_ets_exit_critical_and_wait_int>: +400010d4: 004136 entry a1, 32 +400010d7: fff331 l32r a3, 400010a4 <_c_ets_critical_level> +400010da: 0328 l32i.n a2, a3, 0 +400010dc: 220b addi.n a2, a2, -1 +400010de: 0329 s32i.n a2, a3, 0 +400010e0: 007000 waiti 0 +400010e3: f01d retw.n +400010e5: 000000 ill + +400010e8 <_X_ets_isr_attach>: +400010e8: 004136 entry a1, 32 +400010eb: 04cd mov.n a12, a4 +400010ed: 03bd mov.n a11, a3 +400010ef: 02ad mov.n a10, a2 +400010f1: 048e65 call8 400059d8 <_X_xtos_set_interrupt_handler_arg> +400010f4: f01d retw.n + ... + +400010f8 <_X_ets_isr_mask>: +400010f8: 004136 entry a1, 32 +400010fb: 02ad mov.n a10, a2 +400010fd: 0495a5 call8 40005a58 <_X_xtos_ints_off> +40001100: f01d retw.n + ... + +40001104 <_X_ets_isr_unmask>: +40001104: 004136 entry a1, 32 +40001107: 02ad mov.n a10, a2 +40001109: 0492a5 call8 40005a34 <_X_xtos_ints_on> +4000110c: f01d retw.n + ... + +40001110 <_c_0x3fffda30>: +40001110: ffda30 excw +40001113: 3f .byte 0x3f + +40001114 <_XX_set_0x3fffda30_0>: +40001114: 004136 entry a1, 32 +40001117: fffe31 l32r a3, 40001110 <_c_0x3fffda30> +4000111a: 0329 s32i.n a2, a3, 0 +4000111c: f01d retw.n + ... + +40001120 <_XX_set_0x3fffda30_4>: +40001120: 004136 entry a1, 32 +40001123: fffb31 l32r a3, 40001110 <_c_0x3fffda30> +40001126: 1329 s32i.n a2, a3, 4 +40001128: f01d retw.n + ... + +4000112c <_c_0xfffdffff>: +4000112c: ff .byte 0xff +4000112d: ff .byte 0xff +4000112e: fffd excw + +40001130 <_c_0x60003e00>: +40001130: 003e00 excw +40001133: 820060 mull a0, a0, a6 + +40001134 <_c_0x60008200>: +40001134: 008200 any4 b0, b0:b1:b2:b3 +40001137: 7e0060 excw + +40001138 <_c_0x60007e00>: +40001138: 007e00 waiti 14 +4000113b: 100060 and a0, a0, a6 + +4000113c <_c_0x1000>: +4000113c: 001000 movsp a0, a0 + ... + +40001140 <_s_fw_build>: +40001140: ca7c movi.n a10, -4 +40001142: ff .byte 0xff +40001143: 3f .byte 0x3f + +40001144 <_s_boot_banner>: +40001144: ffca54 excw +40001147: 3f .byte 0x3f + +40001148 <_s_pct_s_pct_u>: +40001148: ca88 l32i.n a8, a10, 48 +4000114a: ff .byte 0xff +4000114b: 3f .byte 0x3f + +4000114c <_s_ets_main_c>: +4000114c: ffca90 excw +4000114f: 3f .byte 0x3f +40001150: 1d18 l32i.n a1, a13, 4 +40001152: 904000 addx2 a4, a0, a0 +40001155: 001d mov.n a1, a0 +40001157: ca9c40 depbits a4, a12, 12, 10 +4000115a: ff .byte 0xff +4000115b: 3f .byte 0x3f + +4000115c <_X_main>: +4000115c: 004136 entry a1, 32 +4000115f: 0177a5 call8 400028d8 <_X_get_rst_cause> +40001162: fff281 l32r a8, 4000112c <_c_0xfffdffff> +40001165: 00afe2 movi a14, 0xffffff00 +40001168: fff221 l32r a2, 40001130 <_c_0x60003e00> +4000116b: 871c movi.n a7, 24 +4000116d: fff1d1 l32r a13, 40001134 <_c_0x60008200> +40001170: 063c movi.n a6, 48 +40001172: 203aa0 or a3, a10, a10 +40001175: fff0b1 l32r a11, 40001138 <_c_0x60007e00> +40001178: 1aa0a2 movi a10, 26 +4000117b: 0020c0 memw +4000117e: a02bf2 l32i a15, a11, 0x280 +40001181: 10ff80 and a15, a15, a8 +40001184: 0020c0 memw +40001187: a06bf2 s32i a15, a11, 0x280 +4000118a: ffecc1 l32r a12, 4000113c <_c_0x1000> +4000118d: 0020c0 memw +40001190: a02b92 l32i a9, a11, 0x280 +40001193: 2099c0 or a9, a9, a12 +40001196: 0020c0 memw +40001199: a06b92 s32i a9, a11, 0x280 +4000119c: 0020c0 memw +4000119f: 8e2282 l32i a8, a2, 0x238 +400011a2: 174867 ball a8, a6, 400011bd <_X_main+0x61> +400011a5: 0020c0 memw +400011a8: 8e2292 l32i a9, a2, 0x238 +400011ab: 549090 extui a9, a9, 0, 6 +400011ae: 0b1977 beq a9, a7, 400011bd <_X_main+0x61> +400011b1: 0020c0 memw +400011b4: 8e22b2 l32i a11, a2, 0x238 +400011b7: 54b0b0 extui a11, a11, 0, 6 +400011ba: 0e9ba7 bne a11, a10, 400011cc <_X_main+0x70> +400011bd: 0020c0 memw +400011c0: 9d2dc2 l32i a12, a13, 0x274 +400011c3: 10cce0 and a12, a12, a14 +400011c6: 0020c0 memw +400011c9: 9d6dc2 s32i a12, a13, 0x274 +400011cc: 0020c0 memw +400011cf: 8e22f2 l32i a15, a2, 0x238 +400011d2: f03d nop.n +400011d4: 10ff60 and a15, a15, a6 +400011d7: 6bcf66 bnei a15, 32, 40001246 <_X_main+0xea> +400011da: fd6991 l32r a9, 40000780 <_c_0x7fffffff> +400011dd: 0020c0 memw +400011e0: a52dc2 l32i a12, a13, 0x294 +400011e3: 10cc90 and a12, a12, a9 +400011e6: 0020c0 memw +400011e9: a56dc2 s32i a12, a13, 0x294 +400011ec: 0020c0 memw +400011ef: a62db2 l32i a11, a13, 0x298 +400011f2: 10bb90 and a11, a11, a9 +400011f5: 0020c0 memw +400011f8: a66db2 s32i a11, a13, 0x298 +400011fb: 0020c0 memw +400011fe: a72d82 l32i a8, a13, 0x29c +40001201: 108890 and a8, a8, a9 +40001204: 0020c0 memw +40001207: a76d82 s32i a8, a13, 0x29c +4000120a: 0020c0 memw +4000120d: a82df2 l32i a15, a13, 0x2a0 +40001210: 10ff90 and a15, a15, a9 +40001213: 0020c0 memw +40001216: a86df2 s32i a15, a13, 0x2a0 +40001219: 0020c0 memw +4000121c: a92dc2 l32i a12, a13, 0x2a4 +4000121f: 10cc90 and a12, a12, a9 +40001222: 0020c0 memw +40001225: a96dc2 s32i a12, a13, 0x2a4 +40001228: 0020c0 memw +4000122b: aa2db2 l32i a11, a13, 0x2a8 +4000122e: 10bb90 and a11, a11, a9 +40001231: 0020c0 memw +40001234: aa6db2 s32i a11, a13, 0x2a8 +40001237: 0020c0 memw +4000123a: ab2d82 l32i a8, a13, 0x2ac +4000123d: 108890 and a8, a8, a9 +40001240: 0020c0 memw +40001243: ab6d82 s32i a8, a13, 0x2ac +40001246: 950c movi.n a5, 9 +40001248: b40c movi.n a4, 11 +4000124a: 8c2c movi.n a12, 40 +4000124c: eb2c movi.n a11, 46 +4000124e: 0020c0 memw +40001251: 8e22f2 l32i a15, a2, 0x238 +40001254: 020fc7 bnone a15, a12, 4000125a <_l_strap_0x0xxx> +40001257: 006a86 j 40001405 <_l_strap_NxNxxx> + +4000125a <_l_strap_0x0xxx>: +4000125a: 0020c0 memw +4000125d: 9d2d82 l32i a8, a13, 0x274 +40001260: 1088e0 and a8, a8, a14 +40001263: 0020c0 memw +40001266: 9d6d82 s32i a8, a13, 0x274 + +40001269 <_l_strap_init_uart0>: +40001269: 020da5 call8 40003344 <_X_uart_attach> +4000126c: 00a0a2 movi a10, 0 +4000126f: 022c65 call8 40003534 <_X_uart_init> +40001272: 015125 call8 40002784 <_X_ets_install_uart_printf> +40001275: ba2c movi.n a10, 43 +40001277: 0020c0 memw +4000127a: 8e2292 l32i a9, a2, 0x238 +4000127d: 1099a0 and a9, a9, a10 +40001280: fec992 addi a9, a9, -2 +40001283: 25f916 beqz a9, 400014e6 <_l_strap_0x0x10> +40001286: 0020c0 memw +40001289: 8e22b2 l32i a11, a2, 0x238 +4000128c: 54b0b0 extui a11, a11, 0, 6 +4000128f: c0bb40 sub a11, a11, a4 +40001292: 233b16 beqz a11, 400014c9 <_l_strap_0x0x11_loader> +40001295: 0020c0 memw +40001298: 8e22c2 l32i a12, a2, 0x238 +4000129b: 10cca0 and a12, a12, a10 +4000129e: fdccc2 addi a12, a12, -3 +400012a1: 224c16 beqz a12, 400014c9 <_l_strap_0x0x11_loader> +400012a4: 0020c0 memw +400012a7: 8e22d2 l32i a13, a2, 0x238 +400012aa: 340da7 bnone a13, a10, 400012e2 <_l_strap_0x0x00> +400012ad: 0020c0 memw +400012b0: 8e22e2 l32i a14, a2, 0x238 +400012b3: 54e0e0 extui a14, a14, 0, 6 +400012b6: c0ee50 sub a14, a14, a5 +400012b9: 217e16 beqz a14, 400014d4 <_l_strap_0x0x01> +400012bc: 0020c0 memw +400012bf: 8e22f2 l32i a15, a2, 0x238 +400012c2: 10ffa0 and a15, a15, a10 +400012c5: ff0b addi.n a15, a15, -1 +400012c7: 209f16 beqz a15, 400014d4 <_l_strap_0x0x01> +400012ca: 0020c0 memw +400012cd: 8e2282 l32i a8, a2, 0x238 +400012d0: 548080 extui a8, a8, 0, 6 +400012d3: 139866 bnei a8, 10, 400012ea <_l_boot> +400012d6: 04a0a2 movi a10, 4 +400012d9: 03e965 call8 40005170 <_X_sip_init_attach> +400012dc: 000286 j 400012ea <_l_boot> +400012df: 000000 ill + +400012e2 <_l_strap_0x0x00>: +400012e2: 0a0c movi.n a10, 0 +400012e4: 201110 or a1, a1, a1 +400012e7: 03e8a5 call8 40005170 <_X_sip_init_attach> + +400012ea <_l_boot>: +400012ea: 20c330 or a12, a3, a3 +400012ed: ff94b1 l32r a11, 40001140 <_s_fw_build> +400012f0: ff95a1 l32r a10, 40001144 <_s_boot_banner> +400012f3: 0020c0 memw +400012f6: 8e22d2 l32i a13, a2, 0x238 +400012f9: 54d0d0 extui a13, a13, 0, 6 +400012fc: 015065 call8 40002804 +400012ff: 009ea5 call8 40001ce8 <_X_print_mem_banner> +40001302: 0da0a2 movi a10, 13 +40001305: ff8251 l32r a5, 40001110 <_c_0x3fffda30> +40001308: 0233f6 bgeui a3, 3, 4000130e <_l_boot+0x24> +4000130b: 033356 bnez a3, 40001342 <_l_rst_cause_12> +4000130e: 0263f6 bgeui a3, 6, 40001314 <_l_boot+0x2a> +40001311: 2133f6 bgeui a3, 3, 40001336 <_l_rst_cause_345> +40001314: ae0c movi.n a14, 10 +40001316: 023e37 bltu a14, a3, 4000131c <_l_boot+0x32> +40001319: 1973f6 bgeui a3, 7, 40001336 <_l_rst_cause_345> +4000131c: 023a37 bltu a10, a3, 40001322 <_l_boot+0x38> +4000131f: 24b347 bgeu a3, a4, 40001347 <_l_rst_cause_12+0x5> +40001322: 4f1c movi.n a15, 20 +40001324: 0e13f7 beq a3, a15, 40001336 <_l_rst_cause_345> +40001327: ff88a1 l32r a10, 40001148 <_s_pct_s_pct_u> +4000132a: ff88b1 l32r a11, 4000114c <_s_ets_main_c> +4000132d: 31a1c2 movi a12, 0x131 +40001330: 014d25 call8 40002804 +40001333: ffff06 j 40001333 <_l_boot+0x49> + +40001336 <_l_rst_cause_345>: +40001336: 05ad mov.n a10, a5 +40001338: 03bd mov.n a11, a3 +4000133a: 0160e5 call8 40002948 <_XX_unk2948> +4000133d: 0588 l32i.n a8, a5, 0 +4000133f: 11b856 bnez a8, 4000145e <_l_strap_0010xx+0x12> + +40001342 <_l_rst_cause_12>: +40001342: 05ad mov.n a10, a5 +40001344: 001f65 call8 4000153c <_XX_unk153c> +40001347: 071347 beq a3, a4, 40001352 <_l_rst_cause_12+0x10> +4000134a: 04a326 beqi a3, 12, 40001352 <_l_rst_cause_12+0x10> +4000134d: d90c movi.n a9, 13 +4000134f: 4d9397 bne a3, a9, 400013a0 <_l_rst_cause_12+0x5e> +40001352: 1b2c movi.n a11, 33 +40001354: 0020c0 memw +40001357: 8e22a2 l32i a10, a2, 0x238 +4000135a: 02cab7 bnall a10, a11, 40001360 <_l_rst_cause_12+0x1e> +4000135d: 0058c6 j 400014c4 <_l_strap_001000_0x110x+0x14> +40001360: 0020c0 memw +40001363: 8e22c2 l32i a12, a2, 0x238 +40001366: 54c0c0 extui a12, a12, 0, 6 +40001369: c0cc70 sub a12, a12, a7 +4000136c: 154c16 beqz a12, 400014c4 <_l_strap_001000_0x110x+0x14> +4000136f: 0020c0 memw +40001372: 8e22d2 l32i a13, a2, 0x238 +40001375: 54d0d0 extui a13, a13, 0, 6 +40001378: e6cdd2 addi a13, a13, -26 +4000137b: 145d16 beqz a13, 400014c4 <_l_strap_001000_0x110x+0x14> +4000137e: 1b0c movi.n a11, 1 +40001380: 00a032 movi a3, 0 +40001383: 20a330 or a10, a3, a3 +40001386: 0020c0 memw +40001389: 8e22d2 l32i a13, a2, 0x238 +4000138c: 01a0c2 movi a12, 1 +4000138f: 10dd60 and a13, a13, a6 +40001392: e0cdd2 addi a13, a13, -32 +40001395: 83acd0 moveqz a10, a12, a13 +40001398: 74a0a0 extui a10, a10, 0, 8 +4000139b: 02fd65 call8 40004370 <_X_spi_flash_attach> +4000139e: 0539 s32i.n a3, a5, 0 +400013a0: 0020c0 memw +400013a3: 8e22e2 l32i a14, a2, 0x238 +400013a6: 46ee57 bbsi a14, 5, 400013f0 <_l_rst_cause_12+0xae> +400013a9: 0020c0 memw +400013ac: 8e22f2 l32i a15, a2, 0x238 +400013af: 54f0f0 extui a15, a15, 0, 6 +400013b2: 3a1f77 beq a15, a7, 400013f0 <_l_rst_cause_12+0xae> +400013b5: 0020c0 memw +400013b8: 8e2282 l32i a8, a2, 0x238 +400013bb: 1aa092 movi a9, 26 +400013be: 548080 extui a8, a8, 0, 6 +400013c1: 2b1897 beq a8, a9, 400013f0 <_l_rst_cause_12+0xae> +400013c4: 0020c0 memw +400013c7: 8e2292 l32i a9, a2, 0x238 +400013ca: 549090 extui a9, a9, 0, 6 +400013cd: 1a8926 beqi a9, 8, 400013eb <_l_rst_cause_12+0xa9> +400013d0: 0020c0 memw +400013d3: 8e22a2 l32i a10, a2, 0x238 +400013d6: 2ea0b2 movi a11, 46 +400013d9: 10aab0 and a10, a10, a11 +400013dc: 0baa26 beqi a10, 12, 400013eb <_l_rst_cause_12+0xa9> +400013df: 0020c0 memw +400013e2: 8e22c2 l32i a12, a2, 0x238 +400013e5: 28a0d2 movi a13, 40 +400013e8: 728cd7 bany a12, a13, 4000145e <_l_strap_0010xx+0x12> +400013eb: 05e8 l32i.n a14, a5, 0 +400013ed: 06de56 bnez a14, 4000145e <_l_strap_0010xx+0x12> +400013f0: 002f65 call8 400016e8 <_XX_unk153c+0x1ac> +400013f3: 067a16 beqz a10, 4000145e <_l_strap_0010xx+0x12> +400013f6: ff54a1 l32r a10, 40001148 <_s_pct_s_pct_u> +400013f9: ff54b1 l32r a11, 4000114c <_s_ets_main_c> +400013fc: 2ca1c2 movi a12, 0x12c +400013ff: 014065 call8 40002804 +40001402: ffff06 j 40001402 <_l_rst_cause_12+0xc0> + +40001405 <_l_strap_NxNxxx>: +40001405: 0020c0 memw +40001408: 8e22f2 l32i a15, a2, 0x238 +4000140b: f03d nop.n +4000140d: 54f0f0 extui a15, a15, 0, 6 +40001410: f8cff2 addi a15, a15, -8 +40001413: 099f16 beqz a15, 400014b0 <_l_strap_001000_0x110x> +40001416: 0020c0 memw +40001419: 8e2282 l32i a8, a2, 0x238 +4000141c: 1088b0 and a8, a8, a11 +4000141f: f4c882 addi a8, a8, -12 +40001422: 08a816 beqz a8, 400014b0 <_l_strap_001000_0x110x> +40001425: 0020c0 memw +40001428: 8e2292 l32i a9, a2, 0x238 +4000142b: 549090 extui a9, a9, 0, 6 +4000142e: 1a1947 beq a9, a4, 4000144c <_l_strap_0010xx> +40001431: 0020c0 memw +40001434: 8e22b2 l32i a11, a2, 0x238 +40001437: 54b0b0 extui a11, a11, 0, 6 +4000143a: 0e9b26 beqi a11, 10, 4000144c <_l_strap_0010xx> +4000143d: 0020c0 memw +40001440: 8e22c2 l32i a12, a2, 0x238 +40001443: 54c0c0 extui a12, a12, 0, 6 +40001446: c0cc50 sub a12, a12, a5 +40001449: e1cc56 bnez a12, 40001269 <_l_strap_init_uart0> + +4000144c <_l_strap_0010xx>: +4000144c: 0020c0 memw +4000144f: 9d2df2 l32i a15, a13, 0x274 +40001452: 10ffe0 and a15, a15, a14 +40001455: 0020c0 memw +40001458: 9d6df2 s32i a15, a13, 0x274 +4000145b: ff8286 j 40001269 <_l_strap_init_uart0> +4000145e: 09a0a2 movi a10, 9 +40001461: ff3bb1 l32r a11, 40001150 <_s_ets_main_c+0x4> +40001464: ff3765 call8 400007dc <_X_start+0x30> +40001467: ff3b21 l32r a2, 40001154 <_s_ets_main_c+0x8> +4000146a: 0a0c movi.n a10, 0 +4000146c: 02bd mov.n a11, a2 +4000146e: ff36e5 call8 400007dc <_X_start+0x30> +40001471: 2a0c movi.n a10, 2 +40001473: 02bd mov.n a11, a2 +40001475: ff3665 call8 400007dc <_X_start+0x30> +40001478: 3a0c movi.n a10, 3 +4000147a: 02bd mov.n a11, a2 +4000147c: ff35e5 call8 400007dc <_X_start+0x30> +4000147f: ca1c movi.n a10, 28 +40001481: 02bd mov.n a11, a2 +40001483: ff35a5 call8 400007dc <_X_start+0x30> +40001486: da1c movi.n a10, 29 +40001488: 02bd mov.n a11, a2 +4000148a: ff3525 call8 400007dc <_X_start+0x30> +4000148d: 02bd mov.n a11, a2 +4000148f: 08a0a2 movi a10, 8 +40001492: ff34a5 call8 400007dc <_X_start+0x30> +40001495: 05a8 l32i.n a10, a5, 0 +40001497: 1a8c beqz.n a10, 4000149c <_l_strap_0010xx+0x50> +40001499: 000ae0 callx8 a10 +4000149c: ff2fa1 l32r a10, 40001158 <_s_ets_main_c+0xc> +4000149f: 013665 call8 40002804 +400014a2: 0125a2 l32i a10, a5, 4 +400014a5: 033a16 beqz a10, 400014dc <_l_strap_0x0x01+0x8> +400014a8: 000ae0 callx8 a10 +400014ab: 00a022 movi a2, 0 +400014ae: f01d retw.n + +400014b0 <_l_strap_001000_0x110x>: +400014b0: c97c movi.n a9, -4 +400014b2: 0020c0 memw +400014b5: 9d2d82 l32i a8, a13, 0x274 +400014b8: 108890 and a8, a8, a9 +400014bb: 0020c0 memw +400014be: 9d6d82 s32i a8, a13, 0x274 +400014c1: ff6906 j 40001269 <_l_strap_init_uart0> +400014c4: 0b0c movi.n a11, 0 +400014c6: ffad86 j 40001380 <_l_rst_cause_12+0x3e> + +400014c9 <_l_strap_0x0x11_loader>: +400014c9: 3a0c movi.n a10, 3 +400014cb: 03ca65 call8 40005170 <_X_sip_init_attach> +400014ce: ff8606 j 400012ea <_l_boot> +400014d1: 000000 ill + +400014d4 <_l_strap_0x0x01>: +400014d4: 1a0c movi.n a10, 1 +400014d6: 03c9a5 call8 40005170 <_X_sip_init_attach> +400014d9: ff8346 j 400012ea <_l_boot> +400014dc: ffaae5 call8 40000f8c <_XX_unk0f84+0x8> +400014df: 020c movi.n a2, 0 +400014e1: f01d retw.n +400014e3: 000000 ill + +400014e6 <_l_strap_0x0x10>: +400014e6: 2a0c movi.n a10, 2 +400014e8: 03c865 call8 40005170 <_X_sip_init_attach> +400014eb: ff7ec6 j 400012ea <_l_boot> + ... + +400014f0 <_c_0xffff8fff>: +400014f0: ff .byte 0xff +400014f1: 8f .byte 0x8f +400014f2: ff .byte 0xff +400014f3: ff .byte 0xff + +400014f4 <_c_0x60008e00>: +400014f4: 008e00 any4 b0, b12:b13:b14:b15 +400014f7: 413660 srli a3, a6, 6 +400014fa: fdb100 excw +400014fd: ff .byte 0xff +400014fe: fffd91 l32r a9, 400014f4 <_c_0x60008e00> +40001501: 0020c0 memw +40001504: 9a29a2 l32i a10, a9, 0x268 +40001507: 10aab0 and a10, a10, a11 +4000150a: 0020c0 memw +4000150d: 9a69a2 s32i a10, a9, 0x268 +40001510: 0020c0 memw +40001513: 9a2982 l32i a8, a9, 0x268 +40001516: 0020c0 memw +40001519: 9a6982 s32i a8, a9, 0x268 +4000151c: 000106 j 40001524 <_c_0x60008e00+0x30> +4000151f: 000000 ill +40001522: 250070 extui a0, a7, 16, 3 +40001525: 6603c3 excw +40001528: f62a add.n a15, a6, a2 +4000152a: f01d retw.n + +4000152c <_s_waiting_for_host>: +4000152c: caac beqz.n a10, 4000155c <_XX_unk153c+0x20> +4000152e: ff .byte 0xff +4000152f: 3f .byte 0x3f +40001530: ffa000 excw +40001533: 3f .byte 0x3f +40001534: 002000 isync +40001537: 000000 ill +4000153a: 364004 excw + +4000153c <_XX_unk153c>: +4000153c: 004136 entry a1, 32 +4000153f: fefc31 l32r a3, 40001130 <_c_0x60003e00> +40001542: 1d0c movi.n a13, 1 +40001544: 8b1c movi.n a11, 24 +40001546: ac1c movi.n a12, 26 +40001548: 040c movi.n a4, 0 +4000154a: 852c movi.n a5, 40 +4000154c: 04ad mov.n a10, a4 +4000154e: 0020c0 memw +40001551: 8e2392 l32i a9, a3, 0x238 +40001554: 109950 and a9, a9, a5 +40001557: 83ad90 moveqz a10, a13, a9 +4000155a: 0020c0 memw +4000155d: 8e2382 l32i a8, a3, 0x238 +40001560: 048580 extui a8, a8, 5, 1 +40001563: 0ad856 bnez a8, 40001614 <_XX_unk153c+0xd8> +40001566: 0020c0 memw +40001569: 8e23e2 l32i a14, a3, 0x238 +4000156c: 54e0e0 extui a14, a14, 0, 6 +4000156f: c0eeb0 sub a14, a14, a11 +40001572: 09ee16 beqz a14, 40001614 <_XX_unk153c+0xd8> +40001575: 0020c0 memw +40001578: 8e23f2 l32i a15, a3, 0x238 +4000157b: 54f0f0 extui a15, a15, 0, 6 +4000157e: c0ffc0 sub a15, a15, a12 +40001581: 08ff16 beqz a15, 40001614 <_XX_unk153c+0xd8> +40001584: 0020c0 memw +40001587: 8e2382 l32i a8, a3, 0x238 +4000158a: 548080 extui a8, a8, 0, 6 +4000158d: e5c882 addi a8, a8, -27 +40001590: 124816 beqz a8, 400016b8 <_XX_unk153c+0x17c> +40001593: 0020c0 memw +40001596: 8e2392 l32i a9, a3, 0x238 +40001599: ec2c movi.n a12, 46 +4000159b: 549090 extui a9, a9, 0, 6 +4000159e: f8c992 addi a9, a9, -8 +400015a1: 0f5916 beqz a9, 4000169a <_XX_unk153c+0x15e> +400015a4: 0020c0 memw +400015a7: 8e23b2 l32i a11, a3, 0x238 +400015aa: 040c movi.n a4, 0 +400015ac: 10bbc0 and a11, a11, a12 +400015af: f4cbb2 addi a11, a11, -12 +400015b2: 0e4b16 beqz a11, 4000169a <_XX_unk153c+0x15e> +400015b5: 0ba0e2 movi a14, 11 +400015b8: 0020c0 memw +400015bb: 8e23d2 l32i a13, a3, 0x238 +400015be: 54d0d0 extui a13, a13, 0, 6 +400015c1: 1a1de7 beq a13, a14, 400015df <_XX_unk153c+0xa3> +400015c4: 0020c0 memw +400015c7: 8e23e2 l32i a14, a3, 0x238 +400015ca: 09a082 movi a8, 9 +400015cd: 54e0e0 extui a14, a14, 0, 6 +400015d0: 0b9e26 beqi a14, 10, 400015df <_XX_unk153c+0xa3> +400015d3: 0020c0 memw +400015d6: 8e23f2 l32i a15, a3, 0x238 +400015d9: 54f0f0 extui a15, a15, 0, 6 +400015dc: 089f87 bne a15, a8, 400015e8 <_XX_unk153c+0xac> +400015df: ffd3a1 l32r a10, 4000152c <_s_waiting_for_host> +400015e2: 012225 call8 40002804 +400015e5: 01a042 movi a4, 1 +400015e8: 0020c0 memw +400015eb: 8e2382 l32i a8, a3, 0x238 +400015ee: 178857 bany a8, a5, 40001609 <_XX_unk153c+0xcd> +400015f1: 01a0a2 movi a10, 1 +400015f4: 01f3e5 call8 40003534 <_X_uart_init> +400015f7: 0a0c movi.n a10, 0 +400015f9: 020a25 call8 4000369c <_X_uart_wait_tx_empty> +400015fc: 0a2c movi.n a10, 32 +400015fe: ffb065 call8 40001104 <_X_ets_isr_unmask> +40001601: 06b416 beqz a4, 40001670 <_XX_unk153c+0x134> +40001604: 0a2c movi.n a10, 32 +40001606: ffaf25 call8 400010f8 <_X_ets_isr_mask> +40001609: 532426 beqi a4, 2, 40001660 <_XX_unk153c+0x124> +4000160c: ffeea5 call8 400014f8 <_c_0x60008e00+0x4> +4000160f: f01d retw.n +40001611: 000000 ill +40001614: 1a2c movi.n a10, 33 +40001616: 0020c0 memw +40001619: 8e2392 l32i a9, a3, 0x238 +4000161c: 3949a7 ball a9, a10, 40001659 <_XX_unk153c+0x11d> +4000161f: 0020c0 memw +40001622: 8e23e2 l32i a14, a3, 0x238 +40001625: 54e0e0 extui a14, a14, 0, 6 +40001628: 2d1eb7 beq a14, a11, 40001659 <_XX_unk153c+0x11d> +4000162b: 0020c0 memw +4000162e: 8e23f2 l32i a15, a3, 0x238 +40001631: 1b0c movi.n a11, 1 +40001633: 54f0f0 extui a15, a15, 0, 6 +40001636: 1f1fc7 beq a15, a12, 40001659 <_XX_unk153c+0x11d> +40001639: 30a0e2 movi a14, 48 +4000163c: 20a440 or a10, a4, a4 +4000163f: 0020c0 memw +40001642: 8e23c2 l32i a12, a3, 0x238 +40001645: 10cce0 and a12, a12, a14 +40001648: e0ccc2 addi a12, a12, -32 +4000164b: 83adc0 moveqz a10, a13, a12 +4000164e: 74a0a0 extui a10, a10, 0, 8 +40001651: 02d1e5 call8 40004370 <_X_spi_flash_attach> +40001654: 006242 s32i a4, a2, 0 +40001657: f01d retw.n +40001659: 0b0c movi.n a11, 0 +4000165b: fff686 j 40001639 <_XX_unk153c+0xfd> +4000165e: a10000 sll a0, a0 +40001661: b1ffb4 excw +40001664: cdffb4 excw +40001667: 902502 l32i a0, a5, 0x240 +4000166a: f01d01 l32r a0, 3fffd6e0 <_start-0x2920> +4000166d: 000000 ill +40001670: 00a0a2 movi a10, 0 +40001673: 017e65 call8 40002e58 <_XX_unk2e58> +40001676: 7440a0 extui a4, a10, 0, 8 +40001679: f87456 bnez a4, 40001604 <_XX_unk153c+0xc8> +4000167c: 1a0c movi.n a10, 1 +4000167e: 017da5 call8 40002e58 <_XX_unk2e58> +40001681: 7440a0 extui a4, a10, 0, 8 +40001684: f7c456 bnez a4, 40001604 <_XX_unk153c+0xc8> +40001687: 03ad25 call8 40005158 <_XX_unk500c+0x14c> +4000168a: feca82 addi a8, a10, -2 +4000168d: 1f0c movi.n a15, 1 +4000168f: 040c movi.n a4, 0 +40001691: 834f80 moveqz a4, a15, a8 +40001694: fd8416 beqz a4, 40001670 <_XX_unk153c+0x134> +40001697: ffda46 j 40001604 <_XX_unk153c+0xc8> +4000169a: 00a0b2 movi a11, 0 +4000169d: 7440a0 extui a4, a10, 0, 8 +400016a0: 20a440 or a10, a4, a4 +400016a3: 01daa5 call8 4000344c <_c_0x000fffff+0x4> +400016a6: 20baa0 or a11, a10, a10 +400016a9: 04ad mov.n a10, a4 +400016ab: f4b0b0 extui a11, a11, 0, 16 +400016ae: 01e3a5 call8 400034e8 +400016b1: 240c movi.n a4, 2 +400016b3: ffbf86 j 400015b5 <_XX_unk153c+0x79> +400016b6: c10000 mul16u a0, a0, a0 +400016b9: c9ffa0 excw +400016bc: f01d02 l16ui a0, a13, 0x1e0 +400016bf: cae400 depbits a0, a4, 12, 15 +400016c2: ff .byte 0xff +400016c3: 3f .byte 0x3f +400016c4: cb18 l32i.n a1, a11, 48 +400016c6: ff .byte 0xff +400016c7: 3f .byte 0x3f +400016c8: ffcb24 excw +400016cb: 3f .byte 0x3f +400016cc: ffcb04 excw +400016cf: 3f .byte 0x3f +400016d0: ffc880 excw +400016d3: 3f .byte 0x3f +400016d4: ffcb50 excw +400016d7: 3f .byte 0x3f +400016d8: ffcac0 excw +400016db: 3f .byte 0x3f +400016dc: ffcad0 excw +400016df: 3f .byte 0x3f +400016e0: ffcb34 excw +400016e3: 3f .byte 0x3f +400016e4: ffcb44 excw +400016e7: 3f .byte 0x3f +400016e8: 00e136 entry a1, 112 +400016eb: 1a2c movi.n a10, 33 +400016ed: fe9081 l32r a8, 40001130 <_c_0x60003e00> +400016f0: 0020c0 memw +400016f3: 8e2892 l32i a9, a8, 0x238 +400016f6: 4249a7 ball a9, a10, 4000173c <_XX_unk153c+0x200> +400016f9: 8c1c movi.n a12, 24 +400016fb: 0020c0 memw +400016fe: 8e28b2 l32i a11, a8, 0x238 +40001701: 54b0b0 extui a11, a11, 0, 6 +40001704: 341bc7 beq a11, a12, 4000173c <_XX_unk153c+0x200> +40001707: 1aa0d2 movi a13, 26 +4000170a: 0020c0 memw +4000170d: 8e28c2 l32i a12, a8, 0x238 +40001710: 54c0c0 extui a12, a12, 0, 6 +40001713: 251cd7 beq a12, a13, 4000173c <_XX_unk153c+0x200> +40001716: 01a022 movi a2, 1 +40001719: 00a0a2 movi a10, 0 +4000171c: 20b110 or a11, a1, a1 +4000171f: 10a0c2 movi a12, 16 +40001722: 031765 call8 40004898 +40001725: facc bnez.n a10, 40001738 <_XX_unk153c+0x1fc> +40001727: 010162 l8ui a6, a1, 1 +4000172a: 030132 l8ui a3, a1, 3 +4000172d: 11c8 l32i.n a12, a1, 4 +4000172f: 0001d2 l8ui a13, a1, 0 +40001732: e9a0e2 movi a14, 233 +40001735: 081de7 beq a13, a14, 40001741 <_XX_unk153c+0x205> +40001738: 120c movi.n a2, 1 +4000173a: f01d retw.n +4000173c: 020c movi.n a2, 0 +4000173e: fff5c6 j 40001719 <_XX_unk153c+0x1dd> +40001741: 20b220 or a11, a2, a2 +40001744: 0201a2 l8ui a10, a1, 2 +40001747: 0e61c2 s32i a12, a1, 56 +4000174a: 02dee5 call8 40004538 <_X_SPIReadModeConfig> +4000174d: 348030 extui a8, a3, 0, 4 +40001750: 0fa032 movi a3, 15 +40001753: 0738f6 bgeui a8, 3, 4000175e <_XX_unk153c+0x222> +40001756: a82b addi.n a10, a8, 2 +40001758: 74a0a0 extui a10, a10, 0, 8 +4000175b: 000046 j 40001760 <_XX_unk153c+0x224> +4000175e: 1a0c movi.n a10, 1 +40001760: 02f165 call8 40004678 <_X_SPIReadModeConfig+0x140> +40001763: 0c0182 l8ui a8, a1, 12 +40001766: 0e0192 l8ui a9, a1, 14 +40001769: 0f0152 l8ui a5, a1, 15 +4000176c: 0b0122 l8ui a2, a1, 11 +4000176f: 115580 slli a5, a5, 8 +40001772: 112280 slli a2, a2, 8 +40001775: 205590 or a5, a5, a9 +40001778: 0d0192 l8ui a9, a1, 13 +4000177b: 115580 slli a5, a5, 8 +4000177e: 205590 or a5, a5, a9 +40001781: 115580 slli a5, a5, 8 +40001784: 0a0192 l8ui a9, a1, 10 +40001787: 205580 or a5, a5, a8 +4000178a: 080182 l8ui a8, a1, 8 +4000178d: 202290 or a2, a2, a9 +40001790: 090192 l8ui a9, a1, 9 +40001793: 112280 slli a2, a2, 8 +40001796: 202290 or a2, a2, a9 +40001799: 112280 slli a2, a2, 8 +4000179c: 202280 or a2, a2, a8 +4000179f: 3c1296 bltz a2, 40001b64 <_XX_unk153c+0x628> +400017a2: 031c movi.n a3, 16 +400017a4: 20a330 or a10, a3, a3 +400017a7: 20b110 or a11, a1, a1 +400017aa: 10a0c2 movi a12, 16 +400017ad: 030ea5 call8 40004898 +400017b0: f84a56 bnez a10, 40001738 <_XX_unk153c+0x1fc> +400017b3: 10c332 addi a3, a3, 16 +400017b6: 278616 beqz a6, 40001a32 <_XX_unk153c+0x4f6> +400017b9: f119 s32i.n a1, a1, 60 +400017bb: efa042 movi a4, 239 +400017be: 017d mov.n a7, a1 +400017c0: c159 s32i.n a5, a1, 48 +400017c2: 6169 s32i.n a6, a1, 24 +400017c4: 02bd mov.n a11, a2 +400017c6: 0d0c movi.n a13, 0 +400017c8: d1d9 s32i.n a13, a1, 52 +400017ca: 0b2d mov.n a2, a11 +400017cc: ffbda1 l32r a10, 400016c0 <_XX_unk153c+0x184> +400017cf: c1c8 l32i.n a12, a1, 48 +400017d1: d188 l32i.n a8, a1, 52 +400017d3: 051c movi.n a5, 16 +400017d5: c05580 sub a5, a5, a8 +400017d8: 745050 extui a5, a5, 0, 8 +400017db: 05dd mov.n a13, a5 +400017dd: 010265 call8 40002804 +400017e0: 1a0c movi.n a10, 1 +400017e2: c198 l32i.n a9, a1, 48 +400017e4: a1a9 s32i.n a10, a1, 40 +400017e6: 03b957 bgeu a9, a5, 400017ed <_XX_unk153c+0x2b1> +400017e9: 0b0c movi.n a11, 0 +400017eb: a1b9 s32i.n a11, a1, 40 +400017ed: 0f0c movi.n a15, 0 +400017ef: d1e8 l32i.n a14, a1, 52 +400017f1: a1c8 l32i.n a12, a1, 40 +400017f3: 81c9 s32i.n a12, a1, 32 +400017f5: 93efc0 movnez a14, a15, a12 +400017f8: 91e9 s32i.n a14, a1, 36 +400017fa: 3b2c16 beqz a12, 40001bb0 <_XX_unk153c+0x674> +400017fd: 380c movi.n a8, 3 +400017ff: 028857 bany a8, a5, 40001805 <_XX_unk153c+0x2c9> +40001802: 008d86 j 40001a3c <_XX_unk153c+0x500> +40001805: 0215e6 bgei a5, 1, 4000180b <_XX_unk153c+0x2cf> +40001808: 002206 j 40001894 <_XX_unk153c+0x358> +4000180b: 249050 extui a9, a5, 0, 3 +4000180e: 139976 loopnez a9, 40001825 <_XX_unk153c+0x2e9> +40001811: f188 l32i.n a8, a1, 60 +40001813: 000892 l8ui a9, a8, 0 +40001816: 004292 s8i a9, a2, 0 +40001819: 881b addi.n a8, a8, 1 +4000181b: f189 s32i.n a8, a1, 60 +4000181d: 221b addi.n a2, a2, 1 +4000181f: 304940 xor a4, a9, a4 +40001822: 744040 extui a4, a4, 0, 8 +40001825: f188 l32i.n a8, a1, 60 +40001827: 419350 srli a9, a5, 3 +4000182a: 639976 loopnez a9, 40001891 <_XX_unk153c+0x355> +4000182d: 000892 l8ui a9, a8, 0 +40001830: 004292 s8i a9, a2, 0 +40001833: 309940 xor a9, a9, a4 +40001836: 749090 extui a9, a9, 0, 8 +40001839: 010842 l8ui a4, a8, 1 +4000183c: 014242 s8i a4, a2, 1 +4000183f: 309490 xor a9, a4, a9 +40001842: 749090 extui a9, a9, 0, 8 +40001845: 020842 l8ui a4, a8, 2 +40001848: 024242 s8i a4, a2, 2 +4000184b: 309490 xor a9, a4, a9 +4000184e: 749090 extui a9, a9, 0, 8 +40001851: 030842 l8ui a4, a8, 3 +40001854: 034242 s8i a4, a2, 3 +40001857: 309490 xor a9, a4, a9 +4000185a: 749090 extui a9, a9, 0, 8 +4000185d: 040842 l8ui a4, a8, 4 +40001860: 044242 s8i a4, a2, 4 +40001863: 309490 xor a9, a4, a9 +40001866: 749090 extui a9, a9, 0, 8 +40001869: 050842 l8ui a4, a8, 5 +4000186c: 054242 s8i a4, a2, 5 +4000186f: 309490 xor a9, a4, a9 +40001872: 749090 extui a9, a9, 0, 8 +40001875: 060842 l8ui a4, a8, 6 +40001878: 064242 s8i a4, a2, 6 +4000187b: 309490 xor a9, a4, a9 +4000187e: 749090 extui a9, a9, 0, 8 +40001881: 070842 l8ui a4, a8, 7 +40001884: 074242 s8i a4, a2, 7 +40001887: 309490 xor a9, a4, a9 +4000188a: 888b addi.n a8, a8, 8 +4000188c: 228b addi.n a2, a2, 8 +4000188e: 744090 extui a4, a9, 0, 8 +40001891: 0f6182 s32i a8, a1, 60 +40001894: c168 l32i.n a6, a1, 48 +40001896: a1a8 l32i.n a10, a1, 40 +40001898: c06650 sub a6, a6, a5 +4000189b: 416460 srli a6, a6, 4 +4000189e: 35ea16 beqz a10, 40001c00 <_XX_unk153c+0x6c4> +400018a1: d198 l32i.n a9, a1, 52 +400018a3: c188 l32i.n a8, a1, 48 +400018a5: 889a add.n a8, a8, a9 +400018a7: f0c882 addi a8, a8, -16 +400018aa: 348080 extui a8, a8, 0, 4 +400018ad: 076182 s32i a8, a1, 28 +400018b0: 91a8 l32i.n a10, a1, 36 +400018b2: 74a0a0 extui a10, a10, 0, 8 +400018b5: d1a9 s32i.n a10, a1, 52 +400018b7: 3e16a6 blti a6, 1, 400018f9 <_XX_unk153c+0x3bd> +400018ba: 050c movi.n a5, 0 +400018bc: 20a330 or a10, a3, a3 +400018bf: 20b110 or a11, a1, a1 +400018c2: 10a0c2 movi a12, 16 +400018c5: 02fd25 call8 40004898 +400018c8: 1fca56 bnez a10, 40001ac8 <_XX_unk153c+0x58c> +400018cb: 080c movi.n a8, 0 +400018cd: 10c332 addi a3, a3, 16 +400018d0: 091c movi.n a9, 16 +400018d2: 10a976 loopgtz a9, 400018e6 <_XX_unk153c+0x3aa> +400018d5: 987a add.n a9, a8, a7 +400018d7: 01c882 addi a8, a8, 1 +400018da: 000992 l8ui a9, a9, 0 +400018dd: 748080 extui a8, a8, 0, 8 +400018e0: 309940 xor a9, a9, a4 +400018e3: 744090 extui a4, a9, 0, 8 +400018e6: 20a220 or a10, a2, a2 +400018e9: 20b110 or a11, a1, a1 +400018ec: 0c1c movi.n a12, 16 +400018ee: 050865 call8 40006974 +400018f1: 10c222 addi a2, a2, 16 +400018f4: 551b addi.n a5, a5, 1 +400018f6: c29567 bne a5, a6, 400018bc <_XX_unk153c+0x380> +400018f9: 0821b2 l32i a11, a1, 32 +400018fc: 011b16 beqz a11, 40001911 <_XX_unk153c+0x3d5> +400018ff: 20a330 or a10, a3, a3 +40001902: 01bd mov.n a11, a1 +40001904: 0c1c movi.n a12, 16 +40001906: 02f925 call8 40004898 +40001909: 3afa56 bnez a10, 40001cbc <_XX_unk153c+0x780> +4000190c: 10c332 addi a3, a3, 16 +4000190f: f119 s32i.n a1, a1, 60 +40001911: 072152 l32i a5, a1, 28 +40001914: ff6ca1 l32r a10, 400016c4 <_XX_unk153c+0x188> +40001917: 745050 extui a5, a5, 0, 8 +4000191a: 20b550 or a11, a5, a5 +4000191d: 00ee65 call8 40002804 +40001920: 03a082 movi a8, 3 +40001923: 028857 bany a8, a5, 40001929 <_XX_unk153c+0x3ed> +40001926: 006b86 j 40001ad8 <_XX_unk153c+0x59c> +40001929: 0215e6 bgei a5, 1, 4000192f <_XX_unk153c+0x3f3> +4000192c: 002206 j 400019b8 <_XX_unk153c+0x47c> +4000192f: 249050 extui a9, a5, 0, 3 +40001932: 139976 loopnez a9, 40001949 <_XX_unk153c+0x40d> +40001935: f188 l32i.n a8, a1, 60 +40001937: 000892 l8ui a9, a8, 0 +4000193a: 004292 s8i a9, a2, 0 +4000193d: 881b addi.n a8, a8, 1 +4000193f: f189 s32i.n a8, a1, 60 +40001941: 221b addi.n a2, a2, 1 +40001943: 304940 xor a4, a9, a4 +40001946: 744040 extui a4, a4, 0, 8 +40001949: f188 l32i.n a8, a1, 60 +4000194b: 419350 srli a9, a5, 3 +4000194e: 639976 loopnez a9, 400019b5 <_XX_unk153c+0x479> +40001951: 000892 l8ui a9, a8, 0 +40001954: 004292 s8i a9, a2, 0 +40001957: 309940 xor a9, a9, a4 +4000195a: 749090 extui a9, a9, 0, 8 +4000195d: 010842 l8ui a4, a8, 1 +40001960: 014242 s8i a4, a2, 1 +40001963: 309490 xor a9, a4, a9 +40001966: 749090 extui a9, a9, 0, 8 +40001969: 020842 l8ui a4, a8, 2 +4000196c: 024242 s8i a4, a2, 2 +4000196f: 309490 xor a9, a4, a9 +40001972: 749090 extui a9, a9, 0, 8 +40001975: 030842 l8ui a4, a8, 3 +40001978: 034242 s8i a4, a2, 3 +4000197b: 309490 xor a9, a4, a9 +4000197e: 749090 extui a9, a9, 0, 8 +40001981: 040842 l8ui a4, a8, 4 +40001984: 044242 s8i a4, a2, 4 +40001987: 309490 xor a9, a4, a9 +4000198a: 749090 extui a9, a9, 0, 8 +4000198d: 050842 l8ui a4, a8, 5 +40001990: 054242 s8i a4, a2, 5 +40001993: 309490 xor a9, a4, a9 +40001996: 749090 extui a9, a9, 0, 8 +40001999: 060842 l8ui a4, a8, 6 +4000199c: 064242 s8i a4, a2, 6 +4000199f: 309490 xor a9, a4, a9 +400019a2: 749090 extui a9, a9, 0, 8 +400019a5: 070842 l8ui a4, a8, 7 +400019a8: 074242 s8i a4, a2, 7 +400019ab: 309490 xor a9, a4, a9 +400019ae: 888b addi.n a8, a8, 8 +400019b0: 228b addi.n a2, a2, 8 +400019b2: 744090 extui a4, a9, 0, 8 +400019b5: 0f6182 s32i a8, a1, 60 +400019b8: ff44a1 l32r a10, 400016c8 <_XX_unk153c+0x18c> +400019bb: 04bd mov.n a11, a4 +400019bd: 00e465 call8 40002804 +400019c0: 61a8 l32i.n a10, a1, 24 +400019c2: aa0b addi.n a10, a10, -1 +400019c4: 2d5a16 beqz a10, 40001c9d <_XX_unk153c+0x761> +400019c7: d128 l32i.n a2, a1, 52 +400019c9: 061c movi.n a6, 16 +400019cb: 252a add.n a2, a5, a2 +400019cd: c06620 sub a6, a6, a2 +400019d0: 746060 extui a6, a6, 0, 8 +400019d3: 0286f6 bgeui a6, 8, 400019d9 <_XX_unk153c+0x49d> +400019d6: 0078c6 j 40001bbd <_XX_unk153c+0x681> +400019d9: f158 l32i.n a5, a1, 60 +400019db: a28b addi.n a10, a2, 8 +400019dd: 74a0a0 extui a10, a10, 0, 8 +400019e0: d1a9 s32i.n a10, a1, 52 +400019e2: 040562 l8ui a6, a5, 4 +400019e5: 0005c2 l8ui a12, a5, 0 +400019e8: 050582 l8ui a8, a5, 5 +400019eb: 060592 l8ui a9, a5, 6 +400019ee: 0105d2 l8ui a13, a5, 1 +400019f1: 0305b2 l8ui a11, a5, 3 +400019f4: 0205e2 l8ui a14, a5, 2 +400019f7: 11bb80 slli a11, a11, 8 +400019fa: 20bbe0 or a11, a11, a14 +400019fd: 11bb80 slli a11, a11, 8 +40001a00: 20bbd0 or a11, a11, a13 +40001a03: 11bb80 slli a11, a11, 8 +40001a06: 070552 l8ui a5, a5, 7 +40001a09: 20bbc0 or a11, a11, a12 +40001a0c: 115580 slli a5, a5, 8 +40001a0f: 205590 or a5, a5, a9 +40001a12: 115580 slli a5, a5, 8 +40001a15: 205580 or a5, a5, a8 +40001a18: 115580 slli a5, a5, 8 +40001a1b: 205560 or a5, a5, a6 +40001a1e: c159 s32i.n a5, a1, 48 +40001a20: 6188 l32i.n a8, a1, 24 +40001a22: d198 l32i.n a9, a1, 52 +40001a24: a80b addi.n a10, a8, -1 +40001a26: 997a add.n a9, a9, a7 +40001a28: f199 s32i.n a9, a1, 60 +40001a2a: 7480a0 extui a8, a10, 0, 8 +40001a2d: 6189 s32i.n a8, a1, 24 +40001a2f: d97856 bnez a8, 400017ca <_XX_unk153c+0x28e> +40001a32: e1a8 l32i.n a10, a1, 56 +40001a34: ff6de5 call8 40001114 <_XX_set_0x3fffda30_0> +40001a37: 020c movi.n a2, 0 +40001a39: f01d retw.n +40001a3b: 451600 extui a1, a0, 22, 5 +40001a3e: 02ade5 call8 4000451c +40001a41: 41d250 srli a13, a5, 2 +40001a44: 7a1da6 blti a13, 1, 40001ac2 <_XX_unk153c+0x586> +40001a47: 276527 bbci a5, 2, 40001a72 <_XX_unk153c+0x536> +40001a4a: f198 l32i.n a9, a1, 60 +40001a4c: a24b addi.n a10, a2, 4 +40001a4e: 0009c2 l8ui a12, a9, 0 +40001a51: 0988 l32i.n a8, a9, 0 +40001a53: 0289 s32i.n a8, a2, 0 +40001a55: 75b080 extui a11, a8, 16, 8 +40001a58: 74e880 extui a14, a8, 8, 8 +40001a5b: 756880 extui a6, a8, 24, 8 +40001a5e: 994b addi.n a9, a9, 4 +40001a60: 0f6192 s32i a9, a1, 60 +40001a63: 306640 xor a6, a6, a4 +40001a66: 304ce0 xor a4, a12, a14 +40001a69: 304b40 xor a4, a11, a4 +40001a6c: 304460 xor a4, a4, a6 +40001a6f: 744040 extui a4, a4, 0, 8 +40001a72: 0afd mov.n a15, a10 +40001a74: f188 l32i.n a8, a1, 60 +40001a76: 4121d0 srli a2, a13, 1 +40001a79: 419276 loopnez a2, 40001abe <_XX_unk153c+0x582> +40001a7c: 030892 l8ui a9, a8, 3 +40001a7f: 0108b2 l8ui a11, a8, 1 +40001a82: 0008c2 l8ui a12, a8, 0 +40001a85: 0208d2 l8ui a13, a8, 2 +40001a88: 08e8 l32i.n a14, a8, 0 +40001a8a: 309940 xor a9, a9, a4 +40001a8d: 30bcb0 xor a11, a12, a11 +40001a90: 0fe9 s32i.n a14, a15, 0 +40001a92: 30bdb0 xor a11, a13, a11 +40001a95: 0708c2 l8ui a12, a8, 7 +40001a98: 0508d2 l8ui a13, a8, 5 +40001a9b: 0408e2 l8ui a14, a8, 4 +40001a9e: 060842 l8ui a4, a8, 6 +40001aa1: 1828 l32i.n a2, a8, 4 +40001aa3: 309b90 xor a9, a11, a9 +40001aa6: 30bed0 xor a11, a14, a13 +40001aa9: 1f29 s32i.n a2, a15, 4 +40001aab: 888b addi.n a8, a8, 8 +40001aad: 749090 extui a9, a9, 0, 8 +40001ab0: 30b4b0 xor a11, a4, a11 +40001ab3: ff8b addi.n a15, a15, 8 +40001ab5: 309c90 xor a9, a12, a9 +40001ab8: 309b90 xor a9, a11, a9 +40001abb: 744090 extui a4, a9, 0, 8 +40001abe: f189 s32i.n a8, a1, 60 +40001ac0: 0fad mov.n a10, a15 +40001ac2: 0a2d mov.n a2, a10 +40001ac4: ff7306 j 40001894 <_XX_unk153c+0x358> +40001ac7: 01a100 slli a10, a1, 32 +40001aca: ff .byte 0xff +40001acb: ff01b1 l32r a11, 400016d0 <_XX_unk153c+0x194> +40001ace: 00d365 call8 40002804 +40001ad1: 120c movi.n a2, 1 +40001ad3: f01d retw.n +40001ad5: 000000 ill +40001ad8: edc516 beqz a5, 400019b8 <_XX_unk153c+0x47c> +40001adb: 02ad mov.n a10, a2 +40001add: 41d250 srli a13, a5, 2 +40001ae0: 021de6 bgei a13, 1, 40001ae6 <_XX_unk153c+0x5aa> +40001ae3: ffb446 j 400019b8 <_XX_unk153c+0x47c> +40001ae6: 286527 bbci a5, 2, 40001b12 <_XX_unk153c+0x5d6> +40001ae9: f198 l32i.n a9, a1, 60 +40001aeb: a24b addi.n a10, a2, 4 +40001aed: 0009c2 l8ui a12, a9, 0 +40001af0: 0988 l32i.n a8, a9, 0 +40001af2: 0289 s32i.n a8, a2, 0 +40001af4: 75b080 extui a11, a8, 16, 8 +40001af7: 74e880 extui a14, a8, 8, 8 +40001afa: 756880 extui a6, a8, 24, 8 +40001afd: 04c992 addi a9, a9, 4 +40001b00: 0f6192 s32i a9, a1, 60 +40001b03: 306640 xor a6, a6, a4 +40001b06: 304ce0 xor a4, a12, a14 +40001b09: 304b40 xor a4, a11, a4 +40001b0c: 304460 xor a4, a4, a6 +40001b0f: 744040 extui a4, a4, 0, 8 +40001b12: 0afd mov.n a15, a10 +40001b14: f188 l32i.n a8, a1, 60 +40001b16: 4121d0 srli a2, a13, 1 +40001b19: 419276 loopnez a2, 40001b5e <_XX_unk153c+0x622> +40001b1c: 030892 l8ui a9, a8, 3 +40001b1f: 0108b2 l8ui a11, a8, 1 +40001b22: 0008c2 l8ui a12, a8, 0 +40001b25: 0208d2 l8ui a13, a8, 2 +40001b28: 08e8 l32i.n a14, a8, 0 +40001b2a: 309940 xor a9, a9, a4 +40001b2d: 30bcb0 xor a11, a12, a11 +40001b30: 0fe9 s32i.n a14, a15, 0 +40001b32: 30bdb0 xor a11, a13, a11 +40001b35: 0708c2 l8ui a12, a8, 7 +40001b38: 0508d2 l8ui a13, a8, 5 +40001b3b: 0408e2 l8ui a14, a8, 4 +40001b3e: 060842 l8ui a4, a8, 6 +40001b41: 1828 l32i.n a2, a8, 4 +40001b43: 309b90 xor a9, a11, a9 +40001b46: 30bed0 xor a11, a14, a13 +40001b49: 1f29 s32i.n a2, a15, 4 +40001b4b: 888b addi.n a8, a8, 8 +40001b4d: 749090 extui a9, a9, 0, 8 +40001b50: 30b4b0 xor a11, a4, a11 +40001b53: ff8b addi.n a15, a15, 8 +40001b55: 309c90 xor a9, a12, a9 +40001b58: 309b90 xor a9, a11, a9 +40001b5b: 744090 extui a4, a9, 0, 8 +40001b5e: f189 s32i.n a8, a1, 60 +40001b60: ff9506 j 400019b8 <_XX_unk153c+0x47c> +40001b63: 402000 ssa8l a0 +40001b66: c58275 call12 3ffc738c <_start-0x38c74> +40001b69: 87f8 l32i.n a15, a7, 32 +40001b6b: 060203 excw +40001b6e: 500032 l8ui a3, a0, 80 +40001b71: 16f474 excw +40001b74: 0c0917 bnone a9, a1, 40001b84 <_XX_unk153c+0x648> +40001b77: 031c05 call0 40004d38 +40001b7a: 918b addi.n a9, a1, 8 +40001b7c: b199 s32i.n a9, a1, 44 +40001b7e: 03ad mov.n a10, a3 +40001b80: 01bd mov.n a11, a1 +40001b82: 0c1c movi.n a12, 16 +40001b84: 02d125 call8 40004898 +40001b87: ea7a56 bnez a10, 40001a32 <_XX_unk153c+0x4f6> +40001b8a: 10c332 addi a3, a3, 16 +40001b8d: 449c beqz.n a4, 40001ba5 <_XX_unk153c+0x669> +40001b8f: 01ad mov.n a10, a1 +40001b91: 0247e5 call8 40004010 <_XX_unk4010> +40001b94: 440b addi.n a4, a4, -1 +40001b96: f44040 extui a4, a4, 0, 16 +40001b99: 848c beqz.n a4, 40001ba5 <_XX_unk153c+0x669> +40001b9b: b1a8 l32i.n a10, a1, 44 +40001b9d: 024725 call8 40004010 <_XX_unk4010> +40001ba0: 440b addi.n a4, a4, -1 +40001ba2: f44040 extui a4, a4, 0, 16 +40001ba5: 551b addi.n a5, a5, 1 +40001ba7: f45050 extui a5, a5, 0, 16 +40001baa: d09757 bne a7, a5, 40001b7e <_XX_unk153c+0x642> +40001bad: 001646 j 40001c0a <_XX_unk153c+0x6ce> +40001bb0: 7480e0 extui a8, a14, 0, 8 +40001bb3: c198 l32i.n a9, a1, 48 +40001bb5: 7199 s32i.n a9, a1, 28 +40001bb7: d189 s32i.n a8, a1, 52 +40001bb9: ff4f06 j 400018f9 <_XX_unk153c+0x3bd> +40001bbc: 05cd00 extui a12, a0, 29, 1 +40001bbf: fec5a1 l32r a10, 400016d4 <_XX_unk153c+0x198> +40001bc2: d1b8 l32i.n a11, a1, 52 +40001bc4: 06dd mov.n a13, a6 +40001bc6: 00c3e5 call8 40002804 +40001bc9: b27a add.n a11, a2, a7 +40001bcb: 10c1a2 addi a10, a1, 16 +40001bce: 06cd mov.n a12, a6 +40001bd0: 04da25 call8 40006974 +40001bd3: 03ad mov.n a10, a3 +40001bd5: 01bd mov.n a11, a1 +40001bd7: 0c1c movi.n a12, 16 +40001bd9: 02cbe5 call8 40004898 +40001bdc: 0eca56 bnez a10, 40001ccc <_XX_unk153c+0x790> +40001bdf: 10c332 addi a3, a3, 16 +40001be2: 01bd mov.n a11, a1 +40001be4: 8c0c movi.n a12, 8 +40001be6: 10c1a2 addi a10, a1, 16 +40001be9: a6aa add.n a10, a6, a10 +40001beb: c0cc60 sub a12, a12, a6 +40001bee: 74c0c0 extui a12, a12, 0, 8 +40001bf1: d1c9 s32i.n a12, a1, 52 +40001bf3: 04d825 call8 40006974 +40001bf6: 41b8 l32i.n a11, a1, 16 +40001bf8: 51c8 l32i.n a12, a1, 20 +40001bfa: c1c9 s32i.n a12, a1, 48 +40001bfc: ff8806 j 40001a20 <_XX_unk153c+0x4e4> +40001bff: c1d800 mul16u a13, a8, a0 +40001c02: 71d9 s32i.n a13, a1, 28 +40001c04: ff2a06 j 400018b0 <_XX_unk153c+0x374> +40001c07: 031c00 excw +40001c0a: 03ad mov.n a10, a3 +40001c0c: 01bd mov.n a11, a1 +40001c0e: 0c1c movi.n a12, 16 +40001c10: 02c865 call8 40004898 +40001c13: b21a56 bnez a10, 40001738 <_XX_unk153c+0x1fc> +40001c16: 10c332 addi a3, a3, 16 +40001c19: 448c beqz.n a4, 40001c21 <_XX_unk153c+0x6e5> +40001c1b: 20a110 or a10, a1, a1 +40001c1e: 023f25 call8 40004010 <_XX_unk4010> +40001c21: 744020 extui a4, a2, 0, 8 +40001c24: 04ad mov.n a10, a4 +40001c26: 0299a5 call8 400045c0 <_X_SPIReadModeConfig+0x88> +40001c29: 348820 extui a8, a2, 8, 4 +40001c2c: 1838f6 bgeui a8, 3, 40001c48 <_XX_unk153c+0x70c> +40001c2f: 282b addi.n a2, a8, 2 +40001c31: 742020 extui a2, a2, 0, 8 +40001c34: 000486 j 40001c4a <_XX_unk153c+0x70e> +40001c37: a10000 sll a0, a0 +40001c3a: 50fea7 bbsi a14, 26, 40001c8e <_XX_unk153c+0x752> +40001c3d: 6520b5 call12 40066e48 <__bss_start+0x56e48> +40001c40: 00bc beqz.n a0, 40001c74 <_XX_unk153c+0x738> +40001c42: 01a022 movi a2, 1 +40001c45: 000090 retw +40001c48: 120c movi.n a2, 1 +40001c4a: 20c440 or a12, a4, a4 +40001c4d: fea3a1 l32r a10, 400016dc <_XX_unk153c+0x1a0> +40001c50: 20b220 or a11, a2, a2 +40001c53: 00bb25 call8 40002804 +40001c56: 20a220 or a10, a2, a2 +40001c59: 02a1e5 call8 40004678 <_X_SPIReadModeConfig+0x140> +40001c5c: 0e0192 l8ui a9, a1, 14 +40001c5f: 080152 l8ui a5, a1, 8 +40001c62: 0b0122 l8ui a2, a1, 11 +40001c65: 0a0182 l8ui a8, a1, 10 +40001c68: 112280 slli a2, a2, 8 +40001c6b: 202280 or a2, a2, a8 +40001c6e: 090182 l8ui a8, a1, 9 +40001c71: 112280 slli a2, a2, 8 +40001c74: 202280 or a2, a2, a8 +40001c77: 112280 slli a2, a2, 8 +40001c7a: 202250 or a2, a2, a5 +40001c7d: 0f0152 l8ui a5, a1, 15 +40001c80: 0c0182 l8ui a8, a1, 12 +40001c83: 115580 slli a5, a5, 8 +40001c86: 205590 or a5, a5, a9 +40001c89: 0d0192 l8ui a9, a1, 13 +40001c8c: 115580 slli a5, a5, 8 +40001c8f: 205590 or a5, a5, a9 +40001c92: 115580 slli a5, a5, 8 +40001c95: 205580 or a5, a5, a8 +40001c98: fec206 j 400017a4 <_XX_unk153c+0x268> +40001c9b: a10000 sll a0, a0 +40001c9e: 40fe90 nsau a9, a14 +40001ca1: 2520b4 excw +40001ca4: 8200b6 bltui a0, 0x8000, 40001c2a <_XX_unk153c+0x6ee> +40001ca7: 400f01 l32r a0, 3ffd1ce4 <_start-0x2e31c> +40001caa: c088 l32i.n a8, a0, 48 +40001cac: d82816 beqz a8, 40001a32 <_XX_unk153c+0x4f6> +40001caf: fe8da1 l32r a10, 400016e4 <_XX_unk153c+0x1a8> +40001cb2: 00b525 call8 40002804 +40001cb5: 01a022 movi a2, 1 +40001cb8: f01d retw.n +40001cba: a10000 sll a0, a0 +40001cbd: b2fe84 excw +40001cc0: 25efa2 s32c1i a10, a15, 148 +40001cc3: 0c00b4 excw +40001cc6: f01d12 l16ui a1, a13, 0x1e0 +40001cc9: 000000 ill +40001ccc: fe80a1 l32r a10, 400016cc <_XX_unk153c+0x190> +40001ccf: 2ca3b2 movi a11, 0x32c +40001cd2: 00b325 call8 40002804 +40001cd5: ff5646 j 40001a32 <_XX_unk153c+0x4f6> + +40001cd8 <_c_data_end>: +40001cd8: ffc864 excw +40001cdb: 3f .byte 0x3f + +40001cdc <_s_mem_banner>: +40001cdc: cb68 l32i.n a6, a11, 48 +40001cde: ff .byte 0xff +40001cdf: 3f .byte 0x3f + +40001ce0 <_c_stack_sentry>: +40001ce0: ffe1d0 excw +40001ce3: 3f .byte 0x3f + +40001ce4 <_c_data_start>: +40001ce4: ffc000 excw +40001ce7: 3f .byte 0x3f + +40001ce8 <_X_print_mem_banner>: +40001ce8: 006136 entry a1, 48 +40001ceb: fffca1 l32r a10, 40001cdc <_s_mem_banner> +40001cee: fffcb1 l32r a11, 40001ce0 <_c_stack_sentry> +40001cf1: fa83c1 l32r a12, 40000700 <_c_stack> +40001cf4: fa85d1 l32r a13, 40000708 <_c_bss_start> +40001cf7: fa85e1 l32r a14, 4000070c <_c_bss_end> +40001cfa: fffaf1 l32r a15, 40001ce4 <_c_data_start> +40001cfd: fff681 l32r a8, 40001cd8 <_c_data_end> +40001d00: 0189 s32i.n a8, a1, 0 +40001d02: 00b025 call8 40002804 +40001d05: f01d retw.n +40001d07: da4000 depbits a0, a0, 13, 5 +40001d0a: ff .byte 0xff +40001d0b: 3f .byte 0x3f + +40001d0c <_s_exc_sp_fmt>: +40001d0c: ffcbe4 excw +40001d0f: 3f .byte 0x3f + +40001d10 <_s_exc_sf_dump_fmt>: +40001d10: cbec bnez.n a11, 40001d40 <_X_exc_handler+0x28> +40001d12: ff .byte 0xff +40001d13: 3f .byte 0x3f + +40001d14 <_s_exc_regs_fmt>: +40001d14: cc2c movi.n a12, 44 +40001d16: ff .byte 0xff +40001d17: 3f .byte 0x3f + +40001d18 <_X_exc_handler>: +40001d18: 004136 entry a1, 32 +40001d1b: 012d mov.n a2, a1 +40001d1d: 03e765 call8 40005b94 <_XX_xtos_exc_unk5b94> +40001d20: 030c movi.n a3, 0 +40001d22: f8add2 movi a13, 0xfffffdf8 +40001d25: fff841 l32r a4, 40001d08 <_X_print_mem_banner+0x20> +40001d28: 580c movi.n a8, 5 +40001d2a: 209440 or a9, a4, a4 +40001d2d: 1ea876 loopgtz a8, 40001d4f <_X_exc_handler+0x37> +40001d30: c2da add.n a12, a2, a13 +40001d32: 7e2cb2 l32i a11, a12, 0x1f8 +40001d35: 09b9 s32i.n a11, a9, 0 +40001d37: 7f2ca2 l32i a10, a12, 0x1fc +40001d3a: 19a9 s32i.n a10, a9, 4 +40001d3c: 802ce2 l32i a14, a12, 0x200 +40001d3f: 29e9 s32i.n a14, a9, 8 +40001d41: 812cc2 l32i a12, a12, 0x204 +40001d44: 39c9 s32i.n a12, a9, 12 +40001d46: 5b8c beqz.n a11, 40001d4f <_X_exc_handler+0x37> +40001d48: 10c992 addi a9, a9, 16 +40001d4b: 0a2d mov.n a2, a10 +40001d4d: 331b addi.n a3, a3, 1 +40001d4f: ffefa1 l32r a10, 40001d0c <_s_exc_sp_fmt> +40001d52: 02bd mov.n a11, a2 +40001d54: 00aae5 call8 40002804 +40001d57: 020c movi.n a2, 0 +40001d59: 1713a6 blti a3, 1, 40001d74 <_X_exc_handler+0x5c> +40001d5c: ffeda1 l32r a10, 40001d10 <_s_exc_sf_dump_fmt> +40001d5f: 02bd mov.n a11, a2 +40001d61: 04c8 l32i.n a12, a4, 0 +40001d63: 14d8 l32i.n a13, a4, 4 +40001d65: 24e8 l32i.n a14, a4, 8 +40001d67: 34f8 l32i.n a15, a4, 12 +40001d69: 00a9a5 call8 40002804 +40001d6c: 10c442 addi a4, a4, 16 +40001d6f: 221b addi.n a2, a2, 1 +40001d71: e79237 bne a2, a3, 40001d5c <_X_exc_handler+0x44> +40001d74: ffe8a1 l32r a10, 40001d14 <_s_exc_regs_fmt> +40001d77: 03b1b0 rsr.epc1 a11 +40001d7a: 03b2c0 rsr.epc2 a12 +40001d7d: 03b3d0 rsr.epc3 a13 +40001d80: 03eee0 rsr.excvaddr a14 +40001d83: 03c0f0 rsr.depc a15 +40001d86: 00a7e5 call8 40002804 +40001d89: ffff06 j 40001d89 <_X_exc_handler+0x71> +40001d8c: ffcc74 excw +40001d8f: 3f .byte 0x3f + +40001d90 <_XX_unk1d90>: +40001d90: 004136 entry a1, 32 +40001d93: 03e8b0 rsr.exccause a11 +40001d96: fffda1 l32r a10, 40001d8c <_X_exc_handler+0x74> +40001d99: 00a6a5 call8 40002804 +40001d9c: ffdea1 l32r a10, 40001d14 <_s_exc_regs_fmt> +40001d9f: 03b1b0 rsr.epc1 a11 +40001da2: 03b2c0 rsr.epc2 a12 +40001da5: 03b3d0 rsr.epc3 a13 +40001da8: 03eee0 rsr.excvaddr a14 +40001dab: 03c0f0 rsr.depc a15 +40001dae: 00a565 call8 40002804 +40001db1: ffff06 j 40001db1 <_XX_unk1d90+0x21> + +40001db4 <_X_ets_memset>: +40001db4: 004136 entry a1, 32 +40001db7: 04cd mov.n a12, a4 +40001db9: 03bd mov.n a11, a3 +40001dbb: 02ad mov.n a10, a2 +40001dbd: 04e265 call8 40006be4 +40001dc0: 0a2d mov.n a2, a10 +40001dc2: f01d retw.n + +40001dc4 <_X_ets_memcpy>: +40001dc4: 004136 entry a1, 32 +40001dc7: 04cd mov.n a12, a4 +40001dc9: 03bd mov.n a11, a3 +40001dcb: 02ad mov.n a10, a2 +40001dcd: 04ba65 call8 40006974 +40001dd0: 0a2d mov.n a2, a10 +40001dd2: f01d retw.n + +40001dd4 <_X_ets_memmove>: +40001dd4: 004136 entry a1, 32 +40001dd7: 04cd mov.n a12, a4 +40001dd9: 03bd mov.n a11, a3 +40001ddb: 02ad mov.n a10, a2 +40001ddd: 04c8e5 call8 40006a6c +40001de0: 0a2d mov.n a2, a10 +40001de2: f01d retw.n + +40001de4 <_X_ets_memcmp>: +40001de4: 004136 entry a1, 32 +40001de7: 04cd mov.n a12, a4 +40001de9: 03bd mov.n a11, a3 +40001deb: 02ad mov.n a10, a2 +40001ded: 04afe5 call8 400068ec +40001df0: 0a2d mov.n a2, a10 +40001df2: f01d retw.n +40001df4: ffda90 excw +40001df7: 3f .byte 0x3f +40001df8: 004136 entry a1, 32 +40001dfb: 000292 l8ui a9, a2, 0 +40001dfe: fffd81 l32r a8, 40001df4 <_X_ets_memcmp+0x10> +40001e01: 010262 l8ui a6, a2, 1 +40001e04: 0858 l32i.n a5, a8, 0 +40001e06: 116680 slli a6, a6, 8 +40001e09: 206690 or a6, a6, a9 +40001e0c: 665a add.n a6, a6, a5 +40001e0e: 071267 beq a2, a6, 40001e19 <_X_ets_memcmp+0x35> +40001e11: 040692 l8ui a9, a6, 4 +40001e14: 18e8 l32i.n a14, a8, 4 +40001e16: 04e916 beqz a9, 40001e68 <_X_ets_memcmp+0x84> +40001e19: 030272 l8ui a7, a2, 3 +40001e1c: 020292 l8ui a9, a2, 2 +40001e1f: 117780 slli a7, a7, 8 +40001e22: 207790 or a7, a7, a9 +40001e25: 675a add.n a6, a7, a5 +40001e27: 391267 beq a2, a6, 40001e64 <_X_ets_memcmp+0x80> +40001e2a: 040692 l8ui a9, a6, 4 +40001e2d: 28a8 l32i.n a10, a8, 8 +40001e2f: 19fc bnez.n a9, 40001e64 <_X_ets_memcmp+0x80> +40001e31: 41b870 srli a11, a7, 8 +40001e34: 0102d2 l8ui a13, a2, 1 +40001e37: 0002e2 l8ui a14, a2, 0 +40001e3a: 11dd80 slli a13, a13, 8 +40001e3d: 20dde0 or a13, a13, a14 +40001e40: 0046d2 s8i a13, a6, 0 +40001e43: 41d8d0 srli a13, a13, 8 +40001e46: 0146d2 s8i a13, a6, 1 +40001e49: 0102c2 l8ui a12, a2, 1 +40001e4c: 0002d2 l8ui a13, a2, 0 +40001e4f: 11cc80 slli a12, a12, 8 +40001e52: 20ccd0 or a12, a12, a13 +40001e55: cc5a add.n a12, a12, a5 +40001e57: 024c72 s8i a7, a12, 2 +40001e5a: 034cb2 s8i a11, a12, 3 +40001e5d: 019a27 bne a10, a2, 40001e62 <_X_ets_memcmp+0x7e> +40001e60: 2869 s32i.n a6, a8, 8 +40001e62: f01d retw.n +40001e64: f01d retw.n +40001e66: 670000 excw +40001e69: 1e .byte 0x1e +40001e6a: 50ad excw +40001e6c: a2c032 addi a3, a0, -94 +40001e6f: 920006 j 3ffe6673 <_start-0x1998d> +40001e72: f20106 j 3fffe67a <_start-0x1986> +40001e75: 0228 l32i.n a2, a2, 0 +40001e77: 119980 slli a9, a9, 8 +40001e7a: 029f67 bne a15, a6, 40001e80 <_X_ets_memcmp+0x9c> +40001e7d: 026822 s32i a2, a8, 8 +40001e80: 2099a0 or a9, a9, a10 +40001e83: 004292 s8i a9, a2, 0 +40001e86: 419890 srli a9, a9, 8 +40001e89: 014292 s8i a9, a2, 1 +40001e8c: 010642 l8ui a4, a6, 1 +40001e8f: 000692 l8ui a9, a6, 0 +40001e92: 114480 slli a4, a4, 8 +40001e95: 204490 or a4, a4, a9 +40001e98: 445a add.n a4, a4, a5 +40001e9a: 024432 s8i a3, a4, 2 +40001e9d: 413830 srli a3, a3, 8 +40001ea0: 034432 s8i a3, a4, 3 +40001ea3: ffdc86 j 40001e19 <_X_ets_memcmp+0x35> +40001ea6: 360000 excw +40001ea9: 1c0041 l32r a4, 3ffc8eac <_start-0x37154> +40001eac: 051c03 excw +40001eaf: 180c movi.n a8, 1 +40001eb1: fca291 l32r a9, 4000113c <_c_0x1000> +40001eb4: 0d0c movi.n a13, 0 +40001eb6: 0c1c movi.n a12, 16 +40001eb8: cb7c movi.n a11, -4 +40001eba: a23b addi.n a10, a2, 3 +40001ebc: 10aab0 and a10, a10, a11 +40001ebf: 014ac2 s8i a12, a10, 1 +40001ec2: 024ad2 s8i a13, a10, 2 +40001ec5: 004a92 s8i a9, a10, 0 +40001ec8: 034ad2 s8i a13, a10, 3 +40001ecb: 044ad2 s8i a13, a10, 4 +40001ece: ffc9b1 l32r a11, 40001df4 <_X_ets_memcmp+0x10> +40001ed1: 10da42 addmi a4, a10, 0x1000 +40001ed4: 0ba9 s32i.n a10, a11, 0 +40001ed6: 2ba9 s32i.n a10, a11, 8 +40001ed8: 004492 s8i a9, a4, 0 +40001edb: 1b49 s32i.n a4, a11, 4 +40001edd: 024492 s8i a9, a4, 2 +40001ee0: 044482 s8i a8, a4, 4 +40001ee3: 014452 s8i a5, a4, 1 +40001ee6: 034432 s8i a3, a4, 3 +40001ee9: f01d retw.n +40001eeb: 413600 srli a3, a0, 6 +40001eee: b28c00 mulsh a8, a12, a0 +40001ef1: ffc0a1 l32r a10, 40001df4 <_X_ets_memcmp+0x10> +40001ef4: 0a88 l32i.n a8, a10, 0 +40001ef6: 1a98 l32i.n a9, a10, 4 +40001ef8: 023287 bltu a2, a8, 40001efe <_X_ets_memcmp+0x11a> +40001efb: 013297 bltu a2, a9, 40001f00 <_X_ets_memcmp+0x11c> +40001efe: f01d retw.n +40001f00: 0c0c movi.n a12, 0 +40001f02: 2ad8 l32i.n a13, a10, 8 +40001f04: f8c2b2 addi a11, a2, -8 +40001f07: 63ddb0 minu a13, a13, a11 +40001f0a: 044bc2 s8i a12, a11, 4 +40001f0d: 2ad9 s32i.n a13, a10, 8 +40001f0f: 0bad mov.n a10, a11 +40001f11: ffee65 call8 40001df8 <_X_ets_memcmp+0x14> +40001f14: f01d retw.n +40001f16: fc0000 excw +40001f19: ff .byte 0xff +40001f1a: 360000 excw +40001f1d: c10041 l32r a4, 3fff2320 <_start-0xdce0> +40001f20: 51fc87 bbsi a12, 24, 40001f75 <_X_ets_memcmp+0x191> +40001f23: fffd excw +40001f25: 433b addi.n a4, a3, 3 +40001f27: 104450 and a4, a4, a5 +40001f2a: c50c movi.n a5, 12 +40001f2c: 734450 maxu a4, a4, a5 +40001f2f: f43040 extui a3, a4, 0, 16 +40001f32: 02bc37 bgeu a12, a3, 40001f38 <_X_ets_memcmp+0x154> +40001f35: 002246 j 40001fc2 <_X_ets_memcmp+0x1de> +40001f38: ffaf91 l32r a9, 40001df4 <_X_ets_memcmp+0x10> +40001f3b: 0968 l32i.n a6, a9, 0 +40001f3d: 1988 l32i.n a8, a9, 4 +40001f3f: 7d3267 bltu a2, a6, 40001fc0 <_X_ets_memcmp+0x1dc> +40001f42: 7ab287 bgeu a2, a8, 40001fc0 <_X_ets_memcmp+0x1dc> +40001f45: f8c2b2 addi a11, a2, -8 +40001f48: 010b82 l8ui a8, a11, 1 +40001f4b: 000ba2 l8ui a10, a11, 0 +40001f4e: 118880 slli a8, a8, 8 +40001f51: 2088a0 or a8, a8, a10 +40001f54: c0a260 sub a10, a2, a6 +40001f57: f8caa2 addi a10, a10, -8 +40001f5a: c078a0 sub a7, a8, a10 +40001f5d: f8c772 addi a7, a7, -8 +40001f60: f47070 extui a7, a7, 0, 16 +40001f63: 5b3737 bltu a7, a3, 40001fc2 <_X_ets_memcmp+0x1de> +40001f66: 561737 beq a7, a3, 40001fc0 <_X_ets_memcmp+0x1dc> +40001f69: d86a add.n a13, a8, a6 +40001f6b: 040df2 l8ui a15, a13, 4 +40001f6e: 0e0c movi.n a14, 0 +40001f70: 052f16 beqz a15, 40001fc6 <_X_ets_memcmp+0x1e2> +40001f73: 14c342 addi a4, a3, 20 +40001f76: 463747 bltu a7, a4, 40001fc0 <_X_ets_memcmp+0x1dc> +40001f79: 2958 l32i.n a5, a9, 8 +40001f7b: 41f880 srli a15, a8, 8 +40001f7e: 4148a0 srli a4, a10, 8 +40001f81: 3a3a add.n a3, a10, a3 +40001f83: 338b addi.n a3, a3, 8 +40001f85: f4d030 extui a13, a3, 0, 16 +40001f88: d6da add.n a13, a6, a13 +40001f8a: 034d42 s8i a4, a13, 3 +40001f8d: 024da2 s8i a10, a13, 2 +40001f90: 014df2 s8i a15, a13, 1 +40001f93: 044de2 s8i a14, a13, 4 +40001f96: 004d82 s8i a8, a13, 0 +40001f99: 6355d0 minu a5, a5, a13 +40001f9c: 2959 s32i.n a5, a9, 8 +40001f9e: 418830 srli a8, a3, 8 +40001fa1: 004b32 s8i a3, a11, 0 +40001fa4: 014b82 s8i a8, a11, 1 +40001fa7: 010d72 l8ui a7, a13, 1 +40001faa: 000dd2 l8ui a13, a13, 0 +40001fad: 117780 slli a7, a7, 8 +40001fb0: 2077d0 or a7, a7, a13 +40001fb3: 0917c7 beq a7, a12, 40001fc0 <_X_ets_memcmp+0x1dc> +40001fb6: 476a add.n a4, a7, a6 +40001fb8: 024432 s8i a3, a4, 2 +40001fbb: 034482 s8i a8, a4, 3 +40001fbe: f01d retw.n +40001fc0: f01d retw.n +40001fc2: 020c movi.n a2, 0 +40001fc4: f01d retw.n +40001fc6: 2958 l32i.n a5, a9, 8 +40001fc8: 4148a0 srli a4, a10, 8 +40001fcb: 000db2 l8ui a11, a13, 0 +40001fce: 010d82 l8ui a8, a13, 1 +40001fd1: 3a3a add.n a3, a10, a3 +40001fd3: 338b addi.n a3, a3, 8 +40001fd5: f47030 extui a7, a3, 0, 16 +40001fd8: 118880 slli a8, a8, 8 +40001fdb: 2088b0 or a8, a8, a11 +40001fde: 807670 add a7, a6, a7 +40001fe1: 0247a2 s8i a10, a7, 2 +40001fe4: 0447e2 s8i a14, a7, 4 +40001fe7: 034742 s8i a4, a7, 3 +40001fea: 41f880 srli a15, a8, 8 +40001fed: 004782 s8i a8, a7, 0 +40001ff0: 0147f2 s8i a15, a7, 1 +40001ff3: 418830 srli a8, a3, 8 +40001ff6: 0295d7 bne a5, a13, 40001ffc <_X_ets_memcmp+0x218> +40001ff9: 026972 s32i a7, a9, 8 +40001ffc: f8c2d2 addi a13, a2, -8 +40001fff: 014d82 s8i a8, a13, 1 +40002002: 004d32 s8i a3, a13, 0 +40002005: 0107b2 l8ui a11, a7, 1 +40002008: 000792 l8ui a9, a7, 0 +4000200b: 117b80 slli a7, a11, 8 +4000200e: 207790 or a7, a7, a9 +40002011: ab17c7 beq a7, a12, 40001fc0 <_X_ets_memcmp+0x1dc> +40002014: 976a add.n a9, a7, a6 +40002016: 024932 s8i a3, a9, 2 +40002019: 034982 s8i a8, a9, 3 +4000201c: f01d retw.n +4000201e: 360000 excw +40002021: 160041 l32r a4, 3ffc7824 <_start-0x387dc> +40002024: b105d2 l8ui a13, a5, 177 +40002027: ffbc beqz.n a15, 4000206a <_X_ets_memcmp+0x286> +40002029: a23b addi.n a10, a2, 3 +4000202b: 10aab0 and a10, a10, a11 +4000202e: cb0c movi.n a11, 12 +40002030: 73aab0 maxu a10, a10, a11 +40002033: fc42b1 l32r a11, 4000113c <_c_0x1000> +40002036: f4a0a0 extui a10, a10, 0, 16 +40002039: 473ba7 bltu a11, a10, 40002084 <_X_ets_memcmp+0x2a0> +4000203c: ff6ed1 l32r a13, 40001df4 <_X_ets_memcmp+0x10> +4000203f: 002d62 l32i a6, a13, 0 +40002042: 022d92 l32i a9, a13, 8 +40002045: c07ba0 sub a7, a11, a10 +40002048: c02960 sub a2, a9, a6 +4000204b: f42020 extui a2, a2, 0, 16 +4000204e: 32a277 bge a2, a7, 40002084 <_X_ets_memcmp+0x2a0> +40002051: 188076 loop a0, 4000206d <_X_ets_memcmp+0x289> +40002054: 862a add.n a8, a6, a2 +40002056: 040832 l8ui a3, a8, 4 +40002059: 010852 l8ui a5, a8, 1 +4000205c: 0008c2 l8ui a12, a8, 0 +4000205f: 115580 slli a5, a5, 8 +40002062: 2055c0 or a5, a5, a12 +40002065: 007316 beqz a3, 40002070 <_X_ets_memcmp+0x28c> +40002068: 052d mov.n a2, a5 +4000206a: 16a577 bge a5, a7, 40002084 <_X_ets_memcmp+0x2a0> +4000206d: fff806 j 40002051 <_X_ets_memcmp+0x26d> +40002070: c0c520 sub a12, a5, a2 +40002073: f8ccc2 addi a12, a12, -8 +40002076: ee3ca7 bltu a12, a10, 40002068 <_X_ets_memcmp+0x284> +40002079: 1e0c movi.n a14, 1 +4000207b: 14caf2 addi a15, a10, 20 +4000207e: 4f3cf7 bltu a12, a15, 400020d1 <_X_ets_memcmp+0x2ed> +40002081: 0000c6 j 40002088 <_X_ets_memcmp+0x2a4> +40002084: 020c movi.n a2, 0 +40002086: f01d retw.n +40002088: 41f820 srli a15, a2, 8 +4000208b: 41c850 srli a12, a5, 8 +4000208e: 040c movi.n a4, 0 +40002090: 72aa add.n a7, a2, a10 +40002092: 778b addi.n a7, a7, 8 +40002094: f43070 extui a3, a7, 0, 16 +40002097: 363a add.n a3, a6, a3 +40002099: 044342 s8i a4, a3, 4 +4000209c: 0143c2 s8i a12, a3, 1 +4000209f: 024322 s8i a2, a3, 2 +400020a2: 004352 s8i a5, a3, 0 +400020a5: 0343f2 s8i a15, a3, 3 +400020a8: 415870 srli a5, a7, 8 +400020ab: 0448e2 s8i a14, a8, 4 +400020ae: 004872 s8i a7, a8, 0 +400020b1: 014852 s8i a5, a8, 1 +400020b4: 010322 l8ui a2, a3, 1 +400020b7: 000332 l8ui a3, a3, 0 +400020ba: 112280 slli a2, a2, 8 +400020bd: 202230 or a2, a2, a3 +400020c0: 1012b7 beq a2, a11, 400020d4 <_X_ets_memcmp+0x2f0> +400020c3: a62a add.n a10, a6, a2 +400020c5: 024a72 s8i a7, a10, 2 +400020c8: 034a52 s8i a5, a10, 3 +400020cb: 000146 j 400020d4 <_X_ets_memcmp+0x2f0> +400020ce: 000000 ill +400020d1: 0448e2 s8i a14, a8, 4 +400020d4: 219987 bne a9, a8, 400020f9 <_X_ets_memcmp+0x315> +400020d7: 0409b2 l8ui a11, a9, 4 +400020da: bb9c beqz.n a11, 400020f9 <_X_ets_memcmp+0x315> +400020dc: 1d28 l32i.n a2, a13, 4 +400020de: 171297 beq a2, a9, 400020f9 <_X_ets_memcmp+0x315> +400020e1: 0109b2 l8ui a11, a9, 1 +400020e4: 0009a2 l8ui a10, a9, 0 +400020e7: 119b80 slli a9, a11, 8 +400020ea: 2099a0 or a9, a9, a10 +400020ed: 969a add.n a9, a6, a9 +400020ef: 0409c2 l8ui a12, a9, 4 +400020f2: 2d99 s32i.n a9, a13, 8 +400020f4: 1c8c beqz.n a12, 400020f9 <_X_ets_memcmp+0x315> +400020f6: e79297 bne a2, a9, 400020e1 <_X_ets_memcmp+0x2fd> +400020f9: 288b addi.n a2, a8, 8 +400020fb: f01d retw.n +400020fd: 000000 ill +40002100: 004136 entry a1, 32 +40002103: c13230 mul16u a3, a2, a3 +40002106: f4a030 extui a10, a3, 0, 16 +40002109: fff165 call8 40002020 <_X_ets_memcmp+0x23c> +4000210c: 202aa0 or a2, a10, a10 +4000210f: 007a16 beqz a10, 4000211a <_X_ets_memcmp+0x336> +40002112: 20c330 or a12, a3, a3 +40002115: 0b0c movi.n a11, 0 +40002117: ffc9e5 call8 40001db4 <_X_ets_memset> +4000211a: f01d retw.n +4000211c: 004136 entry a1, 32 +4000211f: 02bd mov.n a11, a2 +40002121: 1a0c movi.n a10, 1 +40002123: fffde5 call8 40002100 <_X_ets_memcmp+0x31c> +40002126: 0a2d mov.n a2, a10 +40002128: f01d retw.n +4000212a: 360000 excw +4000212d: 4d0041 l32r a4, 3ffd5530 <_start-0x2aad0> +40002130: 03ad02 movi a0, 0xfffffd03 +40002133: ffeee5 call8 40002020 <_X_ets_memcmp+0x23c> +40002136: 0a2d mov.n a2, a10 +40002138: 2acc bnez.n a10, 4000213e <_X_ets_memcmp+0x35a> +4000213a: f01d retw.n +4000213c: 300000 xor a0, a0, a0 +4000213f: 4020c3 excw +40002142: e520b4 excw +40002145: 40ffc7 bbsi a15, 28, 40002189 <_st_0x3fffda9c+0x39> +40002148: 2520a4 excw +4000214b: ffda add.n a15, a15, a13 +4000214d: f01d retw.n + ... + +40002150 <_st_0x3fffda9c>: +40002150: da9c beqz.n a10, 40002171 <_st_0x3fffda9c+0x21> +40002152: ff .byte 0xff +40002153: 3f .byte 0x3f +40002154: 00fff2 s32ri a15, a15, 0 +40002157: 413600 srli a3, a0, 6 +4000215a: fd4100 excw +4000215d: ff .byte 0xff +4000215e: 1258 l32i.n a5, a2, 4 +40002160: 0429 s32i.n a2, a4, 0 +40002162: 55eb addi.n a5, a5, 14 +40002164: 1459 s32i.n a5, a4, 4 +40002166: fffb51 l32r a5, 40002154 <_st_0x3fffda9c+0x4> +40002169: 0020c0 memw +4000216c: 0238 l32i.n a3, a2, 0 +4000216e: b43030 extui a3, a3, 0, 12 +40002171: 335a add.n a3, a3, a5 +40002173: 045432 s16i a3, a4, 8 +40002176: f01d retw.n +40002178: cc8c beqz.n a12, 40002188 <_st_0x3fffda9c+0x38> +4000217a: ff .byte 0xff +4000217b: 3f .byte 0x3f +4000217c: 004136 entry a1, 32 +4000217f: fff4b1 l32r a11, 40002150 <_st_0x3fffda9c> +40002182: 041b92 l16ui a9, a11, 8 +40002185: 1b88 l32i.n a8, a11, 4 +40002187: d98c beqz.n a9, 40002198 <_st_0x3fffda9c+0x48> +40002189: 004822 s8i a2, a8, 0 +4000218c: a90b addi.n a10, a9, -1 +4000218e: 045ba2 s16i a10, a11, 8 +40002191: 881b addi.n a8, a8, 1 +40002193: 1b89 s32i.n a8, a11, 4 +40002195: f01d retw.n +40002197: f8a100 excw +4000219a: ff .byte 0xff +4000219b: 0066a5 call8 40002804 +4000219e: f01d retw.n +400021a0: 217c movi.n a1, -14 +400021a2: 364000 excw +400021a5: bd00a1 l32r a10, 3fff15a8 <_start-0xea58> +400021a8: 412902 l32i a0, a9, 0x104 +400021ab: 5139 s32i.n a3, a1, 20 +400021ad: 6149 s32i.n a4, a1, 24 +400021af: 7159 s32i.n a5, a1, 28 +400021b1: 8169 s32i.n a6, a1, 32 +400021b3: 9179 s32i.n a7, a1, 36 +400021b5: fffaa1 l32r a10, 400021a0 <_st_0x3fffda9c+0x50> +400021b8: 30c1c2 addi a12, a1, 48 +400021bb: 4e0c movi.n a14, 4 +400021bd: 10c1d2 addi a13, a1, 16 +400021c0: 11d9 s32i.n a13, a1, 4 +400021c2: 21e9 s32i.n a14, a1, 8 +400021c4: 01c9 s32i.n a12, a1, 0 +400021c6: 0015e5 call8 40002324 <_X_ets_unk225c+0xc8> +400021c9: 0a2d mov.n a2, a10 +400021cb: f01d retw.n +400021cd: 000000 ill +400021d0: 004136 entry a1, 32 +400021d3: ffdfa1 l32r a10, 40002150 <_st_0x3fffda9c> +400021d6: 002ab2 l32i a11, a10, 0 +400021d9: 012bd2 l32i a13, a11, 4 +400021dc: 0ccd92 addi a9, a13, 12 +400021df: 0020c0 memw +400021e2: f0a0f2 movi a15, 240 +400021e5: 0be8 l32i.n a14, a11, 0 +400021e7: cd2b addi.n a12, a13, 2 +400021e9: b480e0 extui a8, a14, 0, 12 +400021ec: 34e8e0 extui a14, a14, 8, 4 +400021ef: 004c82 s8i a8, a12, 0 +400021f2: 014ce2 s8i a14, a12, 1 +400021f5: 8c0c movi.n a12, 8 +400021f7: 000de2 l8ui a14, a13, 0 +400021fa: 014dc2 s8i a12, a13, 1 +400021fd: 10eef0 and a14, a14, a15 +40002200: 004de2 s8i a14, a13, 0 +40002203: 041aa2 l16ui a10, a10, 8 +40002206: 0020c0 memw +40002209: 0b88 l32i.n a8, a11, 0 +4000220b: b48080 extui a8, a8, 0, 12 +4000220e: c088a0 sub a8, a8, a10 +40002211: 0bad mov.n a10, a11 +40002213: f2c882 addi a8, a8, -14 +40002216: 005982 s16i a8, a9, 0 +40002219: 02fb25 call8 400051cc <_XX_unk51ac+0x20> +4000221c: f01d retw.n +4000221e: 360000 excw +40002221: 310041 l32r a4, 3ffce624 <_start-0x319dc> +40002224: ffcb addi.n a15, a15, 12 +40002226: 33b8 l32i.n a11, a3, 12 +40002228: 4b8c beqz.n a11, 40002230 <_st_0x3fffda9c+0xe0> +4000222a: 20a220 or a10, a2, a2 +4000222d: 000be0 callx8 a11 +40002230: 43b8 l32i.n a11, a3, 16 +40002232: 3b8c beqz.n a11, 40002239 <_st_0x3fffda9c+0xe9> +40002234: 02ad mov.n a10, a2 +40002236: 000be0 callx8 a11 +40002239: f01d retw.n + ... + +4000223c <_X_ets_uart_putc>: +4000223c: 004136 entry a1, 32 +4000223f: 0b9226 beqi a2, 10, 4000224e <_X_ets_uart_putc+0x12> +40002242: d80c movi.n a8, 13 +40002244: 041287 beq a2, a8, 4000224c <_X_ets_uart_putc+0x10> +40002247: 02ad mov.n a10, a2 +40002249: 013e25 call8 4000362c +4000224c: f01d retw.n +4000224e: 0da0a2 movi a10, 13 +40002251: 013da5 call8 4000362c +40002254: 0aa0a2 movi a10, 10 +40002257: 013d65 call8 4000362c +4000225a: f01d retw.n + +4000225c <_X_ets_unk225c>: +4000225c: 00a136 entry a1, 80 +4000225f: 068d mov.n a8, a6 +40002261: 090c movi.n a9, 0 +40002263: 1b0c movi.n a11, 1 +40002265: 0a0c movi.n a10, 0 +40002267: 83ab30 moveqz a10, a11, a3 +4000226a: 839b20 moveqz a9, a11, a2 +4000226d: 0909a7 bnone a9, a10, 4000227a <_X_ets_unk225c+0x1e> +40002270: 711b addi.n a7, a1, 1 +40002272: 0c3c movi.n a12, 48 +40002274: 0041c2 s8i a12, a1, 0 +40002277: 001046 j 400022bc <_X_ets_unk225c+0x60> +4000227a: 017d mov.n a7, a1 +4000227c: 316f50 srai a6, a5, 31 +4000227f: 086182 s32i a8, a1, 32 +40002282: 20b330 or a11, a3, a3 +40002285: 20a220 or a10, a2, a2 +40002288: 06dd mov.n a13, a6 +4000228a: 05cd mov.n a12, a5 +4000228c: 04bd65 call8 40006e64 <__umoddi3> +4000228f: 03bd mov.n a11, a3 +40002291: 81d8 l32i.n a13, a1, 32 +40002293: 05cd mov.n a12, a5 +40002295: ddaa add.n a13, a13, a10 +40002297: 000dd2 l8ui a13, a13, 0 +4000229a: 02ad mov.n a10, a2 +4000229c: 0047d2 s8i a13, a7, 0 +4000229f: 771b addi.n a7, a7, 1 +400022a1: 06dd mov.n a13, a6 +400022a3: 0498e5 call8 40006c30 <__udivdi3> +400022a6: 0b3d mov.n a3, a11 +400022a8: 0a2d mov.n a2, a10 +400022aa: 0aed mov.n a14, a10 +400022ac: 180c movi.n a8, 1 +400022ae: 0bfd mov.n a15, a11 +400022b0: 93f8b0 movnez a15, a8, a11 +400022b3: 93e8a0 movnez a14, a8, a10 +400022b6: 20eef0 or a14, a14, a15 +400022b9: fc5e56 bnez a14, 40002282 <_X_ets_unk225c+0x26> +400022bc: 441717 beq a7, a1, 40002304 <_X_ets_unk225c+0xa8> +400022bf: c02710 sub a2, a7, a1 +400022c2: 149020 extui a9, a2, 0, 2 +400022c5: 099976 loopnez a9, 400022d2 <_X_ets_unk225c+0x76> +400022c8: 770b addi.n a7, a7, -1 +400022ca: 0007a2 l8ui a10, a7, 0 +400022cd: 0044a2 s8i a10, a4, 0 +400022d0: 441b addi.n a4, a4, 1 +400022d2: 419220 srli a9, a2, 2 +400022d5: 249976 loopnez a9, 400022fd <_X_ets_unk225c+0xa1> +400022d8: 970b addi.n a9, a7, -1 +400022da: fec7b2 addi a11, a7, -2 +400022dd: fdc7c2 addi a12, a7, -3 +400022e0: 000992 l8ui a9, a9, 0 +400022e3: 000bb2 l8ui a11, a11, 0 +400022e6: 000cc2 l8ui a12, a12, 0 +400022e9: fcc772 addi a7, a7, -4 +400022ec: 004492 s8i a9, a4, 0 +400022ef: 0144b2 s8i a11, a4, 1 +400022f2: 0244c2 s8i a12, a4, 2 +400022f5: 000792 l8ui a9, a7, 0 +400022f8: 034492 s8i a9, a4, 3 +400022fb: 444b addi.n a4, a4, 4 +400022fd: 0b0c movi.n a11, 0 +400022ff: 0044b2 s8i a11, a4, 0 +40002302: f01d retw.n +40002304: 020c movi.n a2, 0 +40002306: 0c0c movi.n a12, 0 +40002308: 0044c2 s8i a12, a4, 0 +4000230b: f01d retw.n +4000230d: 000000 ill +40002310: ffc8a0 excw +40002313: 3f .byte 0x3f +40002314: cca8 l32i.n a10, a12, 48 +40002316: ff .byte 0xff +40002317: 3f .byte 0x3f +40002318: cc9c beqz.n a12, 40002338 <_X_ets_unk225c+0xdc> +4000231a: ff .byte 0xff +4000231b: 3f .byte 0x3f +4000231c: ffccd0 excw +4000231f: 3f .byte 0x3f +40002320: ccbc beqz.n a12, 40002360 <_X_ets_unk225c+0x104> +40002322: ff .byte 0xff +40002323: 3f .byte 0x3f +40002324: 00e136 entry a1, 112 +40002327: 106162 s32i a6, a1, 64 +4000232a: 04dd mov.n a13, a4 +4000232c: 05cd mov.n a12, a5 +4000232e: 000352 l8ui a5, a3, 0 +40002331: 431b addi.n a4, a3, 1 +40002333: 42b516 beqz a5, 40002762 <_X_ets_unk225c+0x506> +40002336: 030c movi.n a3, 0 +40002338: a1d9 s32i.n a13, a1, 40 +4000233a: 91c9 s32i.n a12, a1, 36 +4000233c: 080c movi.n a8, 0 +4000233e: 060c movi.n a6, 0 +40002340: 090c movi.n a9, 0 +40002342: 116192 s32i a9, a1, 68 +40002345: d169 s32i.n a6, a1, 52 +40002347: e189 s32i.n a8, a1, 56 +40002349: 016d mov.n a6, a1 +4000234b: 582c movi.n a8, 37 +4000234d: 1b1587 beq a5, a8, 4000236c <_X_ets_unk225c+0x110> +40002350: 05ad mov.n a10, a5 +40002352: 0002e0 callx8 a2 +40002355: 1121a2 l32i a10, a1, 68 +40002358: aa1b addi.n a10, a10, 1 +4000235a: 1161a2 s32i a10, a1, 68 +4000235d: 000452 l8ui a5, a4, 0 +40002360: 01c442 addi a4, a4, 1 +40002363: 19e516 beqz a5, 40002505 <_X_ets_unk225c+0x2a9> +40002366: 25a0b2 movi a11, 37 +40002369: e395b7 bne a5, a11, 40002350 <_X_ets_unk225c+0xf4> +4000236c: 000452 l8ui a5, a4, 0 +4000236f: 441b addi.n a4, a4, 1 +40002371: d3c5c2 addi a12, a5, -45 +40002374: 192c16 beqz a12, 4000250a <_X_ets_unk225c+0x2ae> +40002377: d0c5d2 addi a13, a5, -48 +4000237a: 0e0c movi.n a14, 0 +4000237c: f1e9 s32i.n a14, a1, 60 +4000237e: 197d16 beqz a13, 40002519 <_X_ets_unk225c+0x2bd> +40002381: 0f0c movi.n a15, 0 +40002383: c1f9 s32i.n a15, a1, 48 +40002385: f82c movi.n a8, 47 +40002387: 27b857 bgeu a8, a5, 400023b2 <_X_ets_unk225c+0x156> +4000238a: 993c movi.n a9, 57 +4000238c: 223957 bltu a9, a5, 400023b2 <_X_ets_unk225c+0x156> +4000238f: 070c movi.n a7, 0 +40002391: 178076 loop a0, 400023ac <_X_ets_unk225c+0x150> +40002394: fc2c movi.n a12, 47 +40002396: a07770 addx4 a7, a7, a7 +40002399: 907750 addx2 a7, a7, a5 +4000239c: d0c772 addi a7, a7, -48 +4000239f: 000452 l8ui a5, a4, 0 +400023a2: 441b addi.n a4, a4, 1 +400023a4: 0cbc57 bgeu a12, a5, 400023b4 <_X_ets_unk225c+0x158> +400023a7: 983c movi.n a8, 57 +400023a9: 073857 bltu a8, a5, 400023b4 <_X_ets_unk225c+0x158> +400023ac: fff846 j 40002391 <_X_ets_unk225c+0x135> +400023af: 000000 ill +400023b2: 070c movi.n a7, 0 +400023b4: d2c592 addi a9, a5, -46 +400023b7: 301916 beqz a9, 400026bc <_X_ets_unk225c+0x460> +400023ba: 94c5a2 addi a10, a5, -108 +400023bd: 21ca16 beqz a10, 400025dd <_X_ets_unk225c+0x381> +400023c0: 4b4c movi.n a11, 68 +400023c2: 4615b7 beq a5, a11, 4000240c <_X_ets_unk225c+0x1b0> +400023c5: 5c5c movi.n a12, 85 +400023c7: 4115c7 beq a5, a12, 4000240c <_X_ets_unk225c+0x1b0> +400023ca: 8d5c movi.n a13, 88 +400023cc: 3c15d7 beq a5, a13, 4000240c <_X_ets_unk225c+0x1b0> +400023cf: 64a0e2 movi a14, 100 +400023d2: 3615e7 beq a5, a14, 4000240c <_X_ets_unk225c+0x1b0> +400023d5: 0e0c movi.n a14, 0 +400023d7: 0c0c movi.n a12, 0 +400023d9: 90c5f2 addi a15, a5, -112 +400023dc: 30cf16 beqz a15, 400026ec <_X_ets_unk225c+0x490> +400023df: 75a082 movi a8, 117 +400023e2: 051587 beq a5, a8, 400023eb <_X_ets_unk225c+0x18f> +400023e5: 78a092 movi a9, 120 +400023e8: 649597 bne a5, a9, 40002450 <_X_ets_unk225c+0x1f4> +400023eb: 31ac16 beqz a12, 40002709 <_X_ets_unk225c+0x4ad> +400023ee: 1021c2 l32i a12, a1, 64 +400023f1: 8b7c movi.n a11, -8 +400023f3: ac7b addi.n a10, a12, 7 +400023f5: 10aab0 and a10, a10, a11 +400023f8: 8b1c movi.n a11, 24 +400023fa: ca8b addi.n a12, a10, 8 +400023fc: 1061c2 s32i a12, a1, 64 +400023ff: 02abc7 bge a11, a12, 40002405 <_X_ets_unk225c+0x1a9> +40002402: 006106 j 4000258a <_X_ets_unk225c+0x32e> +40002405: 9198 l32i.n a9, a1, 36 +40002407: 0062c6 j 40002596 <_X_ets_unk225c+0x33a> +4000240a: d20000 quos a0, a0, a0 +4000240d: 1c1021 l32r a2, 3ffc9450 <_start-0x36bb0> +40002410: ad8c beqz.n a13, 4000241e <_X_ets_unk225c+0x1c2> +40002412: 4b0d excw +40002414: d2dd excw +40002416: d71061 l32r a6, 3fff8058 <_start-0x7fa8> +40002419: 042c movi.n a4, 32 +4000241b: 9198 l32i.n a9, a1, 36 +4000241d: 0002c6 j 4000242c <_X_ets_unk225c+0x1d0> +40002420: 8e1c movi.n a14, 24 +40002422: 042ea7 blt a14, a10, 4000242a <_X_ets_unk225c+0x1ce> +40002425: 4f2c movi.n a15, 36 +40002427: 1061f2 s32i a15, a1, 64 +4000242a: a198 l32i.n a9, a1, 40 +4000242c: 102182 l32i a8, a1, 64 +4000242f: 889a add.n a8, a8, a9 +40002431: ffd882 addmi a8, a8, 0xffffff00 +40002434: 3f2882 l32i a8, a8, 252 +40002437: 0d6182 s32i a8, a1, 52 +4000243a: 318f80 srai a8, a8, 31 +4000243d: 0e6182 s32i a8, a1, 56 +40002440: 9cc592 addi a9, a5, -100 +40002443: 167916 beqz a9, 400025ae <_X_ets_unk225c+0x352> +40002446: bcc5a2 addi a10, a5, -68 +40002449: 161a16 beqz a10, 400025ae <_X_ets_unk225c+0x352> +4000244c: 0b0c movi.n a11, 0 +4000244e: e1b9 s32i.n a11, a1, 56 +40002450: 0f0c movi.n a15, 0 +40002452: 81f9 s32i.n a15, a1, 32 +40002454: 485c movi.n a8, 84 +40002456: dbc5c2 addi a12, a5, -37 +40002459: 0bbc87 bgeu a12, a8, 40002468 <_X_ets_unk225c+0x20c> +4000245c: ffad91 l32r a9, 40002310 <_X_ets_unk225c+0xb4> +4000245f: a09c90 addx4 a9, a12, a9 +40002462: 0998 l32i.n a9, a9, 0 +40002464: 0009a0 jx a9 +40002467: 5a2c00 depbits a0, a12, 5, 3 +4000246a: 0002e0 callx8 a2 +4000246d: 05ad mov.n a10, a5 +4000246f: 0002e0 callx8 a2 +40002472: 1121a2 l32i a10, a1, 68 +40002475: aa2b addi.n a10, a10, 2 +40002477: 1161a2 s32i a10, a1, 68 +4000247a: b139 s32i.n a3, a1, 44 +4000247c: c1b8 l32i.n a11, a1, 48 +4000247e: 81c8 l32i.n a12, a1, 32 +40002480: c07730 sub a7, a7, a3 +40002483: ffc7e2 addi a14, a7, -1 +40002486: 937ec0 movnez a7, a14, a12 +40002489: 17cb16 beqz a11, 40002609 <_X_ets_unk225c+0x3ad> +4000248c: 00ec16 beqz a12, 4000249e <_X_ets_unk225c+0x242> +4000248f: 0cad mov.n a10, a12 +40002491: 0002e0 callx8 a2 +40002494: 1121f2 l32i a15, a1, 68 +40002497: 0c0c movi.n a12, 0 +40002499: ff1b addi.n a15, a15, 1 +4000249b: 1161f2 s32i a15, a1, 68 +4000249e: 053c movi.n a5, 48 +400024a0: f188 l32i.n a8, a1, 60 +400024a2: 0861c2 s32i a12, a1, 32 +400024a5: 167816 beqz a8, 40002610 <_X_ets_unk225c+0x3b4> +400024a8: 81c8 l32i.n a12, a1, 32 +400024aa: bc8c beqz.n a12, 400024b9 <_X_ets_unk225c+0x25d> +400024ac: 0cad mov.n a10, a12 +400024ae: 0002e0 callx8 a2 +400024b1: 112192 l32i a9, a1, 68 +400024b4: 991b addi.n a9, a9, 1 +400024b6: 116192 s32i a9, a1, 68 +400024b9: b1a8 l32i.n a10, a1, 44 +400024bb: ffca52 addi a5, a10, -1 +400024be: 203550 or a3, a5, a5 +400024c1: 181aa6 blti a10, 1, 400024dd <_X_ets_unk225c+0x281> +400024c4: 0006a2 l8ui a10, a6, 0 +400024c7: 661b addi.n a6, a6, 1 +400024c9: 0002e0 callx8 a2 +400024cc: 330b addi.n a3, a3, -1 +400024ce: f20366 bnei a3, -1, 400024c4 <_X_ets_unk225c+0x268> +400024d1: f37c movi.n a3, -1 +400024d3: 1121b2 l32i a11, a1, 68 +400024d6: c51b addi.n a12, a5, 1 +400024d8: bbca add.n a11, a11, a12 +400024da: 1161b2 s32i a11, a1, 68 +400024dd: f1d8 l32i.n a13, a1, 60 +400024df: e7ad16 beqz a13, 4000235d <_X_ets_unk225c+0x101> +400024e2: 07ed mov.n a14, a7 +400024e4: 570b addi.n a5, a7, -1 +400024e6: 057d mov.n a7, a5 +400024e8: 021ee6 bgei a14, 1, 400024ee <_X_ets_unk225c+0x292> +400024eb: ff9b86 j 4000235d <_X_ets_unk225c+0x101> +400024ee: 0a2c movi.n a10, 32 +400024f0: 0002e0 callx8 a2 +400024f3: 770b addi.n a7, a7, -1 +400024f5: f50766 bnei a7, -1, 400024ee <_X_ets_unk225c+0x292> +400024f8: 1121f2 l32i a15, a1, 68 +400024fb: 851b addi.n a8, a5, 1 +400024fd: ff8a add.n a15, a15, a8 +400024ff: 1161f2 s32i a15, a1, 68 +40002502: ff95c6 j 4000235d <_X_ets_unk225c+0x101> +40002505: 112122 l32i a2, a1, 68 +40002508: f01d retw.n +4000250a: 1a0c movi.n a10, 1 +4000250c: 000452 l8ui a5, a4, 0 +4000250f: 441b addi.n a4, a4, 1 +40002511: f1a9 s32i.n a10, a1, 60 +40002513: d0c592 addi a9, a5, -48 +40002516: e67956 bnez a9, 40002381 <_X_ets_unk225c+0x125> +40002519: 000452 l8ui a5, a4, 0 +4000251c: 1b0c movi.n a11, 1 +4000251e: 441b addi.n a4, a4, 1 +40002520: c1b9 s32i.n a11, a1, 48 +40002522: ff97c6 j 40002385 <_X_ets_unk225c+0x129> +40002525: 3c0000 excw +40002528: e00a add.n a14, a0, a0 +4000252a: a20002 l8ui a0, a0, 162 +4000252d: e078a0 subx4 a7, a8, a10 +40002530: 0c0002 l8ui a0, a0, 12 +40002533: 1c0c87 bnone a12, a8, 40002553 <_X_ets_unk225c+0x2f7> +40002536: c1c9 s32i.n a12, a1, 48 +40002538: 4d4c movi.n a13, 68 +4000253a: 3615d7 beq a5, a13, 40002574 <_X_ets_unk225c+0x318> +4000253d: 5e5c movi.n a14, 85 +4000253f: 3115e7 beq a5, a14, 40002574 <_X_ets_unk225c+0x318> +40002542: a8c5f2 addi a15, a5, -88 +40002545: 201f16 beqz a15, 4000274a <_X_ets_unk225c+0x4ee> +40002548: 64a082 movi a8, 100 +4000254b: 251587 beq a5, a8, 40002574 <_X_ets_unk225c+0x318> +4000254e: 70a092 movi a9, 112 +40002551: 0b1597 beq a5, a9, 40002560 <_X_ets_unk225c+0x304> +40002554: 75a0a2 movi a10, 117 +40002557: 1915a7 beq a5, a10, 40002574 <_X_ets_unk225c+0x318> +4000255a: 78a0b2 movi a11, 120 +4000255d: 2395b7 bne a5, a11, 40002584 <_X_ets_unk225c+0x328> +40002560: e1b8 l32i.n a11, a1, 56 +40002562: d1a8 l32i.n a10, a1, 52 +40002564: 01cd mov.n a12, a1 +40002566: 0d1c movi.n a13, 16 +40002568: ff6be1 l32r a14, 40002314 <_X_ets_unk225c+0xb8> +4000256b: ffcf25 call8 4000225c <_X_ets_unk225c> +4000256e: 0a3d mov.n a3, a10 +40002570: 000406 j 40002584 <_X_ets_unk225c+0x328> +40002573: e1b800 excw +40002576: d1a8 l32i.n a10, a1, 52 +40002578: 01cd mov.n a12, a1 +4000257a: ad0c movi.n a13, 10 +4000257c: ff67e1 l32r a14, 40002318 <_X_ets_unk225c+0xbc> +4000257f: ffcde5 call8 4000225c <_X_ets_unk225c> +40002582: 0a3d mov.n a3, a10 +40002584: 016d mov.n a6, a1 +40002586: ffbc06 j 4000247a <_X_ets_unk225c+0x21e> +40002589: 8c1c00 excw +4000258c: 042ca7 blt a12, a10, 40002594 <_X_ets_unk225c+0x338> +4000258f: 8d2c movi.n a13, 40 +40002591: 1061d2 s32i a13, a1, 64 +40002594: a198 l32i.n a9, a1, 40 +40002596: 1021f2 l32i a15, a1, 64 +40002599: 9cc5e2 addi a14, a5, -100 +4000259c: ff9a add.n a15, a15, a9 +4000259e: ffdff2 addmi a15, a15, 0xffffff00 +400025a1: 3f2f82 l32i a8, a15, 252 +400025a4: e189 s32i.n a8, a1, 56 +400025a6: 3e2ff2 l32i a15, a15, 248 +400025a9: d1f9 s32i.n a15, a1, 52 +400025ab: e97e56 bnez a14, 40002446 <_X_ets_unk225c+0x1ea> +400025ae: d1a8 l32i.n a10, a1, 52 +400025b0: 0d0c movi.n a13, 0 +400025b2: e188 l32i.n a8, a1, 56 +400025b4: 0c0c movi.n a12, 0 +400025b6: 0218e6 bgei a8, 1, 400025bc <_X_ets_unk225c+0x360> +400025b9: 006146 j 40002742 <_X_ets_unk225c+0x4e6> +400025bc: e90c16 beqz a12, 40002450 <_X_ets_unk225c+0x1f4> +400025bf: d1a8 l32i.n a10, a1, 52 +400025c1: fe7c movi.n a14, -1 +400025c3: e1c8 l32i.n a12, a1, 56 +400025c5: d92c movi.n a9, 45 +400025c7: 8199 s32i.n a9, a1, 32 +400025c9: 30cce0 xor a12, a12, a14 +400025cc: 60b0a0 neg a11, a10 +400025cf: d1b9 s32i.n a11, a1, 52 +400025d1: dc1b addi.n a13, a12, 1 +400025d3: 83cda0 moveqz a12, a13, a10 +400025d6: e1c9 s32i.n a12, a1, 56 +400025d8: ff9e06 j 40002454 <_X_ets_unk225c+0x1f8> +400025db: 520000 excw +400025de: 1b0004 mula.dd.hh.lddec m0, a0, m0, m2 +400025e1: c5f244 excw +400025e4: ff1694 excw +400025e7: 0e .byte 0xe +400025e8: 0c0c movi.n a12, 0 +400025ea: bcc582 addi a8, a5, -68 +400025ed: 0f4816 beqz a8, 400026e5 <_X_ets_unk225c+0x489> +400025f0: abc592 addi a9, a5, -85 +400025f3: 0ee916 beqz a9, 400026e5 <_X_ets_unk225c+0x489> +400025f6: a8c5a2 addi a10, a5, -88 +400025f9: 0e8a16 beqz a10, 400026e5 <_X_ets_unk225c+0x489> +400025fc: 9cc5b2 addi a11, a5, -100 +400025ff: 0e2b16 beqz a11, 400026e5 <_X_ets_unk225c+0x489> +40002602: 1e0c movi.n a14, 1 +40002604: ff7446 j 400023d9 <_X_ets_unk225c+0x17d> +40002607: c80000 excw +4000260a: 052cf1 l32r a15, 3ffc3abc <_start-0x3c544> +4000260d: e97c56 bnez a12, 400024a8 <_X_ets_unk225c+0x24c> +40002610: 07dd mov.n a13, a7 +40002612: 370b addi.n a3, a7, -1 +40002614: 037d mov.n a7, a3 +40002616: 021de6 bgei a13, 1, 4000261c <_X_ets_unk225c+0x3c0> +40002619: ffa2c6 j 400024a8 <_X_ets_unk225c+0x24c> +4000261c: 05ad mov.n a10, a5 +4000261e: 0002e0 callx8 a2 +40002621: 770b addi.n a7, a7, -1 +40002623: f50766 bnei a7, -1, 4000261c <_X_ets_unk225c+0x3c0> +40002626: f77c movi.n a7, -1 +40002628: 1121e2 l32i a14, a1, 68 +4000262b: f31b addi.n a15, a3, 1 +4000262d: eefa add.n a14, a14, a15 +4000262f: 1161e2 s32i a14, a1, 68 +40002632: ff9c86 j 400024a8 <_X_ets_unk225c+0x24c> +40002635: 102192 l32i a9, a1, 64 +40002638: 881c movi.n a8, 24 +4000263a: 09cd mov.n a12, a9 +4000263c: 994b addi.n a9, a9, 4 +4000263e: 106192 s32i a9, a1, 64 +40002641: 052897 blt a8, a9, 4000264a <_X_ets_unk225c+0x3ee> +40002644: 9198 l32i.n a9, a1, 36 +40002646: 000306 j 40002656 <_X_ets_unk225c+0x3fa> +40002649: 8a1c00 depbits a0, a12, 8, 2 +4000264c: 042ac7 blt a10, a12, 40002654 <_X_ets_unk225c+0x3f8> +4000264f: 4b2c movi.n a11, 36 +40002651: 1061b2 s32i a11, a1, 64 +40002654: a198 l32i.n a9, a1, 40 +40002656: 102162 l32i a6, a1, 64 +40002659: ff30d1 l32r a13, 4000231c <_X_ets_unk225c+0xc0> +4000265c: 669a add.n a6, a6, a9 +4000265e: ffd662 addmi a6, a6, 0xffffff00 +40002661: 3f2662 l32i a6, a6, 252 +40002664: 836d60 moveqz a6, a13, a6 +40002667: 0006c2 l8ui a12, a6, 0 +4000266a: 0efc16 beqz a12, 4000275d <_X_ets_unk225c+0x501> +4000266d: 069d mov.n a9, a6 +4000266f: 030c movi.n a3, 0 +40002671: 098076 loop a0, 4000267e <_X_ets_unk225c+0x422> +40002674: 331b addi.n a3, a3, 1 +40002676: 010982 l8ui a8, a9, 1 +40002679: 991b addi.n a9, a9, 1 +4000267b: dfb816 beqz a8, 4000247a <_X_ets_unk225c+0x21e> +4000267e: fffbc6 j 40002671 <_X_ets_unk225c+0x415> +40002681: 1021a2 l32i a10, a1, 64 +40002684: 891c movi.n a9, 24 +40002686: 0acd mov.n a12, a10 +40002688: aa4b addi.n a10, a10, 4 +4000268a: 1061a2 s32i a10, a1, 64 +4000268d: 0529a7 blt a9, a10, 40002696 <_X_ets_unk225c+0x43a> +40002690: 91c8 l32i.n a12, a1, 36 +40002692: 000306 j 400026a2 <_X_ets_unk225c+0x446> +40002695: 8b1c00 depbits a0, a12, 24, 2 +40002698: 042bc7 blt a11, a12, 400026a0 <_X_ets_unk225c+0x444> +4000269b: 4c2c movi.n a12, 36 +4000269d: 1061c2 s32i a12, a1, 64 +400026a0: a1c8 l32i.n a12, a1, 40 +400026a2: 1021a2 l32i a10, a1, 64 +400026a5: aaca add.n a10, a10, a12 +400026a7: ffdaa2 addmi a10, a10, 0xffffff00 +400026aa: fc0aa2 l8ui a10, a10, 252 +400026ad: 0002e0 callx8 a2 +400026b0: ff2846 j 40002355 <_X_ets_unk225c+0xf9> +400026b3: 5a2c movi.n a10, 37 +400026b5: 0002e0 callx8 a2 +400026b8: ff6f86 j 4000247a <_X_ets_unk225c+0x21e> +400026bb: fb2c00 depbits a0, a12, 31, 3 +400026be: 000452 l8ui a5, a4, 0 +400026c1: c1c8 l32i.n a12, a1, 48 +400026c3: 441b addi.n a4, a4, 1 +400026c5: cc1b addi.n a12, a12, 1 +400026c7: c1c9 s32i.n a12, a1, 48 +400026c9: 023b57 bltu a11, a5, 400026cf <_X_ets_unk225c+0x473> +400026cc: ff3a86 j 400023ba <_X_ets_unk225c+0x15e> +400026cf: 9d3c movi.n a13, 57 +400026d1: 25bd57 bgeu a13, a5, 400026fa <_X_ets_unk225c+0x49e> +400026d4: ff3886 j 400023ba <_X_ets_unk225c+0x15e> +400026d7: 1c0c00 excw +400026da: 000452 l8ui a5, a4, 0 +400026dd: 441b addi.n a4, a4, 1 +400026df: bcc5e2 addi a14, a5, -68 +400026e2: f0ae56 bnez a14, 400025f0 <_X_ets_unk225c+0x394> +400026e5: d05c56 bnez a12, 400023ee <_X_ets_unk225c+0x192> +400026e8: 000806 j 4000270c <_X_ets_unk225c+0x4b0> +400026eb: ec5600 excw +400026ee: cf .byte 0xcf +400026ef: 000646 j 4000270c <_X_ets_unk225c+0x4b0> +400026f2: 9f3c movi.n a15, 57 +400026f4: 02bf57 bgeu a15, a5, 400026fa <_X_ets_unk225c+0x49e> +400026f7: ff2fc6 j 400023ba <_X_ets_unk225c+0x15e> +400026fa: f82c movi.n a8, 47 +400026fc: 000452 l8ui a5, a4, 0 +400026ff: 441b addi.n a4, a4, 1 +40002701: ed3857 bltu a8, a5, 400026f2 <_X_ets_unk225c+0x496> +40002704: ff2c86 j 400023ba <_X_ets_unk225c+0x15e> +40002707: 160000 excw +4000270a: fe .byte 0xfe +4000270b: cf .byte 0xcf +4000270c: 1021b2 l32i a11, a1, 64 +4000270f: 891c movi.n a9, 24 +40002711: 0bad mov.n a10, a11 +40002713: bb4b addi.n a11, a11, 4 +40002715: 1061b2 s32i a11, a1, 64 +40002718: 0429b7 blt a9, a11, 40002720 <_X_ets_unk225c+0x4c4> +4000271b: 9198 l32i.n a9, a1, 36 +4000271d: 0002c6 j 4000272c <_X_ets_unk225c+0x4d0> +40002720: 8c1c movi.n a12, 24 +40002722: 042ca7 blt a12, a10, 4000272a <_X_ets_unk225c+0x4ce> +40002725: 4d2c movi.n a13, 36 +40002727: 1061d2 s32i a13, a1, 64 +4000272a: a198 l32i.n a9, a1, 40 +4000272c: 1021e2 l32i a14, a1, 64 +4000272f: ee9a add.n a14, a14, a9 +40002731: ffdee2 addmi a14, a14, 0xffffff00 +40002734: 3f2ee2 l32i a14, a14, 252 +40002737: d1e9 s32i.n a14, a1, 52 +40002739: 31efe0 srai a14, a14, 31 +4000273c: e1e9 s32i.n a14, a1, 56 +4000273e: ff3f86 j 40002440 <_X_ets_unk225c+0x1e4> +40002741: 28ac00 excw +40002744: 1c0c movi.n a12, 1 +40002746: ff9c86 j 400025bc <_X_ets_unk225c+0x360> +40002749: e1b800 excw +4000274c: d1a8 l32i.n a10, a1, 52 +4000274e: 01cd mov.n a12, a1 +40002750: 0d1c movi.n a13, 16 +40002752: fef3e1 l32r a14, 40002320 <_X_ets_unk225c+0xc4> +40002755: ffb065 call8 4000225c <_X_ets_unk225c> +40002758: 0a3d mov.n a3, a10 +4000275a: ff8986 j 40002584 <_X_ets_unk225c+0x328> +4000275d: 030c movi.n a3, 0 +4000275f: ff45c6 j 4000247a <_X_ets_unk225c+0x21e> +40002762: 020c movi.n a2, 0 +40002764: f01d retw.n +40002766: d70000 excw +40002769: 023a add.n a0, a2, a3 +4000276b: ff9346 j 400025bc <_X_ets_unk225c+0x360> +4000276e: fff486 j 40002744 <_X_ets_unk225c+0x4e8> +40002771: 000000 ill + +40002774 : +40002774: 004136 entry a1, 32 +40002777: fe7631 l32r a3, 40002150 <_st_0x3fffda9c> +4000277a: 3329 s32i.n a2, a3, 12 +4000277c: f01d retw.n + ... + +40002780 <_c_0x4000223c_ets_uart_putc>: +40002780: 223c movi.n a2, 50 +40002782: 364000 excw + +40002784 <_X_ets_install_uart_printf>: +40002784: 004136 entry a1, 32 +40002787: fffea1 l32r a10, 40002780 <_c_0x4000223c_ets_uart_putc> +4000278a: fffea5 call8 40002774 +4000278d: f01d retw.n + ... + +40002790 <_c_0x400027dc>: +40002790: 27dc bnez.n a7, 400027a6 <_X_ets_install_external_printf+0x12> +40002792: 364000 excw + +40002794 <_X_ets_install_external_printf>: +40002794: 004136 entry a1, 32 +40002797: fe6e51 l32r a5, 40002150 <_st_0x3fffda9c> +4000279a: 5529 s32i.n a2, a5, 20 +4000279c: 838c beqz.n a3, 400027a8 <_X_ets_install_external_printf+0x14> +4000279e: 20a330 or a10, a3, a3 +400027a1: 000125 call8 400027b4 <_X_ets_install_putc2> +400027a4: 6549 s32i.n a4, a5, 24 +400027a6: f01d retw.n +400027a8: fffaa1 l32r a10, 40002790 <_c_0x400027dc> +400027ab: 0000a5 call8 400027b4 <_X_ets_install_putc2> +400027ae: 6549 s32i.n a4, a5, 24 +400027b0: f01d retw.n + ... + +400027b4 <_X_ets_install_putc2>: +400027b4: 004136 entry a1, 32 +400027b7: fe6631 l32r a3, 40002150 <_st_0x3fffda9c> +400027ba: 4329 s32i.n a2, a3, 16 +400027bc: f01d retw.n + ... + +400027c0 <_X_ets_get_printf_buf_remain_len>: +400027c0: 004136 entry a1, 32 +400027c3: fe6321 l32r a2, 40002150 <_st_0x3fffda9c> +400027c6: 0e1222 l16ui a2, a2, 28 +400027c9: f01d retw.n + ... + +400027cc <_X_ets_reset_printf_buf_len>: +400027cc: 004136 entry a1, 32 +400027cf: fe6031 l32r a3, 40002150 <_st_0x3fffda9c> +400027d2: 020c movi.n a2, 0 +400027d4: 0e5322 s16i a2, a3, 28 +400027d7: f01d retw.n +400027d9: 000000 ill + +400027dc <_X_ets_putc>: +400027dc: 004136 entry a1, 32 +400027df: fe5c61 l32r a6, 40002150 <_st_0x3fffda9c> +400027e2: 0e1642 l16ui a4, a6, 28 +400027e5: 8638 l32i.n a3, a6, 32 +400027e7: d48c beqz.n a4, 400027f8 <_X_ets_putc+0x1c> +400027e9: 004322 s8i a2, a3, 0 +400027ec: 540b addi.n a5, a4, -1 +400027ee: 331b addi.n a3, a3, 1 +400027f0: 0e5652 s16i a5, a6, 28 +400027f3: 8639 s32i.n a3, a6, 32 +400027f5: f01d retw.n +400027f7: f01d00 subx8 a1, a13, a0 +400027fa: bc0000 excw +400027fd: ffda add.n a15, a15, a13 +400027ff: 3f .byte 0x3f +40002800: 002220 excw +40002803: a13640 excw + +40002804 : +40002804: 00a136 entry a1, 80 +40002807: 4129 s32i.n a2, a1, 16 +40002809: 5139 s32i.n a3, a1, 20 +4000280b: 6149 s32i.n a4, a1, 24 +4000280d: 7159 s32i.n a5, a1, 28 +4000280f: 079d mov.n a9, a7 +40002811: fe4f71 l32r a7, 40002150 <_st_0x3fffda9c> +40002814: 8169 s32i.n a6, a1, 32 +40002816: 3788 l32i.n a8, a7, 12 +40002818: 9199 s32i.n a9, a1, 36 +4000281a: 38cc bnez.n a8, 40002821 +4000281c: 0427b2 l32i a11, a7, 16 +4000281f: 5bbc beqz.n a11, 40002858 +40002821: 57d8 l32i.n a13, a7, 20 +40002823: 00dd16 beqz a13, 40002834 +40002826: fff5a1 l32r a10, 400027fc <_X_ets_putc+0x20> +40002829: fccab2 addi a11, a10, -4 +4000282c: 04cac2 addi a12, a10, 4 +4000282f: 000de0 callx8 a13 +40002832: 4128 l32i.n a2, a1, 16 +40002834: fff3a1 l32r a10, 40002800 <_X_ets_putc+0x24> +40002837: 10c1d2 addi a13, a1, 16 +4000283a: 30c1c2 addi a12, a1, 48 +4000283d: 4e0c movi.n a14, 4 +4000283f: 02bd mov.n a11, a2 +40002841: 21e9 s32i.n a14, a1, 8 +40002843: 01c9 s32i.n a12, a1, 0 +40002845: 11d9 s32i.n a13, a1, 4 +40002847: ffade5 call8 40002324 <_X_ets_unk225c+0xc8> +4000284a: 67b8 l32i.n a11, a7, 24 +4000284c: 0a2d mov.n a2, a10 +4000284e: 3b8c beqz.n a11, 40002855 +40002850: 97a8 l32i.n a10, a7, 36 +40002852: 000be0 callx8 a11 +40002855: f01d retw.n +40002857: 020c00 andb b0, b12, b0 +4000285a: f01d retw.n +4000285c: 00a136 entry a1, 80 +4000285f: 02bd mov.n a11, a2 +40002861: 4129 s32i.n a2, a1, 16 +40002863: 5139 s32i.n a3, a1, 20 +40002865: 6149 s32i.n a4, a1, 24 +40002867: 7159 s32i.n a5, a1, 28 +40002869: 8169 s32i.n a6, a1, 32 +4000286b: 9179 s32i.n a7, a1, 36 +4000286d: ffc4a1 l32r a10, 40002780 <_c_0x4000223c_ets_uart_putc> +40002870: 30c1c2 addi a12, a1, 48 +40002873: 4e0c movi.n a14, 4 +40002875: 10c1d2 addi a13, a1, 16 +40002878: 11d9 s32i.n a13, a1, 4 +4000287a: 21e9 s32i.n a14, a1, 8 +4000287c: 01c9 s32i.n a12, a1, 0 +4000287e: ffaa65 call8 40002324 <_X_ets_unk225c+0xc8> +40002881: 0a2d mov.n a2, a10 +40002883: f01d retw.n +40002885: 000000 ill +40002888: 00a136 entry a1, 80 +4000288b: 5139 s32i.n a3, a1, 20 +4000288d: 6149 s32i.n a4, a1, 24 +4000288f: 7159 s32i.n a5, a1, 28 +40002891: 8169 s32i.n a6, a1, 32 +40002893: 02bd mov.n a11, a2 +40002895: fe2e21 l32r a2, 40002150 <_st_0x3fffda9c> +40002898: 9179 s32i.n a7, a1, 36 +4000289a: 52d8 l32i.n a13, a2, 20 +4000289c: 0461b2 s32i a11, a1, 16 +4000289f: 00ed16 beqz a13, 400028b1 +400028a2: 20c2a2 addi a10, a2, 32 +400028a5: 1cc2b2 addi a11, a2, 28 +400028a8: 24c2c2 addi a12, a2, 36 +400028ab: 000de0 callx8 a13 +400028ae: 0421b2 l32i a11, a1, 16 +400028b1: ffb7a1 l32r a10, 40002790 <_c_0x400027dc> +400028b4: 10c1d2 addi a13, a1, 16 +400028b7: 30c1c2 addi a12, a1, 48 +400028ba: 4e0c movi.n a14, 4 +400028bc: 21e9 s32i.n a14, a1, 8 +400028be: 01c9 s32i.n a12, a1, 0 +400028c0: 11d9 s32i.n a13, a1, 4 +400028c2: ffa625 call8 40002324 <_X_ets_unk225c+0xc8> +400028c5: 62b8 l32i.n a11, a2, 24 +400028c7: 0a3d mov.n a3, a10 +400028c9: 3b8c beqz.n a11, 400028d0 +400028cb: 92a8 l32i.n a10, a2, 36 +400028cd: 000be0 callx8 a11 +400028d0: 032d mov.n a2, a3 +400028d2: f01d retw.n + +400028d4 <_c_0xdfffffff>: +400028d4: ff .byte 0xff +400028d5: ff .byte 0xff +400028d6: ff .byte 0xff +400028d7: df .byte 0xdf + +400028d8 <_X_get_rst_cause>: +400028d8: 004136 entry a1, 32 +400028db: fa1741 l32r a4, 40001138 <_c_0x60007e00> +400028de: 0020c0 memw +400028e1: 8d2422 l32i a2, a4, 0x234 +400028e4: fffcc1 l32r a12, 400028d4 <_c_0xdfffffff> +400028e7: 542020 extui a2, a2, 0, 6 +400028ea: 325266 bnei a2, 5, 40002920 <_X_get_rst_cause+0x48> +400028ed: 0020c0 memw +400028f0: 8d2432 l32i a3, a4, 0x234 +400028f3: 943c30 extui a3, a3, 12, 10 +400028f6: 371326 beqi a3, 1, 40002931 <_X_get_rst_cause+0x59> +400028f9: 0020c0 memw +400028fc: fff681 l32r a8, 400028d4 <_c_0xdfffffff> +400028ff: 8d24a2 l32i a10, a4, 0x234 +40002902: 090c movi.n a9, 0 +40002904: 94aca0 extui a10, a10, 12, 10 +40002907: f8caa2 addi a10, a10, -8 +4000290a: 9329a0 movnez a2, a9, a10 +4000290d: 0020c0 memw +40002910: 862452 l32i a5, a4, 0x218 +40002913: 105580 and a5, a5, a8 +40002916: 0020c0 memw +40002919: 866452 s32i a5, a4, 0x218 +4000291c: f01d retw.n +4000291e: c00000 sub a0, a0, a0 +40002921: b20020 mulsh a0, a0, a2 +40002924: c08624 excw +40002927: 10bb addi.n a1, a0, 11 +40002929: 0020c0 memw +4000292c: 8664b2 s32i a11, a4, 0x218 +4000292f: f01d retw.n +40002931: ffe8e1 l32r a14, 400028d4 <_c_0xdfffffff> +40002934: 421c movi.n a2, 20 +40002936: 0020c0 memw +40002939: 8624d2 l32i a13, a4, 0x218 +4000293c: 10dde0 and a13, a13, a14 +4000293f: 0020c0 memw +40002942: 8664d2 s32i a13, a4, 0x218 +40002945: f01d retw.n + ... + +40002948 <_XX_unk2948>: +40002948: 004136 entry a1, 32 +4000294b: 070c movi.n a7, 0 +4000294d: f9fa51 l32r a5, 40001138 <_c_0x60007e00> +40002950: 0020c0 memw +40002953: a82562 l32i a6, a5, 0x2a0 +40002956: 0269 s32i.n a6, a2, 0 +40002958: 0020c0 memw +4000295b: a92542 l32i a4, a5, 0x2a4 +4000295e: 400300 ssr a3 +40002961: 914040 srl a4, a4 +40002964: 086407 bbci a4, 0, 40002970 <_l_2970> +40002967: 0020c0 memw +4000296a: a86572 s32i a7, a5, 0x2a0 +4000296d: f01d retw.n + ... + +40002970 <_l_2970>: +40002970: 0279 s32i.n a7, a2, 0 +40002972: 0020c0 memw +40002975: a86572 s32i a7, a5, 0x2a0 +40002978: f01d retw.n + ... + +4000297c <_X_sw_sys_rst>: +4000297c: 004136 entry a1, 32 +4000297f: f76e41 l32r a4, 40000738 <_c_0x80000000> +40002982: f9ed31 l32r a3, 40001138 <_c_0x60007e00> +40002985: 0020c0 memw +40002988: 802322 l32i a2, a3, 0x200 +4000298b: 202240 or a2, a2, a4 +4000298e: 0020c0 memw +40002991: 806322 s32i a2, a3, 0x200 +40002994: f01d retw.n + ... + +40002998 : +40002998: 004136 entry a1, 32 +4000299b: 80a042 movi a4, 128 +4000299e: f9e631 l32r a3, 40001138 <_c_0x60007e00> +400029a1: 0020c0 memw +400029a4: 802322 l32i a2, a3, 0x200 +400029a7: 202240 or a2, a2, a4 +400029aa: 0020c0 memw +400029ad: 806322 s32i a2, a3, 0x200 +400029b0: f01d retw.n + ... + +400029b4 <_c_0x00400000>: +400029b4: 400000 ssr a0 + ... + +400029b8 <_c_0xffbfffff>: +400029b8: ff .byte 0xff +400029b9: ff .byte 0xff +400029ba: bf .byte 0xbf +400029bb: ff .byte 0xff + +400029bc <_XX_apb_bridge_toggle>: +400029bc: 004136 entry a1, 32 +400029bf: f9de41 l32r a4, 40001138 <_c_0x60007e00> +400029c2: 229c beqz.n a2, 400029d8 <_XX_apb_bridge_toggle+0x1c> +400029c4: fffc31 l32r a3, 400029b4 <_c_0x00400000> +400029c7: 0020c0 memw +400029ca: 862422 l32i a2, a4, 0x218 +400029cd: 202230 or a2, a2, a3 +400029d0: 0020c0 memw +400029d3: 866422 s32i a2, a4, 0x218 +400029d6: f01d retw.n +400029d8: fff891 l32r a9, 400029b8 <_c_0xffbfffff> +400029db: 0020c0 memw +400029de: 862482 l32i a8, a4, 0x218 +400029e1: 108890 and a8, a8, a9 +400029e4: 0020c0 memw +400029e7: 866482 s32i a8, a4, 0x218 +400029ea: f01d retw.n + +400029ec <_X_ets_strcpy>: +400029ec: 004136 entry a1, 32 +400029ef: 20b330 or a11, a3, a3 +400029f2: 20a220 or a10, a2, a2 +400029f5: 032e65 call8 40005cdc +400029f8: 202aa0 or a2, a10, a10 +400029fb: f01d retw.n +400029fd: 000000 ill + +40002a00 <_X_ets_strncpy>: +40002a00: 004136 entry a1, 32 +40002a03: 04cd mov.n a12, a4 +40002a05: 03bd mov.n a11, a3 +40002a07: 02ad mov.n a10, a2 +40002a09: 034865 call8 40005e90 +40002a0c: 0a2d mov.n a2, a10 +40002a0e: f01d retw.n + +40002a10 <_X_ets_strcmp>: +40002a10: 004136 entry a1, 32 +40002a13: 20b330 or a11, a3, a3 +40002a16: 20a220 or a10, a2, a2 +40002a19: 0319e5 call8 40005bb8 +40002a1c: 202aa0 or a2, a10, a10 +40002a1f: f01d retw.n +40002a21: 000000 ill + +40002a24 <_X_ets_strncmp>: +40002a24: 004136 entry a1, 32 +40002a27: 04cd mov.n a12, a4 +40002a29: 03bd mov.n a11, a3 +40002a2b: 02ad mov.n a10, a2 +40002a2d: 033a25 call8 40005dd0 +40002a30: 0a2d mov.n a2, a10 +40002a32: f01d retw.n + +40002a34 <_X_ets_strlen>: +40002a34: 004136 entry a1, 32 +40002a37: 02ad mov.n a10, a2 +40002a39: 033325 call8 40005d6c +40002a3c: 0a2d mov.n a2, a10 +40002a3e: f01d retw.n + +40002a40 <_X_ets_strstr>: +40002a40: 004136 entry a1, 32 +40002a43: 20b330 or a11, a3, a3 +40002a46: 20a220 or a10, a2, a2 +40002a49: 035225 call8 40005f6c +40002a4c: 202aa0 or a2, a10, a10 +40002a4f: f01d retw.n +40002a51: 000000 ill + +40002a54 : +40002a54: 004136 entry a1, 32 +40002a57: 20b330 or a11, a3, a3 +40002a5a: 20a220 or a10, a2, a2 +40002a5d: 03e4e5 call8 400068ac +40002a60: 000090 retw + ... + +40002a64 : +40002a64: 004136 entry a1, 32 +40002a67: 0003a2 l8ui a10, a3, 0 +40002a6a: 9b0c movi.n a11, 9 +40002a6c: 02ca26 beqi a10, 32, 40002a72 +40002a6f: 139ab7 bne a10, a11, 40002a86 +40002a72: 0a8076 loop a0, 40002a80 +40002a75: 0103a2 l8ui a10, a3, 1 +40002a78: 331b addi.n a3, a3, 1 +40002a7a: f7ca26 beqi a10, 32, 40002a75 +40002a7d: 059ab7 bne a10, a11, 40002a86 +40002a80: fffb86 j 40002a72 +40002a83: 000000 ill +40002a86: 040c movi.n a4, 0 +40002a88: 0003a2 l8ui a10, a3, 0 +40002a8b: 0002e5 call8 40002ab8 +40002a8e: 0a5d mov.n a5, a10 +40002a90: 01ea96 bltz a10, 40002ab2 +40002a93: 0103a2 l8ui a10, a3, 1 +40002a96: 000225 call8 40002ab8 +40002a99: 015a96 bltz a10, 40002ab2 +40002a9c: 333b addi.n a3, a3, 3 +40002a9e: 924a add.n a9, a2, a4 +40002aa0: 1185c0 slli a8, a5, 4 +40002aa3: 441b addi.n a4, a4, 1 +40002aa5: 8a8a add.n a8, a10, a8 +40002aa7: 004982 s8i a8, a9, 0 +40002aaa: 744040 extui a4, a4, 0, 8 +40002aad: d76466 bnei a4, 6, 40002a88 +40002ab0: f01d retw.n +40002ab2: 020c movi.n a2, 0 +40002ab4: f01d retw.n +40002ab6: 360000 excw +40002ab9: 3c0041 l32r a4, 3ffd1abc <_start-0x2e544> +40002abc: f62c95 call4 3fff8d88 <_start-0x7278> +40002abf: 02b627 bgeu a6, a2, 40002ac5 +40002ac2: 3ab527 bgeu a5, a2, 40002b00 +40002ac5: 60a032 movi a3, 96 +40002ac8: 05b327 bgeu a3, a2, 40002ad1 +40002acb: 66a042 movi a4, 102 +40002ace: 0ab427 bgeu a4, a2, 40002adc +40002ad1: 084c movi.n a8, 64 +40002ad3: 1db827 bgeu a8, a2, 40002af4 +40002ad6: 46a092 movi a9, 70 +40002ad9: 173927 bltu a9, a2, 40002af4 +40002adc: 02b627 bgeu a6, a2, 40002ae2 +40002adf: 1db527 bgeu a5, a2, 40002b00 +40002ae2: 0a4c movi.n a10, 64 +40002ae4: 10ba27 bgeu a10, a2, 40002af8 +40002ae7: ab5c movi.n a11, 90 +40002ae9: 0b3b27 bltu a11, a2, 40002af8 +40002aec: c9c222 addi a2, a2, -55 +40002aef: 232200 sext a2, a2, 7 +40002af2: f01d retw.n +40002af4: f27c movi.n a2, -1 +40002af6: f01d retw.n +40002af8: a9c222 addi a2, a2, -87 +40002afb: 232200 sext a2, a2, 7 +40002afe: f01d retw.n +40002b00: d0c222 addi a2, a2, -48 +40002b03: 232200 sext a2, a2, 7 +40002b06: f01d retw.n +40002b08: 004136 entry a1, 32 +40002b0b: 00bbe5 call8 400036c8 <_X_uart_wait_tx_empty+0x2c> +40002b0e: 0042a2 s8i a10, a2, 0 +40002b11: f01d retw.n + ... + +40002b14 : +40002b14: 004136 entry a1, 32 +40002b17: 02ad mov.n a10, a2 +40002b19: 00b125 call8 4000362c +40002b1c: f01d retw.n +40002b1e: 000000 ill +40002b21: 6e .byte 0x6e +40002b22: 366000 excw +40002b25: 310041 l32r a4, 3ffcef28 <_start-0x310d8> +40002b28: fe .byte 0xfe +40002b29: ff .byte 0xff +40002b2a: 0020c0 memw +40002b2d: 8c6322 s32i a2, a3, 0x230 +40002b30: f01d retw.n +40002b32: 360000 excw +40002b35: c20041 l32r a4, 3fff3338 <_start-0xccc8> +40002b38: b180a2 excw +40002b3b: fff9 s32i.n a15, a15, 60 +40002b3d: 0020c0 memw +40002b40: 892bb2 l32i a11, a11, 0x224 +40002b43: c0a2b0 sub a10, a2, a11 +40002b46: 0e2ca7 blt a12, a10, 40002b58 +40002b49: 131aa6 blti a10, 1, 40002b60 +40002b4c: 03d2a2 addmi a10, a2, 0x300 +40002b4f: 84caa2 addi a10, a10, -124 +40002b52: fffd25 call8 40002b24 +40002b55: f01d retw.n +40002b57: 02ad00 andb b10, b13, b0 +40002b5a: fffca5 call8 40002b24 +40002b5d: f01d retw.n +40002b5f: abca00 depbits a0, a10, 26, 13 +40002b62: fffc25 call8 40002b24 +40002b65: f01d retw.n +40002b67: 413600 srli a3, a0, 6 +40002b6a: 323900 orbc b3, b9, b0 +40002b6d: 4249 s32i.n a4, a2, 16 +40002b6f: 080c movi.n a8, 0 +40002b71: f57c movi.n a5, -1 +40002b73: 0259 s32i.n a5, a2, 0 +40002b75: 1289 s32i.n a8, a2, 4 +40002b77: 2289 s32i.n a8, a2, 8 +40002b79: f01d retw.n +40002b7b: dad000 depbits a0, a0, 13, 14 +40002b7e: ff .byte 0xff +40002b7f: 3f .byte 0x3f +40002b80: ca88 l32i.n a8, a10, 48 +40002b82: ff .byte 0xff +40002b83: 3f .byte 0x3f +40002b84: ccd8 l32i.n a13, a12, 48 +40002b86: ff .byte 0xff +40002b87: 3f .byte 0x3f +40002b88: 004136 entry a1, 32 +40002b8b: fffcb1 l32r a11, 40002b7c +40002b8e: 0b98 l32i.n a9, a11, 0 +40002b90: 69bc beqz.n a9, 40002bca +40002b92: 00a0a2 movi a10, 0 +40002b95: 0d8076 loop a0, 40002ba6 +40002b98: 1988 l32i.n a8, a9, 4 +40002b9a: c08280 sub a8, a2, a8 +40002b9d: 0818a6 blti a8, 1, 40002ba9 +40002ba0: 09ad mov.n a10, a9 +40002ba2: 0998 l32i.n a9, a9, 0 +40002ba4: 198c beqz.n a9, 40002ba9 +40002ba6: fffac6 j 40002b95 +40002ba9: 0399 s32i.n a9, a3, 0 +40002bab: 1329 s32i.n a2, a3, 4 +40002bad: da9c beqz.n a10, 40002bce +40002baf: 0a39 s32i.n a3, a10, 0 +40002bb1: 0398 l32i.n a9, a3, 0 +40002bb3: 119937 bne a9, a3, 40002bc8 +40002bb6: fff2a1 l32r a10, 40002b80 +40002bb9: fff2b1 l32r a11, 40002b84 +40002bbc: b1a0c2 movi a12, 177 +40002bbf: 201110 or a1, a1, a1 +40002bc2: ffc425 call8 40002804 +40002bc5: ffff06 j 40002bc5 +40002bc8: f01d retw.n +40002bca: 0399 s32i.n a9, a3, 0 +40002bcc: 1329 s32i.n a2, a3, 4 +40002bce: 02ad mov.n a10, a2 +40002bd0: 0b39 s32i.n a3, a11, 0 +40002bd2: fff625 call8 40002b34 +40002bd5: fff606 j 40002bb1 +40002bd8: 1388 l32i.n a8, a3, 4 +40002bda: 360000 excw +40002bdd: 880041 l32r a4, 3ffe4be0 <_start-0x1b420> +40002be0: fd9102 l16si a0, a1, 0x1fa +40002be3: ff .byte 0xff +40002be4: 110826 beqi a8, -1, 40002bf9 +40002be7: ffe6a1 l32r a10, 40002b80 +40002bea: ffe6b1 l32r a11, 40002b84 +40002bed: bda0c2 movi a12, 189 +40002bf0: ffc125 call8 40002804 +40002bf3: ffff06 j 40002bf3 +40002bf6: 000000 ill +40002bf9: 823390 mull a3, a3, a9 +40002bfc: 148c beqz.n a4, 40002c01 +40002bfe: 026232 s32i a3, a2, 8 +40002c01: fe4a65 call8 400010a8 <_X_ets_enter_critical> +40002c04: 20b220 or a11, a2, a2 +40002c07: ffc6a1 l32r a10, 40002b20 +40002c0a: 0020c0 memw +40002c0d: 892aa2 l32i a10, a10, 0x224 +40002c10: f03d nop.n +40002c12: 80a3a0 add a10, a3, a10 +40002c15: fff725 call8 40002b88 +40002c18: fe4a25 call8 400010bc <_X_ets_exit_critical> +40002c1b: f01d retw.n +40002c1d: 000000 ill +40002c20: 004136 entry a1, 32 +40002c23: 0288 l32i.n a8, a2, 0 +40002c25: a03330 addx4 a3, a3, a3 +40002c28: 110826 beqi a8, -1, 40002c3d +40002c2b: ffd5a1 l32r a10, 40002b80 +40002c2e: ffd5b1 l32r a11, 40002b84 +40002c31: d3a0c2 movi a12, 211 +40002c34: ffbce5 call8 40002804 +40002c37: ffff06 j 40002c37 +40002c3a: 000000 ill +40002c3d: 048c beqz.n a4, 40002c41 +40002c3f: 2239 s32i.n a3, a2, 8 +40002c41: fe4665 call8 400010a8 <_X_ets_enter_critical> +40002c44: 20b220 or a11, a2, a2 +40002c47: ffb6a1 l32r a10, 40002b20 +40002c4a: 0020c0 memw +40002c4d: 892aa2 l32i a10, a10, 0x224 +40002c50: 80a3a0 add a10, a3, a10 +40002c53: fff365 call8 40002b88 +40002c56: fe4665 call8 400010bc <_X_ets_exit_critical> +40002c59: 000090 retw +40002c5c: 004136 entry a1, 32 +40002c5f: fe44a5 call8 400010a8 <_X_ets_enter_critical> +40002c62: ffc6c1 l32r a12, 40002b7c +40002c65: 0020f0 nop +40002c68: 002ca2 l32i a10, a12, 0 +40002c6b: 019a16 beqz a10, 40002c88 +40002c6e: 00a0b2 movi a11, 0 +40002c71: 088076 loop a0, 40002c7d +40002c74: 0812a7 beq a2, a10, 40002c80 +40002c77: 0abd mov.n a11, a10 +40002c79: 0aa8 l32i.n a10, a10, 0 +40002c7b: 9a8c beqz.n a10, 40002c88 +40002c7d: fffc06 j 40002c71 +40002c80: 4a8c beqz.n a10, 40002c88 +40002c82: 2b9c beqz.n a11, 40002c98 +40002c84: 0a88 l32i.n a8, a10, 0 +40002c86: 0b89 s32i.n a8, a11, 0 +40002c88: fe4325 call8 400010bc <_X_ets_exit_critical> +40002c8b: 00a092 movi a9, 0 +40002c8e: ffafa2 movi a10, -1 +40002c91: 0062a2 s32i a10, a2, 0 +40002c94: 2299 s32i.n a9, a2, 8 +40002c96: f01d retw.n +40002c98: 0ab8 l32i.n a11, a10, 0 +40002c9a: 0cb9 s32i.n a11, a12, 0 +40002c9c: fffa06 j 40002c88 +40002c9f: 413600 srli a3, a0, 6 +40002ca2: 028800 andb b8, b8, b0 +40002ca5: 0f0826 beqi a8, -1, 40002cb8 +40002ca8: ffb6a1 l32r a10, 40002b80 +40002cab: ffb6b1 l32r a11, 40002b84 +40002cae: 02a1c2 movi a12, 0x102 +40002cb1: ffb525 call8 40002804 +40002cb4: ffff06 j 40002cb4 +40002cb7: 02ad00 andb b10, b13, b0 +40002cba: 0b0c movi.n a11, 0 +40002cbc: 0c0c movi.n a12, 0 +40002cbe: ffeaa5 call8 40002b68 +40002cc1: f01d retw.n +40002cc3: 413600 srli a3, a0, 6 +40002cc6: 3e2500 excw +40002cc9: fe .byte 0xfe +40002cca: ff9521 l32r a2, 40002b20 +40002ccd: ffab31 l32r a3, 40002b7c +40002cd0: 0020c0 memw +40002cd3: 03b8 l32i.n a11, a3, 0 +40002cd5: 892292 l32i a9, a2, 0x224 +40002cd8: 0b9c beqz.n a11, 40002cec +40002cda: 1b88 l32i.n a8, a11, 4 +40002cdc: ffaf42 movi a4, -1 +40002cdf: c08890 sub a8, a8, a9 +40002ce2: 0e18a6 blti a8, 1, 40002cf4 +40002ce5: 3b8c beqz.n a11, 40002cec +40002ce7: 1ba8 l32i.n a10, a11, 4 +40002ce9: ffe4a5 call8 40002b34 +40002cec: fe3ce5 call8 400010bc <_X_ets_exit_critical> +40002cef: f01d retw.n +40002cf1: 000000 ill +40002cf4: 000506 j 40002d0c +40002cf7: c00000 sub a0, a0, a0 +40002cfa: b80020 excw +40002cfd: 229203 excw +40002d00: 1689 s32i.n a8, a6, 4 +40002d02: fe7b addi.n a15, a14, 7 +40002d04: 1ba8 l32i.n a10, a11, 4 +40002d06: c0aa90 sub a10, a10, a9 +40002d09: d81ae6 bgei a10, 1, 40002ce5 +40002d0c: 0b5d mov.n a5, a11 +40002d0e: 13b9 s32i.n a11, a3, 4 +40002d10: 3bd8 l32i.n a13, a11, 12 +40002d12: 23d9 s32i.n a13, a3, 8 +40002d14: 0bc8 l32i.n a12, a11, 0 +40002d16: 03c9 s32i.n a12, a3, 0 +40002d18: 0b49 s32i.n a4, a11, 0 +40002d1a: fe3a25 call8 400010bc <_X_ets_exit_critical> +40002d1d: 3588 l32i.n a8, a5, 12 +40002d1f: 45a8 l32i.n a10, a5, 16 +40002d21: 0008e0 callx8 a8 +40002d24: fe3825 call8 400010a8 <_X_ets_enter_critical> +40002d27: 0598 l32i.n a9, a5, 0 +40002d29: 25a8 l32i.n a10, a5, 8 +40002d2b: ca0966 bnei a9, -1, 40002cf9 +40002d2e: fc7a16 beqz a10, 40002cf9 +40002d31: 15b8 l32i.n a11, a5, 4 +40002d33: abaa add.n a10, a11, a10 +40002d35: 05bd mov.n a11, a5 +40002d37: 15a9 s32i.n a10, a5, 4 +40002d39: ffe4e5 call8 40002b88 +40002d3c: ffee46 j 40002cf9 +40002d3f: 413600 srli a3, a0, 6 +40002d42: fa1c00 depbits a0, a12, 15, 2 +40002d45: 0b0c movi.n a11, 0 +40002d47: 0c0c movi.n a12, 0 +40002d49: fe24e5 call8 40000f98 <_XX_unk0f96> +40002d4c: f01d retw.n +40002d4e: 360000 excw +40002d51: 880041 l32r a4, 3ffe4d54 <_start-0x1b2ac> +40002d54: 088c02 excw +40002d57: f01d retw.n +40002d59: 0a0c movi.n a10, 0 +40002d5b: fff6a5 call8 40002cc4 +40002d5e: f01d retw.n +40002d60: 002d40 excw +40002d63: 2d5040 excw +40002d66: e04000 subx4 a4, a0, a0 +40002d69: ffda add.n a15, a15, a13 +40002d6b: 3f .byte 0x3f +40002d6c: 004136 entry a1, 32 +40002d6f: 0aa0a2 movi a10, 10 +40002d72: fffbb1 l32r a11, 40002d60 +40002d75: ff8181 l32r a8, 40002b7c +40002d78: 020c movi.n a2, 0 +40002d7a: 02cd mov.n a12, a2 +40002d7c: 006822 s32i a2, a8, 0 +40002d7f: fe36a5 call8 400010e8 <_X_ets_isr_attach> +40002d82: 00a4a2 movi a10, 0x400 +40002d85: fe37e5 call8 40001104 <_X_ets_isr_unmask> +40002d88: fff7a1 l32r a10, 40002d64 +40002d8b: fb1c movi.n a11, 31 +40002d8d: fff6c1 l32r a12, 40002d68 +40002d90: 4d0c movi.n a13, 4 +40002d92: fe1c25 call8 40000f54 <_X_ets_task> +40002d95: ff6291 l32r a9, 40002b20 +40002d98: 0020c0 memw +40002d9b: 8c6922 s32i a2, a9, 0x230 +40002d9e: 88a0a2 movi a10, 136 +40002da1: 0020c0 memw +40002da4: 8a69a2 s32i a10, a9, 0x228 +40002da7: 0020c0 memw +40002daa: 886922 s32i a2, a9, 0x220 +40002dad: f01d retw.n +40002daf: c84400 excw +40002db2: ff .byte 0xff +40002db3: 3f .byte 0x3f + +40002db4 : +40002db4: 004136 entry a1, 32 +40002db7: 0392e5 call8 400066e4 <_xtos_set_intlevel+0x74> +40002dba: fffd81 l32r a8, 40002db0 +40002dbd: 002882 l32i a8, a8, 0 +40002dc0: 0a3d mov.n a3, a10 +40002dc2: 822820 mull a2, a8, a2 +40002dc5: 0391e5 call8 400066e4 <_xtos_set_intlevel+0x74> +40002dc8: c09a30 sub a9, a10, a3 +40002dcb: 08b927 bgeu a9, a2, 40002dd7 +40002dce: 039165 call8 400066e4 <_xtos_set_intlevel+0x74> +40002dd1: c0aa30 sub a10, a10, a3 +40002dd4: f63a27 bltu a10, a2, 40002dce +40002dd7: f01d retw.n +40002dd9: 000000 ill + +40002ddc : +40002ddc: 004136 entry a1, 32 +40002ddf: fff431 l32r a3, 40002db0 +40002de2: 0329 s32i.n a2, a3, 0 +40002de4: f01d retw.n + ... + +40002de8 : +40002de8: 004136 entry a1, 32 +40002deb: fff121 l32r a2, 40002db0 +40002dee: 0228 l32i.n a2, a2, 0 +40002df0: f01d retw.n +40002df2: 360000 excw +40002df5: a60041 l32r a4, 3ffec5f8 <_start-0x13a08> +40002df8: 424913 excw +40002dfb: 30efa0 xor a14, a15, a10 +40002dfe: 6d1450 excw +40002e01: 326003 excw +40002e04: a57621 l32r a2, 3ffec3dc <_start-0x13c24> +40002e07: 520a add.n a5, a2, a0 +40002e09: 1b0002 l8ui a0, a0, 27 +40002e0c: 454022 s8i a2, a0, 69 +40002e0f: 404030 excw +40002e12: f03d74 excw +40002e15: 25a376 loopgtz a3, 40002e3e +40002e18: 000232 l8ui a3, a2, 0 +40002e1b: 010252 l8ui a5, a2, 1 +40002e1e: 020262 l8ui a6, a2, 2 +40002e21: 030272 l8ui a7, a2, 3 +40002e24: 303340 xor a3, a3, a4 +40002e27: 224b addi.n a2, a2, 4 +40002e29: 743030 extui a3, a3, 0, 8 +40002e2c: 303530 xor a3, a5, a3 +40002e2f: 743030 extui a3, a3, 0, 8 +40002e32: 303630 xor a3, a6, a3 +40002e35: 743030 extui a3, a3, 0, 8 +40002e38: 303730 xor a3, a7, a3 +40002e3b: 744030 extui a4, a3, 0, 8 +40002e3e: 042d mov.n a2, a4 +40002e40: f01d retw.n +40002e42: 220000 orb b0, b0, b0 +40002e45: 1defa0 excw +40002e48: 0000f0 callx12 a0 + ... + +40002e4c <_st_0x3fffdb10_uartdev>: +40002e4c: ffdb10 excw +40002e4f: 3f .byte 0x3f + +40002e50 <_c_0x3fffdb00>: +40002e50: ffdb00 excw +40002e53: 3f .byte 0x3f +40002e54: 004000 break 0, 0 + ... + +40002e58 <_XX_unk2e58>: +40002e58: 006136 entry a1, 48 +40002e5b: 02ad mov.n a10, a2 +40002e5d: 005765 call8 400033d4 <_c_0x0000ffff+0x4> +40002e60: fffb41 l32r a4, 40002e4c <_st_0x3fffdb10_uartdev> +40002e63: c498 l32i.n a9, a4, 48 +40002e65: 230c movi.n a3, 2 +40002e67: 39ac beqz.n a9, 40002e8e <_XX_unk2e58+0x36> +40002e69: 211926 beqi a9, 1, 40002e8e <_XX_unk2e58+0x36> +40002e6c: f9b1a1 l32r a10, 40001530 <_s_waiting_for_host+0x4> +40002e6f: f9b1b1 l32r a11, 40001534 <_s_waiting_for_host+0x8> +40002e72: 01a0c2 movi a12, 1 +40002e75: 00b125 call8 40003988 <_X_RcvMsg> +40002e78: 00ea56 bnez a10, 40002e8a <_XX_unk2e58+0x32> +40002e7b: f9ada1 l32r a10, 40001530 <_s_waiting_for_host+0x4> +40002e7e: 000a82 l8ui a8, a10, 0 +40002e81: 010a92 l8ui a9, a10, 1 +40002e84: 002856 bnez a8, 40002e8a <_XX_unk2e58+0x32> +40002e87: 1e8926 beqi a9, 8, 40002ea9 <_XX_unk2e58+0x51> +40002e8a: 020c movi.n a2, 0 +40002e8c: f01d retw.n +40002e8e: 20a220 or a10, a2, a2 +40002e91: 01a0b2 movi a11, 1 +40002e94: 005b65 call8 4000344c <_c_0x000fffff+0x4> +40002e97: f4b0a0 extui a11, a10, 0, 16 +40002e9a: fecb16 beqz a11, 40002e8a <_XX_unk2e58+0x32> +40002e9d: 02ad mov.n a10, a2 +40002e9f: 0064a5 call8 400034e8 +40002ea2: c439 s32i.n a3, a4, 48 +40002ea4: fff106 j 40002e6c <_XX_unk2e58+0x14> +40002ea7: 0c0000 excw +40002eaa: 411c movi.n a1, 20 +40002eac: ffe9 s32i.n a14, a15, 60 +40002eae: 8b0c movi.n a11, 8 +40002eb0: 015432 s16i a3, a4, 2 +40002eb3: 0144b2 s8i a11, a4, 1 +40002eb6: 0044c2 s8i a12, a4, 0 +40002eb9: 01bd mov.n a11, a1 +40002ebb: c44b addi.n a12, a4, 4 +40002ebd: 004065 call8 400032c4 <_X_UartConnectProc> +40002ec0: f98d61 l32r a6, 400014f4 <_c_0x60008e00> +40002ec3: f98b51 l32r a5, 400014f0 <_c_0xffff8fff> +40002ec6: 00a0d2 movi a13, 0 +40002ec9: 0844d2 s8i a13, a4, 8 +40002ecc: 0944d2 s8i a13, a4, 9 +40002ecf: 053216 beqz a2, 40002f26 <_XX_unk2e58+0xce> +40002ed2: f897f1 l32r a15, 40001130 <_c_0x60003e00> +40002ed5: ffdfa1 l32r a10, 40002e54 <_c_0x3fffdb00+0x4> +40002ed8: 0020c0 memw +40002edb: 9626d2 l32i a13, a6, 0x258 +40002ede: 10dd50 and a13, a13, a5 +40002ee1: 0020c0 memw +40002ee4: 9666d2 s32i a13, a6, 0x258 +40002ee7: 0020c0 memw +40002eea: 9626c2 l32i a12, a6, 0x258 +40002eed: 20cca0 or a12, a12, a10 +40002ef0: 0020c0 memw +40002ef3: 9666c2 s32i a12, a6, 0x258 +40002ef6: 0020c0 memw +40002ef9: 9526b2 l32i a11, a6, 0x254 +40002efc: 10bb50 and a11, a11, a5 +40002eff: 0020c0 memw +40002f02: 9566b2 s32i a11, a6, 0x254 +40002f05: 0020c0 memw +40002f08: 952692 l32i a9, a6, 0x254 +40002f0b: 2099a0 or a9, a9, a10 +40002f0e: 0020c0 memw +40002f11: 956692 s32i a9, a6, 0x254 +40002f14: f88681 l32r a8, 4000112c <_c_0xfffdffff> +40002f17: 0020c0 memw +40002f1a: f02fe2 l32i a14, a15, 0x3c0 +40002f1d: 10ee80 and a14, a14, a8 +40002f20: 0020c0 memw +40002f23: f06fe2 s32i a14, a15, 0x3c0 +40002f26: 830c movi.n a3, 8 +40002f28: 20a440 or a10, a4, a4 +40002f2b: 0aa0b2 movi a11, 10 +40002f2e: 008fa5 call8 40003828 <_X_SendMsg> +40002f31: 0b1a26 beqi a10, 1, 40002f40 <_XX_unk2e58+0xe8> +40002f34: ffc332 addi a3, a3, -1 +40002f37: fed356 bnez a3, 40002f28 <_XX_unk2e58+0xd0> +40002f3a: 220c movi.n a2, 2 +40002f3c: f01d retw.n +40002f3e: 160000 excw +40002f41: c0f462 s32ri a6, a4, 0x300 +40002f44: f20020 rems a0, a0, a2 +40002f47: 509626 beqi a6, 10, 40002f9b <_X_UartDwnLdProc+0x2f> +40002f4a: ff .byte 0xff +40002f4b: 20c010 or a12, a0, a1 +40002f4e: 66f200 excw +40002f51: 20c096 bltz a0, 40003161 <_XX_unk313c+0x25> +40002f54: 26e200 excw +40002f57: 20c096 bltz a0, 40003167 <_XX_unk313c+0x2b> +40002f5a: 66e200 excw +40002f5d: 020c96 bltz a12, 40002f81 <_X_UartDwnLdProc+0x15> +40002f60: f01d retw.n + ... + +40002f64 <_c_0x3fffdb04>: +40002f64: ffdb04 excw +40002f67: 3f .byte 0x3f +40002f68: ffc9f0 excw +40002f6b: 3f .byte 0x3f + +40002f6c <_X_UartDwnLdProc>: +40002f6c: 00a136 entry a1, 80 +40002f6f: 0a2c movi.n a10, 32 +40002f71: 280c movi.n a8, 2 +40002f73: ffb751 l32r a5, 40002e50 <_c_0x3fffdb00> +40002f76: 046d mov.n a6, a4 +40002f78: 070c movi.n a7, 0 +40002f7a: 6179 s32i.n a7, a1, 24 +40002f7c: 140c movi.n a4, 1 +40002f7e: 004542 s8i a4, a5, 0 +40002f81: 015582 s16i a8, a5, 2 +40002f84: fe17e5 call8 40001104 <_X_ets_isr_unmask> +40002f87: 6198 l32i.n a9, a1, 24 +40002f89: 5c2926 beqi a9, 2, 40002fe9 <_X_UartDwnLdProc+0x7d> +40002f8c: f43030 extui a3, a3, 0, 16 +40002f8f: 870c movi.n a7, 8 +40002f91: 8169 s32i.n a6, a1, 32 +40002f93: fff491 l32r a9, 40002f64 <_c_0x3fffdb04> +40002f96: 9199 s32i.n a9, a1, 36 +40002f98: 20b330 or a11, a3, a3 +40002f9b: 20a220 or a10, a2, a2 +40002f9e: 00a0c2 movi a12, 0 +40002fa1: 009e65 call8 40003988 <_X_RcvMsg> +40002fa4: aa0b addi.n a10, a10, -1 +40002fa6: 134a16 beqz a10, 400030de <_X_UartDwnLdProc+0x172> +40002fa9: 0002b2 l8ui a11, a2, 0 +40002fac: 1a0c movi.n a10, 1 +40002fae: 04fb16 beqz a11, 40003001 <_X_UartDwnLdProc+0x95> +40002fb1: 590c movi.n a9, 5 +40002fb3: 7199 s32i.n a9, a1, 28 +40002fb5: 094592 s8i a9, a5, 9 +40002fb8: 04cd mov.n a12, a4 +40002fba: 0105d2 l8ui a13, a5, 1 +40002fbd: 01a0e2 movi a14, 1 +40002fc0: 93aea0 movnez a10, a14, a10 +40002fc3: 0845a2 s8i a10, a5, 8 +40002fc6: f8cdd2 addi a13, a13, -8 +40002fc9: 83c7d0 moveqz a12, a7, a13 +40002fcc: 206cc0 or a6, a12, a12 +40002fcf: 011c16 beqz a12, 40002fe4 <_X_UartDwnLdProc+0x78> +40002fd2: 20a550 or a10, a5, a5 +40002fd5: 0aa0b2 movi a11, 10 +40002fd8: 0084e5 call8 40003828 <_X_SendMsg> +40002fdb: 161a26 beqi a10, 1, 40002ff5 <_X_UartDwnLdProc+0x89> +40002fde: ffc662 addi a6, a6, -1 +40002fe1: fed656 bnez a6, 40002fd2 <_X_UartDwnLdProc+0x66> +40002fe4: 61f8 l32i.n a15, a1, 24 +40002fe6: ae2f66 bnei a15, 2, 40002f98 <_X_UartDwnLdProc+0x2c> +40002fe9: 20a0a2 movi a10, 32 +40002fec: fe10a5 call8 400010f8 <_X_ets_isr_mask> +40002fef: 00a022 movi a2, 0 +40002ff2: 000090 retw +40002ff5: 20a0a2 movi a10, 32 +40002ff8: fe0fe5 call8 400010f8 <_X_ets_isr_mask> +40002ffb: 01a022 movi a2, 1 +40002ffe: 000090 retw +40003001: 010292 l8ui a9, a2, 1 +40003004: ffd981 l32r a8, 40002f68 <_c_0x3fffdb04+0x4> +40003007: 07a9f6 bgeui a9, 12, 40003012 <_X_UartDwnLdProc+0xa6> +4000300a: a08980 addx4 a8, a9, a8 +4000300d: 0888 l32i.n a8, a8, 0 +4000300f: 0008a0 jx a8 +40003012: 1a0c movi.n a10, 1 +40003014: 590c movi.n a9, 5 +40003016: 7199 s32i.n a9, a1, 28 +40003018: 7198 l32i.n a9, a1, 28 +4000301a: ffe5c6 j 40002fb5 <_X_UartDwnLdProc+0x49> +4000301d: 02ad00 andb b10, b13, b0 +40003020: 1cc1b2 addi a11, a1, 28 +40003023: 01cd mov.n a12, a1 +40003025: 001ea5 call8 40003210 <_XX_unk3210> +40003028: 5b0c movi.n a11, 5 +4000302a: 0145b2 s8i a11, a5, 1 +4000302d: fff9c6 j 40003018 <_X_UartDwnLdProc+0xac> +40003030: 02ad mov.n a10, a2 +40003032: 1cc1b2 addi a11, a1, 28 +40003035: 001ae5 call8 400031e4 <_XX_unk31e4> +40003038: bc0c movi.n a12, 11 +4000303a: 0145c2 s8i a12, a5, 1 +4000303d: fff5c6 j 40003018 <_X_UartDwnLdProc+0xac> +40003040: 02ad mov.n a10, a2 +40003042: 1cc1b2 addi a11, a1, 28 +40003045: 91c8 l32i.n a12, a1, 36 +40003047: 002d25 call8 40003318 <_X_UartRegReadProc> +4000304a: ad0c movi.n a13, 10 +4000304c: 0145d2 s8i a13, a5, 1 +4000304f: fff146 j 40003018 <_X_UartDwnLdProc+0xac> +40003052: 02ad mov.n a10, a2 +40003054: 1cc1b2 addi a11, a1, 28 +40003057: 0027e5 call8 400032d4 <_X_UartRegWriteProc> +4000305a: 9e0c movi.n a14, 9 +4000305c: 0145e2 s8i a14, a5, 1 +4000305f: ffed46 j 40003018 <_X_UartDwnLdProc+0xac> +40003062: 02ad mov.n a10, a2 +40003064: 1cc1b2 addi a11, a1, 28 +40003067: 91c8 l32i.n a12, a1, 36 +40003069: 0025a5 call8 400032c4 <_X_UartConnectProc> +4000306c: 014572 s8i a7, a5, 1 +4000306f: ffe946 j 40003018 <_X_UartDwnLdProc+0xac> +40003072: 02ad mov.n a10, a2 +40003074: 1cc1b2 addi a11, a1, 28 +40003077: 01cd mov.n a12, a1 +40003079: 001c65 call8 40003240 <_XX_unk3240> +4000307c: 7f0c movi.n a15, 7 +4000307e: 0145f2 s8i a15, a5, 1 +40003081: ffe4c6 j 40003018 <_X_UartDwnLdProc+0xac> +40003084: 02ad mov.n a10, a2 +40003086: 1cc1b2 addi a11, a1, 28 +40003089: 01cd mov.n a12, a1 +4000308b: 81d8 l32i.n a13, a1, 32 +4000308d: 0020e5 call8 4000329c <_X_MemDwnLdStopReqMsgProc> +40003090: 680c movi.n a8, 6 +40003092: 014582 s8i a8, a5, 1 +40003095: ffdfc6 j 40003018 <_X_UartDwnLdProc+0xac> +40003098: 02ad mov.n a10, a2 +4000309a: 1cc1b2 addi a11, a1, 28 +4000309d: 01cd mov.n a12, a1 +4000309f: 0011e5 call8 400031bc <_XX_unk31bc> +400030a2: 490c movi.n a9, 4 +400030a4: 81c8 l32i.n a12, a1, 32 +400030a6: 0b0c movi.n a11, 0 +400030a8: 0cb9 s32i.n a11, a12, 0 +400030aa: 014592 s8i a9, a5, 1 +400030ad: ffd9c6 j 40003018 <_X_UartDwnLdProc+0xac> +400030b0: 02ad mov.n a10, a2 +400030b2: 1cc1b2 addi a11, a1, 28 +400030b5: 01cd mov.n a12, a1 +400030b7: 000865 call8 4000313c <_XX_unk313c> +400030ba: 3d0c movi.n a13, 3 +400030bc: 0145d2 s8i a13, a5, 1 +400030bf: ffd546 j 40003018 <_X_UartDwnLdProc+0xac> +400030c2: 00a0a2 movi a10, 0 +400030c5: 00a0b2 movi a11, 0 +400030c8: 012a65 call8 40004370 <_X_spi_flash_attach> +400030cb: 20a220 or a10, a2, a2 +400030ce: 1cc1b2 addi a11, a1, 28 +400030d1: 01cd mov.n a12, a1 +400030d3: 0001e5 call8 400030f0 <_X_FlashDwnLdStartMsgProc> +400030d6: 2e0c movi.n a14, 2 +400030d8: 0145e2 s8i a14, a5, 1 +400030db: ffce46 j 40003018 <_X_UartDwnLdProc+0xac> +400030de: 20a0a2 movi a10, 32 +400030e1: fe0165 call8 400010f8 <_X_ets_isr_mask> +400030e4: 01a022 movi a2, 1 +400030e7: 000090 retw + ... + +400030ec <_c_0x00001800>: +400030ec: 001800 movsp a0, a8 + ... + +400030f0 <_X_FlashDwnLdStartMsgProc>: +400030f0: 004136 entry a1, 32 +400030f3: 011282 l16ui a8, a2, 2 +400030f6: 05a0a2 movi a10, 5 +400030f9: 05b826 beqi a8, 16, 40003102 <_X_FlashDwnLdStartMsgProc+0x12> +400030fc: 120c movi.n a2, 1 +400030fe: 03a9 s32i.n a10, a3, 0 +40003100: f01d retw.n +40003102: 0422b2 l32i a11, a2, 16 +40003105: fff991 l32r a9, 400030ec <_c_0x00001800> +40003108: f039b7 bltu a9, a11, 400030fc <_X_FlashDwnLdStartMsgProc+0xc> +4000310b: 0264b2 s32i a11, a4, 8 +4000310e: 0a0c movi.n a10, 0 +40003110: 32c8 l32i.n a12, a2, 12 +40003112: 22d8 l32i.n a13, a2, 8 +40003114: 04d9 s32i.n a13, a4, 0 +40003116: 14c9 s32i.n a12, a4, 4 +40003118: 34a9 s32i.n a10, a4, 12 +4000311a: 44a9 s32i.n a10, a4, 16 +4000311c: 52b8 l32i.n a11, a2, 20 +4000311e: 54b9 s32i.n a11, a4, 20 +40003120: 0132a5 call8 4000444c +40003123: 54a8 l32i.n a10, a4, 20 +40003125: 04b8 l32i.n a11, a4, 0 +40003127: 0178e5 call8 400048b4 <_X_SPIEraseArea> +4000312a: 6a8c beqz.n a10, 40003134 <_X_FlashDwnLdStartMsgProc+0x44> +4000312c: 120c movi.n a2, 1 +4000312e: 6e0c movi.n a14, 6 +40003130: 03e9 s32i.n a14, a3, 0 +40003132: f01d retw.n +40003134: 020c movi.n a2, 0 +40003136: f01d retw.n +40003138: 001810 movsp a1, a8 + ... + +4000313c <_XX_unk313c>: +4000313c: 004136 entry a1, 32 +4000313f: 011292 l16ui a9, a2, 2 +40003142: fffd81 l32r a8, 40003138 <_X_FlashDwnLdStartMsgProc+0x48> +40003145: 5a0c movi.n a10, 5 +40003147: 07b897 bgeu a8, a9, 40003152 <_XX_unk313c+0x16> +4000314a: 120c movi.n a2, 1 +4000314c: 03a9 s32i.n a10, a3, 0 +4000314e: f01d retw.n +40003150: c80000 excw +40003153: 32b834 excw +40003156: 24d8 l32i.n a13, a4, 8 +40003158: ee9bc7 bne a11, a12, 4000314a <_XX_unk313c+0xe> +4000315b: 22b8 l32i.n a11, a2, 8 +4000315d: 3e0c movi.n a14, 3 +4000315f: e73db7 bltu a13, a11, 4000314a <_XX_unk313c+0xe> +40003162: e48eb7 bany a14, a11, 4000314a <_XX_unk313c+0xe> +40003165: 040252 l8ui a5, a2, 4 +40003168: 18c262 addi a6, a2, 24 +4000316b: fc1b addi.n a15, a12, 1 +4000316d: 34f9 s32i.n a15, a4, 12 +4000316f: 06ad mov.n a10, a6 +40003171: ffc825 call8 40002df4 +40003174: 0915a7 beq a5, a10, 40003181 <_XX_unk313c+0x45> +40003177: 720c movi.n a2, 7 +40003179: 0329 s32i.n a2, a3, 0 +4000317b: 120c movi.n a2, 1 +4000317d: f01d retw.n +4000317f: a20000 muluh a0, a0, a0 +40003182: 600524 excw +40003185: c820b6 bltui a0, 2, 40003151 <_XX_unk313c+0x15> +40003188: 5ae522 s32c1i a2, a5, 0x168 +4000318b: 54a801 l32r a0, 3ffd842c <_start-0x27bd4> +4000318e: 06bd mov.n a11, a6 +40003190: 22c8 l32i.n a12, a2, 8 +40003192: 017065 call8 40004898 +40003195: 06ad mov.n a10, a6 +40003197: 22b8 l32i.n a11, a2, 8 +40003199: ffc5a5 call8 40002df4 +4000319c: 0815a7 beq a5, a10, 400031a8 <_XX_unk313c+0x6c> +4000319f: 120c movi.n a2, 1 +400031a1: 880c movi.n a8, 8 +400031a3: 0389 s32i.n a8, a3, 0 +400031a5: f01d retw.n +400031a7: 44b800 extui a11, a0, 8, 5 +400031aa: 5498 l32i.n a9, a4, 20 +400031ac: 22a8 l32i.n a10, a2, 8 +400031ae: 020c movi.n a2, 0 +400031b0: 99aa add.n a9, a9, a10 +400031b2: bbaa add.n a11, a11, a10 +400031b4: 44b9 s32i.n a11, a4, 16 +400031b6: 5499 s32i.n a9, a4, 20 +400031b8: f01d retw.n + ... + +400031bc <_XX_unk31bc>: +400031bc: 004136 entry a1, 32 +400031bf: 3498 l32i.n a9, a4, 12 +400031c1: 2a0c movi.n a10, 2 +400031c3: 1488 l32i.n a8, a4, 4 +400031c5: 64a9 s32i.n a10, a4, 24 +400031c7: 6a0c movi.n a10, 6 +400031c9: 079897 bne a8, a9, 400031d4 <_XX_unk31bc+0x18> +400031cc: 44c8 l32i.n a12, a4, 16 +400031ce: 0024b2 l32i a11, a4, 0 +400031d1: 071bc7 beq a11, a12, 400031dc <_XX_unk31bc+0x20> +400031d4: 120c movi.n a2, 1 +400031d6: 03a9 s32i.n a10, a3, 0 +400031d8: f01d retw.n +400031da: 650000 extui a0, a0, 16, 7 +400031dd: 0c0131 l32r a3, 3ffc61e4 <_start-0x39e1c> +400031e0: f01d02 l16ui a0, a13, 0x1e0 + ... + +400031e4 <_XX_unk31e4>: +400031e4: 004136 entry a1, 32 +400031e7: 011282 l16ui a8, a2, 2 +400031ea: 891c movi.n a9, 24 +400031ec: 081897 beq a8, a9, 400031f8 <_XX_unk31e4+0x14> +400031ef: 120c movi.n a2, 1 +400031f1: 5a0c movi.n a10, 5 +400031f3: 03a9 s32i.n a10, a3, 0 +400031f5: f01d retw.n +400031f7: 22a200 orb b10, b2, b0 +400031fa: 22b202 l32ai a0, a2, 136 +400031fd: 22c203 excw +40003200: 52d804 excw +40003203: 62e8 l32i.n a14, a2, 24 +40003205: 72f8 l32i.n a15, a2, 28 +40003207: 0173a5 call8 40004940 <_XX_unk4940> +4000320a: 020c movi.n a2, 0 +4000320c: f01d retw.n + ... + +40003210 <_XX_unk3210>: +40003210: 004136 entry a1, 32 +40003213: 011252 l16ui a5, a2, 2 +40003216: 05a072 movi a7, 5 +40003219: 05b526 beqi a5, 16, 40003222 <_XX_unk3210+0x12> +4000321c: 120c movi.n a2, 1 +4000321e: 0379 s32i.n a7, a3, 0 +40003220: f01d retw.n +40003222: 4288 l32i.n a8, a2, 16 +40003224: ffb261 l32r a6, 400030ec <_c_0x00001800> +40003227: 5298 l32i.n a9, a2, 20 +40003229: ef3687 bltu a6, a8, 4000321c <_XX_unk3210+0xc> +4000322c: 2489 s32i.n a8, a4, 8 +4000322e: 5499 s32i.n a9, a4, 20 +40003230: 32a8 l32i.n a10, a2, 12 +40003232: 22b8 l32i.n a11, a2, 8 +40003234: 04b9 s32i.n a11, a4, 0 +40003236: 14a9 s32i.n a10, a4, 4 +40003238: 020c movi.n a2, 0 +4000323a: 3429 s32i.n a2, a4, 12 +4000323c: 4429 s32i.n a2, a4, 16 +4000323e: f01d retw.n + +40003240 <_XX_unk3240>: +40003240: 004136 entry a1, 32 +40003243: 011292 l16ui a9, a2, 2 +40003246: ffbc81 l32r a8, 40003138 <_X_FlashDwnLdStartMsgProc+0x48> +40003249: 5a0c movi.n a10, 5 +4000324b: 05b897 bgeu a8, a9, 40003254 <_XX_unk3240+0x14> +4000324e: 120c movi.n a2, 1 +40003250: 03a9 s32i.n a10, a3, 0 +40003252: f01d retw.n +40003254: 34c8 l32i.n a12, a4, 12 +40003256: 32b8 l32i.n a11, a2, 12 +40003258: 24d8 l32i.n a13, a4, 8 +4000325a: f09bc7 bne a11, a12, 4000324e <_XX_unk3240+0xe> +4000325d: 22b8 l32i.n a11, a2, 8 +4000325f: eb3db7 bltu a13, a11, 4000324e <_XX_unk3240+0xe> +40003262: 040252 l8ui a5, a2, 4 +40003265: 18c262 addi a6, a2, 24 +40003268: ec1b addi.n a14, a12, 1 +4000326a: 34e9 s32i.n a14, a4, 12 +4000326c: 06ad mov.n a10, a6 +4000326e: ffb865 call8 40002df4 +40003271: 0715a7 beq a5, a10, 4000327c <_XX_unk3240+0x3c> +40003274: 120c movi.n a2, 1 +40003276: 7f0c movi.n a15, 7 +40003278: 03f9 s32i.n a15, a3, 0 +4000327a: f01d retw.n +4000327c: 20b660 or a11, a6, a6 +4000327f: 0524a2 l32i a10, a4, 20 +40003282: 0222c2 l32i a12, a2, 8 +40003285: feb3e5 call8 40001dc4 <_X_ets_memcpy> +40003288: 4498 l32i.n a9, a4, 16 +4000328a: 2288 l32i.n a8, a2, 8 +4000328c: 5428 l32i.n a2, a4, 20 +4000328e: 998a add.n a9, a9, a8 +40003290: 228a add.n a2, a2, a8 +40003292: 5429 s32i.n a2, a4, 20 +40003294: 4499 s32i.n a9, a4, 16 +40003296: 020c movi.n a2, 0 +40003298: f01d retw.n + ... + +4000329c <_X_MemDwnLdStopReqMsgProc>: +4000329c: 004136 entry a1, 32 +4000329f: 011272 l16ui a7, a2, 2 +400032a2: 860c movi.n a6, 8 +400032a4: 08b677 bgeu a6, a7, 400032b0 <_X_MemDwnLdStopReqMsgProc+0x14> +400032a7: 120c movi.n a2, 1 +400032a9: 580c movi.n a8, 5 +400032ab: 0389 s32i.n a8, a3, 0 +400032ad: f01d retw.n +400032af: 2b0c00 depbits a0, a12, 18, 1 +400032b2: 0a0c movi.n a10, 0 +400032b4: 3298 l32i.n a9, a2, 12 +400032b6: 0802c2 l8ui a12, a2, 8 +400032b9: 020c movi.n a2, 0 +400032bb: 83abc0 moveqz a10, a11, a12 +400032be: 64a9 s32i.n a10, a4, 24 +400032c0: 0599 s32i.n a9, a5, 0 +400032c2: f01d retw.n + +400032c4 <_X_UartConnectProc>: +400032c4: 004136 entry a1, 32 +400032c7: 2228 l32i.n a2, a2, 8 +400032c9: 0429 s32i.n a2, a4, 0 +400032cb: 020c movi.n a2, 0 +400032cd: 0329 s32i.n a2, a3, 0 +400032cf: f01d retw.n +400032d1: 000000 ill + +400032d4 <_X_UartRegWriteProc>: +400032d4: 004136 entry a1, 32 +400032d7: 011242 l16ui a4, a2, 2 +400032da: 228b addi.n a2, a2, 8 +400032dc: 414440 srli a4, a4, 4 +400032df: e4ac beqz.n a4, 40003311 <_X_UartRegWriteProc+0x3d> +400032e1: ffaf52 movi a5, -1 +400032e4: 0612a2 l16ui a10, a2, 12 +400032e7: fface5 call8 40002db4 +400032ea: 002292 l32i a9, a2, 0 +400032ed: 0020c0 memw +400032f0: 012282 l32i a8, a2, 4 +400032f3: 0222b2 l32i a11, a2, 8 +400032f6: 09a8 l32i.n a10, a9, 0 +400032f8: 30cb50 xor a12, a11, a5 +400032fb: 10aac0 and a10, a10, a12 +400032fe: 1088b0 and a8, a8, a11 +40003301: 2088a0 or a8, a8, a10 +40003304: 0020c0 memw +40003307: 0989 s32i.n a8, a9, 0 +40003309: 10c222 addi a2, a2, 16 +4000330c: 440b addi.n a4, a4, -1 +4000330e: d214e6 bgei a4, 1, 400032e4 <_X_UartRegWriteProc+0x10> +40003311: 020c movi.n a2, 0 +40003313: 0329 s32i.n a2, a3, 0 +40003315: f01d retw.n + ... + +40003318 <_X_UartRegReadProc>: +40003318: 004136 entry a1, 32 +4000331b: 2228 l32i.n a2, a2, 8 +4000331d: 0020c0 memw +40003320: 0228 l32i.n a2, a2, 0 +40003322: 0429 s32i.n a2, a4, 0 +40003324: 020c movi.n a2, 0 +40003326: 0329 s32i.n a2, a3, 0 +40003328: f01d retw.n + ... + +4000332c <_c_115200>: +4000332c: 01c200 slli a12, a2, 32 + ... + +40003330 <_c_0x3feffe00>: +40003330: effe00 excw +40003333: 3f .byte 0x3f + +40003334 <_c_0xffff83ff>: +40003334: ff .byte 0xff +40003335: ffff83 excw + +40003338 <_c_0x00001400>: +40003338: 001400 movsp a0, a4 + ... + +4000333c <_c_0x40003728_uart_int_handler>: +4000333c: 3728 l32i.n a2, a7, 12 +4000333e: 2c4000 excw + +40003340 <_c_0x3fffdb2c_uart_int_handler_arg>: +40003340: db2c movi.n a11, 45 +40003342: ff .byte 0xff +40003343: 3f .byte 0x3f + +40003344 <_X_uart_attach>: +40003344: 004136 entry a1, 32 +40003347: 03a0a2 movi a10, 3 +4000334a: 01a082 movi a8, 1 +4000334d: fff7b1 l32r a11, 4000332c <_c_115200> +40003350: febf91 l32r a9, 40002e4c <_st_0x3fffdb10_uartdev> +40003353: 0d0c movi.n a13, 0 +40003355: 59d9 s32i.n a13, a9, 20 +40003357: 29d9 s32i.n a13, a9, 8 +40003359: 39d9 s32i.n a13, a9, 12 +4000335b: 49d9 s32i.n a13, a9, 16 +4000335d: b9d9 s32i.n a13, a9, 44 +4000335f: c9d9 s32i.n a13, a9, 48 +40003361: d9d9 s32i.n a13, a9, 52 +40003363: 1849d2 s8i a13, a9, 24 +40003366: 1949d2 s8i a13, a9, 25 +40003369: 09b9 s32i.n a11, a9, 0 +4000336b: 284982 s8i a8, a9, 40 +4000336e: 19a9 s32i.n a10, a9, 4 +40003370: 40c9c2 addi a12, a9, 64 +40003373: 79c9 s32i.n a12, a9, 28 +40003375: 89c9 s32i.n a12, a9, 32 +40003377: 99c9 s32i.n a12, a9, 36 +40003379: 20a0a2 movi a10, 32 +4000337c: fdd7a5 call8 400010f8 <_X_ets_isr_mask> +4000337f: 1facc2 movi a12, 0xfffffc1f +40003382: a0a0b2 movi a11, 160 +40003385: ffeaf1 l32r a15, 40003330 <_c_0x3feffe00> +40003388: 0020c0 memw +4000338b: 9e2fa2 l32i a10, a15, 0x278 +4000338e: 10aac0 and a10, a10, a12 +40003391: 20aab0 or a10, a10, a11 +40003394: 0020c0 memw +40003397: 9e6fa2 s32i a10, a15, 0x278 +4000339a: ffe691 l32r a9, 40003334 <_c_0xffff83ff> +4000339d: ffe681 l32r a8, 40003338 <_c_0x00001400> +400033a0: 0020c0 memw +400033a3: 9e2fe2 l32i a14, a15, 0x278 +400033a6: 10ee90 and a14, a14, a9 +400033a9: 20ee80 or a14, a14, a8 +400033ac: 0020c0 memw +400033af: 9e6fe2 s32i a14, a15, 0x278 +400033b2: 5a0c movi.n a10, 5 +400033b4: ffe2b1 l32r a11, 4000333c <_c_0x40003728_uart_int_handler> +400033b7: ffe2c1 l32r a12, 40003340 <_c_0x3fffdb2c_uart_int_handler_arg> +400033ba: fdd2e5 call8 400010e8 <_X_ets_isr_attach> +400033bd: f01d retw.n + ... + +400033c0 <_XX_uart_set_unk33c0>: +400033c0: 004136 entry a1, 32 +400033c3: fea231 l32r a3, 40002e4c <_st_0x3fffdb10_uartdev> +400033c6: 194322 s8i a2, a3, 25 +400033c9: f01d retw.n + ... + +400033cc <_c_0x5ffffe00>: +400033cc: fffe00 excw +400033cf: 5f .byte 0x5f + +400033d0 <_c_0x0000ffff>: +400033d0: ff .byte 0xff +400033d1: ff .byte 0xff +400033d2: 360000 excw +400033d5: 510061 l32r a6, 3ffd77d8 <_start-0x28828> +400033d8: fe .byte 0xfe +400033d9: ff .byte 0xff +400033da: 190c movi.n a9, 1 +400033dc: 080c movi.n a8, 0 +400033de: fffb41 l32r a4, 400033cc <_c_0x5ffffe00> +400033e1: 838920 moveqz a8, a9, a2 +400033e4: 118800 slli a8, a8, 16 +400033e7: 884a add.n a8, a8, a4 +400033e9: 0020c0 memw +400033ec: 846852 s32i a5, a8, 0x210 +400033ef: e97c movi.n a9, -2 +400033f1: 0020c0 memw +400033f4: 832832 l32i a3, a8, 0x20c +400033f7: 103390 and a3, a3, a9 +400033fa: 0020c0 memw +400033fd: 836832 s32i a3, a8, 0x20c +40003400: ffd031 l32r a3, 40003340 <_c_0x3fffdb2c_uart_int_handler_arg> +40003403: 01bd mov.n a11, a1 +40003405: 03ad mov.n a10, a3 +40003407: 0059a5 call8 400039a0 <_X_uart_rx_readbuff> +4000340a: 7aac beqz.n a10, 40003435 <_c_0x0000ffff+0x65> +4000340c: fe90a1 l32r a10, 40002e4c <_st_0x3fffdb10_uartdev> +4000340f: 0d0c movi.n a13, 0 +40003411: bad9 s32i.n a13, a10, 44 +40003413: 11c200 slli a12, a2, 16 +40003416: cc4a add.n a12, a12, a4 +40003418: 0020c0 memw +4000341b: 846c52 s32i a5, a12, 0x210 +4000341e: 1d0c movi.n a13, 1 +40003420: 0020c0 memw +40003423: 832cb2 l32i a11, a12, 0x20c +40003426: 20bbd0 or a11, a11, a13 +40003429: 0020c0 memw +4000342c: 836cb2 s32i a11, a12, 0x20c +4000342f: 184a22 s8i a2, a10, 24 +40003432: f01d retw.n +40003434: 03ad00 excw +40003437: 01bd mov.n a11, a1 +40003439: 005665 call8 400039a0 <_X_uart_rx_readbuff> +4000343c: ff5a16 beqz a10, 40003435 <_c_0x0000ffff+0x65> +4000343f: fff246 j 4000340c <_c_0x0000ffff+0x3c> +40003442: 010000 slli a0, a0, 32 +40003445: 0008 l32i.n a0, a0, 0 + ... + +40003448 <_c_0x000fffff>: +40003448: ff .byte 0xff +40003449: ff .byte 0xff +4000344a: 0f .byte 0xf +4000344b: 413600 srli a3, a0, 6 +4000344e: 7fc100 excw +40003451: fe .byte 0xfe +40003452: e47c movi.n a4, -2 +40003454: ccb8 l32i.n a11, a12, 48 +40003456: 01a0d2 movi a13, 1 +40003459: 83bdb0 moveqz a11, a13, a11 +4000345c: 0c6cb2 s32i a11, a12, 48 +4000345f: ffdba1 l32r a10, 400033cc <_c_0x5ffffe00> +40003462: 119200 slli a9, a2, 16 +40003465: 29aa add.n a2, a9, a10 +40003467: 0020c0 memw +4000346a: 862282 l32i a8, a2, 0x218 +4000346d: 17e807 bbsi a8, 0, 40003488 <_c_0x000fffff+0x40> +40003470: 0020c0 memw +40003473: 8622f2 l32i a15, a2, 0x218 +40003476: 10ff40 and a15, a15, a4 +40003479: 0020c0 memw +4000347c: 8662f2 s32i a15, a2, 0x218 +4000347f: fff1e1 l32r a14, 40003444 <_c_0x0000ffff+0x74> +40003482: 0020c0 memw +40003485: 8662e2 s32i a14, a2, 0x218 +40003488: 0020c0 memw +4000348b: 8c2282 l32i a8, a2, 0x230 +4000348e: 13e8f6 bgeui a8, 128, 400034a5 <_c_0x000fffff+0x5d> +40003491: 045356 bnez a3, 400034da <_c_0x000fffff+0x92> +40003494: e8a3a2 movi a10, 0x3e8 +40003497: ff91e5 call8 40002db4 +4000349a: 0020c0 memw +4000349d: 8c2292 l32i a9, a2, 0x230 +400034a0: f03d nop.n +400034a2: ebe9b6 bltui a9, 128, 40003491 <_c_0x000fffff+0x49> +400034a5: ffe8a1 l32r a10, 40003448 <_c_0x000fffff> +400034a8: 0020c0 memw +400034ab: 8a2282 l32i a8, a2, 0x228 +400034ae: 1088a0 and a8, a8, a10 +400034b1: 0020c0 memw +400034b4: 8b2292 l32i a9, a2, 0x22c +400034b7: 1099a0 and a9, a9, a10 +400034ba: 0020c0 memw +400034bd: 8622a2 l32i a10, a2, 0x218 +400034c0: 10aa40 and a10, a10, a4 +400034c3: 0020c0 memw +400034c6: 8662a2 s32i a10, a2, 0x218 +400034c9: 289a add.n a2, a8, a9 +400034cb: 080c movi.n a8, 0 +400034cd: 212120 srai a2, a2, 1 +400034d0: 532280 max a2, a2, a8 +400034d3: 223b addi.n a2, a2, 3 +400034d5: f01d retw.n +400034d7: 000000 ill +400034da: 020c movi.n a2, 0 +400034dc: f01d retw.n + ... + +400034e0 <_c_0x00060000>: +400034e0: 060000 excw + ... + +400034e4 <_c_0xfff9ffff>: +400034e4: ff .byte 0xff +400034e5: ff .byte 0xff +400034e6: fff9 s32i.n a15, a15, 60 + +400034e8 : +400034e8: 004136 entry a1, 32 +400034eb: ffb861 l32r a6, 400033cc <_c_0x5ffffe00> +400034ee: 115200 slli a5, a2, 16 +400034f1: 556a add.n a5, a5, a6 +400034f3: 0020c0 memw +400034f6: 856532 s32i a3, a5, 0x214 +400034f9: fff991 l32r a9, 400034e0 <_c_0x00060000> +400034fc: 0020c0 memw +400034ff: 882582 l32i a8, a5, 0x220 +40003502: 208890 or a8, a8, a9 +40003505: 0020c0 memw +40003508: 886582 s32i a8, a5, 0x220 +4000350b: fff661 l32r a6, 400034e4 <_c_0xfff9ffff> +4000350e: 0020c0 memw +40003511: 882542 l32i a4, a5, 0x220 +40003514: 104460 and a4, a4, a6 +40003517: 0020c0 memw +4000351a: 886542 s32i a4, a5, 0x220 +4000351d: f01d retw.n + ... + +40003520 <_c_0xfffc0fff>: +40003520: ff .byte 0xff +40003521: 0f .byte 0xf +40003522: fffc bnez.n a15, 40003565 <_X_uart_init+0x31> + +40003524 <_c_0x00009000>: +40003524: 009000 all4 b0, b0:b1:b2:b3 + ... + +40003528 <_c_0x00020000>: +40003528: 020000 andb b0, b0, b0 + ... + +4000352c <_c_13000000>: +4000352c: c65d40 excw + ... + +40003530 <_c_0x08000000>: +40003530: 000000 ill +40003533: 3608 l32i.n a0, a6, 12 + +40003534 <_X_uart_init>: +40003534: 004136 entry a1, 32 +40003537: 0b9216 beqz a2, 400035f4 <_l_35f4> +4000353a: fff9d1 l32r a13, 40003520 <_c_0xfffc0fff> +4000353d: fff9c1 l32r a12, 40003524 <_c_0x00009000> +40003540: f6fc91 l32r a9, 40001130 <_c_0x60003e00> +40003543: 0020c0 memw +40003546: cf29b2 l32i a11, a9, 0x33c +40003549: f03d nop.n +4000354b: 10bbd0 and a11, a11, a13 +4000354e: 20bbc0 or a11, a11, a12 +40003551: 0020c0 memw +40003554: cf69b2 s32i a11, a9, 0x33c +40003557: fff4a1 l32r a10, 40003528 <_c_0x00020000> +4000355a: 0020c0 memw +4000355d: f02982 l32i a8, a9, 0x3c0 +40003560: 2088a0 or a8, a8, a10 +40003563: 0020c0 memw +40003566: f06982 s32i a8, a9, 0x3c0 +40003569: fe3831 l32r a3, 40002e4c <_st_0x3fffdb10_uartdev> +4000356c: fff0b1 l32r a11, 4000352c <_c_13000000> +4000356f: 03c8 l32i.n a12, a3, 0 +40003571: 02ad mov.n a10, a2 +40003573: c2bbc0 quou a11, a11, a12 +40003576: f4b0b0 extui a11, a11, 0, 16 +40003579: fff6e5 call8 400034e8 +4000357c: 3398 l32i.n a9, a3, 12 +4000357e: ff93f1 l32r a15, 400033cc <_c_0x5ffffe00> +40003581: 11e200 slli a14, a2, 16 +40003584: 4388 l32i.n a8, a3, 16 +40003586: eefa add.n a14, a14, a15 +40003588: 23f8 l32i.n a15, a3, 8 +4000358a: 1188c0 slli a8, a8, 4 +4000358d: 20ff90 or a15, a15, a9 +40003590: 1398 l32i.n a9, a3, 4 +40003592: 20ff80 or a15, a15, a8 +40003595: 1199e0 slli a9, a9, 2 +40003598: ffe681 l32r a8, 40003530 <_c_0x08000000> +4000359b: 20ff90 or a15, a15, a9 +4000359e: 20ff80 or a15, a15, a8 +400035a1: 0020c0 memw +400035a4: 886ef2 s32i a15, a14, 0x220 +400035a7: ffced1 l32r a13, 400034e0 <_c_0x00060000> +400035aa: 0020c0 memw +400035ad: 882ec2 l32i a12, a14, 0x220 +400035b0: 20ccd0 or a12, a12, a13 +400035b3: 0020c0 memw +400035b6: 886ec2 s32i a12, a14, 0x220 +400035b9: ffcab1 l32r a11, 400034e4 <_c_0xfff9ffff> +400035bc: 0020c0 memw +400035bf: 882ea2 l32i a10, a14, 0x220 +400035c2: 10aab0 and a10, a10, a11 +400035c5: 0020c0 memw +400035c8: 886ea2 s32i a10, a14, 0x220 +400035cb: 280392 l8ui a9, a3, 40 +400035ce: 649090 extui a9, a9, 0, 7 +400035d1: 0020c0 memw +400035d4: 896e92 s32i a9, a14, 0x224 +400035d7: ff7e81 l32r a8, 400033d0 <_c_0x0000ffff> +400035da: 0020c0 memw +400035dd: 846e82 s32i a8, a14, 0x210 +400035e0: 1f0c movi.n a15, 1 +400035e2: 0020c0 memw +400035e5: 832ed2 l32i a13, a14, 0x20c +400035e8: 20ddf0 or a13, a13, a15 +400035eb: 0020c0 memw +400035ee: 836ed2 s32i a13, a14, 0x20c +400035f1: f01d retw.n + ... + +400035f4 <_l_35f4>: +400035f4: 7d7c movi.n a13, -9 +400035f6: f7bf91 l32r a9, 400014f4 <_c_0x60008e00> +400035f9: 0020c0 memw +400035fc: a229c2 l32i a12, a9, 0x288 +400035ff: 10ccd0 and a12, a12, a13 +40003602: 0020c0 memw +40003605: a269c2 s32i a12, a9, 0x288 +40003608: f7bab1 l32r a11, 400014f0 <_c_0xffff8fff> +4000360b: 0020c0 memw +4000360e: a229a2 l32i a10, a9, 0x288 +40003611: 10aab0 and a10, a10, a11 +40003614: 0020c0 memw +40003617: a269a2 s32i a10, a9, 0x288 +4000361a: 0020c0 memw +4000361d: a22982 l32i a8, a9, 0x288 +40003620: 0020c0 memw +40003623: a26982 s32i a8, a9, 0x288 +40003626: ffcfc6 j 40003569 <_X_uart_init+0x35> +40003629: 000000 ill + +4000362c : +4000362c: 004136 entry a1, 32 +4000362f: fe0741 l32r a4, 40002e4c <_st_0x3fffdb10_uartdev> +40003632: 0020f0 nop +40003635: 190442 l8ui a4, a4, 25 +40003638: ff6581 l32r a8, 400033cc <_c_0x5ffffe00> +4000363b: 114400 slli a4, a4, 16 +4000363e: 448a add.n a4, a4, a8 +40003640: 0020c0 memw +40003643: 872432 l32i a3, a4, 0x21c +40003646: 0e7377 bbci a3, 23, 40003658 +40003649: 088076 loop a0, 40003655 +4000364c: 0020c0 memw +4000364f: 872482 l32i a8, a4, 0x21c +40003652: 027877 bbci a8, 23, 40003658 +40003655: fffc06 j 40003649 +40003658: 0020c0 memw +4000365b: 806422 s32i a2, a4, 0x200 +4000365e: 020c movi.n a2, 0 +40003660: f01d retw.n + ... + +40003664 <_X_uart_tx_one_char2>: +40003664: 004136 entry a1, 32 +40003667: fdf941 l32r a4, 40002e4c <_st_0x3fffdb10_uartdev> +4000366a: 0020f0 nop +4000366d: 180442 l8ui a4, a4, 24 +40003670: ff5781 l32r a8, 400033cc <_c_0x5ffffe00> +40003673: 114400 slli a4, a4, 16 +40003676: 448a add.n a4, a4, a8 +40003678: 0020c0 memw +4000367b: 872432 l32i a3, a4, 0x21c +4000367e: 0e7377 bbci a3, 23, 40003690 <_X_uart_tx_one_char2+0x2c> +40003681: 088076 loop a0, 4000368d <_X_uart_tx_one_char2+0x29> +40003684: 0020c0 memw +40003687: 872482 l32i a8, a4, 0x21c +4000368a: 027877 bbci a8, 23, 40003690 <_X_uart_tx_one_char2+0x2c> +4000368d: fffc06 j 40003681 <_X_uart_tx_one_char2+0x1d> +40003690: 0020c0 memw +40003693: 806422 s32i a2, a4, 0x200 +40003696: 020c movi.n a2, 0 +40003698: f01d retw.n + ... + +4000369c <_X_uart_wait_tx_empty>: +4000369c: 004136 entry a1, 32 +4000369f: 115200 slli a5, a2, 16 +400036a2: ff4a81 l32r a8, 400033cc <_c_0x5ffffe00> +400036a5: f43c21 l32r a2, 40000798 <_c_0x00ff0000> +400036a8: 805580 add a5, a5, a8 +400036ab: 0020c0 memw +400036ae: 872532 l32i a3, a5, 0x21c +400036b1: 100237 bnone a2, a3, 400036c5 <_X_uart_wait_tx_empty+0x29> +400036b4: f03d nop.n +400036b6: 088076 loop a0, 400036c2 <_X_uart_wait_tx_empty+0x26> +400036b9: 0020c0 memw +400036bc: 872592 l32i a9, a5, 0x21c +400036bf: 020927 bnone a9, a2, 400036c5 <_X_uart_wait_tx_empty+0x29> +400036c2: fffb86 j 400036b4 <_X_uart_wait_tx_empty+0x18> +400036c5: f01d retw.n +400036c7: 413600 srli a3, a0, 6 +400036ca: e02100 subx4 a2, a1, a0 +400036cd: 52fd excw +400036cf: 22ffa0 orb b15, b15, b10 +400036d2: 311802 l16ui a0, a8, 98 +400036d5: 3e .byte 0x3e +400036d6: ff .byte 0xff +400036d7: 112200 slli a2, a2, 16 +400036da: 802230 add a2, a2, a3 +400036dd: 088076 loop a0, 400036e9 <_X_uart_wait_tx_empty+0x4d> +400036e0: 0020c0 memw +400036e3: 872232 l32i a3, a2, 0x21c +400036e6: 028537 bany a5, a3, 400036ec <_X_uart_wait_tx_empty+0x50> +400036e9: fffc06 j 400036dd <_X_uart_wait_tx_empty+0x41> +400036ec: 0020c0 memw +400036ef: 802222 l32i a2, a2, 0x200 +400036f2: 742020 extui a2, a2, 0, 8 +400036f5: f01d retw.n +400036f7: 413600 srli a3, a0, 6 +400036fa: d44100 extui a4, a0, 1, 14 +400036fd: 42fd excw +400036ff: 811804 excw +40003702: 00ff32 s32ri a3, a15, 0 +40003705: 8a1144 excw +40003708: 20c044 excw +4000370b: 243200 extui a3, a0, 2, 3 +4000370e: 303087 bltu a0, a8, 40003742 <_X_uart_int_handler+0x1a> +40003711: b38c74 excw +40003714: 0020c0 memw +40003717: 802482 l32i a8, a4, 0x200 +4000371a: 004282 s8i a8, a2, 0 +4000371d: 020c movi.n a2, 0 +4000371f: f01d retw.n +40003721: 120c movi.n a2, 1 +40003723: f01d retw.n +40003725: 000000 ill + +40003728 <_X_uart_int_handler>: +40003728: 004136 entry a1, 32 +4000372b: fdc861 l32r a6, 40002e4c <_st_0x3fffdb10_uartdev> +4000372e: 180c movi.n a8, 1 +40003730: 180662 l8ui a6, a6, 24 +40003733: ff2671 l32r a7, 400033cc <_c_0x5ffffe00> +40003736: 115600 slli a5, a6, 16 +40003739: 557a add.n a5, a5, a7 +4000373b: 0020c0 memw +4000373e: 822532 l32i a3, a5, 0x208 +40003741: 546307 bbci a3, 0, 40003799 <_X_uart_int_handler+0x71> +40003744: ffa092 movi a9, 255 +40003747: 0020c0 memw +4000374a: 846582 s32i a8, a5, 0x210 +4000374d: 116600 slli a6, a6, 16 +40003750: 667a add.n a6, a6, a7 +40003752: 0020c0 memw +40003755: 8726a2 l32i a10, a6, 0x21c +40003758: 3d09a7 bnone a9, a10, 40003799 <_X_uart_int_handler+0x71> +4000375b: 1258 l32i.n a5, a2, 4 +4000375d: 290c movi.n a9, 2 +4000375f: d80c movi.n a8, 13 +40003761: 2b8076 loop a0, 40003790 <_X_uart_int_handler+0x68> +40003764: 0020c0 memw +40003767: 8026a2 l32i a10, a6, 0x200 +4000376a: 74a0a0 extui a10, a10, 0, 8 +4000376d: 0045a2 s8i a10, a5, 0 +40003770: 0278 l32i.n a7, a2, 0 +40003772: 1258 l32i.n a5, a2, 4 +40003774: 01d7b2 addmi a11, a7, 0x100 +40003777: 551b addi.n a5, a5, 1 +40003779: 171a87 beq a10, a8, 40003794 <_X_uart_int_handler+0x6c> +4000377c: 1259 s32i.n a5, a2, 4 +4000377e: 0395b7 bne a5, a11, 40003785 <_X_uart_int_handler+0x5d> +40003781: 075d mov.n a5, a7 +40003783: 1279 s32i.n a7, a2, 4 +40003785: 0020c0 memw +40003788: 8726a2 l32i a10, a6, 0x21c +4000378b: 74a0a0 extui a10, a10, 0, 8 +4000378e: 7a8c beqz.n a10, 40003799 <_X_uart_int_handler+0x71> +40003790: fff346 j 40003761 <_X_uart_int_handler+0x39> +40003793: 429900 xorb b9, b9, b0 +40003796: fff886 j 4000377c <_X_uart_int_handler+0x54> +40003799: f01d retw.n +4000379b: 413600 srli a3, a0, 6 +4000379e: 23b600 sext a11, a6, 7 +400037a1: 140c26 beqi a12, -1, 400037b9 <_X_uart_int_handler+0x91> +400037a4: d50c movi.n a5, 13 +400037a6: 02ad mov.n a10, a2 +400037a8: fff4e5 call8 400036f8 <_X_uart_wait_tx_empty+0x5c> +400037ab: 071a66 bnei a10, 1, 400037b6 <_X_uart_int_handler+0x8e> +400037ae: 02ad mov.n a10, a2 +400037b0: fff465 call8 400036f8 <_X_uart_wait_tx_empty+0x5c> +400037b3: f71a26 beqi a10, 1, 400037ae <_X_uart_int_handler+0x86> +400037b6: 0002a2 l8ui a10, a2, 0 +400037b9: 0c9a26 beqi a10, 10, 400037c9 <_X_uart_int_handler+0xa1> +400037bc: 091a57 beq a10, a5, 400037c9 <_X_uart_int_handler+0xa1> +400037bf: 221b addi.n a2, a2, 1 +400037c1: 441b addi.n a4, a4, 1 +400037c3: 744040 extui a4, a4, 0, 8 +400037c6: dc9347 bne a3, a4, 400037a6 <_X_uart_int_handler+0x7e> +400037c9: 0d0c movi.n a13, 0 +400037cb: 0142d2 s8i a13, a2, 1 +400037ce: 0d2d mov.n a2, a13 +400037d0: f01d retw.n + ... + +400037d4 <_X_send_packet>: +400037d4: 004136 entry a1, 32 +400037d7: c0a0a2 movi a10, 192 +400037da: ffe8a5 call8 40003664 <_X_uart_tx_one_char2> +400037dd: 330b addi.n a3, a3, -1 +400037df: 3d0326 beqi a3, -1, 40003820 <_X_send_packet+0x4c> +400037e2: dba052 movi a5, 219 +400037e5: c0a042 movi a4, 192 +400037e8: 000306 j 400037f8 <_X_send_packet+0x24> +400037eb: 650000 extui a0, a0, 16, 7 +400037ee: 1bffe7 bbsi a15, 30, 4000380d <_X_send_packet+0x39> +400037f1: c33222 excw +400037f4: ff .byte 0xff +400037f5: 027396 bltz a3, 40003820 <_X_send_packet+0x4c> +400037f8: 0002a2 l8ui a10, a2, 0 +400037fb: 111a47 beq a10, a4, 40003810 <_X_send_packet+0x3c> +400037fe: eb9a57 bne a10, a5, 400037ed <_X_send_packet+0x19> +40003801: dba0a2 movi a10, 219 +40003804: ffe5e5 call8 40003664 <_X_uart_tx_one_char2> +40003807: dda0a2 movi a10, 221 +4000380a: ffe5a5 call8 40003664 <_X_uart_tx_one_char2> +4000380d: fff7c6 j 400037f0 <_X_send_packet+0x1c> +40003810: dba0a2 movi a10, 219 +40003813: ffe525 call8 40003664 <_X_uart_tx_one_char2> +40003816: dca0a2 movi a10, 220 +40003819: ffe4a5 call8 40003664 <_X_uart_tx_one_char2> +4000381c: fff406 j 400037f0 <_X_send_packet+0x1c> +4000381f: a0a200 addx4 a10, a2, a0 +40003822: e425c0 extui a2, a12, 5, 15 +40003825: ff .byte 0xff +40003826: f01d retw.n + +40003828 <_X_SendMsg>: +40003828: 004136 entry a1, 32 +4000382b: 03bd mov.n a11, a3 +4000382d: 02ad mov.n a10, a2 +4000382f: fffa65 call8 400037d4 <_X_send_packet> +40003832: 020c movi.n a2, 0 +40003834: f01d retw.n +40003836: c80000 excw +40003839: ffc8 l32i.n a12, a15, 60 +4000383b: 3f .byte 0x3f + +4000383c <_X_recv_packet>: +4000383c: 006136 entry a1, 48 +4000383f: 180c movi.n a8, 1 +40003841: 047d mov.n a7, a4 +40003843: fd8261 l32r a6, 40002e4c <_st_0x3fffdb10_uartdev> +40003846: 029d mov.n a9, a2 +40003848: 1199 s32i.n a9, a1, 4 +4000384a: 020c movi.n a2, 0 +4000384c: 004122 s8i a2, a1, 0 +4000384f: d658 l32i.n a5, a6, 52 +40003851: 937840 movnez a7, a8, a4 +40003854: 2179 s32i.n a7, a1, 8 +40003856: 932570 movnez a2, a5, a7 +40003859: c0a072 movi a7, 192 +4000385c: 1cc652 addi a5, a6, 28 +4000385f: 0d3416 beqz a4, 40003936 <_X_recv_packet+0xfa> +40003862: c698 l32i.n a9, a6, 48 +40003864: 1e2966 bnei a9, 2, 40003886 <_X_recv_packet+0x4a> +40003867: 000f86 j 400038a9 <_X_recv_packet+0x6d> +4000386a: 648c beqz.n a4, 40003874 <_X_recv_packet+0x38> +4000386c: c6e8 l32i.n a14, a6, 48 +4000386e: 024e66 bnei a14, 4, 40003874 <_X_recv_packet+0x38> +40003871: 002406 j 40003905 <_X_recv_packet+0xc9> +40003874: 0001a2 l8ui a10, a1, 0 +40003877: 1d1a77 beq a10, a7, 40003898 <_X_recv_packet+0x5c> +4000387a: dba0f2 movi a15, 219 +4000387d: 511af7 beq a10, a15, 400038d2 <_X_recv_packet+0x96> +40003880: 02a237 bge a2, a3, 40003886 <_X_recv_packet+0x4a> +40003883: 002746 j 40003924 <_X_recv_packet+0xe8> +40003886: 05ad mov.n a10, a5 +40003888: 01bd mov.n a11, a1 +4000388a: 001165 call8 400039a0 <_X_uart_rx_readbuff> +4000388d: fd9a16 beqz a10, 4000386a <_X_recv_packet+0x2e> +40003890: ff2416 beqz a4, 40003886 <_X_recv_packet+0x4a> +40003893: 020c movi.n a2, 0 +40003895: f01d retw.n +40003897: a21600 muluh a1, a6, a0 +4000389a: fe .byte 0xfe +4000389b: 0e7416 beqz a4, 40003986 <_X_recv_packet+0x14a> +4000389e: 280c movi.n a8, 2 +400038a0: 090c movi.n a9, 0 +400038a2: d699 s32i.n a9, a6, 52 +400038a4: c689 s32i.n a8, a6, 48 +400038a6: f01d retw.n +400038a8: 05ad00 extui a10, a0, 29, 1 +400038ab: 01bd mov.n a11, a1 +400038ad: 000f25 call8 400039a0 <_X_uart_rx_readbuff> +400038b0: 4adc bnez.n a10, 400038c8 <_X_recv_packet+0x8c> +400038b2: 0001a2 l8ui a10, a1, 0 +400038b5: 0020f0 nop +400038b8: c0ba70 sub a11, a10, a7 +400038bb: 09db16 beqz a11, 4000395c <_X_recv_packet+0x120> +400038be: 05ad mov.n a10, a5 +400038c0: 01bd mov.n a11, a1 +400038c2: 000de5 call8 400039a0 <_X_uart_rx_readbuff> +400038c5: fe9a16 beqz a10, 400038b2 <_X_recv_packet+0x76> +400038c8: 0001c2 l8ui a12, a1, 0 +400038cb: c49c77 bne a12, a7, 40003893 <_X_recv_packet+0x57> +400038ce: ffed06 j 40003886 <_X_recv_packet+0x4a> +400038d1: e4bc00 extui a11, a0, 12, 15 +400038d4: c698 l32i.n a9, a6, 48 +400038d6: 0101d2 l8ui a13, a1, 1 +400038d9: 023966 bnei a9, 3, 400038df <_X_recv_packet+0xa3> +400038dc: 002186 j 40003966 <_X_recv_packet+0x12a> +400038df: 0041d2 s8i a13, a1, 0 +400038e2: 24afe2 movi a14, -220 +400038e5: 0001a2 l8ui a10, a1, 0 +400038e8: 21d8 l32i.n a13, a1, 8 +400038ea: 3c0c movi.n a12, 3 +400038ec: 939cd0 movnez a9, a12, a13 +400038ef: c699 s32i.n a9, a6, 48 +400038f1: eaea add.n a14, a10, a14 +400038f3: 0a2ef6 bgeui a14, 2, 40003901 <_X_recv_packet+0xc5> +400038f6: ffd0b1 l32r a11, 40003838 <_X_SendMsg+0x10> +400038f9: aaba add.n a10, a10, a11 +400038fb: 7c0aa2 l8ui a10, a10, 124 +400038fe: 0041a2 s8i a10, a1, 0 +40003901: ffdec6 j 40003880 <_X_recv_packet+0x44> +40003904: a0e200 addx4 a14, a2, a0 +40003907: f2db addi.n a15, a2, 13 +40003909: f20001 l32r a0, 4000010c <_WindowOverflowHandler+0xc> +4000390c: e20141 l32r a4, 3fffc110 <_start-0x3ef0> +4000390f: 560041 l32r a4, 3ffd9110 <_start-0x26ef0> +40003912: adfbf4 excw +40003915: b11005 call0 3ffb4a18 <_start-0x4b5e8> +40003918: 086520 excw +4000391b: 1a2600 depbits a0, a6, 1, 3 +4000391e: 9859 s32i.n a5, a8, 36 +40003920: ef46c6 j 3ffff63f <_start-0x9c1> +40003923: ff .byte 0xff +40003924: 1198 l32i.n a9, a1, 4 +40003926: 929a add.n a9, a2, a9 +40003928: 0049a2 s8i a10, a9, 0 +4000392b: d688 l32i.n a8, a6, 52 +4000392d: 221b addi.n a2, a2, 1 +4000392f: 881b addi.n a8, a8, 1 +40003931: d689 s32i.n a8, a6, 52 +40003933: ffd3c6 j 40003886 <_X_recv_packet+0x4a> +40003936: 05ad mov.n a10, a5 +40003938: 01bd mov.n a11, a1 +4000393a: 000665 call8 400039a0 <_X_uart_rx_readbuff> +4000393d: 081a26 beqi a10, 1, 40003949 <_X_recv_packet+0x10d> +40003940: 0001a2 l8ui a10, a1, 0 +40003943: c0aa70 sub a10, a10, a7 +40003946: f3ca16 beqz a10, 40003886 <_X_recv_packet+0x4a> +40003949: 05ad mov.n a10, a5 +4000394b: 01bd mov.n a11, a1 +4000394d: 000525 call8 400039a0 <_X_uart_rx_readbuff> +40003950: f51a26 beqi a10, 1, 40003949 <_X_recv_packet+0x10d> +40003953: 0001b2 l8ui a11, a1, 0 +40003956: ef9b77 bne a11, a7, 40003949 <_X_recv_packet+0x10d> +40003959: ffca46 j 40003886 <_X_recv_packet+0x4a> +4000395c: 3d0c movi.n a13, 3 +4000395e: c6d9 s32i.n a13, a6, 48 +40003960: f2fb56 bnez a11, 40003893 <_X_recv_packet+0x57> +40003963: ffc7c6 j 40003886 <_X_recv_packet+0x4a> +40003966: 05ad mov.n a10, a5 +40003968: 01bd mov.n a11, a1 +4000396a: 4e0c movi.n a14, 4 +4000396c: c6e9 s32i.n a14, a6, 48 +4000396e: 000325 call8 400039a0 <_X_uart_rx_readbuff> +40003971: aa1a66 bnei a10, 1, 4000391f <_X_recv_packet+0xe3> +40003974: ffc6c6 j 40003893 <_X_recv_packet+0x57> +40003977: ad0000 excw +4000397a: 01bd05 call0 4000554c <_X_slc_init_attach+0x64> +4000397d: 000225 call8 400039a0 <_X_uart_rx_readbuff> +40003980: f51a26 beqi a10, 1, 40003979 <_X_recv_packet+0x13d> +40003983: ffe606 j 4000391f <_X_recv_packet+0xe3> +40003986: f01d retw.n + +40003988 <_X_RcvMsg>: +40003988: 004136 entry a1, 32 +4000398b: 04cd mov.n a12, a4 +4000398d: 03bd mov.n a11, a3 +4000398f: 02ad mov.n a10, a2 +40003991: ffeaa5 call8 4000383c <_X_recv_packet> +40003994: 2a8c beqz.n a10, 4000399a <_X_RcvMsg+0x12> +40003996: 020c movi.n a2, 0 +40003998: f01d retw.n +4000399a: 120c movi.n a2, 1 +4000399c: f01d retw.n + ... + +400039a0 <_X_uart_rx_readbuff>: +400039a0: 004136 entry a1, 32 +400039a3: 2268 l32i.n a6, a2, 8 +400039a5: 1248 l32i.n a4, a2, 4 +400039a7: 059467 bne a4, a6, 400039b0 <_X_uart_rx_readbuff+0x10> +400039aa: 120c movi.n a2, 1 +400039ac: f01d retw.n +400039ae: 820000 mull a0, a0, a0 +400039b1: 820006 j 3ffe41b5 <_start-0x1be4b> +400039b4: 380043 excw +400039b7: 225802 s16i a0, a8, 68 +400039ba: 01d382 addmi a8, a3, 0x100 +400039bd: 551b addi.n a5, a5, 1 +400039bf: 2259 s32i.n a5, a2, 8 +400039c1: 059587 bne a5, a8, 400039ca <_X_uart_rx_readbuff+0x2a> +400039c4: 2239 s32i.n a3, a2, 8 +400039c6: 020c movi.n a2, 0 +400039c8: f01d retw.n +400039ca: 020c movi.n a2, 0 +400039cc: f01d retw.n +400039ce: 360000 excw +400039d1: 410061 l32r a6, 3ffd3dd4 <_start-0x2c22c> +400039d4: 1e .byte 0x1e +400039d5: 88fd excw +400039d7: 2826b4 excw +400039da: 120c04 excw +400039dd: f01d retw.n +400039df: 01bd00 slli a11, a13, 32 +400039e2: 1cc432 addi a3, a4, 28 +400039e5: 03ad mov.n a10, a3 +400039e7: fffba5 call8 400039a0 <_X_uart_rx_readbuff> +400039ea: ba8c beqz.n a10, 400039f9 <_X_uart_rx_readbuff+0x59> +400039ec: 090c movi.n a9, 0 +400039ee: 004292 s8i a9, a2, 0 +400039f1: 092d mov.n a2, a9 +400039f3: b429 s32i.n a2, a4, 44 +400039f5: f01d retw.n +400039f7: ad0000 excw +400039fa: 019203 excw +400039fd: 01bd00 slli a11, a13, 32 +40003a00: 004292 s8i a9, a2, 0 +40003a03: 221b addi.n a2, a2, 1 +40003a05: fff9a5 call8 400039a0 <_X_uart_rx_readbuff> +40003a08: feda16 beqz a10, 400039f9 <_X_uart_rx_readbuff+0x59> +40003a0b: fff746 j 400039ec <_X_uart_rx_readbuff+0x4c> +40003a0e: 360000 excw +40003a11: 210041 l32r a4, 3ffcbe14 <_start-0x341ec> +40003a14: 0e .byte 0xe +40003a15: 1dfd excw +40003a17: 4e00f0 excw + +40003a18 <_c_0x60004e00>: +40003a18: 004e00 break 14, 0 +40003a1b: 413660 srli a3, a6, 6 + +40003a1c <_X_SelectSpiFunction>: +40003a1c: 004136 entry a1, 32 +40003a1f: 787c movi.n a8, -9 +40003a21: f6b371 l32r a7, 400014f0 <_c_0xffff8fff> +40003a24: f5c641 l32r a4, 4000113c <_c_0x1000> +40003a27: fffc51 l32r a5, 40003a18 <_c_0x60004e00> +40003a2a: f6b261 l32r a6, 400014f4 <_c_0x60008e00> +40003a2d: 123216 beqz a2, 40003b54 <_X_SelectSpiFunction+0x138> +40003a30: 0020c0 memw +40003a33: 8c2632 l32i a3, a6, 0x230 +40003a36: 103380 and a3, a3, a8 +40003a39: 0020c0 memw +40003a3c: 8c6632 s32i a3, a6, 0x230 +40003a3f: 0020c0 memw +40003a42: 8c2622 l32i a2, a6, 0x230 +40003a45: 102270 and a2, a2, a7 +40003a48: 0020c0 memw +40003a4b: 8c6622 s32i a2, a6, 0x230 +40003a4e: 0020c0 memw +40003a51: 8c26f2 l32i a15, a6, 0x230 +40003a54: 20ff40 or a15, a15, a4 +40003a57: 0020c0 memw +40003a5a: 8c66f2 s32i a15, a6, 0x230 +40003a5d: 0020c0 memw +40003a60: 8d26e2 l32i a14, a6, 0x234 +40003a63: 10ee80 and a14, a14, a8 +40003a66: 0020c0 memw +40003a69: 8d66e2 s32i a14, a6, 0x234 +40003a6c: 0020c0 memw +40003a6f: 8d26d2 l32i a13, a6, 0x234 +40003a72: 10dd70 and a13, a13, a7 +40003a75: 0020c0 memw +40003a78: 8d66d2 s32i a13, a6, 0x234 +40003a7b: 0020c0 memw +40003a7e: 8d26c2 l32i a12, a6, 0x234 +40003a81: 20cc40 or a12, a12, a4 +40003a84: 0020c0 memw +40003a87: 8d66c2 s32i a12, a6, 0x234 +40003a8a: 0020c0 memw +40003a8d: 8e26b2 l32i a11, a6, 0x238 +40003a90: 10bb80 and a11, a11, a8 +40003a93: 0020c0 memw +40003a96: 8e66b2 s32i a11, a6, 0x238 +40003a99: 0020c0 memw +40003a9c: 8e26a2 l32i a10, a6, 0x238 +40003a9f: 10aa70 and a10, a10, a7 +40003aa2: 0020c0 memw +40003aa5: 8e66a2 s32i a10, a6, 0x238 +40003aa8: 0020c0 memw +40003aab: 8e2692 l32i a9, a6, 0x238 +40003aae: 209940 or a9, a9, a4 +40003ab1: 0020c0 memw +40003ab4: 8e6692 s32i a9, a6, 0x238 +40003ab7: 0020c0 memw +40003aba: 8f2632 l32i a3, a6, 0x23c +40003abd: 103380 and a3, a3, a8 +40003ac0: 0020c0 memw +40003ac3: 8f6632 s32i a3, a6, 0x23c +40003ac6: 0020c0 memw +40003ac9: 8f2622 l32i a2, a6, 0x23c +40003acc: 102270 and a2, a2, a7 +40003acf: 0020c0 memw +40003ad2: 8f6622 s32i a2, a6, 0x23c +40003ad5: 0020c0 memw +40003ad8: 8f26f2 l32i a15, a6, 0x23c +40003adb: 20ff40 or a15, a15, a4 +40003ade: 0020c0 memw +40003ae1: 8f66f2 s32i a15, a6, 0x23c +40003ae4: 0020c0 memw +40003ae7: 9026e2 l32i a14, a6, 0x240 +40003aea: 10ee80 and a14, a14, a8 +40003aed: 0020c0 memw +40003af0: 9066e2 s32i a14, a6, 0x240 +40003af3: 0020c0 memw +40003af6: 9026d2 l32i a13, a6, 0x240 +40003af9: 10dd70 and a13, a13, a7 +40003afc: 0020c0 memw +40003aff: 9066d2 s32i a13, a6, 0x240 +40003b02: 0020c0 memw +40003b05: 9026c2 l32i a12, a6, 0x240 +40003b08: 20cc40 or a12, a12, a4 +40003b0b: 0020c0 memw +40003b0e: 9066c2 s32i a12, a6, 0x240 +40003b11: 0020c0 memw +40003b14: 9226b2 l32i a11, a6, 0x248 +40003b17: 10bb80 and a11, a11, a8 +40003b1a: 0020c0 memw +40003b1d: 9266b2 s32i a11, a6, 0x248 +40003b20: 0020c0 memw +40003b23: 9226a2 l32i a10, a6, 0x248 +40003b26: 10aa70 and a10, a10, a7 +40003b29: 0020c0 memw +40003b2c: 9266a2 s32i a10, a6, 0x248 +40003b2f: 0020c0 memw +40003b32: 922692 l32i a9, a6, 0x248 +40003b35: 209940 or a9, a9, a4 +40003b38: 0020c0 memw +40003b3b: 926692 s32i a9, a6, 0x248 +40003b3e: f2fe31 l32r a3, 40000738 <_c_0x80000000> +40003b41: 0020c0 memw +40003b44: 492522 l32i a2, a5, 0x124 +40003b47: 202230 or a2, a2, a3 +40003b4a: 0020c0 memw +40003b4d: 496522 s32i a2, a5, 0x124 +40003b50: f01d retw.n +40003b52: c00000 sub a0, a0, a0 +40003b55: a20020 muluh a0, a0, a2 +40003b58: 809826 beqi a8, 10, 40003adc <_X_SelectSpiFunction+0xc0> +40003b5b: 10aa add.n a1, a0, a10 +40003b5d: 0020c0 memw +40003b60: 9866a2 s32i a10, a6, 0x260 +40003b63: 0020c0 memw +40003b66: 982692 l32i a9, a6, 0x260 +40003b69: 109970 and a9, a9, a7 +40003b6c: 0020c0 memw +40003b6f: 986692 s32i a9, a6, 0x260 +40003b72: 0020c0 memw +40003b75: 982632 l32i a3, a6, 0x260 +40003b78: 203340 or a3, a3, a4 +40003b7b: 0020c0 memw +40003b7e: 986632 s32i a3, a6, 0x260 +40003b81: 0020c0 memw +40003b84: 992622 l32i a2, a6, 0x264 +40003b87: 102280 and a2, a2, a8 +40003b8a: 0020c0 memw +40003b8d: 996622 s32i a2, a6, 0x264 +40003b90: 0020c0 memw +40003b93: 9926f2 l32i a15, a6, 0x264 +40003b96: 10ff70 and a15, a15, a7 +40003b99: 0020c0 memw +40003b9c: 9966f2 s32i a15, a6, 0x264 +40003b9f: 0020c0 memw +40003ba2: 9926e2 l32i a14, a6, 0x264 +40003ba5: 20ee40 or a14, a14, a4 +40003ba8: 0020c0 memw +40003bab: 9966e2 s32i a14, a6, 0x264 +40003bae: 0020c0 memw +40003bb1: 9a26d2 l32i a13, a6, 0x268 +40003bb4: 10dd80 and a13, a13, a8 +40003bb7: 0020c0 memw +40003bba: 9a66d2 s32i a13, a6, 0x268 +40003bbd: 0020c0 memw +40003bc0: 9a26c2 l32i a12, a6, 0x268 +40003bc3: 10cc70 and a12, a12, a7 +40003bc6: 0020c0 memw +40003bc9: 9a66c2 s32i a12, a6, 0x268 +40003bcc: 0020c0 memw +40003bcf: 9a26b2 l32i a11, a6, 0x268 +40003bd2: 20bb40 or a11, a11, a4 +40003bd5: 0020c0 memw +40003bd8: 9a66b2 s32i a11, a6, 0x268 +40003bdb: 0020c0 memw +40003bde: 9526a2 l32i a10, a6, 0x254 +40003be1: 10aa80 and a10, a10, a8 +40003be4: 0020c0 memw +40003be7: 9566a2 s32i a10, a6, 0x254 +40003bea: 0020c0 memw +40003bed: 952692 l32i a9, a6, 0x254 +40003bf0: 109970 and a9, a9, a7 +40003bf3: 0020c0 memw +40003bf6: 956692 s32i a9, a6, 0x254 +40003bf9: 0020c0 memw +40003bfc: 952632 l32i a3, a6, 0x254 +40003bff: 203340 or a3, a3, a4 +40003c02: 0020c0 memw +40003c05: 956632 s32i a3, a6, 0x254 +40003c08: 0020c0 memw +40003c0b: 962622 l32i a2, a6, 0x258 +40003c0e: 102280 and a2, a2, a8 +40003c11: 0020c0 memw +40003c14: 966622 s32i a2, a6, 0x258 +40003c17: 0020c0 memw +40003c1a: 9626f2 l32i a15, a6, 0x258 +40003c1d: 10ff70 and a15, a15, a7 +40003c20: 0020c0 memw +40003c23: 9666f2 s32i a15, a6, 0x258 +40003c26: 0020c0 memw +40003c29: 9626e2 l32i a14, a6, 0x258 +40003c2c: 20ee40 or a14, a14, a4 +40003c2f: 0020c0 memw +40003c32: 9666e2 s32i a14, a6, 0x258 +40003c35: 0020c0 memw +40003c38: 9726d2 l32i a13, a6, 0x25c +40003c3b: 10dd80 and a13, a13, a8 +40003c3e: 0020c0 memw +40003c41: 9766d2 s32i a13, a6, 0x25c +40003c44: 0020c0 memw +40003c47: 9726c2 l32i a12, a6, 0x25c +40003c4a: 10cc70 and a12, a12, a7 +40003c4d: 0020c0 memw +40003c50: 9766c2 s32i a12, a6, 0x25c +40003c53: 0020c0 memw +40003c56: 9726b2 l32i a11, a6, 0x25c +40003c59: 20bb40 or a11, a11, a4 +40003c5c: 0020c0 memw +40003c5f: 9766b2 s32i a11, a6, 0x25c +40003c62: f2c7a1 l32r a10, 40000780 <_c_0x7fffffff> +40003c65: 0020c0 memw +40003c68: 492592 l32i a9, a5, 0x124 +40003c6b: 1099a0 and a9, a9, a10 +40003c6e: 0020c0 memw +40003c71: 496592 s32i a9, a5, 0x124 +40003c74: f01d retw.n + ... + +40003c78 <_c_0x60002e00>: +40003c78: 002e00 excw +40003c7b: 413660 srli a3, a6, 6 + +40003c7c <_X_SPI_chip_erase>: +40003c7c: 004136 entry a1, 32 +40003c7f: 20a220 or a10, a2, a2 +40003c82: 005865 call8 40004208 <_X_Wait_SPI_Idle> +40003c85: fb4ba1 l32r a10, 400029b4 <_c_0x00400000> +40003c88: fffc91 l32r a9, 40003c78 <_c_0x60002e00> +40003c8b: 0020c0 memw +40003c8e: 8069a2 s32i a10, a9, 0x200 +40003c91: 0020c0 memw +40003c94: 802982 l32i a8, a9, 0x200 +40003c97: d88c beqz.n a8, 40003ca8 <_X_SPI_chip_erase+0x2c> +40003c99: 078076 loop a0, 40003ca4 <_X_SPI_chip_erase+0x28> +40003c9c: 0020c0 memw +40003c9f: 8029b2 l32i a11, a9, 0x200 +40003ca2: 2b8c beqz.n a11, 40003ca8 <_X_SPI_chip_erase+0x2c> +40003ca4: fffc46 j 40003c99 <_X_SPI_chip_erase+0x1d> +40003ca7: 02ad00 andb b10, b13, b0 +40003caa: 0055e5 call8 40004208 <_X_Wait_SPI_Idle> +40003cad: 020c movi.n a2, 0 +40003caf: f01d retw.n +40003cb1: 000000 ill + +40003cb4 <_c_0x00ffffff>: +40003cb4: ff .byte 0xff +40003cb5: ff .byte 0xff +40003cb6: ff .byte 0xff + ... + +40003cb8 <_c_0x01000000>: +40003cb8: 000000 ill +40003cbb: 413601 l32r a0, 3ffd4194 <_start-0x2be6c> + +40003cbc <_XX_unk3cbc>: +40003cbc: 004136 entry a1, 32 +40003cbf: b48030 extui a8, a3, 0, 12 +40003cc2: 288c beqz.n a8, 40003cc8 <_XX_unk3cbc+0xc> +40003cc4: 120c movi.n a2, 1 +40003cc6: f01d retw.n +40003cc8: 02ad mov.n a10, a2 +40003cca: 0053e5 call8 40004208 <_X_Wait_SPI_Idle> +40003ccd: fff9c1 l32r a12, 40003cb4 <_c_0x00ffffff> +40003cd0: ffea91 l32r a9, 40003c78 <_c_0x60002e00> +40003cd3: 10c3c0 and a12, a3, a12 +40003cd6: 0020c0 memw +40003cd9: 8169c2 s32i a12, a9, 0x204 +40003cdc: fff7b1 l32r a11, 40003cb8 <_c_0x01000000> +40003cdf: 0020c0 memw +40003ce2: 8069b2 s32i a11, a9, 0x200 +40003ce5: 0020c0 memw +40003ce8: 8029a2 l32i a10, a9, 0x200 +40003ceb: 7a8c beqz.n a10, 40003cf6 <_XX_unk3cbc+0x3a> +40003ced: 0020c0 memw +40003cf0: 8029d2 l32i a13, a9, 0x200 +40003cf3: ff6d56 bnez a13, 40003ced <_XX_unk3cbc+0x31> +40003cf6: 02ad mov.n a10, a2 +40003cf8: 0050e5 call8 40004208 <_X_Wait_SPI_Idle> +40003cfb: 020c movi.n a2, 0 +40003cfd: f01d retw.n + ... + +40003d00 <_c_0x00800000>: +40003d00: 800000 add a0, a0, a0 +40003d03: 413600 srli a3, a0, 6 +40003d06: a22000 muluh a2, a0, a0 +40003d09: 4fe520 excw +40003d0c: e9b100 excw +40003d0f: ff .byte 0xff +40003d10: ffda91 l32r a9, 40003c78 <_c_0x60002e00> +40003d13: 10b3b0 and a11, a3, a11 +40003d16: 0020c0 memw +40003d19: 8169b2 s32i a11, a9, 0x204 +40003d1c: fff9a1 l32r a10, 40003d00 <_c_0x00800000> +40003d1f: 0020c0 memw +40003d22: 8069a2 s32i a10, a9, 0x200 +40003d25: 0020c0 memw +40003d28: 802982 l32i a8, a9, 0x200 +40003d2b: d88c beqz.n a8, 40003d3c <_c_0x00800000+0x3c> +40003d2d: 078076 loop a0, 40003d38 <_c_0x00800000+0x38> +40003d30: 0020c0 memw +40003d33: 8029c2 l32i a12, a9, 0x200 +40003d36: 2c8c beqz.n a12, 40003d3c <_c_0x00800000+0x3c> +40003d38: fffc46 j 40003d2d <_c_0x00800000+0x2d> +40003d3b: 02ad00 andb b10, b13, b0 +40003d3e: 004ca5 call8 40004208 <_X_Wait_SPI_Idle> +40003d41: 020c movi.n a2, 0 +40003d43: f01d retw.n +40003d45: 000000 ill +40003d48: 000000 ill +40003d4b: 000020 excw + +40003d4c <_c_0x02000000>: +40003d4c: 000000 ill +40003d4f: 413602 excw +40003d52: 380c00 excw +40003d55: 1f8857 bany a8, a5, 40003d78 <_c_0x02000000+0x2c> +40003d58: 4298 l32i.n a9, a2, 16 +40003d5a: e2a390 remu a10, a3, a9 +40003d5d: a5aa add.n a10, a5, a10 +40003d5f: 1539a7 bltu a9, a10, 40003d78 <_c_0x02000000+0x2c> +40003d62: 20a220 or a10, a2, a2 +40003d65: 004a25 call8 40004208 <_X_Wait_SPI_Idle> +40003d68: 0215e6 bgei a5, 1, 40003d6e <_c_0x02000000+0x22> +40003d6b: 002b86 j 40003e1d <_c_0x02000000+0xd1> +40003d6e: ffc261 l32r a6, 40003c78 <_c_0x60002e00> +40003d71: ffd071 l32r a7, 40003cb4 <_c_0x00ffffff> +40003d74: 0003c6 j 40003d87 <_c_0x02000000+0x3b> +40003d77: 120c00 andbc b0, b12, b0 +40003d7a: f01d retw.n +40003d7c: 02ad mov.n a10, a2 +40003d7e: 0048a5 call8 40004208 <_X_Wait_SPI_Idle> +40003d81: 0215e6 bgei a5, 1, 40003d87 <_c_0x02000000+0x3b> +40003d84: 002546 j 40003e1d <_c_0x02000000+0xd1> +40003d87: 10a370 and a10, a3, a7 +40003d8a: 56c5a6 blti a5, 32, 40003de4 <_c_0x02000000+0x98> +40003d8d: ffeed1 l32r a13, 40003d48 <_c_0x00800000+0x48> +40003d90: 20dad0 or a13, a10, a13 +40003d93: 0020c0 memw +40003d96: 8166d2 s32i a13, a6, 0x204 +40003d99: 080c movi.n a8, 0 +40003d9b: 890c movi.n a9, 8 +40003d9d: 11a976 loopgtz a9, 40003db2 <_c_0x02000000+0x66> +40003da0: a09860 addx4 a9, a8, a6 +40003da3: 04b8 l32i.n a11, a4, 0 +40003da5: 0020c0 memw +40003da8: 9069b2 s32i a11, a9, 0x240 +40003dab: 444b addi.n a4, a4, 4 +40003dad: 981b addi.n a9, a8, 1 +40003daf: 748090 extui a8, a9, 0, 8 +40003db2: e0c552 addi a5, a5, -32 +40003db5: 20c332 addi a3, a3, 32 +40003db8: 02ad mov.n a10, a2 +40003dba: 004025 call8 400041bc <_X_SPI_write_enable> +40003dbd: fb7a56 bnez a10, 40003d78 <_c_0x02000000+0x2c> +40003dc0: 0020c0 memw +40003dc3: ffe2f1 l32r a15, 40003d4c <_c_0x02000000> +40003dc6: 8066f2 s32i a15, a6, 0x200 +40003dc9: 0020c0 memw +40003dcc: 8026e2 l32i a14, a6, 0x200 +40003dcf: 0020f0 nop +40003dd2: fa6e16 beqz a14, 40003d7c <_c_0x02000000+0x30> +40003dd5: 088076 loop a0, 40003de1 <_c_0x02000000+0x95> +40003dd8: 0020c0 memw +40003ddb: 802682 l32i a8, a6, 0x200 +40003dde: f9a816 beqz a8, 40003d7c <_c_0x02000000+0x30> +40003de1: fffc06 j 40003dd5 <_c_0x02000000+0x89> +40003de4: 03bd mov.n a11, a3 +40003de6: 8b75b0 depbits a11, a5, 24, 8 +40003de9: 0020c0 memw +40003dec: 8166b2 s32i a11, a6, 0x204 +40003def: 219250 srai a9, a5, 2 +40003df2: 14a050 extui a10, a5, 0, 2 +40003df5: fa9c beqz.n a10, 40003e18 <_c_0x02000000+0xcc> +40003df7: a91b addi.n a10, a9, 1 +40003df9: 080c movi.n a8, 0 +40003dfb: 7490a0 extui a9, a10, 0, 8 +40003dfe: 119976 loopnez a9, 40003e13 <_c_0x02000000+0xc7> +40003e01: a09860 addx4 a9, a8, a6 +40003e04: 04b8 l32i.n a11, a4, 0 +40003e06: 0020c0 memw +40003e09: 9069b2 s32i a11, a9, 0x240 +40003e0c: 444b addi.n a4, a4, 4 +40003e0e: 981b addi.n a9, a8, 1 +40003e10: 748090 extui a8, a9, 0, 8 +40003e13: 050c movi.n a5, 0 +40003e15: ffe7c6 j 40003db8 <_c_0x02000000+0x6c> +40003e18: 09ad mov.n a10, a9 +40003e1a: fff6c6 j 40003df9 <_c_0x02000000+0xad> +40003e1d: 020c movi.n a2, 0 +40003e1f: f01d retw.n +40003e21: 000000 ill + +40003e24 <_XX_unk3e24>: +40003e24: 004136 entry a1, 32 +40003e27: 1288 l32i.n a8, a2, 4 +40003e29: 953a add.n a9, a5, a3 +40003e2b: 03b897 bgeu a8, a9, 40003e32 <_XX_unk3e24+0xe> +40003e2e: 120c movi.n a2, 1 +40003e30: f01d retw.n +40003e32: 02ad mov.n a10, a2 +40003e34: 003d25 call8 40004208 <_X_Wait_SPI_Idle> +40003e37: 0215e6 bgei a5, 1, 40003e3d <_XX_unk3e24+0x19> +40003e3a: 002e06 j 40003ef6 <_XX_unk3e24+0xd2> +40003e3d: ffc2f1 l32r a15, 40003d48 <_c_0x00800000+0x48> +40003e40: f23ee1 l32r a14, 40000738 <_c_0x80000000> +40003e43: ff8dd1 l32r a13, 40003c78 <_c_0x60002e00> +40003e46: 000b46 j 40003e77 <_XX_unk3e24+0x53> +40003e49: 219250 srai a9, a5, 2 +40003e4c: 14a050 extui a10, a5, 0, 2 +40003e4f: 09ea16 beqz a10, 40003ef1 <_XX_unk3e24+0xcd> +40003e52: a91b addi.n a10, a9, 1 +40003e54: 080c movi.n a8, 0 +40003e56: 7490a0 extui a9, a10, 0, 8 +40003e59: 119976 loopnez a9, 40003e6e <_XX_unk3e24+0x4a> +40003e5c: a098d0 addx4 a9, a8, a13 +40003e5f: 0020c0 memw +40003e62: 902992 l32i a9, a9, 0x240 +40003e65: 881b addi.n a8, a8, 1 +40003e67: 0499 s32i.n a9, a4, 0 +40003e69: 748080 extui a8, a8, 0, 8 +40003e6c: 444b addi.n a4, a4, 4 +40003e6e: 00a052 movi a5, 0 +40003e71: 0215e6 bgei a5, 1, 40003e77 <_XX_unk3e24+0x53> +40003e74: 001f86 j 40003ef6 <_XX_unk3e24+0xd2> +40003e77: 4ac5a6 blti a5, 32, 40003ec5 <_XX_unk3e24+0xa1> +40003e7a: 20c3f0 or a12, a3, a15 +40003e7d: 0020c0 memw +40003e80: 816dc2 s32i a12, a13, 0x204 +40003e83: 0020c0 memw +40003e86: 806de2 s32i a14, a13, 0x200 +40003e89: 0020c0 memw +40003e8c: 802db2 l32i a11, a13, 0x200 +40003e8f: eb8c beqz.n a11, 40003ea1 <_XX_unk3e24+0x7d> +40003e91: 078076 loop a0, 40003e9c <_XX_unk3e24+0x78> +40003e94: 0020c0 memw +40003e97: 802d82 l32i a8, a13, 0x200 +40003e9a: 388c beqz.n a8, 40003ea1 <_XX_unk3e24+0x7d> +40003e9c: fffc46 j 40003e91 <_XX_unk3e24+0x6d> +40003e9f: 0c0000 excw +40003ea2: 0c08 l32i.n a0, a12, 0 +40003ea4: 7689 s32i.n a8, a6, 28 +40003ea6: 11a9 s32i.n a10, a1, 4 +40003ea8: a098d0 addx4 a9, a8, a13 +40003eab: 0020c0 memw +40003eae: 902992 l32i a9, a9, 0x240 +40003eb1: 881b addi.n a8, a8, 1 +40003eb3: 0499 s32i.n a9, a4, 0 +40003eb5: 748080 extui a8, a8, 0, 8 +40003eb8: 444b addi.n a4, a4, 4 +40003eba: e0c552 addi a5, a5, -32 +40003ebd: 20c332 addi a3, a3, 32 +40003ec0: ffeb46 j 40003e71 <_XX_unk3e24+0x4d> +40003ec3: 800000 add a0, a0, a0 +40003ec6: a001a5 call8 3ffa3ee0 <_start-0x5c120> +40003ec9: c020a3 excw +40003ecc: a20020 muluh a0, a0, a2 +40003ecf: 816d excw +40003ed1: 0020c0 memw +40003ed4: 806de2 s32i a14, a13, 0x200 +40003ed7: 0020c0 memw +40003eda: 802d92 l32i a9, a13, 0x200 +40003edd: f68916 beqz a9, 40003e49 <_XX_unk3e24+0x25> +40003ee0: f03d nop.n +40003ee2: 088076 loop a0, 40003eee <_XX_unk3e24+0xca> +40003ee5: 0020c0 memw +40003ee8: 802db2 l32i a11, a13, 0x200 +40003eeb: f5ab16 beqz a11, 40003e49 <_XX_unk3e24+0x25> +40003eee: fffb86 j 40003ee0 <_XX_unk3e24+0xbc> +40003ef1: 09ad mov.n a10, a9 +40003ef3: ffd746 j 40003e54 <_XX_unk3e24+0x30> +40003ef6: 020c movi.n a2, 0 +40003ef8: f01d retw.n + ... + +40003efc <_X_SPI_read_status>: +40003efc: 004136 entry a1, 32 +40003eff: fd8c61 l32r a6, 40003530 <_c_0x08000000> +40003f02: ff5d51 l32r a5, 40003c78 <_c_0x60002e00> +40003f05: 080c movi.n a8, 0 +40003f07: 000346 j 40003f18 <_X_SPI_read_status+0x1c> +40003f0a: 5298 l32i.n a9, a2, 20 +40003f0c: 0020c0 memw +40003f0f: 842572 l32i a7, a5, 0x210 +40003f12: 107790 and a7, a7, a9 +40003f15: 236707 bbci a7, 0, 40003f3c <_X_SPI_read_status+0x40> +40003f18: 0020c0 memw +40003f1b: 846582 s32i a8, a5, 0x210 +40003f1e: 0020c0 memw +40003f21: 806562 s32i a6, a5, 0x200 +40003f24: 0020c0 memw +40003f27: 8025a2 l32i a10, a5, 0x200 +40003f2a: fdca16 beqz a10, 40003f0a <_X_SPI_read_status+0xe> +40003f2d: 088076 loop a0, 40003f39 <_X_SPI_read_status+0x3d> +40003f30: 0020c0 memw +40003f33: 8025b2 l32i a11, a5, 0x200 +40003f36: fd0b16 beqz a11, 40003f0a <_X_SPI_read_status+0xe> +40003f39: fffc06 j 40003f2d <_X_SPI_read_status+0x31> +40003f3c: 0379 s32i.n a7, a3, 0 +40003f3e: 020c movi.n a2, 0 +40003f40: f01d retw.n +40003f42: 480000 excw +40003f45: ffc8 l32i.n a12, a15, 60 +40003f47: 3f .byte 0x3f + +40003f48 <_c_0x90000000>: +40003f48: 000000 ill +40003f4b: 003590 excw + +40003f4c <_c_0x70000035>: +40003f4c: 000035 call12 40003f50 <_c_0x00040000> +40003f4f: 000070 excw + +40003f50 <_c_0x00040000>: +40003f50: 040000 extui a0, a0, 0, 1 + ... + +40003f54 <_XX_unk3f54>: +40003f54: 004136 entry a1, 32 +40003f57: fffba1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> +40003f5a: 0020f0 nop +40003f5d: 0aa8 l32i.n a10, a10, 0 +40003f5f: 002aa5 call8 40004208 <_X_Wait_SPI_Idle> +40003f62: 0d0c movi.n a13, 0 +40003f64: ff4591 l32r a9, 40003c78 <_c_0x60002e00> +40003f67: 0020c0 memw +40003f6a: 8229a2 l32i a10, a9, 0x208 +40003f6d: 0020c0 memw +40003f70: 8269d2 s32i a13, a9, 0x208 +40003f73: 00a782 movi a8, 0x700 +40003f76: 0020c0 memw +40003f79: 886982 s32i a8, a9, 0x220 +40003f7c: fff3f1 l32r a15, 40003f48 <_c_0x90000000> +40003f7f: 0020c0 memw +40003f82: 8769f2 s32i a15, a9, 0x21c +40003f85: fff1e1 l32r a14, 40003f4c <_c_0x70000035> +40003f88: 0020c0 memw +40003f8b: 8969e2 s32i a14, a9, 0x224 +40003f8e: 0020c0 memw +40003f91: 9069d2 s32i a13, a9, 0x240 +40003f94: ffefc1 l32r a12, 40003f50 <_c_0x00040000> +40003f97: 0020c0 memw +40003f9a: 8069c2 s32i a12, a9, 0x200 +40003f9d: 0020c0 memw +40003fa0: 8029b2 l32i a11, a9, 0x200 +40003fa3: db8c beqz.n a11, 40003fb4 <_XX_unk3f54+0x60> +40003fa5: 078076 loop a0, 40003fb0 <_XX_unk3f54+0x5c> +40003fa8: 0020c0 memw +40003fab: 8029b2 l32i a11, a9, 0x200 +40003fae: 2b8c beqz.n a11, 40003fb4 <_XX_unk3f54+0x60> +40003fb0: fffc46 j 40003fa5 <_XX_unk3f54+0x51> +40003fb3: 20c000 or a12, a0, a0 +40003fb6: 29c200 excw +40003fb9: c0c090 sub a12, a0, a9 +40003fbc: cc8074 excw +40003fbf: 03c911 l32r a1, 3ffc4ee4 <_start-0x3b11c> +40003fc2: 0020c0 memw +40003fc5: 8269a2 s32i a10, a9, 0x208 +40003fc8: 020c movi.n a2, 0 +40003fca: f01d retw.n + +40003fcc <_c_0x04000000>: +40003fcc: 000000 ill +40003fcf: 413604 excw +40003fd2: 02ad00 andb b10, b13, b0 +40003fd5: 002325 call8 40004208 <_X_Wait_SPI_Idle> +40003fd8: ff2891 l32r a9, 40003c78 <_c_0x60002e00> +40003fdb: 0020c0 memw +40003fde: 846932 s32i a3, a9, 0x210 +40003fe1: fffaa1 l32r a10, 40003fcc <_c_0x04000000> +40003fe4: 0020c0 memw +40003fe7: 8069a2 s32i a10, a9, 0x200 +40003fea: 0020c0 memw +40003fed: 802982 l32i a8, a9, 0x200 +40003ff0: 0020f0 nop +40003ff3: d88c beqz.n a8, 40004004 <_c_0x04000000+0x38> +40003ff5: 078076 loop a0, 40004000 <_c_0x04000000+0x34> +40003ff8: 0020c0 memw +40003ffb: 8029b2 l32i a11, a9, 0x200 +40003ffe: 2b8c beqz.n a11, 40004004 <_c_0x04000000+0x38> +40004000: fffc46 j 40003ff5 <_c_0x04000000+0x29> +40004003: 020c00 andb b0, b12, b0 +40004006: f01d retw.n +40004008: 000000 ill +4000400b: 000070 excw +4000400e: 368800 excw + +40004010 <_XX_unk4010>: +40004010: 004136 entry a1, 32 +40004013: 000232 l8ui a3, a2, 0 +40004016: 0020f0 nop +40004019: 144230 extui a4, a3, 2, 2 +4000401c: 145430 extui a5, a3, 4, 2 +4000401f: 143030 extui a3, a3, 0, 2 +40004022: 09a316 beqz a3, 400040c0 <_XX_unk4010+0xb0> +40004025: 0233b6 bltui a3, 3, 4000402b <_XX_unk4010+0x1b> +40004028: 002506 j 400040c0 <_XX_unk4010+0xb0> +4000402b: 022366 bnei a3, 2, 40004031 <_XX_unk4010+0x21> +4000402e: 002186 j 400040b8 <_XX_unk4010+0xa8> +40004031: 0b1366 bnei a3, 1, 40004040 <_XX_unk4010+0x30> +40004034: 0224b6 bltui a4, 2, 4000403a <_XX_unk4010+0x2a> +40004037: 002146 j 400040c0 <_XX_unk4010+0xb0> +4000403a: 808340 add a8, a3, a4 +4000403d: 7f9587 bne a5, a8, 400040c0 <_XX_unk4010+0xb0> +40004040: ffc1a1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> +40004043: f03d nop.n +40004045: 0aa8 l32i.n a10, a10, 0 +40004047: 001c25 call8 40004208 <_X_Wait_SPI_Idle> +4000404a: 0f0c movi.n a15, 0 +4000404c: ffefc1 l32r a12, 40004008 <_c_0x04000000+0x3c> +4000404f: ffc0b1 l32r a11, 40003f50 <_c_0x00040000> +40004052: ff09a1 l32r a10, 40003c78 <_c_0x60002e00> +40004055: 0020c0 memw +40004058: 822ad2 l32i a13, a10, 0x208 +4000405b: 0020c0 memw +4000405e: 0212e2 l16ui a14, a2, 4 +40004061: 826af2 s32i a15, a10, 0x208 +40004064: 10ce16 beqz a14, 40004174 <_XX_unk4010+0x164> +40004067: f87c movi.n a8, -1 +40004069: b09580 addx8 a9, a5, a8 +4000406c: ffb751 l32r a5, 40003f48 <_c_0x90000000> +4000406f: b08380 addx8 a8, a3, a8 +40004072: 0199f0 slli a9, a9, 17 +40004075: 118880 slli a8, a8, 8 +40004078: 208890 or a8, a8, a9 +4000407b: 0c9d mov.n a9, a12 +4000407d: 0020c0 memw +40004080: 886a82 s32i a8, a10, 0x220 +40004083: 0020c0 memw +40004086: 876a52 s32i a5, a10, 0x21c +40004089: 0102e2 l8ui a14, a2, 1 +4000408c: 0a7e90 depbits a9, a14, 0, 8 +4000408f: 0020c0 memw +40004092: 896a92 s32i a9, a10, 0x224 +40004095: 0020c0 memw +40004098: 906af2 s32i a15, a10, 0x240 +4000409b: 0020c0 memw +4000409e: 806ab2 s32i a11, a10, 0x200 +400040a1: 0020c0 memw +400040a4: 802a82 l32i a8, a10, 0x200 +400040a7: a89c beqz.n a8, 400040c5 <_XX_unk4010+0xb5> +400040a9: 078076 loop a0, 400040b4 <_XX_unk4010+0xa4> +400040ac: 0020c0 memw +400040af: 802a92 l32i a9, a10, 0x200 +400040b2: f98c beqz.n a9, 400040c5 <_XX_unk4010+0xb5> +400040b4: fffc46 j 400040a9 <_XX_unk4010+0x99> +400040b7: 44cc00 extui a12, a0, 12, 5 +400040ba: 022566 bnei a5, 2, 400040c0 <_XX_unk4010+0xb0> +400040bd: ffdc06 j 40004031 <_XX_unk4010+0x21> +400040c0: f01d retw.n +400040c2: 000000 ill +400040c5: 0020c0 memw +400040c8: 902ae2 l32i a14, a10, 0x240 +400040cb: 930b addi.n a9, a3, -1 +400040cd: 7480e0 extui a8, a14, 0, 8 +400040d0: f4e0e0 extui a14, a14, 0, 16 +400040d3: 83e890 moveqz a14, a8, a9 +400040d6: 0ca416 beqz a4, 400041a4 <_XX_unk4010+0x194> +400040d9: 0c9d mov.n a9, a12 +400040db: 0020c0 memw +400040de: 876a52 s32i a5, a10, 0x21c +400040e1: 020282 l8ui a8, a2, 2 +400040e4: 0a7890 depbits a9, a8, 0, 8 +400040e7: 0020c0 memw +400040ea: 896a92 s32i a9, a10, 0x224 +400040ed: 0020c0 memw +400040f0: 906af2 s32i a15, a10, 0x240 +400040f3: 0020c0 memw +400040f6: 806ab2 s32i a11, a10, 0x200 +400040f9: 0020c0 memw +400040fc: 802a82 l32i a8, a10, 0x200 +400040ff: d88c beqz.n a8, 40004110 <_XX_unk4010+0x100> +40004101: 078076 loop a0, 4000410c <_XX_unk4010+0xfc> +40004104: 0020c0 memw +40004107: 802a92 l32i a9, a10, 0x200 +4000410a: 298c beqz.n a9, 40004110 <_XX_unk4010+0x100> +4000410c: fffc46 j 40004101 <_XX_unk4010+0xf1> +4000410f: 20c000 or a12, a0, a0 +40004112: 128200 andbc b8, b2, b0 +40004115: 2a9202 l16si a0, a2, 84 +40004118: 12f290 andbc b15, b2, b9 +4000411b: 909003 excw +4000411e: ff8074 excw +40004121: 998010 excw +40004124: 9e9011 l32r a1, 3ffebb64 <_start-0x1449c> +40004127: 909020 addx2 a9, a0, a2 +4000412a: 8890f4 excw +4000412d: 1f8710 excw +40004130: 913a add.n a9, a1, a3 +40004132: c0ffb6 bltui a15, 0x100, 400040f6 <_XX_unk4010+0xe6> +40004135: 920020 excw +40004138: 876a add.n a8, a7, a6 +4000413a: 030282 l8ui a8, a2, 3 +4000413d: 0a78c0 depbits a12, a8, 0, 8 +40004140: 0020c0 memw +40004143: 896ac2 s32i a12, a10, 0x224 +40004146: 0312f2 l16ui a15, a2, 6 +40004149: 0020c0 memw +4000414c: 906af2 s32i a15, a10, 0x240 +4000414f: 0020c0 memw +40004152: 806ab2 s32i a11, a10, 0x200 +40004155: 0020c0 memw +40004158: 802ae2 l32i a14, a10, 0x200 +4000415b: de8c beqz.n a14, 4000416c <_XX_unk4010+0x15c> +4000415d: 078076 loop a0, 40004168 <_XX_unk4010+0x158> +40004160: 0020c0 memw +40004163: 802ab2 l32i a11, a10, 0x200 +40004166: 2b8c beqz.n a11, 4000416c <_XX_unk4010+0x15c> +40004168: fffc46 j 4000415d <_XX_unk4010+0x14d> +4000416b: 20c000 or a12, a0, a0 +4000416e: 6ad200 depbits a0, a2, 6, 14 +40004171: f01d82 l16ui a8, a13, 0x1e0 +40004174: f17181 l32r a8, 40000738 <_c_0x80000000> +40004177: 0020c0 memw +4000417a: 876a82 s32i a8, a10, 0x21c +4000417d: 0102f2 l8ui a15, a2, 1 +40004180: 0a7fc0 depbits a12, a15, 0, 8 +40004183: 0020c0 memw +40004186: 896ac2 s32i a12, a10, 0x224 +40004189: 0020c0 memw +4000418c: 806ab2 s32i a11, a10, 0x200 +4000418f: 0020c0 memw +40004192: 802ae2 l32i a14, a10, 0x200 +40004195: fd3e16 beqz a14, 4000416c <_XX_unk4010+0x15c> +40004198: 0020c0 memw +4000419b: 802a92 l32i a9, a10, 0x200 +4000419e: ff6956 bnez a9, 40004198 <_XX_unk4010+0x188> +400041a1: fff1c6 j 4000416c <_XX_unk4010+0x15c> +400041a4: 021292 l16ui a9, a2, 4 +400041a7: 0312f2 l16ui a15, a2, 6 +400041aa: 108e90 and a8, a14, a9 +400041ad: 10ff90 and a15, a15, a9 +400041b0: 021f87 beq a15, a8, 400041b6 <_XX_unk4010+0x1a6> +400041b3: ffde86 j 40004131 <_XX_unk4010+0x121> +400041b6: ffec86 j 4000416c <_XX_unk4010+0x15c> +400041b9: 000000 ill + +400041bc <_X_SPI_write_enable>: +400041bc: 006136 entry a1, 48 +400041bf: 02ad mov.n a10, a2 +400041c1: 080c movi.n a8, 0 +400041c3: 006182 s32i a8, a1, 0 +400041c6: 000425 call8 40004208 <_X_Wait_SPI_Idle> +400041c9: f165b1 l32r a11, 40000760 <_c_0x40000000> +400041cc: feab91 l32r a9, 40003c78 <_c_0x60002e00> +400041cf: 0020c0 memw +400041d2: 8069b2 s32i a11, a9, 0x200 +400041d5: 0020c0 memw +400041d8: 8029a2 l32i a10, a9, 0x200 +400041db: fa8c beqz.n a10, 400041ee <_X_SPI_write_enable+0x32> +400041dd: 078076 loop a0, 400041e8 <_X_SPI_write_enable+0x2c> +400041e0: 0020c0 memw +400041e3: 8029c2 l32i a12, a9, 0x200 +400041e6: 4c8c beqz.n a12, 400041ee <_X_SPI_write_enable+0x32> +400041e8: fffc46 j 400041dd <_X_SPI_write_enable+0x21> +400041eb: 000000 ill +400041ee: 01d8 l32i.n a13, a1, 0 +400041f0: 230c movi.n a3, 2 +400041f2: 0bed17 bbsi a13, 1, 40004201 <_X_SPI_write_enable+0x45> +400041f5: 02ad mov.n a10, a2 +400041f7: 01bd mov.n a11, a1 +400041f9: ffd025 call8 40003efc <_X_SPI_read_status> +400041fc: 01e8 l32i.n a14, a1, 0 +400041fe: f30e37 bnone a14, a3, 400041f5 <_X_SPI_write_enable+0x39> +40004201: 020c movi.n a2, 0 +40004203: f01d retw.n +40004205: 000000 ill + +40004208 <_X_Wait_SPI_Idle>: +40004208: 006136 entry a1, 48 +4000420b: 20a220 or a10, a2, a2 +4000420e: fe9a91 l32r a9, 40003c78 <_c_0x60002e00> +40004211: 0020c0 memw +40004214: be2982 l32i a8, a9, 0x2f8 +40004217: d88c beqz.n a8, 40004228 <_l_4228> +40004219: 078076 loop a0, 40004224 <_X_Wait_SPI_Idle+0x1c> +4000421c: 0020c0 memw +4000421f: be29b2 l32i a11, a9, 0x2f8 +40004222: 2b8c beqz.n a11, 40004228 <_l_4228> +40004224: fffc46 j 40004219 <_X_Wait_SPI_Idle+0x11> + ... + +40004228 <_l_4228>: +40004228: 01bd mov.n a11, a1 +4000422a: ffcd25 call8 40003efc <_X_SPI_read_status> +4000422d: 3a8c beqz.n a10, 40004234 <_l_4234> +4000422f: 120c movi.n a2, 1 +40004231: f01d retw.n + ... + +40004234 <_l_4234>: +40004234: 020c movi.n a2, 0 +40004236: f01d retw.n + +40004238 <_XX_unk4238>: +40004238: 006136 entry a1, 48 +4000423b: 02ad mov.n a10, a2 +4000423d: 01bd mov.n a11, a1 +4000423f: ffd165 call8 40003f54 <_XX_unk3f54> +40004242: 2a8c beqz.n a10, 40004248 <_XX_unk4238+0x10> +40004244: 120c movi.n a2, 1 +40004246: f01d retw.n +40004248: 0188 l32i.n a8, a1, 0 +4000424a: 036897 bbci a8, 9, 40004251 <_XX_unk4238+0x19> +4000424d: 020c movi.n a2, 0 +4000424f: f01d retw.n +40004251: f9d8b1 l32r a11, 400029b4 <_c_0x00400000> +40004254: fe89a1 l32r a10, 40003c78 <_c_0x60002e00> +40004257: 0020c0 memw +4000425a: 822a92 l32i a9, a10, 0x208 +4000425d: f03d nop.n +4000425f: 2099b0 or a9, a9, a11 +40004262: 0020c0 memw +40004265: 826a92 s32i a9, a10, 0x208 +40004268: 02ad mov.n a10, a2 +4000426a: fff525 call8 400041bc <_X_SPI_write_enable> +4000426d: fd3a56 bnez a10, 40004244 <_XX_unk4238+0xc> +40004270: 02ad mov.n a10, a2 +40004272: 00a2b2 movi a11, 0x200 +40004275: ffd5a5 call8 40003fd0 <_c_0x04000000+0x4> +40004278: fc8a56 bnez a10, 40004244 <_XX_unk4238+0xc> +4000427b: fff386 j 4000424d <_XX_unk4238+0x15> +4000427e: 360000 excw +40004281: ad0061 l32r a6, 3ffef684 <_start-0x1097c> +40004284: 01bd02 l32ai a0, a13, 4 +40004287: ffcce5 call8 40003f54 <_XX_unk3f54> +4000428a: 2a8c beqz.n a10, 40004290 <_XX_unk4238+0x58> +4000428c: 120c movi.n a2, 1 +4000428e: f01d retw.n +40004290: 0188 l32i.n a8, a1, 0 +40004292: 03e897 bbsi a8, 9, 40004299 <_XX_unk4238+0x61> +40004295: 020c movi.n a2, 0 +40004297: f01d retw.n +40004299: f9c6b1 l32r a11, 400029b4 <_c_0x00400000> +4000429c: fe77a1 l32r a10, 40003c78 <_c_0x60002e00> +4000429f: 0020c0 memw +400042a2: 822a92 l32i a9, a10, 0x208 +400042a5: f03d nop.n +400042a7: 2099b0 or a9, a9, a11 +400042aa: 0020c0 memw +400042ad: 826a92 s32i a9, a10, 0x208 +400042b0: 02ad mov.n a10, a2 +400042b2: fff0a5 call8 400041bc <_X_SPI_write_enable> +400042b5: fd3a56 bnez a10, 4000428c <_XX_unk4238+0x54> +400042b8: 02ad mov.n a10, a2 +400042ba: 04c1b2 addi a11, a1, 4 +400042bd: ffc3e5 call8 40003efc <_X_SPI_read_status> +400042c0: 02ad mov.n a10, a2 +400042c2: 0401b2 l8ui a11, a1, 4 +400042c5: ffd0a5 call8 40003fd0 <_c_0x04000000+0x4> +400042c8: fc0a56 bnez a10, 4000428c <_XX_unk4238+0x54> +400042cb: fff186 j 40004295 <_XX_unk4238+0x5d> +400042ce: 000000 ill +400042d1: 002880 excw +400042d4: 100000 and a0, a0, a0 + ... + +400042d8 <_X_SPIFlashModeConfig>: +400042d8: 004136 entry a1, 32 +400042db: 069216 beqz a2, 40004348 <_X_SPIFlashModeConfig+0x70> +400042de: 6e1226 beqi a2, 1, 40004350 <_X_SPIFlashModeConfig+0x78> +400042e1: 732226 beqi a2, 2, 40004358 <_X_SPIFlashModeConfig+0x80> +400042e4: 783226 beqi a2, 3, 40004360 <_X_SPIFlashModeConfig+0x88> +400042e7: 7d4226 beqi a2, 4, 40004368 <_X_SPIFlashModeConfig+0x90> +400042ea: 050c movi.n a5, 0 +400042ec: fe6321 l32r a2, 40003c78 <_c_0x60002e00> +400042ef: 0b23f6 bgeui a3, 2, 400042fe <_X_SPIFlashModeConfig+0x26> +400042f2: f11141 l32r a4, 40000738 <_c_0x80000000> +400042f5: 0020c0 memw +400042f8: 866242 s32i a4, a2, 0x218 +400042fb: 000646 j 40004318 <_X_SPIFlashModeConfig+0x40> +400042fe: 630b addi.n a6, a3, -1 +40004300: 118640 slli a8, a6, 12 +40004303: 206680 or a6, a6, a8 +40004306: 418130 srli a8, a3, 1 +40004309: ffc882 addi a8, a8, -1 +4000430c: 1188a0 slli a8, a8, 6 +4000430f: 206680 or a6, a6, a8 +40004312: 0020c0 memw +40004315: 866262 s32i a6, a2, 0x218 +40004318: ffeeb1 l32r a11, 400042d0 <_XX_unk4238+0x98> +4000431b: 20b5b0 or a11, a5, a11 +4000431e: 0020c0 memw +40004321: 8262b2 s32i a11, a2, 0x208 +40004324: ffeca1 l32r a10, 400042d4 <_XX_unk4238+0x9c> +40004327: 0020c0 memw +4000432a: 8062a2 s32i a10, a2, 0x200 +4000432d: 0020c0 memw +40004330: 802292 l32i a9, a2, 0x200 +40004333: d98c beqz.n a9, 40004344 <_X_SPIFlashModeConfig+0x6c> +40004335: 078076 loop a0, 40004340 <_X_SPIFlashModeConfig+0x68> +40004338: 0020c0 memw +4000433b: 8022c2 l32i a12, a2, 0x200 +4000433e: 2c8c beqz.n a12, 40004344 <_X_SPIFlashModeConfig+0x6c> +40004340: fffc46 j 40004335 <_X_SPIFlashModeConfig+0x5d> +40004343: f01d00 subx8 a1, a13, a0 +40004346: 510000 excw +40004349: fe5c movi.n a14, 95 +4000434b: ffe746 j 400042ec <_X_SPIFlashModeConfig+0x14> +4000434e: 510000 excw +40004351: 46ffe1 l32r a14, 3ffd5f50 <_start-0x2a0b0> +40004354: 00ffe5 call8 40005354 <_XX_unk51ac+0x1a8> +40004357: 6a5100 depbits a0, a1, 6, 6 +4000435a: fe .byte 0xfe +4000435b: ffe346 j 400042ec <_X_SPIFlashModeConfig+0x14> +4000435e: 510000 excw +40004361: fabd excw +40004363: ffe146 j 400042ec <_X_SPIFlashModeConfig+0x14> +40004366: 510000 excw +40004369: 46f473 excw +4000436c: df .byte 0xdf +4000436d: ff .byte 0xff + ... + +40004370 <_X_spi_flash_attach>: +40004370: 004136 entry a1, 32 +40004373: 20a220 or a10, a2, a2 +40004376: ff6a65 call8 40003a1c <_X_SelectSpiFunction> +40004379: 5a0c movi.n a10, 5 +4000437b: 04a0b2 movi a11, 4 +4000437e: fff5a5 call8 400042d8 <_X_SPIFlashModeConfig> +40004381: 03bd mov.n a11, a3 +40004383: 5a0c movi.n a10, 5 +40004385: 001b25 call8 40004538 <_X_SPIReadModeConfig> +40004388: f01d retw.n + ... + +4000438c : +4000438c: 004136 entry a1, 32 +4000438f: feeda1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> +40004392: 002aa2 l32i a10, a10, 0 +40004395: ffe725 call8 40004208 <_X_Wait_SPI_Idle> +40004398: 0b2c movi.n a11, 32 +4000439a: fd6c movi.n a13, -17 +4000439c: fe37c1 l32r a12, 40003c78 <_c_0x60002e00> +4000439f: f7af92 movi a9, -9 +400043a2: fbe3a1 l32r a10, 40003330 <_c_0x3feffe00> +400043a5: 0020c0 memw +400043a8: 822a82 l32i a8, a10, 0x208 +400043ab: 108890 and a8, a8, a9 +400043ae: 0020c0 memw +400043b1: 826a82 s32i a8, a10, 0x208 +400043b4: f35ef1 l32r a15, 4000112c <_c_0xfffdffff> +400043b7: 0020c0 memw +400043ba: 822ce2 l32i a14, a12, 0x208 +400043bd: 10eef0 and a14, a14, a15 +400043c0: 0020c0 memw +400043c3: 826ce2 s32i a14, a12, 0x208 +400043c6: 0020c0 memw +400043c9: 822a92 l32i a9, a10, 0x208 +400043cc: 1099d0 and a9, a9, a13 +400043cf: 0020c0 memw +400043d2: 826a92 s32i a9, a10, 0x208 +400043d5: 10a082 movi a8, 16 +400043d8: 0020c0 memw +400043db: 822af2 l32i a15, a10, 0x208 +400043de: f03d nop.n +400043e0: 20ff80 or a15, a15, a8 +400043e3: 0020c0 memw +400043e6: 826af2 s32i a15, a10, 0x208 +400043e9: 0020c0 memw +400043ec: 822ae2 l32i a14, a10, 0x208 +400043ef: 0020f0 nop +400043f2: 0eee57 bbsi a14, 5, 40004404 +400043f5: 088076 loop a0, 40004401 +400043f8: 0020c0 memw +400043fb: 822ae2 l32i a14, a10, 0x208 +400043fe: 028eb7 bany a14, a11, 40004404 +40004401: fffc06 j 400043f5 +40004404: 0020c0 memw +40004407: 822a82 l32i a8, a10, 0x208 +4000440a: 1088d0 and a8, a8, a13 +4000440d: 0020c0 memw +40004410: 826a82 s32i a8, a10, 0x208 +40004413: 7f0c movi.n a15, 7 +40004415: 0020c0 memw +40004418: 822ae2 l32i a14, a10, 0x208 +4000441b: 20eef0 or a14, a14, a15 +4000441e: 0020c0 memw +40004421: 826ae2 s32i a14, a10, 0x208 +40004424: fc41b1 l32r a11, 40003528 <_c_0x00020000> +40004427: 0020c0 memw +4000442a: 822c92 l32i a9, a12, 0x208 +4000442d: 2099b0 or a9, a9, a11 +40004430: 0020c0 memw +40004433: 826c92 s32i a9, a12, 0x208 +40004436: 880c movi.n a8, 8 +40004438: 0020c0 memw +4000443b: 822af2 l32i a15, a10, 0x208 +4000443e: 20ff80 or a15, a15, a8 +40004441: 0020c0 memw +40004444: 826af2 s32i a15, a10, 0x208 +40004447: f01d retw.n +40004449: 000000 ill + +4000444c : +4000444c: 004136 entry a1, 32 +4000444f: 052c movi.n a5, 32 +40004451: f76c movi.n a7, -17 +40004453: fe09a1 l32r a10, 40003c78 <_c_0x60002e00> +40004456: 7d7c movi.n a13, -9 +40004458: fbb641 l32r a4, 40003330 <_c_0x3feffe00> +4000445b: 0020c0 memw +4000445e: 8224c2 l32i a12, a4, 0x208 +40004461: 10ccd0 and a12, a12, a13 +40004464: 0020c0 memw +40004467: 8264c2 s32i a12, a4, 0x208 +4000446a: f330b1 l32r a11, 4000112c <_c_0xfffdffff> +4000446d: 0020c0 memw +40004470: 822a92 l32i a9, a10, 0x208 +40004473: 1099b0 and a9, a9, a11 +40004476: 0020c0 memw +40004479: 826a92 s32i a9, a10, 0x208 +4000447c: 0020c0 memw +4000447f: 822482 l32i a8, a4, 0x208 +40004482: 108870 and a8, a8, a7 +40004485: 0020c0 memw +40004488: 826482 s32i a8, a4, 0x208 +4000448b: 061c movi.n a6, 16 +4000448d: 0020c0 memw +40004490: 822432 l32i a3, a4, 0x208 +40004493: 203360 or a3, a3, a6 +40004496: 0020c0 memw +40004499: 826432 s32i a3, a4, 0x208 +4000449c: 0020c0 memw +4000449f: 822422 l32i a2, a4, 0x208 +400044a2: 0ee257 bbsi a2, 5, 400044b4 +400044a5: 088076 loop a0, 400044b1 +400044a8: 0020c0 memw +400044ab: 8224e2 l32i a14, a4, 0x208 +400044ae: 028e57 bany a14, a5, 400044b4 +400044b1: fffc06 j 400044a5 +400044b4: 0020c0 memw +400044b7: 8224f2 l32i a15, a4, 0x208 +400044ba: 10ff70 and a15, a15, a7 +400044bd: 0020c0 memw +400044c0: 8264f2 s32i a15, a4, 0x208 +400044c3: f01d retw.n +400044c5: 000000 ill +400044c8: 006136 entry a1, 48 +400044cb: fe9e21 l32r a2, 40003f44 <_X_SPI_read_status+0x48> +400044ce: 01bd mov.n a11, a1 +400044d0: 0022a2 l32i a10, a2, 0 +400044d3: ffa825 call8 40003f54 <_XX_unk3f54> +400044d6: 2a8c beqz.n a10, 400044dc +400044d8: 120c movi.n a2, 1 +400044da: f01d retw.n +400044dc: 02a8 l32i.n a10, a2, 0 +400044de: ffcde5 call8 400041bc <_X_SPI_write_enable> +400044e1: ff3a56 bnez a10, 400044d8 +400044e4: 02a8 l32i.n a10, a2, 0 +400044e6: 0021b2 l32i a11, a1, 0 +400044e9: ffae65 call8 40003fd0 <_c_0x04000000+0x4> +400044ec: fe8a56 bnez a10, 400044d8 +400044ef: 020c movi.n a2, 0 +400044f1: f01d retw.n +400044f3: 613600 excw +400044f6: 932100 movnez a2, a1, a0 +400044f9: fe .byte 0xfe +400044fa: 01bd mov.n a11, a1 +400044fc: 0022a2 l32i a10, a2, 0 +400044ff: ffa565 call8 40003f54 <_XX_unk3f54> +40004502: 2a8c beqz.n a10, 40004508 +40004504: 120c movi.n a2, 1 +40004506: f01d retw.n +40004508: 02a8 l32i.n a10, a2, 0 +4000450a: ffcb25 call8 400041bc <_X_SPI_write_enable> +4000450d: ff3a56 bnez a10, 40004504 +40004510: 02a8 l32i.n a10, a2, 0 +40004512: 01b8 l32i.n a11, a1, 0 +40004514: cc1c movi.n a12, 28 +40004516: 20bbc0 or a11, a11, a12 +40004519: ffab65 call8 40003fd0 <_c_0x04000000+0x4> +4000451c: fe4a56 bnez a10, 40004504 +4000451f: 020c movi.n a2, 0 +40004521: f01d retw.n +40004523: 9fff00 excw +40004526: 6f .byte 0x6f +40004527: fe .byte 0xfe +40004528: 002000 isync +4000452b: 200001 l32r a0, 3ffcc52c <_start-0x33ad4> +4000452e: 000010 excw +40004531: 008020 any4 b2, b0:b1:b2:b3 +40004534: 006000 rsil a0, 0 + ... + +40004538 <_X_SPIReadModeConfig>: +40004538: 004136 entry a1, 32 +4000453b: fffa91 l32r a9, 40004524 +4000453e: fe81a1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> +40004541: fdcd51 l32r a5, 40003c78 <_c_0x60002e00> +40004544: 0020c0 memw +40004547: 822582 l32i a8, a5, 0x208 +4000454a: f03d nop.n +4000454c: 108890 and a8, a8, a9 +4000454f: 0020c0 memw +40004552: 826582 s32i a8, a5, 0x208 +40004555: c2ac beqz.n a2, 40004585 <_X_SPIReadModeConfig+0x4d> +40004557: 451226 beqi a2, 1, 400045a0 <_X_SPIReadModeConfig+0x68> +4000455a: 4a2226 beqi a2, 2, 400045a8 <_X_SPIReadModeConfig+0x70> +4000455d: 4f3226 beqi a2, 3, 400045b0 <_X_SPIReadModeConfig+0x78> +40004560: 544226 beqi a2, 4, 400045b8 <_X_SPIReadModeConfig+0x80> +40004563: 040c movi.n a4, 0 +40004565: 1f1226 beqi a2, 1, 40004588 <_X_SPIReadModeConfig+0x50> +40004568: 438c beqz.n a3, 40004570 <_X_SPIReadModeConfig+0x38> +4000456a: 002aa2 l32i a10, a10, 0 +4000456d: ffd125 call8 40004280 <_XX_unk4238+0x48> +40004570: 0020c0 memw +40004573: 8225a2 l32i a10, a5, 0x208 +40004576: 20aa40 or a10, a10, a4 +40004579: 0020c0 memw +4000457c: 8265a2 s32i a10, a5, 0x208 +4000457f: 020c movi.n a2, 0 +40004581: f01d retw.n +40004583: 410000 srli a0, a0, 0 +40004586: ffe8 l32i.n a14, a15, 60 +40004588: 0aa8 l32i.n a10, a10, 0 +4000458a: ffcae5 call8 40004238 <_XX_unk4238> +4000458d: 0020c0 memw +40004590: 8225b2 l32i a11, a5, 0x208 +40004593: 20bb40 or a11, a11, a4 +40004596: 0020c0 memw +40004599: 8265b2 s32i a11, a5, 0x208 +4000459c: 020c movi.n a2, 0 +4000459e: f01d retw.n +400045a0: ffe341 l32r a4, 4000452c +400045a3: ffef86 j 40004565 <_X_SPIReadModeConfig+0x2d> +400045a6: 410000 srli a0, a0, 0 +400045a9: 86ffe2 s32ri a14, a15, 0x218 +400045ac: ffed excw +400045ae: 410000 srli a0, a0, 0 +400045b1: 86ffe1 l32r a14, 3ffe61b0 <_start-0x19e50> +400045b4: ffeb addi.n a15, a15, 14 +400045b6: 410000 srli a0, a0, 0 +400045b9: df .byte 0xdf +400045ba: e986f3 excw +400045bd: ff .byte 0xff +400045be: 360000 excw +400045c1: a10041 l32r a4, 3ffec9c4 <_start-0x1363c> +400045c4: a2fe60 muluh a15, a14, a6 +400045c7: 002a add.n a0, a0, a2 +400045c9: ffc3e5 call8 40004208 <_X_Wait_SPI_Idle> +400045cc: ffd6c1 l32r a12, 40004524 +400045cf: fdaaa1 l32r a10, 40003c78 <_c_0x60002e00> +400045d2: 0020c0 memw +400045d5: 822ab2 l32i a11, a10, 0x208 +400045d8: 10bbc0 and a11, a11, a12 +400045db: 0020c0 memw +400045de: 826ab2 s32i a11, a10, 0x208 +400045e1: 32bc beqz.n a2, 40004618 <_X_SPIReadModeConfig+0xe0> +400045e3: 491226 beqi a2, 1, 40004630 <_X_SPIReadModeConfig+0xf8> +400045e6: 5e2226 beqi a2, 2, 40004648 <_X_SPIReadModeConfig+0x110> +400045e9: 733226 beqi a2, 3, 40004660 <_X_SPIReadModeConfig+0x128> +400045ec: 104226 beqi a2, 4, 40004600 <_X_SPIReadModeConfig+0xc8> +400045ef: 0020c0 memw +400045f2: 822ad2 l32i a13, a10, 0x208 +400045f5: 0020c0 memw +400045f8: 826ad2 s32i a13, a10, 0x208 +400045fb: 020c movi.n a2, 0 +400045fd: f01d retw.n +400045ff: cdf100 excw +40004602: 20c0f3 excw +40004605: 2ae200 depbits a0, a2, 2, 15 +40004608: eef082 s32ri a8, a0, 0x3b8 +4000460b: 20c020 or a12, a0, a2 +4000460e: 6ae200 depbits a0, a2, 6, 15 +40004611: 020c82 l8ui a8, a12, 2 +40004614: f01d retw.n +40004616: 910000 srl a0, a0 +40004619: c0ffc4 excw +4000461c: 220020 orb b0, b0, b2 +4000461f: 822a add.n a8, a2, a2 +40004621: 202290 or a2, a2, a9 +40004624: 0020c0 memw +40004627: 826a22 s32i a2, a10, 0x208 +4000462a: 020c movi.n a2, 0 +4000462c: f01d retw.n +4000462e: 910000 srl a0, a0 +40004631: bf .byte 0xbf +40004632: ff .byte 0xff +40004633: 0020c0 memw +40004636: 822a82 l32i a8, a10, 0x208 +40004639: 208890 or a8, a8, a9 +4000463c: 0020c0 memw +4000463f: 826a82 s32i a8, a10, 0x208 +40004642: 020c movi.n a2, 0 +40004644: f01d retw.n +40004646: c10000 mul16u a0, a0, a0 +40004649: ffba add.n a15, a15, a11 +4000464b: 0020c0 memw +4000464e: 822ab2 l32i a11, a10, 0x208 +40004651: 20bbc0 or a11, a11, a12 +40004654: 0020c0 memw +40004657: 826ab2 s32i a11, a10, 0x208 +4000465a: 020c movi.n a2, 0 +4000465c: f01d retw.n +4000465e: 910000 srl a0, a0 +40004661: c0ffb5 call12 3ffc565c <_start-0x3a9a4> +40004664: 220020 orb b0, b0, b2 +40004667: 822a add.n a8, a2, a2 +40004669: 202290 or a2, a2, a9 +4000466c: 0020c0 memw +4000466f: 826a22 s32i a2, a10, 0x208 +40004672: 020c movi.n a2, 0 +40004674: f01d retw.n +40004676: 360000 excw +40004679: a10041 l32r a4, 3ffeca7c <_start-0x13584> +4000467c: a2fe32 s32ri a3, a14, 0x288 +4000467f: 002a add.n a0, a0, a2 +40004681: ffb865 call8 40004208 <_X_Wait_SPI_Idle> +40004684: fd7d91 l32r a9, 40003c78 <_c_0x60002e00> +40004687: 0d22f6 bgeui a2, 2, 40004698 <_X_SPIReadModeConfig+0x160> +4000468a: f02bb1 l32r a11, 40000738 <_c_0x80000000> +4000468d: 0020c0 memw +40004690: 8669b2 s32i a11, a9, 0x218 +40004693: 020c movi.n a2, 0 +40004695: f01d retw.n +40004697: c20b00 quou a0, a11, a0 +4000469a: 11dc40 slli a13, a12, 12 +4000469d: 20ccd0 or a12, a12, a13 +400046a0: 41d120 srli a13, a2, 1 +400046a3: dd0b addi.n a13, a13, -1 +400046a5: 11dda0 slli a13, a13, 6 +400046a8: 20ccd0 or a12, a12, a13 +400046ab: 0020c0 memw +400046ae: 8669c2 s32i a12, a9, 0x218 +400046b1: 020c movi.n a2, 0 +400046b3: f01d retw.n +400046b5: 000000 ill +400046b8: 004136 entry a1, 32 +400046bb: fe2221 l32r a2, 40003f44 <_X_SPI_read_status+0x48> +400046be: f03d nop.n +400046c0: 0022a2 l32i a10, a2, 0 +400046c3: ffafa5 call8 400041bc <_X_SPI_write_enable> +400046c6: 2a8c beqz.n a10, 400046cc <_X_SPIReadModeConfig+0x194> +400046c8: 120c movi.n a2, 1 +400046ca: f01d retw.n +400046cc: 02a8 l32i.n a10, a2, 0 +400046ce: ff5ae5 call8 40003c7c <_X_SPI_chip_erase> +400046d1: ff3a56 bnez a10, 400046c8 <_X_SPIReadModeConfig+0x190> +400046d4: 020c movi.n a2, 0 +400046d6: f01d retw.n +400046d8: 004136 entry a1, 32 +400046db: fe1a31 l32r a3, 40003f44 <_X_SPI_read_status+0x48> +400046de: 03a8 l32i.n a10, a3, 0 +400046e0: 2a98 l32i.n a9, a10, 8 +400046e2: 1a88 l32i.n a8, a10, 4 +400046e4: c28890 quou a8, a8, a9 +400046e7: 063287 bltu a2, a8, 400046f1 <_X_SPIReadModeConfig+0x1b9> +400046ea: 120c movi.n a2, 1 +400046ec: f01d retw.n +400046ee: 000000 ill +400046f1: ffaca5 call8 400041bc <_X_SPI_write_enable> +400046f4: ff2a56 bnez a10, 400046ea <_X_SPIReadModeConfig+0x1b2> +400046f7: 03a8 l32i.n a10, a3, 0 +400046f9: 2ab8 l32i.n a11, a10, 8 +400046fb: 82bb20 mull a11, a11, a2 +400046fe: ff6065 call8 40003d04 <_c_0x00800000+0x4> +40004701: fe5a56 bnez a10, 400046ea <_X_SPIReadModeConfig+0x1b2> +40004704: 020c movi.n a2, 0 +40004706: f01d retw.n + +40004708 : +40004708: 004136 entry a1, 32 +4000470b: fe0e31 l32r a3, 40003f44 <_X_SPI_read_status+0x48> +4000470e: 03a8 l32i.n a10, a3, 0 +40004710: 3a98 l32i.n a9, a10, 12 +40004712: 1a88 l32i.n a8, a10, 4 +40004714: c28890 quou a8, a8, a9 +40004717: 063287 bltu a2, a8, 40004721 +4000471a: 120c movi.n a2, 1 +4000471c: f01d retw.n +4000471e: 000000 ill +40004721: ffa9a5 call8 400041bc <_X_SPI_write_enable> +40004724: ff2a56 bnez a10, 4000471a +40004727: 03a8 l32i.n a10, a3, 0 +40004729: 3ab8 l32i.n a11, a10, 12 +4000472b: 82bb20 mull a11, a11, a2 +4000472e: ff58e5 call8 40003cbc <_XX_unk3cbc> +40004731: fe5a56 bnez a10, 4000471a +40004734: 020c movi.n a2, 0 +40004736: f01d retw.n + +40004738 : +40004738: 006136 entry a1, 48 +4000473b: fe02c1 l32r a12, 40003f44 <_X_SPI_read_status+0x48> +4000473e: 046d mov.n a6, a4 +40004740: 0ca8 l32i.n a10, a12, 0 +40004742: 942a add.n a9, a4, a2 +40004744: 1a88 l32i.n a8, a10, 4 +40004746: 4a78 l32i.n a7, a10, 16 +40004748: 143897 bltu a8, a9, 40004760 +4000474b: e25270 remu a5, a2, a7 +4000474e: c05750 sub a5, a7, a5 +40004751: 0fb457 bgeu a4, a5, 40004764 +40004754: 04dd mov.n a13, a4 +40004756: 03cd mov.n a12, a3 +40004758: 02bd mov.n a11, a2 +4000475a: ff5f65 call8 40003d50 <_c_0x02000000+0x4> +4000475d: 05fa16 beqz a10, 400047c0 +40004760: 120c movi.n a2, 1 +40004762: f01d retw.n +40004764: 02bd mov.n a11, a2 +40004766: 03cd mov.n a12, a3 +40004768: 05dd mov.n a13, a5 +4000476a: ff5e65 call8 40003d50 <_c_0x02000000+0x4> +4000476d: fefa56 bnez a10, 40004760 +40004770: 0129 s32i.n a2, a1, 0 +40004772: 1169 s32i.n a6, a1, 4 +40004774: c04650 sub a4, a6, a5 +40004777: c24470 quou a4, a4, a7 +4000477a: 74ac beqz.n a4, 400047a5 +4000477c: 806520 add a6, a5, a2 +4000477f: 00a022 movi a2, 0 +40004782: 06bd mov.n a11, a6 +40004784: 07dd mov.n a13, a7 +40004786: fdefa1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> +40004789: 41c250 srli a12, a5, 2 +4000478c: a0cc30 addx4 a12, a12, a3 +4000478f: 002aa2 l32i a10, a10, 0 +40004792: ff5be5 call8 40003d50 <_c_0x02000000+0x4> +40004795: fc7a56 bnez a10, 40004760 +40004798: 667a add.n a6, a6, a7 +4000479a: 575a add.n a5, a7, a5 +4000479c: 01c222 addi a2, a2, 1 +4000479f: 742020 extui a2, a2, 0, 8 +400047a2: dc9427 bne a4, a2, 40004782 +400047a5: fde7a1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> +400047a8: 11d8 l32i.n a13, a1, 4 +400047aa: 01b8 l32i.n a11, a1, 0 +400047ac: 41c250 srli a12, a5, 2 +400047af: a0cc30 addx4 a12, a12, a3 +400047b2: bb5a add.n a11, a11, a5 +400047b4: c0dd50 sub a13, a13, a5 +400047b7: 002aa2 l32i a10, a10, 0 +400047ba: ff5965 call8 40003d50 <_c_0x02000000+0x4> +400047bd: f9fa56 bnez a10, 40004760 +400047c0: 020c movi.n a2, 0 +400047c2: f01d retw.n +400047c4: 011000 slli a1, a0, 32 +400047c7: 413600 srli a3, a0, 6 +400047ca: fe4100 excw +400047cd: ff .byte 0xff +400047ce: fad831 l32r a3, 40003330 <_c_0x3feffe00> +400047d1: 0020c0 memw +400047d4: 9a2322 l32i a2, a3, 0x268 +400047d7: 202240 or a2, a2, a4 +400047da: 0020c0 memw +400047dd: 9a6322 s32i a2, a3, 0x268 +400047e0: f01d retw.n +400047e2: 000000 ill +400047e5: ae .byte 0xae +400047e6: 366001 l32r a0, 3ffd2168 <_start-0x2de98> +400047e9: 4d0041 l32r a4, 3ffd7bec <_start-0x28414> +400047ec: f51c03 excw +400047ef: 458527 bany a5, a2, 40004838 +400047f2: fffc61 l32r a6, 400047e4 +400047f5: 0020c0 memw +400047f8: 896622 s32i a2, a6, 0x224 +400047fb: 08a032 movi a3, 8 +400047fe: 00a022 movi a2, 0 +40004801: 0ea376 loopgtz a3, 40004813 +40004804: a03260 addx4 a3, a2, a6 +40004807: 0458 l32i.n a5, a4, 0 +40004809: 0020c0 memw +4000480c: 806352 s32i a5, a3, 0x200 +4000480f: 444b addi.n a4, a4, 4 +40004811: 221b addi.n a2, a2, 1 +40004813: 01a0a2 movi a10, 1 +40004816: 0020c0 memw +40004819: 8866a2 s32i a10, a6, 0x220 +4000481c: 0020c0 memw +4000481f: 882692 l32i a9, a6, 0x220 +40004822: 0ee907 bbsi a9, 0, 40004834 +40004825: 088076 loop a0, 40004831 +40004828: 0020c0 memw +4000482b: 8826b2 l32i a11, a6, 0x220 +4000482e: 02eb07 bbsi a11, 0, 40004834 +40004831: fffc06 j 40004825 +40004834: 020c movi.n a2, 0 +40004836: f01d retw.n +40004838: 120c movi.n a2, 1 +4000483a: f01d retw.n +4000483c: ff .byte 0xff +4000483d: ef .byte 0xef +4000483e: fe .byte 0xfe +4000483f: ff .byte 0xff +40004840: 004136 entry a1, 32 +40004843: fffe41 l32r a4, 4000483c +40004846: faba31 l32r a3, 40003330 <_c_0x3feffe00> +40004849: 0020c0 memw +4000484c: 9a2322 l32i a2, a3, 0x268 +4000484f: 102240 and a2, a2, a4 +40004852: 0020c0 memw +40004855: 9a6322 s32i a2, a3, 0x268 +40004858: f01d retw.n +4000485a: 360000 excw +4000485d: 1c0041 l32r a4, 3ffcb860 <_start-0x347a0> +40004860: 20fa add.n a2, a0, a15 +40004862: cc4480 excw +40004865: 4718 l32i.n a1, a7, 16 +40004867: 030a add.n a0, a3, a0 +40004869: 120c movi.n a2, 1 +4000486b: f01d retw.n +4000486d: fff5a5 call8 400047c8 +40004870: 415540 srli a5, a4, 5 +40004873: a59c beqz.n a5, 40004891 +40004875: 00a042 movi a4, 0 +40004878: 02ad mov.n a10, a2 +4000487a: 11b4d0 slli a11, a4, 3 +4000487d: a0bb30 addx4 a11, a11, a3 +40004880: fff665 call8 400047e8 +40004883: 02ad mov.n a10, a2 +40004885: 03bd mov.n a11, a3 +40004887: 0c2c movi.n a12, 32 +40004889: ffeae5 call8 40004738 +4000488c: 441b addi.n a4, a4, 1 +4000488e: e69457 bne a4, a5, 40004878 +40004891: fffae5 call8 40004840 +40004894: f01d retw.n + ... + +40004898 : +40004898: 004136 entry a1, 32 +4000489b: 04dd mov.n a13, a4 +4000489d: 03cd mov.n a12, a3 +4000489f: fda9a1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> +400048a2: 02bd mov.n a11, a2 +400048a4: 0aa8 l32i.n a10, a10, 0 +400048a6: ff57e5 call8 40003e24 <_XX_unk3e24> +400048a9: 3a8c beqz.n a10, 400048b0 +400048ab: 120c movi.n a2, 1 +400048ad: f01d retw.n +400048af: 020c00 andb b0, b12, b0 +400048b2: f01d retw.n + +400048b4 <_X_SPIEraseArea>: +400048b4: 004136 entry a1, 32 +400048b7: 05a0a2 movi a10, 5 +400048ba: 01a0b2 movi a11, 1 +400048bd: ffc7a5 call8 40004538 <_X_SPIReadModeConfig> +400048c0: fda141 l32r a4, 40003f44 <_X_SPI_read_status+0x48> +400048c3: 04a8 l32i.n a10, a4, 0 +400048c5: 923a add.n a9, a2, a3 +400048c7: 1a88 l32i.n a8, a10, 4 +400048c9: 3ab8 l32i.n a11, a10, 12 +400048cb: 053897 bltu a8, a9, 400048d4 <_X_SPIEraseArea+0x20> +400048ce: e2b2b0 remu a11, a2, a11 +400048d1: 004b16 beqz a11, 400048d9 <_X_SPIEraseArea+0x25> +400048d4: 120c movi.n a2, 1 +400048d6: f01d retw.n +400048d8: bee500 excw +400048db: ff .byte 0xff +400048dc: ff4a56 bnez a10, 400048d4 <_X_SPIEraseArea+0x20> +400048df: 0448 l32i.n a4, a4, 0 +400048e1: 3488 l32i.n a8, a4, 12 +400048e3: 2448 l32i.n a4, a4, 8 +400048e5: c29380 quou a9, a3, a8 +400048e8: e2c380 remu a12, a3, a8 +400048eb: c22280 quou a2, a2, a8 +400048ee: c24480 quou a4, a4, a8 +400048f1: e25240 remu a5, a2, a4 +400048f4: 1c8c beqz.n a12, 400048f9 <_X_SPIEraseArea+0x45> +400048f6: 01c992 addi a9, a9, 1 +400048f9: c05450 sub a5, a4, a5 +400048fc: 435950 min a5, a9, a5 +400048ff: c03950 sub a3, a9, a5 +40004902: e58c beqz.n a5, 40004914 <_X_SPIEraseArea+0x60> +40004904: 02ad mov.n a10, a2 +40004906: ffe025 call8 40004708 +40004909: fc7a56 bnez a10, 400048d4 <_X_SPIEraseArea+0x20> +4000490c: 221b addi.n a2, a2, 1 +4000490e: ffc552 addi a5, a5, -1 +40004911: fef556 bnez a5, 40004904 <_X_SPIEraseArea+0x50> +40004914: 11b437 bgeu a4, a3, 40004929 <_X_SPIEraseArea+0x75> +40004917: c2a240 quou a10, a2, a4 +4000491a: ffdbe5 call8 400046d8 <_X_SPIReadModeConfig+0x1a0> +4000491d: fb3a56 bnez a10, 400048d4 <_X_SPIEraseArea+0x20> +40004920: 802240 add a2, a2, a4 +40004923: c03340 sub a3, a3, a4 +40004926: ed3437 bltu a4, a3, 40004917 <_X_SPIEraseArea+0x63> +40004929: 0f13a6 blti a3, 1, 4000493c <_X_SPIEraseArea+0x88> +4000492c: 02ad mov.n a10, a2 +4000492e: ffdda5 call8 40004708 +40004931: f9fa56 bnez a10, 400048d4 <_X_SPIEraseArea+0x20> +40004934: 221b addi.n a2, a2, 1 +40004936: ffc332 addi a3, a3, -1 +40004939: fef356 bnez a3, 4000492c <_X_SPIEraseArea+0x78> +4000493c: 020c movi.n a2, 0 +4000493e: f01d retw.n + +40004940 <_XX_unk4940>: +40004940: 004136 entry a1, 32 +40004943: fd8081 l32r a8, 40003f44 <_X_SPI_read_status+0x48> +40004946: 0888 l32i.n a8, a8, 0 +40004948: 1839 s32i.n a3, a8, 4 +4000494a: 2849 s32i.n a4, a8, 8 +4000494c: 3859 s32i.n a5, a8, 12 +4000494e: 4869 s32i.n a6, a8, 16 +40004950: 0829 s32i.n a2, a8, 0 +40004952: 5879 s32i.n a7, a8, 20 +40004954: 020c movi.n a2, 0 +40004956: f01d retw.n +40004958: 4bac beqz.n a11, 40004980 <_XX_unk4940+0x40> +4000495a: 884000 excw +4000495d: 600040 neg a0, a4 +40004960: 4128 l32i.n a2, a1, 16 +40004962: 506000 witlb a0, a0 +40004965: ffdc bnez.n a15, 40004988 <_XX_unk4940+0x48> +40004967: 3f .byte 0x3f +40004968: 004136 entry a1, 32 +4000496b: 04a0a2 movi a10, 4 +4000496e: fffab1 l32r a11, 40004958 <_XX_unk4940+0x18> +40004971: 00a0c2 movi a12, 0 +40004974: fc7725 call8 400010e8 <_X_ets_isr_attach> +40004977: 10a0a2 movi a10, 16 +4000497a: fc77e5 call8 400010f8 <_X_ets_isr_mask> +4000497d: 0a0c movi.n a10, 0 +4000497f: 0b0c movi.n a11, 0 +40004981: 0c0c movi.n a12, 0 +40004983: fd7c movi.n a13, -1 +40004985: 000525 call8 400049d8 +40004988: 0a0c movi.n a10, 0 +4000498a: 0b0c movi.n a11, 0 +4000498c: 0c0c movi.n a12, 0 +4000498e: ffa0d2 movi a13, 255 +40004991: 000665 call8 400049f8 +40004994: fff221 l32r a2, 4000495c <_XX_unk4940+0x1c> +40004997: f1e631 l32r a3, 40001130 <_c_0x60003e00> +4000499a: fff151 l32r a5, 40004960 <_XX_unk4940+0x20> +4000499d: fff141 l32r a4, 40004964 <_XX_unk4940+0x24> +400049a0: 080c movi.n a8, 0 +400049a2: 0489 s32i.n a8, a4, 0 +400049a4: 1489 s32i.n a8, a4, 4 +400049a6: 02ad mov.n a10, a2 +400049a8: 0b0c movi.n a11, 0 +400049aa: 000925 call8 40004a3c +400049ad: 0020c0 memw +400049b0: 224b addi.n a2, a2, 4 +400049b2: 441b addi.n a4, a4, 1 +400049b4: a22392 l32i a9, a3, 0x288 +400049b7: 334b addi.n a3, a3, 4 +400049b9: 249790 extui a9, a9, 7, 3 +400049bc: 0f4492 s8i a9, a4, 15 +400049bf: e39257 bne a2, a5, 400049a6 <_XX_unk4940+0x66> +400049c2: fc7c movi.n a12, -1 +400049c4: f1dbb1 l32r a11, 40001130 <_c_0x60003e00> +400049c7: 0020c0 memw +400049ca: 936bc2 s32i a12, a11, 0x24c +400049cd: ffa0a2 movi a10, 255 +400049d0: 0020c0 memw +400049d3: 966ba2 s32i a10, a11, 0x258 +400049d6: f01d retw.n + +400049d8 : +400049d8: 004136 entry a1, 32 +400049db: f1d561 l32r a6, 40001130 <_c_0x60003e00> +400049de: 0020c0 memw +400049e1: 826622 s32i a2, a6, 0x208 +400049e4: 0020c0 memw +400049e7: 836632 s32i a3, a6, 0x20c +400049ea: 0020c0 memw +400049ed: 896642 s32i a4, a6, 0x224 +400049f0: 0020c0 memw +400049f3: 8a6652 s32i a5, a6, 0x228 +400049f6: f01d retw.n + +400049f8 : +400049f8: 004136 entry a1, 32 +400049fb: f1cd61 l32r a6, 40001130 <_c_0x60003e00> +400049fe: 0020c0 memw +40004a01: 856622 s32i a2, a6, 0x214 +40004a04: 0020c0 memw +40004a07: 866632 s32i a3, a6, 0x218 +40004a0a: 0020c0 memw +40004a0d: 8c6642 s32i a4, a6, 0x230 +40004a10: 0020c0 memw +40004a13: 8d6652 s32i a5, a6, 0x234 +40004a16: f01d retw.n +40004a18: 004136 entry a1, 32 +40004a1b: f1c521 l32r a2, 40001130 <_c_0x60003e00> +40004a1e: 0020c0 memw +40004a21: 8f2222 l32i a2, a2, 0x23c +40004a24: f01d retw.n +40004a26: 360000 excw +40004a29: 210041 l32r a4, 3ffcce2c <_start-0x331d4> +40004a2c: c0f1c1 l32r a12, 3fff4df0 <_start-0xb210> +40004a2f: 220020 orb b0, b0, b2 +40004a32: 1d9022 l16si a2, a0, 58 +40004a35: 0000f0 callx12 a0 +40004a38: ffdc60 excw +40004a3b: 3f .byte 0x3f +40004a3c: 004136 entry a1, 32 +40004a3f: fc66a5 call8 400010a8 <_X_ets_enter_critical> +40004a42: ffc6a1 l32r a10, 4000495c <_XX_unk4940+0x1c> +40004a45: 1192e0 slli a9, a2, 2 +40004a48: 8099a0 add a9, a9, a10 +40004a4b: 0020c0 memw +40004a4e: 006932 s32i a3, a9, 0 +40004a51: 27a082 movi a8, 39 +40004a54: 3e3827 bltu a8, a2, 40004a96 +40004a57: fff8a1 l32r a10, 40004a38 +40004a5a: 24b730 extui a11, a3, 7, 3 +40004a5d: c2aa add.n a12, a2, a10 +40004a5f: 004cb2 s8i a11, a12, 0 +40004a62: 0bfc bnez.n a11, 40004a96 +40004a64: 1c0c movi.n a12, 1 +40004a66: fb7c movi.n a11, -1 +40004a68: f0caa2 addi a10, a10, -16 +40004a6b: 12c2f6 bgeui a2, 32, 40004a81 +40004a6e: 0ad8 l32i.n a13, a10, 0 +40004a70: 401200 ssl a2 +40004a73: a1ec00 sll a14, a12 +40004a76: 30eeb0 xor a14, a14, a11 +40004a79: 10dde0 and a13, a13, a14 +40004a7c: 0ad9 s32i.n a13, a10, 0 +40004a7e: 000506 j 40004a96 +40004a81: 012af2 l32i a15, a10, 4 +40004a84: e0c292 addi a9, a2, -32 +40004a87: 401900 ssl a9 +40004a8a: a18c00 sll a8, a12 +40004a8d: 3088b0 xor a8, a8, a11 +40004a90: 10ff80 and a15, a15, a8 +40004a93: 016af2 s32i a15, a10, 4 +40004a96: fc6265 call8 400010bc <_X_ets_exit_critical> +40004a99: 000090 retw +40004a9c: 004136 entry a1, 32 +40004a9f: ffaf81 l32r a8, 4000495c <_XX_unk4940+0x1c> +40004aa2: 1152e0 slli a5, a2, 2 +40004aa5: 558a add.n a5, a5, a8 +40004aa7: 0020c0 memw +40004aaa: 0558 l32i.n a5, a5, 0 +40004aac: 732c movi.n a3, 39 +40004aae: 163327 bltu a3, a2, 40004ac8 +40004ab1: ffe131 l32r a3, 40004a38 +40004ab4: 223a add.n a2, a2, a3 +40004ab6: 000222 l8ui a2, a2, 0 +40004ab9: 7fac32 movi a3, 0xfffffc7f +40004abc: 103530 and a3, a5, a3 +40004abf: 112290 slli a2, a2, 7 +40004ac2: 202230 or a2, a2, a3 +40004ac5: f01d retw.n +40004ac7: 052d00 extui a2, a0, 29, 1 +40004aca: f01d retw.n +40004acc: 004136 entry a1, 32 +40004acf: ffa521 l32r a2, 40004964 <_XX_unk4940+0x24> +40004ad2: 0228 l32i.n a2, a2, 0 +40004ad4: f01d retw.n +40004ad6: 360000 excw +40004ad9: 210041 l32r a4, 3ffccedc <_start-0x33124> +40004adc: 28ffa2 s32ri a10, a15, 160 +40004adf: f01d12 l16ui a1, a13, 0x1e0 +40004ae2: 360000 excw +40004ae5: 250041 l32r a4, 3ffcdee8 <_start-0x32118> +40004ae8: fc5c movi.n a12, 95 +40004aea: f19191 l32r a9, 40001130 <_c_0x60003e00> +40004aed: a09290 addx4 a9, a2, a9 +40004af0: 0020c0 memw +40004af3: 7facb2 movi a11, 0xfffffc7f +40004af6: a22982 l32i a8, a9, 0x288 +40004af9: 11a390 slli a10, a3, 7 +40004afc: 1088b0 and a8, a8, a11 +40004aff: 2088a0 or a8, a8, a10 +40004b02: 0020c0 memw +40004b05: a26982 s32i a8, a9, 0x288 +40004b08: fc5b25 call8 400010bc <_X_ets_exit_critical> +40004b0b: f01d retw.n +40004b0d: 000000 ill +40004b10: 004136 entry a1, 32 +40004b13: fc5965 call8 400010a8 <_X_ets_enter_critical> +40004b16: ff93b1 l32r a11, 40004964 <_XX_unk4940+0x24> +40004b19: ffaf92 movi a9, -1 +40004b1c: 002b82 l32i a8, a11, 0 +40004b1f: 309290 xor a9, a2, a9 +40004b22: 10a890 and a10, a8, a9 +40004b25: 006ba2 s32i a10, a11, 0 +40004b28: 0c0897 bnone a8, a9, 40004b38 +40004b2b: f181d1 l32r a13, 40001130 <_c_0x60003e00> +40004b2e: 0020c0 memw +40004b31: 926da2 s32i a10, a13, 0x248 +40004b34: 0c0c movi.n a12, 0 +40004b36: 0bc9 s32i.n a12, a11, 0 +40004b38: ffc041 l32r a4, 40004a38 +40004b3b: 030c movi.n a3, 0 +40004b3d: 075237 bbc a2, a3, 40004b48 +40004b40: 03ad mov.n a10, a3 +40004b42: 0004b2 l8ui a11, a4, 0 +40004b45: fff9e5 call8 40004ae4 +40004b48: 01c442 addi a4, a4, 1 +40004b4b: 01c332 addi a3, a3, 1 +40004b4e: ebc366 bnei a3, 32, 40004b3d +40004b51: fc56a5 call8 400010bc <_X_ets_exit_critical> +40004b54: 000090 retw +40004b57: 413600 srli a3, a0, 6 +40004b5a: 54e500 extui a14, a0, 5, 6 +40004b5d: b1fc bnez.n a1, 40004b9c +40004b5f: 92ff81 l32r a8, 3ffe975c <_start-0x168a4> +40004b62: af .byte 0xaf +40004b63: ff .byte 0xff +40004b64: 012b82 l32i a8, a11, 4 +40004b67: 309290 xor a9, a2, a9 +40004b6a: 10a890 and a10, a8, a9 +40004b6d: 016ba2 s32i a10, a11, 4 +40004b70: 0c0897 bnone a8, a9, 40004b80 +40004b73: f16fd1 l32r a13, 40001130 <_c_0x60003e00> +40004b76: 0020c0 memw +40004b79: 956da2 s32i a10, a13, 0x254 +40004b7c: 0c0c movi.n a12, 0 +40004b7e: 1bc9 s32i.n a12, a11, 4 +40004b80: ffae41 l32r a4, 40004a38 +40004b83: 030c movi.n a3, 0 +40004b85: 085237 bbc a2, a3, 40004b91 +40004b88: 20c3a2 addi a10, a3, 32 +40004b8b: 2004b2 l8ui a11, a4, 32 +40004b8e: fff565 call8 40004ae4 +40004b91: 01c442 addi a4, a4, 1 +40004b94: 01c332 addi a3, a3, 1 +40004b97: ea8366 bnei a3, 8, 40004b85 +40004b9a: fc5225 call8 400010bc <_X_ets_exit_critical> +40004b9d: 000090 retw +40004ba0: 004136 entry a1, 32 +40004ba3: ff7041 l32r a4, 40004964 <_XX_unk4940+0x24> +40004ba6: 3439 s32i.n a3, a4, 12 +40004ba8: 2429 s32i.n a2, a4, 8 +40004baa: f01d retw.n +40004bac: 004136 entry a1, 32 +40004baf: f16051 l32r a5, 40001130 <_c_0x60003e00> +40004bb2: 0020c0 memw +40004bb5: 912532 l32i a3, a5, 0x244 +40004bb8: 0020c0 memw +40004bbb: 942522 l32i a2, a5, 0x250 +40004bbe: 040c movi.n a4, 0 +40004bc0: 065347 bbc a3, a4, 40004bca +40004bc3: 04ad mov.n a10, a4 +40004bc5: 0b0c movi.n a11, 0 +40004bc7: fff1e5 call8 40004ae4 +40004bca: 441b addi.n a4, a4, 1 +40004bcc: f0c466 bnei a4, 32, 40004bc0 +40004bcf: ff6561 l32r a6, 40004964 <_XX_unk4940+0x24> +40004bd2: 0020c0 memw +40004bd5: 936532 s32i a3, a5, 0x24c +40004bd8: 0698 l32i.n a9, a6, 0 +40004bda: 89ac beqz.n a9, 40004c06 +40004bdc: 208390 or a8, a3, a9 +40004bdf: 006682 s32i a8, a6, 0 +40004be2: 040c movi.n a4, 0 +40004be4: 085247 bbc a2, a4, 40004bf0 +40004be7: 20c4a2 addi a10, a4, 32 +40004bea: 00a0b2 movi a11, 0 +40004bed: ffef65 call8 40004ae4 +40004bf0: 441b addi.n a4, a4, 1 +40004bf2: ee8466 bnei a4, 8, 40004be4 +40004bf5: 0020c0 memw +40004bf8: 966522 s32i a2, a5, 0x258 +40004bfb: 1698 l32i.n a9, a6, 4 +40004bfd: 499c beqz.n a9, 40004c15 +40004bff: 20a290 or a10, a2, a9 +40004c02: 16a9 s32i.n a10, a6, 4 +40004c04: f01d retw.n +40004c06: 03ad mov.n a10, a3 +40004c08: 0639 s32i.n a3, a6, 0 +40004c0a: 2688 l32i.n a8, a6, 8 +40004c0c: 36b8 l32i.n a11, a6, 12 +40004c0e: 0008e0 callx8 a8 +40004c11: fff346 j 40004be2 +40004c14: 02ad00 andb b10, b13, b0 +40004c17: 1629 s32i.n a2, a6, 4 +40004c19: 2688 l32i.n a8, a6, 8 +40004c1b: 36b8 l32i.n a11, a6, 12 +40004c1d: 0008e0 callx8 a8 +40004c20: f01d retw.n +40004c22: 360000 excw +40004c25: 250041 l32r a4, 3ffce028 <_start-0x31fd8> +40004c28: fc48 l32i.n a4, a12, 60 +40004c2a: f14191 l32r a9, 40001130 <_c_0x60003e00> +40004c2d: a09290 addx4 a9, a2, a9 +40004c30: 0020c0 memw +40004c33: 7facb2 movi a11, 0xfffffc7f +40004c36: a22982 l32i a8, a9, 0x288 +40004c39: 11a390 slli a10, a3, 7 +40004c3c: 1088b0 and a8, a8, a11 +40004c3f: 2088a0 or a8, a8, a10 +40004c42: 00a4a2 movi a10, 0x400 +40004c45: 2088a0 or a8, a8, a10 +40004c48: 0020c0 memw +40004c4b: a26982 s32i a8, a9, 0x288 +40004c4e: fc46e5 call8 400010bc <_X_ets_exit_critical> +40004c51: f01d retw.n +40004c53: 413600 srli a3, a0, 6 +40004c56: 030c00 rsr.scompare1 a0 +40004c59: 7fa872 movi a7, 0xfffff87f +40004c5c: f13541 l32r a4, 40001130 <_c_0x60003e00> +40004c5f: 822c movi.n a2, 40 +40004c61: 19a276 loopgtz a2, 40004c7e +40004c64: a06340 addx4 a6, a3, a4 +40004c67: 0020c0 memw +40004c6a: a22652 l32i a5, a6, 0x288 +40004c6d: 0865a7 bbci a5, 10, 40004c79 +40004c70: 108570 and a8, a5, a7 +40004c73: 0020c0 memw +40004c76: a26682 s32i a8, a6, 0x288 +40004c79: 331b addi.n a3, a3, 1 +40004c7b: 743030 extui a3, a3, 0, 8 +40004c7e: f01d retw.n + +40004c80 <_c_0xbfffffff>: +40004c80: ff .byte 0xff +40004c81: ff .byte 0xff +40004c82: ff .byte 0xff +40004c83: bf .byte 0xbf +40004c84: fff000 excw +40004c87: ff .byte 0xff + +40004c88 <_c_0xff000fff>: +40004c88: ff .byte 0xff +40004c89: 0f .byte 0xf +40004c8a: 36ff00 excw + +40004c8c : +40004c8c: 006136 entry a1, 48 +40004c8f: b4f060 extui a15, a6, 0, 12 +40004c92: 00a092 movi a9, 0 +40004c95: fffae1 l32r a14, 40004c80 <_c_0xbfffffff> +40004c98: eebad1 l32r a13, 40000780 <_c_0x7fffffff> +40004c9b: eea7c1 l32r a12, 40000738 <_c_0x80000000> +40004c9e: f70db1 l32r a11, 400028d4 <_c_0xdfffffff> +40004ca1: 036172 s32i a7, a1, 12 +40004ca4: fff8a1 l32r a10, 40004c84 <_c_0xbfffffff+0x4> +40004ca7: c27560 quou a7, a5, a6 +40004caa: 205110 or a5, a1, a1 +40004cad: 628076 loop a0, 40004d13 +40004cb0: 1249 s32i.n a4, a2, 4 +40004cb2: 0020c0 memw +40004cb5: 0238 l32i.n a3, a2, 0 +40004cb7: 1033a0 and a3, a3, a10 +40004cba: 2033f0 or a3, a3, a15 +40004cbd: 0020c0 memw +40004cc0: 0239 s32i.n a3, a2, 0 +40004cc2: 0020c0 memw +40004cc5: 0288 l32i.n a8, a2, 0 +40004cc7: 1088d0 and a8, a8, a13 +40004cca: 2088c0 or a8, a8, a12 +40004ccd: 0020c0 memw +40004cd0: 3138 l32i.n a3, a1, 12 +40004cd2: 0289 s32i.n a8, a2, 0 +40004cd4: 040316 beqz a3, 40004d18 +40004cd7: 0020c0 memw +40004cda: 0288 l32i.n a8, a2, 0 +40004cdc: 0020c0 memw +40004cdf: 0238 l32i.n a3, a2, 0 +40004ce1: b43030 extui a3, a3, 0, 12 +40004ce4: cab380 depbits a8, a3, 12, 12 +40004ce7: 0020c0 memw +40004cea: 0289 s32i.n a8, a2, 0 +40004cec: 0020c0 memw +40004cef: 0288 l32i.n a8, a2, 0 +40004cf1: 1088e0 and a8, a8, a14 +40004cf4: 0020c0 memw +40004cf7: 0289 s32i.n a8, a2, 0 +40004cf9: 0020c0 memw +40004cfc: 0288 l32i.n a8, a2, 0 +40004cfe: 1088b0 and a8, a8, a11 +40004d01: 0020c0 memw +40004d04: 464a add.n a4, a6, a4 +40004d06: 991b addi.n a9, a9, 1 +40004d08: 0289 s32i.n a8, a2, 0 +40004d0a: 2529 s32i.n a2, a5, 8 +40004d0c: 2558 l32i.n a5, a5, 8 +40004d0e: 22cb addi.n a2, a2, 12 +40004d10: 18b977 bgeu a9, a7, 40004d2c +40004d13: ffe586 j 40004cad +40004d16: c00000 sub a0, a0, a0 +40004d19: 810020 src a0, a0, a2 +40004d1c: ffdb addi.n a15, a15, 13 +40004d1e: 0238 l32i.n a3, a2, 0 +40004d20: 103380 and a3, a3, a8 +40004d23: 0020c0 memw +40004d26: 0239 s32i.n a3, a2, 0 +40004d28: fff006 j 40004cec +40004d2b: d19800 mul16s a9, a8, a0 +40004d2e: c1b8 l32i.n a11, a1, 48 +40004d30: 0a0c movi.n a10, 0 +40004d32: 25a9 s32i.n a10, a5, 8 +40004d34: 21a8 l32i.n a10, a1, 8 +40004d36: 0ba9 s32i.n a10, a11, 0 +40004d38: 0959 s32i.n a5, a9, 0 +40004d3a: f01d retw.n +40004d3c: 004136 entry a1, 32 +40004d3f: ffc332 addi a3, a3, -1 +40004d42: f43030 extui a3, a3, 0, 16 +40004d45: 019376 loopnez a3, 40004d4a +40004d48: 2228 l32i.n a2, a2, 8 +40004d4a: f01d retw.n +40004d4c: 004136 entry a1, 32 +40004d4f: 015410 slli a5, a4, 31 +40004d52: ee8b81 l32r a8, 40000780 <_c_0x7fffffff> +40004d55: 0020c0 memw +40004d58: 0268 l32i.n a6, a2, 0 +40004d5a: fb0460 depbits a6, a4, 31, 1 +40004d5d: 084d mov.n a4, a8 +40004d5f: 0020c0 memw +40004d62: 0269 s32i.n a6, a2, 0 +40004d64: 330b addi.n a3, a3, -1 +40004d66: f43030 extui a3, a3, 0, 16 +40004d69: 119376 loopnez a3, 40004d7e +40004d6c: 2228 l32i.n a2, a2, 8 +40004d6e: 0020c0 memw +40004d71: 0238 l32i.n a3, a2, 0 +40004d73: 103340 and a3, a3, a4 +40004d76: 203350 or a3, a3, a5 +40004d79: 0020c0 memw +40004d7c: 0239 s32i.n a3, a2, 0 +40004d7e: f01d retw.n + +40004d80 <_st_0x3fffdc90>: +40004d80: ffdc90 excw +40004d83: 3f .byte 0x3f +40004d84: ffdd50 excw +40004d87: 3f .byte 0x3f + +40004d88 <_XX_unk4d88>: +40004d88: 008136 entry a1, 64 +40004d8b: fffec1 l32r a12, 40004d84 <_st_0x3fffdc90+0x4> +40004d8e: 00a2d2 movi a13, 0x200 +40004d91: 14c182 addi a8, a1, 20 +40004d94: 10c192 addi a9, a1, 16 +40004d97: fffa21 l32r a2, 40004d80 <_st_0x3fffdc90> +40004d9a: 00a0f2 movi a15, 0 +40004d9d: 40a0b2 movi a11, 64 +40004da0: 20ebb0 or a14, a11, a11 +40004da3: 22f9 s32i.n a15, a2, 8 +40004da5: 0052b2 s16i a11, a2, 0 +40004da8: a28b addi.n a10, a2, 8 +40004daa: 32a9 s32i.n a10, a2, 12 +40004dac: 60a0b2 movi a11, 96 +40004daf: 60c2a2 addi a10, a2, 96 +40004db2: 0199 s32i.n a9, a1, 0 +40004db4: 1189 s32i.n a8, a1, 4 +40004db6: ffed65 call8 40004c8c +40004db9: 51c8 l32i.n a12, a1, 20 +40004dbb: 41d8 l32i.n a13, a1, 16 +40004dbd: 22d9 s32i.n a13, a2, 8 +40004dbf: cc8b addi.n a12, a12, 8 +40004dc1: 32c9 s32i.n a12, a2, 12 +40004dc3: f01d retw.n +40004dc5: 000000 ill +40004dc8: dc98 l32i.n a9, a12, 52 +40004dca: ff .byte 0xff +40004dcb: 3f .byte 0x3f +40004dcc: 004136 entry a1, 32 +40004dcf: ffec51 l32r a5, 40004d80 <_st_0x3fffdc90> +40004dd2: 2588 l32i.n a8, a5, 8 +40004dd4: 058816 beqz a8, 40004e30 <_XX_unk4d88+0xa8> +40004dd7: fc2d25 call8 400010a8 <_X_ets_enter_critical> +40004dda: 2548 l32i.n a4, a5, 8 +40004ddc: 2498 l32i.n a9, a4, 8 +40004dde: 026592 s32i a9, a5, 8 +40004de1: 04f916 beqz a9, 40004e34 <_XX_unk4d88+0xac> +40004de4: fc2d65 call8 400010bc <_X_ets_exit_critical> +40004de7: 00a0a2 movi a10, 0 +40004dea: 0ca0b2 movi a11, 12 +40004ded: 24a9 s32i.n a10, a4, 8 +40004def: 14a8 l32i.n a10, a4, 4 +40004df1: fdc625 call8 40002a54 +40004df4: f0a0f2 movi a15, 240 +40004df7: 14d8 l32i.n a13, a4, 4 +40004df9: 22cb addi.n a2, a2, 12 +40004dfb: 418820 srli a8, a2, 8 +40004dfe: 034d82 s8i a8, a13, 3 +40004e01: 024d22 s8i a2, a13, 2 +40004e04: 000de2 l8ui a14, a13, 0 +40004e07: 014d32 s8i a3, a13, 1 +40004e0a: 10eef0 and a14, a14, a15 +40004e0d: 004de2 s8i a14, a13, 0 +40004e10: 0020c0 memw +40004e13: dd2b addi.n a13, a13, 2 +40004e15: 04b8 l32i.n a11, a4, 0 +40004e17: 010dc2 l8ui a12, a13, 1 +40004e1a: 000dd2 l8ui a13, a13, 0 +40004e1d: 11cc80 slli a12, a12, 8 +40004e20: 20ccd0 or a12, a12, a13 +40004e23: cabcb0 depbits a11, a12, 12, 12 +40004e26: 0020c0 memw +40004e29: 04b9 s32i.n a11, a4, 0 +40004e2b: 042d mov.n a2, a4 +40004e2d: f01d retw.n +40004e2f: 020c00 andb b0, b12, b0 +40004e32: f01d retw.n +40004e34: ffe591 l32r a9, 40004dc8 <_XX_unk4d88+0x40> +40004e37: 3599 s32i.n a9, a5, 12 +40004e39: ffe9c6 j 40004de4 <_XX_unk4d88+0x5c> +40004e3c: 004136 entry a1, 32 +40004e3f: ee5081 l32r a8, 40000780 <_c_0x7fffffff> +40004e42: ee3df1 l32r a15, 40000738 <_c_0x80000000> +40004e45: 00a032 movi a3, 0 +40004e48: 0020c0 memw +40004e4b: 0022e2 l32i a14, a2, 0 +40004e4e: 10ee80 and a14, a14, a8 +40004e51: 20eef0 or a14, a14, a15 +40004e54: 0020c0 memw +40004e57: 02e9 s32i.n a14, a2, 0 +40004e59: ff89d1 l32r a13, 40004c80 <_c_0xbfffffff> +40004e5c: 0020c0 memw +40004e5f: 02c8 l32i.n a12, a2, 0 +40004e61: 10ccd0 and a12, a12, a13 +40004e64: 0020c0 memw +40004e67: 02c9 s32i.n a12, a2, 0 +40004e69: f69ab1 l32r a11, 400028d4 <_c_0xdfffffff> +40004e6c: 0020c0 memw +40004e6f: 02a8 l32i.n a10, a2, 0 +40004e71: 10aab0 and a10, a10, a11 +40004e74: 0020c0 memw +40004e77: 02a9 s32i.n a10, a2, 0 +40004e79: ff8391 l32r a9, 40004c88 <_c_0xff000fff> +40004e7c: 0020c0 memw +40004e7f: 0288 l32i.n a8, a2, 0 +40004e81: 108890 and a8, a8, a9 +40004e84: 0020c0 memw +40004e87: 0289 s32i.n a8, a2, 0 +40004e89: fc21e5 call8 400010a8 <_X_ets_enter_critical> +40004e8c: ffbda1 l32r a10, 40004d80 <_st_0x3fffdc90> +40004e8f: 2239 s32i.n a3, a2, 8 +40004e91: 3ab8 l32i.n a11, a10, 12 +40004e93: 928b addi.n a9, a2, 8 +40004e95: 0b29 s32i.n a2, a11, 0 +40004e97: 3a99 s32i.n a9, a10, 12 +40004e99: fc2225 call8 400010bc <_X_ets_exit_critical> +40004e9c: f01d retw.n +40004e9e: 360000 excw +40004ea1: cd0041 l32r a4, 3fff82a4 <_start-0x7d5c> +40004ea4: 03bd04 excw +40004ea7: 02ad mov.n a10, a2 +40004ea9: 209220 or a9, a2, a2 +40004eac: 05c216 beqz a2, 40004f0c <_XX_unk4d88+0x184> +40004eaf: ff7631 l32r a3, 40004c88 <_c_0xff000fff> +40004eb2: ff7321 l32r a2, 40004c80 <_c_0xbfffffff> +40004eb5: ee32f1 l32r a15, 40000780 <_c_0x7fffffff> +40004eb8: ee20e1 l32r a14, 40000738 <_c_0x80000000> +40004ebb: f686d1 l32r a13, 400028d4 <_c_0xdfffffff> +40004ebe: 00a042 movi a4, 0 +40004ec1: 438076 loop a0, 40004f08 <_XX_unk4d88+0x180> +40004ec4: 0020c0 memw +40004ec7: 0988 l32i.n a8, a9, 0 +40004ec9: 1088f0 and a8, a8, a15 +40004ecc: 2088e0 or a8, a8, a14 +40004ecf: 0020c0 memw +40004ed2: 0989 s32i.n a8, a9, 0 +40004ed4: 0020c0 memw +40004ed7: 0988 l32i.n a8, a9, 0 +40004ed9: 108820 and a8, a8, a2 +40004edc: 208840 or a8, a8, a4 +40004edf: 0020c0 memw +40004ee2: 0989 s32i.n a8, a9, 0 +40004ee4: 0020c0 memw +40004ee7: 0988 l32i.n a8, a9, 0 +40004ee9: 1088d0 and a8, a8, a13 +40004eec: 208840 or a8, a8, a4 +40004eef: 0020c0 memw +40004ef2: 0989 s32i.n a8, a9, 0 +40004ef4: 0020c0 memw +40004ef7: 0988 l32i.n a8, a9, 0 +40004ef9: 108830 and a8, a8, a3 +40004efc: 208840 or a8, a8, a4 +40004eff: 0020c0 memw +40004f02: 0989 s32i.n a8, a9, 0 +40004f04: 2998 l32i.n a9, a9, 8 +40004f06: 298c beqz.n a9, 40004f0c <_XX_unk4d88+0x184> +40004f08: ffed46 j 40004ec1 <_XX_unk4d88+0x139> +40004f0b: 8aa500 depbits a0, a5, 8, 11 +40004f0e: f01d00 subx8 a1, a13, a0 +40004f11: 000000 ill + +40004f14 <_XX_unk4f14>: +40004f14: 004136 entry a1, 32 +40004f17: 20b220 or a11, a2, a2 +40004f1a: ee1991 l32r a9, 40000780 <_c_0x7fffffff> +40004f1d: ee0681 l32r a8, 40000738 <_c_0x80000000> +40004f20: 0020c0 memw +40004f23: 0022f2 l32i a15, a2, 0 +40004f26: 10ff90 and a15, a15, a9 +40004f29: 20ff80 or a15, a15, a8 +40004f2c: 0020c0 memw +40004f2f: 0062f2 s32i a15, a2, 0 +40004f32: ff53e1 l32r a14, 40004c80 <_c_0xbfffffff> +40004f35: 0020c0 memw +40004f38: 02d8 l32i.n a13, a2, 0 +40004f3a: 10dde0 and a13, a13, a14 +40004f3d: 0020c0 memw +40004f40: 02d9 s32i.n a13, a2, 0 +40004f42: f664c1 l32r a12, 400028d4 <_c_0xdfffffff> +40004f45: 0020c0 memw +40004f48: 02a8 l32i.n a10, a2, 0 +40004f4a: 10aac0 and a10, a10, a12 +40004f4d: 0020c0 memw +40004f50: 02a9 s32i.n a10, a2, 0 +40004f52: ff4d91 l32r a9, 40004c88 <_c_0xff000fff> +40004f55: 02ad mov.n a10, a2 +40004f57: 0020c0 memw +40004f5a: 0288 l32i.n a8, a2, 0 +40004f5c: 108890 and a8, a8, a9 +40004f5f: 0020c0 memw +40004f62: 1c0c movi.n a12, 1 +40004f64: 0289 s32i.n a8, a2, 0 +40004f66: 008525 call8 400057b8 <_XX_unk57b8> +40004f69: f01d retw.n + ... + +40004f6c <_XX_unk4f6c>: +40004f6c: 006136 entry a1, 48 +40004f6f: 20a110 or a10, a1, a1 +40004f72: 04c1b2 addi a11, a1, 4 +40004f75: 0087e5 call8 400057f4 <_XX_unk57f4> +40004f78: 0021c2 l32i a12, a1, 0 +40004f7b: ff81a1 l32r a10, 40004d80 <_st_0x3fffdc90> +40004f7e: ac9c beqz.n a12, 40004f9c <_XX_unk4f6c+0x30> +40004f80: 6a98 l32i.n a9, a10, 24 +40004f82: 698c beqz.n a9, 40004f8c <_XX_unk4f6c+0x20> +40004f84: 29c9 s32i.n a12, a9, 8 +40004f86: 1188 l32i.n a8, a1, 4 +40004f88: 6a89 s32i.n a8, a10, 24 +40004f8a: f01d retw.n +40004f8c: 00a0b2 movi a11, 0 +40004f8f: 012192 l32i a9, a1, 4 +40004f92: 056ac2 s32i a12, a10, 20 +40004f95: 6a99 s32i.n a9, a10, 24 +40004f97: da1c movi.n a10, 29 +40004f99: fbffe5 call8 40000f98 <_XX_unk0f96> +40004f9c: f01d retw.n +40004f9e: 360000 excw +40004fa1: 410041 l32r a4, 3ffd53a4 <_start-0x2ac5c> +40004fa4: 32ff77 bbsi a15, 23, 40004fda <_XX_unk4fc8+0x12> +40004fa7: 201424 excw +40004faa: 329332 l16si a3, a3, 100 +40004fad: 1d1464 excw +40004fb0: 0000f0 callx12 a0 +40004fb3: 413600 srli a3, a0, 6 +40004fb6: 724100 excw +40004fb9: ff .byte 0xff +40004fba: 152432 l32i a3, a4, 84 +40004fbd: 933220 movnez a3, a2, a2 +40004fc0: 156432 s32i a3, a4, 84 +40004fc3: f01d retw.n +40004fc5: 000000 ill + +40004fc8 <_XX_unk4fc8>: +40004fc8: 006136 entry a1, 48 +40004fcb: 01ad mov.n a10, a1 +40004fcd: b14b addi.n a11, a1, 4 +40004fcf: 0087a5 call8 40005848 <_XX_unk5848> +40004fd2: 2e0a26 beqi a10, -1, 40005004 <_XX_unk4fc8+0x3c> +40004fd5: ff6aa1 l32r a10, 40004d80 <_st_0x3fffdc90> +40004fd8: 01c8 l32i.n a12, a1, 0 +40004fda: 162ad2 l32i a13, a10, 88 +40004fdd: 8a98 l32i.n a9, a10, 32 +40004fdf: 7d8c beqz.n a13, 40004fea <_XX_unk4fc8+0x22> +40004fe1: 0cad mov.n a10, a12 +40004fe3: 11b8 l32i.n a11, a1, 4 +40004fe5: 000de0 callx8 a13 +40004fe8: f01d retw.n +40004fea: 698c beqz.n a9, 40004ff4 <_XX_unk4fc8+0x2c> +40004fec: 29c9 s32i.n a12, a9, 8 +40004fee: 1188 l32i.n a8, a1, 4 +40004ff0: 8a89 s32i.n a8, a10, 32 +40004ff2: f01d retw.n +40004ff4: 02a0b2 movi a11, 2 +40004ff7: 012192 l32i a9, a1, 4 +40004ffa: 076ac2 s32i a12, a10, 28 +40004ffd: 8a99 s32i.n a9, a10, 32 +40004fff: da1c movi.n a10, 29 +40005001: fbf965 call8 40000f98 <_XX_unk0f96> +40005004: f01d retw.n + ... + +40005008 <_s_no_rds>: +40005008: ffcce4 excw +4000500b: 3f .byte 0x3f + +4000500c <_XX_unk500c>: +4000500c: 004136 entry a1, 32 +4000500f: 12a8 l32i.n a10, a2, 4 +40005011: 070a92 l8ui a9, a10, 7 +40005014: 060ab2 l8ui a11, a10, 6 +40005017: 119980 slli a9, a9, 8 +4000501a: 2099b0 or a9, a9, a11 +4000501d: 050ab2 l8ui a11, a10, 5 +40005020: 119980 slli a9, a9, 8 +40005023: 2099b0 or a9, a9, a11 +40005026: 040ab2 l8ui a11, a10, 4 +40005029: 119980 slli a9, a9, 8 +4000502c: 2099b0 or a9, a9, a11 +4000502f: 890b addi.n a8, a9, -1 +40005031: 0a8816 beqz a8, 400050dd <_XX_unk500c+0xd1> +40005034: 603926 beqi a9, 3, 40005098 <_XX_unk500c+0x8c> +40005037: fbc9b2 addi a11, a9, -5 +4000503a: 0e6b16 beqz a11, 40005124 <_XX_unk500c+0x118> +4000503d: 07a926 beqi a9, 12, 40005048 <_XX_unk500c+0x3c> +40005040: 02ad mov.n a10, a2 +40005042: ffed25 call8 40004f14 <_XX_unk4f14> +40005045: f01d retw.n +40005047: ca3200 depbits a0, a2, 12, 4 +4000504a: 420c movi.n a2, 4 +4000504c: 18ca add.n a1, a8, a12 +4000504e: 07a0b2 movi a11, 7 +40005051: 0ca0a2 movi a10, 12 +40005054: ffd765 call8 40004dcc <_XX_unk4d88+0x44> +40005057: 0a5d mov.n a5, a10 +40005059: 7acc bnez.n a10, 40005064 <_XX_unk500c+0x58> +4000505b: ffeba1 l32r a10, 40005008 <_s_no_rds> +4000505e: fd7a65 call8 40002804 +40005061: fff6c6 j 40005040 <_XX_unk500c+0x34> +40005064: 20b440 or a11, a4, a4 +40005067: 0603d2 l8ui a13, a3, 6 +4000506a: 0703c2 l8ui a12, a3, 7 +4000506d: 012aa2 l32i a10, a10, 4 +40005070: 11cc80 slli a12, a12, 8 +40005073: 0ccaa2 addi a10, a10, 12 +40005076: 20ccd0 or a12, a12, a13 +40005079: 0503d2 l8ui a13, a3, 5 +4000507c: 11cc80 slli a12, a12, 8 +4000507f: 20ccd0 or a12, a12, a13 +40005082: 0403d2 l8ui a13, a3, 4 +40005085: 11cc80 slli a12, a12, 8 +40005088: 20ccd0 or a12, a12, a13 +4000508b: fcd3a5 call8 40001dc4 <_X_ets_memcpy> +4000508e: 05bd mov.n a11, a5 +40005090: 05ad mov.n a10, a5 +40005092: 0013a5 call8 400051cc <_XX_unk51ac+0x20> +40005095: ffe9c6 j 40005040 <_XX_unk500c+0x34> +40005098: 0d0a82 l8ui a8, a10, 13 +4000509b: 0e0af2 l8ui a15, a10, 14 +4000509e: 0f0ae2 l8ui a14, a10, 15 +400050a1: 130ad2 l8ui a13, a10, 19 +400050a4: 11ee80 slli a14, a14, 8 +400050a7: 11dd80 slli a13, a13, 8 +400050aa: 20eef0 or a14, a14, a15 +400050ad: 120af2 l8ui a15, a10, 18 +400050b0: 11ee80 slli a14, a14, 8 +400050b3: 20ee80 or a14, a14, a8 +400050b6: 20ddf0 or a13, a13, a15 +400050b9: 110af2 l8ui a15, a10, 17 +400050bc: 11dd80 slli a13, a13, 8 +400050bf: 20ddf0 or a13, a13, a15 +400050c2: 100af2 l8ui a15, a10, 16 +400050c5: 11dd80 slli a13, a13, 8 +400050c8: 20ddf0 or a13, a13, a15 +400050cb: 0c0af2 l8ui a15, a10, 12 +400050ce: 11ee80 slli a14, a14, 8 +400050d1: 20eef0 or a14, a14, a15 +400050d4: 0020c0 memw +400050d7: 0ed9 s32i.n a13, a14, 0 +400050d9: ffd8c6 j 40005040 <_XX_unk500c+0x34> +400050dc: cab200 depbits a0, a2, 12, 12 +400050df: 0ae214 excw +400050e2: 0e .byte 0xe +400050e3: 110af2 l8ui a15, a10, 17 +400050e6: 130ac2 l8ui a12, a10, 19 +400050e9: 120ad2 l8ui a13, a10, 18 +400050ec: 11cc80 slli a12, a12, 8 +400050ef: 20ccd0 or a12, a12, a13 +400050f2: 11cc80 slli a12, a12, 8 +400050f5: 0f0ad2 l8ui a13, a10, 15 +400050f8: 20ccf0 or a12, a12, a15 +400050fb: 100af2 l8ui a15, a10, 16 +400050fe: 11dd80 slli a13, a13, 8 +40005101: 11cc80 slli a12, a12, 8 +40005104: 20dde0 or a13, a13, a14 +40005107: 0d0ae2 l8ui a14, a10, 13 +4000510a: 11dd80 slli a13, a13, 8 +4000510d: 20ccf0 or a12, a12, a15 +40005110: 20dde0 or a13, a13, a14 +40005113: 0c0ae2 l8ui a14, a10, 12 +40005116: 11dd80 slli a13, a13, 8 +40005119: 20dde0 or a13, a13, a14 +4000511c: 0dad mov.n a10, a13 +4000511e: fcca65 call8 40001dc4 <_X_ets_memcpy> +40005121: ffc6c6 j 40005040 <_XX_unk500c+0x34> +40005124: 0c0ab2 l8ui a11, a10, 12 +40005127: 0f0ac2 l8ui a12, a10, 15 +4000512a: 0e0ad2 l8ui a13, a10, 14 +4000512d: 11cc80 slli a12, a12, 8 +40005130: 20ccd0 or a12, a12, a13 +40005133: 0d0ad2 l8ui a13, a10, 13 +40005136: 11cc80 slli a12, a12, 8 +40005139: 20ccd0 or a12, a12, a13 +4000513c: 11ac80 slli a10, a12, 8 +4000513f: 20aab0 or a10, a10, a11 +40005142: fbfd25 call8 40001114 <_XX_set_0x3fffda30_0> +40005145: 02a0d2 movi a13, 2 +40005148: ff0ee1 l32r a14, 40004d80 <_st_0x3fffdc90> +4000514b: 00a0f2 movi a15, 0 +4000514e: 166ef2 s32i a15, a14, 88 +40005151: 0b6ed2 s32i a13, a14, 44 +40005154: ffba06 j 40005040 <_XX_unk500c+0x34> +40005157: 413600 srli a3, a0, 6 +4000515a: 092100 l32e a0, a1, -56 +4000515d: ff .byte 0xff +4000515e: b228 l32i.n a2, a2, 44 +40005160: f01d retw.n + ... + +40005164 <_fp_0x40004f6c>: +40005164: 4f6c movi.n a15, -28 +40005166: c84000 excw + +40005168 <_fp_0x40004fc8>: +40005168: 4fc8 l32i.n a12, a15, 16 +4000516a: 0c4000 excw +4000516d: 400050 excw + +40005170 <_X_sip_init_attach>: +40005170: 004136 entry a1, 32 +40005173: 02dd mov.n a13, a2 +40005175: fffba1 l32r a10, 40005164 <_fp_0x40004f6c> +40005178: fffcb1 l32r a11, 40005168 <_fp_0x40004fc8> +4000517b: ff0131 l32r a3, 40004d80 <_st_0x3fffdc90> +4000517e: 080c movi.n a8, 0 +40005180: 03cd mov.n a12, a3 +40005182: b389 s32i.n a8, a3, 44 +40005184: 003625 call8 400054e8 <_X_slc_init_attach> +40005187: c3a9 s32i.n a10, a3, 48 +40005189: ffbfe5 call8 40004d88 <_XX_unk4d88> +4000518c: c398 l32i.n a9, a3, 48 +4000518e: fff7a1 l32r a10, 4000516c <_fp_0x40004fc8+0x4> +40005191: 1663a2 s32i a10, a3, 88 +40005194: 001992 l16ui a9, a9, 0 +40005197: 015392 s16i a9, a3, 2 +4000519a: 004de5 call8 40005678 <_X_slc_enable> +4000519d: 1b0c movi.n a11, 1 +4000519f: b3b9 s32i.n a11, a3, 44 +400051a1: f01d retw.n +400051a3: 544400 extui a4, a0, 4, 6 +400051a6: 504000 iitlb a0 +400051a9: df .byte 0xdf +400051aa: ff .byte 0xff +400051ab: 3f .byte 0x3f + +400051ac <_XX_unk51ac>: +400051ac: 004136 entry a1, 32 +400051af: fffda1 l32r a10, 400051a4 <_X_sip_init_attach+0x34> +400051b2: 1da0b2 movi a11, 29 +400051b5: fffcc1 l32r a12, 400051a8 <_X_sip_init_attach+0x38> +400051b8: 04a0d2 movi a13, 4 +400051bb: fef191 l32r a9, 40004d80 <_st_0x3fffdc90> +400051be: 00a082 movi a8, 0 +400051c1: a989 s32i.n a8, a9, 40 +400051c3: 9989 s32i.n a8, a9, 36 +400051c5: fbd8e5 call8 40000f54 <_X_ets_task> +400051c8: f01d retw.n +400051ca: 360000 excw +400051cd: ad0041 l32r a4, 3fff05d0 <_start-0xfa30> +400051d0: eb8102 excw +400051d3: fe .byte 0xfe +400051d4: 1298 l32i.n a9, a2, 4 +400051d6: a8f8 l32i.n a15, a8, 40 +400051d8: 0849f2 s8i a15, a9, 8 +400051db: 41c8f0 srli a12, a15, 8 +400051de: 0949c2 s8i a12, a9, 9 +400051e1: ff1b addi.n a15, a15, 1 +400051e3: 41c8c0 srli a12, a12, 8 +400051e6: 0a49c2 s8i a12, a9, 10 +400051e9: 41c8c0 srli a12, a12, 8 +400051ec: 0b49c2 s8i a12, a9, 11 +400051ef: a8f9 s32i.n a15, a8, 40 +400051f1: 03bd mov.n a11, a3 +400051f3: fea3e1 l32r a14, 40004c80 <_c_0xbfffffff> +400051f6: ed5ad1 l32r a13, 40000760 <_c_0x40000000> +400051f9: 0020c0 memw +400051fc: 03c8 l32i.n a12, a3, 0 +400051fe: 10cce0 and a12, a12, a14 +40005201: 20ccd0 or a12, a12, a13 +40005204: 0020c0 memw +40005207: 03c9 s32i.n a12, a3, 0 +40005209: 994b addi.n a9, a9, 4 +4000520b: 0d0c movi.n a13, 0 +4000520d: 0020c0 memw +40005210: 0049d2 s8i a13, a9, 0 +40005213: 0020c0 memw +40005216: 0149d2 s8i a13, a9, 1 +40005219: 0c0c movi.n a12, 0 +4000521b: 0020c0 memw +4000521e: 0249c2 s8i a12, a9, 2 +40005221: 080c movi.n a8, 0 +40005223: 0020c0 memw +40005226: 034982 s8i a8, a9, 3 +40005229: 0050a5 call8 40005734 <_XX_unk5734> +4000522c: 020c movi.n a2, 0 +4000522e: f01d retw.n +40005230: 004136 entry a1, 32 +40005233: fed321 l32r a2, 40004d80 <_st_0x3fffdc90> +40005236: f01d retw.n +40005238: 004136 entry a1, 32 +4000523b: fed141 l32r a4, 40004d80 <_st_0x3fffdc90> +4000523e: 5488 l32i.n a8, a4, 20 +40005240: 010816 beqz a8, 40005254 <_XX_unk51ac+0xa8> +40005243: fbe665 call8 400010a8 <_X_ets_enter_critical> +40005246: 5428 l32i.n a2, a4, 20 +40005248: 050c movi.n a5, 0 +4000524a: 6459 s32i.n a5, a4, 24 +4000524c: 5459 s32i.n a5, a4, 20 +4000524e: fbe6e5 call8 400010bc <_X_ets_exit_critical> +40005251: 057256 bnez a2, 400052ac <_XX_unk51ac+0x100> +40005254: f01d retw.n +40005256: ffc9b2 addi a11, a9, -1 +40005259: 083b16 beqz a11, 400052e0 <_XX_unk51ac+0x134> +4000525c: fdc9c2 addi a12, a9, -3 +4000525f: 07dc16 beqz a12, 400052e0 <_XX_unk51ac+0x134> +40005262: 432966 bnei a9, 2, 400052a9 <_XX_unk51ac+0xfd> +40005265: 030ac2 l8ui a12, a10, 3 +40005268: 020ab2 l8ui a11, a10, 2 +4000526b: 11ac80 slli a10, a12, 8 +4000526e: 20aab0 or a10, a10, a11 +40005271: 0314b2 l16ui a11, a4, 6 +40005274: 0162a5 call8 400068a0 +40005277: ffca62 addi a6, a10, -1 +4000527a: 02ad mov.n a10, a2 +4000527c: f46060 extui a6, a6, 0, 16 +4000527f: 06bd mov.n a11, a6 +40005281: ffaba5 call8 40004d3c +40005284: 2a28 l32i.n a2, a10, 8 +40005286: 2a59 s32i.n a5, a10, 8 +40005288: 1224d2 l32i a13, a4, 72 +4000528b: 0abd mov.n a11, a10 +4000528d: 5d8c beqz.n a13, 40005296 <_XX_unk51ac+0xea> +4000528f: 06cd mov.n a12, a6 +40005291: 03ad mov.n a10, a3 +40005293: 000de0 callx8 a13 +40005296: 02bd mov.n a11, a2 +40005298: 2228 l32i.n a2, a2, 8 +4000529a: 2b59 s32i.n a5, a11, 8 +4000529c: 1324d2 l32i a13, a4, 76 +4000529f: 6d8c beqz.n a13, 400052a9 <_XX_unk51ac+0xfd> +400052a1: 0bad mov.n a10, a11 +400052a3: 01a0c2 movi a12, 1 +400052a6: 000de0 callx8 a13 +400052a9: fa7216 beqz a2, 40005254 <_XX_unk51ac+0xa8> +400052ac: 12a8 l32i.n a10, a2, 4 +400052ae: 000a92 l8ui a9, a10, 0 +400052b1: 023d mov.n a3, a2 +400052b3: 349090 extui a9, a9, 0, 4 +400052b6: f9c956 bnez a9, 40005256 <_XX_unk51ac+0xaa> +400052b9: 2228 l32i.n a2, a2, 8 +400052bb: 2359 s32i.n a5, a3, 8 +400052bd: 010ab2 l8ui a11, a10, 1 +400052c0: 1324d2 l32i a13, a4, 76 +400052c3: 118b66 bnei a11, 8, 400052d8 <_XX_unk51ac+0x12c> +400052c6: fdfd16 beqz a13, 400052a9 <_XX_unk51ac+0xfd> +400052c9: 03bd mov.n a11, a3 +400052cb: 20a330 or a10, a3, a3 +400052ce: 01a0c2 movi a12, 1 +400052d1: 000de0 callx8 a13 +400052d4: fff446 j 400052a9 <_XX_unk51ac+0xfd> +400052d7: 03ad00 excw +400052da: ffb625 call8 40004e3c <_XX_unk4d88+0xb4> +400052dd: fff206 j 400052a9 <_XX_unk51ac+0xfd> +400052e0: 030ac2 l8ui a12, a10, 3 +400052e3: 020ab2 l8ui a11, a10, 2 +400052e6: 11ac80 slli a10, a12, 8 +400052e9: 20aab0 or a10, a10, a11 +400052ec: 0314b2 l16ui a11, a4, 6 +400052ef: 015b25 call8 400068a0 +400052f2: f460a0 extui a6, a10, 0, 16 +400052f5: 20a330 or a10, a3, a3 +400052f8: 20b660 or a11, a6, a6 +400052fb: ffa425 call8 40004d3c +400052fe: 2a28 l32i.n a2, a10, 8 +40005300: 2a59 s32i.n a5, a10, 8 +40005302: 1224d2 l32i a13, a4, 72 +40005305: 0abd mov.n a11, a10 +40005307: f9ed16 beqz a13, 400052a9 <_XX_unk51ac+0xfd> +4000530a: 06cd mov.n a12, a6 +4000530c: 03ad mov.n a10, a3 +4000530e: 000de0 callx8 a13 +40005311: ffe506 j 400052a9 <_XX_unk51ac+0xfd> +40005314: ccec bnez.n a12, 40005344 <_XX_unk51ac+0x198> +40005316: ff .byte 0xff +40005317: 3f .byte 0x3f +40005318: 004136 entry a1, 32 +4000531b: fe9961 l32r a6, 40004d80 <_st_0x3fffdc90> +4000531e: 072682 l32i a8, a6, 28 +40005321: 014816 beqz a8, 40005339 <_XX_unk51ac+0x18d> +40005324: fbd825 call8 400010a8 <_X_ets_enter_critical> +40005327: 7628 l32i.n a2, a6, 28 +40005329: 030c movi.n a3, 0 +4000532b: 8639 s32i.n a3, a6, 32 +4000532d: 7639 s32i.n a3, a6, 28 +4000532f: fbd8e5 call8 400010bc <_X_ets_exit_critical> +40005332: 328c beqz.n a2, 40005339 <_XX_unk51ac+0x18d> +40005334: 050c movi.n a5, 0 +40005336: 001246 j 40005383 <_XX_unk51ac+0x1d7> +40005339: f01d retw.n +4000533b: 370000 excw +4000533e: 1c69 s32i.n a6, a12, 4 +40005340: 0b03a2 l8ui a10, a3, 11 +40005343: 11aa80 slli a10, a10, 8 +40005346: 20aab0 or a10, a10, a11 +40005349: 0903b2 l8ui a11, a3, 9 +4000534c: 11aa80 slli a10, a10, 8 +4000534f: 20aab0 or a10, a10, a11 +40005352: 0803b2 l8ui a11, a3, 8 +40005355: 11aa80 slli a10, a10, 8 +40005358: 20aab0 or a10, a10, a11 +4000535b: 96a9 s32i.n a10, a6, 36 +4000535d: 010556 bnez a5, 40005371 <_XX_unk51ac+0x1c5> +40005360: 0103b2 l8ui a11, a3, 1 +40005363: 0a6b17 bbci a11, 1, 40005371 <_XX_unk51ac+0x1c5> +40005366: 001165 call8 4000547c <_c_0x60017e00+0x4> +40005369: 4acc bnez.n a10, 40005371 <_XX_unk51ac+0x1c5> +4000536b: 000f65 call8 40005460 <_XX_unk51ac+0x2b4> +4000536e: 01a052 movi a5, 1 +40005371: 000392 l8ui a9, a3, 0 +40005374: 349090 extui a9, a9, 0, 4 +40005377: 04d916 beqz a9, 400053c8 <_XX_unk51ac+0x21c> +4000537a: 621926 beqi a9, 1, 400053e0 <_XX_unk51ac+0x234> +4000537d: 5f2926 beqi a9, 2, 400053e0 <_XX_unk51ac+0x234> +40005380: fb5216 beqz a2, 40005339 <_XX_unk51ac+0x18d> +40005383: 1238 l32i.n a3, a2, 4 +40005385: 024d mov.n a4, a2 +40005387: 010392 l8ui a9, a3, 1 +4000538a: 0a03b2 l8ui a11, a3, 10 +4000538d: 14a290 extui a10, a9, 2, 2 +40005390: fa9a56 bnez a10, 4000533d <_XX_unk51ac+0x191> +40005393: 96c8 l32i.n a12, a6, 36 +40005395: bc1b addi.n a11, a12, 1 +40005397: 96b9 s32i.n a11, a6, 36 +40005399: 0b03a2 l8ui a10, a3, 11 +4000539c: 0a03b2 l8ui a11, a3, 10 +4000539f: 11aa80 slli a10, a10, 8 +400053a2: 20aab0 or a10, a10, a11 +400053a5: 0903b2 l8ui a11, a3, 9 +400053a8: 11aa80 slli a10, a10, 8 +400053ab: 20aab0 or a10, a10, a11 +400053ae: 0803b2 l8ui a11, a3, 8 +400053b1: 11aa80 slli a10, a10, 8 +400053b4: 20aab0 or a10, a10, a11 +400053b7: a21ac7 beq a10, a12, 4000535d <_XX_unk51ac+0x1b1> +400053ba: 0abd mov.n a11, a10 +400053bc: 02dd mov.n a13, a2 +400053be: ffd5a1 l32r a10, 40005314 <_XX_unk51ac+0x168> +400053c1: fd4425 call8 40002804 +400053c4: ffe546 j 4000535d <_XX_unk51ac+0x1b1> +400053c7: 04ad00 extui a10, a0, 13, 1 +400053ca: 04bd mov.n a11, a4 +400053cc: 2428 l32i.n a2, a4, 8 +400053ce: 090c movi.n a9, 0 +400053d0: 142682 l32i a8, a6, 80 +400053d3: 2499 s32i.n a9, a4, 8 +400053d5: 0008e0 callx8 a8 +400053d8: 04ad mov.n a10, a4 +400053da: ffb3a5 call8 40004f14 <_XX_unk4f14> +400053dd: ffe7c6 j 40005380 <_XX_unk51ac+0x1d4> +400053e0: 0303a2 l8ui a10, a3, 3 +400053e3: 0203b2 l8ui a11, a3, 2 +400053e6: 11aa80 slli a10, a10, 8 +400053e9: 20aab0 or a10, a10, a11 +400053ec: 0216b2 l16ui a11, a6, 4 +400053ef: 014b25 call8 400068a0 +400053f2: 0a7d mov.n a7, a10 +400053f4: 04ad mov.n a10, a4 +400053f6: f4b070 extui a11, a7, 0, 16 +400053f9: ff9425 call8 40004d3c +400053fc: 2a28 l32i.n a2, a10, 8 +400053fe: 0d0c movi.n a13, 0 +40005400: 2ad9 s32i.n a13, a10, 8 +40005402: 0abd mov.n a11, a10 +40005404: 0020c0 memw +40005407: 0a98 l32i.n a9, a10, 0 +40005409: c32b addi.n a12, a3, 2 +4000540b: 0216d2 l16ui a13, a6, 4 +4000540e: 000ce2 l8ui a14, a12, 0 +40005411: 010cc2 l8ui a12, a12, 1 +40005414: 82d7d0 mull a13, a7, a13 +40005417: 11cc80 slli a12, a12, 8 +4000541a: 20cce0 or a12, a12, a14 +4000541d: c0ccd0 sub a12, a12, a13 +40005420: 0020c0 memw +40005423: fa24d1 l32r a13, 40003cb4 <_c_0x00ffffff> +40005426: 0aa8 l32i.n a10, a10, 0 +40005428: 10aad0 and a10, a10, a13 +4000542b: 41aca0 srli a10, a10, 12 +4000542e: aaca add.n a10, a10, a12 +40005430: caba90 depbits a9, a10, 12, 12 +40005433: 04ad mov.n a10, a4 +40005435: 0020c0 memw +40005438: 152682 l32i a8, a6, 84 +4000543b: 0b99 s32i.n a9, a11, 0 +4000543d: 0008e0 callx8 a8 +40005440: ffcf06 j 40005380 <_XX_unk51ac+0x1d4> +40005443: 413600 srli a3, a0, 6 +40005446: 029800 andb b9, b8, b0 +40005449: 398c beqz.n a9, 40005450 <_XX_unk51ac+0x2a4> +4000544b: 092926 beqi a9, 2, 40005458 <_XX_unk51ac+0x2ac> +4000544e: f01d retw.n +40005450: 12a8 l32i.n a10, a2, 4 +40005452: ffde65 call8 40005238 <_XX_unk51ac+0x8c> +40005455: f01d retw.n +40005457: 12a800 andbc b10, b8, b0 +4000545a: ffebe5 call8 40005318 <_XX_unk51ac+0x16c> +4000545d: f01d retw.n +4000545f: 413600 srli a3, a0, 6 +40005462: a0a200 addx4 a10, a2, a0 +40005465: a0b200 addx4 a11, a2, a0 +40005468: 962505 call0 3ff9b6bc <_start-0x64944> +4000546b: ff .byte 0xff +4000546c: 0abd mov.n a11, a10 +4000546e: ffd5e5 call8 400051cc <_XX_unk51ac+0x20> +40005471: f01d retw.n +40005473: 4e0000 excw +40005476: 006001 l32r a0, 3ffc55f8 <_start-0x3aa08> + +40005478 <_c_0x60017e00>: +40005478: 017e00 slli a7, a14, 32 +4000547b: 413660 srli a3, a6, 6 +4000547e: fd3100 excw +40005481: ff .byte 0xff +40005482: 0020c0 memw +40005485: 942332 l32i a3, a3, 0x250 +40005488: 181c movi.n a8, 17 +4000548a: 0ff377 bbsi a3, 23, 4000549d <_c_0x60017e00+0x25> +4000548d: fffa41 l32r a4, 40005478 <_c_0x60017e00> +40005490: 0020c0 memw +40005493: a52442 l32i a4, a4, 0x294 +40005496: 020c movi.n a2, 0 +40005498: 01c487 bnall a4, a8, 4000549d <_c_0x60017e00+0x25> +4000549b: f01d retw.n +4000549d: 120c movi.n a2, 1 +4000549f: f01d retw.n +400054a1: 000000 ill + +400054a4 <_st_0x3fffdf70>: +400054a4: ffdf70 excw +400054a7: 3f .byte 0x3f +400054a8: 004136 entry a1, 32 +400054ab: fffe51 l32r a5, 400054a4 <_st_0x3fffdf70> +400054ae: b549 s32i.n a4, a5, 44 +400054b0: 6539 s32i.n a3, a5, 24 +400054b2: 5529 s32i.n a2, a5, 20 +400054b4: f01d retw.n + ... + +400054b8 <_c_0x6000ae00>: +400054b8: 00ae00 any8 b0, b8:b9:b10:b11:b12:b13:b14:b15 +400054bb: 000060 excw + +400054bc <_c_0xf0000000>: +400054bc: 000000 ill +400054bf: 0017f0 movsp a15, a7 + +400054c0 <_c_0x02320017>: +400054c0: 320017 bnone a0, a1, 400054f6 <_X_slc_init_attach+0xe> +400054c3: 666602 s32i a0, a6, 0x198 + +400054c4 <_c_0x11116666>: +400054c4: 116666 bnei a6, 6, 400054d9 <_c_0x11116666+0x15> +400054c7: bfff11 l32r a1, 3fff54c4 <_start-0xab3c> +400054ca: ff .byte 0xff +400054cb: ff .byte 0xff +400054cc: ff .byte 0xff +400054cd: fffc01 l32r a0, 400054c0 <_c_0x02320017> +400054d0: 00c800 excw +400054d3: dfb000 excw +400054d6: ff .byte 0xff +400054d7: 3f .byte 0x3f +400054d8: 030003 excw +400054db: fffc00 excw +400054de: fffc bnez.n a15, 40005521 <_X_slc_init_attach+0x39> +400054e0: 800040 add a0, a0, a4 + ... + +400054e4 <_c_0x01110013>: +400054e4: 110013 excw +400054e7: 813601 l32r a0, 3ffe59c0 <_start-0x1a640> + +400054e8 <_X_slc_init_attach>: +400054e8: 008136 entry a1, 64 +400054eb: 029d mov.n a9, a2 +400054ed: 0e2c movi.n a14, 32 +400054ef: ffed21 l32r a2, 400054a4 <_st_0x3fffdf70> +400054f2: 0f0c movi.n a15, 0 +400054f4: 92f9 s32i.n a15, a2, 36 +400054f6: 82f9 s32i.n a15, a2, 32 +400054f8: 72f9 s32i.n a15, a2, 28 +400054fa: b249 s32i.n a4, a2, 44 +400054fc: 6239 s32i.n a3, a2, 24 +400054fe: 056292 s32i a9, a2, 20 +40005501: ffeda1 l32r a10, 400054b8 <_c_0x6000ae00> +40005504: f78bb1 l32r a11, 40003330 <_c_0x3feffe00> +40005507: ffed91 l32r a9, 400054bc <_c_0xf0000000> +4000550a: 0020c0 memw +4000550d: 952bd2 l32i a13, a11, 0x254 +40005510: 20dde0 or a13, a13, a14 +40005513: 0020c0 memw +40005516: 956bd2 s32i a13, a11, 0x254 +40005519: dfafc2 movi a12, -33 +4000551c: 0020c0 memw +4000551f: 952b82 l32i a8, a11, 0x254 +40005522: 1088c0 and a8, a8, a12 +40005525: 0020c0 memw +40005528: 956b82 s32i a8, a11, 0x254 +4000552b: 0545b6 bltui a5, 4, 40005534 <_X_slc_init_attach+0x4c> +4000552e: fcc582 addi a8, a5, -4 +40005531: 11f816 beqz a8, 40005654 <_l_slc_boot_mode_4> +40005534: 0020c0 memw +40005537: 812ab2 l32i a11, a10, 0x204 +4000553a: ffe1c1 l32r a12, 400054c0 <_c_0x02320017> +4000553d: 10bb90 and a11, a11, a9 +40005540: 20bbc0 or a11, a11, a12 +40005543: 0020c0 memw +40005546: 816ab2 s32i a11, a10, 0x204 +40005549: ffdec1 l32r a12, 400054c4 <_c_0x11116666> +4000554c: 0020c0 memw +4000554f: 806ac2 s32i a12, a10, 0x200 +40005552: 0043a5 call8 4000598c <_X_slc_set_host_io_max_window> +40005555: 3c0c movi.n a12, 3 +40005557: ffc741 l32r a4, 40005474 <_XX_unk51ac+0x2c8> +4000555a: ffc731 l32r a3, 40005478 <_c_0x60017e00> +4000555d: 0020c0 memw +40005560: 8023b2 l32i a11, a3, 0x200 +40005563: 20bbc0 or a11, a11, a12 +40005566: 0020c0 memw +40005569: 8063b2 s32i a11, a3, 0x200 +4000556c: ca7c movi.n a10, -4 +4000556e: 0020c0 memw +40005571: 802392 l32i a9, a3, 0x200 +40005574: 1099a0 and a9, a9, a10 +40005577: 0020c0 memw +4000557a: 806392 s32i a9, a3, 0x200 +4000557d: 10a382 movi a8, 0x310 +40005580: 0020c0 memw +40005583: 8023f2 l32i a15, a3, 0x200 +40005586: 20ff80 or a15, a15, a8 +40005589: 0020c0 memw +4000558c: 8063f2 s32i a15, a3, 0x200 +4000558f: ffcee1 l32r a14, 400054c8 <_c_0x11116666+0x4> +40005592: 0020c0 memw +40005595: 8023d2 l32i a13, a3, 0x200 +40005598: 10dde0 and a13, a13, a14 +4000559b: 0020c0 memw +4000559e: 8063d2 s32i a13, a3, 0x200 +400055a1: 00a1c2 movi a12, 0x100 +400055a4: 0020c0 memw +400055a7: 9e24b2 l32i a11, a4, 0x278 +400055aa: 20bbc0 or a11, a11, a12 +400055ad: 0020c0 memw +400055b0: 9e64b2 s32i a11, a4, 0x278 +400055b3: ffc6a1 l32r a10, 400054cc <_c_0x11116666+0x8> +400055b6: ffc691 l32r a9, 400054d0 <_c_0x11116666+0xc> +400055b9: 0020c0 memw +400055bc: c02482 l32i a8, a4, 0x300 +400055bf: 1088a0 and a8, a8, a10 +400055c2: 208890 or a8, a8, a9 +400055c5: 0020c0 memw +400055c8: cb0c movi.n a11, 12 +400055ca: 10c1f2 addi a15, a1, 16 +400055cd: 14c1e2 addi a14, a1, 20 +400055d0: c06482 s32i a8, a4, 0x300 +400055d3: ffc0a1 l32r a10, 400054d4 <_c_0x11116666+0x10> +400055d6: 10a2d2 movi a13, 0x210 +400055d9: 10cac2 addi a12, a10, 16 +400055dc: 0052d2 s16i a13, a2, 0 +400055df: 11e9 s32i.n a14, a1, 4 +400055e1: 01f9 s32i.n a15, a1, 0 +400055e3: 10a2e2 movi a14, 0x210 +400055e6: 01a0f2 movi a15, 1 +400055e9: ff6a25 call8 40004c8c +400055ec: ffbb81 l32r a8, 400054d8 <_c_0x11116666+0x14> +400055ef: 0020c0 memw +400055f2: 8023f2 l32i a15, a3, 0x200 +400055f5: 20ff80 or a15, a15, a8 +400055f8: 0020c0 memw +400055fb: 8063f2 s32i a15, a3, 0x200 +400055fe: ffb7e1 l32r a14, 400054dc <_c_0x11116666+0x18> +40005601: 0020c0 memw +40005604: 8023d2 l32i a13, a3, 0x200 +40005607: 10dde0 and a13, a13, a14 +4000560a: 0020c0 memw +4000560d: 8063d2 s32i a13, a3, 0x200 +40005610: 41a8 l32i.n a10, a1, 16 +40005612: 51b8 l32i.n a11, a1, 20 +40005614: 1c0c movi.n a12, 1 +40005616: 001a25 call8 400057b8 <_XX_unk57b8> +40005619: f9b9e1 l32r a14, 40003d00 <_c_0x00800000> +4000561c: fc7c movi.n a12, -1 +4000561e: 0020c0 memw +40005621: b864e2 s32i a14, a4, 0x2e0 +40005624: ffafd1 l32r a13, 400054e0 <_c_0x11116666+0x1c> +40005627: 0020c0 memw +4000562a: b464d2 s32i a13, a4, 0x2d0 +4000562d: 0020c0 memw +40005630: b264c2 s32i a12, a4, 0x2c8 +40005633: 0020c0 memw +40005636: 8463c2 s32i a12, a3, 0x210 +40005639: 00acb2 movi a11, 0xfffffc00 +4000563c: 0a1c movi.n a10, 16 +4000563e: 0020c0 memw +40005641: c62392 l32i a9, a3, 0x318 +40005644: 1099b0 and a9, a9, a11 +40005647: 2099a0 or a9, a9, a10 +4000564a: 0020c0 memw +4000564d: c66392 s32i a9, a3, 0x318 +40005650: f01d retw.n + ... + +40005654 <_l_slc_boot_mode_4>: +40005654: 0020c0 memw +40005657: 812af2 l32i a15, a10, 0x204 +4000565a: ffa281 l32r a8, 400054e4 <_c_0x01110013> +4000565d: 10ff90 and a15, a15, a9 +40005660: 20ff80 or a15, a15, a8 +40005663: 0020c0 memw +40005666: 816af2 s32i a15, a10, 0x204 +40005669: ffb706 j 40005549 <_X_slc_init_attach+0x61> +4000566c: ff .byte 0xff +4000566d: ff .byte 0xff +4000566e: cf .byte 0xcf +4000566f: ff .byte 0xff +40005670: 0058e0 excw +40005673: 840040 extui a0, a4, 0, 9 +40005676: 040a add.n a0, a4, a0 + +40005678 <_X_slc_enable>: +40005678: 004136 entry a1, 32 +4000567b: fffcc1 l32r a12, 4000566c <_l_slc_boot_mode_4+0x18> +4000567e: f72c91 l32r a9, 40003330 <_c_0x3feffe00> +40005681: ff7d21 l32r a2, 40005478 <_c_0x60017e00> +40005684: 0020c0 memw +40005687: 9822b2 l32i a11, a2, 0x260 +4000568a: 10bbc0 and a11, a11, a12 +4000568d: 0020c0 memw +40005690: 9862b2 s32i a11, a2, 0x260 +40005693: 20a0a2 movi a10, 32 +40005696: 0020c0 memw +40005699: 982282 l32i a8, a2, 0x260 +4000569c: 2088a0 or a8, a8, a10 +4000569f: 0020c0 memw +400056a2: 986282 s32i a8, a2, 0x260 +400056a5: 40a0f2 movi a15, 64 +400056a8: 0020c0 memw +400056ab: 9822e2 l32i a14, a2, 0x260 +400056ae: 20eef0 or a14, a14, a15 +400056b1: 0020c0 memw +400056b4: 9862e2 s32i a14, a2, 0x260 +400056b7: fd6c movi.n a13, -17 +400056b9: 0020c0 memw +400056bc: 9822c2 l32i a12, a2, 0x260 +400056bf: 10ccd0 and a12, a12, a13 +400056c2: 0020c0 memw +400056c5: 9862c2 s32i a12, a2, 0x260 +400056c8: f71bb1 l32r a11, 40003334 <_c_0xffff83ff> +400056cb: 00a4a2 movi a10, 0x400 +400056ce: 0020c0 memw +400056d1: 9c2982 l32i a8, a9, 0x270 +400056d4: 1088b0 and a8, a8, a11 +400056d7: 2088a0 or a8, a8, a10 +400056da: 0020c0 memw +400056dd: 1a0c movi.n a10, 1 +400056df: ffe4b1 l32r a11, 40005670 <_l_slc_boot_mode_4+0x1c> +400056e2: 00a0c2 movi a12, 0 +400056e5: 9c6982 s32i a8, a9, 0x270 +400056e8: fb9fe5 call8 400010e8 <_X_ets_isr_attach> +400056eb: ffe2d1 l32r a13, 40005674 <_l_slc_boot_mode_4+0x20> +400056ee: 0020c0 memw +400056f1: 8362d2 s32i a13, a2, 0x20c +400056f4: 2a0c movi.n a10, 2 +400056f6: fba0e5 call8 40001104 <_X_ets_isr_unmask> +400056f9: f01d retw.n + ... + +400056fc <_X_slc_select_tohost_gpio_mode>: +400056fc: 004136 entry a1, 32 +400056ff: ff6931 l32r a3, 400054a4 <_st_0x3fffdf70> +40005702: d329 s32i.n a2, a3, 52 +40005704: f01d retw.n + ... + +40005708 <_X_slc_select_tohost_gpio>: +40005708: 004136 entry a1, 32 +4000570b: 11b2f6 bgeui a2, 16, 40005720 <_X_slc_select_tohost_gpio+0x18> +4000570e: ff6541 l32r a4, 400054a4 <_st_0x3fffdf70> +40005711: 401200 ssl a2 +40005714: 130c movi.n a3, 1 +40005716: a13300 sll a3, a3 +40005719: 1c5432 s16i a3, a4, 56 +4000571c: f01d retw.n +4000571e: 910000 srl a0, a0 +40005721: 0cff61 l32r a6, 3ffc8b20 <_start-0x374e0> +40005724: 8208 l32i.n a0, a2, 32 +40005726: 1c59 s32i.n a5, a12, 4 +40005728: f01d retw.n +4000572a: 000000 ill +4000572d: 00c000 excw + +40005730 <_c_0xff300000>: +40005730: 300000 xor a0, a0, a0 +40005733: ff .byte 0xff + +40005734 <_XX_unk5734>: +40005734: 006136 entry a1, 48 +40005737: 026d mov.n a6, a2 +40005739: 040c movi.n a4, 0 +4000573b: 0020c0 memw +4000573e: 0149 s32i.n a4, a1, 0 +40005740: 06c216 beqz a2, 400057b0 <_XX_unk5734+0x7c> +40005743: 00a052 movi a5, 0 +40005746: f95b71 l32r a7, 40003cb4 <_c_0x00ffffff> +40005749: 108076 loop a0, 4000575d <_XX_unk5734+0x29> +4000574c: 0020c0 memw +4000574f: 0688 l32i.n a8, a6, 0 +40005751: 2668 l32i.n a6, a6, 8 +40005753: 108870 and a8, a8, a7 +40005756: 418c80 srli a8, a8, 12 +40005759: 585a add.n a5, a8, a5 +4000575b: 268c beqz.n a6, 40005761 <_XX_unk5734+0x2d> +4000575d: fffa06 j 40005749 <_XX_unk5734+0x15> +40005760: 20c000 or a12, a0, a0 +40005763: 615200 excw +40005766: 446100 extui a6, a0, 1, 5 +40005769: ff .byte 0xff +4000576a: f96571 l32r a7, 40003d00 <_c_0x00800000> +4000576d: 088076 loop a0, 40005779 <_XX_unk5734+0x45> +40005770: 0020c0 memw +40005773: b92692 l32i a9, a6, 0x2e4 +40005776: 020977 bnone a9, a7, 4000577c <_XX_unk5734+0x48> +40005779: fffc06 j 4000576d <_XX_unk5734+0x39> +4000577c: 0020c0 memw +4000577f: bd6622 s32i a2, a6, 0x2f4 +40005782: 0020c0 memw +40005785: be6632 s32i a3, a6, 0x2f8 +40005788: ffe9b1 l32r a11, 4000572c <_X_slc_select_tohost_gpio+0x24> +4000578b: f72fd1 l32r a13, 40003448 <_c_0x000fffff> +4000578e: 0020c0 memw +40005791: 01c8 l32i.n a12, a1, 0 +40005793: 10ccd0 and a12, a12, a13 +40005796: ffe6d1 l32r a13, 40005730 <_c_0xff300000> +40005799: 0020c0 memw +4000579c: b926a2 l32i a10, a6, 0x2e4 +4000579f: 10aad0 and a10, a10, a13 +400057a2: 20aac0 or a10, a10, a12 +400057a5: 20aab0 or a10, a10, a11 +400057a8: 0020c0 memw +400057ab: b966a2 s32i a10, a6, 0x2e4 +400057ae: f01d retw.n +400057b0: 050c movi.n a5, 0 +400057b2: ffeac6 j 40005761 <_XX_unk5734+0x2d> +400057b5: 000000 ill + +400057b8 <_XX_unk57b8>: +400057b8: 004136 entry a1, 32 +400057bb: ff2fb1 l32r a11, 40005478 <_c_0x60017e00> +400057be: f93ea1 l32r a10, 40003cb8 <_c_0x01000000> +400057c1: 088076 loop a0, 400057cd <_XX_unk57b8+0x15> +400057c4: 0020c0 memw +400057c7: b92b82 l32i a8, a11, 0x2e4 +400057ca: 0208a7 bnone a8, a10, 400057d0 <_XX_unk57b8+0x18> +400057cd: fffc06 j 400057c1 <_XX_unk57b8+0x9> +400057d0: 0020c0 memw +400057d3: bb6b22 s32i a2, a11, 0x2ec +400057d6: 0020c0 memw +400057d9: bc6b32 s32i a3, a11, 0x2f0 +400057dc: 0020c0 memw +400057df: b92b92 l32i a9, a11, 0x2e4 +400057e2: 2099a0 or a9, a9, a10 +400057e5: 0020c0 memw +400057e8: b96b92 s32i a9, a11, 0x2e4 +400057eb: f4a040 extui a10, a4, 0, 16 +400057ee: 001d65 call8 400059c4 <_X_slc_add_credits> +400057f1: f01d retw.n + ... + +400057f4 <_XX_unk57f4>: +400057f4: 004136 entry a1, 32 +400057f7: 00a082 movi a8, 0 +400057fa: ff1f61 l32r a6, 40005478 <_c_0x60017e00> +400057fd: 0020c0 memw +40005800: b92642 l32i a4, a6, 0x2e4 +40005803: 3974b7 bbci a4, 27, 40005840 <_XX_unk57f4+0x4c> +40005806: f951a1 l32r a10, 40003d4c <_c_0x02000000> +40005809: 0020c0 memw +4000580c: b92692 l32i a9, a6, 0x2e4 +4000580f: 2099a0 or a9, a9, a10 +40005812: 0020c0 memw +40005815: b96692 s32i a9, a6, 0x2e4 +40005818: 0020c0 memw +4000581b: c12672 l32i a7, a6, 0x304 +4000581e: 006272 s32i a7, a2, 0 +40005821: 0020c0 memw +40005824: f93721 l32r a2, 40003d00 <_c_0x00800000> +40005827: c22672 l32i a7, a6, 0x308 +4000582a: 006372 s32i a7, a3, 0 +4000582d: 088076 loop a0, 40005839 <_XX_unk57f4+0x45> +40005830: 0020c0 memw +40005833: b926b2 l32i a11, a6, 0x2e4 +40005836: 020b27 bnone a11, a2, 4000583c <_XX_unk57f4+0x48> +40005839: fffc06 j 4000582d <_XX_unk57f4+0x39> +4000583c: 2789 s32i.n a8, a7, 8 +4000583e: f01d retw.n +40005840: 0289 s32i.n a8, a2, 0 +40005842: 0389 s32i.n a8, a3, 0 +40005844: f01d retw.n + ... + +40005848 <_XX_unk5848>: +40005848: 004136 entry a1, 32 +4000584b: ff0b61 l32r a6, 40005478 <_c_0x60017e00> +4000584e: 0020c0 memw +40005851: b92642 l32i a4, a6, 0x2e4 +40005854: 7474c7 bbci a4, 28, 400058cc <_XX_unk5848+0x84> +40005857: f9dd91 l32r a9, 40003fcc <_c_0x04000000> +4000585a: 0020c0 memw +4000585d: b92682 l32i a8, a6, 0x2e4 +40005860: 208890 or a8, a8, a9 +40005863: 0020c0 memw +40005866: b96682 s32i a8, a6, 0x2e4 +40005869: 0020c0 memw +4000586c: bf2672 l32i a7, a6, 0x2fc +4000586f: 0279 s32i.n a7, a2, 0 +40005871: 0020c0 memw +40005874: f91171 l32r a7, 40003cb8 <_c_0x01000000> +40005877: c02682 l32i a8, a6, 0x300 +4000587a: 006382 s32i a8, a3, 0 +4000587d: 088076 loop a0, 40005889 <_XX_unk5848+0x41> +40005880: 0020c0 memw +40005883: b926a2 l32i a10, a6, 0x2e4 +40005886: 040a77 bnone a10, a7, 4000588e <_XX_unk5848+0x46> +40005889: fffc06 j 4000587d <_XX_unk5848+0x35> +4000588c: 0c0000 excw +4000588f: 286906 j 4000fa37 <__umoddi3+0x8bd3> +40005892: 002222 l32i a2, a2, 0 +40005895: f2ac beqz.n a2, 400058c8 <_XX_unk5848+0x80> +40005897: fcfa51 l32r a5, 40004c80 <_c_0xbfffffff> +4000589a: ebb931 l32r a3, 40000780 <_c_0x7fffffff> +4000589d: 118076 loop a0, 400058b2 <_XX_unk5848+0x6a> +400058a0: 0020c0 memw +400058a3: 02b8 l32i.n a11, a2, 0 +400058a5: 10bb30 and a11, a11, a3 +400058a8: 15beb0 extui a11, a11, 30, 2 +400058ab: 061b26 beqi a11, 1, 400058b5 <_XX_unk5848+0x6d> +400058ae: 2228 l32i.n a2, a2, 8 +400058b0: 429c beqz.n a2, 400058c8 <_XX_unk5848+0x80> +400058b2: fff9c6 j 4000589d <_XX_unk5848+0x55> +400058b5: 0020c0 memw +400058b8: 02c8 l32i.n a12, a2, 0 +400058ba: 10cc50 and a12, a12, a5 +400058bd: 20cc60 or a12, a12, a6 +400058c0: 0020c0 memw +400058c3: 02c9 s32i.n a12, a2, 0 +400058c5: fff946 j 400058ae <_XX_unk5848+0x66> +400058c8: 020c movi.n a2, 0 +400058ca: f01d retw.n +400058cc: f27c movi.n a2, -1 +400058ce: f01d retw.n +400058d0: ff .byte 0xff +400058d1: ff .byte 0xff +400058d2: fe .byte 0xfe +400058d3: ff .byte 0xff +400058d4: 020000 andb b0, b0, b0 +400058d7: 000004 excw +400058da: 000001 l32r a0, 3ffc58dc <_start-0x3a724> +400058dd: 000080 ret +400058e0: 004136 entry a1, 32 +400058e3: fffb71 l32r a7, 400058d0 <_XX_unk5848+0x88> +400058e6: fffb51 l32r a5, 400058d4 <_XX_unk5848+0x8c> +400058e9: fffb61 l32r a6, 400058d8 <_XX_unk5848+0x90> +400058ec: fee331 l32r a3, 40005478 <_c_0x60017e00> +400058ef: feed41 l32r a4, 400054a4 <_st_0x3fffdf70> +400058f2: 0020c0 memw +400058f5: 822322 l32i a2, a3, 0x208 +400058f8: 74b8 l32i.n a11, a4, 28 +400058fa: 07e216 beqz a2, 4000597c <_XX_unk5848+0x134> +400058fd: 4b8c beqz.n a11, 40005905 <_XX_unk5848+0xbd> +400058ff: 20a220 or a10, a2, a2 +40005902: 000be0 callx8 a11 +40005905: 0c0527 bnone a5, a2, 40005915 <_XX_unk5848+0xcd> +40005908: 0020c0 memw +4000590b: 846352 s32i a5, a3, 0x210 +4000590e: 5488 l32i.n a8, a4, 20 +40005910: b4a8 l32i.n a10, a4, 44 +40005912: 0008e0 callx8 a8 +40005915: 1062f7 bbci a2, 15, 40005929 <_XX_unk5848+0xe1> +40005918: 0020c0 memw +4000591b: fff091 l32r a9, 400058dc <_XX_unk5848+0x94> +4000591e: 846392 s32i a9, a3, 0x210 +40005921: 6488 l32i.n a8, a4, 24 +40005923: 0b24a2 l32i a10, a4, 44 +40005926: 0008e0 callx8 a8 +40005929: 1c7207 bbci a2, 16, 40005949 <_XX_unk5848+0x101> +4000592c: d498 l32i.n a9, a4, 52 +4000592e: 2a2926 beqi a9, 2, 4000595c <_XX_unk5848+0x114> +40005931: 373926 beqi a9, 3, 4000596c <_XX_unk5848+0x124> +40005934: 0020c0 memw +40005937: 8323a2 l32i a10, a3, 0x20c +4000593a: 10aa70 and a10, a10, a7 +4000593d: 0020c0 memw +40005940: 8363a2 s32i a10, a3, 0x20c +40005943: 0020c0 memw +40005946: 846362 s32i a6, a3, 0x210 +40005949: 2ff237 bbsi a2, 19, 4000597c <_XX_unk5848+0x134> +4000594c: 2ce2a7 bbsi a2, 10, 4000597c <_XX_unk5848+0x134> +4000594f: 84b8 l32i.n a11, a4, 32 +40005951: f9db16 beqz a11, 400058f2 <_XX_unk5848+0xaa> +40005954: 02ad mov.n a10, a2 +40005956: 000be0 callx8 a11 +40005959: ffe546 j 400058f2 <_XX_unk5848+0xaa> +4000595c: 1c14b2 l16ui a11, a4, 56 +4000595f: 0020c0 memw +40005962: edf3c1 l32r a12, 40001130 <_c_0x60003e00> +40005965: 836cb2 s32i a11, a12, 0x20c +40005968: fff206 j 40005934 <_XX_unk5848+0xec> +4000596b: 14d200 extui a13, a0, 2, 2 +4000596e: c01c movi.n a0, 28 +40005970: e10020 excw +40005973: ef .byte 0xef +40005974: d2ed excw +40005976: 6e .byte 0x6e +40005977: ee0682 l8ui a8, a6, 238 +4000597a: ff .byte 0xff +4000597b: 94b800 extui a11, a0, 8, 10 +4000597e: 3b8c beqz.n a11, 40005985 <_XX_unk5848+0x13d> +40005980: 0a0c movi.n a10, 0 +40005982: 000be0 callx8 a11 +40005985: f01d retw.n + ... + +40005988 <_c_0xfffff0c0>: +40005988: fff0c0 excw +4000598b: ff .byte 0xff + +4000598c <_X_slc_set_host_io_max_window>: +4000598c: 004136 entry a1, 32 +4000598f: feba31 l32r a3, 40005478 <_c_0x60017e00> +40005992: 0020c0 memw +40005995: fffc41 l32r a4, 40005988 <_c_0xfffff0c0> +40005998: 9d2322 l32i a2, a3, 0x274 +4000599b: 102240 and a2, a2, a4 +4000599e: 20a742 movi a4, 0x720 +400059a1: 202240 or a2, a2, a4 +400059a4: 0020c0 memw +400059a7: 9d6322 s32i a2, a3, 0x274 +400059aa: f01d retw.n + +400059ac <_X_slc_init_credit>: +400059ac: 004136 entry a1, 32 +400059af: ede381 l32r a8, 4000113c <_c_0x1000> +400059b2: feb191 l32r a9, 40005478 <_c_0x60017e00> +400059b5: 0020c0 memw +400059b8: 1a0c movi.n a10, 1 +400059ba: 956982 s32i a8, a9, 0x254 +400059bd: 000065 call8 400059c4 <_X_slc_add_credits> +400059c0: f01d retw.n + ... + +400059c4 <_X_slc_add_credits>: +400059c4: 004136 entry a1, 32 +400059c7: f52331 l32r a3, 40002e54 <_c_0x3fffdb00+0x4> +400059ca: feab41 l32r a4, 40005478 <_c_0x60017e00> +400059cd: 0ab230 depbits a3, a2, 0, 12 +400059d0: 0020c0 memw +400059d3: 956432 s32i a3, a4, 0x254 +400059d6: f01d retw.n + +400059d8 <_X_xtos_set_interrupt_handler_arg>: +400059d8: 004136 entry a1, 32 +400059db: 02e296 bltz a2, 40005a0d <_X_xtos_set_interrupt_handler_arg+0x35> +400059de: 2bc2e6 bgei a2, 32, 40005a0d <_X_xtos_set_interrupt_handler_arg+0x35> +400059e1: eb6851 l32r a5, 40000784 <_c_0x7fffffff+0x4> +400059e4: 1182d0 slli a8, a2, 3 +400059e7: 525a add.n a5, a2, a5 +400059e9: 000552 l8ui a5, a5, 0 +400059ec: eb5471 l32r a7, 4000073c <_c_0x80000000+0x4> +400059ef: 1a75f6 bgeui a5, 7, 40005a0d <_X_xtos_set_interrupt_handler_arg+0x35> +400059f2: c07780 sub a7, a7, a8 +400059f5: eb6481 l32r a8, 40000788 <_c_0x7fffffff+0x8> +400059f8: 3e2762 l32i a6, a7, 248 +400059fb: 23dc bnez.n a3, 40005a11 <_X_xtos_set_interrupt_handler_arg+0x39> +400059fd: 3e6782 s32i a8, a7, 248 +40005a00: 3f6722 s32i a2, a7, 252 +40005a03: c09680 sub a9, a6, a8 +40005a06: 020c movi.n a2, 0 +40005a08: 932690 movnez a2, a6, a9 +40005a0b: f01d retw.n +40005a0d: 020c movi.n a2, 0 +40005a0f: f01d retw.n +40005a11: 3e6732 s32i a3, a7, 248 +40005a14: 3f6742 s32i a4, a7, 252 +40005a17: c0a680 sub a10, a6, a8 +40005a1a: 020c movi.n a2, 0 +40005a1c: 9326a0 movnez a2, a6, a10 +40005a1f: f01d retw.n +40005a21: 000000 ill + +40005a24 <_X_xtos_set_interrupt_handler>: +40005a24: 004136 entry a1, 32 +40005a27: 03bd mov.n a11, a3 +40005a29: 02ad mov.n a10, a2 +40005a2b: 02cd mov.n a12, a2 +40005a2d: fffaa5 call8 400059d8 <_X_xtos_set_interrupt_handler_arg> +40005a30: 0a2d mov.n a2, a10 +40005a32: f01d retw.n + +40005a34 <_X_xtos_ints_on>: +40005a34: 002136 entry a1, 16 +40005a37: eb3f41 l32r a4, 40000734 <_c_0x3fffc210> +40005a3a: 006670 rsil a7, 6 +40005a3d: 0438 l32i.n a3, a4, 0 +40005a3f: 1468 l32i.n a6, a4, 4 +40005a41: 205320 or a5, a3, a2 +40005a44: 0459 s32i.n a5, a4, 0 +40005a46: 105560 and a5, a5, a6 +40005a49: 13e450 wsr.intenable a5 +40005a4c: 13e670 wsr.ps a7 +40005a4f: 002010 rsync +40005a52: 032d mov.n a2, a3 +40005a54: f01d retw.n + ... + +40005a58 <_X_xtos_ints_off>: +40005a58: 002136 entry a1, 16 +40005a5b: eb3641 l32r a4, 40000734 <_c_0x3fffc210> +40005a5e: 006670 rsil a7, 6 +40005a61: 0438 l32i.n a3, a4, 0 +40005a63: 1468 l32i.n a6, a4, 4 +40005a65: 205320 or a5, a3, a2 +40005a68: 305520 xor a5, a5, a2 +40005a6b: 0459 s32i.n a5, a4, 0 +40005a6d: 105560 and a5, a5, a6 +40005a70: 13e450 wsr.intenable a5 +40005a73: 13e670 wsr.ps a7 +40005a76: 002010 rsync +40005a79: 032d mov.n a2, a3 +40005a7b: f01d retw.n +40005a7d: 000000 ill + +40005a80 <_XX_xtos_exc_unk5a80>: +40005a80: 034820 rsr.windowbase a2 +40005a83: 221b addi.n a2, a2, 1 +40005a85: 400200 ssr a2 +40005a88: 034930 rsr.windowstart a3 +40005a8b: 912030 srl a2, a3 +40005a8e: a13300 sll a3, a3 +40005a91: 0bf3d6 bgez a3, 40005b54 <_XX_xtos_exc_unk5a80+0xd4> +40005a94: f53030 extui a3, a3, 16, 16 +40005a97: 202230 or a2, a2, a3 +40005a9a: 603020 neg a3, a2 +40005a9d: 103320 and a3, a3, a2 +40005aa0: 40f330 nsau a3, a3 +40005aa3: 401300 ssl a3 +40005aa6: 912020 srl a2, a2 +40005aa9: 134920 wsr.windowstart a2 +40005aac: 034820 rsr.windowbase a2 +40005aaf: 1fc222 addi a2, a2, 31 +40005ab2: c03230 sub a3, a2, a3 +40005ab5: 134830 wsr.windowbase a3 +40005ab8: 002010 rsync +40005abb: 034920 rsr.windowstart a2 +40005abe: 07a216 beqz a2, 40005b3c <_XX_xtos_exc_unk5a80+0xbc> +40005ac1: 08e207 bbsi a2, 0, 40005acd <_XX_xtos_exc_unk5a80+0x4d> +40005ac4: 19e217 bbsi a2, 1, 40005ae1 <_XX_xtos_exc_unk5a80+0x61> +40005ac7: 3ae227 bbsi a2, 2, 40005b05 <_XX_xtos_exc_unk5a80+0x85> +40005aca: 0022c6 j 40005b59 <_XX_xtos_exc_unk5a80+0xd9> +40005acd: f0c932 addi a3, a9, -16 +40005ad0: 0349 s32i.n a4, a3, 0 +40005ad2: 1359 s32i.n a5, a3, 4 +40005ad4: 2369 s32i.n a6, a3, 8 +40005ad6: 3379 s32i.n a7, a3, 12 +40005ad8: 416120 srli a6, a2, 1 +40005adb: 408010 rotw 1 +40005ade: fff706 j 40005abe <_XX_xtos_exc_unk5a80+0x3e> +40005ae1: f0cd32 addi a3, a13, -16 +40005ae4: 0349 s32i.n a4, a3, 0 +40005ae6: 1359 s32i.n a5, a3, 4 +40005ae8: 2369 s32i.n a6, a3, 8 +40005aea: 3379 s32i.n a7, a3, 12 +40005aec: f4c532 addi a3, a5, -12 +40005aef: 0338 l32i.n a3, a3, 0 +40005af1: e0c332 addi a3, a3, -32 +40005af4: 0389 s32i.n a8, a3, 0 +40005af6: 1399 s32i.n a9, a3, 4 +40005af8: 23a9 s32i.n a10, a3, 8 +40005afa: 33b9 s32i.n a11, a3, 12 +40005afc: 41a220 srli a10, a2, 2 +40005aff: 408020 rotw 2 +40005b02: ffee06 j 40005abe <_XX_xtos_exc_unk5a80+0x3e> +40005b05: 408010 rotw 1 +40005b08: f0cdd2 addi a13, a13, -16 +40005b0b: 0d09 s32i.n a0, a13, 0 +40005b0d: 1d19 s32i.n a1, a13, 4 +40005b0f: 2d29 s32i.n a2, a13, 8 +40005b11: 3d39 s32i.n a3, a13, 12 +40005b13: f4c132 addi a3, a1, -12 +40005b16: 0338 l32i.n a3, a3, 0 +40005b18: 10cdd2 addi a13, a13, 16 +40005b1b: d0c332 addi a3, a3, -48 +40005b1e: 0349 s32i.n a4, a3, 0 +40005b20: 1359 s32i.n a5, a3, 4 +40005b22: 2369 s32i.n a6, a3, 8 +40005b24: 3379 s32i.n a7, a3, 12 +40005b26: 4389 s32i.n a8, a3, 16 +40005b28: 5399 s32i.n a9, a3, 20 +40005b2a: 63a9 s32i.n a10, a3, 24 +40005b2c: 73b9 s32i.n a11, a3, 28 +40005b2e: 4080f0 rotw -1 +40005b31: 41e320 srli a14, a2, 3 +40005b34: 408030 rotw 3 +40005b37: ffe0c6 j 40005abe <_XX_xtos_exc_unk5a80+0x3e> +40005b3a: 100000 and a0, a0, a0 +40005b3d: 204080 or a4, a0, a8 +40005b40: 0348 l32i.n a4, a3, 0 +40005b42: 401200 ssl a2 +40005b45: 120c movi.n a2, 1 +40005b47: a12200 sll a2, a2 +40005b4a: 134920 wsr.windowstart a2 +40005b4d: 002010 rsync +40005b50: 020c movi.n a2, 0 +40005b52: f00d ret.n +40005b54: 120c movi.n a2, 1 +40005b56: f00d ret.n +40005b58: 22f000 orb b15, b0, b0 +40005b5b: c22211 l32r a1, 3fff63e4 <_start-0x9c1c> +40005b5e: 483001 l32r a0, 3ffd7c20 <_start-0x283e0> +40005b61: e2f703 excw +40005b64: 0b0a add.n a0, a11, a0 +40005b66: 22f033 excw +40005b69: 62f711 l32r a1, 3ffde748 <_start-0x218b8> +40005b6c: 3030f7 bltu a0, a15, 40005ba0 <_XX_xtos_exc_unk5b94+0xc> +40005b6f: 130034 excw +40005b72: 320040 orbc b0, b0, b4 +40005b75: 223011 l32r a1, 3ffce438 <_start-0x31bc8> +40005b78: 202081 l32r a8, 3ffcdbf8 <_start-0x32408> +40005b7b: 4920f4 mula.da.hl.ldinc m2, a0, m0, a15 +40005b7e: 033013 excw +40005b81: 022c03 excw +40005b84: c03230 sub a3, a2, a3 +40005b87: 134830 wsr.windowbase a3 +40005b8a: 002010 rsync +40005b8d: 220c movi.n a2, 2 +40005b8f: f00d ret.n +40005b91: 000000 ill + +40005b94 <_XX_xtos_exc_unk5b94>: +40005b94: 002136 entry a1, 16 +40005b97: eafd61 l32r a6, 4000078c <_c_0x7fffffff+0xc> +40005b9a: 03e650 rsr.ps a5 +40005b9d: 004d mov.n a4, a0 +40005b9f: 102560 and a2, a5, a6 +40005ba2: 223b addi.n a2, a2, 3 +40005ba4: 13e620 wsr.ps a2 +40005ba7: 002010 rsync +40005baa: ffed45 call0 40005a80 <_XX_xtos_exc_unk5a80> +40005bad: 040d mov.n a0, a4 +40005baf: 13e650 wsr.ps a5 +40005bb2: 002010 rsync +40005bb5: f01d retw.n + ... + +40005bb8 : +40005bb8: 002136 entry a1, 16 +40005bbb: 000282 l8ui a8, a2, 0 +40005bbe: 000392 l8ui a9, a3, 0 +40005bc1: 3a0c movi.n a10, 3 +40005bc3: 529897 bne a8, a9, 40005c19 +40005bc6: 20b230 or a11, a2, a3 +40005bc9: 530ba7 bnone a11, a10, 40005c20 +40005bcc: 30b230 xor a11, a2, a3 +40005bcf: 318ba7 bany a11, a10, 40005c04 +40005bd2: 221b addi.n a2, a2, 1 +40005bd4: 0b2816 beqz a8, 40005c8a +40005bd7: 331b addi.n a3, a3, 1 +40005bd9: 4302a7 bnone a2, a10, 40005c20 +40005bdc: 000282 l8ui a8, a2, 0 +40005bdf: 000392 l8ui a9, a3, 0 +40005be2: 221b addi.n a2, a2, 1 +40005be4: 319897 bne a8, a9, 40005c19 +40005be7: 09f816 beqz a8, 40005c8a +40005bea: 331b addi.n a3, a3, 1 +40005bec: 3002a7 bnone a2, a10, 40005c20 +40005bef: 000282 l8ui a8, a2, 0 +40005bf2: 000392 l8ui a9, a3, 0 +40005bf5: 221b addi.n a2, a2, 1 +40005bf7: 1e9897 bne a8, a9, 40005c19 +40005bfa: 08c816 beqz a8, 40005c8a +40005bfd: 331b addi.n a3, a3, 1 +40005bff: 000746 j 40005c20 +40005c02: 0c0000 excw +40005c05: 7608 l32i.n a0, a6, 28 +40005c07: 0f88 l32i.n a8, a15, 0 +40005c09: 000282 l8ui a8, a2, 0 +40005c0c: 000392 l8ui a9, a3, 0 +40005c0f: 221b addi.n a2, a2, 1 +40005c11: 049897 bne a8, a9, 40005c19 +40005c14: 331b addi.n a3, a3, 1 +40005c16: fff816 beqz a8, 40005c19 +40005c19: c02890 sub a2, a8, a9 +40005c1c: f01d retw.n +40005c1e: 410000 srli a0, a0, 0 +40005c21: eadc bnez.n a10, 40005c43 +40005c23: eadf71 l32r a7, 400007a0 <_c_0x00ff0000+0x8> +40005c26: 238076 loop a0, 40005c4d +40005c29: 0288 l32i.n a8, a2, 0 +40005c2b: 0398 l32i.n a9, a3, 0 +40005c2d: 1158f0 slli a5, a8, 1 +40005c30: 5c9897 bne a8, a9, 40005c90 +40005c33: 209850 or a9, a8, a5 +40005c36: 1ac977 bnall a9, a7, 40005c54 +40005c39: 1288 l32i.n a8, a2, 4 +40005c3b: 1398 l32i.n a9, a3, 4 +40005c3d: 1158f0 slli a5, a8, 1 +40005c40: 4c9897 bne a8, a9, 40005c90 +40005c43: 209850 or a9, a8, a5 +40005c46: 06c977 bnall a9, a7, 40005c50 +40005c49: 228b addi.n a2, a2, 8 +40005c4b: 338b addi.n a3, a3, 8 +40005c4d: fff546 j 40005c26 +40005c50: 224b addi.n a2, a2, 4 +40005c52: 334b addi.n a3, a3, 4 +40005c54: 320847 bnone a8, a4, 40005c8a +40005c57: eacf51 l32r a5, 40000794 <_c_0x7fffffff+0x14> +40005c5a: eacf61 l32r a6, 40000798 <_c_0x00ff0000> +40005c5d: 290857 bnone a8, a5, 40005c8a +40005c60: eacf71 l32r a7, 4000079c <_c_0x00ff0000+0x4> +40005c63: 230867 bnone a8, a6, 40005c8a +40005c66: 200877 bnone a8, a7, 40005c8a +40005c69: 224b addi.n a2, a2, 4 +40005c6b: 334b addi.n a3, a3, 4 +40005c6d: 198076 loop a0, 40005c8a +40005c70: 0288 l32i.n a8, a2, 0 +40005c72: 0398 l32i.n a9, a3, 0 +40005c74: 224b addi.n a2, a2, 4 +40005c76: 169897 bne a8, a9, 40005c90 +40005c79: 0d0847 bnone a8, a4, 40005c8a +40005c7c: 0a0857 bnone a8, a5, 40005c8a +40005c7f: 070867 bnone a8, a6, 40005c8a +40005c82: 040877 bnone a8, a7, 40005c8a +40005c85: 334b addi.n a3, a3, 4 +40005c87: fff886 j 40005c6d +40005c8a: 020c movi.n a2, 0 +40005c8c: f01d retw.n +40005c8e: 900000 addx2 a0, a0, a0 +40005c91: 3028 l32i.n a2, a0, 12 +40005c93: 218247 bany a2, a4, 40005cb8 +40005c96: eabf51 l32r a5, 40000794 <_c_0x7fffffff+0x14> +40005c99: ed0847 bnone a8, a4, 40005c8a +40005c9c: 248257 bany a2, a5, 40005cc4 +40005c9f: eabe61 l32r a6, 40000798 <_c_0x00ff0000> +40005ca2: e40857 bnone a8, a5, 40005c8a +40005ca5: 278267 bany a2, a6, 40005cd0 +40005ca8: de0867 bnone a8, a6, 40005c8a +40005cab: 75a880 extui a10, a8, 24, 8 +40005cae: 75b890 extui a11, a9, 24, 8 +40005cb1: c02ab0 sub a2, a10, a11 +40005cb4: f01d retw.n +40005cb6: 800000 add a0, a0, a0 +40005cb9: 9074a0 addx2 a7, a4, a10 +40005cbc: b074b0 addx8 a7, a4, a11 +40005cbf: c02a add.n a12, a0, a2 +40005cc1: f01d retw.n +40005cc3: a88000 excw +40005cc6: b89074 excw +40005cc9: 2ab074 excw +40005ccc: f01dc0 subx8 a1, a13, a12 +40005ccf: a08000 addx4 a8, a0, a0 +40005cd2: b09075 call12 3ffb65d8 <_start-0x49a28> +40005cd5: 2ab075 call12 400307dc <__bss_start+0x207dc> +40005cd8: f01dc0 subx8 a1, a13, a12 + ... + +40005cdc : +40005cdc: 002136 entry a1, 16 +40005cdf: 20a220 or a10, a2, a2 +40005ce2: ffa042 movi a4, 255 +40005ce5: eaab51 l32r a5, 40000794 <_c_0x7fffffff+0x14> +40005ce8: eaac61 l32r a6, 40000798 <_c_0x00ff0000> +40005ceb: eaac71 l32r a7, 4000079c <_c_0x00ff0000+0x4> +40005cee: 0be307 bbsi a3, 0, 40005cfd +40005cf1: 17e317 bbsi a3, 1, 40005d0c +40005cf4: 380c movi.n a8, 3 +40005cf6: 2a0a87 bnone a10, a8, 40005d24 +40005cf9: 0016c6 j 40005d58 +40005cfc: 038200 excw +40005cff: 331b00 clamps a1, a11, 7 +40005d02: 004a82 s8i a8, a10, 0 +40005d05: 889c beqz.n a8, 40005d21 +40005d07: aa1b addi.n a10, a10, 1 +40005d09: e76317 bbci a3, 1, 40005cf4 +40005d0c: 000382 l8ui a8, a3, 0 +40005d0f: 004a82 s8i a8, a10, 0 +40005d12: b88c beqz.n a8, 40005d21 +40005d14: 010382 l8ui a8, a3, 1 +40005d17: 332b addi.n a3, a3, 2 +40005d19: 014a82 s8i a8, a10, 1 +40005d1c: aa2b addi.n a10, a10, 2 +40005d1e: fd2856 bnez a8, 40005cf4 +40005d21: f01d retw.n +40005d23: 080c00 excw +40005d26: 138876 loop a8, 40005d3d +40005d29: 0388 l32i.n a8, a3, 0 +40005d2b: 334b addi.n a3, a3, 4 +40005d2d: 0f0847 bnone a8, a4, 40005d40 +40005d30: 110857 bnone a8, a5, 40005d45 +40005d33: 150867 bnone a8, a6, 40005d4c +40005d36: 0a89 s32i.n a8, a10, 0 +40005d38: 010877 bnone a8, a7, 40005d3d +40005d3b: aa4b addi.n a10, a10, 4 +40005d3d: f01d retw.n +40005d3f: 4a8200 depbits a0, a2, 4, 9 +40005d42: f01d00 subx8 a1, a13, a0 +40005d45: 005a82 s16i a8, a10, 0 +40005d48: f01d retw.n +40005d4a: 820000 mull a0, a0, a0 +40005d4d: 005a add.n a0, a0, a5 +40005d4f: 080c movi.n a8, 0 +40005d51: 024a82 s8i a8, a10, 2 +40005d54: f01d retw.n +40005d56: 0c0000 excw +40005d59: 7608 l32i.n a0, a6, 28 +40005d5b: 0c88 l32i.n a8, a12, 0 +40005d5d: 000382 l8ui a8, a3, 0 +40005d60: 331b addi.n a3, a3, 1 +40005d62: 004a82 s8i a8, a10, 0 +40005d65: aa1b addi.n a10, a10, 1 +40005d67: fff816 beqz a8, 40005d6a +40005d6a: f01d retw.n + +40005d6c : +40005d6c: 002136 entry a1, 16 +40005d6f: fcc232 addi a3, a2, -4 +40005d72: ffa042 movi a4, 255 +40005d75: ea8751 l32r a5, 40000794 <_c_0x7fffffff+0x14> +40005d78: ea8861 l32r a6, 40000798 <_c_0x00ff0000> +40005d7b: ea8871 l32r a7, 4000079c <_c_0x00ff0000+0x4> +40005d7e: 06e207 bbsi a2, 0, 40005d88 +40005d81: 0de217 bbsi a2, 1, 40005d92 +40005d84: 000706 j 40005da4 +40005d87: 038200 excw +40005d8a: 331b04 excw +40005d8d: 88ac beqz.n a8, 40005db9 +40005d8f: 116317 bbci a3, 1, 40005da4 +40005d92: 332b addi.n a3, a3, 2 +40005d94: 0388 l32i.n a8, a3, 0 +40005d96: 2e0867 bnone a8, a6, 40005dc8 +40005d99: 078877 bany a8, a7, 40005da4 +40005d9c: 333b addi.n a3, a3, 3 +40005d9e: c02320 sub a2, a3, a2 +40005da1: f01d retw.n +40005da3: 080c00 excw +40005da6: 0f8876 loop a8, 40005db9 +40005da9: 1388 l32i.n a8, a3, 4 +40005dab: 334b addi.n a3, a3, 4 +40005dad: 0a0847 bnone a8, a4, 40005dbb +40005db0: 0c0857 bnone a8, a5, 40005dc0 +40005db3: 110867 bnone a8, a6, 40005dc8 +40005db6: ff0877 bnone a8, a7, 40005db9 +40005db9: 333b addi.n a3, a3, 3 +40005dbb: c02320 sub a2, a3, a2 +40005dbe: f01d retw.n +40005dc0: 331b addi.n a3, a3, 1 +40005dc2: c02320 sub a2, a3, a2 +40005dc5: f01d retw.n +40005dc7: 332b00 clamps a2, a11, 7 +40005dca: c02320 sub a2, a3, a2 +40005dcd: f01d retw.n + ... + +40005dd0 : +40005dd0: 004136 entry a1, 32 +40005dd3: a4bc beqz.n a4, 40005e11 +40005dd5: 205320 or a5, a3, a2 +40005dd8: 145050 extui a5, a5, 0, 2 +40005ddb: 041516 beqz a5, 40005e20 +40005dde: 000272 l8ui a7, a2, 0 +40005de1: 000382 l8ui a8, a3, 0 +40005de4: 049d mov.n a9, a4 +40005de6: 440b addi.n a4, a4, -1 +40005de8: 09ac beqz.n a9, 40005e0c +40005dea: 1e9787 bne a7, a8, 40005e0c +40005ded: 168076 loop a0, 40005e07 +40005df0: 046d mov.n a6, a4 +40005df2: b49c beqz.n a4, 40005e11 +40005df4: 979c beqz.n a7, 40005e11 +40005df6: 440b addi.n a4, a4, -1 +40005df8: 331b addi.n a3, a3, 1 +40005dfa: 221b addi.n a2, a2, 1 +40005dfc: 569c beqz.n a6, 40005e15 +40005dfe: 000272 l8ui a7, a2, 0 +40005e01: 000382 l8ui a8, a3, 0 +40005e04: 049877 bne a8, a7, 40005e0c +40005e07: fff886 j 40005ded +40005e0a: 800000 add a0, a0, a0 +40005e0d: 1dc027 bnall a0, a2, 40005e2e +40005e10: 020cf0 andb b0, b12, b15 +40005e13: f01d retw.n +40005e15: 000352 l8ui a5, a3, 0 +40005e18: 000222 l8ui a2, a2, 0 +40005e1b: c02250 sub a2, a2, a5 +40005e1e: f01d retw.n +40005e20: ba44b6 bltui a4, 4, 40005dde +40005e23: 0278 l32i.n a7, a2, 0 +40005e25: 002362 l32i a6, a3, 0 +40005e28: b29677 bne a6, a7, 40005dde +40005e2b: ea5e91 l32r a9, 400007a4 <_c_0x00ff0000+0xc> +40005e2e: ffafa2 movi a10, -1 +40005e31: ea5d81 l32r a8, 400007a8 <_c_0x00ff0000+0x10> +40005e34: fcc442 addi a4, a4, -4 +40005e37: fd6416 beqz a4, 40005e11 +40005e3a: 30c7a0 xor a12, a7, a10 +40005e3d: b78a add.n a11, a7, a8 +40005e3f: 10bbc0 and a11, a11, a12 +40005e42: cb89b7 bany a9, a11, 40005e11 +40005e45: 334b addi.n a3, a3, 4 +40005e47: 224b addi.n a2, a2, 4 +40005e49: 9144b6 bltui a4, 4, 40005dde +40005e4c: 0278 l32i.n a7, a2, 0 +40005e4e: 03d8 l32i.n a13, a3, 0 +40005e50: e01d77 beq a13, a7, 40005e34 +40005e53: ffe1c6 j 40005dde +40005e56: 820000 mull a0, a0, a0 +40005e59: 1b0003 excw +40005e5c: 4a8233 excw +40005e5f: 440b00 extui a0, a0, 11, 5 +40005e62: 74ac beqz.n a4, 40005e8d +40005e64: aa1b addi.n a10, a10, 1 +40005e66: 048816 beqz a8, 40005eb2 +40005e69: 3d6317 bbci a3, 1, 40005eaa +40005e6c: 000382 l8ui a8, a3, 0 +40005e6f: 440b addi.n a4, a4, -1 +40005e71: 004a82 s8i a8, a10, 0 +40005e74: 549c beqz.n a4, 40005e8d +40005e76: aa1b addi.n a10, a10, 1 +40005e78: 68bc beqz.n a8, 40005eb2 +40005e7a: 010382 l8ui a8, a3, 1 +40005e7d: 332b addi.n a3, a3, 2 +40005e7f: 004a82 s8i a8, a10, 0 +40005e82: 440b addi.n a4, a4, -1 +40005e84: 548c beqz.n a4, 40005e8d +40005e86: aa1b addi.n a10, a10, 1 +40005e88: e8dc bnez.n a8, 40005eaa +40005e8a: 000906 j 40005eb2 +40005e8d: f01d retw.n + ... + +40005e90 : +40005e90: 002136 entry a1, 16 +40005e93: 02ad mov.n a10, a2 +40005e95: ff4416 beqz a4, 40005e8d +40005e98: ffa0b2 movi a11, 255 +40005e9b: ea3e51 l32r a5, 40000794 <_c_0x7fffffff+0x14> +40005e9e: ea3e61 l32r a6, 40000798 <_c_0x00ff0000> +40005ea1: ea3e71 l32r a7, 4000079c <_c_0x00ff0000+0x4> +40005ea4: b0e307 bbsi a3, 0, 40005e58 +40005ea7: c1e317 bbsi a3, 1, 40005e6c +40005eaa: 380c movi.n a8, 3 +40005eac: 540a87 bnone a10, a8, 40005f04 +40005eaf: 002746 j 40005f50 +40005eb2: 090c movi.n a9, 0 +40005eb4: 28ea07 bbsi a10, 0, 40005ee0 +40005eb7: 32ea17 bbsi a10, 1, 40005eed +40005eba: 1344a6 blti a4, 4, 40005ed1 +40005ebd: 218240 srai a8, a4, 2 +40005ec0: f03d nop.n +40005ec2: 038876 loop a8, 40005ec9 +40005ec5: 0a99 s32i.n a9, a10, 0 +40005ec7: aa4b addi.n a10, a10, 4 +40005ec9: 1188e0 slli a8, a8, 2 +40005ecc: c04480 sub a4, a4, a8 +40005ecf: 948c beqz.n a4, 40005edc +40005ed1: 004a92 s8i a9, a10, 0 +40005ed4: 440b addi.n a4, a4, -1 +40005ed6: 01caa2 addi a10, a10, 1 +40005ed9: ff4456 bnez a4, 40005ed1 +40005edc: f01d retw.n +40005ede: 920000 excw +40005ee1: 004a add.n a0, a0, a4 +40005ee3: 440b addi.n a4, a4, -1 +40005ee5: ff3416 beqz a4, 40005edc +40005ee8: aa1b addi.n a10, a10, 1 +40005eea: cc6a17 bbci a10, 1, 40005eba +40005eed: 004a92 s8i a9, a10, 0 +40005ef0: 440b addi.n a4, a4, -1 +40005ef2: fe6416 beqz a4, 40005edc +40005ef5: 014a92 s8i a9, a10, 1 +40005ef8: 440b addi.n a4, a4, -1 +40005efa: fde416 beqz a4, 40005edc +40005efd: aa2b addi.n a10, a10, 2 +40005eff: ffedc6 j 40005eba +40005f02: 0c0000 excw +40005f05: 7608 l32i.n a0, a6, 28 +40005f07: 1988 l32i.n a8, a9, 4 +40005f09: 4354a6 blti a4, 5, 40005f50 +40005f0c: 0388 l32i.n a8, a3, 0 +40005f0e: 334b addi.n a3, a3, 4 +40005f10: 1408b7 bnone a8, a11, 40005f28 +40005f13: 1d0857 bnone a8, a5, 40005f34 +40005f16: 260867 bnone a8, a6, 40005f40 +40005f19: 0a89 s32i.n a8, a10, 0 +40005f1b: fcc442 addi a4, a4, -4 +40005f1e: aa4b addi.n a10, a10, 4 +40005f20: 8e0877 bnone a8, a7, 40005eb2 +40005f23: ffe2c6 j 40005eb2 +40005f26: 820000 mull a0, a0, a0 +40005f29: 004a add.n a0, a0, a4 +40005f2b: 440b addi.n a4, a4, -1 +40005f2d: aa1b addi.n a10, a10, 1 +40005f2f: ffdfc6 j 40005eb2 +40005f32: 820000 mull a0, a0, a0 +40005f35: 005a add.n a0, a0, a5 +40005f37: fec442 addi a4, a4, -2 +40005f3a: aa2b addi.n a10, a10, 2 +40005f3c: ffdc86 j 40005eb2 +40005f3f: 5a8200 depbits a0, a2, 5, 9 +40005f42: 080c00 excw +40005f45: 024a82 s8i a8, a10, 2 +40005f48: fdc442 addi a4, a4, -3 +40005f4b: aa3b addi.n a10, a10, 3 +40005f4d: ffd846 j 40005eb2 +40005f50: 080c movi.n a8, 0 +40005f52: 108876 loop a8, 40005f66 +40005f55: 000382 l8ui a8, a3, 0 +40005f58: 331b addi.n a3, a3, 1 +40005f5a: 004a82 s8i a8, a10, 0 +40005f5d: 440b addi.n a4, a4, -1 +40005f5f: 648c beqz.n a4, 40005f69 +40005f61: aa1b addi.n a10, a10, 1 +40005f63: fff816 beqz a8, 40005f66 +40005f66: ffd206 j 40005eb2 +40005f69: f01d retw.n + ... + +40005f6c : +40005f6c: 090136 entry a1, 0x480 +40005f6f: 50c172 addi a7, a1, 80 +40005f72: 025d mov.n a5, a2 +40005f74: 038d mov.n a8, a3 +40005f76: 20c220 or a12, a2, a2 +40005f79: 0003b2 l8ui a11, a3, 0 +40005f7c: 0002e2 l8ui a14, a2, 0 +40005f7f: 209bb0 or a9, a11, a11 +40005f82: eebc beqz.n a14, 40005fc4 +40005f84: cbac beqz.n a11, 40005fb4 +40005f86: 01a0a2 movi a10, 1 +40005f89: 228076 loop a0, 40005faf +40005f8c: cc1b addi.n a12, a12, 1 +40005f8e: 1d0c movi.n a13, 1 +40005f90: 74f0e0 extui a15, a14, 0, 8 +40005f93: 040c movi.n a4, 0 +40005f95: 74e090 extui a14, a9, 0, 8 +40005f98: c0eef0 sub a14, a14, a15 +40005f9b: 834de0 moveqz a4, a13, a14 +40005f9e: 10aa40 and a10, a10, a4 +40005fa1: 000ce2 l8ui a14, a12, 0 +40005fa4: 481b addi.n a4, a8, 1 +40005fa6: 048d mov.n a8, a4 +40005fa8: ce8c beqz.n a14, 40005fb8 +40005faa: 000492 l8ui a9, a4, 0 +40005fad: 198c beqz.n a9, 40005fb2 +40005faf: fff586 j 40005f89 +40005fb2: 4a9c beqz.n a10, 40005fca +40005fb4: 052d mov.n a2, a5 +40005fb6: f01d retw.n +40005fb8: 000482 l8ui a8, a4, 0 +40005fbb: ff3816 beqz a8, 40005fb2 +40005fbe: 020c movi.n a2, 0 +40005fc0: f01d retw.n +40005fc2: 560000 excw +40005fc5: ff6b addi.n a15, a15, 6 +40005fc7: fffa46 j 40005fb4 +40005fca: 01c5a2 addi a10, a5, 1 +40005fcd: 005aa5 call8 40006578 +40005fd0: 202aa0 or a2, a10, a10 +40005fd3: 271a16 beqz a10, 40006248 +40005fd6: c04430 sub a4, a4, a3 +40005fd9: ffc492 addi a9, a4, -1 +40005fdc: 268916 beqz a9, 40006248 +40005fdf: 845a add.n a8, a4, a5 +40005fe1: 0538a7 bltu a8, a10, 40005fea +40005fe4: c088a0 sub a8, a8, a10 +40005fe7: 000046 j 40005fec +40005fea: 180c movi.n a8, 1 +40005fec: 18c4f6 bgeui a4, 32, 40006008 +40005fef: 085d mov.n a5, a8 +40005ff1: 0224f6 bgeui a4, 2, 40005ff7 +40005ff4: 002f06 j 400060b4 +40005ff7: 190c movi.n a9, 1 +40005ff9: 180c movi.n a8, 1 +40005ffb: 1f0c movi.n a15, 1 +40005ffd: 0a0c movi.n a10, 0 +40005fff: f67c movi.n a6, -1 +40006001: 5159 s32i.n a5, a1, 20 +40006003: 0008c6 j 4000602a +40006006: 820000 mull a0, a0, a0 +40006009: f61061 l32r a6, 4000384c <_X_recv_packet+0x10> +4000600c: 860224 excw +4000600f: 0c0067 bnone a0, a6, 4000601f +40006012: 0c19 s32i.n a1, a12, 0 +40006014: 0c18 l32i.n a1, a12, 0 +40006016: 1f .byte 0x1f +40006017: 0a0c movi.n a10, 0 +40006019: f67c movi.n a6, -1 +4000601b: 0042c6 j 4000612a +4000601e: 09ad mov.n a10, a9 +40006020: c0f960 sub a15, a9, a6 +40006023: 180c movi.n a8, 1 +40006025: 98aa add.n a9, a8, a10 +40006027: 2db947 bgeu a9, a4, 40006058 +4000602a: b86a add.n a11, a8, a6 +4000602c: c93a add.n a12, a9, a3 +4000602e: 000cc2 l8ui a12, a12, 0 +40006031: b3ba add.n a11, a3, a11 +40006033: 000bb2 l8ui a11, a11, 0 +40006036: e43cb7 bltu a12, a11, 4000601e +40006039: 0f9cb7 bne a12, a11, 4000604c +4000603c: 0418f7 beq a8, a15, 40006044 +4000603f: 881b addi.n a8, a8, 1 +40006041: fff806 j 40006025 +40006044: afaa add.n a10, a15, a10 +40006046: 180c movi.n a8, 1 +40006048: fff646 j 40006025 +4000604b: 180c00 excw +4000604e: 0a6d mov.n a6, a10 +40006050: 1f0c movi.n a15, 1 +40006052: aa1b addi.n a10, a10, 1 +40006054: fff346 j 40006025 +40006057: 190c00 excw +4000605a: f57c movi.n a5, -1 +4000605c: 180c movi.n a8, 1 +4000605e: 11f9 s32i.n a15, a1, 4 +40006060: 0a0c movi.n a10, 0 +40006062: 1f0c movi.n a15, 1 +40006064: 000346 j 40006075 +40006067: ad0000 excw +4000606a: 5009 s32i.n a0, a0, 20 +4000606c: c0f9 s32i.n a15, a0, 48 +4000606e: 180c movi.n a8, 1 +40006070: 98aa add.n a9, a8, a10 +40006072: 2eb947 bgeu a9, a4, 400060a4 +40006075: b58a add.n a11, a5, a8 +40006077: c93a add.n a12, a9, a3 +40006079: 000cc2 l8ui a12, a12, 0 +4000607c: b3ba add.n a11, a3, a11 +4000607e: 000bb2 l8ui a11, a11, 0 +40006081: e43bc7 bltu a11, a12, 40006069 +40006084: 109cb7 bne a12, a11, 40006098 +40006087: 0518f7 beq a8, a15, 40006090 +4000608a: 881b addi.n a8, a8, 1 +4000608c: fff806 j 40006070 +4000608f: afaa00 excw +40006092: 180c movi.n a8, 1 +40006094: fff606 j 40006070 +40006097: 180c00 excw +4000609a: 0a5d mov.n a5, a10 +4000609c: 1f0c movi.n a15, 1 +4000609e: aa1b addi.n a10, a10, 1 +400060a0: fff306 j 40006070 +400060a3: 961b00 excw +400060a6: c51b addi.n a12, a5, 1 +400060a8: 5158 l32i.n a5, a1, 20 +400060aa: 0abc97 bgeu a12, a9, 400060b8 +400060ad: 1168 l32i.n a6, a1, 4 +400060af: 09cd mov.n a12, a9 +400060b1: 000146 j 400060ba +400060b4: 0c0c movi.n a12, 0 +400060b6: 1f0c movi.n a15, 1 +400060b8: 0f6d mov.n a6, a15 +400060ba: 81c9 s32i.n a12, a1, 32 +400060bc: 03ad mov.n a10, a3 +400060be: b36a add.n a11, a3, a6 +400060c0: 0082a5 call8 400068ec +400060c3: 5159 s32i.n a5, a1, 20 +400060c5: 213a16 beqz a10, 400062dc +400060c8: 81e8 l32i.n a14, a1, 32 +400060ca: 6e0b addi.n a6, a14, -1 +400060cc: c0f4e0 sub a15, a4, a14 +400060cf: 6169 s32i.n a6, a1, 24 +400060d1: 00a062 movi a6, 0 +400060d4: 73eef0 maxu a14, a14, a15 +400060d7: 21e9 s32i.n a14, a1, 8 +400060d9: a25a add.n a10, a2, a5 +400060db: c46a add.n a12, a4, a6 +400060dd: 00a0b2 movi a11, 0 +400060e0: 0761c2 s32i a12, a1, 28 +400060e3: c0cc50 sub a12, a12, a5 +400060e6: 0041a5 call8 40006500 +400060e9: 15da56 bnez a10, 4000624a +400060ec: 71d8 l32i.n a13, a1, 28 +400060ee: 81e8 l32i.n a14, a1, 32 +400060f0: 0d5d mov.n a5, a13 +400060f2: 154d16 beqz a13, 4000624a +400060f5: 0e8d mov.n a8, a14 +400060f7: 11be47 bgeu a14, a4, 4000610c +400060fa: fe6a add.n a15, a14, a6 +400060fc: 9e3a add.n a9, a14, a3 +400060fe: 000992 l8ui a9, a9, 0 +40006101: f2fa add.n a15, a2, a15 +40006103: 000ff2 l8ui a15, a15, 0 +40006106: c0ff90 sub a15, a15, a9 +40006109: 154f16 beqz a15, 40006261 +4000610c: 023847 bltu a8, a4, 40006112 +4000610f: 0054c6 j 40006266 +40006112: 81a8 l32i.n a10, a1, 32 +40006114: c0a8a0 sub a10, a8, a10 +40006117: aa1b addi.n a10, a10, 1 +40006119: 66aa add.n a6, a6, a10 +4000611b: ffee86 j 400060d9 +4000611e: 09ad mov.n a10, a9 +40006120: c0f960 sub a15, a9, a6 +40006123: 180c movi.n a8, 1 +40006125: 98aa add.n a9, a8, a10 +40006127: 2db947 bgeu a9, a4, 40006158 +4000612a: b86a add.n a11, a8, a6 +4000612c: c93a add.n a12, a9, a3 +4000612e: 000cc2 l8ui a12, a12, 0 +40006131: b3ba add.n a11, a3, a11 +40006133: 000bb2 l8ui a11, a11, 0 +40006136: e43cb7 bltu a12, a11, 4000611e +40006139: 0f9cb7 bne a12, a11, 4000614c +4000613c: 0418f7 beq a8, a15, 40006144 +4000613f: 881b addi.n a8, a8, 1 +40006141: fff806 j 40006125 +40006144: afaa add.n a10, a15, a10 +40006146: 180c movi.n a8, 1 +40006148: fff646 j 40006125 +4000614b: 180c00 excw +4000614e: 0a6d mov.n a6, a10 +40006150: 1f0c movi.n a15, 1 +40006152: aa1b addi.n a10, a10, 1 +40006154: fff346 j 40006125 +40006157: 190c00 excw +4000615a: f57c movi.n a5, -1 +4000615c: 180c movi.n a8, 1 +4000615e: f1f9 s32i.n a15, a1, 60 +40006160: 0a0c movi.n a10, 0 +40006162: 1f0c movi.n a15, 1 +40006164: 000346 j 40006175 +40006167: ad0000 excw +4000616a: 5009 s32i.n a0, a0, 20 +4000616c: c0f9 s32i.n a15, a0, 48 +4000616e: 180c movi.n a8, 1 +40006170: 98aa add.n a9, a8, a10 +40006172: 2eb947 bgeu a9, a4, 400061a4 +40006175: b58a add.n a11, a5, a8 +40006177: c93a add.n a12, a9, a3 +40006179: 000cc2 l8ui a12, a12, 0 +4000617c: b3ba add.n a11, a3, a11 +4000617e: 000bb2 l8ui a11, a11, 0 +40006181: e43bc7 bltu a11, a12, 40006169 +40006184: 109cb7 bne a12, a11, 40006198 +40006187: 0518f7 beq a8, a15, 40006190 +4000618a: 881b addi.n a8, a8, 1 +4000618c: fff806 j 40006170 +4000618f: afaa00 excw +40006192: 180c movi.n a8, 1 +40006194: fff606 j 40006170 +40006197: 180c00 excw +4000619a: 0a5d mov.n a5, a10 +4000619c: 1f0c movi.n a15, 1 +4000619e: aa1b addi.n a10, a10, 1 +400061a0: fff306 j 40006170 +400061a3: 961b00 excw +400061a6: c51b addi.n a12, a5, 1 +400061a8: 08bc97 bgeu a12, a9, 400061b4 +400061ab: 09ed mov.n a14, a9 +400061ad: 0001c6 j 400061b8 +400061b0: 0c0c movi.n a12, 0 +400061b2: 1f0c movi.n a15, 1 +400061b4: 0ced mov.n a14, a12 +400061b6: f1f9 s32i.n a15, a1, 60 +400061b8: 0a2c movi.n a10, 32 +400061ba: 10c762 addi a6, a7, 16 +400061bd: 068d mov.n a8, a6 +400061bf: 12aa76 loopgtz a10, 400061d5 +400061c2: 0849 s32i.n a4, a8, 0 +400061c4: 1849 s32i.n a4, a8, 4 +400061c6: 2849 s32i.n a4, a8, 8 +400061c8: 3849 s32i.n a4, a8, 12 +400061ca: 4849 s32i.n a4, a8, 16 +400061cc: 5849 s32i.n a4, a8, 20 +400061ce: 6849 s32i.n a4, a8, 24 +400061d0: 7849 s32i.n a4, a8, 28 +400061d2: 20c882 addi a8, a8, 32 +400061d5: 080c movi.n a8, 0 +400061d7: a40b addi.n a10, a4, -1 +400061d9: e1a9 s32i.n a10, a1, 56 +400061db: 10a476 loopgtz a4, 400061ef +400061de: 938a add.n a9, a3, a8 +400061e0: 000992 l8ui a9, a9, 0 +400061e3: 01c882 addi a8, a8, 1 +400061e6: a09960 addx4 a9, a9, a6 +400061e9: 0069a2 s32i a10, a9, 0 +400061ec: ffcaa2 addi a10, a10, -1 +400061ef: 0ecd mov.n a12, a14 +400061f1: d1e9 s32i.n a14, a1, 52 +400061f3: f1b8 l32i.n a11, a1, 60 +400061f5: 03ad mov.n a10, a3 +400061f7: b3ba add.n a11, a3, a11 +400061f9: 006f25 call8 400068ec +400061fc: 174a16 beqz a10, 40006374 +400061ff: d1d8 l32i.n a13, a1, 52 +40006201: 050c movi.n a5, 0 +40006203: c0e4d0 sub a14, a4, a13 +40006206: 73ede0 maxu a14, a13, a14 +40006209: a1e9 s32i.n a14, a1, 40 +4000620b: dd0b addi.n a13, a13, -1 +4000620d: b1d9 s32i.n a13, a1, 44 +4000620f: 000686 j 4000622d +40006212: c1f8 l32i.n a15, a1, 48 +40006214: 1061f2 s32i a15, a1, 64 +40006217: 9fac beqz.n a15, 40006244 +40006219: 8092f0 add a9, a2, a15 +4000621c: ffd992 addmi a9, a9, 0xffffff00 +4000621f: ff0992 l8ui a9, a9, 255 +40006222: a09960 addx4 a9, a9, a6 +40006225: 0998 l32i.n a9, a9, 0 +40006227: 07a916 beqz a9, 400062a5 +4000622a: 805950 add a5, a9, a5 +4000622d: 00a0b2 movi a11, 0 +40006230: 1021d2 l32i a13, a1, 64 +40006233: 80c450 add a12, a4, a5 +40006236: 0c61c2 s32i a12, a1, 48 +40006239: a2da add.n a10, a2, a13 +4000623b: c0ccd0 sub a12, a12, a13 +4000623e: 002c25 call8 40006500 +40006241: fcda16 beqz a10, 40006212 +40006244: 020c movi.n a2, 0 +40006246: f01d retw.n +40006248: f01d retw.n +4000624a: 020c movi.n a2, 0 +4000624c: f01d retw.n +4000624e: e86a add.n a14, a8, a6 +40006250: f38a add.n a15, a3, a8 +40006252: 000ff2 l8ui a15, a15, 0 +40006255: 80e2e0 add a14, a2, a14 +40006258: 000ee2 l8ui a14, a14, 0 +4000625b: c0eef0 sub a14, a14, a15 +4000625e: eaae56 bnez a14, 4000610c +40006261: 881b addi.n a8, a8, 1 +40006263: e73847 bltu a8, a4, 4000624e +40006266: 8198 l32i.n a9, a1, 32 +40006268: 6188 l32i.n a8, a1, 24 +4000626a: 69ac beqz.n a9, 40006294 +4000626c: a86a add.n a10, a8, a6 +4000626e: b83a add.n a11, a8, a3 +40006270: 000bb2 l8ui a11, a11, 0 +40006273: 80a2a0 add a10, a2, a10 +40006276: 000aa2 l8ui a10, a10, 0 +40006279: 179ab7 bne a10, a11, 40006294 +4000627c: 126162 s32i a6, a1, 72 +4000627f: 880b addi.n a8, a8, -1 +40006281: 4f0826 beqi a8, -1, 400062d4 +40006284: c86a add.n a12, a8, a6 +40006286: d38a add.n a13, a3, a8 +40006288: 000dd2 l8ui a13, a13, 0 +4000628b: 80c2c0 add a12, a2, a12 +4000628e: 000cc2 l8ui a12, a12, 0 +40006291: e71cd7 beq a12, a13, 4000627c +40006294: 126162 s32i a6, a1, 72 +40006297: 390826 beqi a8, -1, 400062d4 +4000629a: 21e8 l32i.n a14, a1, 8 +4000629c: ee1b addi.n a14, a14, 1 +4000629e: 66ea add.n a6, a6, a14 +400062a0: ff8d46 j 400060d9 +400062a3: f80000 excw +400062a6: e198d1 l32r a13, 3fffe908 <_start-0x16f8> +400062a9: 0f8d mov.n a8, a15 +400062ab: 11bf97 bgeu a15, a9, 400062c0 +400062ae: af5a add.n a10, a15, a5 +400062b0: bf3a add.n a11, a15, a3 +400062b2: 000bb2 l8ui a11, a11, 0 +400062b5: a2aa add.n a10, a2, a10 +400062b7: 000aa2 l8ui a10, a10, 0 +400062ba: c0aab0 sub a10, a10, a11 +400062bd: 145a16 beqz a10, 40006406 +400062c0: e1c8 l32i.n a12, a1, 56 +400062c2: 0238c7 bltu a8, a12, 400062c8 +400062c5: 005106 j 4000640d +400062c8: d1d8 l32i.n a13, a1, 52 +400062ca: c0d8d0 sub a13, a8, a13 +400062cd: dd1b addi.n a13, a13, 1 +400062cf: 55da add.n a5, a5, a13 +400062d1: ffd606 j 4000622d +400062d4: 1221e2 l32i a14, a1, 72 +400062d7: 22ea add.n a2, a2, a14 +400062d9: f01d retw.n +400062db: 714900 excw +400062de: 090c movi.n a9, 0 +400062e0: 8158 l32i.n a5, a1, 32 +400062e2: c0f460 sub a15, a4, a6 +400062e5: 31f9 s32i.n a15, a1, 12 +400062e7: 550b addi.n a5, a5, -1 +400062e9: 6159 s32i.n a5, a1, 24 +400062eb: 050c movi.n a5, 0 +400062ed: 00a0b2 movi a11, 0 +400062f0: 116192 s32i a9, a1, 68 +400062f3: 0521d2 l32i a13, a1, 20 +400062f6: 0721c2 l32i a12, a1, 28 +400062f9: a2da add.n a10, a2, a13 +400062fb: c0ccd0 sub a12, a12, a13 +400062fe: 002025 call8 40006500 +40006301: 112192 l32i a9, a1, 68 +40006304: f42a56 bnez a10, 4000624a +40006307: 71e8 l32i.n a14, a1, 28 +40006309: 51e9 s32i.n a14, a1, 20 +4000630b: f3be16 beqz a14, 4000624a +4000630e: 8188 l32i.n a8, a1, 32 +40006310: 738890 maxu a8, a8, a9 +40006313: 2ab847 bgeu a8, a4, 40006341 +40006316: a85a add.n a10, a8, a5 +40006318: b83a add.n a11, a8, a3 +4000631a: 000bb2 l8ui a11, a11, 0 +4000631d: a2aa add.n a10, a2, a10 +4000631f: 000aa2 l8ui a10, a10, 0 +40006322: c0aab0 sub a10, a10, a11 +40006325: 3a9c beqz.n a10, 4000633c +40006327: 004746 j 40006448 +4000632a: c85a add.n a12, a8, a5 +4000632c: d38a add.n a13, a3, a8 +4000632e: 000dd2 l8ui a13, a13, 0 +40006331: c2ca add.n a12, a2, a12 +40006333: 000cc2 l8ui a12, a12, 0 +40006336: c0ccd0 sub a12, a12, a13 +40006339: 10bc56 bnez a12, 40006448 +4000633c: 881b addi.n a8, a8, 1 +4000633e: e83847 bltu a8, a4, 4000632a +40006341: 81e8 l32i.n a14, a1, 32 +40006343: 6188 l32i.n a8, a1, 24 +40006345: 11b9e7 bgeu a9, a14, 4000635a +40006348: f85a add.n a15, a8, a5 +4000634a: a83a add.n a10, a8, a3 +4000634c: 000aa2 l8ui a10, a10, 0 +4000634f: f2fa add.n a15, a2, a15 +40006351: 000ff2 l8ui a15, a15, 0 +40006354: c0ffa0 sub a15, a15, a10 +40006357: 117f16 beqz a15, 40006472 +4000635a: 81a8 l32i.n a10, a1, 32 +4000635c: 126152 s32i a5, a1, 72 +4000635f: b91b addi.n a11, a9, 1 +40006361: 02bab7 bgeu a10, a11, 40006367 +40006364: ffdb06 j 400062d4 +40006367: 556a add.n a5, a5, a6 +40006369: 71c8 l32i.n a12, a1, 28 +4000636b: 3198 l32i.n a9, a1, 12 +4000636d: cc6a add.n a12, a12, a6 +4000636f: 71c9 s32i.n a12, a1, 28 +40006371: ffde06 j 400062ed +40006374: c149 s32i.n a4, a1, 48 +40006376: 050c movi.n a5, 0 +40006378: d1f8 l32i.n a15, a1, 52 +4000637a: f1e8 l32i.n a14, a1, 60 +4000637c: 0d0c movi.n a13, 0 +4000637e: 91d9 s32i.n a13, a1, 36 +40006380: c0e4e0 sub a14, a4, a14 +40006383: ff0b addi.n a15, a15, -1 +40006385: b1f9 s32i.n a15, a1, 44 +40006387: 41e9 s32i.n a14, a1, 16 +40006389: 000546 j 400063a2 +4000638c: 919800 excw +4000638f: 598c beqz.n a9, 40006398 +40006391: f1a8 l32i.n a10, a1, 60 +40006393: 01b8a7 bgeu a8, a10, 40006398 +40006396: 4188 l32i.n a8, a1, 16 +40006398: 585a add.n a5, a8, a5 +4000639a: 0c0c movi.n a12, 0 +4000639c: 91c9 s32i.n a12, a1, 36 +4000639e: b45a add.n a11, a4, a5 +400063a0: c1b9 s32i.n a11, a1, 48 +400063a2: 00a0b2 movi a11, 0 +400063a5: 1021d2 l32i a13, a1, 64 +400063a8: 0c21c2 l32i a12, a1, 48 +400063ab: 80a2d0 add a10, a2, a13 +400063ae: c0ccd0 sub a12, a12, a13 +400063b1: 0014e5 call8 40006500 +400063b4: e8ca56 bnez a10, 40006244 +400063b7: c1e8 l32i.n a14, a1, 48 +400063b9: 1061e2 s32i a14, a1, 64 +400063bc: e84e16 beqz a14, 40006244 +400063bf: 82ea add.n a8, a2, a14 +400063c1: ffd882 addmi a8, a8, 0xffffff00 +400063c4: ff0882 l8ui a8, a8, 255 +400063c7: a08860 addx4 a8, a8, a6 +400063ca: 0888 l32i.n a8, a8, 0 +400063cc: fbd856 bnez a8, 4000638d +400063cf: 9198 l32i.n a9, a1, 36 +400063d1: d188 l32i.n a8, a1, 52 +400063d3: 738890 maxu a8, a8, a9 +400063d6: e198 l32i.n a9, a1, 56 +400063d8: 023897 bltu a8, a9, 400063de +400063db: 002d46 j 40006494 +400063de: a85a add.n a10, a8, a5 +400063e0: b83a add.n a11, a8, a3 +400063e2: 000bb2 l8ui a11, a11, 0 +400063e5: a2aa add.n a10, a2, a10 +400063e7: 000aa2 l8ui a10, a10, 0 +400063ea: c0aab0 sub a10, a10, a11 +400063ed: 09ca16 beqz a10, 4000648d +400063f0: 003586 j 400064ca +400063f3: c58a00 extui a8, a0, 26, 13 +400063f6: d38a add.n a13, a3, a8 +400063f8: 000dd2 l8ui a13, a13, 0 +400063fb: c2ca add.n a12, a2, a12 +400063fd: 000cc2 l8ui a12, a12, 0 +40006400: c0ccd0 sub a12, a12, a13 +40006403: eb9c56 bnez a12, 400062c0 +40006406: e1e8 l32i.n a14, a1, 56 +40006408: 881b addi.n a8, a8, 1 +4000640a: e638e7 bltu a8, a14, 400063f4 +4000640d: d1f8 l32i.n a15, a1, 52 +4000640f: b188 l32i.n a8, a1, 44 +40006411: 3fac beqz.n a15, 40006438 +40006413: 985a add.n a9, a8, a5 +40006415: a83a add.n a10, a8, a3 +40006417: 000aa2 l8ui a10, a10, 0 +4000641a: 929a add.n a9, a2, a9 +4000641c: 000992 l8ui a9, a9, 0 +4000641f: 1599a7 bne a9, a10, 40006438 +40006422: 880b addi.n a8, a8, -1 +40006424: b81b addi.n a11, a8, 1 +40006426: 09cb16 beqz a11, 400064c6 +40006429: c58a add.n a12, a5, a8 +4000642b: d38a add.n a13, a3, a8 +4000642d: 000dd2 l8ui a13, a13, 0 +40006430: c2ca add.n a12, a2, a12 +40006432: 000cc2 l8ui a12, a12, 0 +40006435: e91cd7 beq a12, a13, 40006422 +40006438: 020866 bnei a8, -1, 4000643e +4000643b: 0021c6 j 400064c6 +4000643e: a1e8 l32i.n a14, a1, 40 +40006440: ee1b addi.n a14, a14, 1 +40006442: 55ea add.n a5, a5, a14 +40006444: ff7946 j 4000622d +40006447: 384700 excw +4000644a: bc8602 excw +4000644d: ff .byte 0xff +4000644e: 8198 l32i.n a9, a1, 32 +40006450: c09890 sub a9, a8, a9 +40006453: 991b addi.n a9, a9, 1 +40006455: 559a add.n a5, a5, a9 +40006457: 090c movi.n a9, 0 +40006459: f45a add.n a15, a4, a5 +4000645b: 71f9 s32i.n a15, a1, 28 +4000645d: ffa306 j 400062ed +40006460: b85a add.n a11, a8, a5 +40006462: c38a add.n a12, a3, a8 +40006464: 000cc2 l8ui a12, a12, 0 +40006467: b2ba add.n a11, a2, a11 +40006469: 000bb2 l8ui a11, a11, 0 +4000646c: 021bc7 beq a11, a12, 40006472 +4000646f: ffba46 j 4000635c +40006472: 08ad mov.n a10, a8 +40006474: 880b addi.n a8, a8, -1 +40006476: e639a7 bltu a9, a10, 40006460 +40006479: ffb7c6 j 4000635c +4000647c: 8a0000 depbits a0, a0, 8, 1 +4000647f: e38ad5 call4 3ffe9d2c <_start-0x162d4> +40006482: 000ee2 l8ui a14, a14, 0 +40006485: d2da add.n a13, a2, a13 +40006487: 000dd2 l8ui a13, a13, 0 +4000648a: 3c9de7 bne a13, a14, 400064ca +4000648d: e1f8 l32i.n a15, a1, 56 +4000648f: 881b addi.n a8, a8, 1 +40006491: e938f7 bltu a8, a15, 4000647e +40006494: d1a8 l32i.n a10, a1, 52 +40006496: 9198 l32i.n a9, a1, 36 +40006498: b188 l32i.n a8, a1, 44 +4000649a: 0eb9a7 bgeu a9, a10, 400064ac +4000649d: b85a add.n a11, a8, a5 +4000649f: c83a add.n a12, a8, a3 +400064a1: 000cc2 l8ui a12, a12, 0 +400064a4: b2ba add.n a11, a2, a11 +400064a6: 000bb2 l8ui a11, a11, 0 +400064a9: 471bc7 beq a11, a12, 400064f4 +400064ac: d198 l32i.n a9, a1, 52 +400064ae: 91d8 l32i.n a13, a1, 36 +400064b0: dd1b addi.n a13, a13, 1 +400064b2: 1039d7 bltu a9, a13, 400064c6 +400064b5: c1f8 l32i.n a15, a1, 48 +400064b7: f188 l32i.n a8, a1, 60 +400064b9: 41e8 l32i.n a14, a1, 16 +400064bb: 91e9 s32i.n a14, a1, 36 +400064bd: 558a add.n a5, a5, a8 +400064bf: ff8a add.n a15, a15, a8 +400064c1: c1f9 s32i.n a15, a1, 48 +400064c3: ffb6c6 j 400063a2 +400064c6: 225a add.n a2, a2, a5 +400064c8: f01d retw.n +400064ca: e198 l32i.n a9, a1, 56 +400064cc: c4b897 bgeu a8, a9, 40006494 +400064cf: d1c8 l32i.n a12, a1, 52 +400064d1: 0b0c movi.n a11, 0 +400064d3: 91b9 s32i.n a11, a1, 36 +400064d5: c0c8c0 sub a12, a8, a12 +400064d8: cc1b addi.n a12, a12, 1 +400064da: 55ca add.n a5, a5, a12 +400064dc: a45a add.n a10, a4, a5 +400064de: c1a9 s32i.n a10, a1, 48 +400064e0: ffaf86 j 400063a2 +400064e3: 8a0000 depbits a0, a0, 8, 1 +400064e6: e38ad5 call4 3ffe9d94 <_start-0x1626c> +400064e9: 000ee2 l8ui a14, a14, 0 +400064ec: d2da add.n a13, a2, a13 +400064ee: 000dd2 l8ui a13, a13, 0 +400064f1: b99de7 bne a13, a14, 400064ae +400064f4: 089d mov.n a9, a8 +400064f6: 91f8 l32i.n a15, a1, 36 +400064f8: 880b addi.n a8, a8, -1 +400064fa: e73f97 bltu a15, a9, 400064e5 +400064fd: ffeb46 j 400064ae +40006500: 004136 entry a1, 32 +40006503: 03a052 movi a5, 3 +40006506: 1e0527 bnone a5, a2, 40006528 +40006509: 747030 extui a7, a3, 0, 8 +4000650c: f03d nop.n +4000650e: 118076 loop a0, 40006523 +40006511: 440b addi.n a4, a4, -1 +40006513: 5d0426 beqi a4, -1, 40006574 +40006516: 000262 l8ui a6, a2, 0 +40006519: 551677 beq a6, a7, 40006572 +4000651c: 221b addi.n a2, a2, 1 +4000651e: 148020 extui a8, a2, 0, 2 +40006521: 388c beqz.n a8, 40006528 +40006523: fff946 j 4000650c +40006526: b60000 excw +40006529: 812b44 excw +4000652c: 9e .byte 0x9e +4000652d: 7ce8 l32i.n a14, a12, 28 +4000652f: 71fa add.n a7, a1, a15 +40006531: 9e .byte 0x9e +40006532: 40e8 l32i.n a14, a0, 16 +40006534: 3041b2 s8i a11, a1, 48 +40006537: 907490 addx2 a7, a4, a9 +4000653a: 908a73 excw +4000653d: 0bf9 s32i.n a15, a11, 0 +4000653f: 149b76 loopnez a11, 40006557 +40006542: 02c8 l32i.n a12, a2, 0 +40006544: 30cc90 xor a12, a12, a9 +40006547: 30dca0 xor a13, a12, a10 +4000654a: cc7a add.n a12, a12, a7 +4000654c: 10ccd0 and a12, a12, a13 +4000654f: 048c87 bany a12, a8, 40006557 +40006552: fcc442 addi a4, a4, -4 +40006555: 224b addi.n a2, a2, 4 +40006557: 046d mov.n a6, a4 +40006559: 440b addi.n a4, a4, -1 +4000655b: 150426 beqi a4, -1, 40006574 +4000655e: 747030 extui a7, a3, 0, 8 +40006561: 099676 loopnez a6, 4000656e +40006564: 0002d2 l8ui a13, a2, 0 +40006567: 440b addi.n a4, a4, -1 +40006569: 051d77 beq a13, a7, 40006572 +4000656c: 221b addi.n a2, a2, 1 +4000656e: 020c movi.n a2, 0 +40006570: f01d retw.n +40006572: f01d retw.n +40006574: 020c movi.n a2, 0 +40006576: f01d retw.n +40006578: 004136 entry a1, 32 +4000657b: e88b81 l32r a8, 400007a8 <_c_0x00ff0000+0x10> +4000657e: ffaf92 movi a9, -1 +40006581: e88871 l32r a7, 400007a4 <_c_0x00ff0000+0xc> +40006584: 746030 extui a6, a3, 0, 8 +40006587: 03a032 movi a3, 3 +4000658a: 08f616 beqz a6, 4000661d +4000658d: 180327 bnone a3, a2, 400065a9 +40006590: f03d nop.n +40006592: 0f8076 loop a0, 400065a5 +40006595: 000232 l8ui a3, a2, 0 +40006598: 0c8316 beqz a3, 40006664 +4000659b: 791637 beq a6, a3, 40006618 +4000659e: 221b addi.n a2, a2, 1 +400065a0: 144020 extui a4, a2, 0, 2 +400065a3: 248c beqz.n a4, 400065a9 +400065a5: fff9c6 j 40006590 +400065a8: 063d00 excw +400065ab: 850c movi.n a5, 8 +400065ad: 0e8076 loop a0, 400065bf +400065b0: 401500 ssl a5 +400065b3: 1155f0 slli a5, a5, 1 +400065b6: a1a300 sll a10, a3 +400065b9: 2033a0 or a3, a3, a10 +400065bc: 02c5f6 bgeui a5, 32, 400065c2 +400065bf: fffa86 j 400065ad +400065c2: 02a8 l32i.n a10, a2, 0 +400065c4: 30ca90 xor a12, a10, a9 +400065c7: ba8a add.n a11, a10, a8 +400065c9: 10bbc0 and a11, a11, a12 +400065cc: 2d8b77 bany a11, a7, 400065fd +400065cf: 30da30 xor a13, a10, a3 +400065d2: 30ed90 xor a14, a13, a9 +400065d5: dd8a add.n a13, a13, a8 +400065d7: 10dde0 and a13, a13, a14 +400065da: 100d77 bnone a13, a7, 400065ee +400065dd: 000706 j 400065fd +400065e0: 30ea30 xor a14, a10, a3 +400065e3: 30fe90 xor a15, a14, a9 +400065e6: ee8a add.n a14, a14, a8 +400065e8: 10eef0 and a14, a14, a15 +400065eb: 0e8e77 bany a14, a7, 400065fd +400065ee: 12a8 l32i.n a10, a2, 4 +400065f0: 224b addi.n a2, a2, 4 +400065f2: 304a90 xor a4, a10, a9 +400065f5: fa8a add.n a15, a10, a8 +400065f7: 10ff40 and a15, a15, a4 +400065fa: e20f77 bnone a15, a7, 400065e0 +400065fd: 000232 l8ui a3, a2, 0 +40006600: 139c beqz.n a3, 40006615 +40006602: 121637 beq a6, a3, 40006618 +40006605: 098076 loop a0, 40006612 +40006608: 010232 l8ui a3, a2, 1 +4000660b: 221b addi.n a2, a2, 1 +4000660d: 438c beqz.n a3, 40006615 +4000660f: 051637 beq a6, a3, 40006618 +40006612: fffbc6 j 40006605 +40006615: 4b9637 bne a6, a3, 40006664 +40006618: f01d retw.n +4000661a: 000000 ill +4000661d: 0d0327 bnone a3, a2, 4000662e +40006620: 0002a2 l8ui a10, a2, 0 +40006623: ff1a16 beqz a10, 40006618 +40006626: 221b addi.n a2, a2, 1 +40006628: 14b020 extui a11, a2, 0, 2 +4000662b: ff1b56 bnez a11, 40006620 +4000662e: 02c8 l32i.n a12, a2, 0 +40006630: f03d nop.n +40006632: 30dc90 xor a13, a12, a9 +40006635: cc8a add.n a12, a12, a8 +40006637: 10ccd0 and a12, a12, a13 +4000663a: 0e8c77 bany a12, a7, 4000664c +4000663d: 12d8 l32i.n a13, a2, 4 +4000663f: 224b addi.n a2, a2, 4 +40006641: 30ed90 xor a14, a13, a9 +40006644: dd8a add.n a13, a13, a8 +40006646: 10dde0 and a13, a13, a14 +40006649: f00d77 bnone a13, a7, 4000663d +4000664c: 0002e2 l8ui a14, a2, 0 +4000664f: 0020f0 nop +40006652: fc2e16 beqz a14, 40006618 +40006655: 078076 loop a0, 40006660 +40006658: 0102f2 l8ui a15, a2, 1 +4000665b: 221b addi.n a2, a2, 1 +4000665d: fb7f16 beqz a15, 40006618 +40006660: fffc46 j 40006655 +40006663: 020c00 andb b0, b12, b0 +40006666: f01d retw.n +40006668: 002136 entry a1, 16 +4000666b: f01d retw.n +4000666d: 000000 ill + +40006670 <_xtos_set_intlevel>: +40006670: 002136 entry a1, 16 +40006673: 03e630 rsr.ps a3 +40006676: 047c movi.n a4, -16 +40006678: 342020 extui a2, a2, 0, 4 +4000667b: 104430 and a4, a4, a3 +4000667e: 204420 or a4, a4, a2 +40006681: 13e640 wsr.ps a4 +40006684: 342030 extui a2, a3, 0, 4 +40006687: 002010 rsync +4000668a: f01d retw.n +4000668c: 002136 entry a1, 16 +4000668f: 343020 extui a3, a2, 0, 4 +40006692: 03e620 rsr.ps a2 +40006695: 047c movi.n a4, -16 +40006697: 345020 extui a5, a2, 0, 4 +4000669a: c05350 sub a5, a3, a5 +4000669d: 104420 and a4, a4, a2 +400066a0: 204430 or a4, a4, a3 +400066a3: a34250 movltz a4, a2, a5 +400066a6: 13e640 wsr.ps a4 +400066a9: 002010 rsync +400066ac: f01d retw.n +400066ae: 360000 excw +400066b1: f00021 l32r a2, 400026b4 <_X_ets_unk225c+0x458> +400066b4: 1d0041 l32r a4, 3ffcdab4 <_start-0x3254c> +400066b7: 4128f0 srli a2, a15, 8 +400066ba: 5138 l32i.n a3, a1, 20 +400066bc: 01d112 addmi a1, a1, 0x100 +400066bf: 004110 break 1, 1 +400066c2: 003000 rfe +400066c5: 000000 ill +400066c8: 3b6522 s32i a2, a5, 236 +400066cb: 1528 l32i.n a2, a5, 4 +400066cd: 0538 l32i.n a3, a5, 0 +400066cf: 13e620 wsr.ps a2 +400066d2: 4528 l32i.n a2, a5, 16 +400066d4: 6548 l32i.n a4, a5, 24 +400066d6: 002010 rsync +400066d9: 13b130 wsr.epc1 a3 +400066dc: 5538 l32i.n a3, a5, 20 +400066de: 7558 l32i.n a5, a5, 28 +400066e0: 003000 rfe +400066e3: 213600 srai a3, a0, 6 +400066e6: ea2000 depbits a0, a0, 14, 3 +400066e9: f01d03 excw +400066ec: 002136 entry a1, 16 +400066ef: 52cc bnez.n a2, 400066f8 <_xtos_set_intlevel+0x88> +400066f1: 13f030 wsr.ccompare0 a3 +400066f4: 0003c6 j 40006707 <_xtos_set_intlevel+0x97> +400066f7: 22f600 orb b15, b6, b0 +400066fa: f13005 call0 3fff79fc <_start-0x8604> +400066fd: 014613 excw +40006700: 32f600 orbc b15, b6, b0 +40006703: f23005 call0 3fff8a04 <_start-0x75fc> +40006706: 200013 excw +40006709: f01d00 subx8 a1, a13, a0 +4000670c: 002136 entry a1, 16 +4000670f: 52cc bnez.n a2, 40006718 <_xtos_set_intlevel+0xa8> +40006711: 03f020 rsr.ccompare0 a2 +40006714: f01d retw.n +40006716: f60000 excw +40006719: 200422 l8ui a2, a4, 32 +4000671c: 1d03f1 l32r a15, 3ffcdb28 <_start-0x324d8> +4000671f: 32f6f0 orbc b15, b6, b15 +40006722: f22004 excw +40006725: f01d03 excw +40006728: 020c movi.n a2, 0 +4000672a: f01d retw.n +4000672c: 004136 entry a1, 32 +4000672f: 035d mov.n a5, a3 +40006731: 023d mov.n a3, a2 +40006733: 052d mov.n a2, a5 +40006735: 000706 j 40006755 <_xtos_set_intlevel+0xe5> +40006738: 05ad mov.n a10, a5 +4000673a: 03bd mov.n a11, a3 +4000673c: 4c0c movi.n a12, 4 +4000673e: c06c60 sub a6, a12, a6 +40006741: 06cd mov.n a12, a6 +40006743: 000765 call8 400067b8 <_xtos_set_intlevel+0x148> +40006746: 556a add.n a5, a5, a6 +40006748: 336a add.n a3, a3, a6 +4000674a: c04460 sub a4, a4, a6 +4000674d: 000346 j 4000675e <_xtos_set_intlevel+0xee> +40006750: 004136 entry a1, 32 +40006753: 025d mov.n a5, a2 +40006755: 6244b6 bltui a4, 4, 400067bb <_xtos_set_intlevel+0x14b> +40006758: 146020 extui a6, a2, 0, 2 +4000675b: fd9656 bnez a6, 40006738 <_xtos_set_intlevel+0xc8> +4000675e: 417440 srli a7, a4, 4 +40006761: 14b030 extui a11, a3, 0, 2 +40006764: 09cb56 bnez a11, 40006804 <_xtos_set_intlevel+0x194> +40006767: 159776 loopnez a7, 40006780 <_xtos_set_intlevel+0x110> +4000676a: 0368 l32i.n a6, a3, 0 +4000676c: 1378 l32i.n a7, a3, 4 +4000676e: 0569 s32i.n a6, a5, 0 +40006770: 2368 l32i.n a6, a3, 8 +40006772: 1579 s32i.n a7, a5, 4 +40006774: 3378 l32i.n a7, a3, 12 +40006776: 2569 s32i.n a6, a5, 8 +40006778: 10c332 addi a3, a3, 16 +4000677b: 3579 s32i.n a7, a5, 12 +4000677d: 10c552 addi a5, a5, 16 +40006780: 0d6437 bbci a4, 3, 40006791 <_xtos_set_intlevel+0x121> +40006783: 0368 l32i.n a6, a3, 0 +40006785: 1378 l32i.n a7, a3, 4 +40006787: 338b addi.n a3, a3, 8 +40006789: 0569 s32i.n a6, a5, 0 +4000678b: 016572 s32i a7, a5, 4 +4000678e: 08c552 addi a5, a5, 8 +40006791: 076427 bbci a4, 2, 4000679c <_xtos_set_intlevel+0x12c> +40006794: 0368 l32i.n a6, a3, 0 +40006796: 334b addi.n a3, a3, 4 +40006798: 0569 s32i.n a6, a5, 0 +4000679a: 554b addi.n a5, a5, 4 +4000679c: 144040 extui a4, a4, 0, 2 +4000679f: 349c beqz.n a4, 400067b6 <_xtos_set_intlevel+0x146> +400067a1: 0368 l32i.n a6, a3, 0 +400067a3: 0578 l32i.n a7, a5, 0 +400067a5: 402400 ssa8l a4 +400067a8: a16600 sll a6, a6 +400067ab: 917070 srl a7, a7 +400067ae: 403400 ssa8b a4 +400067b1: 817760 src a7, a7, a6 +400067b4: 0579 s32i.n a7, a5, 0 +400067b6: f01d retw.n +400067b8: 002136 entry a1, 16 +400067bb: 041416 beqz a4, 40006800 <_xtos_set_intlevel+0x190> +400067be: c57c movi.n a5, -4 +400067c0: 105350 and a5, a3, a5 +400067c3: 0568 l32i.n a6, a5, 0 +400067c5: 1578 l32i.n a7, a5, 4 +400067c7: 402300 ssa8l a3 +400067ca: 813760 src a3, a7, a6 +400067cd: 14a020 extui a10, a2, 0, 2 +400067d0: c052a0 sub a5, a2, a10 +400067d3: 0588 l32i.n a8, a5, 0 +400067d5: 6a4a add.n a6, a10, a4 +400067d7: 402200 ssa8l a2 +400067da: a1a800 sll a10, a8 +400067dd: 0b46b6 bltui a6, 4, 400067ec <_xtos_set_intlevel+0x17c> +400067e0: 1588 l32i.n a8, a5, 4 +400067e2: 403200 ssa8b a2 +400067e5: 81a3a0 src a10, a3, a10 +400067e8: 05a9 s32i.n a10, a5, 0 +400067ea: 554b addi.n a5, a5, 4 +400067ec: 402400 ssa8l a4 +400067ef: 8133a0 src a3, a3, a10 +400067f2: 402600 ssa8l a6 +400067f5: 918080 srl a8, a8 +400067f8: 403600 ssa8b a6 +400067fb: 818830 src a8, a8, a3 +400067fe: 0589 s32i.n a8, a5, 0 +40006800: f01d retw.n +40006802: 000000 ill +40006805: 304023 excw +40006808: b014b0 addx8 a1, a4, a11 +4000680b: 68c033 excw +4000680e: 977603 excw +40006811: 137821 l32r a2, 3ffcb5f4 <_start-0x34a0c> +40006814: 2388 l32i.n a8, a3, 8 +40006816: 816760 src a6, a7, a6 +40006819: 0569 s32i.n a6, a5, 0 +4000681b: 3398 l32i.n a9, a3, 12 +4000681d: 817870 src a7, a8, a7 +40006820: 1579 s32i.n a7, a5, 4 +40006822: 4368 l32i.n a6, a3, 16 +40006824: 818980 src a8, a9, a8 +40006827: 2589 s32i.n a8, a5, 8 +40006829: 10c332 addi a3, a3, 16 +4000682c: 819690 src a9, a6, a9 +4000682f: 3599 s32i.n a9, a5, 12 +40006831: 10c552 addi a5, a5, 16 +40006834: 146437 bbci a4, 3, 4000684c <_xtos_set_intlevel+0x1dc> +40006837: 1378 l32i.n a7, a3, 4 +40006839: 2388 l32i.n a8, a3, 8 +4000683b: 816760 src a6, a7, a6 +4000683e: 0569 s32i.n a6, a5, 0 +40006840: 338b addi.n a3, a3, 8 +40006842: 817870 src a7, a8, a7 +40006845: 1579 s32i.n a7, a5, 4 +40006847: 558b addi.n a5, a5, 8 +40006849: 206880 or a6, a8, a8 +4000684c: 0c6427 bbci a4, 2, 4000685c <_xtos_set_intlevel+0x1ec> +4000684f: 1378 l32i.n a7, a3, 4 +40006851: 334b addi.n a3, a3, 4 +40006853: 816760 src a6, a7, a6 +40006856: 0569 s32i.n a6, a5, 0 +40006858: 554b addi.n a5, a5, 4 +4000685a: 076d mov.n a6, a7 +4000685c: 1378 l32i.n a7, a3, 4 +4000685e: 0538 l32i.n a3, a5, 0 +40006860: 816760 src a6, a7, a6 +40006863: 402400 ssa8l a4 +40006866: a16600 sll a6, a6 +40006869: 913030 srl a3, a3 +4000686c: 403400 ssa8b a4 +4000686f: 813360 src a3, a3, a6 +40006872: 0539 s32i.n a3, a5, 0 +40006874: f01d retw.n +40006876: 360000 excw +40006879: 200021 l32r a2, 3ffce87c <_start-0x31784> +4000687c: 1d03e2 l8ui a14, a3, 29 +4000687f: 2136f0 srai a3, a15, 6 +40006882: e32000 excw +40006885: f01d13 excw + +40006888 <__divsi3>: +40006888: 002136 entry a1, 16 +4000688b: d22230 quos a2, a2, a3 +4000688e: f01d retw.n + +40006890 : +40006890: 004136 entry a1, 32 +40006893: 604030 neg a4, a3 +40006896: 223a add.n a2, a2, a3 +40006898: 220b addi.n a2, a2, -1 +4000689a: 102240 and a2, a2, a4 +4000689d: f01d retw.n + ... + +400068a0 : +400068a0: 004136 entry a1, 32 +400068a3: 223a add.n a2, a2, a3 +400068a5: 220b addi.n a2, a2, -1 +400068a7: c22230 quou a2, a2, a3 +400068aa: f01d retw.n +400068ac: 004136 entry a1, 32 +400068af: 430b addi.n a4, a3, -1 +400068b1: 320426 beqi a4, -1, 400068e7 +400068b4: 206330 or a6, a3, a3 +400068b7: 248030 extui a8, a3, 0, 3 +400068ba: 00a032 movi a3, 0 +400068bd: 049876 loopnez a8, 400068c5 +400068c0: 004232 s8i a3, a2, 0 +400068c3: 221b addi.n a2, a2, 1 +400068c5: 414360 srli a4, a6, 3 +400068c8: f03d nop.n +400068ca: 199476 loopnez a4, 400068e7 +400068cd: 004232 s8i a3, a2, 0 +400068d0: 014232 s8i a3, a2, 1 +400068d3: 024232 s8i a3, a2, 2 +400068d6: 034232 s8i a3, a2, 3 +400068d9: 044232 s8i a3, a2, 4 +400068dc: 054232 s8i a3, a2, 5 +400068df: 064232 s8i a3, a2, 6 +400068e2: 074232 s8i a3, a2, 7 +400068e5: 228b addi.n a2, a2, 8 +400068e7: f01d retw.n +400068e9: 000000 ill + +400068ec : +400068ec: 004136 entry a1, 32 +400068ef: 1b44b6 bltui a4, 4, 4000690e +400068f2: 205320 or a5, a3, a2 +400068f5: 145050 extui a5, a5, 0, 2 +400068f8: 25dc bnez.n a5, 4000690e +400068fa: 418240 srli a8, a4, 2 +400068fd: 0d9876 loopnez a8, 4000690e +40006900: 03a8 l32i.n a10, a3, 0 +40006902: 0298 l32i.n a9, a2, 0 +40006904: 0699a7 bne a9, a10, 4000690e +40006907: 224b addi.n a2, a2, 4 +40006909: 334b addi.n a3, a3, 4 +4000690b: fcc442 addi a4, a4, -4 +4000690e: 046d mov.n a6, a4 +40006910: 440b addi.n a4, a4, -1 +40006912: 110426 beqi a4, -1, 40006927 +40006915: 0e9676 loopnez a6, 40006927 +40006918: 000372 l8ui a7, a3, 0 +4000691b: 000282 l8ui a8, a2, 0 +4000691e: 221b addi.n a2, a2, 1 +40006920: 089877 bne a8, a7, 4000692c +40006923: 440b addi.n a4, a4, -1 +40006925: 331b addi.n a3, a3, 1 +40006927: 020c movi.n a2, 0 +40006929: f01d retw.n +4000692b: 287000 excw +4000692e: f01dc0 subx8 a1, a13, a12 +40006931: 000000 ill +40006934: 947600 extui a7, a0, 6, 10 +40006937: 6209 s32i.n a0, a2, 24 +40006939: 1b0003 excw +4000693c: 456233 excw +4000693f: 551b00 extui a1, a0, 27, 6 +40006942: f01d retw.n +40006944: ed74b6 bltui a4, 7, 40006935 +40006947: 000362 l8ui a6, a3, 0 +4000694a: 331b addi.n a3, a3, 1 +4000694c: ffc442 addi a4, a4, -1 +4000694f: 004562 s8i a6, a5, 0 +40006952: 01c552 addi a5, a5, 1 +40006955: 276517 bbci a5, 1, 40006980 +40006958: d964b6 bltui a4, 6, 40006935 +4000695b: 000362 l8ui a6, a3, 0 +4000695e: 010372 l8ui a7, a3, 1 +40006961: 332b addi.n a3, a3, 2 +40006963: fec442 addi a4, a4, -2 +40006966: 004562 s8i a6, a5, 0 +40006969: 014572 s8i a7, a5, 1 +4000696c: 552b addi.n a5, a5, 2 +4000696e: 000386 j 40006980 +40006971: 000000 ill + +40006974 : +40006974: 002136 entry a1, 16 +40006977: 205220 or a5, a2, a2 +4000697a: c6e207 bbsi a2, 0, 40006944 +4000697d: d7e217 bbsi a2, 1, 40006958 +40006980: 417440 srli a7, a4, 4 +40006983: 018320 slli a8, a3, 30 +40006986: 05a856 bnez a8, 400069e4 +40006989: 159776 loopnez a7, 400069a2 +4000698c: 0368 l32i.n a6, a3, 0 +4000698e: 1378 l32i.n a7, a3, 4 +40006990: 0569 s32i.n a6, a5, 0 +40006992: 2368 l32i.n a6, a3, 8 +40006994: 1579 s32i.n a7, a5, 4 +40006996: 3378 l32i.n a7, a3, 12 +40006998: 2569 s32i.n a6, a5, 8 +4000699a: 10c332 addi a3, a3, 16 +4000699d: 3579 s32i.n a7, a5, 12 +4000699f: 10c552 addi a5, a5, 16 +400069a2: 0b6437 bbci a4, 3, 400069b1 +400069a5: 0368 l32i.n a6, a3, 0 +400069a7: 1378 l32i.n a7, a3, 4 +400069a9: 338b addi.n a3, a3, 8 +400069ab: 0569 s32i.n a6, a5, 0 +400069ad: 1579 s32i.n a7, a5, 4 +400069af: 558b addi.n a5, a5, 8 +400069b1: 07e427 bbsi a4, 2, 400069bc +400069b4: 14e417 bbsi a4, 1, 400069cc +400069b7: 21e407 bbsi a4, 0, 400069dc +400069ba: f01d retw.n +400069bc: 0368 l32i.n a6, a3, 0 +400069be: 334b addi.n a3, a3, 4 +400069c0: 0569 s32i.n a6, a5, 0 +400069c2: 554b addi.n a5, a5, 4 +400069c4: 04e417 bbsi a4, 1, 400069cc +400069c7: 11e407 bbsi a4, 0, 400069dc +400069ca: f01d retw.n +400069cc: 001362 l16ui a6, a3, 0 +400069cf: 332b addi.n a3, a3, 2 +400069d1: 005562 s16i a6, a5, 0 +400069d4: 552b addi.n a5, a5, 2 +400069d6: 02e407 bbsi a4, 0, 400069dc +400069d9: f01d retw.n +400069db: 036200 excw +400069de: 456200 extui a6, a0, 18, 5 +400069e1: f01d00 subx8 a1, a13, a0 +400069e4: ffa416 beqz a4, 400069e2 +400069e7: 402300 ssa8l a3 +400069ea: 15be80 extui a11, a8, 30, 2 +400069ed: c033b0 sub a3, a3, a11 +400069f0: 0368 l32i.n a6, a3, 0 +400069f2: 219776 loopnez a7, 40006a17 +400069f5: 1378 l32i.n a7, a3, 4 +400069f7: 2388 l32i.n a8, a3, 8 +400069f9: 816760 src a6, a7, a6 +400069fc: 0569 s32i.n a6, a5, 0 +400069fe: 3398 l32i.n a9, a3, 12 +40006a00: 817870 src a7, a8, a7 +40006a03: 1579 s32i.n a7, a5, 4 +40006a05: 4368 l32i.n a6, a3, 16 +40006a07: 818980 src a8, a9, a8 +40006a0a: 2589 s32i.n a8, a5, 8 +40006a0c: 10c332 addi a3, a3, 16 +40006a0f: 819690 src a9, a6, a9 +40006a12: 3599 s32i.n a9, a5, 12 +40006a14: 10c552 addi a5, a5, 16 +40006a17: 156437 bbci a4, 3, 40006a30 +40006a1a: 1378 l32i.n a7, a3, 4 +40006a1c: 2388 l32i.n a8, a3, 8 +40006a1e: 816760 src a6, a7, a6 +40006a21: 0569 s32i.n a6, a5, 0 +40006a23: 338b addi.n a3, a3, 8 +40006a25: 817870 src a7, a8, a7 +40006a28: 1579 s32i.n a7, a5, 4 +40006a2a: 08c552 addi a5, a5, 8 +40006a2d: 206880 or a6, a8, a8 +40006a30: 0c6427 bbci a4, 2, 40006a40 +40006a33: 1378 l32i.n a7, a3, 4 +40006a35: 334b addi.n a3, a3, 4 +40006a37: 816760 src a6, a7, a6 +40006a3a: 0569 s32i.n a6, a5, 0 +40006a3c: 554b addi.n a5, a5, 4 +40006a3e: 076d mov.n a6, a7 +40006a40: 33ba add.n a3, a3, a11 +40006a42: 06e417 bbsi a4, 1, 40006a4c +40006a45: 18e407 bbsi a4, 0, 40006a61 +40006a48: f01d retw.n +40006a4a: 620000 excw +40006a4d: 720003 excw +40006a50: 2b0103 excw +40006a53: 456233 excw +40006a56: 457200 extui a7, a0, 18, 5 +40006a59: 552b01 l32r a0, 3ffdbf08 <_start-0x240f8> +40006a5c: 01e407 bbsi a4, 0, 40006a61 +40006a5f: f01d retw.n +40006a61: 000362 l8ui a6, a3, 0 +40006a64: 004562 s8i a6, a5, 0 +40006a67: f01d retw.n +40006a69: 000000 ill + +40006a6c : +40006a6c: 004136 entry a1, 32 +40006a6f: 049d mov.n a9, a4 +40006a71: 028d mov.n a8, a2 +40006a73: 04ad mov.n a10, a4 +40006a75: 032d mov.n a2, a3 +40006a77: 087d mov.n a7, a8 +40006a79: 1bb387 bgeu a3, a8, 40006a98 +40006a7c: 343a add.n a3, a4, a3 +40006a7e: 16b837 bgeu a8, a3, 40006a98 +40006a81: 748a add.n a7, a4, a8 +40006a83: 131416 beqz a4, 40006bb8 +40006a86: 099476 loopnez a4, 40006a93 +40006a89: 770b addi.n a7, a7, -1 +40006a8b: 330b addi.n a3, a3, -1 +40006a8d: 000342 l8ui a4, a3, 0 +40006a90: 004742 s8i a4, a7, 0 +40006a93: 082d mov.n a2, a8 +40006a95: f01d retw.n +40006a97: b9f600 excw +40006a9a: 320602 l8ui a0, a6, 50 +40006a9d: b28000 mulsh a8, a0, a0 +40006aa0: b0b020 addx8 a11, a0, a2 +40006aa3: fb5614 excw +40006aa6: 7d0b addi.n a7, a13, -1 +40006aa8: 9008 l32i.n a0, a0, 36 +40006aaa: 9014c4 excw +40006aad: 4d4164 excw +40006ab0: 9c7606 j 3ffedc8c <_start-0x12374> +40006ab3: 025815 call4 40009034 <__umoddi3+0x21d0> +40006ab6: 0759 s32i.n a5, a7, 0 +40006ab8: 12f8 l32i.n a15, a2, 4 +40006aba: 17f9 s32i.n a15, a7, 4 +40006abc: 22e8 l32i.n a14, a2, 8 +40006abe: 27e9 s32i.n a14, a7, 8 +40006ac0: 32d8 l32i.n a13, a2, 12 +40006ac2: 37d9 s32i.n a13, a7, 12 +40006ac4: 10c222 addi a2, a2, 16 +40006ac7: 10c772 addi a7, a7, 16 +40006aca: 413240 srli a3, a4, 2 +40006acd: 459376 loopnez a3, 40006b16 +40006ad0: 0238 l32i.n a3, a2, 0 +40006ad2: 0739 s32i.n a3, a7, 0 +40006ad4: 1238 l32i.n a3, a2, 4 +40006ad6: 1739 s32i.n a3, a7, 4 +40006ad8: 2238 l32i.n a3, a2, 8 +40006ada: 2739 s32i.n a3, a7, 8 +40006adc: 3238 l32i.n a3, a2, 12 +40006ade: 3739 s32i.n a3, a7, 12 +40006ae0: 4238 l32i.n a3, a2, 16 +40006ae2: 4739 s32i.n a3, a7, 16 +40006ae4: 5238 l32i.n a3, a2, 20 +40006ae6: 5739 s32i.n a3, a7, 20 +40006ae8: 6238 l32i.n a3, a2, 24 +40006aea: 6739 s32i.n a3, a7, 24 +40006aec: 7238 l32i.n a3, a2, 28 +40006aee: 7739 s32i.n a3, a7, 28 +40006af0: 8238 l32i.n a3, a2, 32 +40006af2: 8739 s32i.n a3, a7, 32 +40006af4: 9238 l32i.n a3, a2, 36 +40006af6: 9739 s32i.n a3, a7, 36 +40006af8: a238 l32i.n a3, a2, 40 +40006afa: a739 s32i.n a3, a7, 40 +40006afc: b238 l32i.n a3, a2, 44 +40006afe: b739 s32i.n a3, a7, 44 +40006b00: c238 l32i.n a3, a2, 48 +40006b02: c739 s32i.n a3, a7, 48 +40006b04: d238 l32i.n a3, a2, 52 +40006b06: d739 s32i.n a3, a7, 52 +40006b08: e238 l32i.n a3, a2, 56 +40006b0a: e739 s32i.n a3, a7, 56 +40006b0c: f238 l32i.n a3, a2, 60 +40006b0e: f739 s32i.n a3, a7, 60 +40006b10: 40c222 addi a2, a2, 64 +40006b13: 40c772 addi a7, a7, 64 +40006b16: 1166c0 slli a6, a6, 4 +40006b19: c06960 sub a6, a9, a6 +40006b1c: 06ad mov.n a10, a6 +40006b1e: 4546b6 bltui a6, 4, 40006b67 +40006b21: 24b260 extui a11, a6, 2, 3 +40006b24: 419260 srli a9, a6, 2 +40006b27: 204990 or a4, a9, a9 +40006b2a: 413340 srli a3, a4, 3 +40006b2d: 079b76 loopnez a11, 40006b38 +40006b30: 02c8 l32i.n a12, a2, 0 +40006b32: 07c9 s32i.n a12, a7, 0 +40006b34: 224b addi.n a2, a2, 4 +40006b36: 774b addi.n a7, a7, 4 +40006b38: 11a9e0 slli a10, a9, 2 +40006b3b: 259376 loopnez a3, 40006b64 +40006b3e: 0238 l32i.n a3, a2, 0 +40006b40: 0739 s32i.n a3, a7, 0 +40006b42: 1238 l32i.n a3, a2, 4 +40006b44: 1739 s32i.n a3, a7, 4 +40006b46: 2238 l32i.n a3, a2, 8 +40006b48: 2739 s32i.n a3, a7, 8 +40006b4a: 3238 l32i.n a3, a2, 12 +40006b4c: 3739 s32i.n a3, a7, 12 +40006b4e: 4238 l32i.n a3, a2, 16 +40006b50: 4739 s32i.n a3, a7, 16 +40006b52: 5238 l32i.n a3, a2, 20 +40006b54: 5739 s32i.n a3, a7, 20 +40006b56: 6238 l32i.n a3, a2, 24 +40006b58: 6739 s32i.n a3, a7, 24 +40006b5a: 7238 l32i.n a3, a2, 28 +40006b5c: 7739 s32i.n a3, a7, 28 +40006b5e: 20c222 addi a2, a2, 32 +40006b61: 20c772 addi a7, a7, 32 +40006b64: c0a6a0 sub a10, a6, a10 +40006b67: ba0b addi.n a11, a10, -1 +40006b69: 4b0b26 beqi a11, -1, 40006bb8 +40006b6c: 4133a0 srli a3, a10, 3 +40006b6f: 24c0a0 extui a12, a10, 0, 3 +40006b72: 099c76 loopnez a12, 40006b7f +40006b75: 0002d2 l8ui a13, a2, 0 +40006b78: 0047d2 s8i a13, a7, 0 +40006b7b: 221b addi.n a2, a2, 1 +40006b7d: 771b addi.n a7, a7, 1 +40006b7f: f03d nop.n +40006b81: 339376 loopnez a3, 40006bb8 +40006b84: 000232 l8ui a3, a2, 0 +40006b87: 004732 s8i a3, a7, 0 +40006b8a: 010232 l8ui a3, a2, 1 +40006b8d: 014732 s8i a3, a7, 1 +40006b90: 020232 l8ui a3, a2, 2 +40006b93: 024732 s8i a3, a7, 2 +40006b96: 030232 l8ui a3, a2, 3 +40006b99: 034732 s8i a3, a7, 3 +40006b9c: 040232 l8ui a3, a2, 4 +40006b9f: 044732 s8i a3, a7, 4 +40006ba2: 050232 l8ui a3, a2, 5 +40006ba5: 054732 s8i a3, a7, 5 +40006ba8: 060232 l8ui a3, a2, 6 +40006bab: 064732 s8i a3, a7, 6 +40006bae: 070232 l8ui a3, a2, 7 +40006bb1: 074732 s8i a3, a7, 7 +40006bb4: 228b addi.n a2, a2, 8 +40006bb6: 778b addi.n a7, a7, 8 +40006bb8: 082d mov.n a2, a8 +40006bba: f01d retw.n +40006bbc: 947600 extui a7, a0, 6, 10 +40006bbf: 453204 excw +40006bc2: 551b00 extui a1, a0, 27, 6 +40006bc5: f01d retw.n +40006bc7: 84b600 extui a11, a0, 6, 9 +40006bca: 4532f1 l32r a15, 3ffd8094 <_start-0x27f6c> +40006bcd: 551b00 extui a1, a0, 27, 6 +40006bd0: 440b addi.n a4, a4, -1 +40006bd2: 286517 bbci a5, 1, 40006bfe +40006bd5: e484b6 bltui a4, 8, 40006bbd +40006bd8: 005532 s16i a3, a5, 0 +40006bdb: 552b addi.n a5, a5, 2 +40006bdd: fec442 addi a4, a4, -2 +40006be0: 000686 j 40006bfe + ... + +40006be4 : +40006be4: 002136 entry a1, 16 +40006be7: 743030 extui a3, a3, 0, 8 +40006bea: 117380 slli a7, a3, 8 +40006bed: 203370 or a3, a3, a7 +40006bf0: 117300 slli a7, a3, 16 +40006bf3: 203370 or a3, a3, a7 +40006bf6: 025d mov.n a5, a2 +40006bf8: cce207 bbsi a2, 0, 40006bc8 +40006bfb: d6e217 bbsi a2, 1, 40006bd5 +40006bfe: 417440 srli a7, a4, 4 +40006c01: 0a9776 loopnez a7, 40006c0f +40006c04: 0539 s32i.n a3, a5, 0 +40006c06: 1539 s32i.n a3, a5, 4 +40006c08: 2539 s32i.n a3, a5, 8 +40006c0a: 3539 s32i.n a3, a5, 12 +40006c0c: 10c552 addi a5, a5, 16 +40006c0f: 066437 bbci a4, 3, 40006c19 +40006c12: 0539 s32i.n a3, a5, 0 +40006c14: 1539 s32i.n a3, a5, 4 +40006c16: 08c552 addi a5, a5, 8 +40006c19: 036427 bbci a4, 2, 40006c20 +40006c1c: 0539 s32i.n a3, a5, 0 +40006c1e: 554b addi.n a5, a5, 4 +40006c20: 046417 bbci a4, 1, 40006c28 +40006c23: 005532 s16i a3, a5, 0 +40006c26: 552b addi.n a5, a5, 2 +40006c28: 026407 bbci a4, 0, 40006c2e +40006c2b: 004532 s8i a3, a5, 0 +40006c2e: f01d retw.n + +40006c30 <__udivdi3>: +40006c30: 008136 entry a1, 64 +40006c33: 02ad mov.n a10, a2 +40006c35: 038d mov.n a8, a3 +40006c37: 0b2c movi.n a11, 32 +40006c39: 049d mov.n a9, a4 +40006c3b: 0a2516 beqz a5, 40006ce1 <__udivdi3+0xb1> +40006c3e: 07b357 bgeu a3, a5, 40006c49 <__udivdi3+0x19> +40006c41: 020c movi.n a2, 0 +40006c43: 030c movi.n a3, 0 +40006c45: f01d retw.n +40006c47: 900000 addx2 a0, a0, a0 +40006c4a: 1640f5 call12 4001d058 <__bss_start+0xd058> +40006c4d: 1e89 s32i.n a8, a14, 4 +40006c4f: c02b90 sub a2, a11, a9 +40006c52: 400200 ssr a2 +40006c55: 91f030 srl a15, a3 +40006c58: 401900 ssl a9 +40006c5b: 81c3a0 src a12, a3, a10 +40006c5e: 81b540 src a11, a5, a4 +40006c61: f480b0 extui a8, a11, 0, 16 +40006c64: f5d0b0 extui a13, a11, 16, 16 +40006c67: 404010 ssai 16 +40006c6a: c2efd0 quou a14, a15, a13 +40006c6d: 0e5d mov.n a5, a14 +40006c6f: 8238e0 mull a3, a8, a14 +40006c72: e2ffd0 remu a15, a15, a13 +40006c75: 81ffc0 src a15, a15, a12 +40006c78: 0f2d mov.n a2, a15 +40006c7a: 0ebf37 bgeu a15, a3, 40006c8c <__udivdi3+0x5c> +40006c7d: 5e0b addi.n a5, a14, -1 +40006c7f: 2bfa add.n a2, a11, a15 +40006c81: 0732b7 bltu a2, a11, 40006c8c <__udivdi3+0x5c> +40006c84: 04b237 bgeu a2, a3, 40006c8c <__udivdi3+0x5c> +40006c87: fece52 addi a5, a14, -2 +40006c8a: 2b2a add.n a2, a11, a2 +40006c8c: 115500 slli a5, a5, 16 +40006c8f: c03230 sub a3, a2, a3 +40006c92: 401900 ssl a9 +40006c95: a14400 sll a4, a4 +40006c98: c2e3d0 quou a14, a3, a13 +40006c9b: 0e2d mov.n a2, a14 +40006c9d: 8288e0 mull a8, a8, a14 +40006ca0: e233d0 remu a3, a3, a13 +40006ca3: 0bf3c0 depbits a12, a3, 16, 16 +40006ca6: 0c3d mov.n a3, a12 +40006ca8: 11bc87 bgeu a12, a8, 40006cbd <__udivdi3+0x8d> +40006cab: ffce22 addi a2, a14, -1 +40006cae: 803bc0 add a3, a11, a12 +40006cb1: 0833b7 bltu a3, a11, 40006cbd <__udivdi3+0x8d> +40006cb4: 05b387 bgeu a3, a8, 40006cbd <__udivdi3+0x8d> +40006cb7: 803b30 add a3, a11, a3 +40006cba: fece22 addi a2, a14, -2 +40006cbd: 202250 or a2, a2, a5 +40006cc0: 825240 mull a5, a2, a4 +40006cc3: a24240 muluh a4, a2, a4 +40006cc6: c03380 sub a3, a3, a8 +40006cc9: 0b3347 bltu a3, a4, 40006cd8 <__udivdi3+0xa8> +40006ccc: c06340 sub a6, a3, a4 +40006ccf: 083656 bnez a6, 40006d56 <__udivdi3+0x126> +40006cd2: a17a00 sll a7, a10 +40006cd5: 7db757 bgeu a7, a5, 40006d56 <__udivdi3+0x126> +40006cd8: 220b addi.n a2, a2, -1 +40006cda: 030c movi.n a3, 0 +40006cdc: f01d retw.n +40006cde: 000000 ill +40006ce1: 75b347 bgeu a3, a4, 40006d5a <__udivdi3+0x12a> +40006ce4: 40f450 nsau a5, a4 +40006ce7: a58c beqz.n a5, 40006cf5 <__udivdi3+0xc5> +40006ce9: 401500 ssl a5 +40006cec: a19400 sll a9, a4 +40006cef: 813320 src a3, a3, a2 +40006cf2: a12200 sll a2, a2 +40006cf5: f44090 extui a4, a9, 0, 16 +40006cf8: 404010 ssai 16 +40006cfb: f55090 extui a5, a9, 16, 16 +40006cfe: e2c350 remu a12, a3, a5 +40006d01: c2b350 quou a11, a3, a5 +40006d04: 0bad mov.n a10, a11 +40006d06: 81cc20 src a12, a12, a2 +40006d09: 8284b0 mull a8, a4, a11 +40006d0c: 0c3d mov.n a3, a12 +40006d0e: 0ebc87 bgeu a12, a8, 40006d20 <__udivdi3+0xf0> +40006d11: ab0b addi.n a10, a11, -1 +40006d13: 3c9a add.n a3, a12, a9 +40006d15: 073397 bltu a3, a9, 40006d20 <__udivdi3+0xf0> +40006d18: 04b387 bgeu a3, a8, 40006d20 <__udivdi3+0xf0> +40006d1b: fecba2 addi a10, a11, -2 +40006d1e: 393a add.n a3, a9, a3 +40006d20: c0d380 sub a13, a3, a8 +40006d23: c28d50 quou a8, a13, a5 +40006d26: 083d mov.n a3, a8 +40006d28: 824480 mull a4, a4, a8 +40006d2b: e2dd50 remu a13, a13, a5 +40006d2e: 0bfd20 depbits a2, a13, 16, 16 +40006d31: 17b247 bgeu a2, a4, 40006d4c <__udivdi3+0x11c> +40006d34: 380b addi.n a3, a8, -1 +40006d36: 229a add.n a2, a2, a9 +40006d38: 103297 bltu a2, a9, 40006d4c <__udivdi3+0x11c> +40006d3b: 0db247 bgeu a2, a4, 40006d4c <__udivdi3+0x11c> +40006d3e: 112a00 slli a2, a10, 16 +40006d41: fec832 addi a3, a8, -2 +40006d44: 202320 or a2, a3, a2 +40006d47: 030c movi.n a3, 0 +40006d49: f01d retw.n +40006d4b: 2a0000 depbits a0, a0, 2, 1 +40006d4e: 232011 l32r a1, 3ffcf9d0 <_start-0x30630> +40006d51: 030c20 rsr.scompare1 a2 +40006d54: f01d retw.n +40006d56: 030c movi.n a3, 0 +40006d58: f01d retw.n +40006d5a: 34cc bnez.n a4, 40006d61 <__udivdi3+0x131> +40006d5c: 190c movi.n a9, 1 +40006d5e: c29940 quou a9, a9, a4 +40006d61: 40f940 nsau a4, a9 +40006d64: 0ec416 beqz a4, 40006e54 <__udivdi3+0x224> +40006d67: 401400 ssl a4 +40006d6a: c06b40 sub a6, a11, a4 +40006d6d: a19900 sll a9, a9 +40006d70: 400600 ssr a6 +40006d73: 91d080 srl a13, a8 +40006d76: 401400 ssl a4 +40006d79: a15800 sll a5, a8 +40006d7c: 400600 ssr a6 +40006d7f: 9130a0 srl a3, a10 +40006d82: 401400 ssl a4 +40006d85: 203350 or a3, a3, a5 +40006d88: f44090 extui a4, a9, 0, 16 +40006d8b: a12a00 sll a2, a10 +40006d8e: f55090 extui a5, a9, 16, 16 +40006d91: 404010 ssai 16 +40006d94: c2cd50 quou a12, a13, a5 +40006d97: 0cbd mov.n a11, a12 +40006d99: e2dd50 remu a13, a13, a5 +40006d9c: 81dd30 src a13, a13, a3 +40006d9f: 82a4c0 mull a10, a4, a12 +40006da2: 0d8d mov.n a8, a13 +40006da4: 10bda7 bgeu a13, a10, 40006db8 <__udivdi3+0x188> +40006da7: ffccb2 addi a11, a12, -1 +40006daa: 808d90 add a8, a13, a9 +40006dad: 073897 bltu a8, a9, 40006db8 <__udivdi3+0x188> +40006db0: 04b8a7 bgeu a8, a10, 40006db8 <__udivdi3+0x188> +40006db3: feccb2 addi a11, a12, -2 +40006db6: 898a add.n a8, a9, a8 +40006db8: c0d8a0 sub a13, a8, a10 +40006dbb: e2ed50 remu a14, a13, a5 +40006dbe: 0bfe30 depbits a3, a14, 16, 16 +40006dc1: c2dd50 quou a13, a13, a5 +40006dc4: 0dad mov.n a10, a13 +40006dc6: 82c4d0 mull a12, a4, a13 +40006dc9: 038d mov.n a8, a3 +40006dcb: 0eb3c7 bgeu a3, a12, 40006ddd <__udivdi3+0x1ad> +40006dce: ad0b addi.n a10, a13, -1 +40006dd0: 839a add.n a8, a3, a9 +40006dd2: 073897 bltu a8, a9, 40006ddd <__udivdi3+0x1ad> +40006dd5: 04b8c7 bgeu a8, a12, 40006ddd <__udivdi3+0x1ad> +40006dd8: fecda2 addi a10, a13, -2 +40006ddb: 898a add.n a8, a9, a8 +40006ddd: c038c0 sub a3, a8, a12 +40006de0: 11bb00 slli a11, a11, 16 +40006de3: 20bab0 or a11, a10, a11 +40006de6: 404010 ssai 16 +40006de9: e2d350 remu a13, a3, a5 +40006dec: c2c350 quou a12, a3, a5 +40006def: 8284c0 mull a8, a4, a12 +40006df2: 0c3d mov.n a3, a12 +40006df4: 81dd20 src a13, a13, a2 +40006df7: 0dad mov.n a10, a13 +40006df9: 0fbd87 bgeu a13, a8, 40006e0c <__udivdi3+0x1dc> +40006dfc: 3c0b addi.n a3, a12, -1 +40006dfe: ad9a add.n a10, a13, a9 +40006e00: 083a97 bltu a10, a9, 40006e0c <__udivdi3+0x1dc> +40006e03: 05ba87 bgeu a10, a8, 40006e0c <__udivdi3+0x1dc> +40006e06: fecc32 addi a3, a12, -2 +40006e09: 80a9a0 add a10, a9, a10 +40006e0c: c0ea80 sub a14, a10, a8 +40006e0f: c28e50 quou a8, a14, a5 +40006e12: e2ee50 remu a14, a14, a5 +40006e15: 0bfe20 depbits a2, a14, 16, 16 +40006e18: 825480 mull a5, a4, a8 +40006e1b: 084d mov.n a4, a8 +40006e1d: 23b257 bgeu a2, a5, 40006e44 <__udivdi3+0x214> +40006e20: 480b addi.n a4, a8, -1 +40006e22: 229a add.n a2, a2, a9 +40006e24: 1c3297 bltu a2, a9, 40006e44 <__udivdi3+0x214> +40006e27: 19b257 bgeu a2, a5, 40006e44 <__udivdi3+0x214> +40006e2a: 113300 slli a3, a3, 16 +40006e2d: fec822 addi a2, a8, -2 +40006e30: 202230 or a2, a2, a3 +40006e33: 0b3d mov.n a3, a11 +40006e35: f01d retw.n +40006e37: 353700 extui a3, a0, 23, 4 +40006e3a: 324702 s8i a0, a7, 50 +40006e3d: 0f .byte 0xf +40006e3e: 120c movi.n a2, 1 +40006e40: 030c movi.n a3, 0 +40006e42: f01d retw.n +40006e44: 112300 slli a2, a3, 16 +40006e47: 202420 or a2, a4, a2 +40006e4a: 0b3d mov.n a3, a11 +40006e4c: f01d retw.n +40006e4e: 020c movi.n a2, 0 +40006e50: 030c movi.n a3, 0 +40006e52: f01d retw.n +40006e54: c03890 sub a3, a8, a9 +40006e57: f55090 extui a5, a9, 16, 16 +40006e5a: f44090 extui a4, a9, 0, 16 +40006e5d: 1b0c movi.n a11, 1 +40006e5f: ffe0c6 j 40006de6 <__udivdi3+0x1b6> + ... + +40006e64 <__umoddi3>: +40006e64: 00a136 entry a1, 80 +40006e67: 029d mov.n a9, a2 +40006e69: 03ad mov.n a10, a3 +40006e6b: 0b2c movi.n a11, 32 +40006e6d: 048d mov.n a8, a4 +40006e6f: 0d2516 beqz a5, 40006f45 <__umoddi3+0xe1> +40006e72: 02b357 bgeu a3, a5, 40006e78 <__umoddi3+0x14> +40006e75: 0031c6 j 40006f40 <__umoddi3+0xdc> +40006e78: 40f580 nsau a8, a5 +40006e7b: 151816 beqz a8, 40006fd0 <__umoddi3+0x16c> +40006e7e: c0eb80 sub a14, a11, a8 +40006e81: 400e00 ssr a14 +40006e84: 916030 srl a6, a3 +40006e87: 401800 ssl a8 +40006e8a: 81c320 src a12, a3, a2 +40006e8d: 815540 src a5, a5, a4 +40006e90: f4b050 extui a11, a5, 0, 16 +40006e93: f5d050 extui a13, a5, 16, 16 +40006e96: 404010 ssai 16 +40006e99: c2f6d0 quou a15, a6, a13 +40006e9c: 0f9d mov.n a9, a15 +40006e9e: 82abf0 mull a10, a11, a15 +40006ea1: e266d0 remu a6, a6, a13 +40006ea4: 8166c0 src a6, a6, a12 +40006ea7: 063d mov.n a3, a6 +40006ea9: 0fb6a7 bgeu a6, a10, 40006ebc <__umoddi3+0x58> +40006eac: 9f0b addi.n a9, a15, -1 +40006eae: 356a add.n a3, a5, a6 +40006eb0: 083357 bltu a3, a5, 40006ebc <__umoddi3+0x58> +40006eb3: 05b3a7 bgeu a3, a10, 40006ebc <__umoddi3+0x58> +40006eb6: fecf92 addi a9, a15, -2 +40006eb9: 803530 add a3, a5, a3 +40006ebc: 401800 ssl a8 +40006ebf: 119900 slli a9, a9, 16 +40006ec2: c063a0 sub a6, a3, a10 +40006ec5: c2f6d0 quou a15, a6, a13 +40006ec8: 0fad mov.n a10, a15 +40006eca: 82bbf0 mull a11, a11, a15 +40006ecd: e266d0 remu a6, a6, a13 +40006ed0: 0bf6c0 depbits a12, a6, 16, 16 +40006ed3: 0c3d mov.n a3, a12 +40006ed5: 0fbcb7 bgeu a12, a11, 40006ee8 <__umoddi3+0x84> +40006ed8: af0b addi.n a10, a15, -1 +40006eda: 35ca add.n a3, a5, a12 +40006edc: 083357 bltu a3, a5, 40006ee8 <__umoddi3+0x84> +40006edf: 05b3b7 bgeu a3, a11, 40006ee8 <__umoddi3+0x84> +40006ee2: fecfa2 addi a10, a15, -2 +40006ee5: 803530 add a3, a5, a3 +40006ee8: c033b0 sub a3, a3, a11 +40006eeb: 209a90 or a9, a10, a9 +40006eee: a1b400 sll a11, a4 +40006ef1: 8249b0 mull a4, a9, a11 +40006ef4: a299b0 muluh a9, a9, a11 +40006ef7: c0b4b0 sub a11, a4, a11 +40006efa: 09ad mov.n a10, a9 +40006efc: 0d3397 bltu a3, a9, 40006f0d <__umoddi3+0xa9> +40006eff: a12200 sll a2, a2 +40006f02: 199397 bne a3, a9, 40006f1f <__umoddi3+0xbb> +40006f05: 16b247 bgeu a2, a4, 40006f1f <__umoddi3+0xbb> +40006f08: 000106 j 40006f10 <__umoddi3+0xac> +40006f0b: 000000 ill +40006f0e: 0ca122 movi a2, 0x10c +40006f11: 5019 s32i.n a1, a0, 20 +40006f13: c0ca add.n a12, a0, a12 +40006f15: 0134b7 bltu a4, a11, 40006f1a <__umoddi3+0xb6> +40006f18: 090c movi.n a9, 0 +40006f1a: 0b4d mov.n a4, a11 +40006f1c: c09c90 sub a9, a12, a9 +40006f1f: c03390 sub a3, a3, a9 +40006f22: c04240 sub a4, a2, a4 +40006f25: 02b247 bgeu a2, a4, 40006f2b <__umoddi3+0xc7> +40006f28: 002346 j 40006fb9 <__umoddi3+0x155> +40006f2b: 401e00 ssl a14 +40006f2e: a16300 sll a6, a3 +40006f31: 400800 ssr a8 +40006f34: 913030 srl a3, a3 +40006f37: 912040 srl a2, a4 +40006f3a: 202260 or a2, a2, a6 +40006f3d: f01d retw.n +40006f3f: f01d00 subx8 a1, a13, a0 +40006f42: 000000 ill +40006f45: 023347 bltu a3, a4, 40006f4b <__umoddi3+0xe7> +40006f48: 002986 j 40006ff2 <__umoddi3+0x18e> +40006f4b: 40f450 nsau a5, a4 +40006f4e: a58c beqz.n a5, 40006f5c <__umoddi3+0xf8> +40006f50: 401500 ssl a5 +40006f53: a18400 sll a8, a4 +40006f56: 813320 src a3, a3, a2 +40006f59: a19200 sll a9, a2 +40006f5c: f42080 extui a2, a8, 0, 16 +40006f5f: 404010 ssai 16 +40006f62: f54080 extui a4, a8, 16, 16 +40006f65: c27340 quou a7, a3, a4 +40006f68: e2b340 remu a11, a3, a4 +40006f6b: 81bb90 src a11, a11, a9 +40006f6e: 823270 mull a3, a2, a7 +40006f71: 0bad mov.n a10, a11 +40006f73: 09bb37 bgeu a11, a3, 40006f80 <__umoddi3+0x11c> +40006f76: ab8a add.n a10, a11, a8 +40006f78: 043a87 bltu a10, a8, 40006f80 <__umoddi3+0x11c> +40006f7b: 01ba37 bgeu a10, a3, 40006f80 <__umoddi3+0x11c> +40006f7e: a8aa add.n a10, a8, a10 +40006f80: c06a30 sub a6, a10, a3 +40006f83: e2c640 remu a12, a6, a4 +40006f86: 0bfc90 depbits a9, a12, 16, 16 +40006f89: c24640 quou a4, a6, a4 +40006f8c: 824240 mull a4, a2, a4 +40006f8f: 093d mov.n a3, a9 +40006f91: 17b947 bgeu a9, a4, 40006fac <__umoddi3+0x148> +40006f94: 398a add.n a3, a9, a8 +40006f96: 123387 bltu a3, a8, 40006fac <__umoddi3+0x148> +40006f99: 0fb347 bgeu a3, a4, 40006fac <__umoddi3+0x148> +40006f9c: 400500 ssr a5 +40006f9f: 383a add.n a3, a8, a3 +40006fa1: c02340 sub a2, a3, a4 +40006fa4: 912020 srl a2, a2 +40006fa7: 030c movi.n a3, 0 +40006fa9: f01d retw.n +40006fab: 234000 sext a4, a0, 7 +40006fae: 0500c0 extui a0, a12, 16, 1 +40006fb1: 030c40 rsr.scompare1 a4 +40006fb4: 912020 srl a2, a2 +40006fb7: f01d retw.n +40006fb9: 401e00 ssl a14 +40006fbc: 330b addi.n a3, a3, -1 +40006fbe: a16300 sll a6, a3 +40006fc1: 400800 ssr a8 +40006fc4: 913030 srl a3, a3 +40006fc7: 912040 srl a2, a4 +40006fca: 202260 or a2, a2, a6 +40006fcd: f01d retw.n +40006fcf: 353700 extui a3, a0, 23, 4 +40006fd2: b24705 call0 3ffb9444 <_start-0x46bbc> +40006fd5: 3ac602 addi a0, a6, 58 +40006fd8: 424000 xorb b4, b0, b0 +40006fdb: 3247c0 orbc b4, b7, b12 +40006fde: 2d08 l32i.n a0, a13, 8 +40006fe0: 6a5004 excw +40006fe3: 063dc0 excw +40006fe6: f01d retw.n +40006fe8: 042d mov.n a2, a4 +40006fea: c06a50 sub a6, a10, a5 +40006fed: 360b addi.n a3, a6, -1 +40006fef: f01d retw.n +40006ff1: 34cc00 extui a12, a0, 12, 4 +40006ff4: 180c movi.n a8, 1 +40006ff6: c28840 quou a8, a8, a4 +40006ff9: 40f850 nsau a5, a8 +40006ffc: 0c8516 beqz a5, 400070c8 <__umoddi3+0x264> +40006fff: 401500 ssl a5 +40007002: c06b50 sub a6, a11, a5 +40007005: a18800 sll a8, a8 +40007008: 400600 ssr a6 +4000700b: 91c0a0 srl a12, a10 +4000700e: 401500 ssl a5 +40007011: a14a00 sll a4, a10 +40007014: 400600 ssr a6 +40007017: 913020 srl a3, a2 +4000701a: 203340 or a3, a3, a4 +4000701d: 401500 ssl a5 +40007020: a19200 sll a9, a2 +40007023: f54080 extui a4, a8, 16, 16 +40007026: f42080 extui a2, a8, 0, 16 +40007029: c2bc40 quou a11, a12, a4 +4000702c: 404010 ssai 16 +4000702f: e2cc40 remu a12, a12, a4 +40007032: 81cc30 src a12, a12, a3 +40007035: 82b2b0 mull a11, a2, a11 +40007038: 0cad mov.n a10, a12 +4000703a: 0abcb7 bgeu a12, a11, 40007048 <__umoddi3+0x1e4> +4000703d: 80ac80 add a10, a12, a8 +40007040: 043a87 bltu a10, a8, 40007048 <__umoddi3+0x1e4> +40007043: 01bab7 bgeu a10, a11, 40007048 <__umoddi3+0x1e4> +40007046: a8aa add.n a10, a8, a10 +40007048: c0bab0 sub a11, a10, a11 +4000704b: e2cb40 remu a12, a11, a4 +4000704e: 0bfc30 depbits a3, a12, 16, 16 +40007051: c2bb40 quou a11, a11, a4 +40007054: 82b2b0 mull a11, a2, a11 +40007057: 20a330 or a10, a3, a3 +4000705a: 0bb3b7 bgeu a3, a11, 40007069 <__umoddi3+0x205> +4000705d: 80a380 add a10, a3, a8 +40007060: 053a87 bltu a10, a8, 40007069 <__umoddi3+0x205> +40007063: 02bab7 bgeu a10, a11, 40007069 <__umoddi3+0x205> +40007066: 80a8a0 add a10, a8, a10 +40007069: c03ab0 sub a3, a10, a11 +4000706c: e2b340 remu a11, a3, a4 +4000706f: 404010 ssai 16 +40007072: c23340 quou a3, a3, a4 +40007075: 81bb90 src a11, a11, a9 +40007078: 823230 mull a3, a2, a3 +4000707b: 0bad mov.n a10, a11 +4000707d: 0bbb37 bgeu a11, a3, 4000708c <__umoddi3+0x228> +40007080: 80ab80 add a10, a11, a8 +40007083: 053a87 bltu a10, a8, 4000708c <__umoddi3+0x228> +40007086: 02ba37 bgeu a10, a3, 4000708c <__umoddi3+0x228> +40007089: 80a8a0 add a10, a8, a10 +4000708c: c07a30 sub a7, a10, a3 +4000708f: e26740 remu a6, a7, a4 +40007092: 0bf690 depbits a9, a6, 16, 16 +40007095: c24740 quou a4, a7, a4 +40007098: 824240 mull a4, a2, a4 +4000709b: 093d mov.n a3, a9 +4000709d: 17b947 bgeu a9, a4, 400070b8 <__umoddi3+0x254> +400070a0: 398a add.n a3, a9, a8 +400070a2: 123387 bltu a3, a8, 400070b8 <__umoddi3+0x254> +400070a5: 0fb347 bgeu a3, a4, 400070b8 <__umoddi3+0x254> +400070a8: 400500 ssr a5 +400070ab: 383a add.n a3, a8, a3 +400070ad: c02340 sub a2, a3, a4 +400070b0: 912020 srl a2, a2 +400070b3: 030c movi.n a3, 0 +400070b5: f01d retw.n +400070b7: 234000 sext a4, a0, 7 +400070ba: 0500c0 extui a0, a12, 16, 1 +400070bd: 030c40 rsr.scompare1 a4 +400070c0: 912020 srl a2, a2 +400070c3: f01d retw.n +400070c5: f01d retw.n +400070c7: 3a8000 depbits a0, a0, 3, 9 +400070ca: 4080c0 rotw -4 +400070cd: 2080f5 call12 400278dc <__bss_start+0x178dc> +400070d0: e5c6f4 excw +400070d3: ff .byte 0xff +400070d4: ffc000 excw +400070d7: 3f .byte 0x3f +400070d8: ffc864 excw +400070db: 3f .byte 0x3f +400070dc: 007104 excw +400070df: c87040 excw +400070e2: ff .byte 0xff +400070e3: 3f .byte 0x3f +400070e4: ffcd40 excw +400070e7: 3f .byte 0x3f +400070e8: 007974 excw +400070eb: cd4040 excw +400070ee: ff .byte 0xff +400070ef: 3f .byte 0x3f +400070f0: ffcd44 excw +400070f3: 3f .byte 0x3f +400070f4: 007e44 excw +400070f7: 000040 excw + ... +4000710a: b80000 excw +4000710d: 400066 bnei a0, -1, 40007151 <__umoddi3+0x2ed> +40007110: 0818 l32i.n a1, a8, 0 +40007112: b84000 excw +40007115: 400066 bnei a0, -1, 40007159 <__umoddi3+0x2f5> +40007118: 66b8 l32i.n a11, a6, 24 +4000711a: 9c4000 excw +4000711d: 0008 l32i.n a0, a0, 0 +4000711f: 001040 movsp a4, a0 +40007122: b84000 excw +40007125: 400066 bnei a0, -1, 40007169 <__umoddi3+0x305> +40007128: 66b8 l32i.n a11, a6, 24 +4000712a: b84000 excw +4000712d: 400066 bnei a0, -1, 40007171 <__umoddi3+0x30d> +40007130: 66b8 l32i.n a11, a6, 24 +40007132: b84000 excw +40007135: 400066 bnei a0, -1, 40007179 <__umoddi3+0x315> +40007138: 66b8 l32i.n a11, a6, 24 +4000713a: 304000 xor a4, a0, a0 +4000713d: 0e .byte 0xe +4000713e: 304000 xor a4, a0, a0 +40007141: 0e .byte 0xe +40007142: 304000 xor a4, a0, a0 +40007145: 0e .byte 0xe +40007146: 304000 xor a4, a0, a0 +40007149: 0e .byte 0xe +4000714a: b84000 excw +4000714d: 400066 bnei a0, -1, 40007191 <__umoddi3+0x32d> +40007150: 66b8 l32i.n a11, a6, 24 +40007152: b84000 excw +40007155: 400066 bnei a0, -1, 40007199 <__umoddi3+0x335> +40007158: 66b8 l32i.n a11, a6, 24 +4000715a: b84000 excw +4000715d: 400066 bnei a0, -1, 400071a1 <__umoddi3+0x33d> +40007160: 66b8 l32i.n a11, a6, 24 +40007162: b84000 excw +40007165: 400066 bnei a0, -1, 400071a9 <__umoddi3+0x345> +40007168: 66b8 l32i.n a11, a6, 24 +4000716a: b84000 excw +4000716d: 400066 bnei a0, -1, 400071b1 <__umoddi3+0x34d> +40007170: 66b8 l32i.n a11, a6, 24 +40007172: b84000 excw +40007175: 400066 bnei a0, -1, 400071b9 <__umoddi3+0x355> +40007178: 66b8 l32i.n a11, a6, 24 +4000717a: b84000 excw +4000717d: 400066 bnei a0, -1, 400071c1 <__umoddi3+0x35d> +40007180: 66b8 l32i.n a11, a6, 24 +40007182: b84000 excw +40007185: 400066 bnei a0, -1, 400071c9 <__umoddi3+0x365> +40007188: 66b8 l32i.n a11, a6, 24 +4000718a: b84000 excw +4000718d: 400066 bnei a0, -1, 400071d1 <__umoddi3+0x36d> +40007190: 66b8 l32i.n a11, a6, 24 +40007192: b84000 excw +40007195: 400066 bnei a0, -1, 400071d9 <__umoddi3+0x375> +40007198: 66b8 l32i.n a11, a6, 24 +4000719a: b84000 excw +4000719d: 400066 bnei a0, -1, 400071e1 <__umoddi3+0x37d> +400071a0: 66b8 l32i.n a11, a6, 24 +400071a2: b84000 excw +400071a5: 400066 bnei a0, -1, 400071e9 <__umoddi3+0x385> +400071a8: 66b8 l32i.n a11, a6, 24 +400071aa: b84000 excw +400071ad: 400066 bnei a0, -1, 400071f1 <__umoddi3+0x38d> +400071b0: 66b8 l32i.n a11, a6, 24 +400071b2: b84000 excw +400071b5: 400066 bnei a0, -1, 400071f9 <__umoddi3+0x395> +400071b8: 66b8 l32i.n a11, a6, 24 +400071ba: b84000 excw +400071bd: 400066 bnei a0, -1, 40007201 <__umoddi3+0x39d> +400071c0: 66b8 l32i.n a11, a6, 24 +400071c2: b84000 excw +400071c5: 400066 bnei a0, -1, 40007209 <__umoddi3+0x3a5> +400071c8: 66b8 l32i.n a11, a6, 24 +400071ca: b84000 excw +400071cd: 400066 bnei a0, -1, 40007211 <__umoddi3+0x3ad> +400071d0: 66b8 l32i.n a11, a6, 24 +400071d2: b84000 excw +400071d5: 400066 bnei a0, -1, 40007219 <__umoddi3+0x3b5> +400071d8: 66b8 l32i.n a11, a6, 24 +400071da: b84000 excw +400071dd: 400066 bnei a0, -1, 40007221 <__umoddi3+0x3bd> +400071e0: 66b8 l32i.n a11, a6, 24 +400071e2: b84000 excw +400071e5: 400066 bnei a0, -1, 40007229 <__umoddi3+0x3c5> +400071e8: 66b8 l32i.n a11, a6, 24 +400071ea: b84000 excw +400071ed: 400066 bnei a0, -1, 40007231 <__umoddi3+0x3cd> +400071f0: 66b8 l32i.n a11, a6, 24 +400071f2: b84000 excw +400071f5: 400066 bnei a0, -1, 40007239 <__umoddi3+0x3d5> +400071f8: 66b8 l32i.n a11, a6, 24 +400071fa: b84000 excw +400071fd: 400066 bnei a0, -1, 40007241 <__umoddi3+0x3dd> +40007200: 66b8 l32i.n a11, a6, 24 +40007202: b84000 excw +40007205: 400066 bnei a0, -1, 40007249 <__umoddi3+0x3e5> +40007208: 66b8 l32i.n a11, a6, 24 +4000720a: 684000 excw +4000720d: 400066 bnei a0, -1, 40007251 <__umoddi3+0x3ed> +40007210: 6668 l32i.n a6, a6, 24 +40007212: 684000 excw +40007215: 400066 bnei a0, -1, 40007259 <__umoddi3+0x3f5> +40007218: 6668 l32i.n a6, a6, 24 +4000721a: 684000 excw +4000721d: 400066 bnei a0, -1, 40007261 <__umoddi3+0x3fd> +40007220: 6668 l32i.n a6, a6, 24 +40007222: 684000 excw +40007225: 400066 bnei a0, -1, 40007269 <__umoddi3+0x405> +40007228: 6668 l32i.n a6, a6, 24 +4000722a: 684000 excw +4000722d: 400066 bnei a0, -1, 40007271 <__umoddi3+0x40d> +40007230: 6668 l32i.n a6, a6, 24 +40007232: 684000 excw +40007235: 400066 bnei a0, -1, 40007279 <__umoddi3+0x415> +40007238: 6668 l32i.n a6, a6, 24 +4000723a: 684000 excw +4000723d: 400066 bnei a0, -1, 40007281 <__umoddi3+0x41d> +40007240: 6668 l32i.n a6, a6, 24 +40007242: 684000 excw +40007245: 400066 bnei a0, -1, 40007289 <__umoddi3+0x425> +40007248: 6668 l32i.n a6, a6, 24 +4000724a: 684000 excw +4000724d: 400066 bnei a0, -1, 40007291 <__umoddi3+0x42d> +40007250: 6668 l32i.n a6, a6, 24 +40007252: 684000 excw +40007255: 400066 bnei a0, -1, 40007299 <__umoddi3+0x435> +40007258: 6668 l32i.n a6, a6, 24 +4000725a: 684000 excw +4000725d: 400066 bnei a0, -1, 400072a1 <__umoddi3+0x43d> +40007260: 6668 l32i.n a6, a6, 24 +40007262: 684000 excw +40007265: 400066 bnei a0, -1, 400072a9 <__umoddi3+0x445> +40007268: 6668 l32i.n a6, a6, 24 +4000726a: 684000 excw +4000726d: 400066 bnei a0, -1, 400072b1 <__umoddi3+0x44d> +40007270: 6668 l32i.n a6, a6, 24 +40007272: 684000 excw +40007275: 400066 bnei a0, -1, 400072b9 <__umoddi3+0x455> +40007278: 6668 l32i.n a6, a6, 24 +4000727a: 684000 excw +4000727d: 400066 bnei a0, -1, 400072c1 <__umoddi3+0x45d> +40007280: 6668 l32i.n a6, a6, 24 +40007282: 684000 excw +40007285: 400066 bnei a0, -1, 400072c9 <__umoddi3+0x465> +40007288: 6668 l32i.n a6, a6, 24 +4000728a: 684000 excw +4000728d: 400066 bnei a0, -1, 400072d1 <__umoddi3+0x46d> +40007290: 6668 l32i.n a6, a6, 24 +40007292: 684000 excw +40007295: 400066 bnei a0, -1, 400072d9 <__umoddi3+0x475> +40007298: 6668 l32i.n a6, a6, 24 +4000729a: 684000 excw +4000729d: 400066 bnei a0, -1, 400072e1 <__umoddi3+0x47d> +400072a0: 6668 l32i.n a6, a6, 24 +400072a2: 684000 excw +400072a5: 400066 bnei a0, -1, 400072e9 <__umoddi3+0x485> +400072a8: 6668 l32i.n a6, a6, 24 +400072aa: 684000 excw +400072ad: 400066 bnei a0, -1, 400072f1 <__umoddi3+0x48d> +400072b0: 6668 l32i.n a6, a6, 24 +400072b2: 684000 excw +400072b5: 400066 bnei a0, -1, 400072f9 <__umoddi3+0x495> +400072b8: 6668 l32i.n a6, a6, 24 +400072ba: 684000 excw +400072bd: 400066 bnei a0, -1, 40007301 <__umoddi3+0x49d> +400072c0: 6668 l32i.n a6, a6, 24 +400072c2: 684000 excw +400072c5: 400066 bnei a0, -1, 40007309 <__umoddi3+0x4a5> +400072c8: 6668 l32i.n a6, a6, 24 +400072ca: 684000 excw +400072cd: 400066 bnei a0, -1, 40007311 <__umoddi3+0x4ad> +400072d0: 6668 l32i.n a6, a6, 24 +400072d2: 684000 excw +400072d5: 400066 bnei a0, -1, 40007319 <__umoddi3+0x4b5> +400072d8: 6668 l32i.n a6, a6, 24 +400072da: 684000 excw +400072dd: 400066 bnei a0, -1, 40007321 <__umoddi3+0x4bd> +400072e0: 6668 l32i.n a6, a6, 24 +400072e2: 684000 excw +400072e5: 400066 bnei a0, -1, 40007329 <__umoddi3+0x4c5> +400072e8: 6668 l32i.n a6, a6, 24 +400072ea: 684000 excw +400072ed: 400066 bnei a0, -1, 40007331 <__umoddi3+0x4cd> +400072f0: 6668 l32i.n a6, a6, 24 +400072f2: 684000 excw +400072f5: 400066 bnei a0, -1, 40007339 <__umoddi3+0x4d5> +400072f8: 6668 l32i.n a6, a6, 24 +400072fa: 684000 excw +400072fd: 400066 bnei a0, -1, 40007341 <__umoddi3+0x4dd> +40007300: 6668 l32i.n a6, a6, 24 +40007302: 684000 excw +40007305: 400066 bnei a0, -1, 40007349 <__umoddi3+0x4e5> +40007308: 6668 l32i.n a6, a6, 24 +4000730a: 004000 break 0, 0 + ... +40007315: 000000 ill +40007318: ff .byte 0xff +40007319: ff .byte 0xff +4000731a: ff .byte 0xff +4000731b: ff .byte 0xff +4000731c: 0066b0 rsil a11, 6 +4000731f: 001f40 movsp a4, a15 +40007322: b00000 addx8 a0, a0, a0 +40007325: 400066 bnei a0, -1, 40007369 <__umoddi3+0x505> +40007328: 1e .byte 0x1e +40007329: 000000 ill +4000732c: 0066b0 rsil a11, 6 +4000732f: 001d40 movsp a4, a13 +40007332: b00000 addx8 a0, a0, a0 +40007335: 400066 bnei a0, -1, 40007379 <__umoddi3+0x515> +40007338: 001c movi.n a0, 16 +4000733a: b00000 addx8 a0, a0, a0 +4000733d: 400066 bnei a0, -1, 40007381 <__umoddi3+0x51d> +40007340: 001b addi.n a0, a0, 1 +40007342: b00000 addx8 a0, a0, a0 +40007345: 400066 bnei a0, -1, 40007389 <__umoddi3+0x525> +40007348: 001a add.n a0, a0, a1 +4000734a: b00000 addx8 a0, a0, a0 +4000734d: 400066 bnei a0, -1, 40007391 <__umoddi3+0x52d> +40007350: 0019 s32i.n a1, a0, 0 +40007352: b00000 addx8 a0, a0, a0 +40007355: 400066 bnei a0, -1, 40007399 <__umoddi3+0x535> +40007358: 0018 l32i.n a1, a0, 0 +4000735a: b00000 addx8 a0, a0, a0 +4000735d: 400066 bnei a0, -1, 400073a1 <__umoddi3+0x53d> +40007360: 000017 bnone a0, a1, 40007364 <__umoddi3+0x500> +40007363: 66b000 excw +40007366: 164000 excw +40007369: 000000 ill +4000736c: 0066b0 rsil a11, 6 +4000736f: 001540 movsp a4, a5 +40007372: b00000 addx8 a0, a0, a0 +40007375: 400066 bnei a0, -1, 400073b9 <__umoddi3+0x555> +40007378: 000014 excw +4000737b: 66b000 excw +4000737e: 134000 excw +40007381: 000000 ill +40007384: 0066b0 rsil a11, 6 +40007387: 001240 movsp a4, a2 +4000738a: b00000 addx8 a0, a0, a0 +4000738d: 400066 bnei a0, -1, 400073d1 <__umoddi3+0x56d> +40007390: 000011 l32r a1, 3ffc7390 <_start-0x38c70> +40007393: 66b000 excw +40007396: 104000 and a4, a0, a0 +40007399: 000000 ill +4000739c: 0066b0 rsil a11, 6 +4000739f: 000f40 excw +400073a2: b00000 addx8 a0, a0, a0 +400073a5: 400066 bnei a0, -1, 400073e9 <__umoddi3+0x585> +400073a8: 0e .byte 0xe +400073a9: 000000 ill +400073ac: 0066b0 rsil a11, 6 +400073af: 000d40 excw +400073b2: b00000 addx8 a0, a0, a0 +400073b5: 400066 bnei a0, -1, 400073f9 <__umoddi3+0x595> +400073b8: 000c movi.n a0, 0 +400073ba: b00000 addx8 a0, a0, a0 +400073bd: 400066 bnei a0, -1, 40007401 <__umoddi3+0x59d> +400073c0: 000b addi.n a0, a0, -1 +400073c2: b00000 addx8 a0, a0, a0 +400073c5: 400066 bnei a0, -1, 40007409 <__umoddi3+0x5a5> +400073c8: 000a add.n a0, a0, a0 +400073ca: b00000 addx8 a0, a0, a0 +400073cd: 400066 bnei a0, -1, 40007411 <__umoddi3+0x5ad> +400073d0: 0009 s32i.n a0, a0, 0 +400073d2: b00000 addx8 a0, a0, a0 +400073d5: 400066 bnei a0, -1, 40007419 <__umoddi3+0x5b5> +400073d8: 0008 l32i.n a0, a0, 0 +400073da: b00000 addx8 a0, a0, a0 +400073dd: 400066 bnei a0, -1, 40007421 <__umoddi3+0x5bd> +400073e0: 000007 bnone a0, a0, 400073e4 <__umoddi3+0x580> +400073e3: 66b000 excw +400073e6: 064000 excw +400073e9: 000000 ill +400073ec: 0066b0 rsil a11, 6 +400073ef: 000540 excw +400073f2: b00000 addx8 a0, a0, a0 +400073f5: 400066 bnei a0, -1, 40007439 <__umoddi3+0x5d5> +400073f8: 000004 excw +400073fb: 66b000 excw +400073fe: 034000 excw +40007401: 000000 ill +40007404: 0066b0 rsil a11, 6 +40007407: 000240 excw +4000740a: b00000 addx8 a0, a0, a0 +4000740d: 400066 bnei a0, -1, 40007451 <__umoddi3+0x5ed> +40007410: 000001 l32r a0, 3ffc7410 <_start-0x38bf0> +40007413: 66b000 excw +40007416: 004000 break 0, 0 +40007419: 000000 ill +4000741c: 81c000 src a12, a0, a0 +4000741f: ff .byte 0xff +40007420: 000000 ill +40007423: c00080 sub a0, a0, a8 +40007426: 00ff81 l32r a8, 3ffc7824 <_start-0x387dc> +40007429: 400000 ssr a0 +4000742c: 81c000 src a12, a0, a0 +4000742f: ff .byte 0xff +40007430: 000000 ill +40007433: c00020 sub a0, a0, a2 +40007436: 00ff81 l32r a8, 3ffc7834 <_start-0x387cc> +40007439: 100000 and a0, a0, a0 +4000743c: 81c000 src a12, a0, a0 +4000743f: ff .byte 0xff +40007440: 000000 ill +40007443: 0008 l32i.n a0, a0, 0 +40007445: ff81c0 excw +40007448: 000000 ill +4000744b: c00004 excw +4000744e: 00ff81 l32r a8, 3ffc784c <_start-0x387b4> +40007451: 020000 andb b0, b0, b0 +40007454: 81c000 src a12, a0, a0 +40007457: ff .byte 0xff +40007458: 000000 ill +4000745b: c00001 l32r a0, 3fff745c <_start-0x8ba4> +4000745e: 00ff81 l32r a8, 3ffc785c <_start-0x387a4> +40007461: 008000 any4 b0, b0:b1:b2:b3 +40007464: 81c000 src a12, a0, a0 +40007467: ff .byte 0xff +40007468: 400000 ssr a0 +4000746b: c00000 sub a0, a0, a0 +4000746e: 00ffc1 l32r a12, 3ffc786c <_start-0x38794> +40007471: 002000 isync +40007474: e1c000 excw +40007477: ff .byte 0xff +40007478: 100000 and a0, a0, a0 +4000747b: c00000 sub a0, a0, a0 +4000747e: 00fff1 l32r a15, 3ffc787c <_start-0x38784> +40007481: 000800 excw +40007484: f9c000 excw +40007487: ff .byte 0xff +40007488: 040000 extui a0, a0, 0, 1 +4000748b: c00000 sub a0, a0, a0 +4000748e: fffd excw +40007490: 020000 andb b0, b0, b0 +40007493: c00000 sub a0, a0, a0 +40007496: ff .byte 0xff +40007497: ff .byte 0xff +40007498: 010000 slli a0, a0, 32 +4000749b: c00000 sub a0, a0, a0 +4000749e: ff .byte 0xff +4000749f: ff .byte 0xff +400074a0: 008000 any4 b0, b0:b1:b2:b3 +400074a3: c00000 sub a0, a0, a0 +400074a6: ff .byte 0xff +400074a7: ff .byte 0xff +400074a8: 004000 break 0, 0 +400074ab: c00000 sub a0, a0, a0 +400074ae: ff .byte 0xff +400074af: ff .byte 0xff +400074b0: 002000 isync +400074b3: e00000 subx4 a0, a0, a0 +400074b6: ff .byte 0xff +400074b7: ff .byte 0xff +400074b8: 001000 movsp a0, a0 +400074bb: f00000 subx8 a0, a0, a0 +400074be: ff .byte 0xff +400074bf: ff .byte 0xff +400074c0: 000800 excw +400074c3: f80000 excw +400074c6: ff .byte 0xff +400074c7: ff .byte 0xff +400074c8: 000400 excw +400074cb: fc0000 excw +400074ce: ff .byte 0xff +400074cf: ff .byte 0xff +400074d0: 000200 excw +400074d3: fe0000 excw +400074d6: ff .byte 0xff +400074d7: ff .byte 0xff +400074d8: 000100 excw +400074db: ff0000 excw +400074de: ff .byte 0xff +400074df: ff .byte 0xff +400074e0: 000080 ret +400074e3: ff8000 excw +400074e6: ff .byte 0xff +400074e7: ff .byte 0xff +400074e8: 000040 excw +400074eb: ffc000 excw +400074ee: ff .byte 0xff +400074ef: ff .byte 0xff +400074f0: 000020 excw +400074f3: ffe000 excw +400074f6: ff .byte 0xff +400074f7: ff .byte 0xff +400074f8: 000010 excw +400074fb: fff000 excw +400074fe: ff .byte 0xff +400074ff: ff .byte 0xff +40007500: 0008 l32i.n a0, a0, 0 +40007502: f80000 excw +40007505: ff .byte 0xff +40007506: ff .byte 0xff +40007507: ff .byte 0xff +40007508: 000004 excw +4000750b: fffc00 excw +4000750e: ff .byte 0xff +4000750f: ff .byte 0xff +40007510: 000002 l8ui a0, a0, 0 +40007513: fffe00 excw +40007516: ff .byte 0xff +40007517: ff .byte 0xff +40007518: 000001 l32r a0, 3ffc7518 <_start-0x38ae8> +4000751b: 000000 ill +4000751e: 040000 extui a0, a0, 0, 1 +40007521: 3fffc7 bbsi a15, 28, 40007564 <__umoddi3+0x700> +40007524: c76c movi.n a7, -20 +40007526: ff .byte 0xff +40007527: 3f .byte 0x3f +40007528: ffc7d4 excw +4000752b: 3f .byte 0x3f + ... +40007550: ffca50 excw +40007553: 3f .byte 0x3f + ... +400075c4: 000001 l32r a0, 3ffc75c4 <_start-0x38a3c> +400075c7: 000000 ill +400075ca: 0e0000 read_impwire a0 +400075cd: abcd33 excw +400075d0: 6d1234 excw +400075d3: deece6 bgei a12, 128, 400075b5 <__umoddi3+0x751> +400075d6: 0b0005 call0 400125d8 <__bss_start+0x25d8> + ... +40007941: 000000 ill +40007944: c418 l32i.n a1, a4, 48 +40007946: ff .byte 0xff +40007947: 3f .byte 0x3f +40007948: 000d mov.n a0, a0 +4000794a: 4c0000 excw +4000794d: ffc8 l32i.n a12, a15, 60 +4000794f: 3f .byte 0x3f +40007950: ef .byte 0xef +40007951: 001540 movsp a4, a5 +40007954: 200000 or a0, a0, a0 +40007957: 000000 ill +4000795a: 000001 l32r a0, 3ffc795c <_start-0x386a4> +4000795d: 000010 excw +40007960: 000100 excw +40007963: ffff00 excw + ... +40007972: 180000 excw +40007975: 3fffc4 excw + ... +40007984: 737465 call8 4007b0cc <__bss_start+0x6b0cc> +40007987: 5f .byte 0x5f +40007988: 706e75 call12 40078070 <__bss_start+0x68070> +4000798b: 6b6361 l32r a6, 3ffe2718 <_start-0x1d8e8> +4000798e: 5f .byte 0x5f +4000798f: 616c66 bnei a12, 6, 400079f4 <__umoddi3+0xb90> +40007992: 5f6873 excw +40007995: 646f63 excw +40007998: 000065 call8 400079a0 <__umoddi3+0xb3c> + ... +400079a3: 26b300 excw +400079a6: 684000 excw +400079a9: 400024 excw +400079ac: 2468 l32i.n a6, a4, 8 +400079ae: 684000 excw +400079b1: 400024 excw +400079b4: 2468 l32i.n a6, a4, 8 +400079b6: 684000 excw +400079b9: 400024 excw +400079bc: 2468 l32i.n a6, a4, 8 +400079be: 684000 excw +400079c1: 400024 excw +400079c4: 2468 l32i.n a6, a4, 8 +400079c6: 684000 excw +400079c9: 400024 excw +400079cc: 2468 l32i.n a6, a4, 8 +400079ce: 684000 excw +400079d1: 400024 excw +400079d4: 2468 l32i.n a6, a4, 8 +400079d6: 684000 excw +400079d9: 400024 excw +400079dc: 2468 l32i.n a6, a4, 8 +400079de: 684000 excw +400079e1: 400024 excw +400079e4: 2468 l32i.n a6, a4, 8 +400079e6: 684000 excw +400079e9: 400024 excw +400079ec: 2468 l32i.n a6, a4, 8 +400079ee: 684000 excw +400079f1: 400024 excw +400079f4: 2468 l32i.n a6, a4, 8 +400079f6: 684000 excw +400079f9: 400024 excw +400079fc: 2468 l32i.n a6, a4, 8 +400079fe: 684000 excw +40007a01: 400024 excw +40007a04: 2468 l32i.n a6, a4, 8 +40007a06: 684000 excw +40007a09: 400024 excw +40007a0c: 2468 l32i.n a6, a4, 8 +40007a0e: 684000 excw +40007a11: 400024 excw +40007a14: 2468 l32i.n a6, a4, 8 +40007a16: 684000 excw +40007a19: 400024 excw +40007a1c: 002681 l32r a8, 3ffc7ab4 <_start-0x3854c> +40007a1f: 253840 extui a3, a4, 24, 3 +40007a22: 684000 excw +40007a25: 400024 excw +40007a28: 2468 l32i.n a6, a4, 8 +40007a2a: 684000 excw +40007a2d: 400024 excw +40007a30: 2468 l32i.n a6, a4, 8 +40007a32: 684000 excw +40007a35: 400024 excw +40007a38: 2468 l32i.n a6, a4, 8 +40007a3a: 684000 excw +40007a3d: 400024 excw +40007a40: 2468 l32i.n a6, a4, 8 +40007a42: 684000 excw +40007a45: 400024 excw +40007a48: 2468 l32i.n a6, a4, 8 +40007a4a: 684000 excw +40007a4d: 400024 excw +40007a50: 2468 l32i.n a6, a4, 8 +40007a52: 684000 excw +40007a55: 400024 excw +40007a58: 2468 l32i.n a6, a4, 8 +40007a5a: 354000 extui a4, a0, 16, 4 +40007a5d: 400026 beqi a0, -1, 40007aa1 <__umoddi3+0xc3d> +40007a60: 2468 l32i.n a6, a4, 8 +40007a62: 384000 excw +40007a65: 400025 call8 40047a68 <__bss_start+0x37a68> +40007a68: 2468 l32i.n a6, a4, 8 +40007a6a: 684000 excw +40007a6d: 400024 excw +40007a70: 2538 l32i.n a3, a5, 8 +40007a72: 684000 excw +40007a75: 400024 excw +40007a78: 2468 l32i.n a6, a4, 8 +40007a7a: 684000 excw +40007a7d: 400024 excw +40007a80: 2468 l32i.n a6, a4, 8 +40007a82: 684000 excw +40007a85: 400024 excw +40007a88: 2468 l32i.n a6, a4, 8 +40007a8a: 684000 excw +40007a8d: 400024 excw +40007a90: 2468 l32i.n a6, a4, 8 +40007a92: 684000 excw +40007a95: 400024 excw +40007a98: 2468 l32i.n a6, a4, 8 +40007a9a: 814000 src a4, a0, a0 +40007a9d: 400026 beqi a0, -1, 40007ae1 <__umoddi3+0xc7d> +40007aa0: 2538 l32i.n a3, a5, 8 +40007aa2: 684000 excw +40007aa5: 400024 excw +40007aa8: 2468 l32i.n a6, a4, 8 +40007aaa: 684000 excw +40007aad: 400024 excw +40007ab0: 2468 l32i.n a6, a4, 8 +40007ab2: 684000 excw +40007ab5: 400024 excw +40007ab8: 2468 l32i.n a6, a4, 8 +40007aba: 684000 excw +40007abd: 400024 excw +40007ac0: 2468 l32i.n a6, a4, 8 +40007ac2: 684000 excw +40007ac5: 400024 excw +40007ac8: 2468 l32i.n a6, a4, 8 +40007aca: 684000 excw +40007acd: 400024 excw +40007ad0: 002527 blt a5, a2, 40007ad4 <__umoddi3+0xc70> +40007ad3: 246840 extui a6, a4, 8, 3 +40007ad6: 684000 excw +40007ad9: 400024 excw +40007adc: 002635 call12 40007d40 <__umoddi3+0xedc> +40007adf: 246840 extui a6, a4, 8, 3 +40007ae2: 384000 excw +40007ae5: 400025 call8 40047ae8 <__bss_start+0x37ae8> +40007ae8: 2468 l32i.n a6, a4, 8 +40007aea: 684000 excw +40007aed: 400024 excw +40007af0: 2538 l32i.n a3, a5, 8 +40007af2: 124000 andbc b4, b0, b0 +40007af5: 400030 excw +40007af8: 003012 excw +40007afb: 30c240 xor a12, a2, a4 +40007afe: b04000 addx8 a4, a0, a0 +40007b01: 400030 excw +40007b04: 3098 l32i.n a9, a0, 12 +40007b06: 1e4000 excw +40007b09: 400030 excw +40007b0c: 003084 excw +40007b0f: 307240 xor a7, a2, a4 +40007b12: 624000 excw +40007b15: 400030 excw +40007b18: 003052 excw +40007b1b: 304040 xor a4, a0, a4 +40007b1e: 304000 xor a4, a0, a0 +40007b21: 400030 excw +40007b24: 00dbc0 excw + ... +40007b33: 010100 slli a0, a1, 32 +40007b36: 010101 l32r a0, 3ffc7f3c <_start-0x380c4> +40007b39: 010101 l32r a0, 3ffc7f40 <_start-0x380c0> +40007b3c: 010101 l32r a0, 3ffc7f40 <_start-0x380c0> +40007b3f: 010101 l32r a0, 3ffc7f44 <_start-0x380bc> +40007b42: 050307 bnone a3, a0, 40007b4b <__umoddi3+0xce7> +40007b45: 010101 l32r a0, 3ffc7f4c <_start-0x380b4> +40007b48: 010101 l32r a0, 3ffc7f4c <_start-0x380b4> +40007b4b: 020202 l8ui a0, a2, 2 +40007b4e: 030302 l8ui a0, a3, 3 +40007b51: 050403 excw +40007b54: 000043 excw +40007b57: 200a00 or a0, a10, a0 +40007b5a: 737465 call8 4007b2a0 <__bss_start+0x6b2a0> +40007b5d: 732520 maxu a2, a5, a2 +40007b60: 722c movi.n a2, 39 +40007b62: 207473 excw +40007b65: 756163 excw +40007b68: 3a6573 excw +40007b6b: 2c6425 call8 400341ac <__bss_start+0x241ac> +40007b6e: 6f6220 excw +40007b71: 6f .byte 0x6f +40007b72: 6d2074 excw +40007b75: 6f .byte 0x6f +40007b76: 3a6564 excw +40007b79: 2528 l32i.n a2, a5, 8 +40007b7b: 2978 l32i.n a7, a9, 8 +40007b7d: 0a0a add.n a0, a10, a0 +40007b7f: 754a00 extui a4, a0, 26, 8 +40007b82: 206c movi.n a0, -30 +40007b84: 203520 or a3, a5, a2 +40007b87: 313032 excw +40007b8a: 250035 call12 4002cb8c <__bss_start+0x1cb8c> +40007b8d: 252073 excw +40007b90: 0a2075 call12 40011d98 <__bss_start+0x1d98> +40007b93: 746500 extui a6, a0, 5, 8 +40007b96: 6d5f73 excw +40007b99: 6e6961 l32r a6, 3ffe3540 <_start-0x1cac0> +40007b9c: 2e .byte 0x2e +40007b9d: 000063 excw +40007ba0: 657375 call12 4006d2d8 <__bss_start+0x5d2d8> +40007ba3: 632072 l32i a7, a0, 0x18c +40007ba6: 6f .byte 0x6f +40007ba7: 206564 excw +40007baa: 6e6f64 excw +40007bad: 000a65 call8 40007c54 <__umoddi3+0xdf0> +40007bb0: 696177 bbci a1, 7, 40007c1d <__umoddi3+0xdb9> +40007bb3: 6e6974 excw +40007bb6: 662067 blt a0, a6, 40007c20 <__umoddi3+0xdbc> +40007bb9: 6f .byte 0x6f +40007bba: 682072 l32i a7, a0, 0x1a0 +40007bbd: 6f .byte 0x6f +40007bbe: 0a7473 excw +40007bc1: 000000 ill +40007bc4: 697073 excw +40007bc7: 6d6320 excw +40007bca: 6c2064 excw +40007bcd: 206e65 call8 400282b4 <__bss_start+0x182b4> +40007bd0: 0a6425 call8 40012214 <__bss_start+0x2214> +40007bd3: 642500 extui a2, a0, 5, 7 +40007bd6: 696c20 excw +40007bd9: 6e .byte 0x6e +40007bda: 202c65 call8 40027ea0 <__bss_start+0x17ea0> +40007bdd: 206425 call8 40028220 <__bss_start+0x18220> +40007be0: 6f6d excw +40007be2: 0a6564 excw +40007be5: 000000 ill +40007be8: 6f6c movi.n a15, -26 +40007bea: 206461 l32r a6, 3ffcfd7c <_start-0x30284> +40007bed: 257830 extui a7, a3, 24, 3 +40007bf0: 783830 excw +40007bf3: 202c movi.n a0, 34 +40007bf5: 656c movi.n a5, -26 +40007bf7: 6e .byte 0x6e +40007bf8: 642520 extui a2, a2, 5, 7 +40007bfb: 202c movi.n a0, 34 +40007bfd: 6f6f72 s32i a7, a15, 0x1bc +40007c00: 206d excw +40007c02: 0a6425 call8 40012244 <__bss_start+0x2244> +40007c05: 000000 ill +40007c08: 616c66 bnei a12, 6, 40007c6d <__umoddi3+0xe09> +40007c0b: 206873 excw +40007c0e: 616572 s32i a7, a5, 0x184 +40007c11: 652064 excw +40007c14: 2c7272 excw +40007c17: 732520 maxu a2, a5, a2 +40007c1a: 000a add.n a0, a0, a0 +40007c1c: 696174 excw +40007c1f: 206c movi.n a0, -30 +40007c21: 0a6425 call8 40012264 <__bss_start+0x2264> +40007c24: 000000 ill +40007c27: 686300 excw +40007c2a: 736b addi.n a7, a3, 6 +40007c2c: 206d75 call12 40028304 <__bss_start+0x18304> +40007c2f: 257830 extui a7, a3, 24, 3 +40007c32: 783230 excw +40007c35: 000a add.n a0, a0, a0 +40007c37: 736300 maxu a6, a3, a0 +40007c3a: 206d75 call12 40028310 <__bss_start+0x18310> +40007c3d: 257830 extui a7, a3, 24, 3 +40007c40: 783230 excw +40007c43: 000a add.n a0, a0, a0 +40007c45: 000000 ill +40007c48: 757363 excw +40007c4b: 206d excw +40007c4d: 727265 call8 4007a374 <__bss_start+0x6a374> +40007c50: 000a add.n a0, a0, a0 +40007c52: 680000 excw +40007c55: 6f .byte 0x6f +40007c56: 642520 extui a2, a2, 5, 7 +40007c59: 617420 excw +40007c5c: 6c69 s32i.n a6, a12, 24 +40007c5e: 642520 extui a2, a2, 5, 7 +40007c61: 6f7220 excw +40007c64: 6f .byte 0x6f +40007c65: 206d excw +40007c67: 0a6425 call8 400122a8 <__bss_start+0x22a8> +40007c6a: 090000 l32e a0, a0, -64 +40007c6d: 735f20 maxu a5, a15, a2 +40007c70: 636174 excw +40007c73: 5f6b addi.n a5, a15, 6 +40007c75: 6e6573 excw +40007c78: 797274 excw +40007c7b: 203a add.n a2, a0, a3 +40007c7d: 2c7025 call8 40034380 <__bss_start+0x24380> +40007c80: 200920 or a0, a9, a2 +40007c83: 5f .byte 0x5f +40007c84: 5f .byte 0x5f +40007c85: 617473 excw +40007c88: 3a6b63 excw +40007c8b: 783020 excw +40007c8e: 383025 call8 4003ff90 <__bss_start+0x2ff90> +40007c91: 2c78 l32i.n a7, a12, 8 +40007c93: 200a20 or a0, a10, a2 +40007c96: 2009 s32i.n a0, a0, 8 +40007c98: 5f .byte 0x5f +40007c99: 737362 excw +40007c9c: 5f .byte 0x5f +40007c9d: 617473 excw +40007ca0: 3a7472 excw +40007ca3: 783020 excw +40007ca6: 383025 call8 4003ffa8 <__bss_start+0x2ffa8> +40007ca9: 2c78 l32i.n a7, a12, 8 +40007cab: 200920 or a0, a9, a2 +40007cae: 5f .byte 0x5f +40007caf: 737362 excw +40007cb2: 5f .byte 0x5f +40007cb3: 646e65 call8 4006c398 <__bss_start+0x5c398> +40007cb6: 203a add.n a2, a0, a3 +40007cb8: 257830 extui a7, a3, 24, 3 +40007cbb: 783830 excw +40007cbe: 202c movi.n a0, 34 +40007cc0: 200a add.n a2, a0, a0 +40007cc2: 2009 s32i.n a0, a0, 8 +40007cc4: 5f .byte 0x5f +40007cc5: 746164 excw +40007cc8: 735f61 l32r a6, 3ffe4a44 <_start-0x1b5bc> +40007ccb: 726174 excw +40007cce: 203a74 excw +40007cd1: 2c7025 call8 400343d4 <__bss_start+0x243d4> +40007cd4: 200920 or a0, a9, a2 +40007cd7: 5f .byte 0x5f +40007cd8: 746164 excw +40007cdb: 655f61 l32r a6, 3ffe1258 <_start-0x1eda8> +40007cde: 6e .byte 0x6e +40007cdf: 203a64 excw +40007ce2: 207025 call8 400283e4 <__bss_start+0x183e4> +40007ce5: 000a add.n a0, a0, a0 +40007ce7: 707300 excw +40007cea: 702520 excw +40007ced: 000a20 excw +40007cf0: 5f6673 excw +40007cf3: 6d7564 excw +40007cf6: 255b70 extui a5, a7, 27, 3 +40007cf9: 205d64 excw +40007cfc: 3a3061 l32r a6, 3ffd65bc <_start-0x29a44> +40007cff: 783020 excw +40007d02: 383025 call8 40040004 <__bss_start+0x30004> +40007d05: 2078 l32i.n a7, a0, 8 +40007d07: 316120 srai a6, a2, 17 +40007d0a: 203a add.n a2, a0, a3 +40007d0c: 257830 extui a7, a3, 24, 3 +40007d0f: 783830 excw +40007d12: 612020 xsr.m0 a2 +40007d15: 203a32 excw +40007d18: 257830 extui a7, a3, 24, 3 +40007d1b: 783830 excw +40007d1e: 612020 xsr.m0 a2 +40007d21: 203a33 excw +40007d24: 257830 extui a7, a3, 24, 3 +40007d27: 783830 excw +40007d2a: 000a20 excw +40007d2d: 000000 ill +40007d30: 637065 call8 4006b438 <__bss_start+0x5b438> +40007d33: 303d31 l32r a3, 3ffd3e28 <_start-0x2c1d8> +40007d36: 2578 l32i.n a7, a5, 8 +40007d38: 783830 excw +40007d3b: 202c movi.n a0, 34 +40007d3d: 637065 call8 4006b444 <__bss_start+0x5b444> +40007d40: 303d32 excw +40007d43: 2578 l32i.n a7, a5, 8 +40007d45: 783830 excw +40007d48: 202c movi.n a0, 34 +40007d4a: 637065 call8 4006b450 <__bss_start+0x5b450> +40007d4d: 303d33 excw +40007d50: 2578 l32i.n a7, a5, 8 +40007d52: 783830 excw +40007d55: 202c movi.n a0, 34 +40007d57: 637865 call8 4006b4dc <__bss_start+0x5b4dc> +40007d5a: 646176 excw +40007d5d: 3d7264 excw +40007d60: 257830 extui a7, a3, 24, 3 +40007d63: 783830 excw +40007d66: 202c movi.n a0, 34 +40007d68: 706564 excw +40007d6b: 303d63 excw +40007d6e: 2578 l32i.n a7, a5, 8 +40007d70: 783830 excw +40007d73: 000a add.n a0, a0, a0 +40007d75: 000000 ill +40007d78: 746146 j 40024f01 <__bss_start+0x14f01> +40007d7b: 206c61 l32r a6, 3ffcff2c <_start-0x300d4> +40007d7e: 637865 call8 4006b504 <__bss_start+0x5b504> +40007d81: 747065 call8 4007c488 <__bss_start+0x6c488> +40007d84: 6f69 s32i.n a6, a15, 24 +40007d86: 6e .byte 0x6e +40007d87: 252820 extui a2, a2, 24, 3 +40007d8a: 3a2964 excw +40007d8d: 000a20 excw +40007d90: 727065 call8 4007a498 <__bss_start+0x6a498> +40007d93: 6e69 s32i.n a6, a14, 24 +40007d95: 206674 excw +40007d98: 6e .byte 0x6e +40007d99: 6f .byte 0x6f +40007d9a: 756220 extui a6, a2, 18, 8 +40007d9d: 000a66 bnei a10, -1, 40007da1 <__umoddi3+0xf3d> +40007da0: 323130 orbc b3, b1, b3 +40007da3: 353433 excw +40007da6: 383736 entry a7, 0x1c18 +40007da9: 0039 s32i.n a3, a0, 0 +40007dab: 313000 srai a3, a0, 16 +40007dae: 343332 excw +40007db1: 373635 call12 4003f114 <__bss_start+0x2f114> +40007db4: 3938 l32i.n a3, a9, 12 +40007db6: 636261 l32r a6, 3ffe0b40 <_start-0x1f4c0> +40007db9: 666564 excw +40007dbc: 000000 ill +40007dbf: 313000 srai a3, a0, 16 +40007dc2: 343332 excw +40007dc5: 373635 call12 4003f128 <__bss_start+0x2f128> +40007dc8: 3938 l32i.n a3, a9, 12 +40007dca: 434241 l32r a4, 3ffd8ad4 <_start-0x2752c> +40007dcd: 464544 excw +40007dd0: 000000 ill +40007dd3: 6e3c00 excw +40007dd6: 6c6c75 call12 4007449c <__bss_start+0x6449c> +40007dd9: 3e .byte 0x3e +40007dda: 650000 extui a0, a0, 16, 7 +40007ddd: 5f7374 excw +40007de0: 6d6974 excw +40007de3: 2e7265 call8 40036508 <__bss_start+0x26508> +40007de6: 6e0063 excw +40007de9: 6f .byte 0x6f +40007dea: 647220 extui a7, a2, 2, 7 +40007ded: 000a73 excw +40007df0: 716573 excw +40007df3: 752520 extui a2, a2, 21, 8 +40007df6: 202c movi.n a0, 34 +40007df8: 207525 call8 4002854c <__bss_start+0x1854c> +40007dfb: 207364 excw +40007dfe: 0a7025 call8 40012500 <__bss_start+0x2500> +40007e01: 000000 ill +40007e04: 000c movi.n a0, 0 +40007e06: 000000 ill +40007e09: 000000 ill +40007e0c: 010001 l32r a0, 3ffc820c <_start-0x37df4> +40007e0f: 007c movi.n a0, -16 +40007e11: 010c movi.n a1, 0 +40007e13: 001000 movsp a0, a0 +40007e16: 140000 extui a0, a0, 0, 2 +40007e19: 000000 ill +40007e1c: 006c30 rsil a3, 12 +40007e1f: 023240 andb b3, b2, b4 +40007e22: 0e0000 read_impwire a0 +40007e25: 000040 excw +40007e28: 000010 excw +40007e2b: 002800 excw +40007e2e: 640000 extui a0, a0, 0, 7 +40007e31: 6e .byte 0x6e +40007e32: 704000 excw +40007e35: 000002 l8ui a0, a0, 0 +40007e38: 0e .byte 0xe +40007e39: 000050 excw +40007e3c: cd48 l32i.n a4, a13, 48 +40007e3e: ff .byte 0xff +40007e3f: 3f .byte 0x3f +40007e40: ffe1d0 excw +40007e43: 3f .byte 0x3f +40007e44: c008 l32i.n a0, a0, 48 +40007e46: ff .byte 0xff +40007e47: 3f .byte 0x3f + ... diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/disasm.sh b/src/mongoose-6.11/src/common/platforms/esp31/rom/disasm.sh new file mode 100755 index 0000000..5ab8fe5 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp31/rom/disasm.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +xtensa-esp108-elf-gcc -Wl,-N,-Ttext,0x40000000 -nostdlib rom.S -o rom.elf && \ + xtensa-esp108-elf-objdump -d rom.elf > ESP31B_ROM.txt diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/notes.c b/src/mongoose-6.11/src/common/platforms/esp31/rom/notes.c new file mode 100644 index 0000000..431d718 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp31/rom/notes.c @@ -0,0 +1,163 @@ +/* Just some notes scribbled while disassembling */ + +/* + * RTC = 0x60008000 + * RTC+0x18: ??c????? ???????? ???????? ???????? + * RTC+0x34: ???????? ??bbbbbb bbbb???? ??aaaaaa + */ +int _X_get_rst_cause(void) { + int ret; + int a = GET_PERI_REG_BITS(RTC_STATE1, 6, 0); + if (a == 5) { + int b = (RTC_STATE1 >> 12) && 0xfff; + if (b != 1) { + ret = (b == 8 ? a : 0); + } else { + ret = 20; + } + } else { + ret = a; + } + CLEAR_PERI_REG_MASK(RTC_STATE0, RTC_CNTL_SLP_WAKEUP); + return ret; +} + +/* + * RTC = 0x60008000 + * RTC+0x38: ???????? ???????? ???????? ??cccccc + * RTC+0x74: ???????? ???????? ???????? dddddddd + * RTC+0x80: ???????? ??????a? ???b???? ???????? + */ +void main(void) { + uint32_t rst_cause = _X_get_rst_cause(); + CLEAR_PERI_REG_MASK(RTC+0x80, BIT(17)); // a + SET_PERI_REG_MASK(RTC+0x80, BIT(12)); // b + uint32_t boot_mode = GET_PERI_REG_BITS(GPIO_STRAP, 6, 0); // c + if (boot_mode & (BIT(5) | BIT(4)) == (BIT(5) | BIT(4)) || boot_mode == 24 || boot_mode == 26) { + CLEAR_PERI_REG_MASK(RTC+0x74, 0xff); + } + if (boot_mode & (BIT(5) | BIT(4)) == BIT(5)) { + CLEAR_PERI_REG_MASK(RTC+0x94, BIT(31)); + CLEAR_PERI_REG_MASK(RTC+0x98, BIT(31)); + CLEAR_PERI_REG_MASK(RTC+0x9c, BIT(31)); + CLEAR_PERI_REG_MASK(RTC+0xa0, BIT(31)); + CLEAR_PERI_REG_MASK(RTC+0xa4, BIT(31)); + CLEAR_PERI_REG_MASK(RTC+0xa8, BIT(31)); + CLEAR_PERI_REG_MASK(RTC+0xac, BIT(31)); + } + if (boot_mode & (BIT(5) | BIT(3)) == 0) { + // ... 1405 + } + CLEAR_PERI_REG_MASK(RTC+0x74, 0xff); + _X_uart_attach(); + _X_uart_init(0); + // GPIO_STRAP ... + ets_printf(boot_banner, fw_build, rst_cause, boot_mode); + // rst_cause + if (rst_cause == 1 || rst_cause == 2) { + + } else { + // ... + } + ets_printf("%s %u", "ets_main.c", 305); + while(1) {} +} + +/* + * GPIO strap mapping: + * + * 0011 1111 1011 0011 + * || |||| |||| |||| + * || |||| |||| |||`- IO5 + * || |||| |||| ||`-- IO15 + * || |||| |||| |`--- IO4 + * || |||| |||| `---- IO2 + * || |||| |||`------ ? + * || |||| ||`------- IO0 + * || |||| |`-------- IO12 + * || |||| `--------- ? + * || |||`----------- CLK + * || ||`------------ ? + * || |`------------- SD0 + * || `-------------- SD1 + * |`---------------- ? SD2 + * `----------------- SD3 + */ + +struct uartdev { + uint32_t baud_rate; // 0 + uint32_t ud4; + uint32_t ud8; + uint32_t ud12; + uint32_t ud16; + uint32_t ud20; + uint8_t ud24; + uint8_t ud25; + uint32_t ud28; + uint32_t ud32; + uint32_t ud36; + uint8_t ud40; + uint32_t ud48; + uint32_t ud52; +}; + +void _X_uart_attach(void) { + // zero uartdev + uartdev.baud_rate = 115200; + _X_xtos_ints_off(1 << ETS_UART_INUM); + // INTR_MAP_REG_C + // 11111111 11111111 11111100 00011111 & + // 00000000 00000000 00000000 10100000 | + // PRODPORT_INTR_MAP_13 -> 5 = ETS_UART_INUM + // 11111111 11111111 10000011 11111111 & + // 00000000 00000000 00010100 11111111 | + // PRODPORT_INTR_MAP_14 -> 5 = ETS_UART_INUM + _xtos_set_interrupt_handler_arg(ETS_UART_INUM, uart_int_handler, _c_0x3fffdb2c_uart_int_handler_arg); +} + +void _X_uart_init(uint32_t a) { + // GPIO_FUNC_IN_SEL3 + // xx999999 88888877 77776666 66555555 + // 11111111 11111100 00001111 11111111 = 0xfffc0fff + // 00000000 00000000 10010000 00000000 = 0x00009000 + // GPIO17 func => 9 + // 00000000 00000010 00000000 00000000 + uart_div_modify(13000000 / uartdev.baud_rate); + // ... +} + +struct _st_0x3fffdc90 { +}; + +struct _st_0x3fffdf70 { + void *fp1; // 20 + void *fp2; // 24 + uint32_t d28; + uint32_t d32; + uint32_t d36; + struct _st_0x3fffdc90 *st; // 44 +} stdf70; + +void _X_slc_init_attach(void *fp1, void *fp2, struct _st_0x3fffdc90 *st, uint32_t gpio_mode) { + stdf70.fp1 = fp1; + stdf70.fp2 = fp2; + stdf70.st = st; + d28 = d32 = d36 = 0; + SET_PERI_REG_MASK(WIFI_RST_EN, PRODPORT_SDIO_RST); + CLEAR_PERI_REG_MASK(WIFI_RST_EN, PRODPORT_SDIO_RST); + if (gpio_mode == 4) { + SET_PERI_REG((READ_PERI_REG(PERIPHS_HINF_BASEADDR+4) & 0xf0000000) | 0x01110013); + } else { + SET_PERI_REG((READ_PERI_REG(PERIPHS_HINF_BASEADDR+4) & 0xf0000000) | 0x02320017); + } + SET_PERI_REG(PERIPHS_HINF_BASEADDR, 0x11116666); + _X_slc_set_host_io_max_window(); + ... +} + +#define SLC_TOKEN1 (PERIPHS_SLC_BASEADDR + 0x54) +#define SLC_BRIDGE_CONF (PERIPHS_SLC_BASEADDR + 0x74) + +void _X_slc_set_host_io_max_window(void) { + SET_PERI_REG(SLC_BRIDGE_CONF, (READ_PERI_REG(SLC_BRIDGE_CONF) & 0xfffff0c0) | 0x720); +} diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/rom.S b/src/mongoose-6.11/src/common/platforms/esp31/rom/rom.S new file mode 100644 index 0000000..55737a5 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp31/rom/rom.S @@ -0,0 +1,16 @@ +.text +.org 0 +.globl _start + +// xtensa-esp108-elf-gcc -Wl,-N,-Ttext,0x40000000 -nostdlib rom.S -o rom.elf + +here = . +#define PROVIDE(name, addr) name = here + addr - 0x40000000 + +#include "rom_functions.S" + +.text + +_start: +.incbin "rom.bin" +_end: diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/rom.bin b/src/mongoose-6.11/src/common/platforms/esp31/rom/rom.bin new file mode 100644 index 0000000000000000000000000000000000000000..355329da2776060e91395da6959f82b384520a00 GIT binary patch literal 65536 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 literal 0 HcmV?d00001 diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/rom_functions.S b/src/mongoose-6.11/src/common/platforms/esp31/rom/rom_functions.S new file mode 100644 index 0000000..5711a5c --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp31/rom/rom_functions.S @@ -0,0 +1,294 @@ +// These come from linker script + +PROVIDE ( Cache_Read_Disable , 0x4000444c ); +PROVIDE ( Cache_Read_Enable , 0x4000438c ); + +PROVIDE ( ets_delay_us , 0x40002db4 ); + +PROVIDE ( bzero , 0x40002a54 ); + +PROVIDE ( memcmp , 0x400068ec ); +PROVIDE ( memcpy , 0x40006974 ); +PROVIDE ( memmove , 0x40006a6c ); +PROVIDE ( memset , 0x40006be4 ); + +PROVIDE ( strcmp , 0x40005bb8 ); +PROVIDE ( strcpy , 0x40005cdc ); +PROVIDE ( strlen , 0x40005d6c ); +PROVIDE ( strncmp , 0x40005dd0 ); +PROVIDE ( strncpy , 0x40005e90 ); +PROVIDE ( strstr , 0x40005f6c ); + +PROVIDE ( ets_install_putc1 , 0x40002774 ); +PROVIDE ( ets_printf , 0x40002804 ); +PROVIDE ( ets_putc , 0x40002b14 ); + +PROVIDE ( ets_str2macaddr , 0x40002a64 ); + +PROVIDE ( gpio_output_set , 0x400049d8 ); +PROVIDE ( gpio_output_set_high , 0x400049f8 ); + +PROVIDE ( ets_get_cpu_frequency , 0x40002de8 ); +PROVIDE ( ets_update_cpu_frequency , 0x40002ddc ); + +PROVIDE ( lldesc_build_chain , 0x40004c8c ); + +PROVIDE ( multofup , 0x400068a0 ); +PROVIDE ( roundup2 , 0x40006890 ); + +PROVIDE (software_reset_cpu , 0x40002998 ); + +PROVIDE ( SPIEraseSector , 0x40004708 ); +PROVIDE ( SPIRead , 0x40004898 ); +PROVIDE ( SPIWrite , 0x40004738 ); + +PROVIDE ( uart_div_modify , 0x400034e8 ); +PROVIDE ( uart_tx_one_char , 0x4000362c ); + +PROVIDE ( __divsi3 , 0x40006888 ); +PROVIDE ( __udivdi3 , 0x40006c30 ); +PROVIDE ( __umoddi3 , 0x40006e64 ); + +PROVIDE ( _xtos_set_intlevel , 0x40006670 ); + +// These have been reverse-engineered. + +PROVIDE(_XX_Vec40, 0x40000040) +PROVIDE(_XX_ExcVec50, 0x40000050) +PROVIDE(_XX_ExcVec80, 0x40000080) + +PROVIDE(_XX_Vec400, 0x40000300) + +PROVIDE(_WindowOverflowHandler, 0x40000100) +PROVIDE(_WindowUnderflowHandler, 0x40000140) + +PROVIDE(_X_ResetVector, 0x40000500) + +PROVIDE(_c_stack, 0x40000700) +PROVIDE(_c_bss_start, 0x40000708) +PROVIDE(_c_bss_end, 0x4000070c) +PROVIDE(_c_0x3fffc210, 0x40000734) +PROVIDE(_c_0x80000000, 0x40000738) +PROVIDE(_c_0x40000000, 0x40000760) +PROVIDE(_c_0x7fffffff, 0x40000780) +PROVIDE(_c_0x00ff0000, 0x40000798) + +PROVIDE(_X_start, 0x400007ac) + +PROVIDE(_c_0x3fffd820, 0x40000f50) +PROVIDE(_X_ets_task, 0x40000f54) +PROVIDE(_XX_unk0f84, 0x40000f84) +PROVIDE(_XX_unk0f96, 0x40000f98) + +PROVIDE(_c_ets_critical_level, 0x400010a4) +PROVIDE(_X_ets_enter_critical, 0x400010a8) +PROVIDE(_X_ets_exit_critical, 0x400010bc) +PROVIDE(_X_ets_exit_critical_and_wait_int, 0x400010d4) +PROVIDE(_X_ets_isr_attach, 0x400010e8) // 3 args +PROVIDE(_X_ets_isr_mask, 0x400010f8) // 1 arg +PROVIDE(_X_ets_isr_unmask, 0x40001104) // 1 arg + +PROVIDE(_c_0x3fffda30, 0x40001110) +PROVIDE(_XX_set_0x3fffda30_0, 0x40001114) +PROVIDE(_XX_set_0x3fffda30_4, 0x40001120) +PROVIDE(_c_0xfffdffff, 0x4000112c) +PROVIDE(_c_0x60003e00, 0x40001130) +PROVIDE(_c_0x60008200, 0x40001134) +PROVIDE(_c_0x60007e00, 0x40001138) +PROVIDE(_c_0x1000, 0x4000113c) +PROVIDE(_s_fw_build, 0x40001140) +PROVIDE(_s_boot_banner, 0x40001144) +PROVIDE(_s_pct_s_pct_u, 0x40001148) +PROVIDE(_s_ets_main_c, 0x4000114c) +PROVIDE(_X_main, 0x4000115c) + +PROVIDE(_l_strap_0x0xxx, 0x4000125a) +PROVIDE(_l_strap_init_uart0, 0x40001269) +PROVIDE(_l_strap_0x0x00, 0x400012e2) +PROVIDE(_l_boot, 0x400012ea) +PROVIDE(_l_rst_cause_345, 0x40001336) +PROVIDE(_l_rst_cause_12, 0x40001342) +PROVIDE(_l_strap_NxNxxx, 0x40001405) +PROVIDE(_l_strap_0010xx, 0x4000144c) +PROVIDE(_l_strap_001000_0x110x, 0x400014b0) +PROVIDE(_l_strap_0x0x11_loader, 0x400014c9) // loader +PROVIDE(_l_strap_0x0x01, 0x400014d4) +PROVIDE(_l_strap_0x0x10, 0x400014e6) +PROVIDE(_c_0xffff8fff, 0x400014f0) +PROVIDE(_c_0x60008e00, 0x400014f4) + +PROVIDE(_s_waiting_for_host, 0x4000152c) +PROVIDE(_s_mem_banner, 0x40001cdc) +PROVIDE(_c_stack_sentry, 0x40001ce0) +PROVIDE(_XX_unk153c, 0x4000153c) + +PROVIDE(_c_data_end, 0x40001cd8) +PROVIDE(_c_data_start, 0x40001ce4) +PROVIDE(_X_print_mem_banner, 0x40001ce8) + +PROVIDE(_s_exc_sp_fmt, 0x40001d0c) +PROVIDE(_s_exc_sf_dump_fmt, 0x40001d10) +PROVIDE(_s_exc_regs_fmt, 0x40001d14) +PROVIDE(_X_exc_handler, 0x40001d18) + +PROVIDE(_XX_unk1d90, 0x40001d90) + +PROVIDE(_X_ets_memset, 0x40001db4) +PROVIDE(_X_ets_memcpy, 0x40001dc4) +PROVIDE(_X_ets_memmove, 0x40001dd4) +PROVIDE(_X_ets_memcmp, 0x40001de4) + +PROVIDE(_st_0x3fffda9c, 0x40002150) // struct + +PROVIDE(_X_ets_uart_putc, 0x4000223c) +PROVIDE(_X_ets_unk225c, 0x4000225c) + +PROVIDE(_c_0x4000223c_ets_uart_putc, 0x40002780) +PROVIDE(_X_ets_install_uart_printf, 0x40002784) +PROVIDE(_c_0x400027dc, 0x40002790) +PROVIDE(_X_ets_install_external_printf, 0x40002794) +PROVIDE(_X_ets_install_putc2, 0x400027b4) +PROVIDE(_X_ets_get_printf_buf_remain_len, 0x400027c0) +PROVIDE(_X_ets_reset_printf_buf_len, 0x400027cc) +PROVIDE(_X_ets_putc, 0x400027dc) + +PROVIDE(_c_0xdfffffff, 0x400028d4) +PROVIDE(_X_get_rst_cause, 0x400028d8) +PROVIDE(_XX_unk2948, 0x40002948) +PROVIDE(_l_2970, 0x40002970) +PROVIDE(_X_sw_sys_rst, 0x4000297c) +PROVIDE(_c_0x00400000, 0x400029b4) +PROVIDE(_c_0xffbfffff, 0x400029b8) +PROVIDE(_XX_apb_bridge_toggle, 0x400029bc) // turns RTC_CNTL_APB2RTC_BRIDGE_SEL on and off +PROVIDE(_X_ets_strcpy, 0x400029ec) +PROVIDE(_X_ets_strncpy, 0x40002a00) +PROVIDE(_X_ets_strcmp, 0x40002a10) +PROVIDE(_X_ets_strncmp, 0x40002a24) +PROVIDE(_X_ets_strlen, 0x40002a34) +PROVIDE(_X_ets_strstr, 0x40002a40) + +PROVIDE(_st_0x3fffdb10_uartdev, 0x40002e4c) // some struct - uartdev? + +PROVIDE(_c_0x3fffdb00, 0x40002e50) +PROVIDE(_c_0x3fffdb04, 0x40002f64) +PROVIDE(_XX_unk2e58, 0x40002e58) +PROVIDE(_X_UartDwnLdProc, 0x40002f6c) +PROVIDE(_c_0x00001800, 0x400030ec) +PROVIDE(_X_FlashDwnLdStartMsgProc, 0x400030f0) +PROVIDE(_XX_unk313c, 0x4000313c) +PROVIDE(_XX_unk31bc, 0x400031bc) +PROVIDE(_XX_unk31e4, 0x400031e4) +PROVIDE(_XX_unk3210, 0x40003210) +PROVIDE(_XX_unk3240, 0x40003240) +PROVIDE(_X_MemDwnLdStopReqMsgProc, 0x4000329c) +PROVIDE(_X_UartConnectProc, 0x400032c4) +PROVIDE(_X_UartRegWriteProc, 0x400032d4) +PROVIDE(_X_UartRegReadProc, 0x40003318) + +PROVIDE(_c_115200, 0x4000332c) +PROVIDE(_c_0x3feffe00, 0x40003330) +PROVIDE(_c_0xffff83ff, 0x40003334) +PROVIDE(_c_0x00001400, 0x40003338) +PROVIDE(_c_0x40003728_uart_int_handler, 0x4000333c) +PROVIDE(_c_0x3fffdb2c_uart_int_handler_arg, 0x40003340) +PROVIDE(_X_uart_attach, 0x40003344) +PROVIDE(_XX_uart_set_unk33c0, 0x400033c0) +PROVIDE(_c_0x5ffffe00, 0x400033cc) +PROVIDE(_c_0x0000ffff, 0x400033d0) +PROVIDE(_c_0x000fffff, 0x40003448) +PROVIDE(_c_0x00060000, 0x400034e0) +PROVIDE(_c_0xfff9ffff, 0x400034e4) + +PROVIDE(_c_0xfffc0fff, 0x40003520) +PROVIDE(_c_0x00009000, 0x40003524) +PROVIDE(_c_0x00020000, 0x40003528) +PROVIDE(_c_13000000, 0x4000352c) +PROVIDE(_c_0x08000000, 0x40003530) +PROVIDE(_X_uart_init, 0x40003534) +PROVIDE(_l_35f4, 0x400035f4) +PROVIDE(_X_uart_wait_tx_empty, 0x4000369c) + +PROVIDE(_X_uart_int_handler, 0x40003728) + +PROVIDE(_X_uart_tx_one_char2, 0x40003664) + +PROVIDE(_X_send_packet, 0x400037d4) +PROVIDE(_X_SendMsg, 0x40003828) +PROVIDE(_X_recv_packet, 0x4000383c) +PROVIDE(_X_RcvMsg, 0x40003988) +PROVIDE(_X_uart_rx_readbuff, 0x400039a0) + +PROVIDE(_c_0x60004e00, 0x40003a18) +PROVIDE(_X_SelectSpiFunction, 0x40003a1c) // 1 arg - SPI number +PROVIDE(_c_0x60002e00, 0x40003c78) +PROVIDE(_X_SPI_chip_erase, 0x40003c7c) +PROVIDE(_c_0x00ffffff, 0x40003cb4) +PROVIDE(_c_0x01000000, 0x40003cb8) +PROVIDE(_XX_unk3cbc, 0x40003cbc) +PROVIDE(_c_0x00800000, 0x40003d00) +PROVIDE(_c_0x02000000, 0x40003d4c) +PROVIDE(_XX_unk3e24, 0x40003e24) +PROVIDE(_X_SPI_read_status, 0x40003efc) +PROVIDE(_c_0x90000000, 0x40003f48) +PROVIDE(_c_0x70000035, 0x40003f4c) +PROVIDE(_c_0x00040000, 0x40003f50) +PROVIDE(_XX_unk3f54, 0x40003f54) +PROVIDE(_c_0x04000000, 0x40003fcc) +PROVIDE(_XX_unk4010, 0x40004010) +PROVIDE(_X_SPI_write_enable, 0x400041bc) +PROVIDE(_X_Wait_SPI_Idle, 0x40004208) +PROVIDE(_l_4228, 0x40004228) +PROVIDE(_l_4234, 0x40004234) +PROVIDE(_XX_unk4238, 0x40004238) +PROVIDE(_X_SPIFlashModeConfig, 0x400042d8) +PROVIDE(_X_spi_flash_attach, 0x40004370) // 2 args: SPI num, ??? +PROVIDE(_X_SPIReadModeConfig, 0x40004538) + +PROVIDE(_X_SPIEraseArea, 0x400048b4) +PROVIDE(_XX_unk4940, 0x40004940) + +PROVIDE(_st_0x3fffdc90, 0x40004d80) +PROVIDE(_XX_unk4d88, 0x40004d88) +PROVIDE(_XX_unk4f6c, 0x40004f6c) +PROVIDE(_XX_unk4fc8, 0x40004fc8) + +PROVIDE(_c_0xbfffffff, 0x40004c80) +PROVIDE(_c_0xff000fff, 0x40004c88) +PROVIDE(_XX_unk4f14, 0x40004f14) +PROVIDE(_s_no_rds, 0x40005008) +PROVIDE(_XX_unk500c, 0x4000500c) + +PROVIDE(_fp_0x40004f6c, 0x40005164) +PROVIDE(_fp_0x40004fc8, 0x40005168) +PROVIDE(_X_sip_init_attach, 0x40005170) // 1 arg, boot_mode? +PROVIDE(_XX_unk51ac, 0x400051ac) +PROVIDE(_c_0x60017e00, 0x40005478) +PROVIDE(_st_0x3fffdf70, 0x400054a4) +PROVIDE(_c_0x6000ae00, 0x400054b8) +PROVIDE(_c_0xf0000000, 0x400054bc) +PROVIDE(_c_0x02320017, 0x400054c0) +PROVIDE(_c_0x11116666, 0x400054c4) +PROVIDE(_c_0x01110013, 0x400054e4) +PROVIDE(_X_slc_init_attach, 0x400054e8) // 4 args - fp, fp, st, boot_mode; PRODPORT_SDIO_RST +PROVIDE(_l_slc_boot_mode_4, 0x40005654) +PROVIDE(_X_slc_enable, 0x40005678) +PROVIDE(_X_slc_select_tohost_gpio_mode, 0x400056fc) +PROVIDE(_X_slc_select_tohost_gpio, 0x40005708) +PROVIDE(_c_0xff300000, 0x40005730) +PROVIDE(_XX_unk5734, 0x40005734) +PROVIDE(_XX_unk57b8, 0x400057b8) +PROVIDE(_XX_unk57f4, 0x400057f4) +PROVIDE(_XX_unk5848, 0x40005848) + +PROVIDE(_c_0xfffff0c0, 0x40005988) +PROVIDE(_X_slc_set_host_io_max_window, 0x4000598c) +PROVIDE(_X_slc_init_credit, 0x400059ac) +PROVIDE(_X_slc_add_credits, 0x400059c4) + +PROVIDE(_X_xtos_set_interrupt_handler_arg, 0x400059d8) +PROVIDE(_X_xtos_set_interrupt_handler, 0x40005a24) +PROVIDE(_X_xtos_ints_on, 0x40005a34) +PROVIDE(_X_xtos_ints_off, 0x40005a58) + +PROVIDE(_XX_xtos_exc_unk5a80, 0x40005a80) +PROVIDE(_XX_xtos_exc_unk5b94, 0x40005b94) diff --git a/src/mongoose-6.11/src/common/platforms/esp32/rom/.gitattributes b/src/mongoose-6.11/src/common/platforms/esp32/rom/.gitattributes new file mode 100644 index 0000000..b2dd225 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/rom/.gitattributes @@ -0,0 +1,2 @@ +rom.bin -nodiff +rom.elf -nodiff diff --git a/src/mongoose-6.11/src/common/platforms/esp32/rom/disasm.sh b/src/mongoose-6.11/src/common/platforms/esp32/rom/disasm.sh new file mode 100755 index 0000000..ae719ac --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/rom/disasm.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +xtensa-esp32-elf-gcc -Wl,-N,-Ttext,0x40000000 -nostdlib rom.S -o rom.elf && \ + xtensa-esp32-elf-objdump -d rom.elf > ESP32_ROM.txt diff --git a/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.S b/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.S new file mode 100644 index 0000000..11bd296 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.S @@ -0,0 +1,28 @@ +.text +.org 0 +.globl _start + +// xtensa-esp32-elf-gcc -Wl,-N,-Ttext,0x40000000 -nostdlib rom.S -o rom.elf + +here = . +#define PROVIDE(name, addr) name = here + addr - 0x40000000 + +#include "rom_functions.S" + +PROVIDE ( _x_unk_40061b88, 0x40061b88 ) +PROVIDE ( _x_unk_spi_400622c0, 0x400622c0 ) +PROVIDE ( _c_3ff000c8, 0x40062df0 ) +PROVIDE ( _c_3ff5b024, 0x40062e0c ) +PROVIDE ( _c_3ff5b000, 0x40062e10 ) +PROVIDE ( _c_3ff5b020, 0x40062e14 ) +PROVIDE ( _c_3ff5b028, 0x40062e18 ) +PROVIDE ( _l_40062e90, 0x40062e90 ) +PROVIDE ( _l_SPI_Prepare_Encrypt_Data_loop, 0x40062e34 ) +PROVIDE ( _l_SPI_Prepare_Encrypt_Data_wait, 0x40062e54 ) +PROVIDE ( _l_SPI_Prepare_Encrypt_Data_out, 0x40062e5e ) + +.text + +_start: +.incbin "rom.bin" +_end: diff --git a/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.bin b/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.bin new file mode 100644 index 0000000000000000000000000000000000000000..8f0bb3fa676368b2cc2fb38b8d7fd7012220dab4 GIT binary patch literal 458752 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⪼dDlCV)_Wy`wOxZk!o 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�`GS5Zb28@M4E(+pnSXovE=3L@|* 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 z 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 kz@HrWlLLQp;7<vP@FxfU%+wpX9**0P;R5aR2}S literal 0 HcmV?d00001 diff --git a/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.elf b/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.elf new file mode 100755 index 0000000000000000000000000000000000000000..f65fbf09996b1f49996ff815c7e28fdabe12fd06 GIT binary patch literal 521912 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%0Au=(YqQQcqP@3=`*i95EilaE~L_ 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%0DU(>2#J>;ph$jnn8-qLgkJC0L+XrH z=`OAwDm_vpqoTD`F`2AdVdr6`DE(FnG@F>~ zBDFmE#%+cvlwQ9bejVk?6`oJ;!PT(86Rw70;%d-{Blz_itNh36oJ9S{O_viUJ|=0geG04r$*1+4W-xGc>SLvY??&Ky1_&q_zMAT8`m>e?<5h)?a^? zP5X=$Zc7NK#mEL3If>m&4U&W_C#~>2OZ+A^=nU2hIx{NXZ3ZgvD)o17<@xki&$`NKvFXVRzOIfcIlv zzyW|eV4&_9Z*2av`oC`egF$ET4fn+nh9ewDBo6LXiuyagcR9{);d|#kpj*jLV z#H@z#xX!V_4a1)Us|Xnmj7904LoK8uf1p0`)%?Pd(-)}*FZnxzTI(Wzr&G2zPS?iq zqokik9TNnd7Esn7Wk>!^W)gB?i$HKpKwA&X0&-Z(i-56-S_DGtZdtcV3&t(L#(rWe zK&6|=Q}`%df?%&e3K*$#t-|RfJe+)bhEkwQ_L@r{f^aP5s(^Xc7 z)1L~1r}{5kI}D$xng~jhMXA?v-KRn7x}H;7{Z7xRK>ZdxHC7EweDBnveHDTTo4XV# z3edHmI_!y}mUUg|n9n)yF#U9q;GAVy}b zUHCwGo<_Ms;T^W?|3(N(zk#;h)k8xdPNA+uX+Ur)?SL`{Cjg4-U@dNZMxaeY+y~mD z-db%Z*>WN)`o-C{R5P)7Fz^iS-6bVzTm#YdF+wOnx*U0n*hl+X*wKxzcQ8+Ieu)kaRX`XfDFnO*8djE*7=9UlEI^gL%b&_*B*ZJ$PED6mf%kps> z!#Je=6AE~7?1^&B5%SCGw`0te$)%n}WXNCXU|CV~F*4?MK_uyFOsvvcjkSe3Qt8t3 zSYpmf8w||&UCH|>)(sbE^CAS=={0ws3wsN8W!S*`cGU1Ddxw-6Qde7Zhx1M7S67T6 zy$R8-2{Cn!nwwndMOUzx3fDI7wz($AUF+t#rUloub6e<^en%k5`lcVLxoY)EPt*pt zNdN8n9y#PApm<<@(?$xkjUfVUL8?G22Av@9yLPwtef|0O9g}XWD~5BmMcP{DV3J%D z`qdogLVtuKYRKW<*D)i}9wzw6yJd&!&@6?$d#o>TXcw%*fi|i! z1-@xVY=^7_osM|eNDL&}m4a{c4xk(p@b@nMa{uADShSByg-dXPK>JJ}*48q#n#e1X zK15y_T>YrLVp35^Ym}3m_lUeQL_=>g&^|_-fK5}EQ7%@gVN>2&LzkH=M)# zo}F*NyzC2~T*$H`K9Ooi`tdV4InVemH(;Vye&le5>DwTec=LetsXx#r`lH6Nvg9;B zQaR0=3#VaTid}c6yk^}S+7z$JYf098quCeJo+8ulJEGO;w3GbSG!;zC^V0fP;LKSZ z_I4cSWuI=Ud!t~U-)Rn;v%8Ob4mp8ocFzatLDWwPQAiqBvv#@X6JoDAdAD|Y^T!;l zsWopc_v|7?{~|>@IZP=krZnuZLiTCBYrfWbLt~1C$TU7!uB%!O#Rl@yk-Jb&-3S$d zv{W0EInIeNf`&BDD@4+6SVyL2237Or-GT?QQo+Nr(z4~A`S(GaKD+yYODSkGU`LxqNR+f>X zOS_M`$QTRnj*C7Gj~8J#4)VnZTt2R?#K8hjw~IdMNqM99l0JN)?RWPvf!4H{wwDMa zuPVbs;C?y{SF>w9U*oCioYlG?&!CDxjk<*Uwkcdj3jD--MOy7TYnlR&k_V?y>OR&9 zY^or`toa)8UUK&bVax!g50q|_qJO)Z>(U6s8oj(6<^|C4*yWxX$nn&acb5q( z`LAo}K7_7B#46{#-b2ZMOD1cC|F3En7ewf1=N;IsGs+y&n%j50Qmu0>TzW9FPCQ_j zjyrtZ_VcT7TJ!u?N|?cqTXd@#KYI-FZ%Hb{2|*UXqYlfXpRMmxNj^no}n# zlmxW~QE;ojcD~R}>)n(!+vyS^lI&SrG>CdGwi(=XVvub!r`vSD%HEhr=y^UpzeAf& zDEUO%_%+u)4%+5+C_ zHopV5g8<*E)3?7PE7{oa!Lg7a@7Dh`{wUkfC)QI}(sYuqMBFyRvzMz=BL7@p>bSaA z$=5L>4TjRK|H(*4H6`Z4i2g@@P#DC>NJd`qz209GbWBL;BVy-B8!7*YFQ1Kahb(W7qGm0Jv`Eky*VdBZ3C2IuXlNg>8yN&+n8!1D zEA~X1FW2SXXBb?`3cFn&)N@6rf7KvB!WeAur~fC?sYGRdwFavZo?~HF5!d<#=YN~$ zUAnY8O=)+uM7Gsw?-*z;f`7H++_gdjJ=&h6($n&)vPMUk+i}{u$`2ZW{~8=0#B^kh zTsGHZ_Cy2@>Jxa|iVSx>xbHv~IG zdOhF>BSx}-ZD1GrDqDD0O~PusG_)$sju&f}RHXorlzx*VERC04tW~`1(pTSdyrLg# zTTwND$UP5b}f_9+ni6ik27d?QHn)^+5(fw=;S5?`FWOzv!H;2@v zAK(@Ag@gv0_D<}O8gmsyG$h5s#RI`aRNLKa*qYq!6$uF58-rc6eCNop32{xgtF;cR zm+sfND=hvZrxEn!Y0E(6l&S&$Lr#1v3_g{K0w|vpbsXnJ2OL*Lc(b@K;mu>K9_7uy zX-f!KOkgeFR)?k)q=ilN={{eH2`m<>|9+dKI(XXcZAnZOK#zJa*7DslkNhr_tHuMqI^0cg{Wh+uG_fqEs|Hj=z!Iur}cJk z0WmS?^{QAVdVDIQr_&D4Dx{y|_YKq2I-BoyBU~kE7{pqkt?@QhB(qkAK{H3eGNE3u zt$nEswh3`6<%%G-36m;zd~l3z>oSk06TJ53?SO59SQH4ioiRSZY}a-^#B4PxRn3*# z$h_BMn@CP3wuxleCIr#yzbx}OI}y@%<~9lG<82cH)TBOrd(<#A!UZ-`**RyMK^?I= zdAK)uq^@y|EV<5~9I#E4b#9PL`lo;k!Zv|_3Ey-q^Q`Oy-~9XSUcR}!6MVDitp^%X zHkB@*)UR~rS#OLYod<86F;%^|)4xT(WQsyo5xbJ|^eQ=xbxdl9*^YvB| zt8A?7so}Ty+*PvnAj5u8>5}#xEyc%1KC|~LnZf`4Guv#i(>mqw+xVUa2OulTVYdmS zFnc9Hdl-QvDKYb-eW}Yl#iV^JZ$qCtOPc2jd?!$2Dk~Y)!)nYLPw92%nupXFs*fzt zzLk%(Z+Rt?@!#9`p3ByG&r9o&jNVSmcd=EKcD?*uBS(geQbsKEOd)NWPTKS{X_GzB zrkH`sS(VJh9*rZQ(w5$}B8$iarQ?s$0opXZQcv`%nDNl7rdCcCcDLwFUI&)yp;x6P zyVJs)Z<6sKdet$8ZP}v_A7Kw4>|!G`9D24N^v?`}Gtql3O=n++G@9Y)PkzH&_;w&1LFmH;k|wQeCDP_h6?V@(HUu+5vL}B&iI2UnW%9Mn!PA?dxJsYX{bJKeDDhyfL8PZ6>se#gnkLD0RgxZf*k2JZ_Hz ztbt&T!Q1s2K-J=Zf@uhhV{@@5h>W9zjN>PPaf~4}5aSq5MxDrMnx?;xDMIeOzW5 zYCHfPOdADq=n&JJ5H&6L7`8lFwCY%YEJz6azEGG`F@?oS03&B&lm#Y zP7923`8=xk-oBm2mmj_N#3T3i-YD!eNhM@NVON^P`64Mx9+|XSX`Xgi!7?JHE|c=V zEiZ9-vvfU0K*vlXgnx`91Cw&^J6O-cg4XHQhLJQ z8V2-3@fw(&3zjJY-m+yO6>sYkW<}pxB1*^}(JH1AmV}vKi(=Lfl``3_@c52HT*H8K z=@K%b#&boU_}h>{hune;y6zMtP(}(yMKSAZB=`NZg9-({cf>I@CyG46`w%PHTSOE! zA-WinrCk~13Azuj3H7FIgDBQ>tf(pW7Ll)K!L@krUZo5gF68SVhs@J?&uLa@)6qm( zX11cC2pyj;AzzBXXNVGl!Ow7HJr23)#*8!ya^5OJ-zve~eOWOaxk@--;CEEucUB}B z%;`ddz^7}tmoY9&M#}jjorud4ldIV4GB?a9=|g>vlqwXw)^{uVyiD>sRYxP!vR^4F1D>Bx-wYwKaX|l!>K}%K&w5Q zD~ZB`pp}Ipp-xm;VAM(Gm<+9n#uHuE99f}o=p0eFaaob)hc3+5XE%w?a0v5N!1vBz zAcV)X)oKt4GqZGt&lBf}B8>}+Jpbr2B~VSs>djfNtRk2J2fw{ZdO0exasV{{E&EG<$fb!PHEMD(5!r9D~?noWeL5GW72 zf-YkLxu;kNTYcUf@&(T&<5Vm(F6gr5-0CgjS?9tou+9uUh>ax(x#!P5SQ*)r?*3il zj4mx}#oSx;x6nM`=Z(+ZLS^gGsN|e6P*8f<5lQBVWT&j>CbY3I_$B z#T)oL6g<+dqNrN%zA&QO*{H0;H>`!SOoN+|PYjwS!jjS5DE~%6OQVVRY_0M5ehq6+MEq1K0rwxjU zx3#uFXXGp}-t<=}Cc4GF2dDmnVY!>{ zS~=CXzo+20rzqEkQt3$q$eDZPshRUq?zkJ_jaby+u-692q2CA85Ur#G)>GZLb z#a|wfqiQ8gMmeyElq-Hr?)1k(-vMB3XA+H~nw{m{=*3vJW_vmFdbhV>6A?uZ1Or3I zC&cg3Uu$Y^I?>8AO8FRRP3zLV;`(5;kpLy1Cj)=p;$5FWu#&=t=6CKl?r!(*cKZ5L zq>csLO2=4Th;R_^l#YSw@aten(5#ex8BDS_7j`pq}vR8~*#p^V0<)SKu>X=6IR5I%q zlVL#8peLy!m15A*@c=uITk2guG?Csb#wgg_65Peu6@eYU1K8*Bdk2^Xt)bJ)DBX~y z)}69MA%k7fleN1QKML`(Iw1U#(8}5%UEEUO`x1ri!?CHUIyrL*Bodm^o#Rf%%LcF4 zHaLBQM(f&ZU>?tC@O>G^Y@R*$``Vz~^D*;McvE%zSee>&$0yk~Y8Vp>MWAux8t?dR z$*FPX*n*m>+uqI8+HTUkAz1ElEp7VYI^-YsKOz5og=X;5k8A4Wn(wZ|obce*XpduQ z(?7_qosl%r=uTvI2x=BQlb_ju>T z;tFau9$p1|lUE$^PVDb$4z0pR3ZORD%&+ZDEs{M>?ClC0)Mii5H_P;Mjc@gQaARnt z@wpAIsV~i7N!H0POjDnCvrb<)OF4sZ_}u2qk#89|4uUs&o{&B1W;=5-iO)@fi?S66Y~FA{mP2?_tw z-_~rUy0Jeu5HQ0;B=hVZs5xaq31MJqm@csKjyRrVJVVeP715 zxXtk|e9uNtVyA|n4c>5Zb(Jz24=1vCR{j1G@1W7F3OXj?g*w|RhSmrp**T+GVzVEp zT(OE-)nm>04SR?(=B`1qE$JO)^j(dlHK|C6+?+W{6%5Oc@5gA^@zG2qLk~&Gj4K05 zX?%?G`W?i)u;b-e(6dqm+H9FXOF1}hGS>6wB!4LU+@05$_<(uzHu9Eko9NQrLMrfn z6ANM0x9j!MdVP%U)E&K02+O8^-8{V^Sf}VZe83>wqz|8zF%Ax^a)1Jbn+)L@b~a8s zjySWvO$#!3El}gyDnPK(RGE&j{we8zhj|Oeua_IP{27-{RoD-3fomG5h$&%NqgR>S zT%jn^nkk$UJ;eCTDyR{YL)~u?F}hw%a4dBm@-N(0odwI2f$xQqfosde#=ln-?o&eG zDP%`OdYKzt`ZLf`qn;kg!tJlkktOFS)H|2y4I!xlgF!l1qfw36LLXV?8LI1u^SWbJrq0psqeFHbfI)ct}lve9{P@Nh8C9b`F$EbAhLuqfM;y} zt^!%!$T^Q{Y@xR?Lzq$iO>3I|dh3&&E&Zu(=xhV&p7%uxANB@el;^KYn$BG#AM_8z zVX*oZzEOwLQ+=!91Kg&cLFrCk1E47*ojjD?18C9$&=hX_^Ab}Y+s*s}gQrmG|+KZv|&3Wa~6 zs9>=wXD;!)e;q7k<2Be_atMtlG#Pe0-S4lRG|A=-0~gXQv#Z8r{bTW-U6w7KHCsCI zbiD+AMf|Ok@IPucb@u!Q7&0b+r7jjhBrO~?6#Npx)XXu3`?L>2*FI9g6UKL9_ z5^>&omn+`N>hl!jAFpF2=9T+v7DTf5-;vN~*VVn6`Eo^{04HuVS;53g!;^}Ixk z6rmJ}d{!26rhM$$JQE^N#Tbx6RZ?!z?ea5d3^h5JqO@8ZuoV%Xp6FW5_1oPJuN`*5 z9`mp;y=t^qrJ5kr@4w|v^Ow_b{5&t8CH1P5aQEIXhb2#(0NdrZOLOgly_aI_f=^Ez zunP{HD7FJW5b=_IO0Yv!Ratr-4Xsp(iNbFC;I2JJgbU_=eO-c2<&{*;zk%&oj1t=q z#mHhMg);=>b2!60b~_rZ5$rIh^lds_zA0@B-MBsM+((E!-$KtmjX(O}pqe9aqYiVX zDQad`XdUpvhFM=z*r!NKZg}} zHJ3E9+t#bHCBF9GZM`sDZf>#F-`+yIq$1c-c}uFTp}42zuNy z%ngTbbr$gl55KhTqd^Qyj|*FVCAPcS%{{m3x}3H_F+# zk(uRTibQ`V8)8Y5@{w1qK|j|Q!br}=S`nVH8%zA`8tQMzneJE{1p7;b6& zxJrJsYi5nI&AV!dT*yW)APqI2&f{bFXRsfHcZ|{)XP2|WtH4ZD`_S-EHq&6Ak%FJf zH_MMjKF?-L*~mrBe<)|O3z#Pu`pAhgLgz1go(+A8E+0uz*M!>^2+RYQWaX4!i=52H zu@yT)v$D#QtNh?BR%>3?sN1HfBYB^O9e%~|O^`{nIUO4wrf(4+?(Y?T)UeaK+n)74 zw?L4&+iM$J-uSW3_hn4`ZkgfBBhb+^|KzvksJvba*jT8!A*Vl!_M-&Bvu}UXWylV( zM=tWNJ8jP{uvRt0(Pz&NMjE~vws>xw1DSxazCR8+s79!z66<_d&;j*vS({RxA+kl@ z9%A-NC3c~_s>+@ww?`IuBZEWne1Uz_6DZ0KwkgW-E?mfM>ktos?NxboK)Jij?{|bP zkXC#~;_>`E=;OPcbo!@Uxfp=^ZQiu)<_NhaPHD)VubGv?&U)H6YnV8Q+9HYnkW+%2pPC2jae_#YnkqhUg7`auL2=PgLfz(ujp@FaD#WI()Pp_|JTG@a9e(I#~MI66{ zkHdxTKJY~x?e;~q7kZM(w=(?d178G>QGg9ySN6ZY6+-imwKF2E9!6wC+V-r^KSjIk zkqa`XgyrR&&Yb2a_Q82MA4GLP`W%%5Tk*&^O{CO#;Em+uf99nqG}%&h>nhXd9m(df zxxTNJyxk;} z&r8vy$<^yu4Vg_^5JyHG9D`3*M2?|B32J2}6R-&kV+U-^U$W z=yUG-2xk|Z+r!^)TX_pvWTeu)$fF{Uzs#4ZYYUtI(@wUwN}YM^{VyTcJl-Axen9v@ zo83-)I1vOJ_mln6ne-)6k9wDh561u+=-<<3n^%MxC2nSdW{bjT!4xcruE zukAxa1Fo%l9-;Rx#4;xzeMuOw)J}hP;y7y9#h$y$5!<73VWH>4t5^ry+p!Lg6Pl?J z1n|QHm7m$M4tU(5gGah|V@RwIK*ZC~pFef?Xv=27%t-T?E=HNkVrIq;f{=tcX)yMDI*8BW`&VG-B zPs0{lh+i;I<+VHVkM80yLjj*vWT7XUjPndK&f5aKi63WtZ?ov;C__eeX{7cI@2cHDUM43|0+QV&+GM#$3z=t0le8OOtV(Er7u`d|ZG0IbgI;Q4EC zyp4k>{xTf>aXf+J1?V};puca&aR+$k4jlJzbm5?IjmD9=4>$$rLg#T@!Z8hX=Ham7 z818|Ke91@~RLz}>qm-Xonnmr2^wHBC%Odd=;#RSS<)W1dQFQtJpj4Z({)$57PNkKh zc0J8Y1CjB{U^~CFVsFfZiA5|I37P<*0ssa}OmCu;Z@hxtH0TA$>@*UhpI)#dVHn+d zBuFpVo)AZuYp8nLfS2R%bk*C={hm{EX@M$|Dro-pibf?qx}AdolZ-Xppj+3GoYD^HKd@+78#`e0 zC#?a@oWDds0+AX$G5Jgo;=arjSb)!tj|f+nT0DR4fFj*;C3Q-(hXmE@YC5utEQ|o= z<=bXizQ{{A07-PY1YNLLFHAr-yqnw(EB0O;~JoEL5*_Q4%VQ<75xX|x90^*^IKQyLyo?}-I<5d zx~B6;`bxr%J+)1!$N|#}83XShDQjaK)A#3hXLyVq4I5A6(HA>lBW|c8k-+Z{OBk#~ zfMd-li`QLT8|>a3=1LMbue@^9$elKZ;5xifZJvBF)908G`_>_U)4-D-Dv+I|^XHc4 zuxrf&FeJ zgAv0HdwXt-t-r-9YN(Yq8?V&PJvxp%@7XKn-`d}NvB;eMy%-X8nA_m_!mZ8k)mQ&2rD}GR%;# zMd=dw03q8LT_|}B!(Lhi%j^b8jOT7alMVw48xv7UTba(Nk-eQwjn02Ac zyH%T)7wI-5&aBr1m*wsBPj&jeT8~8~sCBFOcB||i2eDiw1 z6^=$3rczmTH%bpZR2NF2JEIEkW-d@gZ%OGVzbf!h9FJ!amRBZ@H2D)2O1#`n~F|@U>Z8a>{5ZgH^%#mcpPK?a3@FjHe3>e$Z2>D~hS)1nP>hs>@QoJa4w0b-!I; z_FBf3<(a-QwQ3aRnaZ?ld6`Hg7HDvy#SoFeLQ^FX6XK=J$?yUP^g_cdN+<2qvND7Wu~Vd&r^N826y=>Ipt$S^ zB*6r;8WonV23=78ay={lF1Uz`Zg4JZZ!eO~mz6(gljf#HJKDdGIEovTnxma>k(^WiYiJ z=CnI=2xZf1Ba5QKvo0<2Ja!j8Xw7AWhq7JpL1)o-8HpF~pBHI6x2SpO<$CGS$Gh6* zfsG(PUtg+!(Zw9|_#Y2v*W30A#ju8mTMu#dwr5{FR}PjKf3>R>LJOt*c2V==m%*PO z>mupu(cOSNbYzhy>Bis)y=Xa`BZxKg4uVw;K=-VQ9Uj+bH5~-j$XgNSA_a`xgJewhupCgGHVqM#70nW z;b_Crg`+R7PYTFdkTG}(BSQDVqUK+InIJrRm76dNQEnp00tr!}^7T)dd-c&Z;SdIv1hmQrOQP(TKnz{>AcOF;6Y*KA7kPdl`-px03)x2u zaya%86MkkFZV%-{P}4p$zPuy>S?7mSxl8*X8>SE)MxR*W4zmVQ&w!WLFX}?9<`-Sc zKU~jRbLW95LgI4q3Ew_Pr&mx}Z`_%YW&nEU#k3Jo(Y{DZw`fuGPSUr%eBa2T!KMh1 zG|KKg5JDVx9uY#y?)?9RkVy55i##jtkl1w3FNjTVuII&&f;(9`i+(MJ)c$hR`-m7) z{wHc0b`}$R;#(xh(Ern)0=jXoD+MO`v+mFK&$7Kz62GzwW+C z_^Fb)et5V^xVPHO(zF=QQY^+eO+G!2v|;@dT>5a7X(FG0@472aX^XNu~k*LXoH0*EPcXM}BIa(8P^)2sCl3)2it_uHd+pH=_vQ~69T*}79YTj=flpsl1LaM;?Yv@)j zrPRLKQvtIIyJcrR^D3_CMG=D{RmzdCP8J^tL>#9RTUaq{VOpN%Y10XdC$jUQOOU!J z(DEB(&`+?+e=hW}#2p#?3&D%V69ottOhSJsi%4yRgWh}lUZK*rIG;9sD@Ga_tbs%t zx$D@GP~vG4T`FEL=4}_smW9p1r0G!)HC+_YStlvOd)qx(^c%h!5CME9L!h858y7a; zzJzLod^G{RfzXHqmM;GrC*DAm`2DcGY{l^rjuQbp%mcdMUfBHeC44)#AEpa^0#pEm{nt z2)277fO1d*GdAZE#8-A9VipP!Hizu~RPbHA6k?7U8u%!j4r|T6OFf@Nv^_4?QLMUnB~#Homs5Fru%PtzBXQCKb?7C;nxzWlWwBm?w)s2GJ;OkkW0&Vb@qa2pN)}#D94o48Ovxj^>gc2tc|X-Nn1JzQ33`!;Nj@?4oT6 zz~}>EE;^WdKw=wmsj8?DD$g8wVSFsm`9-Os*>RnqD|_j4!JVx%)%15O+d?<`!q%%h zz2gqDqJwgfUy*;$HRZ>JUXhR2H+yIoJI-$q^B-`OQpv7Fuq&TMtdOOsC@nP&(XN@( zNIC=ZiI^xBqu|0=q?zMqZV|5EeNQhKvvXJwqd^P!blz1x@q6l5~Yl-8oKfcmc;Prkj0G?7b`n-(-*yNI>6h^#ds)MSuFR2v|RPgBay z75~*0OqMNTc`qvB5ZWTYCehIPP-yH+G1%0vPMnwKmePRVmKh&SuJlz$P-a#N%UVd_>Aq)~{ zcYk;qJP{d%(UhVEWWU0_BJAd|=i<>(oEW6Pa6$9Oq>rhOeoy#TJTG1Jxv3WSfZ2<` z^SpQwd7TfREBp=>5!+MlMTXAMq|RI*d~cgqH9G6t`SL1Yf+$zgQ0TbR$mRlu_@NM7 z*p_>mt!GELj^=%UB+Jx*!|NA*ryh@|N9pV1UJ_KVHGVNaUu;Sk3Izi9>bKq)?>d7W zVR0|s_cD1OH<05*U*CN$1+o}-lHGDO!{1DN@B8uhE&P`J^X_wR(`dyy)82p0cF zvHZgCEwaKyB!=&{h13S6ik(S;pnF@tcA)jD?2t7!Dy@#Vy+R_O+^bZt&-+dnbG24B zag8qes%z}FiHmjetF_9BWlWrYY(L%YcGpa$Nl2N-%1!I$nWh9Y!%btOO=Dy7gG|`0 z<@{ewQ=ZS4CCJGZKgQgPh4s`t6HEfo4vWO$7V!el^B19NPrgW0ZQ`-Cyl{~+%SsCI z{Q)iq$e?Ma%GiFnaPuS2-I?zhejCq?AzS39DecP_gtFq zNg?miko%$A;XUgA^ByHCFwJC55tB?)CO<1S&5T7N@3A|EmYb%$gtI9xlzw0u`w=5I zjcqmlZbhLu;xA&8o0}6o(D>vE*roX-J^|3c1GoUby=CIU;0XT4-x=8zxXs1mQ+nV9 zJr-=QV2=~w>~KPrczQtwQ7Z~zNf$>I^&4o3Siwx{HhxP;tk$E(Zwc3>Qe3+9Zm?m@-wt%cRE*Ea49=gUv_g(`|+4#jRfM@b@+{Od9 zhOshK3H(^e5A$zLi^(j2t*!A0+HwQ0f7tVdy+``Hd6l4J(TAm958`|1ELWoVJM$a z!Z4jO2t#!JxMZ_zyf{LaH{bIFN$v8%1%k^Rz(IAeL~+(}vLa!GW!ijCKVl;K>jh-t zXpSbaU-KnJqHv30z9*7gsJ&1;wK;;6Luj;wbD7x+os26f#0&L80(vLr6zye;_VNr? zn<>o8krm`9v=(6jVC*<46j}o(i-o3e%2_1SrU~6?{G}X4o;EFj=&dC%6~JW~nu&Dk z*MJ{1FTQwm;l)#nFJ7I0(fXRox3!|QY5`p+@YSjnMGsP89U4#6q|6(QFk1vm)DPWvoec1%Y$_}FV#jzgD+t8u{;QjQ5oWrKA|_I6NY1QtX8rya>~Q2n z;@6f8&t}g3k67tdS@j>7d9*@ zB{3K#1aPP)LoWTmhzw)5tAD&QEy#GPfUwOpk0IhnJqgX!PdTDVfaxFmO~_yt=6R2@ z{bf%wm_XC;3(&u}6aAZM!4>s|mpy|o!i=1J0cPZ_UETL@0xjK6=qdQX>6bxIQ-$>^ zi0JC0=U)op@eK0CND8vOIwK)=5EA#>`~?A=0g+vKe1UiiJ>ccptKc*{cjA64!epRZ z3Hjt7Y~zp017rQlfu(ku7YX1WdAt;9C-|72TKfNDGid;=#%_nrDi|QXX8Ho$ z07w7HftjFBJR~Gh=(&krHr+el{0|}oF)d{4%hZ(azM)UHznw>)tgPEXGcDEApfOHp z;geC5Yqk6}J96V!rveU#D`cQ3sscPmj*k2lf1~kNNn(G1f4;gLbuAERMDipZ5gZ~| zKZw19 ziITNZ?wzS%$L?eWpYO!_djqr5B4<^o!yA#KP`p`vp-Q@ai&hyp%4-Jbau6iJGP z4S*+N4%iu#a?S-;ME2l8_~nAl^yLeKi`ZzYJ4xSEij^U2U5-nzUs2>QoRU)*kzK-) z&O{>v`k*pQx(kyyc0bQ!p0pG!$LpC%C;346dY&J#-3f7hh|vpV#AV6svvVM8e_kkx z>feBk8@*#cCo3WPch3eZ|FqoSAUguI7M~Jz7fpCy0}r=b=0UO>2(l!-WC^Kc8O?+j z3Xr`{g^>X_yE0mq_RBRPeA$Xc;28iLif#@yX@zB=hXw!E@1NK;NMCkipUPLNjF& zMH$@ORQbRxb~%%H#7xL`v51museDNP5OBdnQ8ZVC^kc>e!@y}CZx>--O2~JN$hdc& zVJwVCn1T9u=z8c!)~2?NL7A+38op#aH1&#c9c9OrxK9)Kzds4EZPwt?5uJ|N2K2d)DN%^&? z6T=+gX6a*Wd{Sa~0&GQyvik#u*()M>ET!(sGMC8b`TD%I(n)exINUWy&$kogwyz)6 zP!Dey(ya2iPg}x8aE|O8Lbr~heBr{6KB3D~DZl^CL%Tm3OmeBgv(8_j#ZM`E)n|kRKic7~UuP+;*rWkZ)BQ^_A2& z?sGeV3?SdC?eh|3M8s-!2lA~Z{?~k~Qm=^QTP1x&*N|`3K5oB@Bpe!2v#kW!VrKG5 zo>cGRquCw5&bP|v@Td&7jacLj2TljRc>>jAG1j2s6zitg*@Is;tm5}7f+{}O3o`-H)Qiq^evIp(O1s<{gFTT5IVpo z{RMH@Ih1Z4LACS~ezBV_f10{*_=_QQc?#u=q?#?~1H=P^NFLRuf)YQ==TT+J zO-Le++@~M`5AvvLq4&3tkN04x9)sG43D+JgBS};xQOCyZ-!Nck)1OL664kUo64gY$ zh9CJF_9Lv-zdu=l8u%2dEiYq+_Yt6RV_X@At-k|9hWWv$5wr15PWi8e$Xg3G%ZkAQ zd&fII@o+eXo1L8mrZ+p0@Ciy@cmdlf4LT2&`Mb8}1i~_(l%$I5gET5Z56?7GStCmJ z5+bf;%_>+8waEH(gXqsJP~G8yVIW zYhAsiH9xoc`qR9a>%%v6sckLmf>;^IZe~DC3R&}XV-g|rK9Kn|e$DqEQgdrrAVDcl z=7Fe3YEsPcqX|lRDxZX0xu1hW^e369The$U-w$c1l|L;bX{hUZ(ooYxA3+-G2U-cq zbsWe~3L4L*eaZ^ASBC?w0t+)w(`N$3CqE{tnYpm4Dls@!8qi+r$gn*bQm<;7U#bYu z^LeG%8uyuDgrfhh7RJN?MNd-cg*yPt%RZNLpUgiDPK)lR3n0+^nD5=M?FPv+v~Jei zEs|6rFE{B$at*Od!~($$p&Cm#Ur_op><Kc6N_RytG}c}9paX(7PC`QrQp z{4T%4flm8sR<>bFqX4BPWHuk}NS|OEGLeD=A~!2iUP~G`tO0cbOcjlD!Qr<6p8K|W zLU*_>u4}8-?#CS+PV-{R}daY{vdrlwAB+p?F8` zoIdLI1r9+k{OB*pwXU*tEO)Q>I~)8>cb8im!iGAqnQQvf6s8iWsT9h#IN$4j%^~%< z5o0vOWfY=)Ui0W$ojgRsW;`}M#E>w$a9rQIc=9(#Y!JhfoH0S*jU$U>a}@?upD;RA z;LR38O-Kq_CSz5~rare%9lUh)2=|rbM+s;^D|kJnJe}`h+n_fj{tUh05TOmNDAwm@ zAkray2O|}SQOfW0J^jc%1IaxtyvUs@4xyA^=X)YZc|TIVm)N2x<=^u?eMwm)Df^he z_rQ~Y=uy;lGETtCwdO0dO9&xAQISz(2(u~r z4a8lqa337S*P+{ykM%W8h}0V1cpCNC-bX!FyL*?kdXnbF7l1NN6*k}a$uaZjb!1~h zC5g}w5;f@ESL5x8tQiJI?;}|82+9-R)TO>vq^4ZpTZ3b{zZdc0kt2 z^xw#`=O9j=dR3+Dpc1`NFZyXCNuyLOz*>T3fZ6Ae9ZLSxri+$<>{nUjG8wN1(Pk*ca{Uzh&op(u`ariV~#;JQyELg^+GH0mdG#9HJo zmaL}#62i1>{>UrYacgh_x}o7aVBkB$unilS&;bmIBact~7B3;8zkfKmQ`hD-*#vrTN4VY4ke5cSQ-eFF9niXGuPs56@96p^WK@ctY^2DE-f8oCs znoYmLrnj@{SK$IozpT%F#b6%Enn$%_MN2#*eomdzH0Gz~GsF*@Dya|Zp-=K}_B?l|wL!<|mSG#*c3R!F_?`t7k5j9<;JfdpMzmnD^vVLPRy)`8hLt z>q0g$`)01YzTf7I4UkG1c@x%4) zzUbQY>-w;u9%|zN*oBAU7=~jwj#M0@a6F4+3=XUjG`rm;8(itHmo+%RL|P7nZIN12 z{O}b)scqPHIyA@^N?9+G21r-#$3~hEvp8 z$#c_rJ5G{3K;Kr(Q6w)(L~3R%A?5sY3qs(0O{DjnPEK+9RkFb;O8>d`6xm=EU_op> zv1<(yJqEML&U08xJI@ijGVm9>&H>9aIE9VCSlk#K526OYQuBxBrp-S369=at4Z9{R zC*tl!G`;)Xf1HD%>#+wtf3DXa)TiddB1nez;K)HSta5V^QfLLxs#P9zU>A+}T0VuA zwTi5!N?Q5U#g>T4aB2BeEK6@{vy{cIIy3=plDf_xp zgX)C@wa-@TQG_T9ysBkNhHqvZkvn1rtN&btHx3O&?FR(JZnWET3_G^)N#n`S29Nu7 z@@o4`>{}JdKQmly5401-wfr-W#sLdjF(?jU^W1YF^?tAlc!;FblY)Rj3B%ScA1?`v zO;$7+n+YPqcj5@&(TOlOJZS#Cb!^<|%_iH;mjh_-zz%a8Sxt6^Scu(Wn={X)&OCaV zyh4ad5o#pv=0{b5y4BR2hxp=^vk&pb=L7smOB(sxe9_%KcUR14-@|aOCSTmq-N%2EI!y7z(dtbAEhGB3?aQ+Zy6ai4ne>r<(k@b!sJTC>>RLC8eb6Op zKfd|Dcem+KPn(u>^XgqaZIVwC+Mcb{Olr z{))cz(t|*s?w-Zyo*@mv*goL(Hv?_J&CHMiB1Q^7;q#%H6Uvy!k7O&DiTZVvc5K*! zU~PVx?#pFNHGcfUXxqwkzdth|1TJR$&dX6?hV#o#<|u~3Xs!4~Xq~CbYuna1Gc0e{ z8Ylj(U1Rc4c{n++#^mKs>em>v&m8%e0)^22@+Pct{zYNzQ{z9VK%QRkmW$S|S2ROF4M5)H6Tfl53XLg)gfvi>Zl@)D#j&;M3UCCObgnXm3|fmnOyfYk$kp9Gh3z4nWWE|teMS%=W8}o zBZOgKSEJhi8ftQ#MJy$zCC}{i50{NHt4FrAn`NVhL_m9Z&If*v^vlQivtCpM~4eOdidBm;r8nLZG!_ERf!m$e5PbUNM1ENn$Xp zX!D#HmC^djuh8}4N7FFa&6H7gHjj1Z)ADkOQTQ^#S9$^l66u;Y&d#b_rpK*0ejBsQ z@Hw)xgh7BemdE!|J%ST9VS!LI3&lpmaMv4v-nLs1qb729m|NB?|tFyY> zj*b4?{&R-(oaAGavlPp!y7e;K_m7mjCiRqm-CgdWLE)=oSm`$GY4(^vyFW~z?ZUU* zigE405fsDAI+kx{qyBsehyzFjmVxjYw-}drTSC9ET8ZZ>_TCXSg`dSv zhOjhx#b4|hoG_R!A4FBJ^NM}G)?w?P>S&cv3YXKrQPxoB+1}Q)qs?9Cf6r-0t{K^?uA7%Cz`$ZiM~9q<^|lV%D`Gb5-SoMa zztN|Ekse7^zlqlY?=`5SJd7e4lKFh$b#1!3wq%D>6N5E(L(2!jXr@tUP^x4vVtks< zo;kXYODBR>^QklV{F({z7UMO{z6p@MCJakfDfll0dqjPE_qj-nO%jd~-D6ZeTVY+I z1-p>c$5j%&%n@WEy3BG&)m8x8sl*NMcl8B3_icc%GY zWEFu4B}$sNo&nV7IJZTLe(;VH$panNgm%v*ZHG_jB{IL*N3~uBHcIBVap_ATb}^r) z{lle`gS$C@r3YW)Z{=xMKX-=rBJCN=3cg4P7bR$o|3%gw-&ftmEq8nai-9G;!=4eFPt z2ialbNDuMGjY;07V5-+Af0K^6K{fZzpc=s$)|?U%k*ubm6+ zjD$g{MxqQM&Z17lIL%JDhe08ua{V+$Wj|R%-7=S?Tjm-X7z6eDvmTmH*Xf7mb8BEe z&z%1M&L;7>$FXR#D}+ zm*ehnE%=+i|Ndjtci7uhhJ1v16t!QCJ#vwU=`emDA!XP=<{4ZcK9r*T_+5bWKj9oZ zb8{PTo`>`ODE}AEH{tvM&iCNhj$;>&;mt&p8!1!glL=3Qw~4Pt2InSQV=Vo zM}!BnLLpN=Xn0Vn&ZwH3tPlcK)B_&fc&a{#32kxbolE!z00z5|sg{>NPFEK*gO(|a zY9}!~nuX`+2%Kikhg+od50 zD;-h}1E+mrw0&ZX!)Bj|P<#7Cojp=upB`(E6xyfvw+nIGNIN<^aTNM%pZE+S1?>}` zwF~>&CyurYAG1#!<7l=|e9mU(Di7L)w0+_@G{Gf&tje5M=VxLVu`|D{Hi(`02c>q2 zY2sK@ASJB)Y31l8@X6WTdBh`Ei;z-6#%cI4cLgE#(i@uA*dT*M6Y$sN%_T~e2^ipy zf6kY=!jCL3XXHfRD#zvm2+KT@J1%=(3L6P)+!BSVnXEAj8!1qvG-Eo+$~*d=Nh1`P z$I7zyoSPFKnzidEwVGpFx)f8eX<^W<43-sAw+sW^UbpgTt^v9t_L?82cuS~=6IOca znv#s=VkomSec)hpbJrSqXMEO*>0OJ~dkEHoR_0Jbb>HGO8%_o-h zk+fRCDbHSLG|L7jB=b;{=`?0lUEgLu(8WZ}RWoAi?6QJEPG_Zau&OEr(a+dyx)CUG zItc3t7#(8IDW2*1Ya8q@pPnMgMNWc}!W%XJP6Zulv&{6o-3GO@>7!F*=Mv8;Tr729 zhATu7ws|u>XS?uRW8m5uT&q>vW}GT0a#mH=l;i{af*r0DDn*`&^kS~mq0F+iS@WGG zOXua75VjC0)BrqYZS&^@VOj~~CugQ-Wg8$r@16p-%xi+l%(A?VN|6-_A}lY=^eiLS z-aJKi>p=o@TwL0QrWVoRmhm$^i^;{+{DZS_tyDrlak`nFg`~WKFQ1KaG|BRETdg{8 zrb3aRC|NeG25n1{BxdTN7+4E=pTbE?ak-utGpw+lX2a z-An*21%%esMU>sPDxw0G3IfVa1VvZ($GUi@o%j39q@}pO|L*(#`FswYJZI+2IcLu0 zInU+$7#dq#+j{Dj8UmiuqSyrla#+9AP*q`0zk`~2w$RY1qQbO%^rRwhjIGfde%A@i z8qE#(?`^V>$y8My?owyj*vhGUGu2Z+&wTLCQ%#2b)!2z~o7SX6s|jD48%gIp;`&E>^1o;o&7J>*++>O5E5cXhlWLvzCn*5Fdl6Ma&^_f zMxJCd;xz~6;VW}-=8PvTb9!1gGq@F}0?iF|fr4E%2AE2imxN%kQTL9&?YJR*NynN~ zh&Pmeprh_osBV=%{q>GEC_--Wr&svvMmq*SxJxk-jHz4F=9}76R_Q=_Tge=(G1}+s z*;PRu1-SLeJ#EE~GH;vt)VWpK1m9k-m_>tDO@p@G(bMI*0ps@gKYjKd++U(^o2~jN zRpooxD~_kzemWZ{ZoiWP_V~v2tkT8%c6i0HJv1Z!b(RVkBWI22QT%mE^e&q`d^UN- zk<|TPo`sK;P4*=POi2B1=+ z)5|K&W#wj93RP@@*3JIO%RaB%g-pbYblu!jrK}IgTwjSv-ALKIx$X{wd8C;$;y=s7 zU*r@su1#NH5x~kYJhjn+r6Ge=actet6<8M56uPzYqELwMx!8*p0W*8CSKROgTEB19^`!wX#N_cBH#&Uz7l?0=5wBi_ z_-oU>;u3o6Q}`aVFd%#J7cg#Jr8D}RUa^Mq{wU|oqr635w2|P)=yQ0*`zddgoOdts zYW1tMM&F%Yv6ARMnO+V$CbO^n3skX^M!OsdYplNPj$Mf8l|H)7b(8%K=u2{=;^8@XGYPpW@ih{*DHzaIPwPJi9T!C)DZW%q{)r^=X z!|qSZzx_!U1(g$kLR-cVr0r_aOqeu3^j<`#$fsI&QoaKUn6cJ+WSV8mCU- zrH)>tqWuYLVLettA*s!KK7|r@)*svNX3Scp=M$KCv3cec%7DNX%iFM5#1J-W<&f#Kz_8@ZE-~Lw0kjW zVoaooK{)vxT7{ucOATx6TNeC6TJmhm4K+B32<-J7J0{+4<7yOV0DRJb9Qv^kCP;ZD z>5$8e&_gNBI7Ry;`t(!O;N)Tda1o=u<$un!uao!w4rp>hkTMY z{p%*zp=u!{d9~M1HYGa_RM&*+u*DOPHlL2+0Zr`>WpZFY%n|%v03_u}2u^*UNBoMc zdUu_17U0ANfraOjvhbTuVJIV{Xz0i)*E)PZ5^W}g3Tns|21Pozy z77E+1hgtLT^tKPQ(5&J2ZtFLiY5NuNdaCOF z%+P_@a3VVu26~r6vMoliQoVg};CjLriNOaA=vviwE)mY_ek%Z-(h)DK1CkIu^TvzF z2Wo~L&(-koQ;_lQGqker30(SS zRlD*1M$%s6l;s%z?h$MGrA)l@EEa0V88D!~rQt&Iy7w?PK2*l3<6mVMAH|xcRLBr~ zpPh!RpJaa9Tm9@L$d)wXLXs_IoN50TRoyqcEZpJ5VNLC~Le9s4q>M?8XrVD_J=6YG zWK8zAr(Nv-W#T!@FB5Z8enAAtpC*pQ7`=G#r-@GdUVy*3_&W{h9wZvG*=3G@NmjKR zIQKk3yjZS0h(<1Dt*N(ohYuUx95Sa2uUL@k)lRWAm%lo+(DtP?pu#(8iV(+mw*i`r zaFM;6nX+se#gGu&&cV+RnXrw%>bDxM&M1tAj|uw36q9FZDMRQ!^c4PccZ8!?rm2cG zUafNhV5-WCOvAhblT8)ZRljq(VgKnGxLd;>e#tzT=)kb9s_gQM(St;>tOd@_F|bL6 zWho-0c_LAQ-jQIB3UCD&J1T}{uro!qf(X|2(V1tW;v0fx;@=?VWLox+u#@ajqZux- zeu=qZj}(a`3LC$HeDzdP)a!o7Ii5OHo< zOF-!c1Ugzp-2iJ!3Uy>a#H2s;_62O4fU1Z{&8Mg@pbizEJVRKh)5}_x(bJ+1owy8P zlE^mKg1!)!^!5czdC?b(ulB`#|IdA~G08K!Oq@-Ja{O1+A>M_KfVaw|ihUCp&!bf} zy2OZ^3h!A_1xxcK5g!xOb+-=Q9hV;CG25@HNEr6M{b~ZE*_zY@dIwuM6g}7PEeZ6NL1s^0-z!j<;x==Q?+PkilykE; zHKFM9n<~}ex2JiB5?+>RO_Xh*5m!@Z%0Vs?XV9E(e ztc{CY+gJ$?UY&W`d&A`k8Iz`9Ix6$;Kq>a;N#$_t{9vy5V-IHG4?Vy2lC=B^NgfywFFZ9-wPFp$q3nNi4Rs+8Wykk=$ENC(D+}2a`(NXAx^=#sdz*N~yX2ty zLU7Gb|1NRQp5kch_(}@zWcuJpxul5QT`?kKRro%WER)Z@xISLVr|><}C-o#nzYM8c z9}$h?ie=XqJdi`2=`O`8!ns4Y_aXK9t@9Ci<%2c!sT|^RYfUPZ2%0UU4{llTA z_k}|xxj7_STo8x-VQ~X3adX?pqai!+9dvLw?+QPKwwNB(6IN9gWAiRTyKYs>@^iDT zY*9pOQb=`dgs*3RU-1&PZD>yzz-i-Dw)6A)WZDu3Qq;9H1;A$NP-eI%;s%R?}U7T)~J*i5_I?S?vPWwM`?u5^|cdxc9 zyd^UB&pxWQYmhc#+-^X69%&^~0BH@UmHf_FBWa;^*8Slc^1dDl?LAX$6l2 z*sZqiyJEI#Oka&A`Ew;VY|CFitjS8Oe(0RzFm%iu+(bJif0oG~uAV}etQ?&Dg{(nB zRsyshZ%)Gq3&4+XL_kmT=%w!3<@iVf(CpumYG3c0QJ}%7A3wu8f8Q6X^;fwKyJ-?a z)c~ul5aJ;GyeJ$qLb0}i`H-(w8XtpV!YF!|6mF82Z(P6Bd$A*O;rOw~A0H_L_xlSN z--}W>fl-!Ac+x0<$apj?iJPQ_WN$--iv4c7Lby(jE z{tFCa3jC>frBdh&aoF;@ zy7Qrc8NC7`dIGI$sJnolFl{4B0U!cZ0?X6|3_!YdJPdtMFIg?HY=m4*Z;X$&Ep{Xh~ zhae$LHM@pOSgj-F#dKpU`qftB(skDiB>&`yiw_)M)!ntUVF|cIKp^Gfhbzj3;!Od6 zIQr>k_<@XL?=rl>&c`~gm+bQ1fGn#0P11s704dP|@weIH#VgnszPN&YVN+aWeZZRQ zJ9CALrg+r2ww%1u#IF6=H+(i^5D1P+kePB2XR=e-IU6f;Wt3t{qkiCiz&s{m7o=cx z%mdS6{_`f`d;3bG{^XQ~M}d6N7`;~;JfRJyjf3hQ@ibMLdVg}PJL_KV?V|nB{^W$B z%4mT7&P#A-!8SH_O0whiDj{F*sQOgUTo0H%$L=eBbYeU}FJPSqp~R3tU9Xp^o7HiP zv0U2=&5jo8Fj&0^M%J?HihCOkEp(V=^A)8F6~lU>gcJ&mG3rtEy?oXbOQ{zpIi7q* zXU81Kh)-U=ux4-Vu*r85VSIpS!*O|5s z^gVE;Nq@Kgarza!Sn)w@G>CU!iJbGYd(Sp~=e$`bpV8dV zs1qk$X)I}RTv3jAkS#R`S0X$k{HO=Fax4i5=-zWD0#i z`rw0-&0r&EBnyLhXuxLhe3X-)JcU>~`kcV>o?N~%iUe25m<=t-S2{KwgJ#^aa8~#o zim{=#Qm6xX!GWv9O-Xc9B5wMOs2#TK`vrxqiNDz<+>bD#vQtN~EGcus1<&qG@AcUr zPoQ1_6z10s)}p==fP8>6{TK}aBoEtHES!F`#Y!S>znk&LsQDOq*#t4UkErEi)w7^eG=c<@E-|Mr)8;D)|>=%-8 zW9`Hq%27dJB_YC~K})vJS&&Jf?huC5?kA1dj#)5%Ci(wS`8nDA*>|zK_PxF>wLVD8 zC-&U4yrFhCJrkM*TWoq}zw*qV>0O_6*Y3Ic3@sy786~VV>kKk);c66F*I?MH_BH3??&D zDSq?#dj}<~Yh@nW|47cLOf{mZhEvG1v-nNleT%$tgG)<=xFJ|yfYQ7-DzBjJ5jm2Z zjMUsb2@A04+X3bZ|9uqOm0+LdEFx6znTzcHUR3YwvwSGNW?*O03t+qS+B7_s z&oc3GgU#7>7TY3ocC7Vf*X@hUe`u?|ZBbqzcD8=iUj}$?S!9>N2!TOZ6L(|HJ3ULb zu>0PTa&E&S=D2m1xSjUfZIT>wo%Y*i31EcyDm6IKHA!W4-{Bgw5SE5@ur$;gUFKQf zb+E@iFF}miVSVCmvvk;$)5fCK)QFi4qN!KMY7fumG z6%)WX*!-m0PO>(P8DBN5?uR89eLv1r4DcU@Os_+H5~FA1Ip4=+bYT1RGI%LEv2mYp z;}Odo11rHV$N|7+l7j);vH)nhkQs$Bx9AXftOR>c;rm<0{hZdW@8;-+LP7B-*rA)19leKzF*$#EhFs9cPV**?;$`8;Bs{bKD zcK5>sP4qk*d#h^Nz#BIgmM(F>`v!*eM{hIk@U5jK zxE#DhhZqv!su|6(?j2KQQq4P3!-@xhC*m%OaTg`IZjhGk8y`hOuaHT5*;RR_A#@%S ztw!67@K%?|hZxcdfa~Vm0|dsZs`#2o&DD+PxsR7GZ5_k_!0*MuH?}XEiR=9weqCJH z{(TReV=P^v(~!$GS|Fj9U54=jtT^F8%(P{wEEo06nwuSg^Pa6A=Q@RW1TsU2JkKlV z>{)N!7l5H3M(P4p!ynH3WoTT8FGc@(1fvQ;g;an3nS?ms?3v=z^w~dw7FF93`U#)C zq2@fNr@G#Eqo;6Y`;nd|-CjN5Bel(4K+Q3b$W*D&;K1M4S2&NDR_UtSOS|5RFOK4F zai@}mt+VrT_=PCUt=2c~+``#07Y=v%)f2;Frhk$y_+%27@XT-(r*C|-C9njZ)|IN& zqdEOSjekMh_q)96iGDHDo2{$CTTDx=tT)7DzL}Onnx$B)wrpNKlP-YzK@42H_UARBP0R@(9{*a zO@4yra9vLy|LS|Br%8XSer>aFc`0_AYgkyuig|w^sJ0KmJQ<2)MH-GY0_irSbR@#r zPQhQYvCqKYQAjyR+FkP>kQzQYIG?jFSU6NWKG z*>`#hOQ)^L)a}*a@Qai0)~LH;OnWk`+vXNW^}*9U9A>=2mep4`N0)mh~*zPja9yemCtqxQ9c99MEPu|5Sx4NRH+0NL+CSj{DnTET2QeO zqS!~&2s`bVqxL0c$8)_RZF-kZ0G9F+!{jKV?V^MxJHmUj@0mvTK^yn zV&$A$*SFcsU>wUOpZ8(8r_T2;)N!$!lNTcFfnRi|_r41T_gG0`a1Zkx+uTy|({6}( zPIbd{KVox#8s|X<^&(wNxfJe5kmbQ|MJOIcJbW1p26TkIN*jSq%wQAhvJLhuv#o^s zkHpIC;0!h?LM-ef-k8A-i4a|V#G5nNnV3#DH=h29hU#Djqcx~($jnT%Jieu^C5M)~I$G^s{ zuH#?VWf*3tjjq9^;z;5N*3-HB$k%=6@qwk{ZA5>P=ySgU9k4HSgibbJJw*?L!5H2L zuR6;IUprFiRL;ei(=~8{vyU{J9hxe14lV=@wFnnkn)+-yl;fHX<;iASPfw({d}hmB ziPMto^!pO=XENSc+)c96typ2`=tgVI{O*9>ln@hS5odv^gFdv;?E3}508j}MEwO4p zWE~3md*YyFqSg;theG_ej~L}=brIq}`iRkf$U2lezwC)P(nBibERh$nSZojxrrqc} zSR$SyrhRHRI2DCwKNI2E35Cbli$;`|g+~c#OZ% z%*RYv>m6zwM79`A!*g@6TPcq27jh+{!4f`qLXL(!hi~tu7@`o{b%igXLh#pAU$t+q zRd{e)50sMvAG`60Gj8n>-|9JG7+cahO!`~Ug?TC=#0cJ4fgN9TZxkTj{lQ8EW7i@{oB9Z>}bMSCg(7BQhwg!I}BRzoB#nWGgk@-jHW7`)9K)F=Q zSjK-_z?mW_mqjFnUK+tN?hkKma+@64bDDUk=F`t4oHv9N6ZgK%R$?jJDHX??o{Fp9 zw#sW9E7{b)m zFMdZCy{EY*iJ9>i{UgzX0~JfpJs0Kj9LpiSJ=5~8#2tt=n9K9JT4o=6JnEX~Vj|B) z*Do{U!OBnZV8O8sPr0Lk(7nnnSkWv3HNUMye2r9tyJaOQbV2|%e^r7krUr^nS$tKc z$YF{Yw{f7S{J8v_(PJ(VU!v!lWtHM^Jm%XbRnI;SX2}_k_MwN$3e8|P(QEEv#+|^_ z?!&LL!dPx7G9uJ?jpKP*t#Gu!1|i#z(dDy}>)8ky7=jo!1k1r#)Fs#|Fb^J=`DG{O zL2s;W(20PZAG#^t@kE**IGk6cAwcg^oxYb`^`+)P`|#8~oIl3WhM?k4G{VmGUmU zrI_vKHG+tUAdRI(>4!?_Y20iafx&)P38@)EoDZ}Et(0o^?|PE2WjzV&S~eR1f$r3? z*aR}wuBwxMZJb+eDlK+rnNnYi=uK7^i#O696DL7$B0i5j2JfLxfZpU|CVl5B8UydkeC7It$e6Ia(-UB~C-|+gYj-!~&kiSO8lxK*U`6qr znbE}dmzM^NYkzFW?RN(x5yl%YoqrxyFVCs%6wl>htR=5vEonpQKza*lAJTrL_mFlY z?Lm42=@3#pc*?)xFY%`b@b@gz5u}fhjw5}HbP|bp($o0cpC?Vgvq?w^#FJvpda8%p zSFA27cQ|&8UpTJi#+$c0b2}O!X-lTYTDDuTfoN)SOUlN$K((l1f&r ze$sbPTKyob?z0ko(HQwgFr-0NhIp~q>>S>LmetXwmS7~A?DZYVCj`Hbh89)&3Y8i}l+LAK{f+8XI%i`2J9L0Y`T z>^t=da%zg08okO31q(RThs~aiw2;U|wHFE&g(z!XWTMs!1&cyl7g4at^i5tUSQPpb zeI;!1LcyXC*Ypuz@C3lx#w%~}tdy!dKaN!J4|GokYPzEL!ry!0i(J@#FL~qWc!~A0 z`jI15@EcWxBKZvH)k$ZRER=)p@M4mJZ>}(m(HU)RGsJ3|B)O#E1m6svZK33Ek^pTS zS+^C)P4r?V)^nz>uY8#c{Q419OKA3 z%JZg{N7nYSGsJ_$FTN@Bi(YCn0&~*(_ZWEIwa#Ej6?Bg8-4|E>FMROn|D6v$(Z}8P z^A_}~Nm_~4aA|IP;wm?6GReDG6O`QQ#J2eNCK4_40*pP_a=F1PC$ zv@5~~ySj@a#-|Qk&g@UYEUM1o0#A;k+b)!g-&aV_6N$2x@L?&`iCgA32ox7s-q z8_;oG;395|)WtVuVZ)e(=#eIU{f1ca##xG4$$DBj8}&~wqdCx|Z#XOYZeIwLB!8MI zBs$hQTcc-1${-&1+lVal7-W|liyN)2dagr%T~orwce_F?_C9(S<7+BXr1kb$wG9WVt47CV%`saidhjUU!fU zLWF)Dpo(|sic<+NNedhHwI9NbKl)%4M8OI^tYtxw_zG1G|DnD;E46?Hv$91sKU=&- zBI*l+B$;uFRw_;3YXugY#X+m-U()CLvWnX4A!qCoQ|0kW-$Y{C zqEb%$G_f9uY3sS+UHk=4(C|_rwCGEtQK_YqVIUX9Tv}mxBxYPw+aAN7=9aYMt5v(2 z>mjZkvMm(K4!_p}d;|EWgD-8!lU}@2+wzt4;wK5>(4v;bvb1*sNqejMq`jsk*QE{0 zn9MC0=AL|AWy!{sl?y6O^MgiD5`9$c?G^IDU<&qj@v*|aQI#>`cpNcU_;P^6>9~Xe z>vrhsn^tta)g7u|EFcV@uCr+ckRxOWol$uo8_IIvkd^4u(RjnE;KoRtc3{2bgaMsK z29~f0vEOZ8!*h_EnDYwEPE~z{A$KHB7oqhA$Jnz7#nkqo0s1NcoGl-Th;rz}Aflbv zFaI#lXuDL1FO0D>o|w$g34C~D3vLxy+b;{nJLnsYmOpVpWL(lbj*+;8MmO+Itxw9+ zW3^IYLX6e@F6K$1|m%9*J_ECz;zJ z$@hU=DGll!sh7raBiiFnVUz};HBL=9g>01l#=$7QF>DXC)is~X&CSa;Y{+hCsI1S9 zox;~%p3*ucjKF!q_zd+xl$@SE3PPF;x8AlKRUz%h`V0RRX+o&ZE&`K`0;VY88`?!q>zT2q> zkuzV;c?&r{?nF>Ou=l{b<+vi(Po_d?~%Gr)uxjQ;oib z&akaab$5yNYCHkUC}}I=Qkl*Tb9{T;m3CSz32sRF2nh1S#e~c#$8dbn zu}$QIp23bAiWm`pWOCNRAK7=)>9VqTZ+7#V6_7JP&kU{;F~Ei{8`PQr=&*wJ(P$30 zJWvVEPnW!Dp~#5Byby=TafoJ;b1zf2E2Zf51Xt|o(tJg&Rnp+n;|JZmV$@LUkTA4f z7YqEMrNvroqwZ8)`^^#pD0YS3#dhIm$C`@Gt3GG<{hmSmkcJ;mh1OiURBYJr6u@nN z_&cjx&Jd7sd^o#V{PtAymU9UFn8YAXBPuoYw-#KBhYQ+cu&9FY<9%JBqloy}9u7rO z$jA1eWdL+WXLqGUf7`p%E0Fyil|=JoXr=YxgXhF4FORcbbj%z&H1r0YB}1sRh# zf`iLQw#nahyqmpq#>?7_H&q$Jc-Kp(GBnK?=fzsj_EQMtQZ}+4fm~)FuII`EaRIH< zf0fTUCOqqT_LR$4z^VO+8(C?9v7n|oP}%-!&t7$xn21Iru4hF>)>+;C(uEf=fNe95 zXQ&Y~)RgHP%a?DHhcsiUDiF7lO>L8GSNv0COxxTgmdE~0S2a%2Roz?A{#>7~YOwr- z>u5eYI-tcWDN8Ab$hzd{*)hS~>-TMuWIk?5KV8=1o`^2%o&0~(WgXd}wr}6Aw(rJT z*MamF(mtg9Nbeyr;~C}v{B}DEhLK^XbZoE69~BRiPT&3${sawdI)g?`6E>Ki;Lm0Kz&Xy!^cz`! zev;p)_U8}wvoX+K`LpiyvjhBDcllY4H%9rh#`5_$*M6I}DeYyePWCkTX5rehUGjYS z5$Lc76I3c*1?&kslP%f>v*ks)U#6>hl?<}4(8GOnjU@yXO1wGSY#UynIczYO zz&+skd~rT0+dk@EI-`BY=U_7t<^rzkjrpRN=$%AA_ZjGL8-NXF_3Yk8%wQ}tuUcZd zKlha2{HrnBHD%d`vTU>2qp7NpFT5IvJcu$+0|pNoA6%2hC&la--CI}zQhRpgrXjp_G@|lxU~WlCo6AY8uE&F z=o_OIy-B%O0tvd5(`2^EmoS#kDjL3~o&yF1j$wb7BF8X{W8OJVC#zP1vTB7J&=r0g zOOm2l>GhvPyhZShM=^iDLCVCuT=ijO7cr_$*PMmZ{(LYxm8T z%z6LK?_x2&>+g`Xv37>X-xMT_Jshyej%RcV@kdWHp455drmHiKiRW#}^R5lc9Z=VP zhoIarSL^l7byZ6lsf=mf5XXx%U=Ie~JQ5#IeS3vZz<>pk10~y#f3?RMMwj2Vf zPupbOgqUT1^{Al<=C#hDR*NncI3h|3d`B#vwQA`6Qv<(KhR9iW_~Q~#A(Sw@(1K;Y zFxINUOK=bADj>bV_xZgW`euBe+ho491a!Fx5L8A=1I4Yr0}FQ^5CrlrN3>CHnYa-| z2L`SitK>Z&xe|}_zVwyJbEkfEs7$XZe%&zr+t@EdS`qWGyK(l&?+-o*im;*6kPegUhjG zy4x@I@+;zYdXfXe)Rj@)! zv$TA8=F=yR{`2XJ@MiaWq7RZsZo?)iyr~|u12%FEaAXMum21BxxqoD053+&a3t$j9&3jU$K6bfmg*wthuY`wP>VY z700WL5#qu=V!Y)(?5Cs!!acr9iBEua^<@O$ZaC=;MvqWLfDmL`yb_q>zQ79d^<{ua z;X%|NKgH*cUj^hxb!JGl%lq!C@8x$JCQBYSy*vmmE>D7&a-_&2#+$%2+;%_wFL5*Y zWZdH|W;{;0*KqS}lBf%~m=(D|ciHW45fN{*OM=V?;@S}n;IKiw#^gBpnam0r9my^+ zlWeuR;F!~X;xjOUzT=;P5hQ`h5ElZkVKaUo!u$541e>%_zlt^bOfJM+1O08Hml3dv z!U+1(u6}I`jDIwd8gzc606#I7#_{w#GVzND`a(3q{UzLsC=WHb3R4YZ)lEPEZ&r5~!?g~JkLP|I?_ITy%@G*?TUWSnov>o%9SnCK(G}ni zxuwVT>_UP3nJ?#M$GXBjd3}a+(BH8-0W*4}VY1rj*qA3i+k>$&m7KOhYe~M=*aKi0 zG&BS58?2T~#(YkP*Yvs`U!y$spk@p9U`@p>0w}elBI_JngE56`{kZ2*EseK49hOGkkC;)}}>L5#O zxy|Cl9zZ8z=ejVY(|1#MhLv$UHC{NOIdyebbJ35J?3 z>otCX;X_P4^*Zu>5A<+@!w@}v=7ID)P>k<=YYe@H@+KDUAQOV)5DlcPbs}pY%lKB! zj13#=n!};+QM!}lyCcua@?H31OuGnS{jkglzFUq2#q{A#8#c78pB|1*So^Qm0XH!3 zB{Q!l4{rX;2m?5WjsZqyPcm~L`7hhG;rFEQl!Q7yG$liBqGfCzjsY0YU&@TeZh7Zt zn5Ebg$1}eGM^S3K^OK(@-h*+8OKCB#^XR%wl6K(Qjq3~eTY$fH_?JiWZS`5{M(ANG ziIKU&Fh#gtfa~$dOGkPfzpqF?PqgA1ccjBx^oj^t8DheHj=vuK-G#hHq|=DU#qL)# zb+|qYyhY6yKTmw)X~YOYYC_`Hpg(+!>#Gl;GZT01&~8cUwEcEEC-9CKt2!J?=lSWa z?J=?KBpxWrjrOY4zW1ROhgB-et5h{AhTxNaoDR`4mYpV?4G_AGrsdlblPxb*|D29U zZpafU5Be(PwF5sN8SQCfvV{?6&*lF8U{!yl(Zs|5Mt1O*R-_j@#8K>QXB zPHnUT6ln2(O@XfFo%p|J!}y>g=LOHhU7RMk^DOrTM1Tsffdg%X(I~>A=iOJaWZgMO zwp0xXgDn3jU{H#;Kqb6&Wy!j49KqwB^51~6RO2g@$jIS|<5lw3x(V8wLd6Vk(vlcRaHyNYqGc1 zKn(QyPi0HY9vjthna`l#kbs#-*ZRb7Roncu7O+ z)Br}0weWA4t@bjkLv=_Yq$uL`J&fbME^fdM4OXBnT^~SaC(1gzO!mdNf#~hsI78jj z-hd-dmk3`(LWj1@@n*=P#$q;oG|hvtByKBaZBy^XZ&yYE_M%>DCj0b*^!?mCV>=^J z_G#&w?DPrRqAbbs#)q{Ra9bstS41V2urA>%%kINO(wZY70bPfwq<+5-{p$Do^xK=t zn1@msd^uzJU5{%WZLb+_8aWwm6~a)@2!7*P(qnt;7^fXv=zITI-mfNZK4V#vzmyFR zQ)A{n5_596FLaOSCv%NFnS~c?u~ya$qB&cWPvh8|uk-GU|NBVrpcRw0*B zrJ74ZYdo#T6f@8gwX3$r_L>J-z;xK{sr^pAay>8EqP_BEChI<;l0Rb#KBKLshf4w- za%ok1W_iY>ZQQ&Ji1fa_`=I9Pc;C=+Uk^TBNVnzrz5zeKhsN}BWlZOKcmC|1)HmKe za$djjK52(Lq<1?QH^zHuk2|$*ylZK^&*+iIdn1ka6(=L(UDj7c#_|oQY43PX?00=* zziU|!61jh3FCF7Oy5IHq-fN6^nk$jYuKmK@!kgLi50s4(5F~c_8(H97le(C8`UDL+Z`dAN27) zF&DycL)NRpU>x;qP*C{=FljRH69%WlUI?C6lha{ z!Afur<`@etVGKyThMFImBaW(eYt;XX?bxVuiwe|iSKsAJ_fg8KpfDD6YxBeDEKRiOd^2t^5t%80;g#1H4@+3i@ z9U*_)k33n>=S0Z==tp)s{{9JEz58tBYF?{XYEILzCXe9+TmYQ&KdDt4;L`v!yj;(=CJZg@xc472{*~V0G}bg^L}pz)Tg1)Teu0b z3vpp*a~K*|3SX(fY$P%5m7P~(UIKW7axa(thSo-JDdSCKYTw6_wM$-Iy(L6w-7fu# zQv3R^2>7Wi(QY`mC&I-3_lmJQ3MEPs<2kfQp#=2>L@Z^Brs({7hvSI@mvE%5qadlc z$aT79xeHmrKYG9&3g$ZLZv3h{;Cg7A{40oGwFmk#e99$JmmbRvIF=s+CnR?nB>O8_ z#`W;AQa1__3K>`YVXrn(&?dMpp23lRhR*1^?QYRYc8ZhX$J>7X3}jip^JnS`V0mNo z4ZB;+A~wcOY|Pm+Sl@A^$5G#Mw>W|5Ll*g@MbkC;MY3%T$Mi;R^bYApcSZ?iX7aQ@B_|^v@bc;mP)TVg}!u}+H%Y# zuaJsOs3mK*0B>sOG~&%R$85qHlo0@6WDac1Nh0L%lhVzyUT+Fy1Gi&6sO##}*?30H zmWB9+G4ZLkjM+o9cvyK3uc}GLe3Kx?2RKz^rp;q)U-mfF!{Xx{ho(W0ievK^Jz&5) z#A@(UCcnY)x4BEZTratn{5ig{-cdhw z#b1)NLz5m^Ih<)azT4`1Y3{BKZS613CCT+0`p~m&xTvo$OJ7kh2l#5)dI+O!&+d-F z%)%G~iaZd@<%SNJafvE-%w$^lA9k&u*K)A4zTxto-Jk`rJpAcU>rSROwC=9;vGCo8 z^DHvM#Y|2t9sHh*geG~4?(83$WcS5@weGRDI^Vsa04;_PAj0+C_r$dJt>7WL#JRFP zTe0@jW(nE~cdBWxdPfut2(N=ERm)@uLPUn+p9a5koNb?#<>hOzyJhI)?*{OW4`-q0 z|06_X4!#@KeaHS;pcWE}aV{#a9Pe6L4f6)+6u2;OO;kIWH% zE1M&%$3AN^-eAji6|4J{EPhs)9RpYJ;%VY}nkLl>e-y?r4YOG#JOzojt+X<(^iIDv z(anIVk2c*6T{_}cS<@d!?;pAq1Fm&2k&6lto9400AMLe zePznkbNZmh;1{xF{TlCUnAM8$yPgD^VLS+!uV;Z-UoVH;rY~8E>5|~qCZZ7ECx`R$ zMkxw1pZEK^LbKbgFYYSIxX>W&J1JR)O#_;=m0ZNYFMUYVv&muN~2ey;49rv0|00Y!+r>hSor_p#08#~@(>fsSR^xQPtxx5`L4k$v(Sj>?cR=8U|bQr5kXNKID`i5lt=mXDn zM>9TD3F>Bh_N7bNhWqaI!h{46kZCGU)X^~OxG&oakXj zbE2>cZRFL>jw?rSptbY$kehl`MiF`A=Yy#T6CTg}FyF9L7gjefo!@?;doSA+=AbYi%>O;RLO82qRb(a{ zrDztw(9{S*4V$%<&tM&EpQ#wc?h7$F|G?y{VGAzJGi+Qk?%}pohBZrk?0a=f>T3W2 zFkj2X=$>X6#E_b2xNoHAmv>=qb>BRA1bFH{06@S>KU4kj37_rZ}2kEEF9 z-&`F!xcou*%+}Rq53OW#mNCBdbHeXg$go(K)Nt%O8a_`*ThEbhRX2ERhGK>Y3&a3C z@szPy6f;;D#4OY)`#d+)_s|Dr3NHlQK)p)PGo;$T*X;P~iE96$+$?mPw*Wb60j?TK z&h8Ue$yz}#+XCQ$mrsBTyqdE~fV+nBz)Sre*doATLm_YNM}EQa;t3d%^*-+K;??x! zWjBmS9awvf>}x@I`{h%`LNaFf9gG>s*CGmF-VA&#AdUdO6C^;&Gmbr7)zjz8MftXM zK>`oa0m_VIx<9C0@^7zk{P8HK>#N7ak8-n6vQn(?hW!v2;2O`@z-b!Z&y^oUvi3dH zKg+TD-M%6oeK&U`y)a+46U@RSqD}2dc4ZY~d2FVDb>TnGrF|7xPvO1^b1B==PCyA| zU_sI)dS-nGBhE6}h%>WmY(_>3V#ti4#cz)Z3)D(!Uom6rIaEFR2ze_W5PvR#sni9tnqj>c);a2blMQpW7fp#CZYpf}Ds}Ct-IW!X zZ(pjqL^XsD7ggRtjBn_X2%aS1U)40Z0{RC2NY#={^-Jnu-*>hwe$^mj^)I>NR|HiV zFMp<3e5MA&5iSZ0;ScvE_%7$RU*C<8_ff!LP&appaq9`GwNW*A3E&qdpD;arFjI4N z;T?zI7+$R*L&(BTF~-E(#?SPl^JFd2QncI7REpRp$|}?0xkDX}J>rc+J6NweAw!+$ z6-=H-JJDr?it3K`aNk|+yG)=3P}ddZe!b;R2~lAz2XosA$7@HF)k_8=w5jdWK!*5E zu6U935@!kDD;%UXR{5OJ4JXW;_FvXm+n=UeIC zj#Atygrvy{W=C6T7++krZfVqzso1r#Fg37q{?Mt#j&meRNHMp*nT<`SOI`5wjaIFo zwnOJCco@I|^Snh|)zq@=_@|=}{qwF*I}eiF`odtzQghky)?6CPtlddNXbQ{6-R#&V zF}z_i8}D%Did$vbtmg{JW?v-PtfTz0Zas;xlpSOjrBY+{aX62*+ktryO)a3q`+#GhO-1xR9I4S#Yr?4 z?~<|RtZ0H9KF@Yjt~ikZ5XM}A8U3bPF|K=g^@QAB8>`!{L~N`o^}Jq?ZLD-jOv4ZN zT(z;n4lPJ4VRx!`fN=yazY^i@)f|qhSlHW z7R4HC)%X3au>_rLjTL)^9NjUwHq8acbgiHJ(o<^tWt{Whf=qz)F)t`QqHSU+*r}j8 zf$(7OD!mpAuhq)@o5_+X7!@??z+*#wdnM8Lw|g4M77G?iGSpCfYxMQ>YO7y}_jS*b zJwwv;zALj(@ie^-elCpf3}7fpAh%mzU%xrDEw-O`4`d>P4nr^@uHM&=+Ngi$%O6&r zm#|j6F-1H~>_w++tJI0_<5wrp(hrQ->q_MabSODfHf>xD=>NLa>HQ3~;y;mX>wRn~ zCR86ztWY~8PYq}S`;5CZ2K*3Cljjru_B>Q>)B0C!;muJfn!kG5i;Ov6SIO1- zC;L7q0h;xHz_|q@{Dwaqy7|IS5|`;0##=sh!d>GLyktZZ`EC%}7|(L6jKeI`?%|T? zxCq}c!n{#<;JH)8#ni9|Wj$N`HSO9}GUETDU0yg+ncPOl<`TCi;SmijJOh&*VhNWM zai=aKmKUK4zR#;j$D)XHHh#PBg+6us&=fW&o$<1F6zQTIBYtvK&bp-nnNzf`3K~@S zd(%BaDwyF3v~V;#%Qia#n(%unzjYegR;dYxVD!sliv@tsmr|uRtiB%6!@NTWf>f;a zhSXkRJRG%QlcR+_RTIwD5vwxwN+aW2H^=eR9IA_zu}_M1{q`Ol+VNAUqyg;IKfRb% zsbalj?Hqd{s$EYvrT(nkB>O`+^Hyktwr+FHO9bWS$ii+mzgYNJFCKD;RS6R8&`TucC4--DLOl zv2PBL|F3R(mVfgPE`~rCN9F>NUWOX&`J0e-WBw;&QM56qL z{8!%F2A3Rac65I1@7VD~=g7AD?6B|Sp7%z+#{)tQ zH}S>nCBzv^PQLWIL2wS>;&E)^i#1_qOsi8TIJu3ZE#bSv`s*X(G!x@-CH7^$SPyMv z*m(o$5MUi~P|9yc@G0y$aLNG<5nkT-nIqQQ$@U(pdgs%JGa(p7&Tf6f;hAf{ofZ>% zogR6Onzl9i#!KOyf5SROKlIb&=7NScI<~_r6@j?8yELZqXh-ecT#Rgl=uRDl!Q@V% z1Frk1)}1;6KP!?(RTAUn{YdK_I^3<+Vf+O&UiARLU52)_Ay8gf={av4i-D+TLNBz{ ztx4Xe!gA?W$M~Pr@L33O0*gJ%GRd7qO)Q3;x!(41cU|3P=vA12AzhX?tVX0uE~#$< zqk(+(_0&veKgohUBp3E{6r%vB8*~Zi?sW5J%*Q%DnWp64Vs21mO3sK($sh9M=plJC zs7Ej*U!14S04@-32C(_K3=|-2J~!siLu?nN_TSL=->tUgJIVcN*RIW(^SZ>-*ek2O zJ;F25cSQ|brxBvAJFpoIA+yNq0NDmJ*8z~uwMJbJ9%F!T1R%L1UL#zmyJL2%)_z=( zG*W^3h42zQq^)&wc(X2`MmS5w6QL`lL3arVr%?*T+yQ*9+Ny>nO3S&~yX=cA;%{@j zFgd)%wiGH7sb*L@cQ=@2+=;Hl6KcAhPr;GjhB1^Ag-+@q_> z!v2$LyB-PS0|OyVWf@lt6Aiagz`zvCm|||#HN%3ff#|6JxbQNrk0x8SVV=VNT0ZZ7 zB&HJ9=0z{u;LDjjEe4ErZz#xyGu@BJY>hU>T(?&@jazLfA8;sOu$+~GZ+)NEXgMP- z2&63~`|?D~ruV(hK|DLe>rCLCroSie3L9XPYaSjl^C7-C+On$@3Z6z)n$;5bB_dT| z{!bl>+6Tkc!?s?>Z{y7sb+?SH8=8!k@Wn~^Sll>Zn zxk((e6h5H~OO2qAe=P zznQ@u`&r9^9!2!o$hPg;TwlMX-h8?4_2jzs$*VYR2e`7=&8t`~-^7^Ln|*V7Ig6?4uA$*FBmJ$3Z-T zHd6oSSNt+@C-~Pdk^0-QYKs+})sNCbot5>)y)UqL*;67Kt6}fT8Y_1kYm<m)>j_kVxqJt*hbv_5@F&t=H z=zoantFU*uj=U@1Pr8-8?|HR13)-7p4JRVHm18;Z^@eU`Bk5KiKY{ywkDrL>RyuRU z^&}jAigYNePheS6bSv-Wh_Eoyw>&}g_+y}>eZCbZex+MkdBXKrf8EN86FjT_|F2u2 za}U;GSCwRIm}=EJ!$s@#e%9q>?^?TbXhPypYvMi_>;u8e<%C&xlye>Sp&^=;v2YTR zDG`ueztJQZ2icu%zICO!&2_nO23GFhjQcqaY%P7k9Pt9RZwA`eA$FjBe&e9Biu%>z zwv~o86~2$&%7cY2YMII`zm8@ScSwfEH_5SW*_T(C^L9R5wA@PI8!yNw)iQVDyDUY=#Ut zbOA(8ok4i8mobmd|6M*CIbJWisxx@4h)am9`!e7W)|~%EXD~!6Ru3C!bQR_RR2JhP zNyf^(jB${uHvNkWt0xxoy!tNE2bAXUOw%Txeg8tPu6dr>U*Fbf*br#2?}r#|Lt|?q z=_nCFh?|9l2>sGp!nBO`HjLR1JMj&T5giOnvd8R4#DiW1O=vxII{WlYUun1Rr?YjS zKAgZbCgUMQd)fVF{j+WL4fiUCN--;| znVr2d8kPkV!DM&1Er?`yQCSd_Sr%N-r0jx-B|6 zcNd9BB*D$iMEMj@5pQWiK239Njjhe4X={wRtNh+)W-Wd%aAxMrInQ~{ z^E~Idg5|9ssRa?WK8C}&aO?yzzOaU{>BYDIrr>OngaaaRm)=*Uavv#$3@a+iM5D~t zEb@)mmU`Hmp(f`O0f}3L)1OX9!YA*YM34sJLJTh#jr;-@7A}HhKwSoyNoULPBIK0; zEFTeJc{_n+Soe2}A+C~3o`NQ^i+r~f8nLgC*lc27A-S^QLmalT+*eC%EDzIWWu;A~ z6*(_&mRH^wchn%o9f21}2Fnsz)B3AcIwu~e{r5stfrm&Uq(=8nZ3n@obQY6dv&qqokgK& zY>N@+RZF5mw72{hwxNaT7yK_H9vImh;8Ef1eK~AzA#fgz0U|WvTSK{;gX#X)X!mB2Pnl{y2{465>nPuce-+&r?ZC=@T{G5&JHNW|5)j&rq|3IE?npm@OB5xkjN@#Aoq`1h&7 z=OQb=(FCer?tcOi=Yfis&z4>4|0PkE^(;t9H;f{5JgX?^-_oh}?~nAQDMr4yf@lvh zs{Jc$aBoM4t~506K$!-$ua>HNU0IiM3+k7a_3B5DESNZwdu}9}1c)oB;dR=^&9(JM zE^O8|u7d&1(?`L`L40oGivnv1Z9hx$_v{s2;cJdi^{_>GW>HO=D)=RUoqL5E?<*Ut zBDq*ViId+e@5p?)nIS7k@+xP%46VL@vzS+Tmaa)z6u6C8UQSE7T-7+HyqEAY76VX( zPJhxqa!l0ef7u8SPXxFR10rys)Bgw2>Br6}u(uBLKX<*<)vuNEc>fhKsJYW9TwBI9_ zwe#Q^g4tc#>ebEI-tRNT4yx(dGY6hM6W{*;Qfo)Q)OBta<%^?$-oaV$*vj`mX+Mj! z4egh<&$$IHO+T}bR9xAjM00bh0)zL}swV3@h1bGCQgMc_I>zBP7J0^GjvCzsI7g@TLM_=SD){92J;*z6-JDB>l4 z@i&4KVk~5yJ@9j)pQlCh@~33&e_mmcTv~Ka!y^^{Nwr1^x)Q^M%MR9xEUGyJ#Id)J zaTPAYRQM@64WlihTksov05 z$n79(f&}vm|9jcU##-Xd|2Q77|HC8G`(y@UQ~vsk_VxwDsLYgsw+32Q6?nCN{`A;|R@4~>yO_2+FA5u-NlN8~7G^;UL`>va z)=!8P$l;e~W3nyGqHuGi(NqpoXSZedEp%`>iRUSW=y01&1= zW*!@>Jw7ImP_l~yxiJRwyi>Kq|0mr17mOG;!*`Ax-}$S{Ap3zIlf+}nsh9HA$^k%7|&@Q}wJbY`x53avOQp+q=%plCmh;q17c=+p2 zrtz>hKb&LEUl{n5)E-(Z+JmaFoY&-&Ps+KU{(Jd^=zLa+w#JV89Bx@*r`oGRM54!N zIGDrjfKZ9UEO}5G78uljh+qOGPNg|AJo_9rTH)mMp5S?v< ziKM)URq-J5ybs9kTyg%Nr#o_wBPc(pzPatuAd+HyJQ-k4wEYmpMa9&b!E!YSP=f-g z+4?u%pV+LvrK9+hjT;w~Y(wSG=1P^VaDlCGp{;O{tuWhGXtfpQ)XC*_h52UFOCHlx9dbuYpME;Uw7bLBC&s~vp>MKcg!)hxReqkvgzQ_7YP+*KG{-JvX}cc< zDh6?0S$LeBSIA#5Ib!YiVflIoYA_UOwrur=sLIQ%xoQq&CmPw;E^xZ%smk*_ED zDtX73lZMYHFP^>yDs?U*=%4(%`dw~RP3_zoQq%q?*M!u};;t{cKCW+^igw}56~Uk@jjwvC-HuLM)atfX}_!;{@w6JHGSc9JM=~V z{c9F??fUhf2D`QXu3`T^zrWw_drHv_uL!oie~oVjJnGofIb2-ja*9)rO-}k8TzE|a zzmx19w{fzOn+3vN{Lmng1cZcVRFyXBq_!yGfz+KA`dKvHPyIX{7l$|9 z5jU)b+h7|Z1#l(;zUe%KVI2}Cxa#WBms*r2?= zZlVUx7Pvr!=F_4rb)0$@~v`{g6sWl`?eg@9No2+8ECMtevu zPxtY=Fw~YTz55+x#tSoLAG5^#)?|0LM@On{2hvWEu(l2X1q39?hx80C-(L(Z3>4X- zEhF~5-%WCd#Co%&BB#>&4558s;dxAltEtQ>qx@2gQzX`GD9`4L*WTEt{Pvyyf&w zYy71}VBybj-5Rv9!aobRd0t}Nf;B7y^=%>mcn<`|(nQYdDTRH}V>F}5ab3*-n=By> zf;c?Dt%eaNarTmI5sajPy1oV+Bgx$VM?mNz;M*X=m)OcIvMKluI{%Xlv$HYzYCA7 zpL;336aIj31wE;nQ0>w?&aEhF4`@~l}W5VwvE_<(fKMBk^+240V zU`HG(IYCLId(63^%3xhAEzpcatiwYC4#cfyCkNPRtbwzxGFuCcLyOmuXE# z;k5DEV@0K2hoWv|9+ax<>AI2h^;*#%ED8G6(1?C?BxzzK^4!Rriz7yY;@c+c`Y2m8 z1=)Tb8(sUmf&dbLqsHPJh(D7PoeAPQw~{zzPb}5oCJGa~zNS_vs2>(KPr6?$_n|6I zJskOeVMA#xd*bUyDO+%UX-RxN{=f}xb}@7+s=!+(;Dm`4?$aE2;{;Y}u&W)4qP=mk z9if6iJqfoxB$fbwz3mgC`}yR(<0m+^s8cZ<=r0&~_G(|mg#S$_Rb>4`>b6h1Uj6Vr zqE~sZ^Hsnmz6;8gl2@?aM12@{i}|y%j0nlyyifKWZ}To$?3M_Z^ejpRd{p+IN(DHn zf{fd&a&Dd_Q7ZgFY|PlZ`5NqABs1am|E)jSa5^+H2j}meUj1oXxx&#gAS?NyjhN~39%-Y~ZH2tePuU77(GiAJz>?188R*h*Ka$JmFyYPeD5<<@I8ru66IaMv zODL*g#C&IYP0=v4!H#p;3!oJq&NWuD72bv*}ptxxsR+Y2~nKq2H9{H!WBLUtDuq(Nz9al)SX%jipmI3AWLR=10qj ziSWA6lke(r&3#Kle&Qn!(umSu6aFMZ8(AEH^KGSxB*eFrg!oF;!cYwcIt&q3nb*Tg zmu1-^Z^8zdHO1Zls}>l# zML8tDSF?f}P@ZQ`2To7E_p>AWb_GsN=03k~GnXD*78jh!uihS978|HI=4~S9)(yc` z>Oi*(zCQc11CN~?QUzD3g2#;?I(t7j)mh80icfprsP;DH9EVJ26?k56TQ$eBEPp_S zU|VRSo}1jjTB0a#lcaS>_yv>>7&yX@Eh?Iafmuq|zkkx!M_jSQb{P)~v(&zKx>3%< zzAQD0Zm{G@n&#=L9)2_RBu&ydhMyGE6Nyiz z6A;1Ex)g*S%6Ng~-TCBI@NX#}k7a3~mb5P>?OT6`b~t^ZFoAnIF(c?8(k95lBl-AM zx9}Gf;~G#(=4CLXa6TSfNcnhhF`bWRDs)e<;@CojTPU@S^7W}MOqoEa4ah%wd|Pfq2rc6Wc2lgpSCU24YFuOtI|_^6Y3})%U=CY?=h(IkNJ5 zaG3meo+BpdypLK|4%y+mg>_3%Rw{|mK!}HfZ|j3k5cvk8jw+9bQz`2yrNdE#H~2ly zz0d-gxC_6#k!MVSeS3EjXgB^hr6Q*oey7#!bRE(^1InqhqusAB30%Z?r~~9*oGE^d zPsL6|xc@oH5f?<*T+RQOgp%mM+oZNN6m&smvHNe6{ZIN3A)%oTq>K@TNI*cXT!h@= z=q|m~$-@;MdiVCeO*pFuk^J0$A7ZM54uo6Ff=pf~?|Gu&o!nqlLMSTU-*yhccZ6~E zd!Ly7bzrX(#^wK=^!G)!9Ng5>a$m57@{}WvCcGY0h#M2yApUIgNLY>S__e&5IB zC8)T&`}cTwjVU)0r{I>X7WeHgNZ*z^#%vz-j#^|%oxEUog5w7Yo$XerX(O{= zQnj=VFHl!#*Tm#RhJq#7-kA5D(`S#czMyQ7?Wy=ciuIlw#-D4jm56CSu2_(zmQ-X;7#$L~x0zQ*q|epm5xU|*EtcM;bgFjKHo9W9v&1b#POh)5VmDs6#En%xmesU5e}#rHF%&uassolHq~Ft+ zYyWK9qGRg%Lf!FR*_R!}$^M;js}QVE^);=2sB1yjQNAMHV$Zu+kLH=x6TL)z6ju$5H)L`w2cJn!?0MCW5pP59Qt*GXNGcGO&9}tTL zl8dLLoNz?#nXn`5ih)!FGMbl(l?i=%!xNKY)(anLmhm#NG9jA9{KR4-5|Xi`-XIzu zM;3V+-w@k-cv&#qE>=Af8+&=#o^azY$Y{`Lkv(DX5$=dTEf~}IRrB~&-xNPKh#B}? zXn%NW^gF^V3P(`s6)>=lj$v@b8(Jc3zTI;EsMOL?`4UvQxDJ2usjDIj46{qKlpqC4 z4>2``3m0MvK63G2lcKX4VV3yQxna`sH06sSienRki9|0uA6&&Cadf~DjHFlj=62*c zFfE*xI+C5Kub8s5yef$luDmxta3^ulX}bzXt;AP|qZa>4i%SfGug?!u5+a3ECsiZ2 zyfV#4Yw`n5(!Q0n_n$#~S(eC0Z<}I#;G@g0@lj+Di#|{ID621HYgTGoR!;FzTu6Kr z7t?$cMJ#wNJOrJoQ{AmZ-TM2J!hE#bm}If%rtJ}VX?}iS4Uw}~i*ojH!XFi_5R|j? z19y@3aw&@=x?$_N}AsO{6=?Z&hLfSKr>nCKi1>5RfE!Cxc1^Abv zZy6O@x&@1!ZSnlxWEDp3l+R9k>`cfmv)JW_e{VLeK4gPDo@@88G7HO)9E98S7M8ZL zqTsmzq+TnGPNEDSD7eWX0HJ|>a-frUkm-Wb|iF{?zyb!|+ zvAi$~Mr)T4Wf8PCL8o`=k!CQxp-w@in;(9!u8>M!;wnT4bG_%U=V)qzrblTeM9YFS z>!am7t++@lSE%Y}bu+E1StY=i1b*}`LFJ+~7CNGAvjCgc#(I67puVtDYg#ANtkl=h z+=U-%U4jPBiw{S*=oxqn4~tK7b@WU;T!TLm#V*Wn(KlUpm)ttLPJlC|OStL6KeVr) zlYi(hysI6?#k=|or?rE)IITx)g&!A(^a!&2pan46336SX-fGPnsJ(ED2z6k*Tabn#_ zWVK_*BH$emDc);ZV6xb#2)^EXUgYDWGTH+~I;{mhww%^`WK`f5Bo4ua3@Fp$rE%?k zu*$zXaz}_{*I7Y)`AFc? z;NWL94flZ*)D_C$Wl^W3alEU^(o_G?p8d0af=)U`Hq(wQ|5K6ezm7ywY16w}PI0b~ z^7jcyErj`#YU+jb4v$aleiizV=nwequdbs%T_XKy)3?d?KV(j<5@}Vg7tdNitMKfjdYpF& ztwIcTmlos##3u)`3mqC&nP*i&eQ7$>!tG!C>B-%(q0uOF6loezO+0A z3XSca7E|9Qi+F%A`R_AX@3S~$mQfa+;3UzW)l%TyG$eP?CR-upDWnTp?kjNIx0?18 zLd_;aFuIFgu=B}>gHU#M_jCKtzkf1z5go+H|FQ~Arv8?i=k+Mq``44^KdrKjB0W54 zXtNzD283oiZ5y3~h6WlV%*7G%Qk|X3wSZ_~K>+WLgxksZcz+)w8j&tRFzncWTlEg* znz_8Xg0d%42+awy;7Hg%nw)onyec|^SFlJIiZIh?e)NihhQCwvVwj1eJ>!u<6>Cwu zP)CAFOib-(;a5yi7IiuP+8bmn4lzj-zU#hv8@}7Z`v+tp#3aFET)LSF?2x|Cclm2vMF+Lwb=}+rL zYJhD<851MA>X{))=Kf3gJ1}I8eqWk1OxPtHOdxCsvfQD_P$}#2E4Sal62GfW5#D;$ z#fUDD8LBNY!XA+o(O<8ckotP{HY`|6mLH}ok6+~}L}-pU`15}~89WlrMP%HGHNZE1 z!~v&kse<84!V|dzoRNiigsihCh?eF-jp4Vq1ySI!WdUMI5T$Z)oKEdzie2FRs7TRT zL!~NJ9>tz|2h(~N+EQ0Bt#?qK6xOGsc^%U{Zro*A64J>hbcz@97asWP2dRs1eSUpL zNXNc+ZgS$(=h%LKSTc7)Cp#CwMYR5$f8q>%_C3SkA^+KTJ)paPcv2No^v(Jk(`uzc zvr7jL2)h>NzF^7r%~l&)Z`Y>I_#Z|(*F`BxUY5io9N_awe zX6dL7$_L~vnot=h(3_DB`Y{1Gpy}{eW%bvhABP{cD6oc2i*m7h_*AN3j~+{ig~T)d z_66L<+S_hRjoZTOs2bjHWPDMX?d@#k2?bWMk%60eYBdAL4X=`m8a3MVFX_R(&;@)x z&vKLpFM0i574PUV{Cp{pfdEj+^NYx6lq2sUg100XV1|e-u~>l(grGVN8E$a&^m??B zG`*M}Y!DH`Prf}JK%?d!|G0ur$kglEO8zo%7xQ3q%i1p)m1Ht};Q-sECG#l5Jnq$W zYEvJJG~By%Kn;htQzBTfzf&7$85+d4yeGvrfNkpAWR8j)%3ZUxph_^R2rRMwZM4{{ zm&TJrs!nZv?hFHan|{B)PZQLvSAI0Z7%`w0yW&o@evGp)96;Vuz42BXJ)lOhz)mF> zKlnVnTx5wc# z>=_p45}OV}A67Mo=SZDN(5t3ra{xLc&J_S3_c;Mx%K~7-;~K-wOV@QWV~G?uKItK>u@&6znwR_sBPJsD9Dn>ro`~LM4O$1o-6LA9NZuR;{9`EP`p7NHk+p~oBcE5vdw(k-2xm(!2R7|m1hr@QwI)8&=>t>Il7wKb~i+ddl(369~7sY-p z++uD6S~yiJI_Kb#I;um&pvpKpARi)*tpss!2l#-Y!$JHf;-usfZoB+9k!jT|7}6L% zT*94M^-HF>fWn-{mXJ9)J64o6;`2Vmo|H6ZEC=CSTVhAwQIBS*L$x%p`XCm3wYxE= zPc@ou8(?kzD+LIpU~$J*yvL@aYuDu1R?#+um?~|n7@JOJTP1`2#kPvI>EyOma+^+J zTcvO)B&!Gk!;G>DzeE0JbSrfIh&aU{T)$UWJ%$D1X4IF~yL9xyV%I9AODEo1rE=+H zXOym0GMA2ZtzxSf^7Ps_wdDDQuHrpN11ffbY61&4zCdbHx|igPC?=~^L#!*AtN@zA z&{Wxy=5Xc-YT?A1Z6rFTkiY?^7GQbYL zNc_NUVN@iA{(6kcaV;nXp`{jdJ1S$yXjDcehW0PkSw;Ae0P0Ga*_@Hf&|wE&E`hxd zsgL~ZS@>LRT#~O5(+Itx5(}Vq%wFMT+kO5{H8xLQiN_chJZZ$R_;MO_x)5{-iMo0I z&M*wVN6tcs>HWL4z<M_(?Y@&Ql^CGNu>>rfjW=_U4kUR@m$0h5SvMu zhY1URUtX{iRMevizBBI5UjiR`vgrX$QXUMd3vPD7N`IByQxrVyv*@UTLcbM(%t4Wt zJBO_xlt?J-eHnZy;4R_q#mB1TSWs7T;e5>iAp@up!Cm6y%epn(QJal&+1N1_|B}9i zmzNh5VUOC_2AhpV@6aK-Xmk7$a!R)(-U;Z=y)Yl}A4e{5tbub+_}GP z%ev* z%Gp)wSQ(UO0C$5JFij;SVeFpk26-``gTfQ3g``nRd~U1t+w`rS(6>i41x8_A*KEW2 ze1IGdj}qGq!B-J+@0ef5&gDQD7`|dNYiim3#lqXL0Q4REU&4o z&kQkui^#or*OQSGty7{>E~4PMEcY#!DWv~tc> ziB^I)Dlr^h@`#~wx!&%FxDk~hiX#ZxrVV@Y!KSBipRDPvBFx|f3A8V0 z5YU?u+EDV1OAhi4s+H_BhS~^u;mi4&reEZ5?^1F{#}crXlskWUP)E1a`@M?cer=~C z6j$nt%WwacXIHKZ!67yZ0$-oQSC!#Iea(|b-gnZ!JRUHKYu`Om7QU+#NO99 z;?5%RO!xbwPiX6}@3dfC5VzH`IdQ_3Qk z$7{glsdb$oeZnk_J^^(yP|zD9k3FJEjl0$GL1{ss0Kpnd#XP_J9Hg-iOsQ|oH!RG@ zPBJo!kBEe2DPBSWWAY)xtv4Vxxgj!1tgqn(5hlQ)X^@;@$lx!g&bb5Yi;PnGd44a$ zt6Y^ASuEy+|E%xidFa}9`J;VrdGE6v9$%&|YR00UbSog+paHC7q+lzPBHh_vZb?cqANcNKXR88cvzL)OPTxKb?Eyk`=) z+cayLlXx(AG!oAqR3&hsQ%!5>lMM>Mxh};lF(NKPl!~}LHrpP<{*^BXho5UVlHYe5vqI!Sn@*v3SL6*BSbn5$SzdFIm zL2jq8)_gSYdg@A1NndADuyHDx|aw6SY*S#yk&DBlQ5NbR`_jp72B5#Da@PL zpa{)oJpQM49CG)U-=%3eG#J=d?oC;K2wjuq5k)d15lBFocwP%30(r(jy&Z5CxBai$ zmK1}HIK@=bMTSDOL-dJXltbIhNXIOx(uNMWS#AMC~u49Fo6-T7iO*R8jZ z>?mE4zRJu_6_@UNQ{zk1h2P#?-KiQc#&A2^aCm^q>9i5OdJG6#Ci%pOjE6`l0N&? zYWQ^}<)xitB|8=(ouu4V*ki#c|ID;5LVnucmO;NM_Z!Z zQQD`3GG#Qq5SvEUCirUQ^Yh~e?ZkHylBR^y~06raP|^dFa4*6TXsfvI$!UkM*0>h(N^^sLdLOyImiuE*Z zIsXe^0ZGO#aQ$B8N}hu@o&^$cH{cE*o$1_Hf|7e>cNlgaGk;cP;v@Nr%(0o6t!obj zMLqsPh!p`V0ew@(wWWCwz zS+0cOM(*N1b?`0q!R{_$KzTNT>J-kX>q}X$c2hPq35(J)#oBdQ7EpdlEy1m(c|Zx6RW9yxA)y^DI)usEXY zl`egJ*Q>Tkk?9lM@MTcJL{{uO0zC(htcib)yR&!a2`-Mv8#x$q<)#P;GZd+mFg#h% z?{{eYSY2-MfYK_v9y3&8%XOH6Z737lfCtwj#+6(NF_6BNV2g+tb1&F*na;%}WfkJO zJ?7kAgL1!hM5f zm)I^jm%1Nx=>3;kc8Ib`A5ov}V@FlJL#pfz(asaOd2a(VftH?RQf?YFlj=S;!O^1; z>*KqI&qR(cUmt&{zU?F5>`X4EY_;B1mFYa3OCrjZo@A07V4A<^GAC}rbD zAKOzO*9bjTV@n8S{z{R#s}1nGM1TSYMnBH(=nZM;pn8407k+)g5)8iH`;jj?lbcOK zEh{qJKg%6L1uIS?9$ht~$7smr7(Q>uaeta-KTo(S2{CnDQ1*EYzRGx7+BVojptJp; z4>z{$FYcvAV`O!KOUD<@t4&zHYfmUs-CoEobMqs*cI{UAqSIgKV#XF> z@LaVd#bETY{SlqYA(=1DbVepA&tdD#@s+|4gLV%r^HnQ-G0YI-t5*49WO*O^s%5?y zmOJCCW_@y0N00KwD16l#UyRaM9pQ^n`KmczjM`T{!xy9RRnPRrMEI(2^2Knz>c|$N z_1PEKsSFIQ#OLsBZauz3bn-DYIRwW*GcV&~pqXb$_10>+5m_2VP0+lMeUDM;7{N*p zKC6ceB@%tb_pgH@_f5inMa2yZ^SA~}(`tZVG6dRy`E|WI;pZj$Kgd6oKs&f$bE5?Ys#a`*J@d1pA(<5r>j9wme5j zt^_lvaxn-|le-Wey+kY>`Quk%TL6*aVCcR9rWqU%UjaQ&42?0qVc(m>6tA2dtA$~N zuvU@ADgM=dx@Flux~9(GsSy4vC#detZj7`dL`T|Ad2cK1c-N=KPKmLy(~M?-Nw|#9 zwdMm>M7#j8gLnZQJ>C>Wc4@Pf6wE~tHN&Ih( z#~{7{AL~61W_e{fNj+lTiOrt5gmb8Qba8ElGvq)hOCn|TJF+dKq!%S z%mOB5(ko%QcUdYDVL_{-i-rUb zDDS~o&-lgND))qy)NIUqTjY3zx#Om?Ud8)orp{*S59+ydsojk!Qcd;6I2SiAt76ZPSwAilp0#=n*^M^`hrp@;%%$*>h%2uX0@8+r{6bhN0j2G`stIQnT9nC~%&v%X6r#V%69nFHLe2`;3hCEdiKHLHOt2I!n4R?>xBiysvg3rNo1Eex{m_h$j~Ql zI_Vo;gSj`WQ%lUJ%ea%lqCLtDH&v-e{`jS2nSl95PF3M~h&QfeGx8(#%5^dt^=(KU2Rgu8CWWB@3B6siH@Ht{^T!p=12wFcCx5=0c zL`s|(<{Js!hQ!^8z2witcf@w1Y|?SL=xnq0ff5IMm$YkQg;Jo0mAy{t#cqMV)Q;v( zhjJ-yt6kwBNBjFy_W_|l}dcv<@ zRur+>g~N-XC@Z{GoUa!>u6f|TDy2Ks3gr3!WxoJr0Rv}Hgu#t_rQ?tYgEoiiUb(XI z{!zAvJM_ZtCP-66!qQvX+i1)LYd(xfrG>*z+hpB-6tZiF%4~pGU9b#5I{0XEM=bUp z^Viw7r!Nx)Kas970e{0ySyq(RCHgBGwOv(Mz=9%DwiRc9!d@gt!+vhn1IDu&H@k5B zTibL^xa*>U?Bvrf_(={s6DK=qq_Tn2rw2nQyVSBW@_{b$aTjG&yPkfhndu!de_6IF z(q-%AngLWoHlTzk5tR_dO3M6N_IG4ys5ucc3q8U$o!qFLmc({$#@bh|PVqB=8kIBQ z_POU@zOLQfSaj_y(d&LO+pEf6KT{$wd2fTn6f>}=kg)VT{Bf;6VF_6eB~>lr&w zR1n!|kV8Btr0coOF-+*GgM-^!a4Wc%+MXK$ZN%Wg7Mwv|cG)$>m(KXYV-YT0;;zT!E?w&O7^m|=OP{AKB(<9c*;|~?7QHr>e2F~P&vv0BzlbV`wGt}hLmp-vu2h24Z3RK zD+9#liV1K1J*s%Q3d^ge=o!fwMB2|!wR4Tv6kT7lE?N1k^CiyL%z@Z)@raR(ki{hv zutCAHd!I{^Qgun9w}?^n7D*J%HmO9KAPIJfm}--i!x)*Q=L^fpQb6b6{;e5n4_%-P zOk=g+sU_KXgg1zj6c`s&O%Fjec9*7Lb7ZF=Bxmo=zP2*bnM2V`=+c0BNJeblgtq%? zS*Vgag`odzD2@)DI!~4%H#HO|o4Sr4jUx;8(rF|@#d;mj)r`imFC7+_>sIK(B>B%0 z@~cPvdNQ70nni8ob?7B(SQS7E3aZ}L6;x2sL#SaX;vvk<#XVb8K*T9+WS0OtS*Ku$ zGYam)1@4(CDvfdT@#FBe6h(n2Ghn%?YV3E*deGv)ECo@ zeMk*^2~nxR*d#B#y;~QIHF~w%yCZ_JiJQCS!PwMQbH3;P!tAO{?o%~a8v4>jD&wk5 ztztcuNnS1Fiz^yWrO}lRf+FXxBmFU?l@g{Sor!*VIgx4s4^a!YYJ z?1hb%*EE(y9q>OkS7A|<&-(LS?s9YBmoBSn2uJa$^#^bHp+#E%SaJPh#r2OB*FTo5 zKk*qN%a1;={19e}%ZqX5V?*k&0tIp{QKJ!@I4tSeYjZ@rP>6UzC;|(wt$@o8#pRAt z2jmIy2s$?eGX2)x_!#rA7m>5sRQp%4)((z z#;>6gd*0 zo(L>Nu?W#XPAQVb;U^Sf_z-WejR*I*7?<7UT)v2F^vb8@b*#OIEuZzlU0y|CHp)%H zb0P0zvhP8{w|Jy=j~3TGT3q*NaowZEbw}7LS$7vp)*V_XdpS{`xa4#KpZb^~%$CBs z@o&Kz8_wPnrq>rBV_uZx%-Uy6gkDH^kwXq>K#q>ng<<&xawglGugGq^1zHB~^>uNx zYG(tyI1TypVGFNIi6cN)P~%$V2B47m2s4&}Qmy0g`7mY75bbe@0BAb{SEcypf=BP~ z(k7RThy3mV#Y@j7aBCgo30!b2GIX@5ciDKK42(IHZXQtJ^bnM5hU>KYl;JwL8WC*% zC-k)wlklN#tOIWjR>C8ibIBt}hT)a^+S8Mbyu`8%Dl^<_l1b8RXLn(zmWVt?rVcC* z**1%MjTQiqJ4fVi*+Cjid?fdCB8W^W@gUDM{lH|}rq^5aIGAzqJU=g?AL<}@QYh!c zP%$q-BQE@1n%aMuymV|X8_TdAKPeniwLc1VL&E;f65!tndl%iCJD!$oRM?7U_VJdL zWI$GBCv8ofuNji#I09K2M`JNp^V|zY#mM_#p@@$r;mL{MtN0i>haA4%7FY}CCyzjJ zUXMV#4JIU8@ywnvM0=bxciPi4)8k~;ULrO_@ZviQx!G7oJni1UP~^#o$twga^B5Dj zd6N4}&?yT+j~n3bOg4>=(iHj|qLVL>urG4P=K9Fn876cCE301tOp>K&rbzfU^*82I@Qf}?KY=mclA)Iyg|&b4Ad8=L(uK|Sx8Gm5e* zAwJ+x!n6N;;<(4(vEw11igKxFpP+LI(LNR9g0r>%v6r4bqc{}s2{A4qHrX()ceVTI z=;5Dg%c_Pww2o9{>I$@X>QN^MuKqiIDh4`wF2K=*+Sb3%~V$A)$`^2E`yFA)lIme=B!4 z#MuHkvL}{=0V)dvbcpW#!xTKtAKEZuaSh4*wsCfwK-n-u6=f4NHkHgLsBHp=sd5jI znLcxdjmJzQ&PR>wJ?%kSBdvmuR+C&L{Q=Zn?4Tyagc%qaLd|?M7o;P|O~9aAANcxe z{DFS&8CC*9@W}niGsEbCbxwYHYFro;*MN1#N5bpux*Y7Gq;REP0z6f& zM=hs;(FqB}zux=s? z5U`AUXYC025W#g7CgPFNWJ?LOM~8l}t2{b}8x)~m)+M~n1#oe1M zx=4>R!s6tT4@!L}h-3N}eJ9{;GMfJneXk9XzCj=ME&yD;Fu92&K}|T1Hzk+mKCsH- z1EP=;gPDOqO|41qQv_5ZtU7VWy=y^y0E>cNM79=#9;Ru*`6s8I1ueKn22Or6m8E4g zKmp;Wvr%`LY}~x|Zjerpfl+nu;9-5Jr1yZ*#;K>OhaQmIxI3r+U~JQu?+DYEZ(X~5 zLwLz6p)VH1FUNp;gHvM?VhB!&qA;TLxVsTI<-7>Iy0ir@PtB=?z%vM!3DyTS+aFaW zGwV~|fQ#Q~rf%&$hMJJ27zT7vW*a=SlM_}ztqU|c?+f>jj8fC@t^POf-6_4d_UGaEn))*zVf6)@ zjPUII7u=Az8_3B91xHM~6&$gjjcsN-4hi6hzreDdk%c)T6mn}%98XK$dXX=hX;fHN z!sFidANV3<#u3ApVaC|=io_TJ7gNh7iUcJG15qJ#kYk^oV*W|Hiv5 z{-1d1>|IRpJmUO9G+;BE5&q==#*`hIl1S3<)bt4*9fIGqsr`rkEz0IunHzt|R(_$p z{6+{mx>YZ{pl$jrbAZ-Fe@}&Ox7=jZKqp+%gCkYT1zD zjQm{|<0H1Xl$gby@%OoymQ{})mIJqqYIJB}dO43iG17cIYd3R)?sUEq2oSa$dk1-yeC~w{M4m@otD?*US5N|qYvg%gGx7RE_y^QwL2W@l z__VF1KQZM02|r4Xs`b9V1Xc-Hr;MGwCyoQJ@czO~nB=$yJm1o<_sa0NRCUYa|H<|_ zGu^+=BI-;c`Z~}7oLPN{7x|3%K-9u*+hIFmY*_`D2WbNp2xpPm`KQ=LUR4Rb+v@Dg z9Qh1Uu`r&k#aAc%_W?nii-g9agRFB_X64%y(7-FbUj-#)llldH!hL z+TR}U`0p%Y@79FUqk~7k-(`R_*l7qJU4y_<;tLavw|uf)^*PyyPuUtg8l8Ojc*nB^ z2Vs(28MNp2$w%XizVs66bYd_dC(r8gpHEY8abzn)Debf8azb zZ&3Iu3_e{-v)$#Z;C;G8Uq!M{m+GrXNqEs$k>smL^aUKg3N#qEeuyx0E*<>UXD?4v zq{XJCq%GogHh9h}>t-6|M5eXC`|eDHh!VWLkGDTh=nMSn4$cSdv!qbm-4dFBOaVvy z{!18(^LGVjZQ9a>2W9U>n5~&3KZR#Cq*GKIQs8dsPpzuM&v3E_RbFMwHn>dg?Di^x zC*jzzkf29aec@lNiqaya{OM!yII<(L<{3aCg48(kh8=5&eSFt^FmV*OJBQMcrXhCJ zFDE{$NF`$0ghQ{H%EFGe%3P&Ln0lwJn$Xy}7ryuL(c6>c-z@zG_?Pe}5eI8Ow{RL) z3Ruu4Nvi^B3rM!Rze>S%PwQD8)Hr`UKkz@dS7vKlVWYj_US;kTc%g~vNYlpS`U!tD z-bXG)oWM$H7~$^HR9Ygo;9%7J2lbX+7C(*6A3S-*pZF+W#9E?d_aw)@mekz;&I4IZ zZ%l5?3Yr#dGX1dKl!?{cF7Ke4S?$hc_$zzeEM(X#l82%J*U+gbLs}`= zVEUofpOQETZX1YF_MfB7Z_Y<{3?ybr77I#LWBw#NDWdq!KXP2Lnc|};B@zxr^Lz+FuWy~OR-`jlM+?%8R#oR61EJds@TIM+xb?Uy@wg?m=vGl)_o8y<4P;bNWVh($g0#!_l z%sv-u=$;?gPBK&cRg5}+N<4IV=>d=h>|A67Vz>>x4;~Nckg=XUyd3>*KBqH0GC%NR z^3t1dc9V|=4g>80V#7p9=u{>Eym9Z;=W35Dl;}nYRSBF=2b3rVO80I#?!}(PCpMhl zd>r7qrcU%J`RWA4Z&kkYnzUr>Lu4$1vt5e&-8@mTiBWK05F+Yf+j~*7{uDj z6wB5o)9Mm9>wss{T88vv$PapCyrMZjCV0cJ&pk4}r=p(--Gxm2I! zH_G^^bf^*yMvF1*?;Ykb!ZR752@W>!*=@EYs3N$(a~zZaTovgOHcV7Xlc`&fPr#_Z z4W?#A{4?Cn<^AVZ95=*T2H5L5BlJaU;ow5?tu1*t*V&yBc=xtxWaG{dWOHDbAy;52 zg&_~I%lk+Ds@`Saf(M!R!{K?qUJ{-+H$872PVIrEWXf4kJEHKL}e;NHXhdS zck{IYlD@WNh%72#L#h(c9{unosn%JT_Pl2X!672VbPNIBxhEs=^lk7V9|Wy4;SG-X ztA}6sz4_YHr2iTMORUk8;#iqczjne;{D}B!%5d^qV_6w~)<5+uGO7_%FS-WY4gX^X zHM}-@(I$z`xwS2I+Om4!2uackCkEPm7*My_Mh0X7l8ZX1 zF#fm`!}It7nFkEe33(Dd$2(+8P==jQJZ{2^z{#l>0V~YKX<14*&e{eAF>imsY;Kt6 zdwQv4;jaB}GnejFOe@WHe%*j%J0$v|6?(WMMyQ;4U4CiN^E?75IUUFY{zKuN95O7z z>&)>>@d@$V4Aq54hv1rYPkH+A_z&Hm&*Rp6RgyLX!TZozgG>-{9yVKDW`-EUNgT>3 zY6*DF1auJpi{I-+7Xd%9Dzlj-U01PU=OU|#v>jP908@z^u)0h0PP)4xBk&2y@bT;w zlD8@N8oKt$?)J#mK)pqxxjXg49&D*$_Xk9w)#7Q9jw?P{y+^iIdH~9Z7Nq)^y5*6r z3m*A?S(V=XN6|#ZlUHD%8iHGSakr0RiBeZ0=SC}U*X6|btM2lAw1 z0`=Drhe5oz5X=RSf&qDY5|+Wp5V*td7RU3w>kC4vJt@nh+^HEO_ermM8zzFEhTps* zy_udgAP>K3i^{av_E?^rM4Cj4ivZnVU#cf%amY2`-SqJP^_)I2!O)OI-7u-H@T3-E zQYrUDx#^6N?@f=Zhq%uFn;B6!SxwC;KFWP@-blf@DH;^XD+*2S>RyvJ}5Z2O*m*m6Y=vViEkx^ zF2Q$!^of9SF=zrjkgWH`yELhtGV%;!Mn0-ceXAS~;GoL$%f&d{`I<`2RjCB%8S9>aQ* z7*D@F=f_-}lxw)-v&miF&kV5$&i0a^88NVt zBY|7ep9nC(b$u?fWQSzE2{O~6NRr!MwexN3r&!;Rp9d&N5F7jk2G|TD^&8QT{oF7)3()}v;q(EvLH-~TIHD4fc;fwhT{okEN4c!0c0+!B18R~K8z^V02||V^nSyfU z4cLxTF@F3N9Fa^E(d18At_Ub(133MLS3V#;m<}HVOk4hBKzuLljspn7S5cNDYj%D# zAbauvMESL+#)zCvBultt6R(bRums?Y5#CjZ9Fby?iR1n#j9NTUNIrzXk;Ex5JS_T6 zH~lOfa0tgGmpF0rUfDjJ(t*$j%}#xkZG8ZrFbc{#ts#)FPxiz9k2s3#SY~V!Wfh1? zED}OUJs)ZA%0~)RFlpJI`a-7oUdn;HsTB;QWb>o>R8#?#90(%s%r=MWdBnEgO&$2t zWYeE+oib-}%-Aw27@Z!hWP{O}%8%gUjGD7I7tbP746^HIef0M}rFcx$an(4ZR(_;v z{d}@ddC(3!;~NjujOhvOoqs!2~I@MhLCB+NTK5s+chWR_G4Jy~80`~DynCJj32K08`0iR>8~-vee^ zViD|PshDR~4v~cKmc%SHI@8k|1n9M5%AO~BE$Q1h;yntt3=ozma@MnA%B1u10r?rl zyY%0QDr~3bFwc_?`HFe)hor?IwJeV0BQV6Kdj*6Ai5~S*hu8-`*klyML6F2_A|S@X zeFN+M)CM6mMn&+#itPgm^C0<#h!g&5J#a$YMuf~YrM~q8Lwr7B7xgKAD`U*s8T3Ta zd=!fN8u;>Lcf+kv6d7SpmLiho;0wI0oTL>9eghA!IsWH}bDBsBxgs3m5$)o<r_~lxaBLn}ufdWc0$JsQ3w=(Q|_|TDl{Xa?VW?EAzY-kuAB#VQ4`{%y>;O*_6 z7!YfCI6&gT4n~bh7eZl@3(2~E*73JISY}tvQSDe=B~SP|SQK#P-nyf$Pu>alI@N?a z;C-~dAucBBl#3w+i|sJ--;7zZViQQVS|peXW(6@LJ7;&Vh(DLO}538^NqBk&ze zEAYepkMdUun1IbM#?Jc}7~*eT)_oJ=zhtnR7Gqw-4~r5;^9qXmURDfYDfW`P9%C{2 z4swuOPYDlMKOK;ZZXZ*0P@eY=z6v@>0&jVq_=JR)*~rvhy3KY3gSfMOZ& z1jBuKuck`Ay^B#;U}o$|$Z0y4xoZwb+~+>r)tyyLYFTv>x7V;^R(G9w^!O*?XT*I*`uC!HsNciv!=|iX z>XIYa<)Rqul7+k;)oXkTs!l-@aj;$wH%LO}U8ES_JbwFQXU#=va33_HvJ7_08KQWU z7zy}@uq5paoXxmW_!Mw=;A}6W@--aeIvMF@r6iNNzDG2{63Ty2*heE*n&1DKn7Whk zAbV%{k@UwN(TMl)+_xTC&ew;YmBJsSptbKoPL3wDyOuKF@~u4}Zqa)+sgM3Y?7e$@ zQ}z8nesXecdcY=a)6y19PH&+UNJ52Dxio1lq3T9LTT0b^ngFGs?$c&y3&YQnqbq5} zebS)zB5uhMq;yu=xFG|(r9mrV*`QPBoKD2c)VU22q1>9^^L>(n?(+M6|M)$AfBha0 z4t?i+&ikDA{r$Q=Uv%4NRHt_yMxq$*OTtzJ!JZg`y?YXf)$}T=dES!KCe@*YlCHjl z(4P`Qa|-(^kH)~p4faVvgBF~7IS_Tov?f%gf;TCulSdMHoW7v>MpqmNKo zG@R@z4p6Xf3;#f6XbE?|c`Fdg7;$eGhR+0K$8mTkUH@_N*6YdrHMi zp|Y}t3JTvcqIzwQ!f)a`vx$gp1XRMh@xU8xK8l%l`jaG}jpfZFaRQs)xfLB+vZGJY zE*e+vX?TooE2Ws7e^?^TyC-SXO*K4CS8Sn}4;RhcQ1KAen7|iEoNpyK^9n&fJXhxx zbxR$$%u^s%8M}D{TJM6cUz|Rt%p^`D+WrMv$F_-X-R5f2F;J}vvk8L+j$W8f=#m7)TLLAw zOZfe%D)tCWH#3H(S1iQRPUx!?^p1sS%yZj1uj*jk^34E!hZ-RX4FWPbU{F!uy@5K)%BUQn7J2~05FQYLG7v4kKFus z?BK+L6QpkJjrR#)pe6#lMC>>{-_--pyIZmSPsFlQAb$Z3x>CRWDR}+CY1;yV*s#bg z?B~GGDBBa?$k8~ErTA15=+>z&DMm)I2o-z*74#JA_1JA!j!gb7M!Lg?7am%}`jI41 znZwga9HjA}1x4cLfum4Q&NJdJ9XrniY@Mab{3qzP6e|Do?~>#84qNH1Sku^4Tcx(n zxzHMbjs#gr%+N!xu7jIc_ULn!SdHoK7vObG^kSxfjLff@#D)+-0E zEo`umb?aJc$J%j_6nlNA7{<=xB%ERy&_Q z4^kWG>T6b0{sR}Ga)@!{LYq1DY+haDS1;5>O58o1fJ|_@UB#;l0r~(@46FdcJ4!CO zA2BmC-0cr@_PO{-9nOgVCbD}o5pm&L2g{Ppv-H8sB)snBSSXkOMx8waCE(x-;Bjzz zwW_Y%D!E_r+#7h-n5+-}g9yg|5sM*n587UU2(tXk=j-esaiuzUK9QH*a$`)P?9}y? zpn6_fGa+Zw^G)e`tF>SO8+a1` zRSIoK!SrV$fZP|tzfjVWma z`GE1o!X3R@uf&o*uWNm}e<13E2W0I$D_~87F~Pt9jgBPm7Sv6%@Mh3Y4&mF6qx18B zk!t=;{>X0hJdv2l;PN;gT#6z?hWsg|cx@sfpFlW)hs7}qSfLdqr=6-ps?EplQEn0i zPOj$zKWb&}Pu%x3En7hKC58&hA`THA2OmVLdF7bu#opOg#-1{-OE74M#ufH)MsHzA z5`^Uocu=8sL^TAiluFyMo|o@w=$AFDyGsUsnfcR;z4m>(zFRB<#RI-$`=78G!b%hy znr!=1$iNS5%Gfl-rWJdPkLFv&eL3j%T9G*m9_DrHtup(8KRG3PiTQ2$Uct<&0`h

iI$b$SO$mK(oO| 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
KpdRJio-TJ(p=E$$nut(zCwGC0rwgsz# z2ku7iU1IqT2@1SoMHi0O;)t;7(oLr`P9TV zDXmpKqN<}RCIKmctB&fflbC`fq&eJI*!9`?WuloZ*+NE~Id5C3-7U?@T)g7ViiBy- zr%ofL3---|9PAkE#bf@}$g0ye?<($g-Y0iE-8dUkCN<+QPcYdKA9NfCoUug^FDOZ% zGYV`7PTi;%OKy(98~zVytpNO{;*&+-=pTdQ(PN!IBXGQQ1CB5oV2jTd&ButK(s4)` zWJr+x1-lQAxtgQAW|h~huKlAE!I_giQ6fc^evSCNg7MlYuSnf4@e)OOI^PS6yDp8$s{z)l?E2PT z4Gs7KSV2kf)D%a4(0u7E@%ELBH}5?P!r+rfy?yy)!P{3tS>!oA4SAJ z^?UhvW>P6C5PzOBiPX##lNony>C8(rRb4S6gtSL*rj*v+7`muOcbz0zw-jS;60fi!`H1<^aL@>V2WBx5DPqAR$~#bv)f>Nk?fN-^1mIgqK4k@{@NJnxwQ z@HGItEqH}bmUcc7!aJCMi9AeK|0dxF;2_9I2wsaRVb*+JPB*eRDGOyk z0)O~O%sQLioM)bQ6!!hN)EkYlIFlyIjAnK1cjgC5A&^&b3^tBypO#SqPk#o%$VQOH;LR{=N zku7nDV)fO!mP{}yBtOOPMult2Q9(?B+9nFT!0n6HPT9I6(qDfDA&cG|2<>1Xr<50u zxFy0itC`H|Mws6tR0H15rb!r%-}_fztonUsriaA6S%*D#RDa?6f26$&T$4xkH~u`i zJPGh;hT^YqumIg0{PWg5b8h1P!9qR;ev$?XO8}AyE1jqSZj{Zju$b zSXT{RQ0s0$OIwSr($>3MwPMw3YqehRmj8F2Cm`CryubhJ$A>(ZnK?6O&YYP!=gc`F zCmh3m1fPSa5Oj}(Ffg)!fgBF*f5DRI-oEyP?@uA@)qrY?(j2?}^h%854{)J9%K1O) z1U0fW*h&ON1sAX(HE$M23c5y8U3&PTxqIBLr`~w6C`4HgY zYP(q|DHURA>{}%CDb@KE=`eU?s9KH6_QMLORconOWxr;tVe!P$pS$RGkEN>0py zF-D?Jny}$ilCVMr%2)Or*9nK9e8=*;uf44Af`62F*&a!c1JiWeo%^*WkPPSAtwM=e zLR)n{r4j+^j*92M3NS_XpFnqjgf`rEq7^wo;@fRGfF{n-N#O(}$9_4hd#2$`JMKRj zJH+iY-fl%(+#kCQx7)Qfr;-q#jt>sm6;6c@*(LI}lEWC+CygiDokEFKLOVI&*89K* zAP>SG!(8wvD^7Hry$SAW1$Tv+8M+zq*vO(<`Nj!{i4ick~UV1z0w=GO-`qT0CVheO!#F{0?eiKKWv(jc} z7aT|RiTCk6H1B=fS^OCL+UhVE+lmaIx2vMygl)2p)6ru z&SybQtVlNl+brfO-@IQ$I(uPwF*J-tS{Z`3!NAnjA@lDbOZ%ZGrfOrKfXi5 z1(nM}?P2jfZb<-`*pV# zAy0A9RGO$*qGClb$%3XMEqcY#3W``fD({@+91jk}(Zf0=JN&rgvB7m8#nt^O3>z9F zr(y(Ug<{k$v&Q`1td%WjA~oMfFTlxhAL@9kRY>omYg_EK4Yi7vS~6e_uJ-P^>BLAN zC4HH^tjJz6d(}rD&PvBALZ(Pamx~L#?1l4c6xv3V_%#fbBJ{`Xd{L=ZhUj7I8Lmx8op%9yAFekV+n@K%^A*8= zzwHi)GW_*j;&37r7=;*Ph$j>b-luMD*Xby2(@Bh`3=!y#pPyjH33&qPIzh#wu$;`X zmrP%|90rEPvYCLadH}MZAM$+PI}Q0F$Ln`c`CUwpeORZRI6lYOPk;kM`D=U8Ly2KDk~D^SY?#(B(Nn0(oxERDoPYz*yHZI6noSS(MWBD;%wa@1(!NmcZ{UH+Hk{ zcrZeW?37N-$bO-4niNMH(S0l1U-y+Y43QVVpIiLu>8{^U+PfDmqT@D8@bP?0B%)Mf zta6dOkKTYbzR?2`ZjY4xL5gkhyhdyL>R8ju#~t4jRdmk{;)`xA5jEBQ@uUUjSCAx* zKIuypw}xi_&$0FJImY_wB1c1VfYCB69eV`yzu!Kwb4?0PyVhl&EG*ur@tHe|Y{Zz~ zs#GJ2y5i9V2`?QZn@mtE@F-*XC$qHg3VSfl6&qy!{{OsH(=w0q%V8Dr2`vmIc z9##VFB5YTJ1?hV|@MAFw4g~Rhj_6bzRQB$R+HC|Sr60e701d^kl^qQWSHJZl;`p!k z&34%g_WiW76{pijQq=xg0h24L-!e-lq|`2wh5E_UiWgmA--o3T{3Kbk8N#RFHGuFb zZ6M;$rlcE5@ zsHzTnM=VF{UiUSpPjGgP>=nCs&%Vdxz2u%D;*dDxxIO%Dta(BD`JR@K#;8eea>nVccMWX&8BV5w_p zs8?D8MaGthgG;Vf?DbGZUYdD{AV_DYia;~h2WUq9GNGBH0rE7^%%anbmeMb{XkkDz zGSEyEXy(Ub4m}}>XE~ZlCNv|HO~H8t5Zp_IX3ietX=WlvGmXC`G;`qFhiK;KqYu*z zgEQ3>PTM%wgh7_S$WwRzsN=)i8XqhrF=!!ZTS`bHg6yBTRSRaM7u&OUe2WE(7|TJ- zq?6OJ$kT|iCgfx$oL4Fs2p>91*gUK8X_gHpF&UUKoC^_pdePH0l+y1zYD^0#VN$jz zilPSOU<~y-oSb_T;!HLxVZaYTQR&+~SpTFKb={r4Wj7%|&>dz7;B@O40h2*x5A(1% zu*7T~`#3)y(LaXg+vocpKbq|#kg(n!w%{R|TlzSlfJgmqZQ#Z1Z5NC7yjBXswqe96 z%lo;r-}{*r?>7X@*t%nLkzQKlHz5nU^RcWvxA>LsL9=lb+%%yh>g#&+zP-=2JA-{o zwE?BU0k*m&OBO3j14V}&#-@ma4bPj-|3Yez1r^ zyCI-mHE8$gV-6SMX>a=Tb@012212{yetwrqXm^~Kc2`~}{BF%$Lc6MmX;=3!?aCO4 zOE`Wfajuc7Gd*=7--0AHK9eUBeg}F~d5JM$w3ir#%~0ju=XYWp`-XEay8to?^+XRj zZ;~CuQ%TCU2j#N!B^*5xM%bwHunb3!A%gTh9(GvGM0n(!zHRp?cn1MJYE3@?PuYv^ zC7Vk3yaHkb-7*uv35O1lq!oft=Phude zei9>gu0>IVz>@0N&>2%_ESm2-h`@f+9wS;UQ+|Hb5laXRe{wq4kigE(CeT-fm%f@! ztUz6H7c#fc)8SumCwx0mXrlD`6Q{fOcoIJRr;Qj45_%(4-tOS2O?KgD=2JpzWG$QU zL!zMO!`%s8N7(Qrp~G^E3qR~WPV5IvtN+PaF_5+AbD$HLy%mQF5_L8FmW$-qF8`-0 zl6szqaGk2B0b@R5W=J8YL>x=6P!~pVaGvd44w*d`%)u@q-oa6LG^J}8WC$WQmh*f} zMAWhiBJG+ZSR(S_O@7PDfu4^hmx$O=unX1DuVcAvEr^ zdwT3r(pH#MUbujR4)DC0+MZ~)CeEM|oy4bV!l5vgDj}KJ4ff*!a_;PZ8cQQIV2+D; zRA7+~KoASQ;~Rb@Z#?V~Kk$vWE?W@092O8zJg(NQjzM^CkxEroMPy1AdX$uT4170a zh_d?vJe-*Y+u6>%5=Qk&G)!uqIR^(_HJQ%{;G&pV-p3B0H?QHIa{3YMYMiQ>q-m{p zVW1C(rd&he7<96QA#QeGg?~KB3F7V!$A80gU-|Kl#dMLx5Cy0&?(wfVgbGPGgTJRx zqi6c})yivqk6xZO*q=6${~w9zx6wC^JE9r5A90j_KaHGm2i`gV+u8wpQ*I4|_uTI+ zqzU}K@9E#-!aJ<-#+z*u^+S(VZhyqWIQn25h_LUSj?Yo+5~lr$qNp&ZI5>9aKH&)w zI}}}vQb@iZ`FxZ&-w!0;56Jh;K)&JsDxc|>Bm0ClsR=ekF`?~APAQ6JhhU>XDi`bB zGam9FMy|@GBJ0AA+a66I95YP;c1RjxPeR~-0av8ZJ3NV{A@uE#Yr<=*W>r{pq7a_A zpBx<<5hF^P=r=mLEkt?fh~o+|pxliaAJNHb2@!EFwuw#3!~&Yl%ZH=)_2PZB;g+ZS zD5x;ncF`=PVNO<@@m+z4Q^%A8Z+mp&lsmY_q>57}0)KYgH{;rf3uS3{9f2=y3@$8L zxxjsm9+0@~IlcQ%EPi})P zcvy_%yvOivo8c$U5Q*#r$!Uzx#=F~;-+7Q9e}c)MNV1LtTv)3s@lAFf%P4c230R;Oo-u@Wo}; zN=%DLDWTZ6$zSV0IV9u#e7!#qmiSDm~7v!4wEL zLD*cBDuY}dQ`bv6JDxF|_T=sGSJBhh+^ih`;)&no&{NII;g#=A9Clie{v?ICel9v! zBYv`pY>Wkziq0Kp9kO185WjFv+S|Pi3ve?%ZK+G4@mFc{G$HaPgn8@i$V1xnUJptC zq$;+!;3XD+65}S?i^p9T{eQsNtw5uiD2+XR`&h4gSTMC#`hOll2X% z=&gZu6OTB)^w@khC3tKFolYTW)=Fm~sdW2M;cl+IaF&YZyvpb2@hiFhm{^~W(mZLUf?l`T?fr)fggOV}qnJEju)1Ro&XkR9xK zA;L0oi7L9ii|O&GzwmUd_mqcVF*cbj`zKC$^~sq_iv28X;_UWDzE8D0hwLBkPw`_4 zc(1$bQ7qHKp+sy9w3!OcepRO%uSs9O&G8NBC0QvuBZQS1?(X!mZ%KWDrKi(I@0BgYrL3@*^7? zSX&!;eNHHfLJ45Da1#qx_*~3R#8cs!Jlf#RYYS+w)WyF_fN|LY5&)s_>d(Nn{WpAD z>+T_x3r(paoX{<_Mnt#z$w^o|I4di^$PawX!!Lmz*W+614}X8qHS(}b+rHeWqALZ# zroD$9H@KLFe2E5fF-G~e63+WL57%_hgX+dwxPu2M&s6zmcCj}# zK}$Vh*f#%bPkZQlAOspd(CCvC^&x6w#{ECY@*s8*r}sSRS%la*6kjjXRS!G-fpWNq zx*t8DQS7vNB-Bf}x7NSWcPs_ck8}nyUxz7j6HSP5{_L36XrFC0hvG4T9 z#E+-#Qq#x$Tl>2E;_6OMo%987(R>B?LJx-aOSJjt;1?cvUg`sw?B4#0a5S+R_{Kt! z@&LnB7Wx`^H4@xXv+%}a1kQsgK2DJd7PCB_H(dp~ye+@%sf*ze757nXtN+GU^1g*2 zyItzI;6b%FUGO~c7F3G%x%=Dqoo7=OY2v5SjUN%|IhuTOpvf-#tU-C9Uo>$Yb$9@8 z$j~gU`-Xe_&A?mJS01bVGWV^&qIC;Bj>1<69G{Kd{`PKC&Ch)5UgSXg0JMD_S92dO z*U~D2s>%0JWlI;Q*m3!1h*O{>PuY+A{gcEgAL92ME)DKy;j-bfb2z>I^hX#!v+lyj z^(o49*#l3}Rc6yq9>*0crXM_2D)J|h{Lzs= zMe{y2>qeP3>@n*eH*eTs*8N6#mQ>v$FdENnw2-P-33?yJ;?iu_#MWJQ1~@aZ+^n=- zF?VR#z~_dmGAPbXD)^T5isQ@|yXa1Pmb!GC@(|{JUWcf>a1e`6vt}yBGI=)*oR$#D zlgIj=-ix*zf}O=8+#kX3di);4Z!dmN;5UoVh@MhzJ_a`<5fib1OA^% zd#X|tXj*s3ahym(j$ujFag5_TvpzuBs!Cy?Y0V+WVe;*3^6en^t?&b*3K5n9O&=U` zd_}(PBi{~?Z{`i|N^ziR)gi|h}!bChy_?e&?P)g;GS@6N#gFA19~)~Li0w0TiW+vFgR1tmpZ!QfYn3oVT09T2)g#W3j=2?{K@E2?(0B1@~r;lQX zOJF`Z2+f`?!(y*bd-OkriwD+ztqIn;(t3RP=wTxVkjLv%*sFwFz8_5f46^9!?s z0?;av_T!^$7*w%e!vJVJ_Fp<-7(sFHh=enwq%?wLix5UKRHkETUrJ`Yk;8GOa8=R# zgkuAMBeh4av%?1iZhkn%AA$S97iOmyZqrd+B&CaddJxR;gio&bL$q$V2cWf?CLSL8 zG~9>a_U}AMkdadR(e(!bK1fCcx27+`MLcfb=5UKKG~@x?t|LVtML!)qfZI4}rCCt% zj7RCX4xH!&!g0Zhgujp~5`1J3y|PV+9F}rqzsH(Ef79SyB={Fr!Szo|`}hZ|f9e%+%i zIBFqwVNMOPw?Zg0QU|H2{F{bWA{{A4;|Lca)XUv+CS+0kt>#7V?>hWKZAeVDRH|@} zg>W($9Y}$j3cN}?-lR<9Q7#x49GRfI&Z@mgpGc5yj-YZd-gD!Px&C0-c#|pA z{zOobB$6$AX4T9Wa|9HI#H5f&?da=qQh2DMLQ<%Fk{72}!7H-3F3ZX%c?(3^Q=S}- zEOS7jG*6em1zq-+GD54ojsTXq+*lBiq6#n;vI+=%t^mo*#rLW zH{=^OgqT145kRyZq~Zkf=_6!B5W^N?CuZiG5IRCD=%-N;;dQx$*Jb6$dxIFwLU6uH z;hcW(3hj++{{?uXhgI{^1M0@WMm&2Z0;*@=N_yQ)!P~dq(UX zgKu%eLA3OmF_+ZM_!dVCB5k!N+f&NJYwd>@GzgwjyZ72NBswV4PxezKY#0x=rXOtJAh08}A6^T82rT$VKh!GG zjhPGjfktQ@zXJMTI|Aesw-5J^&16DEc4MV5MEjNZ<1Yvp#*L#V$u|c@zIA)QUF!cf0pCnzKUXdj zq_Sy*C)_81PjV7xs2;~3?ulHvwCDes1L~iDZ)qQY zxA^eDa@0G|DeJEDYjEE^rZcCxlJEy>=y%0=86M@;J5I+}c!AY<;HvQg#HI9x7Noi% zV~T4>oG=(lDG)D=g53Q%*nv@z6!p}E2^0P>W5$dt_;-=aiFzvg7Y}r^^H>=U@@WOV z>$fJn_EiRq>j*_=2sd3qkYr8ACCF3x=O4J|A3SUr_uO&8v*|o}g^&m$N;~~dxu`iB zbOPC|FBhpjL+JR^bzNjGmzwp}Mapou@Y%03gcb>8&!6fd!_hap5D|dnA9vV-b9)7N zgWtpGSo!_i5ONDyKRGpJLGgUlLyIsx3|2fNH_3_2^rEksrdPLHghdmP^7rAft_}g& z1i~Z6Tr*A8AY{2nreaKghwiu9%rZv9GPt)%5_VUZc6h4Ic&yo)7!e3s;?H{^wr9NK z_SIbowhQe7bVe@EQ@)i}F>FU*R+BI3*x<=Gz}f^>B7gl2SQgnnsW^)suJ00pzJ_6h z3iwc}zk1f!0^2CE@F)9LTBVYabn7YIiFjiXE?a^tC!!-q_kIhk`PkvNi?f_GA}>O5zu6P z9}=5z@hTPy>^VZ2Lb9f@IF{ZrR+FVH8^f=0yz3)jLY^lFep=N`8I*cKR9^0*W&uqY zerR0wv8$L~*z+i*SxlBE^^T>tYu_i4y`6S$7O8gd%R;jOF3UvSPvrbnQ9#2^TAhbl z$tHVpBH`&@kfVMUdQS9P~q9uL;b!eMv#(ygDhgsX~!_~6dTBS z_wZB>k2U@6Dy9tf0;=53O&YDRzqWr!Y(v?%$*lFhqhEex5u<8mjTQzah;DxY5wqbT zge&8Ne_zJB2g}G1IU~6W9sM2+BXeKqNbNiK8^$5&KGxX15439*l_55*xu^TuFDFuw zK~HKAG?jb>UqE1&x4$p>(yRaNbcj-a zP2&WudS%l}!&TIjU_75VvIp zSu62K#1xOA%zhq;cwvE*q)4rdIfPF{1FIsVWO5J4gcPCT@<3gy`V*InjERza5P}=6 zPSe(Vwe?zS2H9BPeS+pIytfFQ7+}}~{q3GBltLUO<+!V^e5?()nh*btY0Opk&H(gu zr~UHAjbyshaJ{|LLHcJk`n+~^yrSN}_I-Y_p%Eq}dU*+f@yG)(-UIsw6vb!p%Ezuk zl;{%Bo%zIlPPF`SQ`l8^cfkIg0f-&b!wDb(2mvO$9pvvW$HsaN%xVJ0S^|bJ7>?-; z#L9vvb&dxt#XUI+M%w=EK{A*7IW?hZBd2ZS_EZT+4q{HXkSK&iAoMt}mLvpQOZ*BE z>mu*hpV)IwU8lctz-6b3qNtv|L}l%m^36WK{ri7`#*)=Q%RiY)4$F)!7{3b#Nc?+$ zyH448a6Lmhj+pH6^>|*u;tlci01H16%3s}T5!9qtF|ahCs>DfxnyJLePlbq@6Os;d zd4Wcdmk2umYR<$*FPEsHZY-Xo9yj(f-f;ELM|m}fJQlU3t3)5(b>H zxbJDtb&n?_fJ#Zhvk_}13UzoB&l!(WDV`f1&&HFto>ri&c3cvq_rW#mNB9{4+;&_y zajh=wdwLDheca}(tFl-ul8KLZ8nbEY#@8OtbzI9MGp(D(3#i+s4|}IA^mtz0IA6A@ zucoaV`MP8j)rrfA>v!ipo^SAb2-jD*2%KoN_ihc>N&j|7iy#V=l z8NbGV;1`$CgBKpyy=kkcqUx+ke8-pR^QbbZ>WFuQn42kXJrD1}8I(>36`P+b-sX}mt)$@gNC4HvC{a!aqKRdMBnvVFlhhecB&#wLso#dIjfgH)((crNlXDJAZlPJi>Km&;M5 z#ei0hDlLxE6$^QAl~KLrfv8MIXMPV?#OeGiqzE=J-WxAMp)3S!#D%u@#G9|I?4%A# zX?cCT#Z7FJskNa(zxwf(Q9e)g+d2Gi(|;g*N08z)o>j|IbXuFxBB&Qs3H{QhsHTiQ z8~h`%4eycA)xp`K+=4DL#=7P8X9Lf3Yb4SKiXt*eAKXjF|5bt02e@Ck7oEf3J|(gz zwvj%=_$KE>MhVY`5pSd1J!6k-L;0YY0%b~8a6z^uyJ*iHHl*;aa>!8+nlQzX-t}gOhRuvHSa$d?gb6nMh z({}~qwm%iWT${4kd|0`pa~Hc_u%!F^LiNv{!^*q9yO5}hZTM?cUPN*aR(tdWInZ27oa{VmKi- zob%WM%ITDP^WuPpqI&I|1bEE!!5X-Gi!GpC((JR{r-hi=HV5!+GxT~a0h29N0c_~x zva+$``NkohF5kA;uFIZ_VclCS3C&EqbUV``Mc(!V67>NwzfO5<32bOey}LdkrPy7z zRggZK;tF8A1sn{!!;GS+cBWafUDCoJ?|KPe&)yTB$z{mIhIXCupl%WiQ_oaM__lcQ z-gwMIa6S|`w7omx|~ zUf|y5MgdhWPssp^G@ixy}2(Q65>%-n)Zra6nWI?>jPCLb}R|^Nt#e6zL z;J--Mh{JebRD^dCo2+|mWeGEfo-edAR5LB_YN&Qe=>i6Sa$;s=>FW0-Lw?wX7LRUT&+#Il~RqWj0ITY%bWihj1)m507m)^3n zl)Ss*lBd`=AR+c!)5Y5xmo1NrNEUSO=@L^$Md3yScsWDW_`-})lnz|8mFdU4E>sB* z?=9W}<55_pq_}Nnf(ZUcJ||E>GiCGovd$G@##4MA9xW{-H#ZF`P2Oj znUe@=9_vSq#XY@Qy|S8K^;C#cNmuf?8Sa-=58O1^INX^1DEZE=iyoL_#;FSgOZ$L; zT1BDTy1eF$-%k6J%Q=*=m0kbzP$YUi!AI^Od=O(JLNMTunfii-b4-itYD&wCy2l}S z?(f#QRD#nT{yiP>SL{-d<38c}fFpDt!pHEm=b#exVx68TSO`<)#dYeeEl>H@%qXAR zJuSn6USntiNzQE_zBQK@X(>Z!U<1<`7%iQkJ-gJ_$M@gMl(l?d+h})y@ z`_t?6RF0l5(hF+LmZH22jF;Y2Qp5h#B=nf!jCTOQzG6J$>o1Jj~ z$0XCqT2|^erbiihTB*LGj7nLye7#`l)m=1BIGgo2#wk(1K%Mm!ars91)=W#HS>8|! zI5JU-^#$5F*^SyQ<=S0>#dLZ0KH=hka{UR>Vx@PHK*pYy=AoIiw+Xq`HewSmR?24r|rf>yS>yYy>M#$f$&ENWEg@Iu&4F%9(~vqeR%J4|Gtgr zkq91?dh{0i48=kCi?qM3noFrRzz9yIr&K(=hrg+flFj@m(u_~Jx zC61wEv}JxL+Dv6;cBwwx;CH`2KvKfS1Zq;OGzq8w5vL(p zz?H3}E%5hVW2<@Dx>jnO=;5DG(x|4&Vx<8Ryiw&|@~XUO!9wMX;=;0**Grq` z#P5<+(T#gH&-b;^-E-MacsIwt_Ea2E7_$ZCnAg1l;|yqhYAp)$RHi~gE%*c`mz3I@ z?Bc$*l_KRK})6 zqsgKzWsjnFGs$xcZ5H&pHnV#&1&lwzSAFV|`UQ*J^}@`lABY#DI`jvm%`PQf`!M&v|@^4_CSmuLJ8Et^|}Eve|U-TXxOjJRMbD=x^66X$4$Pr#orRtChOcJf>^ zbZVa-s~MiB@ua-vUj69_!hVHlkXbJ`97iBVd9kk`{RJu`yZX(lY#qd=!5F$rZ(`gg zWGZ#qRGnTX&#em3w%zU$QYo!&;_PFs!){Sj(ehjui5kN`XYko#q4Rw2O)3%V>jxFjQit%rveN~a79ubQ&PsSj(4VGvD#FxyEnqPR4jJ`52*5SybR zO}FPxhG=CPm6?FADJg}S32bBv4~`*6qEpiB@?2wDEn0>pR^|2|GZSzdcV?39IkT7s zINK8hjAhCSt3GUG?kbHKfGTi;RWkqwCd1WUIAgd#C`*4~5M(14JgXil|0R$B?V{UU zb35%q!>!G$$EQqoKjTa6EG9d!63+C;Bz}w{<>K>ammXgPnaiRTK zuNSSmQ_B$B^xp|jH0Q+R%v zd|Qny!KL<>a9=SBFBgz<5FKs}?q4DI8r+nVn~7A#Unolyh_ksRs&u!mDMn-=j&dtw zgmD>FQ5L2oYOV#|?@Odbsk-!)(6N?BmCcn-X=;fIw~&aKmf3UFE|;Yv)x&8@B#wXHVw(>}`4<)z#vST<&x7)b^n55i8$Dl&PiL7A*B&EbLCN zU96D(fqvb`xb^hL9Y%N`@+)&qqKw6zd0X!lTY^i3lHx~7f-I@CHPX^gJVo~SqDj;O z$S+di@1h+oXr5a3f^^EZ?E{_mf|@*S(+srXp*|~-IP(ZL`BeWJ-z5-zu*{s)*L~6h zsd5xOWmSFDDnbVg5#6AF?zj8XAdVEuCGZI%>D{uqX8eLycS}%7P!~;s?98b;RyHRU zM>bQ*fD-PbPlzsw8#}R4##qM;d6>Kw?UO(z7Ug@fL^$lMG- zNP!_m(tIi}wvD#X&}c(snEngl5n9R;xnAIBsfr|$(1Xp8cCXR|#~T+#*@jx!jrK)y zTRrPSgeZzqr_=m8TnmAGX~b}4@-0QQe|O@HX+!K1{FUe1Xae#nrL zFG8{9Ar|q1g<2evWGLgxTGwz+Y_gjyJq$Bx3(Aj_7nenSR}zWyunS&wFOGaWw>dIz z?wu}ySHObVt??0>oJfxe_QK-kQ+_h7G3OWMO$6gnKi`_o+?u{zoD@hVmTVDsufm9; zvIV%OlM+!aN$*h1!>f9w&fWR`0gB%I&3GJV5Me^TNYua(=rn3+*$!HTv_%3`LWSg~ z#YnFH!)EV%{_EKpTm4^6&h7l)Z^s z@$YBmKYC~8aSzVS63oop82Nvmn{9plb91BdKh4eW_6^LPiDj5qJxY)I$l<~RBV&&`eRc?QkRT^0|agVRGfBJk2oZoLwZSK-86Nvd$&* z^;Lep=4Awp$%7X5>Td@23nuY9B3;Dr9Q~RiePgM9t>jDq=KfV@{6Jo2eN*pmHk*}A zSSTlCYITk`Cu=jawfTK)rB44R?espKzEGRi2Wb`ly~E@{@vI(GBAT%haXl8)`^$Vp zpc^s(SshzA*Ug5JkmYl+uzFq!zjAYExgiID?X#yRjWOE| zl}`vNimB(?E8i1zJwZ)R@{@^&#pPFqOieGsI>tT|@0oG9nT4D5D5~o@+%KpMd3Acy zaI^hI@)fz-1q`M<`<%*<*DI!>mS+1al_B%Z_VUV*>1O-Ol_7$z-%%AYlqG0w=pz;( zLY7P)E?|sQ7ef*&3)c%U^dYjAh27)2T)>o4>5HiRASj-43xe{sRYG}gfws%;vC(me z-ar_b4Pqn&B*)r0TuKO^ZLWZR3W zhI&Q}D~bYozAqbs+0;UNpTS!66;UWa&D`}^=t(7l^fx{ET1m;{X60J4o^k5eHpPg| z%0}d>#9A9C@z$D^Yplw(4P-@Qks?BSMVSXpdN4USStat>BOgv*Ankeqv`ZTO5Wiyw z_+2idOR`TC=-I}Cfc&7fAwK!^Y{O$+L>>xS;@KuhNjCQ$v(b~UKl{W~9G?gK$C)xv zBu9^g8rKe&$}LqwhU5dvz4@e6-i~uzk2Ji;k?7|YKYDtcwFAR4?1Xw6#rRQYa|7|3 zjNpC3uoH$t3R4Q0yY;55_lpP98+^e<%Pk9YZkCbxEFEwbX5pSmQl$e9 ztgrBZ35|zB1~POaJ9s!+s#qxICn{?&W8UyBw*BU5jOA9i# zh%^gMJrT1OMTiW2lx)Vmip#(WrAf1hR;wyq{(j9WUuv9HDz9%5)dvG9sy%5jU$coZ zXH5gh#|5cX!Gv0O1T)F*!xkNIRd)Vn6IjWqC*+W z)hd!ulWGoPVjwq&35pXaGy_6|v4lrH$O7dz!72$447&*)$TLr^;3ivHftvE1aALwB zPB{EuaY9124=}$$e()+v+xxfpRJTF|F{tR%$@xnPAFPzHr z!U}{U);z=u7m<*}5A#AzLY^xu{l#DL!F0#}FFsfid!G-gHUEVVrjHoR2fP0Fd=Me~ z9^iuv*e3Ykzvi0-()4*AHaGp02hKz9vpNZ3b#kyQ&kla0AXX^ zH7mwtc|`Xr+e|ie>5baTzEF2TsDy<82nvii@*jX7(6~gRwX(c>SvAs zvY_9WQg^B?GiCXAibIFnM9ovJK2$>k{^TX!{^*-@3AOqa!o!O-*YQ!}{Ya0Svl50N zQK64&gyAAZnt%6Z&1ktQX0&EzO`s03G*emWjGTg0Zwy8U^|~-&*p2yOOAw;@MHJA< z%7maB`LmPtRRIZ)+_323k0oo_vZUxLJu9us_M2*UJU6|a&H3A3XG~3WJYSHVI9rhT zNqD{_S(~tZYWx$5WJ#h@lw+q{k(7U<-5Co3!ZIQG~oUP<=E7>1Y#p>bB67yQhxYyt1q_(Y|6z;cun9D&y2-zZOj`rKJJ^I6j zW|90m`Ht-(xG8p~Qq93n4fShn2|gPZ&>0QHoi`D?36?r%%G>J$k1tVA2;itKCz{TC zg2EI$EPq(I@22C|vu3!Pr_b2gc(v`4a01H;yM|NEqW0-me5qSIQD@mmrd{&MUqZIM z5NOGkk6zvGXMyN1yC6E9%)iHHl&A0DRBw`r3L>>|yDU8#_L}{3esL3i-Cp_XhAtdslrsRabSr9cnlF{%2n&?PToK96N z7?iL@pcx0k8JD4D1t~AO$B!+UG=6OIBTWq7y0oPIrodeNY@gZh^DGtoml zpyzE|$hC#&d3#?VZRyt$57zVcmh)};wO+ZggtVwX&tSbW!nTtgIIlCpw26kHIYhSw zg%Pg`Q}1hDIPN<1jf?C0l}bw%LYEY~kd>A~k5pN8z^o4>&4mt0J-cl{r$<(G2>Elr z{6Tn&Ac{Sx06%F?P+W)BE4dEFg#=riJ{8JTQXbK!_G`ZeG+yf0yhs9${;eLS@sESh zYM9-Rmw&I@Q5(GVq)d0OA^Hw6aMg?QzM>2tBOK~3novQykTUF{>_>@60jSz1O442; z2I$4UQdC}?Xs_m#WP}d2RAXKn{6r0yLBP2RFX|vP`n)hHvO9vvUlK!@aWiE}6JC9`vfH$f3Isb3Wd<^DT)5zHIAuU=yGXN=-ZYmS{C84 zHMYNn8cGVknhC?9vxx5ce<)jIYngqt+uq`CiOg%b+x4BNuuxP}aTjIGmoRxacR*CB zNf6kS2;a6O1)(6J#~H$419VPZSxsDSf8Rzzee#N%E){h+w_Myl5sI6QmG1ptijN+b zf4YC6a(s7HfV&K;n?zJs(m1^Bl5ck`1HrjVE$UR#r(tYR)#eI$P z-%*oH#~a!wLgq89O&7u>Zg?YpjDKP-Lo_JNA23Y?oS(QjxB5q8nfttWvHFtm`!@QL z(5a?Qexbk~?7Xtq+ICJ^u0C1jUaT$`U-X86G;iU z+O}qOyBLDKB|BTx+r<}maEN}B`Y@JB0h$lQUp?2j|Erk`PV_7|VdgN)oE!=XKM>nB zj%tovQoLZad%+S&w(?e8ta!&$0wg9eZ(B-aRW$pE?Cia~4a92BW%u33VC2?R%a_EL zxw}Xg%3ozipxVGq;rBCb>chEBJEM=*f8V0o=d(-LmjH!SMO(AjrrafNXl;R4D#L5A z=(?Hk(b0|s%yN}3}ZM%N-3c!Rq>GB5L&iW%4{fFz;xi%_U? zd8Hp{S?0t+2!=NRiD)q?u2rrOWdwHNn9~T{iZeKwoas^A(;11DSyhqdU}jv&2y>)J zW}~rZF$G;SwnWt0{7DSA_(iLeyNKB-p>I*IghH(Hr8m1mC}Xw48aW(08`Dlb+2YI^ zZb@qv8C%_!X_c`)Byrq@xxQ1fe1rFyecdFTlJbl*|bzUOp7f#B|#@ zo64JUD}I@RUsjMYX?V1aACQe#JoZ=xXPcuAZzDc%mr&eZus5W|bpDr0?9uqW;P>1; zzZdMqJ)JZL>oMb-5#!G2CY_P6D>4OL3W|!J6+`w&w<+E3v`O3^squmfd!DbbCDLs; zmXhJGo66uhC?hpfh$Ve&@i3?pcI9urUyH! z2+dT5qA+GAq|nr`Gqh%EOhiU1yd|v&L?YCtY?crDY|x$ycBS#ZsFzU1Vg7zo5k0KP zesB6%aiW%Dq-4W`|UsVsH5G70XHwmx=a%y%kbddpX;$#1BH6P!Zg)nszr#%%({r7m!}0}eXQvH*fk1%Xzxs(yB^k_@V`8|ZH_SJtAg)1?HuY` zEEHmIHhAc&&o!G8e<9Q(1d=?pclB!SS#W`+83YV{26i=PVp z1yMZu`?QNgC<;=vz%?r58$Wnz!eEIvNcMpSV0{S5 zh2-31_#f4zIU_-~yx^l<<;M#?wnOmI#XKbVAOeqm;Rmu`xKB%shx=*CaKy#)kKXfM z{?UPBQVjIp6a8y~E8yZ}x#ZwZ8n4WDW7aCsGospzWu|MVAsEcT_!p-Kaf7cz(b(=< zx#Vv4nX6_cjl<8@!zbC0+G)${4pd+~ud>N)ZKi)-t*j3+uc3@-9HUqRXBWX09F3kj z?PVTzBUa{{6$YmZ9OQU|F(YEtIUTFw4K`G}C&dV3Qqe4^p6a@Q%^OpC}50@FGhBF%~LKN})| zR>IOI%NMae`LHgpyPOZN92f#M(>fzUDp=U6EQ1R`0UrrxpKr0R!XjAR?)>xuJKUbR z4DJf^-h_p~cgDwgP3C9 zAsK?2H^Yyt3KoUWS+(M6?aHghH~o3$O2`jfbtd&miC?j=Sf`@$BGJ?k47eN&%yoVD z`E#v{2(Z8;z&JSr8kn0<8L6^aH}ZwX+TcvY&`Qa5SJu;(sZ|j$;ifEqCDkAng`6Gq zBS09k-ZzeEO|$2^&xRag_HjN}EU9_@p1cN6pBtJ@*;mQ@!jHk<<6e|R=1eMaW|ASRueYFD3kNcfMhr3xfx9yy2|J96^{Jm?cPs``Gw5LZBG)-KUjPuWp)l zwOPxCGu0Rl{k8!6z;Xrn(@_muv4!`#0=6N0F`82R~Lu0cx z>gps^j#N~u#%;f>QmeuPQN9H++DES%--^hZ=8q}nxEN#!g&ZhE`}vFgG@6}E0)n=(Wx{Zv=_i%`keQ){-h9o!a_k5a~vAliR;i>S5Ka&E@wFI$(vtcrH zcXWd&PJm@xOb}|%4NJnafcJ#HE8SxVZ>KihxB{1jhHsi=7w*9`wy^Q*CfVeBo34_# zyd=*Y{q06kV{oA_qB*h7~7)mn-#=;bfdC4Z|}Vd zZY{|QS4D-}&ak1YqK4wI5so+pty=46JF_YZMxt1DEWMfx`4&}WPBIq>)*Z-KgL}+(-r^wgt;>d4a$^a zw>v$cFwGr5=46Io^5~4fWI^F9cZOpFXE(>6TDpy7&HoY)@jP_n}{7VTbsjiesN?-`xSg&nLOh^}@QKD^E7pHn{BnNN8xNwJHIqw*E5qNUycj zHvF}IChObO%+&c^S$Yd@8GWx9|B+B@bFmW;!~8EKt?RQp8&@yeB))P$d}ZTy^A5}Y z!!D%Re@MCUgxl3RW#cZq+vNG#_*25hcGr&fjqS>lJKd*0zjN}VWu1Rp?*80-x@rGj z%gMa~tsmN3?e4t+7rJZD+fV*zcYk4b@3XVMJGz_RAHvGP()W*CUb}9Y9VcBrebVZf z*SfQzVOxuj^>53u?Qh*F@3uCtXz1Sl@@G@om`!JXWT6#*qsD-Sq*r7HE!HA7jHZOR1*FZ3!@%Ya1+a zft9Mq&VHK3_Ef6GXKfi30%~o0dU#-^^C|PEXSQm_!SYqP<>yM(aQuyKj#qB}IXW~p zS`(pc`#ILKHE9IHP|8hsC*%J5D>wceZDX)6A>5g)DtO`G*bb`qP?Mra;nYh@?G$9D ze#=*l6QpYeOIFI$RGAd4VpMQB(s@i4L*)hD>keeb33^1ZQ?c&*h7EIM zkK68?>a-IA3{Tx20WvvZ+1~jjzFX~7^mu=8ewzL8$N|I2JBJTs&>#3B$ul#WtRi_D zhP|_M6NzGN*DQ-o>l9JX%}{k8L)PdWg7n7(j;f#SD!WEt2YWCpr7AUJRM}OkQ1I!- z6-`T8-;ww&U!ZcM_N$Tv>4Ac3SEnCUy%i@e<(4Mfg^DzKP_7Y#>DS%Qg&jM^Pap*{K`=N*PQcaY?qgQVDyaQJ}j6cVl;rLxcL zi2eD7Q`WQZ8>Qyw)kDxP%7~vkB7a6L@K34unT+N=Hf=Ky?$zhw>v-8? z$N!U_fXjVu2*)wNT3fbnI&$E=qw$Y-OdlHTe@QTU>s#w_52QYPpncQjTka0w&*+-t zchccYH!eq|rD|U9(b8Mz1f~n9IG0LGv(IAm^n7+J?^$Z3gAq|3H!hEKg#XZZfR>Tq>s8B0m zovu*X@&BYM03KQ(s|S*Iv;xP5sdxTmD%&ZfJNOyZp6hIWuW4N%I~=`S)8bN#oy{wn z)(PPE$+6JTV714%z_A|&5#v^M&az|S18y^z)f@@nD&Fz;%lmuW6;2QJDT-gg1~$cE zlwt_27su4qpZd&kskaIG6iN0?e%ffFP_U3I6RL z5@K=b{f->y>oBB~_T211B92_zH-TVO?&jQ;w`fzE!Tx1}@q+7QuhRXa8w{EIA8_)5 zo59|J>TH^~x3&Hh>CL{5UhN%Ik~jU$j)!uP-n5-UzJqU5odhNCHN^#O+>n47>)-{% zGl6@K9m_GTQLOx~-;Tc*`$_y%F8fS$t3xwLPZZxH8&&qWqsK=$-nbmSL*^um z%Kq;1ns%psq*F`DH?boy=kKB=KMB$|6GL3(OPA{wT<+|C59G@ef#9&3$%5EVqmFWh zueKg|>9XQ43Hy6o`wu8^G7e6JK;1{EHj z*fISw_bflbjQ8+gaxNbZ=^E?ti--ol?WLAPu`TJxDVv(zp|aDfVg=ZT5U$m!;kXYG zQ}#}5s=N2ok{vgF!0>iIH)Mg8!|^BJdEZdrriZNZ>KK)PirsM9mv(k4aEQ<1QZ-r~C6Y>-syXS$)?Q@3(j^ z=}j>c;r+gqvKi#jODejPCoBf*w|*86VOUoqrFq3>cumE`Qy4!r9-DzJ^4rcnT03&N z?Uh5&;_9g54eRO;-1|up;|5}n_U!LbDt?ooIN|Pk5@b{BbZc^58{W4zy`Xr%t-)Hm zG5GlVn_6boewnawyKCbXrF*A)<4!yNpWGR+tv+s0Ql)(s89gyBc8DVS{wQ^0l-9f9 zWV&bB{#N&dXV=aUq${Zj&oc^&>w=0m(U2uksDIZIN@HN>_&F5~%QdgOraJJ=Pqivi z$t7$1s|illar!4zwxa8vEmyg0*2)%Ial{3lw*MQYoluA!{|S*Cad%00hNcd@qd4jU zD?<{Fi29Qpb@RRO#ZOGzJ+vJxy=k7Sc|~mOwpQP`qaay`IlF`u2>E!JWA9JVJJ`4# zc*};hZsNuRR53NxaEKpT+q@hri?|t1OnmEV=1*Xqp9?yt(3}4L69#jyf0I=?)H`+1 zCwwju^9f4x*2+Nt|Bte7fotl>8ovoi2ym^0Dn5d35?-Rx22goa*MJle>-QllhS0hY zN>H}8fLfHL+YlZJ5!w(zNGk13;1ZJZj8^g40E&;|RMnwaS|1b?wFU0~+yt!c z+THK(&*9$8ojK>s%$YMYXJ*dv>@pa2$=eJS`L#OL>wZvL&g=B^Y8JE&#=~yOdN>K5 zTMap%Jm3(1QoT8=!=%S>E>yyx$#SUqjP7Zxr~PPG{)0+KsHM}oE8X1O2Ouu)n9u)% zd*=@Ha}XQYeT(HaR!=X82$rReW9B-we*R$a8aaY^=8Q&cA2($N24;eP6P_n?PcI|+ zF9x+Ile^~u)G5p|RbS9K8cgql;L{DL!#S$cg{+&PX+E1Bx^DtveJV$;pQX*Y~D2G@f3l*$jmpzeS zJc~gEwapTzfI@1pi=IcZruHIJ50j~R&r0_Ryk0GfEgK~ePfW%?JF%>ax zr&auEqZj?Uvm4l`BakIg)qYw#$p6L;?^13p5%6|*YKC0=09M}f`V0q z!4ZitiNng;m_uPsT1|{9dYaqm(K11y87PqEbE6d&(sr2w>k+ctE^p9q?r6zxFe8w2 zVHR%tM$Uu_E$^VxcXEsAwPBS^ui3Og6IS7<4G8upsVTRvmd-IPXDn>f8GmD#!~_^` zhp|bF782v$CV&c~Q$E)+3AN`mU_7PaL}|%yFT^-iy4H%Z!ryDQ4*#+D?ILni$ZWiGJ5C_`(mg0kC(LAi>~&&mNXo_kMXP?@DR( zX0z&nUqTAEXY!*(!b;d|Y5I(lus2t=nFD$Tr|=bXo5Qd-1(D0aLI%K$rPJ2`4IN&*gpvP(Db2Dod)_oLU!qD4|qFGFJd zeXs}Q+TkkZ(e;M+Qcas==&S_I@=Y^RwCqg-ipMST%b86z7QmH)&)MQ&0FAe7E&~<TQzk&h={*)HiCr&r#GHx=R z3P6ZuKY_6oDR!vHPzGof7jMy=N>$i2neM&VEC<~UF}c7mGy|VEJ>&BhmQ1(hpk^nG zpxSV()GUB$WBIQuRC3`NZqe)B6DW7Xp0EJ3^2*P;E-@s8!fICc{=rfnIl&Fb zs!3Rwfz%SfQk-0kgN`8F0&F4pYHx|gJ8WhlzQL4 zod!yg&9bQFf-o*#k6AdkPs;oqPO z#PuM^4I{+$P%WtY*s=#;7YNqpV640K5z+jJ0Iw_D5yDa-ZPG~9J_x4aYnZDP;KYS{ zmuUS79L0d=H)C@>weIMNi&EO8kb+)KrQjDjt}=OR?}9b|{=jc%+mk*x zl8N8GJCgomPsZcHzx>!UmVUb@{cck4ou5*B?~DwlJns2v@bO^YM}z5) z9^d^j{n5Rij~?B*pD^;%o$Ujo3AcJS&8A-OxqTzM=f}jyBRxHd_j+z`zcFxUF#YbG z5rB8+`rS+%uh!M|8zCHopoE}?@BpjpAB4~W;Ts5-AuNDE#u>h>>(8#M>t6%mw6U&V zTU*y(-B{P33&9M(gCSgl_q`2u{WN&)hV&moT6;)048OmF&}Fj4lj-c>os6Fg&lCu? zUqPCQK*CP@stRjwJFoxx*m-kFu4ec7%e#L#-)3p;IR90yR92RotJ!nDs-ooc@`@5o z+Ws<`rl73!{2tjCmFKH9pBLJ)V0-g(&#Md1AJcqRcK(2-Y;WoLlFv)eSMB~HFYo-( z61lAG^WA0hpWYw1Gkm{)*LeSfU88r1TYd2U;Lej>w+DLrd+zrS^xoLyi;@||AOVx)%<}I7dt!h zAK$r^-*NVvi)YVRcIDr=aH8|fiBo4R5OwS~weVt)H2i)9x+a`w`tt9kgnv)x*J=-i1eZ9abIK7M`C8m%vceh6F$_aK~sa1uf* zgmVynf-nZ*DTERTl@Q7xXdv8%@Cd^95V{~-gYW|cPY6B`7D32?Pz<3E0@;tA|91%D z@*y-sI0iunp&o(}!dDQ=O3U|@sCKI>_ud$|dA+BP%)9zvUB9M4sNDxaB*O8fM{&Fu z-g6){LzpcRY4hRPk}uRQh42-;pZ+oF#@;dFz@K5yAk3T@bcFp06P^K`4WCT1cmY z__YA%5RRYp@r1fvAFUMuaZrSl*Wo(}tG*w~3ZWUoF$g&jiXoIiD1k5rFhuhpPd<)o z;CDZSWAtb(?rE({&)qlhcV~)>$BeZ}IDY&UH{%?mqSn3v5BwlzQL$J4qORcu@%A6Q zNnh$U{I;-VJr3z_%n%uC%CF<4jv`}ypaI`^R5zvO^;c&!;QoKoANM^jullnHPoaB$ z@QVMR8jkzEJ{7G2wHV~}HsHtQxNpJMdm?Wge%#mlIxc+&U{tLV;qaM+EsYTw4YVa9 zWAL_RqADGZ%PJ1#%jur-MaP%q=dW4PxU|u`4)@-&-E(@E%dGI0^?8d($kx0bi$rgn#;ByV~54Pv$cNv$+_8u}ClB#h) zLI)(i_Vu{^2ixfY-R*y>Jw1#5(pR@%b6M8^iSNw1KuG)E zc6xG``1AT?`TqP*^ZU#YGn-EDTKjDs#00+gUQ!JXQ6bi-FRW2$eo=m3Eq=Sm|7+~H zZ&e+Rf8wh-Zam~(gRhtGPqxpWz9f0kr(WJ$swd#nyI%e@)W>T<4HWdaZ&LVVIgB4m zSK-E~f=IIRpj^h+rIC1HqhKYxAuAzr_X+$)pXf2Y8VMIo(G`9uZ^TdQ)PZ%!r=q$? zk50P3Ez;De1MA73?t@T=!ffMecWS@08h|o?V9)^IQ#8 zH*oL9YVR#%1kd$osY-Nw>KF3T5>aJMnVeo}Ky}QTs+$0dRw}aW5ar240|vbPCxiRv zcx6?gZ(t41J8I~w#Ez?{4#=a0*as@DE=JFixhf1O^eqZ{zD{N~|IY)Xy$F}#d+vdWC-=|wm6qA7;{ zsopoLL=UE-20HCV+0+evUqMlrHPRJ$QY}8Gddq&M{>>UZsSyY0UQjZ%pP7ViF_ICB ztr1mZM4tiAi;g!QC?CL$jdZCuv<2{Xn27#qzF>=}G(6t7rf@0{O(lt5P-78@=yGr9 zHidttE;(e42^IN#OP6O>MMH1*h=?~H4ZMdZt$Wr_O1H-wj}_NI0gZ-%_tN^ItLArY zUJ>}-htO*`pOU^UQZ$0BGa3rheQN+w?~+;)ED{5VlHH+y7QRYd0-Y0ZuN6&|SFI(P z3TU+aS@cjGU(q;#;fluL#41`sydYWR;T-Q>kLO-{vi90o-n;R}=6qjzaXnO|1-k1& zDx)XqOB`7%qBD|>271oos#;tS4Ik8>7P{Bt(&)(#UP)CrE?q-@nR>9wfa8`}GJ8Rl z(Ezk6bbvJOC)(6_#7$JXB)+@`&)p--J+R-WBe$U09kSw<^+5htEpYoEp7|~7E#$Kd zC|Bs<4@v*lOL*bO`S~Z2S@?}5@za*X`|6A}(*tMZJC-gA%%elkLh0+L?uSZ{r<67L z%I^UuD(Z&agbQ@Vx`sXWq=YKcA#?EIh7d4`dR=(RlAx z{5cpF*7Gnxzv0Gi`9H^Ty*J_Qc=li7bfu&5d86?sM&pZW@~+39I)&#A#XlU2zi~g_SVJ3% z-)k7vS>6$8{uiO*C-%mB?1?X~GJg1pyr|x@I3HmE>?-5Ow;KDZae9AOu3w$HxvHI; zn*=mSUluJaj<2k9-;dKiiN9wUYy4zzs9D)?eE?{67LhV zwz2N}0Ix;gcdgV1O8&a&g4a&oPR=Ds$oJoObyt2>E_oA1Dv<9+%pDLssX2bVT!*(W zmRMfGeZEdo8E7@}(}covjh_sUHbBLbg2_;67^uP9{bFy3gq6S)_Y4C%oWyMyYOZg% z-mvGcq3_Qkk0{~ZdV!y)G*)=4(S8KCye8VmJy5z*sP;1?mEj9l3yoN19od$Hj~;aq z7cc4YcK;2u6~apr;nl|bg9Af&xkLn?9^b^Jexi!Twy$$9eU10}=X*DTQtS2CJ=>F^ zJs&I$tiz9A5A4MsTx|1OKP^Lewc*4z;k~K>T}?Y+@)s@46#CZn?Z?j<%I+mYrbq1^ z-(KrqbaI*RGJg`llo^fq19Zj%yy1GgZvD46VMOvZKzH`ONixbFl0AACuRJ-J47AME z4_h|U*un*k?j9nXmLaUV0`iBpRfudAP1`QK*AUFgnboeSy)T58hksH&aBz1XBGgcTOC~G#aSh(v6l=K#S>!M{1n#fei{p90 z!cl{~%FrHYNIE65{D8xV>GL|xO(@+i>Kg@GUJ@RfA-yJgxL63t)iVr9pWu0}!u$ey zu>k~8-ZEj*{p!5eg;tokfrhF~QR!>K8?OnAYl?JzKZ+ju3Qy35#TdxuJUnl{5N0p~ zxL~>nC>VacF7F3hMiTqGqT&CI>1)RGIJQKHlc@AxLP!UVJZ!%z@4H`dxQLKzB9GUF zXnmCt&-?qc4@omb3r`DaTw!rtQDYy53t&U3ry8H;DJ*W-m-#`t!SIE=sG;-+;f)TU zuM);&LtnnAbX@qkyr`xsS%kEUO2>qU0`Aqwb@=0y+0ZgWFB|%TM8~h+tr4`sU<<-= zK$7HhJ$JFe-CtBXB7FFh@C$h*HbBQ=94@C{K{{>y^GTyShSMK)wR65Q)*JWrpYu9- zJz08LRE6PqZ^2im604!2`)_zYzK&Zu@mp1=5@LP!wex2`xM7{+Ay-ArYGVU(MpX56 zH7Pe~dsfA7S~xbL## z#;R_J;^yc_bqZ{*E?gLQ``dQGhr%ksLojOS?LFH58sdbVo4+c;@Q|jDxxX+q<2YU+|DhE!a#qHG>mxIWZr!*H;Rn6c>7b^YC>S zsY>*4VO*j39OhkxcONtKeH4GZy%R(Bh@j#H7p)*yu6rmA-;XFp{ibFJRtoVk;T!dtXJ~}W*d18|B zps}tj&}&gy*XD}@iEH}{7VleOFV%?pmcn;wKDm+`z%3_5&``s}G|PFB;E3qqUZ~-F z*jx;T%I?KgAapSIH6o4k(Bk8lbk)YDKylI{i`V9M5}(D(XBZOH_i^JNOOlMu#>Ph! zFOWCG{I|;`Ys-2T@8iDR7a_u@6~>ho$4zM{uF0R8Y_W)FcjKUFNf+YtKCv~mbj-#N zOY(A{1>C=n&y(6dAj2YHH92E-nf+o*Azt+@5Kj9d4*5oumm3Gd6^Egs6=}gC5x%Ej zYXNTAB|0v*hw>2dd8W7|o%Q?i=e`#{`<^r$pVu&f{}GAbsLQ7;F8^S#tgQ8hDNaro zRecEvU;qXVEwhy6F0YdD*Y(|v$4}8kZG1ygUpx%4c-ho4d}@KE3DVianw2Qn>KreS ziXe}SpO91$pSNKn2xgK)?}{E9@LP?;$m@^ka8l3ER~#?UiyrRW_$y1h7XVA2vf!2~ zk)V&HX=dZ_4Bi+v*8uX;*mNl=>3hy%pKGU*)}E9u9zN)PD3Ga>E!JC1cwYfj4YD8a z%ZtaWV95`YrMgQnbrEOxFZNp0-!**Q>y-GfCvSS4*6nM}cCUlf!#Ej57LXgmefzo(@VE+1mOa!e-Qo5nuVJ-ZoVfk!uO@b z7uNwtKEQA9$*=TMz={{>qa|+)q+bT~zKx!q=XrbAcqEgUizZ$^JxpO$tv%7yt2{Lo9S{-A;onn_lUZHAgBw%H?aP)@qdvfa}B zPvV+G1Qj`Kf)k36<_AY@Ee2MgG@0{$%=EGc*Tkq}hchB;A0L@JJ?DF(B21azV|C@B zVOID<2F=WzE~0|>TrAminwb$aMB4&~3fN?G8Jz&zhM;@oDd;m4yoWeV0XI>n`Dah2 z)U87qImd`fMu8ootN|j}5;DIdX{Flho*3|YO;0>X>go1Hq$o&7mzR0@hildI>O@462+jd)=z7a2@&kZBSNaz^vB#9BGEvE_{Z2G z0*NI$CKN71!#Uq5FGw(#$`0V{*q#xNaMB)Sgi|;>LNmgtyl^|XTDB^D!|oqoA6thM z+xaCDn_T!oD*u;}1ylHv-e6Y3qHPHY;R)9)!Ccg_l79aDHc_m>^`%;_UjY#k(qnbI zRcjZ$C^XpbExYIdOE9JPWY1C>YG2@mAVYTGzJ?+tD99036Tvat*`4NEpH!(e0$~US zn@ZsN_}PQ48AMZ4_}L>+w?MCvysW~!YB)EgFkO@LL+I_qr)&DbCvAE~&gIZsiBHzR z`Nak3uire+5l`0KO1vCek)ue5!=|pz$Q%LEs7pC+-$EfhjWp^`Q`nhpot!h2-oN*( z3TW{=ID;B-iHNp@yS~LZbs$KPiG|rC{`Pj@U01N2<8MzvV0SwlUZ_&*i;5r4PXKc( z0)e1}f6YR|U#R1oI6*FvtYb_;KyPR$!Cw>_6=16qqum0#dg_LK*7L~c96Z~>EHQaT zw3s>)=7&0f9NJ+0`Ih)_|bqs8ws(d(bJcsO5 zo8?T(n%JFa-b0ua8LE846Kn?3^?XiyWN7Q}OvpnY$Dhe4ABHk%6GMN6^pd#Qk*!&!P+<=bhJ3@Vn)K-`2o4>D zaZBbDzzN`Rf0*!%7fkir{j50OPZr042ziq@ZCn4hgsq(t${voE zyDlVho)873odra25K|gjoJd(oR^1^-NL?OyD^yA(Y$lr{j^-o@ZGkJ+X z=iS@{XpUPy^Ye0J6+f?OA|jYbPb4cUL$qm!{iI-xS{_LgUmUfH?3E0%>VmHLB|W1b zpQPW)!`Ai)!Ou+I?-`O7#gYGvsdJ-CDOJ(1VZLAB`%MTf;A3q z2?3{b!IwrYHm{=Au}3p8Mvx;?X3#Ca70(bVo*|sL+lWp_9XMsVD%G~pmHfEfcORki zNMlCHt%PK@2lVT(69ioSiq7Xq*IA;Yx52<5+AWHya1hejiVcTucfdJAV0_z=ZG`Cz z`~!jPk7ER!J{71r^TUBs4HFAzl{^Jd4*@31grhqQCpdy>X9s?~q@7k7eU#H?hlCix z6@8l>g{_cwvL?LJJHq|MRRJq1&QIRG$+Q)yabwfbo!3Y?3sfj%3RkT<9YK)Z}OW6@HDk9|@qL zv*A7!dH078bRZCD6ZJSsl>9)LsdjoQ3|w$^0&Yfd%v92v3HZ8N#UlqZn+HWWMNnSx zJ@=2cS4LFjv{DD;S7GWV8&)mLmq>AlmqBV@2*BB{82b~+2r&2 z33%f+8LeU3d{J7z=VbmHl5`uA-!`UHFu3dlz>$7E)q+gG`-NzR>KP^ABHhzx#Xe8K zbWN#0Y^o)liyE!f@__N!RvETA?&*J{r_EYBlk^JUyXE*)ixi%nzvB9ZDEWo>J!J5# zIpL);r}-HUXa&GM*UAAvH)5G;vGs}8F{0!g`K^Vi)SopSawHq=fwkhHTx6Ov1exYv zFHAENdqlPiM+#S}xK>B*3aT4iBzL|__Ccy-=JXtEITg$en;iT52(W5^gWpN(d>r!i z50Zx6J%@qMJO`|E0yr=B4H*aLlR;#9_sG@fa5pb8o^E8P8`d|*ye_{d$ zex7gzA04gE$L!ot&m=1jD#ICpTd{4JGr&u11b8$^hBC#i&dO5dV!a*d`G~FHhw%MZ zB9fK?FmQjuIXE=6 zH{Q}+E~&@?=uY7`ryY#Wgd^{ z)=C4vV5Zfqo)xb9%-c}{S14~~l5Tb`Z#j7fu4pr==S3p2({O75o<$4cAZ8)WdQTG0zv>Xr(S~L`A5DLP||W3gig16UJDP)ot)m9&MU zDkERm67?CW>fr9frocUYu8s~^+O${$6Vr!cCRP8VhAob+XYGp}EA7F#l0E8n&7ScJ zayX;c9=Wp}5P1!3>iv8B*p!~pT+{Uou;^s^StE&Br>9UdCS2HDxJ?zOJnv!8J0@=IXR&Fr8f$||sN#a3`kd{~64 zOmKmWGZ*~!@>f*c$R6gn^R4+RUX;)5h;VARV_)*ExuoP>5+X$ z5I{1b-3Lv|)<}==!NDVTL}g|pQbnkA7k`DOFMC+(4#Vq1E8fz7&kw8t`Qet7v?a9Z zD1S0Ht4YC!c>r6pLeuj+2;}N+8;qwMA3jI54JJds5ac<>iE_jFI;|Soc16WK@CpfO ztaxqxD*O374NDze@1O_X1^bX4uY=7`F6%9(cMF@>%138Qo26~7 z;hb)cXC)GQWen20QF)LL454wI+qf)V5(4tPe`;efbxEqy9Y|m+fK*WNNG?Apqy#7S zc(B0ssg<$ytuv)ssT7yiIvQswYib>pCB|9gDZ_qtwYhaBTqUx{_aLhsV?~c`z>Rj` zNXQYS@7pIacWD?E0ka^{;ruWFe&wE#;zmWm53n_#4=XrvXL>*BETbN-Yd1_Dh=g?! zsf&PqL0NTuj6vG%Odhn(Xb@yab?T+qgQsr&BaP#6|L@0I8>ePJn%5y;@p1&emQofm zcDX{jx$$xMuI6}xIDYBvLLN)?m)+?VW1lSFS;J-SUn%x{Wkj$~U|A#D=w&)};PyMt zuU}j{Eu5NjQ+N6FFBb-Gp1aPyXdl=vpYr{~d(%X3(r@<}pHw{<{ek*Hit?>_#bLoe z6FDR4a$aKP7Br}TEd0*#YU8*6T$Qlu$Vl4Jua9^9?X$T*d+YAx^B;UipQE4!pa1TM ze$78_KFQX1_#`q9G&MAtic`TzU3+7Ehe zE*PJ-oKmG@G5<&Qx3!NK4TT&#(tT-O$%TR+VLTGrr5`60yiNX*)C!lZ?NOQ12tL?f zNC@XgW!Ao&*g`n36r^S5Xfs>vLqF} zl`Lf*wt_E+aMmg?fDqXdP+}sG{}l+rb5g_83i#U3q?=on6;l} zNupLt%lWE3&Qj=BrMeO#aw}j}X>v@}sT!p+t%i>oozqNwlcF~FP^fWD%NkuFVMA~F zQme?yN-*-VTIU2vky*RjR%ByhOQKFnC^Jo8rlzWD`Iy03W#XT%GD*OluC2_bx-5xS zD>d-38fPiw)zt2@p*5zrr0cd4sT$Ll&tbpZ4tI+`n8aKEd%PDW@aDAS=p?_#`=k%> ziYM_l{))F{7%=<2O_($xZ`hDSiF4By^ZCKfX#v2;AqsHEY%8l)5fYNMRTacf2ylkQ zL~UqDre8GSyozjLWoAf7YLYaNuL^LM!ngDgiC+}qyu#MDAtBn`08<)3!VC#62`8Mx z1pI)U)BtUnvM^ycA1qBntHGV!Wd!mT*{mT7?dMrL`4#*GjWZvLk$WKr>^A-GBmQtSXNaBN1lWm%s zqT0ddXF97g`B@UO0&8*-?FgkQ_1%!g(p0`m;w+W$Qv=e8aa$dl04(l3 zu*YV!e)Nzru30<#Tw_gtgW#_D=``PJNXHjd_{VeDSdF<`Aq?4%E{W1 zY6=NdeaI)F1f>5s?kphNDC=WWfVMm#3uxjiC;NAP^HUYE^_P19I9YFenR60a-f|lzgb}ibQ2*&T+B?zsFFJ zc!SqfDZUlfkjazL!4zH5?%elhMG zVZ)KOmLEk#$8x0W`Pwf;no!NSb0~=>RU?Hj_oL%D3G4Y;e-mYfmX0IV&Zo^xfG^jg zH*i$z`O0FEBJ`7SA$Jl})SIfcd@%l#wjMBvB%zAmVCtN}v{$65rG(0UgDYwR*EYcQ z(YSEUB(|tG0pA3+JiwOuYpH;aBNMp(B9hdGhNk}pThs)$Qjw-5C-g7B!M5r-Y{}z7 zzZYPuO;_fH{&`%ah>DslUFy54<$OUTCoP=c!itiF#!cXhTBVd|OVi+61lSDXM`?tK zp=-x$sn6;%fFJcHKbT`u6jBoc_*vUpTtYGrue2e}+D1Bs4$(wOcDiVIoBihuQrzE6 zDg5%>gYm0}D?VK3?@X>~e<2E~q`3GiFg<*v;Fd2jI!m#MProyldEsRYu zY>8M*SHuXmc&2XJ9232EUETDz7$SXitVMrzCaIa)urT}_J^AB?daAi?X0nnH1w@Bi z-l0V$f!4X6`4)v zQx}_#NU>bqD3#`}Q!ZaJs;p9n&y;oowpn5AGf}6JBA0$u1L-#x&xOS<>a?UNf`YVR zfsiKo(+0)zdOKOtG{xFuG+EO0nY&CmM!t#bElqujFS8q^m|sC(#%%fl|S#5qAE3yE91Gr-jgf|@#nqj z`Z7(%a}Tmp^Jai`4^=>tndb^yaC+Vx?UBP~rRkjA{3H051{u#aL?IKq%6K#QuuKro zfK18&8Shnpv3oF6#+!-S1?F z%auZ?fUr1cKCDOeUDKTvQcxSTVR>Pj5d3cqB~OU+Rjk<>qr*|JkgTRmR#pZ|1Fbx% zn2btMO2iDr+%>&Pf>kis;b5|u(RzoN-FX^4<nRI(XJ(XHMtwC6+2lquVQgl(X(C1EEhIX@6U3EYj5dt7d1oUf
RQa8* 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 Pv(sy@xEW&j7xn#LgnEZy literal 0 HcmV?d00001 diff --git a/src/mongoose-6.11/src/common/platforms/esp32/rom/rom_functions.S b/src/mongoose-6.11/src/common/platforms/esp32/rom/rom_functions.S new file mode 100644 index 0000000..ec3b76f --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/rom/rom_functions.S @@ -0,0 +1,1852 @@ +// From esp-idf/components/esp32/ld/esp32.rom.ld: +// sort -k 5 esp32.rom.ld | perl -npe 's/=/,/; s/;//' + +PROVIDE ( __month_lengths , 0x3ff9609c ) +PROVIDE ( __ctype_ptr__ , 0x3ff96350 ) +PROVIDE ( _ctype_ , 0x3ff96354 ) +PROVIDE ( __sf_fake_stderr , 0x3ff96458 ) +PROVIDE ( __sf_fake_stdout , 0x3ff96478 ) +PROVIDE ( __sf_fake_stdin , 0x3ff96498 ) +PROVIDE ( __mb_cur_max , 0x3ff96530 ) +PROVIDE ( __wctomb , 0x3ff96540 ) +PROVIDE ( __nsau_data , 0x3ff96544 ) +PROVIDE ( __popcount_tab , 0x3ff96544 ) +PROVIDE ( lmp_ext_desc_tab , 0x3ff96d9c ) +PROVIDE ( lmp_desc_tab , 0x3ff96e6c ) +PROVIDE ( co_sca2ppm , 0x3ff971e8 ) +PROVIDE ( one_bits , 0x3ff971f8 ) +PROVIDE ( dbg_default_handler , 0x3ff97218 ) +PROVIDE ( ecc_Jacobian_InfinityPoint256 , 0x3ff972e8 ) +PROVIDE ( veryBigHexP256 , 0x3ff9736c ) +PROVIDE ( bigHexP256 , 0x3ff973bc ) +PROVIDE ( DebugE256SecretKey , 0x3ff973e8 ) +PROVIDE ( DebugE256PublicKey_y , 0x3ff97408 ) +PROVIDE ( DebugE256PublicKey_x , 0x3ff97428 ) +PROVIDE ( maxSecretKey_256 , 0x3ff97448 ) +PROVIDE ( BasePoint_y_256 , 0x3ff97468 ) +PROVIDE ( BasePoint_x_256 , 0x3ff97488 ) +PROVIDE ( hci_evt_le_desc_tab , 0x3ff974b4 ) +PROVIDE ( hci_evt_dbg_desc_tab , 0x3ff9750c ) +PROVIDE ( hci_evt_desc_tab , 0x3ff9751c ) +PROVIDE ( hci_cmd_desc_root_tab , 0x3ff976d4 ) +PROVIDE ( hci_cmd_desc_tab_vs , 0x3ff97714 ) +PROVIDE ( hci_cmd_desc_tab_le , 0x3ff97870 ) +PROVIDE ( hci_cmd_desc_tab_testing , 0x3ff97a98 ) +PROVIDE ( hci_cmd_desc_tab_stat_par , 0x3ff97ac8 ) +PROVIDE ( hci_cmd_desc_tab_info_par , 0x3ff97b1c ) +PROVIDE ( hci_cmd_desc_tab_ctrl_bb , 0x3ff97b70 ) +PROVIDE ( hci_cmd_desc_tab_lk_pol , 0x3ff97f3c ) +PROVIDE ( hci_cmd_desc_tab_lk_ctrl , 0x3ff97fc0 ) +PROVIDE ( lb_default_handler , 0x3ff982b8 ) +PROVIDE ( lc_default_handler , 0x3ff98648 ) +PROVIDE ( ld_pcm_settings_dft , 0x3ff98a0c ) +PROVIDE ( ld_acl_edr_sizes , 0x3ff98a14 ) +PROVIDE ( ld_acl_edr_types , 0x3ff98a22 ) +PROVIDE ( ld_acl_br_sizes , 0x3ff98a2a ) +PROVIDE ( ld_acl_br_types , 0x3ff98a36 ) +PROVIDE ( ld_sync_train_channels , 0x3ff98a3c ) +PROVIDE ( llc_default_handler , 0x3ff98b3c ) +PROVIDE ( lld_pdu_llcp_pk_desc_tab , 0x3ff98b68 ) +PROVIDE ( lld_pdu_adv_pk_desc_tab , 0x3ff98c70 ) +PROVIDE ( llm_local_data_len_values , 0x3ff98d1c ) +PROVIDE ( llm_local_le_states , 0x3ff98d28 ) +PROVIDE ( llm_local_le_feats , 0x3ff98d30 ) +PROVIDE ( llm_local_cmds , 0x3ff98d38 ) +PROVIDE ( llm_default_handler , 0x3ff98d80 ) +PROVIDE ( LLM_AA_CT2 , 0x3ff98d88 ) +PROVIDE ( LLM_AA_CT1 , 0x3ff98d8a ) +PROVIDE ( lm_default_handler , 0x3ff990e0 ) +PROVIDE ( lm_n_page_tab , 0x3ff990e8 ) +PROVIDE ( lm_local_supp_feats , 0x3ff990ee ) +PROVIDE ( rwip_coex_cfg , 0x3ff9914c ) +PROVIDE ( rwip_priority , 0x3ff99159 ) +PROVIDE ( exc_cause_table , 0x3ff991d0 ) +PROVIDE ( spi_modes , 0x3ff99270 ) +PROVIDE ( sha_blk_bits_bytes , 0x3ff99288 ) +PROVIDE ( sha_blk_hash_bytes , 0x3ff9928c ) +PROVIDE ( sha_blk_bits , 0x3ff99290 ) +PROVIDE ( dh_group18_prime , 0x3ff9a0dc ) +PROVIDE ( dh_group18_generator , 0x3ff9a4dc ) +PROVIDE ( dh_group17_prime , 0x3ff9a4dd ) +PROVIDE ( dh_group17_generator , 0x3ff9a7dd ) +PROVIDE ( dh_group16_prime , 0x3ff9a7de ) +PROVIDE ( dh_group16_generator , 0x3ff9a9de ) +PROVIDE ( dh_group15_prime , 0x3ff9a9df ) +PROVIDE ( dh_group15_generator , 0x3ff9ab5f ) +PROVIDE ( dh_group14_prime , 0x3ff9ab60 ) +PROVIDE ( dh_group14_generator , 0x3ff9ac60 ) +PROVIDE ( dh_group5_prime , 0x3ff9ac61 ) +PROVIDE ( dh_group5_generator , 0x3ff9ad21 ) +PROVIDE ( dh_group2_prime , 0x3ff9ad22 ) +PROVIDE ( dh_group2_generator , 0x3ff9ada2 ) +PROVIDE ( dh_group1_prime , 0x3ff9ada3 ) +PROVIDE ( dh_group1_generator , 0x3ff9ae03 ) +PROVIDE ( Xthal_intlevel , 0x3ff9c2b4 ) +PROVIDE ( syscall_table_ptr_app , 0x3ffae020 ) +PROVIDE ( syscall_table_ptr_pro , 0x3ffae024 ) +PROVIDE ( _tzname , 0x3ffae030 ) +PROVIDE ( _timezone , 0x3ffae0a0 ) +PROVIDE ( _daylight , 0x3ffae0a4 ) +PROVIDE ( _global_impure_ptr , 0x3ffae0b0 ) +PROVIDE ( environ , 0x3ffae0b4 ) +PROVIDE ( rom_phyFuns , 0x3ffae0c0 ) +PROVIDE ( g_phyFuns_instance , 0x3ffae0c4 ) +PROVIDE ( g_rom_flashchip , 0x3ffae270 ) +PROVIDE ( SPI_flashchip_data , 0x3ffae270 ) +PROVIDE ( dummy_len_plus , 0x3ffae290 ) +PROVIDE ( sig_matrix , 0x3ffae293 ) +PROVIDE ( r_btdm_option_data , 0x3ffae6e0 ) +PROVIDE ( _data_start_btdm , 0x3ffae6e0) +PROVIDE ( co_default_bdaddr , 0x3ffae704 ) +PROVIDE ( r_ip_funcs_p , 0x3ffae70c ) +PROVIDE ( r_ip_funcs , 0x3ffae710 ) +PROVIDE ( r_import_rf_phy_func_p , 0x3ffafd64 ) +PROVIDE ( r_modules_funcs_p , 0x3ffafd68 ) +PROVIDE ( r_modules_funcs , 0x3ffafd6c ) +PROVIDE ( _data_end_btdm , 0x3ffaff10) +PROVIDE ( _bss_start_btdm , 0x3ffb8000) +PROVIDE ( co_null_bdaddr , 0x3ffb80e0 ) +PROVIDE ( ld_sco_env , 0x3ffb824c ) +PROVIDE ( ld_acl_env , 0x3ffb8258 ) +PROVIDE ( ld_bcst_acl_env , 0x3ffb8274 ) +PROVIDE ( ld_csb_rx_env , 0x3ffb8278 ) +PROVIDE ( ld_csb_tx_env , 0x3ffb827c ) +PROVIDE ( ld_fm_env , 0x3ffb8284 ) +PROVIDE ( ld_inq_env , 0x3ffb82e4 ) +PROVIDE ( ld_iscan_env , 0x3ffb82e8 ) +PROVIDE ( ld_page_env , 0x3ffb82f0 ) +PROVIDE ( ld_pca_env , 0x3ffb82f4 ) +PROVIDE ( ld_pscan_env , 0x3ffb8308 ) +PROVIDE ( ld_sched_env , 0x3ffb830c ) +PROVIDE ( ld_sscan_env , 0x3ffb832c ) +PROVIDE ( ld_strain_env , 0x3ffb8330 ) +PROVIDE ( ld_active_ch_map , 0x3ffb8334 ) +PROVIDE ( r_import_rf_phy_func , 0x3ffb8354 ) +PROVIDE ( r_plf_funcs_p , 0x3ffb8360 ) +PROVIDE ( RFPLL_ICP_TABLE , 0x3ffb8b7c ) +PROVIDE ( bt_util_buf_env , 0x3ffb8bd4 ) +PROVIDE ( sw_to_hw , 0x3ffb8d40 ) +PROVIDE ( dbg_state , 0x3ffb8d5d ) +PROVIDE ( ecc_env , 0x3ffb8d60 ) +PROVIDE ( em_buf_env , 0x3ffb8d74 ) +PROVIDE ( hci_fc_env , 0x3ffb9340 ) +PROVIDE ( hci_env , 0x3ffb9350 ) +PROVIDE ( ke_env , 0x3ffb93cc ) +PROVIDE ( lb_env , 0x3ffb9424 ) +PROVIDE ( lb_state , 0x3ffb94e8 ) +PROVIDE ( lc_env , 0x3ffb94ec ) +PROVIDE ( lc_state , 0x3ffb9508 ) +PROVIDE ( ld_env , 0x3ffb9510 ) +PROVIDE ( ld_env , 0x3ffb9510 ) +PROVIDE ( ld_sched_params , 0x3ffb96c0 ) +PROVIDE ( ld_sched_params , 0x3ffb96c0 ) +PROVIDE ( llc_env , 0x3ffb96d0 ) +PROVIDE ( llc_state , 0x3ffb96f8 ) +PROVIDE ( lld_evt_env , 0x3ffb9704 ) +PROVIDE ( llm_le_env , 0x3ffb976c ) +PROVIDE ( llm_state , 0x3ffb985c ) +PROVIDE ( lm_env , 0x3ffb9860 ) +PROVIDE ( lm_state , 0x3ffb9a1c ) +PROVIDE ( rwip_rf , 0x3ffbdb28 ) +PROVIDE ( _bss_end_btdm , 0x3ffbff70) +PROVIDE ( gpio_pending_mask , 0x3ffe0038 ) +PROVIDE ( app_gpio_arg , 0x3ffe003c ) +PROVIDE ( app_gpio_handler , 0x3ffe0040 ) +PROVIDE ( gpio_pending_mask_high , 0x3ffe0044 ) +PROVIDE ( gTxMsg , 0x3ffe0050 ) +PROVIDE ( RecvBuff , 0x3ffe009c ) +PROVIDE ( UartDev , 0x3ffe019c ) +PROVIDE ( ets_readySet_ , 0x3ffe01f0 ) +PROVIDE ( ets_intr_count , 0x3ffe03fc ) +PROVIDE ( user_code_start , 0x3ffe0400 ) +PROVIDE ( ets_startup_callback , 0x3ffe0404 ) +PROVIDE ( debug_timer , 0x3ffe042c ) +PROVIDE ( debug_timerfn , 0x3ffe0430 ) +PROVIDE ( _xtos_exc_handler_table , 0x3ffe0448 ) +PROVIDE ( _xtos_c_handler_table , 0x3ffe0548 ) +PROVIDE ( _Pri_4_HandlerAddress , 0x3ffe0648 ) +PROVIDE ( _Pri_5_HandlerAddress , 0x3ffe064c ) +PROVIDE ( _xtos_enabled , 0x3ffe0650 ) +PROVIDE ( _xtos_intstruct , 0x3ffe0650 ) +PROVIDE ( _xtos_vpri_enabled , 0x3ffe0654 ) +PROVIDE ( _xtos_interrupt_table , 0x3ffe0658 ) +PROVIDE ( _xtos_interrupt_mask_table , 0x3ffe0758 ) +PROVIDE ( _stack_sentry , 0x3ffe1320 ) +PROVIDE ( __stack , 0x3ffe3f20 ) +PROVIDE ( _stack_sentry_app , 0x3ffe5230 ) +PROVIDE ( __stack_app , 0x3ffe7e30 ) +PROVIDE ( _WindowOverflow4 , 0x40000000 ) +PROVIDE ( _xtos_alloca_handler , 0x40000010 ) +PROVIDE ( _WindowUnderflow4 , 0x40000040 ) +PROVIDE ( _WindowOverflow8 , 0x40000080 ) +PROVIDE ( _WindowUnderflow8 , 0x400000c0 ) +PROVIDE ( _WindowOverflow12 , 0x40000100 ) +PROVIDE ( _WindowUnderflow12 , 0x40000140 ) +PROVIDE ( _Level2Vector , 0x40000180 ) +PROVIDE ( _Level3Vector , 0x400001c0 ) +PROVIDE ( _Level4Vector , 0x40000200 ) +PROVIDE ( _Level5Vector , 0x40000240 ) +PROVIDE ( _DebugExceptionVector , 0x40000280 ) +PROVIDE ( _NMIExceptionVector , 0x400002c0 ) +PROVIDE ( _KernelExceptionVector , 0x40000300 ) +PROVIDE ( _UserExceptionVector , 0x40000340 ) +PROVIDE ( _DoubleExceptionVector , 0x400003c0 ) +PROVIDE ( _ResetVector , 0x40000400 ) +PROVIDE ( _ResetHandler , 0x40000450 ) +PROVIDE ( _stext , 0x40000560 ) +PROVIDE ( _start , 0x40000704 ) +PROVIDE ( _xtos_set_exception_handler , 0x4000074c ) +PROVIDE ( _xtos_syscall_handler , 0x40000790 ) +PROVIDE ( _SyscallException , 0x400007cf ) +PROVIDE ( _xtos_l1int_handler , 0x40000814 ) +PROVIDE ( _LevelOneInterrupt , 0x40000835 ) +PROVIDE ( _xtos_restore_intlevel , 0x40000928 ) +PROVIDE ( _xtos_set_vpri , 0x40000934 ) +PROVIDE ( _Level2FromVector , 0x40000954 ) +PROVIDE ( _Level3FromVector , 0x40000a28 ) +PROVIDE ( _Level4FromVector , 0x40000af8 ) +PROVIDE ( _Level5FromVector , 0x40000c68 ) +PROVIDE ( _xtos_cause3_handler , 0x40000dd8 ) +PROVIDE ( _xtos_c_wrapper_handler , 0x40000de8 ) +PROVIDE ( _GeneralException , 0x40000e14 ) +PROVIDE ( creat , 0x40000e8c ) +PROVIDE ( _isatty_r , 0x40000ea0 ) +PROVIDE ( asctime_r , 0x40000ec8 ) +PROVIDE ( isalnum , 0x40000f04 ) +PROVIDE ( isalpha , 0x40000f18 ) +PROVIDE ( isblank , 0x40000f2c ) +PROVIDE ( iscntrl , 0x40000f50 ) +PROVIDE ( isdigit , 0x40000f64 ) +PROVIDE ( islower , 0x40000f78 ) +PROVIDE ( isgraph , 0x40000f94 ) +PROVIDE ( isprint , 0x40000fa8 ) +PROVIDE ( ispunct , 0x40000fc0 ) +PROVIDE ( isspace , 0x40000fd4 ) +PROVIDE ( isupper , 0x40000fe8 ) +PROVIDE ( srand , 0x40001004 ) +PROVIDE ( rand , 0x40001058 ) +PROVIDE ( rand_r , 0x400010d4 ) +PROVIDE ( __sread , 0x40001118 ) +PROVIDE ( __seofread , 0x40001148 ) +PROVIDE ( __swrite , 0x40001150 ) +PROVIDE ( __sseek , 0x40001184 ) +PROVIDE ( __sclose , 0x400011b8 ) +PROVIDE ( strcasecmp , 0x400011cc ) +PROVIDE ( strcasestr , 0x40001210 ) +PROVIDE ( strcmp , 0x40001274 ) +PROVIDE ( strcoll , 0x40001398 ) +PROVIDE ( strcpy , 0x400013ac ) +PROVIDE ( strdup , 0x4000143c ) +PROVIDE ( _strdup_r , 0x40001450 ) +PROVIDE ( strlcat , 0x40001470 ) +PROVIDE ( strlen , 0x400014c0 ) +PROVIDE ( strlwr , 0x40001524 ) +PROVIDE ( strncasecmp , 0x40001550 ) +PROVIDE ( strncpy , 0x400015d4 ) +PROVIDE ( strndup , 0x400016b0 ) +PROVIDE ( _strndup_r , 0x400016c4 ) +PROVIDE ( strrchr , 0x40001708 ) +PROVIDE ( strsep , 0x40001734 ) +PROVIDE ( strupr , 0x4000174c ) +PROVIDE ( close , 0x40001778 ) +PROVIDE ( open , 0x4000178c ) +PROVIDE ( read , 0x400017dc ) +PROVIDE ( sbrk , 0x400017f4 ) +PROVIDE ( times , 0x40001808 ) +PROVIDE ( write , 0x4000181c ) +PROVIDE ( __get_current_time_locale , 0x40001834 ) +PROVIDE ( __time_load_locale , 0x4000183c ) +PROVIDE ( time , 0x40001844 ) +PROVIDE ( tolower , 0x40001868 ) +PROVIDE ( toupper , 0x40001884 ) +PROVIDE ( __tzcalc_limits , 0x400018a0 ) +PROVIDE ( __tz_lock , 0x40001a04 ) +PROVIDE ( __tz_unlock , 0x40001a10 ) +PROVIDE ( tzset , 0x40001a1c ) +PROVIDE ( _tzset_r , 0x40001a28 ) +PROVIDE ( _cleanup_r , 0x40001d48 ) +PROVIDE ( __sfmoreglue , 0x40001dc8 ) +PROVIDE ( _cleanup , 0x40001df8 ) +PROVIDE ( __sfp_lock_acquire , 0x40001e08 ) +PROVIDE ( __sfp_lock_release , 0x40001e14 ) +PROVIDE ( __sinit_lock_acquire , 0x40001e20 ) +PROVIDE ( __sinit_lock_release , 0x40001e2c ) +PROVIDE ( __sinit , 0x40001e38 ) +PROVIDE ( __sfp , 0x40001e90 ) +PROVIDE ( __fp_lock_all , 0x40001f1c ) +PROVIDE ( __fp_unlock_all , 0x40001f30 ) +PROVIDE ( _findenv_r , 0x40001f44 ) +PROVIDE ( _getenv_r , 0x40001fbc ) +PROVIDE ( __gettzinfo , 0x40001fcc ) +PROVIDE ( __env_lock , 0x40001fd4 ) +PROVIDE ( __env_unlock , 0x40001fe0 ) +PROVIDE ( _fclose_r , 0x40001fec ) +PROVIDE ( fclose , 0x400020ac ) +PROVIDE ( __negsf2 , 0x400020c0 ) +PROVIDE ( __addsf3 , 0x400020e8 ) +PROVIDE ( __subsf3 , 0x400021d0 ) +PROVIDE ( __divsf3 , 0x4000234c ) +PROVIDE ( __fixsfsi , 0x4000240c ) +PROVIDE ( __fixsfdi , 0x4000244c ) +PROVIDE ( __fixunssfsi , 0x400024ac ) +PROVIDE ( __fixunssfdi , 0x40002504 ) +PROVIDE ( __adddf3 , 0x40002590 ) +PROVIDE ( __subdf3 , 0x400026e4 ) +PROVIDE ( __divdf3 , 0x40002954 ) +PROVIDE ( __fixdfsi , 0x40002a78 ) +PROVIDE ( __fixdfdi , 0x40002ac4 ) +PROVIDE ( __fixunsdfsi , 0x40002b30 ) +PROVIDE ( __truncdfsf2 , 0x40002b90 ) +PROVIDE ( __extendsfdf2 , 0x40002c34 ) +PROVIDE ( __addvsi3 , 0x40002c98 ) +PROVIDE ( __addvdi3 , 0x40002cbc ) +PROVIDE ( __subvsi3 , 0x40002cf8 ) +PROVIDE ( __subvdi3 , 0x40002d20 ) +PROVIDE ( __mulvsi3 , 0x40002d60 ) +PROVIDE ( __mulvdi3 , 0x40002d78 ) +PROVIDE ( __negvsi2 , 0x40002e78 ) +PROVIDE ( __negvdi2 , 0x40002e98 ) +PROVIDE ( __popcountsi2 , 0x40002ed0 ) +PROVIDE ( __popcountdi2 , 0x40002ef8 ) +PROVIDE ( __paritysi2 , 0x40002f3c ) +PROVIDE ( rom_phy_disable_agc , 0x40002f6c ) +PROVIDE ( rom_phy_enable_agc , 0x40002f88 ) +PROVIDE ( rom_disable_agc , 0x40002fa4 ) +PROVIDE ( rom_enable_agc , 0x40002fcc ) +PROVIDE ( rom_phy_disable_cca , 0x40003000 ) +PROVIDE ( rom_phy_enable_cca , 0x4000302c ) +PROVIDE ( rom_pow_usr , 0x40003044 ) +PROVIDE ( rom_set_loopback_gain , 0x40003060 ) +PROVIDE ( rom_set_cal_rxdc , 0x400030b8 ) +PROVIDE ( rom_loopback_mode_en , 0x400030f8 ) +PROVIDE ( rom_get_data_sat , 0x4000312c ) +PROVIDE ( rom_set_pbus_mem , 0x400031a4 ) +PROVIDE ( rom_write_gain_mem , 0x4000348c ) +PROVIDE ( rom_rx_gain_force , 0x4000351c ) +PROVIDE ( rom_set_txclk_en , 0x40003564 ) +PROVIDE ( rom_set_rxclk_en , 0x40003594 ) +PROVIDE ( rom_start_tx_tone_step , 0x400035d0 ) +PROVIDE ( rom_start_tx_tone , 0x400036b4 ) +PROVIDE ( rom_clk_force_on_vit , 0x40003710 ) +PROVIDE ( rom_bb_rx_ht20_cen_bcov_en , 0x40003734 ) +PROVIDE ( rom_bb_tx_ht20_cen , 0x40003760 ) +PROVIDE ( rom_spur_reg_write_one_tone , 0x400037f0 ) +PROVIDE ( rom_spur_coef_cfg , 0x40003ac8 ) +PROVIDE ( rom_bb_wdg_test_en , 0x40003b70 ) +PROVIDE ( rom_bb_bss_cbw40_dig , 0x40003bac ) +PROVIDE ( rom_noise_floor_auto_set , 0x40003bdc ) +PROVIDE ( rom_phy_get_noisefloor , 0x40003c2c ) +PROVIDE ( rom_check_noise_floor , 0x40003c78 ) +PROVIDE ( rom_set_noise_floor , 0x40003d48 ) +PROVIDE ( rom_chip_v7_rx_rifs_en , 0x40003d90 ) +PROVIDE ( rom_rtc_mem_backup , 0x40003db4 ) +PROVIDE ( rom_rtc_mem_recovery , 0x40003df4 ) +PROVIDE ( rom_gen_rx_gain_table , 0x40003e3c ) +PROVIDE ( rom_stop_tx_tone , 0x40003f98 ) +PROVIDE ( rom_bb_bss_bw_40_en , 0x4000401c ) +PROVIDE ( rom_mhz2ieee , 0x4000404c ) +PROVIDE ( rom_cbw2040_cfg , 0x400040b0 ) +PROVIDE ( phy_get_romfuncs , 0x40004100 ) +PROVIDE ( rom_chip_i2c_readReg , 0x40004110 ) +PROVIDE ( rom_i2c_readReg , 0x40004148 ) +PROVIDE ( rom_chip_i2c_writeReg , 0x40004168 ) +PROVIDE ( rom_i2c_writeReg , 0x400041a4 ) +PROVIDE ( rom_i2c_readReg_Mask , 0x400041c0 ) +PROVIDE ( rom_i2c_writeReg_Mask , 0x400041fc ) +PROVIDE ( rom_pbus_force_mode , 0x40004270 ) +PROVIDE ( rom_pbus_rd_addr , 0x40004334 ) +PROVIDE ( rom_pbus_rd_shift , 0x40004374 ) +PROVIDE ( rom_pbus_force_test , 0x400043c0 ) +PROVIDE ( rom_pbus_rd , 0x40004414 ) +PROVIDE ( rom_pbus_debugmode , 0x40004458 ) +PROVIDE ( rom_pbus_workmode , 0x4000446c ) +PROVIDE ( rom_pbus_set_rxgain , 0x40004480 ) +PROVIDE ( rom_pbus_xpd_rx_off , 0x40004508 ) +PROVIDE ( rom_pbus_xpd_rx_on , 0x4000453c ) +PROVIDE ( rom_pbus_xpd_tx_off , 0x40004590 ) +PROVIDE ( rom_pbus_xpd_tx_on , 0x400045e0 ) +PROVIDE ( rom_pbus_set_dco , 0x40004638 ) +PROVIDE ( rom_rfpll_reset , 0x40004680 ) +PROVIDE ( rom_restart_cal , 0x400046e0 ) +PROVIDE ( rom_write_rfpll_sdm , 0x40004740 ) +PROVIDE ( rom_wait_rfpll_cal_end , 0x400047a8 ) +PROVIDE ( rom_rfpll_set_freq , 0x400047f8 ) +PROVIDE ( rom_set_channel_freq , 0x40004880 ) +PROVIDE ( rom_phy_freq_correct , 0x40004b44 ) +PROVIDE ( rom_set_rf_freq_offset , 0x40004ca8 ) +PROVIDE ( rom_chip_v7_rx_init , 0x40004cec ) +PROVIDE ( rom_chip_v7_tx_init , 0x40004d18 ) +PROVIDE ( rom_chip_v7_bt_init , 0x40004d8c ) +PROVIDE ( rom_txbbgain_to_index , 0x40004dc0 ) +PROVIDE ( rom_index_to_txbbgain , 0x40004df8 ) +PROVIDE ( rom_txdc_cal_init , 0x40004e10 ) +PROVIDE ( rom_txdc_cal_v70 , 0x40004ea4 ) +PROVIDE ( rom_en_pwdet , 0x4000506c ) +PROVIDE ( rom_txcal_work_mode , 0x4000510c ) +PROVIDE ( rom_txiq_set_reg , 0x40005154 ) +PROVIDE ( rom_read_sar_dout , 0x400051c0 ) +PROVIDE ( rom_get_fm_sar_dout , 0x40005204 ) +PROVIDE ( rom_txtone_linear_pwr , 0x40005290 ) +PROVIDE ( rom_txiq_get_mis_pwr , 0x400052dc ) +PROVIDE ( rom_txiq_cover , 0x4000538c ) +PROVIDE ( rom_abs_temp , 0x400054f0 ) +PROVIDE ( rom_iq_est_enable , 0x40005514 ) +PROVIDE ( rom_iq_est_disable , 0x40005590 ) +PROVIDE ( rom_dc_iq_est , 0x400055c8 ) +PROVIDE ( rom_pbus_rx_dco_cal , 0x40005620 ) +PROVIDE ( rom_rxiq_get_mis , 0x400058e4 ) +PROVIDE ( rom_rxiq_set_reg , 0x40005a00 ) +PROVIDE ( rom_rxiq_cover_mg_mp , 0x40005a68 ) +PROVIDE ( rom_rfcal_rxiq , 0x40005b4c ) +PROVIDE ( rom_get_rfcal_rxiq_data , 0x40005bbc ) +PROVIDE ( rom_set_chan_cal_interp , 0x40005ce0 ) +PROVIDE ( rom_set_txcap_reg , 0x40005d50 ) +PROVIDE ( rom_rfcal_txcap , 0x40005dec ) +PROVIDE ( rom_linear_to_db , 0x40005f64 ) +PROVIDE ( rom_get_power_db , 0x40005fc8 ) +PROVIDE ( rom_meas_tone_pwr_db , 0x40006004 ) +PROVIDE ( rom_rfcal_pwrctrl , 0x40006058 ) +PROVIDE ( rom_tx_atten_set_interp , 0x400061cc ) +PROVIDE ( rom_target_power_add_backoff , 0x40006268 ) +PROVIDE ( rom_get_rf_gain_qdb , 0x40006290 ) +PROVIDE ( rom_correct_rf_ana_gain , 0x400062a8 ) +PROVIDE ( rom_phy_get_vdd33 , 0x4000642c ) +PROVIDE ( rom_get_sar_dout , 0x40006564 ) +PROVIDE ( rom_get_pwctrl_correct , 0x400065d4 ) +PROVIDE ( rom_tx_pwctrl_bg_init , 0x4000662c ) +PROVIDE ( ets_set_idle_cb , 0x40006674 ) +PROVIDE ( ets_task , 0x40006688 ) +PROVIDE ( ets_run , 0x400066bc ) +PROVIDE ( ets_post , 0x4000673c ) +PROVIDE ( ets_intr_lock , 0x400067b0 ) +PROVIDE ( ets_intr_unlock , 0x400067c4 ) +PROVIDE ( ets_waiti0 , 0x400067d8 ) +PROVIDE ( ets_isr_attach , 0x400067ec ) +PROVIDE ( ets_isr_mask , 0x400067fc ) +PROVIDE ( ets_isr_unmask , 0x40006808 ) +PROVIDE ( intr_matrix_set , 0x4000681c ) +PROVIDE ( ets_set_user_start , 0x4000687c ) +PROVIDE ( ets_set_startup_callback , 0x4000688c ) +PROVIDE ( ets_set_appcpu_boot_addr , 0x4000689c ) +PROVIDE ( check_pos , 0x400068b8 ) +PROVIDE ( ets_unpack_flash_code_legacy , 0x4000694c ) +PROVIDE ( ets_unpack_flash_code , 0x40007018 ) +PROVIDE ( rom_main , 0x400076c4 ) +PROVIDE ( ets_install_putc1 , 0x40007d18 ) +PROVIDE ( ets_install_uart_printf , 0x40007d28 ) +PROVIDE ( ets_install_putc2 , 0x40007d38 ) +PROVIDE ( ets_printf , 0x40007d54 ) +PROVIDE ( calc_rtc_memory_crc , 0x40008170 ) +PROVIDE ( rtc_get_reset_reason , 0x400081d4 ) +PROVIDE ( rtc_get_wakeup_cause , 0x400081f4 ) +PROVIDE ( set_rtc_memory_crc , 0x40008208 ) +PROVIDE ( rtc_boot_control , 0x4000821c ) +PROVIDE ( software_reset , 0x4000824c ) +PROVIDE ( software_reset_cpu , 0x40008264 ) +PROVIDE ( rtc_select_apb_bridge , 0x40008288 ) +PROVIDE ( ets_timer_setfn , 0x40008350 ) +PROVIDE ( ets_timer_arm , 0x40008368 ) +PROVIDE ( ets_timer_arm_us , 0x400083ac ) +PROVIDE ( ets_timer_disarm , 0x400083ec ) +PROVIDE ( ets_timer_done , 0x40008428 ) +PROVIDE ( ets_timer_handler_isr , 0x40008454 ) +PROVIDE ( ets_timer_init , 0x400084e8 ) +PROVIDE ( ets_delay_us , 0x40008534 ) +PROVIDE ( ets_update_cpu_frequency , 0x40008550 ) +PROVIDE ( ets_get_cpu_frequency , 0x4000855c ) +PROVIDE ( ets_get_xtal_scale , 0x4000856c ) +PROVIDE ( ets_get_detected_xtal_freq , 0x40008588 ) +PROVIDE ( ets_efuse_read_op , 0x40008600 ) +PROVIDE ( ets_efuse_program_op , 0x40008628 ) +PROVIDE ( ets_efuse_get_spiconfig , 0x40008658 ) +PROVIDE ( ets_efuse_get_8M_clock , 0x40008710 ) +PROVIDE ( UartConnCheck , 0x40008738 ) +PROVIDE ( FlashDwnLdStartMsgProc , 0x40008820 ) +PROVIDE ( FilePacketSendReqMsgProc , 0x40008860 ) +PROVIDE ( FlashDwnLdStopReqMsgProc , 0x400088ec ) +PROVIDE ( FlashDwnLdParamCfgMsgProc , 0x4000891c ) +PROVIDE ( MemDwnLdStartMsgProc , 0x40008948 ) +PROVIDE ( MemPacketSendReqMsgProc , 0x40008978 ) +PROVIDE ( MemDwnLdStopReqMsgProc , 0x400089dc ) +PROVIDE ( UartConnectProc , 0x40008a04 ) +PROVIDE ( UartRegWriteProc , 0x40008a14 ) +PROVIDE ( UartRegReadProc , 0x40008a58 ) +PROVIDE ( UartSpiAttachProc , 0x40008a6c ) +PROVIDE ( UartSpiReadProc , 0x40008a80 ) +PROVIDE ( UartSetBaudProc , 0x40008aac ) +PROVIDE ( FlashDwnLdDeflatedStartMsgProc , 0x40008ad8 ) +PROVIDE ( FilePacketSendDeflatedReqMsgProc , 0x40008b24 ) +PROVIDE ( FlashDwnLdStopDeflatedReqMsgProc , 0x40008c18 ) +PROVIDE ( VerifyFlashMd5Proc , 0x40008c44 ) +PROVIDE ( UartDwnLdProc , 0x40008ce8 ) +PROVIDE ( uart_rx_intr_handler , 0x40008f4c ) +PROVIDE ( uartAttach , 0x40008fd0 ) +PROVIDE ( uart_tx_switch , 0x40009028 ) +PROVIDE ( uart_baudrate_detect , 0x40009034 ) +PROVIDE ( uart_div_modify , 0x400090cc ) +PROVIDE ( Uart_Init , 0x40009120 ) +PROVIDE ( uart_tx_one_char , 0x40009200 ) +PROVIDE ( uart_tx_one_char2 , 0x4000922c ) +PROVIDE ( uart_tx_flush , 0x40009258 ) +PROVIDE ( uart_tx_wait_idle , 0x40009278 ) +PROVIDE ( uart_rx_one_char_block , 0x400092a4 ) +PROVIDE ( uart_rx_one_char , 0x400092d0 ) +PROVIDE ( UartRxString , 0x400092fc ) +PROVIDE ( send_packet , 0x40009340 ) +PROVIDE ( SendMsg , 0x40009384 ) +PROVIDE ( uart_rx_readbuff , 0x40009394 ) +PROVIDE ( uart_buff_switch , 0x400093c0 ) +PROVIDE ( recv_packet , 0x40009424 ) +PROVIDE ( RcvMsg , 0x4000954c ) +PROVIDE ( UartGetCmdLn , 0x40009564 ) +PROVIDE ( GetUartDevice , 0x40009598 ) +PROVIDE ( mmu_init , 0x400095a4 ) +PROVIDE ( cache_flash_mmu_set , 0x400095e0 ) +PROVIDE ( cache_sram_mmu_set , 0x400097f4 ) +PROVIDE ( Cache_Read_Init , 0x40009950 ) +PROVIDE ( Cache_Flush , 0x40009a14 ) +PROVIDE ( Cache_Read_Enable , 0x40009a84 ) +PROVIDE ( Cache_Read_Disable , 0x40009ab8 ) +PROVIDE ( gpio_output_set , 0x40009b24 ) +PROVIDE ( gpio_output_set_high , 0x40009b5c ) +PROVIDE ( gpio_input_get , 0x40009b88 ) +PROVIDE ( gpio_input_get_high , 0x40009b9c ) +PROVIDE ( gpio_register_set , 0x40009bbc ) +PROVIDE ( gpio_init , 0x40009c20 ) +PROVIDE ( gpio_register_get , 0x40009cbc ) +PROVIDE ( gpio_intr_pending , 0x40009cec ) +PROVIDE ( gpio_intr_pending_high , 0x40009cf8 ) +PROVIDE ( gpio_pin_intr_state_set , 0x40009d04 ) +PROVIDE ( gpio_intr_ack , 0x40009dd4 ) +PROVIDE ( gpio_intr_ack_high , 0x40009e1c ) +PROVIDE ( gpio_intr_handler_register , 0x40009e6c ) +PROVIDE ( gpio_pin_wakeup_enable , 0x40009e7c ) +PROVIDE ( gpio_pin_wakeup_disable , 0x40009eb0 ) +PROVIDE ( gpio_matrix_in , 0x40009edc ) +PROVIDE ( gpio_matrix_out , 0x40009f0c ) +PROVIDE ( gpio_pad_select_gpio , 0x40009fdc ) +PROVIDE ( gpio_pad_set_drv , 0x4000a11c ) +PROVIDE ( gpio_pad_pullup , 0x4000a22c ) +PROVIDE ( gpio_pad_pulldown , 0x4000a348 ) +PROVIDE ( gpio_pad_unhold , 0x4000a484 ) +PROVIDE ( gpio_pad_hold , 0x4000a734 ) +PROVIDE ( lldesc_build_chain , 0x4000a850 ) +PROVIDE ( lldesc_num2link , 0x4000a948 ) +PROVIDE ( lldesc_set_owner , 0x4000a974 ) +PROVIDE ( roundup2 , 0x4000ab7c ) +PROVIDE ( multofup , 0x4000ab8c ) +PROVIDE ( sip_alloc_to_host_evt , 0x4000ab9c ) +PROVIDE ( sip_to_host_evt_send_done , 0x4000ac04 ) +PROVIDE ( sip_reclaim_tx_data_pkt , 0x4000ad5c ) +PROVIDE ( sip_reclaim_from_host_cmd , 0x4000adbc ) +PROVIDE ( sip_install_rx_ctrl_cb , 0x4000ae10 ) +PROVIDE ( sip_install_rx_data_cb , 0x4000ae20 ) +PROVIDE ( sip_get_state , 0x4000ae2c ) +PROVIDE ( sip_init_attach , 0x4000ae58 ) +PROVIDE ( sip_post_init , 0x4000aed8 ) +PROVIDE ( sip_to_host_chain_append , 0x4000aef8 ) +PROVIDE ( sip_send , 0x4000af54 ) +PROVIDE ( sip_get_ptr , 0x4000b34c ) +PROVIDE ( sip_after_tx_complete , 0x4000b358 ) +PROVIDE ( sip_is_active , 0x4000b3c0 ) +PROVIDE ( slc_has_pkt_to_host , 0x4000b5fc ) +PROVIDE ( slc_reattach , 0x4000b62c ) +PROVIDE ( slc_enable , 0x4000b64c ) +PROVIDE ( slc_send_to_host_chain , 0x4000b6a0 ) +PROVIDE ( slc_to_host_chain_recycle , 0x4000b758 ) +PROVIDE ( slc_from_host_chain_fetch , 0x4000b7e8 ) +PROVIDE ( slc_set_host_io_max_window , 0x4000b89c ) +PROVIDE ( slc_init_attach , 0x4000b918 ) +PROVIDE ( slc_init_credit , 0x4000badc ) +PROVIDE ( slc_add_credits , 0x4000baf4 ) +PROVIDE ( slc_from_host_chain_recycle , 0x4000bb10 ) +PROVIDE ( abort , 0x4000bba4 ) +PROVIDE ( _malloc_r , 0x4000bbb4 ) +PROVIDE ( _free_r , 0x4000bbcc ) +PROVIDE ( _realloc_r , 0x4000bbe0 ) +PROVIDE ( _calloc_r , 0x4000bbf8 ) +PROVIDE ( _system_r , 0x4000bc10 ) +PROVIDE ( _rename_r , 0x4000bc28 ) +PROVIDE ( _times_r , 0x4000bc40 ) +PROVIDE ( _gettimeofday_r , 0x4000bc58 ) +PROVIDE ( _raise_r , 0x4000bc70 ) +PROVIDE ( _unlink_r , 0x4000bc84 ) +PROVIDE ( _link_r , 0x4000bc9c ) +PROVIDE ( _stat_r , 0x4000bcb4 ) +PROVIDE ( _fstat_r , 0x4000bccc ) +PROVIDE ( _sbrk_r , 0x4000bce4 ) +PROVIDE ( _getpid_r , 0x4000bcfc ) +PROVIDE ( _kill_r , 0x4000bd10 ) +PROVIDE ( _exit_r , 0x4000bd28 ) +PROVIDE ( _close_r , 0x4000bd3c ) +PROVIDE ( _open_r , 0x4000bd54 ) +PROVIDE ( _write_r , 0x4000bd70 ) +PROVIDE ( _lseek_r , 0x4000bd8c ) +PROVIDE ( _read_r , 0x4000bda8 ) +PROVIDE ( _lock_init , 0x4000bdc4 ) +PROVIDE ( _lock_init_recursive , 0x4000bdd8 ) +PROVIDE ( _lock_close , 0x4000bdec ) +PROVIDE ( _lock_close_recursive , 0x4000be00 ) +PROVIDE ( _lock_acquire , 0x4000be14 ) +PROVIDE ( _lock_acquire_recursive , 0x4000be28 ) +PROVIDE ( _lock_try_acquire , 0x4000be3c ) +PROVIDE ( _lock_try_acquire_recursive , 0x4000be50 ) +PROVIDE ( _lock_release , 0x4000be64 ) +PROVIDE ( _lock_release_recursive , 0x4000be78 ) +PROVIDE ( __getreent , 0x4000be8c ) +PROVIDE ( malloc , 0x4000bea0 ) +PROVIDE ( free , 0x4000beb8 ) +PROVIDE ( realloc , 0x4000becc ) +PROVIDE ( calloc , 0x4000bee4 ) +PROVIDE ( _printf_float , 0x4000befc ) +PROVIDE ( _scanf_float , 0x4000bf18 ) +PROVIDE ( _xtos_set_interrupt_handler_arg , 0x4000bf34 ) +PROVIDE ( _xtos_set_interrupt_handler , 0x4000bf78 ) +PROVIDE ( _xtos_ints_on , 0x4000bf88 ) +PROVIDE ( _xtos_ints_off , 0x4000bfac ) +PROVIDE ( _xtos_p_none , 0x4000bfd4 ) +PROVIDE ( _xtos_set_intlevel , 0x4000bfdc ) +PROVIDE ( _xtos_set_min_intlevel , 0x4000bff8 ) +PROVIDE ( _xtos_unhandled_interrupt , 0x4000c01c ) +PROVIDE ( _xtos_unhandled_exception , 0x4000c024 ) +PROVIDE ( _xtos_return_from_exc , 0x4000c034 ) +PROVIDE ( xthal_get_ccount , 0x4000c050 ) +PROVIDE ( xthal_set_ccompare , 0x4000c058 ) +PROVIDE ( xthal_get_ccompare , 0x4000c078 ) +PROVIDE ( xthal_bcopy , 0x4000c098 ) +PROVIDE ( xthal_memcpy , 0x4000c0bc ) +PROVIDE ( xthal_copy123 , 0x4000c124 ) +PROVIDE ( xthal_get_interrupt , 0x4000c1e4 ) +PROVIDE ( xthal_get_intread , 0x4000c1e4 ) +PROVIDE ( xthal_set_intclear , 0x4000c1ec ) +PROVIDE ( bzero , 0x4000c1f4 ) +PROVIDE ( isascii , 0x4000c20c ) +PROVIDE ( memccpy , 0x4000c220 ) +PROVIDE ( memchr , 0x4000c244 ) +PROVIDE ( memcmp , 0x4000c260 ) +PROVIDE ( memcpy , 0x4000c2c8 ) +PROVIDE ( memmove , 0x4000c3c0 ) +PROVIDE ( memrchr , 0x4000c400 ) +PROVIDE ( memset , 0x4000c44c ) +PROVIDE ( __sccl , 0x4000c498 ) +PROVIDE ( strcat , 0x4000c518 ) +PROVIDE ( strchr , 0x4000c53c ) +PROVIDE ( strcspn , 0x4000c558 ) +PROVIDE ( strlcpy , 0x4000c584 ) +PROVIDE ( strncat , 0x4000c5c4 ) +PROVIDE ( strncmp , 0x4000c5f4 ) +PROVIDE ( strnlen , 0x4000c628 ) +PROVIDE ( strspn , 0x4000c648 ) +PROVIDE ( strstr , 0x4000c674 ) +PROVIDE ( __strtok_r , 0x4000c6a8 ) +PROVIDE ( strtok_r , 0x4000c70c ) +PROVIDE ( toascii , 0x4000c720 ) +PROVIDE ( __dummy_lock , 0x4000c728 ) +PROVIDE ( __dummy_lock_try , 0x4000c730 ) +PROVIDE ( _fwalk , 0x4000c738 ) +PROVIDE ( _fwalk_reent , 0x4000c770 ) +PROVIDE ( __mulsi3 , 0x4000c7b0 ) +PROVIDE ( __divsi3 , 0x4000c7b8 ) +PROVIDE ( __modsi3 , 0x4000c7c0 ) +PROVIDE ( __udivsi3 , 0x4000c7c8 ) +PROVIDE ( __umodsi3 , 0x4000c7d0 ) +PROVIDE ( __umulsidi3 , 0x4000c7d8 ) +PROVIDE ( __clzsi2 , 0x4000c7e8 ) +PROVIDE ( __ctzsi2 , 0x4000c7f0 ) +PROVIDE ( __ffssi2 , 0x4000c804 ) +PROVIDE ( __ashldi3 , 0x4000c818 ) +PROVIDE ( __ashrdi3 , 0x4000c830 ) +PROVIDE ( __lshrdi3 , 0x4000c84c ) +PROVIDE ( __floatunsisf , 0x4000c864 ) +PROVIDE ( __floatsisf , 0x4000c870 ) +PROVIDE ( __floatundisf , 0x4000c8b0 ) +PROVIDE ( __floatdisf , 0x4000c8c0 ) +PROVIDE ( __floatunsidf , 0x4000c938 ) +PROVIDE ( __floatsidf , 0x4000c944 ) +PROVIDE ( __floatundidf , 0x4000c978 ) +PROVIDE ( __floatdidf , 0x4000c988 ) +PROVIDE ( __muldi3 , 0x4000c9fc ) +PROVIDE ( __negdi2 , 0x4000ca14 ) +PROVIDE ( __ffsdi2 , 0x4000ca2c ) +PROVIDE ( __clzdi2 , 0x4000ca50 ) +PROVIDE ( __ctzdi2 , 0x4000ca64 ) +PROVIDE ( __divdi3 , 0x4000ca84 ) +PROVIDE ( __moddi3 , 0x4000cd4c ) +PROVIDE ( __udivdi3 , 0x4000cff8 ) +PROVIDE ( __umoddi3 , 0x4000d280 ) +PROVIDE ( _data_start_btdm_rom , 0x4000d4f4 ) +PROVIDE ( _data_end_btdm_rom , 0x4000d4f8 ) +PROVIDE ( _data_start , 0x4000d4f8 ) +PROVIDE ( _rom_store_table , 0x4000d4f8 ) +PROVIDE ( _data_end , 0x4000d5c8 ) +PROVIDE ( _etext , 0x4000d66c ) +PROVIDE ( _rom_store , 0x4000d66c ) +PROVIDE ( btdm_r_btdm_option_data_p_get , 0x40010004 ) +PROVIDE ( btdm_r_data_init , 0x4001002c ) +PROVIDE ( btdm_r_btdm_rom_version_get , 0x40010078 ) +PROVIDE ( r_bt_util_buf_init , 0x400100e4 ) +PROVIDE ( r_bt_util_buf_lmp_tx_alloc , 0x400101d0 ) +PROVIDE ( r_bt_util_buf_lmp_tx_free , 0x400101ec ) +PROVIDE ( r_bt_util_buf_acl_rx_alloc , 0x40010218 ) +PROVIDE ( r_bt_util_buf_acl_rx_free , 0x40010234 ) +PROVIDE ( r_bt_util_buf_acl_tx_alloc , 0x40010268 ) +PROVIDE ( r_bt_util_buf_acl_tx_free , 0x40010280 ) +PROVIDE ( r_bt_util_buf_sync_init , 0x400102c4 ) +PROVIDE ( r_bt_util_buf_sync_clear , 0x400103c8 ) +PROVIDE ( r_bt_util_buf_sync_tx_alloc , 0x400103ec ) +PROVIDE ( r_bt_util_buf_sync_tx_free , 0x40010428 ) +PROVIDE ( r_bt_util_buf_sync_rx_alloc , 0x40010468 ) +PROVIDE ( r_bt_util_buf_sync_rx_free , 0x4001049c ) +PROVIDE ( r_E1 , 0x400108e8 ) +PROVIDE ( r_E21 , 0x40010968 ) +PROVIDE ( r_E22 , 0x400109b4 ) +PROVIDE ( r_E3 , 0x40010a58 ) +PROVIDE ( r_KPrimC , 0x40010ad4 ) +PROVIDE ( r_XorKey , 0x400112c0 ) +PROVIDE ( r_LM_MakeRandVec , 0x400112d8 ) +PROVIDE ( r_lmp_pack , 0x4001135c ) +PROVIDE ( r_lmp_unpack , 0x4001149c ) +PROVIDE ( r_lm_n_is_zero , 0x40012170 ) +PROVIDE ( r_lm_sp_sha256_calculate , 0x400121a0 ) +PROVIDE ( r_lm_sp_n_one , 0x400123a4 ) +PROVIDE ( r_lm_sp_n192_convert_wnaf , 0x400123c0 ) +PROVIDE ( r_lm_sp_p192_point_to_inf , 0x40012458 ) +PROVIDE ( r_lm_sp_p192_point_jacobian_to_affine , 0x40012468 ) +PROVIDE ( r_lm_sp_p192_points_jacobian_to_affine , 0x400124e4 ) +PROVIDE ( r_lm_sp_pre_compute_points , 0x40012640 ) +PROVIDE ( r_lm_sp_p192_dbl , 0x4001268c ) +PROVIDE ( r_lm_sp_p192_add , 0x40012828 ) +PROVIDE ( r_lm_sp_p192_invert , 0x40012b6c ) +PROVIDE ( r_lm_f1 , 0x40012bb8 ) +PROVIDE ( r_lm_f2 , 0x40012cfc ) +PROVIDE ( r_lm_oob_f1 , 0x40012e54 ) +PROVIDE ( r_lm_g , 0x40012f90 ) +PROVIDE ( r_lm_f3 , 0x40013050 ) +PROVIDE ( r_lm_get_nonce , 0x400131c4 ) +PROVIDE ( r_lm_dhkey_calc_init , 0x40013234 ) +PROVIDE ( r_lm_dhkey_compare , 0x400132d8 ) +PROVIDE ( r_F1_256 , 0x400133e4 ) +PROVIDE ( r_G_256 , 0x40013470 ) +PROVIDE ( r_F2_256 , 0x40013568 ) +PROVIDE ( r_F3_256 , 0x40013664 ) +PROVIDE ( r_H3 , 0x40013760 ) +PROVIDE ( r_H4 , 0x40013830 ) +PROVIDE ( r_H5 , 0x400138dc ) +PROVIDE ( r_HMAC , 0x40013968 ) +PROVIDE ( r_hashConcat , 0x40013a38 ) +PROVIDE ( r_SHA_256 , 0x40013a90 ) +PROVIDE ( r_co_list_init , 0x40013f14 ) +PROVIDE ( r_co_list_pool_init , 0x40013f30 ) +PROVIDE ( r_co_list_push_back , 0x40013fb8 ) +PROVIDE ( r_co_list_push_front , 0x40013ff4 ) +PROVIDE ( r_co_list_pop_front , 0x40014028 ) +PROVIDE ( r_co_list_extract , 0x4001404c ) +PROVIDE ( r_co_list_extract_after , 0x40014118 ) +PROVIDE ( r_co_list_find , 0x4001419c ) +PROVIDE ( r_co_list_merge , 0x400141bc ) +PROVIDE ( r_co_list_insert_before , 0x40014200 ) +PROVIDE ( r_co_list_insert_after , 0x40014254 ) +PROVIDE ( r_co_list_size , 0x400142ac ) +PROVIDE ( r_co_list_check_size_available , 0x400142c4 ) +PROVIDE ( r_co_bytes_to_string , 0x400142e4 ) +PROVIDE ( r_co_bdaddr_compare , 0x40014324 ) +PROVIDE ( r_co_slot_to_duration , 0x40014348 ) +PROVIDE ( r_co_nb_good_channels , 0x40014360 ) +PROVIDE ( r_dbg_init , 0x40014394 ) +PROVIDE ( r_dbg_platform_reset_complete , 0x400143d0 ) +PROVIDE ( r_dbg_swdiag_init , 0x40014470 ) +PROVIDE ( r_dbg_swdiag_read , 0x400144a4 ) +PROVIDE ( r_dbg_swdiag_write , 0x400144d0 ) +PROVIDE ( r_ea_elt_cancel , 0x400150d0 ) +PROVIDE ( r_ea_init , 0x40015228 ) +PROVIDE ( r_ea_elt_create , 0x40015264 ) +PROVIDE ( r_ea_elt_insert , 0x400152a8 ) +PROVIDE ( r_ea_elt_remove , 0x400154f0 ) +PROVIDE ( r_ea_interval_create , 0x4001555c ) +PROVIDE ( r_ea_interval_insert , 0x4001557c ) +PROVIDE ( r_ea_interval_remove , 0x40015590 ) +PROVIDE ( r_ea_interval_delete , 0x400155a8 ) +PROVIDE ( r_ea_finetimer_isr , 0x400155d4 ) +PROVIDE ( r_ea_sw_isr , 0x40015724 ) +PROVIDE ( r_ea_offset_req , 0x40015748 ) +PROVIDE ( r_ea_time_get_halfslot_rounded , 0x40015894 ) +PROVIDE ( r_ea_time_get_slot_rounded , 0x400158d4 ) +PROVIDE ( r_ea_sleep_check , 0x40015928 ) +PROVIDE ( r_ea_interval_duration_req , 0x4001597c ) +PROVIDE ( r_ea_alarm_set , 0x40015a10 ) +PROVIDE ( r_ea_alarm_clear , 0x40015ab4 ) +PROVIDE ( notEqual256 , 0x40015b04 ) +PROVIDE ( AddBigHex256 , 0x40015b28 ) +PROVIDE ( Add2SelfBigHex256 , 0x40015b7c ) +PROVIDE ( SubtractBigHex256 , 0x40015bcc ) +PROVIDE ( SubtractFromSelfBigHex256 , 0x40015c20 ) +PROVIDE ( AddP256 , 0x40015c74 ) +PROVIDE ( AddBigHexModP256 , 0x40015c98 ) +PROVIDE ( AddPdiv2_256 , 0x40015ce0 ) +PROVIDE ( SubtractFromSelfBigHexSign256 , 0x40015dc8 ) +PROVIDE ( SubtractBigHexMod256 , 0x40015e8c ) +PROVIDE ( SubtractBigHexUint32_256 , 0x40015f8c ) +PROVIDE ( MultiplyByU32ModP256 , 0x40015fdc ) +PROVIDE ( specialModP256 , 0x4001600c ) +PROVIDE ( MultiplyBigHexModP256 , 0x400160b8 ) +PROVIDE ( MultiplyBigHexByUint32_256 , 0x40016214 ) +PROVIDE ( GF_Jacobian_Point_Double256 , 0x40016260 ) +PROVIDE ( GF_Jacobian_Point_Addition256 , 0x400163a4 ) +PROVIDE ( bigHexInversion256 , 0x400168f0 ) +PROVIDE ( GF_Point_Jacobian_To_Affine256 , 0x40016b0c ) +PROVIDE ( r_ecc_init , 0x40016dbc ) +PROVIDE ( r_ecc_generate_key256 , 0x40016e00 ) +PROVIDE ( r_ecc_abort_key256_generation , 0x40017070 ) +PROVIDE ( r_ecc_gen_new_public_key , 0x400170c0 ) +PROVIDE ( r_ecc_gen_new_secret_key , 0x400170e4 ) +PROVIDE ( r_ecc_get_debug_Keys , 0x40017224 ) +PROVIDE ( r_em_buf_init , 0x4001729c ) +PROVIDE ( r_em_buf_rx_free , 0x400173c4 ) +PROVIDE ( r_em_buf_rx_buff_addr_get , 0x400173e8 ) +PROVIDE ( r_em_buf_tx_buff_addr_get , 0x40017404 ) +PROVIDE ( r_em_buf_tx_free , 0x4001741c ) +PROVIDE ( r_h4tl_init , 0x40017878 ) +PROVIDE ( r_h4tl_write , 0x400178d0 ) +PROVIDE ( r_h4tl_start , 0x40017924 ) +PROVIDE ( r_h4tl_stop , 0x40017934 ) +PROVIDE ( r_hci_fc_init , 0x40017974 ) +PROVIDE ( r_hci_fc_acl_buf_size_set , 0x40017988 ) +PROVIDE ( r_hci_fc_sync_buf_size_set , 0x400179b0 ) +PROVIDE ( r_hci_fc_acl_en , 0x400179d8 ) +PROVIDE ( r_hci_fc_sync_en , 0x40017a30 ) +PROVIDE ( r_hci_fc_acl_packet_sent , 0x40017a3c ) +PROVIDE ( r_hci_fc_sync_packet_sent , 0x40017a54 ) +PROVIDE ( r_hci_fc_host_nb_acl_pkts_complete , 0x40017a6c ) +PROVIDE ( r_hci_fc_host_nb_sync_pkts_complete , 0x40017a88 ) +PROVIDE ( r_hci_fc_check_host_available_nb_acl_packets , 0x40017aa4 ) +PROVIDE ( r_hci_fc_check_host_available_nb_sync_packets , 0x40017ac8 ) +PROVIDE ( r_hci_look_for_cmd_desc , 0x40018454 ) +PROVIDE ( r_hci_look_for_evt_desc , 0x400184a0 ) +PROVIDE ( r_hci_look_for_dbg_evt_desc , 0x400184c4 ) +PROVIDE ( r_hci_look_for_le_evt_desc , 0x400184e0 ) +PROVIDE ( r_hci_init , 0x40018538 ) +PROVIDE ( r_hci_reset , 0x4001856c ) +PROVIDE ( r_hci_send_2_host , 0x400185bc ) +PROVIDE ( r_hci_bt_acl_bdaddr_register , 0x40018900 ) +PROVIDE ( r_hci_bt_acl_conhdl_register , 0x4001895c ) +PROVIDE ( r_hci_bt_acl_bdaddr_unregister , 0x400189ac ) +PROVIDE ( r_hci_evt_mask_set , 0x400189e4 ) +PROVIDE ( r_hci_evt_filter_add , 0x40018a64 ) +PROVIDE ( r_hci_voice_settings_get , 0x40018bdc ) +PROVIDE ( r_hci_voice_settings_set , 0x40018be8 ) +PROVIDE ( r_hci_tl_send , 0x40019228 ) +PROVIDE ( r_hci_tl_init , 0x40019290 ) +PROVIDE ( r_hci_cmd_get_max_param_size , 0x400192d0 ) +PROVIDE ( r_hci_cmd_received , 0x400192f8 ) +PROVIDE ( r_hci_acl_tx_data_alloc , 0x4001951c ) +PROVIDE ( r_hci_acl_tx_data_received , 0x40019654 ) +PROVIDE ( r_hci_sync_tx_data_alloc , 0x40019754 ) +PROVIDE ( r_hci_sync_tx_data_received , 0x400197c0 ) +PROVIDE ( r_hci_util_pack , 0x40019874 ) +PROVIDE ( r_hci_util_unpack , 0x40019998 ) +PROVIDE ( btdm_r_ip_func_p_get , 0x40019af0 ) +PROVIDE ( btdm_r_ip_func_p_set , 0x40019afc ) +PROVIDE ( btdm_r_ble_bt_handler_tab_p_get , 0x40019b0c ) +PROVIDE ( r_ke_event_init , 0x40019b90 ) +PROVIDE ( r_ke_event_callback_set , 0x40019ba8 ) +PROVIDE ( r_ke_event_set , 0x40019be0 ) +PROVIDE ( r_ke_event_clear , 0x40019c2c ) +PROVIDE ( r_ke_event_get , 0x40019c78 ) +PROVIDE ( r_ke_event_get_all , 0x40019cc0 ) +PROVIDE ( r_ke_event_flush , 0x40019ccc ) +PROVIDE ( r_ke_event_schedule , 0x40019cdc ) +PROVIDE ( r_ke_mem_init , 0x40019d3c ) +PROVIDE ( r_ke_mem_is_empty , 0x40019d8c ) +PROVIDE ( r_ke_check_malloc , 0x40019de0 ) +PROVIDE ( r_ke_malloc , 0x40019eb4 ) +PROVIDE ( r_ke_free , 0x4001a014 ) +PROVIDE ( r_ke_is_free , 0x4001a184 ) +PROVIDE ( r_ke_get_mem_usage , 0x4001a1a0 ) +PROVIDE ( r_ke_get_max_mem_usage , 0x4001a1c8 ) +PROVIDE ( r_ke_msg_alloc , 0x4001a1e0 ) +PROVIDE ( r_ke_msg_send , 0x4001a234 ) +PROVIDE ( r_ke_msg_send_basic , 0x4001a26c ) +PROVIDE ( r_ke_msg_forward , 0x4001a290 ) +PROVIDE ( r_ke_msg_forward_new_id , 0x4001a2ac ) +PROVIDE ( r_ke_msg_free , 0x4001a2cc ) +PROVIDE ( r_ke_msg_dest_id_get , 0x4001a2e0 ) +PROVIDE ( r_ke_msg_src_id_get , 0x4001a2ec ) +PROVIDE ( r_ke_msg_in_queue , 0x4001a2f8 ) +PROVIDE ( r_ke_init , 0x4001a318 ) +PROVIDE ( r_ke_flush , 0x4001a374 ) +PROVIDE ( r_ke_sleep_check , 0x4001a3d8 ) +PROVIDE ( r_ke_stats_get , 0x4001a3f0 ) +PROVIDE ( r_ke_task_init , 0x4001a650 ) +PROVIDE ( r_ke_task_create , 0x4001a674 ) +PROVIDE ( r_ke_task_delete , 0x4001a6c0 ) +PROVIDE ( r_ke_state_set , 0x4001a6fc ) +PROVIDE ( r_ke_state_get , 0x4001a7d8 ) +PROVIDE ( r_ke_msg_discard , 0x4001a850 ) +PROVIDE ( r_ke_msg_save , 0x4001a858 ) +PROVIDE ( r_ke_task_msg_flush , 0x4001a860 ) +PROVIDE ( r_ke_task_check , 0x4001a8a4 ) +PROVIDE ( r_ke_timer_init , 0x4001aa9c ) +PROVIDE ( r_ke_timer_set , 0x4001aac0 ) +PROVIDE ( r_ke_timer_clear , 0x4001ab90 ) +PROVIDE ( r_ke_timer_active , 0x4001ac08 ) +PROVIDE ( r_ke_timer_adjust_all , 0x4001ac30 ) +PROVIDE ( r_ke_timer_sleep_check , 0x4001ac50 ) +PROVIDE ( r_LM_ExtractMaxEncKeySize , 0x4001aca4 ) +PROVIDE ( r_lb_init , 0x4001acd4 ) +PROVIDE ( r_lb_reset , 0x4001ad38 ) +PROVIDE ( r_lb_util_get_nb_broadcast , 0x4001ad80 ) +PROVIDE ( r_lb_util_set_nb_broadcast , 0x4001ad8c ) +PROVIDE ( r_lb_util_get_res_lt_addr , 0x4001ad98 ) +PROVIDE ( r_lb_util_get_csb_mode , 0x4001ada4 ) +PROVIDE ( r_lb_send_lmp , 0x4001adbc ) +PROVIDE ( r_lb_clk_adj_activate , 0x4001ae70 ) +PROVIDE ( r_lb_clk_adj_id_get , 0x4001af14 ) +PROVIDE ( r_lb_clk_adj_period_update , 0x4001af20 ) +PROVIDE ( r_lb_send_pdu_clk_adj , 0x4001af3c ) +PROVIDE ( r_lb_mst_key_cmp , 0x4001af74 ) +PROVIDE ( r_lb_mst_key , 0x4001afc0 ) +PROVIDE ( r_lb_mst_key_restart_enc , 0x4001b0d4 ) +PROVIDE ( r_lb_mst_start_act_bcst_enc , 0x4001b198 ) +PROVIDE ( r_lb_mst_stop_act_bcst_enc , 0x4001b24c ) +PROVIDE ( r_LM_GetMasterKey , 0x4001b260 ) +PROVIDE ( r_LM_GetMasterKeyRand , 0x4001b274 ) +PROVIDE ( r_LM_GetMasterEncRand , 0x4001b288 ) +PROVIDE ( r_LM_GetMasterEncKeySize , 0x4001b29c ) +PROVIDE ( lb_hci_cmd_handler_tab_p_get , 0x4001c18c ) +PROVIDE ( lb_default_state_tab_p_get , 0x4001c198 ) +PROVIDE ( r_lc_send_lmp , 0x4001c1a8 ) +PROVIDE ( r_lc_send_pdu_acc , 0x4001c21c ) +PROVIDE ( r_lc_send_pdu_acc_ext4 , 0x4001c240 ) +PROVIDE ( r_lc_send_pdu_not_acc , 0x4001c26c ) +PROVIDE ( r_lc_send_pdu_not_acc_ext4 , 0x4001c294 ) +PROVIDE ( r_lc_send_pdu_set_afh , 0x4001c2c8 ) +PROVIDE ( r_lc_send_pdu_au_rand , 0x4001c308 ) +PROVIDE ( r_lc_send_pdu_in_rand , 0x4001c338 ) +PROVIDE ( r_lc_send_pdu_comb_key , 0x4001c368 ) +PROVIDE ( r_lc_send_pdu_unit_key , 0x4001c398 ) +PROVIDE ( r_lc_send_pdu_max_slot , 0x4001c3c8 ) +PROVIDE ( r_lc_send_pdu_max_slot_req , 0x4001c3ec ) +PROVIDE ( r_lc_send_pdu_encaps_payl , 0x4001c410 ) +PROVIDE ( r_lc_send_pdu_encaps_head , 0x4001c440 ) +PROVIDE ( r_lc_send_pdu_clk_adj_ack , 0x4001c46c ) +PROVIDE ( r_lc_send_pdu_clk_adj_req , 0x4001c494 ) +PROVIDE ( r_lc_send_pdu_ptt_req , 0x4001c4c0 ) +PROVIDE ( r_lc_send_pdu_sp_nb , 0x4001c4e8 ) +PROVIDE ( r_lc_send_pdu_sp_cfm , 0x4001c518 ) +PROVIDE ( r_lc_send_pdu_sres , 0x4001c548 ) +PROVIDE ( r_lc_send_pdu_sco_lk_rem_req , 0x4001c580 ) +PROVIDE ( r_lc_send_pdu_esco_lk_rem_req , 0x4001c5a8 ) +PROVIDE ( r_lc_send_pdu_auto_rate , 0x4001c5d0 ) +PROVIDE ( r_lc_send_pdu_sniff_req , 0x4001c5f0 ) +PROVIDE ( r_lc_send_pdu_lsto , 0x4001c64c ) +PROVIDE ( r_lc_send_pdu_enc_key_sz_req , 0x4001c670 ) +PROVIDE ( r_lc_send_pdu_feats_res , 0x4001c694 ) +PROVIDE ( r_lc_send_pdu_tim_acc , 0x4001c6cc ) +PROVIDE ( r_lc_send_pdu_feats_ext_req , 0x4001c6ec ) +PROVIDE ( r_lc_send_pdu_io_cap_res , 0x4001c72c ) +PROVIDE ( r_lc_send_pdu_num_comp_fail , 0x4001c770 ) +PROVIDE ( r_lc_send_pdu_pause_enc_aes_req , 0x4001c794 ) +PROVIDE ( r_lc_send_pdu_paus_enc_req , 0x4001c7c0 ) +PROVIDE ( r_lc_send_pdu_resu_enc_req , 0x4001c7e4 ) +PROVIDE ( r_lc_send_pdu_setup_cmp , 0x4001c808 ) +PROVIDE ( r_lc_send_pdu_qos_req , 0x4001c82c ) +PROVIDE ( r_lc_send_pdu_slot_off , 0x4001c854 ) +PROVIDE ( r_lc_send_pdu_unsniff_req , 0x4001c894 ) +PROVIDE ( r_lc_send_pdu_vers_req , 0x4001c8b4 ) +PROVIDE ( r_lc_send_pdu_dhkey_chk , 0x4001c8e8 ) +PROVIDE ( r_lc_cmd_stat_send , 0x4001c914 ) +PROVIDE ( r_lc_init , 0x4001c948 ) +PROVIDE ( r_lc_reset , 0x4001c99c ) +PROVIDE ( r_lc_start , 0x4001ca28 ) +PROVIDE ( r_lc_afh_set , 0x4001cc74 ) +PROVIDE ( r_lc_auth_cmp , 0x4001cd54 ) +PROVIDE ( r_lc_calc_link_key , 0x4001ce7c ) +PROVIDE ( r_lc_chg_pkt_type_cont , 0x4001cfbc ) +PROVIDE ( r_lc_chg_pkt_type_cmp , 0x4001d038 ) +PROVIDE ( r_lc_chg_pkt_type_retry , 0x4001d0ac ) +PROVIDE ( r_lc_afh_start , 0x4001d240 ) +PROVIDE ( r_lc_chk_to , 0x4001d2a8 ) +PROVIDE ( r_lc_comb_key_svr , 0x4001d30c ) +PROVIDE ( r_lc_con_cmp , 0x4001d44c ) +PROVIDE ( r_lc_con_cmp_evt_send , 0x4001d4fc ) +PROVIDE ( r_lc_dhkey , 0x4001d564 ) +PROVIDE ( r_lc_locepr_rsw , 0x4001d5d0 ) +PROVIDE ( r_lc_locepr_lkref , 0x4001d648 ) +PROVIDE ( r_lc_enc_key_refresh , 0x4001d720 ) +PROVIDE ( r_lc_end_chk_colli , 0x4001d858 ) +PROVIDE ( r_lc_enc_cmp , 0x4001d8bc ) +PROVIDE ( r_lc_end_of_sniff_nego , 0x4001d9a4 ) +PROVIDE ( r_lc_epr_cmp , 0x4001da88 ) +PROVIDE ( r_lc_epr_change_lk , 0x4001db38 ) +PROVIDE ( r_lc_rsw_done , 0x4001db94 ) +PROVIDE ( r_lc_rsw_clean_up , 0x4001dc70 ) +PROVIDE ( r_lc_epr_rsw_cmp , 0x4001dd40 ) +PROVIDE ( r_lc_enter_sniff_mode , 0x4001ddb8 ) +PROVIDE ( r_lc_init_calc_f3 , 0x4001deb0 ) +PROVIDE ( r_lc_init_start_mutual_auth , 0x4001df60 ) +PROVIDE ( r_lc_init_passkey_loop , 0x4001dfc0 ) +PROVIDE ( r_lc_initiator_epr , 0x4001e064 ) +PROVIDE ( r_lc_epr_resp , 0x4001e0b4 ) +PROVIDE ( r_lc_key_exch_end , 0x4001e140 ) +PROVIDE ( r_lc_legacy_pair , 0x4001e1c0 ) +PROVIDE ( r_lc_local_switch , 0x4001e22c ) +PROVIDE ( r_lc_local_trans_mode , 0x4001e2e4 ) +PROVIDE ( r_lc_local_untrans_mode , 0x4001e3a0 ) +PROVIDE ( r_lc_max_slot_mgt , 0x4001e410 ) +PROVIDE ( r_lc_mutual_auth_end2 , 0x4001e4f4 ) +PROVIDE ( r_lc_mutual_auth_end , 0x4001e670 ) +PROVIDE ( r_lc_mst_key , 0x4001e7c0 ) +PROVIDE ( r_lc_mst_send_mst_key , 0x4001e8f4 ) +PROVIDE ( r_lc_mst_qos_done , 0x4001ea80 ) +PROVIDE ( r_lc_pairing_cont , 0x4001eafc ) +PROVIDE ( r_lc_passkey_comm , 0x4001ed20 ) +PROVIDE ( r_lc_proc_rcv_dhkey , 0x4001edec ) +PROVIDE ( r_lc_ptt , 0x4001ee2c ) +PROVIDE ( r_lc_ptt_cmp , 0x4001eeec ) +PROVIDE ( r_lc_qos_setup , 0x4001ef50 ) +PROVIDE ( r_lc_rd_rem_name , 0x4001efd0 ) +PROVIDE ( r_lc_restore_afh_reporting , 0x4001f028 ) +PROVIDE ( r_lc_rem_switch , 0x4001f070 ) +PROVIDE ( r_lc_rem_enc , 0x4001f124 ) +PROVIDE ( r_lc_rem_nego_trans_mode , 0x4001f1b4 ) +PROVIDE ( r_lc_rem_name_cont , 0x4001f290 ) +PROVIDE ( r_lc_rem_trans_mode , 0x4001f314 ) +PROVIDE ( r_lc_rem_untrans_mode , 0x4001f36c ) +PROVIDE ( r_lc_sec_auth_compute_sres , 0x4001f3ec ) +PROVIDE ( r_lc_resp_sec_auth , 0x4001f4a0 ) +PROVIDE ( r_lc_resp_auth , 0x4001f518 ) +PROVIDE ( r_lc_resp_oob_wait_nonce , 0x4001f66c ) +PROVIDE ( r_lc_resp_oob_nonce , 0x4001f694 ) +PROVIDE ( r_lc_resp_calc_f3 , 0x4001f710 ) +PROVIDE ( r_lc_resp_wait_dhkey_cont , 0x4001f86c ) +PROVIDE ( r_lc_release , 0x4001f8a8 ) +PROVIDE ( r_lc_restart_enc , 0x4001f8ec ) +PROVIDE ( r_lc_restart_enc_cont , 0x4001f940 ) +PROVIDE ( r_lc_restore_to , 0x4001f9e0 ) +PROVIDE ( r_lc_ret_sniff_max_slot_chg , 0x4001fa30 ) +PROVIDE ( r_lc_start_lmp_to , 0x4001fae8 ) +PROVIDE ( r_lc_start_enc , 0x4001fb28 ) +PROVIDE ( r_lc_start_enc_key_size , 0x4001fd9c ) +PROVIDE ( r_lc_start_key_exch , 0x4001fe10 ) +PROVIDE ( r_lc_start_passkey , 0x4001feac ) +PROVIDE ( r_lc_start_passkey_loop , 0x4001ff88 ) +PROVIDE ( r_lc_start_oob , 0x4001fffc ) +PROVIDE ( r_lc_resp_num_comp , 0x40020074 ) +PROVIDE ( r_lc_stop_enc , 0x40020110 ) +PROVIDE ( r_lc_stop_afh_report , 0x40020184 ) +PROVIDE ( r_lc_skip_hl_oob_req , 0x400201bc ) +PROVIDE ( r_lc_send_enc_mode , 0x40020220 ) +PROVIDE ( r_lc_semi_key_cmp , 0x40020294 ) +PROVIDE ( r_lc_detach , 0x4002037c ) +PROVIDE ( r_lc_switch_cmp , 0x40020448 ) +PROVIDE ( r_lc_sp_fail , 0x40020470 ) +PROVIDE ( r_lc_sp_oob_tid_fail , 0x400204cc ) +PROVIDE ( r_lc_sniff_sub_mode , 0x400204fc ) +PROVIDE ( r_lc_sniff_max_slot_chg , 0x40020590 ) +PROVIDE ( r_lc_upd_to , 0x4002065c ) +PROVIDE ( r_lc_unit_key_svr , 0x400206d8 ) +PROVIDE ( r_lc_unsniff_cont , 0x40020750 ) +PROVIDE ( r_lc_rem_unsniff , 0x400207a0 ) +PROVIDE ( r_lc_unsniff_cmp , 0x40020810 ) +PROVIDE ( r_lc_resp_pair , 0x400208a4 ) +PROVIDE ( r_lc_feat , 0x40020984 ) +PROVIDE ( r_lc_hl_connect , 0x400209e8 ) +PROVIDE ( r_lc_version , 0x40020a30 ) +PROVIDE ( r_lc_loc_sniff , 0x40020a6c ) +PROVIDE ( r_lc_rem_sniff_sub_rate , 0x40020b10 ) +PROVIDE ( r_lc_unsniff , 0x40020c50 ) +PROVIDE ( r_lc_rem_sniff , 0x40020ca4 ) +PROVIDE ( r_lc_ext_feat , 0x40020d6c ) +PROVIDE ( r_lc_pair , 0x40020ddc ) +PROVIDE ( r_lc_loc_auth , 0x40020ecc ) +PROVIDE ( r_lc_packet_type , 0x40021038 ) +PROVIDE ( r_lc_sniff_slot_unchange , 0x40021100 ) +PROVIDE ( r_lc_ssr_nego , 0x4002125c ) +PROVIDE ( r_lc_conn_seq_done , 0x40021334 ) +PROVIDE ( r_lc_send_enc_chg_evt , 0x4002134c ) +PROVIDE ( r_lc_sp_end , 0x400213a8 ) +PROVIDE ( r_lc_prepare_all_links_for_clk_adj , 0x40021430 ) +PROVIDE ( r_lc_sco_init , 0x40021dc8 ) +PROVIDE ( r_lc_sco_reset , 0x40021dfc ) +PROVIDE ( r_lc_sco_detach , 0x40021e40 ) +PROVIDE ( r_lc_sco_release , 0x40021eec ) +PROVIDE ( r_lc_sco_host_request , 0x40021f4c ) +PROVIDE ( r_lc_sco_host_accept , 0x40022118 ) +PROVIDE ( r_lc_sco_host_reject , 0x400222b8 ) +PROVIDE ( r_lc_sco_host_request_disc , 0x4002235c ) +PROVIDE ( r_lc_sco_peer_request , 0x4002240c ) +PROVIDE ( r_lc_sco_peer_accept , 0x40022780 ) +PROVIDE ( r_lc_sco_peer_reject , 0x40022824 ) +PROVIDE ( r_lc_sco_peer_request_disc , 0x400228ec ) +PROVIDE ( r_lc_sco_peer_accept_disc , 0x40022a08 ) +PROVIDE ( r_lc_sco_peer_reject_disc , 0x40022a8c ) +PROVIDE ( r_lc_sco_baseband_ack , 0x40022b00 ) +PROVIDE ( r_lc_sco_timeout , 0x40022bd4 ) +PROVIDE ( r_lc_sniff_init , 0x40022cac ) +PROVIDE ( r_lc_sniff_reset , 0x40022cc8 ) +PROVIDE ( r_LM_AddSniff , 0x40022d20 ) +PROVIDE ( r_LM_RemoveSniff , 0x40023124 ) +PROVIDE ( r_LM_SniffSubratingHlReq , 0x40023154 ) +PROVIDE ( r_LM_SniffSubratingPeerReq , 0x400231dc ) +PROVIDE ( r_LM_GetSniffSubratingParam , 0x4002325c ) +PROVIDE ( r_LM_SniffSubrateNegoRequired , 0x40023334 ) +PROVIDE ( r_LM_ComputeSniffSubRate , 0x400233ac ) +PROVIDE ( r_LM_RemoveSniffSubrating , 0x400233c4 ) +PROVIDE ( r_LM_GetLinkTimeout , 0x400233ec ) +PROVIDE ( lc_hci_cmd_handler_tab_p_get , 0x4002f488 ) +PROVIDE ( lc_default_state_tab_p_get , 0x4002f494 ) +PROVIDE ( r_lc_util_get_max_packet_size , 0x4002f4ac ) +PROVIDE ( r_lc_util_set_loc_trans_coll , 0x4002f500 ) +PROVIDE ( r_lc_util_get_offset_clkn , 0x4002f51c ) +PROVIDE ( r_lc_util_get_offset_clke , 0x4002f538 ) +PROVIDE ( r_LM_ComputePacketType , 0x4002f554 ) +PROVIDE ( r_LM_UpdateAclPacketType , 0x4002f584 ) +PROVIDE ( r_LM_UpdateAclEdrPacketType , 0x4002f5d8 ) +PROVIDE ( r_LM_SuppressAclPacket , 0x4002f658 ) +PROVIDE ( r_LM_MaxSlot , 0x4002f694 ) +PROVIDE ( r_LM_GetQoSParam , 0x4002f6e0 ) +PROVIDE ( r_LM_GetSwitchInstant , 0x4002f7f8 ) +PROVIDE ( r_LM_MakeCof , 0x4002f84c ) +PROVIDE ( r_LM_GetAFHSwitchInstant , 0x4002f86c ) +PROVIDE ( r_LM_CheckSwitchInstant , 0x4002f8c0 ) +PROVIDE ( r_LM_CheckEdrFeatureRequest , 0x4002f90c ) +PROVIDE ( r_LM_GetFeature , 0x4002f924 ) +PROVIDE ( r_lm_look_for_stored_link_key , 0x4002f948 ) +PROVIDE ( r_lc_util_convert_pref_rate_to_packet_type , 0x4002f9b0 ) +PROVIDE ( r_lm_get_common_pkt_types , 0x4002fa1c ) +PROVIDE ( r_ld_acl_init , 0x40034d08 ) +PROVIDE ( r_ld_acl_reset , 0x40034d24 ) +PROVIDE ( r_ld_acl_start , 0x40034ddc ) +PROVIDE ( r_ld_acl_stop , 0x4003532c ) +PROVIDE ( r_ld_acl_flow_off , 0x40035400 ) +PROVIDE ( r_ld_acl_flow_on , 0x4003541c ) +PROVIDE ( r_ld_acl_data_tx , 0x4003544c ) +PROVIDE ( r_ld_acl_data_flush , 0x400357bc ) +PROVIDE ( r_ld_acl_lmp_tx , 0x40035b34 ) +PROVIDE ( r_ld_acl_lmp_flush , 0x40035d80 ) +PROVIDE ( r_ld_acl_rsw_req , 0x40035e74 ) +PROVIDE ( r_ld_acl_flush_timeout_get , 0x40035f9c ) +PROVIDE ( r_ld_acl_flush_timeout_set , 0x40035fe0 ) +PROVIDE ( r_ld_acl_t_poll_get , 0x40036024 ) +PROVIDE ( r_ld_acl_t_poll_set , 0x40036068 ) +PROVIDE ( r_ld_acl_sniff_trans , 0x400360a8 ) +PROVIDE ( r_ld_acl_sniff , 0x4003617c ) +PROVIDE ( r_ld_acl_unsniff , 0x400361e0 ) +PROVIDE ( r_ld_acl_ssr_set , 0x40036274 ) +PROVIDE ( r_ld_acl_tx_enc , 0x400362f8 ) +PROVIDE ( r_ld_acl_rx_enc , 0x40036344 ) +PROVIDE ( r_ld_acl_bcst_rx_dec , 0x40036394 ) +PROVIDE ( r_ld_acl_enc_key_load , 0x40036404 ) +PROVIDE ( r_ld_acl_clock_offset_get , 0x400364c0 ) +PROVIDE ( r_ld_acl_slot_offset_get , 0x4003653c ) +PROVIDE ( r_ld_acl_slot_offset_set , 0x40036658 ) +PROVIDE ( r_ld_acl_lsto_get , 0x400366b4 ) +PROVIDE ( r_ld_acl_lsto_set , 0x400366f8 ) +PROVIDE ( r_ld_acl_timing_accuracy_set , 0x4003673c ) +PROVIDE ( r_ld_acl_edr_set , 0x4003678c ) +PROVIDE ( r_ld_acl_allowed_tx_packet_types_set , 0x40036810 ) +PROVIDE ( r_ld_acl_current_tx_power_get , 0x400368f0 ) +PROVIDE ( r_ld_acl_clk_set , 0x40036950 ) +PROVIDE ( r_ld_acl_clk_adj_set , 0x40036a00 ) +PROVIDE ( r_ld_acl_clk_off_get , 0x40036b00 ) +PROVIDE ( r_ld_acl_bit_off_get , 0x40036b18 ) +PROVIDE ( r_ld_acl_role_get , 0x40036b30 ) +PROVIDE ( r_ld_acl_afh_set , 0x40036b60 ) +PROVIDE ( r_ld_acl_afh_prepare , 0x40036c84 ) +PROVIDE ( r_ld_acl_afh_confirm , 0x40036d40 ) +PROVIDE ( r_ld_acl_active_hop_types_get , 0x40036e10 ) +PROVIDE ( r_ld_acl_rx_max_slot_get , 0x40036e58 ) +PROVIDE ( r_ld_acl_rx_max_slot_set , 0x40036ea0 ) +PROVIDE ( r_ld_acl_test_mode_set , 0x40036f24 ) +PROVIDE ( r_ld_acl_rssi_delta_get , 0x40037028 ) +PROVIDE ( r_ld_sco_start , 0x40037110 ) +PROVIDE ( r_ld_sco_update , 0x40037a74 ) +PROVIDE ( r_ld_sco_stop , 0x40037c40 ) +PROVIDE ( r_ld_sco_audio_isr , 0x40037cc8 ) +PROVIDE ( r_ld_sco_data_tx , 0x40037ee8 ) +PROVIDE ( r_ld_bcst_acl_start , 0x4003882c ) +PROVIDE ( r_ld_bcst_acl_init , 0x40038bd0 ) +PROVIDE ( r_ld_bcst_acl_reset , 0x40038bdc ) +PROVIDE ( r_ld_bcst_lmp_tx , 0x40038bf8 ) +PROVIDE ( r_ld_bcst_acl_data_tx , 0x40038d3c ) +PROVIDE ( r_ld_bcst_afh_update , 0x40038f3c ) +PROVIDE ( r_ld_bcst_tx_enc , 0x40038ff8 ) +PROVIDE ( r_ld_bcst_enc_key_load , 0x4003906c ) +PROVIDE ( r_ld_csb_rx_init , 0x40039690 ) +PROVIDE ( r_ld_csb_rx_reset , 0x4003969c ) +PROVIDE ( r_ld_csb_rx_start , 0x4003972c ) +PROVIDE ( r_ld_csb_rx_afh_update , 0x40039af4 ) +PROVIDE ( r_ld_csb_rx_stop , 0x40039bb8 ) +PROVIDE ( r_ld_csb_tx_init , 0x4003a0e8 ) +PROVIDE ( r_ld_csb_tx_reset , 0x4003a0f8 ) +PROVIDE ( r_ld_csb_tx_en , 0x4003a1c0 ) +PROVIDE ( r_ld_csb_tx_dis , 0x4003a5e8 ) +PROVIDE ( r_ld_csb_tx_afh_update , 0x4003a5fc ) +PROVIDE ( r_ld_csb_tx_set_data , 0x4003a6c0 ) +PROVIDE ( r_ld_csb_tx_clr_data , 0x4003a71c ) +PROVIDE ( r_ld_fm_init , 0x4003a760 ) +PROVIDE ( r_ld_fm_reset , 0x4003a794 ) +PROVIDE ( r_ld_fm_clk_isr , 0x4003a7a8 ) +PROVIDE ( r_ld_fm_rx_isr , 0x4003a7f4 ) +PROVIDE ( r_ld_fm_frame_isr , 0x4003a82c ) +PROVIDE ( r_ld_fm_sket_isr , 0x4003a8a4 ) +PROVIDE ( r_ld_fm_prog_enable , 0x4003a944 ) +PROVIDE ( r_ld_fm_prog_disable , 0x4003a984 ) +PROVIDE ( r_ld_fm_prog_push , 0x4003a9d4 ) +PROVIDE ( r_ld_fm_prog_check , 0x4003ab28 ) +PROVIDE ( r_ld_inq_init , 0x4003b15c ) +PROVIDE ( r_ld_inq_reset , 0x4003b168 ) +PROVIDE ( r_ld_inq_start , 0x4003b1f0 ) +PROVIDE ( r_ld_inq_stop , 0x4003b4f0 ) +PROVIDE ( r_ld_iscan_init , 0x4003b9f0 ) +PROVIDE ( r_ld_iscan_reset , 0x4003ba14 ) +PROVIDE ( r_ld_iscan_restart , 0x4003ba44 ) +PROVIDE ( r_ld_iscan_start , 0x4003bb28 ) +PROVIDE ( r_ld_iscan_stop , 0x4003bf1c ) +PROVIDE ( r_ld_iscan_eir_set , 0x4003bfa0 ) +PROVIDE ( r_ld_iscan_eir_get , 0x4003c118 ) +PROVIDE ( r_ld_iscan_tx_pwr_get , 0x4003c138 ) +PROVIDE ( r_ld_channel_assess , 0x4003c184 ) +PROVIDE ( r_ld_init , 0x4003c294 ) +PROVIDE ( r_ld_reset , 0x4003c714 ) +PROVIDE ( r_ld_read_clock , 0x4003c9e4 ) +PROVIDE ( r_ld_bd_addr_get , 0x4003ca20 ) +PROVIDE ( r_ld_class_of_dev_get , 0x4003ca34 ) +PROVIDE ( r_ld_class_of_dev_set , 0x4003ca50 ) +PROVIDE ( r_ld_version_get , 0x4003ca6c ) +PROVIDE ( r_ld_timing_accuracy_get , 0x4003caac ) +PROVIDE ( r_ld_active_check , 0x4003cac4 ) +PROVIDE ( r_ld_afh_ch_assess_data_get , 0x4003caec ) +PROVIDE ( r_ld_wlcoex_set , 0x4003caf8 ) +PROVIDE ( r_ld_page_init , 0x4003d808 ) +PROVIDE ( r_ld_page_reset , 0x4003d814 ) +PROVIDE ( r_ld_page_start , 0x4003d848 ) +PROVIDE ( r_ld_page_stop , 0x4003da54 ) +PROVIDE ( r_ld_pca_init , 0x4003deb4 ) +PROVIDE ( r_ld_pca_reset , 0x4003df0c ) +PROVIDE ( r_ld_pca_local_config , 0x4003df6c ) +PROVIDE ( r_ld_pca_reporting_enable , 0x4003e018 ) +PROVIDE ( r_ld_pca_update_target_offset , 0x4003e050 ) +PROVIDE ( r_ld_pca_mws_frame_sync , 0x4003e104 ) +PROVIDE ( r_ld_pca_mws_moment_offset_gt , 0x4003e278 ) +PROVIDE ( r_ld_pca_mws_moment_offset_lt , 0x4003e280 ) +PROVIDE ( r_ld_pca_coarse_clock_adjust , 0x4003e324 ) +PROVIDE ( r_ld_pca_initiate_clock_dragging , 0x4003e4ac ) +PROVIDE ( r_ld_pscan_evt_handler , 0x4003f238 ) +PROVIDE ( r_ld_pscan_init , 0x4003f474 ) +PROVIDE ( r_ld_pscan_reset , 0x4003f498 ) +PROVIDE ( r_ld_pscan_restart , 0x4003f4b8 ) +PROVIDE ( r_ld_pscan_start , 0x4003f514 ) +PROVIDE ( r_ld_pscan_stop , 0x4003f618 ) +PROVIDE ( r_ld_sched_compute , 0x4003f6f8 ) +PROVIDE ( r_ld_sched_init , 0x4003f7ac ) +PROVIDE ( r_ld_sched_reset , 0x4003f7d4 ) +PROVIDE ( r_ld_sched_iscan_add , 0x4003f7e8 ) +PROVIDE ( r_ld_sched_iscan_remove , 0x4003f808 ) +PROVIDE ( r_ld_sched_pscan_add , 0x4003f828 ) +PROVIDE ( r_ld_sched_pscan_remove , 0x4003f848 ) +PROVIDE ( r_ld_sched_sscan_add , 0x4003f868 ) +PROVIDE ( r_ld_sched_sscan_remove , 0x4003f888 ) +PROVIDE ( r_ld_sched_inq_add , 0x4003f8a8 ) +PROVIDE ( r_ld_sched_inq_remove , 0x4003f8d0 ) +PROVIDE ( r_ld_sched_page_add , 0x4003f910 ) +PROVIDE ( r_ld_sched_page_remove , 0x4003f938 ) +PROVIDE ( r_ld_sched_acl_add , 0x4003f978 ) +PROVIDE ( r_ld_sched_acl_remove , 0x4003f99c ) +PROVIDE ( r_ld_sched_sniff_add , 0x4003f9c4 ) +PROVIDE ( r_ld_sched_sniff_remove , 0x4003fa0c ) +PROVIDE ( r_ld_sched_sco_add , 0x4003fa4c ) +PROVIDE ( r_ld_sched_sco_remove , 0x4003fa9c ) +PROVIDE ( r_ld_sscan_init , 0x400402f0 ) +PROVIDE ( r_ld_sscan_reset , 0x400402fc ) +PROVIDE ( r_ld_sscan_activated , 0x4004031c ) +PROVIDE ( r_ld_sscan_start , 0x40040384 ) +PROVIDE ( r_ld_strain_init , 0x400409f4 ) +PROVIDE ( r_ld_strain_reset , 0x40040a00 ) +PROVIDE ( r_ld_strain_start , 0x40040a8c ) +PROVIDE ( r_ld_strain_stop , 0x40040df0 ) +PROVIDE ( r_ld_util_fhs_unpk , 0x40040e54 ) +PROVIDE ( r_ld_util_bch_create , 0x40040fcc ) +PROVIDE ( r_ld_util_fhs_pk , 0x400411c8 ) +PROVIDE ( r_ld_util_active_master_afh_map_set , 0x40041308 ) +PROVIDE ( r_ld_util_active_master_afh_map_get , 0x4004131c ) +PROVIDE ( r_ld_util_stp_unpk , 0x40041324 ) +PROVIDE ( r_ld_util_stp_pk , 0x400413f4 ) +PROVIDE ( r_llc_ch_assess_local , 0x40041494 ) +PROVIDE ( r_llc_ch_assess_get_local_ch_map , 0x4004150c ) +PROVIDE ( r_llc_ch_assess_get_current_ch_map , 0x40041574 ) +PROVIDE ( r_llc_ch_assess_merge_ch , 0x40041588 ) +PROVIDE ( r_llc_ch_assess_reass_ch , 0x400415c0 ) +PROVIDE ( llc_hci_cmd_handler_tab_p_get , 0x40042358 ) +PROVIDE ( llc_hci_command_handler , 0x40042360 ) +PROVIDE ( llc_hci_acl_data_tx_handler , 0x40042398 ) +PROVIDE ( llcp_pdu_handler_tab_p_get , 0x40043f64 ) +PROVIDE ( r_llc_llcp_version_ind_pdu_send , 0x40043f6c ) +PROVIDE ( r_llc_llcp_ch_map_update_pdu_send , 0x40043f94 ) +PROVIDE ( r_llc_llcp_pause_enc_req_pdu_send , 0x40043fd8 ) +PROVIDE ( r_llc_llcp_pause_enc_rsp_pdu_send , 0x40044010 ) +PROVIDE ( r_llc_llcp_enc_req_pdu_send , 0x40044064 ) +PROVIDE ( r_llc_llcp_enc_rsp_pdu_send , 0x40044160 ) +PROVIDE ( r_llc_llcp_start_enc_rsp_pdu_send , 0x400441f8 ) +PROVIDE ( r_llc_llcp_reject_ind_pdu_send , 0x4004425c ) +PROVIDE ( r_llc_llcp_con_update_pdu_send , 0x400442c4 ) +PROVIDE ( r_llc_llcp_con_param_req_pdu_send , 0x400442fc ) +PROVIDE ( r_llc_llcp_con_param_rsp_pdu_send , 0x40044358 ) +PROVIDE ( r_llc_llcp_feats_req_pdu_send , 0x400443b4 ) +PROVIDE ( r_llc_llcp_feats_rsp_pdu_send , 0x400443f0 ) +PROVIDE ( r_llc_llcp_start_enc_req_pdu_send , 0x4004441c ) +PROVIDE ( r_llc_llcp_terminate_ind_pdu_send , 0x400444b0 ) +PROVIDE ( r_llc_llcp_unknown_rsp_send_pdu , 0x40044534 ) +PROVIDE ( r_llc_llcp_ping_req_pdu_send , 0x4004454c ) +PROVIDE ( r_llc_llcp_ping_rsp_pdu_send , 0x40044560 ) +PROVIDE ( r_llc_llcp_length_req_pdu_send , 0x40044574 ) +PROVIDE ( r_llc_llcp_length_rsp_pdu_send , 0x400445ac ) +PROVIDE ( r_llc_llcp_tester_send , 0x400445e4 ) +PROVIDE ( r_llc_llcp_recv_handler , 0x40044678 ) +PROVIDE ( r_llc_llcp_get_autorize , 0x4004475c ) +PROVIDE ( r_llc_init , 0x40044778 ) +PROVIDE ( r_llc_reset , 0x400447b8 ) +PROVIDE ( r_llc_start , 0x400447f4 ) +PROVIDE ( r_llc_stop , 0x400449ac ) +PROVIDE ( r_llc_discon_event_complete_send , 0x40044a30 ) +PROVIDE ( r_llc_le_con_cmp_evt_send , 0x40044a78 ) +PROVIDE ( r_llc_con_update_complete_send , 0x40044d68 ) +PROVIDE ( r_llc_ltk_req_send , 0x40044dc0 ) +PROVIDE ( r_llc_feats_rd_event_send , 0x40044e0c ) +PROVIDE ( r_llc_version_rd_event_send , 0x40044e60 ) +PROVIDE ( r_llc_common_cmd_complete_send , 0x40044eac ) +PROVIDE ( r_llc_common_cmd_status_send , 0x40044ee0 ) +PROVIDE ( r_llc_common_flush_occurred_send , 0x40044f0c ) +PROVIDE ( r_llc_common_enc_key_ref_comp_evt_send , 0x40044f38 ) +PROVIDE ( r_llc_common_enc_change_evt_send , 0x40044f6c ) +PROVIDE ( r_llc_common_nb_of_pkt_comp_evt_send , 0x40045000 ) +PROVIDE ( r_llc_con_update_ind , 0x40045038 ) +PROVIDE ( r_llc_lsto_con_update , 0x40045098 ) +PROVIDE ( r_llc_map_update_ind , 0x400450f0 ) +PROVIDE ( r_llc_con_update_finished , 0x4004518c ) +PROVIDE ( r_llc_map_update_finished , 0x40045260 ) +PROVIDE ( llc_default_state_tab_p_get , 0x40046058 ) +PROVIDE ( r_llc_util_get_free_conhdl , 0x400460c8 ) +PROVIDE ( r_llc_util_get_nb_active_link , 0x40046100 ) +PROVIDE ( r_llc_util_dicon_procedure , 0x40046130 ) +PROVIDE ( r_llc_util_update_channel_map , 0x400461ac ) +PROVIDE ( r_llc_util_set_llcp_discard_enable , 0x400461c8 ) +PROVIDE ( r_llc_util_set_auth_payl_to_margin , 0x400461f4 ) +PROVIDE ( r_llc_util_clear_operation_ptr , 0x40046234 ) +PROVIDE ( r_llc_util_bw_mgt , 0x4004629c ) +PROVIDE ( r_llc_end_evt_defer , 0x40046330 ) +PROVIDE ( r_llc_pdu_llcp_tx_ack_defer , 0x400463ac ) +PROVIDE ( r_llc_pdu_acl_tx_ack_defer , 0x400464dc ) +PROVIDE ( r_llc_pdu_defer , 0x40046528 ) +PROVIDE ( r_lld_evt_delete_elt_handler , 0x40046974 ) +PROVIDE ( r_lld_evt_delete_elt_push , 0x40046a3c ) +PROVIDE ( r_lld_evt_channel_next , 0x40046aac ) +PROVIDE ( r_lld_evt_init , 0x40046b3c ) +PROVIDE ( r_lld_evt_init_evt , 0x40046cd0 ) +PROVIDE ( r_lld_evt_restart , 0x40046d50 ) +PROVIDE ( r_lld_evt_elt_insert , 0x400474c8 ) +PROVIDE ( r_lld_evt_elt_delete , 0x40047538 ) +PROVIDE ( r_lld_evt_drift_compute , 0x40047670 ) +PROVIDE ( r_lld_evt_schedule_next_instant , 0x400476a8 ) +PROVIDE ( r_lld_evt_schedule_next , 0x400477dc ) +PROVIDE ( r_lld_evt_schedule , 0x40047908 ) +PROVIDE ( r_lld_evt_prevent_stop , 0x40047adc ) +PROVIDE ( r_lld_evt_scan_create , 0x40047ae8 ) +PROVIDE ( r_lld_evt_move_to_master , 0x40047ba0 ) +PROVIDE ( r_lld_evt_update_create , 0x40047cd8 ) +PROVIDE ( r_lld_evt_move_to_slave , 0x40047e18 ) +PROVIDE ( r_lld_evt_slave_update , 0x40048138 ) +PROVIDE ( r_lld_evt_adv_create , 0x400481f4 ) +PROVIDE ( r_lld_evt_deffered_elt_handler , 0x400482bc ) +PROVIDE ( r_lld_evt_end , 0x400483e8 ) +PROVIDE ( r_lld_evt_rx , 0x40048578 ) +PROVIDE ( r_lld_evt_canceled , 0x400485c8 ) +PROVIDE ( r_lld_evt_end_isr , 0x4004862c ) +PROVIDE ( r_lld_evt_rx_isr , 0x40048678 ) +PROVIDE ( r_lld_init , 0x4004873c ) +PROVIDE ( r_lld_core_reset , 0x40048a9c ) +PROVIDE ( r_lld_adv_start , 0x40048b38 ) +PROVIDE ( r_lld_adv_stop , 0x40048ea0 ) +PROVIDE ( r_lld_scan_start , 0x40048ee0 ) +PROVIDE ( r_lld_scan_stop , 0x40049190 ) +PROVIDE ( r_lld_con_start , 0x400491f8 ) +PROVIDE ( r_lld_move_to_master , 0x400499e0 ) +PROVIDE ( r_lld_con_update_req , 0x40049b60 ) +PROVIDE ( r_lld_con_update_after_param_req , 0x40049bcc ) +PROVIDE ( r_lld_con_param_rsp , 0x40049e00 ) +PROVIDE ( r_lld_con_param_req , 0x40049f0c ) +PROVIDE ( r_lld_con_stop , 0x40049fdc ) +PROVIDE ( r_lld_get_mode , 0x40049ff8 ) +PROVIDE ( r_lld_move_to_slave , 0x4004a024 ) +PROVIDE ( r_lld_ch_map_ind , 0x4004a2f4 ) +PROVIDE ( r_lld_con_update_ind , 0x4004a30c ) +PROVIDE ( r_lld_crypt_isr , 0x4004a324 ) +PROVIDE ( r_lld_test_mode_tx , 0x4004a350 ) +PROVIDE ( r_lld_test_mode_rx , 0x4004a540 ) +PROVIDE ( r_lld_test_stop , 0x4004a710 ) +PROVIDE ( r_lld_ral_renew_req , 0x4004a73c ) +PROVIDE ( r_lld_pdu_check , 0x4004ac34 ) +PROVIDE ( r_lld_pdu_tx_loop , 0x4004ae40 ) +PROVIDE ( r_lld_pdu_data_tx_push , 0x4004aecc ) +PROVIDE ( r_lld_pdu_data_send , 0x4004b018 ) +PROVIDE ( r_lld_pdu_tx_push , 0x4004b080 ) +PROVIDE ( r_lld_pdu_tx_prog , 0x4004b120 ) +PROVIDE ( r_lld_pdu_tx_flush , 0x4004b414 ) +PROVIDE ( r_lld_pdu_adv_pack , 0x4004b488 ) +PROVIDE ( r_lld_pdu_rx_handler , 0x4004b4d4 ) +PROVIDE ( r_lld_pdu_send_packet , 0x4004b774 ) +PROVIDE ( r_lld_util_instant_get , 0x4004b890 ) +PROVIDE ( r_lld_util_get_bd_address , 0x4004b8ac ) +PROVIDE ( r_lld_util_set_bd_address , 0x4004b8f8 ) +PROVIDE ( r_lld_util_ral_force_rpa_renew , 0x4004b980 ) +PROVIDE ( r_lld_util_freq2chnl , 0x4004b9e4 ) +PROVIDE ( r_lld_util_get_local_offset , 0x4004ba10 ) +PROVIDE ( r_lld_util_get_peer_offset , 0x4004ba24 ) +PROVIDE ( r_lld_util_connection_param_set , 0x4004ba40 ) +PROVIDE ( r_lld_util_dle_set_cs_fields , 0x4004ba90 ) +PROVIDE ( r_lld_util_anchor_point_move , 0x4004bacc ) +PROVIDE ( r_lld_util_flush_list , 0x4004bbd8 ) +PROVIDE ( r_lld_util_instant_ongoing , 0x4004bbfc ) +PROVIDE ( r_lld_util_compute_ce_max , 0x4004bc0c ) +PROVIDE ( r_lld_util_elt_programmed , 0x4004bce0 ) +PROVIDE ( r_lld_util_priority_set , 0x4004bd10 ) +PROVIDE ( r_lld_util_priority_update , 0x4004bd78 ) +PROVIDE ( r_lld_util_get_tx_pkt_cnt , 0x4004bd80 ) +PROVIDE ( r_lld_util_eff_tx_time_set , 0x4004bd88 ) +PROVIDE ( r_lld_wlcoex_set , 0x4004bd98 ) +PROVIDE ( llm_hci_cmd_handler_tab_p_get , 0x4004c920 ) +PROVIDE ( hci_command_handler , 0x4004c928 ) +PROVIDE ( r_llm_init , 0x4004c9f8 ) +PROVIDE ( r_llm_ble_ready , 0x4004cc34 ) +PROVIDE ( r_llm_con_req_ind , 0x4004cc54 ) +PROVIDE ( r_llm_le_adv_report_ind , 0x4004cdf4 ) +PROVIDE ( r_llm_con_req_tx_cfm , 0x4004d158 ) +PROVIDE ( r_llm_common_cmd_complete_send , 0x4004d288 ) +PROVIDE ( r_llm_common_cmd_status_send , 0x4004d2b4 ) +PROVIDE ( r_llm_test_mode_start_tx , 0x4004d2fc ) +PROVIDE ( r_llm_test_mode_start_rx , 0x4004d534 ) +PROVIDE ( r_llm_set_adv_param , 0x4004d5f4 ) +PROVIDE ( r_llm_set_adv_en , 0x4004d7ec ) +PROVIDE ( r_llm_set_adv_data , 0x4004d960 ) +PROVIDE ( r_llm_set_scan_rsp_data , 0x4004da14 ) +PROVIDE ( r_llm_set_scan_param , 0x4004dac8 ) +PROVIDE ( r_llm_set_scan_en , 0x4004db64 ) +PROVIDE ( r_llm_wl_clr , 0x4004dc54 ) +PROVIDE ( r_llm_wl_dev_add , 0x4004dcc0 ) +PROVIDE ( r_llm_wl_dev_rem , 0x4004dcfc ) +PROVIDE ( r_llm_wl_dev_add_hdl , 0x4004dd38 ) +PROVIDE ( r_llm_wl_dev_rem_hdl , 0x4004dde0 ) +PROVIDE ( r_llm_create_con , 0x4004de78 ) +PROVIDE ( r_llm_encryption_done , 0x4004dff8 ) +PROVIDE ( r_llm_encryption_start , 0x4004e128 ) +PROVIDE ( r_llm_ral_clear , 0x4004e1fc ) +PROVIDE ( r_llm_ral_dev_add , 0x4004e23c ) +PROVIDE ( r_llm_ral_dev_rm , 0x4004e3bc ) +PROVIDE ( r_llm_ral_get_rpa , 0x4004e400 ) +PROVIDE ( r_llm_ral_set_timeout , 0x4004e4a0 ) +PROVIDE ( r_llm_ral_update , 0x4004e4f8 ) +PROVIDE ( llm_default_state_tab_p_get , 0x4004e718 ) +PROVIDE ( r_llm_util_bd_addr_wl_position , 0x4004e720 ) +PROVIDE ( r_llm_util_bd_addr_in_wl , 0x4004e788 ) +PROVIDE ( r_llm_util_check_address_validity , 0x4004e7e4 ) +PROVIDE ( r_llm_util_check_map_validity , 0x4004e800 ) +PROVIDE ( r_llm_util_apply_bd_addr , 0x4004e868 ) +PROVIDE ( r_llm_util_set_public_addr , 0x4004e89c ) +PROVIDE ( r_llm_util_check_evt_mask , 0x4004e8b0 ) +PROVIDE ( r_llm_util_get_channel_map , 0x4004e8d4 ) +PROVIDE ( r_llm_util_get_supp_features , 0x4004e8e8 ) +PROVIDE ( r_llm_util_adv_data_update , 0x4004e8fc ) +PROVIDE ( r_llm_util_bl_check , 0x4004e930 ) +PROVIDE ( r_llm_util_bl_add , 0x4004e9ac ) +PROVIDE ( r_llm_util_bl_rem , 0x4004ea70 ) +PROVIDE ( r_llm_util_bd_addr_in_ral , 0x4004eb08 ) +PROVIDE ( r_llm_end_evt_defer , 0x4004eb6c ) +PROVIDE ( r_llm_pdu_defer , 0x4004ec48 ) +PROVIDE ( r_lm_init , 0x4004ed34 ) +PROVIDE ( r_lm_lt_addr_alloc , 0x4004ef1c ) +PROVIDE ( r_lm_lt_addr_reserve , 0x4004ef48 ) +PROVIDE ( r_lm_lt_addr_free , 0x4004ef74 ) +PROVIDE ( r_lm_get_nb_acl , 0x4004ef9c ) +PROVIDE ( r_lm_role_switch_start , 0x4004efe0 ) +PROVIDE ( r_lm_role_switch_finished , 0x4004f028 ) +PROVIDE ( r_lm_read_features , 0x4004f0d8 ) +PROVIDE ( r_lm_acl_disc , 0x4004f148 ) +PROVIDE ( r_lm_get_auth_en , 0x4004f1ac ) +PROVIDE ( r_lm_get_sp_en , 0x4004f1c0 ) +PROVIDE ( r_lm_get_sec_con_host_supp , 0x4004f1d4 ) +PROVIDE ( r_LM_GetPINType , 0x4004f1e8 ) +PROVIDE ( r_LM_GetConnectionAcceptTimeout , 0x4004f1f4 ) +PROVIDE ( r_LM_GetLocalNameSeg , 0x4004f200 ) +PROVIDE ( r_lm_get_loopback_mode , 0x4004f248 ) +PROVIDE ( r_lm_get_pub_key_192 , 0x4004f258 ) +PROVIDE ( r_lm_get_priv_key_192 , 0x4004f278 ) +PROVIDE ( r_lm_get_pub_key_256 , 0x4004f298 ) +PROVIDE ( r_lm_get_priv_key_256 , 0x4004f2b8 ) +PROVIDE ( r_lm_get_oob_local_data_192 , 0x4004f2d4 ) +PROVIDE ( r_lm_get_oob_local_data_256 , 0x4004f318 ) +PROVIDE ( r_lm_get_oob_local_commit , 0x4004f374 ) +PROVIDE ( r_lm_sp_debug_mode_get , 0x4004f398 ) +PROVIDE ( r_lm_debug_key_compare_192 , 0x4004f3a8 ) +PROVIDE ( r_lm_debug_key_compare_256 , 0x4004f3d0 ) +PROVIDE ( r_lm_dut_mode_en_get , 0x4004f3ec ) +PROVIDE ( r_lm_afh_ch_ass_en_get , 0x4004f3f8 ) +PROVIDE ( r_lm_sync_flow_ctrl_en_get , 0x4004f404 ) +PROVIDE ( r_lm_afh_host_ch_class_get , 0x4004f410 ) +PROVIDE ( r_lm_afh_peer_ch_class_set , 0x4004f418 ) +PROVIDE ( r_lm_afh_master_ch_map_get , 0x4004f43c ) +PROVIDE ( r_lm_afh_activate_timer , 0x4004f444 ) +PROVIDE ( r_lm_is_acl_con , 0x4004f47c ) +PROVIDE ( r_lm_is_acl_con_role , 0x4004f49c ) +PROVIDE ( r_lm_is_clk_adj_instant_pending , 0x4004f4c8 ) +PROVIDE ( r_lm_clk_adj_instant_pending_set , 0x4004f4d8 ) +PROVIDE ( r_lm_is_clk_adj_ack_pending , 0x4004f4e8 ) +PROVIDE ( r_lm_num_clk_adj_ack_pending_set , 0x4004f500 ) +PROVIDE ( r_lm_clk_adj_ack_pending_clear , 0x4004f514 ) +PROVIDE ( r_lm_local_ext_fr_configured , 0x4004f540 ) +PROVIDE ( r_lm_pca_sscan_link_set , 0x4004f550 ) +PROVIDE ( r_lm_pca_sscan_link_get , 0x4004f560 ) +PROVIDE ( r_lm_get_sync_param , 0x400503b4 ) +PROVIDE ( r_lm_init_sync , 0x400512d8 ) +PROVIDE ( r_lm_reset_sync , 0x40051304 ) +PROVIDE ( r_lm_check_active_sync , 0x40051334 ) +PROVIDE ( r_lm_add_sync , 0x40051358 ) +PROVIDE ( r_lm_modif_sync , 0x40051578 ) +PROVIDE ( r_lm_check_sync_hl_rsp , 0x4005169c ) +PROVIDE ( r_lm_get_synchdl , 0x4005175c ) +PROVIDE ( r_lm_look_for_sync , 0x40051774 ) +PROVIDE ( r_lm_get_nb_sync_link , 0x4005179c ) +PROVIDE ( r_lm_get_min_sync_intv , 0x400517a8 ) +PROVIDE ( r_lm_remove_sync , 0x400517c8 ) +PROVIDE ( r_lm_sco_nego_end , 0x40051828 ) +PROVIDE ( r_lm_master_clk_adj_req_handler , 0x40054180 ) +PROVIDE ( lm_hci_cmd_handler_tab_p_get , 0x4005425c ) +PROVIDE ( lm_default_state_tab_p_get , 0x40054268 ) +PROVIDE ( btdm_r_modules_func_p_set , 0x40054270 ) +PROVIDE ( btdm_r_modules_func_p_get , 0x4005427c ) +PROVIDE ( btdm_r_plf_func_p_set , 0x40054288 ) +PROVIDE ( btdm_r_import_rf_phy_func_p_get , 0x40054298 ) +PROVIDE ( r_nvds_init , 0x40054410 ) +PROVIDE ( r_nvds_get , 0x40054488 ) +PROVIDE ( r_nvds_del , 0x400544c4 ) +PROVIDE ( r_nvds_lock , 0x400544fc ) +PROVIDE ( r_nvds_put , 0x40054534 ) +PROVIDE ( r_rf_rw_bt_init , 0x40054868 ) +PROVIDE ( r_rf_rw_le_init , 0x400549d0 ) +PROVIDE ( r_rf_rw_init , 0x40054b0c ) +PROVIDE ( r_rwble_init , 0x40054bf4 ) +PROVIDE ( r_rwble_reset , 0x40054ce8 ) +PROVIDE ( r_rwble_sleep_check , 0x40054d78 ) +PROVIDE ( r_rwble_activity_ongoing_check , 0x40054d8c ) +PROVIDE ( r_rwble_version , 0x40054dac ) +PROVIDE ( r_rwble_isr , 0x40054e08 ) +PROVIDE ( r_rwbt_init , 0x40055160 ) +PROVIDE ( r_rwbt_reset , 0x400551bc ) +PROVIDE ( r_rwbt_version , 0x4005520c ) +PROVIDE ( r_rwbt_isr , 0x40055248 ) +PROVIDE ( r_rwbt_sleep_check , 0x4005577c ) +PROVIDE ( r_rwbt_sleep_enter , 0x400557a4 ) +PROVIDE ( r_rwbt_sleep_wakeup , 0x400557fc ) +PROVIDE ( r_rwbt_sleep_wakeup_end , 0x400558cc ) +PROVIDE ( rwip_rf_p_get , 0x400558f4 ) +PROVIDE ( r_rwip_check_wakeup_boundary , 0x400558fc ) +PROVIDE ( r_rwip_init , 0x4005595c ) +PROVIDE ( r_rwip_reset , 0x40055ab8 ) +PROVIDE ( r_rwip_version , 0x40055b20 ) +PROVIDE ( r_rwip_schedule , 0x40055b38 ) +PROVIDE ( r_rwip_sleep , 0x40055b5c ) +PROVIDE ( r_rwip_wakeup , 0x40055dc4 ) +PROVIDE ( r_rwip_wakeup_end , 0x40055e18 ) +PROVIDE ( r_rwip_wakeup_delay_set , 0x40055e4c ) +PROVIDE ( r_rwip_prevent_sleep_set , 0x40055e64 ) +PROVIDE ( r_rwip_prevent_sleep_clear , 0x40055ec8 ) +PROVIDE ( r_rwip_sleep_enable , 0x40055f30 ) +PROVIDE ( r_rwip_ext_wakeup_enable , 0x40055f3c ) +PROVIDE ( r_rwip_pca_clock_dragging_only , 0x40055f48 ) +PROVIDE ( r_rwip_wlcoex_set , 0x40055f60 ) +PROVIDE ( r_rwip_assert_err , 0x40055f88 ) +PROVIDE ( r_ke_queue_extract , 0x40055fd0 ) +PROVIDE ( r_ke_queue_insert , 0x40056020 ) +PROVIDE ( __utoa , 0x400561f0 ) +PROVIDE ( utoa , 0x40056258 ) +PROVIDE ( setjmp , 0x40056268 ) +PROVIDE ( longjmp , 0x400562cc ) +PROVIDE ( abs , 0x40056340 ) +PROVIDE ( div , 0x40056348 ) +PROVIDE ( labs , 0x40056370 ) +PROVIDE ( ldiv , 0x40056378 ) +PROVIDE ( qsort , 0x40056424 ) +PROVIDE ( __itoa , 0x40056678 ) +PROVIDE ( itoa , 0x400566b4 ) +PROVIDE ( atoi , 0x400566c4 ) +PROVIDE ( _atoi_r , 0x400566d4 ) +PROVIDE ( atol , 0x400566ec ) +PROVIDE ( _atol_r , 0x400566fc ) +PROVIDE ( _strtol_r , 0x40056714 ) +PROVIDE ( strtol , 0x4005681c ) +PROVIDE ( _strtoul_r , 0x40056834 ) +PROVIDE ( strtoul , 0x4005692c ) +PROVIDE ( _iprintf_r , 0x40056944 ) +PROVIDE ( _printf_r , 0x40056944 ) +PROVIDE ( iprintf , 0x40056978 ) +PROVIDE ( printf , 0x40056978 ) +PROVIDE ( viprintf , 0x400569b4 ) +PROVIDE ( vprintf , 0x400569b4 ) +PROVIDE ( _viprintf_r , 0x400569e4 ) +PROVIDE ( _vprintf_r , 0x400569e4 ) +PROVIDE ( _vsniprintf_r , 0x40056a14 ) +PROVIDE ( _vsnprintf_r , 0x40056a14 ) +PROVIDE ( vsniprintf , 0x40056a68 ) +PROVIDE ( vsnprintf , 0x40056a68 ) +PROVIDE ( _vsiprintf_r , 0x40056a90 ) +PROVIDE ( _vsprintf_r , 0x40056a90 ) +PROVIDE ( vsiprintf , 0x40056ac4 ) +PROVIDE ( vsprintf , 0x40056ac4 ) +PROVIDE ( _sniprintf_r , 0x40056ae4 ) +PROVIDE ( _snprintf_r , 0x40056ae4 ) +PROVIDE ( sniprintf , 0x40056b4c ) +PROVIDE ( snprintf , 0x40056b4c ) +PROVIDE ( _siprintf_r , 0x40056bbc ) +PROVIDE ( _sprintf_r , 0x40056bbc ) +PROVIDE ( siprintf , 0x40056c08 ) +PROVIDE ( sprintf , 0x40056c08 ) +PROVIDE ( _asniprintf_r , 0x40056c64 ) +PROVIDE ( _asnprintf_r , 0x40056c64 ) +PROVIDE ( asniprintf , 0x40056cd8 ) +PROVIDE ( asnprintf , 0x40056cd8 ) +PROVIDE ( _asiprintf_r , 0x40056d4c ) +PROVIDE ( _asprintf_r , 0x40056d4c ) +PROVIDE ( asiprintf , 0x40056d9c ) +PROVIDE ( asprintf , 0x40056d9c ) +PROVIDE ( _vasniprintf_r , 0x40056df8 ) +PROVIDE ( _vasnprintf_r , 0x40056df8 ) +PROVIDE ( vasniprintf , 0x40056e58 ) +PROVIDE ( vasnprintf , 0x40056e58 ) +PROVIDE ( _vasiprintf_r , 0x40056e80 ) +PROVIDE ( _vasprintf_r , 0x40056e80 ) +PROVIDE ( vasiprintf , 0x40056eb8 ) +PROVIDE ( vasprintf , 0x40056eb8 ) +PROVIDE ( _fiprintf_r , 0x40056ed8 ) +PROVIDE ( _fprintf_r , 0x40056ed8 ) +PROVIDE ( fiprintf , 0x40056efc ) +PROVIDE ( fprintf , 0x40056efc ) +PROVIDE ( __ssputs_r , 0x40056f2c ) +PROVIDE ( __ssprint_r , 0x40056ff8 ) +PROVIDE ( _svfiprintf_r , 0x40057100 ) +PROVIDE ( _svfprintf_r , 0x40057100 ) +PROVIDE ( _printf_common , 0x40057338 ) +PROVIDE ( _printf_i , 0x40057404 ) +PROVIDE ( __sfputs_r , 0x40057790 ) +PROVIDE ( __sprint_r , 0x400577e4 ) +PROVIDE ( _vfiprintf_r , 0x40057850 ) +PROVIDE ( _vfprintf_r , 0x40057850 ) +PROVIDE ( vfiprintf , 0x40057ae8 ) +PROVIDE ( vfprintf , 0x40057ae8 ) +PROVIDE ( __svfiscanf_r , 0x40057b08 ) +PROVIDE ( __svfscanf_r , 0x40057b08 ) +PROVIDE ( vfiscanf , 0x40057eb8 ) +PROVIDE ( vfscanf , 0x40057eb8 ) +PROVIDE ( __svfscanf , 0x40057f04 ) +PROVIDE ( _vfiscanf_r , 0x40057f24 ) +PROVIDE ( _vfscanf_r , 0x40057f24 ) +PROVIDE ( _sungetc_r , 0x40057f6c ) +PROVIDE ( __ssrefill_r , 0x40057fec ) +PROVIDE ( __ssvfiscanf_r , 0x4005802c ) +PROVIDE ( __ssvfscanf_r , 0x4005802c ) +PROVIDE ( _scanf_chars , 0x40058384 ) +PROVIDE ( _scanf_i , 0x4005845c ) +PROVIDE ( viscanf , 0x40058698 ) +PROVIDE ( vscanf , 0x40058698 ) +PROVIDE ( _viscanf_r , 0x400586c8 ) +PROVIDE ( _vscanf_r , 0x400586c8 ) +PROVIDE ( _vsiscanf_r , 0x400586f8 ) +PROVIDE ( _vsscanf_r , 0x400586f8 ) +PROVIDE ( vsiscanf , 0x40058740 ) +PROVIDE ( vsscanf , 0x40058740 ) +PROVIDE ( iscanf , 0x40058760 ) +PROVIDE ( scanf , 0x40058760 ) +PROVIDE ( _iscanf_r , 0x4005879c ) +PROVIDE ( _scanf_r , 0x4005879c ) +PROVIDE ( siscanf , 0x400587d0 ) +PROVIDE ( sscanf , 0x400587d0 ) +PROVIDE ( _siscanf_r , 0x40058830 ) +PROVIDE ( _sscanf_r , 0x40058830 ) +PROVIDE ( fiscanf , 0x40058884 ) +PROVIDE ( fscanf , 0x40058884 ) +PROVIDE ( _fiscanf_r , 0x400588b4 ) +PROVIDE ( _fscanf_r , 0x400588b4 ) +PROVIDE ( _wcrtomb_r , 0x400588d8 ) +PROVIDE ( wcrtomb , 0x40058920 ) +PROVIDE ( __sfvwrite_r , 0x4005893c ) +PROVIDE ( __swbuf_r , 0x40058bec ) +PROVIDE ( __swbuf , 0x40058cb4 ) +PROVIDE ( __swsetup_r , 0x40058cc8 ) +PROVIDE ( __fputwc , 0x40058da0 ) +PROVIDE ( _fputwc_r , 0x40058e4c ) +PROVIDE ( fputwc , 0x40058ea8 ) +PROVIDE ( __ascii_wctomb , 0x40058ef0 ) +PROVIDE ( _wctomb_r , 0x40058f14 ) +PROVIDE ( __submore , 0x40058f3c ) +PROVIDE ( _ungetc_r , 0x40058fa0 ) +PROVIDE ( ungetc , 0x400590f4 ) +PROVIDE ( __smakebuf_r , 0x40059108 ) +PROVIDE ( __sflush_r , 0x400591e0 ) +PROVIDE ( _fflush_r , 0x40059320 ) +PROVIDE ( fflush , 0x40059394 ) +PROVIDE ( __srefill_r , 0x400593d4 ) +PROVIDE ( _setlocale_r , 0x4005950c ) +PROVIDE ( __locale_charset , 0x40059540 ) +PROVIDE ( __locale_mb_cur_max , 0x40059548 ) +PROVIDE ( __locale_msgcharset , 0x40059550 ) +PROVIDE ( __locale_cjk_lang , 0x40059558 ) +PROVIDE ( _localeconv_r , 0x40059560 ) +PROVIDE ( setlocale , 0x40059568 ) +PROVIDE ( localeconv , 0x4005957c ) +PROVIDE ( asctime , 0x40059588 ) +PROVIDE ( ctime , 0x400595b0 ) +PROVIDE ( ctime_r , 0x400595c4 ) +PROVIDE ( localtime , 0x400595dc ) +PROVIDE ( localtime_r , 0x400595fc ) +PROVIDE ( gmtime , 0x40059848 ) +PROVIDE ( gmtime_r , 0x40059868 ) +PROVIDE ( strftime , 0x40059ab4 ) +PROVIDE ( mktime , 0x4005a5e8 ) +PROVIDE ( start_tb_console , 0x4005a980 ) +PROVIDE ( ets_sha_enable , 0x4005c07c ) +PROVIDE ( ets_sha_disable , 0x4005c0a8 ) +PROVIDE ( ets_sha_init , 0x4005c0d4 ) +PROVIDE ( ets_sha_finish , 0x4005c104 ) +PROVIDE ( ets_sha_update , 0x4005c2a0 ) +PROVIDE ( ets_bigint_enable , 0x4005c498 ) +PROVIDE ( ets_bigint_disable , 0x4005c4e0 ) +PROVIDE ( ets_bigint_wait_finish , 0x4005c520 ) +PROVIDE ( ets_bigint_mod_power_prepare , 0x4005c54c ) +PROVIDE ( ets_bigint_mod_power_getz , 0x4005c614 ) +PROVIDE ( ets_bigint_mult_prepare , 0x4005c630 ) +PROVIDE ( ets_bigint_mult_getz , 0x4005c6e8 ) +PROVIDE ( ets_bigint_montgomery_mult_prepare , 0x4005c6fc ) +PROVIDE ( ets_bigint_montgomery_mult_getz , 0x4005c7a4 ) +PROVIDE ( ets_bigint_mod_mult_prepare , 0x4005c7b4 ) +PROVIDE ( ets_bigint_mod_mult_getz , 0x4005c818 ) +PROVIDE ( ets_aes_enable , 0x4005c8cc ) +PROVIDE ( ets_aes_disable , 0x4005c8f8 ) +PROVIDE ( ets_aes_set_endian , 0x4005c928 ) +PROVIDE ( ets_aes_setkey_enc , 0x4005c97c ) +PROVIDE ( ets_aes_setkey_dec , 0x4005c994 ) +PROVIDE ( ets_aes_crypt , 0x4005c9b8 ) +PROVIDE ( ets_secure_boot_start , 0x4005ca34 ) +PROVIDE ( ets_secure_boot_finish , 0x4005ca84 ) +PROVIDE ( ets_secure_boot_hash , 0x4005cad4 ) +PROVIDE ( ets_secure_boot_obtain , 0x4005cb14 ) +PROVIDE ( ets_secure_boot_check , 0x4005cb40 ) +PROVIDE ( ets_secure_boot_rd_iv , 0x4005cb84 ) +PROVIDE ( ets_secure_boot_rd_abstract , 0x4005cba8 ) +PROVIDE ( ets_secure_boot_check_start , 0x4005cbcc ) +PROVIDE ( ets_secure_boot_check_finish , 0x4005cc04 ) +PROVIDE ( aes_128_cbc_encrypt , 0x4005cc18 ) +PROVIDE ( aes_128_cbc_decrypt , 0x4005cc7c ) +PROVIDE ( aes_unwrap , 0x4005ccf0 ) +PROVIDE ( base64_encode , 0x4005cdbc ) +PROVIDE ( base64_decode , 0x4005ced8 ) +PROVIDE ( crc32_le , 0x4005cfec ) +PROVIDE ( crc32_be , 0x4005d024 ) +PROVIDE ( crc16_le , 0x4005d05c ) +PROVIDE ( crc16_be , 0x4005d09c ) +PROVIDE ( crc8_le , 0x4005d0e0 ) +PROVIDE ( crc8_be , 0x4005d114 ) +PROVIDE ( esp_crc8 , 0x4005d144 ) +PROVIDE ( hmac_md5_vector , 0x4005d17c ) +PROVIDE ( hmac_md5 , 0x4005d264 ) +PROVIDE ( MD5Init , 0x4005da7c ) +PROVIDE ( MD5Update , 0x4005da9c ) +PROVIDE ( MD5Final , 0x4005db1c ) +PROVIDE ( md5_vector , 0x4005db80 ) +PROVIDE ( mz_adler32 , 0x4005edbc ) +PROVIDE ( mz_crc32 , 0x4005ee88 ) +PROVIDE ( mz_free , 0x4005eed4 ) +PROVIDE ( tinfl_decompress , 0x4005ef30 ) +PROVIDE ( tinfl_decompress_mem_to_mem , 0x40060050 ) +PROVIDE ( tinfl_decompress_mem_to_callback , 0x40060090 ) +PROVIDE ( tdefl_compress , 0x400600bc ) +PROVIDE ( tdefl_compress_buffer , 0x400607f4 ) +PROVIDE ( tdefl_init , 0x40060810 ) +PROVIDE ( tdefl_get_prev_return_status , 0x400608d0 ) +PROVIDE ( tdefl_get_adler32 , 0x400608d8 ) +PROVIDE ( tdefl_compress_mem_to_output , 0x400608e0 ) +PROVIDE ( tdefl_compress_mem_to_mem , 0x40060900 ) +PROVIDE ( tdefl_write_image_to_png_file_in_memory_ex , 0x40060910 ) +PROVIDE ( tdefl_write_image_to_png_file_in_memory , 0x4006091c ) +PROVIDE ( rc4_skip , 0x40060928 ) +PROVIDE ( hmac_sha1_vector , 0x400609e4 ) +PROVIDE ( hmac_sha1 , 0x40060acc ) +PROVIDE ( sha1_prf , 0x40060ae8 ) +PROVIDE ( sha1_vector , 0x40060b64 ) +PROVIDE ( pbkdf2_sha1 , 0x40060ba4 ) +PROVIDE ( hmac_sha256_vector , 0x40060c84 ) +PROVIDE ( hmac_sha256 , 0x40060d58 ) +PROVIDE ( sha256_prf , 0x40060d70 ) +PROVIDE ( sha256_vector , 0x40060e08 ) +PROVIDE ( jd_prepare , 0x40060fa8 ) +PROVIDE ( jd_decomp , 0x400613e8 ) +PROVIDE ( spi_dummy_len_fix , 0x40061d90 ) +PROVIDE ( SelectSpiQIO , 0x40061ddc ) +PROVIDE ( SetSpiDrvs , 0x40061e78 ) +PROVIDE ( SelectSpiFunction , 0x40061f84 ) +PROVIDE ( SPI_user_command_read , 0x400621b0 ) +PROVIDE ( SPI_read_status , 0x4006226c ) +PROVIDE ( SPI_write_status , 0x400622f0 ) +PROVIDE ( SPI_write_enable , 0x40062320 ) +PROVIDE ( SPI_read_status_high , 0x40062448 ) +PROVIDE ( SPI_Common_Command , 0x4006246c ) +PROVIDE ( spi_cache_sram_init , 0x400626e4 ) +PROVIDE ( SPIUnlockXXX , 0x400628b0 ) +PROVIDE ( SPILock , 0x400628f0 ) +PROVIDE ( SPIReadModeCnfig , 0x40062944 ) +PROVIDE ( spi_flash_attach , 0x40062a6c ) +PROVIDE ( SPIMasterReadModeCnfig , 0x40062b64 ) +PROVIDE ( SPIClkConfig , 0x40062bc8 ) +PROVIDE ( SPIEraseChip , 0x40062c14 ) +PROVIDE ( SPIEraseBlock , 0x40062c4c ) +PROVIDE ( SPIEraseSector , 0x40062ccc ) +PROVIDE ( SPIWrite , 0x40062d50 ) +PROVIDE ( SPI_Write_Encrypt_Enable , 0x40062df4 ) +PROVIDE ( SPI_Prepare_Encrypt_Data , 0x40062e1c ) +PROVIDE ( SPI_Write_Encrypt_Disable , 0x40062e60 ) +PROVIDE ( SPI_Encrypt_Write , 0x40062e78 ) +PROVIDE ( SPIRead , 0x40062ed8 ) +PROVIDE ( SPIEraseArea , 0x400631ac ) +PROVIDE ( SPIParamCfg , 0x40063238 ) +PROVIDE ( __mulsf3 , 0x400632c8 ) +PROVIDE ( __eqsf2 , 0x40063374 ) +PROVIDE ( __nesf2 , 0x40063374 ) +PROVIDE ( __gtsf2 , 0x400633a0 ) +PROVIDE ( __lesf2 , 0x400633c0 ) +PROVIDE ( __gesf2 , 0x4006340c ) +PROVIDE ( __ltsf2 , 0x4006342c ) +PROVIDE ( __unordsf2 , 0x40063478 ) +PROVIDE ( __negdf2 , 0x400634a0 ) +PROVIDE ( __muldf3 , 0x4006358c ) +PROVIDE ( __eqdf2 , 0x400636a8 ) +PROVIDE ( __nedf2 , 0x400636a8 ) +PROVIDE ( __gtdf2 , 0x400636dc ) +PROVIDE ( __ledf2 , 0x40063704 ) +PROVIDE ( __gedf2 , 0x40063768 ) +PROVIDE ( __ltdf2 , 0x40063790 ) +PROVIDE ( __unorddf2 , 0x400637f4 ) +PROVIDE ( __cmpdi2 , 0x40063820 ) +PROVIDE ( __ucmpdi2 , 0x40063840 ) +PROVIDE ( __clear_cache , 0x40063860 ) +PROVIDE ( __absvsi2 , 0x40063868 ) +PROVIDE ( __absvdi2 , 0x4006387c ) +PROVIDE ( __powisf2 , 0x4006389c ) +PROVIDE ( __powidf2 , 0x400638d4 ) +PROVIDE ( __mulsc3 , 0x40063934 ) +PROVIDE ( __muldc3 , 0x40063bf4 ) +PROVIDE ( __divsc3 , 0x40064200 ) +PROVIDE ( __divdc3 , 0x40064460 ) +PROVIDE ( __bswapsi2 , 0x4006499c ) +PROVIDE ( __bswapdi2 , 0x400649c4 ) +PROVIDE ( __clrsbsi2 , 0x40064a20 ) +PROVIDE ( __clrsbdi2 , 0x40064a38 ) +PROVIDE ( __gcc_bcmp , 0x40064a70 ) +PROVIDE ( __udiv_w_sdiv , 0x40064aa8 ) +PROVIDE ( __udivmoddi4 , 0x40064ab0 ) diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/Makefile b/src/mongoose-6.11/src/common/platforms/esp32/stubs/Makefile new file mode 100644 index 0000000..857512a --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/stubs/Makefile @@ -0,0 +1,51 @@ +# +# Copyright (c) 2015 Cesanta Software Limited +# All rights reserved +# + +STUB = stub_hello.c +LIBS = +PARAMS = +PORT = /dev/ttyUSB0 + +BUILD_DIR = .build +COMMON_STUB_DIR = ../../esp +STUB_ELF = $(BUILD_DIR)/$(patsubst %.c,%.elf,$(notdir $(STUB))) +STUB_JSON ?= $(BUILD_DIR)/$(patsubst %.c,%.json,$(notdir $(STUB))) +SDK = $(shell cat ../../../../fw/platforms/esp32/sdk.version) +XT_CC = xtensa-esp32-elf-gcc + +.PHONY: all clean run wrap + +all: $(STUB_ELF) + +$(STUB_ELF): $(STUB) $(LIBS) + @echo " CC $^ -> $@" + @[ -d $(BUILD_DIR) ] || mkdir $(BUILD_DIR) + @docker run --rm -i -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \ + "cd /src/common/platforms/esp32/stubs && \ + $(XT_CC) -std=c99 -Wall -Werror -Os -DESP32 \ + -mtext-section-literals -mlongcalls -nostdlib -fno-builtin \ + -I. -I/src/common/platforms/esp \ + -I/opt/Espressif/esp-idf/components/esp32/include \ + -I/opt/Espressif/esp-idf/components/soc/esp32/include \ + -L/opt/Espressif/esp-idf -Wl,-static \ + -ffunction-sections -Wl,--gc-sections \ + -Tstub.ld -o $@ $^" + +wrap: $(STUB_JSON) + +$(STUB_JSON): $(STUB_ELF) $(COMMON_STUB_DIR)/esptool.py + @echo " WRAP $< -> $@" + @docker run --rm -i -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \ + "cd /src/common/platforms/esp32/stubs && \ + $(COMMON_STUB_DIR)/esptool.py wrap_stub $<" > $@ + +run: $(STUB_JSON) + @echo " RUN $< $(PARAMS) -> $(PORT)" + @docker run --rm -i --privileged -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \ + "cd /src/common/platforms/esp32/stubs && \ + $(COMMON_STUB_DIR)/esptool.py --port $(PORT) run_stub $< $(PARAMS)" + +clean: + @rm -rf $(BUILD_DIR) diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/README.md b/src/mongoose-6.11/src/common/platforms/esp32/stubs/README.md new file mode 100644 index 0000000..9265771 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/stubs/README.md @@ -0,0 +1,13 @@ +This is a ESP boot loader stub development environment. + +Code produced in this environment can be loaded and executed +in the bootloader environment. Usually it is used to implement +functionality not found in the bootloader. + +Stubs can be executed using the `run_stub` command of the modified esptool.py provided. +`wrap_stub` produces a JSON represenattion of the stub that can later be reused +or built into other tools. + +Example usage: + $ make run STUB=stub_flash_size.c PORT=/dev/ttyUSB0 + $ make run STUB=stub_md5.c PORT=/dev/ttyUSB0 PARAMS="0x11000 10000 1" diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/led.c b/src/mongoose-6.11/src/common/platforms/esp32/stubs/led.c new file mode 100644 index 0000000..6cfc766 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/stubs/led.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014-2017 Cesanta Software Limited + * All rights reserved + */ + +#include "soc/gpio_reg.h" + +void led_setup(int io) { + if (io < 32) { + WRITE_PERI_REG(GPIO_ENABLE_W1TS_REG, 1 << io); + } else { + WRITE_PERI_REG(GPIO_ENABLE1_W1TS_REG, 1 << (io - 32)); + } +} + +void led_on(int io) { + if (io < 32) { + WRITE_PERI_REG(GPIO_OUT_W1TS_REG, 1 << io); + } else { + WRITE_PERI_REG(GPIO_OUT1_W1TS_REG, 1 << (io - 32)); + } +} + +void led_off(int io) { + if (io < 32) { + WRITE_PERI_REG(GPIO_OUT_W1TC_REG, 1 << io); + } else { + WRITE_PERI_REG(GPIO_OUT1_W1TC_REG, 1 << (io - 32)); + } +} + +void led_toggle(int io) { + if (READ_PERI_REG(GPIO_OUT_REG & (1 << io))) { + WRITE_PERI_REG(GPIO_OUT_W1TC_REG, 1 << io); + } else { + WRITE_PERI_REG(GPIO_OUT_W1TS_REG, 1 << io); + } +} diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/led.h b/src/mongoose-6.11/src/common/platforms/esp32/stubs/led.h new file mode 100644 index 0000000..505a3f6 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/stubs/led.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2014-2017 Cesanta Software Limited + * All rights reserved + */ + +#pragma once + +void led_setup(int io); +void led_on(int io); +void led_off(int io); +void led_toggle(int io); diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/rom_functions.h b/src/mongoose-6.11/src/common/platforms/esp32/stubs/rom_functions.h new file mode 100644 index 0000000..1b2eee4 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/stubs/rom_functions.h @@ -0,0 +1,10 @@ +#ifndef CS_COMMON_PLATFORMS_ESP32_STUBS_ROM_FUNCTIONS_H_ +#define CS_COMMON_PLATFORMS_ESP32_STUBS_ROM_FUNCTIONS_H_ + +#include "rom/ets_sys.h" +#include "rom/spi_flash.h" +#include "rom/md5_hash.h" +#include "rom/uart.h" +#include "rom/rtc.h" + +#endif /* CS_COMMON_PLATFORMS_ESP32_STUBS_ROM_FUNCTIONS_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/sdkconfig.h b/src/mongoose-6.11/src/common/platforms/esp32/stubs/sdkconfig.h new file mode 100644 index 0000000..0ecac13 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/stubs/sdkconfig.h @@ -0,0 +1 @@ +#define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1 diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/stub.ld b/src/mongoose-6.11/src/common/platforms/esp32/stubs/stub.ld new file mode 100644 index 0000000..6c2af65 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/stubs/stub.ld @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016 Cesanta Software Limited + * All rights reserved + */ + +MEMORY { + iram : org = 0x40090000, len = 0x10000 + /* DRAM startin at 0x3FFC0000 gets stomped by something before mem_finish + * and is thus not suitable for initialized data, but works fine for BSS. */ + dram_bss : org = 0x3FFC0000, len = 0x10000 + dram : org = 0x3FFD0000, len = 0x10000 +} + +ENTRY(stub_main) + +SECTIONS { + .params 0x40090000 : { + _params_start = ABSOLUTE(.); + *(.params) + _params_end = ABSOLUTE(.); + } > iram + + .text : ALIGN(4) { + _code_start = ABSOLUTE(.); + *(.literal) + *(.text .text.*) + } > iram + + .bss : ALIGN(4) { + _bss_start = ABSOLUTE(.); + *(.bss) + _bss_end = ABSOLUTE(.); + } > dram + + .data : ALIGN(4) { + _data_start = ABSOLUTE(.); + *(.data) + *(.rodata .rodata.*) + } > dram +} + +INCLUDE "components/esp32/ld/esp32.rom.ld" +INCLUDE "components/esp32/ld/esp32.rom.spiram_incompatible_fns.ld" + +PROVIDE(esp_rom_spiflash_attach = 0x40062a6c); +PROVIDE(esp_rom_spiflash_config_clk = 0x40062bc8); diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/stub_hello.c b/src/mongoose-6.11/src/common/platforms/esp32/stubs/stub_hello.c new file mode 100644 index 0000000..a4c75eb --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/stubs/stub_hello.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2015 Cesanta Software Limited + * All rights reserved + * + * + * Stub template. + */ + +#include +#include +#include "slip.h" + +/* Define the args vector and put it into the ".params" section. */ +uint32_t params[3] __attribute__((section(".params"))); + +/* Define a function called stub_main. Do not return or reboot. + * Use send_packet to communicate to the host. */ + +const char *hello = "Hello"; + +static char buf[1024]; +extern uint32_t _bss_start, _bss_end; + +void stub_main(void) { + uint32_t greeting = 0x4941484f; + SLIP_send(&greeting, 4); + memset(&_bss_start, 0, (&_bss_end - &_bss_start)); + buf[1] = 123; + SLIP_send(hello, 5); + while (1) { + } +} diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/uart.c b/src/mongoose-6.11/src/common/platforms/esp32/stubs/uart.c new file mode 100644 index 0000000..c6eb25d --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/stubs/uart.c @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#include "uart.h" + +#include "rom_functions.h" + +void set_baud_rate(uint32_t uart_no, uint32_t baud_rate) { + uint32_t master_freq = ets_get_detected_xtal_freq() << 4; + master_freq += (baud_rate / 2); + uint32_t div = master_freq / baud_rate; + uart_div_modify(uart_no, div); +} diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/uart.h b/src/mongoose-6.11/src/common/platforms/esp32/stubs/uart.h new file mode 100644 index 0000000..aa3c4ed --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp32/stubs/uart.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_ESP32_STUBS_UART_H_ +#define CS_COMMON_PLATFORMS_ESP32_STUBS_UART_H_ + +#include + +void set_baud_rate(uint32_t uart_no, uint32_t baud_rate); + +#endif /* CS_COMMON_PLATFORMS_ESP32_STUBS_UART_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/common.mk b/src/mongoose-6.11/src/common/platforms/esp8266/common.mk new file mode 100644 index 0000000..5345221 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/common.mk @@ -0,0 +1,76 @@ +CFLAGS_EXTRA ?= + +XTENSA_TOOLS_ROOT ?= /opt/Espressif/crosstool-NG/builds/xtensa-lx106-elf/bin +SDK_PATH ?= /opt/Espressif/ESP8266_SDK +ESPTOOL ?= esptool.py +ESPPORT ?= /dev/ttyACM0 +ESPSPEED ?= 230400 +# For flash = > 16Mbit +ESPFLASHARGS = --flash_mode dio --flash_size 32m + +VERBOSE ?= 0 + +CC := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc +CXX := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-g++ +AR := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-ar +LD := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc +OBJCOPY := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-objcopy +NM := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-nm +CC_WRAPPER ?= + +define link +$(vecho) "LD $@" +$(Q) $(CC_WRAPPER) $(LD) $(LIBDIRS) -T$(LD_SCRIPT) $(LDFLAGS) -o $@ \ +-Wl,-Map=$@.map -Wl,--start-group $1 -Wl,--end-group +endef + +define compile_params +$(vecho) "$5 $1 -> $2" +$(Q) $(CC_WRAPPER) $3 -MD -MP $(INCDIRS) $4 -c $1 -o $2 +endef + +define compile +$(call compile_params,$<,$@, $(CC), $(CFLAGS),"CC") +endef + +define compile_cxx +$(call compile_params,$<,$@, $(CXX), $(CXXFLAGS),"CXX") +endef + +# some of these flags works around for gdb 7.5.x stacktrace issue +# while still allowing -Os to remove padding between data in .rodata +# section, allowing us to gain about 1k of ram. +# text section is 4k bigger, but we care more about ram at the moment. +# TODO(mkm): figure out which flag(s). +NO_Os_FLAGS= -fno-expensive-optimizations -fno-thread-jumps \ + -fno-align-functions -fno-align-jumps \ + -fno-align-loops -fno-align-labels -fno-caller-saves \ + -fno-crossjumping -fno-cse-follow-jumps -fno-cse-skip-blocks \ + -fno-delete-null-pointer-checks -fno-devirtualize \ + -fno-gcse -fno-gcse-lm -fno-hoist-adjacent-loads \ + -fno-inline-small-functions -fno-indirect-inlining -fno-partial-inlining \ + -fno-ipa-cp -fno-ipa-sra -fno-peephole2 -fno-optimize-sibling-calls -fno-optimize-strlen \ + -fno-reorder-blocks -fno-reorder-blocks-and-partition -fno-reorder-functions \ + -fno-sched-interblock -fno-sched-spec -fno-rerun-cse-after-loop \ + -fno-schedule-insns -fno-schedule-insns2 -fno-strict-aliasing -fno-strict-overflow \ + -fno-tree-builtin-call-dce -fno-tree-switch-conversion -fno-tree-tail-merge \ + -fno-tree-pre -fno-tree-vrp + +C_CXX_FLAGS = -W -Wall -Werror -Wundef -Wno-comment -Wno-variadic-macros -Wpointer-arith \ + -Os $(NO_Os_FLAGS) -g3 \ + -Wl,-EL -fno-inline-functions \ + -D_XOPEN_SOURCE=500 \ + -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DSTATIC=static \ + -Wno-parentheses \ + -DIRAM='__attribute__((section(".fast.text")))' \ + -DICACHE_RAM_ATTR=IRAM \ + -DNOINSTR='__attribute__((no_instrument_function))' \ + -DCS_PLATFORM=3 \ + -ffunction-sections -fdata-sections + +CFLAGS = -std=c99 $(C_CXX_FLAGS) +CXXFLAGS = -std=gnu++11 -fno-exceptions $(C_CXX_FLAGS) + +# linker flags used to generate the main object file +LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start \ + -Wl,-static -Wl,--gc-sections diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/esp_crypto.c b/src/mongoose-6.11/src/common/platforms/esp8266/esp_crypto.c new file mode 100644 index 0000000..822dab1 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/esp_crypto.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#include +#include +#include + +#include "mongoose/mongoose.h" + +#ifdef RTOS_SDK +#include "esp_libc.h" +#else +#include "osapi.h" +#endif + +extern int sha1_vector(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest); + +extern int md5_vector(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest); + +/* For digest auth. */ +void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest) { + (void) md5_vector(num_msgs, msgs, msg_lens, digest); +} + +/* For WebSocket handshake. */ +void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest) { + (void) sha1_vector(num_msgs, msgs, msg_lens, digest); +} + +#if MG_ENABLE_SSL + +#include "mbedtls/aes.h" +#include "mbedtls/sha256.h" + +#define AES_PRIV_NR_POS (4 * 15) + +/* + * Crypto functions in ROM/SDK. + * They come from wpa_supplicant, you can find them here https://w1.fi/cgit/ + * + * Note that ROM version of the key setup function is older, does not take the + * number of bits argument and only supports AES-128. This prototype doesn't + * suit it, but since the difference is in the last aegument, it doesn't matter. + */ + +extern void rijndaelKeySetupDec(void *ctx, const uint8_t *key, int bits); +extern int rijndaelKeySetupEnc(void *ctx, const uint8_t *key, int bits); +void aes_encrypt(void *ctx, const uint8_t *plain, uint8_t *crypt); +void aes_decrypt(void *ctx, const uint8_t *crypt, uint8_t *plain); + +/* + * AES that comes with wpa_supplicant allocates its own AES context in + * aes_{encrypt,decrypt}_init. Ideally, we'd take that pointer and store it in + * our mbedtls_aes_context, but then a lot of space would be wasted. + * We do not call _init and go directly to key setup functions and poke number + * of rounds into the right place too. This is a bit hacky, but works fine. + * There is also a difference between older function in ROM and the one coming + * with SDK which is newer: the older one actually takes two arguments, not 3. + * But it doesn't matter, extra argument doesn't hurt and this works with both. + */ +int mbedtls_aes_setkey_enc(mbedtls_aes_context *ctx, const unsigned char *key, + unsigned int keybits) { + if (keybits != 128) return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH; + ((uint32_t *) ctx)[AES_PRIV_NR_POS] = 10; + rijndaelKeySetupEnc(ctx, key, 128); + return 0; +} + +int mbedtls_aes_setkey_dec(mbedtls_aes_context *ctx, const unsigned char *key, + unsigned int keybits) { + if (keybits != 128) return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH; + ((uint32_t *) ctx)[AES_PRIV_NR_POS] = 10; + rijndaelKeySetupDec(ctx, key, 128); + return 0; +} + +int mbedtls_internal_aes_encrypt(mbedtls_aes_context *ctx, + const unsigned char input[16], + unsigned char output[16]) { + aes_encrypt(ctx, input, output); + return 0; +} + +int mbedtls_internal_aes_decrypt(mbedtls_aes_context *ctx, + const unsigned char input[16], + unsigned char output[16]) { + aes_decrypt(ctx, input, output); + return 0; +} + +/* os_get_random uses hardware RNG, so it's cool. */ +int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len) { + os_get_random(buf, len); + (void) ctx; + return 0; +} + +/* For CryptoAuthLib host crypto. */ +int atcac_sw_sha2_256(const uint8_t *data, size_t data_size, + uint8_t digest[32]) { + mbedtls_sha256(data, data_size, digest, false /* is_224 */); + return 0; +} + +#endif /* MG_ENABLE_SSL */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/esp_hw_wdt_register.h b/src/mongoose-6.11/src/common/platforms/esp8266/esp_hw_wdt_register.h new file mode 100644 index 0000000..32c2f3c --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/esp_hw_wdt_register.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014-2017 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_ESP8266_ESP_HW_WDT_REGISTER_H_ +#define CS_COMMON_PLATFORMS_ESP8266_ESP_HW_WDT_REGISTER_H_ + +#ifdef RTOS_SDK +#include +#else +#include +#endif + +#define REG_WDT_BASE 0x60000900 + +#define WDT_CTL (REG_WDT_BASE + 0x0) +#define WDT_CTL_ENABLE (BIT(0)) +#define WDT_CTL_STAGE1_NO_RESET (BIT(1)) +#define WDT_CTL_STAGE1_DISABLE (BIT(2)) +#define WDT_CTL_UNK3 (BIT(3)) +#define WDT_CTL_UNK4 (BIT(4)) +#define WDT_CTL_UNK5 (BIT(5)) + +/* Bits 3, 4, 5 - ???; set to 1 by ROM. */ + +#define WDT_RELOAD_STAGE0 (REG_WDT_BASE + 0x4) +#define WDT_RELOAD_STAGE0_V (0xf) +#define WDT_RELOAD_STAGE0_S (0) + +#define WDT_RELOAD_STAGE1 (REG_WDT_BASE + 0x8) +#define WDT_RELOAD_STAGE1_V (0xf) +#define WDT_RELOAD_STAGE1_S (0) + +#define WDT_COUNT (REG_WDT_BASE + 0xc) /* Counts at CPU_CLK (80 MHz) */ +#define WDT_COUNT_V (0xffffffff) +#define WDT_COUNT_S (0) + +#define WDT_STAGE (REG_WDT_BASE + 0x10) +#define WDT_STAGE_V (1) +#define WDT_STAGE_S (0) + +#define WDT_RESET (REG_WDT_BASE + 0x14) +#define WDT_RESET_V (0xff) +#define WDT_RESET_S (0) + +#define WDT_RESET_STAGE (REG_WDT_BASE + 0x18) +#define WDT_RESET_STAGE_V (0xff) +#define WDT_RESET_STAGE_S (0) + +#define WDT_RESET_VALUE 0x73 + +#endif /* CS_COMMON_PLATFORMS_ESP8266_ESP_HW_WDT_REGISTER_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/esp_missing_includes.h b/src/mongoose-6.11/src/common/platforms/esp8266/esp_missing_includes.h new file mode 100644 index 0000000..fc7d69f --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/esp_missing_includes.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_ESP8266_ESP_MISSING_INCLUDES_H_ +#define CS_COMMON_PLATFORMS_ESP8266_ESP_MISSING_INCLUDES_H_ + +#include +#include + +void pp_soft_wdt_init(void); +void pp_soft_wdt_stop(void); +void pp_soft_wdt_feed(void); +void pp_soft_wdt_restart(void); +void system_soft_wdt_stop(void); /* Alias for pp_soft_wdt_stop */ + +void Cache_Read_Disable(void); +void Cache_Read_Enable(uint32_t, uint32_t, uint32_t); +void Cache_Read_Disable_2(void); +void Cache_Read_Enable_2(void); +void Cache_Read_Enable_New(void); + +int SPIEraseBlock(uint32_t block); +uint32_t SPIRead(uint32_t addr, void *dst, uint32_t size); + +#ifndef RTOS_SDK + +#include + +/* There are no declarations for these anywhere in the SDK (as of 1.2.0). */ +void ets_isr_mask(unsigned intr); +void ets_isr_unmask(unsigned intr); +void system_restart_local(void); +int os_printf_plus(const char *format, ...); + +void ets_wdt_init(void); +void ets_wdt_enable(uint32_t mode, uint32_t arg1, uint32_t arg2); +void ets_wdt_disable(void); +void ets_wdt_restore(uint32_t mode); +uint32_t ets_wdt_get_mode(void); + +void _xtos_l1int_handler(void); +void _xtos_set_exception_handler(); +void xthal_set_intenable(unsigned); + +/* These are present in mem.h but are commented out. */ +void *pvPortMalloc(size_t xWantedSize, const char *file, int line); +void vPortFree(void *pv, const char *file, int line); +void *pvPortZalloc(size_t size, const char *file, int line); +void *pvPortRealloc(void *pv, size_t size, const char *file, int line); + +#else /* !RTOS_SDK */ + +#define BIT(nr) (1UL << (nr)) +void system_soft_wdt_feed(void); +void system_soft_wdt_restart(void); +void ets_putc(char c); + +#endif /* RTOS_SDK */ + +void _ResetVector(void); + +#endif /* CS_COMMON_PLATFORMS_ESP8266_ESP_MISSING_INCLUDES_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/esp_ssl_krypton.h b/src/mongoose-6.11/src/common/platforms/esp8266/esp_ssl_krypton.h new file mode 100644 index 0000000..086c6b5 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/esp_ssl_krypton.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_ESP8266_ESP_SSL_KRYPTON_H_ +#define CS_COMMON_PLATFORMS_ESP8266_ESP_SSL_KRYPTON_H_ + +#include "krypton/krypton.h" + +struct mg_connection; + +void mg_lwip_ssl_do_hs(struct mg_connection *nc); +void mg_lwip_ssl_send(struct mg_connection *nc); +void mg_lwip_ssl_recv(struct mg_connection *nc); + +#endif /* CS_COMMON_PLATFORMS_ESP8266_ESP_SSL_KRYPTON_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/esp_umm_malloc.c b/src/mongoose-6.11/src/common/platforms/esp8266/esp_umm_malloc.c new file mode 100644 index 0000000..3dd4a0e --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/esp_umm_malloc.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#include +#include +#include "common/umm_malloc/umm_malloc.h" +#include "esp_umm_malloc.h" + +#if ESP_UMM_ENABLE + +/* + * ESP-specific glue for the `umm_malloc`. + * + * In SDK (https://github.com/cesanta/esp-open-sdk), there is an archive + * `sdk/lib/libmain.a` which contains some files, including `mem_manager.o`. + * + * The `mem_manager.o` contains all the heap-related functions: `pvPortMalloc`, + * etc. We have weaken all symbols from `mem_manager.o` by + * `xtensa-lx106-elf-objcopy` (see exact commands in Dockerfile: + * `docker/esp8266/Dockerfile-esp8266-build-oss`), and provide our own + * implementations in this file. + * + * ------------------------------------ + * + * NOTE that not all public functions from `mem_manager.o` need to be replaced: + * some of them are used only internally: + * + * - system_show_malloc() + * - pvShowMalloc() + * - prvInsertBlockIntoUsedList() + * - prvRemoveBlockFromUsedList() + * - check_memleak_debug_enable() + * - vPortInitialiseBlocks() + * + * So when we replace all the rest (`pvPortMalloc`, etc), we can check with + * `objdump` that resulting binary (for SJ, it's `fw.out`) doesn't contain + * any of the "internal" functions. + * + * ------------------------------------ + * + * NOTE that to make linker actually consider implementations in this file, + * you should explicitly reference some function from it. This is what + * `esp_umm_init()` is for: it is a dummy no-op function that must be called + * from somewhere outside. + * + * If you don't do this, linker will merely garbage-collect this file, and + * will use heap implementation from SDK. + */ + +void *pvPortMalloc(size_t size, const char *file, unsigned line) { + (void) file; + (void) line; + + return umm_malloc(size); +} + +void *pvPortCalloc(size_t num, size_t size, const char *file, unsigned line) { + (void) file; + (void) line; + + return umm_calloc(num, size); +} + +void *pvPortZalloc(size_t size, const char *file, unsigned line) { + void *ret; + + (void) file; + (void) line; + + ret = umm_malloc(size); + if (ret != NULL) memset(ret, 0, size); + return ret; +} + +void *pvPortRealloc(void *ptr, size_t size, const char *file, unsigned line) { + (void) file; + (void) line; + + return umm_realloc(ptr, size); +} + +void vPortFree(void *ptr, const char *file, unsigned line) { + (void) file; + (void) line; + + umm_free(ptr); +} + +size_t xPortGetFreeHeapSize(void) { + return umm_free_heap_size(); +} + +size_t xPortWantedSizeAlign(void) { + return 4; +} + +void esp_umm_init(void) { + /* Nothing to do, see header for details */ +} + +void esp_umm_oom_cb(size_t size, size_t blocks_cnt) { + fprintf(stderr, "E:M %u (%u blocks)\n", (unsigned int) size, + (unsigned int) blocks_cnt); +} + +#endif /* ESP_UMM_ENABLE */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/esp_umm_malloc.h b/src/mongoose-6.11/src/common/platforms/esp8266/esp_umm_malloc.h new file mode 100644 index 0000000..efb6f4b --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/esp_umm_malloc.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if ESP_UMM_ENABLE + +/* + * This is a no-op dummy function that is merely needed because we have to + * reference any function from the file `esp_umm_malloc.c`, so that linker + * won't garbage-collect the whole compilation unit. + */ +void esp_umm_init(void); + +/* + * Callback that gets called by umm_malloc in case of Out-of-memory error. + * + * `size` is the size requested by user, and `block_cnt` is a number of heap + * blocks that umm_malloc failed to allocate + */ +void esp_umm_oom_cb(size_t size, size_t blocks_cnt); + +#endif /* CS_COMMON_PLATFORMS_ESP8266_ESP_UMM_MALLOC_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/Makefile b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/Makefile new file mode 100644 index 0000000..4cccf2a --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/Makefile @@ -0,0 +1,26 @@ +# +# Makefile for esptool2 +# https://github.com/raburton/esp8266 +# + +CFLAGS = -O2 -Wall +CC = gcc +LD = gcc +BUILD_DIR = ../../build + +all: $(BUILD_DIR) $(BUILD_DIR)/esptool2 + +$(BUILD_DIR): + @mkdir -p $(BUILD_DIR) + +$(BUILD_DIR)/esptool2.o: esptool2.c esptool2.h esptool2_elf.h elf.h + @echo "CC $<" + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/esptool2_elf.o: esptool2_elf.c esptool2.h esptool2_elf.h elf.h + @echo "CC $<" + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/esptool2: $(BUILD_DIR)/esptool2.o $(BUILD_DIR)/esptool2_elf.o + @echo "LD $@" + $(LD) -o $@ $^ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/elf.h b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/elf.h new file mode 100644 index 0000000..a699718 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/elf.h @@ -0,0 +1,76 @@ +// Based on a small portion of ELF.h from LLVM + +//===-- llvm/Support/ELF.h - ELF constants and data structures --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. +// +//===----------------------------------------------------------------------===// +// +// This header contains common, non-processor-specific data structures and +// constants for the ELF file format. +// +// The details of the ELF32 bits in this file are largely based on the Tool +// Interface Standard (TIS) Executable and Linking Format (ELF) Specification +// Version 1.2, May 1995. The ELF64 stuff is based on ELF-64 Object File Format +// Version 1.5, Draft 2, May 1998 as well as OpenBSD header files. +// +//===----------------------------------------------------------------------===// + +#ifndef CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ELF_H_ +#define CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ELF_H_ + +typedef uint32_t Elf32_Addr; // Program address +typedef uint32_t Elf32_Off; // File offset +typedef uint16_t Elf32_Half; +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; + +// e_ident size and indices +enum { + EI_MAG0 = 0, // File identification index. + EI_MAG1 = 1, // File identification index. + EI_MAG2 = 2, // File identification index. + EI_MAG3 = 3, // File identification index. + EI_CLASS = 4, // File class. + EI_DATA = 5, // Data encoding. + EI_VERSION = 6, // File version. + EI_OSABI = 7, // OS/ABI identification. + EI_ABIVERSION = 8, // ABI version. + EI_PAD = 9, // Start of padding bytes. + EI_NIDENT = 16 // Number of bytes in e_ident. +}; + +typedef struct { + unsigned char e_ident[EI_NIDENT]; // ELF Identification bytes + Elf32_Half e_type; // Type of file (see ET_* below) + Elf32_Half e_machine; // Required architecture for this file (see EM_*) + Elf32_Word e_version; // Must be equal to 1 + Elf32_Addr e_entry; // Address to jump to in order to start program + Elf32_Off e_phoff; // Program header table's file offset, in bytes + Elf32_Off e_shoff; // Section header table's file offset, in bytes + Elf32_Word e_flags; // Processor-specific flags + Elf32_Half e_ehsize; // Size of ELF header, in bytes + Elf32_Half e_phentsize; // Size of an entry in the program header table + Elf32_Half e_phnum; // Number of entries in the program header table + Elf32_Half e_shentsize; // Size of an entry in the section header table + Elf32_Half e_shnum; // Number of entries in the section header table + Elf32_Half e_shstrndx; // Sect hdr table index of sect name string table +} Elf32_Ehdr; + +typedef struct { + Elf32_Word sh_name; // Section name (index into string table) + Elf32_Word sh_type; // Section type (SHT_*) + Elf32_Word sh_flags; // Section flags (SHF_*) + Elf32_Addr sh_addr; // Address where section is to be loaded + Elf32_Off sh_offset; // File offset of section data, in bytes + Elf32_Word sh_size; // Size of section, in bytes + Elf32_Word sh_link; // Section type-specific header table index link + Elf32_Word sh_info; // Section type-specific extra information + Elf32_Word sh_addralign; // Section address alignment + Elf32_Word sh_entsize; // Size of records contained within the section +} Elf32_Shdr; + +#endif /* CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ELF_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2.c b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2.c new file mode 100644 index 0000000..aa9b457 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2.c @@ -0,0 +1,561 @@ +/********************************************************************************** +* +* Copyright (c) 2015 Richard A Burton +* +* This file is part of esptool2. +* +* esptool2 is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* esptool2 is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with esptool2. If not, see . +* +**********************************************************************************/ +/* clang-format off */ + +#include +#include +#include + +#include "esptool2.h" +#include "esptool2_elf.h" + +#define IMAGE_PADDING 16 +#define SECTION_PADDING 4 +#define IROM_SECTION_PADDING 4096 +#define CHECKSUM_INIT 0xEF +#define BIN_MAGIC_IROM 0xEA +#define BIN_MAGIC_FLASH 0xE9 + +typedef struct { + Elf32_Addr addr; + Elf32_Word size; +} Section_Header; + +typedef struct { + unsigned char magic; + unsigned char count; + unsigned char byte2; + unsigned char byte3; + Elf32_Addr entry; +} Image_Header; + +static const char PADDING[IROM_SECTION_PADDING] = {0}; + +static bool debugon = false; +static bool quieton = false; + +// Print a standard info message (unless quiet mode) +void print(const char* format, ...) { + va_list args; + + if (!quieton) { + va_start(args, format); + vprintf(format, args); + va_end(args); + } +} + +// Print a debug message (if debug mode) +void debug(const char* format, ...) { + va_list args; + + if (debugon) { + va_start(args, format); + vprintf(format, args); + va_end(args); + } +} + +// Print an error message (always) +void error(const char* format, ...) { + va_list args; + + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} + +// Write an elf section (by name) to an existing file. +// Parameters: +// headed - add a header to the output +// zeroaddr - force zero entry point in header (default is the real entry point) +// padded - output will be padded to multiple of SECTION_PADDING bytes +// chksum - pointer to existing checksum to add this data to (zero if not needed) +// Produces error message on failure (so caller doesn't need to). +bool WriteElfSection(MyElf_File *elf, FILE *outfile, char* name, bool headed, + bool zeroaddr, int padto, unsigned char *chksum) { + + int i, pad = 0; + bool ret = false; + unsigned char *bindata = 0; + Section_Header sechead; + MyElf_Section *sect; + + // get elf section header + sect = GetElfSection(elf, name); + if(!sect) { + error("Error: Section '%s' not found in elf file.\r\n", name); + goto end_function; + } + + // create image section header + sechead.addr = (zeroaddr ? 0 : sect->address); + sechead.size = sect->size; + + // do we need to pad the section? + if (padto) { + pad = sechead.size % padto; + if (pad > 0) { + pad = padto - pad; + sechead.size += pad; + } + } + + debug("Adding section '%s', addr: 0x%08x, size: %d (+%d bytes(s) padding).\r\n", + name, sect->address, sect->size, pad); + + // get elf section binary data + bindata = GetElfSectionData(elf, sect); + if (!bindata) { + goto end_function; + } + + // write section (and pad if required) + if((headed && fwrite(&sechead, 1, sizeof(sechead), outfile) != sizeof(sechead)) + || fwrite(bindata, 1, sect->size, outfile) != sect->size + || (pad > 0 && fwrite(PADDING, 1, pad, outfile) != pad)) { + error("Error: Failed to write section '%s' to image file.\r\n", name); + goto end_function; + } + + // include section data in the checksum + if(chksum) { + for(i = 0; i < (int)sect->size; i++) { + *chksum ^= bindata[i]; + } + } + + ret = true; + +end_function: + if (bindata) free(bindata); + return ret; +} + +// Load an elf file and export a section of it to a new file, without +// header, padding or checksum. For exporting the .irom0.text library. +// Produces error message on failure (so caller doesn't need to). +bool ExportElfSection(char *infile, char *outfile, char *name) { + + bool ret = false; + FILE *fd = 0; + MyElf_File *elf = 0; + + // load elf file + elf = LoadElf(infile); + if (!elf) { + goto end_function; + } + + // open output file + fd = fopen(outfile, "wb"); + if(!fd) { + error("Error: Can't open output file '%s' for writing.\r\n", outfile); + goto end_function; + } + + // actually do the export + ret = WriteElfSection(elf, fd, name, false, false, false, 0); + +end_function: + // clean up + if (fd) fclose(fd); + UnloadElf(elf); + + return ret; +} + +// Create the main binary firmware image, from specified elf sections. +// Can produce for standard standalone app (separate .irom0.text) +// or sdk bootloaded apps (integrated .irom0.text). +// Choice of type requires appropriately linked elf file. +// Produces error message on failure (so caller doesn't need to). +bool CreateHeaderFile(char *elffile, char *imagefile, char *sections[], int numsec) { + + bool ret = false; + int i; + unsigned int j, len; + FILE *outfile = 0; + MyElf_File *elf = 0; + MyElf_Section *sect; + unsigned char *bindata = 0; + char name[31]; + + // load elf file + elf = LoadElf(elffile); + if (!elf) { + goto end_function; + } + + // open output file + outfile = fopen(imagefile, "wb"); + if(outfile == NULL) { + error("Error: Failed to open output file '%s' for writing.\r\n", imagefile); + goto end_function; + } + + // add entry point + fprintf(outfile, "const uint32 entry_addr = 0x%08x;\r\n", elf->header.e_entry); + + // add sections + for (i = 0; i < numsec; i++) { + // get elf section header + sect = GetElfSection(elf, sections[i]); + if(!sect) { + error("Error: Section '%s' not found in elf file.\r\n", sections[i]); + goto end_function; + } + + // simple name fix name + strncpy(name, sect->name, 31); + len = strlen(name); + for (j = 0; j < len; j++) { + if (name[j] == '.') name[j] = '_'; + } + + // add address, length and start the data block + debug("Adding section '%s', addr: 0x%08x, size: %d.\r\n", sections[i], sect->address, sect->size); + fprintf(outfile, "\r\nconst uint32 %s_addr = 0x%08x;\r\nconst uint32 %s_len = %d;\r\nconst uint8 %s_data[] = {", + name, sect->address, name, sect->size, name); + + // get elf section binary data + bindata = GetElfSectionData(elf, sect); + if (!bindata) { + goto end_function; + } + + // add the data and finish off the block + for (j = 0; j < sect->size; j++) { + if (j % 16 == 0) fprintf(outfile, "\r\n 0x%02x,", bindata[j]); + else fprintf(outfile, " 0x%02x,", bindata[j]); + } + fprintf(outfile, "\r\n};\r\n"); + free(bindata); + bindata = 0; + } + + // if we got this far everything worked! + ret = true; + +end_function: + // clean up + if (outfile) fclose(outfile); + if (elf) UnloadElf(elf); + if (bindata) free(bindata); + + return ret; +} + +// Create the main binary firmware image, from specified elf sections. +// Can produce for standard standalone app (separate .irom0.text) +// or sdk bootloaded apps (integrated .irom0.text). +// Choice of type requires appropriately linked elf file. +// Produces error message on failure (so caller doesn't need to). +bool CreateBinFile(char *elffile, char *imagefile, int bootver, unsigned char mode, + unsigned char clock, unsigned char size, bool iromchksum, char *sections[], int numsec) { + + bool ret = false; + int i, pad, len; + unsigned char chksum = CHECKSUM_INIT; + unsigned char *data = 0; + FILE *outfile = 0; + MyElf_File *elf = 0; + Image_Header imghead; + + // load elf file + elf = LoadElf(elffile); + if (!elf) { + goto end_function; + } + + // open output file + outfile = fopen(imagefile, "wb"); + if(outfile == NULL) { + error("Error: Failed to open output file '%s' for writing.\r\n", imagefile); + goto end_function; + } + + // set options common to standard and boot v1.2+ headers + imghead.byte2 = mode; + //imghead.byte3 = (int)((int)size << 4 | clock) && 0xff; + imghead.byte3 = ((size << 4) | clock) & 0xff; + imghead.entry = elf->header.e_entry; + debug("Size = %02x\r\n", size); + debug("Byte2 = %02x\r\n", imghead.byte2); + debug("Byte3 = %02x\r\n", imghead.byte3); + debug("Entry = %08x\r\n", imghead.entry); + + // boot v1.2+ header + if (bootver == 2) { + // extra header + imghead.magic = BIN_MAGIC_IROM; + imghead.count = 4; // probably a version number here, not a count + if(fwrite(&imghead, 1, sizeof(imghead), outfile) != sizeof(imghead)) { + error("Error: Failed to write header to image file.\r\n"); + goto end_function; + } + if(!WriteElfSection(elf, outfile, ".irom0.text", true, true, IROM_SECTION_PADDING, (iromchksum ? &chksum : 0))) { + goto end_function; + } + } + + // standard header + imghead.magic = BIN_MAGIC_FLASH; + imghead.count = numsec; + // write header + if(fwrite(&imghead, 1, sizeof(imghead), outfile) != sizeof(imghead)) { + error("Error: Failed to write header to image file.\r\n"); + goto end_function; + } + + // add sections + for (i = 0; i < numsec; i++) { + if(!WriteElfSection(elf, outfile, sections[i], true, false, SECTION_PADDING, &chksum)) { + goto end_function; + } + } + + // get image length (plus a byte for the checksum) + len = ftell(outfile) + 1; + + // do we need to pad the image? + pad = len % IMAGE_PADDING; + if (pad > 0) { + pad = IMAGE_PADDING - pad; + debug("Padding image with %d byte(s).\r\n", pad); + if(fwrite(PADDING, 1, pad, outfile) != pad) { + error("Error: Failed to write padding to image file.\r\n"); + goto end_function; + } + } + + // write checksum + if(fwrite(&chksum, 1, 1, outfile) != 1) { + error("Error: Failed to write checksum to image file.\r\n"); + goto end_function; + } + + // boot v1.1 + if(bootver == 1) { + // write 'ff' padding up to the position of the library + len = 0x10000 - ftell(outfile); + debug("Adding boot v1.1 padding, %d bytes of '0xff'.\r\n", len); + data = (unsigned char*)malloc(len); + memset(data, 0xff, len); + if(fwrite(data, 1, len, outfile) != len) { + error("Error: Failed to write boot v1.1 spacer.\r\n"); + goto end_function; + } + + // write the library + if(!WriteElfSection(elf, outfile, ".irom0.text", false, false, 0, 0)) { + goto end_function; + } + } + + // if we got this far everything worked! + ret = true; + +end_function: + // clean up + if (outfile) fclose(outfile); + if (data) free(data); + if (elf) UnloadElf(elf); + + return ret; +} + +int main(int argc, char *argv[]) { + + int i; + char *infile; + char *outfile; + int numstr; + bool binfile = false; + bool libfile = false; + bool headerfile = false; + bool paramerror = false; + bool iromchksum = false; + int bootver = 0; + /* Overwrite-friendly by default */ + unsigned char mode = 0xff; + unsigned char size = 0xff; + unsigned char clock = 0; + int opts = 0; + + // parse options + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-bin")) { + binfile = true; + opts++; + } else if (!strcmp(argv[i], "-lib")) { + libfile = true; + opts++; + } else if (!strcmp(argv[i], "-header")) { + headerfile = true; + opts++; + } else if (!strcmp(argv[i], "-quiet")) { + quieton = true; + } else if (!strcmp(argv[i], "-debug")) { + debugon = true; + } else if (!strcmp(argv[i], "-boot0")) { + bootver = 0; + } else if (!strcmp(argv[i], "-boot1")) { + bootver = 1; + } else if (!strcmp(argv[i], "-boot2")) { + bootver = 2; + } else if (!strcmp(argv[i], "-qio")) { + mode = 0; + } else if (!strcmp(argv[i], "-qout")) { + mode = 1; + } else if (!strcmp(argv[i], "-dio")) { + mode = 2; + } else if (!strcmp(argv[i], "-dout")) { + mode = 3; + } else if (!strcmp(argv[i], "-256")) { + size = 1; + } else if (!strcmp(argv[i], "-512")) { + size = 0; + } else if (!strcmp(argv[i], "-1024")) { + size = 2; + } else if (!strcmp(argv[i], "-2048")) { + size = 3; + } else if (!strcmp(argv[i], "-4096")) { + size = 4; + } else if (!strcmp(argv[i], "-20")) { + clock = 2; + } else if (!strcmp(argv[i], "-26.7")) { + clock = 1; + } else if (!strcmp(argv[i], "-40")) { + clock = 0; + } else if (!strcmp(argv[i], "-80")) { + clock = 15; + } else if (!strcmp(argv[i], "-iromchksum")) { + iromchksum = true; + } else if (!strcmp(argv[i], "--")) { + i++; + break; + } else if (argv[i][0] == '-') { + paramerror = true; + break; + } else { + break; + } + } + + print("esptool2 v2.0.0 - (c) 2015 Richard A Burton \r\n"); + print("This program is licensed under the GPL v3.\r\n"); + print("See the file LICENSE for details.\r\n\r\n"); + + if (paramerror) { + error("Error: Unrecognised option '%s'.\r\n", argv[i]); + return -1; + } + + if (argc < 2) { + print("Usage:\r\n"); + print("esptool2 -lib [options] \r\n"); + print("esptool2 -bin [options] ...\r\n"); + print("esptool2 -header [options] ...\r\n"); + print("\r\n"); + print(" -lib\r\n"); + print(" Export the sdk library (.irom0.text), for a standalone app.\r\n"); + print(" e.g. esptool2 -elf esp8266_iot.out out.bin\r\n"); + print("\r\n"); + print(" -header\r\n"); + print(" Export elf sections as bytes to a C header file.\r\n"); + print(" e.g. esptool2 -elf esp8266_iot.out out.h .text .data .rodata\r\n"); + print("\r\n"); + print(" -bin\r\n"); + print(" Create binary program image, for standalone and bootloaded apps, with\r\n"); + print(" specified elf sections. Includes sdk library for bootloaded apps.\r\n"); + print(" e.g. esptool2 -bin esp8266_iot.out out.bin .text .data .rodata\r\n"); + print(" Options:\r\n"); + print(" bootloader: -boot0 -boot1 -boot2 (default -boot0)\r\n"); + print(" -boot0 = standalone app, not built for bootloader use\r\n"); + print(" -boot1 = built for bootloader v1.1\r\n"); + print(" -boot2 = built for bootloader v1.2+ (use for rBoot roms)\r\n"); + print(" (elf file must have been linked appropriately for chosen option)\r\n"); + print(" spi size (kb): -256 -512 -1024 -2048 -4096 (default -512)\r\n"); + print(" spi mode: -qio -qout -dio -dout (default -qio)\r\n"); + print(" spi speed: -20 -26.7 -40 -80 (default -40)\r\n"); + print(" include irom in checksum: -iromchksum (also needs enabling in rBoot)\r\n"); + print("\r\n"); + print("General options:\r\n"); + print(" -quiet prints only error messages\r\n"); + print(" -debug print extra debug information\r\n"); + print(" -- no more options follow (needed if your elf file starts with a '-')\r\n"); + print("\r\n"); + print("Returns:\r\n"); + print(" 0 on success\r\n"); + print(" -1 on failure\r\n"); + print("\r\n"); + return -1; + } + + // validate command line options + if (opts != 1) { + error("Error: You must specify -bin OR -lib OR -header for build type.\r\n"); + return -1; + } + + if (quieton && debugon) { + error("Error: You cannot specify -quiet and -debug.\r\n"); + return -1; + } + + // check enough parameters + if ((libfile && i + 2 > argc) || ((binfile | headerfile) && i + 3 > argc)) { + error("Error: Not enough arguments supplied.\r\n"); + return -1; + } else if (libfile && i + 2 < argc) { + error("Error: Too many arguments supplied.\r\n"); + return -1; + } + + // get parameters + infile = argv[i++]; + outfile = argv[i++]; + numstr = argc - i; + + // do it + if (binfile) { + if (!CreateBinFile(infile, outfile, bootver, mode, clock, size, iromchksum, &argv[i], numstr)) { + remove(outfile); + return -1; + } + } else if (headerfile) { + if (!CreateHeaderFile(infile, outfile, &argv[i], numstr)) { + remove(outfile); + return -1; + } + } else { + if (!ExportElfSection(infile, outfile, ".irom0.text")) { + remove(outfile); + return -1; + } + + } + + print("Successfully created '%s'.\r\n", outfile); + return 0; + +} diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2.h b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2.h new file mode 100644 index 0000000..b0eba08 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2.h @@ -0,0 +1,44 @@ +/********************************************************************************** +* +* Copyright (c) 2015 Richard A Burton +* +* This file is part of esptool2. +* +* esptool2 is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* esptool2 is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with esptool2. If not, see . +* +**********************************************************************************/ + +#ifndef CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ESPTOOL2_H_ +#define CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ESPTOOL2_H_ + +#ifdef WIN32 +typedef signed __int8 int8_t; +typedef signed __int16 int16_t; +typedef signed __int32 int32_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +#else +#include +#endif + +#define true 1 +#define false 0 +#define bool char + +void debug( const char* format, ... ); +void print( const char* format, ... ); +void error( const char* format, ... ); + +#endif /* CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ESPTOOL2_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2_elf.c b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2_elf.c new file mode 100644 index 0000000..88ede4e --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2_elf.c @@ -0,0 +1,183 @@ +/********************************************************************************** +* +* Copyright (c) 2015 Richard A Burton +* +* This file is part of esptool2. +* +* esptool2 is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* esptool2 is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with esptool2. If not, see . +* +**********************************************************************************/ + +#include +#include + +#include "esptool2.h" +#include "esptool2_elf.h" + +// Find a section in an elf file by name. +// Returns pointer to section if found, else returns zero. +// Does not produce any messages. +MyElf_Section* GetElfSection(MyElf_File *elf, char *name) { + + int i; + + for(i = 0; i < elf->header.e_shnum - 1; i++) { + if(!strcmp(name, elf->sections[i].name)) { + debug("Found section '%s'.\r\n", name); + return &elf->sections[i]; + } + } + + debug("Could not find section '%s'.\r\n", name); + return 0; +} + +// Reads an elf section (actual data) from the elf file. +// Returns a pointer to newly allocated memory (or zero on error), +// which should be freed by the caller when finished with. +// Produces error message on failure (so caller doesn't need to). +unsigned char* GetElfSectionData(MyElf_File *elf, MyElf_Section *section) { + + unsigned char *data = 0; + + if (section->size && section->offset) { + + data = (unsigned char*)malloc(section->size); + if(!data) { + error("Error: Out of memory!\r\n"); + return 0; + } + + if(fseek(elf->fd, section->offset, SEEK_SET) || + fread(data, 1, section->size, elf->fd) != section->size) { + error("Error: Can't read section '%s' data from elf file.\r\n", section->name); + free(data); + return 0; + } + + } else { + error("Error: Section '%s' has no data to read.\r\n", section->name); + } + + return data; +} + +// Opens an elf file and reads the string table and file & section headers. +// Returns a pointer to a MyElf_File structure (or zero on error). +// UnloadElf should be called to dispose of the MyElf_File structure. +// Produces error message on failure (so caller doesn't need to). +MyElf_File* LoadElf(char *infile) { + + int i; + MyElf_File *elf; + Elf32_Shdr temp; + + // allocate the elf structure + elf = (MyElf_File*)malloc(sizeof(MyElf_File)); + if(!elf) { + error("Error: Out of memory!\r\n"); + goto error_exit; + } + memset(elf, 0, sizeof(MyElf_File)); + + // open the file + elf->fd = fopen(infile, "rb"); + if(!elf->fd) { + error("Error: Can't open elf file '%s'.\r\n", infile); + goto error_exit; + } + + // read the header + if(fread(&elf->header, 1, sizeof(Elf32_Ehdr), elf->fd) != sizeof(Elf32_Ehdr)) { + error("Error: Can't read elf file header.\r\n"); + goto error_exit; + } + + // check the file header + if (memcmp(elf->header.e_ident, "\x7f" "ELF", 4)) { + error("Error: Input files doesn't look like an elf file (bad header).\r\n"); + goto error_exit; + } + + // is there a string table section (we need one) + if(!elf->header.e_shstrndx) { + error("Error: Elf file does not contain a string table.\r\n"); + goto error_exit; + } + + // get the string table section header + if(fseek(elf->fd, elf->header.e_shoff + (elf->header.e_shentsize * elf->header.e_shstrndx), SEEK_SET) || + fread(&temp, 1, sizeof(Elf32_Shdr), elf->fd) != sizeof(Elf32_Shdr)) { + + error("Error: Can't read string table section from elf file.\r\n"); + goto error_exit; + } + + // read the actual string table + if(!temp.sh_size) { + error("Error: Elf file contains an empty string table.\r\n"); + goto error_exit; + } + elf->strings = (char*)malloc(temp.sh_size); + if(!elf->strings) { + error("Error: Out of memory!\r\n"); + goto error_exit; + } + if(fseek(elf->fd, temp.sh_offset, SEEK_SET) || + fread(elf->strings, 1, temp.sh_size, elf->fd) != temp.sh_size) { + error("Error: Failed to read string stable from elf file.\r\n"); + goto error_exit; + } + + // read section headers + elf->sections = (MyElf_Section*)malloc(sizeof(MyElf_Section) * elf->header.e_shnum); + if(!elf->sections) { + error("Error: Out of memory!\r\n"); + goto error_exit; + } + for(i = 1; i < elf->header.e_shnum; i++) { + if(fseek(elf->fd, elf->header.e_shoff + (elf->header.e_shentsize * i), SEEK_SET) + || fread(&temp, 1, sizeof(Elf32_Shdr), elf->fd) != sizeof(Elf32_Shdr)) { + error("Error: Can't read section %d from elf file.\r\n", i); + break; + } + debug("Read section %d '%s'.\r\n", i, elf->strings + temp.sh_name); + elf->sections[i-1].address = temp.sh_addr; + elf->sections[i-1].offset = temp.sh_offset; + elf->sections[i-1].size = temp.sh_size; + elf->sections[i-1].name = elf->strings + temp.sh_name; + } + + return elf; + +error_exit: + if (elf) { + if (elf->fd) fclose(elf->fd); + if (elf->strings) free(elf->strings); + free(elf); + } + return 0; + +} + +// Close an elf file and dispose of the MyElf_File structure. +void UnloadElf(MyElf_File *elf) { + if (elf) { + debug("Unloading elf file.\r\n"); + if(elf->fd) fclose(elf->fd); + if(elf->strings) free(elf->strings); + if(elf->sections) free(elf->sections); + free(elf); + } +} diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2_elf.h b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2_elf.h new file mode 100644 index 0000000..ef98511 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2_elf.h @@ -0,0 +1,48 @@ +/********************************************************************************** +* +* Copyright (c) 2015 Richard A Burton +* +* This file is part of esptool2. +* +* esptool2 is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* esptool2 is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with esptool2. If not, see . +* +**********************************************************************************/ + +#ifndef CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ESPTOOL2_ELF_H_ +#define CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ESPTOOL2_ELF_H_ + +#include + +#include "elf.h" + +typedef struct { + Elf32_Off offset; + Elf32_Addr address; + Elf32_Word size; + char *name; +} MyElf_Section; + +typedef struct { + FILE *fd; + Elf32_Ehdr header; + char *strings; + MyElf_Section *sections; +} MyElf_File; + +MyElf_File* LoadElf(char *infile); +void UnloadElf(MyElf_File *e_object); +MyElf_Section* GetElfSection(MyElf_File *e_object, char *name); +unsigned char* GetElfSectionData(MyElf_File *e_object, MyElf_Section *section); + +#endif /* CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ESPTOOL2_ELF_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/license.txt b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/license.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/license.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/readme.txt b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/readme.txt new file mode 100644 index 0000000..eb9f0ef --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/readme.txt @@ -0,0 +1,19 @@ +Esptool2 +richardaburton@gmail.com +http://richard.burtons.org/ + +Esptool2 is a tool for creating rom images for the ESP8266. It is an alternative +to using the SDK supplied shell script/Makefile/python script combo, which is a +mess. It was inspired by the windows esptool v0.0.2 by mamalala and found on +www.esp8266.com but made somewhat simpler in code and usage. It also adds +support for boot loader v1.2+ rom types, which was the main reason I wrote it. + +It was written for my own use and the name was simply to distinguish it for the +other version on my system. The 2 is not intended to imply it is better than the +original. The original has since been updated to v0.0.3 which can write to the +flash, but I currently have no intention to add that to esptool2, it is purely a +rom creating utility. It has become an integral part of my build process now and +has added functionality needed for building the rBoot boot loader. Since I have +released rBoot I needed to release this as well. + +Run tool for full usage instructions, or look at the code. diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-api.c b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-api.c new file mode 100644 index 0000000..2d2e83d --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-api.c @@ -0,0 +1,152 @@ +////////////////////////////////////////////////// +// rBoot OTA and config API for ESP8266. +// Copyright 2015 Richard A Burton +// richardaburton@gmail.com +// See license.txt for license terms. +// OTA code based on SDK sample from Espressif. +////////////////////////////////////////////////// + +#ifdef RBOOT_INTEGRATION +#include +#endif + +#include +#include +#include + +#ifdef RTOS_SDK +#include "spi_flash.h" +#else +#include +#endif + +#include "rboot-api.h" +#include "../rboot-private.h" +#include "esp_missing_includes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// get the rboot config +rboot_config ICACHE_FLASH_ATTR rboot_get_config(void) { + rboot_config conf; + spi_flash_read(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32*)&conf, sizeof(rboot_config)); + return conf; +} + +// write the rboot config +// preserves the contents of the rest of the sector, +// so the rest of the sector can be used to store user data +// updates checksum automatically (if enabled) +bool ICACHE_FLASH_ATTR rboot_set_config(rboot_config *conf) { + uint32_t buffer[sizeof(*conf) / sizeof(uint32_t) + 1]; +#ifdef BOOT_CONFIG_CHKSUM + uint8 chksum; + uint8 *ptr; +#endif + +#ifdef BOOT_CONFIG_CHKSUM + chksum = CHKSUM_INIT; + for (ptr = (uint8*)conf; ptr < &conf->chksum; ptr++) { + chksum ^= *ptr; + } + conf->chksum = chksum; +#endif + + memset(buffer, 0xff, sizeof(buffer)); + memcpy(buffer, conf, sizeof(*conf)); + if (spi_flash_erase_sector(BOOT_CONFIG_SECTOR) != SPI_FLASH_RESULT_OK || + spi_flash_write(BOOT_CONFIG_SECTOR * SECTOR_SIZE, buffer, sizeof(buffer)) != SPI_FLASH_RESULT_OK) { + return false; + } + return true; +} + +// get current boot rom +uint8 ICACHE_FLASH_ATTR rboot_get_current_rom(void) { + rboot_config conf; + conf = rboot_get_config(); + return conf.current_rom; +} + +// set current boot rom +bool ICACHE_FLASH_ATTR rboot_set_current_rom(uint8 rom) { + rboot_config conf; + conf = rboot_get_config(); + if (rom >= conf.count) return false; + conf.current_rom = rom; + return rboot_set_config(&conf); +} + +// create the write status struct, based on supplied start address +rboot_write_status ICACHE_FLASH_ATTR rboot_write_init(uint32 start_addr) { + rboot_write_status status = {0}; + status.start_addr = start_addr; + status.start_sector = start_addr / SECTOR_SIZE; + //status.max_sector_count = 200; + //os_printf("init addr: 0x%08x\r\n", start_addr); + + return status; +} + +// function to do the actual writing to flash +// call repeatedly with more data (max len per write is the flash sector size (4k)) +bool ICACHE_FLASH_ATTR rboot_write_flash(rboot_write_status *status, uint8 *data, uint16 len) { + + bool ret = false; + uint8 *buffer; + + if (data == NULL || len == 0) { + return true; + } + + // get a buffer + buffer = (uint8 *) calloc(1, len + status->extra_count); + if (!buffer) { + //os_printf("No ram!\r\n"); + return false; + } + + // copy in any remaining bytes from last chunk + memcpy(buffer, status->extra_bytes, status->extra_count); + // copy in new data + memcpy(buffer + status->extra_count, data, len); + + // calculate length, must be multiple of 4 + // save any remaining bytes for next go + len += status->extra_count; + status->extra_count = len % 4; + len -= status->extra_count; + memcpy(status->extra_bytes, buffer + len, status->extra_count); + + // check data will fit + //if (status->start_addr + len < (status->start_sector + status->max_sector_count) * SECTOR_SIZE) { + + if (len > SECTOR_SIZE) { + // to support larger writes we would need to erase current + // (if not already done), next and possibly later sectors too + } else { + // check if the sector the write finishes in has been erased yet, + // this is fine as long as data len < sector size + if (status->last_sector_erased != (status->start_addr + len) / SECTOR_SIZE) { + status->last_sector_erased = (status->start_addr + len) / SECTOR_SIZE; + spi_flash_erase_sector(status->last_sector_erased); + } + } + + // write current chunk + //os_printf("write addr: 0x%08x, len: 0x%04x\r\n", status->start_addr, len); + if (spi_flash_write(status->start_addr, (uint32 *)buffer, len) == SPI_FLASH_RESULT_OK) { + ret = true; + status->start_addr += len; + } + //} + + free(buffer); + return ret; +} + +#ifdef __cplusplus +} +#endif diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-api.h b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-api.h new file mode 100644 index 0000000..c4601d3 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-api.h @@ -0,0 +1,38 @@ +#ifndef CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_APPCODE_RBOOT_API_H_ +#define CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_APPCODE_RBOOT_API_H_ + +////////////////////////////////////////////////// +// rBoot OTA and config API for ESP8266. +// Copyright 2015 Richard A Burton +// richardaburton@gmail.com +// See license.txt for license terms. +// OTA code based on SDK sample from Espressif. +////////////////////////////////////////////////// + +#include "../rboot.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint32 start_addr; + uint32 start_sector; + //uint32 max_sector_count; + uint32 last_sector_erased; + uint8 extra_count; + uint8 extra_bytes[4]; +} rboot_write_status; + +rboot_config ICACHE_FLASH_ATTR rboot_get_config(void); +bool ICACHE_FLASH_ATTR rboot_set_config(rboot_config *conf); +uint8 ICACHE_FLASH_ATTR rboot_get_current_rom(void); +bool ICACHE_FLASH_ATTR rboot_set_current_rom(uint8 rom); +rboot_write_status ICACHE_FLASH_ATTR rboot_write_init(uint32 start_addr); +bool ICACHE_FLASH_ATTR rboot_write_flash(rboot_write_status *status, uint8 *data, uint16 len); + +#ifdef __cplusplus +} +#endif + +#endif /* CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_APPCODE_RBOOT_API_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-bigflash.c b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-bigflash.c new file mode 100644 index 0000000..cd8a180 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-bigflash.c @@ -0,0 +1,61 @@ +////////////////////////////////////////////////// +// rBoot open source boot loader for ESP8266. +// Copyright 2015 Richard A Burton +// richardaburton@gmail.com +// See license.txt for license terms. +////////////////////////////////////////////////// + +/* clang-format off */ + +#ifdef RBOOT_INTEGRATION +#include +#endif + +typedef unsigned int uint32; +typedef unsigned char uint8; + +#include + +#if 0 //def BOOT_BIG_FLASH + +// plain sdk defaults to iram +#ifndef IRAM_ATTR +#define IRAM_ATTR +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +extern void Cache_Read_Disable(); +extern uint32 SPIRead(uint32, void*, uint32); +extern void Cache_Read_Enable(uint32, uint32, uint32); + +uint8 rBoot_mmap_1 = 0xff; +uint8 rBoot_mmap_2 = 0xff; + +// this function must remain in iram +IRAM NOINSTR void __wrap_Cache_Read_Enable_New(void) { + if (rBoot_mmap_1 == 0xff) { + uint32 addr; + rboot_config conf; + + Cache_Read_Disable(); + + SPIRead(BOOT_CONFIG_SECTOR * SECTOR_SIZE, &conf, sizeof(rboot_config)); + + addr = conf.roms[conf.current_rom]; + addr /= 0x100000; + + rBoot_mmap_2 = addr / 2; + rBoot_mmap_1 = addr % 2; + } + + Cache_Read_Enable(rBoot_mmap_1, rBoot_mmap_2, 1); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/firmware/rboot.bin b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/firmware/rboot.bin new file mode 100644 index 0000000000000000000000000000000000000000..a928e0b0e8fb3b8f63ad57135a110795e3570d3e GIT binary patch literal 2464 zcmb7Fe{56N6~6b`FF&7&ZD^JmTHX6dmeAqUenAj2MZD*xAtnScC4s7~x|i4v9*zy> z1xi&7Mk%dCx>4BF3aP58x{0;>!BW(&8xtX9fkp1NWL3q~RdX|@0{s=Gbfqf1+jFlS zE&ujOpMAb_&i(Fp@4LRfCLy$?#H}Ob){`aZ8$TZ%u-DZ5$X?R`SOL%h>jB#UQNT{X z0l?FMqk!iDS-@$)S->U0+kiZv=AgX>xe>YrF>+(D=g15Qg8pt!2gKQR0sPTMOGifamegW-P*4tpLCDfA4(?+Om@gAOx%nG<@N;dgkT zVktLFf5Y&U=ve?`yb67l;lB#bHDQiZ3{MEoHDQjQGdwOh*L;ED3!Km_!{>Rxjgu8U zqoQ&oOLccw$-{%grk;pCv ziLeA8+vUfv&=k|-ZXgL+^g)v-(*$q3Bn5KrkCqr2M&r!gc0h|FJXn~nWb>qFnLKKg zj~b;VwS40$BAW*F`vUnZ0qE2>1W3c6 zJ{@@Kxcl(8P)n=oK;)Ca_#(I=94-EkgcdK=KM7nMzV~iUzRO8IZ(SU268w9G;&%k; zERb&t@?e1|cl%Wd?Pvnci%r>{{j;}ABk$Y6yv=vR*R;4U@}3PgI14+rE|SLIWXdeL zhqSsvS&ZMcVWqig)rCGdR!%2vpV}bxjLM-&54~tNJMk2+M<#^W(4XlA8^6UfiZp%~ zQYSqPQl$o8tEG2dgCi=#i3p9dP#=O52p-FiHMawrzTv~~ZGKlDI~jnxSvk~-N6mGT z+4+_JE72^(%YU&}=IFhTLcKQ{5h2t4syXiTSyNrKz4h|hpe_X}S z-4)Wc0AJ;0zYCVWAkRjw=kR}d;`)0%_uN*gUO79XTOm{LaegJyPOzX-W0bC&qTy-U zF-zB;@Q9_|M)Ln)%~=Unnn&=!|Iyn3S4Ae2PFrM8uu*Fa5ysM$N1vC=8M zf51#68~f4&t_swiGiwi5> zxYJ6cQ!ONv=qGW$G9j=vldux0eiDtv&B3Hadimz`rBm@lek zJ}VX#6@H zVo~8oyqq|^gIIJqiJQq}ui3YQSZNa9OP1CZdN#v+bx}UL*PQdh{T0j?2J^W$q)C4| iUGQh_0Zp?v87q39TNn3UR}^tS0lx +#endif + +#include "rboot-private.h" + +usercode* NOINLINE load_rom(uint32 readpos) { + + uint8 buffer[BUFFER_SIZE]; + uint8 sectcount; + uint8 *writepos; + uint32 remaining; + usercode* usercode; + + rom_header *header = (rom_header*)buffer; + section_header *section = (section_header*)buffer; + + // read rom header + SPIRead(readpos, header, sizeof(rom_header)); + readpos += sizeof(rom_header); + + // create function pointer for entry point + usercode = header->entry; + + // copy all the sections + for (sectcount = header->count; sectcount > 0; sectcount--) { + + // read section header + SPIRead(readpos, section, sizeof(section_header)); + readpos += sizeof(section_header); + + // get section address and length + writepos = section->address; + remaining = section->length; + + while (remaining > 0) { + // work out how much to read, up to 16 bytes at a time + uint32 readlen = (remaining < BUFFER_SIZE) ? remaining : BUFFER_SIZE; + // read the block + SPIRead(readpos, buffer, readlen); + readpos += readlen; + // copy the block + ets_memcpy(writepos, buffer, readlen); + // increment next write position + writepos += readlen; + // decrement remaining count + remaining -= readlen; + } + } + + return usercode; +} + +#ifdef BOOT_NO_ASM + +void call_user_start(uint32 readpos) { + usercode* user; + user = load_rom(readpos); + user(); +} + +#else + +void call_user_start(uint32 readpos) { + __asm volatile ( + "mov a15, a0\n" // store return addr, we already splatted a15! + "call0 load_rom\n" // load the rom + "mov a0, a15\n" // restore return addr + "jx a2\n" // now jump to the rom code + ); +} + +#endif diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot-stage2a.ld b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot-stage2a.ld new file mode 100644 index 0000000..7af83ea --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot-stage2a.ld @@ -0,0 +1,211 @@ +/* This linker script generated from xt-genldscripts.tpp for LSP . */ +/* Linker Script for ld -N */ +MEMORY +{ + dport0_0_seg : org = 0x3FF00000, len = 0x10 + dram0_0_seg : org = 0x3FFE8000, len = 0x14000 + iram1_0_seg : org = 0x4010FC00, len = 0x400 + irom0_0_seg : org = 0x40240000, len = 0x3C000 +} + +PHDRS +{ + dport0_0_phdr PT_LOAD; + dram0_0_phdr PT_LOAD; + dram0_0_bss_phdr PT_LOAD; + iram1_0_phdr PT_LOAD; + irom0_0_phdr PT_LOAD; +} + + +/* Default entry point: */ +ENTRY(call_user_start) +EXTERN(_DebugExceptionVector) +EXTERN(_DoubleExceptionVector) +EXTERN(_KernelExceptionVector) +EXTERN(_NMIExceptionVector) +EXTERN(_UserExceptionVector) +PROVIDE(_memmap_vecbase_reset = 0x40000000); +/* Various memory-map dependent cache attribute settings: */ +_memmap_cacheattr_wb_base = 0x00000110; +_memmap_cacheattr_wt_base = 0x00000110; +_memmap_cacheattr_bp_base = 0x00000220; +_memmap_cacheattr_unused_mask = 0xFFFFF00F; +_memmap_cacheattr_wb_trapnull = 0x2222211F; +_memmap_cacheattr_wba_trapnull = 0x2222211F; +_memmap_cacheattr_wbna_trapnull = 0x2222211F; +_memmap_cacheattr_wt_trapnull = 0x2222211F; +_memmap_cacheattr_bp_trapnull = 0x2222222F; +_memmap_cacheattr_wb_strict = 0xFFFFF11F; +_memmap_cacheattr_wt_strict = 0xFFFFF11F; +_memmap_cacheattr_bp_strict = 0xFFFFF22F; +_memmap_cacheattr_wb_allvalid = 0x22222112; +_memmap_cacheattr_wt_allvalid = 0x22222112; +_memmap_cacheattr_bp_allvalid = 0x22222222; +PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_wb_trapnull); + +SECTIONS +{ + + .dport0.rodata : ALIGN(4) + { + _dport0_rodata_start = ABSOLUTE(.); + *(.dport0.rodata) + *(.dport.rodata) + _dport0_rodata_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.literal : ALIGN(4) + { + _dport0_literal_start = ABSOLUTE(.); + *(.dport0.literal) + *(.dport.literal) + _dport0_literal_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.data : ALIGN(4) + { + _dport0_data_start = ABSOLUTE(.); + *(.dport0.data) + *(.dport.data) + _dport0_data_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .data : ALIGN(4) + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + _data_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .rodata : ALIGN(4) + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + *(.eh_frame) + /* C++ constructor and destructor tables, properly ordered: */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + . = ALIGN(4); /* this table MUST be 4-byte aligned */ + _bss_table_start = ABSOLUTE(.); + LONG(_bss_start) + LONG(_bss_end) + _bss_table_end = ABSOLUTE(.); + _rodata_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .bss ALIGN(8) (NOLOAD) : ALIGN(4) + { + . = ALIGN (8); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + _heap_start = ABSOLUTE(.); +/* _stack_sentry = ALIGN(0x8); */ + } >dram0_0_seg :dram0_0_bss_phdr +/* __stack = 0x3ffc8000; */ + + .text : ALIGN(4) + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.UserEnter.text) + . = ALIGN(16); + *(.DebugExceptionVector.text) + . = ALIGN(16); + *(.NMIExceptionVector.text) + . = ALIGN(16); + *(.KernelExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.UserExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.DoubleExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + } >iram1_0_seg :iram1_0_phdr + + .lit4 : ALIGN(4) + { + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + } >iram1_0_seg :iram1_0_phdr + + .irom0.text : ALIGN(4) + { + _irom0_text_start = ABSOLUTE(.); + *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text) + _irom0_text_end = ABSOLUTE(.); + } >irom0_0_seg :irom0_0_phdr + +} + +/* get ROM code address */ +INCLUDE "rboot_rom.ld" diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.c b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.c new file mode 100644 index 0000000..4ac5df1 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.c @@ -0,0 +1,414 @@ +////////////////////////////////////////////////// +// rBoot open source boot loader for ESP8266. +// Copyright 2015 Richard A Burton +// richardaburton@gmail.com +// See license.txt for license terms. +////////////////////////////////////////////////// +/* clang-format off */ + +#ifdef RBOOT_INTEGRATION +#include +#endif + +#include "rboot-private.h" +#include + +static uint32 check_image(uint32 readpos) { + + uint8 buffer[BUFFER_SIZE]; + uint8 sectcount; + uint8 sectcurrent; + uint8 *writepos; + uint8 chksum = CHKSUM_INIT; + uint32 loop; + uint32 remaining; + uint32 romaddr; + + rom_header_new *header = (rom_header_new*)buffer; + section_header *section = (section_header*)buffer; + + if (readpos == 0 || readpos == 0xffffffff) { + return 0; + } + + // read rom header + if (SPIRead(readpos, header, sizeof(rom_header_new)) != 0) { + return 0; + } + + // check header type + if (header->magic == ROM_MAGIC) { + // old type, no extra header or irom section to skip over + romaddr = readpos; + readpos += sizeof(rom_header); + sectcount = header->count; + } else if (header->magic == ROM_MAGIC_NEW1 && header->count == ROM_MAGIC_NEW2) { + // new type, has extra header and irom section first + romaddr = readpos + header->len + sizeof(rom_header_new); +#ifdef BOOT_IROM_CHKSUM + // we will set the real section count later, when we read the header + sectcount = 0xff; + // just skip the first part of the header + // rest is processed for the chksum + readpos += sizeof(rom_header); +#else + // skip the extra header and irom section + readpos = romaddr; + // read the normal header that follows + if (SPIRead(readpos, header, sizeof(rom_header)) != 0) { + return 0; + } + sectcount = header->count; + readpos += sizeof(rom_header); +#endif + } else { + return 0; + } + + // test each section + for (sectcurrent = 0; sectcurrent < sectcount; sectcurrent++) { + + // read section header + if (SPIRead(readpos, section, sizeof(section_header)) != 0) { + return 0; + } + readpos += sizeof(section_header); + + // get section address and length + writepos = section->address; + remaining = section->length; + + while (remaining > 0) { + // work out how much to read, up to BUFFER_SIZE + uint32 readlen = (remaining < BUFFER_SIZE) ? remaining : BUFFER_SIZE; + // read the block + if (SPIRead(readpos, buffer, readlen) != 0) { + return 0; + } + // increment next read and write positions + readpos += readlen; + writepos += readlen; + // decrement remaining count + remaining -= readlen; + // add to chksum + for (loop = 0; loop < readlen; loop++) { + chksum ^= buffer[loop]; + } + } + +#ifdef BOOT_IROM_CHKSUM + if (sectcount == 0xff) { + // just processed the irom section, now + // read the normal header that follows + if (SPIRead(readpos, header, sizeof(rom_header)) != 0) { + return 0; + } + sectcount = header->count + 1; + readpos += sizeof(rom_header); + } +#endif + } + + // round up to next 16 and get checksum + readpos = readpos | 0x0f; + if (SPIRead(readpos, buffer, 1) != 0) { + return 0; + } + + // compare calculated and stored checksums + if (buffer[0] != chksum) { + return 0; + } + + return romaddr; +} + +#define ETS_UNCACHED_ADDR(addr) (addr) +#define READ_PERI_REG(addr) (*((volatile uint32 *)ETS_UNCACHED_ADDR(addr))) +#define WRITE_PERI_REG(addr, val) (*((volatile uint32 *)ETS_UNCACHED_ADDR(addr))) = (uint32)(val) +#define PERIPHS_RTC_BASEADDR 0x60000700 +#define REG_RTC_BASE PERIPHS_RTC_BASEADDR +#define RTC_GPIO_OUT (REG_RTC_BASE + 0x068) +#define RTC_GPIO_ENABLE (REG_RTC_BASE + 0x074) +#define RTC_GPIO_IN_DATA (REG_RTC_BASE + 0x08C) +#define RTC_GPIO_CONF (REG_RTC_BASE + 0x090) +#define PAD_XPD_DCDC_CONF (REG_RTC_BASE + 0x0A0) +static uint32 get_gpio16(void) { + // set output level to 1 + WRITE_PERI_REG(RTC_GPIO_OUT, (READ_PERI_REG(RTC_GPIO_OUT) & (uint32)0xfffffffe) | (uint32)(1)); + + // read level + WRITE_PERI_REG(PAD_XPD_DCDC_CONF, (READ_PERI_REG(PAD_XPD_DCDC_CONF) & 0xffffffbc) | (uint32)0x1); // mux configuration for XPD_DCDC and rtc_gpio0 connection + WRITE_PERI_REG(RTC_GPIO_CONF, (READ_PERI_REG(RTC_GPIO_CONF) & (uint32)0xfffffffe) | (uint32)0x0); //mux configuration for out enable + WRITE_PERI_REG(RTC_GPIO_ENABLE, READ_PERI_REG(RTC_GPIO_ENABLE) & (uint32)0xfffffffe); //out disable + + uint32 x = (READ_PERI_REG(RTC_GPIO_IN_DATA) & 1); + + return x; +} + +#ifdef BOOT_CONFIG_CHKSUM +// calculate checksum for block of data +// from start up to (but excluding) end +static uint8 calc_chksum(uint8 *start, uint8 *end) { + uint8 chksum = CHKSUM_INIT; + while(start < end) { + chksum ^= *start; + start++; + } + return chksum; +} +#endif + +#define UART_CLKDIV_26MHZ(B) (52000000 + B / 2) / B + +// prevent this function being placed inline with main +// to keep main's stack size as small as possible +// don't mark as static or it'll be optimised out when +// using the assembler stub +uint32 NOINLINE find_image(void) { + + uint8 flag; + uint32 runAddr; + uint32 flashsize; + int32 romToBoot; + uint8 gpio_boot = FALSE; + uint8 updateConfig = FALSE; + uint8 buffer[SECTOR_SIZE]; + + rboot_config *romconf = (rboot_config*)buffer; + rom_header *header = (rom_header*)buffer; + + // delay to slow boot (help see messages when debugging) + //ets_delay_us(2000000); + + uart_div_modify(0, UART_CLKDIV_26MHZ(115200)); + ets_delay_us(1000); + + ets_printf("\r\nrBoot v1.2.1 - richardaburton@gmail.com\r\n"); + + // read rom header + SPIRead(0, header, sizeof(rom_header)); + + // print and get flash size + ets_printf("Flash Size: "); + flag = header->flags2 >> 4; + if (flag == 0) { + ets_printf("4 Mbit\r\n"); + flashsize = 0x80000; + } else if (flag == 1) { + ets_printf("2 Mbit\r\n"); + flashsize = 0x40000; + } else if (flag == 2) { + ets_printf("8 Mbit\r\n"); + flashsize = 0x100000; + } else { +#ifdef BOOT_BIG_FLASH + if (flag == 3) { + ets_printf("16 Mbit\r\n"); + flashsize = 0x200000; + } else if (flag == 4) { + ets_printf("32 Mbit\r\n"); + flashsize = 0x400000; + } else if (flag == 8) { + ets_printf("64 Mbit\r\n"); + flashsize = 0x800000; + } else if (flag == 9) { + ets_printf("128 Mbit\r\n"); + flashsize = 0x1000000; + } else { + ets_printf("unknown\r\n"); + flashsize = 0x100000; // assume 8Mbit + } +#else + ets_printf("8 Mbit\r\n"); + flashsize = 0x100000; // limit to 8Mbit +#endif + } + + // print spi mode + ets_printf("Flash Mode: "); + if (header->flags1 == 0) { + ets_printf("QIO\r\n"); + } else if (header->flags1 == 1) { + ets_printf("QOUT\r\n"); + } else if (header->flags1 == 2) { + ets_printf("DIO\r\n"); + } else if (header->flags1 == 3) { + ets_printf("DOUT\r\n"); + } else { + ets_printf("unknown\r\n"); + } + + // print spi speed + ets_printf("Flash Speed: "); + flag = header->flags2 & 0x0f; + if (flag == 0) ets_printf("40 MHz\r\n"); + else if (flag == 1) ets_printf("26.7 MHz\r\n"); + else if (flag == 2) ets_printf("20 MHz\r\n"); + else if (flag == 0x0f) ets_printf("80 MHz\r\n"); + else ets_printf("unknown\r\n"); + + // print enabled options +#ifdef BOOT_BIG_FLASH + ets_printf("rBoot Option: Big flash\r\n"); +#endif +#ifdef BOOT_CONFIG_CHKSUM + ets_printf("rBoot Option: Config chksum\r\n"); +#endif +#ifdef BOOT_IROM_CHKSUM + ets_printf("rBoot Option: irom chksum\r\n"); +#endif + + ets_printf("\r\n"); + + // read boot config + SPIRead(BOOT_CONFIG_SECTOR * SECTOR_SIZE, buffer, SECTOR_SIZE); + // fresh install or old version? + if (romconf->magic != BOOT_CONFIG_MAGIC || romconf->version != BOOT_CONFIG_VERSION +#ifdef BOOT_CONFIG_CHKSUM + || romconf->chksum != calc_chksum((uint8*)romconf, (uint8*)&romconf->chksum) +#endif + ) { + /* Modified by Cesanta */ + ets_printf("Writing default boot config @ 0x%x.\r\n", BOOT_CONFIG_SECTOR * SECTOR_SIZE); + ets_memset(romconf, 0x00, sizeof(rboot_config)); + romconf->magic = BOOT_CONFIG_MAGIC; + romconf->version = BOOT_CONFIG_VERSION; + romconf->count = 2; + romconf->mode = MODE_STANDARD; + /* FWx_ADDR, FWx_FS_ADDR and FS_SIZE, FW_SIZE must be defined by -D */ + romconf->roms[0] = FW1_ADDR; + romconf->roms[1] = FW2_ADDR; + romconf->fs_addresses[0] = FW1_FS_ADDR; + romconf->fs_addresses[1] = FW2_FS_ADDR; + romconf->fs_sizes[0] = romconf->fs_sizes[1] = FS_SIZE; + romconf->roms_sizes[0] = romconf->roms_sizes[1] = FW_SIZE; +#ifdef BOOT_CONFIG_CHKSUM + romconf->chksum = calc_chksum((uint8*)romconf, (uint8*)&romconf->chksum); +#endif + // write new config sector + SPIEraseSector(BOOT_CONFIG_SECTOR); + SPIWrite(BOOT_CONFIG_SECTOR * SECTOR_SIZE, buffer, SECTOR_SIZE); + } + + // if gpio mode enabled check status of the gpio + if ((romconf->mode & MODE_GPIO_ROM) && (get_gpio16() == 0)) { + ets_printf("Booting GPIO-selected.\r\n"); + romToBoot = romconf->previous_rom; + /* + * Modified by Cesanta + * Make FD current + */ + updateConfig = TRUE; + romconf->fw_updated = 0; + romconf->is_first_boot = 0; + gpio_boot = TRUE; + } else if (romconf->current_rom >= romconf->count) { + // if invalid rom selected try rom 0 + ets_printf("Invalid rom selected, defaulting.\r\n"); + romToBoot = 0; + romconf->current_rom = 0; + romconf->fw_updated = 0; + romconf->is_first_boot = 0; + updateConfig = TRUE; + } else { + /* Modified by Cesanta */ + if (romconf->is_first_boot != 0) { + ets_printf("First boot, attempt %d\n", romconf->boot_attempts); + /* boot is unconfirmed */ + if (romconf->boot_attempts == 0) { + /* haven't try to load yes */ + ets_printf("Boot is unconfirmed\r\n"); + romconf->boot_attempts++; + } else { + ets_printf("Boot failed, fallback to fw #%d\r\n", + romconf->previous_rom); + romconf->current_rom = romconf->previous_rom; + /* clear fw update flag, to avoid post-update acttions */ + romconf->fw_updated = 0; + romconf->boot_attempts = 0; + } + + updateConfig = TRUE; + } + /* End of Cesanta modifications */ + // try rom selected in the config + romToBoot = romconf->current_rom; + } + + // try to find a good rom + do { + runAddr = check_image(romconf->roms[romToBoot]); + if (runAddr == 0) { + ets_printf("Rom %d is bad.\r\n", romToBoot); + if (gpio_boot) { + // don't switch to backup for gpio-selected rom + ets_printf("GPIO boot failed.\r\n"); + return 0; + } else { + // for normal mode try each previous rom + // until we find a good one or run out + updateConfig = TRUE; + romToBoot--; + if (romToBoot < 0) romToBoot = romconf->count - 1; + if (romToBoot == romconf->current_rom) { + // tried them all and all are bad! + ets_printf("No good rom available.\r\n"); + return 0; + } + } + } + } while (runAddr == 0); + + // re-write config, if required + if (updateConfig) { + romconf->current_rom = romToBoot; +#ifdef BOOT_CONFIG_CHKSUM + romconf->chksum = calc_chksum((uint8*)romconf, (uint8*)&romconf->chksum); +#endif + SPIEraseSector(BOOT_CONFIG_SECTOR); + SPIWrite(BOOT_CONFIG_SECTOR * SECTOR_SIZE, buffer, SECTOR_SIZE); + } + + ets_printf("Booting rom %d (0x%x).\r\n", romToBoot, romconf->roms[romToBoot]); + // copy the loader to top of iram + ets_memcpy((void*)_text_addr, _text_data, _text_len); + // return address to load from + return runAddr; + +} + +#ifdef BOOT_NO_ASM + +// small stub method to ensure minimum stack space used +void call_user_start(void) { + uint32 addr; + stage2a *loader; + + addr = find_image(); + if (addr != 0) { + loader = (stage2a*)entry_addr; + loader(addr); + } +} + +#else + +// assembler stub uses no stack space +// works with gcc +void call_user_start(void) { + __asm volatile ( + "mov a15, a0\n" // store return addr, hope nobody wanted a15! + "call0 find_image\n" // find a good rom to boot + "mov a0, a15\n" // restore return addr + "bnez a2, 1f\n" // ?success + "ret\n" // no, return + "1:\n" // yes... + "movi a3, entry_addr\n" // actually gives us a pointer to entry_addr + "l32i a3, a3, 0\n" // now really load entry_addr + "jx a3\n" // now jump to it + ); +} + +#endif diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.h b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.h new file mode 100644 index 0000000..3949d74 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.h @@ -0,0 +1,82 @@ +#ifndef CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_RBOOT_H_ +#define CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_RBOOT_H_ + +////////////////////////////////////////////////// +// rBoot open source boot loader for ESP8266. +// Copyright 2015 Richard A Burton +// richardaburton@gmail.com +// See license.txt for license terms. +////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif + +// uncomment to use only c code +// if you aren't using gcc you may need to do this +//#define BOOT_NO_ASM + +// uncomment to have a checksum on the boot config +//#define BOOT_CONFIG_CHKSUM + +// uncomment to enable big flash support (>1MB) +//#define BOOT_BIG_FLASH + +// uncomment to include .irom0.text section in the checksum +// roms must be built with esptool2 using -iromchksum option +//#define BOOT_IROM_CHKSUM + +// increase if required +#define MAX_ROMS 4 + +#define CHKSUM_INIT 0xef + +#define SECTOR_SIZE 0x1000 + +#ifndef BOOT_CONFIG_ADDR +#define BOOT_CONFIG_ADDR 0x1000 +#endif + +#define BOOT_CONFIG_SECTOR (BOOT_CONFIG_ADDR / SECTOR_SIZE) + +#define BOOT_CONFIG_MAGIC 0xe1 +#define BOOT_CONFIG_VERSION 0x01 + +#define MODE_STANDARD 0x00 +#define MODE_GPIO_ROM 0x01 + +// boot config structure +// rom addresses must be multiples of 0x1000 (flash sector aligned) +// without BOOT_BIG_FLASH only the first 8Mbit of the chip will be memory mapped +// so rom slots containing .irom0.text sections must remain below 0x100000 +// slots beyond this will only be accessible via spi read calls, so +// use these for stored resources, not code +// with BOOT_BIG_FLASH the flash will be mapped in chunks of 8MBit, so roms can +// be anywhere, but must not straddle two 8MBit blocks +typedef struct { + uint8 magic; // our magic + uint8 version; // config struct version + uint8 mode; // boot loader mode + uint8 current_rom; // currently selected rom + uint8 gpio_rom; // rom to use for gpio boot + uint8 count; // number of roms in use + uint8 previous_rom; // previously selected rom + uint8 is_first_boot; + uint8 boot_attempts; + uint8 fw_updated; + uint8 padding[2]; + uint32 roms[MAX_ROMS]; // flash addresses of the roms + uint32 roms_sizes[MAX_ROMS]; // sizes of the roms + uint32 fs_addresses[MAX_ROMS]; // file system addresses + uint32 fs_sizes[MAX_ROMS]; // file system sizes + uint32 user_flags; +#ifdef BOOT_CONFIG_CHKSUM + uint8 chksum; // config chksum +#endif +} rboot_config; + +#ifdef __cplusplus +} +#endif + +#endif /* CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_RBOOT_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.ld b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.ld new file mode 100644 index 0000000..5e25740 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.ld @@ -0,0 +1,2 @@ +INCLUDE "eagle.app.v6.ld" +INCLUDE "rboot_rom.ld" diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.mk b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.mk new file mode 100644 index 0000000..f7f3987 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.mk @@ -0,0 +1,89 @@ +# +# Makefile for rBoot +# https://github.com/raburton/esp8266 +# + +ESPTOOL2 ?= ../../build/esptool2 +SDK_BASE ?= /opt/Espressif/ESP8266_SDK +SPI_SIZE ?= +RBOOT_INTEGRATION ?= +RBOOT_EXTRA_INCDIR ?= + +# RBOOT_BUILD_BASE and RBOOT_GEN_BASE should be provided via makefile parameters +RBOOT_BUILD_BASE ?= +# RBOOT_GEN_BASE is the directory for generated inputs +RBOOT_GEN_BASE ?= + +ifndef XTENSA_BINDIR +CC := xtensa-lx106-elf-gcc +LD := xtensa-lx106-elf-gcc +else +CC := $(addprefix $(XTENSA_BINDIR)/,xtensa-lx106-elf-gcc) +LD := $(addprefix $(XTENSA_BINDIR)/,xtensa-lx106-elf-gcc) +endif +CC_WRAPPER ?= + +CFLAGS = -Os -O3 -Wpointer-arith -Wundef -Werror -Wl,-EL \ + -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals \ + -D__ets__ -DIRAM='__attribute__((section(".fast.text")))' \ + -DNOINSTR='__attribute__((no_instrument_function))' \ + -DICACHE_FLASH $(CFLAGS_EXTRA) + +LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static -L $(SDK_BASE)/ld/ + +LD_SCRIPT = rboot.ld + +E2_OPTS = -quiet -bin -boot0 + +ifeq ($(RBOOT_BIG_FLASH),1) + CFLAGS += -DBOOT_BIG_FLASH +endif +ifeq ($(RBOOT_INTEGRATION),1) + CFLAGS += -DRBOOT_INTEGRATION +endif +ifeq ($(SPI_SIZE), 256K) + E2_OPTS += -256 +else ifeq ($(SPI_SIZE), 512K) + E2_OPTS += -512 +else ifeq ($(SPI_SIZE), 1M) + E2_OPTS += -1024 +else ifeq ($(SPI_SIZE), 2M) + E2_OPTS += -2048 +else ifeq ($(SPI_SIZE), 4M) + E2_OPTS += -4096 +endif + +RBOOT_EXTRA_INCDIR := $(addprefix -I,$(RBOOT_EXTRA_INCDIR)) + +.SECONDARY: + +all: $(RBOOT_BUILD_BASE)/rboot.bin + +$(RBOOT_BUILD_BASE)/rboot-stage2a.o: rboot-stage2a.c rboot-private.h rboot.h + @echo "CC $<" + @$(CC_WRAPPER) $(CC) $(CFLAGS) $(RBOOT_EXTRA_INCDIR) -c $< -o $@ + +$(RBOOT_BUILD_BASE)/rboot-stage2a.elf: $(RBOOT_BUILD_BASE)/rboot-stage2a.o + @echo "LD $@" + @$(CC_WRAPPER) $(LD) -Trboot-stage2a.ld $(LDFLAGS) -Wl,--start-group $^ -Wl,--end-group -o $@ + +$(RBOOT_GEN_BASE)/rboot-hex2a.h: $(RBOOT_BUILD_BASE)/rboot-stage2a.elf + @echo "E2 $@" + @$(ESPTOOL2) -quiet -header $< $@ .text + +$(RBOOT_BUILD_BASE)/rboot.o: rboot.c rboot-private.h rboot.h $(RBOOT_GEN_BASE)/rboot-hex2a.h + @echo "CC $<" + @$(CC) $(CFLAGS) -I$(RBOOT_GEN_BASE) $(RBOOT_EXTRA_INCDIR) -c $< -o $@ + +$(RBOOT_BUILD_BASE)/%.o: %.c %.h + @echo "CC $<" + @$(CC) $(CFLAGS) $(RBOOT_EXTRA_INCDIR) -c $< -o $@ + +$(RBOOT_BUILD_BASE)/%.elf: $(RBOOT_BUILD_BASE)/%.o + @echo "LD $@" + @$(LD) -T$(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group $^ -Wl,--end-group -o $@ + +$(RBOOT_BUILD_BASE)/%.bin: $(RBOOT_BUILD_BASE)/%.elf + @echo "E2 $@" + @$(ESPTOOL2) $(E2_OPTS) $< $@ .text .rodata + diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot_rom.ld b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot_rom.ld new file mode 100644 index 0000000..69e862c --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot_rom.ld @@ -0,0 +1,8 @@ +PROVIDE ( ets_delay_us = 0x40002ecc ); +PROVIDE ( ets_memcpy = 0x400018b4 ); +PROVIDE ( ets_memset = 0x400018a4 ); +PROVIDE ( ets_printf = 0x400024cc ); +PROVIDE ( SPIEraseSector = 0x40004a00 ); +PROVIDE ( SPIRead = 0x40004b1c ); +PROVIDE ( SPIWrite = 0x40004a4c ); +PROVIDE ( uart_div_modify = 0x400039d8 ); diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/readme-api.txt b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/readme-api.txt new file mode 100644 index 0000000..2304748 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/readme-api.txt @@ -0,0 +1,40 @@ +rBoot - User API for rBoot on the ESP8266 +------------------------------------------ +by Richard A Burton, richardaburton@gmail.com +http://richard.burtons.org/ + +This provides a few simple APIs for getting & setting rBoot config and for +writing data from OTA updates. API source files are in the appcode directory. + +Actual OTA network code is implementation specific and no longer included in +rBoot itself, see the rBoot sample projects for this code (which you can then +use in your own projects). + + rboot_config rboot_get_config(); + Returns rboot_config (defined in rboot.h) allowing you to modify any values + in it, including the rom layout. + + bool rboot_set_config(rboot_config *conf); + Saves the rboot_config structure back to sector 2 of the flash, while + maintaining the contents of the rest of the sector. You can use the rest of + this sector for your app settings, as long as you protect this structure + when you do so. + + uint8 rboot_get_current_rom(); + Get the currently selected boot rom (the currently running rom, as long as + you haven't changed it since boot). + + bool rboot_set_current_rom(uint8 rom); + Set the current boot rom, which will be used when next restarted. + + rboot_write_status rboot_write_init(uint32 start_addr); + Call once before starting to pass data to write to the flash. start_addr is + the address on the SPI flash to write from. Returns a status structure which + must be passed back on each write. The contents of the structure should not + be modified by the calling code. + + bool rboot_write_flash(rboot_write_status *status, uint8 *data, uint16 len); + Call repeatedly to write data to the flash, starting at the address + specified on the prior call to rboot_write_init. Current write position is + tracked automatically. This method is likely to be called each time a packet + of OTA data is received over the network. diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/readme.txt b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/readme.txt new file mode 100644 index 0000000..944e1f2 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/readme.txt @@ -0,0 +1,218 @@ +rBoot - An open source boot loader for the ESP8266 +-------------------------------------------------- +by Richard A Burton, richardaburton@gmail.com +http://richard.burtons.org/ + + +rBoot is designed to be a flexible open source boot loader, a replacement for +the binary blob supplied with the SDK. It has the following advantages over the +Espressif loader: + + - Open source (written in C). + - Supports up to 256 roms. + - Roms can be variable size. + - Able to test multiple roms to find a valid backup (without resetting). + - Flash layout can be changed on the fly (with care and appropriately linked + rom images). + - GPIO support for rom selection. + - Wastes no stack space (SDK boot loader uses 144 bytes). + - Documented config structure to allow easy editing from user code. + - Can validate .irom0.text section with checksum. + +Limitations +----------- +The ESP8266 can only map 8Mbits (1MB) of flash to memory, but which 8Mbits to +map is selectable. This allows individual roms to be up to 1MB in size, so long +as they do not straddle an 8Mbit boundary on the flash. This means you could +have four 1MB roms or 8 512KB roms on a 32Mbit flash (such as on the ESP-12), or +a combination. Note, however, that you could not have, for example, a 512KB rom +followed immediately by a 1MB rom because the 2nd rom would then straddle an +8MBit boundary. By default support for using more than the first 8Mbit of the +flash is disabled, because it requires several steps to get it working. See +below for instructions. + +Building +-------- +A Makefile is included, which should work with the gcc xtensa cross compiler. +There are two source files, the first is compiled and included as data in the +second. When run this code is copied to memory and executed (there is a good +reason for this, see my blog for an explanation). The make file will handle this +for you, but you'll need my esptool2 (see github). + +To use the Makefile set SDK_BASE to point to the root of the Espressif SDK and +either set XTENSA_BINDIR to the gcc xtensa bin directory or include it in your +PATH. These can be set as environment variables or by editing the Makefile. + +Two small assembler stub functions allow the bootloader to launch the user code +without reserving any space on the stack (while the SDK boot loader uses 144 +bytes). This compiles fine with GCC, but if you use another compiler and it +will not compile/work for you then uncomment the #define BOOT_NO_ASM in rboot.h +to use a C version of these functions (this uses 32 bytes). + +Tested with SDK v1.3 and GCC v4.8.2. + +Installation +------------ +Simply write rboot.bin to the first sector of the flash. Remember to set your +flash size correctly with your chosen flash tool (e.g. for esptool.py use the +-fs option). When run rBoot will create it's own config at the start of sector +two for a simple two rom system. You can can then write your two roms to flash +addresses 0x2000 and (half chip size + 0x2000). E.g. for 8Mbit flash: + esptool.py -fs 8m 0x0000 rboot.bin 0x2000 user1.bin 0x82000 user2.bin + +Note: your device may need other options specified. E.g. The nodemcu devkit v1.0 +(commonly, but incorrectly, sold as v2) also needs the "-fm dio" option. + +For more interesting rom layouts you'll need to write an rBoot config sector +manually, see next step. + +The two testload bin files can be flashed in place of normal user roms for +testing rBoot. You do not need these for normal use. + +rBoot Config +------------ +typedef struct { + uint8 magic; // our magic + uint8 version; // config struct version + uint8 mode; // boot loader mode + uint8 current_rom; // currently selected rom + uint8 gpio_rom; // rom to use for gpio boot + uint8 count; // number of roms in use + uint8 unused[2]; // padding + uint32 roms[MAX_ROMS]; // flash addresses of the roms +#ifdef BOOT_CONFIG_CHKSUM + uint8 chksum; // boot config chksum +#endif +} rboot_config; + +Write a config structure as above to address 0x1000 on the flash. If you want +more than 4 roms (default) just increase MAX_ROMS when you compile rBoot. +Think about how you intend to layout your flash before you start! +Rom addresses must be sector aligned i.e start on a multiple of 4096. + + - magic should have value 0xe1 (defined as BOOT_CONFIG_MAGIC). + - version is used in case the config structure changes after deployment. It is + defined as 0x01 (BOOT_CONFIG_VERSION). I don't intend to increase this, but + you should if you choose to reflash the bootloader after deployment and + the config structure has changed. + - mode can be 0x00 (MODE_STANDARD) or 0x01 (MODE_GPIO_ROM). If you set GPIO + you will need to set gpio_rom as well. The sample GPIO code uses GPIO 16 on + a nodemcu dev board, if you want to use a different GPIO you'll need to + adapt the code in rBoot slightly. + - current_rom is the rom to boot, numbered 0 to count-1. + - gpio_rom is the rom to boot when the GPIO is triggered at boot. + - count is the number of roms available (may be less than MAX_ROMS, but not + more). + - unused[2] is padding so the uint32 rom addresses are 4 bytes aligned. + - roms is the array of flash address for the roms. The default generated + config will contain two entries: 0x00002000 and 0x00082000. + - chksum (if enabled, not by deafult) should be the xor of 0xef followed by + each of the bytes of the config structure up to (but obviously not + including) the chksum byte itself. + +Linking user code +----------------- +Each rom will need to be linked with an appropriate linker file, specifying +where it will reside on the flash. If you are only flashing one rom to multiple +places on the flash it must be linked multiple times to produce the set of rom +images. This is the same as with the SDK loader. + +Because there are endless possibilities for layout with this loader I don't +supply sample linker files. Instead I'll tell you how to make them. + +For each rom slot on the flash take a copy of the eagle.app.v6.ld linker script +from the sdk. You then need to modify just one line in it for each rom: + irom0_0_seg : org = 0x40240000, len = 0x3C000 + +Change the org address to be 0x40200000 (base memory mapped location of the +flash) + flash address + 0x10 (offset of data after the header). +The logical place for your first rom is the third sector, address 0x2000. + 0x40200000 + 0x2000 + 0x10 = 0x40202010 +If you use the default generated config the loader will expect to find the +second rom at flash address half-chip-size + 0x2000 (e.g. 0x82000 on an 8MBit +flash) so the irom0_0_seg should be: + 0x40200000 + 0x82000 + 0x10 = 0x40282010 +Due to the limitation of mapped flash (max 8MBit) if you use a larger chip and +do not have big flash support enabled the second rom in the default config will +still be placed at 0x082000, not truly half-chip-size + 0x2000. +Ideally you should also adjust the len to help detect over sized sections at +link time, but more important is the overall size of the rom which you need to +ensure fits in the space you have allocated for it in your flash layout plan. + +Then simply compile and link as you would normally for OTA updates with the SDK +boot loader, except using the linker scripts you've just prepared rather than +the ones supplied with the SDK. Remember when building roms to create them as +'new' type roms (for use with SDK boot loader v1.2+). Or if using my esptool2 +use the -boot2 option. Note: the test loads included with rBoot are built with +-boot0 because they do not contain a .irom0.text section (and so the value of +irom0_0_seg in the linker file is irrelevant to them) but 'normal' user apps +always do. + +irom checksum +------------- +The SDK boot loader checksum only covers sections loaded into ram (data and some +code). Most of the SDK and user code remains on the flash and that is not +included in the checksum. This means you could attempt to boot a corrupt rom +and, because it looks ok to the boot loader, there will be no attempt to switch +to a backup rom. rBoot improves on this by allowing the .irom0.text section to +be included in the checksum. To enable this uncomment #define BOOT_IROM_CHKSUM +in rboot.h and build your roms with esptool2 using the -iromchksum option. + +Big flash support +----------------- +This only needs to be enabled if you wish to be able to memory map more than the +first 8MBit of the flash. Note you can still only map 8Mbit at a time. Use this +if you want to have multiple 1MB roms, or more smaller roms than will fit in +8Mbits. If you have a large flash but only need, for example, two 512KB roms you +do not need to enable this mode. + +Support in rBoot is enabled by uncommenting the #define BOOT_BIG_FLASH in +rboot.h. + +Thinking about your linker files is either simpler or more complicated, +depending on your usage of the flash. If you intend to use multiple 1MB roms you +will only need one linker file and you only need to link once for OTA updates. +Although when you perform an OTA update the rom will be written to a different +position on the flash, each 8Mbit of flash is mapped (separately) to 0x40200000. +So when any given rom is run the code will appear at the same place in memory +regardless of where it is on the flash. Your base address for the linker would +be 0x40202010. (Actually all but the first rom could base at 0x40200010 (because +they don't need to leave space for rBoot and config) but then you're just making +it more complicated again!) +If you wanted eight 512KB roms you would need two linker files - one for the +first half of any given 8Mbits of flash and another for the second half. Just +remember you are really laying out within a single 8MBit area, which can then be +replicated multiple times on the flash. + +Now the clever bit - rBoot needs to hijack the memory mapping code to select +which 8Mbits gets mapped. There is no API for this, but we can override the SDK +function. First we need to slightly modify the SDK library libmain.a, like so: + + xtensa-lx106-elf-objcopy -W Cache_Read_Enable_New libmain.a libmain2.a + +This produces a version of libmain with a 'weakened' Cache_Read_Enable_New +function, which we can then override with our own. Modify your Makefile to link +against the library main2 instead of main. +Next add rboot-bigflash.c (from the appcode directory) & rboot.h to your project +- this adds the replacement Cache_Read_Enable_New to your code. + +Getting gcc to apply the override correctly can be slightly tricky (I'm not sure +why, it shouldn't be). One option is to add "-u Cache_Read_Enable_New" to your +LD_FLAGS and change the order of objects on the LD command so your objects/.a +file is before the libraries. Another way that seems easier was to #include +rboot-bigflash.c into the main .c file, rather than compiling it to a separate +object file. I can't make any sense of that, but I suggest you uncomment the +message in the Cache_Read_Enable_New function when you first build with it, to +make sure you are getting your version into the rom. + +Now when rBoot starts your rom, the SDK code linked in it that normally performs +the memory mapping will delegate part of that task to rBoot code (linked in your +rom, not in rBoot itself) to choose which part of the flash to map. + +Integration into other frameworks +--------------------------------- +If you wish to integrate rBoot into a development framework (e.g. Sming) you +can set the define RBOOT_INTEGRATION and at compile time the file +rboot-integration.h will be included into the source. This should allow you to +set some platform specific options without having to modify the source of rBoot +which makes it easier to integrate and maintain. diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rom/.gitattributes b/src/mongoose-6.11/src/common/platforms/esp8266/rom/.gitattributes new file mode 100644 index 0000000..b2dd225 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rom/.gitattributes @@ -0,0 +1,2 @@ +rom.bin -nodiff +rom.elf -nodiff diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rom/disasm.sh b/src/mongoose-6.11/src/common/platforms/esp8266/rom/disasm.sh new file mode 100755 index 0000000..50c06f2 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rom/disasm.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +xtensa-lx106-elf-gcc -Wl,-N,-Ttext,0x40000000 -nostdlib rom.S -o rom.elf && \ + xtensa-lx106-elf-objdump -d rom.elf > ESP8266_ROM.txt diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.S b/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.S new file mode 100644 index 0000000..ac27596 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.S @@ -0,0 +1,101 @@ +.text +.org 0 +.globl _start + +// xtensa-esp108-elf-gcc -Wl,-N,-Ttext,0x40000000 -nostdlib rom.S -o rom.elf + +here = . +#define PROVIDE(name, addr) name = here + addr - 0x40000000 + +#include "rom_functions.S" + +PROVIDE(SPI_chip_erase, 0x40004080) +PROVIDE(SPIFlashModeConfig, 0x40004568) + +PROVIDE(_c_0x80000000, 0x400003a4) +PROVIDE(_c_0x00400000, 0x40000414) +PROVIDE(_c_0x00ffffff, 0x40000418) +PROVIDE(_p_user_start, 0x40000fb8) // 0x3fffdcd0 +PROVIDE(_c_0x60000200, 0x40000fc4) +PROVIDE(_s_ets_build, 0x40000fc8) // Jan 8 2013 +PROVIDE(_s_ets_banner, 0x40000fcc) // ets %s,rst cause:%d, boot mode:(%d,%d) +PROVIDE(_s_format_s_s, 0x40000fd0) // %s %s +PROVIDE(_s_ets_main_c, 0x40000fd4) // ets_main.c +PROVIDE(_s_user_code_done, 0x40000fe0) // user code done +PROVIDE(_s_wdt_reset, 0x40000fe4) // wdt reset +PROVIDE(_s_unk_reset, 0x40000fe8) // unknown reset +PROVIDE(_l_boot_mode_6, 0x40001044) +PROVIDE(_l_boot_mode_0_1_2_3, 0x4000104a) +PROVIDE(_l_rst_cause_ge_3, 0x40001075) +PROVIDE(_l_rst_cause_ge_3_lt_7, 0x40001084) +PROVIDE(_l_rst_cause_3_soft_wdt, 0x4000108a) +PROVIDE(_l_load_from_flash, 0x400010a8) +PROVIDE(_l_run_user_code, 0x400010be) +PROVIDE(_l_wdt_reset, 0x40001118) +PROVIDE(_l_rst_cause_1_2, 0x40001121) +PROVIDE(_l_rst_cause_unknown, 0x40001130) +PROVIDE(_l_boot_mode_7, 0x40001148) +PROVIDE(_l_boot_mode_4, 0x40001150) +PROVIDE(_c_0x60000600, 0x4000115c) +PROVIDE(_s_waiting_for_host, 0x4000119c) // waiting for host +PROVIDE(_x_unk1160, 0x40001160) +PROVIDE(_c_0x3fffa000_uart_buf, 0x400011a0) +PROVIDE(_c_0x2000, 0x400011a4) +PROVIDE(_c_0x40100000, 0x400011a8) // default user_start +PROVIDE(_x_boot2, 0x400011ac) // arg: 0x3fffdcd0 (&user_start) +PROVIDE(_l_strapping_2_eq_0, 0x400011d2) +PROVIDE(_l_124d, 0x4000124d) +PROVIDE(_l_boot_mode2_eq_2, 0x40001265) +PROVIDE(_l_boot_mode_eq_2, 0x40001268) +PROVIDE(_l_boot_mode_eq_1, 0x4000127a) +PROVIDE(_l_setup_uart, 0x40001291) +PROVIDE(_l_boot_mode_eq_3, 0x400012b0) +PROVIDE(_x_load_from_flash, 0x40001308) +PROVIDE(_c_0x00001000, 0x40001994) +PROVIDE(_c_0xffdfffff, 0x400025dc) +PROVIDE(_l_rr_not_dsleep, 0x40002624) +PROVIDE(_c_100000, 0x40002664) +PROVIDE(_c_0x3feffe00, 0x40002e5c) +PROVIDE(_p_cpu_freq, 0x40002ec8) +PROVIDE(_x_wdt_interval, 0x40002f14) // arg: 3 -> 11, 6 -> 12, 12 -> 13 +PROVIDE(_p_wdt_cfg, 0x40002f30) +PROVIDE(_wdt_soft_timer_fn2, 0x40002f3c) +PROVIDE(_l_wdt_soft_timer_fn2_exit, 0x40002f59) +PROVIDE(_l_wdt_soft_timer_fn2_feed, 0x40002f60) +PROVIDE(_l_wdt_soft_timer_fn2_mode2_stage1_feed, 0x40002f74) +PROVIDE(_wdt_soft_timer_fn, 0x40002f88) +PROVIDE(_p_wdt_soft_timer_fn, 0x40002f98) +PROVIDE(_p_wdt_soft_timer, 0x40002f9c) // 0x3fffdde0 +PROVIDE(_l_skip_wdt_imask, 0x40002fc4) +PROVIDE(_l_wdt_enable_and_exit, 0x40002fea) +PROVIDE(_l_wdt_mode_1, 0x4000309c) +PROVIDE(_l_wdt_mode_2_4, 0x4000300a) +PROVIDE(_l_wdt_mode_3, 0x40003060) +PROVIDE(_l_recv_req, 0x400033a1) +PROVIDE(_l_send_response, 0x400033be) +PROVIDE(_l_send_resp_pkt, 0x400033da) +PROVIDE(_l_process_req, 0x40003424) +PROVIDE(_l_34a3, 0x400034a3) +PROVIDE(_l_34b9, 0x400034b9) +PROVIDE(_c_0x00001800, 0x40003534) +PROVIDE(_c_0x60000a00, 0x40003f54) +PROVIDE(_c_0x01000000, 0x400040bc) +PROVIDE(_x_SPI_erase_sector, 0x400040c0) +PROVIDE(_l_SPI_erase_sector_align_ok, 0x400040dc) +PROVIDE(_c_0x00800000, 0x4000411c) +PROVIDE(_x_SPI_erase_block, 0x40004120) +PROVIDE(_c_0x20000000, 0x4000416c) +PROVIDE(_c_0x08000000, 0x400043c4) +PROVIDE(_c_0x04000000, 0x400043fc) +PROVIDE(_c_0x40000000, 0x40004438) +PROVIDE(_p_flashchip, 0x40004874) +PROVIDE(_s_bootup, 0x400054cc) // bootup , addr 0x%08x +PROVIDE(_p_sip_ctx, 0x40005130) +PROVIDE(_l_sip_cmd_out, 0x4000550a) +PROVIDE(_c_0x00010000, 0x40005de0) + +.text + +_start: +.incbin "rom.bin" +_end: diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.bin b/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.bin new file mode 100644 index 0000000000000000000000000000000000000000..fe4d1956655081254d74cee85fc9341e84765e60 GIT binary patch literal 65536 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

" + "\n", + href.p, slash, path, slash, mod, is_dir ? -1 : fsize, + size); + free((void *) href.p); +} + +static void mg_scan_directory(struct mg_connection *nc, const char *dir, + const struct mg_serve_http_opts *opts, + void (*func)(struct mg_connection *, const char *, + cs_stat_t *)) { + char path[MG_MAX_PATH]; + cs_stat_t st; + struct dirent *dp; + DIR *dirp; + + LOG(LL_DEBUG, ("%p [%s]", nc, dir)); + if ((dirp = (opendir(dir))) != NULL) { + while ((dp = readdir(dirp)) != NULL) { + /* Do not show current dir and hidden files */ + if (mg_is_file_hidden((const char *) dp->d_name, opts, 1)) { + continue; + } + snprintf(path, sizeof(path), "%s/%s", dir, dp->d_name); + if (mg_stat(path, &st) == 0) { + func(nc, (const char *) dp->d_name, &st); + } + } + closedir(dirp); + } else { + LOG(LL_DEBUG, ("%p opendir(%s) -> %d", nc, dir, mg_get_errno())); + } +} + +static void mg_send_directory_listing(struct mg_connection *nc, const char *dir, + struct http_message *hm, + struct mg_serve_http_opts *opts) { + static const char *sort_js_code = + ""; + + mg_send_response_line(nc, 200, opts->extra_headers); + mg_printf(nc, "%s: %s\r\n%s: %s\r\n\r\n", "Transfer-Encoding", "chunked", + "Content-Type", "text/html; charset=utf-8"); + + mg_printf_http_chunk( + nc, + "Index of %.*s%s%s" + "\n" + "

Index of %.*s

\n
NameModifiedSize

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 literal 0 HcmV?d00001 diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.elf b/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.elf new file mode 100755 index 0000000000000000000000000000000000000000..6a21116b321780589032452c7854e66d5b333804 GIT binary patch literal 79984 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 $@" + @[ -d $(BUILD_DIR) ] || mkdir $(BUILD_DIR) + @docker run --rm -i -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \ + "cd /src/common/platforms/esp8266/stubs && \ + $(XT_CC) -std=c99 -Wall -Werror -Os -DESP8266 \ + -mtext-section-literals -mlongcalls -nostdlib -fno-builtin \ + -I. -I/src/common/platforms/esp \ + -I/opt/Espressif/ESP8266_SDK \ + -Wl,-static -ffunction-sections -Wl,--gc-sections \ + -Tstub.ld $(CFLAGS) -o $@ $^" + +wrap: $(STUB_JSON) + +$(STUB_JSON): $(STUB_ELF) $(COMMON_STUB_DIR)/esptool.py + @echo " WRAP $< -> $@" + @docker run --rm -i -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \ + "cd /src/common/platforms/esp8266/stubs && \ + $(COMMON_STUB_DIR)/esptool.py wrap_stub $<" > $@ + +run: $(STUB_JSON) + @echo " RUN $< $(PARAMS) -> $(PORT)" + @docker run --rm -i --privileged -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \ + "cd /src/common/platforms/esp8266/stubs && \ + $(COMMON_STUB_DIR)/esptool.py --port $(PORT) run_stub $< $(PARAMS)" + +clean: + @rm -rf $(BUILD_DIR) diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/README.md b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/README.md new file mode 100644 index 0000000..9265771 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/README.md @@ -0,0 +1,13 @@ +This is a ESP boot loader stub development environment. + +Code produced in this environment can be loaded and executed +in the bootloader environment. Usually it is used to implement +functionality not found in the bootloader. + +Stubs can be executed using the `run_stub` command of the modified esptool.py provided. +`wrap_stub` produces a JSON represenattion of the stub that can later be reused +or built into other tools. + +Example usage: + $ make run STUB=stub_flash_size.c PORT=/dev/ttyUSB0 + $ make run STUB=stub_md5.c PORT=/dev/ttyUSB0 PARAMS="0x11000 10000 1" diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/chop80.py b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/chop80.py new file mode 100755 index 0000000..a488ba2 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/chop80.py @@ -0,0 +1,14 @@ +#!/usr/bin/python + +import sys + +for line in open(sys.argv[1]): + i = 0 + while i < len(line): + n = len(line) - i + if n > 80: n = 80 + l = line[i:i+n] + if n == 80: + l += '\\' + print l + i += n diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/rom_functions.h b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/rom_functions.h new file mode 100644 index 0000000..82ea746 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/rom_functions.h @@ -0,0 +1,91 @@ +#ifndef CS_COMMON_PLATFORMS_ESP8266_STUBS_ROM_FUNCTIONS_H_ +#define CS_COMMON_PLATFORMS_ESP8266_STUBS_ROM_FUNCTIONS_H_ + +#include + +#include "/opt/Espressif/ESP8266_NONOS_SDK/include/c_types.h" +#include "/opt/Espressif/ESP8266_NONOS_SDK/include/spi_flash.h" + +int uart_rx_one_char(uint8_t *ch); +uint8_t uart_rx_one_char_block(); +int uart_tx_one_char(char ch); +void uart_div_modify(uint32_t uart_no, uint32_t baud_div); + +int SendMsg(uint8_t *msg, uint8_t size); +int send_packet(uint8_t *packet, uint32_t size); +// recv_packet depends on global UartDev, better to avoid it. +// uint32_t recv_packet(void *packet, uint32_t len, uint8_t no_sync); + +void _putc1(char *ch); + +void ets_delay_us(uint32_t us); + +uint32_t SPILock(); +uint32_t SPIUnlock(); +uint32_t SPIRead(uint32_t addr, void *dst, uint32_t size); +uint32_t SPIWrite(uint32_t addr, const uint32_t *src, uint32_t size); +uint32_t SPIEraseChip(); +uint32_t SPIEraseBlock(uint32_t block_num); +uint32_t SPIEraseSector(uint32_t sector_num); + +extern SpiFlashChip *flashchip; +uint32_t Wait_SPI_Idle(SpiFlashChip *spi); +uint32_t SPI_chip_erase(SpiFlashChip *spi); +uint32_t SPI_read_status(SpiFlashChip *spi); +uint32_t SPI_write_enable(SpiFlashChip *spi); + +void spi_flash_attach(); + +/* ESP32 API compatibility */ +#define esp_rom_spiflash_unlock SPIUnlock +#define esp_rom_spiflash_erase_sector SPIEraseSector +#define esp_rom_spiflash_erase_block SPIEraseBlock +#define esp_rom_spiflash_erase_chip SPIEraseChip +#define esp_rom_spiflash_read SPIRead +#define esp_rom_spiflash_write SPIWrite +#define esp_rom_spiflash_config_param SPIParamCfg + +void SelectSpiFunction(); +void SPIFlashModeConfig(uint32_t a, uint32_t b); +void SPIReadModeCnfig(uint32_t a); +uint32_t SPIParamCfg(uint32_t deviceId, uint32_t chip_size, uint32_t block_size, + uint32_t sector_size, uint32_t page_size, + uint32_t status_mask); + +void Cache_Read_Disable(); + +void ets_delay_us(uint32_t delay_micros); + +void ets_isr_mask(uint32_t ints); +void ets_isr_unmask(uint32_t ints); +typedef void (*int_handler_t)(void *arg); + +void ets_intr_lock(); +void ets_intr_unlock(); +void ets_set_user_start(void (*user_start_fn)()); + +uint32_t rtc_get_reset_reason(); +void software_reset(); +void rom_phy_reset_req(); + +void uart_rx_intr_handler(void *arg); + +void _ResetVector(); + +/* Crypto functions are from wpa_supplicant. */ +int md5_vector(uint32_t num_msgs, const uint8_t *msgs[], + const uint32_t *msg_lens, uint8_t *digest); +int sha1_vector(uint32_t num_msgs, const uint8_t *msgs[], + const uint32_t *msg_lens, uint8_t *digest); + +struct MD5Context { + uint32_t buf[4]; + uint32_t bits[2]; + uint8_t in[64]; +}; + +void MD5Init(struct MD5Context *ctx); +void MD5Update(struct MD5Context *ctx, void *buf, uint32_t len); +void MD5Final(uint8_t digest[16], struct MD5Context *ctx); + +#endif /* CS_COMMON_PLATFORMS_ESP8266_STUBS_ROM_FUNCTIONS_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/stub.ld b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/stub.ld new file mode 100644 index 0000000..f54a4f8 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/stub.ld @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015 Cesanta Software Limited + * All rights reserved + */ + +MEMORY { + iram : org = 0x40100000, len = 0x8000 + dram : org = 0x3FFE8000, len = 0x14000 +} + +ENTRY(stub_main) + +SECTIONS { + .params 0x40100000 : { + _params_start = ABSOLUTE(.); + *(.params) + _params_end = ABSOLUTE(.); + } > iram + + .text : ALIGN(4) { + _code_start = ABSOLUTE(.); + *(.literal) + *(.text .text.*) + } > iram + + .bss : ALIGN(4) { + _bss_start = ABSOLUTE(.); + *(.bss) + _bss_end = ABSOLUTE(.); + } > dram + + .data : ALIGN(4) { + _data_start = ABSOLUTE(.); + *(.data) + *(.rodata .rodata.*) + } > dram +} + +INCLUDE "eagle.rom.addr.v6.ld" + +PROVIDE(SPIFlashModeConfig = 0x40004568); +PROVIDE(SPI_erase_sector = 0x400040c0); +PROVIDE(SPI_erase_block = 0x40004120); diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/stub_hello.c b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/stub_hello.c new file mode 100644 index 0000000..10a827d --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/stub_hello.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2015 Cesanta Software Limited + * All rights reserved + * + * + * Stub template. + */ + +#include +#include "rom_functions.h" + +/* Define the args vector and put it into the ".params" section. */ +uint32_t params[3] __attribute__((section(".params"))); + +/* Define a function called stub_main. Do not return or reboot. + * Use send_packet to communicate to the host. */ + +const char *hello = "Hello"; + +void stub_main(void) { + send_packet(hello, 5); + _ResetVector(); +} diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/uart.c b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/uart.c new file mode 100644 index 0000000..5651bd8 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/uart.c @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#include "uart.h" +#include "ets_sys.h" + +#define UART_CLKDIV_26MHZ(B) (52000000 + B / 2) / B + +void set_baud_rate(uint32_t uart_no, uint32_t baud_rate) { + uint32_t div = UART_CLKDIV_26MHZ(baud_rate); + WRITE_PERI_REG(UART_CLKDIV_REG(uart_no), div & 0xfffff); +} diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/uart.h b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/uart.h new file mode 100644 index 0000000..fc321c2 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/uart.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_ESP8266_STUBS_UART_H_ +#define CS_COMMON_PLATFORMS_ESP8266_STUBS_UART_H_ + +#include + +void set_baud_rate(uint32_t uart_no, uint32_t baud_rate); + +#define REG_UART_BASE(i) (0x60000000 + (i) *0xf00) +#define UART_FIFO_REG(i) (REG_UART_BASE(i) + 0x0) +#define UART_CONF1_REG(i) (REG_UART_BASE(i) + 0x24) +#define UART_RX_TOUT_EN (BIT(31)) +#define UART_RX_TOUT_THRHD_S 24 +#define UART_RXFIFO_FULL_THRHD_S 0 +#define UART_INT_ST_REG(i) (REG_UART_BASE(i) + 0x8) +#define UART_INT_ENA_REG(i) (REG_UART_BASE(i) + 0xC) +#define UART_RXFIFO_TOUT_INT_ENA (BIT(8)) +#define UART_RXFIFO_FULL_INT_ENA (BIT(0)) + +#define UART_INT_CLR_REG(i) (REG_UART_BASE(i) + 0x10) + +#define UART_CLKDIV_REG(i) (REG_UART_BASE(i) + 0x14) + +#define UART_STATUS_REG(i) (REG_UART_BASE(i) + 0x1C) + +#define UART_CONF0_REG(i) (REG_UART_BASE(i) + 0x20) +#define UART_RXFIFO_RST (BIT(17)) + +#define ETS_UART0_INUM ETS_UART_INUM + +#endif /* CS_COMMON_PLATFORMS_ESP8266_STUBS_UART_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/tools/malloc_analyzer.py b/src/mongoose-6.11/src/common/platforms/esp8266/tools/malloc_analyzer.py new file mode 100644 index 0000000..91d4d0c --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/tools/malloc_analyzer.py @@ -0,0 +1,23 @@ +#!/usr/bin/python + +# Analyzers output of firmware compiler with -DESP_ENABLE_MALLOC_TRACES + +import re +import sys + +allocs = {} + +for l in sys.stdin: + m = re.match(r'(ca|ma|re|fr) (\S+)\s*(\S*)\s*(\S*)', l) + if not m: continue + op = m.group(1) + if op in ('ca', 'ma'): + allocs[m.group(2)] = long(m.group(3)) + else: + if m.group(2) in allocs: + del allocs[m.group(2)] + if op == 're': + allocs[m.group(3)] = long(m.group(4)) + +for k, v in sorted(allocs.iteritems()): + print k, v diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/uart_register.h b/src/mongoose-6.11/src/common/platforms/esp8266/uart_register.h new file mode 100644 index 0000000..e8fcda7 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/esp8266/uart_register.h @@ -0,0 +1,136 @@ +/* clang-format off */ +/* + * Copyright (c) 2010 - 2011 Espressif System + * + */ + +#ifndef CS_COMMON_PLATFORMS_ESP8266_UART_REGISTER_H_ +#define CS_COMMON_PLATFORMS_ESP8266_UART_REGISTER_H_ + +#define REG_UART_BASE(i) (0x60000000 + (i)*0xf00) +//version value:32'h062000 + +#define UART_FIFO(i) (REG_UART_BASE(i) + 0x0) +#define UART_RXFIFO_RD_BYTE 0x000000FF +#define UART_RXFIFO_RD_BYTE_S 0 + +#define UART_INT_RAW(i) (REG_UART_BASE(i) + 0x4) +#define UART_RXFIFO_TOUT_INT_RAW (BIT(8)) +#define UART_BRK_DET_INT_RAW (BIT(7)) +#define UART_CTS_CHG_INT_RAW (BIT(6)) +#define UART_DSR_CHG_INT_RAW (BIT(5)) +#define UART_RXFIFO_OVF_INT_RAW (BIT(4)) +#define UART_FRM_ERR_INT_RAW (BIT(3)) +#define UART_PARITY_ERR_INT_RAW (BIT(2)) +#define UART_TXFIFO_EMPTY_INT_RAW (BIT(1)) +#define UART_RXFIFO_FULL_INT_RAW (BIT(0)) + +#define UART_INT_ST(i) (REG_UART_BASE(i) + 0x8) +#define UART_RXFIFO_TOUT_INT_ST (BIT(8)) +#define UART_BRK_DET_INT_ST (BIT(7)) +#define UART_CTS_CHG_INT_ST (BIT(6)) +#define UART_DSR_CHG_INT_ST (BIT(5)) +#define UART_RXFIFO_OVF_INT_ST (BIT(4)) +#define UART_FRM_ERR_INT_ST (BIT(3)) +#define UART_PARITY_ERR_INT_ST (BIT(2)) +#define UART_TXFIFO_EMPTY_INT_ST (BIT(1)) +#define UART_RXFIFO_FULL_INT_ST (BIT(0)) + +#define UART_INT_ENA(i) (REG_UART_BASE(i) + 0xC) +#define UART_RXFIFO_TOUT_INT_ENA (BIT(8)) +#define UART_BRK_DET_INT_ENA (BIT(7)) +#define UART_CTS_CHG_INT_ENA (BIT(6)) +#define UART_DSR_CHG_INT_ENA (BIT(5)) +#define UART_RXFIFO_OVF_INT_ENA (BIT(4)) +#define UART_FRM_ERR_INT_ENA (BIT(3)) +#define UART_PARITY_ERR_INT_ENA (BIT(2)) +#define UART_TXFIFO_EMPTY_INT_ENA (BIT(1)) +#define UART_RXFIFO_FULL_INT_ENA (BIT(0)) + +#define UART_INT_CLR(i) (REG_UART_BASE(i) + 0x10) +#define UART_RXFIFO_TOUT_INT_CLR (BIT(8)) +#define UART_BRK_DET_INT_CLR (BIT(7)) +#define UART_CTS_CHG_INT_CLR (BIT(6)) +#define UART_DSR_CHG_INT_CLR (BIT(5)) +#define UART_RXFIFO_OVF_INT_CLR (BIT(4)) +#define UART_FRM_ERR_INT_CLR (BIT(3)) +#define UART_PARITY_ERR_INT_CLR (BIT(2)) +#define UART_TXFIFO_EMPTY_INT_CLR (BIT(1)) +#define UART_RXFIFO_FULL_INT_CLR (BIT(0)) + +#define UART_CLKDIV(i) (REG_UART_BASE(i) + 0x14) +#define UART_CLKDIV_CNT 0x000FFFFF +#define UART_CLKDIV_S 0 + +#define UART_AUTOBAUD(i) (REG_UART_BASE(i) + 0x18) +#define UART_GLITCH_FILT 0x000000FF +#define UART_GLITCH_FILT_S 8 +#define UART_AUTOBAUD_EN (BIT(0)) + +#define UART_STATUS(i) (REG_UART_BASE(i) + 0x1C) +#define UART_TXD (BIT(31)) +#define UART_RTSN (BIT(30)) +#define UART_DTRN (BIT(29)) +#define UART_TXFIFO_CNT 0x000000FF +#define UART_TXFIFO_CNT_S 16 +#define UART_RXD (BIT(15)) +#define UART_CTSN (BIT(14)) +#define UART_DSRN (BIT(13)) +#define UART_RXFIFO_CNT 0x000000FF +#define UART_RXFIFO_CNT_S 0 + +#define UART_CONF0(i) (REG_UART_BASE(i) + 0x20) +#define UART_DTR_INV (BIT(24)) +#define UART_RTS_INV (BIT(23)) +#define UART_TXD_INV (BIT(22)) +#define UART_DSR_INV (BIT(21)) +#define UART_CTS_INV (BIT(20)) +#define UART_RXD_INV (BIT(19)) +#define UART_TXFIFO_RST (BIT(18)) +#define UART_RXFIFO_RST (BIT(17)) +#define UART_IRDA_EN (BIT(16)) +#define UART_TX_FLOW_EN (BIT(15)) +#define UART_LOOPBACK (BIT(14)) +#define UART_IRDA_RX_INV (BIT(13)) +#define UART_IRDA_TX_INV (BIT(12)) +#define UART_IRDA_WCTL (BIT(11)) +#define UART_IRDA_TX_EN (BIT(10)) +#define UART_IRDA_DPLX (BIT(9)) +#define UART_TXD_BRK (BIT(8)) +#define UART_SW_DTR (BIT(7)) +#define UART_SW_RTS (BIT(6)) +#define UART_STOP_BIT_NUM 0x00000003 +#define UART_STOP_BIT_NUM_S 4 +#define UART_BIT_NUM 0x00000003 +#define UART_BIT_NUM_S 2 +#define UART_PARITY_EN (BIT(1)) +#define UART_PARITY (BIT(0)) + +#define UART_CONF1(i) (REG_UART_BASE(i) + 0x24) +#define UART_RX_TOUT_EN (BIT(31)) +#define UART_RX_TOUT_THRHD 0x0000007F +#define UART_RX_TOUT_THRHD_S 24 +#define UART_RX_FLOW_EN (BIT(23)) +#define UART_RX_FLOW_THRHD 0x0000007F +#define UART_RX_FLOW_THRHD_S 16 +#define UART_TXFIFO_EMPTY_THRHD 0x0000007F +#define UART_TXFIFO_EMPTY_THRHD_S 8 +#define UART_RXFIFO_FULL_THRHD 0x0000007F +#define UART_RXFIFO_FULL_THRHD_S 0 + +#define UART_LOWPULSE(i) (REG_UART_BASE(i) + 0x28) +#define UART_LOWPULSE_MIN_CNT 0x000FFFFF +#define UART_LOWPULSE_MIN_CNT_S 0 + +#define UART_HIGHPULSE(i) (REG_UART_BASE(i) + 0x2C) +#define UART_HIGHPULSE_MIN_CNT 0x000FFFFF +#define UART_HIGHPULSE_MIN_CNT_S 0 + +#define UART_PULSE_NUM(i) (REG_UART_BASE(i) + 0x30) +#define UART_PULSE_NUM_CNT 0x0003FF +#define UART_PULSE_NUM_CNT_S 0 + +#define UART_DATE(i) (REG_UART_BASE(i) + 0x78) +#define UART_ID(i) (REG_UART_BASE(i) + 0x7C) + +#endif /* CS_COMMON_PLATFORMS_ESP8266_UART_REGISTER_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip.h b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip.h new file mode 100644 index 0000000..1a772aa --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_ +#define CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_ + +#ifndef MG_LWIP +#define MG_LWIP 0 +#endif + +#if MG_LWIP + +/* + * When compiling for nRF5x chips with arm-none-eabi-gcc, it has BYTE_ORDER + * already defined, so in order to avoid warnings in lwip, we have to undefine + * it. + * + * TODO: Check if in the future versions of nRF5 SDK that changes. + * Current version of nRF51 SDK: 0.8.0 + * nRF5 SDK: 0.9.0 + */ +#if CS_PLATFORM == CS_P_NRF51 || CS_PLATFORM == CS_P_NRF52 +#undef BYTE_ORDER +#endif + +#include +#include +#include +#include +#include +#include + +#ifndef LWIP_PROVIDE_ERRNO +#include +#endif + +#if LWIP_SOCKET +#include +#else +/* We really need the definitions from sockets.h. */ +#undef LWIP_SOCKET +#define LWIP_SOCKET 1 +#include +#undef LWIP_SOCKET +#define LWIP_SOCKET 0 +#endif + +#define INVALID_SOCKET (-1) +#define SOMAXCONN 10 +typedef int sock_t; + +#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL +struct mg_mgr; +struct mg_connection; +uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr); +void mg_lwip_set_keepalive_params(struct mg_connection *nc, int idle, + int interval, int count); +#endif + +/* For older version of LWIP */ +#ifndef ipX_2_ip +#define ipX_2_ip(x) (x) +#endif + +#endif /* MG_LWIP */ + +#endif /* CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_ev_mgr.c b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_ev_mgr.c new file mode 100644 index 0000000..5eea26f --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_ev_mgr.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL + +#ifndef MG_SIG_QUEUE_LEN +#define MG_SIG_QUEUE_LEN 32 +#endif + +struct mg_ev_mgr_lwip_signal { + int sig; + struct mg_connection *nc; +}; + +struct mg_ev_mgr_lwip_data { + struct mg_ev_mgr_lwip_signal sig_queue[MG_SIG_QUEUE_LEN]; + int sig_queue_len; + int start_index; +}; + +void mg_lwip_post_signal(enum mg_sig_type sig, struct mg_connection *nc) { + struct mg_ev_mgr_lwip_data *md = + (struct mg_ev_mgr_lwip_data *) nc->iface->data; + mgos_lock(); + if (md->sig_queue_len >= MG_SIG_QUEUE_LEN) { + mgos_unlock(); + return; + } + int end_index = (md->start_index + md->sig_queue_len) % MG_SIG_QUEUE_LEN; + md->sig_queue[end_index].sig = sig; + md->sig_queue[end_index].nc = nc; + md->sig_queue_len++; + mg_lwip_mgr_schedule_poll(nc->mgr); + mgos_unlock(); +} + +void mg_ev_mgr_lwip_process_signals(struct mg_mgr *mgr) { + struct mg_ev_mgr_lwip_data *md = + (struct mg_ev_mgr_lwip_data *) mgr->ifaces[MG_MAIN_IFACE]->data; + while (md->sig_queue_len > 0) { + mgos_lock(); + int sig = md->sig_queue[md->start_index].sig; + struct mg_connection *nc = md->sig_queue[md->start_index].nc; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + md->start_index = (md->start_index + 1) % MG_SIG_QUEUE_LEN; + md->sig_queue_len--; + mgos_unlock(); + if (nc->iface == NULL || nc->mgr == NULL) continue; + switch (sig) { + case MG_SIG_CONNECT_RESULT: { +#if MG_ENABLE_SSL + if (cs->err == 0 && (nc->flags & MG_F_SSL) && + !(nc->flags & MG_F_SSL_HANDSHAKE_DONE)) { + mg_lwip_ssl_do_hs(nc); + } else +#endif + { + mg_if_connect_cb(nc, cs->err); + } + break; + } + case MG_SIG_CLOSE_CONN: { + nc->flags |= MG_F_SEND_AND_CLOSE; + mg_close_conn(nc); + break; + } + case MG_SIG_RECV: { + cs->recv_pending = 0; + if (nc->flags & MG_F_UDP) { + mg_lwip_handle_recv_udp(nc); + } else { + mg_lwip_handle_recv_tcp(nc); + } + break; + } + case MG_SIG_TOMBSTONE: { + break; + } + case MG_SIG_ACCEPT: { + mg_lwip_handle_accept(nc); + break; + } + } + } +} + +void mg_lwip_if_init(struct mg_iface *iface) { + LOG(LL_INFO, ("%p Mongoose init", iface)); + iface->data = MG_CALLOC(1, sizeof(struct mg_ev_mgr_lwip_data)); +} + +void mg_lwip_if_free(struct mg_iface *iface) { + MG_FREE(iface->data); + iface->data = NULL; +} + +void mg_lwip_if_add_conn(struct mg_connection *nc) { + (void) nc; +} + +void mg_lwip_if_remove_conn(struct mg_connection *nc) { + struct mg_ev_mgr_lwip_data *md = + (struct mg_ev_mgr_lwip_data *) nc->iface->data; + /* Walk the queue and null-out further signals for this conn. */ + for (int i = 0; i < MG_SIG_QUEUE_LEN; i++) { + if (md->sig_queue[i].nc == nc) { + md->sig_queue[i].sig = MG_SIG_TOMBSTONE; + } + } +} + +time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms) { + struct mg_mgr *mgr = iface->mgr; + int n = 0; + double now = mg_time(); + struct mg_connection *nc, *tmp; + double min_timer = 0; + int num_timers = 0; +#if 0 + DBG(("begin poll @%u", (unsigned int) (now * 1000))); +#endif + mg_ev_mgr_lwip_process_signals(mgr); + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + tmp = nc->next; + n++; + if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || + ((nc->flags & MG_F_SEND_AND_CLOSE) && (nc->flags & MG_F_UDP) && + (nc->send_mbuf.len == 0))) { + mg_close_conn(nc); + continue; + } + mg_if_poll(nc, now); + mg_if_timer(nc, now); +#if MG_ENABLE_SSL + if ((nc->flags & MG_F_SSL) && cs != NULL && cs->pcb.tcp != NULL && + cs->pcb.tcp->state == ESTABLISHED) { + if (((nc->flags & MG_F_WANT_WRITE) || + ((nc->send_mbuf.len > 0) && + (nc->flags & MG_F_SSL_HANDSHAKE_DONE))) && + cs->pcb.tcp->snd_buf > 0) { + /* Can write more. */ + if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { + if (!(nc->flags & MG_F_CONNECTING)) mg_lwip_ssl_send(nc); + } else { + mg_lwip_ssl_do_hs(nc); + } + } + if (cs->rx_chain != NULL || (nc->flags & MG_F_WANT_READ)) { + if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { + if (!(nc->flags & MG_F_CONNECTING)) mg_lwip_ssl_recv(nc); + } else { + mg_lwip_ssl_do_hs(nc); + } + } + } else +#endif /* MG_ENABLE_SSL */ + { + if (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING)) { + mg_lwip_send_more(nc); + } + } + if (nc->sock != INVALID_SOCKET && + !(nc->flags & (MG_F_UDP | MG_F_LISTENING)) && cs->pcb.tcp != NULL && + cs->pcb.tcp->unsent != NULL) { + tcpip_callback(tcp_output_tcpip, cs->pcb.tcp); + } + if (nc->ev_timer_time > 0) { + if (num_timers == 0 || nc->ev_timer_time < min_timer) { + min_timer = nc->ev_timer_time; + } + num_timers++; + } + + if (nc->sock != INVALID_SOCKET) { + /* Try to consume data from cs->rx_chain */ + mg_lwip_consume_rx_chain_tcp(nc); + + /* + * If the connection is about to close, and rx_chain is finally empty, + * send the MG_SIG_CLOSE_CONN signal + */ + if (cs->draining_rx_chain && cs->rx_chain == NULL) { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } + } + } +#if 0 + DBG(("end poll @%u, %d conns, %d timers (min %u), next in %d ms", + (unsigned int) (now * 1000), n, num_timers, + (unsigned int) (min_timer * 1000), timeout_ms)); +#endif + (void) timeout_ms; + return now; +} + +uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr) { + struct mg_connection *nc; + double now; + double min_timer = 0; + int num_timers = 0; + mg_ev_mgr_lwip_process_signals(mgr); + for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (nc->ev_timer_time > 0) { + if (num_timers == 0 || nc->ev_timer_time < min_timer) { + min_timer = nc->ev_timer_time; + } + num_timers++; + } + if (nc->send_mbuf.len > 0 +#if MG_ENABLE_SSL + || (nc->flags & MG_F_WANT_WRITE) +#endif + ) { + int can_send = 0; + /* We have stuff to send, but can we? */ + if (nc->flags & MG_F_UDP) { + /* UDP is always ready for sending. */ + can_send = (cs->pcb.udp != NULL); + } else { + can_send = (cs->pcb.tcp != NULL && cs->pcb.tcp->snd_buf > 0); + } + /* We want and can send, request a poll immediately. */ + if (can_send) return 0; + } + } + uint32_t timeout_ms = ~0; + now = mg_time(); + if (num_timers > 0) { + /* If we have a timer that is past due, do a poll ASAP. */ + if (min_timer < now) return 0; + double timer_timeout_ms = (min_timer - now) * 1000 + 1 /* rounding */; + if (timer_timeout_ms < timeout_ms) { + timeout_ms = timer_timeout_ms; + } + } + return timeout_ms; +} + +#endif /* MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL */ diff --git a/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_net_if.c b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_net_if.c new file mode 100644 index 0000000..460f8f5 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_net_if.c @@ -0,0 +1,779 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_NET_IF_LWIP_LOW_LEVEL + +#include "common/mg_mem.h" + +#include +#include +#include +#include +#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105 +#include /* For tcp_seg */ +#else +#include +#endif +#include + +#include "common/cs_dbg.h" + +/* + * Newest versions of LWIP have ip_2_ip4, older have ipX_2_ip, + * even older have nothing. + */ +#ifndef ip_2_ip4 +#ifdef ipX_2_ip +#define ip_2_ip4(addr) ipX_2_ip(addr) +#else +#define ip_2_ip4(addr) (addr) +#endif +#endif + +/* + * Depending on whether Mongoose is compiled with ipv6 support, use right + * lwip functions + */ +#if MG_ENABLE_IPV6 +#define TCP_NEW tcp_new_ip6 +#define TCP_BIND tcp_bind_ip6 +#define UDP_BIND udp_bind_ip6 +#define IPADDR_NTOA(x) ip6addr_ntoa((const ip6_addr_t *)(x)) +#define SET_ADDR(dst, src) \ + memcpy((dst)->sin6.sin6_addr.s6_addr, (src)->ip6.addr, \ + sizeof((dst)->sin6.sin6_addr.s6_addr)) +#else +#define TCP_NEW tcp_new +#define TCP_BIND tcp_bind +#define UDP_BIND udp_bind +#define IPADDR_NTOA ipaddr_ntoa +#define SET_ADDR(dst, src) (dst)->sin.sin_addr.s_addr = ip_2_ip4(src)->addr +#endif + +#if NO_SYS +#define tcpip_callback(fn, arg) (fn)(arg) +typedef void (*tcpip_callback_fn)(void *arg); +#endif + +void mg_lwip_ssl_do_hs(struct mg_connection *nc); +void mg_lwip_ssl_send(struct mg_connection *nc); +void mg_lwip_ssl_recv(struct mg_connection *nc); + +void mg_lwip_if_init(struct mg_iface *iface); +void mg_lwip_if_free(struct mg_iface *iface); +void mg_lwip_if_add_conn(struct mg_connection *nc); +void mg_lwip_if_remove_conn(struct mg_connection *nc); +time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms); + +#if defined(RTOS_SDK) || defined(ESP_PLATFORM) +extern void mgos_lock(); +extern void mgos_unlock(); +#else +#define mgos_lock() +#define mgos_unlock() +#endif + +static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p); + +#if LWIP_TCP_KEEPALIVE +void mg_lwip_set_keepalive_params(struct mg_connection *nc, int idle, + int interval, int count) { + if (nc->sock == INVALID_SOCKET || nc->flags & MG_F_UDP) { + return; + } + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct tcp_pcb *tpcb = cs->pcb.tcp; + if (idle > 0 && interval > 0 && count > 0) { + tpcb->keep_idle = idle * 1000; + tpcb->keep_intvl = interval * 1000; + tpcb->keep_cnt = count; + tpcb->so_options |= SOF_KEEPALIVE; + } else { + tpcb->so_options &= ~SOF_KEEPALIVE; + } +} +#elif !defined(MG_NO_LWIP_TCP_KEEPALIVE) +#warning LWIP TCP keepalive is disabled. Please consider enabling it. +#endif /* LWIP_TCP_KEEPALIVE */ + +static err_t mg_lwip_tcp_conn_cb(void *arg, struct tcp_pcb *tpcb, err_t err) { + struct mg_connection *nc = (struct mg_connection *) arg; + DBG(("%p connect to %s:%u = %d", nc, IPADDR_NTOA(ipX_2_ip(&tpcb->remote_ip)), + tpcb->remote_port, err)); + if (nc == NULL) { + tcp_abort(tpcb); + return ERR_ARG; + } + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + cs->err = err; +#if LWIP_TCP_KEEPALIVE + if (err == 0) mg_lwip_set_keepalive_params(nc, 60, 10, 6); +#endif + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); + return ERR_OK; +} + +static void mg_lwip_tcp_error_cb(void *arg, err_t err) { + struct mg_connection *nc = (struct mg_connection *) arg; + DBG(("%p conn error %d", nc, err)); + if (nc == NULL) return; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + cs->pcb.tcp = NULL; /* Has already been deallocated */ + if (nc->flags & MG_F_CONNECTING) { + cs->err = err; + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); + } else { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } +} + +static err_t mg_lwip_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, + struct pbuf *p, err_t err) { + struct mg_connection *nc = (struct mg_connection *) arg; + DBG(("%p %p %u %d", nc, tpcb, (p != NULL ? p->tot_len : 0), err)); + if (p == NULL) { + if (nc != NULL && !(nc->flags & MG_F_CLOSE_IMMEDIATELY)) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (cs->rx_chain != NULL) { + /* + * rx_chain still contains non-consumed data, don't close the + * connection + */ + cs->draining_rx_chain = 1; + } else { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } + } else { + /* Tombstoned connection, do nothing. */ + } + return ERR_OK; + } else if (nc == NULL) { + tcp_abort(tpcb); + return ERR_ARG; + } + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + /* + * If we get a chain of more than one segment at once, we need to bump + * refcount on the subsequent bufs to make them independent. + */ + if (p->next != NULL) { + struct pbuf *q = p->next; + for (; q != NULL; q = q->next) pbuf_ref(q); + } + mgos_lock(); + if (cs->rx_chain == NULL) { + cs->rx_offset = 0; + } else if (pbuf_clen(cs->rx_chain) >= 4) { + /* ESP SDK has a limited pool of 5 pbufs. We must not hog them all or RX + * will be completely blocked. We already have at least 4 in the chain, + * this one is, so we have to make a copy and release this one. */ + struct pbuf *np = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if (np != NULL) { + pbuf_copy(np, p); + pbuf_free(p); + p = np; + } + } + mgos_unlock(); + mg_lwip_recv_common(nc, p); + return ERR_OK; +} + +static void mg_lwip_consume_rx_chain_tcp(struct mg_connection *nc) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (cs->rx_chain == NULL) return; +#if MG_ENABLE_SSL + if (nc->flags & MG_F_SSL) { + if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { + mg_lwip_ssl_recv(nc); + } else { + mg_lwip_ssl_do_hs(nc); + } + return; + } +#endif + mgos_lock(); + while (cs->rx_chain != NULL && nc->recv_mbuf.len < nc->recv_mbuf_limit) { + struct pbuf *seg = cs->rx_chain; + + size_t seg_len = (seg->len - cs->rx_offset); + size_t buf_avail = (nc->recv_mbuf_limit - nc->recv_mbuf.len); + size_t len = MIN(seg_len, buf_avail); + + char *data = (char *) MG_MALLOC(len); + if (data == NULL) { + mgos_unlock(); + DBG(("OOM")); + return; + } + pbuf_copy_partial(seg, data, len, cs->rx_offset); + cs->rx_offset += len; + if (cs->rx_offset == cs->rx_chain->len) { + cs->rx_chain = pbuf_dechain(cs->rx_chain); + pbuf_free(seg); + cs->rx_offset = 0; + } + mgos_unlock(); + mg_if_recv_tcp_cb(nc, data, len, 1 /* own */); + mgos_lock(); + } + mgos_unlock(); +} + +static void mg_lwip_handle_recv_tcp(struct mg_connection *nc) { + mg_lwip_consume_rx_chain_tcp(nc); + + if (nc->send_mbuf.len > 0) { + mg_lwip_mgr_schedule_poll(nc->mgr); + } +} + +static err_t mg_lwip_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb, + u16_t num_sent) { + struct mg_connection *nc = (struct mg_connection *) arg; + DBG(("%p %p %u %p %p", nc, tpcb, num_sent, tpcb->unsent, tpcb->unacked)); + if (nc == NULL) return ERR_OK; + if ((nc->flags & MG_F_SEND_AND_CLOSE) && !(nc->flags & MG_F_WANT_WRITE) && + nc->send_mbuf.len == 0 && tpcb->unsent == NULL && tpcb->unacked == NULL) { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } + return ERR_OK; +} + +struct mg_lwip_if_connect_tcp_ctx { + struct mg_connection *nc; + const union socket_address *sa; +}; + +static void mg_lwip_if_connect_tcp_tcpip(void *arg) { + struct mg_lwip_if_connect_tcp_ctx *ctx = + (struct mg_lwip_if_connect_tcp_ctx *) arg; + struct mg_connection *nc = ctx->nc; + const union socket_address *sa = ctx->sa; + + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct tcp_pcb *tpcb = TCP_NEW(); + cs->pcb.tcp = tpcb; + ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr; + u16_t port = ntohs(sa->sin.sin_port); + tcp_arg(tpcb, nc); + tcp_err(tpcb, mg_lwip_tcp_error_cb); + tcp_sent(tpcb, mg_lwip_tcp_sent_cb); + tcp_recv(tpcb, mg_lwip_tcp_recv_cb); + cs->err = TCP_BIND(tpcb, IP_ADDR_ANY, 0 /* any port */); + DBG(("%p tcp_bind = %d", nc, cs->err)); + if (cs->err != ERR_OK) { + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); + return; + } + cs->err = tcp_connect(tpcb, ip, port, mg_lwip_tcp_conn_cb); + DBG(("%p tcp_connect %p = %d", nc, tpcb, cs->err)); + if (cs->err != ERR_OK) { + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); + return; + } +} + +void mg_lwip_if_connect_tcp(struct mg_connection *nc, + const union socket_address *sa) { + struct mg_lwip_if_connect_tcp_ctx ctx = {.nc = nc, .sa = sa}; + tcpip_callback(mg_lwip_if_connect_tcp_tcpip, &ctx); +} + +/* + * Lwip included in the SDKs for nRF5x chips has different type for the + * callback of `udp_recv()` + */ +#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105 +static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, + const ip_addr_t *addr, u16_t port) +#else +static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *addr, u16_t port) +#endif +{ + struct mg_connection *nc = (struct mg_connection *) arg; + DBG(("%p %s:%u %p %u %u", nc, IPADDR_NTOA(addr), port, p, p->ref, p->len)); + /* Put address in a separate pbuf and tack it onto the packet. */ + struct pbuf *sap = + pbuf_alloc(PBUF_RAW, sizeof(union socket_address), PBUF_RAM); + if (sap == NULL) { + pbuf_free(p); + return; + } + union socket_address *sa = (union socket_address *) sap->payload; +#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105 + sa->sin.sin_addr.s_addr = ip_2_ip4(addr)->addr; +#else + sa->sin.sin_addr.s_addr = addr->addr; +#endif + sa->sin.sin_port = htons(port); + /* Logic in the recv handler requires that there be exactly one data pbuf. */ + p = pbuf_coalesce(p, PBUF_RAW); + pbuf_chain(sap, p); + mg_lwip_recv_common(nc, sap); + (void) pcb; +} + +static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + mgos_lock(); + if (cs->rx_chain == NULL) { + cs->rx_chain = p; + } else { + pbuf_chain(cs->rx_chain, p); + } + if (!cs->recv_pending) { + cs->recv_pending = 1; + mg_lwip_post_signal(MG_SIG_RECV, nc); + } + mgos_unlock(); +} + +static void mg_lwip_handle_recv_udp(struct mg_connection *nc) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + /* + * For UDP, RX chain consists of interleaved address and packet bufs: + * Address pbuf followed by exactly one data pbuf (recv_cb took care of that). + */ + while (cs->rx_chain != NULL) { + struct pbuf *sap = cs->rx_chain; + struct pbuf *p = sap->next; + cs->rx_chain = pbuf_dechain(p); + size_t data_len = p->len; + char *data = (char *) MG_MALLOC(data_len); + if (data != NULL) { + pbuf_copy_partial(p, data, data_len, 0); + pbuf_free(p); + mg_if_recv_udp_cb(nc, data, data_len, + (union socket_address *) sap->payload, sap->len); + pbuf_free(sap); + } else { + pbuf_free(p); + pbuf_free(sap); + } + } +} + +static void mg_lwip_if_connect_udp_tcpip(void *arg) { + struct mg_connection *nc = (struct mg_connection *) arg; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct udp_pcb *upcb = udp_new(); + cs->err = UDP_BIND(upcb, IP_ADDR_ANY, 0 /* any port */); + DBG(("%p udp_bind %p = %d", nc, upcb, cs->err)); + if (cs->err == ERR_OK) { + udp_recv(upcb, mg_lwip_udp_recv_cb, nc); + cs->pcb.udp = upcb; + } else { + udp_remove(upcb); + } + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); +} + +void mg_lwip_if_connect_udp(struct mg_connection *nc) { + tcpip_callback(mg_lwip_if_connect_udp_tcpip, nc); +} + +void mg_lwip_accept_conn(struct mg_connection *nc, struct tcp_pcb *tpcb) { + union socket_address sa; + SET_ADDR(&sa, &tpcb->remote_ip); + sa.sin.sin_port = htons(tpcb->remote_port); + mg_if_accept_tcp_cb(nc, &sa, sizeof(sa.sin)); +} + +static void tcp_close_tcpip(void *arg) { + tcp_close((struct tcp_pcb *) arg); +} + +void mg_lwip_handle_accept(struct mg_connection *nc) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (cs->pcb.tcp == NULL) return; +#if MG_ENABLE_SSL + if (cs->lc->flags & MG_F_SSL) { + if (mg_ssl_if_conn_accept(nc, cs->lc) != MG_SSL_OK) { + LOG(LL_ERROR, ("SSL error")); + tcpip_callback(tcp_close_tcpip, cs->pcb.tcp); + } + } else +#endif + { + mg_lwip_accept_conn(nc, cs->pcb.tcp); + } +} + +static err_t mg_lwip_accept_cb(void *arg, struct tcp_pcb *newtpcb, err_t err) { + struct mg_connection *lc = (struct mg_connection *) arg, *nc; + struct mg_lwip_conn_state *lcs, *cs; + struct tcp_pcb_listen *lpcb; + LOG(LL_DEBUG, + ("%p conn %p from %s:%u", lc, newtpcb, + IPADDR_NTOA(ipX_2_ip(&newtpcb->remote_ip)), newtpcb->remote_port)); + if (lc == NULL) { + tcp_abort(newtpcb); + return ERR_ABRT; + } + lcs = (struct mg_lwip_conn_state *) lc->sock; + lpcb = (struct tcp_pcb_listen *) lcs->pcb.tcp; +#if TCP_LISTEN_BACKLOG + tcp_accepted(lpcb); +#endif + nc = mg_if_accept_new_conn(lc); + if (nc == NULL) { + tcp_abort(newtpcb); + return ERR_ABRT; + } + cs = (struct mg_lwip_conn_state *) nc->sock; + cs->lc = lc; + cs->pcb.tcp = newtpcb; + /* We need to set up callbacks before returning because data may start + * arriving immediately. */ + tcp_arg(newtpcb, nc); + tcp_err(newtpcb, mg_lwip_tcp_error_cb); + tcp_sent(newtpcb, mg_lwip_tcp_sent_cb); + tcp_recv(newtpcb, mg_lwip_tcp_recv_cb); +#if LWIP_TCP_KEEPALIVE + mg_lwip_set_keepalive_params(nc, 60, 10, 6); +#endif + mg_lwip_post_signal(MG_SIG_ACCEPT, nc); + (void) err; + (void) lpcb; + return ERR_OK; +} + +struct mg_lwip_if_listen_ctx { + struct mg_connection *nc; + union socket_address *sa; + int ret; +}; + +static void mg_lwip_if_listen_tcp_tcpip(void *arg) { + struct mg_lwip_if_listen_ctx *ctx = (struct mg_lwip_if_listen_ctx *) arg; + struct mg_connection *nc = ctx->nc; + union socket_address *sa = ctx->sa; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct tcp_pcb *tpcb = TCP_NEW(); + ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr; + u16_t port = ntohs(sa->sin.sin_port); + cs->err = TCP_BIND(tpcb, ip, port); + DBG(("%p tcp_bind(%s:%u) = %d", nc, IPADDR_NTOA(ip), port, cs->err)); + if (cs->err != ERR_OK) { + tcp_close(tpcb); + ctx->ret = -1; + return; + } + tcp_arg(tpcb, nc); + tpcb = tcp_listen(tpcb); + cs->pcb.tcp = tpcb; + tcp_accept(tpcb, mg_lwip_accept_cb); + ctx->ret = 0; +} + +int mg_lwip_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) { + struct mg_lwip_if_listen_ctx ctx = {.nc = nc, .sa = sa}; + tcpip_callback(mg_lwip_if_listen_tcp_tcpip, &ctx); + return ctx.ret; +} + +static void mg_lwip_if_listen_udp_tcpip(void *arg) { + struct mg_lwip_if_listen_ctx *ctx = (struct mg_lwip_if_listen_ctx *) arg; + struct mg_connection *nc = ctx->nc; + union socket_address *sa = ctx->sa; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct udp_pcb *upcb = udp_new(); + ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr; + u16_t port = ntohs(sa->sin.sin_port); + cs->err = UDP_BIND(upcb, ip, port); + DBG(("%p udb_bind(%s:%u) = %d", nc, IPADDR_NTOA(ip), port, cs->err)); + if (cs->err != ERR_OK) { + udp_remove(upcb); + ctx->ret = -1; + } else { + udp_recv(upcb, mg_lwip_udp_recv_cb, nc); + cs->pcb.udp = upcb; + ctx->ret = 0; + } +} + +int mg_lwip_if_listen_udp(struct mg_connection *nc, union socket_address *sa) { + struct mg_lwip_if_listen_ctx ctx = {.nc = nc, .sa = sa}; + tcpip_callback(mg_lwip_if_listen_udp_tcpip, &ctx); + return ctx.ret; +} + +struct mg_lwip_tcp_write_ctx { + struct mg_connection *nc; + const void *data; + uint16_t len; + int ret; +}; + +static void tcp_output_tcpip(void *arg) { + tcp_output((struct tcp_pcb *) arg); +} + +static void mg_lwip_tcp_write_tcpip(void *arg) { + struct mg_lwip_tcp_write_ctx *ctx = (struct mg_lwip_tcp_write_ctx *) arg; + struct mg_connection *nc = ctx->nc; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct tcp_pcb *tpcb = cs->pcb.tcp; + size_t len = MIN(tpcb->mss, MIN(ctx->len, tpcb->snd_buf)); + size_t unsent, unacked; + if (len == 0) { + DBG(("%p no buf avail %u %u %p %p", tpcb, tpcb->snd_buf, tpcb->snd_queuelen, + tpcb->unsent, tpcb->unacked)); + tcpip_callback(tcp_output_tcpip, tpcb); + ctx->ret = 0; + return; + } + unsent = (tpcb->unsent != NULL ? tpcb->unsent->len : 0); + unacked = (tpcb->unacked != NULL ? tpcb->unacked->len : 0); +/* + * On ESP8266 we only allow one TCP segment in flight at any given time. + * This may increase latency and reduce efficiency of tcp windowing, + * but memory is scarce and precious on that platform so we do this to + * reduce footprint. + */ +#if CS_PLATFORM == CS_P_ESP8266 + if (unacked > 0) { + ctx->ret = 0; + return; + } + len = MIN(len, (TCP_MSS - unsent)); +#endif + cs->err = tcp_write(tpcb, ctx->data, len, TCP_WRITE_FLAG_COPY); + unsent = (tpcb->unsent != NULL ? tpcb->unsent->len : 0); + unacked = (tpcb->unacked != NULL ? tpcb->unacked->len : 0); + DBG(("%p tcp_write %u = %d, %u %u", tpcb, len, cs->err, unsent, unacked)); + if (cs->err != ERR_OK) { + /* + * We ignore ERR_MEM because memory will be freed up when the data is sent + * and we'll retry. + */ + ctx->ret = (cs->err == ERR_MEM ? 0 : -1); + return; + } + ctx->ret = len; +} + +static int mg_lwip_tcp_write(struct mg_connection *nc, const void *data, + uint16_t len) { + struct mg_lwip_tcp_write_ctx ctx = {.nc = nc, .data = data, .len = len}; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct tcp_pcb *tpcb = cs->pcb.tcp; + if (tpcb == NULL) { + return -1; + } + tcpip_callback(mg_lwip_tcp_write_tcpip, &ctx); + return ctx.ret; +} + +struct udp_sendto_ctx { + struct udp_pcb *upcb; + struct pbuf *p; + ip_addr_t *ip; + uint16_t port; + int ret; +}; + +static void udp_sendto_tcpip(void *arg) { + struct udp_sendto_ctx *ctx = (struct udp_sendto_ctx *) arg; + ctx->ret = udp_sendto(ctx->upcb, ctx->p, ctx->ip, ctx->port); +} + +static int mg_lwip_udp_send(struct mg_connection *nc, const void *data, + uint16_t len) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (cs->pcb.udp == NULL) { + /* + * In case of UDP, this usually means, what + * async DNS resolve is still in progress and connection + * is not ready yet + */ + DBG(("%p socket is not connected", nc)); + return -1; + } + struct udp_pcb *upcb = cs->pcb.udp; + struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); +#if defined(LWIP_IPV4) && LWIP_IPV4 && defined(LWIP_IPV6) && LWIP_IPV6 + ip_addr_t ip = {.u_addr.ip4.addr = nc->sa.sin.sin_addr.s_addr, .type = 0}; +#else + ip_addr_t ip = {.addr = nc->sa.sin.sin_addr.s_addr}; +#endif + u16_t port = ntohs(nc->sa.sin.sin_port); + if (p == NULL) { + DBG(("OOM")); + return 0; + } + memcpy(p->payload, data, len); + struct udp_sendto_ctx ctx = {.upcb = upcb, .p = p, .ip = &ip, .port = port}; + tcpip_callback(udp_sendto_tcpip, &ctx); + cs->err = ctx.ret; + pbuf_free(p); + return (cs->err == ERR_OK ? len : -1); +} + +static void mg_lwip_send_more(struct mg_connection *nc) { + int num_sent = 0; + if (nc->sock == INVALID_SOCKET) return; + if (nc->flags & MG_F_UDP) { + num_sent = mg_lwip_udp_send(nc, nc->send_mbuf.buf, nc->send_mbuf.len); + DBG(("%p mg_lwip_udp_send %u = %d", nc, nc->send_mbuf.len, num_sent)); + } else { + num_sent = mg_lwip_tcp_write(nc, nc->send_mbuf.buf, nc->send_mbuf.len); + DBG(("%p mg_lwip_tcp_write %u = %d", nc, nc->send_mbuf.len, num_sent)); + } + if (num_sent == 0) return; + if (num_sent > 0) { + mg_if_sent_cb(nc, num_sent); + } else { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } +} + +void mg_lwip_if_tcp_send(struct mg_connection *nc, const void *buf, + size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); + mg_lwip_mgr_schedule_poll(nc->mgr); +} + +void mg_lwip_if_udp_send(struct mg_connection *nc, const void *buf, + size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); + mg_lwip_mgr_schedule_poll(nc->mgr); +} + +struct tcp_recved_ctx { + struct tcp_pcb *tpcb; + size_t len; +}; + +void tcp_recved_tcpip(void *arg) { + struct tcp_recved_ctx *ctx = (struct tcp_recved_ctx *) arg; + tcp_recved(ctx->tpcb, ctx->len); +} + +void mg_lwip_if_recved(struct mg_connection *nc, size_t len) { + if (nc->flags & MG_F_UDP) return; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (nc->sock == INVALID_SOCKET || cs->pcb.tcp == NULL) { + DBG(("%p invalid socket", nc)); + return; + } + DBG(("%p %p %u %u", nc, cs->pcb.tcp, len, + (cs->rx_chain ? cs->rx_chain->tot_len : 0))); + struct tcp_recved_ctx ctx = {.tpcb = cs->pcb.tcp, .len = len}; +#if MG_ENABLE_SSL + if (!(nc->flags & MG_F_SSL)) { + tcpip_callback(tcp_recved_tcpip, &ctx); + } else { + /* Currently SSL acknowledges data immediately. + * TODO(rojer): Find a way to propagate mg_lwip_if_recved. */ + } +#else + tcpip_callback(tcp_recved_tcpip, &ctx); +#endif + mbuf_trim(&nc->recv_mbuf); +} + +int mg_lwip_if_create_conn(struct mg_connection *nc) { + struct mg_lwip_conn_state *cs = + (struct mg_lwip_conn_state *) MG_CALLOC(1, sizeof(*cs)); + if (cs == NULL) return 0; + cs->nc = nc; + nc->sock = (intptr_t) cs; + return 1; +} + +static void udp_remove_tcpip(void *arg) { + udp_remove((struct udp_pcb *) arg); +} + +void mg_lwip_if_destroy_conn(struct mg_connection *nc) { + if (nc->sock == INVALID_SOCKET) return; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (!(nc->flags & MG_F_UDP)) { + struct tcp_pcb *tpcb = cs->pcb.tcp; + if (tpcb != NULL) { + tcp_arg(tpcb, NULL); + DBG(("%p tcp_close %p", nc, tpcb)); + tcp_arg(tpcb, NULL); + tcpip_callback(tcp_close_tcpip, tpcb); + } + while (cs->rx_chain != NULL) { + struct pbuf *seg = cs->rx_chain; + cs->rx_chain = pbuf_dechain(cs->rx_chain); + pbuf_free(seg); + } + memset(cs, 0, sizeof(*cs)); + MG_FREE(cs); + } else if (nc->listener == NULL) { + /* Only close outgoing UDP pcb or listeners. */ + struct udp_pcb *upcb = cs->pcb.udp; + if (upcb != NULL) { + DBG(("%p udp_remove %p", nc, upcb)); + tcpip_callback(udp_remove_tcpip, upcb); + } + memset(cs, 0, sizeof(*cs)); + MG_FREE(cs); + } + nc->sock = INVALID_SOCKET; +} + +void mg_lwip_if_get_conn_addr(struct mg_connection *nc, int remote, + union socket_address *sa) { + memset(sa, 0, sizeof(*sa)); + if (nc == NULL || nc->sock == INVALID_SOCKET) return; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (nc->flags & MG_F_UDP) { + struct udp_pcb *upcb = cs->pcb.udp; + if (remote) { + memcpy(sa, &nc->sa, sizeof(*sa)); + } else if (upcb != NULL) { + sa->sin.sin_port = htons(upcb->local_port); + SET_ADDR(sa, &upcb->local_ip); + } + } else { + struct tcp_pcb *tpcb = cs->pcb.tcp; + if (remote) { + memcpy(sa, &nc->sa, sizeof(*sa)); + } else if (tpcb != NULL) { + sa->sin.sin_port = htons(tpcb->local_port); + SET_ADDR(sa, &tpcb->local_ip); + } + } +} + +void mg_lwip_if_sock_set(struct mg_connection *nc, sock_t sock) { + nc->sock = sock; +} + +/* clang-format off */ +#define MG_LWIP_IFACE_VTABLE \ + { \ + mg_lwip_if_init, \ + mg_lwip_if_free, \ + mg_lwip_if_add_conn, \ + mg_lwip_if_remove_conn, \ + mg_lwip_if_poll, \ + mg_lwip_if_listen_tcp, \ + mg_lwip_if_listen_udp, \ + mg_lwip_if_connect_tcp, \ + mg_lwip_if_connect_udp, \ + mg_lwip_if_tcp_send, \ + mg_lwip_if_udp_send, \ + mg_lwip_if_recved, \ + mg_lwip_if_create_conn, \ + mg_lwip_if_destroy_conn, \ + mg_lwip_if_sock_set, \ + mg_lwip_if_get_conn_addr, \ + } +/* clang-format on */ + +const struct mg_iface_vtable mg_lwip_iface_vtable = MG_LWIP_IFACE_VTABLE; +#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL +const struct mg_iface_vtable mg_default_iface_vtable = MG_LWIP_IFACE_VTABLE; +#endif + +#endif /* MG_ENABLE_NET_IF_LWIP_LOW_LEVEL */ diff --git a/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_net_if.h b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_net_if.h new file mode 100644 index 0000000..d00fd2f --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_net_if.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_LWIP_MG_NET_IF_LWIP_H_ +#define CS_COMMON_PLATFORMS_LWIP_MG_NET_IF_LWIP_H_ + +#ifndef MG_ENABLE_NET_IF_LWIP_LOW_LEVEL +#define MG_ENABLE_NET_IF_LWIP_LOW_LEVEL MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL +#endif + +#if MG_ENABLE_NET_IF_LWIP_LOW_LEVEL + +#include + +extern const struct mg_iface_vtable mg_lwip_iface_vtable; + +struct mg_lwip_conn_state { + struct mg_connection *nc; + struct mg_connection *lc; + union { + struct tcp_pcb *tcp; + struct udp_pcb *udp; + } pcb; + err_t err; + size_t num_sent; /* Number of acknowledged bytes to be reported to the core */ + struct pbuf *rx_chain; /* Chain of incoming data segments. */ + size_t rx_offset; /* Offset within the first pbuf (if partially consumed) */ + /* Last SSL write size, for retries. */ + int last_ssl_write_size; + /* Whether MG_SIG_RECV is already pending for this connection */ + int recv_pending : 1; + /* Whether the connection is about to close, just `rx_chain` needs to drain */ + int draining_rx_chain : 1; +}; + +enum mg_sig_type { + MG_SIG_CONNECT_RESULT = 1, + MG_SIG_RECV = 2, + MG_SIG_CLOSE_CONN = 3, + MG_SIG_TOMBSTONE = 4, + MG_SIG_ACCEPT = 5, +}; + +void mg_lwip_post_signal(enum mg_sig_type sig, struct mg_connection *nc); + +/* To be implemented by the platform. */ +void mg_lwip_mgr_schedule_poll(struct mg_mgr *mgr); + +#endif /* MG_ENABLE_NET_IF_LWIP_LOW_LEVEL */ + +#endif /* CS_COMMON_PLATFORMS_LWIP_MG_NET_IF_LWIP_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_ssl_if.c b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_ssl_if.c new file mode 100644 index 0000000..04ed778 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_ssl_if.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_SSL && MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL + +#include "common/mg_mem.h" +#include "common/cs_dbg.h" + +#include +#include + +#ifndef MG_LWIP_SSL_IO_SIZE +#define MG_LWIP_SSL_IO_SIZE 1024 +#endif + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +void mg_lwip_ssl_do_hs(struct mg_connection *nc) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + int server_side = (nc->listener != NULL); + enum mg_ssl_if_result res; + if (nc->flags & MG_F_CLOSE_IMMEDIATELY) return; + res = mg_ssl_if_handshake(nc); + DBG(("%p %lu %d %d", nc, nc->flags, server_side, res)); + if (res != MG_SSL_OK) { + if (res == MG_SSL_WANT_WRITE) { + nc->flags |= MG_F_WANT_WRITE; + cs->err = 0; + } else if (res == MG_SSL_WANT_READ) { + /* + * Nothing to do in particular, we are callback-driven. + * What we definitely do not need anymore is SSL reading (nothing left). + */ + nc->flags &= ~MG_F_WANT_READ; + cs->err = 0; + } else { + cs->err = res; + if (server_side) { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } else { + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); + } + } + } else { + cs->err = 0; + nc->flags &= ~MG_F_WANT_WRITE; + /* + * Handshake is done. Schedule a read immediately to consume app data + * which may already be waiting. + */ + nc->flags |= (MG_F_SSL_HANDSHAKE_DONE | MG_F_WANT_READ); + if (server_side) { + mg_lwip_accept_conn(nc, cs->pcb.tcp); + } else { + mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); + } + } +} + +void mg_lwip_ssl_send(struct mg_connection *nc) { + if (nc->sock == INVALID_SOCKET) { + DBG(("%p invalid socket", nc)); + return; + } + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + /* It's ok if the buffer is empty. Return value of 0 may also be valid. */ + int len = cs->last_ssl_write_size; + if (len == 0) { + len = MIN(MG_LWIP_SSL_IO_SIZE, nc->send_mbuf.len); + } + int ret = mg_ssl_if_write(nc, nc->send_mbuf.buf, len); + DBG(("%p SSL_write %u = %d", nc, len, ret)); + if (ret > 0) { + mg_if_sent_cb(nc, ret); + cs->last_ssl_write_size = 0; + } else if (ret < 0) { + /* This is tricky. We must remember the exact data we were sending to retry + * exactly the same send next time. */ + cs->last_ssl_write_size = len; + } + if (ret == len) { + nc->flags &= ~MG_F_WANT_WRITE; + } else if (ret == MG_SSL_WANT_WRITE) { + nc->flags |= MG_F_WANT_WRITE; + } else { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + } +} + +void mg_lwip_ssl_recv(struct mg_connection *nc) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + /* Don't deliver data before connect callback */ + if (nc->flags & MG_F_CONNECTING) return; + while (nc->recv_mbuf.len < nc->recv_mbuf_limit) { + char *buf = (char *) MG_MALLOC(MG_LWIP_SSL_IO_SIZE); + if (buf == NULL) return; + int ret = mg_ssl_if_read(nc, buf, MG_LWIP_SSL_IO_SIZE); + DBG(("%p %p SSL_read %u = %d", nc, cs->rx_chain, MG_LWIP_SSL_IO_SIZE, ret)); + if (ret <= 0) { + MG_FREE(buf); + if (ret == MG_SSL_WANT_WRITE) { + nc->flags |= MG_F_WANT_WRITE; + return; + } else if (ret == MG_SSL_WANT_READ) { + /* + * Nothing to do in particular, we are callback-driven. + * What we definitely do not need anymore is SSL reading (nothing left). + */ + nc->flags &= ~MG_F_WANT_READ; + cs->err = 0; + return; + } else { + mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); + return; + } + } else { + mg_if_recv_tcp_cb(nc, buf, ret, 1 /* own */); + } + } +} + +#ifdef KR_VERSION + +ssize_t kr_send(int fd, const void *buf, size_t len) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) fd; + int ret = mg_lwip_tcp_write(cs->nc, buf, len); + DBG(("%p mg_lwip_tcp_write %u = %d", cs->nc, len, ret)); + if (ret == 0) ret = KR_IO_WOULDBLOCK; + return ret; +} + +ssize_t kr_recv(int fd, void *buf, size_t len) { + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) fd; + struct pbuf *seg = cs->rx_chain; + if (seg == NULL) { + DBG(("%u - nothing to read", len)); + return KR_IO_WOULDBLOCK; + } + size_t seg_len = (seg->len - cs->rx_offset); + DBG(("%u %u %u %u", len, cs->rx_chain->len, seg_len, cs->rx_chain->tot_len)); + len = MIN(len, seg_len); + pbuf_copy_partial(seg, buf, len, cs->rx_offset); + cs->rx_offset += len; + tcp_recved(cs->pcb.tcp, len); + if (cs->rx_offset == cs->rx_chain->len) { + cs->rx_chain = pbuf_dechain(cs->rx_chain); + pbuf_free(seg); + cs->rx_offset = 0; + } + return len; +} + +#elif MG_SSL_IF == MG_SSL_IF_MBEDTLS + +int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len) { + struct mg_connection *nc = (struct mg_connection *) ctx; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + int ret = mg_lwip_tcp_write(cs->nc, buf, len); + if (ret == 0) ret = MBEDTLS_ERR_SSL_WANT_WRITE; + LOG(LL_DEBUG, ("%p %d -> %d", nc, len, ret)); + return ret; +} + +int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len) { + struct mg_connection *nc = (struct mg_connection *) ctx; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + struct pbuf *seg = cs->rx_chain; + if (seg == NULL) { + DBG(("%u - nothing to read", len)); + return MBEDTLS_ERR_SSL_WANT_READ; + } + size_t seg_len = (seg->len - cs->rx_offset); + DBG(("%u %u %u %u", len, cs->rx_chain->len, seg_len, cs->rx_chain->tot_len)); + mgos_lock(); + len = MIN(len, seg_len); + pbuf_copy_partial(seg, buf, len, cs->rx_offset); + cs->rx_offset += len; + /* TCP PCB may be NULL if connection has already been closed + * but we still have data to deliver to SSL. */ + if (cs->pcb.tcp != NULL) tcp_recved(cs->pcb.tcp, len); + if (cs->rx_offset == cs->rx_chain->len) { + cs->rx_chain = pbuf_dechain(cs->rx_chain); + pbuf_free(seg); + cs->rx_offset = 0; + } + mgos_unlock(); + LOG(LL_DEBUG, ("%p <- %d", nc, (int) len)); + return len; +} + +#endif + +#endif /* MG_ENABLE_SSL && MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL */ diff --git a/src/mongoose-6.11/src/common/platforms/mbed/mbed_libc.c b/src/mongoose-6.11/src/common/platforms/mbed/mbed_libc.c new file mode 100644 index 0000000..a5b35b1 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/mbed/mbed_libc.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#include "common/platform.h" + +#if CS_PLATFORM == CS_P_MBED + +long timezone; + +/* + * The GCC ARM toolchain for implements a weak + * gettimeofday stub that should be implemented + * to hook the OS time source. But mbed OS doesn't do it; + * the mbed doc only talks about C date and time functions: + * + * https://docs.mbed.com/docs/mbed-os-api-reference/en/5.1/APIs/tasks/Time/ + * + * gettimeof day is a BSD API. + */ +int _gettimeofday(struct timeval *tv, void *tzvp) { + tv->tv_sec = time(NULL); + tv->tv_usec = 0; + return 0; +} + +int inet_aton(const char *cp, struct in_addr *inp) { + /* We don't have aton, but have pton in mbed */ + return inet_pton(AF_INET, cp, inp); +} + +in_addr_t inet_addr(const char *cp) { + in_addr_t ret; + if (inet_pton(AF_INET, cp, &ret) != 1) { + return 0; + } + + return ret; +} + +#endif /* CS_PLATFORM == CS_P_MBED */ diff --git a/src/mongoose-6.11/src/common/platforms/msp432/msp432_libc.c b/src/mongoose-6.11/src/common/platforms/msp432/msp432_libc.c new file mode 100644 index 0000000..faedadc --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/msp432/msp432_libc.c @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if CS_PLATFORM == CS_P_MSP432 + +#include +#include + +int gettimeofday(struct timeval *tp, void *tzp) { + uint32_t ticks = Clock_getTicks(); + tp->tv_sec = ticks / 1000; + tp->tv_usec = (ticks % 1000) * 1000; + return 0; +} + +#endif /* CS_PLATFORM == CS_P_MSP432 */ diff --git a/src/mongoose-6.11/src/common/platforms/nrf5/nrf5_libc.c b/src/mongoose-6.11/src/common/platforms/nrf5/nrf5_libc.c new file mode 100644 index 0000000..9cf66dc --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/nrf5/nrf5_libc.c @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if (CS_PLATFORM == CS_P_NRF51 || CS_PLATFORM == CS_P_NRF52) && \ + defined(__ARMCC_VERSION) +int gettimeofday(struct timeval *tp, void *tzp) { + /* TODO */ + tp->tv_sec = 0; + tp->tv_usec = 0; + return 0; +} +#endif diff --git a/src/mongoose-6.11/src/common/platforms/pic32/pic32_net_if.c b/src/mongoose-6.11/src/common/platforms/pic32/pic32_net_if.c new file mode 100644 index 0000000..d0e67e9 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/pic32/pic32_net_if.c @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_NET_IF_PIC32 + +int mg_pic32_if_create_conn(struct mg_connection *nc) { + (void) nc; + return 1; +} + +void mg_pic32_if_recved(struct mg_connection *nc, size_t len) { + (void) nc; + (void) len; +} + +void mg_pic32_if_add_conn(struct mg_connection *nc) { + (void) nc; +} + +void mg_pic32_if_init(struct mg_iface *iface) { + (void) iface; + (void) mg_get_errno(); /* Shutup compiler */ +} + +void mg_pic32_if_free(struct mg_iface *iface) { + (void) iface; +} + +void mg_pic32_if_remove_conn(struct mg_connection *nc) { + (void) nc; +} + +void mg_pic32_if_destroy_conn(struct mg_connection *nc) { + if (nc->sock == INVALID_SOCKET) return; + /* For UDP, only close outgoing sockets or listeners. */ + if (!(nc->flags & MG_F_UDP)) { + /* Close TCP */ + TCPIP_TCP_Close((TCP_SOCKET) nc->sock); + } else if (nc->listener == NULL) { + /* Only close outgoing UDP or listeners. */ + TCPIP_UDP_Close((UDP_SOCKET) nc->sock); + } + + nc->sock = INVALID_SOCKET; +} + +int mg_pic32_if_listen_udp(struct mg_connection *nc, union socket_address *sa) { + nc->sock = TCPIP_UDP_ServerOpen( + sa->sin.sin_family == AF_INET ? IP_ADDRESS_TYPE_IPV4 + : IP_ADDRESS_TYPE_IPV6, + ntohs(sa->sin.sin_port), + sa->sin.sin_addr.s_addr == 0 ? 0 : (IP_MULTI_ADDRESS *) &sa->sin); + if (nc->sock == INVALID_SOCKET) { + return -1; + } + return 0; +} + +void mg_pic32_if_udp_send(struct mg_connection *nc, const void *buf, + size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); +} + +void mg_pic32_if_tcp_send(struct mg_connection *nc, const void *buf, + size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); +} + +int mg_pic32_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) { + nc->sock = TCPIP_TCP_ServerOpen( + sa->sin.sin_family == AF_INET ? IP_ADDRESS_TYPE_IPV4 + : IP_ADDRESS_TYPE_IPV6, + ntohs(sa->sin.sin_port), + sa->sin.sin_addr.s_addr == 0 ? 0 : (IP_MULTI_ADDRESS *) &sa->sin); + memcpy(&nc->sa, sa, sizeof(*sa)); + if (nc->sock == INVALID_SOCKET) { + return -1; + } + return 0; +} + +static int mg_accept_conn(struct mg_connection *lc) { + struct mg_connection *nc; + TCP_SOCKET_INFO si; + union socket_address sa; + + nc = mg_if_accept_new_conn(lc); + + if (nc == NULL) { + return 0; + } + + nc->sock = lc->sock; + nc->flags &= ~MG_F_LISTENING; + + if (!TCPIP_TCP_SocketInfoGet((TCP_SOCKET) nc->sock, &si)) { + return 0; + } + + if (si.addressType == IP_ADDRESS_TYPE_IPV4) { + sa.sin.sin_family = AF_INET; + sa.sin.sin_port = htons(si.remotePort); + sa.sin.sin_addr.s_addr = si.remoteIPaddress.v4Add.Val; + } else { + /* TODO(alashkin): do something with _potential_ IPv6 */ + memset(&sa, 0, sizeof(sa)); + } + + mg_if_accept_tcp_cb(nc, (union socket_address *) &sa, sizeof(sa)); + + return mg_pic32_if_listen_tcp(lc, &lc->sa) >= 0; +} + +char *inet_ntoa(struct in_addr in) { + static char addr[17]; + snprintf(addr, sizeof(addr), "%d.%d.%d.%d", (int) in.S_un.S_un_b.s_b1, + (int) in.S_un.S_un_b.s_b2, (int) in.S_un.S_un_b.s_b3, + (int) in.S_un.S_un_b.s_b4); + return addr; +} + +static void mg_handle_send(struct mg_connection *nc) { + uint16_t bytes_written = 0; + if (nc->flags & MG_F_UDP) { + if (!TCPIP_UDP_RemoteBind( + (UDP_SOCKET) nc->sock, + nc->sa.sin.sin_family == AF_INET ? IP_ADDRESS_TYPE_IPV4 + : IP_ADDRESS_TYPE_IPV6, + ntohs(nc->sa.sin.sin_port), (IP_MULTI_ADDRESS *) &nc->sa.sin)) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return; + } + bytes_written = TCPIP_UDP_TxPutIsReady((UDP_SOCKET) nc->sock, 0); + if (bytes_written >= nc->send_mbuf.len) { + if (TCPIP_UDP_ArrayPut((UDP_SOCKET) nc->sock, + (uint8_t *) nc->send_mbuf.buf, + nc->send_mbuf.len) != nc->send_mbuf.len) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + bytes_written = 0; + } + } + } else { + bytes_written = TCPIP_TCP_FifoTxFreeGet((TCP_SOCKET) nc->sock); + if (bytes_written != 0) { + if (bytes_written > nc->send_mbuf.len) { + bytes_written = nc->send_mbuf.len; + } + if (TCPIP_TCP_ArrayPut((TCP_SOCKET) nc->sock, + (uint8_t *) nc->send_mbuf.buf, + bytes_written) != bytes_written) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + bytes_written = 0; + } + } + } + + mg_if_sent_cb(nc, bytes_written); +} + +static void mg_handle_recv(struct mg_connection *nc) { + uint16_t bytes_read = 0; + uint8_t *buf = NULL; + if (nc->flags & MG_F_UDP) { + bytes_read = TCPIP_UDP_GetIsReady((UDP_SOCKET) nc->sock); + if (bytes_read != 0 && + (nc->recv_mbuf_limit == -1 || + nc->recv_mbuf.len + bytes_read < nc->recv_mbuf_limit)) { + buf = (uint8_t *) MG_MALLOC(bytes_read); + if (TCPIP_UDP_ArrayGet((UDP_SOCKET) nc->sock, buf, bytes_read) != + bytes_read) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + bytes_read = 0; + MG_FREE(buf); + } + } + } else { + bytes_read = TCPIP_TCP_GetIsReady((TCP_SOCKET) nc->sock); + if (bytes_read != 0) { + if (nc->recv_mbuf_limit != -1 && + nc->recv_mbuf_limit - nc->recv_mbuf.len > bytes_read) { + bytes_read = nc->recv_mbuf_limit - nc->recv_mbuf.len; + } + buf = (uint8_t *) MG_MALLOC(bytes_read); + if (TCPIP_TCP_ArrayGet((TCP_SOCKET) nc->sock, buf, bytes_read) != + bytes_read) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + MG_FREE(buf); + bytes_read = 0; + } + } + } + + if (bytes_read != 0) { + mg_if_recv_tcp_cb(nc, buf, bytes_read, 1 /* own */); + } +} + +time_t mg_pic32_if_poll(struct mg_iface *iface, int timeout_ms) { + struct mg_mgr *mgr = iface->mgr; + double now = mg_time(); + struct mg_connection *nc, *tmp; + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + tmp = nc->next; + + if (nc->flags & MG_F_CONNECTING) { + /* processing connections */ + if (nc->flags & MG_F_UDP || + TCPIP_TCP_IsConnected((TCP_SOCKET) nc->sock)) { + mg_if_connect_cb(nc, 0); + } + } else if (nc->flags & MG_F_LISTENING) { + if (TCPIP_TCP_IsConnected((TCP_SOCKET) nc->sock)) { + /* accept new connections */ + mg_accept_conn(nc); + } + } else { + if (nc->send_mbuf.len != 0) { + mg_handle_send(nc); + } + + if (nc->recv_mbuf_limit == -1 || + nc->recv_mbuf.len < nc->recv_mbuf_limit) { + mg_handle_recv(nc); + } + } + } + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + tmp = nc->next; + if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || + (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) { + mg_close_conn(nc); + } + } + + return now; +} + +void mg_pic32_if_sock_set(struct mg_connection *nc, sock_t sock) { + nc->sock = sock; +} + +void mg_pic32_if_get_conn_addr(struct mg_connection *nc, int remote, + union socket_address *sa) { + /* TODO(alaskin): not implemented yet */ +} + +void mg_pic32_if_connect_tcp(struct mg_connection *nc, + const union socket_address *sa) { + nc->sock = TCPIP_TCP_ClientOpen( + sa->sin.sin_family == AF_INET ? IP_ADDRESS_TYPE_IPV4 + : IP_ADDRESS_TYPE_IPV6, + ntohs(sa->sin.sin_port), (IP_MULTI_ADDRESS *) &sa->sin); + nc->err = (nc->sock == INVALID_SOCKET) ? -1 : 0; +} + +void mg_pic32_if_connect_udp(struct mg_connection *nc) { + nc->sock = TCPIP_UDP_ClientOpen(IP_ADDRESS_TYPE_ANY, 0, NULL); + nc->err = (nc->sock == INVALID_SOCKET) ? -1 : 0; +} + +/* clang-format off */ +#define MG_PIC32_IFACE_VTABLE \ + { \ + mg_pic32_if_init, \ + mg_pic32_if_free, \ + mg_pic32_if_add_conn, \ + mg_pic32_if_remove_conn, \ + mg_pic32_if_poll, \ + mg_pic32_if_listen_tcp, \ + mg_pic32_if_listen_udp, \ + mg_pic32_if_connect_tcp, \ + mg_pic32_if_connect_udp, \ + mg_pic32_if_tcp_send, \ + mg_pic32_if_udp_send, \ + mg_pic32_if_recved, \ + mg_pic32_if_create_conn, \ + mg_pic32_if_destroy_conn, \ + mg_pic32_if_sock_set, \ + mg_pic32_if_get_conn_addr, \ + } +/* clang-format on */ + +const struct mg_iface_vtable mg_pic32_iface_vtable = MG_PIC32_IFACE_VTABLE; +#if MG_NET_IF == MG_NET_IF_PIC32 +const struct mg_iface_vtable mg_default_iface_vtable = MG_PIC32_IFACE_VTABLE; +#endif + +#endif /* MG_ENABLE_NET_IF_PIC32 */ diff --git a/src/mongoose-6.11/src/common/platforms/pic32/pic32_net_if.h b/src/mongoose-6.11/src/common/platforms/pic32/pic32_net_if.h new file mode 100644 index 0000000..2bf66ef --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/pic32/pic32_net_if.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PIC32_NET_IF_H_ +#define CS_COMMON_PLATFORMS_PIC32_NET_IF_H_ + +#include "mongoose/src/net_if.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MG_ENABLE_NET_IF_PIC32 +#define MG_ENABLE_NET_IF_PIC32 MG_NET_IF == MG_NET_IF_PIC32 +#endif + +extern const struct mg_iface_vtable mg_pic32_iface_vtable; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_COMMON_PLATFORMS_PIC32_NET_IF_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/pic32/xc32.mk b/src/mongoose-6.11/src/common/platforms/pic32/xc32.mk new file mode 100644 index 0000000..aff6b4e --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/pic32/xc32.mk @@ -0,0 +1,51 @@ +APP_LDFLAGS ?= +CC_WRAPPER ?= +GENFILES_LIST ?= +CC = $(XC32_PATH)/bin/xc32-gcc +AR = $(XC32_PATH)/bin/xc32-ar +LD = $(XC32_PATH)/bin/xc32-gcc +BIN2HEX = $(XC32_PATH)/bin/xc32-bin2hex + +CFLAGS = -fframe-base-loclist -mprocessor=32MZ2048EFM064 \ + -ffunction-sections -Wall -g -no-legacy-libc \ + -O1 + +CFLAGS += -DCS_ENABLE_DEBUG + +LDFLAGS = -mprocessor=32MZ2048EFM064 + +# TODO(dfrank) enable that conditionally +CFLAGS += -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 +LDFLAGS += -mdebugger -D__MPLAB_DEBUGGER_ICD3=1 + +OBJCOPY = $(XC32_PATH)/bin/xc32-objcopy + +# cc flags,file +define cc + $(vecho) "XC32 $2 -> $@" + $(Q) $(CC_WRAPPER) $(CC) -c $1 -o $@ $2 +endef + +# cc flags,file +define asm + $(vecho) "XC32 ASM $2 -> $@" + $(Q) $(CC) -c $1 -o $@ $2 +endef + +# ar files +define ar + $(vecho) "AR $@" + $(Q) $(AR) cru $@ $1 +endef + +# link script,flags,objs +define link + $(vecho) "LD $@ (ignoring linker script $1)" + $(Q) $(CC_WRAPPER) $(LD) -o $@ $2 $3 -no-legacy-libc \ + -Wl,--defsym=__MPLAB_BUILD=1,--defsym=__MPLAB_DEBUG=1,--defsym=__DEBUG=1,--defsym=__MPLAB_DEBUGGER_ICD3=1,--defsym=_min_heap_size=44960,--gc-sections +endef + +define bin2hex + $(vecho) "HEX $1 -> $@" + $(Q) $(BIN2HEX) $1 +endef diff --git a/src/mongoose-6.11/src/common/platforms/platform_cc3100.h b/src/mongoose-6.11/src/common/platforms/platform_cc3100.h new file mode 100644 index 0000000..ba07f22 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_cc3100.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_ +#if CS_PLATFORM == CS_P_CC3100 + +#include +#include +#include +#include +#include +#include +#include + +#define MG_NET_IF MG_NET_IF_SIMPLELINK +#define MG_SSL_IF MG_SSL_IF_SIMPLELINK + +/* + * CC3100 SDK and STM32 SDK include headers w/out path, just like + * #include "simplelink.h". As result, we have to add all required directories + * into Makefile IPATH and do the same thing (include w/out path) + */ + +#include +#include +#undef timeval + +typedef int sock_t; +#define INVALID_SOCKET (-1) + +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define SIZE_T_FMT "u" + +#define SOMAXCONN 8 + +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); +char *inet_ntoa(struct in_addr in); +int inet_pton(int af, const char *src, void *dst); + +#endif /* CS_PLATFORM == CS_P_CC3100 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_cc3200.h b/src/mongoose-6.11/src/common/platforms/platform_cc3200.h new file mode 100644 index 0000000..1c2033a --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_cc3200.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ +#if CS_PLATFORM == CS_P_CC3200 + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __TI_COMPILER_VERSION__ +#include +#include +#endif + +#define MG_NET_IF MG_NET_IF_SIMPLELINK +#define MG_SSL_IF MG_SSL_IF_SIMPLELINK + +/* Only SPIFFS supports directories, SLFS does not. */ +#if defined(CC3200_FS_SPIFFS) && !defined(MG_ENABLE_DIRECTORY_LISTING) +#define MG_ENABLE_DIRECTORY_LISTING 1 +#endif + +#include "common/platforms/simplelink/cs_simplelink.h" + +typedef int sock_t; +#define INVALID_SOCKET (-1) +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define DIRSEP '/' +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define __cdecl + +#define fileno(x) -1 + +/* Some functions we implement for Mongoose. */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __TI_COMPILER_VERSION__ +struct SlTimeval_t; +#define timeval SlTimeval_t +int gettimeofday(struct timeval *t, void *tz); +int settimeofday(const struct timeval *tv, const void *tz); + +int asprintf(char **strp, const char *fmt, ...); + +#endif + +/* TI's libc does not have stat & friends, add them. */ +#ifdef __TI_COMPILER_VERSION__ + +#include + +typedef unsigned int mode_t; +typedef size_t _off_t; +typedef long ssize_t; + +struct stat { + int st_ino; + mode_t st_mode; + int st_nlink; + time_t st_mtime; + off_t st_size; +}; + +int _stat(const char *pathname, struct stat *st); +int stat(const char *pathname, struct stat *st); + +#define __S_IFMT 0170000 + +#define __S_IFDIR 0040000 +#define __S_IFCHR 0020000 +#define __S_IFREG 0100000 + +#define __S_ISTYPE(mode, mask) (((mode) &__S_IFMT) == (mask)) + +#define S_IFDIR __S_IFDIR +#define S_IFCHR __S_IFCHR +#define S_IFREG __S_IFREG +#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR) +#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG) + +/* 5.x series compilers don't have va_copy, 16.x do. */ +#if __TI_COMPILER_VERSION__ < 16000000 +#define va_copy(apc, ap) ((apc) = (ap)) +#endif + +#endif /* __TI_COMPILER_VERSION__ */ + +#ifdef CC3200_FS_SLFS +#define MG_FS_SLFS +#endif + +#if (defined(CC3200_FS_SPIFFS) || defined(CC3200_FS_SLFS)) && \ + !defined(MG_ENABLE_FILESYSTEM) +#define MG_ENABLE_FILESYSTEM 1 +#define CS_DEFINE_DIRENT +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* CS_PLATFORM == CS_P_CC3200 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_cc3220.h b/src/mongoose-6.11/src/common/platforms/platform_cc3220.h new file mode 100644 index 0000000..a32264a --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_cc3220.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3220_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_CC3220_H_ +#if CS_PLATFORM == CS_P_CC3220 + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __TI_COMPILER_VERSION__ +#include +#include +#endif + +#define MG_NET_IF MG_NET_IF_SIMPLELINK +#define MG_SSL_IF MG_SSL_IF_SIMPLELINK + +/* Only SPIFFS supports directories, SLFS does not. */ +#if defined(CC3220_FS_SPIFFS) && !defined(MG_ENABLE_DIRECTORY_LISTING) +#define MG_ENABLE_DIRECTORY_LISTING 1 +#endif + +#include "common/platforms/simplelink/cs_simplelink.h" + +typedef int sock_t; +#define INVALID_SOCKET (-1) +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define DIRSEP '/' +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define __cdecl + +#define fileno(x) -1 + +/* Some functions we implement for Mongoose. */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __TI_COMPILER_VERSION__ +struct SlTimeval_t; +#define timeval SlTimeval_t +int gettimeofday(struct timeval *t, void *tz); +int settimeofday(const struct timeval *tv, const void *tz); + +int asprintf(char **strp, const char *fmt, ...); + +#endif + +/* TI's libc does not have stat & friends, add them. */ +#ifdef __TI_COMPILER_VERSION__ + +#include + +typedef unsigned int mode_t; +typedef size_t _off_t; +typedef long ssize_t; + +struct stat { + int st_ino; + mode_t st_mode; + int st_nlink; + time_t st_mtime; + off_t st_size; +}; + +int _stat(const char *pathname, struct stat *st); +int stat(const char *pathname, struct stat *st); + +#define __S_IFMT 0170000 + +#define __S_IFDIR 0040000 +#define __S_IFCHR 0020000 +#define __S_IFREG 0100000 + +#define __S_ISTYPE(mode, mask) (((mode) &__S_IFMT) == (mask)) + +#define S_IFDIR __S_IFDIR +#define S_IFCHR __S_IFCHR +#define S_IFREG __S_IFREG +#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR) +#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG) + +#endif /* __TI_COMPILER_VERSION__ */ + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* CS_PLATFORM == CS_P_CC3220 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_esp32.h b/src/mongoose-6.11/src/common/platforms/platform_esp32.h new file mode 100644 index 0000000..4580f75 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_esp32.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_ +#if CS_PLATFORM == CS_P_ESP32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define DIRSEP '/' +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define __cdecl +#define _FILE_OFFSET_BITS 32 + +#define MG_LWIP 1 + +#ifndef MG_NET_IF +#define MG_NET_IF MG_NET_IF_SOCKET +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#endif /* CS_PLATFORM == CS_P_ESP32 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_esp8266.h b/src/mongoose-6.11/src/common/platforms/platform_esp8266.h new file mode 100644 index 0000000..b397101 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_esp8266.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_ +#if CS_PLATFORM == CS_P_ESP8266 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define DIRSEP '/' +#if !defined(MGOS_VFS_DEFINE_DIRENT) +#define CS_DEFINE_DIRENT +#endif + +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define __cdecl +#define _FILE_OFFSET_BITS 32 + +#if !defined(RTOS_SDK) && !defined(__cplusplus) +#define fileno(x) -1 +#endif + +#define MG_LWIP 1 + +/* struct timeval is defined in sys/time.h. */ +#define LWIP_TIMEVAL_PRIVATE 0 + +#ifndef MG_NET_IF +#include +#if LWIP_SOCKET /* RTOS SDK has LWIP sockets */ +#define MG_NET_IF MG_NET_IF_SOCKET +#else +#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL +#endif +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#define inet_ntop(af, src, dst, size) \ + (((af) == AF_INET) ? ipaddr_ntoa_r((const ip_addr_t *) (src), (dst), (size)) \ + : NULL) +#define inet_pton(af, src, dst) \ + (((af) == AF_INET) ? ipaddr_aton((src), (ip_addr_t *) (dst)) : 0) + +#endif /* CS_PLATFORM == CS_P_ESP8266 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_mbed.h b/src/mongoose-6.11/src/common/platforms/platform_mbed.h new file mode 100644 index 0000000..ab3f563 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_mbed.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_MBED_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_MBED_H_ +#if CS_PLATFORM == CS_P_MBED + +/* + * mbed.h contains C++ code (e.g. templates), thus, it should be processed + * only if included directly to startup file (ex: main.cpp) + */ +#ifdef __cplusplus +#include "mbed.h" +#endif /* __cplusplus */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct stat cs_stat_t; +#define DIRSEP '/' + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +/* + * mbed can be compiled with the ARM compiler which + * just doesn't come with a gettimeofday shim + * because it's a BSD API and ARM targets embedded + * non-unix platforms. + */ +#if defined(__ARMCC_VERSION) || defined(__ICCARM__) +#define _TIMEVAL_DEFINED +#define gettimeofday _gettimeofday + +/* copied from GCC on ARM; for some reason useconds are signed */ +typedef long suseconds_t; /* microseconds (signed) */ +struct timeval { + time_t tv_sec; /* seconds */ + suseconds_t tv_usec; /* and microseconds */ +}; + +#endif + +#if MG_NET_IF == MG_NET_IF_SIMPLELINK + +#define MG_SIMPLELINK_NO_OSI 1 + +#include + +typedef int sock_t; +#define INVALID_SOCKET (-1) + +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define SIZE_T_FMT "u" + +#define SOMAXCONN 8 + +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); +char *inet_ntoa(struct in_addr in); +int inet_pton(int af, const char *src, void *dst); +int inet_aton(const char *cp, struct in_addr *inp); +in_addr_t inet_addr(const char *cp); + +#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */ + +#endif /* CS_PLATFORM == CS_P_MBED */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_MBED_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_msp432.h b/src/mongoose-6.11/src/common/platforms/platform_msp432.h new file mode 100644 index 0000000..d8cf16a --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_msp432.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_MSP432_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_MSP432_H_ +#if CS_PLATFORM == CS_P_MSP432 + +#include +#include +#include +#include +#include +#include +#include + +#ifndef __TI_COMPILER_VERSION__ +#include +#include +#endif + +#define MG_NET_IF MG_NET_IF_SIMPLELINK +#define MG_SSL_IF MG_SSL_IF_SIMPLELINK + +#include "common/platforms/simplelink/cs_simplelink.h" + +typedef int sock_t; +#define INVALID_SOCKET (-1) +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define DIRSEP '/' +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define __cdecl + +#define fileno(x) -1 + +/* Some functions we implement for Mongoose. */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __TI_COMPILER_VERSION__ +struct SlTimeval_t; +#define timeval SlTimeval_t +int gettimeofday(struct timeval *t, void *tz); +#endif + +/* TI's libc does not have stat & friends, add them. */ +#ifdef __TI_COMPILER_VERSION__ + +#include + +typedef unsigned int mode_t; +typedef size_t _off_t; +typedef long ssize_t; + +struct stat { + int st_ino; + mode_t st_mode; + int st_nlink; + time_t st_mtime; + off_t st_size; +}; + +int _stat(const char *pathname, struct stat *st); +#define stat(a, b) _stat(a, b) + +#define __S_IFMT 0170000 + +#define __S_IFDIR 0040000 +#define __S_IFCHR 0020000 +#define __S_IFREG 0100000 + +#define __S_ISTYPE(mode, mask) (((mode) &__S_IFMT) == (mask)) + +#define S_IFDIR __S_IFDIR +#define S_IFCHR __S_IFCHR +#define S_IFREG __S_IFREG +#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR) +#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG) + +/* As of 5.2.7, TI compiler does not support va_copy() yet. */ +#define va_copy(apc, ap) ((apc) = (ap)) + +#endif /* __TI_COMPILER_VERSION__ */ + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#if (defined(CC3200_FS_SPIFFS) || defined(CC3200_FS_SLFS)) && \ + !defined(MG_ENABLE_FILESYSTEM) +#define MG_ENABLE_FILESYSTEM 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* CS_PLATFORM == CS_P_MSP432 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_MSP432_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_nrf51.h b/src/mongoose-6.11/src/common/platforms/platform_nrf51.h new file mode 100644 index 0000000..55f89dd --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_nrf51.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ +#ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_ +#if CS_PLATFORM == CS_P_NRF51 + +#include +#include +#include +#include +#include +#include + +#define to64(x) strtoll(x, NULL, 10) + +#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL +#define MG_LWIP 1 +#define MG_ENABLE_IPV6 1 + +/* + * For ARM C Compiler, make lwip to export `struct timeval`; for other + * compilers, suppress it. + */ +#if !defined(__ARMCC_VERSION) +#define LWIP_TIMEVAL_PRIVATE 0 +#else +struct timeval; +int gettimeofday(struct timeval *tp, void *tzp); +#endif + +#define INT64_FMT PRId64 +#define SIZE_T_FMT "u" + +/* + * ARM C Compiler doesn't have strdup, so we provide it + */ +#define CS_ENABLE_STRDUP defined(__ARMCC_VERSION) + +#endif /* CS_PLATFORM == CS_P_NRF51 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_nrf52.h b/src/mongoose-6.11/src/common/platforms/platform_nrf52.h new file mode 100644 index 0000000..3516cf9 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_nrf52.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ +#ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_ +#if CS_PLATFORM == CS_P_NRF52 + +#include +#include +#include +#include +#include +#include +#include +#include + +#define to64(x) strtoll(x, NULL, 10) + +#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL +#define MG_LWIP 1 +#define MG_ENABLE_IPV6 1 + +#if !defined(ENOSPC) +#define ENOSPC 28 /* No space left on device */ +#endif + +/* + * For ARM C Compiler, make lwip to export `struct timeval`; for other + * compilers, suppress it. + */ +#if !defined(__ARMCC_VERSION) +#define LWIP_TIMEVAL_PRIVATE 0 +#endif + +#define INT64_FMT PRId64 +#define SIZE_T_FMT "u" + +/* + * ARM C Compiler doesn't have strdup, so we provide it + */ +#define CS_ENABLE_STRDUP defined(__ARMCC_VERSION) + +#endif /* CS_PLATFORM == CS_P_NRF52 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_nxp_kinetis.h b/src/mongoose-6.11/src/common/platforms/platform_nxp_kinetis.h new file mode 100644 index 0000000..b4ffd53 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_nxp_kinetis.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_ + +#if CS_PLATFORM == CS_P_NXP_KINETIS + +#include +#include +#include +#include + +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT "lld" +#define INT64_X_FMT "llx" +#define __cdecl + +#define MG_LWIP 1 + +#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL + +/* struct timeval is defined in sys/time.h. */ +#define LWIP_TIMEVAL_PRIVATE 0 + +#endif /* CS_PLATFORM == CS_P_NXP_KINETIS */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_nxp_lpc.h b/src/mongoose-6.11/src/common/platforms/platform_nxp_lpc.h new file mode 100644 index 0000000..cf9b7e7 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_nxp_lpc.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_ + +#if CS_PLATFORM == CS_P_NXP_LPC + +#include +#include +#include + +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define INT64_FMT "lld" +#define INT64_X_FMT "llx" +#define __cdecl + +#define MG_LWIP 1 + +#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL + +/* + * LPCXpress comes with 3 C library implementations: Newlib, NewlibNano and + *Redlib. + * See https://community.nxp.com/message/630860 for more details. + * + * Redlib is the default and lacks certain things, so we provide them. + */ +#ifdef __REDLIB_INTERFACE_VERSION__ + +/* Let LWIP define timeval for us. */ +#define LWIP_TIMEVAL_PRIVATE 1 + +#define va_copy(d, s) __builtin_va_copy(d, s) + +#define CS_ENABLE_TO64 1 +#define to64(x) cs_to64(x) + +#define CS_ENABLE_STRDUP 1 + +#else + +#include +#define LWIP_TIMEVAL_PRIVATE 0 +#define to64(x) strtoll(x, NULL, 10) + +#endif + +#endif /* CS_PLATFORM == CS_P_NXP_LPC */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_pic32.h b/src/mongoose-6.11/src/common/platforms/platform_pic32.h new file mode 100644 index 0000000..b568025 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_pic32.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_ + +#if CS_PLATFORM == CS_P_PIC32 + +#define MG_NET_IF MG_NET_IF_PIC32 + +#include +#include +#include +#include + +#include +#include + +#include + +typedef TCP_SOCKET sock_t; +#define to64(x) strtoll(x, NULL, 10) + +#define SIZE_T_FMT "lu" +#define INT64_FMT "lld" + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +char *inet_ntoa(struct in_addr in); + +#endif /* CS_PLATFORM == CS_P_PIC32 */ + +#endif /* CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_stm32.h b/src/mongoose-6.11/src/common/platforms/platform_stm32.h new file mode 100644 index 0000000..ece78c8 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_stm32.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_STM32_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_STM32_H_ +#if CS_PLATFORM == CS_P_STM32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define DIRSEP '/' + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#ifndef MG_ENABLE_FILESYSTEM +#define MG_ENABLE_FILESYSTEM 1 +#endif + +#endif /* CS_PLATFORM == CS_P_STM32 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_STM32_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_tm4c129.h b/src/mongoose-6.11/src/common/platforms/platform_tm4c129.h new file mode 100644 index 0000000..5e87b94 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_tm4c129.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_PLATFORM_TM4C129_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_TM4C129_H_ +#if CS_PLATFORM == CS_P_TM4C129 + +#include +#include +#include +#include +#include +#include +#include + +#ifndef __TI_COMPILER_VERSION__ +#include +#include +#endif + +#define SIZE_T_FMT "u" +typedef struct stat cs_stat_t; +#define DIRSEP '/' +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 +#define __cdecl + +#ifndef MG_NET_IF +#include +#if LWIP_SOCKET +#define MG_NET_IF MG_NET_IF_SOCKET +#else +#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL +#endif +#define MG_LWIP 1 +#elif MG_NET_IF == MG_NET_IF_SIMPLELINK +#include "common/platforms/simplelink/cs_simplelink.h" +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#ifdef __TI_COMPILER_VERSION__ +/* As of 5.2.8, TI compiler does not support va_copy() yet. */ +#define va_copy(apc, ap) ((apc) = (ap)) +#endif /* __TI_COMPILER_VERSION__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* CS_PLATFORM == CS_P_TM4C129 */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_TM4C129_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_unix.h b/src/mongoose-6.11/src/common/platforms/platform_unix.h new file mode 100644 index 0000000..59a04e3 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_unix.h @@ -0,0 +1,145 @@ +#ifndef CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_ +#if CS_PLATFORM == CS_P_UNIX + +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 600 +#endif + +/* wants this for C++ */ +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif + +/* C++ wants that for INT64_MAX */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif + +/* Enable fseeko() and ftello() functions */ +#ifndef _LARGEFILE_SOURCE +#define _LARGEFILE_SOURCE +#endif + +/* Enable 64-bit file offsets */ +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#ifndef BYTE_ORDER +#define LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN +#define BIG_ENDIAN __DARWIN_BIG_ENDIAN +#define PDP_ENDIAN __DARWIN_PDP_ENDIAN +#define BYTE_ORDER __DARWIN_BYTE_ORDER +#endif +#endif + +/* + * osx correctly avoids defining strtoll when compiling in strict ansi mode. + * c++ 11 standard defines strtoll as well. + * We require strtoll, and if your embedded pre-c99 compiler lacks one, please + * implement a shim. + */ +#if !(defined(__cplusplus) && __cplusplus >= 201103L) && \ + !(defined(__DARWIN_C_LEVEL) && __DARWIN_C_LEVEL >= 200809L) +long long strtoll(const char *, char **, int); +#endif + +typedef int sock_t; +#define INVALID_SOCKET (-1) +#define SIZE_T_FMT "zu" +typedef struct stat cs_stat_t; +#define DIRSEP '/' +#define to64(x) strtoll(x, NULL, 10) +#define INT64_FMT PRId64 +#define INT64_X_FMT PRIx64 + +#ifndef __cdecl +#define __cdecl +#endif + +#ifndef va_copy +#ifdef __va_copy +#define va_copy __va_copy +#else +#define va_copy(x, y) (x) = (y) +#endif +#endif + +#define closesocket(x) close(x) + +#ifndef MG_MAX_HTTP_REQUEST_SIZE +#define MG_MAX_HTTP_REQUEST_SIZE 8192 +#endif + +#ifndef MG_MAX_HTTP_SEND_MBUF +#define MG_MAX_HTTP_SEND_MBUF 4096 +#endif + +#ifndef MG_MAX_HTTP_HEADERS +#define MG_MAX_HTTP_HEADERS 40 +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#ifndef MG_ENABLE_BROADCAST +#define MG_ENABLE_BROADCAST 1 +#endif + +#ifndef MG_ENABLE_DIRECTORY_LISTING +#define MG_ENABLE_DIRECTORY_LISTING 1 +#endif + +#ifndef MG_ENABLE_FILESYSTEM +#define MG_ENABLE_FILESYSTEM 1 +#endif + +#ifndef MG_ENABLE_HTTP_CGI +#define MG_ENABLE_HTTP_CGI MG_ENABLE_FILESYSTEM +#endif + +#ifndef MG_NET_IF +#define MG_NET_IF MG_NET_IF_SOCKET +#endif + +#ifndef MG_HOSTS_FILE_NAME +#define MG_HOSTS_FILE_NAME "/etc/hosts" +#endif + +#ifndef MG_RESOLV_CONF_FILE_NAME +#define MG_RESOLV_CONF_FILE_NAME "/etc/resolv.conf" +#endif + +#endif /* CS_PLATFORM == CS_P_UNIX */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_wince.h b/src/mongoose-6.11/src/common/platforms/platform_wince.h new file mode 100644 index 0000000..48c51f6 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_wince.h @@ -0,0 +1,201 @@ +#ifndef CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_ + +#if CS_PLATFORM == CS_P_WINCE + +/* + * MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) + * MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) + * MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) + * MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) + * MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) + * MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) + * MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio 2003) + * MSVC++ 7.0 _MSC_VER == 1300 + * MSVC++ 6.0 _MSC_VER == 1200 + * MSVC++ 5.0 _MSC_VER == 1100 + */ +#pragma warning(disable : 4127) /* FD_SET() emits warning, disable it */ +#pragma warning(disable : 4204) /* missing c99 support */ + +#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS +#define _WINSOCK_DEPRECATED_NO_WARNINGS 1 +#endif + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "ws2.lib") /* Linking with WinCE winsock library */ + +#include +#include +#include + +#define strdup _strdup + +#ifndef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#endif + +#ifndef EWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK +#endif + +#ifndef EAGAIN +#define EAGAIN EWOULDBLOCK +#endif + +#ifndef __func__ +#define STRX(x) #x +#define STR(x) STRX(x) +#define __func__ __FILE__ ":" STR(__LINE__) +#endif + +#define snprintf _snprintf +#define fileno _fileno +#define vsnprintf _vsnprintf +#define sleep(x) Sleep((x) *1000) +#define to64(x) _atoi64(x) +#define rmdir _rmdir + +#if defined(_MSC_VER) && _MSC_VER >= 1400 +#define fseeko(x, y, z) _fseeki64((x), (y), (z)) +#else +#define fseeko(x, y, z) fseek((x), (y), (z)) +#endif + +typedef int socklen_t; + +#if _MSC_VER >= 1700 +#include +#else +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef int int32_t; +typedef unsigned int uint32_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#endif + +typedef SOCKET sock_t; +typedef uint32_t in_addr_t; + +#ifndef UINT16_MAX +#define UINT16_MAX 65535 +#endif + +#ifndef UINT32_MAX +#define UINT32_MAX 4294967295 +#endif + +#ifndef pid_t +#define pid_t HANDLE +#endif + +#define INT64_FMT "I64d" +#define INT64_X_FMT "I64x" +/* TODO(alashkin): check if this is correct */ +#define SIZE_T_FMT "u" + +#define DIRSEP '\\' +#define CS_DEFINE_DIRENT + +#ifndef va_copy +#ifdef __va_copy +#define va_copy __va_copy +#else +#define va_copy(x, y) (x) = (y) +#endif +#endif + +#ifndef MG_MAX_HTTP_REQUEST_SIZE +#define MG_MAX_HTTP_REQUEST_SIZE 8192 +#endif + +#ifndef MG_MAX_HTTP_SEND_MBUF +#define MG_MAX_HTTP_SEND_MBUF 4096 +#endif + +#ifndef MG_MAX_HTTP_HEADERS +#define MG_MAX_HTTP_HEADERS 40 +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#define abort() DebugBreak(); + +#ifndef BUFSIZ +#define BUFSIZ 4096 +#endif +/* + * Explicitly disabling MG_ENABLE_THREADS for WinCE + * because they are enabled for _WIN32 by default + */ +#ifndef MG_ENABLE_THREADS +#define MG_ENABLE_THREADS 0 +#endif + +#ifndef MG_ENABLE_FILESYSTEM +#define MG_ENABLE_FILESYSTEM 1 +#endif + +#ifndef MG_NET_IF +#define MG_NET_IF MG_NET_IF_SOCKET +#endif + +typedef struct _stati64 { + uint32_t st_mtime; + uint32_t st_size; + uint32_t st_mode; +} cs_stat_t; + +/* + * WinCE 6.0 has a lot of useful definitions in ATL (not windows.h) headers + * use #ifdefs to avoid conflicts + */ + +#ifndef ENOENT +#define ENOENT ERROR_PATH_NOT_FOUND +#endif + +#ifndef EACCES +#define EACCES ERROR_ACCESS_DENIED +#endif + +#ifndef ENOMEM +#define ENOMEM ERROR_NOT_ENOUGH_MEMORY +#endif + +#ifndef _UINTPTR_T_DEFINED +typedef unsigned int *uintptr_t; +#endif + +#define _S_IFREG 2 +#define _S_IFDIR 4 + +#ifndef S_ISDIR +#define S_ISDIR(x) (((x) &_S_IFDIR) != 0) +#endif + +#ifndef S_ISREG +#define S_ISREG(x) (((x) &_S_IFREG) != 0) +#endif + +int open(const char *filename, int oflag, int pmode); +int _wstati64(const wchar_t *path, cs_stat_t *st); +const char *strerror(); + +#endif /* CS_PLATFORM == CS_P_WINCE */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_windows.h b/src/mongoose-6.11/src/common/platforms/platform_windows.h new file mode 100644 index 0000000..3b300bd --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/platform_windows.h @@ -0,0 +1,183 @@ +#ifndef CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_ +#define CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_ +#if CS_PLATFORM == CS_P_WINDOWS + +/* + * MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) + * MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) + * MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) + * MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) + * MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) + * MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) + * MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio 2003) + * MSVC++ 7.0 _MSC_VER == 1300 + * MSVC++ 6.0 _MSC_VER == 1200 + * MSVC++ 5.0 _MSC_VER == 1100 + */ +#ifdef _MSC_VER +#pragma warning(disable : 4127) /* FD_SET() emits warning, disable it */ +#pragma warning(disable : 4204) /* missing c99 support */ +#endif + +#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS +#define _WINSOCK_DEPRECATED_NO_WARNINGS 1 +#endif + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */ +#endif + +#include +#include +#include +#include + +#if _MSC_VER < 1700 +typedef int bool; +#else +#include +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1800 +#define strdup _strdup +#endif + +#ifndef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#endif +#ifndef EWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK +#endif +#ifndef __func__ +#define STRX(x) #x +#define STR(x) STRX(x) +#define __func__ __FILE__ ":" STR(__LINE__) +#endif +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#define to64(x) _atoi64(x) +#if !defined(__MINGW32__) && !defined(__MINGW64__) +#define popen(x, y) _popen((x), (y)) +#define pclose(x) _pclose(x) +#define fileno _fileno +#endif +#if defined(_MSC_VER) && _MSC_VER >= 1400 +#define fseeko(x, y, z) _fseeki64((x), (y), (z)) +#else +#define fseeko(x, y, z) fseek((x), (y), (z)) +#endif +#if defined(_MSC_VER) && _MSC_VER <= 1200 +typedef unsigned long uintptr_t; +typedef long intptr_t; +#endif +typedef int socklen_t; +#if _MSC_VER >= 1700 +#include +#else +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef int int32_t; +typedef unsigned int uint32_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#endif +typedef SOCKET sock_t; +typedef uint32_t in_addr_t; +#ifndef UINT16_MAX +#define UINT16_MAX 65535 +#endif +#ifndef UINT32_MAX +#define UINT32_MAX 4294967295 +#endif +#ifndef pid_t +#define pid_t HANDLE +#endif +#define INT64_FMT "I64d" +#define INT64_X_FMT "I64x" +#define SIZE_T_FMT "Iu" +typedef struct _stati64 cs_stat_t; +#ifndef S_ISDIR +#define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR) +#endif +#ifndef S_ISREG +#define S_ISREG(x) (((x) &_S_IFMT) == _S_IFREG) +#endif +#define DIRSEP '\\' +#define CS_DEFINE_DIRENT + +#ifndef va_copy +#ifdef __va_copy +#define va_copy __va_copy +#else +#define va_copy(x, y) (x) = (y) +#endif +#endif + +#ifndef MG_MAX_HTTP_REQUEST_SIZE +#define MG_MAX_HTTP_REQUEST_SIZE 8192 +#endif + +#ifndef MG_MAX_HTTP_SEND_MBUF +#define MG_MAX_HTTP_SEND_MBUF 4096 +#endif + +#ifndef MG_MAX_HTTP_HEADERS +#define MG_MAX_HTTP_HEADERS 40 +#endif + +#ifndef CS_ENABLE_STDIO +#define CS_ENABLE_STDIO 1 +#endif + +#ifndef MG_ENABLE_BROADCAST +#define MG_ENABLE_BROADCAST 1 +#endif + +#ifndef MG_ENABLE_DIRECTORY_LISTING +#define MG_ENABLE_DIRECTORY_LISTING 1 +#endif + +#ifndef MG_ENABLE_FILESYSTEM +#define MG_ENABLE_FILESYSTEM 1 +#endif + +#ifndef MG_ENABLE_HTTP_CGI +#define MG_ENABLE_HTTP_CGI MG_ENABLE_FILESYSTEM +#endif + +#ifndef MG_NET_IF +#define MG_NET_IF MG_NET_IF_SOCKET +#endif + +unsigned int sleep(unsigned int seconds); + +/* https://stackoverflow.com/questions/16647819/timegm-cross-platform */ +#define timegm _mkgmtime + +#define gmtime_r(a, b) \ + do { \ + *(b) = *gmtime(a); \ + } while (0) + +#endif /* CS_PLATFORM == CS_P_WINDOWS */ +#endif /* CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/cs_simplelink.h b/src/mongoose-6.11/src/common/platforms/simplelink/cs_simplelink.h new file mode 100644 index 0000000..ed6afa2 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/simplelink/cs_simplelink.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_ +#define CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_ + +#if defined(MG_NET_IF) && MG_NET_IF == MG_NET_IF_SIMPLELINK + +/* If simplelink.h is already included, all bets are off. */ +#if !defined(__SIMPLELINK_H__) + +#include + +#ifndef __TI_COMPILER_VERSION__ +#undef __CONCAT +#undef FD_CLR +#undef FD_ISSET +#undef FD_SET +#undef FD_SETSIZE +#undef FD_ZERO +#undef fd_set +#endif + +#if CS_PLATFORM == CS_P_CC3220 +#include +#include +#include +#include +#else +/* We want to disable SL_INC_STD_BSD_API_NAMING, so we include user.h ourselves + * and undef it. */ +#define PROVISIONING_API_H_ +#include +#undef PROVISIONING_API_H_ +#undef SL_INC_STD_BSD_API_NAMING + +#include +#include +#endif /* CS_PLATFORM == CS_P_CC3220 */ + +/* Now define only the subset of the BSD API that we use. + * Notably, close(), read() and write() are not defined. */ +#define AF_INET SL_AF_INET + +#define socklen_t SlSocklen_t +#define sockaddr SlSockAddr_t +#define sockaddr_in SlSockAddrIn_t +#define in_addr SlInAddr_t + +#define SOCK_STREAM SL_SOCK_STREAM +#define SOCK_DGRAM SL_SOCK_DGRAM + +#define htonl sl_Htonl +#define ntohl sl_Ntohl +#define htons sl_Htons +#define ntohs sl_Ntohs + +#ifndef EACCES +#define EACCES SL_EACCES +#endif +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT SL_EAFNOSUPPORT +#endif +#ifndef EAGAIN +#define EAGAIN SL_EAGAIN +#endif +#ifndef EBADF +#define EBADF SL_EBADF +#endif +#ifndef EINVAL +#define EINVAL SL_EINVAL +#endif +#ifndef ENOMEM +#define ENOMEM SL_ENOMEM +#endif +#ifndef EWOULDBLOCK +#define EWOULDBLOCK SL_EWOULDBLOCK +#endif + +#define SOMAXCONN 8 + +#ifdef __cplusplus +extern "C" { +#endif + +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); +char *inet_ntoa(struct in_addr in); +int inet_pton(int af, const char *src, void *dst); + +struct mg_mgr; +struct mg_connection; + +typedef void (*mg_init_cb)(struct mg_mgr *mgr); +bool mg_start_task(int priority, int stack_size, mg_init_cb mg_init); + +void mg_run_in_task(void (*cb)(struct mg_mgr *mgr, void *arg), void *cb_arg); + +int sl_fs_init(void); + +void sl_restart_cb(struct mg_mgr *mgr); + +int sl_set_ssl_opts(int sock, struct mg_connection *nc); + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(__SIMPLELINK_H__) */ + +/* Compatibility with older versions of SimpleLink */ +#if SL_MAJOR_VERSION_NUM < 2 + +#define SL_ERROR_BSD_EAGAIN SL_EAGAIN +#define SL_ERROR_BSD_EALREADY SL_EALREADY +#define SL_ERROR_BSD_ENOPROTOOPT SL_ENOPROTOOPT +#define SL_ERROR_BSD_ESECDATEERROR SL_ESECDATEERROR +#define SL_ERROR_BSD_ESECSNOVERIFY SL_ESECSNOVERIFY +#define SL_ERROR_FS_FAILED_TO_ALLOCATE_MEM SL_FS_ERR_FAILED_TO_ALLOCATE_MEM +#define SL_ERROR_FS_FILE_HAS_NOT_BEEN_CLOSE_CORRECTLY \ + SL_FS_FILE_HAS_NOT_BEEN_CLOSE_CORRECTLY +#define SL_ERROR_FS_FILE_NAME_EXIST SL_FS_FILE_NAME_EXIST +#define SL_ERROR_FS_FILE_NOT_EXISTS SL_FS_ERR_FILE_NOT_EXISTS +#define SL_ERROR_FS_NO_AVAILABLE_NV_INDEX SL_FS_ERR_NO_AVAILABLE_NV_INDEX +#define SL_ERROR_FS_NOT_ENOUGH_STORAGE_SPACE SL_FS_ERR_NO_AVAILABLE_BLOCKS +#define SL_ERROR_FS_NOT_SUPPORTED SL_FS_ERR_NOT_SUPPORTED +#define SL_ERROR_FS_WRONG_FILE_NAME SL_FS_WRONG_FILE_NAME +#define SL_ERROR_FS_INVALID_HANDLE SL_FS_ERR_INVALID_HANDLE +#define SL_NETCFG_MAC_ADDRESS_GET SL_MAC_ADDRESS_GET +#define SL_SOCKET_FD_ZERO SL_FD_ZERO +#define SL_SOCKET_FD_SET SL_FD_SET +#define SL_SOCKET_FD_ISSET SL_FD_ISSET +#define SL_SO_SECURE_DOMAIN_NAME_VERIFICATION SO_SECURE_DOMAIN_NAME_VERIFICATION + +#define SL_FS_READ FS_MODE_OPEN_READ +#define SL_FS_WRITE FS_MODE_OPEN_WRITE + +#define SL_FI_FILE_SIZE(fi) ((fi).FileLen) +#define SL_FI_FILE_MAX_SIZE(fi) ((fi).AllocatedLen) + +#define SlDeviceVersion_t SlVersionFull +#define sl_DeviceGet sl_DevGet +#define SL_DEVICE_GENERAL SL_DEVICE_GENERAL_CONFIGURATION +#define SL_LEN_TYPE _u8 +#define SL_OPT_TYPE _u8 + +#else /* SL_MAJOR_VERSION_NUM >= 2 */ + +#define FS_MODE_OPEN_CREATE(max_size, flag) \ + (SL_FS_CREATE | SL_FS_CREATE_MAX_SIZE(max_size)) +#define SL_FI_FILE_SIZE(fi) ((fi).Len) +#define SL_FI_FILE_MAX_SIZE(fi) ((fi).MaxSize) + +#define SL_LEN_TYPE _u16 +#define SL_OPT_TYPE _u16 + +#endif /* SL_MAJOR_VERSION_NUM < 2 */ + +int slfs_open(const unsigned char *fname, uint32_t flags); + +#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */ + +#endif /* CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs.c b/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs.c new file mode 100644 index 0000000..fc09342 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs.c @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_NET_IF == MG_NET_IF_SIMPLELINK && \ + (defined(MG_FS_SLFS) || defined(MG_FS_SPIFFS)) + +int set_errno(int e) { + errno = e; + return (e == 0 ? 0 : -1); +} + +const char *drop_dir(const char *fname, bool *is_slfs) { + if (is_slfs != NULL) { + *is_slfs = (strncmp(fname, "SL:", 3) == 0); + if (*is_slfs) fname += 3; + } + /* Drop "./", if any */ + if (fname[0] == '.' && fname[1] == '/') { + fname += 2; + } + /* + * Drop / if it is the only one in the path. + * This allows use of /pretend/directories but serves /file.txt as normal. + */ + if (fname[0] == '/' && strchr(fname + 1, '/') == NULL) { + fname++; + } + return fname; +} + +#if !defined(MG_FS_NO_VFS) + +#include +#include +#include +#include +#include +#ifdef __TI_COMPILER_VERSION__ +#include +#endif + +#include "common/cs_dbg.h" +#include "common/platform.h" + +#ifdef CC3200_FS_SPIFFS +#include "cc3200_fs_spiffs.h" +#endif + +#ifdef MG_FS_SLFS +#include "sl_fs_slfs.h" +#endif + +#define NUM_SYS_FDS 3 +#define SPIFFS_FD_BASE 10 +#define SLFS_FD_BASE 100 + +#if !defined(MG_UART_CHAR_PUT) && !defined(MG_UART_WRITE) +#if CS_PLATFORM == CS_P_CC3200 +#include +#include +#include +#include +#include +#define MG_UART_CHAR_PUT(fd, c) MAP_UARTCharPut(UARTA0_BASE, c); +#else +#define MG_UART_WRITE(fd, buf, len) +#endif /* CS_PLATFORM == CS_P_CC3200 */ +#endif /* !MG_UART_CHAR_PUT */ + +enum fd_type { + FD_INVALID, + FD_SYS, +#ifdef CC3200_FS_SPIFFS + FD_SPIFFS, +#endif +#ifdef MG_FS_SLFS + FD_SLFS +#endif +}; +static int fd_type(int fd) { + if (fd >= 0 && fd < NUM_SYS_FDS) return FD_SYS; +#ifdef CC3200_FS_SPIFFS + if (fd >= SPIFFS_FD_BASE && fd < SPIFFS_FD_BASE + MAX_OPEN_SPIFFS_FILES) { + return FD_SPIFFS; + } +#endif +#ifdef MG_FS_SLFS + if (fd >= SLFS_FD_BASE && fd < SLFS_FD_BASE + MAX_OPEN_SLFS_FILES) { + return FD_SLFS; + } +#endif + return FD_INVALID; +} + +#if MG_TI_NO_HOST_INTERFACE +int open(const char *pathname, unsigned flags, int mode) { +#else +int _open(const char *pathname, int flags, mode_t mode) { +#endif + int fd = -1; + bool is_sl; + const char *fname = drop_dir(pathname, &is_sl); + if (is_sl) { +#ifdef MG_FS_SLFS + fd = fs_slfs_open(fname, flags, mode); + if (fd >= 0) fd += SLFS_FD_BASE; +#endif + } else { +#ifdef CC3200_FS_SPIFFS + fd = fs_spiffs_open(fname, flags, mode); + if (fd >= 0) fd += SPIFFS_FD_BASE; +#endif + } + LOG(LL_DEBUG, + ("open(%s, 0x%x) = %d, fname = %s", pathname, flags, fd, fname)); + return fd; +} + +int _stat(const char *pathname, struct stat *st) { + int res = -1; + bool is_sl; + const char *fname = drop_dir(pathname, &is_sl); + memset(st, 0, sizeof(*st)); + /* Simulate statting the root directory. */ + if (fname[0] == '\0' || strcmp(fname, ".") == 0) { + st->st_ino = 0; + st->st_mode = S_IFDIR | 0777; + st->st_nlink = 1; + st->st_size = 0; + return 0; + } + if (is_sl) { +#ifdef MG_FS_SLFS + res = fs_slfs_stat(fname, st); +#endif + } else { +#ifdef CC3200_FS_SPIFFS + res = fs_spiffs_stat(fname, st); +#endif + } + LOG(LL_DEBUG, ("stat(%s) = %d; fname = %s", pathname, res, fname)); + return res; +} + +#if MG_TI_NO_HOST_INTERFACE +int close(int fd) { +#else +int _close(int fd) { +#endif + int r = -1; + switch (fd_type(fd)) { + case FD_INVALID: + r = set_errno(EBADF); + break; + case FD_SYS: + r = set_errno(EACCES); + break; +#ifdef CC3200_FS_SPIFFS + case FD_SPIFFS: + r = fs_spiffs_close(fd - SPIFFS_FD_BASE); + break; +#endif +#ifdef MG_FS_SLFS + case FD_SLFS: + r = fs_slfs_close(fd - SLFS_FD_BASE); + break; +#endif + } + DBG(("close(%d) = %d", fd, r)); + return r; +} + +#if MG_TI_NO_HOST_INTERFACE +off_t lseek(int fd, off_t offset, int whence) { +#else +off_t _lseek(int fd, off_t offset, int whence) { +#endif + int r = -1; + switch (fd_type(fd)) { + case FD_INVALID: + r = set_errno(EBADF); + break; + case FD_SYS: + r = set_errno(ESPIPE); + break; +#ifdef CC3200_FS_SPIFFS + case FD_SPIFFS: + r = fs_spiffs_lseek(fd - SPIFFS_FD_BASE, offset, whence); + break; +#endif +#ifdef MG_FS_SLFS + case FD_SLFS: + r = fs_slfs_lseek(fd - SLFS_FD_BASE, offset, whence); + break; +#endif + } + DBG(("lseek(%d, %d, %d) = %d", fd, (int) offset, whence, r)); + return r; +} + +int _fstat(int fd, struct stat *s) { + int r = -1; + memset(s, 0, sizeof(*s)); + switch (fd_type(fd)) { + case FD_INVALID: + r = set_errno(EBADF); + break; + case FD_SYS: { + /* Create barely passable stats for STD{IN,OUT,ERR}. */ + memset(s, 0, sizeof(*s)); + s->st_ino = fd; + s->st_mode = S_IFCHR | 0666; + r = 0; + break; + } +#ifdef CC3200_FS_SPIFFS + case FD_SPIFFS: + r = fs_spiffs_fstat(fd - SPIFFS_FD_BASE, s); + break; +#endif +#ifdef MG_FS_SLFS + case FD_SLFS: + r = fs_slfs_fstat(fd - SLFS_FD_BASE, s); + break; +#endif + } + DBG(("fstat(%d) = %d", fd, r)); + return r; +} + +#if MG_TI_NO_HOST_INTERFACE +int read(int fd, char *buf, unsigned count) { +#else +ssize_t _read(int fd, void *buf, size_t count) { +#endif + int r = -1; + switch (fd_type(fd)) { + case FD_INVALID: + r = set_errno(EBADF); + break; + case FD_SYS: { + if (fd != 0) { + r = set_errno(EACCES); + break; + } + /* Should we allow reading from stdin = uart? */ + r = set_errno(ENOTSUP); + break; + } +#ifdef CC3200_FS_SPIFFS + case FD_SPIFFS: + r = fs_spiffs_read(fd - SPIFFS_FD_BASE, buf, count); + break; +#endif +#ifdef MG_FS_SLFS + case FD_SLFS: + r = fs_slfs_read(fd - SLFS_FD_BASE, buf, count); + break; +#endif + } + DBG(("read(%d, %u) = %d", fd, count, r)); + return r; +} + +#if MG_TI_NO_HOST_INTERFACE +int write(int fd, const char *buf, unsigned count) { +#else +ssize_t _write(int fd, const void *buf, size_t count) { +#endif + int r = -1; + switch (fd_type(fd)) { + case FD_INVALID: + r = set_errno(EBADF); + break; + case FD_SYS: { + if (fd == 0) { + r = set_errno(EACCES); + break; + } +#ifdef MG_UART_WRITE + MG_UART_WRITE(fd, buf, count); +#elif defined(MG_UART_CHAR_PUT) + { + size_t i; + for (i = 0; i < count; i++) { + const char c = ((const char *) buf)[i]; + if (c == '\n') MG_UART_CHAR_PUT(fd, '\r'); + MG_UART_CHAR_PUT(fd, c); + } + } +#endif + r = count; + break; + } +#ifdef CC3200_FS_SPIFFS + case FD_SPIFFS: + r = fs_spiffs_write(fd - SPIFFS_FD_BASE, buf, count); + break; +#endif +#ifdef MG_FS_SLFS + case FD_SLFS: + r = fs_slfs_write(fd - SLFS_FD_BASE, buf, count); + break; +#endif + } + return r; +} + +/* + * On Newlib we override rename directly too, because the default + * implementation using _link and _unlink doesn't work for us. + */ +#if MG_TI_NO_HOST_INTERFACE || defined(_NEWLIB_VERSION) +int rename(const char *frompath, const char *topath) { + int r = -1; + bool is_sl_from, is_sl_to; + const char *from = drop_dir(frompath, &is_sl_from); + const char *to = drop_dir(topath, &is_sl_to); + if (is_sl_from || is_sl_to) { + set_errno(ENOTSUP); + } else { +#ifdef CC3200_FS_SPIFFS + r = fs_spiffs_rename(from, to); +#endif + } + DBG(("rename(%s, %s) = %d", from, to, r)); + return r; +} +#endif /* MG_TI_NO_HOST_INTERFACE || defined(_NEWLIB_VERSION) */ + +#if MG_TI_NO_HOST_INTERFACE +int unlink(const char *pathname) { +#else +int _unlink(const char *pathname) { +#endif + int r = -1; + bool is_sl; + const char *fname = drop_dir(pathname, &is_sl); + if (is_sl) { +#ifdef MG_FS_SLFS + r = fs_slfs_unlink(fname); +#endif + } else { +#ifdef CC3200_FS_SPIFFS + r = fs_spiffs_unlink(fname); +#endif + } + DBG(("unlink(%s) = %d, fname = %s", pathname, r, fname)); + return r; +} + +#ifdef CC3200_FS_SPIFFS /* FailFS does not support listing files. */ +DIR *opendir(const char *dir_name) { + DIR *r = NULL; + bool is_sl; + drop_dir(dir_name, &is_sl); + if (is_sl) { + r = NULL; + set_errno(ENOTSUP); + } else { + r = fs_spiffs_opendir(dir_name); + } + DBG(("opendir(%s) = %p", dir_name, r)); + return r; +} + +struct dirent *readdir(DIR *dir) { + struct dirent *res = fs_spiffs_readdir(dir); + DBG(("readdir(%p) = %p", dir, res)); + return res; +} + +int closedir(DIR *dir) { + int res = fs_spiffs_closedir(dir); + DBG(("closedir(%p) = %d", dir, res)); + return res; +} + +int rmdir(const char *path) { + return fs_spiffs_rmdir(path); +} + +int mkdir(const char *path, mode_t mode) { + (void) path; + (void) mode; + /* for spiffs supports only root dir, which comes from mongoose as '.' */ + return (strlen(path) == 1 && *path == '.') ? 0 : ENOTDIR; +} +#endif + +int sl_fs_init(void) { + int ret = 1; +#ifdef __TI_COMPILER_VERSION__ +#ifdef MG_FS_SLFS +#pragma diag_push +#pragma diag_suppress 169 /* Nothing we can do about the prototype mismatch. \ + */ + ret = (add_device("SL", _MSA, fs_slfs_open, fs_slfs_close, fs_slfs_read, + fs_slfs_write, fs_slfs_lseek, fs_slfs_unlink, + fs_slfs_rename) == 0); +#pragma diag_pop +#endif +#endif + return ret; +} + +#endif /* !defined(MG_FS_NO_VFS) */ +#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK && (defined(MG_FS_SLFS) || \ + defined(MG_FS_SPIFFS)) */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs_slfs.c b/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs_slfs.c new file mode 100644 index 0000000..2754b6b --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs_slfs.c @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +/* Standard libc interface to TI SimpleLink FS. */ + +#if defined(MG_FS_SLFS) || defined(CC3200_FS_SLFS) + +#include "common/platforms/simplelink/sl_fs_slfs.h" + +#include + +#if CS_PLATFORM == CS_P_CC3200 +#include +#endif + +#include "common/cs_dbg.h" +#include "common/mg_mem.h" + +#if SL_MAJOR_VERSION_NUM < 2 +int slfs_open(const unsigned char *fname, uint32_t flags) { + _i32 fh; + _i32 r = sl_FsOpen(fname, flags, NULL /* token */, &fh); + return (r < 0 ? r : fh); +} +#else /* SL_MAJOR_VERSION_NUM >= 2 */ +int slfs_open(const unsigned char *fname, uint32_t flags) { + return sl_FsOpen(fname, flags, NULL /* token */); +} +#endif + +/* From sl_fs.c */ +int set_errno(int e); +const char *drop_dir(const char *fname, bool *is_slfs); + +/* + * With SLFS, you have to pre-declare max file size. Yes. Really. + * 64K should be enough for everyone. Right? + */ +#ifndef FS_SLFS_MAX_FILE_SIZE +#define FS_SLFS_MAX_FILE_SIZE (64 * 1024) +#endif + +struct sl_file_size_hint { + char *name; + size_t size; +}; + +struct sl_fd_info { + _i32 fh; + _off_t pos; + size_t size; +}; + +static struct sl_fd_info s_sl_fds[MAX_OPEN_SLFS_FILES]; +static struct sl_file_size_hint s_sl_file_size_hints[MAX_OPEN_SLFS_FILES]; + +static int sl_fs_to_errno(_i32 r) { + DBG(("SL error: %d", (int) r)); + switch (r) { + case SL_FS_OK: + return 0; + case SL_ERROR_FS_FILE_NAME_EXIST: + return EEXIST; + case SL_ERROR_FS_WRONG_FILE_NAME: + return EINVAL; + case SL_ERROR_FS_NO_AVAILABLE_NV_INDEX: + case SL_ERROR_FS_NOT_ENOUGH_STORAGE_SPACE: + return ENOSPC; + case SL_ERROR_FS_FAILED_TO_ALLOCATE_MEM: + return ENOMEM; + case SL_ERROR_FS_FILE_NOT_EXISTS: + return ENOENT; + case SL_ERROR_FS_NOT_SUPPORTED: + return ENOTSUP; + } + return ENXIO; +} + +int fs_slfs_open(const char *pathname, int flags, mode_t mode) { + int fd; + for (fd = 0; fd < MAX_OPEN_SLFS_FILES; fd++) { + if (s_sl_fds[fd].fh <= 0) break; + } + if (fd >= MAX_OPEN_SLFS_FILES) return set_errno(ENOMEM); + struct sl_fd_info *fi = &s_sl_fds[fd]; + + /* + * Apply path manipulations again, in case we got here directly + * (via TI libc's "add_device"). + */ + pathname = drop_dir(pathname, NULL); + + _u32 am = 0; + fi->size = (size_t) -1; + int rw = (flags & 3); + size_t new_size = FS_SLFS_MAX_FILE_SIZE; + if (rw == O_RDONLY) { + SlFsFileInfo_t sl_fi; + _i32 r = sl_FsGetInfo((const _u8 *) pathname, 0, &sl_fi); + if (r == SL_FS_OK) { + fi->size = SL_FI_FILE_SIZE(sl_fi); + } + am = SL_FS_READ; + } else { + if (!(flags & O_TRUNC) || (flags & O_APPEND)) { + // FailFS files cannot be opened for append and will be truncated + // when opened for write. + return set_errno(ENOTSUP); + } + if (flags & O_CREAT) { + size_t i; + for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) { + if (s_sl_file_size_hints[i].name != NULL && + strcmp(s_sl_file_size_hints[i].name, pathname) == 0) { + new_size = s_sl_file_size_hints[i].size; + MG_FREE(s_sl_file_size_hints[i].name); + s_sl_file_size_hints[i].name = NULL; + break; + } + } + am = FS_MODE_OPEN_CREATE(new_size, 0); + } else { + am = SL_FS_WRITE; + } + } + fi->fh = slfs_open((_u8 *) pathname, am); + LOG(LL_DEBUG, ("sl_FsOpen(%s, 0x%x) sz %u = %d", pathname, (int) am, + (unsigned int) new_size, (int) fi->fh)); + int r; + if (fi->fh >= 0) { + fi->pos = 0; + r = fd; + } else { + r = set_errno(sl_fs_to_errno(fi->fh)); + } + return r; +} + +int fs_slfs_close(int fd) { + struct sl_fd_info *fi = &s_sl_fds[fd]; + if (fi->fh <= 0) return set_errno(EBADF); + _i32 r = sl_FsClose(fi->fh, NULL, NULL, 0); + LOG(LL_DEBUG, ("sl_FsClose(%d) = %d", (int) fi->fh, (int) r)); + s_sl_fds[fd].fh = -1; + return set_errno(sl_fs_to_errno(r)); +} + +ssize_t fs_slfs_read(int fd, void *buf, size_t count) { + struct sl_fd_info *fi = &s_sl_fds[fd]; + if (fi->fh <= 0) return set_errno(EBADF); + /* Simulate EOF. sl_FsRead @ file_size return SL_FS_ERR_OFFSET_OUT_OF_RANGE. + */ + if (fi->pos == fi->size) return 0; + _i32 r = sl_FsRead(fi->fh, fi->pos, buf, count); + DBG(("sl_FsRead(%d, %d, %d) = %d", (int) fi->fh, (int) fi->pos, (int) count, + (int) r)); + if (r >= 0) { + fi->pos += r; + return r; + } + return set_errno(sl_fs_to_errno(r)); +} + +ssize_t fs_slfs_write(int fd, const void *buf, size_t count) { + struct sl_fd_info *fi = &s_sl_fds[fd]; + if (fi->fh <= 0) return set_errno(EBADF); + _i32 r = sl_FsWrite(fi->fh, fi->pos, (_u8 *) buf, count); + DBG(("sl_FsWrite(%d, %d, %d) = %d", (int) fi->fh, (int) fi->pos, (int) count, + (int) r)); + if (r >= 0) { + fi->pos += r; + return r; + } + return set_errno(sl_fs_to_errno(r)); +} + +int fs_slfs_stat(const char *pathname, struct stat *s) { + SlFsFileInfo_t sl_fi; + /* + * Apply path manipulations again, in case we got here directly + * (via TI libc's "add_device"). + */ + pathname = drop_dir(pathname, NULL); + _i32 r = sl_FsGetInfo((const _u8 *) pathname, 0, &sl_fi); + if (r == SL_FS_OK) { + s->st_mode = S_IFREG | 0666; + s->st_nlink = 1; + s->st_size = SL_FI_FILE_SIZE(sl_fi); + return 0; + } + return set_errno(sl_fs_to_errno(r)); +} + +int fs_slfs_fstat(int fd, struct stat *s) { + struct sl_fd_info *fi = &s_sl_fds[fd]; + if (fi->fh <= 0) return set_errno(EBADF); + s->st_mode = 0666; + s->st_mode = S_IFREG | 0666; + s->st_nlink = 1; + s->st_size = fi->size; + return 0; +} + +off_t fs_slfs_lseek(int fd, off_t offset, int whence) { + if (s_sl_fds[fd].fh <= 0) return set_errno(EBADF); + switch (whence) { + case SEEK_SET: + s_sl_fds[fd].pos = offset; + break; + case SEEK_CUR: + s_sl_fds[fd].pos += offset; + break; + case SEEK_END: + return set_errno(ENOTSUP); + } + return 0; +} + +int fs_slfs_unlink(const char *pathname) { + /* + * Apply path manipulations again, in case we got here directly + * (via TI libc's "add_device"). + */ + pathname = drop_dir(pathname, NULL); + return set_errno(sl_fs_to_errno(sl_FsDel((const _u8 *) pathname, 0))); +} + +int fs_slfs_rename(const char *from, const char *to) { + return set_errno(ENOTSUP); +} + +void fs_slfs_set_new_file_size(const char *name, size_t size) { + int i; + for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) { + if (s_sl_file_size_hints[i].name == NULL) { + DBG(("File size hint: %s %d", name, (int) size)); + s_sl_file_size_hints[i].name = strdup(name); + s_sl_file_size_hints[i].size = size; + break; + } + } +} + +#endif /* defined(MG_FS_SLFS) || defined(CC3200_FS_SLFS) */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs_slfs.h b/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs_slfs.h new file mode 100644 index 0000000..0d8a4b1 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs_slfs.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_SIMPLELINK_SL_FS_SLFS_H_ +#define CS_COMMON_PLATFORMS_SIMPLELINK_SL_FS_SLFS_H_ + +#if defined(MG_FS_SLFS) + +#include +#ifndef __TI_COMPILER_VERSION__ +#include +#include +#endif + +#define MAX_OPEN_SLFS_FILES 8 + +/* Indirect libc interface - same functions, different names. */ +int fs_slfs_open(const char *pathname, int flags, mode_t mode); +int fs_slfs_close(int fd); +ssize_t fs_slfs_read(int fd, void *buf, size_t count); +ssize_t fs_slfs_write(int fd, const void *buf, size_t count); +int fs_slfs_stat(const char *pathname, struct stat *s); +int fs_slfs_fstat(int fd, struct stat *s); +off_t fs_slfs_lseek(int fd, off_t offset, int whence); +int fs_slfs_unlink(const char *filename); +int fs_slfs_rename(const char *from, const char *to); + +void fs_slfs_set_new_file_size(const char *name, size_t size); + +#endif /* defined(MG_FS_SLFS) */ + +#endif /* CS_COMMON_PLATFORMS_SIMPLELINK_SL_FS_SLFS_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_mg_task.c b/src/mongoose-6.11/src/common/platforms/simplelink/sl_mg_task.c new file mode 100644 index 0000000..f9c7671 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/simplelink/sl_mg_task.c @@ -0,0 +1,52 @@ +#if MG_NET_IF == MG_NET_IF_SIMPLELINK && !defined(MG_SIMPLELINK_NO_OSI) + +#include "mg_task.h" + +#include + +enum mg_q_msg_type { + MG_Q_MSG_CB, +}; +struct mg_q_msg { + enum mg_q_msg_type type; + void (*cb)(struct mg_mgr *mgr, void *arg); + void *arg; +}; +static OsiMsgQ_t s_mg_q; +static void mg_task(void *arg); + +bool mg_start_task(int priority, int stack_size, mg_init_cb mg_init) { + if (osi_MsgQCreate(&s_mg_q, "MG", sizeof(struct mg_q_msg), 16) != OSI_OK) { + return false; + } + if (osi_TaskCreate(mg_task, (const signed char *) "MG", stack_size, + (void *) mg_init, priority, NULL) != OSI_OK) { + return false; + } + return true; +} + +static void mg_task(void *arg) { + struct mg_mgr mgr; + mg_init_cb mg_init = (mg_init_cb) arg; + mg_mgr_init(&mgr, NULL); + mg_init(&mgr); + while (1) { + struct mg_q_msg msg; + mg_mgr_poll(&mgr, 1); + if (osi_MsgQRead(&s_mg_q, &msg, 1) != OSI_OK) continue; + switch (msg.type) { + case MG_Q_MSG_CB: { + msg.cb(&mgr, msg.arg); + } + } + } +} + +void mg_run_in_task(void (*cb)(struct mg_mgr *mgr, void *arg), void *cb_arg) { + struct mg_q_msg msg = {MG_Q_MSG_CB, cb, cb_arg}; + osi_MsgQWrite(&s_mg_q, &msg, OSI_NO_WAIT); +} + +#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK && !defined(MG_SIMPLELINK_NO_OSI) \ + */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_net_if.c b/src/mongoose-6.11/src/common/platforms/simplelink/sl_net_if.c new file mode 100644 index 0000000..2a39d14 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/simplelink/sl_net_if.c @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#include "common/platforms/simplelink/sl_net_if.h" + +#if MG_ENABLE_NET_IF_SIMPLELINK + +#include "mongoose/src/internal.h" +#include "mongoose/src/util.h" + +#define MG_TCP_RECV_BUFFER_SIZE 1024 +#define MG_UDP_RECV_BUFFER_SIZE 1500 + +static sock_t mg_open_listening_socket(struct mg_connection *nc, + union socket_address *sa, int type, + int proto); + +void mg_set_non_blocking_mode(sock_t sock) { + SlSockNonblocking_t opt; +#if SL_MAJOR_VERSION_NUM < 2 + opt.NonblockingEnabled = 1; +#else + opt.NonBlockingEnabled = 1; +#endif + sl_SetSockOpt(sock, SL_SOL_SOCKET, SL_SO_NONBLOCKING, &opt, sizeof(opt)); +} + +static int mg_is_error(int n) { + return (n < 0 && n != SL_ERROR_BSD_EALREADY && n != SL_ERROR_BSD_EAGAIN); +} + +void mg_sl_if_connect_tcp(struct mg_connection *nc, + const union socket_address *sa) { + int proto = 0; + if (nc->flags & MG_F_SSL) proto = SL_SEC_SOCKET; + sock_t sock = sl_Socket(AF_INET, SOCK_STREAM, proto); + if (sock < 0) { + nc->err = sock; + goto out; + } + mg_sock_set(nc, sock); +#if MG_ENABLE_SSL + nc->err = sl_set_ssl_opts(sock, nc); + if (nc->err != 0) goto out; +#endif + nc->err = sl_Connect(sock, &sa->sa, sizeof(sa->sin)); +out: + DBG(("%p to %s:%d sock %d %d err %d", nc, inet_ntoa(sa->sin.sin_addr), + ntohs(sa->sin.sin_port), nc->sock, proto, nc->err)); +} + +void mg_sl_if_connect_udp(struct mg_connection *nc) { + sock_t sock = sl_Socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + nc->err = sock; + return; + } + mg_sock_set(nc, sock); + nc->err = 0; +} + +int mg_sl_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) { + int proto = 0; + if (nc->flags & MG_F_SSL) proto = SL_SEC_SOCKET; + sock_t sock = mg_open_listening_socket(nc, sa, SOCK_STREAM, proto); + if (sock < 0) return sock; + mg_sock_set(nc, sock); + return 0; +} + +int mg_sl_if_listen_udp(struct mg_connection *nc, union socket_address *sa) { + sock_t sock = mg_open_listening_socket(nc, sa, SOCK_DGRAM, 0); + if (sock == INVALID_SOCKET) return (errno ? errno : 1); + mg_sock_set(nc, sock); + return 0; +} + +void mg_sl_if_tcp_send(struct mg_connection *nc, const void *buf, size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); +} + +void mg_sl_if_udp_send(struct mg_connection *nc, const void *buf, size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); +} + +void mg_sl_if_recved(struct mg_connection *nc, size_t len) { + (void) nc; + (void) len; +} + +int mg_sl_if_create_conn(struct mg_connection *nc) { + (void) nc; + return 1; +} + +void mg_sl_if_destroy_conn(struct mg_connection *nc) { + if (nc->sock == INVALID_SOCKET) return; + /* For UDP, only close outgoing sockets or listeners. */ + if (!(nc->flags & MG_F_UDP) || nc->listener == NULL) { + sl_Close(nc->sock); + } + nc->sock = INVALID_SOCKET; +} + +static int mg_accept_conn(struct mg_connection *lc) { + struct mg_connection *nc; + union socket_address sa; + socklen_t sa_len = sizeof(sa); + sock_t sock = sl_Accept(lc->sock, &sa.sa, &sa_len); + if (sock < 0) { + DBG(("%p: failed to accept: %d", lc, sock)); + return 0; + } + nc = mg_if_accept_new_conn(lc); + if (nc == NULL) { + sl_Close(sock); + return 0; + } + DBG(("%p conn from %s:%d", nc, inet_ntoa(sa.sin.sin_addr), + ntohs(sa.sin.sin_port))); + mg_sock_set(nc, sock); + if (nc->flags & MG_F_SSL) nc->flags |= MG_F_SSL_HANDSHAKE_DONE; + mg_if_accept_tcp_cb(nc, &sa, sa_len); + return 1; +} + +/* 'sa' must be an initialized address to bind to */ +static sock_t mg_open_listening_socket(struct mg_connection *nc, + union socket_address *sa, int type, + int proto) { + int r; + socklen_t sa_len = + (sa->sa.sa_family == AF_INET) ? sizeof(sa->sin) : sizeof(sa->sin6); + sock_t sock = sl_Socket(sa->sa.sa_family, type, proto); + if (sock < 0) return sock; + if ((r = sl_Bind(sock, &sa->sa, sa_len)) < 0) goto clean; + if (type != SOCK_DGRAM) { +#if MG_ENABLE_SSL + if ((r = sl_set_ssl_opts(sock, nc)) < 0) goto clean; +#endif + if ((r = sl_Listen(sock, SOMAXCONN)) < 0) goto clean; + } + mg_set_non_blocking_mode(sock); +clean: + if (r < 0) { + sl_Close(sock); + sock = r; + } + return sock; +} + +static void mg_write_to_socket(struct mg_connection *nc) { + struct mbuf *io = &nc->send_mbuf; + int n = 0; + + if (nc->flags & MG_F_UDP) { + n = sl_SendTo(nc->sock, io->buf, io->len, 0, &nc->sa.sa, + sizeof(nc->sa.sin)); + DBG(("%p %d %d %d %s:%hu", nc, nc->sock, n, errno, + inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port))); + } else { + n = (int) sl_Send(nc->sock, io->buf, io->len, 0); + DBG(("%p %d bytes -> %d", nc, n, nc->sock)); + } + + if (n > 0) { + mg_if_sent_cb(nc, n); + } else if (n < 0 && mg_is_error(n)) { + /* Something went wrong, drop the connection. */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } +} + +MG_INTERNAL size_t recv_avail_size(struct mg_connection *conn, size_t max) { + size_t avail; + if (conn->recv_mbuf_limit < conn->recv_mbuf.len) return 0; + avail = conn->recv_mbuf_limit - conn->recv_mbuf.len; + return avail > max ? max : avail; +} + +static void mg_handle_tcp_read(struct mg_connection *conn) { + int n = 0; + char *buf = (char *) MG_MALLOC(MG_TCP_RECV_BUFFER_SIZE); + + if (buf == NULL) { + DBG(("OOM")); + return; + } + + n = (int) sl_Recv(conn->sock, buf, + recv_avail_size(conn, MG_TCP_RECV_BUFFER_SIZE), 0); + DBG(("%p %d bytes <- %d", conn, n, conn->sock)); + if (n > 0) { + mg_if_recv_tcp_cb(conn, buf, n, 1 /* own */); + } else { + MG_FREE(buf); + } + if (n == 0) { + /* Orderly shutdown of the socket, try flushing output. */ + conn->flags |= MG_F_SEND_AND_CLOSE; + } else if (mg_is_error(n)) { + conn->flags |= MG_F_CLOSE_IMMEDIATELY; + } +} + +static void mg_handle_udp_read(struct mg_connection *nc) { + char *buf = (char *) MG_MALLOC(MG_UDP_RECV_BUFFER_SIZE); + if (buf == NULL) return; + union socket_address sa; + socklen_t sa_len = sizeof(sa); + int n = sl_RecvFrom(nc->sock, buf, MG_UDP_RECV_BUFFER_SIZE, 0, + (SlSockAddr_t *) &sa, &sa_len); + DBG(("%p %d bytes from %s:%d", nc, n, inet_ntoa(nc->sa.sin.sin_addr), + ntohs(nc->sa.sin.sin_port))); + if (n > 0) { + mg_if_recv_udp_cb(nc, buf, n, &sa, sa_len); + } else { + MG_FREE(buf); + } +} + +#define _MG_F_FD_CAN_READ 1 +#define _MG_F_FD_CAN_WRITE 1 << 1 +#define _MG_F_FD_ERROR 1 << 2 + +void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { + DBG(("%p fd=%d fd_flags=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, + fd_flags, nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); + + if (nc->flags & MG_F_CONNECTING) { + if (nc->flags & MG_F_UDP || nc->err != SL_ERROR_BSD_EALREADY) { + mg_if_connect_cb(nc, nc->err); + } else { + /* In SimpleLink, to get status of non-blocking connect() we need to wait + * until socket is writable and repeat the call to sl_Connect again, + * which will now return the real status. */ + if (fd_flags & _MG_F_FD_CAN_WRITE) { + nc->err = sl_Connect(nc->sock, &nc->sa.sa, sizeof(nc->sa.sin)); + DBG(("%p conn res=%d", nc, nc->err)); + if (nc->err == SL_ERROR_BSD_ESECSNOVERIFY || + /* TODO(rojer): Provide API to set the date for verification. */ + nc->err == SL_ERROR_BSD_ESECDATEERROR +#if SL_MAJOR_VERSION_NUM >= 2 + /* Per SWRU455, this error does not mean verification failed, + * it only means that the cert used is not present in the trusted + * root CA catalog. Which is perfectly fine. */ + || + nc->err == SL_ERROR_BSD_ESECUNKNOWNROOTCA +#endif + ) { + nc->err = 0; + } + if (nc->flags & MG_F_SSL && nc->err == 0) { + nc->flags |= MG_F_SSL_HANDSHAKE_DONE; + } + mg_if_connect_cb(nc, nc->err); + } + } + /* Ignore read/write in further processing, we've handled it. */ + fd_flags &= ~(_MG_F_FD_CAN_READ | _MG_F_FD_CAN_WRITE); + } + + if (fd_flags & _MG_F_FD_CAN_READ) { + if (nc->flags & MG_F_UDP) { + mg_handle_udp_read(nc); + } else { + if (nc->flags & MG_F_LISTENING) { + mg_accept_conn(nc); + } else { + mg_handle_tcp_read(nc); + } + } + } + + if (!(nc->flags & MG_F_CLOSE_IMMEDIATELY)) { + if ((fd_flags & _MG_F_FD_CAN_WRITE) && nc->send_mbuf.len > 0) { + mg_write_to_socket(nc); + } + + if (!(fd_flags & (_MG_F_FD_CAN_READ | _MG_F_FD_CAN_WRITE))) { + mg_if_poll(nc, now); + } + mg_if_timer(nc, now); + } + + DBG(("%p after fd=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, nc->flags, + (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); +} + +/* Associate a socket to a connection. */ +void mg_sl_if_sock_set(struct mg_connection *nc, sock_t sock) { + mg_set_non_blocking_mode(sock); + nc->sock = sock; + DBG(("%p %d", nc, sock)); +} + +void mg_sl_if_init(struct mg_iface *iface) { + (void) iface; + DBG(("%p using sl_Select()", iface->mgr)); +} + +void mg_sl_if_free(struct mg_iface *iface) { + (void) iface; +} + +void mg_sl_if_add_conn(struct mg_connection *nc) { + (void) nc; +} + +void mg_sl_if_remove_conn(struct mg_connection *nc) { + (void) nc; +} + +time_t mg_sl_if_poll(struct mg_iface *iface, int timeout_ms) { + struct mg_mgr *mgr = iface->mgr; + double now = mg_time(); + double min_timer; + struct mg_connection *nc, *tmp; + struct SlTimeval_t tv; + SlFdSet_t read_set, write_set, err_set; + sock_t max_fd = INVALID_SOCKET; + int num_fds, num_ev = 0, num_timers = 0; + + SL_SOCKET_FD_ZERO(&read_set); + SL_SOCKET_FD_ZERO(&write_set); + SL_SOCKET_FD_ZERO(&err_set); + + /* + * Note: it is ok to have connections with sock == INVALID_SOCKET in the list, + * e.g. timer-only "connections". + */ + min_timer = 0; + for (nc = mgr->active_connections, num_fds = 0; nc != NULL; nc = tmp) { + tmp = nc->next; + + if (nc->sock != INVALID_SOCKET) { + num_fds++; + + if (!(nc->flags & MG_F_WANT_WRITE) && + nc->recv_mbuf.len < nc->recv_mbuf_limit && + (!(nc->flags & MG_F_UDP) || nc->listener == NULL)) { + SL_SOCKET_FD_SET(nc->sock, &read_set); + if (max_fd == INVALID_SOCKET || nc->sock > max_fd) max_fd = nc->sock; + } + + if (((nc->flags & MG_F_CONNECTING) && !(nc->flags & MG_F_WANT_READ)) || + (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING))) { + SL_SOCKET_FD_SET(nc->sock, &write_set); + SL_SOCKET_FD_SET(nc->sock, &err_set); + if (max_fd == INVALID_SOCKET || nc->sock > max_fd) max_fd = nc->sock; + } + } + + if (nc->ev_timer_time > 0) { + if (num_timers == 0 || nc->ev_timer_time < min_timer) { + min_timer = nc->ev_timer_time; + } + num_timers++; + } + } + + /* + * If there is a timer to be fired earlier than the requested timeout, + * adjust the timeout. + */ + if (num_timers > 0) { + double timer_timeout_ms = (min_timer - mg_time()) * 1000 + 1 /* rounding */; + if (timer_timeout_ms < timeout_ms) { + timeout_ms = timer_timeout_ms; + } + } + if (timeout_ms < 0) timeout_ms = 0; + + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms % 1000) * 1000; + + if (num_fds > 0) { + num_ev = sl_Select((int) max_fd + 1, &read_set, &write_set, &err_set, &tv); + } + + now = mg_time(); + DBG(("sl_Select @ %ld num_ev=%d of %d, timeout=%d", (long) now, num_ev, + num_fds, timeout_ms)); + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + int fd_flags = 0; + if (nc->sock != INVALID_SOCKET) { + if (num_ev > 0) { + fd_flags = + (SL_SOCKET_FD_ISSET(nc->sock, &read_set) && + (!(nc->flags & MG_F_UDP) || nc->listener == NULL) + ? _MG_F_FD_CAN_READ + : 0) | + (SL_SOCKET_FD_ISSET(nc->sock, &write_set) ? _MG_F_FD_CAN_WRITE + : 0) | + (SL_SOCKET_FD_ISSET(nc->sock, &err_set) ? _MG_F_FD_ERROR : 0); + } + /* SimpleLink does not report UDP sockets as writable. */ + if (nc->flags & MG_F_UDP && nc->send_mbuf.len > 0) { + fd_flags |= _MG_F_FD_CAN_WRITE; + } + } + tmp = nc->next; + mg_mgr_handle_conn(nc, fd_flags, now); + } + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + tmp = nc->next; + if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || + (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) { + mg_close_conn(nc); + } + } + + return now; +} + +void mg_sl_if_get_conn_addr(struct mg_connection *nc, int remote, + union socket_address *sa) { + /* SimpleLink does not provide a way to get socket's peer address after + * accept or connect. Address should have been preserved in the connection, + * so we do our best here by using it. */ + if (remote) memcpy(sa, &nc->sa, sizeof(*sa)); +} + +void sl_restart_cb(struct mg_mgr *mgr) { + /* + * SimpleLink has been restarted, meaning all sockets have been invalidated. + * We try our best - we'll restart the listeners, but for outgoing + * connections we have no option but to terminate. + */ + struct mg_connection *nc; + for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) { + if (nc->sock == INVALID_SOCKET) continue; /* Could be a timer */ + if (nc->flags & MG_F_LISTENING) { + DBG(("restarting %p %s:%d", nc, inet_ntoa(nc->sa.sin.sin_addr), + ntohs(nc->sa.sin.sin_port))); + int res = (nc->flags & MG_F_UDP ? mg_sl_if_listen_udp(nc, &nc->sa) + : mg_sl_if_listen_tcp(nc, &nc->sa)); + if (res == 0) continue; + /* Well, we tried and failed. Fall through to closing. */ + } + nc->sock = INVALID_SOCKET; + DBG(("terminating %p %s:%d", nc, inet_ntoa(nc->sa.sin.sin_addr), + ntohs(nc->sa.sin.sin_port))); + /* TODO(rojer): Outgoing UDP? */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } +} + +/* clang-format off */ +#define MG_SL_IFACE_VTABLE \ + { \ + mg_sl_if_init, \ + mg_sl_if_free, \ + mg_sl_if_add_conn, \ + mg_sl_if_remove_conn, \ + mg_sl_if_poll, \ + mg_sl_if_listen_tcp, \ + mg_sl_if_listen_udp, \ + mg_sl_if_connect_tcp, \ + mg_sl_if_connect_udp, \ + mg_sl_if_tcp_send, \ + mg_sl_if_udp_send, \ + mg_sl_if_recved, \ + mg_sl_if_create_conn, \ + mg_sl_if_destroy_conn, \ + mg_sl_if_sock_set, \ + mg_sl_if_get_conn_addr, \ + } +/* clang-format on */ + +const struct mg_iface_vtable mg_simplelink_iface_vtable = MG_SL_IFACE_VTABLE; +#if MG_NET_IF == MG_NET_IF_SIMPLELINK +const struct mg_iface_vtable mg_default_iface_vtable = MG_SL_IFACE_VTABLE; +#endif + +#endif /* MG_ENABLE_NET_IF_SIMPLELINK */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_net_if.h b/src/mongoose-6.11/src/common/platforms/simplelink/sl_net_if.h new file mode 100644 index 0000000..aa49d5d --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/simplelink/sl_net_if.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_COMMON_PLATFORMS_SIMPLELINK_SL_NET_IF_H_ +#define CS_COMMON_PLATFORMS_SIMPLELINK_SL_NET_IF_H_ + +#include "mongoose/src/net_if.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MG_ENABLE_NET_IF_SIMPLELINK +#define MG_ENABLE_NET_IF_SIMPLELINK MG_NET_IF == MG_NET_IF_SIMPLELINK +#endif + +extern const struct mg_iface_vtable mg_simplelink_iface_vtable; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_COMMON_PLATFORMS_SIMPLELINK_SL_NET_IF_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_socket.c b/src/mongoose-6.11/src/common/platforms/simplelink/sl_socket.c new file mode 100644 index 0000000..e4a0c26 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/simplelink/sl_socket.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_NET_IF == MG_NET_IF_SIMPLELINK + +#include +#include + +#include "common/platform.h" + +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) { + int res; + struct in_addr *in = (struct in_addr *) src; + if (af != AF_INET) { + errno = ENOTSUP; + return NULL; + } + res = snprintf(dst, size, "%lu.%lu.%lu.%lu", SL_IPV4_BYTE(in->s_addr, 0), + SL_IPV4_BYTE(in->s_addr, 1), SL_IPV4_BYTE(in->s_addr, 2), + SL_IPV4_BYTE(in->s_addr, 3)); + return res > 0 ? dst : NULL; +} + +char *inet_ntoa(struct in_addr n) { + static char a[16]; + return (char *) inet_ntop(AF_INET, &n, a, sizeof(a)); +} + +int inet_pton(int af, const char *src, void *dst) { + uint32_t a0, a1, a2, a3; + uint8_t *db = (uint8_t *) dst; + if (af != AF_INET) { + errno = ENOTSUP; + return 0; + } + if (sscanf(src, "%lu.%lu.%lu.%lu", &a0, &a1, &a2, &a3) != 4) { + return 0; + } + *db = a3; + *(db + 1) = a2; + *(db + 2) = a1; + *(db + 3) = a0; + return 1; +} + +#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_ssl_if.c b/src/mongoose-6.11/src/common/platforms/simplelink/sl_ssl_if.c new file mode 100644 index 0000000..99642b4 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/simplelink/sl_ssl_if.c @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_SIMPLELINK + +#include "common/mg_mem.h" + +#ifndef MG_SSL_IF_SIMPLELINK_SLFS_PREFIX +#define MG_SSL_IF_SIMPLELINK_SLFS_PREFIX "SL:" +#endif + +#define MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN \ + (sizeof(MG_SSL_IF_SIMPLELINK_SLFS_PREFIX) - 1) + +struct mg_ssl_if_ctx { + char *ssl_cert; + char *ssl_key; + char *ssl_ca_cert; + char *ssl_server_name; +}; + +void mg_ssl_if_init() { +} + +enum mg_ssl_if_result mg_ssl_if_conn_init( + struct mg_connection *nc, const struct mg_ssl_if_conn_params *params, + const char **err_msg) { + struct mg_ssl_if_ctx *ctx = + (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx)); + if (ctx == NULL) { + MG_SET_PTRPTR(err_msg, "Out of memory"); + return MG_SSL_ERROR; + } + nc->ssl_if_data = ctx; + + if (params->cert != NULL || params->key != NULL) { + if (params->cert != NULL && params->key != NULL) { + ctx->ssl_cert = strdup(params->cert); + ctx->ssl_key = strdup(params->key); + } else { + MG_SET_PTRPTR(err_msg, "Both cert and key are required."); + return MG_SSL_ERROR; + } + } + if (params->ca_cert != NULL && strcmp(params->ca_cert, "*") != 0) { + ctx->ssl_ca_cert = strdup(params->ca_cert); + } + /* TODO(rojer): cipher_suites. */ + if (params->server_name != NULL) { + ctx->ssl_server_name = strdup(params->server_name); + } + return MG_SSL_OK; +} + +void mg_ssl_if_conn_close_notify(struct mg_connection *nc) { + /* Nothing to do */ + (void) nc; +} + +void mg_ssl_if_conn_free(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + if (ctx == NULL) return; + nc->ssl_if_data = NULL; + MG_FREE(ctx->ssl_cert); + MG_FREE(ctx->ssl_key); + MG_FREE(ctx->ssl_ca_cert); + MG_FREE(ctx->ssl_server_name); + memset(ctx, 0, sizeof(*ctx)); + MG_FREE(ctx); +} + +bool pem_to_der(const char *pem_file, const char *der_file) { + bool ret = false; + FILE *pf = NULL, *df = NULL; + bool writing = false; + pf = fopen(pem_file, "r"); + if (pf == NULL) goto clean; + remove(der_file); + fs_slfs_set_new_file_size(der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN, + 2048); + df = fopen(der_file, "w"); + if (df == NULL) goto clean; + while (1) { + char pem_buf[70]; + char der_buf[48]; + if (!fgets(pem_buf, sizeof(pem_buf), pf)) break; + if (writing) { + if (strstr(pem_buf, "-----END ") != NULL) { + ret = true; + break; + } + int l = 0; + while (!isspace((unsigned int) pem_buf[l])) l++; + int der_len = 0; + cs_base64_decode((const unsigned char *) pem_buf, sizeof(pem_buf), + der_buf, &der_len); + if (der_len <= 0) break; + if (fwrite(der_buf, 1, der_len, df) != der_len) break; + } else if (strstr(pem_buf, "-----BEGIN ") != NULL) { + writing = true; + } + } + +clean: + if (pf != NULL) fclose(pf); + if (df != NULL) { + fclose(df); + if (!ret) remove(der_file); + } + return ret; +} + +#if MG_ENABLE_FILESYSTEM && defined(MG_FS_SLFS) +/* If the file's extension is .pem, convert it to DER format and put on SLFS. */ +static char *sl_pem2der(const char *pem_file) { + const char *pem_ext = strstr(pem_file, ".pem"); + if (pem_ext == NULL || *(pem_ext + 4) != '\0') { + return strdup(pem_file); + } + char *der_file = NULL; + /* DER file must be located on SLFS, add prefix. */ + int l = mg_asprintf(&der_file, 0, MG_SSL_IF_SIMPLELINK_SLFS_PREFIX "%.*s.der", + (int) (pem_ext - pem_file), pem_file); + if (der_file == NULL) return NULL; + bool result = false; + cs_stat_t st; + if (mg_stat(der_file, &st) != 0) { + result = pem_to_der(pem_file, der_file); + LOG(LL_DEBUG, ("%s -> %s = %d", pem_file, der_file, result)); + } else { + /* File exists, assume it's already been converted. */ + result = true; + } + if (result) { + /* Strip the SL: prefix we added since NWP does not expect it. */ + memmove(der_file, der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN, + l - 2 /* including \0 */); + } else { + MG_FREE(der_file); + der_file = NULL; + } + return der_file; +} +#else +static char *sl_pem2der(const char *pem_file) { + return strdup(pem_file); +} +#endif + +int sl_set_ssl_opts(int sock, struct mg_connection *nc) { + int err; + const struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + DBG(("%p ssl ctx: %p", nc, ctx)); + + if (ctx != NULL) { + DBG(("%p %s,%s,%s,%s", nc, (ctx->ssl_cert ? ctx->ssl_cert : "-"), + (ctx->ssl_key ? ctx->ssl_cert : "-"), + (ctx->ssl_ca_cert ? ctx->ssl_ca_cert : "-"), + (ctx->ssl_server_name ? ctx->ssl_server_name : "-"))); + if (ctx->ssl_cert != NULL && ctx->ssl_key != NULL) { + char *ssl_cert = sl_pem2der(ctx->ssl_cert); + char *ssl_key = sl_pem2der(ctx->ssl_key); + if (ssl_cert != NULL && ssl_key != NULL) { + err = sl_SetSockOpt(sock, SL_SOL_SOCKET, + SL_SO_SECURE_FILES_CERTIFICATE_FILE_NAME, ssl_cert, + strlen(ssl_cert)); + LOG(LL_INFO, ("CERTIFICATE_FILE_NAME %s -> %d", ssl_cert, err)); + err = sl_SetSockOpt(sock, SL_SOL_SOCKET, + SL_SO_SECURE_FILES_PRIVATE_KEY_FILE_NAME, ssl_key, + strlen(ssl_key)); + LOG(LL_INFO, ("PRIVATE_KEY_FILE_NAME %s -> %d", ssl_key, err)); + } else { + err = -1; + } + MG_FREE(ssl_cert); + MG_FREE(ssl_key); + if (err != 0) return err; + } + if (ctx->ssl_ca_cert != NULL) { + if (ctx->ssl_ca_cert[0] != '\0') { + char *ssl_ca_cert = sl_pem2der(ctx->ssl_ca_cert); + if (ssl_ca_cert != NULL) { + err = sl_SetSockOpt(sock, SL_SOL_SOCKET, + SL_SO_SECURE_FILES_CA_FILE_NAME, ssl_ca_cert, + strlen(ssl_ca_cert)); + LOG(LL_INFO, ("CA_FILE_NAME %s -> %d", ssl_ca_cert, err)); + } else { + err = -1; + } + MG_FREE(ssl_ca_cert); + if (err != 0) return err; + } + } + if (ctx->ssl_server_name != NULL) { + err = sl_SetSockOpt(sock, SL_SOL_SOCKET, + SL_SO_SECURE_DOMAIN_NAME_VERIFICATION, + ctx->ssl_server_name, strlen(ctx->ssl_server_name)); + DBG(("DOMAIN_NAME_VERIFICATION %s -> %d", ctx->ssl_server_name, err)); + /* Domain name verificationw as added in a NWP service pack, older + * versions return SL_ERROR_BSD_ENOPROTOOPT. There isn't much we can do + * about it, + * so we ignore the error. */ + if (err != 0 && err != SL_ERROR_BSD_ENOPROTOOPT) return err; + } + } + return 0; +} + +#endif /* MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_SIMPLELINK */ diff --git a/src/mongoose-6.11/src/common/platforms/stm32/LICENSE b/src/mongoose-6.11/src/common/platforms/stm32/LICENSE new file mode 100644 index 0000000..3d464ca --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/stm32/LICENSE @@ -0,0 +1,29 @@ +Copyright (c) 2011 The stlink project (github.com/texane/stlink) contributors. + All rights reserved. +Copyright (c) 2011 The "Capt'ns Missing Link" Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Martin Capitanio nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/mongoose-6.11/src/common/platforms/stm32/common.c b/src/mongoose-6.11/src/common/platforms/stm32/common.c new file mode 100644 index 0000000..9a201ca --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/stm32/common.c @@ -0,0 +1,869 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "stlink.h" +#include "usb.h" + +#ifndef _WIN32 +#define O_BINARY 0 //! @todo get rid of this OH MY (@xor-gate) +#endif + +/* todo: stm32l15xxx flash memory, pm0062 manual */ + +/* stm32f FPEC flash controller interface, pm0063 manual */ +// TODO - all of this needs to be abstracted out.... +// STM32F05x is identical, based on RM0091 (DM00031936, Doc ID 018940 Rev 2, +// August 2012) +#define FLASH_REGS_ADDR 0x40022000 +#define FLASH_REGS_SIZE 0x28 + +#define FLASH_ACR (FLASH_REGS_ADDR + 0x00) +#define FLASH_KEYR (FLASH_REGS_ADDR + 0x04) +#define FLASH_SR (FLASH_REGS_ADDR + 0x0c) +#define FLASH_CR (FLASH_REGS_ADDR + 0x10) +#define FLASH_AR (FLASH_REGS_ADDR + 0x14) +#define FLASH_OBR (FLASH_REGS_ADDR + 0x1c) +#define FLASH_WRPR (FLASH_REGS_ADDR + 0x20) + +// For STM32F05x, the RDPTR_KEY may be wrong, but as it is not used anywhere... +#define FLASH_RDPTR_KEY 0x00a5 +#define FLASH_KEY1 0x45670123 +#define FLASH_KEY2 0xcdef89ab + +#define FLASH_SR_BSY 0 +#define FLASH_SR_EOP 5 + +#define FLASH_CR_PG 0 +#define FLASH_CR_PER 1 +#define FLASH_CR_MER 2 +#define FLASH_CR_STRT 6 +#define FLASH_CR_LOCK 7 + +// 32L = 32F1 same CoreID as 32F4! +#define STM32L_FLASH_REGS_ADDR ((uint32_t) 0x40023c00) +#define STM32L_FLASH_ACR (STM32L_FLASH_REGS_ADDR + 0x00) +#define STM32L_FLASH_PECR (STM32L_FLASH_REGS_ADDR + 0x04) +#define STM32L_FLASH_PDKEYR (STM32L_FLASH_REGS_ADDR + 0x08) +#define STM32L_FLASH_PEKEYR (STM32L_FLASH_REGS_ADDR + 0x0c) +#define STM32L_FLASH_PRGKEYR (STM32L_FLASH_REGS_ADDR + 0x10) +#define STM32L_FLASH_OPTKEYR (STM32L_FLASH_REGS_ADDR + 0x14) +#define STM32L_FLASH_SR (STM32L_FLASH_REGS_ADDR + 0x18) +#define STM32L_FLASH_OBR (STM32L_FLASH_REGS_ADDR + 0x1c) +#define STM32L_FLASH_WRPR (STM32L_FLASH_REGS_ADDR + 0x20) +#define FLASH_L1_FPRG 10 +#define FLASH_L1_PROG 3 + +// 32L4 register base is at FLASH_REGS_ADDR (0x40022000) +#define STM32L4_FLASH_KEYR (FLASH_REGS_ADDR + 0x08) +#define STM32L4_FLASH_SR (FLASH_REGS_ADDR + 0x10) +#define STM32L4_FLASH_CR (FLASH_REGS_ADDR + 0x14) +#define STM32L4_FLASH_OPTR (FLASH_REGS_ADDR + 0x20) + +#define STM32L4_FLASH_SR_BSY 16 +#define STM32L4_FLASH_SR_ERRMASK 0x3f8 /* SR [9:3] */ + +#define STM32L4_FLASH_CR_LOCK 31 /* Lock control register */ +#define STM32L4_FLASH_CR_PG 0 /* Program */ +#define STM32L4_FLASH_CR_PER 1 /* Page erase */ +#define STM32L4_FLASH_CR_MER1 2 /* Bank 1 erase */ +#define STM32L4_FLASH_CR_MER2 15 /* Bank 2 erase */ +#define STM32L4_FLASH_CR_STRT 16 /* Start command */ +#define STM32L4_FLASH_CR_BKER 11 /* Bank select for page erase */ +#define STM32L4_FLASH_CR_PNB 3 /* Page number (8 bits) */ +// Bits requesting flash operations (useful when we want to clear them) +#define STM32L4_FLASH_CR_OPBITS \ + ((1lu << STM32L4_FLASH_CR_PG) | (1lu << STM32L4_FLASH_CR_PER) | \ + (1lu << STM32L4_FLASH_CR_MER1) | (1lu << STM32L4_FLASH_CR_MER1)) +// Page is fully specified by BKER and PNB +#define STM32L4_FLASH_CR_PAGEMASK (0x1fflu << STM32L4_FLASH_CR_PNB) + +#define STM32L4_FLASH_OPTR_DUALBANK 21 + +// STM32L0x flash register base and offsets +// same as 32L1 above +// RM0090 - DM00031020.pdf +#define STM32L0_FLASH_REGS_ADDR ((uint32_t) 0x40022000) +#define FLASH_ACR_OFF ((uint32_t) 0x00) +#define FLASH_PECR_OFF ((uint32_t) 0x04) +#define FLASH_PDKEYR_OFF ((uint32_t) 0x08) +#define FLASH_PEKEYR_OFF ((uint32_t) 0x0c) +#define FLASH_PRGKEYR_OFF ((uint32_t) 0x10) +#define FLASH_OPTKEYR_OFF ((uint32_t) 0x14) +#define FLASH_SR_OFF ((uint32_t) 0x18) +#define FLASH_OBR_OFF ((uint32_t) 0x1c) +#define FLASH_WRPR_OFF ((uint32_t) 0x20) + +// STM32F4 +#define FLASH_F4_REGS_ADDR ((uint32_t) 0x40023c00) +#define FLASH_F4_KEYR (FLASH_F4_REGS_ADDR + 0x04) +#define FLASH_F4_OPT_KEYR (FLASH_F4_REGS_ADDR + 0x08) +#define FLASH_F4_SR (FLASH_F4_REGS_ADDR + 0x0c) +#define FLASH_F4_CR (FLASH_F4_REGS_ADDR + 0x10) +#define FLASH_F4_OPT_CR (FLASH_F4_REGS_ADDR + 0x14) +#define FLASH_F4_CR_STRT 16 +#define FLASH_F4_CR_LOCK 31 +#define FLASH_F4_CR_SER 1 +#define FLASH_F4_CR_SNB 3 +#define FLASH_F4_CR_SNB_MASK 0xf8 +#define FLASH_F4_SR_BSY 16 + +#define L1_WRITE_BLOCK_SIZE 0x80 +#define L0_WRITE_BLOCK_SIZE 0x40 + +#define STLINK_REG_CM3_CPUID 0xE000ED00 +#define STLINK_REG_CM3_FP_CTRL 0xE0002000 +#define STLINK_REG_CM3_FP_COMP0 0xE0002008 + +/* Cortexâ„¢-M3 Technical Reference Manual */ +/* Debug Halting Control and Status Register */ +#define STLINK_REG_DHCSR 0xe000edf0 +#define STLINK_REG_DHCSR_DBGKEY 0xa05f0000 +#define STLINK_REG_DCRSR 0xe000edf4 +#define STLINK_REG_DCRDR 0xe000edf8 + +struct stlink_chipid_params { + uint32_t chip_id; + char *description; + uint32_t flash_size_reg; + uint32_t flash_pagesize; + uint32_t sram_size; + uint32_t bootrom_base; + uint32_t bootrom_size; +}; + +void write_uint32(unsigned char *buf, uint32_t ui) { + if (!is_bigendian()) { // le -> le (don't swap) + buf[0] = ((unsigned char *) &ui)[0]; + buf[1] = ((unsigned char *) &ui)[1]; + buf[2] = ((unsigned char *) &ui)[2]; + buf[3] = ((unsigned char *) &ui)[3]; + } else { + buf[0] = ((unsigned char *) &ui)[3]; + buf[1] = ((unsigned char *) &ui)[2]; + buf[2] = ((unsigned char *) &ui)[1]; + buf[3] = ((unsigned char *) &ui)[0]; + } +} + +void write_uint16(unsigned char *buf, uint16_t ui) { + if (!is_bigendian()) { // le -> le (don't swap) + buf[0] = ((unsigned char *) &ui)[0]; + buf[1] = ((unsigned char *) &ui)[1]; + } else { + buf[0] = ((unsigned char *) &ui)[1]; + buf[1] = ((unsigned char *) &ui)[0]; + } +} + +uint32_t read_uint32(const unsigned char *c, const int pt) { + uint32_t ui; + char *p = (char *) &ui; + + if (!is_bigendian()) { // le -> le (don't swap) + p[0] = c[pt + 0]; + p[1] = c[pt + 1]; + p[2] = c[pt + 2]; + p[3] = c[pt + 3]; + } else { + p[0] = c[pt + 3]; + p[1] = c[pt + 2]; + p[2] = c[pt + 1]; + p[3] = c[pt + 0]; + } + return ui; +} + +static uint32_t __attribute__((unused)) read_flash_rdp(stlink_t *sl) { + uint32_t rdp; + stlink_read_debug32(sl, FLASH_WRPR, &rdp); + return rdp & 0xff; +} + +static inline uint32_t read_flash_cr(stlink_t *sl) { + uint32_t res; + stlink_read_debug32(sl, FLASH_F4_CR, &res); + return res; +} + +static inline unsigned int is_flash_locked(stlink_t *sl) { + /* return non zero for true */ + uint32_t cr_lock_shift = FLASH_F4_CR_LOCK, cr = read_flash_cr(sl); + return cr & (1 << cr_lock_shift); +} + +static void unlock_flash(stlink_t *sl) { + uint32_t key_reg = FLASH_F4_KEYR; + stlink_write_debug32(sl, key_reg, FLASH_KEY1); + stlink_write_debug32(sl, key_reg, FLASH_KEY2); +} + +static int unlock_flash_if(stlink_t *sl) { + /* unlock flash if already locked */ + + if (is_flash_locked(sl)) { + unlock_flash(sl); + if (is_flash_locked(sl)) { + printf("Failed to unlock flash!\n"); + return -1; + } + } + return 0; +} + +static void lock_flash(stlink_t *sl) { + uint32_t n; + uint32_t cr_reg = FLASH_F4_CR; + uint32_t cr_lock_shift = FLASH_F4_CR_LOCK; + + n = read_flash_cr(sl) | (1 << cr_lock_shift); + stlink_write_debug32(sl, cr_reg, n); +} + +static void set_flash_cr_pg(stlink_t *sl) { + uint32_t cr_reg, x; + + x = read_flash_cr(sl); + + cr_reg = FLASH_F4_CR; + x |= 1 << FLASH_CR_PG; + + stlink_write_debug32(sl, cr_reg, x); +} + +static void __attribute__((unused)) clear_flash_cr_pg(stlink_t *sl) { + uint32_t cr_reg, n; + + cr_reg = FLASH_F4_CR; + + n = read_flash_cr(sl) & ~(1 << FLASH_CR_PG); + stlink_write_debug32(sl, cr_reg, n); +} + +static void set_flash_cr_per(stlink_t *sl) { + const uint32_t n = 1 << FLASH_CR_PER; + stlink_write_debug32(sl, FLASH_CR, n); +} + +static void __attribute__((unused)) clear_flash_cr_per(stlink_t *sl) { + const uint32_t n = read_flash_cr(sl) & ~(1 << FLASH_CR_PER); + stlink_write_debug32(sl, FLASH_CR, n); +} + +static void set_flash_cr_mer(stlink_t *sl, bool v) { + uint32_t val, cr_reg, cr_mer, cr_pg; + + cr_reg = FLASH_F4_CR; + cr_mer = 1 << FLASH_CR_MER; + cr_pg = 1 << FLASH_CR_PG; + + stlink_read_debug32(sl, cr_reg, &val); + if (val & cr_pg) { + /* STM32F030 will drop MER bit if PG was set */ + val &= ~cr_pg; + stlink_write_debug32(sl, cr_reg, val); + } + + if (v) + val |= cr_mer; + else + val &= ~cr_mer; + stlink_write_debug32(sl, cr_reg, val); +} + +static void __attribute__((unused)) clear_flash_cr_mer(stlink_t *sl) { + uint32_t val, cr_reg, cr_mer; + + cr_reg = FLASH_F4_CR; + cr_mer = 1 << FLASH_CR_MER; + + stlink_read_debug32(sl, cr_reg, &val); + val &= ~cr_mer; + stlink_write_debug32(sl, cr_reg, val); +} + +static void set_flash_cr_strt(stlink_t *sl) { + uint32_t val, cr_reg, cr_strt; + + cr_reg = FLASH_F4_CR; + cr_strt = 1 << FLASH_F4_CR_STRT; + + stlink_read_debug32(sl, cr_reg, &val); + val |= cr_strt; + stlink_write_debug32(sl, cr_reg, val); +} + +static inline uint32_t read_flash_sr(stlink_t *sl) { + uint32_t res, sr_reg; + sr_reg = FLASH_F4_SR; + stlink_read_debug32(sl, sr_reg, &res); + return res; +} + +static inline unsigned int is_flash_busy(stlink_t *sl) { + uint32_t sr_busy_shift; + sr_busy_shift = FLASH_F4_SR_BSY; + return read_flash_sr(sl) & (1 << sr_busy_shift); +} + +static void wait_flash_busy(stlink_t *sl) { + /* todo: add some delays here */ + while (is_flash_busy(sl)) + ; +} + +static void wait_flash_busy_progress(stlink_t *sl) { + while (is_flash_busy(sl)) { + usleep(10000); + } +} + +static inline unsigned int is_flash_eop(stlink_t *sl) { + return read_flash_sr(sl) & (1 << FLASH_SR_EOP); +} + +static void __attribute__((unused)) clear_flash_sr_eop(stlink_t *sl) { + const uint32_t n = read_flash_sr(sl) & ~(1 << FLASH_SR_EOP); + stlink_write_debug32(sl, FLASH_SR, n); +} + +static void __attribute__((unused)) wait_flash_eop(stlink_t *sl) { + /* todo: add some delays here */ + while (is_flash_eop(sl) == 0) + ; +} + +static inline void write_flash_ar(stlink_t *sl, uint32_t n) { + stlink_write_debug32(sl, FLASH_AR, n); +} + +static inline void write_flash_cr_psiz(stlink_t *sl, uint32_t n) { + uint32_t x = read_flash_cr(sl); + x &= ~(0x03 << 8); + x |= (n << 8); + stlink_write_debug32(sl, FLASH_F4_CR, x); +} + +static inline void write_flash_cr_snb(stlink_t *sl, uint32_t n) { + uint32_t x = read_flash_cr(sl); + x &= ~FLASH_F4_CR_SNB_MASK; + x |= (n << FLASH_F4_CR_SNB); + x |= (1 << FLASH_F4_CR_SER); + stlink_write_debug32(sl, FLASH_F4_CR, x); +} + +static inline void write_flash_cr_bker_pnb(stlink_t *sl, uint32_t n) { + stlink_write_debug32(sl, STM32L4_FLASH_SR, + 0xFFFFFFFF & ~(1 << STM32L4_FLASH_SR_BSY)); + uint32_t x = read_flash_cr(sl); + x &= ~STM32L4_FLASH_CR_OPBITS; + x &= ~STM32L4_FLASH_CR_PAGEMASK; + x &= ~(1 << STM32L4_FLASH_CR_MER1); + x &= ~(1 << STM32L4_FLASH_CR_MER2); + x |= (n << STM32L4_FLASH_CR_PNB); + x |= (1lu << STM32L4_FLASH_CR_PER); + stlink_write_debug32(sl, STM32L4_FLASH_CR, x); +} + +void stlink_close(stlink_t *sl) { + if (!sl) return; + _stlink_usb_close(sl); + free(sl); +} + +int stlink_exit_debug_mode(stlink_t *sl) { + int ret; + + ret = stlink_write_debug32(sl, STLINK_REG_DHCSR, STLINK_REG_DHCSR_DBGKEY); + if (ret == -1) return ret; + + return _stlink_usb_exit_debug_mode(sl); +} + +int stlink_enter_swd_mode(stlink_t *sl) { + return _stlink_usb_enter_swd_mode(sl); +} + +// Force the core into the debug mode -> halted state. +int stlink_force_debug(stlink_t *sl) { + return _stlink_usb_force_debug(sl); +} + +int stlink_exit_dfu_mode(stlink_t *sl) { + return _stlink_usb_exit_dfu_mode(sl); +} + +int stlink_core_id(stlink_t *sl) { + int ret; + + ret = _stlink_usb_core_id(sl); + if (ret == -1) { + printf("Failed to read core_id\n"); + return ret; + } + return ret; +} + +int stlink_chip_id(stlink_t *sl, uint32_t *chip_id) { + int ret; + + ret = stlink_read_debug32(sl, 0xE0042000, chip_id); + if (ret == -1) return ret; + + if (*chip_id == 0) + ret = stlink_read_debug32( + sl, 0x40015800, + chip_id); // Try Corex M0 DBGMCU_IDCODE register address + + return ret; +} + +/** + * reads and decodes the flash parameters, as dynamically as possible + * @param sl + * @return 0 for success, or -1 for unsupported core type. + */ +static const struct stlink_chipid_params F7_params = { + // RM0385 and DS10916 document was used to find these paramaters + .chip_id = STLINK_CHIPID_STM32_F7, + .description = "F7 device", + .flash_size_reg = 0x1ff0f442, // section 41.2 + .flash_pagesize = 0x800, // No flash pages + .sram_size = 0x50000, // "SRAM" byte size in hex from DS Fig 18 + .bootrom_base = + 0x00100000, // "System memory" starting address from DS Fig 18 + .bootrom_size = 0xEDC0 // "System memory" byte size in hex from DS Fig 18 +}; + +int stlink_load_device_params(stlink_t *sl) { + const struct stlink_chipid_params *params = &F7_params; + stlink_core_id(sl); + uint32_t chip_id; + uint32_t flash_size; + + stlink_chip_id(sl, &chip_id); + sl->chip_id = chip_id & 0xfff; + /* Fix chip_id for F4 rev A errata , Read CPU ID, as CoreID is the same for + * F2/F4*/ + if (sl->chip_id == 0x411) { + uint32_t cpuid; + stlink_read_debug32(sl, 0xE000ED00, &cpuid); + if ((cpuid & 0xfff0) == 0xc240) sl->chip_id = 0x413; + } + + if (sl->chip_id != STLINK_CHIPID_STM32_F7) { + printf("unsupported chip id! %d\n", chip_id); + return -1; + } + + // These are fixed... + sl->flash_base = STM32_FLASH_BASE; + sl->sram_base = STM32_SRAM_BASE; + stlink_read_debug32(sl, (params->flash_size_reg) & ~3, &flash_size); + if (params->flash_size_reg & 2) flash_size = flash_size >> 16; + flash_size = flash_size & 0xffff; + + sl->flash_size = flash_size * 1024; + sl->flash_pgsz = params->flash_pagesize; + sl->sram_size = params->sram_size; + sl->sys_base = params->bootrom_base; + sl->sys_size = params->bootrom_size; + + return 0; +} + +int stlink_reset(stlink_t *sl) { + return _stlink_usb_reset(sl); +} + +int stlink_jtag_reset(stlink_t *sl, int value) { + return _stlink_usb_jtag_reset(sl, value); +} + +int stlink_run(stlink_t *sl) { + return _stlink_usb_run(sl); +} + +int stlink_set_swdclk(stlink_t *sl, uint16_t divisor) { + return _stlink_usb_set_swdclk(sl, divisor); +} + +int stlink_status(stlink_t *sl) { + int ret; + + ret = _stlink_usb_status(sl); + stlink_core_stat(sl); + + return ret; +} + +int stlink_target_voltage(stlink_t *sl) { + return _stlink_usb_target_voltage(sl); +} + +int stlink_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data) { + return _stlink_usb_read_debug32(sl, addr, data); +} + +int stlink_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) { + return _stlink_usb_write_debug32(sl, addr, data); +} + +int stlink_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { + if (len % 4 != 0) { + fprintf(stderr, + "Error: Data length doesn't have a 32 bit alignment: +%d byte.\n", + len % 4); + abort(); + } + return _stlink_usb_write_mem32(sl, addr, len); +} + +int stlink_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { + if (len % 4 != 0) { // !!! never ever: fw gives just wrong values + fprintf(stderr, + "Error: Data length doesn't have a 32 bit alignment: +%d byte.\n", + len % 4); + abort(); + } + return _stlink_usb_read_mem32(sl, addr, len); +} + +int stlink_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) { + if (len > 0x40) { // !!! never ever: Writing more then 0x40 bytes gives + // unexpected behaviour + fprintf(stderr, "Error: Data length > 64: +%d byte.\n", len); + abort(); + } + return _stlink_usb_write_mem8(sl, addr, len); +} + +int stlink_write_reg(stlink_t *sl, uint32_t reg, int idx) { + return _stlink_usb_write_reg(sl, reg, idx); +} + +int stlink_read_reg(stlink_t *sl, int r_idx, struct stlink_reg *regp) { + if (r_idx > 20 || r_idx < 0) { + fprintf(stderr, "Error: register index must be in [0..20]\n"); + return -1; + } + + return _stlink_usb_read_reg(sl, r_idx, regp); +} + +bool stlink_is_core_halted(stlink_t *sl) { + bool ret = false; + + stlink_status(sl); + if (sl->q_buf[0] == STLINK_CORE_HALTED) ret = true; + + return ret; +} + +int stlink_step(stlink_t *sl) { + return _stlink_usb_step(sl); +} + +int stlink_current_mode(stlink_t *sl) { + return _stlink_usb_current_mode(sl); +} + +// End of delegates.... Common code below here... + +// Endianness +// http://www.ibm.com/developerworks/aix/library/au-endianc/index.html +// const int i = 1; +// #define is_bigendian() ( (*(char*)&i) == 0 ) + +unsigned int is_bigendian(void) { + static volatile const unsigned int i = 1; + return *(volatile const char *) &i == 0; +} + +uint16_t read_uint16(const unsigned char *c, const int pt) { + uint32_t ui; + char *p = (char *) &ui; + + if (!is_bigendian()) { // le -> le (don't swap) + p[0] = c[pt + 0]; + p[1] = c[pt + 1]; + } else { + p[0] = c[pt + 1]; + p[1] = c[pt + 0]; + } + return ui; +} + +// same as above with entrypoint. + +void stlink_run_at(stlink_t *sl, stm32_addr_t addr) { + stlink_write_reg(sl, addr, 15); /* pc register */ + + stlink_run(sl); + + while (stlink_is_core_halted(sl)) usleep(3000000); +} + +void stlink_core_stat(stlink_t *sl) { + if (sl->q_len <= 0) return; + + switch (sl->q_buf[0]) { + case STLINK_CORE_RUNNING: + sl->core_stat = STLINK_CORE_RUNNING; + return; + case STLINK_CORE_HALTED: + sl->core_stat = STLINK_CORE_HALTED; + return; + default: + sl->core_stat = STLINK_CORE_STAT_UNKNOWN; + fprintf(stderr, " core status: unknown\n"); + } +} + +void stlink_fwrite_finalize(stlink_t *sl, stm32_addr_t addr) { + unsigned int val; + /* set stack*/ + stlink_read_debug32(sl, addr, &val); + stlink_write_reg(sl, val, 13); + /* Set PC to the reset routine*/ + stlink_read_debug32(sl, addr + 4, &val); + stlink_write_reg(sl, val, 15); + stlink_run(sl); +} + +typedef bool (*save_block_fn)(void *arg, uint8_t *block, ssize_t len); + +static int stlink_read(stlink_t *sl, stm32_addr_t addr, size_t size, + save_block_fn fn, void *fn_arg) { + int error = -1; + + if (size < 1) size = sl->flash_size; + + if (size > sl->flash_size) size = sl->flash_size; + + size_t cmp_size = (sl->flash_pgsz > 0x1800) ? 0x1800 : sl->flash_pgsz; + for (size_t off = 0; off < size; off += cmp_size) { + size_t aligned_size; + + /* adjust last page size */ + if ((off + cmp_size) > size) cmp_size = size - off; + + aligned_size = cmp_size; + if (aligned_size & (4 - 1)) aligned_size = (cmp_size + 4) & ~(4 - 1); + + stlink_read_mem32(sl, addr + (uint32_t) off, aligned_size); + + if (!fn(fn_arg, sl->q_buf, aligned_size)) { + goto on_error; + } + } + + /* success */ + error = 0; + +on_error: + return error; +} + +int write_buffer_to_sram(stlink_t *sl, flash_loader_t *fl, const uint8_t *buf, + size_t size) { + /* write the buffer right after the loader */ + size_t chunk = size & ~0x3; + size_t rem = size & 0x3; + if (chunk) { + memcpy(sl->q_buf, buf, chunk); + stlink_write_mem32(sl, fl->buf_addr, chunk); + } + if (rem) { + memcpy(sl->q_buf, buf + chunk, rem); + stlink_write_mem8(sl, (fl->buf_addr) + (uint32_t) chunk, rem); + } + return 0; +} + +uint32_t calculate_F7_sectornum(uint32_t flashaddr) { + flashaddr &= ~STM32_FLASH_BASE; // Page now holding the actual flash address + if (flashaddr < 0x20000) + return (flashaddr / 0x8000); + else if (flashaddr < 0x40000) + return (4); + else + return (flashaddr / 0x40000) + 4; +} + +uint32_t stlink_calculate_pagesize(stlink_t *sl, uint32_t flashaddr) { + if (sl->chip_id == STLINK_CHIPID_STM32_F7) { + uint32_t sector = calculate_F7_sectornum(flashaddr); + if (sector < 4) + sl->flash_pgsz = 0x8000; + else if (sector < 5) + sl->flash_pgsz = 0x20000; + else + sl->flash_pgsz = 0x40000; + } + return (uint32_t) sl->flash_pgsz; +} + +/** + * Erase a page of flash, assumes sl is fully populated with things like + * chip/core ids + * @param sl stlink context + * @param flashaddr an address in the flash page to erase + * @return 0 on success -ve on failure + */ +int stlink_erase_flash_page(stlink_t *sl, stm32_addr_t flashaddr) { + /* wait for ongoing op to finish */ + wait_flash_busy(sl); + + /* unlock if locked */ + unlock_flash_if(sl); + + /* select the page to erase */ + // calculate the actual page from the address + uint32_t sector = calculate_F7_sectornum(flashaddr); + + write_flash_cr_snb(sl, sector); + + /* start erase operation */ + set_flash_cr_strt(sl); + + /* wait for completion */ + wait_flash_busy(sl); + + /* relock the flash */ + // todo: fails to program if this is in + lock_flash(sl); + + return 0; +} + +/** + * Verify addr..addr+len is binary identical to base...base+len + * @param sl stlink context + * @param address stm device address + * @param data host side buffer to check against + * @param length how much + * @return 0 for success, -ve for failure + */ +int stlink_verify_write_flash(stlink_t *sl, stm32_addr_t address, uint8_t *data, + unsigned length) { + size_t off; + size_t cmp_size = (sl->flash_pgsz > 0x1800) ? 0x1800 : sl->flash_pgsz; + printf("Verifying flash...\n"); + for (off = 0; off < length; off += cmp_size) { + size_t aligned_size; + + /* adjust last page size */ + if ((off + cmp_size) > length) cmp_size = length - off; + + aligned_size = cmp_size; + if (aligned_size & (4 - 1)) aligned_size = (cmp_size + 4) & ~(4 - 1); + + stlink_read_mem32(sl, address + (uint32_t) off, aligned_size); + + if (memcmp(sl->q_buf, data + off, cmp_size)) { + printf("Verification of flash failed at offset: %u\n", + (unsigned int) off); + return -1; + } + } + return 0; +} + +int stlink_write_flash(stlink_t *sl, stm32_addr_t addr, uint8_t *base, + uint32_t len, uint8_t eraseonly) { + size_t off; + flash_loader_t fl; + printf("Writing %d (%#x) bytes to stm32 address: %u (%#x)\n", len, len, addr, + addr); + /* check addr range is inside the flash */ + stlink_calculate_pagesize(sl, addr); + if (addr < sl->flash_base) { + printf("addr too low %#x < %#x\n", addr, sl->flash_base); + return -1; + } else if ((addr + len) < addr) { + printf("addr overruns\n"); + return -1; + } else if ((addr + len) > (sl->flash_base + sl->flash_size)) { + printf("addr too high\n"); + return -1; + } else if (addr & 1) { + printf("unaligned addr 0x%x\n", addr); + return -1; + } else if (len & 1) { + printf("unaligned len 0x%x -- padding with zero\n", len); + len += 1; + } else if (addr & (sl->flash_pgsz - 1)) { + printf("addr not a multiple of pagesize, not supported\n"); + return -1; + } + + // Make sure we've loaded the context with the chip details + stlink_core_id(sl); + /* erase each page */ + int page_count = 0; + for (off = 0; off < len; + off += stlink_calculate_pagesize(sl, addr + (uint32_t) off)) { + fprintf(stdout, "Erasing flash page at addr 0x%08X\n", (int) (addr + off)); + /* addr must be an addr inside the page */ + if (stlink_erase_flash_page(sl, addr + (uint32_t) off) == -1) { + printf("Failed to erase_flash_page(%#zx) == -1\n", addr + off); + return -1; + } + page_count++; + } + + if (eraseonly) return 0; + + /* flash loader initialization */ + if (stlink_flash_loader_init(sl, &fl) == -1) { + printf("stlink_flash_loader_init() == -1\n"); + return -1; + } + + /* First unlock the cr */ + unlock_flash_if(sl); + + /* TODO: Check that Voltage range is 2.7 - 3.6 V */ + /* set parallelisim to 32 bit*/ + int voltage = stlink_target_voltage(sl); + if (voltage == -1) { + printf("Failed to read Target voltage\n"); + return voltage; + } else if (voltage > 2700) { + write_flash_cr_psiz(sl, 2); + } else { + printf( + "Target voltage (%d mV) too low for 32-bit flash, using 8-bit " + "flash writes\n", + voltage); + write_flash_cr_psiz(sl, 0); + } + + /* set programming mode */ + set_flash_cr_pg(sl); + + for (off = 0; off < len;) { + size_t size = len - off > 0x8000 ? 0x8000 : len - off; + printf("Writing %d bytes at 0x%08X\n", (int) size, + (int) (addr + (uint32_t) off)); + if (stlink_flash_loader_run(sl, &fl, addr + (uint32_t) off, base + off, + size) == -1) { + printf("stlink_flash_loader_run(%#zx) failed! == -1\n", addr + off); + return -1; + } + off += size; + } + + /* Relock flash */ + lock_flash(sl); + + return stlink_verify_write_flash(sl, addr, base, len); +} diff --git a/src/mongoose-6.11/src/common/platforms/stm32/flash.c b/src/mongoose-6.11/src/common/platforms/stm32/flash.c new file mode 100644 index 0000000..371c6eb --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/stm32/flash.c @@ -0,0 +1,93 @@ +/* simple wrapper around the stlink_flash_write function */ + +#include +#include +#include +#include +#include + +#include "stlink.h" +#include "usb.h" + +#define FLASH_ADDRESS 0x8000000 + +int stm32_flash(const char *device_name, void *data, int len) { + stlink_t *sl = NULL; + int err = -1; + uint8_t serial[16]; + + int j = (int) strlen(device_name); + int length = j / 2; // the length of the destination-array + if (j % 2 != 0) return -1; + + for (size_t k = 0; j >= 0 && k < sizeof(serial); ++k, j -= 2) { + char buffer[3] = {0}; + memcpy(buffer, device_name + j, 2); + serial[length - k] = (uint8_t) strtol(buffer, NULL, 16); + } + + sl = stlink_open_usb(1, (char *) serial); + + if (sl == NULL) return -1; + + if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) { + if (stlink_exit_dfu_mode(sl)) { + printf("Failed to exit DFU mode\n"); + goto on_error; + } + } + + if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) { + if (stlink_enter_swd_mode(sl)) { + printf("Failed to enter SWD mode\n"); + goto on_error; + } + } + + if (stlink_jtag_reset(sl, 2)) { + printf("Failed to reset JTAG\n"); + goto on_error; + } + + if (stlink_reset(sl)) { + printf("Failed to reset device\n"); + goto on_error; + } + + // Core must be halted to use RAM based flashloaders + if (stlink_force_debug(sl)) { + printf("Failed to halt the core\n"); + goto on_error; + } + + if (stlink_status(sl)) { + printf("Failed to get Core's status\n"); + goto on_error; + } + + size_t size = 0; + + if ((FLASH_ADDRESS > sl->flash_base + sl->flash_size)) { + printf("Unknown memory region\n"); + goto on_error; + } + + err = stlink_write_flash(sl, FLASH_ADDRESS, (uint8_t *) data, len, 0); + stlink_fwrite_finalize(sl, FLASH_ADDRESS); + + if (err == -1) { + printf("stlink_fwrite_flash() == -1\n"); + goto on_error; + } + + stlink_jtag_reset(sl, 2); + stlink_reset(sl); + + err = 0; + +on_error: + stlink_exit_debug_mode(sl); + stlink_close(sl); + + return err; +} diff --git a/src/mongoose-6.11/src/common/platforms/stm32/flash_loader.c b/src/mongoose-6.11/src/common/platforms/stm32/flash_loader.c new file mode 100644 index 0000000..b508e8e --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/stm32/flash_loader.c @@ -0,0 +1,93 @@ +#include "stlink.h" + +#include +#include +#include + +static const uint8_t loader_code_stm32f7[] = { + 0x08, 0x4b, 0x72, 0xb1, 0x04, 0x68, 0x0c, 0x60, + 0xbf, 0xf3, 0x4f, 0x8f, // DSB Memory barrier for in order flash write + 0xdc, 0x89, 0x14, 0xf0, 0x01, 0x0f, 0xfb, 0xd1, + 0x00, 0xf1, 0x04, 0x00, 0x01, 0xf1, 0x04, 0x01, + 0xa2, 0xf1, 0x01, 0x02, 0xef, 0xe7, 0x00, 0xbe, // bkpt #0x00 + 0x00, 0x3c, 0x02, 0x40, +}; + +#define WAIT_ROUNDS 10000 + +int stlink_flash_loader_init(stlink_t *sl, flash_loader_t *fl) { + size_t size; + + /* allocate the loader in sram */ + if (stlink_flash_loader_write_to_sram(sl, &fl->loader_addr, &size) == -1) { + printf("Failed to write flash loader to sram!\n"); + return -1; + } + + /* allocate a one page buffer in sram right after loader */ + fl->buf_addr = fl->loader_addr + (uint32_t) size; + + return 0; +} + +int stlink_flash_loader_write_to_sram(stlink_t *sl, stm32_addr_t *addr, + size_t *size) { + memcpy(sl->q_buf, loader_code_stm32f7, sizeof(loader_code_stm32f7)); + stlink_write_mem32(sl, sl->sram_base, sizeof(loader_code_stm32f7)); + + *addr = sl->sram_base; + *size = sizeof(loader_code_stm32f7); + + /* success */ + return 0; +} + +int stlink_flash_loader_run(stlink_t *sl, flash_loader_t *fl, + stm32_addr_t target, const uint8_t *buf, + size_t size) { + struct stlink_reg rr; + int i = 0; + size_t count = 0; + + // FIXME This can never return -1 + if (write_buffer_to_sram(sl, fl, buf, size) == -1) { + // IMPOSSIBLE! + printf("write_buffer_to_sram() == -1\n"); + return -1; + } + + count = size / sizeof(uint32_t); + if (size % sizeof(uint32_t)) ++count; + + /* setup core */ + stlink_write_reg(sl, fl->buf_addr, 0); /* source */ + stlink_write_reg(sl, target, 1); /* target */ + stlink_write_reg(sl, (uint32_t) count, 2); /* count */ + stlink_write_reg( + sl, 0, + 3); /* flash bank 0 (input), only used on F0, but armless fopr others */ + stlink_write_reg(sl, fl->loader_addr, 15); /* pc register */ + + /* run loader */ + stlink_run(sl); + + /* wait until done (reaches breakpoint) */ + for (i = 0; i < WAIT_ROUNDS; i++) { + usleep(10); + if (stlink_is_core_halted(sl)) break; + } + + if (i >= WAIT_ROUNDS) { + printf("flash loader run error\n"); + return -1; + } + + /* check written byte count */ + stlink_read_reg(sl, 2, &rr); + if (rr.r[2] != 0) { + printf("write error, count == %u\n", rr.r[2]); + return -1; + } + + return 0; +} diff --git a/src/mongoose-6.11/src/common/platforms/stm32/flash_loader.h b/src/mongoose-6.11/src/common/platforms/stm32/flash_loader.h new file mode 100644 index 0000000..a3f072d --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/stm32/flash_loader.h @@ -0,0 +1,30 @@ +/* + * File: stlink.h + * + * This should contain all the common top level stlink interfaces, regardless + * of how the backend does the work.... + */ +#ifndef STLINK_FLASH_LOADER_H_ +#define STLINK_FLASH_LOADER_H_ + +#include +#include + +#include "stlink.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int stlink_flash_loader_init(stlink_t *sl, flash_loader_t *fl); +int stlink_flash_loader_write_to_sram(stlink_t *sl, stm32_addr_t *addr, + size_t *size); +int stlink_flash_loader_run(stlink_t *sl, flash_loader_t *fl, + stm32_addr_t target, const uint8_t *buf, + size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* STLINK_FLASH_LOADER_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/stm32/flasher.go b/src/mongoose-6.11/src/common/platforms/stm32/flasher.go new file mode 100644 index 0000000..8ddde03 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/stm32/flasher.go @@ -0,0 +1,38 @@ +// +build linux + +package stm32 + +// #cgo CFLAGS: -I/usr/include/libusb-1.0/ +// #cgo LDFLAGS: -lusb-1.0 +// #include "stm32_flash.h" +import "C" + +import ( + "time" + "unsafe" + + "cesanta.com/mos/flash/common" + "github.com/cesanta/errors" +) + +type FlashOpts struct { + ShareName string + Timeout time.Duration +} + +func Flash(fw *common.FirmwareBundle, opts *FlashOpts) error { + data, err := fw.GetPartData("boot") + if err != nil { + return errors.Annotatef(err, "invalid manifest") + } + + flash_res := C.stm32_flash(C.CString(opts.ShareName), + unsafe.Pointer(&data[0]), + C.int(len(data))) + + if flash_res != 0 { + return errors.Errorf("flash failed") + } + + return nil +} diff --git a/src/mongoose-6.11/src/common/platforms/stm32/stlink.h b/src/mongoose-6.11/src/common/platforms/stm32/stlink.h new file mode 100644 index 0000000..ac34dd0 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/stm32/stlink.h @@ -0,0 +1,198 @@ +/* + * File: stlink.h + * + * This should contain all the common top level stlink interfaces, regardless + * of how the backend does the work.... + */ +#ifndef STLINK_H +#define STLINK_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define STLINK_ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +// Max data transfer size. +// 6kB = max mem32_read block, 8kB sram +//#define Q_BUF_LEN 96 +#define Q_BUF_LEN (1024 * 100) + +// STLINK_DEBUG_RESETSYS, etc: +#define STLINK_CORE_RUNNING 0x80 +#define STLINK_CORE_HALTED 0x81 +#define STLINK_CORE_STAT_UNKNOWN -1 + +#define STLINK_GET_VERSION 0xf1 +#define STLINK_GET_CURRENT_MODE 0xf5 +#define STLINK_GET_TARGET_VOLTAGE 0xF7 + +#define STLINK_DEBUG_COMMAND 0xF2 +#define STLINK_DFU_COMMAND 0xF3 +#define STLINK_DFU_EXIT 0x07 + +// STLINK_GET_CURRENT_MODE +#define STLINK_DEV_DFU_MODE 0x00 +#define STLINK_DEV_MASS_MODE 0x01 +#define STLINK_DEV_DEBUG_MODE 0x02 +#define STLINK_DEV_UNKNOWN_MODE -1 + +// TODO - possible poor names... +#define STLINK_SWD_ENTER 0x30 +#define STLINK_SWD_READCOREID 0x32 // TBD +#define STLINK_JTAG_WRITEDEBUG_32BIT 0x35 +#define STLINK_JTAG_READDEBUG_32BIT 0x36 +#define STLINK_JTAG_DRIVE_NRST 0x3c + +#define STLINK_DEBUG_APIV2_SWD_SET_FREQ 0x43 + +/* cortex core ids */ +// TODO clean this up... +#define STM32VL_CORE_ID 0x1ba01477 +#define STM32F7_CORE_ID 0x5ba02477 + +// Constant STM32 memory map figures +#define STM32_FLASH_BASE 0x08000000 +#define STM32_SRAM_BASE 0x20000000 + +// Baud rate divisors for SWDCLK +#define STLINK_SWDCLK_4MHZ_DIVISOR 0 +#define STLINK_SWDCLK_1P8MHZ_DIVISOR 1 +#define STLINK_SWDCLK_1P2MHZ_DIVISOR 2 +#define STLINK_SWDCLK_950KHZ_DIVISOR 3 +#define STLINK_SWDCLK_480KHZ_DIVISOR 7 +#define STLINK_SWDCLK_240KHZ_DIVISOR 15 +#define STLINK_SWDCLK_125KHZ_DIVISOR 31 +#define STLINK_SWDCLK_100KHZ_DIVISOR 40 +#define STLINK_SWDCLK_50KHZ_DIVISOR 79 +#define STLINK_SWDCLK_25KHZ_DIVISOR 158 +#define STLINK_SWDCLK_15KHZ_DIVISOR 265 +#define STLINK_SWDCLK_5KHZ_DIVISOR 798 + +#define STLINK_CHIPID_STM32_F7 0x449 + +/* Enough space to hold both a V2 command or a V1 command packaged as generic + * scsi*/ +#define C_BUF_LEN 32 + +struct stlink_reg { + uint32_t r[16]; + uint32_t s[32]; + uint32_t xpsr; + uint32_t main_sp; + uint32_t process_sp; + uint32_t rw; + uint32_t rw2; + uint8_t control; + uint8_t faultmask; + uint8_t basepri; + uint8_t primask; + uint32_t fpscr; +}; + +typedef uint32_t stm32_addr_t; + +typedef struct flash_loader { + stm32_addr_t loader_addr; /* loader sram adddr */ + stm32_addr_t buf_addr; /* buffer sram address */ +} flash_loader_t; + +typedef struct stlink_version_ { + uint32_t stlink_v; + uint32_t jtag_v; + uint32_t swim_v; + uint32_t st_vid; + uint32_t stlink_pid; +} stlink_version_t; + +typedef struct _stlink stlink_t; + +struct _stlink { + void *backend_data; + + // Room for the command header + unsigned char c_buf[C_BUF_LEN]; + // Data transferred from or to device + unsigned char q_buf[Q_BUF_LEN]; + int q_len; + + // transport layer verboseness: 0 for no debug info, 10 for lots + uint32_t core_id; + uint32_t chip_id; + int core_stat; + + char serial[16]; + int serial_size; + + stm32_addr_t flash_base; + size_t flash_size; + size_t flash_pgsz; + + /* sram settings */ + stm32_addr_t sram_base; + size_t sram_size; + + // bootloader + stm32_addr_t sys_base; + size_t sys_size; + + struct stlink_version_ version; +}; + +int stlink_enter_swd_mode(stlink_t *sl); +int stlink_enter_jtag_mode(stlink_t *sl); +int stlink_exit_debug_mode(stlink_t *sl); +int stlink_exit_dfu_mode(stlink_t *sl); +void stlink_close(stlink_t *sl); +int stlink_core_id(stlink_t *sl); +int stlink_reset(stlink_t *sl); +int stlink_jtag_reset(stlink_t *sl, int value); +int stlink_run(stlink_t *sl); +int stlink_status(stlink_t *sl); +int stlink_version(stlink_t *sl); +int stlink_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data); +int stlink_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len); +int stlink_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data); +int stlink_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len); +int stlink_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len); +int stlink_read_all_regs(stlink_t *sl, struct stlink_reg *regp); +int stlink_read_reg(stlink_t *sl, int r_idx, struct stlink_reg *regp); +int stlink_write_reg(stlink_t *sl, uint32_t reg, int idx); +int stlink_step(stlink_t *sl); +int stlink_current_mode(stlink_t *sl); +int stlink_force_debug(stlink_t *sl); +int stlink_target_voltage(stlink_t *sl); +int stlink_set_swdclk(stlink_t *sl, uint16_t divisor); +int stlink_write_flash(stlink_t *sl, stm32_addr_t address, uint8_t *data, + uint32_t length, uint8_t eraseonly); +int stlink_fwrite_flash(stlink_t *sl, const char *path, stm32_addr_t addr); +int stlink_verify_write_flash(stlink_t *sl, stm32_addr_t address, uint8_t *data, + uint32_t length); +int stlink_chip_id(stlink_t *sl, uint32_t *chip_id); +int stlink_erase_flash_page(stlink_t *sl, stm32_addr_t flashaddr); +uint32_t stlink_calculate_pagesize(stlink_t *sl, uint32_t flashaddr); +uint16_t read_uint16(const unsigned char *c, const int pt); +void stlink_core_stat(stlink_t *sl); +unsigned int is_bigendian(void); +uint32_t read_uint32(const unsigned char *c, const int pt); +void write_uint32(unsigned char *buf, uint32_t ui); +void write_uint16(unsigned char *buf, uint16_t ui); +bool stlink_is_core_halted(stlink_t *sl); +int write_buffer_to_sram(stlink_t *sl, flash_loader_t *fl, const uint8_t *buf, + size_t size); +int write_loader_to_sram(stlink_t *sl, stm32_addr_t *addr, size_t *size); +int stlink_load_device_params(stlink_t *sl); +void stlink_fwrite_finalize(stlink_t *sl, stm32_addr_t addr); + +#include "usb.h" +#include "flash_loader.h" + +#ifdef __cplusplus +} +#endif + +#endif /* STLINK_H */ diff --git a/src/mongoose-6.11/src/common/platforms/stm32/stm32_flash.h b/src/mongoose-6.11/src/common/platforms/stm32/stm32_flash.h new file mode 100644 index 0000000..2846ab7 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/stm32/stm32_flash.h @@ -0,0 +1,6 @@ +#ifndef STM32_FLASH_H_ +#define STM32_FLASH_H_ + +int stm32_flash(const char *device_name, void *data, int len); + +#endif diff --git a/src/mongoose-6.11/src/common/platforms/stm32/usb.c b/src/mongoose-6.11/src/common/platforms/stm32/usb.c new file mode 100644 index 0000000..a60e99f --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/stm32/usb.c @@ -0,0 +1,773 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "stlink.h" + +enum SCSI_Generic_Direction { SG_DXFER_TO_DEV = 0, SG_DXFER_FROM_DEV = 0x80 }; + +enum stlink_debug_commands { + STLINK_DEBUG_ENTER_JTAG = 0x00, + STLINK_DEBUG_GETSTATUS = 0x01, + STLINK_DEBUG_FORCEDEBUG = 0x02, + STLINK_DEBUG_RESETSYS = 0x03, + STLINK_DEBUG_READALLREGS = 0x04, + STLINK_DEBUG_READREG = 0x05, + STLINK_DEBUG_WRITEREG = 0x06, + STLINK_DEBUG_READMEM_32BIT = 0x07, + STLINK_DEBUG_WRITEMEM_32BIT = 0x08, + STLINK_DEBUG_RUNCORE = 0x09, + STLINK_DEBUG_STEPCORE = 0x0a, + STLINK_DEBUG_SETFP = 0x0b, + STLINK_DEBUG_WRITEMEM_8BIT = 0x0d, + STLINK_DEBUG_CLEARFP = 0x0e, + STLINK_DEBUG_WRITEDEBUGREG = 0x0f, + STLINK_DEBUG_ENTER = 0x20, + STLINK_DEBUG_EXIT = 0x21, + STLINK_DEBUG_READCOREID = 0x22, + STLINK_DEBUG_ENTER_SWD = 0xa3 +}; + +void _stlink_usb_close(stlink_t *sl) { + if (!sl) return; + + struct stlink_libusb *const handle = sl->backend_data; + // maybe we couldn't even get the usb device? + if (handle != NULL) { + if (handle->usb_handle != NULL) { + libusb_close(handle->usb_handle); + } + + libusb_exit(handle->libusb_ctx); + free(handle); + } +} + +ssize_t send_recv(struct stlink_libusb *handle, int terminate, + unsigned char *txbuf, size_t txsize, unsigned char *rxbuf, + size_t rxsize) { + /* note: txbuf and rxbuf can point to the same area */ + int res = 0; + int t; + + t = libusb_bulk_transfer(handle->usb_handle, handle->ep_req, txbuf, + (int) txsize, &res, 3000); + if (t) { + printf("[!] send_recv send request failed: %s\n", libusb_error_name(t)); + return -1; + } else if ((size_t) res != txsize) { + printf("[!] send_recv send request wrote %u bytes (instead of %u).\n", + (unsigned int) res, (unsigned int) txsize); + } + + if (rxsize != 0) { + t = libusb_bulk_transfer(handle->usb_handle, handle->ep_rep, rxbuf, + (int) rxsize, &res, 3000); + if (t) { + printf("[!] send_recv read reply failed: %s\n", libusb_error_name(t)); + return -1; + } + } + + if ((handle->protocoll == 1) && terminate) { + /* Read the SG reply */ + unsigned char sg_buf[13]; + t = libusb_bulk_transfer(handle->usb_handle, handle->ep_rep, sg_buf, 13, + &res, 3000); + if (t) { + printf("[!] send_recv read storage failed: %s\n", libusb_error_name(t)); + return -1; + } + /* The STLink doesn't seem to evaluate the sequence number */ + handle->sg_transfer_idx++; + } + + return res; +} + +static inline int send_only(struct stlink_libusb *handle, int terminate, + unsigned char *txbuf, size_t txsize) { + return (int) send_recv(handle, terminate, txbuf, txsize, NULL, 0); +} + +static int fill_command(stlink_t *sl, enum SCSI_Generic_Direction dir, + uint32_t len) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const cmd = sl->c_buf; + int i = 0; + memset(cmd, 0, sizeof(sl->c_buf)); + if (slu->protocoll == 1) { + cmd[i++] = 'U'; + cmd[i++] = 'S'; + cmd[i++] = 'B'; + cmd[i++] = 'C'; + write_uint32(&cmd[i], slu->sg_transfer_idx); + write_uint32(&cmd[i + 4], len); + i += 8; + cmd[i++] = (dir == SG_DXFER_FROM_DEV) ? 0x80 : 0; + cmd[i++] = 0; /* Logical unit */ + cmd[i++] = 0xa; /* Command length */ + } + return i; +} + +int _stlink_usb_version(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + uint32_t rep_len = 6; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_GET_VERSION; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_GET_VERSION\n"); + return (int) size; + } + + return 0; +} + +int32_t _stlink_usb_target_voltage(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const rdata = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + uint32_t rep_len = 8; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + uint32_t factor, reading; + int voltage; + + cmd[i++] = STLINK_GET_TARGET_VOLTAGE; + + size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_GET_TARGET_VOLTAGE\n"); + return -1; + } else if (size != 8) { + printf("[!] wrong length STLINK_GET_TARGET_VOLTAGE\n"); + return -1; + } + + factor = + (rdata[3] << 24) | (rdata[2] << 16) | (rdata[1] << 8) | (rdata[0] << 0); + reading = + (rdata[7] << 24) | (rdata[6] << 16) | (rdata[5] << 8) | (rdata[4] << 0); + voltage = 2400 * reading / factor; + + return voltage; +} + +int _stlink_usb_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const rdata = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + const int rep_len = 8; + + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_JTAG_READDEBUG_32BIT; + write_uint32(&cmd[i], addr); + size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_JTAG_READDEBUG_32BIT\n"); + return (int) size; + } + *data = read_uint32(rdata, 4); + return 0; +} + +int _stlink_usb_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const rdata = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + const int rep_len = 2; + + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_JTAG_WRITEDEBUG_32BIT; + write_uint32(&cmd[i], addr); + write_uint32(&cmd[i + 4], data); + size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_JTAG_WRITEDEBUG_32BIT\n"); + return (int) size; + } + + return 0; +} + +int _stlink_usb_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + int i, ret; + + i = fill_command(sl, SG_DXFER_TO_DEV, len); + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_WRITEMEM_32BIT; + write_uint32(&cmd[i], addr); + write_uint16(&cmd[i + 4], len); + ret = send_only(slu, 0, cmd, slu->cmd_len); + if (ret == -1) return ret; + + ret = send_only(slu, 1, data, len); + if (ret == -1) return ret; + + return 0; +} + +int _stlink_usb_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + int i, ret; + + i = fill_command(sl, SG_DXFER_TO_DEV, 0); + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_WRITEMEM_8BIT; + write_uint32(&cmd[i], addr); + write_uint16(&cmd[i + 4], len); + ret = send_only(slu, 0, cmd, slu->cmd_len); + if (ret == -1) return ret; + + ret = send_only(slu, 1, data, len); + if (ret == -1) return ret; + + return 0; +} + +int _stlink_usb_current_mode(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const cmd = sl->c_buf; + unsigned char *const data = sl->q_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_GET_CURRENT_MODE; + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_GET_CURRENT_MODE\n"); + return -1; + } + return sl->q_buf[0]; +} + +int _stlink_usb_core_id(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const cmd = sl->c_buf; + unsigned char *const data = sl->q_buf; + ssize_t size; + int rep_len = 4; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_READCOREID; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_READCOREID\n"); + return -1; + } + + sl->core_id = read_uint32(data, 0); + return 0; +} + +int _stlink_usb_status(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_GETSTATUS; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_GETSTATUS\n"); + return (int) size; + } + sl->q_len = (int) size; + + return 0; +} + +int _stlink_usb_force_debug(stlink_t *sl) { + struct stlink_libusb *slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_FORCEDEBUG; + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_FORCEDEBUG\n"); + return (int) size; + } + + return 0; +} + +int _stlink_usb_enter_swd_mode(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + const int rep_len = 0; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_ENTER; + cmd[i++] = STLINK_DEBUG_ENTER_SWD; + + size = send_only(slu, 1, cmd, slu->cmd_len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_ENTER\n"); + return (int) size; + } + + return 0; +} + +int _stlink_usb_exit_dfu_mode(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + int i = fill_command(sl, SG_DXFER_FROM_DEV, 0); + + cmd[i++] = STLINK_DFU_COMMAND; + cmd[i++] = STLINK_DFU_EXIT; + + size = send_only(slu, 1, cmd, slu->cmd_len); + if (size == -1) { + printf("[!] send_recv STLINK_DFU_EXIT\n"); + return (int) size; + } + + return 0; +} + +/** + * TODO - not convinced this does anything... + * @param sl + */ +int _stlink_usb_reset(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_RESETSYS; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_RESETSYS\n"); + return (int) size; + } + + return 0; +} + +int _stlink_usb_jtag_reset(stlink_t *sl, int value) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_JTAG_DRIVE_NRST; + cmd[i++] = value; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_JTAG_DRIVE_NRST\n"); + return (int) size; + } + + return 0; +} + +int _stlink_usb_step(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_STEPCORE; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_STEPCORE\n"); + return (int) size; + } + + return 0; +} + +/** + * This seems to do a good job of restarting things from the beginning? + * @param sl + */ +int _stlink_usb_run(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_RUNCORE; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_RUNCORE\n"); + return (int) size; + } + + return 0; +} + +int _stlink_usb_set_swdclk(stlink_t *sl, uint16_t clk_divisor) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + int rep_len = 2; + int i; + + // clock speed only supported by stlink/v2 and for firmware >= 22 + if (sl->version.stlink_v >= 2 && sl->version.jtag_v >= 22) { + i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_APIV2_SWD_SET_FREQ; + cmd[i++] = clk_divisor & 0xFF; + cmd[i++] = (clk_divisor >> 8) & 0xFF; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_APIV2_SWD_SET_FREQ\n"); + return (int) size; + } + + return 0; + } else { + return -1; + } +} + +int _stlink_usb_exit_debug_mode(stlink_t *sl) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + int i = fill_command(sl, SG_DXFER_FROM_DEV, 0); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_EXIT; + + size = send_only(slu, 1, cmd, slu->cmd_len); + if (size == -1) { + printf("[!] send_only STLINK_DEBUG_EXIT\n"); + return (int) size; + } + + return 0; +} + +int _stlink_usb_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + int i = fill_command(sl, SG_DXFER_FROM_DEV, len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_READMEM_32BIT; + write_uint32(&cmd[i], addr); + write_uint16(&cmd[i + 4], len); + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_READMEM_32BIT\n"); + return (int) size; + } + + sl->q_len = (int) size; + + return 0; +} + +int _stlink_usb_read_reg(stlink_t *sl, int r_idx, struct stlink_reg *regp) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + uint32_t r; + uint32_t rep_len = 4; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_READREG; + cmd[i++] = (uint8_t) r_idx; + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_READREG\n"); + return (int) size; + } + sl->q_len = (int) size; + r = read_uint32(sl->q_buf, 0); + + switch (r_idx) { + case 16: + regp->xpsr = r; + break; + case 17: + regp->main_sp = r; + break; + case 18: + regp->process_sp = r; + break; + case 19: + regp->rw = r; /* XXX ?(primask, basemask etc.) */ + break; + case 20: + regp->rw2 = r; /* XXX ?(primask, basemask etc.) */ + break; + default: + regp->r[r_idx] = r; + } + + return 0; +} + +int _stlink_usb_write_reg(stlink_t *sl, uint32_t reg, int idx) { + struct stlink_libusb *const slu = sl->backend_data; + unsigned char *const data = sl->q_buf; + unsigned char *const cmd = sl->c_buf; + ssize_t size; + uint32_t rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_WRITEREG; + cmd[i++] = idx; + write_uint32(&cmd[i], reg); + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv STLINK_DEBUG_WRITEREG\n"); + return (int) size; + } + sl->q_len = (int) size; + + return 0; +} + +stlink_t *stlink_open_usb(bool reset, char serial[16]) { + stlink_t *sl = NULL; + struct stlink_libusb *slu = NULL; + int ret = -1; + int config; + + sl = calloc(1, sizeof(stlink_t)); + slu = calloc(1, sizeof(struct stlink_libusb)); + if (sl == NULL) goto on_malloc_error; + if (slu == NULL) goto on_malloc_error; + + sl->backend_data = slu; + + sl->core_stat = STLINK_CORE_STAT_UNKNOWN; + if (libusb_init(&(slu->libusb_ctx))) { + printf("failed to init libusb context, wrong version of libraries?\n"); + goto on_error; + } + + libusb_device **list; + /** @todo We should use ssize_t and use it as a counter if > 0. As per libusb + * API: ssize_t libusb_get_device_list (libusb_context *ctx, libusb_device + * ***list) */ + int cnt = (int) libusb_get_device_list(slu->libusb_ctx, &list); + struct libusb_device_descriptor desc; + int devBus = 0; + int devAddr = 0; + + /* @TODO: Reading a environment variable in a usb open function is not very + nice, this + should be refactored and moved into the CLI tools, and instead of giving + USB_BUS:USB_ADDR a real stlink + serial string should be passed to this function. Probably people are using + this but this is very odd because + as programmer can change to multiple busses and it is better to detect them + based on serial. */ + char *device = getenv("STLINK_DEVICE"); + if (device) { + char *c = strchr(device, ':'); + if (c == NULL) { + printf("STLINK_DEVICE must be : format\n"); + goto on_error; + } + devBus = atoi(device); + *c++ = 0; + devAddr = atoi(c); + printf("bus %03d dev %03d\n", devBus, devAddr); + } + + while (cnt--) { + libusb_get_device_descriptor(list[cnt], &desc); + if (desc.idVendor != STLINK_USB_VID_ST) continue; + + if (devBus && devAddr) { + if ((libusb_get_bus_number(list[cnt]) != devBus) || + (libusb_get_device_address(list[cnt]) != devAddr)) { + continue; + } + } + + if ((desc.idProduct == STLINK_USB_PID_STLINK_32L) || + (desc.idProduct == STLINK_USB_PID_STLINK_NUCLEO)) { + struct libusb_device_handle *handle; + + ret = libusb_open(list[cnt], &handle); + if (ret) continue; + + sl->serial_size = libusb_get_string_descriptor_ascii( + handle, desc.iSerialNumber, (unsigned char *) sl->serial, + sizeof(sl->serial)); + libusb_close(handle); + + if ((serial == NULL) || (*serial == 0)) break; + + if (sl->serial_size < 0) continue; + + if (memcmp(serial, &sl->serial, sl->serial_size) == 0) break; + + continue; + } + + if (desc.idProduct == STLINK_USB_PID_STLINK) { + slu->protocoll = 1; + break; + } + } + + if (cnt < 0) { + printf("Couldn't find %s ST-Link/V2 devices\n", + (devBus && devAddr) ? "matched" : "any"); + goto on_error; + } else { + ret = libusb_open(list[cnt], &slu->usb_handle); + if (ret != 0) { + printf("Error %d (%s) opening ST-Link/V2 device %03d:%03d\n", ret, + strerror(errno), libusb_get_bus_number(list[cnt]), + libusb_get_device_address(list[cnt])); + goto on_error; + } + } + + libusb_free_device_list(list, 1); + + if (libusb_kernel_driver_active(slu->usb_handle, 0) == 1) { + ret = libusb_detach_kernel_driver(slu->usb_handle, 0); + if (ret < 0) { + printf("libusb_detach_kernel_driver(() error %s\n", strerror(-ret)); + goto on_libusb_error; + } + } + + if (libusb_get_configuration(slu->usb_handle, &config)) { + /* this may fail for a previous configured device */ + printf("libusb_get_configuration()\n"); + goto on_libusb_error; + } + + if (config != 1) { + printf("setting new configuration (%d -> 1)\n", config); + if (libusb_set_configuration(slu->usb_handle, 1)) { + /* this may fail for a previous configured device */ + printf("libusb_set_configuration() failed\n"); + goto on_libusb_error; + } + } + + if (libusb_claim_interface(slu->usb_handle, 0)) { + printf( + "Stlink usb device found, but unable to claim (probably already in " + "use?)\n"); + goto on_libusb_error; + } + + // TODO - could use the scanning techniq from stm8 code here... + slu->ep_rep = 1 /* ep rep */ | LIBUSB_ENDPOINT_IN; + if (desc.idProduct == STLINK_USB_PID_STLINK_NUCLEO) { + slu->ep_req = 1 /* ep req */ | LIBUSB_ENDPOINT_OUT; + } else { + slu->ep_req = 2 /* ep req */ | LIBUSB_ENDPOINT_OUT; + } + + slu->sg_transfer_idx = 0; + // TODO - never used at the moment, always CMD_SIZE + slu->cmd_len = (slu->protocoll == 1) ? STLINK_SG_SIZE : STLINK_CMD_SIZE; + + if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) { + printf("-- exit_dfu_mode\n"); + stlink_exit_dfu_mode(sl); + } + + if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) { + stlink_enter_swd_mode(sl); + } + + sl->version.stlink_v = 2; + + if (reset) { + if (sl->version.stlink_v > 1) stlink_jtag_reset(sl, 2); + stlink_reset(sl); + usleep(10000); + } + + ret = stlink_load_device_params(sl); + + // Set the stlink clock speed (default is 1800kHz) + stlink_set_swdclk(sl, STLINK_SWDCLK_1P8MHZ_DIVISOR); + +on_libusb_error: + if (ret == -1) { + stlink_close(sl); + return NULL; + } + + return sl; + +on_error: + if (slu->libusb_ctx) libusb_exit(slu->libusb_ctx); + +on_malloc_error: + if (sl != NULL) free(sl); + if (slu != NULL) free(slu); + + return NULL; +} diff --git a/src/mongoose-6.11/src/common/platforms/stm32/usb.h b/src/mongoose-6.11/src/common/platforms/stm32/usb.h new file mode 100644 index 0000000..6ee4ff3 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/stm32/usb.h @@ -0,0 +1,76 @@ +/* + * File: stlink/usb.h + * Author: karl + * + * Created on October 1, 2011, 11:29 PM + */ + +#ifndef STLINK_USB_H +#define STLINK_USB_H + +#include +#include + +#include "stlink.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define STLINK_USB_VID_ST 0x0483 +#define STLINK_USB_PID_STLINK 0x3744 +#define STLINK_USB_PID_STLINK_32L 0x3748 +#define STLINK_USB_PID_STLINK_NUCLEO 0x374b + +#define STLINK_SG_SIZE 31 +#define STLINK_CMD_SIZE 16 + +struct stlink_libusb { + libusb_context *libusb_ctx; + libusb_device_handle *usb_handle; + unsigned int ep_req; + unsigned int ep_rep; + int protocoll; + unsigned int sg_transfer_idx; + unsigned int cmd_len; +}; + +/** + * Open a stlink + * @param verbose Verbosity loglevel + * @param reset Reset stlink programmer + * @param serial Serial number to search for, when NULL the first stlink found + * is opened (binary format) + * @retval NULL Error while opening the stlink + * @retval !NULL Stlink found and ready to use + */ +stlink_t *stlink_open_usb(bool reset, char serial[16]); + +void _stlink_usb_close(stlink_t *sl); +int _stlink_usb_version(stlink_t *sl); +int32_t _stlink_usb_target_voltage(stlink_t *sl); +int _stlink_usb_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data); +int _stlink_usb_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data); +int _stlink_usb_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len); +int _stlink_usb_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len); +int _stlink_usb_current_mode(stlink_t *sl); +int _stlink_usb_core_id(stlink_t *sl); +int _stlink_usb_status(stlink_t *sl); +int _stlink_usb_force_debug(stlink_t *sl); +int _stlink_usb_enter_swd_mode(stlink_t *sl); +int _stlink_usb_exit_dfu_mode(stlink_t *sl); +int _stlink_usb_reset(stlink_t *sl); +int _stlink_usb_jtag_reset(stlink_t *sl, int value); +int _stlink_usb_step(stlink_t *sl); +int _stlink_usb_run(stlink_t *sl); +int _stlink_usb_set_swdclk(stlink_t *sl, uint16_t clk_divisor); +int _stlink_usb_exit_debug_mode(stlink_t *sl); +int _stlink_usb_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len); +int _stlink_usb_read_reg(stlink_t *sl, int r_idx, struct stlink_reg *regp); +int _stlink_usb_write_reg(stlink_t *sl, uint32_t reg, int idx); + +#ifdef __cplusplus +} +#endif + +#endif /* STLINK_USB_H */ diff --git a/src/mongoose-6.11/src/common/platforms/wince/wince_libc.c b/src/mongoose-6.11/src/common/platforms/wince/wince_libc.c new file mode 100644 index 0000000..b385fde --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/wince/wince_libc.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2016 Cesanta Software Limited + * All rights reserved + */ + +#ifdef WINCE + +const char *strerror(int err) { + /* + * TODO(alashkin): there is no strerror on WinCE; + * look for similar wce_xxxx function + */ + static char buf[10]; + snprintf(buf, sizeof(buf), "%d", err); + return buf; +} + +int open(const char *filename, int oflag, int pmode) { + /* + * TODO(alashkin): mg_open function is not used in mongoose + * but exists in documentation as utility function + * Shall we delete it at all or implement for WinCE as well? + */ + DebugBreak(); + return 0; /* for compiler */ +} + +int _wstati64(const wchar_t *path, cs_stat_t *st) { + DWORD fa = GetFileAttributesW(path); + if (fa == INVALID_FILE_ATTRIBUTES) { + return -1; + } + memset(st, 0, sizeof(*st)); + if ((fa & FILE_ATTRIBUTE_DIRECTORY) == 0) { + HANDLE h; + FILETIME ftime; + st->st_mode |= _S_IFREG; + h = CreateFileW(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) { + return -1; + } + st->st_size = GetFileSize(h, NULL); + GetFileTime(h, NULL, NULL, &ftime); + st->st_mtime = (uint32_t)((((uint64_t) ftime.dwLowDateTime + + ((uint64_t) ftime.dwHighDateTime << 32)) / + 10000000.0) - + 11644473600); + CloseHandle(h); + } else { + st->st_mode |= _S_IFDIR; + } + return 0; +} + +/* Windows CE doesn't have neither gmtime nor strftime */ +static void mg_gmt_time_string(char *buf, size_t buf_len, time_t *t) { + FILETIME ft; + SYSTEMTIME systime; + if (t != NULL) { + uint64_t filetime = (*t + 11644473600) * 10000000; + ft.dwLowDateTime = filetime & 0xFFFFFFFF; + ft.dwHighDateTime = (filetime & 0xFFFFFFFF00000000) >> 32; + FileTimeToSystemTime(&ft, &systime); + } else { + GetSystemTime(&systime); + } + /* There is no PRIu16 in WinCE SDK */ + snprintf(buf, buf_len, "%d.%d.%d %d:%d:%d GMT", (int) systime.wYear, + (int) systime.wMonth, (int) systime.wDay, (int) systime.wHour, + (int) systime.wMinute, (int) systime.wSecond); +} + +#endif diff --git a/src/mongoose-6.11/src/common/platforms/windows/windows_direct.c b/src/mongoose-6.11/src/common/platforms/windows/windows_direct.c new file mode 100644 index 0000000..8932899 --- /dev/null +++ b/src/mongoose-6.11/src/common/platforms/windows/windows_direct.c @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2017 Cesanta Software Limited + * All rights reserved + */ + +#ifdef _WIN32 + +int rmdir(const char *dirname) { + return _rmdir(dirname); +} + +unsigned int sleep(unsigned int seconds) { + Sleep(seconds * 1000); + return 0; +} + +#endif /* _WIN32 */ diff --git a/src/mongoose-6.11/src/common/test_util.c b/src/mongoose-6.11/src/common/test_util.c new file mode 100644 index 0000000..705a12f --- /dev/null +++ b/src/mongoose-6.11/src/common/test_util.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#include "common/test_util.h" + +#include +#include +#include +#include +#ifndef _WIN32 +#include +#else +#include +#endif + +int num_tests = 0; + +static char *_escape(const char *s, size_t n) { + size_t i, j; + char *res = (char *) malloc(n * 4 + 1); + for (i = j = 0; s[i] != '\0' && i < n; i++) { + if (!iscntrl((int) s[i])) { + res[j++] = s[i]; + } else { + j += sprintf(res + j, "\\x%02x", s[i]); + } + } + res[j] = '\0'; + return res; +} + +void _strfail(const char *a, const char *e, int len) { + char *ae, *ee; + if (len < 0) { + len = strlen(a); + if (strlen(e) > (size_t) len) len = strlen(e); + } + ae = _escape(a, len); + ee = _escape(e, len); + printf("Expected: %s\nActual : %s\n", ee, ae); + free(ae); + free(ee); +} + +int _assert_streq(const char *actual, const char *expected) { + if (strcmp(actual, expected) != 0) { + _strfail(actual, expected, -1); + return 0; + } + return 1; +} + +int _assert_streq_nz(const char *actual, const char *expected) { + size_t n = strlen(expected); + if (strncmp(actual, expected, n) != 0) { + _strfail(actual, expected, n); + return 0; + } + return 1; +} + +#if MG_ENABLE_POLL_UNTIL +int c_str_ne(void *a, void *b) { + int r = strcmp((const char *) a, (const char *) b); + return r; +} + +int c_int_ne(void *a, void *b) { + return *((int *) a) != (intptr_t) b; +} + +int c_int_eq(void *a, void *b) { + return *((int *) a) == (intptr_t) b; +} + +void poll_until(struct mg_mgr *mgr, double timeout, int (*cond)(void *, void *), + void *cond_arg1, void *cond_arg2) { + int i; + double start = cs_time(); + while (cs_time() - start < timeout) { + mg_mgr_poll(mgr, 2); + if (cond != NULL && cond(cond_arg1, cond_arg2)) { + /* A few more cycles to test for overshoots. */ + for (i = 0; i < 5; i++) { + mg_mgr_poll(mgr, 2); + } + return; + } + } +} +#endif diff --git a/src/mongoose-6.11/src/common/test_util.h b/src/mongoose-6.11/src/common/test_util.h new file mode 100644 index 0000000..6110387 --- /dev/null +++ b/src/mongoose-6.11/src/common/test_util.h @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2015 Cesanta Software Limited + * All rights reserved + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +#ifndef CS_COMMON_TEST_UTIL_H_ +#define CS_COMMON_TEST_UTIL_H_ + +#include +#include + +#include "common/cs_time.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern int g_num_tests; + +#ifdef MG_TEST_ABORT_ON_FAIL +#define MG_TEST_ABORT abort() +#else +#define MG_TEST_ABORT +#endif + +int _assert_streq(const char *actual, const char *expected); +int _assert_streq_nz(const char *actual, const char *expected); +void _strfail(const char *a, const char *e, int len); + +#define FAIL(str, line) \ + do { \ + printf("%s:%d:1 [%s] (in %s)\n", __FILE__, line, str, __func__); \ + MG_TEST_ABORT; \ + return str; \ + } while (0) + +#define ASSERT(expr) \ + do { \ + g_num_tests++; \ + if (!(expr)) FAIL(#expr, __LINE__); \ + } while (0) +#define ASSERT_TRUE(expr) ASSERT(expr) +#define ASSERT_FALSE(expr) ASSERT(!(expr)) + +/* + * Run test function, use its name as the test name to print + */ +#define RUN_TEST(test) RUN_TEST_WNAME(test, #test) + +/* + * Run test function, use the provided name as the test name to print + */ +#define RUN_TEST_WNAME(test, test_name) \ + do { \ + const char *msg = NULL; \ + if (strstr(test_name, filter)) { \ + double elapsed = cs_time(); \ + msg = test(); \ + elapsed = cs_time() - elapsed; \ + printf(" [%.3f] %s\n", elapsed, test_name); \ + fflush(stdout); \ + } \ + if (msg) return msg; \ + } while (0) + +/* VC6 doesn't know how to cast an unsigned 64-bit int to double */ +#if (defined(_MSC_VER) && _MSC_VER <= 1200) +#define AS_DOUBLE(d) (double)(int64_t)(d) +#else +#define AS_DOUBLE(d) (double)(d) +#endif + +/* + * Numeric equality assertion. Comparison is made in native types but for + * printing both are convetrted to double. + */ +#define ASSERT_EQ(actual, expected) \ + do { \ + g_num_tests++; \ + if (!((actual) == (expected))) { \ + printf("%f != %f\n", AS_DOUBLE(actual), AS_DOUBLE(expected)); \ + FAIL(#actual " == " #expected, __LINE__); \ + } \ + } while (0) + +/* "Less than" assertion. */ +#define ASSERT_LT(a, b) \ + do { \ + g_num_tests++; \ + if (!((a) < (b))) { \ + printf("%f >= %f\n", AS_DOUBLE(a), AS_DOUBLE(b)); \ + FAIL(#a " < " #b, __LINE__); \ + } \ + } while (0) + +/* "Greater than" assertion. */ +#define ASSERT_GT(a, b) \ + do { \ + g_num_tests++; \ + if (!((a) > (b))) { \ + printf("%f <= %f\n", AS_DOUBLE(a), AS_DOUBLE(b)); \ + FAIL(#a " > " #b, __LINE__); \ + } \ + } while (0) + +/* Assert that actual == expected, where both are NUL-terminated. */ +#define ASSERT_STREQ(actual, expected) \ + do { \ + g_num_tests++; \ + if (!_assert_streq(actual, expected)) { \ + FAIL("ASSERT_STREQ(" #actual ", " #expected ")", __LINE__); \ + } \ + } while (0) + +/* Assert that actual == expected, where both are pointers */ +#define ASSERT_PTREQ(actual, expected) \ + do { \ + g_num_tests++; \ + if (actual != expected) { \ + printf("%p != %p\n", actual, expected); \ + FAIL("ASSERT_PTREQ(" #actual ", " #expected ")", __LINE__); \ + } \ + } while (0) + +/* Assert that actual != expected, where both are pointers */ +#define ASSERT_PTRNEQ(actual, expected) \ + do { \ + g_num_tests++; \ + if (actual == expected) { \ + printf("%p == %p\n", actual, expected); \ + FAIL("ASSERT_PTRNEQ(" #actual ", " #expected ")", __LINE__); \ + } \ + } while (0) + +/* Same as STREQ, but only expected is NUL-terminated. */ +#define ASSERT_STREQ_NZ(actual, expected) \ + do { \ + g_num_tests++; \ + if (!_assert_streq_nz(actual, expected)) { \ + FAIL("ASSERT_STREQ_NZ(" #actual ", " #expected ")", __LINE__); \ + } \ + } while (0) + +#define ASSERT_MG_STREQ(actual, expected) \ + do { \ + g_num_tests++; \ + if ((actual).len != strlen(expected) || \ + memcmp((actual).p, expected, (actual).len) != 0) { \ + printf("'%.*s' (%d) != '%s'\n", (int)(actual).len, (actual).p, \ + (int)(actual).len, expected); \ + FAIL("ASSERT_MG_STREQ(" #actual ", " #expected ")", __LINE__); \ + } \ + } while (0) + +#define CHECK_CALL(call) \ + do { \ + const char *r = (call); \ + if (r != NULL) { \ + fprintf(stderr, "... %s:%d in %s\n", __FILE__, __LINE__, __func__); \ + return r; \ + } \ + } while (0) + +#ifndef MG_ENABLE_POLL_UNTIL +#define MG_ENABLE_POLL_UNTIL 0 +#endif + +#if MG_ENABLE_POLL_UNTIL +#include "mongoose.h" +int c_str_ne(void *a, void *b); +int c_int_ne(void *a, void *b); +int c_int_eq(void *a, void *b); +void poll_until(struct mg_mgr *mgr, double timeout, int (*cond)(void *, void *), + void *cond_arg1, void *cond_arg2); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* CS_COMMON_TEST_UTIL_H_ */ diff --git a/src/mongoose-6.11/src/mg_coap.c b/src/mongoose-6.11/src/mg_coap.c new file mode 100644 index 0000000..29667b7 --- /dev/null +++ b/src/mongoose-6.11/src/mg_coap.c @@ -0,0 +1,597 @@ +/* + * Copyright (c) 2015 Cesanta Software Limited + * All rights reserved + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +#include "mg_internal.h" +#include "mg_coap.h" + +#if MG_ENABLE_COAP + +void mg_coap_free_options(struct mg_coap_message *cm) { + while (cm->options != NULL) { + struct mg_coap_option *next = cm->options->next; + MG_FREE(cm->options); + cm->options = next; + } +} + +struct mg_coap_option *mg_coap_add_option(struct mg_coap_message *cm, + uint32_t number, char *value, + size_t len) { + struct mg_coap_option *new_option = + (struct mg_coap_option *) MG_CALLOC(1, sizeof(*new_option)); + + new_option->number = number; + new_option->value.p = value; + new_option->value.len = len; + + if (cm->options == NULL) { + cm->options = cm->optiomg_tail = new_option; + } else { + /* + * A very simple attention to help clients to compose options: + * CoAP wants to see options ASC ordered. + * Could be change by using sort in coap_compose + */ + if (cm->optiomg_tail->number <= new_option->number) { + /* if option is already ordered just add it */ + cm->optiomg_tail = cm->optiomg_tail->next = new_option; + } else { + /* looking for appropriate position */ + struct mg_coap_option *current_opt = cm->options; + struct mg_coap_option *prev_opt = 0; + + while (current_opt != NULL) { + if (current_opt->number > new_option->number) { + break; + } + prev_opt = current_opt; + current_opt = current_opt->next; + } + + if (prev_opt != NULL) { + prev_opt->next = new_option; + new_option->next = current_opt; + } else { + /* insert new_option to the beginning */ + new_option->next = cm->options; + cm->options = new_option; + } + } + } + + return new_option; +} + +/* + * Fills CoAP header in mg_coap_message. + * + * Helper function. + */ +static char *coap_parse_header(char *ptr, struct mbuf *io, + struct mg_coap_message *cm) { + if (io->len < sizeof(uint32_t)) { + cm->flags |= MG_COAP_NOT_ENOUGH_DATA; + return NULL; + } + + /* + * Version (Ver): 2-bit unsigned integer. Indicates the CoAP version + * number. Implementations of this specification MUST set this field + * to 1 (01 binary). Other values are reserved for future versions. + * Messages with unknown version numbers MUST be silently ignored. + */ + if (((uint8_t) *ptr >> 6) != 1) { + cm->flags |= MG_COAP_IGNORE; + return NULL; + } + + /* + * Type (T): 2-bit unsigned integer. Indicates if this message is of + * type Confirmable (0), Non-confirmable (1), Acknowledgement (2), or + * Reset (3). + */ + cm->msg_type = ((uint8_t) *ptr & 0x30) >> 4; + cm->flags |= MG_COAP_MSG_TYPE_FIELD; + + /* + * Token Length (TKL): 4-bit unsigned integer. Indicates the length of + * the variable-length Token field (0-8 bytes). Lengths 9-15 are + * reserved, MUST NOT be sent, and MUST be processed as a message + * format error. + */ + cm->token.len = *ptr & 0x0F; + if (cm->token.len > 8) { + cm->flags |= MG_COAP_FORMAT_ERROR; + return NULL; + } + + ptr++; + + /* + * Code: 8-bit unsigned integer, split into a 3-bit class (most + * significant bits) and a 5-bit detail (least significant bits) + */ + cm->code_class = (uint8_t) *ptr >> 5; + cm->code_detail = *ptr & 0x1F; + cm->flags |= (MG_COAP_CODE_CLASS_FIELD | MG_COAP_CODE_DETAIL_FIELD); + + ptr++; + + /* Message ID: 16-bit unsigned integer in network byte order. */ + cm->msg_id = (uint8_t) *ptr << 8 | (uint8_t) * (ptr + 1); + cm->flags |= MG_COAP_MSG_ID_FIELD; + + ptr += 2; + + return ptr; +} + +/* + * Fills token information in mg_coap_message. + * + * Helper function. + */ +static char *coap_get_token(char *ptr, struct mbuf *io, + struct mg_coap_message *cm) { + if (cm->token.len != 0) { + if (ptr + cm->token.len > io->buf + io->len) { + cm->flags |= MG_COAP_NOT_ENOUGH_DATA; + return NULL; + } else { + cm->token.p = ptr; + ptr += cm->token.len; + cm->flags |= MG_COAP_TOKEN_FIELD; + } + } + + return ptr; +} + +/* + * Returns Option Delta or Length. + * + * Helper function. + */ +static int coap_get_ext_opt(char *ptr, struct mbuf *io, uint16_t *opt_info) { + int ret = 0; + + if (*opt_info == 13) { + /* + * 13: An 8-bit unsigned integer follows the initial byte and + * indicates the Option Delta/Length minus 13. + */ + if (ptr < io->buf + io->len) { + *opt_info = (uint8_t) *ptr + 13; + ret = sizeof(uint8_t); + } else { + ret = -1; /* LCOV_EXCL_LINE */ + } + } else if (*opt_info == 14) { + /* + * 14: A 16-bit unsigned integer in network byte order follows the + * initial byte and indicates the Option Delta/Length minus 269. + */ + if (ptr + sizeof(uint8_t) < io->buf + io->len) { + *opt_info = ((uint8_t) *ptr << 8 | (uint8_t) * (ptr + 1)) + 269; + ret = sizeof(uint16_t); + } else { + ret = -1; /* LCOV_EXCL_LINE */ + } + } + + return ret; +} + +/* + * Fills options in mg_coap_message. + * + * Helper function. + * + * General options format: + * +---------------+---------------+ + * | Option Delta | Option Length | 1 byte + * +---------------+---------------+ + * \ Option Delta (extended) \ 0-2 bytes + * +-------------------------------+ + * / Option Length (extended) \ 0-2 bytes + * +-------------------------------+ + * \ Option Value \ 0 or more bytes + * +-------------------------------+ + */ +static char *coap_get_options(char *ptr, struct mbuf *io, + struct mg_coap_message *cm) { + uint16_t prev_opt = 0; + + if (ptr == io->buf + io->len) { + /* end of packet, ok */ + return NULL; + } + + /* 0xFF is payload marker */ + while (ptr < io->buf + io->len && (uint8_t) *ptr != 0xFF) { + uint16_t option_delta, option_lenght; + int optinfo_len; + + /* Option Delta: 4-bit unsigned integer */ + option_delta = ((uint8_t) *ptr & 0xF0) >> 4; + /* Option Length: 4-bit unsigned integer */ + option_lenght = *ptr & 0x0F; + + if (option_delta == 15 || option_lenght == 15) { + /* + * 15: Reserved for future use. If the field is set to this value, + * it MUST be processed as a message format error + */ + cm->flags |= MG_COAP_FORMAT_ERROR; + break; + } + + ptr++; + + /* check for extended option delta */ + optinfo_len = coap_get_ext_opt(ptr, io, &option_delta); + if (optinfo_len == -1) { + cm->flags |= MG_COAP_NOT_ENOUGH_DATA; /* LCOV_EXCL_LINE */ + break; /* LCOV_EXCL_LINE */ + } + + ptr += optinfo_len; + + /* check or extended option lenght */ + optinfo_len = coap_get_ext_opt(ptr, io, &option_lenght); + if (optinfo_len == -1) { + cm->flags |= MG_COAP_NOT_ENOUGH_DATA; /* LCOV_EXCL_LINE */ + break; /* LCOV_EXCL_LINE */ + } + + ptr += optinfo_len; + + /* + * Instead of specifying the Option Number directly, the instances MUST + * appear in order of their Option Numbers and a delta encoding is used + * between them. + */ + option_delta += prev_opt; + + mg_coap_add_option(cm, option_delta, ptr, option_lenght); + + prev_opt = option_delta; + + if (ptr + option_lenght > io->buf + io->len) { + cm->flags |= MG_COAP_NOT_ENOUGH_DATA; /* LCOV_EXCL_LINE */ + break; /* LCOV_EXCL_LINE */ + } + + ptr += option_lenght; + } + + if ((cm->flags & MG_COAP_ERROR) != 0) { + mg_coap_free_options(cm); + return NULL; + } + + cm->flags |= MG_COAP_OPTIOMG_FIELD; + + if (ptr == io->buf + io->len) { + /* end of packet, ok */ + return NULL; + } + + ptr++; + + return ptr; +} + +uint32_t mg_coap_parse(struct mbuf *io, struct mg_coap_message *cm) { + char *ptr; + + memset(cm, 0, sizeof(*cm)); + + if ((ptr = coap_parse_header(io->buf, io, cm)) == NULL) { + return cm->flags; + } + + if ((ptr = coap_get_token(ptr, io, cm)) == NULL) { + return cm->flags; + } + + if ((ptr = coap_get_options(ptr, io, cm)) == NULL) { + return cm->flags; + } + + /* the rest is payload */ + cm->payload.len = io->len - (ptr - io->buf); + if (cm->payload.len != 0) { + cm->payload.p = ptr; + cm->flags |= MG_COAP_PAYLOAD_FIELD; + } + + return cm->flags; +} + +/* + * Calculates extended size of given Opt Number/Length in coap message. + * + * Helper function. + */ +static size_t coap_get_ext_opt_size(uint32_t value) { + int ret = 0; + + if (value >= 13 && value <= 0xFF + 13) { + ret = sizeof(uint8_t); + } else if (value > 0xFF + 13 && value <= 0xFFFF + 269) { + ret = sizeof(uint16_t); + } + + return ret; +} + +/* + * Splits given Opt Number/Length into base and ext values. + * + * Helper function. + */ +static int coap_split_opt(uint32_t value, uint8_t *base, uint16_t *ext) { + int ret = 0; + + if (value < 13) { + *base = value; + } else if (value >= 13 && value <= 0xFF + 13) { + *base = 13; + *ext = value - 13; + ret = sizeof(uint8_t); + } else if (value > 0xFF + 13 && value <= 0xFFFF + 269) { + *base = 14; + *ext = value - 269; + ret = sizeof(uint16_t); + } + + return ret; +} + +/* + * Puts uint16_t (in network order) into given char stream. + * + * Helper function. + */ +static char *coap_add_uint16(char *ptr, uint16_t val) { + *ptr = val >> 8; + ptr++; + *ptr = val & 0x00FF; + ptr++; + return ptr; +} + +/* + * Puts extended value of Opt Number/Length into given char stream. + * + * Helper function. + */ +static char *coap_add_opt_info(char *ptr, uint16_t val, size_t len) { + if (len == sizeof(uint8_t)) { + *ptr = (char) val; + ptr++; + } else if (len == sizeof(uint16_t)) { + ptr = coap_add_uint16(ptr, val); + } + + return ptr; +} + +/* + * Verifies given mg_coap_message and calculates message size for it. + * + * Helper function. + */ +static uint32_t coap_calculate_packet_size(struct mg_coap_message *cm, + size_t *len) { + struct mg_coap_option *opt; + uint32_t prev_opt_number; + + *len = 4; /* header */ + if (cm->msg_type > MG_COAP_MSG_MAX) { + return MG_COAP_ERROR | MG_COAP_MSG_TYPE_FIELD; + } + if (cm->token.len > 8) { + return MG_COAP_ERROR | MG_COAP_TOKEN_FIELD; + } + if (cm->code_class > 7) { + return MG_COAP_ERROR | MG_COAP_CODE_CLASS_FIELD; + } + if (cm->code_detail > 31) { + return MG_COAP_ERROR | MG_COAP_CODE_DETAIL_FIELD; + } + + *len += cm->token.len; + if (cm->payload.len != 0) { + *len += cm->payload.len + 1; /* ... + 1; add payload marker */ + } + + opt = cm->options; + prev_opt_number = 0; + while (opt != NULL) { + *len += 1; /* basic delta/length */ + *len += coap_get_ext_opt_size(opt->number - prev_opt_number); + *len += coap_get_ext_opt_size((uint32_t) opt->value.len); + /* + * Current implementation performs check if + * option_number > previous option_number and produces an error + * TODO(alashkin): write design doc with limitations + * May be resorting is more suitable solution. + */ + if ((opt->next != NULL && opt->number > opt->next->number) || + opt->value.len > 0xFFFF + 269 || + opt->number - prev_opt_number > 0xFFFF + 269) { + return MG_COAP_ERROR | MG_COAP_OPTIOMG_FIELD; + } + *len += opt->value.len; + prev_opt_number = opt->number; + opt = opt->next; + } + + return 0; +} + +uint32_t mg_coap_compose(struct mg_coap_message *cm, struct mbuf *io) { + struct mg_coap_option *opt; + uint32_t res, prev_opt_number; + size_t prev_io_len, packet_size; + char *ptr; + + res = coap_calculate_packet_size(cm, &packet_size); + if (res != 0) { + return res; + } + + /* saving previous lenght to handle non-empty mbuf */ + prev_io_len = io->len; + if (mbuf_append(io, NULL, packet_size) == 0) return MG_COAP_ERROR; + ptr = io->buf + prev_io_len; + + /* + * since cm is verified, it is possible to use bits shift operator + * without additional zeroing of unused bits + */ + + /* ver: 2 bits, msg_type: 2 bits, toklen: 4 bits */ + *ptr = (1 << 6) | (cm->msg_type << 4) | (uint8_t)(cm->token.len); + ptr++; + + /* code class: 3 bits, code detail: 5 bits */ + *ptr = (cm->code_class << 5) | (cm->code_detail); + ptr++; + + ptr = coap_add_uint16(ptr, cm->msg_id); + + if (cm->token.len != 0) { + memcpy(ptr, cm->token.p, cm->token.len); + ptr += cm->token.len; + } + + opt = cm->options; + prev_opt_number = 0; + while (opt != NULL) { + uint8_t delta_base = 0, length_base = 0; + uint16_t delta_ext = 0, length_ext = 0; + + size_t opt_delta_len = + coap_split_opt(opt->number - prev_opt_number, &delta_base, &delta_ext); + size_t opt_lenght_len = + coap_split_opt((uint32_t) opt->value.len, &length_base, &length_ext); + + *ptr = (delta_base << 4) | length_base; + ptr++; + + ptr = coap_add_opt_info(ptr, delta_ext, opt_delta_len); + ptr = coap_add_opt_info(ptr, length_ext, opt_lenght_len); + + if (opt->value.len != 0) { + memcpy(ptr, opt->value.p, opt->value.len); + ptr += opt->value.len; + } + + prev_opt_number = opt->number; + opt = opt->next; + } + + if (cm->payload.len != 0) { + *ptr = (char) -1; + ptr++; + memcpy(ptr, cm->payload.p, cm->payload.len); + } + + return 0; +} + +uint32_t mg_coap_send_message(struct mg_connection *nc, + struct mg_coap_message *cm) { + struct mbuf packet_out; + uint32_t compose_res; + + mbuf_init(&packet_out, 0); + compose_res = mg_coap_compose(cm, &packet_out); + if (compose_res != 0) { + return compose_res; /* LCOV_EXCL_LINE */ + } + + mg_send(nc, packet_out.buf, (int) packet_out.len); + mbuf_free(&packet_out); + + return 0; +} + +uint32_t mg_coap_send_ack(struct mg_connection *nc, uint16_t msg_id) { + struct mg_coap_message cm; + memset(&cm, 0, sizeof(cm)); + cm.msg_type = MG_COAP_MSG_ACK; + cm.msg_id = msg_id; + + return mg_coap_send_message(nc, &cm); +} + +static void coap_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct mbuf *io = &nc->recv_mbuf; + struct mg_coap_message cm; + uint32_t parse_res; + + memset(&cm, 0, sizeof(cm)); + + nc->handler(nc, ev, ev_data MG_UD_ARG(user_data)); + + switch (ev) { + case MG_EV_RECV: + parse_res = mg_coap_parse(io, &cm); + if ((parse_res & MG_COAP_IGNORE) == 0) { + if ((cm.flags & MG_COAP_NOT_ENOUGH_DATA) != 0) { + /* + * Since we support UDP only + * MG_COAP_NOT_ENOUGH_DATA == MG_COAP_FORMAT_ERROR + */ + cm.flags |= MG_COAP_FORMAT_ERROR; /* LCOV_EXCL_LINE */ + } /* LCOV_EXCL_LINE */ + nc->handler(nc, MG_COAP_EVENT_BASE + cm.msg_type, + &cm MG_UD_ARG(user_data)); + } + + mg_coap_free_options(&cm); + mbuf_remove(io, io->len); + break; + } +} +/* + * Attach built-in CoAP event handler to the given connection. + * + * The user-defined event handler will receive following extra events: + * + * - MG_EV_COAP_CON + * - MG_EV_COAP_NOC + * - MG_EV_COAP_ACK + * - MG_EV_COAP_RST + */ +int mg_set_protocol_coap(struct mg_connection *nc) { + /* supports UDP only */ + if ((nc->flags & MG_F_UDP) == 0) { + return -1; + } + + nc->proto_handler = coap_handler; + + return 0; +} + +#endif /* MG_ENABLE_COAP */ diff --git a/src/mongoose-6.11/src/mg_coap.h b/src/mongoose-6.11/src/mg_coap.h new file mode 100644 index 0000000..296933f --- /dev/null +++ b/src/mongoose-6.11/src/mg_coap.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2015 Cesanta Software Limited + * All rights reserved + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +/* + * === CoAP API reference + * + * CoAP message format: + * + * ``` + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * |Ver| T | TKL | Code | Message ID | Token (if any, TKL bytes) ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * | Options (if any) ... |1 1 1 1 1 1 1 1| Payload (if any) ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * ``` + */ + +#ifndef CS_MONGOOSE_SRC_COAP_H_ +#define CS_MONGOOSE_SRC_COAP_H_ + +#if MG_ENABLE_COAP + +#define MG_COAP_MSG_TYPE_FIELD 0x2 +#define MG_COAP_CODE_CLASS_FIELD 0x4 +#define MG_COAP_CODE_DETAIL_FIELD 0x8 +#define MG_COAP_MSG_ID_FIELD 0x10 +#define MG_COAP_TOKEN_FIELD 0x20 +#define MG_COAP_OPTIOMG_FIELD 0x40 +#define MG_COAP_PAYLOAD_FIELD 0x80 + +#define MG_COAP_ERROR 0x10000 +#define MG_COAP_FORMAT_ERROR (MG_COAP_ERROR | 0x20000) +#define MG_COAP_IGNORE (MG_COAP_ERROR | 0x40000) +#define MG_COAP_NOT_ENOUGH_DATA (MG_COAP_ERROR | 0x80000) +#define MG_COAP_NETWORK_ERROR (MG_COAP_ERROR | 0x100000) + +#define MG_COAP_MSG_CON 0 +#define MG_COAP_MSG_NOC 1 +#define MG_COAP_MSG_ACK 2 +#define MG_COAP_MSG_RST 3 +#define MG_COAP_MSG_MAX 3 + +#define MG_COAP_CODECLASS_REQUEST 0 +#define MG_COAP_CODECLASS_RESP_OK 2 +#define MG_COAP_CODECLASS_CLIENT_ERR 4 +#define MG_COAP_CODECLASS_SRV_ERR 5 + +#define MG_COAP_EVENT_BASE 300 +#define MG_EV_COAP_CON (MG_COAP_EVENT_BASE + MG_COAP_MSG_CON) +#define MG_EV_COAP_NOC (MG_COAP_EVENT_BASE + MG_COAP_MSG_NOC) +#define MG_EV_COAP_ACK (MG_COAP_EVENT_BASE + MG_COAP_MSG_ACK) +#define MG_EV_COAP_RST (MG_COAP_EVENT_BASE + MG_COAP_MSG_RST) + +/* + * CoAP options. + * Use mg_coap_add_option and mg_coap_free_options + * for creation and destruction. + */ +struct mg_coap_option { + struct mg_coap_option *next; + uint32_t number; + struct mg_str value; +}; + +/* CoAP message. See RFC 7252 for details. */ +struct mg_coap_message { + uint32_t flags; + uint8_t msg_type; + uint8_t code_class; + uint8_t code_detail; + uint16_t msg_id; + struct mg_str token; + struct mg_coap_option *options; + struct mg_str payload; + struct mg_coap_option *optiomg_tail; +}; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Sets CoAP protocol handler - triggers CoAP specific events. */ +int mg_set_protocol_coap(struct mg_connection *nc); + +/* + * Adds a new option to mg_coap_message structure. + * Returns pointer to the newly created option. + * Note: options must be freed by using mg_coap_free_options + */ +struct mg_coap_option *mg_coap_add_option(struct mg_coap_message *cm, + uint32_t number, char *value, + size_t len); + +/* + * Frees the memory allocated for options. + * If the cm parameter doesn't contain any option it does nothing. + */ +void mg_coap_free_options(struct mg_coap_message *cm); + +/* + * Composes a CoAP message from `mg_coap_message` + * and sends it into `nc` connection. + * Returns 0 on success. On error, it is a bitmask: + * + * - `#define MG_COAP_ERROR 0x10000` + * - `#define MG_COAP_FORMAT_ERROR (MG_COAP_ERROR | 0x20000)` + * - `#define MG_COAP_IGNORE (MG_COAP_ERROR | 0x40000)` + * - `#define MG_COAP_NOT_ENOUGH_DATA (MG_COAP_ERROR | 0x80000)` + * - `#define MG_COAP_NETWORK_ERROR (MG_COAP_ERROR | 0x100000)` + */ +uint32_t mg_coap_send_message(struct mg_connection *nc, + struct mg_coap_message *cm); + +/* + * Composes CoAP acknowledgement from `mg_coap_message` + * and sends it into `nc` connection. + * Return value: see `mg_coap_send_message()` + */ +uint32_t mg_coap_send_ack(struct mg_connection *nc, uint16_t msg_id); + +/* + * Parses CoAP message and fills mg_coap_message and returns cm->flags. + * This is a helper function. + * + * NOTE: usually CoAP works over UDP, so lack of data means format error. + * But, in theory, it is possible to use CoAP over TCP (according to RFC) + * + * The caller has to check results and treat COAP_NOT_ENOUGH_DATA according to + * underlying protocol: + * + * - in case of UDP COAP_NOT_ENOUGH_DATA means COAP_FORMAT_ERROR, + * - in case of TCP client can try to receive more data + * + * Return value: see `mg_coap_send_message()` + */ +uint32_t mg_coap_parse(struct mbuf *io, struct mg_coap_message *cm); + +/* + * Composes CoAP message from mg_coap_message structure. + * This is a helper function. + * Return value: see `mg_coap_send_message()` + */ +uint32_t mg_coap_compose(struct mg_coap_message *cm, struct mbuf *io); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MG_ENABLE_COAP */ + +#endif /* CS_MONGOOSE_SRC_COAP_H_ */ diff --git a/src/mongoose-6.11/src/mg_common.h b/src/mongoose-6.11/src/mg_common.h new file mode 100644 index 0000000..81ecdf9 --- /dev/null +++ b/src/mongoose-6.11/src/mg_common.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2004-2013 Sergey Lyubka + * Copyright (c) 2013-2015 Cesanta Software Limited + * All rights reserved + * + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +#ifndef CS_MONGOOSE_SRC_COMMON_H_ +#define CS_MONGOOSE_SRC_COMMON_H_ + +#define MG_VERSION "6.11" + +/* Local tweaks, applied before any of Mongoose's own headers. */ +#ifdef MG_LOCALS +#include +#endif + +#endif /* CS_MONGOOSE_SRC_COMMON_H_ */ diff --git a/src/mongoose-6.11/src/mg_dns.c b/src/mongoose-6.11/src/mg_dns.c new file mode 100644 index 0000000..21bc8c2 --- /dev/null +++ b/src/mongoose-6.11/src/mg_dns.c @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_DNS + +#include "mg_internal.h" +#include "mg_dns.h" + +static int mg_dns_tid = 0xa0; + +struct mg_dns_header { + uint16_t transaction_id; + uint16_t flags; + uint16_t num_questions; + uint16_t num_answers; + uint16_t num_authority_prs; + uint16_t num_other_prs; +}; + +struct mg_dns_resource_record *mg_dns_next_record( + struct mg_dns_message *msg, int query, + struct mg_dns_resource_record *prev) { + struct mg_dns_resource_record *rr; + + for (rr = (prev == NULL ? msg->answers : prev + 1); + rr - msg->answers < msg->num_answers; rr++) { + if (rr->rtype == query) { + return rr; + } + } + return NULL; +} + +int mg_dns_parse_record_data(struct mg_dns_message *msg, + struct mg_dns_resource_record *rr, void *data, + size_t data_len) { + switch (rr->rtype) { + case MG_DNS_A_RECORD: + if (data_len < sizeof(struct in_addr)) { + return -1; + } + if (rr->rdata.p + data_len > msg->pkt.p + msg->pkt.len) { + return -1; + } + memcpy(data, rr->rdata.p, data_len); + return 0; +#if MG_ENABLE_IPV6 + case MG_DNS_AAAA_RECORD: + if (data_len < sizeof(struct in6_addr)) { + return -1; /* LCOV_EXCL_LINE */ + } + memcpy(data, rr->rdata.p, data_len); + return 0; +#endif + case MG_DNS_CNAME_RECORD: + mg_dns_uncompress_name(msg, &rr->rdata, (char *) data, data_len); + return 0; + } + + return -1; +} + +int mg_dns_insert_header(struct mbuf *io, size_t pos, + struct mg_dns_message *msg) { + struct mg_dns_header header; + + memset(&header, 0, sizeof(header)); + header.transaction_id = msg->transaction_id; + header.flags = htons(msg->flags); + header.num_questions = htons(msg->num_questions); + header.num_answers = htons(msg->num_answers); + + return mbuf_insert(io, pos, &header, sizeof(header)); +} + +int mg_dns_copy_questions(struct mbuf *io, struct mg_dns_message *msg) { + unsigned char *begin, *end; + struct mg_dns_resource_record *last_q; + if (msg->num_questions <= 0) return 0; + begin = (unsigned char *) msg->pkt.p + sizeof(struct mg_dns_header); + last_q = &msg->questions[msg->num_questions - 1]; + end = (unsigned char *) last_q->name.p + last_q->name.len + 4; + return mbuf_append(io, begin, end - begin); +} + +int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len) { + const char *s; + unsigned char n; + size_t pos = io->len; + + do { + if ((s = strchr(name, '.')) == NULL) { + s = name + len; + } + + if (s - name > 127) { + return -1; /* TODO(mkm) cover */ + } + n = s - name; /* chunk length */ + mbuf_append(io, &n, 1); /* send length */ + mbuf_append(io, name, n); + + if (*s == '.') { + n++; + } + + name += n; + len -= n; + } while (*s != '\0'); + mbuf_append(io, "\0", 1); /* Mark end of host name */ + + return io->len - pos; +} + +int mg_dns_encode_record(struct mbuf *io, struct mg_dns_resource_record *rr, + const char *name, size_t nlen, const void *rdata, + size_t rlen) { + size_t pos = io->len; + uint16_t u16; + uint32_t u32; + + if (rr->kind == MG_DNS_INVALID_RECORD) { + return -1; /* LCOV_EXCL_LINE */ + } + + if (mg_dns_encode_name(io, name, nlen) == -1) { + return -1; + } + + u16 = htons(rr->rtype); + mbuf_append(io, &u16, 2); + u16 = htons(rr->rclass); + mbuf_append(io, &u16, 2); + + if (rr->kind == MG_DNS_ANSWER) { + u32 = htonl(rr->ttl); + mbuf_append(io, &u32, 4); + + if (rr->rtype == MG_DNS_CNAME_RECORD) { + int clen; + /* fill size after encoding */ + size_t off = io->len; + mbuf_append(io, &u16, 2); + if ((clen = mg_dns_encode_name(io, (const char *) rdata, rlen)) == -1) { + return -1; + } + u16 = clen; + io->buf[off] = u16 >> 8; + io->buf[off + 1] = u16 & 0xff; + } else { + u16 = htons((uint16_t) rlen); + mbuf_append(io, &u16, 2); + mbuf_append(io, rdata, rlen); + } + } + + return io->len - pos; +} + +void mg_send_dns_query(struct mg_connection *nc, const char *name, + int query_type) { + struct mg_dns_message *msg = + (struct mg_dns_message *) MG_CALLOC(1, sizeof(*msg)); + struct mbuf pkt; + struct mg_dns_resource_record *rr = &msg->questions[0]; + + DBG(("%s %d", name, query_type)); + + mbuf_init(&pkt, 64 /* Start small, it'll grow as needed. */); + + msg->transaction_id = ++mg_dns_tid; + msg->flags = 0x100; + msg->num_questions = 1; + + mg_dns_insert_header(&pkt, 0, msg); + + rr->rtype = query_type; + rr->rclass = 1; /* Class: inet */ + rr->kind = MG_DNS_QUESTION; + + if (mg_dns_encode_record(&pkt, rr, name, strlen(name), NULL, 0) == -1) { + /* TODO(mkm): return an error code */ + goto cleanup; /* LCOV_EXCL_LINE */ + } + + /* TCP DNS requires messages to be prefixed with len */ + if (!(nc->flags & MG_F_UDP)) { + uint16_t len = htons((uint16_t) pkt.len); + mbuf_insert(&pkt, 0, &len, 2); + } + + mg_send(nc, pkt.buf, pkt.len); + mbuf_free(&pkt); + +cleanup: + MG_FREE(msg); +} + +static unsigned char *mg_parse_dns_resource_record( + unsigned char *data, unsigned char *end, struct mg_dns_resource_record *rr, + int reply) { + unsigned char *name = data; + int chunk_len, data_len; + + while (data < end && (chunk_len = *data)) { + if (((unsigned char *) data)[0] & 0xc0) { + data += 1; + break; + } + data += chunk_len + 1; + } + + if (data > end - 5) { + return NULL; + } + + rr->name.p = (char *) name; + rr->name.len = data - name + 1; + data++; + + rr->rtype = data[0] << 8 | data[1]; + data += 2; + + rr->rclass = data[0] << 8 | data[1]; + data += 2; + + rr->kind = reply ? MG_DNS_ANSWER : MG_DNS_QUESTION; + if (reply) { + if (data >= end - 6) { + return NULL; + } + + rr->ttl = (uint32_t) data[0] << 24 | (uint32_t) data[1] << 16 | + data[2] << 8 | data[3]; + data += 4; + + data_len = *data << 8 | *(data + 1); + data += 2; + + rr->rdata.p = (char *) data; + rr->rdata.len = data_len; + data += data_len; + } + return data; +} + +int mg_parse_dns(const char *buf, int len, struct mg_dns_message *msg) { + struct mg_dns_header *header = (struct mg_dns_header *) buf; + unsigned char *data = (unsigned char *) buf + sizeof(*header); + unsigned char *end = (unsigned char *) buf + len; + int i; + + memset(msg, 0, sizeof(*msg)); + msg->pkt.p = buf; + msg->pkt.len = len; + + if (len < (int) sizeof(*header)) return -1; + + msg->transaction_id = header->transaction_id; + msg->flags = ntohs(header->flags); + msg->num_questions = ntohs(header->num_questions); + if (msg->num_questions > (int) ARRAY_SIZE(msg->questions)) { + msg->num_questions = (int) ARRAY_SIZE(msg->questions); + } + msg->num_answers = ntohs(header->num_answers); + if (msg->num_answers > (int) ARRAY_SIZE(msg->answers)) { + msg->num_answers = (int) ARRAY_SIZE(msg->answers); + } + + for (i = 0; i < msg->num_questions; i++) { + data = mg_parse_dns_resource_record(data, end, &msg->questions[i], 0); + if (data == NULL) return -1; + } + + for (i = 0; i < msg->num_answers; i++) { + data = mg_parse_dns_resource_record(data, end, &msg->answers[i], 1); + if (data == NULL) return -1; + } + + return 0; +} + +size_t mg_dns_uncompress_name(struct mg_dns_message *msg, struct mg_str *name, + char *dst, int dst_len) { + int chunk_len, num_ptrs = 0; + char *old_dst = dst; + const unsigned char *data = (unsigned char *) name->p; + const unsigned char *end = (unsigned char *) msg->pkt.p + msg->pkt.len; + + if (data >= end) { + return 0; + } + + while ((chunk_len = *data++)) { + int leeway = dst_len - (dst - old_dst); + if (data >= end) { + return 0; + } + + if ((chunk_len & 0xc0) == 0xc0) { + uint16_t off = (data[-1] & (~0xc0)) << 8 | data[0]; + if (off >= msg->pkt.len) { + return 0; + } + /* Basic circular loop avoidance: allow up to 16 pointer hops. */ + if (++num_ptrs > 15) { + return 0; + } + data = (unsigned char *) msg->pkt.p + off; + continue; + } + if (chunk_len > 63) { + return 0; + } + if (chunk_len > leeway) { + chunk_len = leeway; + } + + if (data + chunk_len >= end) { + return 0; + } + + memcpy(dst, data, chunk_len); + data += chunk_len; + dst += chunk_len; + leeway -= chunk_len; + if (leeway == 0) { + return dst - old_dst; + } + *dst++ = '.'; + } + + if (dst != old_dst) { + *--dst = 0; + } + return dst - old_dst; +} + +static void dns_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct mbuf *io = &nc->recv_mbuf; + struct mg_dns_message msg; + + /* Pass low-level events to the user handler */ + nc->handler(nc, ev, ev_data MG_UD_ARG(user_data)); + + switch (ev) { + case MG_EV_RECV: + if (!(nc->flags & MG_F_UDP)) { + mbuf_remove(&nc->recv_mbuf, 2); + } + if (mg_parse_dns(nc->recv_mbuf.buf, nc->recv_mbuf.len, &msg) == -1) { + /* reply + recursion allowed + format error */ + memset(&msg, 0, sizeof(msg)); + msg.flags = 0x8081; + mg_dns_insert_header(io, 0, &msg); + if (!(nc->flags & MG_F_UDP)) { + uint16_t len = htons((uint16_t) io->len); + mbuf_insert(io, 0, &len, 2); + } + mg_send(nc, io->buf, io->len); + } else { + /* Call user handler with parsed message */ + nc->handler(nc, MG_DNS_MESSAGE, &msg MG_UD_ARG(user_data)); + } + mbuf_remove(io, io->len); + break; + } +} + +void mg_set_protocol_dns(struct mg_connection *nc) { + nc->proto_handler = dns_handler; +} + +#endif /* MG_ENABLE_DNS */ diff --git a/src/mongoose-6.11/src/mg_dns.h b/src/mongoose-6.11/src/mg_dns.h new file mode 100644 index 0000000..8a76f58 --- /dev/null +++ b/src/mongoose-6.11/src/mg_dns.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* + * === DNS API reference + */ + +#ifndef CS_MONGOOSE_SRC_DNS_H_ +#define CS_MONGOOSE_SRC_DNS_H_ + +#include "mg_net.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define MG_DNS_A_RECORD 0x01 /* Lookup IP address */ +#define MG_DNS_CNAME_RECORD 0x05 /* Lookup CNAME */ +#define MG_DNS_PTR_RECORD 0x0c /* Lookup PTR */ +#define MG_DNS_TXT_RECORD 0x10 /* Lookup TXT */ +#define MG_DNS_AAAA_RECORD 0x1c /* Lookup IPv6 address */ +#define MG_DNS_SRV_RECORD 0x21 /* Lookup SRV */ +#define MG_DNS_MX_RECORD 0x0f /* Lookup mail server for domain */ +#define MG_DNS_ANY_RECORD 0xff +#define MG_DNS_NSEC_RECORD 0x2f + +#define MG_MAX_DNS_QUESTIONS 32 +#define MG_MAX_DNS_ANSWERS 32 + +#define MG_DNS_MESSAGE 100 /* High-level DNS message event */ + +enum mg_dns_resource_record_kind { + MG_DNS_INVALID_RECORD = 0, + MG_DNS_QUESTION, + MG_DNS_ANSWER +}; + +/* DNS resource record. */ +struct mg_dns_resource_record { + struct mg_str name; /* buffer with compressed name */ + int rtype; + int rclass; + int ttl; + enum mg_dns_resource_record_kind kind; + struct mg_str rdata; /* protocol data (can be a compressed name) */ +}; + +/* DNS message (request and response). */ +struct mg_dns_message { + struct mg_str pkt; /* packet body */ + uint16_t flags; + uint16_t transaction_id; + int num_questions; + int num_answers; + struct mg_dns_resource_record questions[MG_MAX_DNS_QUESTIONS]; + struct mg_dns_resource_record answers[MG_MAX_DNS_ANSWERS]; +}; + +struct mg_dns_resource_record *mg_dns_next_record( + struct mg_dns_message *msg, int query, struct mg_dns_resource_record *prev); + +/* + * Parses the record data from a DNS resource record. + * + * - A: struct in_addr *ina + * - AAAA: struct in6_addr *ina + * - CNAME: char buffer + * + * Returns -1 on error. + * + * TODO(mkm): MX + */ +int mg_dns_parse_record_data(struct mg_dns_message *msg, + struct mg_dns_resource_record *rr, void *data, + size_t data_len); + +/* + * Sends a DNS query to the remote end. + */ +void mg_send_dns_query(struct mg_connection *nc, const char *name, + int query_type); + +/* + * Inserts a DNS header to an IO buffer. + * + * Returns the number of bytes inserted. + */ +int mg_dns_insert_header(struct mbuf *io, size_t pos, + struct mg_dns_message *msg); + +/* + * Appends already encoded questions from an existing message. + * + * This is useful when generating a DNS reply message which includes + * all question records. + * + * Returns the number of appended bytes. + */ +int mg_dns_copy_questions(struct mbuf *io, struct mg_dns_message *msg); + +/* + * Encodes and appends a DNS resource record to an IO buffer. + * + * The record metadata is taken from the `rr` parameter, while the name and data + * are taken from the parameters, encoded in the appropriate format depending on + * record type and stored in the IO buffer. The encoded values might contain + * offsets within the IO buffer. It's thus important that the IO buffer doesn't + * get trimmed while a sequence of records are encoded while preparing a DNS + * reply. + * + * This function doesn't update the `name` and `rdata` pointers in the `rr` + * struct because they might be invalidated as soon as the IO buffer grows + * again. + * + * Returns the number of bytes appended or -1 in case of error. + */ +int mg_dns_encode_record(struct mbuf *io, struct mg_dns_resource_record *rr, + const char *name, size_t nlen, const void *rdata, + size_t rlen); + +/* + * Encodes a DNS name. + */ +int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len); + +/* Low-level: parses a DNS response. */ +int mg_parse_dns(const char *buf, int len, struct mg_dns_message *msg); + +/* + * Uncompresses a DNS compressed name. + * + * The containing DNS message is required because of the compressed encoding + * and reference suffixes present elsewhere in the packet. + * + * If the name is less than `dst_len` characters long, the remainder + * of `dst` is terminated with `\0` characters. Otherwise, `dst` is not + * terminated. + * + * If `dst_len` is 0 `dst` can be NULL. + * Returns the uncompressed name length. + */ +size_t mg_dns_uncompress_name(struct mg_dns_message *msg, struct mg_str *name, + char *dst, int dst_len); + +/* + * Attaches a built-in DNS event handler to the given listening connection. + * + * The DNS event handler parses the incoming UDP packets, treating them as DNS + * requests. If an incoming packet gets successfully parsed by the DNS event + * handler, a user event handler will receive an `MG_DNS_REQUEST` event, with + * `ev_data` pointing to the parsed `struct mg_dns_message`. + * + * See + * [captive_dns_server](https://github.com/cesanta/mongoose/tree/master/examples/captive_dns_server) + * example on how to handle DNS request and send DNS reply. + */ +void mg_set_protocol_dns(struct mg_connection *nc); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CS_MONGOOSE_SRC_DNS_H_ */ diff --git a/src/mongoose-6.11/src/mg_dns_server.c b/src/mongoose-6.11/src/mg_dns_server.c new file mode 100644 index 0000000..2dafb46 --- /dev/null +++ b/src/mongoose-6.11/src/mg_dns_server.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_DNS_SERVER + +#include "mg_internal.h" +#include "dns-server.h" + +struct mg_dns_reply mg_dns_create_reply(struct mbuf *io, + struct mg_dns_message *msg) { + struct mg_dns_reply rep; + rep.msg = msg; + rep.io = io; + rep.start = io->len; + + /* reply + recursion allowed */ + msg->flags |= 0x8080; + mg_dns_copy_questions(io, msg); + + msg->num_answers = 0; + return rep; +} + +void mg_dns_send_reply(struct mg_connection *nc, struct mg_dns_reply *r) { + size_t sent = r->io->len - r->start; + mg_dns_insert_header(r->io, r->start, r->msg); + if (!(nc->flags & MG_F_UDP)) { + uint16_t len = htons((uint16_t) sent); + mbuf_insert(r->io, r->start, &len, 2); + } + + if (&nc->send_mbuf != r->io) { + mg_send(nc, r->io->buf + r->start, r->io->len - r->start); + r->io->len = r->start; + } +} + +int mg_dns_reply_record(struct mg_dns_reply *reply, + struct mg_dns_resource_record *question, + const char *name, int rtype, int ttl, const void *rdata, + size_t rdata_len) { + struct mg_dns_message *msg = (struct mg_dns_message *) reply->msg; + char rname[512]; + struct mg_dns_resource_record *ans = &msg->answers[msg->num_answers]; + if (msg->num_answers >= MG_MAX_DNS_ANSWERS) { + return -1; /* LCOV_EXCL_LINE */ + } + + if (name == NULL) { + name = rname; + rname[511] = 0; + mg_dns_uncompress_name(msg, &question->name, rname, sizeof(rname) - 1); + } + + *ans = *question; + ans->kind = MG_DNS_ANSWER; + ans->rtype = rtype; + ans->ttl = ttl; + + if (mg_dns_encode_record(reply->io, ans, name, strlen(name), rdata, + rdata_len) == -1) { + return -1; /* LCOV_EXCL_LINE */ + }; + + msg->num_answers++; + return 0; +} + +#endif /* MG_ENABLE_DNS_SERVER */ diff --git a/src/mongoose-6.11/src/mg_dns_server.h b/src/mongoose-6.11/src/mg_dns_server.h new file mode 100644 index 0000000..168a174 --- /dev/null +++ b/src/mongoose-6.11/src/mg_dns_server.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* + * === DNS server API reference + * + * Disabled by default; enable with `-DMG_ENABLE_DNS_SERVER`. + */ + +#ifndef CS_MONGOOSE_SRC_DNS_SERVER_H_ +#define CS_MONGOOSE_SRC_DNS_SERVER_H_ + +#if MG_ENABLE_DNS_SERVER + +#include "mg_dns.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define MG_DNS_SERVER_DEFAULT_TTL 3600 + +struct mg_dns_reply { + struct mg_dns_message *msg; + struct mbuf *io; + size_t start; +}; + +/* + * Creates a DNS reply. + * + * The reply will be based on an existing query message `msg`. + * The query body will be appended to the output buffer. + * "reply + recursion allowed" will be added to the message flags and the + * message's num_answers will be set to 0. + * + * Answer records can be appended with `mg_dns_send_reply` or by lower + * level function defined in the DNS API. + * + * In order to send a reply use `mg_dns_send_reply`. + * It's possible to use a connection's send buffer as reply buffer, + * and it will work for both UDP and TCP connections. + * + * Example: + * + * ```c + * reply = mg_dns_create_reply(&nc->send_mbuf, msg); + * for (i = 0; i < msg->num_questions; i++) { + * rr = &msg->questions[i]; + * if (rr->rtype == MG_DNS_A_RECORD) { + * mg_dns_reply_record(&reply, rr, 3600, &dummy_ip_addr, 4); + * } + * } + * mg_dns_send_reply(nc, &reply); + * ``` + */ +struct mg_dns_reply mg_dns_create_reply(struct mbuf *io, + struct mg_dns_message *msg); + +/* + * Appends a DNS reply record to the IO buffer and to the DNS message. + * + * The message's num_answers field will be incremented. It's the caller's duty + * to ensure num_answers is properly initialised. + * + * Returns -1 on error. + */ +int mg_dns_reply_record(struct mg_dns_reply *reply, + struct mg_dns_resource_record *question, + const char *name, int rtype, int ttl, const void *rdata, + size_t rdata_len); + +/* + * Sends a DNS reply through a connection. + * + * The DNS data is stored in an IO buffer pointed by reply structure in `r`. + * This function mutates the content of that buffer in order to ensure that + * the DNS header reflects the size and flags of the message, that might have + * been updated either with `mg_dns_reply_record` or by direct manipulation of + * `r->message`. + * + * Once sent, the IO buffer will be trimmed unless the reply IO buffer + * is the connection's send buffer and the connection is not in UDP mode. + */ +void mg_dns_send_reply(struct mg_connection *nc, struct mg_dns_reply *r); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MG_ENABLE_DNS_SERVER */ +#endif /* CS_MONGOOSE_SRC_DNS_SERVER_H_ */ diff --git a/src/mongoose-6.11/src/mg_features.h b/src/mongoose-6.11/src/mg_features.h new file mode 100644 index 0000000..515242d --- /dev/null +++ b/src/mongoose-6.11/src/mg_features.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_FEATURES_H_ +#define CS_MONGOOSE_SRC_FEATURES_H_ + +#ifndef MG_DISABLE_HTTP_DIGEST_AUTH +#define MG_DISABLE_HTTP_DIGEST_AUTH 0 +#endif + +#ifndef MG_DISABLE_HTTP_KEEP_ALIVE +#define MG_DISABLE_HTTP_KEEP_ALIVE 0 +#endif + +#ifndef MG_DISABLE_PFS +#define MG_DISABLE_PFS 0 +#endif + +#ifndef MG_DISABLE_WS_RANDOM_MASK +#define MG_DISABLE_WS_RANDOM_MASK 0 +#endif + +#ifndef MG_ENABLE_ASYNC_RESOLVER +#define MG_ENABLE_ASYNC_RESOLVER 1 +#endif + +#ifndef MG_ENABLE_BROADCAST +#define MG_ENABLE_BROADCAST 0 +#endif + +#ifndef MG_ENABLE_COAP +#define MG_ENABLE_COAP 0 +#endif + +#ifndef MG_ENABLE_DEBUG +#define MG_ENABLE_DEBUG 0 +#endif + +#ifndef MG_ENABLE_DIRECTORY_LISTING +#define MG_ENABLE_DIRECTORY_LISTING 0 +#endif + +#ifndef MG_ENABLE_DNS +#define MG_ENABLE_DNS 1 +#endif + +#ifndef MG_ENABLE_DNS_SERVER +#define MG_ENABLE_DNS_SERVER 0 +#endif + +#ifndef MG_ENABLE_FAKE_DAVLOCK +#define MG_ENABLE_FAKE_DAVLOCK 0 +#endif + +#ifndef MG_ENABLE_FILESYSTEM +#define MG_ENABLE_FILESYSTEM 0 +#endif + +#ifndef MG_ENABLE_GETADDRINFO +#define MG_ENABLE_GETADDRINFO 0 +#endif + +#ifndef MG_ENABLE_HEXDUMP +#define MG_ENABLE_HEXDUMP CS_ENABLE_STDIO +#endif + +#ifndef MG_ENABLE_HTTP +#define MG_ENABLE_HTTP 1 +#endif + +#ifndef MG_ENABLE_HTTP_CGI +#define MG_ENABLE_HTTP_CGI 0 +#endif + +#ifndef MG_ENABLE_HTTP_SSI +#define MG_ENABLE_HTTP_SSI MG_ENABLE_FILESYSTEM +#endif + +#ifndef MG_ENABLE_HTTP_SSI_EXEC +#define MG_ENABLE_HTTP_SSI_EXEC 0 +#endif + +#ifndef MG_ENABLE_HTTP_STREAMING_MULTIPART +#define MG_ENABLE_HTTP_STREAMING_MULTIPART 0 +#endif + +#ifndef MG_ENABLE_HTTP_WEBDAV +#define MG_ENABLE_HTTP_WEBDAV 0 +#endif + +#ifndef MG_ENABLE_HTTP_WEBSOCKET +#define MG_ENABLE_HTTP_WEBSOCKET MG_ENABLE_HTTP +#endif + +#ifndef MG_ENABLE_IPV6 +#define MG_ENABLE_IPV6 0 +#endif + +#ifndef MG_ENABLE_MQTT +#define MG_ENABLE_MQTT 1 +#endif + +#ifndef MG_ENABLE_SOCKS +#define MG_ENABLE_SOCKS 0 +#endif + +#ifndef MG_ENABLE_MQTT_BROKER +#define MG_ENABLE_MQTT_BROKER 0 +#endif + +#ifndef MG_ENABLE_SSL +#define MG_ENABLE_SSL 0 +#endif + +#ifndef MG_ENABLE_SYNC_RESOLVER +#define MG_ENABLE_SYNC_RESOLVER 0 +#endif + +#ifndef MG_ENABLE_STDIO +#define MG_ENABLE_STDIO CS_ENABLE_STDIO +#endif + +#ifndef MG_NET_IF +#define MG_NET_IF MG_NET_IF_SOCKET +#endif + +#ifndef MG_SSL_IF +#define MG_SSL_IF MG_SSL_IF_OPENSSL +#endif + +#ifndef MG_ENABLE_THREADS /* ifdef-ok */ +#ifdef _WIN32 +#define MG_ENABLE_THREADS 1 +#else +#define MG_ENABLE_THREADS 0 +#endif +#endif + +#if MG_ENABLE_DEBUG && !defined(CS_ENABLE_DEBUG) +#define CS_ENABLE_DEBUG 1 +#endif + +/* MQTT broker requires MQTT */ +#if MG_ENABLE_MQTT_BROKER && !MG_ENABLE_MQTT +#undef MG_ENABLE_MQTT +#define MG_ENABLE_MQTT 1 +#endif + +#ifndef MG_ENABLE_HTTP_URL_REWRITES +#define MG_ENABLE_HTTP_URL_REWRITES \ + (CS_PLATFORM == CS_P_WINDOWS || CS_PLATFORM == CS_P_UNIX) +#endif + +#ifndef MG_ENABLE_SNTP +#define MG_ENABLE_SNTP 0 +#endif + +#ifndef MG_ENABLE_EXTRA_ERRORS_DESC +#define MG_ENABLE_EXTRA_ERRORS_DESC 0 +#endif + +#ifndef MG_ENABLE_CALLBACK_USERDATA +#define MG_ENABLE_CALLBACK_USERDATA 0 +#endif + +#if MG_ENABLE_CALLBACK_USERDATA +#define MG_UD_ARG(ud) , ud +#define MG_CB(cb, ud) cb, ud +#else +#define MG_UD_ARG(ud) +#define MG_CB(cb, ud) cb +#endif + +#endif /* CS_MONGOOSE_SRC_FEATURES_H_ */ diff --git a/src/mongoose-6.11/src/mg_http.c b/src/mongoose-6.11/src/mg_http.c new file mode 100644 index 0000000..068d208 --- /dev/null +++ b/src/mongoose-6.11/src/mg_http.c @@ -0,0 +1,3077 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_HTTP + +#include "common/cs_md5.h" +#include "mg_internal.h" +#include "mg_util.h" + +/* altbuf {{{ */ + +/* + * Alternate buffer: fills the client-provided buffer with data; and if it's + * not large enough, allocates another buffer (via mbuf), similar to asprintf. + */ +struct altbuf { + struct mbuf m; + char *user_buf; + size_t len; + size_t user_buf_size; +}; + +/* + * Initializes altbuf; `buf`, `buf_size` is the client-provided buffer. + */ +MG_INTERNAL void altbuf_init(struct altbuf *ab, char *buf, size_t buf_size) { + mbuf_init(&ab->m, 0); + ab->user_buf = buf; + ab->user_buf_size = buf_size; + ab->len = 0; +} + +/* + * Appends a single char to the altbuf. + */ +MG_INTERNAL void altbuf_append(struct altbuf *ab, char c) { + if (ab->len < ab->user_buf_size) { + /* The data fits into the original buffer */ + ab->user_buf[ab->len++] = c; + } else { + /* The data can't fit into the original buffer, so write it to mbuf. */ + + /* + * First of all, see if that's the first byte which overflows the original + * buffer: if so, copy the existing data from there to a newly allocated + * mbuf. + */ + if (ab->len > 0 && ab->m.len == 0) { + mbuf_append(&ab->m, ab->user_buf, ab->len); + } + + mbuf_append(&ab->m, &c, 1); + ab->len = ab->m.len; + } +} + +/* + * Resets any data previously appended to altbuf. + */ +MG_INTERNAL void altbuf_reset(struct altbuf *ab) { + mbuf_free(&ab->m); + ab->len = 0; +} + +/* + * Returns whether the additional buffer was allocated (and thus the data + * is in the mbuf, not the client-provided buffer) + */ +MG_INTERNAL int altbuf_reallocated(struct altbuf *ab) { + return ab->len > ab->user_buf_size; +} + +/* + * Returns the actual buffer with data, either the client-provided or a newly + * allocated one. If `trim` is non-zero, mbuf-backed buffer is trimmed first. + */ +MG_INTERNAL char *altbuf_get_buf(struct altbuf *ab, int trim) { + if (altbuf_reallocated(ab)) { + if (trim) { + mbuf_trim(&ab->m); + } + return ab->m.buf; + } else { + return ab->user_buf; + } +} + +/* }}} */ + +static const char *mg_version_header = "Mongoose/" MG_VERSION; + +enum mg_http_proto_data_type { DATA_NONE, DATA_FILE, DATA_PUT }; + +struct mg_http_proto_data_file { + FILE *fp; /* Opened file. */ + int64_t cl; /* Content-Length. How many bytes to send. */ + int64_t sent; /* How many bytes have been already sent. */ + int keepalive; /* Keep connection open after sending. */ + enum mg_http_proto_data_type type; +}; + +#if MG_ENABLE_HTTP_CGI +struct mg_http_proto_data_cgi { + struct mg_connection *cgi_nc; +}; +#endif + +struct mg_http_proto_data_chuncked { + int64_t body_len; /* How many bytes of chunked body was reassembled. */ +}; + +struct mg_http_endpoint { + struct mg_http_endpoint *next; + struct mg_str uri_pattern; /* owned */ + char *auth_domain; /* owned */ + char *auth_file; /* owned */ + + mg_event_handler_t handler; +#if MG_ENABLE_CALLBACK_USERDATA + void *user_data; +#endif +}; + +enum mg_http_multipart_stream_state { + MPS_BEGIN, + MPS_WAITING_FOR_BOUNDARY, + MPS_WAITING_FOR_CHUNK, + MPS_GOT_CHUNK, + MPS_GOT_BOUNDARY, + MPS_FINALIZE, + MPS_FINISHED +}; + +struct mg_http_multipart_stream { + const char *boundary; + int boundary_len; + const char *var_name; + const char *file_name; + void *user_data; + int prev_io_len; + enum mg_http_multipart_stream_state state; + int processing_part; +}; + +struct mg_reverse_proxy_data { + struct mg_connection *linked_conn; +}; + +struct mg_ws_proto_data { + /* + * Defragmented size of the frame so far. + * + * First byte of nc->recv_mbuf.buf is an op, the rest of the data is + * defragmented data. + */ + size_t reass_len; +}; + +struct mg_http_proto_data { +#if MG_ENABLE_FILESYSTEM + struct mg_http_proto_data_file file; +#endif +#if MG_ENABLE_HTTP_CGI + struct mg_http_proto_data_cgi cgi; +#endif +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + struct mg_http_multipart_stream mp_stream; +#endif +#if MG_ENABLE_HTTP_WEBSOCKET + struct mg_ws_proto_data ws_data; +#endif + struct mg_http_proto_data_chuncked chunk; + struct mg_http_endpoint *endpoints; + mg_event_handler_t endpoint_handler; + struct mg_reverse_proxy_data reverse_proxy_data; + size_t rcvd; /* How many bytes we have received. */ +}; + +static void mg_http_conn_destructor(void *proto_data); +struct mg_connection *mg_connect_http_base( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + struct mg_connect_opts opts, const char *scheme1, const char *scheme2, + const char *scheme_ssl1, const char *scheme_ssl2, const char *url, + struct mg_str *path, struct mg_str *user_info, struct mg_str *host); + +static struct mg_http_proto_data *mg_http_get_proto_data( + struct mg_connection *c) { + if (c->proto_data == NULL) { + c->proto_data = MG_CALLOC(1, sizeof(struct mg_http_proto_data)); + c->proto_data_destructor = mg_http_conn_destructor; + } + + return (struct mg_http_proto_data *) c->proto_data; +} + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART +static void mg_http_free_proto_data_mp_stream( + struct mg_http_multipart_stream *mp) { + MG_FREE((void *) mp->boundary); + MG_FREE((void *) mp->var_name); + MG_FREE((void *) mp->file_name); + memset(mp, 0, sizeof(*mp)); +} +#endif + +#if MG_ENABLE_FILESYSTEM +static void mg_http_free_proto_data_file(struct mg_http_proto_data_file *d) { + if (d != NULL) { + if (d->fp != NULL) { + fclose(d->fp); + } + memset(d, 0, sizeof(struct mg_http_proto_data_file)); + } +} +#endif + +static void mg_http_free_proto_data_endpoints(struct mg_http_endpoint **ep) { + struct mg_http_endpoint *current = *ep; + + while (current != NULL) { + struct mg_http_endpoint *tmp = current->next; + MG_FREE((void *) current->uri_pattern.p); + MG_FREE((void *) current->auth_domain); + MG_FREE((void *) current->auth_file); + MG_FREE(current); + current = tmp; + } + + ep = NULL; +} + +static void mg_http_free_reverse_proxy_data(struct mg_reverse_proxy_data *rpd) { + if (rpd->linked_conn != NULL) { + /* + * Connection has linked one, we have to unlink & close it + * since _this_ connection is going to die and + * it doesn't make sense to keep another one + */ + struct mg_http_proto_data *pd = mg_http_get_proto_data(rpd->linked_conn); + if (pd->reverse_proxy_data.linked_conn != NULL) { + pd->reverse_proxy_data.linked_conn->flags |= MG_F_SEND_AND_CLOSE; + pd->reverse_proxy_data.linked_conn = NULL; + } + rpd->linked_conn = NULL; + } +} + +static void mg_http_conn_destructor(void *proto_data) { + struct mg_http_proto_data *pd = (struct mg_http_proto_data *) proto_data; +#if MG_ENABLE_FILESYSTEM + mg_http_free_proto_data_file(&pd->file); +#endif +#if MG_ENABLE_HTTP_CGI + mg_http_free_proto_data_cgi(&pd->cgi); +#endif +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + mg_http_free_proto_data_mp_stream(&pd->mp_stream); +#endif + mg_http_free_proto_data_endpoints(&pd->endpoints); + mg_http_free_reverse_proxy_data(&pd->reverse_proxy_data); + MG_FREE(proto_data); +} + +#if MG_ENABLE_FILESYSTEM + +#define MIME_ENTRY(_ext, _type) \ + { _ext, sizeof(_ext) - 1, _type } +static const struct { + const char *extension; + size_t ext_len; + const char *mime_type; +} mg_static_builtin_mime_types[] = { + MIME_ENTRY("html", "text/html"), + MIME_ENTRY("html", "text/html"), + MIME_ENTRY("htm", "text/html"), + MIME_ENTRY("shtm", "text/html"), + MIME_ENTRY("shtml", "text/html"), + MIME_ENTRY("css", "text/css"), + MIME_ENTRY("js", "application/x-javascript"), + MIME_ENTRY("ico", "image/x-icon"), + MIME_ENTRY("gif", "image/gif"), + MIME_ENTRY("jpg", "image/jpeg"), + MIME_ENTRY("jpeg", "image/jpeg"), + MIME_ENTRY("png", "image/png"), + MIME_ENTRY("svg", "image/svg+xml"), + MIME_ENTRY("txt", "text/plain"), + MIME_ENTRY("torrent", "application/x-bittorrent"), + MIME_ENTRY("wav", "audio/x-wav"), + MIME_ENTRY("mp3", "audio/x-mp3"), + MIME_ENTRY("mid", "audio/mid"), + MIME_ENTRY("m3u", "audio/x-mpegurl"), + MIME_ENTRY("ogg", "application/ogg"), + MIME_ENTRY("ram", "audio/x-pn-realaudio"), + MIME_ENTRY("xml", "text/xml"), + MIME_ENTRY("ttf", "application/x-font-ttf"), + MIME_ENTRY("json", "application/json"), + MIME_ENTRY("xslt", "application/xml"), + MIME_ENTRY("xsl", "application/xml"), + MIME_ENTRY("ra", "audio/x-pn-realaudio"), + MIME_ENTRY("doc", "application/msword"), + MIME_ENTRY("exe", "application/octet-stream"), + MIME_ENTRY("zip", "application/x-zip-compressed"), + MIME_ENTRY("xls", "application/excel"), + MIME_ENTRY("tgz", "application/x-tar-gz"), + MIME_ENTRY("tar", "application/x-tar"), + MIME_ENTRY("gz", "application/x-gunzip"), + MIME_ENTRY("arj", "application/x-arj-compressed"), + MIME_ENTRY("rar", "application/x-rar-compressed"), + MIME_ENTRY("rtf", "application/rtf"), + MIME_ENTRY("pdf", "application/pdf"), + MIME_ENTRY("swf", "application/x-shockwave-flash"), + MIME_ENTRY("mpg", "video/mpeg"), + MIME_ENTRY("webm", "video/webm"), + MIME_ENTRY("mpeg", "video/mpeg"), + MIME_ENTRY("mov", "video/quicktime"), + MIME_ENTRY("mp4", "video/mp4"), + MIME_ENTRY("m4v", "video/x-m4v"), + MIME_ENTRY("asf", "video/x-ms-asf"), + MIME_ENTRY("avi", "video/x-msvideo"), + MIME_ENTRY("bmp", "image/bmp"), + {NULL, 0, NULL}}; + +static struct mg_str mg_get_mime_type(const char *path, const char *dflt, + const struct mg_serve_http_opts *opts) { + const char *ext, *overrides; + size_t i, path_len; + struct mg_str r, k, v; + + path_len = strlen(path); + + overrides = opts->custom_mime_types; + while ((overrides = mg_next_comma_list_entry(overrides, &k, &v)) != NULL) { + ext = path + (path_len - k.len); + if (path_len > k.len && mg_vcasecmp(&k, ext) == 0) { + return v; + } + } + + for (i = 0; mg_static_builtin_mime_types[i].extension != NULL; i++) { + ext = path + (path_len - mg_static_builtin_mime_types[i].ext_len); + if (path_len > mg_static_builtin_mime_types[i].ext_len && ext[-1] == '.' && + mg_casecmp(ext, mg_static_builtin_mime_types[i].extension) == 0) { + r.p = mg_static_builtin_mime_types[i].mime_type; + r.len = strlen(r.p); + return r; + } + } + + r.p = dflt; + r.len = strlen(r.p); + return r; +} +#endif + +/* + * Check whether full request is buffered. Return: + * -1 if request is malformed + * 0 if request is not yet fully buffered + * >0 actual request length, including last \r\n\r\n + */ +static int mg_http_get_request_len(const char *s, int buf_len) { + const unsigned char *buf = (unsigned char *) s; + int i; + + for (i = 0; i < buf_len; i++) { + if (!isprint(buf[i]) && buf[i] != '\r' && buf[i] != '\n' && buf[i] < 128) { + return -1; + } else if (buf[i] == '\n' && i + 1 < buf_len && buf[i + 1] == '\n') { + return i + 2; + } else if (buf[i] == '\n' && i + 2 < buf_len && buf[i + 1] == '\r' && + buf[i + 2] == '\n') { + return i + 3; + } + } + + return 0; +} + +static const char *mg_http_parse_headers(const char *s, const char *end, + int len, struct http_message *req) { + int i = 0; + while (i < (int) ARRAY_SIZE(req->header_names) - 1) { + struct mg_str *k = &req->header_names[i], *v = &req->header_values[i]; + + s = mg_skip(s, end, ": ", k); + s = mg_skip(s, end, "\r\n", v); + + while (v->len > 0 && v->p[v->len - 1] == ' ') { + v->len--; /* Trim trailing spaces in header value */ + } + + /* + * If header value is empty - skip it and go to next (if any). + * NOTE: Do not add it to headers_values because such addition changes API + * behaviour + */ + if (k->len != 0 && v->len == 0) { + continue; + } + + if (k->len == 0 || v->len == 0) { + k->p = v->p = NULL; + k->len = v->len = 0; + break; + } + + if (!mg_ncasecmp(k->p, "Content-Length", 14)) { + req->body.len = (size_t) to64(v->p); + req->message.len = len + req->body.len; + } + + i++; + } + + return s; +} + +int mg_parse_http(const char *s, int n, struct http_message *hm, int is_req) { + const char *end, *qs; + int len = mg_http_get_request_len(s, n); + + if (len <= 0) return len; + + memset(hm, 0, sizeof(*hm)); + hm->message.p = s; + hm->body.p = s + len; + hm->message.len = hm->body.len = (size_t) ~0; + end = s + len; + + /* Request is fully buffered. Skip leading whitespaces. */ + while (s < end && isspace(*(unsigned char *) s)) s++; + + if (is_req) { + /* Parse request line: method, URI, proto */ + s = mg_skip(s, end, " ", &hm->method); + s = mg_skip(s, end, " ", &hm->uri); + s = mg_skip(s, end, "\r\n", &hm->proto); + if (hm->uri.p <= hm->method.p || hm->proto.p <= hm->uri.p) return -1; + + /* If URI contains '?' character, initialize query_string */ + if ((qs = (char *) memchr(hm->uri.p, '?', hm->uri.len)) != NULL) { + hm->query_string.p = qs + 1; + hm->query_string.len = &hm->uri.p[hm->uri.len] - (qs + 1); + hm->uri.len = qs - hm->uri.p; + } + } else { + s = mg_skip(s, end, " ", &hm->proto); + if (end - s < 4 || s[3] != ' ') return -1; + hm->resp_code = atoi(s); + if (hm->resp_code < 100 || hm->resp_code >= 600) return -1; + s += 4; + s = mg_skip(s, end, "\r\n", &hm->resp_status_msg); + } + + s = mg_http_parse_headers(s, end, len, hm); + + /* + * mg_parse_http() is used to parse both HTTP requests and HTTP + * responses. If HTTP response does not have Content-Length set, then + * body is read until socket is closed, i.e. body.len is infinite (~0). + * + * For HTTP requests though, according to + * http://tools.ietf.org/html/rfc7231#section-8.1.3, + * only POST and PUT methods have defined body semantics. + * Therefore, if Content-Length is not specified and methods are + * not one of PUT or POST, set body length to 0. + * + * So, + * if it is HTTP request, and Content-Length is not set, + * and method is not (PUT or POST) then reset body length to zero. + */ + if (hm->body.len == (size_t) ~0 && is_req && + mg_vcasecmp(&hm->method, "PUT") != 0 && + mg_vcasecmp(&hm->method, "POST") != 0) { + hm->body.len = 0; + hm->message.len = len; + } + + return len; +} + +struct mg_str *mg_get_http_header(struct http_message *hm, const char *name) { + size_t i, len = strlen(name); + + for (i = 0; hm->header_names[i].len > 0; i++) { + struct mg_str *h = &hm->header_names[i], *v = &hm->header_values[i]; + if (h->p != NULL && h->len == len && !mg_ncasecmp(h->p, name, len)) + return v; + } + + return NULL; +} + +#if MG_ENABLE_FILESYSTEM +static void mg_http_transfer_file_data(struct mg_connection *nc) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + char buf[MG_MAX_HTTP_SEND_MBUF]; + size_t n = 0, to_read = 0, left = (size_t)(pd->file.cl - pd->file.sent); + + if (pd->file.type == DATA_FILE) { + struct mbuf *io = &nc->send_mbuf; + if (io->len >= MG_MAX_HTTP_SEND_MBUF) { + to_read = 0; + } else { + to_read = MG_MAX_HTTP_SEND_MBUF - io->len; + } + if (to_read > left) { + to_read = left; + } + if (to_read > 0) { + n = mg_fread(buf, 1, to_read, pd->file.fp); + if (n > 0) { + mg_send(nc, buf, n); + pd->file.sent += n; + DBG(("%p sent %d (total %d)", nc, (int) n, (int) pd->file.sent)); + } + } else { + /* Rate-limited */ + } + if (pd->file.sent >= pd->file.cl) { + LOG(LL_DEBUG, ("%p done, %d bytes", nc, (int) pd->file.sent)); + if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE; + mg_http_free_proto_data_file(&pd->file); + } + } else if (pd->file.type == DATA_PUT) { + struct mbuf *io = &nc->recv_mbuf; + size_t to_write = left <= 0 ? 0 : left < io->len ? (size_t) left : io->len; + size_t n = mg_fwrite(io->buf, 1, to_write, pd->file.fp); + if (n > 0) { + mbuf_remove(io, n); + pd->file.sent += n; + } + if (n == 0 || pd->file.sent >= pd->file.cl) { + if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE; + mg_http_free_proto_data_file(&pd->file); + } + } +#if MG_ENABLE_HTTP_CGI + else if (pd->cgi.cgi_nc != NULL) { + /* This is POST data that needs to be forwarded to the CGI process */ + if (pd->cgi.cgi_nc != NULL) { + mg_forward(nc, pd->cgi.cgi_nc); + } else { + nc->flags |= MG_F_SEND_AND_CLOSE; + } + } +#endif +} +#endif /* MG_ENABLE_FILESYSTEM */ + +/* + * Parse chunked-encoded buffer. Return 0 if the buffer is not encoded, or + * if it's incomplete. If the chunk is fully buffered, return total number of + * bytes in a chunk, and store data in `data`, `data_len`. + */ +static size_t mg_http_parse_chunk(char *buf, size_t len, char **chunk_data, + size_t *chunk_len) { + unsigned char *s = (unsigned char *) buf; + size_t n = 0; /* scanned chunk length */ + size_t i = 0; /* index in s */ + + /* Scan chunk length. That should be a hexadecimal number. */ + while (i < len && isxdigit(s[i])) { + n *= 16; + n += (s[i] >= '0' && s[i] <= '9') ? s[i] - '0' : tolower(s[i]) - 'a' + 10; + i++; + } + + /* Skip new line */ + if (i == 0 || i + 2 > len || s[i] != '\r' || s[i + 1] != '\n') { + return 0; + } + i += 2; + + /* Record where the data is */ + *chunk_data = (char *) s + i; + *chunk_len = n; + + /* Skip data */ + i += n; + + /* Skip new line */ + if (i == 0 || i + 2 > len || s[i] != '\r' || s[i + 1] != '\n') { + return 0; + } + return i + 2; +} + +MG_INTERNAL size_t mg_handle_chunked(struct mg_connection *nc, + struct http_message *hm, char *buf, + size_t blen) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + char *data; + size_t i, n, data_len, body_len, zero_chunk_received = 0; + /* Find out piece of received data that is not yet reassembled */ + body_len = (size_t) pd->chunk.body_len; + assert(blen >= body_len); + + /* Traverse all fully buffered chunks */ + for (i = body_len; + (n = mg_http_parse_chunk(buf + i, blen - i, &data, &data_len)) > 0; + i += n) { + /* Collapse chunk data to the rest of HTTP body */ + memmove(buf + body_len, data, data_len); + body_len += data_len; + hm->body.len = body_len; + + if (data_len == 0) { + zero_chunk_received = 1; + i += n; + break; + } + } + + if (i > body_len) { + /* Shift unparsed content to the parsed body */ + assert(i <= blen); + memmove(buf + body_len, buf + i, blen - i); + memset(buf + body_len + blen - i, 0, i - body_len); + nc->recv_mbuf.len -= i - body_len; + pd->chunk.body_len = body_len; + + /* Send MG_EV_HTTP_CHUNK event */ + nc->flags &= ~MG_F_DELETE_CHUNK; + mg_call(nc, nc->handler, nc->user_data, MG_EV_HTTP_CHUNK, hm); + + /* Delete processed data if user set MG_F_DELETE_CHUNK flag */ + if (nc->flags & MG_F_DELETE_CHUNK) { + memset(buf, 0, body_len); + memmove(buf, buf + body_len, blen - i); + nc->recv_mbuf.len -= body_len; + hm->body.len = 0; + pd->chunk.body_len = 0; + } + + if (zero_chunk_received) { + /* Total message size is len(body) + len(headers) */ + hm->message.len = + (size_t) pd->chunk.body_len + blen - i + (hm->body.p - hm->message.p); + } + } + + return body_len; +} + +struct mg_http_endpoint *mg_http_get_endpoint_handler(struct mg_connection *nc, + struct mg_str *uri_path) { + struct mg_http_proto_data *pd; + struct mg_http_endpoint *ret = NULL; + int matched, matched_max = 0; + struct mg_http_endpoint *ep; + + if (nc == NULL) { + return NULL; + } + + pd = mg_http_get_proto_data(nc); + + ep = pd->endpoints; + while (ep != NULL) { + if ((matched = mg_match_prefix_n(ep->uri_pattern, *uri_path)) > 0) { + if (matched > matched_max) { + /* Looking for the longest suitable handler */ + ret = ep; + matched_max = matched; + } + } + + ep = ep->next; + } + + return ret; +} + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART +static void mg_http_multipart_continue(struct mg_connection *nc); + +static void mg_http_multipart_begin(struct mg_connection *nc, + struct http_message *hm, int req_len); + +#endif + +static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev, + struct http_message *hm); + +static void deliver_chunk(struct mg_connection *c, struct http_message *hm, + int req_len) { + /* Incomplete message received. Send MG_EV_HTTP_CHUNK event */ + hm->body.len = c->recv_mbuf.len - req_len; + c->flags &= ~MG_F_DELETE_CHUNK; + mg_call(c, c->handler, c->user_data, MG_EV_HTTP_CHUNK, hm); + /* Delete processed data if user set MG_F_DELETE_CHUNK flag */ + if (c->flags & MG_F_DELETE_CHUNK) c->recv_mbuf.len = req_len; +} + +/* + * lx106 compiler has a bug (TODO(mkm) report and insert tracking bug here) + * If a big structure is declared in a big function, lx106 gcc will make it + * even bigger (round up to 4k, from 700 bytes of actual size). + */ +#ifdef __xtensa__ +static void mg_http_handler2(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data), + struct http_message *hm) __attribute__((noinline)); + +void mg_http_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct http_message hm; + mg_http_handler2(nc, ev, ev_data MG_UD_ARG(user_data), &hm); +} + +static void mg_http_handler2(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data), + struct http_message *hm) { +#else /* !__XTENSA__ */ +void mg_http_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct http_message shm, *hm = &shm; +#endif /* __XTENSA__ */ + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + struct mbuf *io = &nc->recv_mbuf; + int req_len; + const int is_req = (nc->listener != NULL); +#if MG_ENABLE_HTTP_WEBSOCKET + struct mg_str *vec; +#endif + if (ev == MG_EV_CLOSE) { +#if MG_ENABLE_HTTP_CGI + /* Close associated CGI forwarder connection */ + if (pd->cgi.cgi_nc != NULL) { + pd->cgi.cgi_nc->user_data = NULL; + pd->cgi.cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } +#endif +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + if (pd->mp_stream.boundary != NULL) { + /* + * Multipart message is in progress, but connection is closed. + * Finish part and request with an error flag. + */ + struct mg_http_multipart_part mp; + memset(&mp, 0, sizeof(mp)); + mp.status = -1; + mp.var_name = pd->mp_stream.var_name; + mp.file_name = pd->mp_stream.file_name; + mg_call(nc, (pd->endpoint_handler ? pd->endpoint_handler : nc->handler), + nc->user_data, MG_EV_HTTP_PART_END, &mp); + mp.var_name = NULL; + mp.file_name = NULL; + mg_call(nc, (pd->endpoint_handler ? pd->endpoint_handler : nc->handler), + nc->user_data, MG_EV_HTTP_MULTIPART_REQUEST_END, &mp); + } else +#endif + if (io->len > 0 && + (req_len = mg_parse_http(io->buf, io->len, hm, is_req)) > 0) { + /* + * For HTTP messages without Content-Length, always send HTTP message + * before MG_EV_CLOSE message. + */ + int ev2 = is_req ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY; + hm->message.len = io->len; + hm->body.len = io->buf + io->len - hm->body.p; + deliver_chunk(nc, hm, req_len); + mg_http_call_endpoint_handler(nc, ev2, hm); + } + pd->rcvd = 0; + } + +#if MG_ENABLE_FILESYSTEM + if (pd->file.fp != NULL) { + mg_http_transfer_file_data(nc); + } +#endif + + mg_call(nc, nc->handler, nc->user_data, ev, ev_data); + + if (ev == MG_EV_RECV) { + struct mg_str *s; + pd->rcvd += *(int *) ev_data; + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + if (pd->mp_stream.boundary != NULL) { + mg_http_multipart_continue(nc); + return; + } +#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ + + req_len = mg_parse_http(io->buf, io->len, hm, is_req); + + if (req_len > 0 && + (s = mg_get_http_header(hm, "Transfer-Encoding")) != NULL && + mg_vcasecmp(s, "chunked") == 0) { + mg_handle_chunked(nc, hm, io->buf + req_len, io->len - req_len); + } + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + if (req_len > 0 && (s = mg_get_http_header(hm, "Content-Type")) != NULL && + s->len >= 9 && strncmp(s->p, "multipart", 9) == 0) { + mg_http_multipart_begin(nc, hm, req_len); + mg_http_multipart_continue(nc); + return; + } +#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ + + /* TODO(alashkin): refactor this ifelseifelseifelseifelse */ + if ((req_len < 0 || + (req_len == 0 && io->len >= MG_MAX_HTTP_REQUEST_SIZE))) { + DBG(("invalid request")); + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } else if (req_len == 0) { + /* Do nothing, request is not yet fully buffered */ + } +#if MG_ENABLE_HTTP_WEBSOCKET + else if (nc->listener == NULL && + mg_get_http_header(hm, "Sec-WebSocket-Accept")) { + /* We're websocket client, got handshake response from server. */ + /* TODO(lsm): check the validity of accept Sec-WebSocket-Accept */ + mbuf_remove(io, req_len); + nc->proto_handler = mg_ws_handler; + nc->flags |= MG_F_IS_WEBSOCKET; + mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE, + NULL); + mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data)); + } else if (nc->listener != NULL && + (vec = mg_get_http_header(hm, "Sec-WebSocket-Key")) != NULL) { + struct mg_http_endpoint *ep; + + /* This is a websocket request. Switch protocol handlers. */ + mbuf_remove(io, req_len); + nc->proto_handler = mg_ws_handler; + nc->flags |= MG_F_IS_WEBSOCKET; + + /* + * If we have a handler set up with mg_register_http_endpoint(), + * deliver subsequent websocket events to this handler after the + * protocol switch. + */ + ep = mg_http_get_endpoint_handler(nc->listener, &hm->uri); + if (ep != NULL) { + nc->handler = ep->handler; +#if MG_ENABLE_CALLBACK_USERDATA + nc->user_data = ep->user_data; +#endif + } + + /* Send handshake */ + mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_REQUEST, + hm); + if (!(nc->flags & (MG_F_CLOSE_IMMEDIATELY | MG_F_SEND_AND_CLOSE))) { + if (nc->send_mbuf.len == 0) { + mg_ws_handshake(nc, vec, hm); + } + mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE, + NULL); + mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data)); + } + } +#endif /* MG_ENABLE_HTTP_WEBSOCKET */ + else if (hm->message.len > pd->rcvd) { + /* Not yet received all HTTP body, deliver MG_EV_HTTP_CHUNK */ + deliver_chunk(nc, hm, req_len); + if (nc->recv_mbuf_limit > 0 && nc->recv_mbuf.len >= nc->recv_mbuf_limit) { + LOG(LL_ERROR, ("%p recv buffer (%lu bytes) exceeds the limit " + "%lu bytes, and not drained, closing", + nc, (unsigned long) nc->recv_mbuf.len, + (unsigned long) nc->recv_mbuf_limit)); + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + } else { + /* We did receive all HTTP body. */ + int trigger_ev = nc->listener ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY; + char addr[32]; + mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr), + MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT); + DBG(("%p %s %.*s %.*s", nc, addr, (int) hm->method.len, hm->method.p, + (int) hm->uri.len, hm->uri.p)); + deliver_chunk(nc, hm, req_len); + /* Whole HTTP message is fully buffered, call event handler */ + mg_http_call_endpoint_handler(nc, trigger_ev, hm); + mbuf_remove(io, hm->message.len); + pd->rcvd = 0; + } + } +} + +static size_t mg_get_line_len(const char *buf, size_t buf_len) { + size_t len = 0; + while (len < buf_len && buf[len] != '\n') len++; + return len == buf_len ? 0 : len + 1; +} + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART +static void mg_http_multipart_begin(struct mg_connection *nc, + struct http_message *hm, int req_len) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + struct mg_str *ct; + struct mbuf *io = &nc->recv_mbuf; + + char boundary_buf[100]; + char *boundary = boundary_buf; + int boundary_len; + + ct = mg_get_http_header(hm, "Content-Type"); + if (ct == NULL) { + /* We need more data - or it isn't multipart mesage */ + goto exit_mp; + } + + /* Content-type should start with "multipart" */ + if (ct->len < 9 || strncmp(ct->p, "multipart", 9) != 0) { + goto exit_mp; + } + + boundary_len = + mg_http_parse_header2(ct, "boundary", &boundary, sizeof(boundary_buf)); + if (boundary_len == 0) { + /* + * Content type is multipart, but there is no boundary, + * probably malformed request + */ + nc->flags = MG_F_CLOSE_IMMEDIATELY; + DBG(("invalid request")); + goto exit_mp; + } + + /* If we reach this place - that is multipart request */ + + if (pd->mp_stream.boundary != NULL) { + /* + * Another streaming request was in progress, + * looks like protocol error + */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } else { + struct mg_http_endpoint *ep = NULL; + pd->mp_stream.state = MPS_BEGIN; + pd->mp_stream.boundary = strdup(boundary); + pd->mp_stream.boundary_len = strlen(boundary); + pd->mp_stream.var_name = pd->mp_stream.file_name = NULL; + pd->endpoint_handler = nc->handler; + + ep = mg_http_get_endpoint_handler(nc->listener, &hm->uri); + if (ep != NULL) { + pd->endpoint_handler = ep->handler; + } + + mg_http_call_endpoint_handler(nc, MG_EV_HTTP_MULTIPART_REQUEST, hm); + + mbuf_remove(io, req_len); + } +exit_mp: + if (boundary != boundary_buf) MG_FREE(boundary); +} + +#define CONTENT_DISPOSITION "Content-Disposition: " + +static void mg_http_multipart_call_handler(struct mg_connection *c, int ev, + const char *data, size_t data_len) { + struct mg_http_multipart_part mp; + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + memset(&mp, 0, sizeof(mp)); + + mp.var_name = pd->mp_stream.var_name; + mp.file_name = pd->mp_stream.file_name; + mp.user_data = pd->mp_stream.user_data; + mp.data.p = data; + mp.data.len = data_len; + mg_call(c, pd->endpoint_handler, c->user_data, ev, &mp); + pd->mp_stream.user_data = mp.user_data; +} + +static int mg_http_multipart_got_chunk(struct mg_connection *c) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + struct mbuf *io = &c->recv_mbuf; + + mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_DATA, io->buf, + pd->mp_stream.prev_io_len); + mbuf_remove(io, pd->mp_stream.prev_io_len); + pd->mp_stream.prev_io_len = 0; + pd->mp_stream.state = MPS_WAITING_FOR_CHUNK; + + return 0; +} + +static int mg_http_multipart_finalize(struct mg_connection *c) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + + mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_END, NULL, 0); + MG_FREE((void *) pd->mp_stream.file_name); + pd->mp_stream.file_name = NULL; + MG_FREE((void *) pd->mp_stream.var_name); + pd->mp_stream.var_name = NULL; + mg_http_multipart_call_handler(c, MG_EV_HTTP_MULTIPART_REQUEST_END, NULL, 0); + mg_http_free_proto_data_mp_stream(&pd->mp_stream); + pd->mp_stream.state = MPS_FINISHED; + + return 1; +} + +static int mg_http_multipart_wait_for_boundary(struct mg_connection *c) { + const char *boundary; + struct mbuf *io = &c->recv_mbuf; + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + + if (pd->mp_stream.boundary == NULL) { + pd->mp_stream.state = MPS_FINALIZE; + DBG(("Invalid request: boundary not initialized")); + return 0; + } + + if ((int) io->len < pd->mp_stream.boundary_len + 2) { + return 0; + } + + boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len); + if (boundary != NULL) { + const char *boundary_end = (boundary + pd->mp_stream.boundary_len); + if (io->len - (boundary_end - io->buf) < 4) { + return 0; + } + if (strncmp(boundary_end, "--\r\n", 4) == 0) { + pd->mp_stream.state = MPS_FINALIZE; + mbuf_remove(io, (boundary_end - io->buf) + 4); + } else { + pd->mp_stream.state = MPS_GOT_BOUNDARY; + } + } else { + return 0; + } + + return 1; +} + +static void mg_http_parse_header_internal(struct mg_str *hdr, + const char *var_name, + struct altbuf *ab); + +static int mg_http_multipart_process_boundary(struct mg_connection *c) { + int data_size; + const char *boundary, *block_begin; + struct mbuf *io = &c->recv_mbuf; + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + struct altbuf ab_file_name, ab_var_name; + int line_len; + boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len); + block_begin = boundary + pd->mp_stream.boundary_len + 2; + data_size = io->len - (block_begin - io->buf); + + altbuf_init(&ab_file_name, NULL, 0); + altbuf_init(&ab_var_name, NULL, 0); + + while (data_size > 0 && + (line_len = mg_get_line_len(block_begin, data_size)) != 0) { + if (line_len > (int) sizeof(CONTENT_DISPOSITION) && + mg_ncasecmp(block_begin, CONTENT_DISPOSITION, + sizeof(CONTENT_DISPOSITION) - 1) == 0) { + struct mg_str header; + + header.p = block_begin + sizeof(CONTENT_DISPOSITION) - 1; + header.len = line_len - sizeof(CONTENT_DISPOSITION) - 1; + + altbuf_reset(&ab_var_name); + mg_http_parse_header_internal(&header, "name", &ab_var_name); + + altbuf_reset(&ab_file_name); + mg_http_parse_header_internal(&header, "filename", &ab_file_name); + + block_begin += line_len; + data_size -= line_len; + + continue; + } + + if (line_len == 2 && mg_ncasecmp(block_begin, "\r\n", 2) == 0) { + mbuf_remove(io, block_begin - io->buf + 2); + + if (pd->mp_stream.processing_part != 0) { + mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_END, NULL, 0); + } + + /* Reserve 2 bytes for "\r\n" in file_name and var_name */ + altbuf_append(&ab_file_name, '\0'); + altbuf_append(&ab_file_name, '\0'); + altbuf_append(&ab_var_name, '\0'); + altbuf_append(&ab_var_name, '\0'); + + MG_FREE((void *) pd->mp_stream.file_name); + pd->mp_stream.file_name = altbuf_get_buf(&ab_file_name, 1 /* trim */); + MG_FREE((void *) pd->mp_stream.var_name); + pd->mp_stream.var_name = altbuf_get_buf(&ab_var_name, 1 /* trim */); + + mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_BEGIN, NULL, 0); + pd->mp_stream.state = MPS_WAITING_FOR_CHUNK; + pd->mp_stream.processing_part++; + return 1; + } + + block_begin += line_len; + } + + pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY; + + altbuf_reset(&ab_var_name); + altbuf_reset(&ab_file_name); + + return 0; +} + +static int mg_http_multipart_continue_wait_for_chunk(struct mg_connection *c) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + struct mbuf *io = &c->recv_mbuf; + + const char *boundary; + if ((int) io->len < pd->mp_stream.boundary_len + 6 /* \r\n, --, -- */) { + return 0; + } + + boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len); + if (boundary == NULL && pd->mp_stream.prev_io_len == 0) { + pd->mp_stream.prev_io_len = io->len; + return 0; + } else if (boundary == NULL && + (int) io->len > + pd->mp_stream.prev_io_len + pd->mp_stream.boundary_len + 4) { + pd->mp_stream.state = MPS_GOT_CHUNK; + return 1; + } else if (boundary != NULL) { + int data_size = (boundary - io->buf - 4); + mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_DATA, io->buf, data_size); + mbuf_remove(io, (boundary - io->buf)); + pd->mp_stream.prev_io_len = 0; + pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY; + return 1; + } else { + return 0; + } +} + +static void mg_http_multipart_continue(struct mg_connection *c) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(c); + while (1) { + switch (pd->mp_stream.state) { + case MPS_BEGIN: { + pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY; + break; + } + case MPS_WAITING_FOR_BOUNDARY: { + if (mg_http_multipart_wait_for_boundary(c) == 0) { + return; + } + break; + } + case MPS_GOT_BOUNDARY: { + if (mg_http_multipart_process_boundary(c) == 0) { + return; + } + break; + } + case MPS_WAITING_FOR_CHUNK: { + if (mg_http_multipart_continue_wait_for_chunk(c) == 0) { + return; + } + break; + } + case MPS_GOT_CHUNK: { + if (mg_http_multipart_got_chunk(c) == 0) { + return; + } + break; + } + case MPS_FINALIZE: { + if (mg_http_multipart_finalize(c) == 0) { + return; + } + break; + } + case MPS_FINISHED: { + mbuf_remove(&c->recv_mbuf, c->recv_mbuf.len); + return; + } + } + } +} + +struct file_upload_state { + char *lfn; + size_t num_recd; + FILE *fp; +}; + +#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ + +void mg_set_protocol_http_websocket(struct mg_connection *nc) { + nc->proto_handler = mg_http_handler; +} + +const char *mg_status_message(int status_code) { + switch (status_code) { + case 206: + return "Partial Content"; + case 301: + return "Moved"; + case 302: + return "Found"; + case 400: + return "Bad Request"; + case 401: + return "Unauthorized"; + case 403: + return "Forbidden"; + case 404: + return "Not Found"; + case 416: + return "Requested Range Not Satisfiable"; + case 418: + return "I'm a teapot"; + case 500: + return "Internal Server Error"; + case 502: + return "Bad Gateway"; + case 503: + return "Service Unavailable"; + +#if MG_ENABLE_EXTRA_ERRORS_DESC + case 100: + return "Continue"; + case 101: + return "Switching Protocols"; + case 102: + return "Processing"; + case 200: + return "OK"; + case 201: + return "Created"; + case 202: + return "Accepted"; + case 203: + return "Non-Authoritative Information"; + case 204: + return "No Content"; + case 205: + return "Reset Content"; + case 207: + return "Multi-Status"; + case 208: + return "Already Reported"; + case 226: + return "IM Used"; + case 300: + return "Multiple Choices"; + case 303: + return "See Other"; + case 304: + return "Not Modified"; + case 305: + return "Use Proxy"; + case 306: + return "Switch Proxy"; + case 307: + return "Temporary Redirect"; + case 308: + return "Permanent Redirect"; + case 402: + return "Payment Required"; + case 405: + return "Method Not Allowed"; + case 406: + return "Not Acceptable"; + case 407: + return "Proxy Authentication Required"; + case 408: + return "Request Timeout"; + case 409: + return "Conflict"; + case 410: + return "Gone"; + case 411: + return "Length Required"; + case 412: + return "Precondition Failed"; + case 413: + return "Payload Too Large"; + case 414: + return "URI Too Long"; + case 415: + return "Unsupported Media Type"; + case 417: + return "Expectation Failed"; + case 422: + return "Unprocessable Entity"; + case 423: + return "Locked"; + case 424: + return "Failed Dependency"; + case 426: + return "Upgrade Required"; + case 428: + return "Precondition Required"; + case 429: + return "Too Many Requests"; + case 431: + return "Request Header Fields Too Large"; + case 451: + return "Unavailable For Legal Reasons"; + case 501: + return "Not Implemented"; + case 504: + return "Gateway Timeout"; + case 505: + return "HTTP Version Not Supported"; + case 506: + return "Variant Also Negotiates"; + case 507: + return "Insufficient Storage"; + case 508: + return "Loop Detected"; + case 510: + return "Not Extended"; + case 511: + return "Network Authentication Required"; +#endif /* MG_ENABLE_EXTRA_ERRORS_DESC */ + + default: + return "OK"; + } +} + +void mg_send_response_line_s(struct mg_connection *nc, int status_code, + const struct mg_str extra_headers) { + mg_printf(nc, "HTTP/1.1 %d %s\r\nServer: %s\r\n", status_code, + mg_status_message(status_code), mg_version_header); + if (extra_headers.len > 0) { + mg_printf(nc, "%.*s\r\n", (int) extra_headers.len, extra_headers.p); + } +} + +void mg_send_response_line(struct mg_connection *nc, int status_code, + const char *extra_headers) { + mg_send_response_line_s(nc, status_code, mg_mk_str(extra_headers)); +} + +void mg_http_send_redirect(struct mg_connection *nc, int status_code, + const struct mg_str location, + const struct mg_str extra_headers) { + char bbody[100], *pbody = bbody; + int bl = mg_asprintf(&pbody, sizeof(bbody), + "

Moved here.\r\n", + (int) location.len, location.p); + char bhead[150], *phead = bhead; + mg_asprintf(&phead, sizeof(bhead), + "Location: %.*s\r\n" + "Content-Type: text/html\r\n" + "Content-Length: %d\r\n" + "Cache-Control: no-cache\r\n" + "%.*s%s", + (int) location.len, location.p, bl, (int) extra_headers.len, + extra_headers.p, (extra_headers.len > 0 ? "\r\n" : "")); + mg_send_response_line(nc, status_code, phead); + if (phead != bhead) MG_FREE(phead); + mg_send(nc, pbody, bl); + if (pbody != bbody) MG_FREE(pbody); +} + +void mg_send_head(struct mg_connection *c, int status_code, + int64_t content_length, const char *extra_headers) { + mg_send_response_line(c, status_code, extra_headers); + if (content_length < 0) { + mg_printf(c, "%s", "Transfer-Encoding: chunked\r\n"); + } else { + mg_printf(c, "Content-Length: %" INT64_FMT "\r\n", content_length); + } + mg_send(c, "\r\n", 2); +} + +void mg_http_send_error(struct mg_connection *nc, int code, + const char *reason) { + if (!reason) reason = mg_status_message(code); + LOG(LL_DEBUG, ("%p %d %s", nc, code, reason)); + mg_send_head(nc, code, strlen(reason), + "Content-Type: text/plain\r\nConnection: close"); + mg_send(nc, reason, strlen(reason)); + nc->flags |= MG_F_SEND_AND_CLOSE; +} + +#if MG_ENABLE_FILESYSTEM +static void mg_http_construct_etag(char *buf, size_t buf_len, + const cs_stat_t *st) { + snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"", (unsigned long) st->st_mtime, + (int64_t) st->st_size); +} + +#ifndef WINCE +static void mg_gmt_time_string(char *buf, size_t buf_len, time_t *t) { + strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t)); +} +#else +/* Look wince_lib.c for WindowsCE implementation */ +static void mg_gmt_time_string(char *buf, size_t buf_len, time_t *t); +#endif + +static int mg_http_parse_range_header(const struct mg_str *header, int64_t *a, + int64_t *b) { + /* + * There is no snscanf. Headers are not guaranteed to be NUL-terminated, + * so we have this. Ugh. + */ + int result; + char *p = (char *) MG_MALLOC(header->len + 1); + if (p == NULL) return 0; + memcpy(p, header->p, header->len); + p[header->len] = '\0'; + result = sscanf(p, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b); + MG_FREE(p); + return result; +} + +void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm, + const char *path, const struct mg_str mime_type, + const struct mg_str extra_headers) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + cs_stat_t st; + LOG(LL_DEBUG, ("%p [%s] %.*s", nc, path, (int) mime_type.len, mime_type.p)); + if (mg_stat(path, &st) != 0 || (pd->file.fp = mg_fopen(path, "rb")) == NULL) { + int code, err = mg_get_errno(); + switch (err) { + case EACCES: + code = 403; + break; + case ENOENT: + code = 404; + break; + default: + code = 500; + }; + mg_http_send_error(nc, code, "Open failed"); + } else { + char etag[50], current_time[50], last_modified[50], range[70]; + time_t t = (time_t) mg_time(); + int64_t r1 = 0, r2 = 0, cl = st.st_size; + struct mg_str *range_hdr = mg_get_http_header(hm, "Range"); + int n, status_code = 200; + + /* Handle Range header */ + range[0] = '\0'; + if (range_hdr != NULL && + (n = mg_http_parse_range_header(range_hdr, &r1, &r2)) > 0 && r1 >= 0 && + r2 >= 0) { + /* If range is specified like "400-", set second limit to content len */ + if (n == 1) { + r2 = cl - 1; + } + if (r1 > r2 || r2 >= cl) { + status_code = 416; + cl = 0; + snprintf(range, sizeof(range), + "Content-Range: bytes */%" INT64_FMT "\r\n", + (int64_t) st.st_size); + } else { + status_code = 206; + cl = r2 - r1 + 1; + snprintf(range, sizeof(range), "Content-Range: bytes %" INT64_FMT + "-%" INT64_FMT "/%" INT64_FMT "\r\n", + r1, r1 + cl - 1, (int64_t) st.st_size); +#if _FILE_OFFSET_BITS == 64 || _POSIX_C_SOURCE >= 200112L || \ + _XOPEN_SOURCE >= 600 + fseeko(pd->file.fp, r1, SEEK_SET); +#else + fseek(pd->file.fp, (long) r1, SEEK_SET); +#endif + } + } + +#if !MG_DISABLE_HTTP_KEEP_ALIVE + { + struct mg_str *conn_hdr = mg_get_http_header(hm, "Connection"); + if (conn_hdr != NULL) { + pd->file.keepalive = (mg_vcasecmp(conn_hdr, "keep-alive") == 0); + } else { + pd->file.keepalive = (mg_vcmp(&hm->proto, "HTTP/1.1") == 0); + } + } +#endif + + mg_http_construct_etag(etag, sizeof(etag), &st); + mg_gmt_time_string(current_time, sizeof(current_time), &t); + mg_gmt_time_string(last_modified, sizeof(last_modified), &st.st_mtime); + /* + * Content length casted to size_t because: + * 1) that's the maximum buffer size anyway + * 2) ESP8266 RTOS SDK newlib vprintf cannot contain a 64bit arg at non-last + * position + * TODO(mkm): fix ESP8266 RTOS SDK + */ + mg_send_response_line_s(nc, status_code, extra_headers); + mg_printf(nc, + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "Accept-Ranges: bytes\r\n" + "Content-Type: %.*s\r\n" + "Connection: %s\r\n" + "Content-Length: %" SIZE_T_FMT + "\r\n" + "%sEtag: %s\r\n\r\n", + current_time, last_modified, (int) mime_type.len, mime_type.p, + (pd->file.keepalive ? "keep-alive" : "close"), (size_t) cl, range, + etag); + + pd->file.cl = cl; + pd->file.type = DATA_FILE; + mg_http_transfer_file_data(nc); + } +} + +static void mg_http_serve_file2(struct mg_connection *nc, const char *path, + struct http_message *hm, + struct mg_serve_http_opts *opts) { +#if MG_ENABLE_HTTP_SSI + if (mg_match_prefix(opts->ssi_pattern, strlen(opts->ssi_pattern), path) > 0) { + mg_handle_ssi_request(nc, hm, path, opts); + return; + } +#endif + mg_http_serve_file(nc, hm, path, mg_get_mime_type(path, "text/plain", opts), + mg_mk_str(opts->extra_headers)); +} + +#endif + +int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, + int is_form_url_encoded) { + int i, j, a, b; +#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') + + for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) { + if (src[i] == '%') { + if (i < src_len - 2 && isxdigit(*(const unsigned char *) (src + i + 1)) && + isxdigit(*(const unsigned char *) (src + i + 2))) { + a = tolower(*(const unsigned char *) (src + i + 1)); + b = tolower(*(const unsigned char *) (src + i + 2)); + dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b)); + i += 2; + } else { + return -1; + } + } else if (is_form_url_encoded && src[i] == '+') { + dst[j] = ' '; + } else { + dst[j] = src[i]; + } + } + + dst[j] = '\0'; /* Null-terminate the destination */ + + return i >= src_len ? j : -1; +} + +int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst, + size_t dst_len) { + const char *p, *e, *s; + size_t name_len; + int len; + + /* + * According to the documentation function returns negative + * value in case of error. For debug purposes it returns: + * -1 - src is wrong (NUUL) + * -2 - dst is wrong (NULL) + * -3 - failed to decode url or dst is to small + * -4 - name does not exist + */ + if (dst == NULL || dst_len == 0) { + len = -2; + } else if (buf->p == NULL || name == NULL || buf->len == 0) { + len = -1; + dst[0] = '\0'; + } else { + name_len = strlen(name); + e = buf->p + buf->len; + len = -4; + dst[0] = '\0'; + + for (p = buf->p; p + name_len < e; p++) { + if ((p == buf->p || p[-1] == '&') && p[name_len] == '=' && + !mg_ncasecmp(name, p, name_len)) { + p += name_len + 1; + s = (const char *) memchr(p, '&', (size_t)(e - p)); + if (s == NULL) { + s = e; + } + len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1); + /* -1 means: failed to decode or dst is too small */ + if (len == -1) { + len = -3; + } + break; + } + } + } + + return len; +} + +void mg_send_http_chunk(struct mg_connection *nc, const char *buf, size_t len) { + char chunk_size[50]; + int n; + + n = snprintf(chunk_size, sizeof(chunk_size), "%lX\r\n", (unsigned long) len); + mg_send(nc, chunk_size, n); + mg_send(nc, buf, len); + mg_send(nc, "\r\n", 2); +} + +void mg_printf_http_chunk(struct mg_connection *nc, const char *fmt, ...) { + char mem[MG_VPRINTF_BUFFER_SIZE], *buf = mem; + int len; + va_list ap; + + va_start(ap, fmt); + len = mg_avprintf(&buf, sizeof(mem), fmt, ap); + va_end(ap); + + if (len >= 0) { + mg_send_http_chunk(nc, buf, len); + } + + /* LCOV_EXCL_START */ + if (buf != mem && buf != NULL) { + MG_FREE(buf); + } + /* LCOV_EXCL_STOP */ +} + +void mg_printf_html_escape(struct mg_connection *nc, const char *fmt, ...) { + char mem[MG_VPRINTF_BUFFER_SIZE], *buf = mem; + int i, j, len; + va_list ap; + + va_start(ap, fmt); + len = mg_avprintf(&buf, sizeof(mem), fmt, ap); + va_end(ap); + + if (len >= 0) { + for (i = j = 0; i < len; i++) { + if (buf[i] == '<' || buf[i] == '>') { + mg_send(nc, buf + j, i - j); + mg_send(nc, buf[i] == '<' ? "<" : ">", 4); + j = i + 1; + } + } + mg_send(nc, buf + j, i - j); + } + + /* LCOV_EXCL_START */ + if (buf != mem && buf != NULL) { + MG_FREE(buf); + } + /* LCOV_EXCL_STOP */ +} + +static void mg_http_parse_header_internal(struct mg_str *hdr, + const char *var_name, + struct altbuf *ab) { + int ch = ' ', ch1 = ',', n = strlen(var_name); + const char *p, *end = hdr ? hdr->p + hdr->len : NULL, *s = NULL; + + /* Find where variable starts */ + for (s = hdr->p; s != NULL && s + n < end; s++) { + if ((s == hdr->p || s[-1] == ch || s[-1] == ch1 || s[-1] == ';') && + s[n] == '=' && !strncmp(s, var_name, n)) + break; + } + + if (s != NULL && &s[n + 1] < end) { + s += n + 1; + if (*s == '"' || *s == '\'') { + ch = ch1 = *s++; + } + p = s; + while (p < end && p[0] != ch && p[0] != ch1) { + if (ch != ' ' && p[0] == '\\' && p[1] == ch) p++; + altbuf_append(ab, *p++); + } + + if (ch != ' ' && *p != ch) { + altbuf_reset(ab); + } + } + + /* If there is some data, append a NUL. */ + if (ab->len > 0) { + altbuf_append(ab, '\0'); + } +} + +int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf, + size_t buf_size) { + struct altbuf ab; + altbuf_init(&ab, *buf, buf_size); + if (hdr == NULL) return 0; + if (*buf != NULL && buf_size > 0) *buf[0] = '\0'; + + mg_http_parse_header_internal(hdr, var_name, &ab); + + /* + * Get a (trimmed) buffer, and return a len without a NUL byte which might + * have been added. + */ + *buf = altbuf_get_buf(&ab, 1 /* trim */); + return ab.len > 0 ? ab.len - 1 : 0; +} + +int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf, + size_t buf_size) { + char *buf2 = buf; + + int len = mg_http_parse_header2(hdr, var_name, &buf2, buf_size); + + if (buf2 != buf) { + /* Buffer was not enough and was reallocated: free it and just return 0 */ + MG_FREE(buf2); + return 0; + } + + return len; +} + +int mg_get_http_basic_auth(struct http_message *hm, char *user, size_t user_len, + char *pass, size_t pass_len) { + struct mg_str *hdr = mg_get_http_header(hm, "Authorization"); + if (hdr == NULL) return -1; + return mg_parse_http_basic_auth(hdr, user, user_len, pass, pass_len); +} + +int mg_parse_http_basic_auth(struct mg_str *hdr, char *user, size_t user_len, + char *pass, size_t pass_len) { + char *buf = NULL; + char fmt[64]; + int res = 0; + + if (mg_strncmp(*hdr, mg_mk_str("Basic "), 6) != 0) return -1; + + buf = (char *) MG_MALLOC(hdr->len); + cs_base64_decode((unsigned char *) hdr->p + 6, hdr->len, buf, NULL); + + /* e.g. "%123[^:]:%321[^\n]" */ + snprintf(fmt, sizeof(fmt), "%%%" SIZE_T_FMT "[^:]:%%%" SIZE_T_FMT "[^\n]", + user_len - 1, pass_len - 1); + if (sscanf(buf, fmt, user, pass) == 0) { + res = -1; + } + + MG_FREE(buf); + return res; +} + +#if MG_ENABLE_FILESYSTEM +static int mg_is_file_hidden(const char *path, + const struct mg_serve_http_opts *opts, + int exclude_specials) { + const char *p1 = opts->per_directory_auth_file; + const char *p2 = opts->hidden_file_pattern; + + /* Strip directory path from the file name */ + const char *pdir = strrchr(path, DIRSEP); + if (pdir != NULL) { + path = pdir + 1; + } + + return (exclude_specials && (!strcmp(path, ".") || !strcmp(path, ".."))) || + (p1 != NULL && mg_match_prefix(p1, strlen(p1), path) == strlen(p1)) || + (p2 != NULL && mg_match_prefix(p2, strlen(p2), path) > 0); +} + +#if !MG_DISABLE_HTTP_DIGEST_AUTH + +#ifndef MG_EXT_MD5 +void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest) { + size_t i; + cs_md5_ctx md5_ctx; + cs_md5_init(&md5_ctx); + for (i = 0; i < num_msgs; i++) { + cs_md5_update(&md5_ctx, msgs[i], msg_lens[i]); + } + cs_md5_final(digest, &md5_ctx); +} +#else +extern void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest); +#endif + +void cs_md5(char buf[33], ...) { + unsigned char hash[16]; + const uint8_t *msgs[20], *p; + size_t msg_lens[20]; + size_t num_msgs = 0; + va_list ap; + + va_start(ap, buf); + while ((p = va_arg(ap, const unsigned char *) ) != NULL) { + msgs[num_msgs] = p; + msg_lens[num_msgs] = va_arg(ap, size_t); + num_msgs++; + } + va_end(ap); + + mg_hash_md5_v(num_msgs, msgs, msg_lens, hash); + cs_to_hex(buf, hash, sizeof(hash)); +} + +static void mg_mkmd5resp(const char *method, size_t method_len, const char *uri, + size_t uri_len, const char *ha1, size_t ha1_len, + const char *nonce, size_t nonce_len, const char *nc, + size_t nc_len, const char *cnonce, size_t cnonce_len, + const char *qop, size_t qop_len, char *resp) { + static const char colon[] = ":"; + static const size_t one = 1; + char ha2[33]; + cs_md5(ha2, method, method_len, colon, one, uri, uri_len, NULL); + cs_md5(resp, ha1, ha1_len, colon, one, nonce, nonce_len, colon, one, nc, + nc_len, colon, one, cnonce, cnonce_len, colon, one, qop, qop_len, + colon, one, ha2, sizeof(ha2) - 1, NULL); +} + +int mg_http_create_digest_auth_header(char *buf, size_t buf_len, + const char *method, const char *uri, + const char *auth_domain, const char *user, + const char *passwd, const char *nonce) { + static const char colon[] = ":", qop[] = "auth"; + static const size_t one = 1; + char ha1[33], resp[33], cnonce[40]; + + snprintf(cnonce, sizeof(cnonce), "%lx", (unsigned long) mg_time()); + cs_md5(ha1, user, (size_t) strlen(user), colon, one, auth_domain, + (size_t) strlen(auth_domain), colon, one, passwd, + (size_t) strlen(passwd), NULL); + mg_mkmd5resp(method, strlen(method), uri, strlen(uri), ha1, sizeof(ha1) - 1, + nonce, strlen(nonce), "1", one, cnonce, strlen(cnonce), qop, + sizeof(qop) - 1, resp); + return snprintf(buf, buf_len, + "Authorization: Digest username=\"%s\"," + "realm=\"%s\",uri=\"%s\",qop=%s,nc=1,cnonce=%s," + "nonce=%s,response=%s\r\n", + user, auth_domain, uri, qop, cnonce, nonce, resp); +} + +/* + * Check for authentication timeout. + * Clients send time stamp encoded in nonce. Make sure it is not too old, + * to prevent replay attacks. + * Assumption: nonce is a hexadecimal number of seconds since 1970. + */ +static int mg_check_nonce(const char *nonce) { + unsigned long now = (unsigned long) mg_time(); + unsigned long val = (unsigned long) strtoul(nonce, NULL, 16); + return (now >= val) && (now - val < 60 * 60); +} + +int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain, + FILE *fp) { + int ret = 0; + struct mg_str *hdr; + char username_buf[50], cnonce_buf[64], response_buf[40], uri_buf[200], + qop_buf[20], nc_buf[20], nonce_buf[16]; + + char *username = username_buf, *cnonce = cnonce_buf, *response = response_buf, + *uri = uri_buf, *qop = qop_buf, *nc = nc_buf, *nonce = nonce_buf; + + /* Parse "Authorization:" header, fail fast on parse error */ + if (hm == NULL || fp == NULL || + (hdr = mg_get_http_header(hm, "Authorization")) == NULL || + mg_http_parse_header2(hdr, "username", &username, sizeof(username_buf)) == + 0 || + mg_http_parse_header2(hdr, "cnonce", &cnonce, sizeof(cnonce_buf)) == 0 || + mg_http_parse_header2(hdr, "response", &response, sizeof(response_buf)) == + 0 || + mg_http_parse_header2(hdr, "uri", &uri, sizeof(uri_buf)) == 0 || + mg_http_parse_header2(hdr, "qop", &qop, sizeof(qop_buf)) == 0 || + mg_http_parse_header2(hdr, "nc", &nc, sizeof(nc_buf)) == 0 || + mg_http_parse_header2(hdr, "nonce", &nonce, sizeof(nonce_buf)) == 0 || + mg_check_nonce(nonce) == 0) { + ret = 0; + goto clean; + } + + /* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */ + + ret = mg_check_digest_auth( + hm->method, + mg_mk_str_n( + hm->uri.p, + hm->uri.len + (hm->query_string.len ? hm->query_string.len + 1 : 0)), + mg_mk_str(username), mg_mk_str(cnonce), mg_mk_str(response), + mg_mk_str(qop), mg_mk_str(nc), mg_mk_str(nonce), mg_mk_str(auth_domain), + fp); + +clean: + if (username != username_buf) MG_FREE(username); + if (cnonce != cnonce_buf) MG_FREE(cnonce); + if (response != response_buf) MG_FREE(response); + if (uri != uri_buf) MG_FREE(uri); + if (qop != qop_buf) MG_FREE(qop); + if (nc != nc_buf) MG_FREE(nc); + if (nonce != nonce_buf) MG_FREE(nonce); + + return ret; +} + +int mg_check_digest_auth(struct mg_str method, struct mg_str uri, + struct mg_str username, struct mg_str cnonce, + struct mg_str response, struct mg_str qop, + struct mg_str nc, struct mg_str nonce, + struct mg_str auth_domain, FILE *fp) { + char buf[128], f_user[sizeof(buf)], f_ha1[sizeof(buf)], f_domain[sizeof(buf)]; + char expected_response[33]; + + /* + * Read passwords file line by line. If should have htdigest format, + * i.e. each line should be a colon-separated sequence: + * USER_NAME:DOMAIN_NAME:HA1_HASH_OF_USER_DOMAIN_AND_PASSWORD + */ + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (sscanf(buf, "%[^:]:%[^:]:%s", f_user, f_domain, f_ha1) == 3 && + mg_vcmp(&username, f_user) == 0 && + mg_vcmp(&auth_domain, f_domain) == 0) { + /* Username and domain matched, check the password */ + mg_mkmd5resp(method.p, method.len, uri.p, uri.len, f_ha1, strlen(f_ha1), + nonce.p, nonce.len, nc.p, nc.len, cnonce.p, cnonce.len, + qop.p, qop.len, expected_response); + LOG(LL_DEBUG, + ("%.*s %s %.*s %s", (int) username.len, username.p, f_domain, + (int) response.len, response.p, expected_response)); + return mg_ncasecmp(response.p, expected_response, response.len) == 0; + } + } + + /* None of the entries in the passwords file matched - return failure */ + return 0; +} + +int mg_http_is_authorized(struct http_message *hm, struct mg_str path, + const char *domain, const char *passwords_file, + int flags) { + char buf[MG_MAX_PATH]; + const char *p; + FILE *fp; + int authorized = 1; + + if (domain != NULL && passwords_file != NULL) { + if (flags & MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE) { + fp = mg_fopen(passwords_file, "r"); + } else if (flags & MG_AUTH_FLAG_IS_DIRECTORY) { + snprintf(buf, sizeof(buf), "%.*s%c%s", (int) path.len, path.p, DIRSEP, + passwords_file); + fp = mg_fopen(buf, "r"); + } else { + p = strrchr(path.p, DIRSEP); + if (p == NULL) p = path.p; + snprintf(buf, sizeof(buf), "%.*s%c%s", (int) (p - path.p), path.p, DIRSEP, + passwords_file); + fp = mg_fopen(buf, "r"); + } + + if (fp != NULL) { + authorized = mg_http_check_digest_auth(hm, domain, fp); + fclose(fp); + } else if (!(flags & MG_AUTH_FLAG_ALLOW_MISSING_FILE)) { + authorized = 0; + } + } + + LOG(LL_DEBUG, ("%.*s %s %x %d", (int) path.len, path.p, + passwords_file ? passwords_file : "", flags, authorized)); + return authorized; +} +#else +int mg_http_is_authorized(struct http_message *hm, const struct mg_str path, + const char *domain, const char *passwords_file, + int flags) { + (void) hm; + (void) path; + (void) domain; + (void) passwords_file; + (void) flags; + return 1; +} +#endif + +#if MG_ENABLE_DIRECTORY_LISTING +static void mg_escape(const char *src, char *dst, size_t dst_len) { + size_t n = 0; + while (*src != '\0' && n + 5 < dst_len) { + unsigned char ch = *(unsigned char *) src++; + if (ch == '<') { + n += snprintf(dst + n, dst_len - n, "%s", "<"); + } else { + dst[n++] = ch; + } + } + dst[n] = '\0'; +} + +static void mg_print_dir_entry(struct mg_connection *nc, const char *file_name, + cs_stat_t *stp) { + char size[64], mod[64], path[MG_MAX_PATH]; + int64_t fsize = stp->st_size; + int is_dir = S_ISDIR(stp->st_mode); + const char *slash = is_dir ? "/" : ""; + struct mg_str href; + + if (is_dir) { + snprintf(size, sizeof(size), "%s", "[DIRECTORY]"); + } else { + /* + * We use (double) cast below because MSVC 6 compiler cannot + * convert unsigned __int64 to double. + */ + if (fsize < 1024) { + snprintf(size, sizeof(size), "%d", (int) fsize); + } else if (fsize < 0x100000) { + snprintf(size, sizeof(size), "%.1fk", (double) fsize / 1024.0); + } else if (fsize < 0x40000000) { + snprintf(size, sizeof(size), "%.1fM", (double) fsize / 1048576); + } else { + snprintf(size, sizeof(size), "%.1fG", (double) fsize / 1073741824); + } + } + strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&stp->st_mtime)); + mg_escape(file_name, path, sizeof(path)); + href = mg_url_encode(mg_mk_str(file_name)); + mg_printf_http_chunk(nc, + "

%s%s%s%s
" + "" + "\n" + "\n" + "", + (int) hm->uri.len, hm->uri.p, sort_js_code, sort_js_code2, + (int) hm->uri.len, hm->uri.p); + mg_scan_directory(nc, dir, opts, mg_print_dir_entry); + mg_printf_http_chunk(nc, + "\n" + "
Name" + "Modified" + "Size


\n" + "
%s
\n" + "", + mg_version_header); + mg_send_http_chunk(nc, "", 0); + /* TODO(rojer): Remove when cesanta/dev/issues/197 is fixed. */ + nc->flags |= MG_F_SEND_AND_CLOSE; +} +#endif /* MG_ENABLE_DIRECTORY_LISTING */ + +/* + * Given a directory path, find one of the files specified in the + * comma-separated list of index files `list`. + * First found index file wins. If an index file is found, then gets + * appended to the `path`, stat-ed, and result of `stat()` passed to `stp`. + * If index file is not found, then `path` and `stp` remain unchanged. + */ +MG_INTERNAL void mg_find_index_file(const char *path, const char *list, + char **index_file, cs_stat_t *stp) { + struct mg_str vec; + size_t path_len = strlen(path); + int found = 0; + *index_file = NULL; + + /* Traverse index files list. For each entry, append it to the given */ + /* path and see if the file exists. If it exists, break the loop */ + while ((list = mg_next_comma_list_entry(list, &vec, NULL)) != NULL) { + cs_stat_t st; + size_t len = path_len + 1 + vec.len + 1; + *index_file = (char *) MG_REALLOC(*index_file, len); + if (*index_file == NULL) break; + snprintf(*index_file, len, "%s%c%.*s", path, DIRSEP, (int) vec.len, vec.p); + + /* Does it exist? Is it a file? */ + if (mg_stat(*index_file, &st) == 0 && S_ISREG(st.st_mode)) { + /* Yes it does, break the loop */ + *stp = st; + found = 1; + break; + } + } + if (!found) { + MG_FREE(*index_file); + *index_file = NULL; + } + LOG(LL_DEBUG, ("[%s] [%s]", path, (*index_file ? *index_file : ""))); +} + +#if MG_ENABLE_HTTP_URL_REWRITES +static int mg_http_send_port_based_redirect( + struct mg_connection *c, struct http_message *hm, + const struct mg_serve_http_opts *opts) { + const char *rewrites = opts->url_rewrites; + struct mg_str a, b; + char local_port[20] = {'%'}; + + mg_conn_addr_to_str(c, local_port + 1, sizeof(local_port) - 1, + MG_SOCK_STRINGIFY_PORT); + + while ((rewrites = mg_next_comma_list_entry(rewrites, &a, &b)) != NULL) { + if (mg_vcmp(&a, local_port) == 0) { + mg_send_response_line(c, 301, NULL); + mg_printf(c, "Content-Length: 0\r\nLocation: %.*s%.*s\r\n\r\n", + (int) b.len, b.p, (int) (hm->proto.p - hm->uri.p - 1), + hm->uri.p); + return 1; + } + } + + return 0; +} + +static void mg_reverse_proxy_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct http_message *hm = (struct http_message *) ev_data; + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + + if (pd == NULL || pd->reverse_proxy_data.linked_conn == NULL) { + DBG(("%p: upstream closed", nc)); + return; + } + + switch (ev) { + case MG_EV_CONNECT: + if (*(int *) ev_data != 0) { + mg_http_send_error(pd->reverse_proxy_data.linked_conn, 502, NULL); + } + break; + /* TODO(mkm): handle streaming */ + case MG_EV_HTTP_REPLY: + mg_send(pd->reverse_proxy_data.linked_conn, hm->message.p, + hm->message.len); + pd->reverse_proxy_data.linked_conn->flags |= MG_F_SEND_AND_CLOSE; + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + break; + case MG_EV_CLOSE: + pd->reverse_proxy_data.linked_conn->flags |= MG_F_SEND_AND_CLOSE; + break; + } + +#if MG_ENABLE_CALLBACK_USERDATA + (void) user_data; +#endif +} + +void mg_http_reverse_proxy(struct mg_connection *nc, + const struct http_message *hm, struct mg_str mount, + struct mg_str upstream) { + struct mg_connection *be; + char burl[256], *purl = burl; + int i; + const char *error; + struct mg_connect_opts opts; + struct mg_str path = MG_NULL_STR, user_info = MG_NULL_STR, host = MG_NULL_STR; + memset(&opts, 0, sizeof(opts)); + opts.error_string = &error; + + mg_asprintf(&purl, sizeof(burl), "%.*s%.*s", (int) upstream.len, upstream.p, + (int) (hm->uri.len - mount.len), hm->uri.p + mount.len); + + be = mg_connect_http_base(nc->mgr, MG_CB(mg_reverse_proxy_handler, NULL), + opts, "http", NULL, "https", NULL, purl, &path, + &user_info, &host); + LOG(LL_DEBUG, ("Proxying %.*s to %s (rule: %.*s)", (int) hm->uri.len, + hm->uri.p, purl, (int) mount.len, mount.p)); + + if (be == NULL) { + LOG(LL_ERROR, ("Error connecting to %s: %s", purl, error)); + mg_http_send_error(nc, 502, NULL); + goto cleanup; + } + + /* link connections to each other, they must live and die together */ + mg_http_get_proto_data(be)->reverse_proxy_data.linked_conn = nc; + mg_http_get_proto_data(nc)->reverse_proxy_data.linked_conn = be; + + /* send request upstream */ + mg_printf(be, "%.*s %.*s HTTP/1.1\r\n", (int) hm->method.len, hm->method.p, + (int) path.len, path.p); + + mg_printf(be, "Host: %.*s\r\n", (int) host.len, host.p); + for (i = 0; i < MG_MAX_HTTP_HEADERS && hm->header_names[i].len > 0; i++) { + struct mg_str hn = hm->header_names[i]; + struct mg_str hv = hm->header_values[i]; + + /* we rewrite the host header */ + if (mg_vcasecmp(&hn, "Host") == 0) continue; + /* + * Don't pass chunked transfer encoding to the client because hm->body is + * already dechunked when we arrive here. + */ + if (mg_vcasecmp(&hn, "Transfer-encoding") == 0 && + mg_vcasecmp(&hv, "chunked") == 0) { + mg_printf(be, "Content-Length: %" SIZE_T_FMT "\r\n", hm->body.len); + continue; + } + /* We don't support proxying Expect: 100-continue. */ + if (mg_vcasecmp(&hn, "Expect") == 0 && + mg_vcasecmp(&hv, "100-continue") == 0) { + continue; + } + + mg_printf(be, "%.*s: %.*s\r\n", (int) hn.len, hn.p, (int) hv.len, hv.p); + } + + mg_send(be, "\r\n", 2); + mg_send(be, hm->body.p, hm->body.len); + +cleanup: + if (purl != burl) MG_FREE(purl); +} + +static int mg_http_handle_forwarding(struct mg_connection *nc, + struct http_message *hm, + const struct mg_serve_http_opts *opts) { + const char *rewrites = opts->url_rewrites; + struct mg_str a, b; + struct mg_str p1 = MG_MK_STR("http://"), p2 = MG_MK_STR("https://"); + + while ((rewrites = mg_next_comma_list_entry(rewrites, &a, &b)) != NULL) { + if (mg_strncmp(a, hm->uri, a.len) == 0) { + if (mg_strncmp(b, p1, p1.len) == 0 || mg_strncmp(b, p2, p2.len) == 0) { + mg_http_reverse_proxy(nc, hm, a, b); + return 1; + } + } + } + + return 0; +} +#endif /* MG_ENABLE_FILESYSTEM */ + +MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm, + const struct mg_serve_http_opts *opts, + char **local_path, + struct mg_str *remainder) { + int ok = 1; + const char *cp = hm->uri.p, *cp_end = hm->uri.p + hm->uri.len; + struct mg_str root = {NULL, 0}; + const char *file_uri_start = cp; + *local_path = NULL; + remainder->p = NULL; + remainder->len = 0; + + { /* 1. Determine which root to use. */ + +#if MG_ENABLE_HTTP_URL_REWRITES + const char *rewrites = opts->url_rewrites; +#else + const char *rewrites = ""; +#endif + struct mg_str *hh = mg_get_http_header(hm, "Host"); + struct mg_str a, b; + /* Check rewrites first. */ + while ((rewrites = mg_next_comma_list_entry(rewrites, &a, &b)) != NULL) { + if (a.len > 1 && a.p[0] == '@') { + /* Host rewrite. */ + if (hh != NULL && hh->len == a.len - 1 && + mg_ncasecmp(a.p + 1, hh->p, a.len - 1) == 0) { + root = b; + break; + } + } else { + /* Regular rewrite, URI=directory */ + size_t match_len = mg_match_prefix_n(a, hm->uri); + if (match_len > 0) { + file_uri_start = hm->uri.p + match_len; + if (*file_uri_start == '/' || file_uri_start == cp_end) { + /* Match ended at component boundary, ok. */ + } else if (*(file_uri_start - 1) == '/') { + /* Pattern ends with '/', backtrack. */ + file_uri_start--; + } else { + /* No match: must fall on the component boundary. */ + continue; + } + root = b; + break; + } + } + } + /* If no rewrite rules matched, use DAV or regular document root. */ + if (root.p == NULL) { +#if MG_ENABLE_HTTP_WEBDAV + if (opts->dav_document_root != NULL && mg_is_dav_request(&hm->method)) { + root.p = opts->dav_document_root; + root.len = strlen(opts->dav_document_root); + } else +#endif + { + root.p = opts->document_root; + root.len = strlen(opts->document_root); + } + } + assert(root.p != NULL && root.len > 0); + } + + { /* 2. Find where in the canonical URI path the local path ends. */ + const char *u = file_uri_start + 1; + char *lp = (char *) MG_MALLOC(root.len + hm->uri.len + 1); + char *lp_end = lp + root.len + hm->uri.len + 1; + char *p = lp, *ps; + int exists = 1; + if (lp == NULL) { + ok = 0; + goto out; + } + memcpy(p, root.p, root.len); + p += root.len; + if (*(p - 1) == DIRSEP) p--; + *p = '\0'; + ps = p; + + /* Chop off URI path components one by one and build local path. */ + while (u <= cp_end) { + const char *next = u; + struct mg_str component; + if (exists) { + cs_stat_t st; + exists = (mg_stat(lp, &st) == 0); + if (exists && S_ISREG(st.st_mode)) { + /* We found the terminal, the rest of the URI (if any) is path_info. + */ + if (*(u - 1) == '/') u--; + break; + } + } + if (u >= cp_end) break; + parse_uri_component((const char **) &next, cp_end, "/", &component); + if (component.len > 0) { + int len; + memmove(p + 1, component.p, component.len); + len = mg_url_decode(p + 1, component.len, p + 1, lp_end - p - 1, 0); + if (len <= 0) { + ok = 0; + break; + } + component.p = p + 1; + component.len = len; + if (mg_vcmp(&component, ".") == 0) { + /* Yum. */ + } else if (mg_vcmp(&component, "..") == 0) { + while (p > ps && *p != DIRSEP) p--; + *p = '\0'; + } else { + size_t i; +#ifdef _WIN32 + /* On Windows, make sure it's valid Unicode (no funny stuff). */ + wchar_t buf[MG_MAX_PATH * 2]; + if (to_wchar(component.p, buf, MG_MAX_PATH) == 0) { + DBG(("[%.*s] smells funny", (int) component.len, component.p)); + ok = 0; + break; + } +#endif + *p++ = DIRSEP; + /* No NULs and DIRSEPs in the component (percent-encoded). */ + for (i = 0; i < component.len; i++, p++) { + if (*p == '\0' || *p == DIRSEP +#ifdef _WIN32 + /* On Windows, "/" is also accepted, so check for that too. */ + || + *p == '/' +#endif + ) { + ok = 0; + break; + } + } + } + } + u = next; + } + if (ok) { + *local_path = lp; + if (u > cp_end) u = cp_end; + remainder->p = u; + remainder->len = cp_end - u; + } else { + MG_FREE(lp); + } + } + +out: + LOG(LL_DEBUG, + ("'%.*s' -> '%s' + '%.*s'", (int) hm->uri.len, hm->uri.p, + *local_path ? *local_path : "", (int) remainder->len, remainder->p)); + return ok; +} + +static int mg_get_month_index(const char *s) { + static const char *month_names[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + size_t i; + + for (i = 0; i < ARRAY_SIZE(month_names); i++) + if (!strcmp(s, month_names[i])) return (int) i; + + return -1; +} + +static int mg_num_leap_years(int year) { + return year / 4 - year / 100 + year / 400; +} + +/* Parse UTC date-time string, and return the corresponding time_t value. */ +MG_INTERNAL time_t mg_parse_date_string(const char *datetime) { + static const unsigned short days_before_month[] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + char month_str[32]; + int second, minute, hour, day, month, year, leap_days, days; + time_t result = (time_t) 0; + + if (((sscanf(datetime, "%d/%3s/%d %d:%d:%d", &day, month_str, &year, &hour, + &minute, &second) == 6) || + (sscanf(datetime, "%d %3s %d %d:%d:%d", &day, month_str, &year, &hour, + &minute, &second) == 6) || + (sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d", &day, month_str, &year, + &hour, &minute, &second) == 6) || + (sscanf(datetime, "%d-%3s-%d %d:%d:%d", &day, month_str, &year, &hour, + &minute, &second) == 6)) && + year > 1970 && (month = mg_get_month_index(month_str)) != -1) { + leap_days = mg_num_leap_years(year) - mg_num_leap_years(1970); + year -= 1970; + days = year * 365 + days_before_month[month] + (day - 1) + leap_days; + result = days * 24 * 3600 + hour * 3600 + minute * 60 + second; + } + + return result; +} + +MG_INTERNAL int mg_is_not_modified(struct http_message *hm, cs_stat_t *st) { + struct mg_str *hdr; + if ((hdr = mg_get_http_header(hm, "If-None-Match")) != NULL) { + char etag[64]; + mg_http_construct_etag(etag, sizeof(etag), st); + return mg_vcasecmp(hdr, etag) == 0; + } else if ((hdr = mg_get_http_header(hm, "If-Modified-Since")) != NULL) { + return st->st_mtime <= mg_parse_date_string(hdr->p); + } else { + return 0; + } +} + +void mg_http_send_digest_auth_request(struct mg_connection *c, + const char *domain) { + mg_printf(c, + "HTTP/1.1 401 Unauthorized\r\n" + "WWW-Authenticate: Digest qop=\"auth\", " + "realm=\"%s\", nonce=\"%lx\"\r\n" + "Content-Length: 0\r\n\r\n", + domain, (unsigned long) mg_time()); +} + +static void mg_http_send_options(struct mg_connection *nc) { + mg_printf(nc, "%s", + "HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, CONNECT, OPTIONS" +#if MG_ENABLE_HTTP_WEBDAV + ", MKCOL, PUT, DELETE, PROPFIND, MOVE\r\nDAV: 1,2" +#endif + "\r\n\r\n"); + nc->flags |= MG_F_SEND_AND_CLOSE; +} + +static int mg_is_creation_request(const struct http_message *hm) { + return mg_vcmp(&hm->method, "MKCOL") == 0 || mg_vcmp(&hm->method, "PUT") == 0; +} + +MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path, + const struct mg_str *path_info, + struct http_message *hm, + struct mg_serve_http_opts *opts) { + int exists, is_directory, is_cgi; +#if MG_ENABLE_HTTP_WEBDAV + int is_dav = mg_is_dav_request(&hm->method); +#else + int is_dav = 0; +#endif + char *index_file = NULL; + cs_stat_t st; + + exists = (mg_stat(path, &st) == 0); + is_directory = exists && S_ISDIR(st.st_mode); + + if (is_directory) + mg_find_index_file(path, opts->index_files, &index_file, &st); + + is_cgi = + (mg_match_prefix(opts->cgi_file_pattern, strlen(opts->cgi_file_pattern), + index_file ? index_file : path) > 0); + + LOG(LL_DEBUG, + ("%p %.*s [%s] exists=%d is_dir=%d is_dav=%d is_cgi=%d index=%s", nc, + (int) hm->method.len, hm->method.p, path, exists, is_directory, is_dav, + is_cgi, index_file ? index_file : "")); + + if (is_directory && hm->uri.p[hm->uri.len - 1] != '/' && !is_dav) { + mg_printf(nc, + "HTTP/1.1 301 Moved\r\nLocation: %.*s/\r\n" + "Content-Length: 0\r\n\r\n", + (int) hm->uri.len, hm->uri.p); + MG_FREE(index_file); + return; + } + + /* If we have path_info, the only way to handle it is CGI. */ + if (path_info->len > 0 && !is_cgi) { + mg_http_send_error(nc, 501, NULL); + MG_FREE(index_file); + return; + } + + if (is_dav && opts->dav_document_root == NULL) { + mg_http_send_error(nc, 501, NULL); + } else if (!mg_http_is_authorized( + hm, mg_mk_str(path), opts->auth_domain, opts->global_auth_file, + ((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) | + MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE | + MG_AUTH_FLAG_ALLOW_MISSING_FILE)) || + !mg_http_is_authorized( + hm, mg_mk_str(path), opts->auth_domain, + opts->per_directory_auth_file, + ((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) | + MG_AUTH_FLAG_ALLOW_MISSING_FILE))) { + mg_http_send_digest_auth_request(nc, opts->auth_domain); + } else if (is_cgi) { +#if MG_ENABLE_HTTP_CGI + mg_handle_cgi(nc, index_file ? index_file : path, path_info, hm, opts); +#else + mg_http_send_error(nc, 501, NULL); +#endif /* MG_ENABLE_HTTP_CGI */ + } else if ((!exists || + mg_is_file_hidden(path, opts, 0 /* specials are ok */)) && + !mg_is_creation_request(hm)) { + mg_http_send_error(nc, 404, NULL); +#if MG_ENABLE_HTTP_WEBDAV + } else if (!mg_vcmp(&hm->method, "PROPFIND")) { + mg_handle_propfind(nc, path, &st, hm, opts); +#if !MG_DISABLE_DAV_AUTH + } else if (is_dav && + (opts->dav_auth_file == NULL || + (strcmp(opts->dav_auth_file, "-") != 0 && + !mg_http_is_authorized( + hm, mg_mk_str(path), opts->auth_domain, opts->dav_auth_file, + ((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) | + MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE | + MG_AUTH_FLAG_ALLOW_MISSING_FILE))))) { + mg_http_send_digest_auth_request(nc, opts->auth_domain); +#endif + } else if (!mg_vcmp(&hm->method, "MKCOL")) { + mg_handle_mkcol(nc, path, hm); + } else if (!mg_vcmp(&hm->method, "DELETE")) { + mg_handle_delete(nc, opts, path); + } else if (!mg_vcmp(&hm->method, "PUT")) { + mg_handle_put(nc, path, hm); + } else if (!mg_vcmp(&hm->method, "MOVE")) { + mg_handle_move(nc, opts, path, hm); +#if MG_ENABLE_FAKE_DAVLOCK + } else if (!mg_vcmp(&hm->method, "LOCK")) { + mg_handle_lock(nc, path); +#endif +#endif /* MG_ENABLE_HTTP_WEBDAV */ + } else if (!mg_vcmp(&hm->method, "OPTIONS")) { + mg_http_send_options(nc); + } else if (is_directory && index_file == NULL) { +#if MG_ENABLE_DIRECTORY_LISTING + if (strcmp(opts->enable_directory_listing, "yes") == 0) { + mg_send_directory_listing(nc, path, hm, opts); + } else { + mg_http_send_error(nc, 403, NULL); + } +#else + mg_http_send_error(nc, 501, NULL); +#endif + } else if (mg_is_not_modified(hm, &st)) { + mg_http_send_error(nc, 304, "Not Modified"); + } else { + mg_http_serve_file2(nc, index_file ? index_file : path, hm, opts); + } + MG_FREE(index_file); +} + +void mg_serve_http(struct mg_connection *nc, struct http_message *hm, + struct mg_serve_http_opts opts) { + char *path = NULL; + struct mg_str *hdr, path_info; + uint32_t remote_ip = ntohl(*(uint32_t *) &nc->sa.sin.sin_addr); + + if (mg_check_ip_acl(opts.ip_acl, remote_ip) != 1) { + /* Not allowed to connect */ + mg_http_send_error(nc, 403, NULL); + nc->flags |= MG_F_SEND_AND_CLOSE; + return; + } + +#if MG_ENABLE_HTTP_URL_REWRITES + if (mg_http_handle_forwarding(nc, hm, &opts)) { + return; + } + + if (mg_http_send_port_based_redirect(nc, hm, &opts)) { + return; + } +#endif + + if (opts.document_root == NULL) { + opts.document_root = "."; + } + if (opts.per_directory_auth_file == NULL) { + opts.per_directory_auth_file = ".htpasswd"; + } + if (opts.enable_directory_listing == NULL) { + opts.enable_directory_listing = "yes"; + } + if (opts.cgi_file_pattern == NULL) { + opts.cgi_file_pattern = "**.cgi$|**.php$"; + } + if (opts.ssi_pattern == NULL) { + opts.ssi_pattern = "**.shtml$|**.shtm$"; + } + if (opts.index_files == NULL) { + opts.index_files = "index.html,index.htm,index.shtml,index.cgi,index.php"; + } + /* Normalize path - resolve "." and ".." (in-place). */ + if (!mg_normalize_uri_path(&hm->uri, &hm->uri)) { + mg_http_send_error(nc, 400, NULL); + return; + } + if (mg_uri_to_local_path(hm, &opts, &path, &path_info) == 0) { + mg_http_send_error(nc, 404, NULL); + return; + } + mg_send_http_file(nc, path, &path_info, hm, &opts); + + MG_FREE(path); + path = NULL; + + /* Close connection for non-keep-alive requests */ + if (mg_vcmp(&hm->proto, "HTTP/1.1") != 0 || + ((hdr = mg_get_http_header(hm, "Connection")) != NULL && + mg_vcmp(hdr, "keep-alive") != 0)) { +#if 0 + nc->flags |= MG_F_SEND_AND_CLOSE; +#endif + } +} + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART +void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, + mg_fu_fname_fn local_name_fn + MG_UD_ARG(void *user_data)) { + switch (ev) { + case MG_EV_HTTP_PART_BEGIN: { + struct mg_http_multipart_part *mp = + (struct mg_http_multipart_part *) ev_data; + struct file_upload_state *fus = + (struct file_upload_state *) MG_CALLOC(1, sizeof(*fus)); + struct mg_str lfn = local_name_fn(nc, mg_mk_str(mp->file_name)); + mp->user_data = NULL; + if (lfn.p == NULL || lfn.len == 0) { + LOG(LL_ERROR, ("%p Not allowed to upload %s", nc, mp->file_name)); + mg_printf(nc, + "HTTP/1.1 403 Not Allowed\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n" + "Not allowed to upload %s\r\n", + mp->file_name); + nc->flags |= MG_F_SEND_AND_CLOSE; + return; + } + fus->lfn = (char *) MG_MALLOC(lfn.len + 1); + memcpy(fus->lfn, lfn.p, lfn.len); + fus->lfn[lfn.len] = '\0'; + if (lfn.p != mp->file_name) MG_FREE((char *) lfn.p); + LOG(LL_DEBUG, + ("%p Receiving file %s -> %s", nc, mp->file_name, fus->lfn)); + fus->fp = mg_fopen(fus->lfn, "w"); + if (fus->fp == NULL) { + mg_printf(nc, + "HTTP/1.1 500 Internal Server Error\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n"); + LOG(LL_ERROR, ("Failed to open %s: %d\n", fus->lfn, mg_get_errno())); + mg_printf(nc, "Failed to open %s: %d\n", fus->lfn, mg_get_errno()); + /* Do not close the connection just yet, discard remainder of the data. + * This is because at the time of writing some browsers (Chrome) fail to + * render response before all the data is sent. */ + } + mp->user_data = (void *) fus; + break; + } + case MG_EV_HTTP_PART_DATA: { + struct mg_http_multipart_part *mp = + (struct mg_http_multipart_part *) ev_data; + struct file_upload_state *fus = + (struct file_upload_state *) mp->user_data; + if (fus == NULL || fus->fp == NULL) break; + if (mg_fwrite(mp->data.p, 1, mp->data.len, fus->fp) != mp->data.len) { + LOG(LL_ERROR, ("Failed to write to %s: %d, wrote %d", fus->lfn, + mg_get_errno(), (int) fus->num_recd)); + if (mg_get_errno() == ENOSPC +#ifdef SPIFFS_ERR_FULL + || mg_get_errno() == SPIFFS_ERR_FULL +#endif + ) { + mg_printf(nc, + "HTTP/1.1 413 Payload Too Large\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n"); + mg_printf(nc, "Failed to write to %s: no space left; wrote %d\r\n", + fus->lfn, (int) fus->num_recd); + } else { + mg_printf(nc, + "HTTP/1.1 500 Internal Server Error\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n"); + mg_printf(nc, "Failed to write to %s: %d, wrote %d", mp->file_name, + mg_get_errno(), (int) fus->num_recd); + } + fclose(fus->fp); + remove(fus->lfn); + fus->fp = NULL; + /* Do not close the connection just yet, discard remainder of the data. + * This is because at the time of writing some browsers (Chrome) fail to + * render response before all the data is sent. */ + return; + } + fus->num_recd += mp->data.len; + LOG(LL_DEBUG, ("%p rec'd %d bytes, %d total", nc, (int) mp->data.len, + (int) fus->num_recd)); + break; + } + case MG_EV_HTTP_PART_END: { + struct mg_http_multipart_part *mp = + (struct mg_http_multipart_part *) ev_data; + struct file_upload_state *fus = + (struct file_upload_state *) mp->user_data; + if (fus == NULL) break; + if (mp->status >= 0 && fus->fp != NULL) { + LOG(LL_DEBUG, ("%p Uploaded %s (%s), %d bytes", nc, mp->file_name, + fus->lfn, (int) fus->num_recd)); + mg_printf(nc, + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n" + "Ok, %s - %d bytes.\r\n", + mp->file_name, (int) fus->num_recd); + } else { + LOG(LL_ERROR, ("Failed to store %s (%s)", mp->file_name, fus->lfn)); + /* + * mp->status < 0 means connection was terminated, so no reason to send + * HTTP reply + */ + } + if (fus->fp != NULL) fclose(fus->fp); + MG_FREE(fus->lfn); + MG_FREE(fus); + mp->user_data = NULL; + nc->flags |= MG_F_SEND_AND_CLOSE; + break; + } + } + +#if MG_ENABLE_CALLBACK_USERDATA + (void) user_data; +#endif +} + +#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ +#endif /* MG_ENABLE_FILESYSTEM */ + +struct mg_connection *mg_connect_http_base( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + struct mg_connect_opts opts, const char *scheme1, const char *scheme2, + const char *scheme_ssl1, const char *scheme_ssl2, const char *url, + struct mg_str *path, struct mg_str *user_info, struct mg_str *host) { + struct mg_connection *nc = NULL; + unsigned int port_i = 0; + int use_ssl = 0; + struct mg_str scheme, query, fragment; + char conn_addr_buf[2]; + char *conn_addr = conn_addr_buf; + + if (mg_parse_uri(mg_mk_str(url), &scheme, user_info, host, &port_i, path, + &query, &fragment) != 0) { + MG_SET_PTRPTR(opts.error_string, "cannot parse url"); + goto out; + } + + /* If query is present, do not strip it. Pass to the caller. */ + if (query.len > 0) path->len += query.len + 1; + + if (scheme.len == 0 || mg_vcmp(&scheme, scheme1) == 0 || + (scheme2 != NULL && mg_vcmp(&scheme, scheme2) == 0)) { + use_ssl = 0; + if (port_i == 0) port_i = 80; + } else if (mg_vcmp(&scheme, scheme_ssl1) == 0 || + (scheme2 != NULL && mg_vcmp(&scheme, scheme_ssl2) == 0)) { + use_ssl = 1; + if (port_i == 0) port_i = 443; + } else { + goto out; + } + + mg_asprintf(&conn_addr, sizeof(conn_addr_buf), "tcp://%.*s:%u", + (int) host->len, host->p, port_i); + if (conn_addr == NULL) goto out; + + LOG(LL_DEBUG, ("%s use_ssl? %d %s", url, use_ssl, conn_addr)); + if (use_ssl) { +#if MG_ENABLE_SSL + /* + * Schema requires SSL, but no SSL parameters were provided in opts. + * In order to maintain backward compatibility, use a faux-SSL with no + * verification. + */ + if (opts.ssl_ca_cert == NULL) { + opts.ssl_ca_cert = "*"; + } +#else + MG_SET_PTRPTR(opts.error_string, "ssl is disabled"); + goto out; +#endif + } + + if ((nc = mg_connect_opt(mgr, conn_addr, MG_CB(ev_handler, user_data), + opts)) != NULL) { + mg_set_protocol_http_websocket(nc); + } + +out: + if (conn_addr != NULL && conn_addr != conn_addr_buf) MG_FREE(conn_addr); + return nc; +} + +struct mg_connection *mg_connect_http_opt( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + struct mg_connect_opts opts, const char *url, const char *extra_headers, + const char *post_data) { + struct mg_str user = MG_NULL_STR, null_str = MG_NULL_STR; + struct mg_str host = MG_NULL_STR, path = MG_NULL_STR; + struct mbuf auth; + struct mg_connection *nc = + mg_connect_http_base(mgr, MG_CB(ev_handler, user_data), opts, "http", + NULL, "https", NULL, url, &path, &user, &host); + + if (nc == NULL) { + return NULL; + } + + mbuf_init(&auth, 0); + if (user.len > 0) { + mg_basic_auth_header(user, null_str, &auth); + } + + if (post_data == NULL) post_data = ""; + if (extra_headers == NULL) extra_headers = ""; + if (path.len == 0) path = mg_mk_str("/"); + if (host.len == 0) host = mg_mk_str(""); + + mg_printf(nc, "%s %.*s HTTP/1.1\r\nHost: %.*s\r\nContent-Length: %" SIZE_T_FMT + "\r\n%.*s%s\r\n%s", + (post_data[0] == '\0' ? "GET" : "POST"), (int) path.len, path.p, + (int) (path.p - host.p), host.p, strlen(post_data), (int) auth.len, + (auth.buf == NULL ? "" : auth.buf), extra_headers, post_data); + + mbuf_free(&auth); + return nc; +} + +struct mg_connection *mg_connect_http( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + const char *url, const char *extra_headers, const char *post_data) { + struct mg_connect_opts opts; + memset(&opts, 0, sizeof(opts)); + return mg_connect_http_opt(mgr, MG_CB(ev_handler, user_data), opts, url, + extra_headers, post_data); +} + +size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name, + size_t var_name_len, char *file_name, + size_t file_name_len, const char **data, + size_t *data_len) { + static const char cd[] = "Content-Disposition: "; + size_t hl, bl, n, ll, pos, cdl = sizeof(cd) - 1; + int shl; + + if (buf == NULL || buf_len <= 0) return 0; + if ((shl = mg_http_get_request_len(buf, buf_len)) <= 0) return 0; + hl = shl; + if (buf[0] != '-' || buf[1] != '-' || buf[2] == '\n') return 0; + + /* Get boundary length */ + bl = mg_get_line_len(buf, buf_len); + + /* Loop through headers, fetch variable name and file name */ + var_name[0] = file_name[0] = '\0'; + for (n = bl; (ll = mg_get_line_len(buf + n, hl - n)) > 0; n += ll) { + if (mg_ncasecmp(cd, buf + n, cdl) == 0) { + struct mg_str header; + header.p = buf + n + cdl; + header.len = ll - (cdl + 2); + { + char *var_name2 = var_name; + mg_http_parse_header2(&header, "name", &var_name2, var_name_len); + /* TODO: handle reallocated buffer correctly */ + if (var_name2 != var_name) { + MG_FREE(var_name2); + var_name[0] = '\0'; + } + } + { + char *file_name2 = file_name; + mg_http_parse_header2(&header, "filename", &file_name2, file_name_len); + /* TODO: handle reallocated buffer correctly */ + if (file_name2 != file_name) { + MG_FREE(file_name2); + file_name[0] = '\0'; + } + } + } + } + + /* Scan through the body, search for terminating boundary */ + for (pos = hl; pos + (bl - 2) < buf_len; pos++) { + if (buf[pos] == '-' && !strncmp(buf, &buf[pos], bl - 2)) { + if (data_len != NULL) *data_len = (pos - 2) - hl; + if (data != NULL) *data = buf + hl; + return pos; + } + } + + return 0; +} + +void mg_register_http_endpoint_opt(struct mg_connection *nc, + const char *uri_path, + mg_event_handler_t handler, + struct mg_http_endpoint_opts opts) { + struct mg_http_proto_data *pd = NULL; + struct mg_http_endpoint *new_ep = NULL; + + if (nc == NULL) return; + new_ep = (struct mg_http_endpoint *) MG_CALLOC(1, sizeof(*new_ep)); + if (new_ep == NULL) return; + + pd = mg_http_get_proto_data(nc); + new_ep->uri_pattern = mg_strdup(mg_mk_str(uri_path)); + if (opts.auth_domain != NULL && opts.auth_file != NULL) { + new_ep->auth_domain = strdup(opts.auth_domain); + new_ep->auth_file = strdup(opts.auth_file); + } + new_ep->handler = handler; +#if MG_ENABLE_CALLBACK_USERDATA + new_ep->user_data = opts.user_data; +#endif + new_ep->next = pd->endpoints; + pd->endpoints = new_ep; +} + +static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev, + struct http_message *hm) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + void *user_data = nc->user_data; + + if (ev == MG_EV_HTTP_REQUEST +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + || ev == MG_EV_HTTP_MULTIPART_REQUEST +#endif + ) { + struct mg_http_endpoint *ep = + mg_http_get_endpoint_handler(nc->listener, &hm->uri); + if (ep != NULL) { +#if MG_ENABLE_FILESYSTEM && !MG_DISABLE_HTTP_DIGEST_AUTH + if (!mg_http_is_authorized(hm, hm->uri, ep->auth_domain, ep->auth_file, + MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE)) { + mg_http_send_digest_auth_request(nc, ep->auth_domain); + return; + } +#endif + pd->endpoint_handler = ep->handler; +#if MG_ENABLE_CALLBACK_USERDATA + user_data = ep->user_data; +#endif + } + } + mg_call(nc, pd->endpoint_handler ? pd->endpoint_handler : nc->handler, + user_data, ev, hm); +} + +void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path, + MG_CB(mg_event_handler_t handler, + void *user_data)) { + struct mg_http_endpoint_opts opts; + memset(&opts, 0, sizeof(opts)); +#if MG_ENABLE_CALLBACK_USERDATA + opts.user_data = user_data; +#endif + mg_register_http_endpoint_opt(nc, uri_path, handler, opts); +} + +#endif /* MG_ENABLE_HTTP */ diff --git a/src/mongoose-6.11/src/mg_http.h b/src/mongoose-6.11/src/mg_http.h new file mode 100644 index 0000000..c89d38f --- /dev/null +++ b/src/mongoose-6.11/src/mg_http.h @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* + * === Common API reference + */ + +#ifndef CS_MONGOOSE_SRC_HTTP_H_ +#define CS_MONGOOSE_SRC_HTTP_H_ + +#if MG_ENABLE_HTTP + +#include "mg_net.h" +#include "common/mg_str.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MG_MAX_HTTP_HEADERS +#define MG_MAX_HTTP_HEADERS 20 +#endif + +#ifndef MG_MAX_HTTP_REQUEST_SIZE +#define MG_MAX_HTTP_REQUEST_SIZE 1024 +#endif + +#ifndef MG_MAX_HTTP_SEND_MBUF +#define MG_MAX_HTTP_SEND_MBUF 1024 +#endif + +#ifndef MG_CGI_ENVIRONMENT_SIZE +#define MG_CGI_ENVIRONMENT_SIZE 8192 +#endif + +/* HTTP message */ +struct http_message { + struct mg_str message; /* Whole message: request line + headers + body */ + struct mg_str body; /* Message body. 0-length for requests with no body */ + + /* HTTP Request line (or HTTP response line) */ + struct mg_str method; /* "GET" */ + struct mg_str uri; /* "/my_file.html" */ + struct mg_str proto; /* "HTTP/1.1" -- for both request and response */ + + /* For responses, code and response status message are set */ + int resp_code; + struct mg_str resp_status_msg; + + /* + * Query-string part of the URI. For example, for HTTP request + * GET /foo/bar?param1=val1¶m2=val2 + * | uri | query_string | + * + * Note that question mark character doesn't belong neither to the uri, + * nor to the query_string + */ + struct mg_str query_string; + + /* Headers */ + struct mg_str header_names[MG_MAX_HTTP_HEADERS]; + struct mg_str header_values[MG_MAX_HTTP_HEADERS]; +}; + +#if MG_ENABLE_HTTP_WEBSOCKET +/* WebSocket message */ +struct websocket_message { + unsigned char *data; + size_t size; + unsigned char flags; +}; +#endif + +/* HTTP multipart part */ +struct mg_http_multipart_part { + const char *file_name; + const char *var_name; + struct mg_str data; + int status; /* <0 on error */ + void *user_data; +}; + +/* SSI call context */ +struct mg_ssi_call_ctx { + struct http_message *req; /* The request being processed. */ + struct mg_str file; /* Filesystem path of the file being processed. */ + struct mg_str arg; /* The argument passed to the tag: . */ +}; + +/* HTTP and websocket events. void *ev_data is described in a comment. */ +#define MG_EV_HTTP_REQUEST 100 /* struct http_message * */ +#define MG_EV_HTTP_REPLY 101 /* struct http_message * */ +#define MG_EV_HTTP_CHUNK 102 /* struct http_message * */ +#define MG_EV_SSI_CALL 105 /* char * */ +#define MG_EV_SSI_CALL_CTX 106 /* struct mg_ssi_call_ctx * */ + +#if MG_ENABLE_HTTP_WEBSOCKET +#define MG_EV_WEBSOCKET_HANDSHAKE_REQUEST 111 /* struct http_message * */ +#define MG_EV_WEBSOCKET_HANDSHAKE_DONE 112 /* NULL */ +#define MG_EV_WEBSOCKET_FRAME 113 /* struct websocket_message * */ +#define MG_EV_WEBSOCKET_CONTROL_FRAME 114 /* struct websocket_message * */ +#endif + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART +#define MG_EV_HTTP_MULTIPART_REQUEST 121 /* struct http_message */ +#define MG_EV_HTTP_PART_BEGIN 122 /* struct mg_http_multipart_part */ +#define MG_EV_HTTP_PART_DATA 123 /* struct mg_http_multipart_part */ +#define MG_EV_HTTP_PART_END 124 /* struct mg_http_multipart_part */ +/* struct mg_http_multipart_part */ +#define MG_EV_HTTP_MULTIPART_REQUEST_END 125 +#endif + +/* + * Attaches a built-in HTTP event handler to the given connection. + * The user-defined event handler will receive following extra events: + * + * - MG_EV_HTTP_REQUEST: HTTP request has arrived. Parsed HTTP request + * is passed as + * `struct http_message` through the handler's `void *ev_data` pointer. + * - MG_EV_HTTP_REPLY: The HTTP reply has arrived. The parsed HTTP reply is + * passed as `struct http_message` through the handler's `void *ev_data` + * pointer. + * - MG_EV_HTTP_CHUNK: The HTTP chunked-encoding chunk has arrived. + * The parsed HTTP reply is passed as `struct http_message` through the + * handler's `void *ev_data` pointer. `http_message::body` would contain + * incomplete, reassembled HTTP body. + * It will grow with every new chunk that arrives, and it can + * potentially consume a lot of memory. An event handler may process + * the body as chunks are coming, and signal Mongoose to delete processed + * body by setting `MG_F_DELETE_CHUNK` in `mg_connection::flags`. When + * the last zero chunk is received, + * Mongoose sends `MG_EV_HTTP_REPLY` event with + * full reassembled body (if handler did not signal to delete chunks) or + * with empty body (if handler did signal to delete chunks). + * - MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: server has received the WebSocket + * handshake request. `ev_data` contains parsed HTTP request. + * - MG_EV_WEBSOCKET_HANDSHAKE_DONE: server has completed the WebSocket + * handshake. `ev_data` is `NULL`. + * - MG_EV_WEBSOCKET_FRAME: new WebSocket frame has arrived. `ev_data` is + * `struct websocket_message *` + * + * When compiled with MG_ENABLE_HTTP_STREAMING_MULTIPART, Mongoose parses + * multipart requests and splits them into separate events: + * - MG_EV_HTTP_MULTIPART_REQUEST: Start of the request. + * This event is sent before body is parsed. After this, the user + * should expect a sequence of PART_BEGIN/DATA/END requests. + * This is also the last time when headers and other request fields are + * accessible. + * - MG_EV_HTTP_PART_BEGIN: Start of a part of a multipart message. + * Argument: mg_http_multipart_part with var_name and file_name set + * (if present). No data is passed in this message. + * - MG_EV_HTTP_PART_DATA: new portion of data from the multipart message. + * Argument: mg_http_multipart_part. var_name and file_name are preserved, + * data is available in mg_http_multipart_part.data. + * - MG_EV_HTTP_PART_END: End of the current part. var_name, file_name are + * the same, no data in the message. If status is 0, then the part is + * properly terminated with a boundary, status < 0 means that connection + * was terminated. + * - MG_EV_HTTP_MULTIPART_REQUEST_END: End of the multipart request. + * Argument: mg_http_multipart_part, var_name and file_name are NULL, + * status = 0 means request was properly closed, < 0 means connection + * was terminated (note: in this case both PART_END and REQUEST_END are + * delivered). + */ +void mg_set_protocol_http_websocket(struct mg_connection *nc); + +#if MG_ENABLE_HTTP_WEBSOCKET +/* + * Send websocket handshake to the server. + * + * `nc` must be a valid connection, connected to a server. `uri` is an URI + * to fetch, extra_headers` is extra HTTP headers to send or `NULL`. + * + * This function is intended to be used by websocket client. + * + * Note that the Host header is mandatory in HTTP/1.1 and must be + * included in `extra_headers`. `mg_send_websocket_handshake2` offers + * a better API for that. + * + * Deprecated in favour of `mg_send_websocket_handshake2` + */ +void mg_send_websocket_handshake(struct mg_connection *nc, const char *uri, + const char *extra_headers); + +/* + * Send websocket handshake to the server. + * + * `nc` must be a valid connection, connected to a server. `uri` is an URI + * to fetch, `host` goes into the `Host` header, `protocol` goes into the + * `Sec-WebSocket-Proto` header (NULL to omit), extra_headers` is extra HTTP + * headers to send or `NULL`. + * + * This function is intended to be used by websocket client. + */ +void mg_send_websocket_handshake2(struct mg_connection *nc, const char *path, + const char *host, const char *protocol, + const char *extra_headers); + +/* Like mg_send_websocket_handshake2 but also passes basic auth header */ +void mg_send_websocket_handshake3(struct mg_connection *nc, const char *path, + const char *host, const char *protocol, + const char *extra_headers, const char *user, + const char *pass); + +/* Same as mg_send_websocket_handshake3 but with strings not necessarily + * NUL-temrinated */ +void mg_send_websocket_handshake3v(struct mg_connection *nc, + const struct mg_str path, + const struct mg_str host, + const struct mg_str protocol, + const struct mg_str extra_headers, + const struct mg_str user, + const struct mg_str pass); + +/* + * Helper function that creates an outbound WebSocket connection. + * + * `url` is a URL to connect to. It must be properly URL-encoded, e.g. have + * no spaces, etc. By default, `mg_connect_ws()` sends Connection and + * Host headers. `extra_headers` is an extra HTTP header to send, e.g. + * `"User-Agent: my-app\r\n"`. + * If `protocol` is not NULL, then a `Sec-WebSocket-Protocol` header is sent. + * + * Examples: + * + * ```c + * nc1 = mg_connect_ws(mgr, ev_handler_1, "ws://echo.websocket.org", NULL, + * NULL); + * nc2 = mg_connect_ws(mgr, ev_handler_1, "wss://echo.websocket.org", NULL, + * NULL); + * nc3 = mg_connect_ws(mgr, ev_handler_1, "ws://api.cesanta.com", + * "clubby.cesanta.com", NULL); + * ``` + */ +struct mg_connection *mg_connect_ws(struct mg_mgr *mgr, + MG_CB(mg_event_handler_t event_handler, + void *user_data), + const char *url, const char *protocol, + const char *extra_headers); + +/* + * Helper function that creates an outbound WebSocket connection + * + * Mostly identical to `mg_connect_ws`, but allows to provide extra parameters + * (for example, SSL parameters) + */ +struct mg_connection *mg_connect_ws_opt( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + struct mg_connect_opts opts, const char *url, const char *protocol, + const char *extra_headers); + +/* + * Send WebSocket frame to the remote end. + * + * `op_and_flags` specifies the frame's type. It's one of: + * + * - WEBSOCKET_OP_CONTINUE + * - WEBSOCKET_OP_TEXT + * - WEBSOCKET_OP_BINARY + * - WEBSOCKET_OP_CLOSE + * - WEBSOCKET_OP_PING + * - WEBSOCKET_OP_PONG + * + * Orred with one of the flags: + * + * - WEBSOCKET_DONT_FIN: Don't set the FIN flag on the frame to be sent. + * + * `data` and `data_len` contain frame data. + */ +void mg_send_websocket_frame(struct mg_connection *nc, int op_and_flags, + const void *data, size_t data_len); + +/* + * Like `mg_send_websocket_frame()`, but composes a single frame from multiple + * buffers. + */ +void mg_send_websocket_framev(struct mg_connection *nc, int op_and_flags, + const struct mg_str *strings, int num_strings); + +/* + * Sends WebSocket frame to the remote end. + * + * Like `mg_send_websocket_frame()`, but allows to create formatted messages + * with `printf()`-like semantics. + */ +void mg_printf_websocket_frame(struct mg_connection *nc, int op_and_flags, + const char *fmt, ...); + +/* Websocket opcodes, from http://tools.ietf.org/html/rfc6455 */ +#define WEBSOCKET_OP_CONTINUE 0 +#define WEBSOCKET_OP_TEXT 1 +#define WEBSOCKET_OP_BINARY 2 +#define WEBSOCKET_OP_CLOSE 8 +#define WEBSOCKET_OP_PING 9 +#define WEBSOCKET_OP_PONG 10 + +/* + * If set causes the FIN flag to not be set on outbound + * frames. This enables sending multiple fragments of a single + * logical message. + * + * The WebSocket protocol mandates that if the FIN flag of a data + * frame is not set, the next frame must be a WEBSOCKET_OP_CONTINUE. + * The last frame must have the FIN bit set. + * + * Note that mongoose will automatically defragment incoming messages, + * so this flag is used only on outbound messages. + */ +#define WEBSOCKET_DONT_FIN 0x100 + +#endif /* MG_ENABLE_HTTP_WEBSOCKET */ + +/* + * Decodes a URL-encoded string. + * + * Source string is specified by (`src`, `src_len`), and destination is + * (`dst`, `dst_len`). If `is_form_url_encoded` is non-zero, then + * `+` character is decoded as a blank space character. This function + * guarantees to NUL-terminate the destination. If destination is too small, + * then the source string is partially decoded and `-1` is returned. + *Otherwise, + * a length of the decoded string is returned, not counting final NUL. + */ +int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, + int is_form_url_encoded); + +extern void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest); +extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest); + +/* + * Flags for `mg_http_is_authorized()`. + */ +#define MG_AUTH_FLAG_IS_DIRECTORY (1 << 0) +#define MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE (1 << 1) +#define MG_AUTH_FLAG_ALLOW_MISSING_FILE (1 << 2) + +/* + * Checks whether an http request is authorized. `domain` is the authentication + * realm, `passwords_file` is a htdigest file (can be created e.g. with + * `htdigest` utility). If either `domain` or `passwords_file` is NULL, this + * function always returns 1; otherwise checks the authentication in the + * http request and returns 1 only if there is a match; 0 otherwise. + */ +int mg_http_is_authorized(struct http_message *hm, struct mg_str path, + const char *domain, const char *passwords_file, + int flags); + +/* + * Sends 401 Unauthorized response. + */ +void mg_http_send_digest_auth_request(struct mg_connection *c, + const char *domain); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MG_ENABLE_HTTP */ + +#endif /* CS_MONGOOSE_SRC_HTTP_H_ */ diff --git a/src/mongoose-6.11/src/mg_http_cgi.c b/src/mongoose-6.11/src/mg_http_cgi.c new file mode 100644 index 0000000..437c93c --- /dev/null +++ b/src/mongoose-6.11/src/mg_http_cgi.c @@ -0,0 +1,514 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef _WIN32 +#include +#endif + +#if MG_ENABLE_HTTP && MG_ENABLE_HTTP_CGI + +#ifndef MG_MAX_CGI_ENVIR_VARS +#define MG_MAX_CGI_ENVIR_VARS 64 +#endif + +#ifndef MG_ENV_EXPORT_TO_CGI +#define MG_ENV_EXPORT_TO_CGI "MONGOOSE_CGI" +#endif + +#define MG_F_HTTP_CGI_PARSE_HEADERS MG_F_USER_1 + +/* + * This structure helps to create an environment for the spawned CGI program. + * Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings, + * last element must be NULL. + * However, on Windows there is a requirement that all these VARIABLE=VALUE\0 + * strings must reside in a contiguous buffer. The end of the buffer is + * marked by two '\0' characters. + * We satisfy both worlds: we create an envp array (which is vars), all + * entries are actually pointers inside buf. + */ +struct mg_cgi_env_block { + struct mg_connection *nc; + char buf[MG_CGI_ENVIRONMENT_SIZE]; /* Environment buffer */ + const char *vars[MG_MAX_CGI_ENVIR_VARS]; /* char *envp[] */ + int len; /* Space taken */ + int nvars; /* Number of variables in envp[] */ +}; + +#ifdef _WIN32 +struct mg_threadparam { + sock_t s; + HANDLE hPipe; +}; + +static int mg_wait_until_ready(sock_t sock, int for_read) { + fd_set set; + FD_ZERO(&set); + FD_SET(sock, &set); + return select(sock + 1, for_read ? &set : 0, for_read ? 0 : &set, 0, 0) == 1; +} + +static void *mg_push_to_stdin(void *arg) { + struct mg_threadparam *tp = (struct mg_threadparam *) arg; + int n, sent, stop = 0; + DWORD k; + char buf[BUFSIZ]; + + while (!stop && mg_wait_until_ready(tp->s, 1) && + (n = recv(tp->s, buf, sizeof(buf), 0)) > 0) { + if (n == -1 && GetLastError() == WSAEWOULDBLOCK) continue; + for (sent = 0; !stop && sent < n; sent += k) { + if (!WriteFile(tp->hPipe, buf + sent, n - sent, &k, 0)) stop = 1; + } + } + DBG(("%s", "FORWARED EVERYTHING TO CGI")); + CloseHandle(tp->hPipe); + MG_FREE(tp); + return NULL; +} + +static void *mg_pull_from_stdout(void *arg) { + struct mg_threadparam *tp = (struct mg_threadparam *) arg; + int k = 0, stop = 0; + DWORD n, sent; + char buf[BUFSIZ]; + + while (!stop && ReadFile(tp->hPipe, buf, sizeof(buf), &n, NULL)) { + for (sent = 0; !stop && sent < n; sent += k) { + if (mg_wait_until_ready(tp->s, 0) && + (k = send(tp->s, buf + sent, n - sent, 0)) <= 0) + stop = 1; + } + } + DBG(("%s", "EOF FROM CGI")); + CloseHandle(tp->hPipe); + shutdown(tp->s, 2); // Without this, IO thread may get truncated data + closesocket(tp->s); + MG_FREE(tp); + return NULL; +} + +static void mg_spawn_stdio_thread(sock_t sock, HANDLE hPipe, + void *(*func)(void *)) { + struct mg_threadparam *tp = (struct mg_threadparam *) MG_MALLOC(sizeof(*tp)); + if (tp != NULL) { + tp->s = sock; + tp->hPipe = hPipe; + mg_start_thread(func, tp); + } +} + +static void mg_abs_path(const char *utf8_path, char *abs_path, size_t len) { + wchar_t buf[MG_MAX_PATH], buf2[MG_MAX_PATH]; + to_wchar(utf8_path, buf, ARRAY_SIZE(buf)); + GetFullPathNameW(buf, ARRAY_SIZE(buf2), buf2, NULL); + WideCharToMultiByte(CP_UTF8, 0, buf2, wcslen(buf2) + 1, abs_path, len, 0, 0); +} + +static int mg_start_process(const char *interp, const char *cmd, + const char *env, const char *envp[], + const char *dir, sock_t sock) { + STARTUPINFOW si; + PROCESS_INFORMATION pi; + HANDLE a[2], b[2], me = GetCurrentProcess(); + wchar_t wcmd[MG_MAX_PATH], full_dir[MG_MAX_PATH]; + char buf[MG_MAX_PATH], buf2[MG_MAX_PATH], buf5[MG_MAX_PATH], + buf4[MG_MAX_PATH], cmdline[MG_MAX_PATH]; + DWORD flags = DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS; + FILE *fp; + + memset(&si, 0, sizeof(si)); + memset(&pi, 0, sizeof(pi)); + + si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + + CreatePipe(&a[0], &a[1], NULL, 0); + CreatePipe(&b[0], &b[1], NULL, 0); + DuplicateHandle(me, a[0], me, &si.hStdInput, 0, TRUE, flags); + DuplicateHandle(me, b[1], me, &si.hStdOutput, 0, TRUE, flags); + + if (interp == NULL && (fp = mg_fopen(cmd, "r")) != NULL) { + buf[0] = buf[1] = '\0'; + fgets(buf, sizeof(buf), fp); + buf[sizeof(buf) - 1] = '\0'; + if (buf[0] == '#' && buf[1] == '!') { + interp = buf + 2; + /* Trim leading spaces: https://github.com/cesanta/mongoose/issues/489 */ + while (*interp != '\0' && isspace(*(unsigned char *) interp)) { + interp++; + } + } + fclose(fp); + } + + snprintf(buf, sizeof(buf), "%s/%s", dir, cmd); + mg_abs_path(buf, buf2, ARRAY_SIZE(buf2)); + + mg_abs_path(dir, buf5, ARRAY_SIZE(buf5)); + to_wchar(dir, full_dir, ARRAY_SIZE(full_dir)); + + if (interp != NULL) { + mg_abs_path(interp, buf4, ARRAY_SIZE(buf4)); + snprintf(cmdline, sizeof(cmdline), "%s \"%s\"", buf4, buf2); + } else { + snprintf(cmdline, sizeof(cmdline), "\"%s\"", buf2); + } + to_wchar(cmdline, wcmd, ARRAY_SIZE(wcmd)); + + if (CreateProcessW(NULL, wcmd, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, + (void *) env, full_dir, &si, &pi) != 0) { + mg_spawn_stdio_thread(sock, a[1], mg_push_to_stdin); + mg_spawn_stdio_thread(sock, b[0], mg_pull_from_stdout); + + CloseHandle(si.hStdOutput); + CloseHandle(si.hStdInput); + + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + } else { + CloseHandle(a[1]); + CloseHandle(b[0]); + closesocket(sock); + } + DBG(("CGI command: [%ls] -> %p", wcmd, pi.hProcess)); + + /* Not closing a[0] and b[1] because we've used DUPLICATE_CLOSE_SOURCE */ + (void) envp; + return (pi.hProcess != NULL); +} +#else +static int mg_start_process(const char *interp, const char *cmd, + const char *env, const char *envp[], + const char *dir, sock_t sock) { + char buf[500]; + pid_t pid = fork(); + (void) env; + + if (pid == 0) { + /* + * In Linux `chdir` declared with `warn_unused_result` attribute + * To shutup compiler we have yo use result in some way + */ + int tmp = chdir(dir); + (void) tmp; + (void) dup2(sock, 0); + (void) dup2(sock, 1); + closesocket(sock); + + /* + * After exec, all signal handlers are restored to their default values, + * with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's + * implementation, SIGCHLD's handler will leave unchanged after exec + * if it was set to be ignored. Restore it to default action. + */ + signal(SIGCHLD, SIG_DFL); + + if (interp == NULL) { + execle(cmd, cmd, (char *) 0, envp); /* (char *) 0 to squash warning */ + } else { + execle(interp, interp, cmd, (char *) 0, envp); + } + snprintf(buf, sizeof(buf), + "Status: 500\r\n\r\n" + "500 Server Error: %s%s%s: %s", + interp == NULL ? "" : interp, interp == NULL ? "" : " ", cmd, + strerror(errno)); + send(1, buf, strlen(buf), 0); + _exit(EXIT_FAILURE); /* exec call failed */ + } + + return (pid != 0); +} +#endif /* _WIN32 */ + +/* + * Append VARIABLE=VALUE\0 string to the buffer, and add a respective + * pointer into the vars array. + */ +static char *mg_addenv(struct mg_cgi_env_block *block, const char *fmt, ...) { + int n, space; + char *added = block->buf + block->len; + va_list ap; + + /* Calculate how much space is left in the buffer */ + space = sizeof(block->buf) - (block->len + 2); + if (space > 0) { + /* Copy VARIABLE=VALUE\0 string into the free space */ + va_start(ap, fmt); + n = vsnprintf(added, (size_t) space, fmt, ap); + va_end(ap); + + /* Make sure we do not overflow buffer and the envp array */ + if (n > 0 && n + 1 < space && + block->nvars < (int) ARRAY_SIZE(block->vars) - 2) { + /* Append a pointer to the added string into the envp array */ + block->vars[block->nvars++] = added; + /* Bump up used length counter. Include \0 terminator */ + block->len += n + 1; + } + } + + return added; +} + +static void mg_addenv2(struct mg_cgi_env_block *blk, const char *name) { + const char *s; + if ((s = getenv(name)) != NULL) mg_addenv(blk, "%s=%s", name, s); +} + +static void mg_prepare_cgi_environment(struct mg_connection *nc, + const char *prog, + const struct mg_str *path_info, + const struct http_message *hm, + const struct mg_serve_http_opts *opts, + struct mg_cgi_env_block *blk) { + const char *s; + struct mg_str *h; + char *p; + size_t i; + char buf[100]; + size_t path_info_len = path_info != NULL ? path_info->len : 0; + + blk->len = blk->nvars = 0; + blk->nc = nc; + + if ((s = getenv("SERVER_NAME")) != NULL) { + mg_addenv(blk, "SERVER_NAME=%s", s); + } else { + mg_sock_to_str(nc->sock, buf, sizeof(buf), 3); + mg_addenv(blk, "SERVER_NAME=%s", buf); + } + mg_addenv(blk, "SERVER_ROOT=%s", opts->document_root); + mg_addenv(blk, "DOCUMENT_ROOT=%s", opts->document_root); + mg_addenv(blk, "SERVER_SOFTWARE=%s/%s", "Mongoose", MG_VERSION); + + /* Prepare the environment block */ + mg_addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1"); + mg_addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1"); + mg_addenv(blk, "%s", "REDIRECT_STATUS=200"); /* For PHP */ + + mg_addenv(blk, "REQUEST_METHOD=%.*s", (int) hm->method.len, hm->method.p); + + mg_addenv(blk, "REQUEST_URI=%.*s%s%.*s", (int) hm->uri.len, hm->uri.p, + hm->query_string.len == 0 ? "" : "?", (int) hm->query_string.len, + hm->query_string.p); + + mg_conn_addr_to_str(nc, buf, sizeof(buf), + MG_SOCK_STRINGIFY_REMOTE | MG_SOCK_STRINGIFY_IP); + mg_addenv(blk, "REMOTE_ADDR=%s", buf); + mg_conn_addr_to_str(nc, buf, sizeof(buf), MG_SOCK_STRINGIFY_PORT); + mg_addenv(blk, "SERVER_PORT=%s", buf); + + s = hm->uri.p + hm->uri.len - path_info_len - 1; + if (*s == '/') { + const char *base_name = strrchr(prog, DIRSEP); + mg_addenv(blk, "SCRIPT_NAME=%.*s/%s", (int) (s - hm->uri.p), hm->uri.p, + (base_name != NULL ? base_name + 1 : prog)); + } else { + mg_addenv(blk, "SCRIPT_NAME=%.*s", (int) (s - hm->uri.p + 1), hm->uri.p); + } + mg_addenv(blk, "SCRIPT_FILENAME=%s", prog); + + if (path_info != NULL && path_info->len > 0) { + mg_addenv(blk, "PATH_INFO=%.*s", (int) path_info->len, path_info->p); + /* Not really translated... */ + mg_addenv(blk, "PATH_TRANSLATED=%.*s", (int) path_info->len, path_info->p); + } + +#if MG_ENABLE_SSL + mg_addenv(blk, "HTTPS=%s", (nc->flags & MG_F_SSL ? "on" : "off")); +#else + mg_addenv(blk, "HTTPS=off"); +#endif + + if ((h = mg_get_http_header((struct http_message *) hm, "Content-Type")) != + NULL) { + mg_addenv(blk, "CONTENT_TYPE=%.*s", (int) h->len, h->p); + } + + if (hm->query_string.len > 0) { + mg_addenv(blk, "QUERY_STRING=%.*s", (int) hm->query_string.len, + hm->query_string.p); + } + + if ((h = mg_get_http_header((struct http_message *) hm, "Content-Length")) != + NULL) { + mg_addenv(blk, "CONTENT_LENGTH=%.*s", (int) h->len, h->p); + } + + mg_addenv2(blk, "PATH"); + mg_addenv2(blk, "TMP"); + mg_addenv2(blk, "TEMP"); + mg_addenv2(blk, "TMPDIR"); + mg_addenv2(blk, "PERLLIB"); + mg_addenv2(blk, MG_ENV_EXPORT_TO_CGI); + +#ifdef _WIN32 + mg_addenv2(blk, "COMSPEC"); + mg_addenv2(blk, "SYSTEMROOT"); + mg_addenv2(blk, "SystemDrive"); + mg_addenv2(blk, "ProgramFiles"); + mg_addenv2(blk, "ProgramFiles(x86)"); + mg_addenv2(blk, "CommonProgramFiles(x86)"); +#else + mg_addenv2(blk, "LD_LIBRARY_PATH"); +#endif /* _WIN32 */ + + /* Add all headers as HTTP_* variables */ + for (i = 0; hm->header_names[i].len > 0; i++) { + p = mg_addenv(blk, "HTTP_%.*s=%.*s", (int) hm->header_names[i].len, + hm->header_names[i].p, (int) hm->header_values[i].len, + hm->header_values[i].p); + + /* Convert variable name into uppercase, and change - to _ */ + for (; *p != '=' && *p != '\0'; p++) { + if (*p == '-') *p = '_'; + *p = (char) toupper(*(unsigned char *) p); + } + } + + blk->vars[blk->nvars++] = NULL; + blk->buf[blk->len++] = '\0'; +} + +static void mg_cgi_ev_handler(struct mg_connection *cgi_nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { +#if !MG_ENABLE_CALLBACK_USERDATA + void *user_data = cgi_nc->user_data; +#endif + struct mg_connection *nc = (struct mg_connection *) user_data; + (void) ev_data; + + if (nc == NULL) { + /* The corresponding network connection was closed. */ + cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return; + } + + switch (ev) { + case MG_EV_RECV: + /* + * CGI script does not output reply line, like "HTTP/1.1 CODE XXXXX\n" + * It outputs headers, then body. Headers might include "Status" + * header, which changes CODE, and it might include "Location" header + * which changes CODE to 302. + * + * Therefore we do not send the output from the CGI script to the user + * until all CGI headers are received. + * + * Here we parse the output from the CGI script, and if all headers has + * been received, send appropriate reply line, and forward all + * received headers to the client. + */ + if (nc->flags & MG_F_HTTP_CGI_PARSE_HEADERS) { + struct mbuf *io = &cgi_nc->recv_mbuf; + int len = mg_http_get_request_len(io->buf, io->len); + + if (len == 0) break; + if (len < 0 || io->len > MG_MAX_HTTP_REQUEST_SIZE) { + cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY; + mg_http_send_error(nc, 500, "Bad headers"); + } else { + struct http_message hm; + struct mg_str *h; + mg_http_parse_headers(io->buf, io->buf + io->len, io->len, &hm); + if (mg_get_http_header(&hm, "Location") != NULL) { + mg_printf(nc, "%s", "HTTP/1.1 302 Moved\r\n"); + } else if ((h = mg_get_http_header(&hm, "Status")) != NULL) { + mg_printf(nc, "HTTP/1.1 %.*s\r\n", (int) h->len, h->p); + } else { + mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\n"); + } + } + nc->flags &= ~MG_F_HTTP_CGI_PARSE_HEADERS; + } + if (!(nc->flags & MG_F_HTTP_CGI_PARSE_HEADERS)) { + mg_forward(cgi_nc, nc); + } + break; + case MG_EV_CLOSE: + DBG(("%p CLOSE", cgi_nc)); + mg_http_free_proto_data_cgi(&mg_http_get_proto_data(nc)->cgi); + nc->flags |= MG_F_SEND_AND_CLOSE; + break; + } +} + +MG_INTERNAL void mg_handle_cgi(struct mg_connection *nc, const char *prog, + const struct mg_str *path_info, + const struct http_message *hm, + const struct mg_serve_http_opts *opts) { + struct mg_cgi_env_block blk; + char dir[MG_MAX_PATH]; + const char *p; + sock_t fds[2]; + + DBG(("%p [%s]", nc, prog)); + mg_prepare_cgi_environment(nc, prog, path_info, hm, opts, &blk); + /* + * CGI must be executed in its own directory. 'dir' must point to the + * directory containing executable program, 'p' must point to the + * executable program name relative to 'dir'. + */ + if ((p = strrchr(prog, DIRSEP)) == NULL) { + snprintf(dir, sizeof(dir), "%s", "."); + } else { + snprintf(dir, sizeof(dir), "%.*s", (int) (p - prog), prog); + prog = p + 1; + } + + if (!mg_socketpair(fds, SOCK_STREAM)) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return; + } + +#ifndef _WIN32 + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigaction(SIGCHLD, &sa, NULL); +#endif + + if (mg_start_process(opts->cgi_interpreter, prog, blk.buf, blk.vars, dir, + fds[1]) != 0) { + size_t n = nc->recv_mbuf.len - (hm->message.len - hm->body.len); + struct mg_connection *cgi_nc = + mg_add_sock(nc->mgr, fds[0], mg_cgi_ev_handler MG_UD_ARG(nc)); + struct mg_http_proto_data *cgi_pd = mg_http_get_proto_data(nc); + cgi_pd->cgi.cgi_nc = cgi_nc; +#if !MG_ENABLE_CALLBACK_USERDATA + cgi_pd->cgi.cgi_nc->user_data = nc; +#endif + nc->flags |= MG_F_HTTP_CGI_PARSE_HEADERS; + /* Push POST data to the CGI */ + if (n > 0 && n < nc->recv_mbuf.len) { + mg_send(cgi_pd->cgi.cgi_nc, hm->body.p, n); + } + mbuf_remove(&nc->recv_mbuf, nc->recv_mbuf.len); + } else { + closesocket(fds[0]); + mg_http_send_error(nc, 500, "CGI failure"); + } + +#ifndef _WIN32 + closesocket(fds[1]); /* On Windows, CGI stdio thread closes that socket */ +#endif +} + +MG_INTERNAL void mg_http_free_proto_data_cgi(struct mg_http_proto_data_cgi *d) { + if (d == NULL) return; + if (d->cgi_nc != NULL) { + d->cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY; + d->cgi_nc->user_data = NULL; + } + memset(d, 0, sizeof(*d)); +} + +#endif /* MG_ENABLE_HTTP && MG_ENABLE_HTTP_CGI */ diff --git a/src/mongoose-6.11/src/mg_http_client.h b/src/mongoose-6.11/src/mg_http_client.h new file mode 100644 index 0000000..9597ec5 --- /dev/null +++ b/src/mongoose-6.11/src/mg_http_client.h @@ -0,0 +1,62 @@ +/* + * === Client API reference + */ + +#ifndef CS_MONGOOSE_SRC_HTTP_CLIENT_H_ +#define CS_MONGOOSE_SRC_HTTP_CLIENT_H_ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Helper function that creates an outbound HTTP connection. + * + * `url` is the URL to fetch. It must be properly URL-encoded, e.g. have + * no spaces, etc. By default, `mg_connect_http()` sends the Connection and + * Host headers. `extra_headers` is an extra HTTP header to send, e.g. + * `"User-Agent: my-app\r\n"`. + * If `post_data` is NULL, then a GET request is created. Otherwise, a POST + * request is created with the specified POST data. Note that if the data being + * posted is a form submission, the `Content-Type` header should be set + * accordingly (see example below). + * + * Examples: + * + * ```c + * nc1 = mg_connect_http(mgr, ev_handler_1, "http://www.google.com", NULL, + * NULL); + * nc2 = mg_connect_http(mgr, ev_handler_1, "https://github.com", NULL, NULL); + * nc3 = mg_connect_http( + * mgr, ev_handler_1, "my_server:8000/form_submit/", + * "Content-Type: application/x-www-form-urlencoded\r\n", + * "var_1=value_1&var_2=value_2"); + * ``` + */ +struct mg_connection *mg_connect_http( + struct mg_mgr *mgr, + MG_CB(mg_event_handler_t event_handler, void *user_data), const char *url, + const char *extra_headers, const char *post_data); + +/* + * Helper function that creates an outbound HTTP connection. + * + * Mostly identical to mg_connect_http, but allows you to provide extra + *parameters + * (for example, SSL parameters) + */ +struct mg_connection *mg_connect_http_opt( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + struct mg_connect_opts opts, const char *url, const char *extra_headers, + const char *post_data); + +/* Creates digest authentication header for a client request. */ +int mg_http_create_digest_auth_header(char *buf, size_t buf_len, + const char *method, const char *uri, + const char *auth_domain, const char *user, + const char *passwd, const char *nonce); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CS_MONGOOSE_SRC_HTTP_CLIENT_H_ */ diff --git a/src/mongoose-6.11/src/mg_http_server.h b/src/mongoose-6.11/src/mg_http_server.h new file mode 100644 index 0000000..c4dfee4 --- /dev/null +++ b/src/mongoose-6.11/src/mg_http_server.h @@ -0,0 +1,561 @@ +/* + * === Server API reference + */ + +#ifndef CS_MONGOOSE_SRC_HTTP_SERVER_H_ +#define CS_MONGOOSE_SRC_HTTP_SERVER_H_ + +#if MG_ENABLE_HTTP + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Parses a HTTP message. + * + * `is_req` should be set to 1 if parsing a request, 0 if reply. + * + * Returns the number of bytes parsed. If HTTP message is + * incomplete `0` is returned. On parse error, a negative number is returned. + */ +int mg_parse_http(const char *s, int n, struct http_message *hm, int is_req); + +/* + * Searches and returns the header `name` in parsed HTTP message `hm`. + * If header is not found, NULL is returned. Example: + * + * struct mg_str *host_hdr = mg_get_http_header(hm, "Host"); + */ +struct mg_str *mg_get_http_header(struct http_message *hm, const char *name); + +/* + * Parses the HTTP header `hdr`. Finds variable `var_name` and stores its value + * in the buffer `*buf`, `buf_size`. If the buffer size is not enough, + * allocates a buffer of required size and writes it to `*buf`, similar to + * asprintf(). The caller should always check whether the buffer was updated, + * and free it if so. + * + * This function is supposed to parse cookies, authentication headers, etc. + * Example (error handling omitted): + * + * char user_buf[20]; + * char *user = user_buf; + * struct mg_str *hdr = mg_get_http_header(hm, "Authorization"); + * mg_http_parse_header2(hdr, "username", &user, sizeof(user_buf)); + * // ... do something useful with user + * if (user != user_buf) { + * free(user); + * } + * + * Returns the length of the variable's value. If variable is not found, 0 is + * returned. + */ +int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf, + size_t buf_size); + +/* + * DEPRECATED: use mg_http_parse_header2() instead. + * + * Same as mg_http_parse_header2(), but takes buffer as a `char *` (instead of + * `char **`), and thus it cannot allocate a new buffer if the provided one + * is not enough, and just returns 0 in that case. + */ +int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf, + size_t buf_size) +#ifdef __GNUC__ + __attribute__((deprecated)); +#endif +; + +/* + * Gets and parses the Authorization: Basic header + * Returns -1 if no Authorization header is found, or if + * mg_parse_http_basic_auth + * fails parsing the resulting header. + */ +int mg_get_http_basic_auth(struct http_message *hm, char *user, size_t user_len, + char *pass, size_t pass_len); + +/* + * Parses the Authorization: Basic header + * Returns -1 iif the authorization type is not "Basic" or any other error such + * as incorrectly encoded base64 user password pair. + */ +int mg_parse_http_basic_auth(struct mg_str *hdr, char *user, size_t user_len, + char *pass, size_t pass_len); + +/* + * Parses the buffer `buf`, `buf_len` that contains multipart form data chunks. + * Stores the chunk name in a `var_name`, `var_name_len` buffer. + * If a chunk is an uploaded file, then `file_name`, `file_name_len` is + * filled with an uploaded file name. `chunk`, `chunk_len` + * points to the chunk data. + * + * Return: number of bytes to skip to the next chunk or 0 if there are + * no more chunks. + * + * Usage example: + * + * ```c + * static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { + * switch(ev) { + * case MG_EV_HTTP_REQUEST: { + * struct http_message *hm = (struct http_message *) ev_data; + * char var_name[100], file_name[100]; + * const char *chunk; + * size_t chunk_len, n1, n2; + * + * n1 = n2 = 0; + * while ((n2 = mg_parse_multipart(hm->body.p + n1, + * hm->body.len - n1, + * var_name, sizeof(var_name), + * file_name, sizeof(file_name), + * &chunk, &chunk_len)) > 0) { + * printf("var: %s, file_name: %s, size: %d, chunk: [%.*s]\n", + * var_name, file_name, (int) chunk_len, + * (int) chunk_len, chunk); + * n1 += n2; + * } + * } + * break; + * ``` + */ +size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name, + size_t var_name_len, char *file_name, + size_t file_name_len, const char **chunk, + size_t *chunk_len); + +/* + * Fetches a HTTP form variable. + * + * Fetches a variable `name` from a `buf` into a buffer specified by `dst`, + * `dst_len`. The destination is always zero-terminated. Returns the length of + * a fetched variable. If not found, 0 is returned. `buf` must be valid + * url-encoded buffer. If destination is too small or an error occured, + * negative number is returned. + */ +int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst, + size_t dst_len); + +#if MG_ENABLE_FILESYSTEM +/* + * This structure defines how `mg_serve_http()` works. + * Best practice is to set only required settings, and leave the rest as NULL. + */ +struct mg_serve_http_opts { + /* Path to web root directory */ + const char *document_root; + + /* List of index files. Default is "" */ + const char *index_files; + + /* + * Leave as NULL to disable authentication. + * To enable directory protection with authentication, set this to ".htpasswd" + * Then, creating ".htpasswd" file in any directory automatically protects + * it with digest authentication. + * Use `mongoose` web server binary, or `htdigest` Apache utility to + * create/manipulate passwords file. + * Make sure `auth_domain` is set to a valid domain name. + */ + const char *per_directory_auth_file; + + /* Authorization domain (domain name of this web server) */ + const char *auth_domain; + + /* + * Leave as NULL to disable authentication. + * Normally, only selected directories in the document root are protected. + * If absolutely every access to the web server needs to be authenticated, + * regardless of the URI, set this option to the path to the passwords file. + * Format of that file is the same as ".htpasswd" file. Make sure that file + * is located outside document root to prevent people fetching it. + */ + const char *global_auth_file; + + /* Set to "no" to disable directory listing. Enabled by default. */ + const char *enable_directory_listing; + + /* + * SSI files pattern. If not set, "**.shtml$|**.shtm$" is used. + * + * All files that match ssi_pattern are treated as SSI. + * + * Server Side Includes (SSI) is a simple interpreted server-side scripting + * language which is most commonly used to include the contents of a file + * into a web page. It can be useful when it is desirable to include a common + * piece of code throughout a website, for example, headers and footers. + * + * In order for a webpage to recognize an SSI-enabled HTML file, the + * filename should end with a special extension, by default the extension + * should be either .shtml or .shtm + * + * Unknown SSI directives are silently ignored by Mongoose. Currently, + * the following SSI directives are supported: + * <!--#include FILE_TO_INCLUDE --> + * <!--#exec "COMMAND_TO_EXECUTE" --> + * <!--#call COMMAND --> + * + * Note that <!--#include ...> directive supports three path + *specifications: + * + * <!--#include virtual="path" --> Path is relative to web server root + * <!--#include abspath="path" --> Path is absolute or relative to the + * web server working dir + * <!--#include file="path" -->, Path is relative to current document + * <!--#include "path" --> + * + * The include directive may be used to include the contents of a file or + * the result of running a CGI script. + * + * The exec directive is used to execute + * a command on a server, and show command's output. Example: + * + * <!--#exec "ls -l" --> + * + * The call directive is a way to invoke a C handler from the HTML page. + * On each occurence of <!--#call COMMAND OPTIONAL_PARAMS> directive, + * Mongoose calls a registered event handler with MG_EV_SSI_CALL event, + * and event parameter will point to the COMMAND OPTIONAL_PARAMS string. + * An event handler can output any text, for example by calling + * `mg_printf()`. This is a flexible way of generating a web page on + * server side by calling a C event handler. Example: + * + * <!--#call foo --> ... <!--#call bar --> + * + * In the event handler: + * case MG_EV_SSI_CALL: { + * const char *param = (const char *) ev_data; + * if (strcmp(param, "foo") == 0) { + * mg_printf(c, "hello from foo"); + * } else if (strcmp(param, "bar") == 0) { + * mg_printf(c, "hello from bar"); + * } + * break; + * } + */ + const char *ssi_pattern; + + /* IP ACL. By default, NULL, meaning all IPs are allowed to connect */ + const char *ip_acl; + +#if MG_ENABLE_HTTP_URL_REWRITES + /* URL rewrites. + * + * Comma-separated list of `uri_pattern=url_file_or_directory_path` rewrites. + * When HTTP request is received, Mongoose constructs a file name from the + * requested URI by combining `document_root` and the URI. However, if the + * rewrite option is used and `uri_pattern` matches requested URI, then + * `document_root` is ignored. Instead, `url_file_or_directory_path` is used, + * which should be a full path name or a path relative to the web server's + * current working directory. It can also be an URI (http:// or https://) + * in which case mongoose will behave as a reverse proxy for that destination. + * + * Note that `uri_pattern`, as all Mongoose patterns, is a prefix pattern. + * + * If uri_pattern starts with `@` symbol, then Mongoose compares it with the + * HOST header of the request. If they are equal, Mongoose sets document root + * to `file_or_directory_path`, implementing virtual hosts support. + * Example: `@foo.com=/document/root/for/foo.com` + * + * If `uri_pattern` starts with `%` symbol, then Mongoose compares it with + * the listening port. If they match, then Mongoose issues a 301 redirect. + * For example, to redirect all HTTP requests to the + * HTTPS port, do `%80=https://my.site.com`. Note that the request URI is + * automatically appended to the redirect location. + */ + const char *url_rewrites; +#endif + + /* DAV document root. If NULL, DAV requests are going to fail. */ + const char *dav_document_root; + + /* + * DAV passwords file. If NULL, DAV requests are going to fail. + * If passwords file is set to "-", then DAV auth is disabled. + */ + const char *dav_auth_file; + + /* Glob pattern for the files to hide. */ + const char *hidden_file_pattern; + + /* Set to non-NULL to enable CGI, e.g. **.cgi$|**.php$" */ + const char *cgi_file_pattern; + + /* If not NULL, ignore CGI script hashbang and use this interpreter */ + const char *cgi_interpreter; + + /* + * Comma-separated list of Content-Type overrides for path suffixes, e.g. + * ".txt=text/plain; charset=utf-8,.c=text/plain" + */ + const char *custom_mime_types; + + /* + * Extra HTTP headers to add to each server response. + * Example: to enable CORS, set this to "Access-Control-Allow-Origin: *". + */ + const char *extra_headers; +}; + +/* + * Serves given HTTP request according to the `options`. + * + * Example code snippet: + * + * ```c + * static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { + * struct http_message *hm = (struct http_message *) ev_data; + * struct mg_serve_http_opts opts = { .document_root = "/var/www" }; // C99 + * + * switch (ev) { + * case MG_EV_HTTP_REQUEST: + * mg_serve_http(nc, hm, opts); + * break; + * default: + * break; + * } + * } + * ``` + */ +void mg_serve_http(struct mg_connection *nc, struct http_message *hm, + struct mg_serve_http_opts opts); + +/* + * Serves a specific file with a given MIME type and optional extra headers. + * + * Example code snippet: + * + * ```c + * static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { + * switch (ev) { + * case MG_EV_HTTP_REQUEST: { + * struct http_message *hm = (struct http_message *) ev_data; + * mg_http_serve_file(nc, hm, "file.txt", + * mg_mk_str("text/plain"), mg_mk_str("")); + * break; + * } + * ... + * } + * } + * ``` + */ +void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm, + const char *path, const struct mg_str mime_type, + const struct mg_str extra_headers); + +#if MG_ENABLE_HTTP_STREAMING_MULTIPART + +/* Callback prototype for `mg_file_upload_handler()`. */ +typedef struct mg_str (*mg_fu_fname_fn)(struct mg_connection *nc, + struct mg_str fname); + +/* + * File upload handler. + * This handler can be used to implement file uploads with minimum code. + * This handler will process MG_EV_HTTP_PART_* events and store file data into + * a local file. + * `local_name_fn` will be invoked with whatever name was provided by the client + * and will expect the name of the local file to open. A return value of NULL + * will abort file upload (client will get a "403 Forbidden" response). If + * non-null, the returned string must be heap-allocated and will be freed by + * the caller. + * Exception: it is ok to return the same string verbatim. + * + * Example: + * + * ```c + * struct mg_str upload_fname(struct mg_connection *nc, struct mg_str fname) { + * // Just return the same filename. Do not actually do this except in test! + * // fname is user-controlled and needs to be sanitized. + * return fname; + * } + * void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { + * switch (ev) { + * ... + * case MG_EV_HTTP_PART_BEGIN: + * case MG_EV_HTTP_PART_DATA: + * case MG_EV_HTTP_PART_END: + * mg_file_upload_handler(nc, ev, ev_data, upload_fname); + * break; + * } + * } + * ``` + */ +void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, + mg_fu_fname_fn local_name_fn + MG_UD_ARG(void *user_data)); +#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ +#endif /* MG_ENABLE_FILESYSTEM */ + +/* + * Registers a callback for a specified http endpoint + * Note: if callback is registered it is called instead of the + * callback provided in mg_bind + * + * Example code snippet: + * + * ```c + * static void handle_hello1(struct mg_connection *nc, int ev, void *ev_data) { + * (void) ev; (void) ev_data; + * mg_printf(nc, "HTTP/1.0 200 OK\r\n\r\n[I am Hello1]"); + * nc->flags |= MG_F_SEND_AND_CLOSE; + * } + * + * static void handle_hello2(struct mg_connection *nc, int ev, void *ev_data) { + * (void) ev; (void) ev_data; + * mg_printf(nc, "HTTP/1.0 200 OK\r\n\r\n[I am Hello2]"); + * nc->flags |= MG_F_SEND_AND_CLOSE; + * } + * + * void init() { + * nc = mg_bind(&mgr, local_addr, cb1); + * mg_register_http_endpoint(nc, "/hello1", handle_hello1); + * mg_register_http_endpoint(nc, "/hello1/hello2", handle_hello2); + * } + * ``` + */ +void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path, + MG_CB(mg_event_handler_t handler, + void *user_data)); + +struct mg_http_endpoint_opts { + void *user_data; + /* Authorization domain (realm) */ + const char *auth_domain; + const char *auth_file; +}; + +void mg_register_http_endpoint_opt(struct mg_connection *nc, + const char *uri_path, + mg_event_handler_t handler, + struct mg_http_endpoint_opts opts); + +/* + * Authenticates a HTTP request against an opened password file. + * Returns 1 if authenticated, 0 otherwise. + */ +int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain, + FILE *fp); + +/* + * Authenticates given response params against an opened password file. + * Returns 1 if authenticated, 0 otherwise. + * + * It's used by mg_http_check_digest_auth(). + */ +int mg_check_digest_auth(struct mg_str method, struct mg_str uri, + struct mg_str username, struct mg_str cnonce, + struct mg_str response, struct mg_str qop, + struct mg_str nc, struct mg_str nonce, + struct mg_str auth_domain, FILE *fp); + +/* + * Sends buffer `buf` of size `len` to the client using chunked HTTP encoding. + * This function sends the buffer size as hex number + newline first, then + * the buffer itself, then the newline. For example, + * `mg_send_http_chunk(nc, "foo", 3)` will append the `3\r\nfoo\r\n` string + * to the `nc->send_mbuf` output IO buffer. + * + * NOTE: The HTTP header "Transfer-Encoding: chunked" should be sent prior to + * using this function. + * + * NOTE: do not forget to send an empty chunk at the end of the response, + * to tell the client that everything was sent. Example: + * + * ``` + * mg_printf_http_chunk(nc, "%s", "my response!"); + * mg_send_http_chunk(nc, "", 0); // Tell the client we're finished + * ``` + */ +void mg_send_http_chunk(struct mg_connection *nc, const char *buf, size_t len); + +/* + * Sends a printf-formatted HTTP chunk. + * Functionality is similar to `mg_send_http_chunk()`. + */ +void mg_printf_http_chunk(struct mg_connection *nc, const char *fmt, ...); + +/* + * Sends the response status line. + * If `extra_headers` is not NULL, then `extra_headers` are also sent + * after the response line. `extra_headers` must NOT end end with new line. + * Example: + * + * mg_send_response_line(nc, 200, "Access-Control-Allow-Origin: *"); + * + * Will result in: + * + * HTTP/1.1 200 OK\r\n + * Access-Control-Allow-Origin: *\r\n + */ +void mg_send_response_line(struct mg_connection *nc, int status_code, + const char *extra_headers); + +/* + * Sends an error response. If reason is NULL, the message will be inferred + * from the error code (if supported). + */ +void mg_http_send_error(struct mg_connection *nc, int code, const char *reason); + +/* + * Sends a redirect response. + * `status_code` should be either 301 or 302 and `location` point to the + * new location. + * If `extra_headers` is not empty, then `extra_headers` are also sent + * after the response line. `extra_headers` must NOT end end with new line. + * + * Example: + * + * mg_http_send_redirect(nc, 302, mg_mk_str("/login"), mg_mk_str(NULL)); + */ +void mg_http_send_redirect(struct mg_connection *nc, int status_code, + const struct mg_str location, + const struct mg_str extra_headers); + +/* + * Sends the response line and headers. + * This function sends the response line with the `status_code`, and + * automatically + * sends one header: either "Content-Length" or "Transfer-Encoding". + * If `content_length` is negative, then "Transfer-Encoding: chunked" header + * is sent, otherwise, "Content-Length" header is sent. + * + * NOTE: If `Transfer-Encoding` is `chunked`, then message body must be sent + * using `mg_send_http_chunk()` or `mg_printf_http_chunk()` functions. + * Otherwise, `mg_send()` or `mg_printf()` must be used. + * Extra headers could be set through `extra_headers`. Note `extra_headers` + * must NOT be terminated by a new line. + */ +void mg_send_head(struct mg_connection *n, int status_code, + int64_t content_length, const char *extra_headers); + +/* + * Sends a printf-formatted HTTP chunk, escaping HTML tags. + */ +void mg_printf_html_escape(struct mg_connection *nc, const char *fmt, ...); + +#if MG_ENABLE_HTTP_URL_REWRITES +/* + * Proxies a given request to a given upstream http server. The path prefix + * in `mount` will be stripped of the path requested to the upstream server, + * e.g. if mount is /api and upstream is http://localhost:8001/foo + * then an incoming request to /api/bar will cause a request to + * http://localhost:8001/foo/bar + * + * EXPERIMENTAL API. Please use http_serve_http + url_rewrites if a static + * mapping is good enough. + */ +void mg_http_reverse_proxy(struct mg_connection *nc, + const struct http_message *hm, struct mg_str mount, + struct mg_str upstream); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MG_ENABLE_HTTP */ + +#endif /* CS_MONGOOSE_SRC_HTTP_SERVER_H_ */ diff --git a/src/mongoose-6.11/src/mg_http_ssi.c b/src/mongoose-6.11/src/mg_http_ssi.c new file mode 100644 index 0000000..6d384db --- /dev/null +++ b/src/mongoose-6.11/src/mg_http_ssi.c @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_HTTP && MG_ENABLE_HTTP_SSI && MG_ENABLE_FILESYSTEM + +static void mg_send_ssi_file(struct mg_connection *nc, struct http_message *hm, + const char *path, FILE *fp, int include_level, + const struct mg_serve_http_opts *opts); + +static void mg_send_file_data(struct mg_connection *nc, FILE *fp) { + char buf[BUFSIZ]; + size_t n; + while ((n = mg_fread(buf, 1, sizeof(buf), fp)) > 0) { + mg_send(nc, buf, n); + } +} + +static void mg_do_ssi_include(struct mg_connection *nc, struct http_message *hm, + const char *ssi, char *tag, int include_level, + const struct mg_serve_http_opts *opts) { + char file_name[MG_MAX_PATH], path[MG_MAX_PATH], *p; + FILE *fp; + + /* + * sscanf() is safe here, since send_ssi_file() also uses buffer + * of size MG_BUF_LEN to get the tag. So strlen(tag) is always < MG_BUF_LEN. + */ + if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) { + /* File name is relative to the webserver root */ + snprintf(path, sizeof(path), "%s/%s", opts->document_root, file_name); + } else if (sscanf(tag, " abspath=\"%[^\"]\"", file_name) == 1) { + /* + * File name is relative to the webserver working directory + * or it is absolute system path + */ + snprintf(path, sizeof(path), "%s", file_name); + } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1 || + sscanf(tag, " \"%[^\"]\"", file_name) == 1) { + /* File name is relative to the currect document */ + snprintf(path, sizeof(path), "%s", ssi); + if ((p = strrchr(path, DIRSEP)) != NULL) { + p[1] = '\0'; + } + snprintf(path + strlen(path), sizeof(path) - strlen(path), "%s", file_name); + } else { + mg_printf(nc, "Bad SSI #include: [%s]", tag); + return; + } + + if ((fp = mg_fopen(path, "rb")) == NULL) { + mg_printf(nc, "SSI include error: mg_fopen(%s): %s", path, + strerror(mg_get_errno())); + } else { + mg_set_close_on_exec((sock_t) fileno(fp)); + if (mg_match_prefix(opts->ssi_pattern, strlen(opts->ssi_pattern), path) > + 0) { + mg_send_ssi_file(nc, hm, path, fp, include_level + 1, opts); + } else { + mg_send_file_data(nc, fp); + } + fclose(fp); + } +} + +#if MG_ENABLE_HTTP_SSI_EXEC +static void do_ssi_exec(struct mg_connection *nc, char *tag) { + char cmd[BUFSIZ]; + FILE *fp; + + if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) { + mg_printf(nc, "Bad SSI #exec: [%s]", tag); + } else if ((fp = popen(cmd, "r")) == NULL) { + mg_printf(nc, "Cannot SSI #exec: [%s]: %s", cmd, strerror(mg_get_errno())); + } else { + mg_send_file_data(nc, fp); + pclose(fp); + } +} +#endif /* MG_ENABLE_HTTP_SSI_EXEC */ + +/* + * SSI directive has the following format: + * + */ +static void mg_send_ssi_file(struct mg_connection *nc, struct http_message *hm, + const char *path, FILE *fp, int include_level, + const struct mg_serve_http_opts *opts) { + static const struct mg_str btag = MG_MK_STR(" */ + buf[i--] = '\0'; + while (i > 0 && buf[i] == ' ') { + buf[i--] = '\0'; + } + + /* Handle known SSI directives */ + if (strncmp(p, d_include.p, d_include.len) == 0) { + mg_do_ssi_include(nc, hm, path, p + d_include.len + 1, include_level, + opts); + } else if (strncmp(p, d_call.p, d_call.len) == 0) { + struct mg_ssi_call_ctx cctx; + memset(&cctx, 0, sizeof(cctx)); + cctx.req = hm; + cctx.file = mg_mk_str(path); + cctx.arg = mg_mk_str(p + d_call.len + 1); + mg_call(nc, NULL, nc->user_data, MG_EV_SSI_CALL, + (void *) cctx.arg.p); /* NUL added above */ + mg_call(nc, NULL, nc->user_data, MG_EV_SSI_CALL_CTX, &cctx); +#if MG_ENABLE_HTTP_SSI_EXEC + } else if (strncmp(p, d_exec.p, d_exec.len) == 0) { + do_ssi_exec(nc, p + d_exec.len + 1); +#endif + } else { + /* Silently ignore unknown SSI directive. */ + } + len = 0; + } else if (ch == '<') { + in_ssi_tag = 1; + if (len > 0) { + mg_send(nc, buf, (size_t) len); + } + len = 0; + buf[len++] = ch & 0xff; + } else if (in_ssi_tag) { + if (len == (int) btag.len && strncmp(buf, btag.p, btag.len) != 0) { + /* Not an SSI tag */ + in_ssi_tag = 0; + } else if (len == (int) sizeof(buf) - 2) { + mg_printf(nc, "%s: SSI tag is too large", path); + len = 0; + } + buf[len++] = ch & 0xff; + } else { + buf[len++] = ch & 0xff; + if (len == (int) sizeof(buf)) { + mg_send(nc, buf, (size_t) len); + len = 0; + } + } + } + + /* Send the rest of buffered data */ + if (len > 0) { + mg_send(nc, buf, (size_t) len); + } +} + +MG_INTERNAL void mg_handle_ssi_request(struct mg_connection *nc, + struct http_message *hm, + const char *path, + const struct mg_serve_http_opts *opts) { + FILE *fp; + struct mg_str mime_type; + DBG(("%p %s", nc, path)); + + if ((fp = mg_fopen(path, "rb")) == NULL) { + mg_http_send_error(nc, 404, NULL); + } else { + mg_set_close_on_exec((sock_t) fileno(fp)); + + mime_type = mg_get_mime_type(path, "text/plain", opts); + mg_send_response_line(nc, 200, opts->extra_headers); + mg_printf(nc, + "Content-Type: %.*s\r\n" + "Connection: close\r\n\r\n", + (int) mime_type.len, mime_type.p); + mg_send_ssi_file(nc, hm, path, fp, 0, opts); + fclose(fp); + nc->flags |= MG_F_SEND_AND_CLOSE; + } +} + +#endif /* MG_ENABLE_HTTP_SSI && MG_ENABLE_HTTP && MG_ENABLE_FILESYSTEM */ diff --git a/src/mongoose-6.11/src/mg_http_webdav.c b/src/mongoose-6.11/src/mg_http_webdav.c new file mode 100644 index 0000000..6efb928 --- /dev/null +++ b/src/mongoose-6.11/src/mg_http_webdav.c @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBDAV + +MG_INTERNAL int mg_is_dav_request(const struct mg_str *s) { + static const char *methods[] = { + "PUT", + "DELETE", + "MKCOL", + "PROPFIND", + "MOVE" +#if MG_ENABLE_FAKE_DAVLOCK + , + "LOCK", + "UNLOCK" +#endif + }; + size_t i; + + for (i = 0; i < ARRAY_SIZE(methods); i++) { + if (mg_vcmp(s, methods[i]) == 0) { + return 1; + } + } + + return 0; +} + +static int mg_mkdir(const char *path, uint32_t mode) { +#ifndef _WIN32 + return mkdir(path, mode); +#else + (void) mode; + return _mkdir(path); +#endif +} + +static void mg_print_props(struct mg_connection *nc, const char *name, + cs_stat_t *stp) { + char mtime[64]; + time_t t = stp->st_mtime; /* store in local variable for NDK compile */ + struct mg_str name_esc = mg_url_encode(mg_mk_str(name)); + mg_gmt_time_string(mtime, sizeof(mtime), &t); + mg_printf(nc, + "" + "%s" + "" + "" + "%s" + "%" INT64_FMT + "" + "%s" + "" + "HTTP/1.1 200 OK" + "" + "\n", + name_esc.p, S_ISDIR(stp->st_mode) ? "" : "", + (int64_t) stp->st_size, mtime); + free((void *) name_esc.p); +} + +MG_INTERNAL void mg_handle_propfind(struct mg_connection *nc, const char *path, + cs_stat_t *stp, struct http_message *hm, + struct mg_serve_http_opts *opts) { + static const char header[] = + "HTTP/1.1 207 Multi-Status\r\n" + "Connection: close\r\n" + "Content-Type: text/xml; charset=utf-8\r\n\r\n" + "" + "\n"; + static const char footer[] = "\n"; + const struct mg_str *depth = mg_get_http_header(hm, "Depth"); + + /* Print properties for the requested resource itself */ + if (S_ISDIR(stp->st_mode) && + strcmp(opts->enable_directory_listing, "yes") != 0) { + mg_printf(nc, "%s", "HTTP/1.1 403 Directory Listing Denied\r\n\r\n"); + } else { + char uri[MG_MAX_PATH]; + mg_send(nc, header, sizeof(header) - 1); + snprintf(uri, sizeof(uri), "%.*s", (int) hm->uri.len, hm->uri.p); + mg_print_props(nc, uri, stp); + if (S_ISDIR(stp->st_mode) && (depth == NULL || mg_vcmp(depth, "0") != 0)) { + mg_scan_directory(nc, path, opts, mg_print_props); + } + mg_send(nc, footer, sizeof(footer) - 1); + nc->flags |= MG_F_SEND_AND_CLOSE; + } +} + +#if MG_ENABLE_FAKE_DAVLOCK +/* + * Windows explorer (probably there are another WebDav clients like it) + * requires LOCK support in webdav. W/out this, it still works, but fails + * to save file: shows error message and offers "Save As". + * "Save as" works, but this message is very annoying. + * This is fake lock, which doesn't lock something, just returns LOCK token, + * UNLOCK always answers "OK". + * With this fake LOCK Windows Explorer looks happy and saves file. + * NOTE: that is not DAV LOCK imlementation, it is just a way to shut up + * Windows native DAV client. This is why FAKE LOCK is not enabed by default + */ +MG_INTERNAL void mg_handle_lock(struct mg_connection *nc, const char *path) { + static const char *reply = + "HTTP/1.1 207 Multi-Status\r\n" + "Connection: close\r\n" + "Content-Type: text/xml; charset=utf-8\r\n\r\n" + "" + "\n" + "\n" + "\n" + "\n" + "\n" + "opaquelocktoken:%s%u" + "" + "" + "\n" + "" + "\n"; + mg_printf(nc, reply, path, (unsigned int) mg_time()); + nc->flags |= MG_F_SEND_AND_CLOSE; +} +#endif + +MG_INTERNAL void mg_handle_mkcol(struct mg_connection *nc, const char *path, + struct http_message *hm) { + int status_code = 500; + if (hm->body.len != (size_t) ~0 && hm->body.len > 0) { + status_code = 415; + } else if (!mg_mkdir(path, 0755)) { + status_code = 201; + } else if (errno == EEXIST) { + status_code = 405; + } else if (errno == EACCES) { + status_code = 403; + } else if (errno == ENOENT) { + status_code = 409; + } else { + status_code = 500; + } + mg_http_send_error(nc, status_code, NULL); +} + +static int mg_remove_directory(const struct mg_serve_http_opts *opts, + const char *dir) { + char path[MG_MAX_PATH]; + struct dirent *dp; + cs_stat_t st; + DIR *dirp; + + if ((dirp = opendir(dir)) == NULL) return 0; + + while ((dp = readdir(dirp)) != NULL) { + if (mg_is_file_hidden((const char *) dp->d_name, opts, 1)) { + continue; + } + snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name); + mg_stat(path, &st); + if (S_ISDIR(st.st_mode)) { + mg_remove_directory(opts, path); + } else { + remove(path); + } + } + closedir(dirp); + rmdir(dir); + + return 1; +} + +MG_INTERNAL void mg_handle_move(struct mg_connection *c, + const struct mg_serve_http_opts *opts, + const char *path, struct http_message *hm) { + const struct mg_str *dest = mg_get_http_header(hm, "Destination"); + if (dest == NULL) { + mg_http_send_error(c, 411, NULL); + } else { + const char *p = (char *) memchr(dest->p, '/', dest->len); + if (p != NULL && p[1] == '/' && + (p = (char *) memchr(p + 2, '/', dest->p + dest->len - p)) != NULL) { + char buf[MG_MAX_PATH]; + snprintf(buf, sizeof(buf), "%s%.*s", opts->dav_document_root, + (int) (dest->p + dest->len - p), p); + if (rename(path, buf) == 0) { + mg_http_send_error(c, 200, NULL); + } else { + mg_http_send_error(c, 418, NULL); + } + } else { + mg_http_send_error(c, 500, NULL); + } + } +} + +MG_INTERNAL void mg_handle_delete(struct mg_connection *nc, + const struct mg_serve_http_opts *opts, + const char *path) { + cs_stat_t st; + if (mg_stat(path, &st) != 0) { + mg_http_send_error(nc, 404, NULL); + } else if (S_ISDIR(st.st_mode)) { + mg_remove_directory(opts, path); + mg_http_send_error(nc, 204, NULL); + } else if (remove(path) == 0) { + mg_http_send_error(nc, 204, NULL); + } else { + mg_http_send_error(nc, 423, NULL); + } +} + +/* Return -1 on error, 1 on success. */ +static int mg_create_itermediate_directories(const char *path) { + const char *s; + + /* Create intermediate directories if they do not exist */ + for (s = path + 1; *s != '\0'; s++) { + if (*s == '/') { + char buf[MG_MAX_PATH]; + cs_stat_t st; + snprintf(buf, sizeof(buf), "%.*s", (int) (s - path), path); + buf[sizeof(buf) - 1] = '\0'; + if (mg_stat(buf, &st) != 0 && mg_mkdir(buf, 0755) != 0) { + return -1; + } + } + } + + return 1; +} + +MG_INTERNAL void mg_handle_put(struct mg_connection *nc, const char *path, + struct http_message *hm) { + struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); + cs_stat_t st; + const struct mg_str *cl_hdr = mg_get_http_header(hm, "Content-Length"); + int rc, status_code = mg_stat(path, &st) == 0 ? 200 : 201; + + mg_http_free_proto_data_file(&pd->file); + if ((rc = mg_create_itermediate_directories(path)) == 0) { + mg_printf(nc, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n", status_code); + } else if (rc == -1) { + mg_http_send_error(nc, 500, NULL); + } else if (cl_hdr == NULL) { + mg_http_send_error(nc, 411, NULL); + } else if ((pd->file.fp = mg_fopen(path, "w+b")) == NULL) { + mg_http_send_error(nc, 500, NULL); + } else { + const struct mg_str *range_hdr = mg_get_http_header(hm, "Content-Range"); + int64_t r1 = 0, r2 = 0; + pd->file.type = DATA_PUT; + mg_set_close_on_exec((sock_t) fileno(pd->file.fp)); + pd->file.cl = to64(cl_hdr->p); + if (range_hdr != NULL && + mg_http_parse_range_header(range_hdr, &r1, &r2) > 0) { + status_code = 206; + fseeko(pd->file.fp, r1, SEEK_SET); + pd->file.cl = r2 > r1 ? r2 - r1 + 1 : pd->file.cl - r1; + } + mg_printf(nc, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n", status_code); + /* Remove HTTP request from the mbuf, leave only payload */ + mbuf_remove(&nc->recv_mbuf, hm->message.len - hm->body.len); + mg_http_transfer_file_data(nc); + } +} + +#endif /* MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBDAV */ diff --git a/src/mongoose-6.11/src/mg_http_websocket.c b/src/mongoose-6.11/src/mg_http_websocket.c new file mode 100644 index 0000000..e69e4b7 --- /dev/null +++ b/src/mongoose-6.11/src/mg_http_websocket.c @@ -0,0 +1,517 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBSOCKET + +#include "common/cs_sha1.h" + +#ifndef MG_WEBSOCKET_PING_INTERVAL_SECONDS +#define MG_WEBSOCKET_PING_INTERVAL_SECONDS 5 +#endif + +#define FLAGS_MASK_FIN (1 << 7) +#define FLAGS_MASK_OP 0x0f + +static int mg_is_ws_fragment(unsigned char flags) { + return (flags & FLAGS_MASK_FIN) == 0 || + (flags & FLAGS_MASK_OP) == WEBSOCKET_OP_CONTINUE; +} + +static int mg_is_ws_first_fragment(unsigned char flags) { + return (flags & FLAGS_MASK_FIN) == 0 && + (flags & FLAGS_MASK_OP) != WEBSOCKET_OP_CONTINUE; +} + +static int mg_is_ws_control_frame(unsigned char flags) { + unsigned char op = (flags & FLAGS_MASK_OP); + return op == WEBSOCKET_OP_CLOSE || op == WEBSOCKET_OP_PING || + op == WEBSOCKET_OP_PONG; +} + +static void mg_handle_incoming_websocket_frame(struct mg_connection *nc, + struct websocket_message *wsm) { + if (wsm->flags & 0x8) { + mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_CONTROL_FRAME, wsm); + } else { + mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_FRAME, wsm); + } +} + +static struct mg_ws_proto_data *mg_ws_get_proto_data(struct mg_connection *nc) { + struct mg_http_proto_data *htd = mg_http_get_proto_data(nc); + return (htd != NULL ? &htd->ws_data : NULL); +} + +/* + * Sends a Close websocket frame with the given data, and closes the underlying + * connection. If `len` is ~0, strlen(data) is used. + */ +static void mg_ws_close(struct mg_connection *nc, const void *data, + size_t len) { + if ((int) len == ~0) { + len = strlen((const char *) data); + } + mg_send_websocket_frame(nc, WEBSOCKET_OP_CLOSE, data, len); + nc->flags |= MG_F_SEND_AND_CLOSE; +} + +static int mg_deliver_websocket_data(struct mg_connection *nc) { + /* Using unsigned char *, cause of integer arithmetic below */ + uint64_t i, data_len = 0, frame_len = 0, new_data_len = nc->recv_mbuf.len, + len, mask_len = 0, header_len = 0; + struct mg_ws_proto_data *wsd = mg_ws_get_proto_data(nc); + unsigned char *new_data = (unsigned char *) nc->recv_mbuf.buf, + *e = (unsigned char *) nc->recv_mbuf.buf + nc->recv_mbuf.len; + uint8_t flags; + int ok, reass; + + if (wsd->reass_len > 0) { + /* + * We already have some previously received data which we need to + * reassemble and deliver to the client code when we get the final + * fragment. + * + * NOTE: it doesn't mean that the current message must be a continuation: + * it might be a control frame (Close, Ping or Pong), which should be + * handled without breaking the fragmented message. + */ + + size_t existing_len = wsd->reass_len; + assert(new_data_len >= existing_len); + + new_data += existing_len; + new_data_len -= existing_len; + } + + flags = new_data[0]; + + reass = new_data_len > 0 && mg_is_ws_fragment(flags) && + !(nc->flags & MG_F_WEBSOCKET_NO_DEFRAG); + + if (reass && mg_is_ws_control_frame(flags)) { + /* + * Control frames can't be fragmented, so if we encounter fragmented + * control frame, close connection immediately. + */ + mg_ws_close(nc, "fragmented control frames are illegal", ~0); + return 0; + } else if (new_data_len > 0 && !reass && !mg_is_ws_control_frame(flags) && + wsd->reass_len > 0) { + /* + * When in the middle of a fragmented message, only the continuations + * and control frames are allowed. + */ + mg_ws_close(nc, "non-continuation in the middle of a fragmented message", + ~0); + return 0; + } + + if (new_data_len >= 2) { + len = new_data[1] & 0x7f; + mask_len = new_data[1] & FLAGS_MASK_FIN ? 4 : 0; + if (len < 126 && new_data_len >= mask_len) { + data_len = len; + header_len = 2 + mask_len; + } else if (len == 126 && new_data_len >= 4 + mask_len) { + header_len = 4 + mask_len; + data_len = ntohs(*(uint16_t *) &new_data[2]); + } else if (new_data_len >= 10 + mask_len) { + header_len = 10 + mask_len; + data_len = (((uint64_t) ntohl(*(uint32_t *) &new_data[2])) << 32) + + ntohl(*(uint32_t *) &new_data[6]); + } + } + + frame_len = header_len + data_len; + ok = (frame_len > 0 && frame_len <= new_data_len); + + /* Check for overflow */ + if (frame_len < header_len || frame_len < data_len) { + ok = 0; + mg_ws_close(nc, "overflowed message", ~0); + } + + if (ok) { + size_t cleanup_len = 0; + struct websocket_message wsm; + + wsm.size = (size_t) data_len; + wsm.data = new_data + header_len; + wsm.flags = flags; + + /* Apply mask if necessary */ + if (mask_len > 0) { + for (i = 0; i < data_len; i++) { + new_data[i + header_len] ^= (new_data + header_len - mask_len)[i % 4]; + } + } + + if (reass) { + /* This is a message fragment */ + + if (mg_is_ws_first_fragment(flags)) { + /* + * On the first fragmented frame, skip the first byte (op) and also + * reset size to 1 (op), it'll be incremented with the data len below. + */ + new_data += 1; + wsd->reass_len = 1 /* op */; + } + + /* Append this frame to the reassembled buffer */ + memmove(new_data, wsm.data, e - wsm.data); + wsd->reass_len += wsm.size; + nc->recv_mbuf.len -= wsm.data - new_data; + + if (flags & FLAGS_MASK_FIN) { + /* On last fragmented frame - call user handler and remove data */ + wsm.flags = FLAGS_MASK_FIN | nc->recv_mbuf.buf[0]; + wsm.data = (unsigned char *) nc->recv_mbuf.buf + 1 /* op */; + wsm.size = wsd->reass_len - 1 /* op */; + cleanup_len = wsd->reass_len; + wsd->reass_len = 0; + + /* Pass reassembled message to the client code. */ + mg_handle_incoming_websocket_frame(nc, &wsm); + mbuf_remove(&nc->recv_mbuf, cleanup_len); /* Cleanup frame */ + } + } else { + /* + * This is a complete message, not a fragment. It might happen in between + * of a fragmented message (in this case, WebSocket protocol requires + * current message to be a control frame). + */ + cleanup_len = (size_t) frame_len; + + /* First of all, check if we need to react on a control frame. */ + switch (flags & FLAGS_MASK_OP) { + case WEBSOCKET_OP_PING: + mg_send_websocket_frame(nc, WEBSOCKET_OP_PONG, wsm.data, wsm.size); + break; + + case WEBSOCKET_OP_CLOSE: + mg_ws_close(nc, wsm.data, wsm.size); + break; + } + + /* Pass received message to the client code. */ + mg_handle_incoming_websocket_frame(nc, &wsm); + + /* Cleanup frame */ + memmove(nc->recv_mbuf.buf + wsd->reass_len, + nc->recv_mbuf.buf + wsd->reass_len + cleanup_len, + nc->recv_mbuf.len - wsd->reass_len - cleanup_len); + nc->recv_mbuf.len -= cleanup_len; + } + } + + return ok; +} + +struct ws_mask_ctx { + size_t pos; /* zero means unmasked */ + uint32_t mask; +}; + +static uint32_t mg_ws_random_mask(void) { + uint32_t mask; +/* + * The spec requires WS client to generate hard to + * guess mask keys. From RFC6455, Section 5.3: + * + * The unpredictability of the masking key is essential to prevent + * authors of malicious applications from selecting the bytes that appear on + * the wire. + * + * Hence this feature is essential when the actual end user of this API + * is untrusted code that wouldn't have access to a lower level net API + * anyway (e.g. web browsers). Hence this feature is low prio for most + * mongoose use cases and thus can be disabled, e.g. when porting to a platform + * that lacks rand(). + */ +#if MG_DISABLE_WS_RANDOM_MASK + mask = 0xefbeadde; /* generated with a random number generator, I swear */ +#else + if (sizeof(long) >= 4) { + mask = (uint32_t) rand(); + } else if (sizeof(long) == 2) { + mask = (uint32_t) rand() << 16 | (uint32_t) rand(); + } +#endif + return mask; +} + +static void mg_send_ws_header(struct mg_connection *nc, int op, size_t len, + struct ws_mask_ctx *ctx) { + int header_len; + unsigned char header[10]; + + header[0] = + (op & WEBSOCKET_DONT_FIN ? 0x0 : FLAGS_MASK_FIN) | (op & FLAGS_MASK_OP); + if (len < 126) { + header[1] = (unsigned char) len; + header_len = 2; + } else if (len < 65535) { + uint16_t tmp = htons((uint16_t) len); + header[1] = 126; + memcpy(&header[2], &tmp, sizeof(tmp)); + header_len = 4; + } else { + uint32_t tmp; + header[1] = 127; + tmp = htonl((uint32_t)((uint64_t) len >> 32)); + memcpy(&header[2], &tmp, sizeof(tmp)); + tmp = htonl((uint32_t)(len & 0xffffffff)); + memcpy(&header[6], &tmp, sizeof(tmp)); + header_len = 10; + } + + /* client connections enable masking */ + if (nc->listener == NULL) { + header[1] |= 1 << 7; /* set masking flag */ + mg_send(nc, header, header_len); + ctx->mask = mg_ws_random_mask(); + mg_send(nc, &ctx->mask, sizeof(ctx->mask)); + ctx->pos = nc->send_mbuf.len; + } else { + mg_send(nc, header, header_len); + ctx->pos = 0; + } +} + +static void mg_ws_mask_frame(struct mbuf *mbuf, struct ws_mask_ctx *ctx) { + size_t i; + if (ctx->pos == 0) return; + for (i = 0; i < (mbuf->len - ctx->pos); i++) { + mbuf->buf[ctx->pos + i] ^= ((char *) &ctx->mask)[i % 4]; + } +} + +void mg_send_websocket_frame(struct mg_connection *nc, int op, const void *data, + size_t len) { + struct ws_mask_ctx ctx; + DBG(("%p %d %d", nc, op, (int) len)); + mg_send_ws_header(nc, op, len, &ctx); + mg_send(nc, data, len); + + mg_ws_mask_frame(&nc->send_mbuf, &ctx); + + if (op == WEBSOCKET_OP_CLOSE) { + nc->flags |= MG_F_SEND_AND_CLOSE; + } +} + +void mg_send_websocket_framev(struct mg_connection *nc, int op, + const struct mg_str *strv, int strvcnt) { + struct ws_mask_ctx ctx; + int i; + int len = 0; + for (i = 0; i < strvcnt; i++) { + len += strv[i].len; + } + + mg_send_ws_header(nc, op, len, &ctx); + + for (i = 0; i < strvcnt; i++) { + mg_send(nc, strv[i].p, strv[i].len); + } + + mg_ws_mask_frame(&nc->send_mbuf, &ctx); + + if (op == WEBSOCKET_OP_CLOSE) { + nc->flags |= MG_F_SEND_AND_CLOSE; + } +} + +void mg_printf_websocket_frame(struct mg_connection *nc, int op, + const char *fmt, ...) { + char mem[MG_VPRINTF_BUFFER_SIZE], *buf = mem; + va_list ap; + int len; + + va_start(ap, fmt); + if ((len = mg_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) { + mg_send_websocket_frame(nc, op, buf, len); + } + va_end(ap); + + if (buf != mem && buf != NULL) { + MG_FREE(buf); + } +} + +MG_INTERNAL void mg_ws_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + mg_call(nc, nc->handler, nc->user_data, ev, ev_data); + + switch (ev) { + case MG_EV_RECV: + do { + } while (mg_deliver_websocket_data(nc)); + break; + case MG_EV_POLL: + /* Ping idle websocket connections */ + { + time_t now = *(time_t *) ev_data; + if (nc->flags & MG_F_IS_WEBSOCKET && + now > nc->last_io_time + MG_WEBSOCKET_PING_INTERVAL_SECONDS) { + mg_send_websocket_frame(nc, WEBSOCKET_OP_PING, "", 0); + } + } + break; + default: + break; + } +#if MG_ENABLE_CALLBACK_USERDATA + (void) user_data; +#endif +} + +#ifndef MG_EXT_SHA1 +void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest) { + size_t i; + cs_sha1_ctx sha_ctx; + cs_sha1_init(&sha_ctx); + for (i = 0; i < num_msgs; i++) { + cs_sha1_update(&sha_ctx, msgs[i], msg_lens[i]); + } + cs_sha1_final(digest, &sha_ctx); +} +#else +extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest); +#endif + +MG_INTERNAL void mg_ws_handshake(struct mg_connection *nc, + const struct mg_str *key, + struct http_message *hm) { + static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + const uint8_t *msgs[2] = {(const uint8_t *) key->p, (const uint8_t *) magic}; + const size_t msg_lens[2] = {key->len, 36}; + unsigned char sha[20]; + char b64_sha[30]; + struct mg_str *s; + + mg_hash_sha1_v(2, msgs, msg_lens, sha); + mg_base64_encode(sha, sizeof(sha), b64_sha); + mg_printf(nc, "%s", + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n"); + + s = mg_get_http_header(hm, "Sec-WebSocket-Protocol"); + if (s != NULL) { + mg_printf(nc, "Sec-WebSocket-Protocol: %.*s\r\n", (int) s->len, s->p); + } + mg_printf(nc, "Sec-WebSocket-Accept: %s%s", b64_sha, "\r\n\r\n"); + + DBG(("%p %.*s %s", nc, (int) key->len, key->p, b64_sha)); +} + +void mg_send_websocket_handshake2(struct mg_connection *nc, const char *path, + const char *host, const char *protocol, + const char *extra_headers) { + mg_send_websocket_handshake3(nc, path, host, protocol, extra_headers, NULL, + NULL); +} + +void mg_send_websocket_handshake3(struct mg_connection *nc, const char *path, + const char *host, const char *protocol, + const char *extra_headers, const char *user, + const char *pass) { + mg_send_websocket_handshake3v(nc, mg_mk_str(path), mg_mk_str(host), + mg_mk_str(protocol), mg_mk_str(extra_headers), + mg_mk_str(user), mg_mk_str(pass)); +} + +void mg_send_websocket_handshake3v(struct mg_connection *nc, + const struct mg_str path, + const struct mg_str host, + const struct mg_str protocol, + const struct mg_str extra_headers, + const struct mg_str user, + const struct mg_str pass) { + struct mbuf auth; + char key[25]; + uint32_t nonce[4]; + nonce[0] = mg_ws_random_mask(); + nonce[1] = mg_ws_random_mask(); + nonce[2] = mg_ws_random_mask(); + nonce[3] = mg_ws_random_mask(); + mg_base64_encode((unsigned char *) &nonce, sizeof(nonce), key); + + mbuf_init(&auth, 0); + if (user.len > 0) { + mg_basic_auth_header(user, pass, &auth); + } + + /* + * NOTE: the (auth.buf == NULL ? "" : auth.buf) is because cc3200 libc is + * broken: it doesn't like zero length to be passed to %.*s + * i.e. sprintf("f%.*so", (int)0, NULL), yields `f\0o`. + * because it handles NULL specially (and incorrectly). + */ + mg_printf(nc, + "GET %.*s HTTP/1.1\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "%.*s" + "Sec-WebSocket-Version: 13\r\n" + "Sec-WebSocket-Key: %s\r\n", + (int) path.len, path.p, (int) auth.len, + (auth.buf == NULL ? "" : auth.buf), key); + + /* TODO(mkm): take default hostname from http proto data if host == NULL */ + if (host.len > 0) { + int host_len = (int) (path.p - host.p); /* Account for possible :PORT */ + mg_printf(nc, "Host: %.*s\r\n", host_len, host.p); + } + if (protocol.len > 0) { + mg_printf(nc, "Sec-WebSocket-Protocol: %.*s\r\n", (int) protocol.len, + protocol.p); + } + if (extra_headers.len > 0) { + mg_printf(nc, "%.*s", (int) extra_headers.len, extra_headers.p); + } + mg_printf(nc, "\r\n"); + + mbuf_free(&auth); +} + +void mg_send_websocket_handshake(struct mg_connection *nc, const char *path, + const char *extra_headers) { + struct mg_str null_str = MG_NULL_STR; + mg_send_websocket_handshake3v( + nc, mg_mk_str(path), null_str /* host */, null_str /* protocol */, + mg_mk_str(extra_headers), null_str /* user */, null_str /* pass */); +} + +struct mg_connection *mg_connect_ws_opt( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + struct mg_connect_opts opts, const char *url, const char *protocol, + const char *extra_headers) { + struct mg_str null_str = MG_NULL_STR; + struct mg_str host = MG_NULL_STR, path = MG_NULL_STR, user_info = MG_NULL_STR; + struct mg_connection *nc = + mg_connect_http_base(mgr, MG_CB(ev_handler, user_data), opts, "http", + "ws", "https", "wss", url, &path, &user_info, &host); + if (nc != NULL) { + mg_send_websocket_handshake3v(nc, path, host, mg_mk_str(protocol), + mg_mk_str(extra_headers), user_info, + null_str); + } + return nc; +} + +struct mg_connection *mg_connect_ws( + struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), + const char *url, const char *protocol, const char *extra_headers) { + struct mg_connect_opts opts; + memset(&opts, 0, sizeof(opts)); + return mg_connect_ws_opt(mgr, MG_CB(ev_handler, user_data), opts, url, + protocol, extra_headers); +} +#endif /* MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBSOCKET */ diff --git a/src/mongoose-6.11/src/mg_internal.h b/src/mongoose-6.11/src/mg_internal.h new file mode 100644 index 0000000..2061423 --- /dev/null +++ b/src/mongoose-6.11/src/mg_internal.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_INTERNAL_H_ +#define CS_MONGOOSE_SRC_INTERNAL_H_ + +#include "common/mg_mem.h" + +#ifndef MBUF_REALLOC +#define MBUF_REALLOC MG_REALLOC +#endif + +#ifndef MBUF_FREE +#define MBUF_FREE MG_FREE +#endif + +#define MG_SET_PTRPTR(_ptr, _v) \ + do { \ + if (_ptr) *(_ptr) = _v; \ + } while (0) + +#ifndef MG_INTERNAL +#define MG_INTERNAL static +#endif + +#ifdef PICOTCP +#define NO_LIBC +#define MG_DISABLE_PFS +#endif + +#include "common/cs_dbg.h" +#include "mg_http.h" +#include "mg_net.h" + +#define MG_CTL_MSG_MESSAGE_SIZE 8192 + +/* internals that need to be accessible in unit tests */ +MG_INTERNAL struct mg_connection *mg_do_connect(struct mg_connection *nc, + int proto, + union socket_address *sa); + +MG_INTERNAL int mg_parse_address(const char *str, union socket_address *sa, + int *proto, char *host, size_t host_len); +MG_INTERNAL void mg_call(struct mg_connection *nc, + mg_event_handler_t ev_handler, void *user_data, int ev, + void *ev_data); +void mg_forward(struct mg_connection *from, struct mg_connection *to); +MG_INTERNAL void mg_add_conn(struct mg_mgr *mgr, struct mg_connection *c); +MG_INTERNAL void mg_remove_conn(struct mg_connection *c); +MG_INTERNAL struct mg_connection *mg_create_connection( + struct mg_mgr *mgr, mg_event_handler_t callback, + struct mg_add_sock_opts opts); +#ifdef _WIN32 +/* Retur value is the same as for MultiByteToWideChar. */ +int to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len); +#endif + +struct ctl_msg { + mg_event_handler_t callback; + char message[MG_CTL_MSG_MESSAGE_SIZE]; +}; + +#if MG_ENABLE_MQTT +struct mg_mqtt_message; + +#define MG_MQTT_ERROR_INCOMPLETE_MSG -1 +#define MG_MQTT_ERROR_MALFORMED_MSG -2 + +MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm); +#endif + +/* Forward declarations for testing. */ +extern void *(*test_malloc)(size_t size); +extern void *(*test_calloc)(size_t count, size_t size); + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#if MG_ENABLE_HTTP +struct mg_serve_http_opts; + +/* + * Reassemble the content of the buffer (buf, blen) which should be + * in the HTTP chunked encoding, by collapsing data chunks to the + * beginning of the buffer. + * + * If chunks get reassembled, modify hm->body to point to the reassembled + * body and fire MG_EV_HTTP_CHUNK event. If handler sets MG_F_DELETE_CHUNK + * in nc->flags, delete reassembled body from the mbuf. + * + * Return reassembled body size. + */ +MG_INTERNAL size_t mg_handle_chunked(struct mg_connection *nc, + struct http_message *hm, char *buf, + size_t blen); + +#if MG_ENABLE_FILESYSTEM +MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm, + const struct mg_serve_http_opts *opts, + char **local_path, + struct mg_str *remainder); +MG_INTERNAL time_t mg_parse_date_string(const char *datetime); +MG_INTERNAL int mg_is_not_modified(struct http_message *hm, cs_stat_t *st); +#endif +#if MG_ENABLE_HTTP_CGI +MG_INTERNAL void mg_handle_cgi(struct mg_connection *nc, const char *prog, + const struct mg_str *path_info, + const struct http_message *hm, + const struct mg_serve_http_opts *opts); +struct mg_http_proto_data_cgi; +MG_INTERNAL void mg_http_free_proto_data_cgi(struct mg_http_proto_data_cgi *d); +#endif +#if MG_ENABLE_HTTP_SSI +MG_INTERNAL void mg_handle_ssi_request(struct mg_connection *nc, + struct http_message *hm, + const char *path, + const struct mg_serve_http_opts *opts); +#endif +#if MG_ENABLE_HTTP_WEBDAV +MG_INTERNAL int mg_is_dav_request(const struct mg_str *s); +MG_INTERNAL void mg_handle_propfind(struct mg_connection *nc, const char *path, + cs_stat_t *stp, struct http_message *hm, + struct mg_serve_http_opts *opts); +MG_INTERNAL void mg_handle_lock(struct mg_connection *nc, const char *path); +MG_INTERNAL void mg_handle_mkcol(struct mg_connection *nc, const char *path, + struct http_message *hm); +MG_INTERNAL void mg_handle_move(struct mg_connection *c, + const struct mg_serve_http_opts *opts, + const char *path, struct http_message *hm); +MG_INTERNAL void mg_handle_delete(struct mg_connection *nc, + const struct mg_serve_http_opts *opts, + const char *path); +MG_INTERNAL void mg_handle_put(struct mg_connection *nc, const char *path, + struct http_message *hm); +#endif +#if MG_ENABLE_HTTP_WEBSOCKET +MG_INTERNAL void mg_ws_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)); +MG_INTERNAL void mg_ws_handshake(struct mg_connection *nc, + const struct mg_str *key, + struct http_message *); +#endif +#endif /* MG_ENABLE_HTTP */ + +MG_INTERNAL int mg_get_errno(void); + +MG_INTERNAL void mg_close_conn(struct mg_connection *conn); + +#if MG_ENABLE_SNTP +MG_INTERNAL int mg_sntp_parse_reply(const char *buf, int len, + struct mg_sntp_message *msg); +#endif + +#endif /* CS_MONGOOSE_SRC_INTERNAL_H_ */ diff --git a/src/mongoose-6.11/src/mg_modules.mk b/src/mongoose-6.11/src/mg_modules.mk new file mode 100644 index 0000000..235e0d5 --- /dev/null +++ b/src/mongoose-6.11/src/mg_modules.mk @@ -0,0 +1,103 @@ +COMMON = ../../common + +HEADERS = mg_common.h \ + $(COMMON)/platform.h \ + $(COMMON)/platforms/platform_windows.h \ + $(COMMON)/platforms/platform_unix.h \ + $(COMMON)/platforms/platform_esp32.h \ + $(COMMON)/platforms/platform_esp8266.h \ + $(COMMON)/platforms/platform_cc3100.h \ + $(COMMON)/platforms/platform_cc3200.h \ + $(COMMON)/platforms/platform_msp432.h \ + $(COMMON)/platforms/platform_tm4c129.h \ + $(COMMON)/platforms/platform_mbed.h \ + $(COMMON)/platforms/platform_nrf51.h \ + $(COMMON)/platforms/platform_nrf52.h \ + $(COMMON)/platforms/simplelink/cs_simplelink.h \ + $(COMMON)/platforms/platform_wince.h \ + $(COMMON)/platforms/platform_nxp_lpc.h \ + $(COMMON)/platforms/platform_nxp_kinetis.h \ + $(COMMON)/platforms/platform_pic32.h \ + $(COMMON)/platforms/platform_stm32.h \ + $(COMMON)/platforms/lwip/mg_lwip.h \ + $(COMMON)/cs_md5.h \ + $(COMMON)/cs_sha1.h \ + $(COMMON)/cs_time.h \ + $(COMMON)/mg_str.h \ + $(COMMON)/mbuf.h \ + $(COMMON)/cs_base64.h \ + $(COMMON)/str_util.h \ + $(COMMON)/queue.h \ + mg_features.h \ + mg_net_if.h \ + mg_ssl_if.h \ + mg_net.h \ + mg_uri.h \ + mg_util.h \ + mg_http.h \ + mg_http_server.h \ + mg_http_client.h \ + mg_mqtt.h \ + mg_mqtt_server.h \ + mg_dns.h \ + mg_dns_server.h \ + mg_resolv.h \ + mg_coap.h \ + mg_sntp.h \ + mg_socks.h + +SOURCES = $(COMMON)/mg_mem.h \ + $(COMMON)/cs_base64.c \ + $(COMMON)/cs_dbg.h \ + $(COMMON)/cs_dbg.c \ + $(COMMON)/cs_dirent.h \ + $(COMMON)/cs_dirent.c \ + $(COMMON)/cs_time.c \ + $(COMMON)/cs_endian.h \ + $(COMMON)/cs_md5.c \ + $(COMMON)/cs_sha1.c \ + $(COMMON)/mbuf.c \ + $(COMMON)/mg_str.c \ + $(COMMON)/str_util.c \ + mg_net.c \ + mg_net_if_socket.h \ + mg_net_if_socks.h \ + mg_net_if.c \ + mg_net_if_socket.c \ + mg_net_if_socks.c \ + mg_ssl_if_openssl.c \ + mg_ssl_if_mbedtls.c \ + mg_uri.c \ + mg_http.c \ + mg_http_cgi.c \ + mg_http_ssi.c \ + mg_http_webdav.c \ + mg_http_websocket.c \ + mg_util.c \ + mg_mqtt.c \ + mg_mqtt_server.c \ + mg_dns.c \ + mg_dns_server.c \ + mg_resolv.c \ + mg_coap.c \ + mg_sntp.c \ + mg_socks.c \ + $(COMMON)/platforms/cc3200/cc3200_libc.c \ + $(COMMON)/platforms/msp432/msp432_libc.c \ + $(COMMON)/platforms/nrf5/nrf5_libc.c \ + $(COMMON)/platforms/simplelink/sl_fs_slfs.h \ + $(COMMON)/platforms/simplelink/sl_fs_slfs.c \ + $(COMMON)/platforms/simplelink/sl_fs.c \ + $(COMMON)/platforms/simplelink/sl_socket.c \ + $(COMMON)/platforms/simplelink/sl_mg_task.c \ + $(COMMON)/platforms/simplelink/sl_net_if.h \ + $(COMMON)/platforms/simplelink/sl_net_if.c \ + $(COMMON)/platforms/simplelink/sl_ssl_if.c \ + $(COMMON)/platforms/lwip/mg_lwip_net_if.h \ + $(COMMON)/platforms/lwip/mg_lwip_net_if.c \ + $(COMMON)/platforms/lwip/mg_lwip_ev_mgr.c \ + $(COMMON)/platforms/lwip/mg_lwip_ssl_if.c \ + $(COMMON)/platforms/wince/wince_libc.c \ + $(COMMON)/platforms/pic32/pic32_net_if.h \ + $(COMMON)/platforms/pic32/pic32_net_if.c \ + $(COMMON)/platforms/windows/windows_direct.c diff --git a/src/mongoose-6.11/src/mg_mqtt.c b/src/mongoose-6.11/src/mg_mqtt.c new file mode 100644 index 0000000..369b456 --- /dev/null +++ b/src/mongoose-6.11/src/mg_mqtt.c @@ -0,0 +1,466 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_MQTT + +#include + +#include "mg_internal.h" +#include "mg_mqtt.h" + +static uint16_t getu16(const char *p) { + const uint8_t *up = (const uint8_t *) p; + return (up[0] << 8) + up[1]; +} + +static const char *scanto(const char *p, struct mg_str *s) { + s->len = getu16(p); + s->p = p + 2; + return s->p + s->len; +} + +MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) { + uint8_t header; + size_t len = 0, len_len = 0; + const char *p, *end; + unsigned char lc = 0; + int cmd; + + if (io->len < 2) return MG_MQTT_ERROR_INCOMPLETE_MSG; + header = io->buf[0]; + cmd = header >> 4; + + /* decode mqtt variable length */ + len = len_len = 0; + p = io->buf + 1; + while ((size_t)(p - io->buf) < io->len) { + lc = *((const unsigned char *) p++); + len += (lc & 0x7f) << 7 * len_len; + len_len++; + if (!(lc & 0x80)) break; + if (len_len > 4) return MG_MQTT_ERROR_MALFORMED_MSG; + } + + end = p + len; + if (lc & 0x80 || len > (io->len - (p - io->buf))) { + return MG_MQTT_ERROR_INCOMPLETE_MSG; + } + + mm->cmd = cmd; + mm->qos = MG_MQTT_GET_QOS(header); + + switch (cmd) { + case MG_MQTT_CMD_CONNECT: { + p = scanto(p, &mm->protocol_name); + if (p > end - 4) return MG_MQTT_ERROR_MALFORMED_MSG; + mm->protocol_version = *(uint8_t *) p++; + mm->connect_flags = *(uint8_t *) p++; + mm->keep_alive_timer = getu16(p); + p += 2; + if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG; + p = scanto(p, &mm->client_id); + if (p > end) return MG_MQTT_ERROR_MALFORMED_MSG; + if (mm->connect_flags & MG_MQTT_HAS_WILL) { + if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG; + p = scanto(p, &mm->will_topic); + } + if (mm->connect_flags & MG_MQTT_HAS_WILL) { + if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG; + p = scanto(p, &mm->will_message); + } + if (mm->connect_flags & MG_MQTT_HAS_USER_NAME) { + if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG; + p = scanto(p, &mm->user_name); + } + if (mm->connect_flags & MG_MQTT_HAS_PASSWORD) { + if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG; + p = scanto(p, &mm->password); + } + if (p != end) return MG_MQTT_ERROR_MALFORMED_MSG; + + LOG(LL_DEBUG, + ("%d %2x %d proto [%.*s] client_id [%.*s] will_topic [%.*s] " + "will_msg [%.*s] user_name [%.*s] password [%.*s]", + (int) len, (int) mm->connect_flags, (int) mm->keep_alive_timer, + (int) mm->protocol_name.len, mm->protocol_name.p, + (int) mm->client_id.len, mm->client_id.p, (int) mm->will_topic.len, + mm->will_topic.p, (int) mm->will_message.len, mm->will_message.p, + (int) mm->user_name.len, mm->user_name.p, (int) mm->password.len, + mm->password.p)); + break; + } + case MG_MQTT_CMD_CONNACK: + if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG; + mm->connack_ret_code = p[1]; + break; + case MG_MQTT_CMD_PUBACK: + case MG_MQTT_CMD_PUBREC: + case MG_MQTT_CMD_PUBREL: + case MG_MQTT_CMD_PUBCOMP: + case MG_MQTT_CMD_SUBACK: + mm->message_id = getu16(p); + break; + case MG_MQTT_CMD_PUBLISH: { + p = scanto(p, &mm->topic); + if (p > end) return MG_MQTT_ERROR_MALFORMED_MSG; + if (mm->qos > 0) { + if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG; + mm->message_id = getu16(p); + p += 2; + } + mm->payload.p = p; + mm->payload.len = end - p; + break; + } + case MG_MQTT_CMD_SUBSCRIBE: + if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG; + mm->message_id = getu16(p); + p += 2; + /* + * topic expressions are left in the payload and can be parsed with + * `mg_mqtt_next_subscribe_topic` + */ + mm->payload.p = p; + mm->payload.len = end - p; + break; + default: + /* Unhandled command */ + break; + } + + mm->len = end - io->buf; + return mm->len; +} + +static void mqtt_handler(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct mbuf *io = &nc->recv_mbuf; + struct mg_mqtt_message mm; + memset(&mm, 0, sizeof(mm)); + + nc->handler(nc, ev, ev_data MG_UD_ARG(user_data)); + + switch (ev) { + case MG_EV_ACCEPT: + if (nc->proto_data == NULL) mg_set_protocol_mqtt(nc); + break; + case MG_EV_RECV: { + /* There can be multiple messages in the buffer, process them all. */ + while (1) { + int len = parse_mqtt(io, &mm); + if (len < 0) { + if (len == MG_MQTT_ERROR_MALFORMED_MSG) { + /* Protocol error. */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } else if (len == MG_MQTT_ERROR_INCOMPLETE_MSG) { + /* Not fully buffered, let's check if we have a chance to get more + * data later */ + if (nc->recv_mbuf_limit > 0 && + nc->recv_mbuf.len >= nc->recv_mbuf_limit) { + LOG(LL_ERROR, ("%p recv buffer (%lu bytes) exceeds the limit " + "%lu bytes, and not drained, closing", + nc, (unsigned long) nc->recv_mbuf.len, + (unsigned long) nc->recv_mbuf_limit)); + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + } else { + /* Should never be here */ + LOG(LL_ERROR, ("%p invalid len: %d, closing", nc, len)); + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + break; + } + + nc->handler(nc, MG_MQTT_EVENT_BASE + mm.cmd, &mm MG_UD_ARG(user_data)); + mbuf_remove(io, len); + } + break; + } + case MG_EV_POLL: { + struct mg_mqtt_proto_data *pd = + (struct mg_mqtt_proto_data *) nc->proto_data; + double now = mg_time(); + if (pd->keep_alive > 0 && pd->last_control_time > 0 && + (now - pd->last_control_time) > pd->keep_alive) { + LOG(LL_DEBUG, ("Send PINGREQ")); + mg_mqtt_ping(nc); + } + break; + } + } +} + +static void mg_mqtt_proto_data_destructor(void *proto_data) { + MG_FREE(proto_data); +} + +int mg_mqtt_match_topic_expression(struct mg_str exp, struct mg_str topic) { + /* TODO(mkm): implement real matching */ + if (memchr(exp.p, '#', exp.len)) { + /* exp `foo/#` will become `foo/` */ + exp.len -= 1; + /* + * topic should be longer than the expression: e.g. topic `foo/bar` does + * match `foo/#`, but neither `foo` nor `foo/` do. + */ + if (topic.len <= exp.len) { + return 0; + } + + /* Truncate topic so that it'll pass the next length check */ + topic.len = exp.len; + } + if (topic.len != exp.len) { + return 0; + } + return strncmp(topic.p, exp.p, exp.len) == 0; +} + +int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic) { + return mg_mqtt_match_topic_expression(mg_mk_str(exp), topic); +} + +void mg_set_protocol_mqtt(struct mg_connection *nc) { + nc->proto_handler = mqtt_handler; + nc->proto_data = MG_CALLOC(1, sizeof(struct mg_mqtt_proto_data)); + nc->proto_data_destructor = mg_mqtt_proto_data_destructor; +} + +static void mg_mqtt_prepend_header(struct mg_connection *nc, uint8_t cmd, + uint8_t flags, size_t len) { + struct mg_mqtt_proto_data *pd = (struct mg_mqtt_proto_data *) nc->proto_data; + size_t off = nc->send_mbuf.len - len; + uint8_t header = cmd << 4 | (uint8_t) flags; + + uint8_t buf[1 + sizeof(size_t)]; + uint8_t *vlen = &buf[1]; + + assert(nc->send_mbuf.len >= len); + + buf[0] = header; + + /* mqtt variable length encoding */ + do { + *vlen = len % 0x80; + len /= 0x80; + if (len > 0) *vlen |= 0x80; + vlen++; + } while (len > 0); + + mbuf_insert(&nc->send_mbuf, off, buf, vlen - buf); + pd->last_control_time = mg_time(); +} + +void mg_send_mqtt_handshake(struct mg_connection *nc, const char *client_id) { + static struct mg_send_mqtt_handshake_opts opts; + mg_send_mqtt_handshake_opt(nc, client_id, opts); +} + +void mg_send_mqtt_handshake_opt(struct mg_connection *nc, const char *client_id, + struct mg_send_mqtt_handshake_opts opts) { + uint16_t hlen, nlen, rem_len = 0; + struct mg_mqtt_proto_data *pd = (struct mg_mqtt_proto_data *) nc->proto_data; + + mg_send(nc, "\00\04MQTT\04", 7); + rem_len += 7; + + if (opts.user_name != NULL) { + opts.flags |= MG_MQTT_HAS_USER_NAME; + } + if (opts.password != NULL) { + opts.flags |= MG_MQTT_HAS_PASSWORD; + } + if (opts.will_topic != NULL && opts.will_message != NULL) { + opts.flags |= MG_MQTT_HAS_WILL; + } + if (opts.keep_alive == 0) { + opts.keep_alive = 60; + } + + mg_send(nc, &opts.flags, 1); + rem_len += 1; + + nlen = htons(opts.keep_alive); + mg_send(nc, &nlen, 2); + rem_len += 2; + + hlen = strlen(client_id); + nlen = htons((uint16_t) hlen); + mg_send(nc, &nlen, 2); + mg_send(nc, client_id, hlen); + rem_len += 2 + hlen; + + if (opts.flags & MG_MQTT_HAS_WILL) { + hlen = strlen(opts.will_topic); + nlen = htons((uint16_t) hlen); + mg_send(nc, &nlen, 2); + mg_send(nc, opts.will_topic, hlen); + rem_len += 2 + hlen; + + hlen = strlen(opts.will_message); + nlen = htons((uint16_t) hlen); + mg_send(nc, &nlen, 2); + mg_send(nc, opts.will_message, hlen); + rem_len += 2 + hlen; + } + + if (opts.flags & MG_MQTT_HAS_USER_NAME) { + hlen = strlen(opts.user_name); + nlen = htons((uint16_t) hlen); + mg_send(nc, &nlen, 2); + mg_send(nc, opts.user_name, hlen); + rem_len += 2 + hlen; + } + if (opts.flags & MG_MQTT_HAS_PASSWORD) { + hlen = strlen(opts.password); + nlen = htons((uint16_t) hlen); + mg_send(nc, &nlen, 2); + mg_send(nc, opts.password, hlen); + rem_len += 2 + hlen; + } + + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_CONNECT, 0, rem_len); + + if (pd != NULL) { + pd->keep_alive = opts.keep_alive; + } +} + +void mg_mqtt_publish(struct mg_connection *nc, const char *topic, + uint16_t message_id, int flags, const void *data, + size_t len) { + size_t old_len = nc->send_mbuf.len; + + uint16_t topic_len = htons((uint16_t) strlen(topic)); + uint16_t message_id_net = htons(message_id); + + mg_send(nc, &topic_len, 2); + mg_send(nc, topic, strlen(topic)); + if (MG_MQTT_GET_QOS(flags) > 0) { + mg_send(nc, &message_id_net, 2); + } + mg_send(nc, data, len); + + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_PUBLISH, flags, + nc->send_mbuf.len - old_len); +} + +void mg_mqtt_subscribe(struct mg_connection *nc, + const struct mg_mqtt_topic_expression *topics, + size_t topics_len, uint16_t message_id) { + size_t old_len = nc->send_mbuf.len; + + uint16_t message_id_n = htons(message_id); + size_t i; + + mg_send(nc, (char *) &message_id_n, 2); + for (i = 0; i < topics_len; i++) { + uint16_t topic_len_n = htons((uint16_t) strlen(topics[i].topic)); + mg_send(nc, &topic_len_n, 2); + mg_send(nc, topics[i].topic, strlen(topics[i].topic)); + mg_send(nc, &topics[i].qos, 1); + } + + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_SUBSCRIBE, MG_MQTT_QOS(1), + nc->send_mbuf.len - old_len); +} + +int mg_mqtt_next_subscribe_topic(struct mg_mqtt_message *msg, + struct mg_str *topic, uint8_t *qos, int pos) { + unsigned char *buf = (unsigned char *) msg->payload.p + pos; + int new_pos; + + if ((size_t) pos >= msg->payload.len) return -1; + + topic->len = buf[0] << 8 | buf[1]; + topic->p = (char *) buf + 2; + new_pos = pos + 2 + topic->len + 1; + if ((size_t) new_pos > msg->payload.len) return -1; + *qos = buf[2 + topic->len]; + return new_pos; +} + +void mg_mqtt_unsubscribe(struct mg_connection *nc, char **topics, + size_t topics_len, uint16_t message_id) { + size_t old_len = nc->send_mbuf.len; + + uint16_t message_id_n = htons(message_id); + size_t i; + + mg_send(nc, (char *) &message_id_n, 2); + for (i = 0; i < topics_len; i++) { + uint16_t topic_len_n = htons((uint16_t) strlen(topics[i])); + mg_send(nc, &topic_len_n, 2); + mg_send(nc, topics[i], strlen(topics[i])); + } + + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_UNSUBSCRIBE, MG_MQTT_QOS(1), + nc->send_mbuf.len - old_len); +} + +void mg_mqtt_connack(struct mg_connection *nc, uint8_t return_code) { + uint8_t unused = 0; + mg_send(nc, &unused, 1); + mg_send(nc, &return_code, 1); + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_CONNACK, 0, 2); +} + +/* + * Sends a command which contains only a `message_id` and a QoS level of 1. + * + * Helper function. + */ +static void mg_send_mqtt_short_command(struct mg_connection *nc, uint8_t cmd, + uint16_t message_id) { + uint16_t message_id_net = htons(message_id); + uint8_t flags = (cmd == MG_MQTT_CMD_PUBREL ? 2 : 0); + mg_send(nc, &message_id_net, 2); + mg_mqtt_prepend_header(nc, cmd, flags, 2 /* len */); +} + +void mg_mqtt_puback(struct mg_connection *nc, uint16_t message_id) { + mg_send_mqtt_short_command(nc, MG_MQTT_CMD_PUBACK, message_id); +} + +void mg_mqtt_pubrec(struct mg_connection *nc, uint16_t message_id) { + mg_send_mqtt_short_command(nc, MG_MQTT_CMD_PUBREC, message_id); +} + +void mg_mqtt_pubrel(struct mg_connection *nc, uint16_t message_id) { + mg_send_mqtt_short_command(nc, MG_MQTT_CMD_PUBREL, message_id); +} + +void mg_mqtt_pubcomp(struct mg_connection *nc, uint16_t message_id) { + mg_send_mqtt_short_command(nc, MG_MQTT_CMD_PUBCOMP, message_id); +} + +void mg_mqtt_suback(struct mg_connection *nc, uint8_t *qoss, size_t qoss_len, + uint16_t message_id) { + size_t i; + uint16_t message_id_net = htons(message_id); + mg_send(nc, &message_id_net, 2); + for (i = 0; i < qoss_len; i++) { + mg_send(nc, &qoss[i], 1); + } + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_SUBACK, MG_MQTT_QOS(1), 2 + qoss_len); +} + +void mg_mqtt_unsuback(struct mg_connection *nc, uint16_t message_id) { + mg_send_mqtt_short_command(nc, MG_MQTT_CMD_UNSUBACK, message_id); +} + +void mg_mqtt_ping(struct mg_connection *nc) { + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_PINGREQ, 0, 0); +} + +void mg_mqtt_pong(struct mg_connection *nc) { + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_PINGRESP, 0, 0); +} + +void mg_mqtt_disconnect(struct mg_connection *nc) { + mg_mqtt_prepend_header(nc, MG_MQTT_CMD_DISCONNECT, 0, 0); +} + +#endif /* MG_ENABLE_MQTT */ diff --git a/src/mongoose-6.11/src/mg_mqtt.h b/src/mongoose-6.11/src/mg_mqtt.h new file mode 100644 index 0000000..6067ccc --- /dev/null +++ b/src/mongoose-6.11/src/mg_mqtt.h @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +/* + * === MQTT API reference + */ + +#ifndef CS_MONGOOSE_SRC_MQTT_H_ +#define CS_MONGOOSE_SRC_MQTT_H_ + +#include "mg_net.h" + +struct mg_mqtt_message { + int cmd; + int qos; + int len; /* message length in the IO buffer */ + struct mg_str topic; + struct mg_str payload; + + uint8_t connack_ret_code; /* connack */ + uint16_t message_id; /* puback */ + + /* connect */ + uint8_t protocol_version; + uint8_t connect_flags; + uint16_t keep_alive_timer; + struct mg_str protocol_name; + struct mg_str client_id; + struct mg_str will_topic; + struct mg_str will_message; + struct mg_str user_name; + struct mg_str password; +}; + +struct mg_mqtt_topic_expression { + const char *topic; + uint8_t qos; +}; + +struct mg_send_mqtt_handshake_opts { + unsigned char flags; /* connection flags */ + uint16_t keep_alive; + const char *will_topic; + const char *will_message; + const char *user_name; + const char *password; +}; + +/* mg_mqtt_proto_data should be in header to allow external access to it */ +struct mg_mqtt_proto_data { + uint16_t keep_alive; + double last_control_time; +}; + +/* Message types */ +#define MG_MQTT_CMD_CONNECT 1 +#define MG_MQTT_CMD_CONNACK 2 +#define MG_MQTT_CMD_PUBLISH 3 +#define MG_MQTT_CMD_PUBACK 4 +#define MG_MQTT_CMD_PUBREC 5 +#define MG_MQTT_CMD_PUBREL 6 +#define MG_MQTT_CMD_PUBCOMP 7 +#define MG_MQTT_CMD_SUBSCRIBE 8 +#define MG_MQTT_CMD_SUBACK 9 +#define MG_MQTT_CMD_UNSUBSCRIBE 10 +#define MG_MQTT_CMD_UNSUBACK 11 +#define MG_MQTT_CMD_PINGREQ 12 +#define MG_MQTT_CMD_PINGRESP 13 +#define MG_MQTT_CMD_DISCONNECT 14 + +/* MQTT event types */ +#define MG_MQTT_EVENT_BASE 200 +#define MG_EV_MQTT_CONNECT (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_CONNECT) +#define MG_EV_MQTT_CONNACK (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_CONNACK) +#define MG_EV_MQTT_PUBLISH (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBLISH) +#define MG_EV_MQTT_PUBACK (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBACK) +#define MG_EV_MQTT_PUBREC (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBREC) +#define MG_EV_MQTT_PUBREL (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBREL) +#define MG_EV_MQTT_PUBCOMP (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBCOMP) +#define MG_EV_MQTT_SUBSCRIBE (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_SUBSCRIBE) +#define MG_EV_MQTT_SUBACK (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_SUBACK) +#define MG_EV_MQTT_UNSUBSCRIBE (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_UNSUBSCRIBE) +#define MG_EV_MQTT_UNSUBACK (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_UNSUBACK) +#define MG_EV_MQTT_PINGREQ (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PINGREQ) +#define MG_EV_MQTT_PINGRESP (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PINGRESP) +#define MG_EV_MQTT_DISCONNECT (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_DISCONNECT) + +/* Message flags */ +#define MG_MQTT_RETAIN 0x1 +#define MG_MQTT_DUP 0x4 +#define MG_MQTT_QOS(qos) ((qos) << 1) +#define MG_MQTT_GET_QOS(flags) (((flags) &0x6) >> 1) +#define MG_MQTT_SET_QOS(flags, qos) (flags) = ((flags) & ~0x6) | ((qos) << 1) + +/* Connection flags */ +#define MG_MQTT_CLEAN_SESSION 0x02 +#define MG_MQTT_HAS_WILL 0x04 +#define MG_MQTT_WILL_RETAIN 0x20 +#define MG_MQTT_HAS_PASSWORD 0x40 +#define MG_MQTT_HAS_USER_NAME 0x80 +#define MG_MQTT_GET_WILL_QOS(flags) (((flags) &0x18) >> 3) +#define MG_MQTT_SET_WILL_QOS(flags, qos) \ + (flags) = ((flags) & ~0x18) | ((qos) << 3) + +/* CONNACK return codes */ +#define MG_EV_MQTT_CONNACK_ACCEPTED 0 +#define MG_EV_MQTT_CONNACK_UNACCEPTABLE_VERSION 1 +#define MG_EV_MQTT_CONNACK_IDENTIFIER_REJECTED 2 +#define MG_EV_MQTT_CONNACK_SERVER_UNAVAILABLE 3 +#define MG_EV_MQTT_CONNACK_BAD_AUTH 4 +#define MG_EV_MQTT_CONNACK_NOT_AUTHORIZED 5 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Attaches a built-in MQTT event handler to the given connection. + * + * The user-defined event handler will receive following extra events: + * + * - MG_EV_MQTT_CONNACK + * - MG_EV_MQTT_PUBLISH + * - MG_EV_MQTT_PUBACK + * - MG_EV_MQTT_PUBREC + * - MG_EV_MQTT_PUBREL + * - MG_EV_MQTT_PUBCOMP + * - MG_EV_MQTT_SUBACK + */ +void mg_set_protocol_mqtt(struct mg_connection *nc); + +/* Sends an MQTT handshake. */ +void mg_send_mqtt_handshake(struct mg_connection *nc, const char *client_id); + +/* Sends an MQTT handshake with optional parameters. */ +void mg_send_mqtt_handshake_opt(struct mg_connection *nc, const char *client_id, + struct mg_send_mqtt_handshake_opts); + +/* Publishes a message to a given topic. */ +void mg_mqtt_publish(struct mg_connection *nc, const char *topic, + uint16_t message_id, int flags, const void *data, + size_t len); + +/* Subscribes to a bunch of topics. */ +void mg_mqtt_subscribe(struct mg_connection *nc, + const struct mg_mqtt_topic_expression *topics, + size_t topics_len, uint16_t message_id); + +/* Unsubscribes from a bunch of topics. */ +void mg_mqtt_unsubscribe(struct mg_connection *nc, char **topics, + size_t topics_len, uint16_t message_id); + +/* Sends a DISCONNECT command. */ +void mg_mqtt_disconnect(struct mg_connection *nc); + +/* Sends a CONNACK command with a given `return_code`. */ +void mg_mqtt_connack(struct mg_connection *nc, uint8_t return_code); + +/* Sends a PUBACK command with a given `message_id`. */ +void mg_mqtt_puback(struct mg_connection *nc, uint16_t message_id); + +/* Sends a PUBREC command with a given `message_id`. */ +void mg_mqtt_pubrec(struct mg_connection *nc, uint16_t message_id); + +/* Sends a PUBREL command with a given `message_id`. */ +void mg_mqtt_pubrel(struct mg_connection *nc, uint16_t message_id); + +/* Sends a PUBCOMP command with a given `message_id`. */ +void mg_mqtt_pubcomp(struct mg_connection *nc, uint16_t message_id); + +/* + * Sends a SUBACK command with a given `message_id` + * and a sequence of granted QoSs. + */ +void mg_mqtt_suback(struct mg_connection *nc, uint8_t *qoss, size_t qoss_len, + uint16_t message_id); + +/* Sends a UNSUBACK command with a given `message_id`. */ +void mg_mqtt_unsuback(struct mg_connection *nc, uint16_t message_id); + +/* Sends a PINGREQ command. */ +void mg_mqtt_ping(struct mg_connection *nc); + +/* Sends a PINGRESP command. */ +void mg_mqtt_pong(struct mg_connection *nc); + +/* + * Extracts the next topic expression from a SUBSCRIBE command payload. + * + * The topic expression name will point to a string in the payload buffer. + * Returns the pos of the next topic expression or -1 when the list + * of topics is exhausted. + */ +int mg_mqtt_next_subscribe_topic(struct mg_mqtt_message *msg, + struct mg_str *topic, uint8_t *qos, int pos); + +/* + * Matches a topic against a topic expression + * + * Returns 1 if it matches; 0 otherwise. + */ +int mg_mqtt_match_topic_expression(struct mg_str exp, struct mg_str topic); + +/* + * Same as `mg_mqtt_match_topic_expression()`, but takes `exp` as a + * NULL-terminated string. + */ +int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_MONGOOSE_SRC_MQTT_H_ */ diff --git a/src/mongoose-6.11/src/mg_mqtt_client.h b/src/mongoose-6.11/src/mg_mqtt_client.h new file mode 100644 index 0000000..998414d --- /dev/null +++ b/src/mongoose-6.11/src/mg_mqtt_client.h @@ -0,0 +1,8 @@ +/* + * === API reference + */ + +#ifndef CS_MONGOOSE_SRC_MQTT_CLIENT_H_ +#define CS_MONGOOSE_SRC_MQTT_CLIENT_H_ + +#endif /* CS_MONGOOSE_SRC_MQTT_CLIENT_H_ */ diff --git a/src/mongoose-6.11/src/mg_mqtt_server.c b/src/mongoose-6.11/src/mg_mqtt_server.c new file mode 100644 index 0000000..cb56648 --- /dev/null +++ b/src/mongoose-6.11/src/mg_mqtt_server.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#include "mg_internal.h" +#include "mg_mqtt_server.h" + +#if MG_ENABLE_MQTT_BROKER + +static void mg_mqtt_session_init(struct mg_mqtt_broker *brk, + struct mg_mqtt_session *s, + struct mg_connection *nc) { + s->brk = brk; + s->subscriptions = NULL; + s->num_subscriptions = 0; + s->nc = nc; +} + +static void mg_mqtt_add_session(struct mg_mqtt_session *s) { + LIST_INSERT_HEAD(&s->brk->sessions, s, link); +} + +static void mg_mqtt_remove_session(struct mg_mqtt_session *s) { + LIST_REMOVE(s, link); +} + +static void mg_mqtt_destroy_session(struct mg_mqtt_session *s) { + size_t i; + for (i = 0; i < s->num_subscriptions; i++) { + MG_FREE((void *) s->subscriptions[i].topic); + } + MG_FREE(s->subscriptions); + MG_FREE(s); +} + +static void mg_mqtt_close_session(struct mg_mqtt_session *s) { + mg_mqtt_remove_session(s); + mg_mqtt_destroy_session(s); +} + +void mg_mqtt_broker_init(struct mg_mqtt_broker *brk, void *user_data) { + LIST_INIT(&brk->sessions); + brk->user_data = user_data; +} + +static void mg_mqtt_broker_handle_connect(struct mg_mqtt_broker *brk, + struct mg_connection *nc) { + struct mg_mqtt_session *s = + (struct mg_mqtt_session *) MG_CALLOC(1, sizeof *s); + if (s == NULL) { + /* LCOV_EXCL_START */ + mg_mqtt_connack(nc, MG_EV_MQTT_CONNACK_SERVER_UNAVAILABLE); + return; + /* LCOV_EXCL_STOP */ + } + + /* TODO(mkm): check header (magic and version) */ + + mg_mqtt_session_init(brk, s, nc); + nc->priv_2 = s; + mg_mqtt_add_session(s); + + mg_mqtt_connack(nc, MG_EV_MQTT_CONNACK_ACCEPTED); +} + +static void mg_mqtt_broker_handle_subscribe(struct mg_connection *nc, + struct mg_mqtt_message *msg) { + struct mg_mqtt_session *ss = (struct mg_mqtt_session *) nc->priv_2; + uint8_t qoss[MG_MQTT_MAX_SESSION_SUBSCRIPTIONS]; + size_t num_subs = 0; + struct mg_str topic; + uint8_t qos; + int pos; + struct mg_mqtt_topic_expression *te; + + for (pos = 0; + (pos = mg_mqtt_next_subscribe_topic(msg, &topic, &qos, pos)) != -1;) { + if (num_subs >= sizeof(MG_MQTT_MAX_SESSION_SUBSCRIPTIONS) || + (ss->num_subscriptions + num_subs >= + MG_MQTT_MAX_SESSION_SUBSCRIPTIONS)) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return; + } + qoss[num_subs++] = qos; + } + + if (num_subs > 0) { + te = (struct mg_mqtt_topic_expression *) MG_REALLOC( + ss->subscriptions, + sizeof(*ss->subscriptions) * (ss->num_subscriptions + num_subs)); + if (te == NULL) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return; + } + ss->subscriptions = te; + for (pos = 0; + pos < (int) msg->payload.len && + (pos = mg_mqtt_next_subscribe_topic(msg, &topic, &qos, pos)) != -1; + ss->num_subscriptions++) { + te = &ss->subscriptions[ss->num_subscriptions]; + te->topic = (char *) MG_MALLOC(topic.len + 1); + te->qos = qos; + memcpy((char *) te->topic, topic.p, topic.len); + ((char *) te->topic)[topic.len] = '\0'; + } + } + + if (pos == (int) msg->payload.len) { + mg_mqtt_suback(nc, qoss, num_subs, msg->message_id); + } else { + /* We did not fully parse the payload, something must be wrong. */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } +} + +static void mg_mqtt_broker_handle_publish(struct mg_mqtt_broker *brk, + struct mg_mqtt_message *msg) { + struct mg_mqtt_session *s; + size_t i; + + for (s = mg_mqtt_next(brk, NULL); s != NULL; s = mg_mqtt_next(brk, s)) { + for (i = 0; i < s->num_subscriptions; i++) { + if (mg_mqtt_vmatch_topic_expression(s->subscriptions[i].topic, + msg->topic)) { + char buf[100], *p = buf; + mg_asprintf(&p, sizeof(buf), "%.*s", (int) msg->topic.len, + msg->topic.p); + if (p == NULL) { + return; + } + mg_mqtt_publish(s->nc, p, 0, 0, msg->payload.p, msg->payload.len); + if (p != buf) { + MG_FREE(p); + } + break; + } + } + } +} + +void mg_mqtt_broker(struct mg_connection *nc, int ev, void *data) { + struct mg_mqtt_message *msg = (struct mg_mqtt_message *) data; + struct mg_mqtt_broker *brk; + + if (nc->listener) { + brk = (struct mg_mqtt_broker *) nc->listener->priv_2; + } else { + brk = (struct mg_mqtt_broker *) nc->priv_2; + } + + switch (ev) { + case MG_EV_ACCEPT: + if (nc->proto_data == NULL) mg_set_protocol_mqtt(nc); + nc->priv_2 = NULL; /* Clear up the inherited pointer to broker */ + break; + case MG_EV_MQTT_CONNECT: + if (nc->priv_2 == NULL) { + mg_mqtt_broker_handle_connect(brk, nc); + } else { + /* Repeated CONNECT */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + break; + case MG_EV_MQTT_SUBSCRIBE: + if (nc->priv_2 != NULL) { + mg_mqtt_broker_handle_subscribe(nc, msg); + } else { + /* Subscribe before CONNECT */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + break; + case MG_EV_MQTT_PUBLISH: + if (nc->priv_2 != NULL) { + mg_mqtt_broker_handle_publish(brk, msg); + } else { + /* Publish before CONNECT */ + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + break; + case MG_EV_CLOSE: + if (nc->listener && nc->priv_2 != NULL) { + mg_mqtt_close_session((struct mg_mqtt_session *) nc->priv_2); + } + break; + } +} + +struct mg_mqtt_session *mg_mqtt_next(struct mg_mqtt_broker *brk, + struct mg_mqtt_session *s) { + return s == NULL ? LIST_FIRST(&brk->sessions) : LIST_NEXT(s, link); +} + +#endif /* MG_ENABLE_MQTT_BROKER */ diff --git a/src/mongoose-6.11/src/mg_mqtt_server.h b/src/mongoose-6.11/src/mg_mqtt_server.h new file mode 100644 index 0000000..8c34b5e --- /dev/null +++ b/src/mongoose-6.11/src/mg_mqtt_server.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +/* + * === MQTT Server API reference + */ + +#ifndef CS_MONGOOSE_SRC_MQTT_BROKER_H_ +#define CS_MONGOOSE_SRC_MQTT_BROKER_H_ + +#if MG_ENABLE_MQTT_BROKER + +#include "common/queue.h" +#include "mg_mqtt.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MG_MQTT_MAX_SESSION_SUBSCRIPTIONS +#define MG_MQTT_MAX_SESSION_SUBSCRIPTIONS 512 +#endif + +struct mg_mqtt_broker; + +/* MQTT session (Broker side). */ +struct mg_mqtt_session { + struct mg_mqtt_broker *brk; /* Broker */ + LIST_ENTRY(mg_mqtt_session) link; /* mg_mqtt_broker::sessions linkage */ + struct mg_connection *nc; /* Connection with the client */ + size_t num_subscriptions; /* Size of `subscriptions` array */ + void *user_data; /* User data */ + struct mg_mqtt_topic_expression *subscriptions; +}; + +/* MQTT broker. */ +struct mg_mqtt_broker { + LIST_HEAD(_mg_sesshead, mg_mqtt_session) sessions; /* Session list */ + void *user_data; /* User data */ +}; + +/* Initialises a MQTT broker. */ +void mg_mqtt_broker_init(struct mg_mqtt_broker *brk, void *user_data); + +/* + * Processes a MQTT broker message. + * + * The listening connection expects a pointer to an initialised + * `mg_mqtt_broker` structure in the `user_data` field. + * + * Basic usage: + * + * ```c + * mg_mqtt_broker_init(&brk, NULL); + * + * if ((nc = mg_bind(&mgr, address, mg_mqtt_broker)) == NULL) { + * // fail; + * } + * nc->user_data = &brk; + * ``` + * + * New incoming connections will receive a `mg_mqtt_session` structure + * in the connection `user_data`. The original `user_data` will be stored + * in the `user_data` field of the session structure. This allows the user + * handler to store user data before `mg_mqtt_broker` creates the session. + * + * Since only the MG_EV_ACCEPT message is processed by the listening socket, + * for most events the `user_data` will thus point to a `mg_mqtt_session`. + */ +void mg_mqtt_broker(struct mg_connection *brk, int ev, void *data); + +/* + * Iterates over all MQTT session connections. Example: + * + * ```c + * struct mg_mqtt_session *s; + * for (s = mg_mqtt_next(brk, NULL); s != NULL; s = mg_mqtt_next(brk, s)) { + * // Do something + * } + * ``` + */ +struct mg_mqtt_session *mg_mqtt_next(struct mg_mqtt_broker *brk, + struct mg_mqtt_session *s); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MG_ENABLE_MQTT_BROKER */ +#endif /* CS_MONGOOSE_SRC_MQTT_BROKER_H_ */ diff --git a/src/mongoose-6.11/src/mg_net.c b/src/mongoose-6.11/src/mg_net.c new file mode 100644 index 0000000..70e2cc8 --- /dev/null +++ b/src/mongoose-6.11/src/mg_net.c @@ -0,0 +1,1008 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + * + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +#include "common/cs_time.h" +#include "mg_dns.h" +#include "mg_internal.h" +#include "mg_resolv.h" +#include "mg_util.h" + +#define MG_MAX_HOST_LEN 200 + +#define MG_COPY_COMMON_CONNECTION_OPTIONS(dst, src) \ + memcpy(dst, src, sizeof(*dst)); + +/* Which flags can be pre-set by the user at connection creation time. */ +#define _MG_ALLOWED_CONNECT_FLAGS_MASK \ + (MG_F_USER_1 | MG_F_USER_2 | MG_F_USER_3 | MG_F_USER_4 | MG_F_USER_5 | \ + MG_F_USER_6 | MG_F_WEBSOCKET_NO_DEFRAG | MG_F_ENABLE_BROADCAST) +/* Which flags should be modifiable by user's callbacks. */ +#define _MG_CALLBACK_MODIFIABLE_FLAGS_MASK \ + (MG_F_USER_1 | MG_F_USER_2 | MG_F_USER_3 | MG_F_USER_4 | MG_F_USER_5 | \ + MG_F_USER_6 | MG_F_WEBSOCKET_NO_DEFRAG | MG_F_SEND_AND_CLOSE | \ + MG_F_CLOSE_IMMEDIATELY | MG_F_IS_WEBSOCKET | MG_F_DELETE_CHUNK) + +#ifndef intptr_t +#define intptr_t long +#endif + +MG_INTERNAL void mg_add_conn(struct mg_mgr *mgr, struct mg_connection *c) { + DBG(("%p %p", mgr, c)); + c->mgr = mgr; + c->next = mgr->active_connections; + mgr->active_connections = c; + c->prev = NULL; + if (c->next != NULL) c->next->prev = c; + if (c->sock != INVALID_SOCKET) { + c->iface->vtable->add_conn(c); + } +} + +MG_INTERNAL void mg_remove_conn(struct mg_connection *conn) { + if (conn->prev == NULL) conn->mgr->active_connections = conn->next; + if (conn->prev) conn->prev->next = conn->next; + if (conn->next) conn->next->prev = conn->prev; + conn->prev = conn->next = NULL; + conn->iface->vtable->remove_conn(conn); +} + +MG_INTERNAL void mg_call(struct mg_connection *nc, + mg_event_handler_t ev_handler, void *user_data, int ev, + void *ev_data) { + static int nesting_level = 0; + nesting_level++; + if (ev_handler == NULL) { + /* + * If protocol handler is specified, call it. Otherwise, call user-specified + * event handler. + */ + ev_handler = nc->proto_handler ? nc->proto_handler : nc->handler; + } + if (ev != MG_EV_POLL) { + DBG(("%p %s ev=%d ev_data=%p flags=%lu rmbl=%d smbl=%d", nc, + ev_handler == nc->handler ? "user" : "proto", ev, ev_data, nc->flags, + (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); + } + +#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP + if (nc->mgr->hexdump_file != NULL && ev != MG_EV_POLL && ev != MG_EV_RECV && + ev != MG_EV_SEND /* handled separately */) { + mg_hexdump_connection(nc, nc->mgr->hexdump_file, NULL, 0, ev); + } +#endif + if (ev_handler != NULL) { + unsigned long flags_before = nc->flags; + size_t recv_mbuf_before = nc->recv_mbuf.len, recved; + ev_handler(nc, ev, ev_data MG_UD_ARG(user_data)); + recved = (recv_mbuf_before - nc->recv_mbuf.len); + /* Prevent user handler from fiddling with system flags. */ + if (ev_handler == nc->handler && nc->flags != flags_before) { + nc->flags = (flags_before & ~_MG_CALLBACK_MODIFIABLE_FLAGS_MASK) | + (nc->flags & _MG_CALLBACK_MODIFIABLE_FLAGS_MASK); + } + /* It's important to not double-count recved bytes, and since mg_call can be + * called recursively (e.g. proto_handler invokes user handler), we keep + * track of recursion and only report received bytes at the top level. */ + if (nesting_level == 1 && recved > 0 && !(nc->flags & MG_F_UDP)) { + nc->iface->vtable->recved(nc, recved); + } + } + if (ev != MG_EV_POLL) { + DBG(("%p after %s flags=%lu rmbl=%d smbl=%d", nc, + ev_handler == nc->handler ? "user" : "proto", nc->flags, + (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); + } + nesting_level--; +#if !MG_ENABLE_CALLBACK_USERDATA + (void) user_data; +#endif +} + +void mg_if_timer(struct mg_connection *c, double now) { + if (c->ev_timer_time > 0 && now >= c->ev_timer_time) { + double old_value = c->ev_timer_time; + c->ev_timer_time = 0; + mg_call(c, NULL, c->user_data, MG_EV_TIMER, &old_value); + } +} + +void mg_if_poll(struct mg_connection *nc, time_t now) { + if (!(nc->flags & MG_F_SSL) || (nc->flags & MG_F_SSL_HANDSHAKE_DONE)) { + mg_call(nc, NULL, nc->user_data, MG_EV_POLL, &now); + } +} + +void mg_destroy_conn(struct mg_connection *conn, int destroy_if) { + if (destroy_if) conn->iface->vtable->destroy_conn(conn); + if (conn->proto_data != NULL && conn->proto_data_destructor != NULL) { + conn->proto_data_destructor(conn->proto_data); + } +#if MG_ENABLE_SSL + mg_ssl_if_conn_free(conn); +#endif + mbuf_free(&conn->recv_mbuf); + mbuf_free(&conn->send_mbuf); + + memset(conn, 0, sizeof(*conn)); + MG_FREE(conn); +} + +void mg_close_conn(struct mg_connection *conn) { + DBG(("%p %lu %d", conn, conn->flags, conn->sock)); +#if MG_ENABLE_SSL + if (conn->flags & MG_F_SSL_HANDSHAKE_DONE) { + mg_ssl_if_conn_close_notify(conn); + } +#endif + mg_remove_conn(conn); + conn->iface->vtable->destroy_conn(conn); + mg_call(conn, NULL, conn->user_data, MG_EV_CLOSE, NULL); + mg_destroy_conn(conn, 0 /* destroy_if */); +} + +void mg_mgr_init(struct mg_mgr *m, void *user_data) { + struct mg_mgr_init_opts opts; + memset(&opts, 0, sizeof(opts)); + mg_mgr_init_opt(m, user_data, opts); +} + +void mg_mgr_init_opt(struct mg_mgr *m, void *user_data, + struct mg_mgr_init_opts opts) { + memset(m, 0, sizeof(*m)); +#if MG_ENABLE_BROADCAST + m->ctl[0] = m->ctl[1] = INVALID_SOCKET; +#endif + m->user_data = user_data; + +#ifdef _WIN32 + { + WSADATA data; + WSAStartup(MAKEWORD(2, 2), &data); + } +#elif defined(__unix__) + /* Ignore SIGPIPE signal, so if client cancels the request, it + * won't kill the whole process. */ + signal(SIGPIPE, SIG_IGN); +#endif + +#if MG_ENABLE_SSL + { + static int init_done; + if (!init_done) { + mg_ssl_if_init(); + init_done++; + } + } +#endif + { + int i; + if (opts.num_ifaces == 0) { + opts.num_ifaces = mg_num_ifaces; + opts.ifaces = mg_ifaces; + } + if (opts.main_iface != NULL) { + opts.ifaces[MG_MAIN_IFACE] = opts.main_iface; + } + m->num_ifaces = opts.num_ifaces; + m->ifaces = + (struct mg_iface **) MG_MALLOC(sizeof(*m->ifaces) * opts.num_ifaces); + for (i = 0; i < mg_num_ifaces; i++) { + m->ifaces[i] = mg_if_create_iface(opts.ifaces[i], m); + m->ifaces[i]->vtable->init(m->ifaces[i]); + } + } + if (opts.nameserver != NULL) { + m->nameserver = strdup(opts.nameserver); + } + DBG(("==================================")); + DBG(("init mgr=%p", m)); +} + +void mg_mgr_free(struct mg_mgr *m) { + struct mg_connection *conn, *tmp_conn; + + DBG(("%p", m)); + if (m == NULL) return; + /* Do one last poll, see https://github.com/cesanta/mongoose/issues/286 */ + mg_mgr_poll(m, 0); + +#if MG_ENABLE_BROADCAST + if (m->ctl[0] != INVALID_SOCKET) closesocket(m->ctl[0]); + if (m->ctl[1] != INVALID_SOCKET) closesocket(m->ctl[1]); + m->ctl[0] = m->ctl[1] = INVALID_SOCKET; +#endif + + for (conn = m->active_connections; conn != NULL; conn = tmp_conn) { + tmp_conn = conn->next; + mg_close_conn(conn); + } + + { + int i; + for (i = 0; i < m->num_ifaces; i++) { + m->ifaces[i]->vtable->free(m->ifaces[i]); + MG_FREE(m->ifaces[i]); + } + MG_FREE(m->ifaces); + } + + MG_FREE((char *) m->nameserver); +} + +time_t mg_mgr_poll(struct mg_mgr *m, int timeout_ms) { + int i; + time_t now = 0; /* oh GCC, seriously ? */ + + if (m->num_ifaces == 0) { + LOG(LL_ERROR, ("cannot poll: no interfaces")); + return 0; + } + + for (i = 0; i < m->num_ifaces; i++) { + now = m->ifaces[i]->vtable->poll(m->ifaces[i], timeout_ms); + } + return now; +} + +int mg_vprintf(struct mg_connection *nc, const char *fmt, va_list ap) { + char mem[MG_VPRINTF_BUFFER_SIZE], *buf = mem; + int len; + + if ((len = mg_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) { + mg_send(nc, buf, len); + } + if (buf != mem && buf != NULL) { + MG_FREE(buf); /* LCOV_EXCL_LINE */ + } /* LCOV_EXCL_LINE */ + + return len; +} + +int mg_printf(struct mg_connection *conn, const char *fmt, ...) { + int len; + va_list ap; + va_start(ap, fmt); + len = mg_vprintf(conn, fmt, ap); + va_end(ap); + return len; +} + +#if MG_ENABLE_SYNC_RESOLVER +/* TODO(lsm): use non-blocking resolver */ +static int mg_resolve2(const char *host, struct in_addr *ina) { +#if MG_ENABLE_GETADDRINFO + int rv = 0; + struct addrinfo hints, *servinfo, *p; + struct sockaddr_in *h = NULL; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + if ((rv = getaddrinfo(host, NULL, NULL, &servinfo)) != 0) { + DBG(("getaddrinfo(%s) failed: %s", host, strerror(mg_get_errno()))); + return 0; + } + for (p = servinfo; p != NULL; p = p->ai_next) { + memcpy(&h, &p->ai_addr, sizeof(struct sockaddr_in *)); + memcpy(ina, &h->sin_addr, sizeof(ina)); + } + freeaddrinfo(servinfo); + return 1; +#else + struct hostent *he; + if ((he = gethostbyname(host)) == NULL) { + DBG(("gethostbyname(%s) failed: %s", host, strerror(mg_get_errno()))); + } else { + memcpy(ina, he->h_addr_list[0], sizeof(*ina)); + return 1; + } + return 0; +#endif /* MG_ENABLE_GETADDRINFO */ +} + +int mg_resolve(const char *host, char *buf, size_t n) { + struct in_addr ad; + return mg_resolve2(host, &ad) ? snprintf(buf, n, "%s", inet_ntoa(ad)) : 0; +} +#endif /* MG_ENABLE_SYNC_RESOLVER */ + +MG_INTERNAL struct mg_connection *mg_create_connection_base( + struct mg_mgr *mgr, mg_event_handler_t callback, + struct mg_add_sock_opts opts) { + struct mg_connection *conn; + + if ((conn = (struct mg_connection *) MG_CALLOC(1, sizeof(*conn))) != NULL) { + conn->sock = INVALID_SOCKET; + conn->handler = callback; + conn->mgr = mgr; + conn->last_io_time = (time_t) mg_time(); + conn->iface = + (opts.iface != NULL ? opts.iface : mgr->ifaces[MG_MAIN_IFACE]); + conn->flags = opts.flags & _MG_ALLOWED_CONNECT_FLAGS_MASK; + conn->user_data = opts.user_data; + /* + * SIZE_MAX is defined as a long long constant in + * system headers on some platforms and so it + * doesn't compile with pedantic ansi flags. + */ + conn->recv_mbuf_limit = ~0; + } else { + MG_SET_PTRPTR(opts.error_string, "failed to create connection"); + } + + return conn; +} + +MG_INTERNAL struct mg_connection *mg_create_connection( + struct mg_mgr *mgr, mg_event_handler_t callback, + struct mg_add_sock_opts opts) { + struct mg_connection *conn = mg_create_connection_base(mgr, callback, opts); + + if (conn != NULL && !conn->iface->vtable->create_conn(conn)) { + MG_FREE(conn); + conn = NULL; + } + if (conn == NULL) { + MG_SET_PTRPTR(opts.error_string, "failed to init connection"); + } + + return conn; +} + +/* + * Address format: [PROTO://][HOST]:PORT + * + * HOST could be IPv4/IPv6 address or a host name. + * `host` is a destination buffer to hold parsed HOST part. Should be at least + * MG_MAX_HOST_LEN bytes long. + * `proto` is a returned socket type, either SOCK_STREAM or SOCK_DGRAM + * + * Return: + * -1 on parse error + * 0 if HOST needs DNS lookup + * >0 length of the address string + */ +MG_INTERNAL int mg_parse_address(const char *str, union socket_address *sa, + int *proto, char *host, size_t host_len) { + unsigned int a, b, c, d, port = 0; + int ch, len = 0; +#if MG_ENABLE_IPV6 + char buf[100]; +#endif + + /* + * MacOS needs that. If we do not zero it, subsequent bind() will fail. + * Also, all-zeroes in the socket address means binding to all addresses + * for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT). + */ + memset(sa, 0, sizeof(*sa)); + sa->sin.sin_family = AF_INET; + + *proto = SOCK_STREAM; + + if (strncmp(str, "udp://", 6) == 0) { + str += 6; + *proto = SOCK_DGRAM; + } else if (strncmp(str, "tcp://", 6) == 0) { + str += 6; + } + + if (sscanf(str, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) { + /* Bind to a specific IPv4 address, e.g. 192.168.1.5:8080 */ + sa->sin.sin_addr.s_addr = + htonl(((uint32_t) a << 24) | ((uint32_t) b << 16) | c << 8 | d); + sa->sin.sin_port = htons((uint16_t) port); +#if MG_ENABLE_IPV6 + } else if (sscanf(str, "[%99[^]]]:%u%n", buf, &port, &len) == 2 && + inet_pton(AF_INET6, buf, &sa->sin6.sin6_addr)) { + /* IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080 */ + sa->sin6.sin6_family = AF_INET6; + sa->sin.sin_port = htons((uint16_t) port); +#endif +#if MG_ENABLE_ASYNC_RESOLVER + } else if (strlen(str) < host_len && + sscanf(str, "%[^ :]:%u%n", host, &port, &len) == 2) { + sa->sin.sin_port = htons((uint16_t) port); + if (mg_resolve_from_hosts_file(host, sa) != 0) { + /* + * if resolving from hosts file failed and the host + * we are trying to resolve is `localhost` - we should + * try to resolve it using `gethostbyname` and do not try + * to resolve it via DNS server if gethostbyname has failed too + */ + if (mg_ncasecmp(host, "localhost", 9) != 0) { + return 0; + } + +#if MG_ENABLE_SYNC_RESOLVER + if (!mg_resolve2(host, &sa->sin.sin_addr)) { + return -1; + } +#else + return -1; +#endif + } +#endif + } else if (sscanf(str, ":%u%n", &port, &len) == 1 || + sscanf(str, "%u%n", &port, &len) == 1) { + /* If only port is specified, bind to IPv4, INADDR_ANY */ + sa->sin.sin_port = htons((uint16_t) port); + } else { + return -1; + } + + /* Required for MG_ENABLE_ASYNC_RESOLVER=0 */ + (void) host; + (void) host_len; + + ch = str[len]; /* Character that follows the address */ + return port < 0xffffUL && (ch == '\0' || ch == ',' || isspace(ch)) ? len : -1; +} + +struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc) { + struct mg_add_sock_opts opts; + struct mg_connection *nc; + memset(&opts, 0, sizeof(opts)); + nc = mg_create_connection(lc->mgr, lc->handler, opts); + if (nc == NULL) return NULL; + nc->listener = lc; + nc->proto_handler = lc->proto_handler; + nc->user_data = lc->user_data; + nc->recv_mbuf_limit = lc->recv_mbuf_limit; + nc->iface = lc->iface; + if (lc->flags & MG_F_SSL) nc->flags |= MG_F_SSL; + mg_add_conn(nc->mgr, nc); + DBG(("%p %p %d %d", lc, nc, nc->sock, (int) nc->flags)); + return nc; +} + +void mg_if_accept_tcp_cb(struct mg_connection *nc, union socket_address *sa, + size_t sa_len) { + (void) sa_len; + nc->sa = *sa; + mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa); +} + +void mg_send(struct mg_connection *nc, const void *buf, int len) { + nc->last_io_time = (time_t) mg_time(); + if (nc->flags & MG_F_UDP) { + nc->iface->vtable->udp_send(nc, buf, len); + } else { + nc->iface->vtable->tcp_send(nc, buf, len); + } +} + +void mg_if_sent_cb(struct mg_connection *nc, int num_sent) { + DBG(("%p %d", nc, num_sent)); +#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP + if (nc->mgr && nc->mgr->hexdump_file != NULL) { + char *buf = nc->send_mbuf.buf; + mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, num_sent, MG_EV_SEND); + } +#endif + if (num_sent < 0) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } else { + mbuf_remove(&nc->send_mbuf, num_sent); + mbuf_trim(&nc->send_mbuf); + } + mg_call(nc, NULL, nc->user_data, MG_EV_SEND, &num_sent); +} + +MG_INTERNAL void mg_recv_common(struct mg_connection *nc, void *buf, int len, + int own) { + DBG(("%p %d %u", nc, len, (unsigned int) nc->recv_mbuf.len)); + +#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP + if (nc->mgr && nc->mgr->hexdump_file != NULL) { + mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, len, MG_EV_RECV); + } +#endif + + if (nc->flags & MG_F_CLOSE_IMMEDIATELY) { + DBG(("%p discarded %d bytes", nc, len)); + /* + * This connection will not survive next poll. Do not deliver events, + * send data to /dev/null without acking. + */ + if (own) { + MG_FREE(buf); + } + return; + } + nc->last_io_time = (time_t) mg_time(); + if (!own) { + mbuf_append(&nc->recv_mbuf, buf, len); + } else if (nc->recv_mbuf.len == 0) { + /* Adopt buf as recv_mbuf's backing store. */ + mbuf_free(&nc->recv_mbuf); + nc->recv_mbuf.buf = (char *) buf; + nc->recv_mbuf.size = nc->recv_mbuf.len = len; + } else { + mbuf_append(&nc->recv_mbuf, buf, len); + MG_FREE(buf); + } + mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &len); +} + +void mg_if_recv_tcp_cb(struct mg_connection *nc, void *buf, int len, int own) { + mg_recv_common(nc, buf, len, own); +} + +void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, + union socket_address *sa, size_t sa_len) { + assert(nc->flags & MG_F_UDP); + DBG(("%p %u", nc, (unsigned int) len)); + if (nc->flags & MG_F_LISTENING) { + struct mg_connection *lc = nc; + /* + * Do we have an existing connection for this source? + * This is very inefficient for long connection lists. + */ + for (nc = mg_next(lc->mgr, NULL); nc != NULL; nc = mg_next(lc->mgr, nc)) { + if (memcmp(&nc->sa.sa, &sa->sa, sa_len) == 0 && nc->listener == lc) { + break; + } + } + if (nc == NULL) { + struct mg_add_sock_opts opts; + memset(&opts, 0, sizeof(opts)); + /* Create fake connection w/out sock initialization */ + nc = mg_create_connection_base(lc->mgr, lc->handler, opts); + if (nc != NULL) { + nc->sock = lc->sock; + nc->listener = lc; + nc->sa = *sa; + nc->proto_handler = lc->proto_handler; + nc->user_data = lc->user_data; + nc->recv_mbuf_limit = lc->recv_mbuf_limit; + nc->flags = MG_F_UDP; + /* + * Long-lived UDP "connections" i.e. interactions that involve more + * than one request and response are rare, most are transactional: + * response is sent and the "connection" is closed. Or - should be. + * But users (including ourselves) tend to forget about that part, + * because UDP is connectionless and one does not think about + * processing a UDP request as handling a connection that needs to be + * closed. Thus, we begin with SEND_AND_CLOSE flag set, which should + * be a reasonable default for most use cases, but it is possible to + * turn it off the connection should be kept alive after processing. + */ + nc->flags |= MG_F_SEND_AND_CLOSE; + mg_add_conn(lc->mgr, nc); + mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa); + } else { + DBG(("OOM")); + /* No return here, we still need to drop on the floor */ + } + } + } + if (nc != NULL) { + mg_recv_common(nc, buf, len, 1); + } else { + /* Drop on the floor. */ + MG_FREE(buf); + } +} + +/* + * Schedules an async connect for a resolved address and proto. + * Called from two places: `mg_connect_opt()` and from async resolver. + * When called from the async resolver, it must trigger `MG_EV_CONNECT` event + * with a failure flag to indicate connection failure. + */ +MG_INTERNAL struct mg_connection *mg_do_connect(struct mg_connection *nc, + int proto, + union socket_address *sa) { + DBG(("%p %s://%s:%hu", nc, proto == SOCK_DGRAM ? "udp" : "tcp", + inet_ntoa(sa->sin.sin_addr), ntohs(sa->sin.sin_port))); + + nc->flags |= MG_F_CONNECTING; + if (proto == SOCK_DGRAM) { + nc->iface->vtable->connect_udp(nc); + } else { + nc->iface->vtable->connect_tcp(nc, sa); + } + mg_add_conn(nc->mgr, nc); + return nc; +} + +void mg_if_connect_cb(struct mg_connection *nc, int err) { + DBG(("%p connect, err=%d", nc, err)); + nc->flags &= ~MG_F_CONNECTING; + if (err != 0) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + mg_call(nc, NULL, nc->user_data, MG_EV_CONNECT, &err); +} + +#if MG_ENABLE_ASYNC_RESOLVER +/* + * Callback for the async resolver on mg_connect_opt() call. + * Main task of this function is to trigger MG_EV_CONNECT event with + * either failure (and dealloc the connection) + * or success (and proceed with connect() + */ +static void resolve_cb(struct mg_dns_message *msg, void *data, + enum mg_resolve_err e) { + struct mg_connection *nc = (struct mg_connection *) data; + int i; + int failure = -1; + + nc->flags &= ~MG_F_RESOLVING; + if (msg != NULL) { + /* + * Take the first DNS A answer and run... + */ + for (i = 0; i < msg->num_answers; i++) { + if (msg->answers[i].rtype == MG_DNS_A_RECORD) { + /* + * Async resolver guarantees that there is at least one answer. + * TODO(lsm): handle IPv6 answers too + */ + mg_dns_parse_record_data(msg, &msg->answers[i], &nc->sa.sin.sin_addr, + 4); + mg_do_connect(nc, nc->flags & MG_F_UDP ? SOCK_DGRAM : SOCK_STREAM, + &nc->sa); + return; + } + } + } + + if (e == MG_RESOLVE_TIMEOUT) { + double now = mg_time(); + mg_call(nc, NULL, nc->user_data, MG_EV_TIMER, &now); + } + + /* + * If we get there was no MG_DNS_A_RECORD in the answer + */ + mg_call(nc, NULL, nc->user_data, MG_EV_CONNECT, &failure); + mg_call(nc, NULL, nc->user_data, MG_EV_CLOSE, NULL); + mg_destroy_conn(nc, 1 /* destroy_if */); +} +#endif + +struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t callback, + void *user_data)) { + struct mg_connect_opts opts; + memset(&opts, 0, sizeof(opts)); + return mg_connect_opt(mgr, address, MG_CB(callback, user_data), opts); +} + +struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t callback, + void *user_data), + struct mg_connect_opts opts) { + struct mg_connection *nc = NULL; + int proto, rc; + struct mg_add_sock_opts add_sock_opts; + char host[MG_MAX_HOST_LEN]; + + MG_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts); + + if ((nc = mg_create_connection(mgr, callback, add_sock_opts)) == NULL) { + return NULL; + } + + if ((rc = mg_parse_address(address, &nc->sa, &proto, host, sizeof(host))) < + 0) { + /* Address is malformed */ + MG_SET_PTRPTR(opts.error_string, "cannot parse address"); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + + nc->flags |= opts.flags & _MG_ALLOWED_CONNECT_FLAGS_MASK; + nc->flags |= (proto == SOCK_DGRAM) ? MG_F_UDP : 0; +#if MG_ENABLE_CALLBACK_USERDATA + nc->user_data = user_data; +#else + nc->user_data = opts.user_data; +#endif + +#if MG_ENABLE_SSL + DBG(("%p %s %s,%s,%s", nc, address, (opts.ssl_cert ? opts.ssl_cert : "-"), + (opts.ssl_key ? opts.ssl_key : "-"), + (opts.ssl_ca_cert ? opts.ssl_ca_cert : "-"))); + + if (opts.ssl_cert != NULL || opts.ssl_ca_cert != NULL || + opts.ssl_psk_identity != NULL) { + const char *err_msg = NULL; + struct mg_ssl_if_conn_params params; + if (nc->flags & MG_F_UDP) { + MG_SET_PTRPTR(opts.error_string, "SSL for UDP is not supported"); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + memset(¶ms, 0, sizeof(params)); + params.cert = opts.ssl_cert; + params.key = opts.ssl_key; + params.ca_cert = opts.ssl_ca_cert; + params.cipher_suites = opts.ssl_cipher_suites; + params.psk_identity = opts.ssl_psk_identity; + params.psk_key = opts.ssl_psk_key; + if (opts.ssl_ca_cert != NULL) { + if (opts.ssl_server_name != NULL) { + if (strcmp(opts.ssl_server_name, "*") != 0) { + params.server_name = opts.ssl_server_name; + } + } else if (rc == 0) { /* If it's a DNS name, use host. */ + params.server_name = host; + } + } + if (mg_ssl_if_conn_init(nc, ¶ms, &err_msg) != MG_SSL_OK) { + MG_SET_PTRPTR(opts.error_string, err_msg); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + nc->flags |= MG_F_SSL; + } +#endif /* MG_ENABLE_SSL */ + + if (rc == 0) { +#if MG_ENABLE_ASYNC_RESOLVER + /* + * DNS resolution is required for host. + * mg_parse_address() fills port in nc->sa, which we pass to resolve_cb() + */ + struct mg_connection *dns_conn = NULL; + struct mg_resolve_async_opts o; + memset(&o, 0, sizeof(o)); + o.dns_conn = &dns_conn; + o.nameserver = opts.nameserver; + if (mg_resolve_async_opt(nc->mgr, host, MG_DNS_A_RECORD, resolve_cb, nc, + o) != 0) { + MG_SET_PTRPTR(opts.error_string, "cannot schedule DNS lookup"); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + nc->priv_2 = dns_conn; + nc->flags |= MG_F_RESOLVING; + return nc; +#else + MG_SET_PTRPTR(opts.error_string, "Resolver is disabled"); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; +#endif + } else { + /* Address is parsed and resolved to IP. proceed with connect() */ + return mg_do_connect(nc, proto, &nc->sa); + } +} + +struct mg_connection *mg_bind(struct mg_mgr *srv, const char *address, + MG_CB(mg_event_handler_t event_handler, + void *user_data)) { + struct mg_bind_opts opts; + memset(&opts, 0, sizeof(opts)); + return mg_bind_opt(srv, address, MG_CB(event_handler, user_data), opts); +} + +struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t callback, + void *user_data), + struct mg_bind_opts opts) { + union socket_address sa; + struct mg_connection *nc = NULL; + int proto, rc; + struct mg_add_sock_opts add_sock_opts; + char host[MG_MAX_HOST_LEN]; + +#if MG_ENABLE_CALLBACK_USERDATA + opts.user_data = user_data; +#endif + + if (callback == NULL) { + MG_SET_PTRPTR(opts.error_string, "handler is required"); + return NULL; + } + + MG_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts); + + if (mg_parse_address(address, &sa, &proto, host, sizeof(host)) <= 0) { + MG_SET_PTRPTR(opts.error_string, "cannot parse address"); + return NULL; + } + + nc = mg_create_connection(mgr, callback, add_sock_opts); + if (nc == NULL) { + return NULL; + } + + nc->sa = sa; + nc->flags |= MG_F_LISTENING; + if (proto == SOCK_DGRAM) nc->flags |= MG_F_UDP; + +#if MG_ENABLE_SSL + DBG(("%p %s %s,%s,%s", nc, address, (opts.ssl_cert ? opts.ssl_cert : "-"), + (opts.ssl_key ? opts.ssl_key : "-"), + (opts.ssl_ca_cert ? opts.ssl_ca_cert : "-"))); + + if (opts.ssl_cert != NULL || opts.ssl_ca_cert != NULL) { + const char *err_msg = NULL; + struct mg_ssl_if_conn_params params; + if (nc->flags & MG_F_UDP) { + MG_SET_PTRPTR(opts.error_string, "SSL for UDP is not supported"); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + memset(¶ms, 0, sizeof(params)); + params.cert = opts.ssl_cert; + params.key = opts.ssl_key; + params.ca_cert = opts.ssl_ca_cert; + params.cipher_suites = opts.ssl_cipher_suites; + if (mg_ssl_if_conn_init(nc, ¶ms, &err_msg) != MG_SSL_OK) { + MG_SET_PTRPTR(opts.error_string, err_msg); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + nc->flags |= MG_F_SSL; + } +#endif /* MG_ENABLE_SSL */ + + if (nc->flags & MG_F_UDP) { + rc = nc->iface->vtable->listen_udp(nc, &nc->sa); + } else { + rc = nc->iface->vtable->listen_tcp(nc, &nc->sa); + } + if (rc != 0) { + DBG(("Failed to open listener: %d", rc)); + MG_SET_PTRPTR(opts.error_string, "failed to open listener"); + mg_destroy_conn(nc, 1 /* destroy_if */); + return NULL; + } + mg_add_conn(nc->mgr, nc); + + return nc; +} + +struct mg_connection *mg_next(struct mg_mgr *s, struct mg_connection *conn) { + return conn == NULL ? s->active_connections : conn->next; +} + +#if MG_ENABLE_BROADCAST +void mg_broadcast(struct mg_mgr *mgr, mg_event_handler_t cb, void *data, + size_t len) { + struct ctl_msg ctl_msg; + + /* + * Mongoose manager has a socketpair, `struct mg_mgr::ctl`, + * where `mg_broadcast()` pushes the message. + * `mg_mgr_poll()` wakes up, reads a message from the socket pair, and calls + * specified callback for each connection. Thus the callback function executes + * in event manager thread. + */ + if (mgr->ctl[0] != INVALID_SOCKET && data != NULL && + len < sizeof(ctl_msg.message)) { + size_t dummy; + + ctl_msg.callback = cb; + memcpy(ctl_msg.message, data, len); + dummy = MG_SEND_FUNC(mgr->ctl[0], (char *) &ctl_msg, + offsetof(struct ctl_msg, message) + len, 0); + dummy = MG_RECV_FUNC(mgr->ctl[0], (char *) &len, 1, 0); + (void) dummy; /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509 */ + } +} +#endif /* MG_ENABLE_BROADCAST */ + +static int isbyte(int n) { + return n >= 0 && n <= 255; +} + +static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) { + int n, a, b, c, d, slash = 32, len = 0; + + if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 || + sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) && + isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) && slash >= 0 && + slash < 33) { + len = n; + *net = + ((uint32_t) a << 24) | ((uint32_t) b << 16) | ((uint32_t) c << 8) | d; + *mask = slash ? 0xffffffffU << (32 - slash) : 0; + } + + return len; +} + +int mg_check_ip_acl(const char *acl, uint32_t remote_ip) { + int allowed, flag; + uint32_t net, mask; + struct mg_str vec; + + /* If any ACL is set, deny by default */ + allowed = (acl == NULL || *acl == '\0') ? '+' : '-'; + + while ((acl = mg_next_comma_list_entry(acl, &vec, NULL)) != NULL) { + flag = vec.p[0]; + if ((flag != '+' && flag != '-') || + parse_net(&vec.p[1], &net, &mask) == 0) { + return -1; + } + + if (net == (remote_ip & mask)) { + allowed = flag; + } + } + + DBG(("%08x %c", (unsigned int) remote_ip, allowed)); + return allowed == '+'; +} + +/* Move data from one connection to another */ +void mg_forward(struct mg_connection *from, struct mg_connection *to) { + mg_send(to, from->recv_mbuf.buf, from->recv_mbuf.len); + mbuf_remove(&from->recv_mbuf, from->recv_mbuf.len); +} + +double mg_set_timer(struct mg_connection *c, double timestamp) { + double result = c->ev_timer_time; + c->ev_timer_time = timestamp; + /* + * If this connection is resolving, it's not in the list of active + * connections, so not processed yet. It has a DNS resolver connection + * linked to it. Set up a timer for the DNS connection. + */ + DBG(("%p %p %d -> %lu", c, c->priv_2, (c->flags & MG_F_RESOLVING ? 1 : 0), + (unsigned long) timestamp)); + if ((c->flags & MG_F_RESOLVING) && c->priv_2 != NULL) { + ((struct mg_connection *) c->priv_2)->ev_timer_time = timestamp; + } + return result; +} + +void mg_sock_set(struct mg_connection *nc, sock_t sock) { + if (sock != INVALID_SOCKET) { + nc->iface->vtable->sock_set(nc, sock); + } +} + +void mg_if_get_conn_addr(struct mg_connection *nc, int remote, + union socket_address *sa) { + nc->iface->vtable->get_conn_addr(nc, remote, sa); +} + +struct mg_connection *mg_add_sock_opt(struct mg_mgr *s, sock_t sock, + MG_CB(mg_event_handler_t callback, + void *user_data), + struct mg_add_sock_opts opts) { +#if MG_ENABLE_CALLBACK_USERDATA + opts.user_data = user_data; +#endif + + struct mg_connection *nc = mg_create_connection_base(s, callback, opts); + if (nc != NULL) { + mg_sock_set(nc, sock); + mg_add_conn(nc->mgr, nc); + } + return nc; +} + +struct mg_connection *mg_add_sock(struct mg_mgr *s, sock_t sock, + MG_CB(mg_event_handler_t callback, + void *user_data)) { + struct mg_add_sock_opts opts; + memset(&opts, 0, sizeof(opts)); + return mg_add_sock_opt(s, sock, MG_CB(callback, user_data), opts); +} + +double mg_time(void) { + return cs_time(); +} diff --git a/src/mongoose-6.11/src/mg_net.h b/src/mongoose-6.11/src/mg_net.h new file mode 100644 index 0000000..90b42b4 --- /dev/null +++ b/src/mongoose-6.11/src/mg_net.h @@ -0,0 +1,591 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +/* + * === Core API: TCP/UDP/SSL + * + * NOTE: Mongoose manager is single threaded. It does not protect + * its data structures by mutexes, therefore all functions that are dealing + * with a particular event manager should be called from the same thread, + * with exception of the `mg_broadcast()` function. It is fine to have different + * event managers handled by different threads. + */ + +#ifndef CS_MONGOOSE_SRC_NET_H_ +#define CS_MONGOOSE_SRC_NET_H_ + +#include "mg_common.h" +#include "mg_net_if.h" +#include "common/mbuf.h" + +#ifndef MG_VPRINTF_BUFFER_SIZE +#define MG_VPRINTF_BUFFER_SIZE 100 +#endif + +#ifdef MG_USE_READ_WRITE +#define MG_RECV_FUNC(s, b, l, f) read(s, b, l) +#define MG_SEND_FUNC(s, b, l, f) write(s, b, l) +#else +#define MG_RECV_FUNC(s, b, l, f) recv(s, b, l, f) +#define MG_SEND_FUNC(s, b, l, f) send(s, b, l, f) +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +union socket_address { + struct sockaddr sa; + struct sockaddr_in sin; +#if MG_ENABLE_IPV6 + struct sockaddr_in6 sin6; +#else + struct sockaddr sin6; +#endif +}; + +struct mg_connection; + +/* + * Callback function (event handler) prototype. Must be defined by the user. + * Mongoose calls the event handler, passing the events defined below. + */ +typedef void (*mg_event_handler_t)(struct mg_connection *nc, int ev, + void *ev_data MG_UD_ARG(void *user_data)); + +/* Events. Meaning of event parameter (evp) is given in the comment. */ +#define MG_EV_POLL 0 /* Sent to each connection on each mg_mgr_poll() call */ +#define MG_EV_ACCEPT 1 /* New connection accepted. union socket_address * */ +#define MG_EV_CONNECT 2 /* connect() succeeded or failed. int * */ +#define MG_EV_RECV 3 /* Data has been received. int *num_bytes */ +#define MG_EV_SEND 4 /* Data has been written to a socket. int *num_bytes */ +#define MG_EV_CLOSE 5 /* Connection is closed. NULL */ +#define MG_EV_TIMER 6 /* now >= conn->ev_timer_time. double * */ + +/* + * Mongoose event manager. + */ +struct mg_mgr { + struct mg_connection *active_connections; +#if MG_ENABLE_HEXDUMP + const char *hexdump_file; /* Debug hexdump file path */ +#endif +#if MG_ENABLE_BROADCAST + sock_t ctl[2]; /* Socketpair for mg_broadcast() */ +#endif + void *user_data; /* User data */ + int num_ifaces; + struct mg_iface **ifaces; /* network interfaces */ + const char *nameserver; /* DNS server to use */ +}; + +/* + * Mongoose connection. + */ +struct mg_connection { + struct mg_connection *next, *prev; /* mg_mgr::active_connections linkage */ + struct mg_connection *listener; /* Set only for accept()-ed connections */ + struct mg_mgr *mgr; /* Pointer to containing manager */ + + sock_t sock; /* Socket to the remote peer */ + int err; + union socket_address sa; /* Remote peer address */ + size_t recv_mbuf_limit; /* Max size of recv buffer */ + struct mbuf recv_mbuf; /* Received data */ + struct mbuf send_mbuf; /* Data scheduled for sending */ + time_t last_io_time; /* Timestamp of the last socket IO */ + double ev_timer_time; /* Timestamp of the future MG_EV_TIMER */ +#if MG_ENABLE_SSL + void *ssl_if_data; /* SSL library data. */ +#endif + mg_event_handler_t proto_handler; /* Protocol-specific event handler */ + void *proto_data; /* Protocol-specific data */ + void (*proto_data_destructor)(void *proto_data); + mg_event_handler_t handler; /* Event handler function */ + void *user_data; /* User-specific data */ + union { + void *v; + /* + * the C standard is fussy about fitting function pointers into + * void pointers, since some archs might have fat pointers for functions. + */ + mg_event_handler_t f; + } priv_1; + void *priv_2; + void *mgr_data; /* Implementation-specific event manager's data. */ + struct mg_iface *iface; + unsigned long flags; +/* Flags set by Mongoose */ +#define MG_F_LISTENING (1 << 0) /* This connection is listening */ +#define MG_F_UDP (1 << 1) /* This connection is UDP */ +#define MG_F_RESOLVING (1 << 2) /* Waiting for async resolver */ +#define MG_F_CONNECTING (1 << 3) /* connect() call in progress */ +#define MG_F_SSL (1 << 4) /* SSL is enabled on the connection */ +#define MG_F_SSL_HANDSHAKE_DONE (1 << 5) /* SSL hanshake has completed */ +#define MG_F_WANT_READ (1 << 6) /* SSL specific */ +#define MG_F_WANT_WRITE (1 << 7) /* SSL specific */ +#define MG_F_IS_WEBSOCKET (1 << 8) /* Websocket specific */ + +/* Flags that are settable by user */ +#define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */ +#define MG_F_CLOSE_IMMEDIATELY (1 << 11) /* Disconnect */ +#define MG_F_WEBSOCKET_NO_DEFRAG (1 << 12) /* Websocket specific */ +#define MG_F_DELETE_CHUNK (1 << 13) /* HTTP specific */ +#define MG_F_ENABLE_BROADCAST (1 << 14) /* Allow broadcast address usage */ + +#define MG_F_USER_1 (1 << 20) /* Flags left for application */ +#define MG_F_USER_2 (1 << 21) +#define MG_F_USER_3 (1 << 22) +#define MG_F_USER_4 (1 << 23) +#define MG_F_USER_5 (1 << 24) +#define MG_F_USER_6 (1 << 25) +}; + +/* + * Initialise Mongoose manager. Side effect: ignores SIGPIPE signal. + * `mgr->user_data` field will be initialised with a `user_data` parameter. + * That is an arbitrary pointer, where the user code can associate some data + * with the particular Mongoose manager. For example, a C++ wrapper class + * could be written in which case `user_data` can hold a pointer to the + * class instance. + */ +void mg_mgr_init(struct mg_mgr *mgr, void *user_data); + +/* + * Optional parameters to `mg_mgr_init_opt()`. + * + * If `main_iface` is not NULL, it will be used as the main interface in the + * default interface set. The pointer will be free'd by `mg_mgr_free`. + * Otherwise, the main interface will be autodetected based on the current + * platform. + * + * If `num_ifaces` is 0 and `ifaces` is NULL, the default interface set will be + * used. + * This is an advanced option, as it requires you to construct a full interface + * set, including special networking interfaces required by some optional + * features such as TCP tunneling. Memory backing `ifaces` and each of the + * `num_ifaces` pointers it contains will be reclaimed by `mg_mgr_free`. + */ +struct mg_mgr_init_opts { + const struct mg_iface_vtable *main_iface; + int num_ifaces; + const struct mg_iface_vtable **ifaces; + const char *nameserver; +}; + +/* + * Like `mg_mgr_init` but with more options. + * + * Notably, this allows you to create a manger and choose + * dynamically which networking interface implementation to use. + */ +void mg_mgr_init_opt(struct mg_mgr *mgr, void *user_data, + struct mg_mgr_init_opts opts); + +/* + * De-initialises Mongoose manager. + * + * Closes and deallocates all active connections. + */ +void mg_mgr_free(struct mg_mgr *); + +/* + * This function performs the actual IO and must be called in a loop + * (an event loop). It returns the current timestamp. + * `milli` is the maximum number of milliseconds to sleep. + * `mg_mgr_poll()` checks all connections for IO readiness. If at least one + * of the connections is IO-ready, `mg_mgr_poll()` triggers the respective + * event handlers and returns. + */ +time_t mg_mgr_poll(struct mg_mgr *, int milli); + +#if MG_ENABLE_BROADCAST +/* + * Passes a message of a given length to all connections. + * + * Must be called from a thread that does NOT call `mg_mgr_poll()`. + * Note that `mg_broadcast()` is the only function + * that can be, and must be, called from a different (non-IO) thread. + * + * `func` callback function will be called by the IO thread for each + * connection. When called, the event will be `MG_EV_POLL`, and a message will + * be passed as the `ev_data` pointer. Maximum message size is capped + * by `MG_CTL_MSG_MESSAGE_SIZE` which is set to 8192 bytes. + */ +void mg_broadcast(struct mg_mgr *mgr, mg_event_handler_t cb, void *data, + size_t len); +#endif + +/* + * Iterates over all active connections. + * + * Returns the next connection from the list + * of active connections or `NULL` if there are no more connections. Below + * is the iteration idiom: + * + * ```c + * for (c = mg_next(srv, NULL); c != NULL; c = mg_next(srv, c)) { + * // Do something with connection `c` + * } + * ``` + */ +struct mg_connection *mg_next(struct mg_mgr *mgr, struct mg_connection *c); + +/* + * Optional parameters to `mg_add_sock_opt()`. + * + * `flags` is an initial `struct mg_connection::flags` bitmask to set, + * see `MG_F_*` flags definitions. + */ +struct mg_add_sock_opts { + void *user_data; /* Initial value for connection's user_data */ + unsigned int flags; /* Initial connection flags */ + const char **error_string; /* Placeholder for the error string */ + struct mg_iface *iface; /* Interface instance */ +}; + +/* + * Creates a connection, associates it with the given socket and event handler + * and adds it to the manager. + * + * For more options see the `mg_add_sock_opt` variant. + */ +struct mg_connection *mg_add_sock(struct mg_mgr *mgr, sock_t sock, + MG_CB(mg_event_handler_t handler, + void *user_data)); + +/* + * Creates a connection, associates it with the given socket and event handler + * and adds to the manager. + * + * See the `mg_add_sock_opts` structure for a description of the options. + */ +struct mg_connection *mg_add_sock_opt(struct mg_mgr *mgr, sock_t sock, + MG_CB(mg_event_handler_t handler, + void *user_data), + struct mg_add_sock_opts opts); + +/* + * Optional parameters to `mg_bind_opt()`. + * + * `flags` is an initial `struct mg_connection::flags` bitmask to set, + * see `MG_F_*` flags definitions. + */ +struct mg_bind_opts { + void *user_data; /* Initial value for connection's user_data */ + unsigned int flags; /* Extra connection flags */ + const char **error_string; /* Placeholder for the error string */ + struct mg_iface *iface; /* Interface instance */ +#if MG_ENABLE_SSL + /* + * SSL settings. + * + * Server certificate to present to clients or client certificate to + * present to tunnel dispatcher (for tunneled connections). + */ + const char *ssl_cert; + /* Private key corresponding to the certificate. If ssl_cert is set but + * ssl_key is not, ssl_cert is used. */ + const char *ssl_key; + /* CA bundle used to verify client certificates or tunnel dispatchers. */ + const char *ssl_ca_cert; + /* Colon-delimited list of acceptable cipher suites. + * Names depend on the library used, for example: + * + * ECDH-ECDSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256 (OpenSSL) + * TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256 + * (mbedTLS) + * + * For OpenSSL the list can be obtained by running "openssl ciphers". + * For mbedTLS, names can be found in library/ssl_ciphersuites.c + * If NULL, a reasonable default is used. + */ + const char *ssl_cipher_suites; +#endif +}; + +/* + * Creates a listening connection. + * + * See `mg_bind_opt` for full documentation. + */ +struct mg_connection *mg_bind(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t handler, + void *user_data)); +/* + * Creates a listening connection. + * + * The `address` parameter specifies which address to bind to. It's format is + * the same as for the `mg_connect()` call, where `HOST` part is optional. + * `address` can be just a port number, e.g. `:8000`. To bind to a specific + * interface, an IP address can be specified, e.g. `1.2.3.4:8000`. By default, + * a TCP connection is created. To create UDP connection, prepend `udp://` + * prefix, e.g. `udp://:8000`. To summarize, `address` parameter has following + * format: `[PROTO://][IP_ADDRESS]:PORT`, where `PROTO` could be `tcp` or + * `udp`. + * + * See the `mg_bind_opts` structure for a description of the optional + * parameters. + * + * Returns a new listening connection or `NULL` on error. + * NOTE: The connection remains owned by the manager, do not free(). + */ +struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t handler, + void *user_data), + struct mg_bind_opts opts); + +/* Optional parameters to `mg_connect_opt()` */ +struct mg_connect_opts { + void *user_data; /* Initial value for connection's user_data */ + unsigned int flags; /* Extra connection flags */ + const char **error_string; /* Placeholder for the error string */ + struct mg_iface *iface; /* Interface instance */ + const char *nameserver; /* DNS server to use, NULL for default */ +#if MG_ENABLE_SSL + /* + * SSL settings. + * Client certificate to present to the server. + */ + const char *ssl_cert; + /* + * Private key corresponding to the certificate. + * If ssl_cert is set but ssl_key is not, ssl_cert is used. + */ + const char *ssl_key; + /* + * Verify server certificate using this CA bundle. If set to "*", then SSL + * is enabled but no cert verification is performed. + */ + const char *ssl_ca_cert; + /* Colon-delimited list of acceptable cipher suites. + * Names depend on the library used, for example: + * + * ECDH-ECDSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256 (OpenSSL) + * TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256 + * (mbedTLS) + * + * For OpenSSL the list can be obtained by running "openssl ciphers". + * For mbedTLS, names can be found in library/ssl_ciphersuites.c + * If NULL, a reasonable default is used. + */ + const char *ssl_cipher_suites; + /* + * Server name verification. If ssl_ca_cert is set and the certificate has + * passed verification, its subject will be verified against this string. + * By default (if ssl_server_name is NULL) hostname part of the address will + * be used. Wildcard matching is supported. A special value of "*" disables + * name verification. + */ + const char *ssl_server_name; + /* + * PSK identity and key. Identity is a NUL-terminated string and key is a hex + * string. Key must be either 16 or 32 bytes (32 or 64 hex digits) for AES-128 + * or AES-256 respectively. + * Note: Default list of cipher suites does not include PSK suites, if you + * want to use PSK you will need to set ssl_cipher_suites as well. + */ + const char *ssl_psk_identity; + const char *ssl_psk_key; +#endif +}; + +/* + * Connects to a remote host. + * + * See `mg_connect_opt()` for full documentation. + */ +struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t handler, + void *user_data)); + +/* + * Connects to a remote host. + * + * The `address` format is `[PROTO://]HOST:PORT`. `PROTO` could be `tcp` or + * `udp`. `HOST` could be an IP address, + * IPv6 address (if Mongoose is compiled with `-DMG_ENABLE_IPV6`) or a host + * name. If `HOST` is a name, Mongoose will resolve it asynchronously. Examples + * of valid addresses: `google.com:80`, `udp://1.2.3.4:53`, `10.0.0.1:443`, + * `[::1]:80` + * + * See the `mg_connect_opts` structure for a description of the optional + * parameters. + * + * Returns a new outbound connection or `NULL` on error. + * + * NOTE: The connection remains owned by the manager, do not free(). + * + * NOTE: To enable IPv6 addresses `-DMG_ENABLE_IPV6` should be specified + * in the compilation flags. + * + * NOTE: The new connection will receive `MG_EV_CONNECT` as its first event + * which will report the connect success status. + * If the asynchronous resolution fails or the `connect()` syscall fails for + * whatever reason (e.g. with `ECONNREFUSED` or `ENETUNREACH`), then + * `MG_EV_CONNECT` event will report failure. Code example below: + * + * ```c + * static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { + * int connect_status; + * + * switch (ev) { + * case MG_EV_CONNECT: + * connect_status = * (int *) ev_data; + * if (connect_status == 0) { + * // Success + * } else { + * // Error + * printf("connect() error: %s\n", strerror(connect_status)); + * } + * break; + * ... + * } + * } + * + * ... + * mg_connect(mgr, "my_site.com:80", ev_handler); + * ``` + */ +struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address, + MG_CB(mg_event_handler_t handler, + void *user_data), + struct mg_connect_opts opts); + +#if MG_ENABLE_SSL && MG_NET_IF != MG_NET_IF_SIMPLELINK +/* + * Note: This function is deprecated. Please, use SSL options in + * mg_connect_opt. + * + * Enables SSL for a given connection. + * `cert` is a server certificate file name for a listening connection + * or a client certificate file name for an outgoing connection. + * The certificate files must be in PEM format. The server certificate file + * must contain a certificate, concatenated with a private key, optionally + * concatenated with DH parameters. + * `ca_cert` is a CA certificate or NULL if peer verification is not + * required. + * Return: NULL on success or error message on error. + */ +const char *mg_set_ssl(struct mg_connection *nc, const char *cert, + const char *ca_cert); +#endif + +/* + * Sends data to the connection. + * + * Note that sending functions do not actually push data to the socket. + * They just append data to the output buffer. MG_EV_SEND will be delivered when + * the data has actually been pushed out. + */ +void mg_send(struct mg_connection *, const void *buf, int len); + +/* Enables format string warnings for mg_printf */ +#if defined(__GNUC__) +__attribute__((format(printf, 2, 3))) +#endif +/* don't separate from mg_printf declaration */ + +/* + * Sends `printf`-style formatted data to the connection. + * + * See `mg_send` for more details on send semantics. + */ +int mg_printf(struct mg_connection *, const char *fmt, ...); + +/* Same as `mg_printf()`, but takes `va_list ap` as an argument. */ +int mg_vprintf(struct mg_connection *, const char *fmt, va_list ap); + +/* + * Creates a socket pair. + * `sock_type` can be either `SOCK_STREAM` or `SOCK_DGRAM`. + * Returns 0 on failure and 1 on success. + */ +int mg_socketpair(sock_t[2], int sock_type); + +#if MG_ENABLE_SYNC_RESOLVER +/* + * Convert domain name into IP address. + * + * This is a utility function. If compilation flags have + * `-DMG_ENABLE_GETADDRINFO`, then `getaddrinfo()` call is used for name + * resolution. Otherwise, `gethostbyname()` is used. + * + * CAUTION: this function can block. + * Return 1 on success, 0 on failure. + */ +int mg_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len); +#endif + +/* + * Verify given IP address against the ACL. + * + * `remote_ip` - an IPv4 address to check, in host byte order + * `acl` - a comma separated list of IP subnets: `x.x.x.x/x` or `x.x.x.x`. + * Each subnet is + * prepended by either a - or a + sign. A plus sign means allow, where a + * minus sign means deny. If a subnet mask is omitted, such as `-1.2.3.4`, + * it means that only that single IP address is denied. + * Subnet masks may vary from 0 to 32, inclusive. The default setting + * is to allow all access. On each request the full list is traversed, + * and the last match wins. Example: + * + * `-0.0.0.0/0,+192.168/16` - deny all accesses, only allow 192.168/16 subnet + * + * To learn more about subnet masks, see this + * link:https://en.wikipedia.org/wiki/Subnetwork[Wikipedia page on Subnetwork]. + * + * Returns -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed. + */ +int mg_check_ip_acl(const char *acl, uint32_t remote_ip); + +/* + * Schedules an MG_EV_TIMER event to be delivered at `timestamp` time. + * `timestamp` is UNIX time (the number of seconds since Epoch). It is + * `double` instead of `time_t` to allow for sub-second precision. + * Returns the old timer value. + * + * Example: set the connect timeout to 1.5 seconds: + * + * ``` + * c = mg_connect(&mgr, "cesanta.com", ev_handler); + * mg_set_timer(c, mg_time() + 1.5); + * ... + * + * void ev_handler(struct mg_connection *c, int ev, void *ev_data) { + * switch (ev) { + * case MG_EV_CONNECT: + * mg_set_timer(c, 0); // Clear connect timer + * break; + * case MG_EV_TIMER: + * log("Connect timeout"); + * c->flags |= MG_F_CLOSE_IMMEDIATELY; + * break; + * ``` + */ +double mg_set_timer(struct mg_connection *c, double timestamp); + +/* + * A sub-second precision version of time(). + */ +double mg_time(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_MONGOOSE_SRC_NET_H_ */ diff --git a/src/mongoose-6.11/src/mg_net_if.c b/src/mongoose-6.11/src/mg_net_if.c new file mode 100644 index 0000000..2a93dcf --- /dev/null +++ b/src/mongoose-6.11/src/mg_net_if.c @@ -0,0 +1,41 @@ +#include "mg_net_if.h" +#include "mg_internal.h" +#include "mg_net_if_socket.h" + +extern const struct mg_iface_vtable mg_default_iface_vtable; + +const struct mg_iface_vtable *mg_ifaces[] = { + &mg_default_iface_vtable, +}; + +int mg_num_ifaces = (int) (sizeof(mg_ifaces) / sizeof(mg_ifaces[0])); + +struct mg_iface *mg_if_create_iface(const struct mg_iface_vtable *vtable, + struct mg_mgr *mgr) { + struct mg_iface *iface = (struct mg_iface *) MG_CALLOC(1, sizeof(*iface)); + iface->mgr = mgr; + iface->data = NULL; + iface->vtable = vtable; + return iface; +} + +struct mg_iface *mg_find_iface(struct mg_mgr *mgr, + const struct mg_iface_vtable *vtable, + struct mg_iface *from) { + int i = 0; + if (from != NULL) { + for (i = 0; i < mgr->num_ifaces; i++) { + if (mgr->ifaces[i] == from) { + i++; + break; + } + } + } + + for (; i < mgr->num_ifaces; i++) { + if (mgr->ifaces[i]->vtable == vtable) { + return mgr->ifaces[i]; + } + } + return NULL; +} diff --git a/src/mongoose-6.11/src/mg_net_if.h b/src/mongoose-6.11/src/mg_net_if.h new file mode 100644 index 0000000..43eb2d9 --- /dev/null +++ b/src/mongoose-6.11/src/mg_net_if.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_NET_IF_H_ +#define CS_MONGOOSE_SRC_NET_IF_H_ + +#include "common/platform.h" + +/* + * Internal async networking core interface. + * Consists of calls made by the core, which should not block, + * and callbacks back into the core ("..._cb"). + * Callbacks may (will) cause methods to be invoked from within, + * but methods are not allowed to invoke callbacks inline. + * + * Implementation must ensure that only one callback is invoked at any time. + */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define MG_MAIN_IFACE 0 + +struct mg_mgr; +struct mg_connection; +union socket_address; + +struct mg_iface_vtable; + +struct mg_iface { + struct mg_mgr *mgr; + void *data; /* Implementation-specific data */ + const struct mg_iface_vtable *vtable; +}; + +struct mg_iface_vtable { + void (*init)(struct mg_iface *iface); + void (*free)(struct mg_iface *iface); + void (*add_conn)(struct mg_connection *nc); + void (*remove_conn)(struct mg_connection *nc); + time_t (*poll)(struct mg_iface *iface, int timeout_ms); + + /* Set up a listening TCP socket on a given address. rv = 0 -> ok. */ + int (*listen_tcp)(struct mg_connection *nc, union socket_address *sa); + /* Request that a "listening" UDP socket be created. */ + int (*listen_udp)(struct mg_connection *nc, union socket_address *sa); + + /* Request that a TCP connection is made to the specified address. */ + void (*connect_tcp)(struct mg_connection *nc, const union socket_address *sa); + /* Open a UDP socket. Doesn't actually connect anything. */ + void (*connect_udp)(struct mg_connection *nc); + + /* Send functions for TCP and UDP. Sent data is copied before return. */ + void (*tcp_send)(struct mg_connection *nc, const void *buf, size_t len); + void (*udp_send)(struct mg_connection *nc, const void *buf, size_t len); + + void (*recved)(struct mg_connection *nc, size_t len); + + /* Perform interface-related connection initialization. Return 1 on ok. */ + int (*create_conn)(struct mg_connection *nc); + /* Perform interface-related cleanup on connection before destruction. */ + void (*destroy_conn)(struct mg_connection *nc); + + /* Associate a socket to a connection. */ + void (*sock_set)(struct mg_connection *nc, sock_t sock); + + /* Put connection's address into *sa, local (remote = 0) or remote. */ + void (*get_conn_addr)(struct mg_connection *nc, int remote, + union socket_address *sa); +}; + +extern const struct mg_iface_vtable *mg_ifaces[]; +extern int mg_num_ifaces; + +/* Creates a new interface instance. */ +struct mg_iface *mg_if_create_iface(const struct mg_iface_vtable *vtable, + struct mg_mgr *mgr); + +/* + * Find an interface with a given implementation. The search is started from + * interface `from`, exclusive. Returns NULL if none is found. + */ +struct mg_iface *mg_find_iface(struct mg_mgr *mgr, + const struct mg_iface_vtable *vtable, + struct mg_iface *from); +/* + * Deliver a new TCP connection. Returns NULL in case on error (unable to + * create connection, in which case interface state should be discarded. + * This is phase 1 of the two-phase process - MG_EV_ACCEPT will be delivered + * when mg_if_accept_tcp_cb is invoked. + */ +struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc); +void mg_if_accept_tcp_cb(struct mg_connection *nc, union socket_address *sa, + size_t sa_len); + +/* Callback invoked by connect methods. err = 0 -> ok, != 0 -> error. */ +void mg_if_connect_cb(struct mg_connection *nc, int err); +/* Callback that reports that data has been put on the wire. */ +void mg_if_sent_cb(struct mg_connection *nc, int num_sent); +/* + * Receive callback. + * if `own` is true, buf must be heap-allocated and ownership is transferred + * to the core. + * Core will acknowledge consumption by calling iface::recved. + */ +void mg_if_recv_tcp_cb(struct mg_connection *nc, void *buf, int len, int own); +/* + * Receive callback. + * buf must be heap-allocated and ownership is transferred to the core. + * Core will acknowledge consumption by calling iface::recved. + */ +void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, + union socket_address *sa, size_t sa_len); + +/* void mg_if_close_conn(struct mg_connection *nc); */ + +/* Deliver a POLL event to the connection. */ +void mg_if_poll(struct mg_connection *nc, time_t now); + +/* Deliver a TIMER event to the connection. */ +void mg_if_timer(struct mg_connection *c, double now); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_MONGOOSE_SRC_NET_IF_H_ */ diff --git a/src/mongoose-6.11/src/mg_net_if_socket.c b/src/mongoose-6.11/src/mg_net_if_socket.c new file mode 100644 index 0000000..0d15764 --- /dev/null +++ b/src/mongoose-6.11/src/mg_net_if_socket.c @@ -0,0 +1,749 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_NET_IF_SOCKET + +#include "mg_net_if_socket.h" +#include "mg_internal.h" +#include "mg_util.h" + +#define MG_TCP_RECV_BUFFER_SIZE 1024 +#define MG_UDP_RECV_BUFFER_SIZE 1500 + +static sock_t mg_open_listening_socket(union socket_address *sa, int type, + int proto); +#if MG_ENABLE_SSL +static void mg_ssl_begin(struct mg_connection *nc); +#endif + +void mg_set_non_blocking_mode(sock_t sock) { +#ifdef _WIN32 + unsigned long on = 1; + ioctlsocket(sock, FIONBIO, &on); +#else + int flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, flags | O_NONBLOCK); +#endif +} + +static int mg_is_error(void) { + int err = mg_get_errno(); + return err != EINPROGRESS && err != EWOULDBLOCK +#ifndef WINCE + && err != EAGAIN && err != EINTR +#endif +#ifdef _WIN32 + && WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK +#endif + ; +} + +void mg_socket_if_connect_tcp(struct mg_connection *nc, + const union socket_address *sa) { + int rc, proto = 0; + nc->sock = socket(AF_INET, SOCK_STREAM, proto); + if (nc->sock == INVALID_SOCKET) { + nc->err = mg_get_errno() ? mg_get_errno() : 1; + return; + } +#if !defined(MG_ESP8266) + mg_set_non_blocking_mode(nc->sock); +#endif + rc = connect(nc->sock, &sa->sa, sizeof(sa->sin)); + nc->err = rc < 0 && mg_is_error() ? mg_get_errno() : 0; + DBG(("%p sock %d rc %d errno %d err %d", nc, nc->sock, rc, mg_get_errno(), + nc->err)); +} + +void mg_socket_if_connect_udp(struct mg_connection *nc) { + nc->sock = socket(AF_INET, SOCK_DGRAM, 0); + if (nc->sock == INVALID_SOCKET) { + nc->err = mg_get_errno() ? mg_get_errno() : 1; + return; + } + if (nc->flags & MG_F_ENABLE_BROADCAST) { + int optval = 1; + if (setsockopt(nc->sock, SOL_SOCKET, SO_BROADCAST, (const char *) &optval, + sizeof(optval)) < 0) { + nc->err = mg_get_errno() ? mg_get_errno() : 1; + return; + } + } + nc->err = 0; +} + +int mg_socket_if_listen_tcp(struct mg_connection *nc, + union socket_address *sa) { + int proto = 0; + sock_t sock = mg_open_listening_socket(sa, SOCK_STREAM, proto); + if (sock == INVALID_SOCKET) { + return (mg_get_errno() ? mg_get_errno() : 1); + } + mg_sock_set(nc, sock); + return 0; +} + +int mg_socket_if_listen_udp(struct mg_connection *nc, + union socket_address *sa) { + sock_t sock = mg_open_listening_socket(sa, SOCK_DGRAM, 0); + if (sock == INVALID_SOCKET) return (mg_get_errno() ? mg_get_errno() : 1); + mg_sock_set(nc, sock); + return 0; +} + +void mg_socket_if_tcp_send(struct mg_connection *nc, const void *buf, + size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); +} + +void mg_socket_if_udp_send(struct mg_connection *nc, const void *buf, + size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); +} + +void mg_socket_if_recved(struct mg_connection *nc, size_t len) { + (void) nc; + (void) len; +} + +int mg_socket_if_create_conn(struct mg_connection *nc) { + (void) nc; + return 1; +} + +void mg_socket_if_destroy_conn(struct mg_connection *nc) { + if (nc->sock == INVALID_SOCKET) return; + if (!(nc->flags & MG_F_UDP)) { + closesocket(nc->sock); + } else { + /* Only close outgoing UDP sockets or listeners. */ + if (nc->listener == NULL) closesocket(nc->sock); + } + nc->sock = INVALID_SOCKET; +} + +static int mg_accept_conn(struct mg_connection *lc) { + struct mg_connection *nc; + union socket_address sa; + socklen_t sa_len = sizeof(sa); + /* NOTE(lsm): on Windows, sock is always > FD_SETSIZE */ + sock_t sock = accept(lc->sock, &sa.sa, &sa_len); + if (sock == INVALID_SOCKET) { + if (mg_is_error()) DBG(("%p: failed to accept: %d", lc, mg_get_errno())); + return 0; + } + nc = mg_if_accept_new_conn(lc); + if (nc == NULL) { + closesocket(sock); + return 0; + } + DBG(("%p conn from %s:%d", nc, inet_ntoa(sa.sin.sin_addr), + ntohs(sa.sin.sin_port))); + mg_sock_set(nc, sock); +#if MG_ENABLE_SSL + if (lc->flags & MG_F_SSL) { + if (mg_ssl_if_conn_accept(nc, lc) != MG_SSL_OK) mg_close_conn(nc); + } else +#endif + { + mg_if_accept_tcp_cb(nc, &sa, sa_len); + } + return 1; +} + +/* 'sa' must be an initialized address to bind to */ +static sock_t mg_open_listening_socket(union socket_address *sa, int type, + int proto) { + socklen_t sa_len = + (sa->sa.sa_family == AF_INET) ? sizeof(sa->sin) : sizeof(sa->sin6); + sock_t sock = INVALID_SOCKET; +#if !MG_LWIP + int on = 1; +#endif + + if ((sock = socket(sa->sa.sa_family, type, proto)) != INVALID_SOCKET && +#if !MG_LWIP /* LWIP doesn't support either */ +#if defined(_WIN32) && defined(SO_EXCLUSIVEADDRUSE) && !defined(WINCE) + /* "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE" http://goo.gl/RmrFTm */ + !setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void *) &on, + sizeof(on)) && +#endif + +#if !defined(_WIN32) || !defined(SO_EXCLUSIVEADDRUSE) + /* + * SO_RESUSEADDR is not enabled on Windows because the semantics of + * SO_REUSEADDR on UNIX and Windows is different. On Windows, + * SO_REUSEADDR allows to bind a socket to a port without error even if + * the port is already open by another program. This is not the behavior + * SO_REUSEADDR was designed for, and leads to hard-to-track failure + * scenarios. Therefore, SO_REUSEADDR was disabled on Windows unless + * SO_EXCLUSIVEADDRUSE is supported and set on a socket. + */ + !setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) && +#endif +#endif /* !MG_LWIP */ + + !bind(sock, &sa->sa, sa_len) && + (type == SOCK_DGRAM || listen(sock, SOMAXCONN) == 0)) { +#if !MG_LWIP + mg_set_non_blocking_mode(sock); + /* In case port was set to 0, get the real port number */ + (void) getsockname(sock, &sa->sa, &sa_len); +#endif + } else if (sock != INVALID_SOCKET) { + closesocket(sock); + sock = INVALID_SOCKET; + } + + return sock; +} + +static void mg_write_to_socket(struct mg_connection *nc) { + struct mbuf *io = &nc->send_mbuf; + int n = 0; + +#if MG_LWIP + /* With LWIP we don't know if the socket is ready */ + if (io->len == 0) return; +#endif + + assert(io->len > 0); + + if (nc->flags & MG_F_UDP) { + int n = + sendto(nc->sock, io->buf, io->len, 0, &nc->sa.sa, sizeof(nc->sa.sin)); + DBG(("%p %d %d %d %s:%hu", nc, nc->sock, n, mg_get_errno(), + inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port))); + mg_if_sent_cb(nc, n); + return; + } + +#if MG_ENABLE_SSL + if (nc->flags & MG_F_SSL) { + if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { + n = mg_ssl_if_write(nc, io->buf, io->len); + DBG(("%p %d bytes -> %d (SSL)", nc, n, nc->sock)); + if (n < 0) { + if (n != MG_SSL_WANT_READ && n != MG_SSL_WANT_WRITE) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + return; + } else { + /* Successful SSL operation, clear off SSL wait flags */ + nc->flags &= ~(MG_F_WANT_READ | MG_F_WANT_WRITE); + } + } else { + mg_ssl_begin(nc); + return; + } + } else +#endif + { + n = (int) MG_SEND_FUNC(nc->sock, io->buf, io->len, 0); + DBG(("%p %d bytes -> %d", nc, n, nc->sock)); + } + + mg_if_sent_cb(nc, n); +} + +MG_INTERNAL size_t recv_avail_size(struct mg_connection *conn, size_t max) { + size_t avail; + if (conn->recv_mbuf_limit < conn->recv_mbuf.len) return 0; + avail = conn->recv_mbuf_limit - conn->recv_mbuf.len; + return avail > max ? max : avail; +} + +static void mg_handle_tcp_read(struct mg_connection *conn) { + int n = 0; + char *buf = (char *) MG_MALLOC(MG_TCP_RECV_BUFFER_SIZE); + + if (buf == NULL) { + DBG(("OOM")); + return; + } + +#if MG_ENABLE_SSL + if (conn->flags & MG_F_SSL) { + if (conn->flags & MG_F_SSL_HANDSHAKE_DONE) { + /* SSL library may have more bytes ready to read than we ask to read. + * Therefore, read in a loop until we read everything. Without the loop, + * we skip to the next select() cycle which can just timeout. */ + while ((n = mg_ssl_if_read(conn, buf, MG_TCP_RECV_BUFFER_SIZE)) > 0) { + DBG(("%p %d bytes <- %d (SSL)", conn, n, conn->sock)); + mg_if_recv_tcp_cb(conn, buf, n, 1 /* own */); + buf = NULL; + if (conn->flags & MG_F_CLOSE_IMMEDIATELY) break; + /* buf has been freed, we need a new one. */ + buf = (char *) MG_MALLOC(MG_TCP_RECV_BUFFER_SIZE); + if (buf == NULL) break; + } + MG_FREE(buf); + if (n < 0 && n != MG_SSL_WANT_READ) conn->flags |= MG_F_CLOSE_IMMEDIATELY; + } else { + MG_FREE(buf); + mg_ssl_begin(conn); + return; + } + } else +#endif + { + n = (int) MG_RECV_FUNC(conn->sock, buf, + recv_avail_size(conn, MG_TCP_RECV_BUFFER_SIZE), 0); + DBG(("%p %d bytes (PLAIN) <- %d", conn, n, conn->sock)); + if (n > 0) { + mg_if_recv_tcp_cb(conn, buf, n, 1 /* own */); + } else { + MG_FREE(buf); + } + if (n == 0) { + /* Orderly shutdown of the socket, try flushing output. */ + conn->flags |= MG_F_SEND_AND_CLOSE; + } else if (n < 0 && mg_is_error()) { + conn->flags |= MG_F_CLOSE_IMMEDIATELY; + } + } +} + +static int mg_recvfrom(struct mg_connection *nc, union socket_address *sa, + socklen_t *sa_len, char **buf) { + int n; + *buf = (char *) MG_MALLOC(MG_UDP_RECV_BUFFER_SIZE); + if (*buf == NULL) { + DBG(("Out of memory")); + return -ENOMEM; + } + n = recvfrom(nc->sock, *buf, MG_UDP_RECV_BUFFER_SIZE, 0, &sa->sa, sa_len); + if (n <= 0) { + DBG(("%p recvfrom: %s", nc, strerror(mg_get_errno()))); + MG_FREE(*buf); + } + return n; +} + +static void mg_handle_udp_read(struct mg_connection *nc) { + char *buf = NULL; + union socket_address sa; + socklen_t sa_len = sizeof(sa); + int n = mg_recvfrom(nc, &sa, &sa_len, &buf); + DBG(("%p %d bytes from %s:%d", nc, n, inet_ntoa(nc->sa.sin.sin_addr), + ntohs(nc->sa.sin.sin_port))); + mg_if_recv_udp_cb(nc, buf, n, &sa, sa_len); +} + +#if MG_ENABLE_SSL +static void mg_ssl_begin(struct mg_connection *nc) { + int server_side = (nc->listener != NULL); + enum mg_ssl_if_result res = mg_ssl_if_handshake(nc); + DBG(("%p %d res %d", nc, server_side, res)); + + if (res == MG_SSL_OK) { + nc->flags |= MG_F_SSL_HANDSHAKE_DONE; + nc->flags &= ~(MG_F_WANT_READ | MG_F_WANT_WRITE); + + if (server_side) { + union socket_address sa; + socklen_t sa_len = sizeof(sa); + (void) getpeername(nc->sock, &sa.sa, &sa_len); + mg_if_accept_tcp_cb(nc, &sa, sa_len); + } else { + mg_if_connect_cb(nc, 0); + } + } else if (res != MG_SSL_WANT_READ && res != MG_SSL_WANT_WRITE) { + if (!server_side) { + mg_if_connect_cb(nc, res); + } + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } +} +#endif /* MG_ENABLE_SSL */ + +#define _MG_F_FD_CAN_READ 1 +#define _MG_F_FD_CAN_WRITE 1 << 1 +#define _MG_F_FD_ERROR 1 << 2 + +void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { + int worth_logging = + fd_flags != 0 || (nc->flags & (MG_F_WANT_READ | MG_F_WANT_WRITE)); + if (worth_logging) { + DBG(("%p fd=%d fd_flags=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, + fd_flags, nc->flags, (int) nc->recv_mbuf.len, + (int) nc->send_mbuf.len)); + } + + if (nc->flags & MG_F_CONNECTING) { + if (fd_flags != 0) { + int err = 0; +#if !defined(MG_ESP8266) + if (!(nc->flags & MG_F_UDP)) { + socklen_t len = sizeof(err); + int ret = + getsockopt(nc->sock, SOL_SOCKET, SO_ERROR, (char *) &err, &len); + if (ret != 0) { + err = 1; + } else if (err == EAGAIN || err == EWOULDBLOCK) { + err = 0; + } + } +#else + /* + * On ESP8266 we use blocking connect. + */ + err = nc->err; +#endif +#if MG_ENABLE_SSL + if ((nc->flags & MG_F_SSL) && err == 0) { + mg_ssl_begin(nc); + } else { + mg_if_connect_cb(nc, err); + } +#else + mg_if_connect_cb(nc, err); +#endif + } else if (nc->err != 0) { + mg_if_connect_cb(nc, nc->err); + } + } + + if (fd_flags & _MG_F_FD_CAN_READ) { + if (nc->flags & MG_F_UDP) { + mg_handle_udp_read(nc); + } else { + if (nc->flags & MG_F_LISTENING) { + /* + * We're not looping here, and accepting just one connection at + * a time. The reason is that eCos does not respect non-blocking + * flag on a listening socket and hangs in a loop. + */ + mg_accept_conn(nc); + } else { + mg_handle_tcp_read(nc); + } + } + } + + if (!(nc->flags & MG_F_CLOSE_IMMEDIATELY)) { + if ((fd_flags & _MG_F_FD_CAN_WRITE) && nc->send_mbuf.len > 0) { + mg_write_to_socket(nc); + } + mg_if_poll(nc, (time_t) now); + mg_if_timer(nc, now); + } + + if (worth_logging) { + DBG(("%p after fd=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, nc->flags, + (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); + } +} + +#if MG_ENABLE_BROADCAST +static void mg_mgr_handle_ctl_sock(struct mg_mgr *mgr) { + struct ctl_msg ctl_msg; + int len = + (int) MG_RECV_FUNC(mgr->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0); + size_t dummy = MG_SEND_FUNC(mgr->ctl[1], ctl_msg.message, 1, 0); + DBG(("read %d from ctl socket", len)); + (void) dummy; /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509 */ + if (len >= (int) sizeof(ctl_msg.callback) && ctl_msg.callback != NULL) { + struct mg_connection *nc; + for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) { + ctl_msg.callback(nc, MG_EV_POLL, + ctl_msg.message MG_UD_ARG(nc->user_data)); + } + } +} +#endif + +/* Associate a socket to a connection. */ +void mg_socket_if_sock_set(struct mg_connection *nc, sock_t sock) { + mg_set_non_blocking_mode(sock); + mg_set_close_on_exec(sock); + nc->sock = sock; + DBG(("%p %d", nc, sock)); +} + +void mg_socket_if_init(struct mg_iface *iface) { + (void) iface; + DBG(("%p using select()", iface->mgr)); +#if MG_ENABLE_BROADCAST + mg_socketpair(iface->mgr->ctl, SOCK_DGRAM); +#endif +} + +void mg_socket_if_free(struct mg_iface *iface) { + (void) iface; +} + +void mg_socket_if_add_conn(struct mg_connection *nc) { + (void) nc; +} + +void mg_socket_if_remove_conn(struct mg_connection *nc) { + (void) nc; +} + +void mg_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) { + if (sock != INVALID_SOCKET +#ifdef __unix__ + && sock < (sock_t) FD_SETSIZE +#endif + ) { + FD_SET(sock, set); + if (*max_fd == INVALID_SOCKET || sock > *max_fd) { + *max_fd = sock; + } + } +} + +time_t mg_socket_if_poll(struct mg_iface *iface, int timeout_ms) { + struct mg_mgr *mgr = iface->mgr; + double now = mg_time(); + double min_timer; + struct mg_connection *nc, *tmp; + struct timeval tv; + fd_set read_set, write_set, err_set; + sock_t max_fd = INVALID_SOCKET; + int num_fds, num_ev, num_timers = 0; +#ifdef __unix__ + int try_dup = 1; +#endif + + FD_ZERO(&read_set); + FD_ZERO(&write_set); + FD_ZERO(&err_set); +#if MG_ENABLE_BROADCAST + mg_add_to_set(mgr->ctl[1], &read_set, &max_fd); +#endif + + /* + * Note: it is ok to have connections with sock == INVALID_SOCKET in the list, + * e.g. timer-only "connections". + */ + min_timer = 0; + for (nc = mgr->active_connections, num_fds = 0; nc != NULL; nc = tmp) { + tmp = nc->next; + + if (nc->sock != INVALID_SOCKET) { + num_fds++; + +#ifdef __unix__ + /* A hack to make sure all our file descriptos fit into FD_SETSIZE. */ + if (nc->sock >= (sock_t) FD_SETSIZE && try_dup) { + int new_sock = dup(nc->sock); + if (new_sock >= 0) { + if (new_sock < (sock_t) FD_SETSIZE) { + closesocket(nc->sock); + DBG(("new sock %d -> %d", nc->sock, new_sock)); + nc->sock = new_sock; + } else { + closesocket(new_sock); + DBG(("new sock is still larger than FD_SETSIZE, disregard")); + try_dup = 0; + } + } else { + try_dup = 0; + } + } +#endif + + if (!(nc->flags & MG_F_WANT_WRITE) && + nc->recv_mbuf.len < nc->recv_mbuf_limit && + (!(nc->flags & MG_F_UDP) || nc->listener == NULL)) { + mg_add_to_set(nc->sock, &read_set, &max_fd); + } + + if (((nc->flags & MG_F_CONNECTING) && !(nc->flags & MG_F_WANT_READ)) || + (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING))) { + mg_add_to_set(nc->sock, &write_set, &max_fd); + mg_add_to_set(nc->sock, &err_set, &max_fd); + } + } + + if (nc->ev_timer_time > 0) { + if (num_timers == 0 || nc->ev_timer_time < min_timer) { + min_timer = nc->ev_timer_time; + } + num_timers++; + } + } + + /* + * If there is a timer to be fired earlier than the requested timeout, + * adjust the timeout. + */ + if (num_timers > 0) { + double timer_timeout_ms = (min_timer - mg_time()) * 1000 + 1 /* rounding */; + if (timer_timeout_ms < timeout_ms) { + timeout_ms = (int) timer_timeout_ms; + } + } + if (timeout_ms < 0) timeout_ms = 0; + + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms % 1000) * 1000; + + num_ev = select((int) max_fd + 1, &read_set, &write_set, &err_set, &tv); + now = mg_time(); +#if 0 + DBG(("select @ %ld num_ev=%d of %d, timeout=%d", (long) now, num_ev, num_fds, + timeout_ms)); +#endif + +#if MG_ENABLE_BROADCAST + if (num_ev > 0 && mgr->ctl[1] != INVALID_SOCKET && + FD_ISSET(mgr->ctl[1], &read_set)) { + mg_mgr_handle_ctl_sock(mgr); + } +#endif + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + int fd_flags = 0; + if (nc->sock != INVALID_SOCKET) { + if (num_ev > 0) { + fd_flags = (FD_ISSET(nc->sock, &read_set) && + (!(nc->flags & MG_F_UDP) || nc->listener == NULL) + ? _MG_F_FD_CAN_READ + : 0) | + (FD_ISSET(nc->sock, &write_set) ? _MG_F_FD_CAN_WRITE : 0) | + (FD_ISSET(nc->sock, &err_set) ? _MG_F_FD_ERROR : 0); + } +#if MG_LWIP + /* With LWIP socket emulation layer, we don't get write events for UDP */ + if ((nc->flags & MG_F_UDP) && nc->listener == NULL) { + fd_flags |= _MG_F_FD_CAN_WRITE; + } +#endif + } + tmp = nc->next; + mg_mgr_handle_conn(nc, fd_flags, now); + } + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + tmp = nc->next; + if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || + (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) { + mg_close_conn(nc); + } + } + + return (time_t) now; +} + +#if MG_ENABLE_BROADCAST +MG_INTERNAL void mg_socketpair_close(sock_t *sock) { + while (1) { + if (closesocket(*sock) == -1 && errno == EINTR) continue; + break; + } + *sock = INVALID_SOCKET; +} + +MG_INTERNAL sock_t +mg_socketpair_accept(sock_t sock, union socket_address *sa, socklen_t sa_len) { + sock_t rc; + while (1) { + if ((rc = accept(sock, &sa->sa, &sa_len)) == INVALID_SOCKET && + errno == EINTR) + continue; + break; + } + return rc; +} + +int mg_socketpair(sock_t sp[2], int sock_type) { + union socket_address sa; + sock_t sock; + socklen_t len = sizeof(sa.sin); + int ret = 0; + + sock = sp[0] = sp[1] = INVALID_SOCKET; + + (void) memset(&sa, 0, sizeof(sa)); + sa.sin.sin_family = AF_INET; + sa.sin.sin_port = htons(0); + sa.sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */ + + if ((sock = socket(AF_INET, sock_type, 0)) == INVALID_SOCKET) { + } else if (bind(sock, &sa.sa, len) != 0) { + } else if (sock_type == SOCK_STREAM && listen(sock, 1) != 0) { + } else if (getsockname(sock, &sa.sa, &len) != 0) { + } else if ((sp[0] = socket(AF_INET, sock_type, 0)) == INVALID_SOCKET) { + } else if (connect(sp[0], &sa.sa, len) != 0) { + } else if (sock_type == SOCK_DGRAM && + (getsockname(sp[0], &sa.sa, &len) != 0 || + connect(sock, &sa.sa, len) != 0)) { + } else if ((sp[1] = (sock_type == SOCK_DGRAM ? sock : mg_socketpair_accept( + sock, &sa, len))) == + INVALID_SOCKET) { + } else { + mg_set_close_on_exec(sp[0]); + mg_set_close_on_exec(sp[1]); + if (sock_type == SOCK_STREAM) mg_socketpair_close(&sock); + ret = 1; + } + + if (!ret) { + if (sp[0] != INVALID_SOCKET) mg_socketpair_close(&sp[0]); + if (sp[1] != INVALID_SOCKET) mg_socketpair_close(&sp[1]); + if (sock != INVALID_SOCKET) mg_socketpair_close(&sock); + } + + return ret; +} +#endif /* MG_ENABLE_BROADCAST */ + +static void mg_sock_get_addr(sock_t sock, int remote, + union socket_address *sa) { + socklen_t slen = sizeof(*sa); + memset(sa, 0, slen); + if (remote) { + getpeername(sock, &sa->sa, &slen); + } else { + getsockname(sock, &sa->sa, &slen); + } +} + +void mg_sock_to_str(sock_t sock, char *buf, size_t len, int flags) { + union socket_address sa; + mg_sock_get_addr(sock, flags & MG_SOCK_STRINGIFY_REMOTE, &sa); + mg_sock_addr_to_str(&sa, buf, len, flags); +} + +void mg_socket_if_get_conn_addr(struct mg_connection *nc, int remote, + union socket_address *sa) { + if ((nc->flags & MG_F_UDP) && remote) { + memcpy(sa, &nc->sa, sizeof(*sa)); + return; + } + mg_sock_get_addr(nc->sock, remote, sa); +} + +/* clang-format off */ +#define MG_SOCKET_IFACE_VTABLE \ + { \ + mg_socket_if_init, \ + mg_socket_if_free, \ + mg_socket_if_add_conn, \ + mg_socket_if_remove_conn, \ + mg_socket_if_poll, \ + mg_socket_if_listen_tcp, \ + mg_socket_if_listen_udp, \ + mg_socket_if_connect_tcp, \ + mg_socket_if_connect_udp, \ + mg_socket_if_tcp_send, \ + mg_socket_if_udp_send, \ + mg_socket_if_recved, \ + mg_socket_if_create_conn, \ + mg_socket_if_destroy_conn, \ + mg_socket_if_sock_set, \ + mg_socket_if_get_conn_addr, \ + } +/* clang-format on */ + +const struct mg_iface_vtable mg_socket_iface_vtable = MG_SOCKET_IFACE_VTABLE; +#if MG_NET_IF == MG_NET_IF_SOCKET +const struct mg_iface_vtable mg_default_iface_vtable = MG_SOCKET_IFACE_VTABLE; +#endif + +#endif /* MG_ENABLE_NET_IF_SOCKET */ diff --git a/src/mongoose-6.11/src/mg_net_if_socket.h b/src/mongoose-6.11/src/mg_net_if_socket.h new file mode 100644 index 0000000..744fc0f --- /dev/null +++ b/src/mongoose-6.11/src/mg_net_if_socket.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_NET_IF_SOCKET_H_ +#define CS_MONGOOSE_SRC_NET_IF_SOCKET_H_ + +#include "mg_net_if.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MG_ENABLE_NET_IF_SOCKET +#define MG_ENABLE_NET_IF_SOCKET MG_NET_IF == MG_NET_IF_SOCKET +#endif + +extern const struct mg_iface_vtable mg_socket_iface_vtable; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_MONGOOSE_SRC_NET_IF_SOCKET_H_ */ diff --git a/src/mongoose-6.11/src/mg_net_if_socks.c b/src/mongoose-6.11/src/mg_net_if_socks.c new file mode 100644 index 0000000..c5661e9 --- /dev/null +++ b/src/mongoose-6.11/src/mg_net_if_socks.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_SOCKS + +struct socksdata { + char *proxy_addr; /* HOST:PORT of the socks5 proxy server */ + struct mg_connection *s; /* Respective connection to the server */ + struct mg_connection *c; /* Connection to the client */ + struct mbuf tmp; /* Temporary buffer for sent data */ +}; + +static void socks_if_disband(struct socksdata *d) { + LOG(LL_DEBUG, ("disbanding proxy %p %p", d->c, d->s)); + if (d->c) d->c->flags |= MG_F_SEND_AND_CLOSE; + if (d->s) d->s->flags |= MG_F_SEND_AND_CLOSE; + d->c = d->s = NULL; +} + +static void socks_if_handler(struct mg_connection *c, int ev, void *ev_data) { + struct socksdata *d = (struct socksdata *) c->user_data; + if (ev == MG_EV_CONNECT) { + int res = *(int *) ev_data; + if (res == 0) { + /* Send handshake to the proxy server */ + unsigned char buf[] = {MG_SOCKS_VERSION, 1, MG_SOCKS_HANDSHAKE_NOAUTH}; + mg_send(d->s, buf, sizeof(buf)); + LOG(LL_DEBUG, ("Sent handshake to %s", d->proxy_addr)); + } else { + LOG(LL_ERROR, ("Cannot connect to %s: %d", d->proxy_addr, res)); + d->c->flags |= MG_F_CLOSE_IMMEDIATELY; + } + } else if (ev == MG_EV_CLOSE) { + socks_if_disband(d); + } else if (ev == MG_EV_RECV) { + /* Handle handshake reply */ + if (!(c->flags & MG_SOCKS_HANDSHAKE_DONE)) { + /* TODO(lsm): process IPv6 too */ + unsigned char buf[10] = {MG_SOCKS_VERSION, MG_SOCKS_CMD_CONNECT, 0, + MG_SOCKS_ADDR_IPV4}; + if (c->recv_mbuf.len < 2) return; + if ((unsigned char) c->recv_mbuf.buf[1] == MG_SOCKS_HANDSHAKE_FAILURE) { + LOG(LL_ERROR, ("Server kicked us out")); + socks_if_disband(d); + return; + } + mbuf_remove(&c->recv_mbuf, 2); + c->flags |= MG_SOCKS_HANDSHAKE_DONE; + + /* Send connect request */ + memcpy(buf + 4, &d->c->sa.sin.sin_addr, 4); + memcpy(buf + 8, &d->c->sa.sin.sin_port, 2); + mg_send(c, buf, sizeof(buf)); + } + /* Process connect request */ + if ((c->flags & MG_SOCKS_HANDSHAKE_DONE) && + !(c->flags & MG_SOCKS_CONNECT_DONE)) { + if (c->recv_mbuf.len < 10) return; + if (c->recv_mbuf.buf[1] != MG_SOCKS_SUCCESS) { + LOG(LL_ERROR, ("Socks connection error: %d", c->recv_mbuf.buf[1])); + socks_if_disband(d); + return; + } + mbuf_remove(&c->recv_mbuf, 10); + c->flags |= MG_SOCKS_CONNECT_DONE; + /* Connected. Move sent data from client, if any, to server */ + if (d->s && d->c) { + mbuf_append(&d->s->send_mbuf, d->tmp.buf, d->tmp.len); + mbuf_free(&d->tmp); + } + } + /* All flags are set, we're in relay mode */ + if ((c->flags & MG_SOCKS_CONNECT_DONE) && d->c && d->s) { + mbuf_append(&d->c->recv_mbuf, d->s->recv_mbuf.buf, d->s->recv_mbuf.len); + mbuf_remove(&d->s->recv_mbuf, d->s->recv_mbuf.len); + } + } +} + +static void mg_socks_if_connect_tcp(struct mg_connection *c, + const union socket_address *sa) { + struct socksdata *d = (struct socksdata *) c->iface->data; + d->c = c; + d->s = mg_connect(c->mgr, d->proxy_addr, socks_if_handler); + d->s->user_data = d; + LOG(LL_DEBUG, ("%p %s", c, d->proxy_addr)); + (void) sa; +} + +static void mg_socks_if_connect_udp(struct mg_connection *c) { + (void) c; +} + +static int mg_socks_if_listen_tcp(struct mg_connection *c, + union socket_address *sa) { + (void) c; + (void) sa; + return 0; +} + +static int mg_socks_if_listen_udp(struct mg_connection *c, + union socket_address *sa) { + (void) c; + (void) sa; + return -1; +} + +static void mg_socks_if_tcp_send(struct mg_connection *c, const void *buf, + size_t len) { + struct socksdata *d = (struct socksdata *) c->iface->data; + LOG(LL_DEBUG, ("%p -> %p %d %d", c, buf, (int) len, (int) c->send_mbuf.len)); + if (d && d->s && d->s->flags & MG_SOCKS_CONNECT_DONE) { + mbuf_append(&d->s->send_mbuf, d->tmp.buf, d->tmp.len); + mbuf_append(&d->s->send_mbuf, buf, len); + mbuf_free(&d->tmp); + } else { + mbuf_append(&d->tmp, buf, len); + } +} + +static void mg_socks_if_udp_send(struct mg_connection *c, const void *buf, + size_t len) { + (void) c; + (void) buf; + (void) len; +} + +static void mg_socks_if_recved(struct mg_connection *c, size_t len) { + (void) c; + (void) len; +} + +static int mg_socks_if_create_conn(struct mg_connection *c) { + (void) c; + return 1; +} + +static void mg_socks_if_destroy_conn(struct mg_connection *c) { + c->iface->vtable->free(c->iface); + MG_FREE(c->iface); + c->iface = NULL; + LOG(LL_DEBUG, ("%p", c)); +} + +static void mg_socks_if_sock_set(struct mg_connection *c, sock_t sock) { + (void) c; + (void) sock; +} + +static void mg_socks_if_init(struct mg_iface *iface) { + (void) iface; +} + +static void mg_socks_if_free(struct mg_iface *iface) { + struct socksdata *d = (struct socksdata *) iface->data; + LOG(LL_DEBUG, ("%p", iface)); + if (d != NULL) { + socks_if_disband(d); + mbuf_free(&d->tmp); + MG_FREE(d->proxy_addr); + MG_FREE(d); + iface->data = NULL; + } +} + +static void mg_socks_if_add_conn(struct mg_connection *c) { + c->sock = INVALID_SOCKET; +} + +static void mg_socks_if_remove_conn(struct mg_connection *c) { + (void) c; +} + +static time_t mg_socks_if_poll(struct mg_iface *iface, int timeout_ms) { + LOG(LL_DEBUG, ("%p", iface)); + (void) iface; + (void) timeout_ms; + return (time_t) cs_time(); +} + +static void mg_socks_if_get_conn_addr(struct mg_connection *c, int remote, + union socket_address *sa) { + LOG(LL_DEBUG, ("%p", c)); + (void) c; + (void) remote; + (void) sa; +} + +const struct mg_iface_vtable mg_socks_iface_vtable = { + mg_socks_if_init, mg_socks_if_free, + mg_socks_if_add_conn, mg_socks_if_remove_conn, + mg_socks_if_poll, mg_socks_if_listen_tcp, + mg_socks_if_listen_udp, mg_socks_if_connect_tcp, + mg_socks_if_connect_udp, mg_socks_if_tcp_send, + mg_socks_if_udp_send, mg_socks_if_recved, + mg_socks_if_create_conn, mg_socks_if_destroy_conn, + mg_socks_if_sock_set, mg_socks_if_get_conn_addr, +}; + +struct mg_iface *mg_socks_mk_iface(struct mg_mgr *mgr, const char *proxy_addr) { + struct mg_iface *iface = mg_if_create_iface(&mg_socks_iface_vtable, mgr); + iface->data = MG_CALLOC(1, sizeof(struct socksdata)); + ((struct socksdata *) iface->data)->proxy_addr = strdup(proxy_addr); + return iface; +} + +#endif diff --git a/src/mongoose-6.11/src/mg_net_if_socks.h b/src/mongoose-6.11/src/mg_net_if_socks.h new file mode 100644 index 0000000..80e3107 --- /dev/null +++ b/src/mongoose-6.11/src/mg_net_if_socks.h @@ -0,0 +1,22 @@ +/* +* Copyright (c) 2014-2017 Cesanta Software Limited +* All rights reserved +*/ + +#ifndef CS_MONGOOSE_SRC_NET_IF_SOCKS_H_ +#define CS_MONGOOSE_SRC_NET_IF_SOCKS_H_ + +#if MG_ENABLE_SOCKS +#include "mg_net_if.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern const struct mg_iface_vtable mg_socks_iface_vtable; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* MG_ENABLE_SOCKS */ +#endif /* CS_MONGOOSE_SRC_NET_IF_SOCKS_H_ */ diff --git a/src/mongoose-6.11/src/mg_resolv.c b/src/mongoose-6.11/src/mg_resolv.c new file mode 100644 index 0000000..e445ac5 --- /dev/null +++ b/src/mongoose-6.11/src/mg_resolv.c @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_ASYNC_RESOLVER + +#include "mg_internal.h" +#include "mg_resolv.h" + +#ifndef MG_DEFAULT_NAMESERVER +#define MG_DEFAULT_NAMESERVER "8.8.8.8" +#endif + +struct mg_resolve_async_request { + char name[1024]; + int query; + mg_resolve_callback_t callback; + void *data; + time_t timeout; + int max_retries; + enum mg_resolve_err err; + + /* state */ + time_t last_time; + int retries; +}; + +/* + * Find what nameserver to use. + * + * Return 0 if OK, -1 if error + */ +static int mg_get_ip_address_of_nameserver(char *name, size_t name_len) { + int ret = -1; + +#ifdef _WIN32 + int i; + LONG err; + HKEY hKey, hSub; + wchar_t subkey[512], value[128], + *key = L"SYSTEM\\ControlSet001\\Services\\Tcpip\\Parameters\\Interfaces"; + + if ((err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hKey)) != + ERROR_SUCCESS) { + fprintf(stderr, "cannot open reg key %S: %ld\n", key, err); + ret = -1; + } else { + for (ret = -1, i = 0; 1; i++) { + DWORD subkey_size = sizeof(subkey), type, len = sizeof(value); + if (RegEnumKeyExW(hKey, i, subkey, &subkey_size, NULL, NULL, NULL, + NULL) != ERROR_SUCCESS) { + break; + } + if (RegOpenKeyExW(hKey, subkey, 0, KEY_READ, &hSub) == ERROR_SUCCESS && + ((RegQueryValueExW(hSub, L"NameServer", 0, &type, (void *) value, + &len) == ERROR_SUCCESS && + value[0] != '\0') || + (RegQueryValueExW(hSub, L"DhcpNameServer", 0, &type, (void *) value, + &len) == ERROR_SUCCESS && + value[0] != '\0'))) { + /* + * See https://github.com/cesanta/mongoose/issues/176 + * The value taken from the registry can be empty, a single + * IP address, or multiple IP addresses separated by comma. + * If it's empty, check the next interface. + * If it's multiple IP addresses, take the first one. + */ + wchar_t *comma = wcschr(value, ','); + if (comma != NULL) { + *comma = '\0'; + } + /* %S will convert wchar_t -> char */ + snprintf(name, name_len, "%S", value); + ret = 0; + RegCloseKey(hSub); + break; + } + } + RegCloseKey(hKey); + } +#elif MG_ENABLE_FILESYSTEM && defined(MG_RESOLV_CONF_FILE_NAME) + FILE *fp; + char line[512]; + + if ((fp = mg_fopen(MG_RESOLV_CONF_FILE_NAME, "r")) == NULL) { + ret = -1; + } else { + /* Try to figure out what nameserver to use */ + for (ret = -1; fgets(line, sizeof(line), fp) != NULL;) { + unsigned int a, b, c, d; + if (sscanf(line, "nameserver %u.%u.%u.%u", &a, &b, &c, &d) == 4) { + snprintf(name, name_len, "%u.%u.%u.%u", a, b, c, d); + ret = 0; + break; + } + } + (void) fclose(fp); + } +#else + snprintf(name, name_len, "%s", MG_DEFAULT_NAMESERVER); +#endif /* _WIN32 */ + + return ret; +} + +int mg_resolve_from_hosts_file(const char *name, union socket_address *usa) { +#if MG_ENABLE_FILESYSTEM && defined(MG_HOSTS_FILE_NAME) + /* TODO(mkm) cache /etc/hosts */ + FILE *fp; + char line[1024]; + char *p; + char alias[256]; + unsigned int a, b, c, d; + int len = 0; + + if ((fp = mg_fopen(MG_HOSTS_FILE_NAME, "r")) == NULL) { + return -1; + } + + for (; fgets(line, sizeof(line), fp) != NULL;) { + if (line[0] == '#') continue; + + if (sscanf(line, "%u.%u.%u.%u%n", &a, &b, &c, &d, &len) == 0) { + /* TODO(mkm): handle ipv6 */ + continue; + } + for (p = line + len; sscanf(p, "%s%n", alias, &len) == 1; p += len) { + if (strcmp(alias, name) == 0) { + usa->sin.sin_addr.s_addr = htonl(a << 24 | b << 16 | c << 8 | d); + fclose(fp); + return 0; + } + } + } + + fclose(fp); +#else + (void) name; + (void) usa; +#endif + + return -1; +} + +static void mg_resolve_async_eh(struct mg_connection *nc, int ev, + void *data MG_UD_ARG(void *user_data)) { + time_t now = (time_t) mg_time(); + struct mg_resolve_async_request *req; + struct mg_dns_message *msg; + int first = 0; +#if !MG_ENABLE_CALLBACK_USERDATA + void *user_data = nc->user_data; +#endif + + if (ev != MG_EV_POLL) DBG(("ev=%d user_data=%p", ev, user_data)); + + req = (struct mg_resolve_async_request *) user_data; + + if (req == NULL) { + return; + } + + switch (ev) { + case MG_EV_CONNECT: + /* don't depend on timer not being at epoch for sending out first req */ + first = 1; + /* fallthrough */ + case MG_EV_POLL: + if (req->retries > req->max_retries) { + req->err = MG_RESOLVE_EXCEEDED_RETRY_COUNT; + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + break; + } + if (first || now - req->last_time >= req->timeout) { + mg_send_dns_query(nc, req->name, req->query); + req->last_time = now; + req->retries++; + } + break; + case MG_EV_RECV: + msg = (struct mg_dns_message *) MG_MALLOC(sizeof(*msg)); + if (mg_parse_dns(nc->recv_mbuf.buf, *(int *) data, msg) == 0 && + msg->num_answers > 0) { + req->callback(msg, req->data, MG_RESOLVE_OK); + nc->user_data = NULL; + MG_FREE(req); + } else { + req->err = MG_RESOLVE_NO_ANSWERS; + } + MG_FREE(msg); + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + break; + case MG_EV_SEND: + /* + * If a send error occurs, prevent closing of the connection by the core. + * We will retry after timeout. + */ + nc->flags &= ~MG_F_CLOSE_IMMEDIATELY; + mbuf_remove(&nc->send_mbuf, nc->send_mbuf.len); + break; + case MG_EV_TIMER: + req->err = MG_RESOLVE_TIMEOUT; + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + break; + case MG_EV_CLOSE: + /* If we got here with request still not done, fire an error callback. */ + if (req != NULL) { + char addr[32]; + mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr), MG_SOCK_STRINGIFY_IP); +#ifdef MG_LOG_DNS_FAILURES + LOG(LL_ERROR, ("Failed to resolve '%s', server %s", req->name, addr)); +#endif + req->callback(NULL, req->data, req->err); + nc->user_data = NULL; + MG_FREE(req); + } + break; + } +} + +int mg_resolve_async(struct mg_mgr *mgr, const char *name, int query, + mg_resolve_callback_t cb, void *data) { + struct mg_resolve_async_opts opts; + memset(&opts, 0, sizeof(opts)); + return mg_resolve_async_opt(mgr, name, query, cb, data, opts); +} + +int mg_resolve_async_opt(struct mg_mgr *mgr, const char *name, int query, + mg_resolve_callback_t cb, void *data, + struct mg_resolve_async_opts opts) { + struct mg_resolve_async_request *req; + struct mg_connection *dns_nc; + const char *nameserver = opts.nameserver; + char dns_server_buff[17], nameserver_url[26]; + + if (nameserver == NULL) { + nameserver = mgr->nameserver; + } + + DBG(("%s %d %p", name, query, opts.dns_conn)); + + /* resolve with DNS */ + req = (struct mg_resolve_async_request *) MG_CALLOC(1, sizeof(*req)); + if (req == NULL) { + return -1; + } + + strncpy(req->name, name, sizeof(req->name)); + req->name[sizeof(req->name) - 1] = '\0'; + + req->query = query; + req->callback = cb; + req->data = data; + /* TODO(mkm): parse defaults out of resolve.conf */ + req->max_retries = opts.max_retries ? opts.max_retries : 2; + req->timeout = opts.timeout ? opts.timeout : 5; + + /* Lazily initialize dns server */ + if (nameserver == NULL) { + if (mg_get_ip_address_of_nameserver(dns_server_buff, + sizeof(dns_server_buff)) != -1) { + nameserver = dns_server_buff; + } else { + nameserver = MG_DEFAULT_NAMESERVER; + } + } + + snprintf(nameserver_url, sizeof(nameserver_url), "udp://%s:53", nameserver); + + dns_nc = mg_connect(mgr, nameserver_url, MG_CB(mg_resolve_async_eh, NULL)); + if (dns_nc == NULL) { + MG_FREE(req); + return -1; + } + dns_nc->user_data = req; + if (opts.dns_conn != NULL) { + *opts.dns_conn = dns_nc; + } + + return 0; +} + +void mg_set_nameserver(struct mg_mgr *mgr, const char *nameserver) { + MG_FREE((char *) mgr->nameserver); + mgr->nameserver = NULL; + if (nameserver != NULL) { + mgr->nameserver = strdup(nameserver); + } +} + +#endif /* MG_ENABLE_ASYNC_RESOLVER */ diff --git a/src/mongoose-6.11/src/mg_resolv.h b/src/mongoose-6.11/src/mg_resolv.h new file mode 100644 index 0000000..a604c17 --- /dev/null +++ b/src/mongoose-6.11/src/mg_resolv.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* + * === API reference + */ + +#ifndef CS_MONGOOSE_SRC_RESOLV_H_ +#define CS_MONGOOSE_SRC_RESOLV_H_ + +#include "mg_dns.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +enum mg_resolve_err { + MG_RESOLVE_OK = 0, + MG_RESOLVE_NO_ANSWERS = 1, + MG_RESOLVE_EXCEEDED_RETRY_COUNT = 2, + MG_RESOLVE_TIMEOUT = 3 +}; + +typedef void (*mg_resolve_callback_t)(struct mg_dns_message *dns_message, + void *user_data, enum mg_resolve_err); + +/* Options for `mg_resolve_async_opt`. */ +struct mg_resolve_async_opts { + const char *nameserver; + int max_retries; /* defaults to 2 if zero */ + int timeout; /* in seconds; defaults to 5 if zero */ + int accept_literal; /* pseudo-resolve literal ipv4 and ipv6 addrs */ + int only_literal; /* only resolves literal addrs; sync cb invocation */ + struct mg_connection **dns_conn; /* return DNS connection */ +}; + +/* See `mg_resolve_async_opt()` */ +int mg_resolve_async(struct mg_mgr *mgr, const char *name, int query, + mg_resolve_callback_t cb, void *data); + +/* Set default DNS server */ +void mg_set_nameserver(struct mg_mgr *mgr, const char *nameserver); + +/* + * Resolved a DNS name asynchronously. + * + * Upon successful resolution, the user callback will be invoked + * with the full DNS response message and a pointer to the user's + * context `data`. + * + * In case of timeout while performing the resolution the callback + * will receive a NULL `msg`. + * + * The DNS answers can be extracted with `mg_next_record` and + * `mg_dns_parse_record_data`: + * + * [source,c] + * ---- + * struct in_addr ina; + * struct mg_dns_resource_record *rr = mg_next_record(msg, MG_DNS_A_RECORD, + * NULL); + * mg_dns_parse_record_data(msg, rr, &ina, sizeof(ina)); + * ---- + */ +int mg_resolve_async_opt(struct mg_mgr *mgr, const char *name, int query, + mg_resolve_callback_t cb, void *data, + struct mg_resolve_async_opts opts); + +/* + * Resolve a name from `/etc/hosts`. + * + * Returns 0 on success, -1 on failure. + */ +int mg_resolve_from_hosts_file(const char *host, union socket_address *usa); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CS_MONGOOSE_SRC_RESOLV_H_ */ diff --git a/src/mongoose-6.11/src/mg_sntp.c b/src/mongoose-6.11/src/mg_sntp.c new file mode 100644 index 0000000..5d40e1a --- /dev/null +++ b/src/mongoose-6.11/src/mg_sntp.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2016 Cesanta Software Limited + * All rights reserved + */ + +#include "mg_internal.h" +#include "mg_sntp.h" +#include "mg_util.h" + +#if MG_ENABLE_SNTP + +#define SNTP_TIME_OFFSET 2208988800 + +#ifndef SNTP_TIMEOUT +#define SNTP_TIMEOUT 10 +#endif + +#ifndef SNTP_ATTEMPTS +#define SNTP_ATTEMPTS 3 +#endif + +static uint64_t mg_get_sec(uint64_t val) { + return (val & 0xFFFFFFFF00000000) >> 32; +} + +static uint64_t mg_get_usec(uint64_t val) { + uint64_t tmp = (val & 0x00000000FFFFFFFF); + tmp *= 1000000; + tmp >>= 32; + return tmp; +} + +static void mg_ntp_to_tv(uint64_t val, struct timeval *tv) { + uint64_t tmp; + tmp = mg_get_sec(val); + tmp -= SNTP_TIME_OFFSET; + tv->tv_sec = tmp; + tv->tv_usec = mg_get_usec(val); +} + +static void mg_get_ntp_ts(const char *ntp, uint64_t *val) { + uint32_t tmp; + memcpy(&tmp, ntp, sizeof(tmp)); + tmp = ntohl(tmp); + *val = (uint64_t) tmp << 32; + memcpy(&tmp, ntp + 4, sizeof(tmp)); + tmp = ntohl(tmp); + *val |= tmp; +} + +void mg_sntp_send_request(struct mg_connection *c) { + uint8_t buf[48] = {0}; + /* + * header - 8 bit: + * LI (2 bit) - 3 (not in sync), VN (3 bit) - 4 (version), + * mode (3 bit) - 3 (client) + */ + buf[0] = (3 << 6) | (4 << 3) | 3; + +/* + * Next fields should be empty in client request + * stratum, 8 bit + * poll interval, 8 bit + * rrecision, 8 bit + * root delay, 32 bit + * root dispersion, 32 bit + * ref id, 32 bit + * ref timestamp, 64 bit + * originate Timestamp, 64 bit + * receive Timestamp, 64 bit +*/ + +/* + * convert time to sntp format (sntp starts from 00:00:00 01.01.1900) + * according to rfc868 it is 2208988800L sec + * this information is used to correct roundtrip delay + * but if local clock is absolutely broken (and doesn't work even + * as simple timer), it is better to disable it +*/ +#ifndef MG_SNTP_NO_DELAY_CORRECTION + uint32_t sec; + sec = htonl((uint32_t)(mg_time() + SNTP_TIME_OFFSET)); + memcpy(&buf[40], &sec, sizeof(sec)); +#endif + + mg_send(c, buf, sizeof(buf)); +} + +#ifndef MG_SNTP_NO_DELAY_CORRECTION +static uint64_t mg_calculate_delay(uint64_t t1, uint64_t t2, uint64_t t3) { + /* roundloop delay = (T4 - T1) - (T3 - T2) */ + uint64_t d1 = ((mg_time() + SNTP_TIME_OFFSET) * 1000000) - + (mg_get_sec(t1) * 1000000 + mg_get_usec(t1)); + uint64_t d2 = (mg_get_sec(t3) * 1000000 + mg_get_usec(t3)) - + (mg_get_sec(t2) * 1000000 + mg_get_usec(t2)); + + return (d1 > d2) ? d1 - d2 : 0; +} +#endif + +MG_INTERNAL int mg_sntp_parse_reply(const char *buf, int len, + struct mg_sntp_message *msg) { + uint8_t hdr; + uint64_t trsm_ts_T3, delay = 0; + int mode; + struct timeval tv; + + if (len < 48) { + return -1; + } + + hdr = buf[0]; + + if ((hdr & 0x38) >> 3 != 4) { + /* Wrong version */ + return -1; + } + + mode = hdr & 0x7; + if (mode != 4 && mode != 5) { + /* Not a server reply */ + return -1; + } + + memset(msg, 0, sizeof(*msg)); + + msg->kiss_of_death = (buf[1] == 0); /* Server asks to not send requests */ + + mg_get_ntp_ts(&buf[40], &trsm_ts_T3); + +#ifndef MG_SNTP_NO_DELAY_CORRECTION + { + uint64_t orig_ts_T1, recv_ts_T2; + mg_get_ntp_ts(&buf[24], &orig_ts_T1); + mg_get_ntp_ts(&buf[32], &recv_ts_T2); + delay = mg_calculate_delay(orig_ts_T1, recv_ts_T2, trsm_ts_T3); + } +#endif + + mg_ntp_to_tv(trsm_ts_T3, &tv); + + msg->time = (double) tv.tv_sec + (((double) tv.tv_usec + delay) / 1000000.0); + + return 0; +} + +static void mg_sntp_handler(struct mg_connection *c, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { + struct mbuf *io = &c->recv_mbuf; + struct mg_sntp_message msg; + + c->handler(c, ev, ev_data MG_UD_ARG(user_data)); + + switch (ev) { + case MG_EV_RECV: { + if (mg_sntp_parse_reply(io->buf, io->len, &msg) < 0) { + DBG(("Invalid SNTP packet received (%d)", (int) io->len)); + c->handler(c, MG_SNTP_MALFORMED_REPLY, NULL MG_UD_ARG(user_data)); + } else { + c->handler(c, MG_SNTP_REPLY, (void *) &msg MG_UD_ARG(user_data)); + } + + mbuf_remove(io, io->len); + break; + } + } +} + +int mg_set_protocol_sntp(struct mg_connection *c) { + if ((c->flags & MG_F_UDP) == 0) { + return -1; + } + + c->proto_handler = mg_sntp_handler; + + return 0; +} + +struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, + MG_CB(mg_event_handler_t event_handler, + void *user_data), + const char *sntp_server_name) { + struct mg_connection *c = NULL; + char url[100], *p_url = url; + const char *proto = "", *port = "", *tmp; + + /* If port is not specified, use default (123) */ + tmp = strchr(sntp_server_name, ':'); + if (tmp != NULL && *(tmp + 1) == '/') { + tmp = strchr(tmp + 1, ':'); + } + + if (tmp == NULL) { + port = ":123"; + } + + /* Add udp:// if needed */ + if (strncmp(sntp_server_name, "udp://", 6) != 0) { + proto = "udp://"; + } + + mg_asprintf(&p_url, sizeof(url), "%s%s%s", proto, sntp_server_name, port); + + c = mg_connect(mgr, p_url, event_handler MG_UD_ARG(user_data)); + + if (c == NULL) { + goto cleanup; + } + + mg_set_protocol_sntp(c); + +cleanup: + if (p_url != url) { + MG_FREE(p_url); + } + + return c; +} + +struct sntp_data { + mg_event_handler_t hander; + int count; +}; + +static void mg_sntp_util_ev_handler(struct mg_connection *c, int ev, + void *ev_data MG_UD_ARG(void *user_data)) { +#if !MG_ENABLE_CALLBACK_USERDATA + void *user_data = c->user_data; +#endif + struct sntp_data *sd = (struct sntp_data *) user_data; + + switch (ev) { + case MG_EV_CONNECT: + if (*(int *) ev_data != 0) { + mg_call(c, sd->hander, c->user_data, MG_SNTP_FAILED, NULL); + break; + } + /* fallthrough */ + case MG_EV_TIMER: + if (sd->count <= SNTP_ATTEMPTS) { + mg_sntp_send_request(c); + mg_set_timer(c, mg_time() + 10); + sd->count++; + } else { + mg_call(c, sd->hander, c->user_data, MG_SNTP_FAILED, NULL); + c->flags |= MG_F_CLOSE_IMMEDIATELY; + } + break; + case MG_SNTP_MALFORMED_REPLY: + mg_call(c, sd->hander, c->user_data, MG_SNTP_FAILED, NULL); + c->flags |= MG_F_CLOSE_IMMEDIATELY; + break; + case MG_SNTP_REPLY: + mg_call(c, sd->hander, c->user_data, MG_SNTP_REPLY, ev_data); + c->flags |= MG_F_CLOSE_IMMEDIATELY; + break; + case MG_EV_CLOSE: + MG_FREE(user_data); + c->user_data = NULL; + break; + } +} + +struct mg_connection *mg_sntp_get_time(struct mg_mgr *mgr, + mg_event_handler_t event_handler, + const char *sntp_server_name) { + struct mg_connection *c; + struct sntp_data *sd = (struct sntp_data *) MG_CALLOC(1, sizeof(*sd)); + if (sd == NULL) { + return NULL; + } + + c = mg_sntp_connect(mgr, MG_CB(mg_sntp_util_ev_handler, sd), + sntp_server_name); + if (c == NULL) { + MG_FREE(sd); + return NULL; + } + + sd->hander = event_handler; +#if !MG_ENABLE_CALLBACK_USERDATA + c->user_data = sd; +#endif + + return c; +} + +#endif /* MG_ENABLE_SNTP */ diff --git a/src/mongoose-6.11/src/mg_sntp.h b/src/mongoose-6.11/src/mg_sntp.h new file mode 100644 index 0000000..5cdff2e --- /dev/null +++ b/src/mongoose-6.11/src/mg_sntp.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_SNTP_H_ +#define CS_MONGOOSE_SRC_SNTP_H_ + +#if MG_ENABLE_SNTP + +#define MG_SNTP_EVENT_BASE 500 + +/* + * Received reply from time server. Event handler parameter contains + * pointer to mg_sntp_message structure + */ +#define MG_SNTP_REPLY (MG_SNTP_EVENT_BASE + 1) + +/* Received malformed SNTP packet */ +#define MG_SNTP_MALFORMED_REPLY (MG_SNTP_EVENT_BASE + 2) + +/* Failed to get time from server (timeout etc) */ +#define MG_SNTP_FAILED (MG_SNTP_EVENT_BASE + 3) + +struct mg_sntp_message { + /* if server sends this flags, user should not send requests to it */ + int kiss_of_death; + /* usual mg_time */ + double time; +}; + +/* Establishes connection to given sntp server */ +struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, + MG_CB(mg_event_handler_t event_handler, + void *user_data), + const char *sntp_server_name); + +/* Sends time request to given connection */ +void mg_sntp_send_request(struct mg_connection *c); + +/* + * Helper function + * Establishes connection to time server, tries to send request + * repeats sending SNTP_ATTEMPTS times every SNTP_TIMEOUT sec + * (if needed) + * See sntp_client example + */ +struct mg_connection *mg_sntp_get_time(struct mg_mgr *mgr, + mg_event_handler_t event_handler, + const char *sntp_server_name); + +#endif + +#endif /* CS_MONGOOSE_SRC_SNTP_H_ */ diff --git a/src/mongoose-6.11/src/mg_socks.c b/src/mongoose-6.11/src/mg_socks.c new file mode 100644 index 0000000..56b2d5c --- /dev/null +++ b/src/mongoose-6.11/src/mg_socks.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2017 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_SOCKS + +#include "mg_socks.h" +#include "mg_internal.h" + +/* + * https://www.ietf.org/rfc/rfc1928.txt paragraph 3, handle client handshake + * + * +----+----------+----------+ + * |VER | NMETHODS | METHODS | + * +----+----------+----------+ + * | 1 | 1 | 1 to 255 | + * +----+----------+----------+ + */ +static void mg_socks5_handshake(struct mg_connection *c) { + struct mbuf *r = &c->recv_mbuf; + if (r->buf[0] != MG_SOCKS_VERSION) { + c->flags |= MG_F_CLOSE_IMMEDIATELY; + } else if (r->len > 2 && (size_t) r->buf[1] + 2 <= r->len) { + /* https://www.ietf.org/rfc/rfc1928.txt paragraph 3 */ + unsigned char reply[2] = {MG_SOCKS_VERSION, MG_SOCKS_HANDSHAKE_FAILURE}; + int i; + for (i = 2; i < r->buf[1] + 2; i++) { + /* TODO(lsm): support other auth methods */ + if (r->buf[i] == MG_SOCKS_HANDSHAKE_NOAUTH) reply[1] = r->buf[i]; + } + mbuf_remove(r, 2 + r->buf[1]); + mg_send(c, reply, sizeof(reply)); + c->flags |= MG_SOCKS_HANDSHAKE_DONE; /* Mark handshake done */ + } +} + +static void disband(struct mg_connection *c) { + struct mg_connection *c2 = (struct mg_connection *) c->user_data; + if (c2 != NULL) { + c2->flags |= MG_F_SEND_AND_CLOSE; + c2->user_data = NULL; + } + c->flags |= MG_F_SEND_AND_CLOSE; + c->user_data = NULL; +} + +static void relay_data(struct mg_connection *c) { + struct mg_connection *c2 = (struct mg_connection *) c->user_data; + if (c2 != NULL) { + mg_send(c2, c->recv_mbuf.buf, c->recv_mbuf.len); + mbuf_remove(&c->recv_mbuf, c->recv_mbuf.len); + } else { + c->flags |= MG_F_SEND_AND_CLOSE; + } +} + +static void serv_ev_handler(struct mg_connection *c, int ev, void *ev_data) { + if (ev == MG_EV_CLOSE) { + disband(c); + } else if (ev == MG_EV_RECV) { + relay_data(c); + } else if (ev == MG_EV_CONNECT) { + int res = *(int *) ev_data; + if (res != 0) LOG(LL_ERROR, ("connect error: %d", res)); + } +} + +static void mg_socks5_connect(struct mg_connection *c, const char *addr) { + struct mg_connection *serv = mg_connect(c->mgr, addr, serv_ev_handler); + serv->user_data = c; + c->user_data = serv; +} + +/* + * Request, https://www.ietf.org/rfc/rfc1928.txt paragraph 4 + * + * +----+-----+-------+------+----------+----------+ + * |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | + * +----+-----+-------+------+----------+----------+ + * | 1 | 1 | X'00' | 1 | Variable | 2 | + * +----+-----+-------+------+----------+----------+ + */ +static void mg_socks5_handle_request(struct mg_connection *c) { + struct mbuf *r = &c->recv_mbuf; + unsigned char *p = (unsigned char *) r->buf; + unsigned char addr_len = 4, reply = MG_SOCKS_SUCCESS; + int ver, cmd, atyp; + char addr[300]; + + if (r->len < 8) return; /* return if not fully buffered. min DST.ADDR is 2 */ + ver = p[0]; + cmd = p[1]; + atyp = p[3]; + + /* TODO(lsm): support other commands */ + if (ver != MG_SOCKS_VERSION || cmd != MG_SOCKS_CMD_CONNECT) { + reply = MG_SOCKS_CMD_NOT_SUPPORTED; + } else if (atyp == MG_SOCKS_ADDR_IPV4) { + addr_len = 4; + if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */ + snprintf(addr, sizeof(addr), "%d.%d.%d.%d:%d", p[4], p[5], p[6], p[7], + p[8] << 8 | p[9]); + mg_socks5_connect(c, addr); + } else if (atyp == MG_SOCKS_ADDR_IPV6) { + addr_len = 16; + if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */ + snprintf(addr, sizeof(addr), "[%x:%x:%x:%x:%x:%x:%x:%x]:%d", + p[4] << 8 | p[5], p[6] << 8 | p[7], p[8] << 8 | p[9], + p[10] << 8 | p[11], p[12] << 8 | p[13], p[14] << 8 | p[15], + p[16] << 8 | p[17], p[18] << 8 | p[19], p[20] << 8 | p[21]); + mg_socks5_connect(c, addr); + } else if (atyp == MG_SOCKS_ADDR_DOMAIN) { + addr_len = p[4] + 1; + if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */ + snprintf(addr, sizeof(addr), "%.*s:%d", p[4], p + 5, + p[4 + addr_len] << 8 | p[4 + addr_len + 1]); + mg_socks5_connect(c, addr); + } else { + reply = MG_SOCKS_ADDR_NOT_SUPPORTED; + } + + /* + * Reply, https://www.ietf.org/rfc/rfc1928.txt paragraph 5 + * + * +----+-----+-------+------+----------+----------+ + * |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | + * +----+-----+-------+------+----------+----------+ + * | 1 | 1 | X'00' | 1 | Variable | 2 | + * +----+-----+-------+------+----------+----------+ + */ + { + unsigned char buf[] = {MG_SOCKS_VERSION, reply, 0}; + mg_send(c, buf, sizeof(buf)); + } + mg_send(c, r->buf + 3, addr_len + 1 + 2); + + mbuf_remove(r, 6 + addr_len); /* Remove request from the input stream */ + c->flags |= MG_SOCKS_CONNECT_DONE; /* Mark ourselves as connected */ +} + +static void socks_handler(struct mg_connection *c, int ev, void *ev_data) { + if (ev == MG_EV_RECV) { + if (!(c->flags & MG_SOCKS_HANDSHAKE_DONE)) mg_socks5_handshake(c); + if (c->flags & MG_SOCKS_HANDSHAKE_DONE && + !(c->flags & MG_SOCKS_CONNECT_DONE)) { + mg_socks5_handle_request(c); + } + if (c->flags & MG_SOCKS_CONNECT_DONE) relay_data(c); + } else if (ev == MG_EV_CLOSE) { + disband(c); + } + (void) ev_data; +} + +void mg_set_protocol_socks(struct mg_connection *c) { + c->proto_handler = socks_handler; +} +#endif diff --git a/src/mongoose-6.11/src/mg_socks.h b/src/mongoose-6.11/src/mg_socks.h new file mode 100644 index 0000000..e7b231a --- /dev/null +++ b/src/mongoose-6.11/src/mg_socks.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_SOCKS_H_ +#define CS_MONGOOSE_SRC_SOCKS_H_ + +#if MG_ENABLE_SOCKS + +#define MG_SOCKS_VERSION 5 + +#define MG_SOCKS_HANDSHAKE_DONE MG_F_USER_1 +#define MG_SOCKS_CONNECT_DONE MG_F_USER_2 + +/* SOCKS5 handshake methods */ +enum mg_socks_handshake_method { + MG_SOCKS_HANDSHAKE_NOAUTH = 0, /* Handshake method - no authentication */ + MG_SOCKS_HANDSHAKE_GSSAPI = 1, /* Handshake method - GSSAPI auth */ + MG_SOCKS_HANDSHAKE_USERPASS = 2, /* Handshake method - user/password auth */ + MG_SOCKS_HANDSHAKE_FAILURE = 0xff, /* Handshake method - failure */ +}; + +/* SOCKS5 commands */ +enum mg_socks_command { + MG_SOCKS_CMD_CONNECT = 1, /* Command: CONNECT */ + MG_SOCKS_CMD_BIND = 2, /* Command: BIND */ + MG_SOCKS_CMD_UDP_ASSOCIATE = 3, /* Command: UDP ASSOCIATE */ +}; + +/* SOCKS5 address types */ +enum mg_socks_address_type { + MG_SOCKS_ADDR_IPV4 = 1, /* Address type: IPv4 */ + MG_SOCKS_ADDR_DOMAIN = 3, /* Address type: Domain name */ + MG_SOCKS_ADDR_IPV6 = 4, /* Address type: IPv6 */ +}; + +/* SOCKS5 response codes */ +enum mg_socks_response { + MG_SOCKS_SUCCESS = 0, /* Response: success */ + MG_SOCKS_FAILURE = 1, /* Response: failure */ + MG_SOCKS_NOT_ALLOWED = 2, /* Response: connection not allowed */ + MG_SOCKS_NET_UNREACHABLE = 3, /* Response: network unreachable */ + MG_SOCKS_HOST_UNREACHABLE = 4, /* Response: network unreachable */ + MG_SOCKS_CONN_REFUSED = 5, /* Response: network unreachable */ + MG_SOCKS_TTL_EXPIRED = 6, /* Response: network unreachable */ + MG_SOCKS_CMD_NOT_SUPPORTED = 7, /* Response: network unreachable */ + MG_SOCKS_ADDR_NOT_SUPPORTED = 8, /* Response: network unreachable */ +}; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Turn the connection into the SOCKS server */ +void mg_set_protocol_socks(struct mg_connection *c); + +/* Create socks tunnel for the client connection */ +struct mg_iface *mg_socks_mk_iface(struct mg_mgr *, const char *proxy_addr); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif +#endif diff --git a/src/mongoose-6.11/src/mg_ssl_if.h b/src/mongoose-6.11/src/mg_ssl_if.h new file mode 100644 index 0000000..62f24b5 --- /dev/null +++ b/src/mongoose-6.11/src/mg_ssl_if.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_SRC_SSL_IF_H_ +#define CS_MONGOOSE_SRC_SSL_IF_H_ + +#if MG_ENABLE_SSL + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +struct mg_ssl_if_ctx; +struct mg_connection; + +void mg_ssl_if_init(); + +enum mg_ssl_if_result { + MG_SSL_OK = 0, + MG_SSL_WANT_READ = -1, + MG_SSL_WANT_WRITE = -2, + MG_SSL_ERROR = -3, +}; + +struct mg_ssl_if_conn_params { + const char *cert; + const char *key; + const char *ca_cert; + const char *server_name; + const char *cipher_suites; + const char *psk_identity; + const char *psk_key; +}; + +enum mg_ssl_if_result mg_ssl_if_conn_init( + struct mg_connection *nc, const struct mg_ssl_if_conn_params *params, + const char **err_msg); +enum mg_ssl_if_result mg_ssl_if_conn_accept(struct mg_connection *nc, + struct mg_connection *lc); +void mg_ssl_if_conn_close_notify(struct mg_connection *nc); +void mg_ssl_if_conn_free(struct mg_connection *nc); + +enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc); +int mg_ssl_if_read(struct mg_connection *nc, void *buf, size_t buf_size); +int mg_ssl_if_write(struct mg_connection *nc, const void *data, size_t len); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MG_ENABLE_SSL */ + +#endif /* CS_MONGOOSE_SRC_SSL_IF_H_ */ diff --git a/src/mongoose-6.11/src/mg_ssl_if_mbedtls.c b/src/mongoose-6.11/src/mg_ssl_if_mbedtls.c new file mode 100644 index 0000000..4c1c1db --- /dev/null +++ b/src/mongoose-6.11/src/mg_ssl_if_mbedtls.c @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_MBEDTLS + +#include +#include +#include +#include +#include + +static void mg_ssl_mbed_log(void *ctx, int level, const char *file, int line, + const char *str) { + enum cs_log_level cs_level; + switch (level) { + case 1: + cs_level = LL_ERROR; + break; + case 2: + case 3: + cs_level = LL_DEBUG; + break; + default: + cs_level = LL_VERBOSE_DEBUG; + } + /* mbedTLS passes strings with \n at the end, strip it. */ + LOG(cs_level, ("%p %.*s", ctx, (int) (strlen(str) - 1), str)); + (void) file; + (void) line; +} + +struct mg_ssl_if_ctx { + mbedtls_ssl_config *conf; + mbedtls_ssl_context *ssl; + mbedtls_x509_crt *cert; + mbedtls_pk_context *key; + mbedtls_x509_crt *ca_cert; + struct mbuf cipher_suites; +}; + +/* Must be provided by the platform. ctx is struct mg_connection. */ +extern int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len); + +void mg_ssl_if_init() { +} + +enum mg_ssl_if_result mg_ssl_if_conn_accept(struct mg_connection *nc, + struct mg_connection *lc) { + struct mg_ssl_if_ctx *ctx = + (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx)); + struct mg_ssl_if_ctx *lc_ctx = (struct mg_ssl_if_ctx *) lc->ssl_if_data; + nc->ssl_if_data = ctx; + if (ctx == NULL || lc_ctx == NULL) return MG_SSL_ERROR; + ctx->ssl = (mbedtls_ssl_context *) MG_CALLOC(1, sizeof(*ctx->ssl)); + if (mbedtls_ssl_setup(ctx->ssl, lc_ctx->conf) != 0) { + return MG_SSL_ERROR; + } + return MG_SSL_OK; +} + +static enum mg_ssl_if_result mg_use_cert(struct mg_ssl_if_ctx *ctx, + const char *cert, const char *key, + const char **err_msg); +static enum mg_ssl_if_result mg_use_ca_cert(struct mg_ssl_if_ctx *ctx, + const char *cert); +static enum mg_ssl_if_result mg_set_cipher_list(struct mg_ssl_if_ctx *ctx, + const char *ciphers); +static enum mg_ssl_if_result mg_ssl_if_mbed_set_psk(struct mg_ssl_if_ctx *ctx, + const char *identity, + const char *key); + +enum mg_ssl_if_result mg_ssl_if_conn_init( + struct mg_connection *nc, const struct mg_ssl_if_conn_params *params, + const char **err_msg) { + struct mg_ssl_if_ctx *ctx = + (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx)); + DBG(("%p %s,%s,%s", nc, (params->cert ? params->cert : ""), + (params->key ? params->key : ""), + (params->ca_cert ? params->ca_cert : ""))); + + if (ctx == NULL) { + MG_SET_PTRPTR(err_msg, "Out of memory"); + return MG_SSL_ERROR; + } + nc->ssl_if_data = ctx; + ctx->conf = (mbedtls_ssl_config *) MG_CALLOC(1, sizeof(*ctx->conf)); + mbuf_init(&ctx->cipher_suites, 0); + mbedtls_ssl_config_init(ctx->conf); + mbedtls_ssl_conf_dbg(ctx->conf, mg_ssl_mbed_log, nc); + if (mbedtls_ssl_config_defaults( + ctx->conf, (nc->flags & MG_F_LISTENING ? MBEDTLS_SSL_IS_SERVER + : MBEDTLS_SSL_IS_CLIENT), + MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT) != 0) { + MG_SET_PTRPTR(err_msg, "Failed to init SSL config"); + return MG_SSL_ERROR; + } + + /* TLS 1.2 and up */ + mbedtls_ssl_conf_min_version(ctx->conf, MBEDTLS_SSL_MAJOR_VERSION_3, + MBEDTLS_SSL_MINOR_VERSION_3); + mbedtls_ssl_conf_rng(ctx->conf, mg_ssl_if_mbed_random, nc); + + if (params->cert != NULL && + mg_use_cert(ctx, params->cert, params->key, err_msg) != MG_SSL_OK) { + return MG_SSL_ERROR; + } + + if (params->ca_cert != NULL && + mg_use_ca_cert(ctx, params->ca_cert) != MG_SSL_OK) { + MG_SET_PTRPTR(err_msg, "Invalid SSL CA cert"); + return MG_SSL_ERROR; + } + + if (mg_set_cipher_list(ctx, params->cipher_suites) != MG_SSL_OK) { + MG_SET_PTRPTR(err_msg, "Invalid cipher suite list"); + return MG_SSL_ERROR; + } + + if (mg_ssl_if_mbed_set_psk(ctx, params->psk_identity, params->psk_key) != + MG_SSL_OK) { + MG_SET_PTRPTR(err_msg, "Invalid PSK settings"); + return MG_SSL_ERROR; + } + + if (!(nc->flags & MG_F_LISTENING)) { + ctx->ssl = (mbedtls_ssl_context *) MG_CALLOC(1, sizeof(*ctx->ssl)); + mbedtls_ssl_init(ctx->ssl); + if (mbedtls_ssl_setup(ctx->ssl, ctx->conf) != 0) { + MG_SET_PTRPTR(err_msg, "Failed to create SSL session"); + return MG_SSL_ERROR; + } + if (params->server_name != NULL && + mbedtls_ssl_set_hostname(ctx->ssl, params->server_name) != 0) { + return MG_SSL_ERROR; + } + } + +#ifdef MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN + if (mbedtls_ssl_conf_max_frag_len(ctx->conf, +#if MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN == 512 + MBEDTLS_SSL_MAX_FRAG_LEN_512 +#elif MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN == 1024 + MBEDTLS_SSL_MAX_FRAG_LEN_1024 +#elif MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN == 2048 + MBEDTLS_SSL_MAX_FRAG_LEN_2048 +#elif MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN == 4096 + MBEDTLS_SSL_MAX_FRAG_LEN_4096 +#else +#error Invalid MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN +#endif + ) != 0) { + return MG_SSL_ERROR; + } +#endif + + nc->flags |= MG_F_SSL; + + return MG_SSL_OK; +} + +#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL +int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len); +int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len); +#else +static int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len) { + struct mg_connection *nc = (struct mg_connection *) ctx; + int n = (int) MG_SEND_FUNC(nc->sock, buf, len, 0); + LOG(LL_DEBUG, ("%p %d -> %d", nc, (int) len, n)); + if (n >= 0) return n; + n = mg_get_errno(); + return ((n == EAGAIN || n == EINPROGRESS) ? MBEDTLS_ERR_SSL_WANT_WRITE : -1); +} + +static int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len) { + struct mg_connection *nc = (struct mg_connection *) ctx; + int n = (int) MG_RECV_FUNC(nc->sock, buf, len, 0); + LOG(LL_DEBUG, ("%p %d <- %d", nc, (int) len, n)); + if (n >= 0) return n; + n = mg_get_errno(); + return ((n == EAGAIN || n == EINPROGRESS) ? MBEDTLS_ERR_SSL_WANT_READ : -1); +} +#endif + +static enum mg_ssl_if_result mg_ssl_if_mbed_err(struct mg_connection *nc, + int ret) { + if (ret == MBEDTLS_ERR_SSL_WANT_READ) return MG_SSL_WANT_READ; + if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) return MG_SSL_WANT_WRITE; + if (ret != + MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { /* CLOSE_NOTIFY = Normal shutdown */ + LOG(LL_ERROR, ("%p SSL error: %d", nc, ret)); + } + nc->err = ret; + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return MG_SSL_ERROR; +} + +static void mg_ssl_if_mbed_free_certs_and_keys(struct mg_ssl_if_ctx *ctx) { + if (ctx->cert != NULL) { + mbedtls_x509_crt_free(ctx->cert); + MG_FREE(ctx->cert); + ctx->cert = NULL; + mbedtls_pk_free(ctx->key); + MG_FREE(ctx->key); + ctx->key = NULL; + } + if (ctx->ca_cert != NULL) { + mbedtls_ssl_conf_ca_chain(ctx->conf, NULL, NULL); +#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK + if (ctx->ca_cert->ca_chain_file != NULL) { + MG_FREE((void *) ctx->ca_cert->ca_chain_file); + ctx->ca_cert->ca_chain_file = NULL; + } +#endif + mbedtls_x509_crt_free(ctx->ca_cert); + MG_FREE(ctx->ca_cert); + ctx->ca_cert = NULL; + } +} + +enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int err; + /* If bio is not yet set, do it now. */ + if (ctx->ssl->p_bio == NULL) { + mbedtls_ssl_set_bio(ctx->ssl, nc, ssl_socket_send, ssl_socket_recv, NULL); + } + err = mbedtls_ssl_handshake(ctx->ssl); + if (err != 0) return mg_ssl_if_mbed_err(nc, err); +#ifdef MG_SSL_IF_MBEDTLS_FREE_CERTS + /* + * Free the peer certificate, we don't need it after handshake. + * Note that this effectively disables renegotiation. + */ + mbedtls_x509_crt_free(ctx->ssl->session->peer_cert); + mbedtls_free(ctx->ssl->session->peer_cert); + ctx->ssl->session->peer_cert = NULL; + /* On a client connection we can also free our own and CA certs. */ + if (nc->listener == NULL) { + if (ctx->conf->key_cert != NULL) { + /* Note that this assumes one key_cert entry, which matches our init. */ + MG_FREE(ctx->conf->key_cert); + ctx->conf->key_cert = NULL; + } + mbedtls_ssl_conf_ca_chain(ctx->conf, NULL, NULL); + mg_ssl_if_mbed_free_certs_and_keys(ctx); + } +#endif + return MG_SSL_OK; +} + +int mg_ssl_if_read(struct mg_connection *nc, void *buf, size_t buf_size) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int n = mbedtls_ssl_read(ctx->ssl, (unsigned char *) buf, buf_size); + DBG(("%p %d -> %d", nc, (int) buf_size, n)); + if (n < 0) return mg_ssl_if_mbed_err(nc, n); + if (n == 0) nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return n; +} + +int mg_ssl_if_write(struct mg_connection *nc, const void *data, size_t len) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int n = mbedtls_ssl_write(ctx->ssl, (const unsigned char *) data, len); + DBG(("%p %d -> %d", nc, (int) len, n)); + if (n < 0) return mg_ssl_if_mbed_err(nc, n); + return n; +} + +void mg_ssl_if_conn_close_notify(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + if (ctx == NULL) return; + mbedtls_ssl_close_notify(ctx->ssl); +} + +void mg_ssl_if_conn_free(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + if (ctx == NULL) return; + nc->ssl_if_data = NULL; + if (ctx->ssl != NULL) { + mbedtls_ssl_free(ctx->ssl); + MG_FREE(ctx->ssl); + } + mg_ssl_if_mbed_free_certs_and_keys(ctx); + if (ctx->conf != NULL) { + mbedtls_ssl_config_free(ctx->conf); + MG_FREE(ctx->conf); + } + mbuf_free(&ctx->cipher_suites); + memset(ctx, 0, sizeof(*ctx)); + MG_FREE(ctx); +} + +static enum mg_ssl_if_result mg_use_ca_cert(struct mg_ssl_if_ctx *ctx, + const char *ca_cert) { + if (ca_cert == NULL || strcmp(ca_cert, "*") == 0) { + mbedtls_ssl_conf_authmode(ctx->conf, MBEDTLS_SSL_VERIFY_NONE); + return MG_SSL_OK; + } + ctx->ca_cert = (mbedtls_x509_crt *) MG_CALLOC(1, sizeof(*ctx->ca_cert)); + mbedtls_x509_crt_init(ctx->ca_cert); +#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK + ca_cert = strdup(ca_cert); + if (mbedtls_x509_crt_set_ca_chain_file(ctx->ca_cert, ca_cert) != 0) { + return MG_SSL_ERROR; + } +#else + if (mbedtls_x509_crt_parse_file(ctx->ca_cert, ca_cert) != 0) { + return MG_SSL_ERROR; + } +#endif + mbedtls_ssl_conf_ca_chain(ctx->conf, ctx->ca_cert, NULL); + mbedtls_ssl_conf_authmode(ctx->conf, MBEDTLS_SSL_VERIFY_REQUIRED); + return MG_SSL_OK; +} + +static enum mg_ssl_if_result mg_use_cert(struct mg_ssl_if_ctx *ctx, + const char *cert, const char *key, + const char **err_msg) { + if (key == NULL) key = cert; + if (cert == NULL || cert[0] == '\0' || key == NULL || key[0] == '\0') { + return MG_SSL_OK; + } + ctx->cert = (mbedtls_x509_crt *) MG_CALLOC(1, sizeof(*ctx->cert)); + mbedtls_x509_crt_init(ctx->cert); + ctx->key = (mbedtls_pk_context *) MG_CALLOC(1, sizeof(*ctx->key)); + mbedtls_pk_init(ctx->key); + if (mbedtls_x509_crt_parse_file(ctx->cert, cert) != 0) { + MG_SET_PTRPTR(err_msg, "Invalid SSL cert"); + return MG_SSL_ERROR; + } + if (mbedtls_pk_parse_keyfile(ctx->key, key, NULL) != 0) { + MG_SET_PTRPTR(err_msg, "Invalid SSL key"); + return MG_SSL_ERROR; + } + if (mbedtls_ssl_conf_own_cert(ctx->conf, ctx->cert, ctx->key) != 0) { + MG_SET_PTRPTR(err_msg, "Invalid SSL key or cert"); + return MG_SSL_ERROR; + } + return MG_SSL_OK; +} + +static const int mg_s_cipher_list[] = { +#if CS_PLATFORM != CS_P_ESP8266 + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA, +#else + /* + * ECDHE is way too slow on ESP8266 w/o cryptochip, this sometimes results + * in WiFi STA deauths. Use weaker but faster cipher suites. Sad but true. + * Disable DHE completely because it's just hopelessly slow. + */ + MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA, +#endif /* CS_PLATFORM != CS_P_ESP8266 */ + 0, +}; + +/* + * Ciphers can be specified as a colon-separated list of cipher suite names. + * These can be found in + * https://github.com/ARMmbed/mbedtls/blob/development/library/ssl_ciphersuites.c#L267 + * E.g.: TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-CCM + */ +static enum mg_ssl_if_result mg_set_cipher_list(struct mg_ssl_if_ctx *ctx, + const char *ciphers) { + if (ciphers != NULL) { + int l, id; + const char *s = ciphers, *e; + char tmp[50]; + while (s != NULL) { + e = strchr(s, ':'); + l = (e != NULL ? (e - s) : (int) strlen(s)); + strncpy(tmp, s, l); + tmp[l] = '\0'; + id = mbedtls_ssl_get_ciphersuite_id(tmp); + DBG(("%s -> %04x", tmp, id)); + if (id != 0) { + mbuf_append(&ctx->cipher_suites, &id, sizeof(id)); + } + s = (e != NULL ? e + 1 : NULL); + } + if (ctx->cipher_suites.len == 0) return MG_SSL_ERROR; + id = 0; + mbuf_append(&ctx->cipher_suites, &id, sizeof(id)); + mbuf_trim(&ctx->cipher_suites); + mbedtls_ssl_conf_ciphersuites(ctx->conf, + (const int *) ctx->cipher_suites.buf); + } else { + mbedtls_ssl_conf_ciphersuites(ctx->conf, mg_s_cipher_list); + } + return MG_SSL_OK; +} + +static enum mg_ssl_if_result mg_ssl_if_mbed_set_psk(struct mg_ssl_if_ctx *ctx, + const char *identity, + const char *key_str) { + unsigned char key[32]; + size_t key_len; + if (identity == NULL && key_str == NULL) return MG_SSL_OK; + if (identity == NULL || key_str == NULL) return MG_SSL_ERROR; + key_len = strlen(key_str); + if (key_len != 32 && key_len != 64) return MG_SSL_ERROR; + size_t i = 0; + memset(key, 0, sizeof(key)); + key_len = 0; + for (i = 0; key_str[i] != '\0'; i++) { + unsigned char c; + char hc = tolower((int) key_str[i]); + if (hc >= '0' && hc <= '9') { + c = hc - '0'; + } else if (hc >= 'a' && hc <= 'f') { + c = hc - 'a' + 0xa; + } else { + return MG_SSL_ERROR; + } + key_len = i / 2; + key[key_len] <<= 4; + key[key_len] |= c; + } + key_len++; + DBG(("identity = '%s', key = (%u)", identity, (unsigned int) key_len)); + /* mbedTLS makes copies of psk and identity. */ + if (mbedtls_ssl_conf_psk(ctx->conf, (const unsigned char *) key, key_len, + (const unsigned char *) identity, + strlen(identity)) != 0) { + return MG_SSL_ERROR; + } + return MG_SSL_OK; +} + +const char *mg_set_ssl(struct mg_connection *nc, const char *cert, + const char *ca_cert) { + const char *err_msg = NULL; + struct mg_ssl_if_conn_params params; + memset(¶ms, 0, sizeof(params)); + params.cert = cert; + params.ca_cert = ca_cert; + if (mg_ssl_if_conn_init(nc, ¶ms, &err_msg) != MG_SSL_OK) { + return err_msg; + } + return NULL; +} + +/* Lazy RNG. Warning: it would be a bad idea to do this in production! */ +#ifdef MG_SSL_MBED_DUMMY_RANDOM +int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len) { + (void) ctx; + while (len--) *buf++ = rand(); + return 0; +} +#endif + +#endif /* MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_MBEDTLS */ diff --git a/src/mongoose-6.11/src/mg_ssl_if_openssl.c b/src/mongoose-6.11/src/mg_ssl_if_openssl.c new file mode 100644 index 0000000..2f3da5e --- /dev/null +++ b/src/mongoose-6.11/src/mg_ssl_if_openssl.c @@ -0,0 +1,394 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_OPENSSL + +#ifdef __APPLE__ +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +#include + +struct mg_ssl_if_ctx { + SSL *ssl; + SSL_CTX *ssl_ctx; + struct mbuf psk; + size_t identity_len; +}; + +void mg_ssl_if_init() { + SSL_library_init(); +} + +enum mg_ssl_if_result mg_ssl_if_conn_accept(struct mg_connection *nc, + struct mg_connection *lc) { + struct mg_ssl_if_ctx *ctx = + (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx)); + struct mg_ssl_if_ctx *lc_ctx = (struct mg_ssl_if_ctx *) lc->ssl_if_data; + nc->ssl_if_data = ctx; + if (ctx == NULL || lc_ctx == NULL) return MG_SSL_ERROR; + ctx->ssl_ctx = lc_ctx->ssl_ctx; + if ((ctx->ssl = SSL_new(ctx->ssl_ctx)) == NULL) { + return MG_SSL_ERROR; + } + return MG_SSL_OK; +} + +static enum mg_ssl_if_result mg_use_cert(SSL_CTX *ctx, const char *cert, + const char *key, const char **err_msg); +static enum mg_ssl_if_result mg_use_ca_cert(SSL_CTX *ctx, const char *cert); +static enum mg_ssl_if_result mg_set_cipher_list(SSL_CTX *ctx, const char *cl); +static enum mg_ssl_if_result mg_ssl_if_ossl_set_psk(struct mg_ssl_if_ctx *ctx, + const char *identity, + const char *key_str); + +enum mg_ssl_if_result mg_ssl_if_conn_init( + struct mg_connection *nc, const struct mg_ssl_if_conn_params *params, + const char **err_msg) { + struct mg_ssl_if_ctx *ctx = + (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx)); + DBG(("%p %s,%s,%s", nc, (params->cert ? params->cert : ""), + (params->key ? params->key : ""), + (params->ca_cert ? params->ca_cert : ""))); + if (ctx == NULL) { + MG_SET_PTRPTR(err_msg, "Out of memory"); + return MG_SSL_ERROR; + } + nc->ssl_if_data = ctx; + if (nc->flags & MG_F_LISTENING) { + ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + } else { + ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + } + if (ctx->ssl_ctx == NULL) { + MG_SET_PTRPTR(err_msg, "Failed to create SSL context"); + return MG_SSL_ERROR; + } + +#ifndef KR_VERSION + /* Disable deprecated protocols. */ + SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2); + SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv3); + SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1); +#ifdef MG_SSL_OPENSSL_NO_COMPRESSION + SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_COMPRESSION); +#endif +#ifdef MG_SSL_OPENSSL_CIPHER_SERVER_PREFERENCE + SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); +#endif +#else +/* Krypton only supports TLSv1.2 anyway. */ +#endif + + if (params->cert != NULL && + mg_use_cert(ctx->ssl_ctx, params->cert, params->key, err_msg) != + MG_SSL_OK) { + return MG_SSL_ERROR; + } + + if (params->ca_cert != NULL && + mg_use_ca_cert(ctx->ssl_ctx, params->ca_cert) != MG_SSL_OK) { + MG_SET_PTRPTR(err_msg, "Invalid SSL CA cert"); + return MG_SSL_ERROR; + } + + if (params->server_name != NULL) { +#ifdef KR_VERSION + SSL_CTX_kr_set_verify_name(ctx->ssl_ctx, params->server_name); +#else +/* TODO(rojer): Implement server name verification on OpenSSL. */ +#endif + } + + if (mg_set_cipher_list(ctx->ssl_ctx, params->cipher_suites) != MG_SSL_OK) { + MG_SET_PTRPTR(err_msg, "Invalid cipher suite list"); + return MG_SSL_ERROR; + } + + mbuf_init(&ctx->psk, 0); + if (mg_ssl_if_ossl_set_psk(ctx, params->psk_identity, params->psk_key) != + MG_SSL_OK) { + MG_SET_PTRPTR(err_msg, "Invalid PSK settings"); + return MG_SSL_ERROR; + } + + if (!(nc->flags & MG_F_LISTENING) && + (ctx->ssl = SSL_new(ctx->ssl_ctx)) == NULL) { + MG_SET_PTRPTR(err_msg, "Failed to create SSL session"); + return MG_SSL_ERROR; + } + + nc->flags |= MG_F_SSL; + + return MG_SSL_OK; +} + +static enum mg_ssl_if_result mg_ssl_if_ssl_err(struct mg_connection *nc, + int res) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int err = SSL_get_error(ctx->ssl, res); + if (err == SSL_ERROR_WANT_READ) return MG_SSL_WANT_READ; + if (err == SSL_ERROR_WANT_WRITE) return MG_SSL_WANT_WRITE; + DBG(("%p %p SSL error: %d %d", nc, ctx->ssl_ctx, res, err)); + nc->err = err; + return MG_SSL_ERROR; +} + +enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int server_side = (nc->listener != NULL); + int res; + /* If descriptor is not yet set, do it now. */ + if (SSL_get_fd(ctx->ssl) < 0) { + if (SSL_set_fd(ctx->ssl, nc->sock) != 1) return MG_SSL_ERROR; + } + res = server_side ? SSL_accept(ctx->ssl) : SSL_connect(ctx->ssl); + if (res != 1) return mg_ssl_if_ssl_err(nc, res); + return MG_SSL_OK; +} + +int mg_ssl_if_read(struct mg_connection *nc, void *buf, size_t buf_size) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int n = SSL_read(ctx->ssl, buf, buf_size); + DBG(("%p %d -> %d", nc, (int) buf_size, n)); + if (n < 0) return mg_ssl_if_ssl_err(nc, n); + if (n == 0) nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return n; +} + +int mg_ssl_if_write(struct mg_connection *nc, const void *data, size_t len) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + int n = SSL_write(ctx->ssl, data, len); + DBG(("%p %d -> %d", nc, (int) len, n)); + if (n <= 0) return mg_ssl_if_ssl_err(nc, n); + return n; +} + +void mg_ssl_if_conn_close_notify(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + if (ctx == NULL) return; + SSL_shutdown(ctx->ssl); +} + +void mg_ssl_if_conn_free(struct mg_connection *nc) { + struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; + if (ctx == NULL) return; + nc->ssl_if_data = NULL; + if (ctx->ssl != NULL) SSL_free(ctx->ssl); + if (ctx->ssl_ctx != NULL && nc->listener == NULL) SSL_CTX_free(ctx->ssl_ctx); + mbuf_free(&ctx->psk); + memset(ctx, 0, sizeof(*ctx)); + MG_FREE(ctx); +} + +/* + * Cipher suite options used for TLS negotiation. + * https://wiki.mozilla.org/Security/Server_Side_TLS#Recommended_configurations + */ +static const char mg_s_cipher_list[] = +#if defined(MG_SSL_CRYPTO_MODERN) + "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:" + "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:" + "DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:" + "ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:" + "ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:" + "ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:" + "DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:" + "DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:" + "!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK" +#elif defined(MG_SSL_CRYPTO_OLD) + "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:" + "DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:" + "ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:" + "ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:" + "ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:" + "DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:" + "DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:" + "ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:" + "AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:" + "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:" + "!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA" +#else /* Default - intermediate. */ + "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:" + "DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:" + "ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:" + "ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:" + "ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:" + "DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:" + "DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:" + "AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:" + "DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:" + "!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA" +#endif + ; + +/* + * Default DH params for PFS cipher negotiation. This is a 2048-bit group. + * Will be used if none are provided by the user in the certificate file. + */ +#if !MG_DISABLE_PFS && !defined(KR_VERSION) +static const char mg_s_default_dh_params[] = + "\ +-----BEGIN DH PARAMETERS-----\n\ +MIIBCAKCAQEAlvbgD/qh9znWIlGFcV0zdltD7rq8FeShIqIhkQ0C7hYFThrBvF2E\n\ +Z9bmgaP+sfQwGpVlv9mtaWjvERbu6mEG7JTkgmVUJrUt/wiRzwTaCXBqZkdUO8Tq\n\ ++E6VOEQAilstG90ikN1Tfo+K6+X68XkRUIlgawBTKuvKVwBhuvlqTGerOtnXWnrt\n\ +ym//hd3cd5PBYGBix0i7oR4xdghvfR2WLVu0LgdThTBb6XP7gLd19cQ1JuBtAajZ\n\ +wMuPn7qlUkEFDIkAZy59/Hue/H2Q2vU/JsvVhHWCQBL4F1ofEAt50il6ZxR1QfFK\n\ +9VGKDC4oOgm9DlxwwBoC2FjqmvQlqVV3kwIBAg==\n\ +-----END DH PARAMETERS-----\n"; +#endif + +static enum mg_ssl_if_result mg_use_ca_cert(SSL_CTX *ctx, const char *cert) { + if (cert == NULL || strcmp(cert, "*") == 0) { + return MG_SSL_OK; + } + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0); + return SSL_CTX_load_verify_locations(ctx, cert, NULL) == 1 ? MG_SSL_OK + : MG_SSL_ERROR; +} + +static enum mg_ssl_if_result mg_use_cert(SSL_CTX *ctx, const char *cert, + const char *key, + const char **err_msg) { + if (key == NULL) key = cert; + if (cert == NULL || cert[0] == '\0' || key == NULL || key[0] == '\0') { + return MG_SSL_OK; + } else if (SSL_CTX_use_certificate_file(ctx, cert, 1) == 0) { + MG_SET_PTRPTR(err_msg, "Invalid SSL cert"); + return MG_SSL_ERROR; + } else if (SSL_CTX_use_PrivateKey_file(ctx, key, 1) == 0) { + MG_SET_PTRPTR(err_msg, "Invalid SSL key"); + return MG_SSL_ERROR; + } else if (SSL_CTX_use_certificate_chain_file(ctx, cert) == 0) { + MG_SET_PTRPTR(err_msg, "Invalid CA bundle"); + return MG_SSL_ERROR; + } else { + SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); +#if !MG_DISABLE_PFS && !defined(KR_VERSION) + BIO *bio = NULL; + DH *dh = NULL; + + /* Try to read DH parameters from the cert/key file. */ + bio = BIO_new_file(cert, "r"); + if (bio != NULL) { + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); + } + /* + * If there are no DH params in the file, fall back to hard-coded ones. + * Not ideal, but better than nothing. + */ + if (dh == NULL) { + bio = BIO_new_mem_buf((void *) mg_s_default_dh_params, -1); + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); + } + if (dh != NULL) { + SSL_CTX_set_tmp_dh(ctx, dh); + SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE); + DH_free(dh); + } +#if OPENSSL_VERSION_NUMBER > 0x10002000L + SSL_CTX_set_ecdh_auto(ctx, 1); +#endif +#endif + } + return MG_SSL_OK; +} + +static enum mg_ssl_if_result mg_set_cipher_list(SSL_CTX *ctx, const char *cl) { + return (SSL_CTX_set_cipher_list(ctx, cl ? cl : mg_s_cipher_list) == 1 + ? MG_SSL_OK + : MG_SSL_ERROR); +} + +#ifndef KR_VERSION +static unsigned int mg_ssl_if_ossl_psk_cb(SSL *ssl, const char *hint, + char *identity, + unsigned int max_identity_len, + unsigned char *psk, + unsigned int max_psk_len) { + struct mg_ssl_if_ctx *ctx = + (struct mg_ssl_if_ctx *) SSL_CTX_get_app_data(SSL_get_SSL_CTX(ssl)); + size_t key_len = ctx->psk.len - ctx->identity_len - 1; + DBG(("hint: '%s'", (hint ? hint : ""))); + if (ctx->identity_len + 1 > max_identity_len) { + DBG(("identity too long")); + return 0; + } + if (key_len > max_psk_len) { + DBG(("key too long")); + return 0; + } + memcpy(identity, ctx->psk.buf, ctx->identity_len + 1); + memcpy(psk, ctx->psk.buf + ctx->identity_len + 1, key_len); + (void) ssl; + return key_len; +} + +static enum mg_ssl_if_result mg_ssl_if_ossl_set_psk(struct mg_ssl_if_ctx *ctx, + const char *identity, + const char *key_str) { + unsigned char key[32]; + size_t key_len; + size_t i = 0; + if (identity == NULL && key_str == NULL) return MG_SSL_OK; + if (identity == NULL || key_str == NULL) return MG_SSL_ERROR; + key_len = strlen(key_str); + if (key_len != 32 && key_len != 64) return MG_SSL_ERROR; + memset(key, 0, sizeof(key)); + key_len = 0; + for (i = 0; key_str[i] != '\0'; i++) { + unsigned char c; + char hc = tolower((int) key_str[i]); + if (hc >= '0' && hc <= '9') { + c = hc - '0'; + } else if (hc >= 'a' && hc <= 'f') { + c = hc - 'a' + 0xa; + } else { + return MG_SSL_ERROR; + } + key_len = i / 2; + key[key_len] <<= 4; + key[key_len] |= c; + } + key_len++; + DBG(("identity = '%s', key = (%u)", identity, (unsigned int) key_len)); + ctx->identity_len = strlen(identity); + mbuf_append(&ctx->psk, identity, ctx->identity_len + 1); + mbuf_append(&ctx->psk, key, key_len); + SSL_CTX_set_psk_client_callback(ctx->ssl_ctx, mg_ssl_if_ossl_psk_cb); + SSL_CTX_set_app_data(ctx->ssl_ctx, ctx); + return MG_SSL_OK; +} +#else +static enum mg_ssl_if_result mg_ssl_if_ossl_set_psk(struct mg_ssl_if_ctx *ctx, + const char *identity, + const char *key_str) { + (void) ctx; + (void) identity; + (void) key_str; + /* Krypton does not support PSK. */ + return MG_SSL_ERROR; +} +#endif /* defined(KR_VERSION) */ + +const char *mg_set_ssl(struct mg_connection *nc, const char *cert, + const char *ca_cert) { + const char *err_msg = NULL; + struct mg_ssl_if_conn_params params; + memset(¶ms, 0, sizeof(params)); + params.cert = cert; + params.ca_cert = ca_cert; + if (mg_ssl_if_conn_init(nc, ¶ms, &err_msg) != MG_SSL_OK) { + return err_msg; + } + return NULL; +} + +#endif /* MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_OPENSSL */ diff --git a/src/mongoose-6.11/src/mg_uri.c b/src/mongoose-6.11/src/mg_uri.c new file mode 100644 index 0000000..9b6228b --- /dev/null +++ b/src/mongoose-6.11/src/mg_uri.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#include "mg_internal.h" +#include "mg_uri.h" + +/* + * scan string until encountering one of `seps`, keeping track of component + * boundaries in `res`. + * + * `p` will point to the char after the separator or it will be `end`. + */ +static void parse_uri_component(const char **p, const char *end, + const char *seps, struct mg_str *res) { + const char *q; + res->p = *p; + for (; *p < end; (*p)++) { + for (q = seps; *q != '\0'; q++) { + if (**p == *q) break; + } + if (*q != '\0') break; + } + res->len = (*p) - res->p; + if (*p < end) (*p)++; +} + +int mg_parse_uri(const struct mg_str uri, struct mg_str *scheme, + struct mg_str *user_info, struct mg_str *host, + unsigned int *port, struct mg_str *path, struct mg_str *query, + struct mg_str *fragment) { + struct mg_str rscheme = {0, 0}, ruser_info = {0, 0}, rhost = {0, 0}, + rpath = {0, 0}, rquery = {0, 0}, rfragment = {0, 0}; + unsigned int rport = 0; + enum { + P_START, + P_SCHEME_OR_PORT, + P_USER_INFO, + P_HOST, + P_PORT, + P_REST + } state = P_START; + + const char *p = uri.p, *end = p + uri.len; + while (p < end) { + switch (state) { + case P_START: + /* + * expecting on of: + * - `scheme://xxxx` + * - `xxxx:port` + * - `[a:b:c]:port` + * - `xxxx/path` + */ + if (*p == '[') { + state = P_HOST; + break; + } + for (; p < end; p++) { + if (*p == ':') { + state = P_SCHEME_OR_PORT; + break; + } else if (*p == '/') { + state = P_REST; + break; + } + } + if (state == P_START || state == P_REST) { + rhost.p = uri.p; + rhost.len = p - uri.p; + } + break; + case P_SCHEME_OR_PORT: + if (end - p >= 3 && strncmp(p, "://", 3) == 0) { + rscheme.p = uri.p; + rscheme.len = p - uri.p; + state = P_USER_INFO; + p += 3; + } else { + rhost.p = uri.p; + rhost.len = p - uri.p; + state = P_PORT; + } + break; + case P_USER_INFO: + ruser_info.p = p; + for (; p < end; p++) { + if (*p == '@' || *p == '[' || *p == '/') { + break; + } + } + if (p == end || *p == '/' || *p == '[') { + /* backtrack and parse as host */ + p = ruser_info.p; + } + ruser_info.len = p - ruser_info.p; + state = P_HOST; + break; + case P_HOST: + if (*p == '@') p++; + rhost.p = p; + if (*p == '[') { + int found = 0; + for (; !found && p < end; p++) { + found = (*p == ']'); + } + if (!found) return -1; + } else { + for (; p < end; p++) { + if (*p == ':' || *p == '/') break; + } + } + rhost.len = p - rhost.p; + if (p < end) { + if (*p == ':') { + state = P_PORT; + break; + } else if (*p == '/') { + state = P_REST; + break; + } + } + break; + case P_PORT: + p++; + for (; p < end; p++) { + if (*p == '/') { + state = P_REST; + break; + } + rport *= 10; + rport += *p - '0'; + } + break; + case P_REST: + /* `p` points to separator. `path` includes the separator */ + parse_uri_component(&p, end, "?#", &rpath); + if (p < end && *(p - 1) == '?') { + parse_uri_component(&p, end, "#", &rquery); + } + parse_uri_component(&p, end, "", &rfragment); + break; + } + } + + if (scheme != 0) *scheme = rscheme; + if (user_info != 0) *user_info = ruser_info; + if (host != 0) *host = rhost; + if (port != 0) *port = rport; + if (path != 0) *path = rpath; + if (query != 0) *query = rquery; + if (fragment != 0) *fragment = rfragment; + + return 0; +} + +/* Normalize the URI path. Remove/resolve "." and "..". */ +int mg_normalize_uri_path(const struct mg_str *in, struct mg_str *out) { + const char *s = in->p, *se = s + in->len; + char *cp = (char *) out->p, *d; + + if (in->len == 0 || *s != '/') { + out->len = 0; + return 0; + } + + d = cp; + + while (s < se) { + const char *next = s; + struct mg_str component; + parse_uri_component(&next, se, "/", &component); + if (mg_vcmp(&component, ".") == 0) { + /* Yum. */ + } else if (mg_vcmp(&component, "..") == 0) { + /* Backtrack to previous slash. */ + if (d > cp + 1 && *(d - 1) == '/') d--; + while (d > cp && *(d - 1) != '/') d--; + } else { + memmove(d, s, next - s); + d += next - s; + } + s = next; + } + if (d == cp) *d++ = '/'; + + out->p = cp; + out->len = d - cp; + return 1; +} + +int mg_assemble_uri(const struct mg_str *scheme, const struct mg_str *user_info, + const struct mg_str *host, unsigned int port, + const struct mg_str *path, const struct mg_str *query, + const struct mg_str *fragment, int normalize_path, + struct mg_str *uri) { + int result = -1; + struct mbuf out; + mbuf_init(&out, 0); + + if (scheme != NULL && scheme->len > 0) { + mbuf_append(&out, scheme->p, scheme->len); + mbuf_append(&out, "://", 3); + } + + if (user_info != NULL && user_info->len > 0) { + mbuf_append(&out, user_info->p, user_info->len); + mbuf_append(&out, "@", 1); + } + + if (host != NULL && host->len > 0) { + mbuf_append(&out, host->p, host->len); + } + + if (port != 0) { + char port_str[20]; + int port_str_len = sprintf(port_str, ":%u", port); + mbuf_append(&out, port_str, port_str_len); + } + + if (path != NULL && path->len > 0) { + if (normalize_path) { + struct mg_str npath = mg_strdup(*path); + if (npath.len != path->len) goto out; + if (!mg_normalize_uri_path(path, &npath)) { + free((void *) npath.p); + goto out; + } + mbuf_append(&out, npath.p, npath.len); + free((void *) npath.p); + } else { + mbuf_append(&out, path->p, path->len); + } + } else if (normalize_path) { + mbuf_append(&out, "/", 1); + } + + if (query != NULL && query->len > 0) { + mbuf_append(&out, "?", 1); + mbuf_append(&out, query->p, query->len); + } + + if (fragment != NULL && fragment->len > 0) { + mbuf_append(&out, "#", 1); + mbuf_append(&out, fragment->p, fragment->len); + } + + result = 0; + +out: + if (result == 0) { + uri->p = out.buf; + uri->len = out.len; + } else { + mbuf_free(&out); + uri->p = NULL; + uri->len = 0; + } + return result; +} diff --git a/src/mongoose-6.11/src/mg_uri.h b/src/mongoose-6.11/src/mg_uri.h new file mode 100644 index 0000000..f79e547 --- /dev/null +++ b/src/mongoose-6.11/src/mg_uri.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* + * === URI + */ + +#ifndef CS_MONGOOSE_SRC_URI_H_ +#define CS_MONGOOSE_SRC_URI_H_ + +#include "mg_net.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Parses an URI and fills string chunks with locations of the respective + * uri components within the input uri string. NULL pointers will be + * ignored. + * + * General syntax: + * + * [scheme://[user_info@]]host[:port][/path][?query][#fragment] + * + * Example: + * + * foo.com:80 + * tcp://foo.com:1234 + * http://foo.com:80/bar?baz=1 + * https://user:pw@foo.com:443/blah + * + * `path` will include the leading slash. `query` won't include the leading `?`. + * `host` can contain embedded colons if surrounded by square brackets in order + * to support IPv6 literal addresses. + * + * + * Returns 0 on success, -1 on error. + */ +int mg_parse_uri(const struct mg_str uri, struct mg_str *scheme, + struct mg_str *user_info, struct mg_str *host, + unsigned int *port, struct mg_str *path, struct mg_str *query, + struct mg_str *fragment); + +/* + * Assemble URI from parts. Any of the inputs can be NULL or zero-length mg_str. + * + * If normalize_path is true, path is normalized by resolving relative refs. + * + * Result is a heap-allocated string (uri->p must be free()d after use). + * + * Returns 0 on success, -1 on error. + */ +int mg_assemble_uri(const struct mg_str *scheme, const struct mg_str *user_info, + const struct mg_str *host, unsigned int port, + const struct mg_str *path, const struct mg_str *query, + const struct mg_str *fragment, int normalize_path, + struct mg_str *uri); + +int mg_normalize_uri_path(const struct mg_str *in, struct mg_str *out); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CS_MONGOOSE_SRC_URI_H_ */ diff --git a/src/mongoose-6.11/src/mg_util.c b/src/mongoose-6.11/src/mg_util.c new file mode 100644 index 0000000..485ba27 --- /dev/null +++ b/src/mongoose-6.11/src/mg_util.c @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#include "common/cs_base64.h" +#include "mg_internal.h" +#include "mg_util.h" + +/* For platforms with limited libc */ +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +const char *mg_skip(const char *s, const char *end, const char *delims, + struct mg_str *v) { + v->p = s; + while (s < end && strchr(delims, *(unsigned char *) s) == NULL) s++; + v->len = s - v->p; + while (s < end && strchr(delims, *(unsigned char *) s) != NULL) s++; + return s; +} + +#if MG_ENABLE_FILESYSTEM && !defined(MG_USER_FILE_FUNCTIONS) +int mg_stat(const char *path, cs_stat_t *st) { +#ifdef _WIN32 + wchar_t wpath[MG_MAX_PATH]; + to_wchar(path, wpath, ARRAY_SIZE(wpath)); + DBG(("[%ls] -> %d", wpath, _wstati64(wpath, st))); + return _wstati64(wpath, st); +#else + return stat(path, st); +#endif +} + +FILE *mg_fopen(const char *path, const char *mode) { +#ifdef _WIN32 + wchar_t wpath[MG_MAX_PATH], wmode[10]; + to_wchar(path, wpath, ARRAY_SIZE(wpath)); + to_wchar(mode, wmode, ARRAY_SIZE(wmode)); + return _wfopen(wpath, wmode); +#else + return fopen(path, mode); +#endif +} + +int mg_open(const char *path, int flag, int mode) { /* LCOV_EXCL_LINE */ +#if defined(_WIN32) && !defined(WINCE) + wchar_t wpath[MG_MAX_PATH]; + to_wchar(path, wpath, ARRAY_SIZE(wpath)); + return _wopen(wpath, flag, mode); +#else + return open(path, flag, mode); /* LCOV_EXCL_LINE */ +#endif +} + +size_t mg_fread(void *ptr, size_t size, size_t count, FILE *f) { + return fread(ptr, size, count, f); +} + +size_t mg_fwrite(const void *ptr, size_t size, size_t count, FILE *f) { + return fwrite(ptr, size, count, f); +} +#endif + +void mg_base64_encode(const unsigned char *src, int src_len, char *dst) { + cs_base64_encode(src, src_len, dst); +} + +int mg_base64_decode(const unsigned char *s, int len, char *dst) { + return cs_base64_decode(s, len, dst, NULL); +} + +#if MG_ENABLE_THREADS +void *mg_start_thread(void *(*f)(void *), void *p) { +#ifdef WINCE + return (void *) CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) f, p, 0, NULL); +#elif defined(_WIN32) + return (void *) _beginthread((void(__cdecl *) (void *) ) f, 0, p); +#else + pthread_t thread_id = (pthread_t) 0; + pthread_attr_t attr; + + (void) pthread_attr_init(&attr); + (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + +#if defined(MG_STACK_SIZE) && MG_STACK_SIZE > 1 + (void) pthread_attr_setstacksize(&attr, MG_STACK_SIZE); +#endif + + pthread_create(&thread_id, &attr, f, p); + pthread_attr_destroy(&attr); + + return (void *) thread_id; +#endif +} +#endif /* MG_ENABLE_THREADS */ + +/* Set close-on-exec bit for a given socket. */ +void mg_set_close_on_exec(sock_t sock) { +#if defined(_WIN32) && !defined(WINCE) + (void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0); +#elif defined(__unix__) + fcntl(sock, F_SETFD, FD_CLOEXEC); +#else + (void) sock; +#endif +} + +int mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len, + int flags) { + int is_v6; + if (buf == NULL || len <= 0) return 0; + memset(buf, 0, len); +#if MG_ENABLE_IPV6 + is_v6 = sa->sa.sa_family == AF_INET6; +#else + is_v6 = 0; +#endif + if (flags & MG_SOCK_STRINGIFY_IP) { +#if MG_ENABLE_IPV6 + const void *addr = NULL; + char *start = buf; + socklen_t capacity = len; + if (!is_v6) { + addr = &sa->sin.sin_addr; + } else { + addr = (void *) &sa->sin6.sin6_addr; + if (flags & MG_SOCK_STRINGIFY_PORT) { + *buf = '['; + start++; + capacity--; + } + } + if (inet_ntop(sa->sa.sa_family, addr, start, capacity) == NULL) { + goto cleanup; + } +#elif defined(_WIN32) || MG_LWIP || (MG_NET_IF == MG_NET_IF_PIC32) + /* Only Windoze Vista (and newer) have inet_ntop() */ + char *addr_str = inet_ntoa(sa->sin.sin_addr); + if (addr_str != NULL) { + strncpy(buf, inet_ntoa(sa->sin.sin_addr), len - 1); + } else { + goto cleanup; + } +#else + if (inet_ntop(AF_INET, (void *) &sa->sin.sin_addr, buf, len - 1) == NULL) { + goto cleanup; + } +#endif + } + if (flags & MG_SOCK_STRINGIFY_PORT) { + int port = ntohs(sa->sin.sin_port); + if (flags & MG_SOCK_STRINGIFY_IP) { + int buf_len = strlen(buf); + snprintf(buf + buf_len, len - (buf_len + 1), "%s:%d", (is_v6 ? "]" : ""), + port); + } else { + snprintf(buf, len, "%d", port); + } + } + + return strlen(buf); + +cleanup: + *buf = '\0'; + return 0; +} + +int mg_conn_addr_to_str(struct mg_connection *nc, char *buf, size_t len, + int flags) { + union socket_address sa; + memset(&sa, 0, sizeof(sa)); + mg_if_get_conn_addr(nc, flags & MG_SOCK_STRINGIFY_REMOTE, &sa); + return mg_sock_addr_to_str(&sa, buf, len, flags); +} + +#if MG_ENABLE_HEXDUMP +static int mg_hexdump_n(const void *buf, int len, char *dst, int dst_len, + int offset) { + const unsigned char *p = (const unsigned char *) buf; + char ascii[17] = ""; + int i, idx, n = 0; + + for (i = 0; i < len; i++) { + idx = i % 16; + if (idx == 0) { + if (i > 0) n += snprintf(dst + n, MAX(dst_len - n, 0), " %s\n", ascii); + n += snprintf(dst + n, MAX(dst_len - n, 0), "%04x ", i + offset); + } + if (dst_len - n < 0) { + return n; + } + n += snprintf(dst + n, MAX(dst_len - n, 0), " %02x", p[i]); + ascii[idx] = p[i] < 0x20 || p[i] > 0x7e ? '.' : p[i]; + ascii[idx + 1] = '\0'; + } + + while (i++ % 16) n += snprintf(dst + n, MAX(dst_len - n, 0), "%s", " "); + n += snprintf(dst + n, MAX(dst_len - n, 0), " %s\n", ascii); + + return n; +} + +int mg_hexdump(const void *buf, int len, char *dst, int dst_len) { + return mg_hexdump_n(buf, len, dst, dst_len, 0); +} + +void mg_hexdumpf(FILE *fp, const void *buf, int len) { + char tmp[80]; + int offset = 0, n; + while (len > 0) { + n = (len < 16 ? len : 16); + mg_hexdump_n(((const char *) buf) + offset, n, tmp, sizeof(tmp), offset); + fputs(tmp, fp); + offset += n; + len -= n; + } +} + +void mg_hexdump_connection(struct mg_connection *nc, const char *path, + const void *buf, int num_bytes, int ev) { + FILE *fp = NULL; + char src[60], dst[60]; + const char *tag = NULL; + switch (ev) { + case MG_EV_RECV: + tag = "<-"; + break; + case MG_EV_SEND: + tag = "->"; + break; + case MG_EV_ACCEPT: + tag = " 0) { + mg_hexdumpf(fp, buf, num_bytes); + } + if (fp != stdout && fp != stderr) fclose(fp); +} +#endif + +int mg_is_big_endian(void) { + static const int n = 1; + /* TODO(mkm) use compiletime check with 4-byte char literal */ + return ((char *) &n)[0] == 0; +} + +DO_NOT_WARN_UNUSED MG_INTERNAL int mg_get_errno(void) { +#ifndef WINCE + return errno; +#else + /* TODO(alashkin): translate error codes? */ + return GetLastError(); +#endif +} + +void mg_mbuf_append_base64_putc(char ch, void *user_data) { + struct mbuf *mbuf = (struct mbuf *) user_data; + mbuf_append(mbuf, &ch, sizeof(ch)); +} + +void mg_mbuf_append_base64(struct mbuf *mbuf, const void *data, size_t len) { + struct cs_base64_ctx ctx; + cs_base64_init(&ctx, mg_mbuf_append_base64_putc, mbuf); + cs_base64_update(&ctx, (const char *) data, len); + cs_base64_finish(&ctx); +} + +void mg_basic_auth_header(const struct mg_str user, const struct mg_str pass, + struct mbuf *buf) { + const char *header_prefix = "Authorization: Basic "; + const char *header_suffix = "\r\n"; + + struct cs_base64_ctx ctx; + cs_base64_init(&ctx, mg_mbuf_append_base64_putc, buf); + + mbuf_append(buf, header_prefix, strlen(header_prefix)); + + cs_base64_update(&ctx, user.p, user.len); + if (pass.len > 0) { + cs_base64_update(&ctx, ":", 1); + cs_base64_update(&ctx, pass.p, pass.len); + } + cs_base64_finish(&ctx); + mbuf_append(buf, header_suffix, strlen(header_suffix)); +} + +struct mg_str mg_url_encode(const struct mg_str src) { + static const char *dont_escape = "._-$,;~()/"; + static const char *hex = "0123456789abcdef"; + size_t i = 0; + struct mbuf mb; + mbuf_init(&mb, src.len); + + for (i = 0; i < src.len; i++) { + const unsigned char c = *((const unsigned char *) src.p + i); + if (isalnum(c) || strchr(dont_escape, c) != NULL) { + mbuf_append(&mb, &c, 1); + } else { + mbuf_append(&mb, "%", 1); + mbuf_append(&mb, &hex[c >> 4], 1); + mbuf_append(&mb, &hex[c & 15], 1); + } + } + mbuf_append(&mb, "", 1); + mbuf_trim(&mb); + return mg_mk_str_n(mb.buf, mb.len - 1); +} diff --git a/src/mongoose-6.11/src/mg_util.h b/src/mongoose-6.11/src/mg_util.h new file mode 100644 index 0000000..e6f2e48 --- /dev/null +++ b/src/mongoose-6.11/src/mg_util.h @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +/* + * === Utility API + */ + +#ifndef CS_MONGOOSE_SRC_UTIL_H_ +#define CS_MONGOOSE_SRC_UTIL_H_ + +#include + +#include "mg_common.h" +#include "mg_net_if.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MG_MAX_PATH +#ifdef PATH_MAX +#define MG_MAX_PATH PATH_MAX +#else +#define MG_MAX_PATH 256 +#endif +#endif + +/* + * Fetches substring from input string `s`, `end` into `v`. + * Skips initial delimiter characters. Records first non-delimiter character + * at the beginning of substring `v`. Then scans the rest of the string + * until a delimiter character or end-of-string is found. + * `delimiters` is a 0-terminated string containing delimiter characters. + * Either one of `delimiters` or `end_string` terminates the search. + * Returns an `s` pointer, advanced forward where parsing has stopped. + */ +const char *mg_skip(const char *s, const char *end_string, + const char *delimiters, struct mg_str *v); + +/* + * Decodes base64-encoded string `s`, `len` into the destination `dst`. + * The destination has to have enough space to hold the decoded buffer. + * Decoding stops either when all strings have been decoded or invalid an + * character appeared. + * Destination is '\0'-terminated. + * Returns the number of decoded characters. On success, that should be equal + * to `len`. On error (invalid character) the return value is smaller then + * `len`. + */ +int mg_base64_decode(const unsigned char *s, int len, char *dst); + +/* + * Base64-encode chunk of memory `src`, `src_len` into the destination `dst`. + * Destination has to have enough space to hold encoded buffer. + * Destination is '\0'-terminated. + */ +void mg_base64_encode(const unsigned char *src, int src_len, char *dst); + +#if MG_ENABLE_FILESYSTEM +/* + * Performs a 64-bit `stat()` call against a given file. + * + * `path` should be UTF8 encoded. + * + * Return value is the same as for `stat()` syscall. + */ +int mg_stat(const char *path, cs_stat_t *st); + +/* + * Opens the given file and returns a file stream. + * + * `path` and `mode` should be UTF8 encoded. + * + * Return value is the same as for the `fopen()` call. + */ +FILE *mg_fopen(const char *path, const char *mode); + +/* + * Opens the given file and returns a file stream. + * + * `path` should be UTF8 encoded. + * + * Return value is the same as for the `open()` syscall. + */ +int mg_open(const char *path, int flag, int mode); + +/* + * Reads data from the given file stream. + * + * Return value is a number of bytes readen. + */ +size_t mg_fread(void *ptr, size_t size, size_t count, FILE *f); + +/* + * Writes data to the given file stream. + * + * Return value is a number of bytes wtitten. + */ +size_t mg_fwrite(const void *ptr, size_t size, size_t count, FILE *f); + +#endif /* MG_ENABLE_FILESYSTEM */ + +#if MG_ENABLE_THREADS +/* + * Starts a new detached thread. + * Arguments and semantics are the same as pthead's `pthread_create()`. + * `thread_func` is a thread function, `thread_func_param` is a parameter + * that is passed to the thread function. + */ +void *mg_start_thread(void *(*thread_func)(void *), void *thread_func_param); +#endif + +void mg_set_close_on_exec(sock_t); + +#define MG_SOCK_STRINGIFY_IP 1 +#define MG_SOCK_STRINGIFY_PORT 2 +#define MG_SOCK_STRINGIFY_REMOTE 4 +/* + * Converts a connection's local or remote address into string. + * + * The `flags` parameter is a bit mask that controls the behaviour, + * see `MG_SOCK_STRINGIFY_*` definitions. + * + * - MG_SOCK_STRINGIFY_IP - print IP address + * - MG_SOCK_STRINGIFY_PORT - print port number + * - MG_SOCK_STRINGIFY_REMOTE - print remote peer's IP/port, not local address + * + * If both port number and IP address are printed, they are separated by `:`. + * If compiled with `-DMG_ENABLE_IPV6`, IPv6 addresses are supported. + * Return length of the stringified address. + */ +int mg_conn_addr_to_str(struct mg_connection *c, char *buf, size_t len, + int flags); +#if MG_NET_IF == MG_NET_IF_SOCKET +/* Legacy interface. */ +void mg_sock_to_str(sock_t sock, char *buf, size_t len, int flags); +#endif + +/* + * Convert the socket's address into string. + * + * `flags` is MG_SOCK_STRINGIFY_IP and/or MG_SOCK_STRINGIFY_PORT. + */ +int mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len, + int flags); + +#if MG_ENABLE_HEXDUMP +/* + * Generates a human-readable hexdump of memory chunk. + * + * Takes a memory buffer `buf` of length `len` and creates a hex dump of that + * buffer in `dst`. The generated output is a-la hexdump(1). + * Returns the length of generated string, excluding terminating `\0`. If + * returned length is bigger than `dst_len`, the overflow bytes are discarded. + */ +int mg_hexdump(const void *buf, int len, char *dst, int dst_len); + +/* Same as mg_hexdump, but with output going to file instead of a buffer. */ +void mg_hexdumpf(FILE *fp, const void *buf, int len); + +/* + * Generates human-readable hexdump of the data sent or received by the + * connection. `path` is a file name where hexdump should be written. + * `num_bytes` is a number of bytes sent/received. `ev` is one of the `MG_*` + * events sent to an event handler. This function is supposed to be called from + * the event handler. + */ +void mg_hexdump_connection(struct mg_connection *nc, const char *path, + const void *buf, int num_bytes, int ev); +#endif + +/* + * Returns true if target platform is big endian. + */ +int mg_is_big_endian(void); + +/* + * Use with cs_base64_init/update/finish in order to write out base64 in chunks. + */ +void mg_mbuf_append_base64_putc(char ch, void *user_data); + +/* + * Encode `len` bytes starting at `data` as base64 and append them to an mbuf. + */ +void mg_mbuf_append_base64(struct mbuf *mbuf, const void *data, size_t len); + +/* + * Generate a Basic Auth header and appends it to buf. + * If pass is NULL, then user is expected to contain the credentials pair + * already encoded as `user:pass`. + */ +void mg_basic_auth_header(const struct mg_str user, const struct mg_str pass, + struct mbuf *buf); + +/* + * URL-escape the specified string. + * All non-printable characters are escaped, plus `._-$,;~()/`. + * Input need not be NUL-terminated, but the returned string is. + * Returned string is heap-allocated and must be free()'d. + */ +struct mg_str mg_url_encode(const struct mg_str src); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CS_MONGOOSE_SRC_UTIL_H_ */ diff --git a/src/mpd_client.c b/src/mpd_client.c index 0acf93e..ff44d8c 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -36,21 +36,21 @@ #include "json_encode.h" /* forward declaration */ -static int mpd_notify_callback(struct mg_connection *c, enum mg_event ev); +static int mpd_notify_callback(struct mg_connection *c, const char *param); const char * mpd_cmd_strs[] = { MPD_CMDS(GEN_STR) }; -char * get_arg1 (char *p) { +char * get_arg1 (const char *p) { return strchr(p, ',') + 1; } -char * get_arg2 (char *p) { +char * get_arg2 (const char *p) { return get_arg1(get_arg1(p)); } -static inline enum mpd_cmd_ids get_cmd_id(char *cmd) +static inline enum mpd_cmd_ids get_cmd_id(const char *cmd) { for(int i = 0; i < sizeof(mpd_cmd_strs)/sizeof(mpd_cmd_strs[0]); i++) if(!strncmp(cmd, mpd_cmd_strs[i], strlen(mpd_cmd_strs[i]))) @@ -59,9 +59,9 @@ static inline enum mpd_cmd_ids get_cmd_id(char *cmd) return -1; } -int callback_mpd(struct mg_connection *c) +int callback_mpd(struct mg_connection *c,const struct mg_str msg) { - enum mpd_cmd_ids cmd_id = get_cmd_id(c->content); + enum mpd_cmd_ids cmd_id = get_cmd_id(msg.p); size_t n = 0; unsigned int uint_buf, uint_buf_2; int int_buf; @@ -70,10 +70,10 @@ int callback_mpd(struct mg_connection *c) char *p_charbuf2 = NULL; char *searchstr = NULL; - fprintf(stdout,"Got request: %s:%d\n",c->content,cmd_id); + fprintf(stdout,"Got request: %s:%d\n",msg.p,cmd_id); if(cmd_id == -1) - return MG_TRUE; + return 0; switch(cmd_id) { @@ -99,15 +99,15 @@ int callback_mpd(struct mg_connection *c) mpd_run_clear(mpd.conn); break; case MPD_API_RM_TRACK: - if(sscanf(c->content, "MPD_API_RM_TRACK,%u", &uint_buf)) + if(sscanf(msg.p, "MPD_API_RM_TRACK,%u", &uint_buf)) mpd_run_delete_id(mpd.conn, uint_buf); break; case MPD_API_RM_RANGE: - if(sscanf(c->content, "MPD_API_RM_RANGE,%u,%u", &uint_buf, &uint_buf_2)) + if(sscanf(msg.p, "MPD_API_RM_RANGE,%u,%u", &uint_buf, &uint_buf_2)) mpd_run_delete_range(mpd.conn, uint_buf, uint_buf_2); break; case MPD_API_MOVE_TRACK: - if (sscanf(c->content, "MPD_API_MOVE_TRACK,%u,%u", &uint_buf, &uint_buf_2) == 2) + if (sscanf(msg.p, "MPD_API_MOVE_TRACK,%u,%u", &uint_buf, &uint_buf_2) == 2) { uint_buf -= 1; uint_buf_2 -= 1; @@ -115,44 +115,43 @@ int callback_mpd(struct mg_connection *c) } break; case MPD_API_PLAY_TRACK: - if(sscanf(c->content, "MPD_API_PLAY_TRACK,%u", &uint_buf)) + if(sscanf(msg.p, "MPD_API_PLAY_TRACK,%u", &uint_buf)) mpd_run_play_id(mpd.conn, uint_buf); break; case MPD_API_TOGGLE_RANDOM: - if(sscanf(c->content, "MPD_API_TOGGLE_RANDOM,%u", &uint_buf)) + if(sscanf(msg.p, "MPD_API_TOGGLE_RANDOM,%u", &uint_buf)) mpd_run_random(mpd.conn, uint_buf); break; case MPD_API_TOGGLE_REPEAT: - if(sscanf(c->content, "MPD_API_TOGGLE_REPEAT,%u", &uint_buf)) + if(sscanf(msg.p, "MPD_API_TOGGLE_REPEAT,%u", &uint_buf)) mpd_run_repeat(mpd.conn, uint_buf); break; case MPD_API_TOGGLE_CONSUME: - if(sscanf(c->content, "MPD_API_TOGGLE_CONSUME,%u", &uint_buf)) + if(sscanf(msg.p, "MPD_API_TOGGLE_CONSUME,%u", &uint_buf)) mpd_run_consume(mpd.conn, uint_buf); break; case MPD_API_TOGGLE_SINGLE: - if(sscanf(c->content, "MPD_API_TOGGLE_SINGLE,%u", &uint_buf)) + if(sscanf(msg.p, "MPD_API_TOGGLE_SINGLE,%u", &uint_buf)) mpd_run_single(mpd.conn, uint_buf); break; case MPD_API_SET_CROSSFADE: - if(sscanf(c->content, "MPD_API_SET_CROSSFADE,%u", &uint_buf)) + if(sscanf(msg.p, "MPD_API_SET_CROSSFADE,%u", &uint_buf)) mpd_run_crossfade(mpd.conn, uint_buf); break; case MPD_API_SET_MIXRAMPDB: - if(sscanf(c->content, "MPD_API_SET_MIXRAMPDB,%f", &float_buf)) + if(sscanf(msg.p, "MPD_API_SET_MIXRAMPDB,%f", &float_buf)) mpd_run_mixrampdb(mpd.conn, float_buf); break; case MPD_API_SET_MIXRAMPDELAY: - if(sscanf(c->content, "MPD_API_SET_MIXRAMPDELAY,%f", &float_buf)) + if(sscanf(msg.p, "MPD_API_SET_MIXRAMPDELAY,%f", &float_buf)) mpd_run_mixrampdelay(mpd.conn, float_buf); break; case MPD_API_GET_OUTPUTS: mpd.buf_size = mpd_put_outputs(mpd.buf, 1); - c->callback_param = NULL; - mpd_notify_callback(c, MG_POLL); + mpd_notify_callback(c, NULL); break; case MPD_API_TOGGLE_OUTPUT: - if (sscanf(c->content, "MPD_API_TOGGLE_OUTPUT,%u,%u", &uint_buf, &uint_buf_2)) { + if (sscanf(msg.p, "MPD_API_TOGGLE_OUTPUT,%u,%u", &uint_buf, &uint_buf_2)) { if (uint_buf_2) mpd_run_enable_output(mpd.conn, uint_buf); else @@ -160,15 +159,15 @@ int callback_mpd(struct mg_connection *c) } break; case MPD_API_SET_VOLUME: - if(sscanf(c->content, "MPD_API_SET_VOLUME,%ud", &uint_buf) && uint_buf <= 100) + if(sscanf(msg.p, "MPD_API_SET_VOLUME,%ud", &uint_buf) && uint_buf <= 100) mpd_run_set_volume(mpd.conn, uint_buf); break; case MPD_API_SET_SEEK: - if(sscanf(c->content, "MPD_API_SET_SEEK,%u,%u", &uint_buf, &uint_buf_2)) + if(sscanf(msg.p, "MPD_API_SET_SEEK,%u,%u", &uint_buf, &uint_buf_2)) mpd_run_seek_id(mpd.conn, uint_buf, uint_buf_2); break; case MPD_API_GET_QUEUE: - if(sscanf(c->content, "MPD_API_GET_QUEUE,%u", &uint_buf)) + if(sscanf(msg.p, "MPD_API_GET_QUEUE,%u", &uint_buf)) n = mpd_put_queue(mpd.buf, uint_buf); break; case MPD_API_GET_CURRENT_SONG: @@ -176,7 +175,7 @@ int callback_mpd(struct mg_connection *c) break; case MPD_API_GET_ARTISTS: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_ARTISTS")) goto out_artist; uint_buf = strtoul(strtok(NULL, ","), NULL, 10); @@ -192,7 +191,7 @@ out_artist: free(p_charbuf); break; case MPD_API_GET_ARTISTALBUMS: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_ARTISTALBUMS")) goto out_artistalbum; uint_buf = strtoul(strtok(NULL, ","), NULL, 10); @@ -216,7 +215,7 @@ out_artistalbum: free(p_charbuf); break; case MPD_API_GET_ARTISTALBUMTITLES: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_ARTISTALBUMTITLES")) goto out_artistalbumtitle; @@ -231,7 +230,7 @@ out_artistalbumtitle: free(p_charbuf); break; case MPD_API_GET_PLAYLISTS: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_PLAYLISTS")) goto out_artist; uint_buf = strtoul(strtok(NULL, ","), NULL, 10); @@ -247,7 +246,7 @@ out_playlists: free(p_charbuf); break; case MPD_API_GET_BROWSE: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_BROWSE")) goto out_browse; @@ -274,7 +273,7 @@ out_browse: break; case MPD_API_ADD_TRACK: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_ADD_TRACK")) goto out_add_track; @@ -282,13 +281,13 @@ out_browse: goto out_add_track; free(p_charbuf); - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); mpd_run_add(mpd.conn, get_arg1(p_charbuf)); out_add_track: free(p_charbuf); break; case MPD_API_ADD_PLAY_TRACK: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_ADD_PLAY_TRACK")) goto out_play_track; @@ -296,7 +295,7 @@ out_add_track: goto out_play_track; free(p_charbuf); - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); int_buf = mpd_run_add_id(mpd.conn, get_arg1(p_charbuf)); if(int_buf != -1) mpd_run_play_id(mpd.conn, int_buf); @@ -304,7 +303,7 @@ out_play_track: free(p_charbuf); break; case MPD_API_ADD_PLAYLIST: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_ADD_PLAYLIST")) goto out_playlist; @@ -312,13 +311,13 @@ out_play_track: goto out_playlist; free(p_charbuf); - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); mpd_run_load(mpd.conn, get_arg1(p_charbuf)); out_playlist: free(p_charbuf); break; case MPD_API_SAVE_QUEUE: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_SAVE_QUEUE")) goto out_save_queue; @@ -326,13 +325,13 @@ out_playlist: goto out_save_queue; free(p_charbuf); - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); mpd_run_save(mpd.conn, get_arg1(p_charbuf)); out_save_queue: free(p_charbuf); break; case MPD_API_SEARCH_QUEUE: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_SEARCH_QUEUE")) goto out_search_queue; if((token = strtok(NULL, ",")) == NULL) { @@ -356,7 +355,7 @@ out_search_queue: free(p_charbuf); break; case MPD_API_SEARCH_ADD: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_SEARCH_ADD")) goto out_search_add; if((token = strtok(NULL, ",")) == NULL) { @@ -378,7 +377,7 @@ out_search_add: free(p_charbuf); break; case MPD_API_SEARCH: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_SEARCH")) goto out_search; if((token = strtok(NULL, ",")) == NULL) { @@ -405,7 +404,7 @@ out_search: mpd_run_shuffle(mpd.conn); break; case MPD_API_SEND_MESSAGE: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_SEND_MESSAGE")) goto out_send_message; @@ -413,7 +412,7 @@ out_search: goto out_send_message; free(p_charbuf); - p_charbuf = strdup(get_arg1(c->content)); + p_charbuf = strdup(get_arg1(msg.p)); if ( strtok(p_charbuf, ",") == NULL ) goto out_send_message; @@ -426,7 +425,7 @@ out_send_message: free(p_charbuf); break; case MPD_API_RM_PLAYLIST: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_RM_PLAYLIST")) goto out_rm_playlist; @@ -434,7 +433,7 @@ out_send_message: goto out_rm_playlist; free(p_charbuf); - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); mpd_run_rm(mpd.conn, get_arg1(p_charbuf)); out_rm_playlist: free(p_charbuf); @@ -446,7 +445,7 @@ out_rm_playlist: n = mympd_get_stats(mpd.buf); break; case MPD_API_SET_REPLAYGAIN: - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); if(strcmp(strtok(p_charbuf, ","), "MPD_API_SET_REPLAYGAIN")) goto out_set_replaygain; @@ -454,7 +453,7 @@ out_rm_playlist: goto out_set_replaygain; free(p_charbuf); - p_charbuf = strdup(c->content); + p_charbuf = strdup(msg.p); mpd_send_command(mpd.conn, "replay_gain_mode", get_arg1(p_charbuf), NULL); struct mpd_pair *pair; while ((pair = mpd_recv_pair(mpd.conn)) != NULL) { @@ -477,34 +476,33 @@ out_set_replaygain: if(n > 0) { //fprintf(stdout,"Send response:\n %s\n",mpd.buf); - mg_websocket_write(c, 1, mpd.buf, n); + mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); } - return MG_TRUE; + return 0; } int mpd_close_handler(struct mg_connection *c) { /* Cleanup session data */ - if(c->connection_param) - free(c->connection_param); +// if(c->connection_param) +// free(c->connection_param); return 0; } -static int mpd_notify_callback(struct mg_connection *c, enum mg_event ev) { +static int mpd_notify_callback(struct mg_connection *c, const char *param) { size_t n; - if(!c->is_websocket) - return MG_TRUE; + if(is_websocket(c)) + return 0; - if(c->callback_param) + if(param) { /* error message? */ - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\",\"data\":\"%s\"}", - (const char *)c->callback_param); + n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\",\"data\":\"%s\"}",param); - mg_websocket_write(c, 1, mpd.buf, n); - return MG_TRUE; + mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); + return 0; } if(!c->connection_param) @@ -514,32 +512,32 @@ static int mpd_notify_callback(struct mg_connection *c, enum mg_event ev) { if(mpd.conn_state != MPD_CONNECTED) { n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"disconnected\"}"); - mg_websocket_write(c, 1, mpd.buf, n); + mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); } else { - mg_websocket_write(c, 1, mpd.buf, mpd.buf_size); - - if(s->song_id != mpd.song_id)// || s->queue_version != mpd.queue_version) + mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); + + if(s->song_id != mpd.song_id) { n = mpd_put_current_song(mpd.buf); - mg_websocket_write(c, 1, mpd.buf, n); + mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); s->song_id = mpd.song_id; } if(s->queue_version != mpd.queue_version) { n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"update_queue\"}"); - mg_websocket_write(c, 1, mpd.buf, n); + mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); s->queue_version = mpd.queue_version; } } - return MG_TRUE; + return 0; } -void mpd_poll(struct mg_server *s) +void mpd_poll(struct mg_mgr *s) { switch (mpd.conn_state) { case MPD_DISCONNECTED: @@ -556,8 +554,7 @@ void mpd_poll(struct mg_server *s) fprintf(stderr, "MPD connection: %s\n", mpd_connection_get_error_message(mpd.conn)); for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) { - c->callback_param = (void *)mpd_connection_get_error_message(mpd.conn); - mpd_notify_callback(c, MG_POLL); + mpd_notify_callback(c, mpd_connection_get_error_message(mpd.conn)); } mpd.conn_state = MPD_FAILURE; return; @@ -568,8 +565,7 @@ void mpd_poll(struct mg_server *s) fprintf(stderr, "MPD connection: %s\n", mpd_connection_get_error_message(mpd.conn)); for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) { - c->callback_param = (void *)mpd_connection_get_error_message(mpd.conn); - mpd_notify_callback(c, MG_POLL); + mpd_notify_callback(c, mpd_connection_get_error_message(mpd.conn)); } mpd.conn_state = MPD_FAILURE; return; @@ -582,8 +578,7 @@ void mpd_poll(struct mg_server *s) mpd.buf_size = mpd_put_outputs(mpd.buf, 1); for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) { - c->callback_param = NULL; - mpd_notify_callback(c, MG_POLL); + mpd_notify_callback(c, NULL); } break; @@ -602,14 +597,12 @@ void mpd_poll(struct mg_server *s) mpd.buf_size = mpd_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.queue_version); for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) { - c->callback_param = NULL; - mpd_notify_callback(c, MG_POLL); + mpd_notify_callback(c, NULL); } mpd.buf_size = mpd_put_outputs(mpd.buf, 0); for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) { - c->callback_param = NULL; - mpd_notify_callback(c, MG_POLL); + mpd_notify_callback(c, NULL); } break; } diff --git a/src/mpd_client.h b/src/mpd_client.h index 36ebd53..770dbbf 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -120,14 +120,18 @@ struct t_mpd { int streamport; char coverimage[40]; +static int is_websocket(const struct mg_connection *nc) { + return nc->flags & MG_F_IS_WEBSOCKET; +}; + struct t_mpd_client_session { int song_id; int next_song_id; unsigned queue_version; }; -void mpd_poll(struct mg_server *s); -int callback_mpd(struct mg_connection *c); +void mpd_poll(struct mg_mgr *s); +int callback_mpd(struct mg_connection *c,const struct mg_str msg); int mpd_close_handler(struct mg_connection *c); int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsigned *queue_version); int mpd_put_outputs(char *buffer, int putnames); diff --git a/src/mympd.c b/src/mympd.c index 8ddf691..935fdf4 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -31,68 +31,57 @@ #include #include "mongoose/mongoose.h" -//#include "http_server.h" #include "mpd_client.h" #include "config.h" extern char *optarg; +static sig_atomic_t s_signal_received = 0; +static struct mg_serve_http_opts s_http_server_opts; -int force_exit = 0; - -void bye() -{ - force_exit = 1; +static void signal_handler(int sig_num) { + signal(sig_num, signal_handler); // Reinstantiate signal handler + s_signal_received = sig_num; } - -static int is_websocket(const struct mg_connection *nc) { - return nc->flags & MG_F_IS_WEBSOCKET; -} - -static void server_callback(struct mg_connection *c, int ev, void *p) { -// int result = MG_EV_FALSE; - FILE *fp = NULL; - +static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { switch(ev) { - case MG_EV_CLOSE: - mpd_close_handler(c); - break; -// return MG_TRUE; - case MG_EV_HTTP_REQUEST: - if (is_websocket(c)) { - c->content[c->content_len] = '\0'; - if(c->content_len) - callback_mpd(c); - break; - } else + case MG_EV_CLOSE: { + mpd_close_handler(nc); break; - case MG_EV_AUTH: - // no auth for websockets since mobile safari does not support it - if ( (mpd.gpass == NULL) || (c->is_websocket) || ((mpd.local_port > 0) && (c->local_port == mpd.local_port)) ) - break; - else { - if ( (fp = fopen(mpd.gpass, "r")) != NULL ) { - mg_authorize_digest(c, fp); - fclose(fp); - } - } + } + case MG_EV_WEBSOCKET_FRAME: { + struct websocket_message *wm = (struct websocket_message *) ev_data; + struct mg_str d = {(char *) wm->data, wm->size}; + callback_mpd(nc, d); break; + } + case MG_EV_HTTP_REQUEST: { + mg_serve_http(nc, (struct http_message *) ev_data, s_http_server_opts); + break; + } } } int main(int argc, char **argv) { int n, option_index = 0; - struct mg_server *server = mg_create_server(NULL, server_callback); + + struct mg_mgr mgr; + struct mg_connection *nc; + unsigned int current_timer = 0, last_timer = 0; + + signal(SIGTERM, signal_handler); + signal(SIGINT, signal_handler); + setvbuf(stdout, NULL, _IOLBF, 0); + setvbuf(stderr, NULL, _IOLBF, 0); + + mg_mgr_init(&mgr, NULL); + char *run_as_user = NULL; char const *error_msg = NULL; char *webport = "8080"; - atexit(bye); - mg_set_option(server, "document_root", SRC_PATH); - - mg_set_option(server, "auth_domain", "mympd"); mpd.port = 6600; mpd.local_port = 0; mpd.gpass = NULL; @@ -171,42 +160,23 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - if(error_msg) - { - fprintf(stderr, "Mongoose error: %s\n", error_msg); - return EXIT_FAILURE; - } } - error_msg = mg_set_option(server, "listening_port", webport); - if(error_msg) { - fprintf(stderr, "Mongoose error: %s\n", error_msg); - return EXIT_FAILURE; - } + nc = mg_bind(&mgr, webport, ev_handler); + mg_set_protocol_http_websocket(nc); + s_http_server_opts.document_root = SRC_PATH; - /* drop privilges at last to ensure proper port binding */ - if(run_as_user != NULL) { - error_msg = mg_set_option(server, "run_as_user", run_as_user); - free(run_as_user); - if(error_msg) - { - fprintf(stderr, "Mongoose error: %s\n", error_msg); - return EXIT_FAILURE; - } - } - - while (!force_exit) { - mg_poll_server(server, 200); + printf("Started on port %s\n", webport); + while (s_signal_received == 0) { + mg_mgr_poll(&mgr, 200); current_timer = time(NULL); if(current_timer - last_timer) { last_timer = current_timer; - mpd_poll(server); + mpd_poll(&mgr); } } - + mg_mgr_free(&mgr); mpd_disconnect(); - mg_destroy_server(&server); - return EXIT_SUCCESS; } From 401bd5302f51522a3af2d0ceaedfd2ce7de9b33b Mon Sep 17 00:00:00 2001 From: jcorporation Date: Tue, 12 Jun 2018 02:09:24 +0100 Subject: [PATCH 03/32] Rewrite to newest mongoose version --- src/mpd_client.c | 15 ++++++++------- src/mpd_client.h | 2 +- src/mympd.c | 5 ++++- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/mpd_client.c b/src/mpd_client.c index ff44d8c..13a5112 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -59,7 +59,7 @@ static inline enum mpd_cmd_ids get_cmd_id(const char *cmd) return -1; } -int callback_mpd(struct mg_connection *c,const struct mg_str msg) +void callback_mpd(struct mg_connection *c,const struct mg_str msg) { enum mpd_cmd_ids cmd_id = get_cmd_id(msg.p); size_t n = 0; @@ -69,8 +69,9 @@ int callback_mpd(struct mg_connection *c,const struct mg_str msg) char *p_charbuf = NULL, *token; char *p_charbuf2 = NULL; char *searchstr = NULL; + char request[500]; - fprintf(stdout,"Got request: %s:%d\n",msg.p,cmd_id); + fprintf(stdout,"Got request: %s\n",msg.p); if(cmd_id == -1) return 0; @@ -485,8 +486,8 @@ out_set_replaygain: int mpd_close_handler(struct mg_connection *c) { /* Cleanup session data */ -// if(c->connection_param) -// free(c->connection_param); + if(c->user_data) + free(c->user_data); return 0; } @@ -505,10 +506,10 @@ static int mpd_notify_callback(struct mg_connection *c, const char *param) { return 0; } - if(!c->connection_param) - c->connection_param = calloc(1, sizeof(struct t_mpd_client_session)); + if(!c->user_data) + c->user_data = calloc(1, sizeof(struct t_mpd_client_session)); - struct t_mpd_client_session *s = (struct t_mpd_client_session *)c->connection_param; + struct t_mpd_client_session *s = (struct t_mpd_client_session *)c->user_data; if(mpd.conn_state != MPD_CONNECTED) { n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"disconnected\"}"); diff --git a/src/mpd_client.h b/src/mpd_client.h index 770dbbf..4ae484a 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -131,7 +131,7 @@ struct t_mpd_client_session { }; void mpd_poll(struct mg_mgr *s); -int callback_mpd(struct mg_connection *c,const struct mg_str msg); +void callback_mpd(struct mg_connection *c, const struct mg_str msg); int mpd_close_handler(struct mg_connection *c); int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsigned *queue_version); int mpd_put_outputs(char *buffer, int putnames); diff --git a/src/mympd.c b/src/mympd.c index 935fdf4..5e5038a 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -46,11 +46,14 @@ static void signal_handler(int sig_num) { static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { switch(ev) { case MG_EV_CLOSE: { - mpd_close_handler(nc); + if (is_websocket(nc)) { + mpd_close_handler(nc); + } break; } case MG_EV_WEBSOCKET_FRAME: { struct websocket_message *wm = (struct websocket_message *) ev_data; + wm->data[wm->size]='\0'; struct mg_str d = {(char *) wm->data, wm->size}; callback_mpd(nc, d); break; From 998d675b2c49229e299d4a69b8d5bb7531df4d22 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Wed, 13 Jun 2018 00:29:58 +0100 Subject: [PATCH 04/32] Fixed websocket connection - Send on connect settings and output --- htdocs/js/mpd.js | 6 ++---- src/mongoose-6.11/mongoose.h | 2 +- src/mpd_client.c | 41 ++++++++++++++++++------------------ src/mpd_client.h | 4 ++-- src/mympd.c | 28 ++++++++++++++++++------ 5 files changed, 48 insertions(+), 33 deletions(-) diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index b99ea38..1c54a58 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -283,10 +283,8 @@ function webSocketConnect() { try { socket.onopen = function() { console.log("connected"); - /* emit request for mympd settings */ - socket.send('MPD_API_GET_SETTINGS'); - /* emit initial request for output names */ - socket.send('MPD_API_GET_OUTPUTS'); + //socket.send('MPD_API_GET_OUTPUTS'); + //socket.send('MPD_API_GET_SETTINGS'); showNotification('Connected to myMPD','','','success'); $('#modalConnectionError').modal('hide'); app.route(); diff --git a/src/mongoose-6.11/mongoose.h b/src/mongoose-6.11/mongoose.h index eecbbef..2529bdf 100644 --- a/src/mongoose-6.11/mongoose.h +++ b/src/mongoose-6.11/mongoose.h @@ -4682,7 +4682,7 @@ int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf, #ifdef __GNUC__ __attribute__((deprecated)); #endif -; + /* * Gets and parses the Authorization: Basic header diff --git a/src/mpd_client.c b/src/mpd_client.c index 13a5112..0d0d8ca 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -59,7 +59,7 @@ static inline enum mpd_cmd_ids get_cmd_id(const char *cmd) return -1; } -void callback_mpd(struct mg_connection *c,const struct mg_str msg) +void callback_mpd(struct mg_connection *nc, const struct mg_str msg) { enum mpd_cmd_ids cmd_id = get_cmd_id(msg.p); size_t n = 0; @@ -69,12 +69,11 @@ void callback_mpd(struct mg_connection *c,const struct mg_str msg) char *p_charbuf = NULL, *token; char *p_charbuf2 = NULL; char *searchstr = NULL; - char request[500]; fprintf(stdout,"Got request: %s\n",msg.p); if(cmd_id == -1) - return 0; + return; switch(cmd_id) { @@ -149,7 +148,7 @@ void callback_mpd(struct mg_connection *c,const struct mg_str msg) break; case MPD_API_GET_OUTPUTS: mpd.buf_size = mpd_put_outputs(mpd.buf, 1); - mpd_notify_callback(c, NULL); + mpd_notify_callback(nc, NULL); break; case MPD_API_TOGGLE_OUTPUT: if (sscanf(msg.p, "MPD_API_TOGGLE_OUTPUT,%u,%u", &uint_buf, &uint_buf_2)) { @@ -467,6 +466,7 @@ out_set_replaygain: if(mpd.conn_state == MPD_CONNECTED && mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS) { + fprintf(stdout,"Error: %s\n",mpd_connection_get_error_message(mpd.conn)); n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\", \"data\": \"%s\"}", mpd_connection_get_error_message(mpd.conn)); @@ -476,11 +476,9 @@ out_set_replaygain: } if(n > 0) { - //fprintf(stdout,"Send response:\n %s\n",mpd.buf); - mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); + fprintf(stdout,"Send response:\n %s\n",mpd.buf); + mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, mpd.buf, n); } - - return 0; } int mpd_close_handler(struct mg_connection *c) @@ -493,16 +491,15 @@ int mpd_close_handler(struct mg_connection *c) static int mpd_notify_callback(struct mg_connection *c, const char *param) { size_t n; - - if(is_websocket(c)) + if(!is_websocket(c)) return 0; if(param) { /* error message? */ - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\",\"data\":\"%s\"}",param); - - mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); + n=snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\",\"data\":\"%s\"}",param); + fprintf(stdout,"Error in mpd_notify_callback: %s\n",param); + mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, n); return 0; } @@ -512,24 +509,28 @@ static int mpd_notify_callback(struct mg_connection *c, const char *param) { struct t_mpd_client_session *s = (struct t_mpd_client_session *)c->user_data; if(mpd.conn_state != MPD_CONNECTED) { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"disconnected\"}"); - mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); + n=snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"disconnected\"}"); + fprintf(stdout,"Notify: disconnected\n"); + mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, n); } else { - mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); + fprintf(stdout,"Notify: %s\n",mpd.buf); + mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, mpd.buf_size); if(s->song_id != mpd.song_id) { - n = mpd_put_current_song(mpd.buf); - mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); + n=mpd_put_current_song(mpd.buf); + fprintf(stdout,"Notify: %s\n",mpd.buf); + mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, n); s->song_id = mpd.song_id; } if(s->queue_version != mpd.queue_version) { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"update_queue\"}"); - mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); + n=snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"update_queue\"}"); + fprintf(stdout,"Notify: update_queue\n"); + mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, n); s->queue_version = mpd.queue_version; } diff --git a/src/mpd_client.h b/src/mpd_client.h index 4ae484a..0f515e1 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -122,7 +122,7 @@ char coverimage[40]; static int is_websocket(const struct mg_connection *nc) { return nc->flags & MG_F_IS_WEBSOCKET; -}; +} struct t_mpd_client_session { int song_id; @@ -131,7 +131,7 @@ struct t_mpd_client_session { }; void mpd_poll(struct mg_mgr *s); -void callback_mpd(struct mg_connection *c, const struct mg_str msg); +void callback_mpd(struct mg_connection *nc, const struct mg_str msg); int mpd_close_handler(struct mg_connection *c); int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsigned *queue_version); int mpd_put_outputs(char *buffer, int putnames); diff --git a/src/mympd.c b/src/mympd.c index 5e5038a..7f2ea81 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -45,23 +45,39 @@ static void signal_handler(int sig_num) { static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { switch(ev) { - case MG_EV_CLOSE: { - if (is_websocket(nc)) { - mpd_close_handler(nc); - } - break; + case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { + fprintf(stdout,"New Websocket connection\n"); + struct mg_str d = {(char *) "MPD_API_GET_SETTINGS", 20 }; + callback_mpd(nc, d); + d.p="MPD_API_GET_OUTPUTS"; + d.len=19; + callback_mpd(nc, d); + break; } case MG_EV_WEBSOCKET_FRAME: { struct websocket_message *wm = (struct websocket_message *) ev_data; wm->data[wm->size]='\0'; struct mg_str d = {(char *) wm->data, wm->size}; + fprintf(stdout,"Websocket request: %s\n",wm->data); callback_mpd(nc, d); break; } case MG_EV_HTTP_REQUEST: { - mg_serve_http(nc, (struct http_message *) ev_data, s_http_server_opts); + struct http_message *hm = (struct http_message *) ev_data; + printf("HTTP request: %.*s\n",hm->uri.len,hm->uri.p); + mg_serve_http(nc, hm, s_http_server_opts); break; } + case MG_EV_CLOSE: { + if (is_websocket(nc)) { + fprintf(stdout,"Websocket connection closed\n"); + mpd_close_handler(nc); + } + else { + fprintf(stdout,"HTTP Close\n"); + } + break; + } } } From 7a3103d035f148b7e6d3022bedb86e861cac9471 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 14 Jun 2018 16:28:10 +0100 Subject: [PATCH 05/32] Removed mongoose src --- CMakeLists.txt | 8 +- src/config.h.in | 1 + src/mongoose | 1 - src/mongoose-6.11/.mbedignore | 9 - src/mongoose-6.11/CONTRIBUTING.md | 8 - src/mongoose-6.11/README.md | 68 - src/mongoose-6.11/jni/Android.mk | 8 - src/mongoose-6.11/src/CPPLINT.cfg | 1 - src/mongoose-6.11/src/common/cs_dbg.c | 105 - src/mongoose-6.11/src/common/cs_dbg.h | 149 - src/mongoose-6.11/src/common/cs_md5.c | 211 - src/mongoose-6.11/src/common/cs_md5.h | 33 - src/mongoose-6.11/src/common/cs_time.c | 83 - src/mongoose-6.11/src/common/cs_time.h | 30 - src/mongoose-6.11/src/common/mg_mem.h | 33 - src/mongoose-6.11/src/common/platform.h | 121 - .../src/common/platforms/arm/arm_exc.c | 168 - .../src/common/platforms/arm/arm_exc_top.S | 69 - .../src/common/platforms/cc3200/cc3200.ld | 92 - .../src/common/platforms/cc3200/cc3200_libc.c | 103 - .../common/platforms/cc3200/cc3200v1p32.cmd | 82 - .../src/common/platforms/cc3200/gcc.mk | 52 - .../src/common/platforms/cc3200/ti.mk | 52 - .../src/common/platforms/esp/esptool.py | 2133 --- .../src/common/platforms/esp/slip.c | 35 - .../src/common/platforms/esp/slip.h | 9 - .../src/common/platforms/esp/stub_flasher.c | 559 - .../src/common/platforms/esp/stub_flasher.h | 96 - .../common/platforms/esp31/rom/.gitattributes | 1 - .../common/platforms/esp31/rom/ESP31B_ROM.txt | 11917 ---------------- .../src/common/platforms/esp31/rom/disasm.sh | 4 - .../src/common/platforms/esp31/rom/notes.c | 163 - .../src/common/platforms/esp31/rom/rom.S | 16 - .../src/common/platforms/esp31/rom/rom.bin | Bin 65536 -> 0 bytes .../platforms/esp31/rom/rom_functions.S | 294 - .../common/platforms/esp32/rom/.gitattributes | 2 - .../src/common/platforms/esp32/rom/disasm.sh | 4 - .../src/common/platforms/esp32/rom/rom.S | 28 - .../src/common/platforms/esp32/rom/rom.bin | Bin 458752 -> 0 bytes .../src/common/platforms/esp32/rom/rom.elf | Bin 521912 -> 0 bytes .../platforms/esp32/rom/rom_functions.S | 1852 --- .../src/common/platforms/esp32/stubs/Makefile | 51 - .../common/platforms/esp32/stubs/README.md | 13 - .../src/common/platforms/esp32/stubs/led.c | 38 - .../src/common/platforms/esp32/stubs/led.h | 11 - .../platforms/esp32/stubs/rom_functions.h | 10 - .../common/platforms/esp32/stubs/sdkconfig.h | 1 - .../src/common/platforms/esp32/stubs/stub.ld | 46 - .../common/platforms/esp32/stubs/stub_hello.c | 32 - .../src/common/platforms/esp32/stubs/uart.c | 15 - .../src/common/platforms/esp32/stubs/uart.h | 13 - .../src/common/platforms/esp8266/common.mk | 76 - .../src/common/platforms/esp8266/esp_crypto.c | 111 - .../platforms/esp8266/esp_hw_wdt_register.h | 53 - .../platforms/esp8266/esp_missing_includes.h | 64 - .../platforms/esp8266/esp_ssl_krypton.h | 17 - .../common/platforms/esp8266/esp_umm_malloc.c | 108 - .../common/platforms/esp8266/esp_umm_malloc.h | 23 - .../platforms/esp8266/rboot/esptool2/Makefile | 26 - .../platforms/esp8266/rboot/esptool2/elf.h | 76 - .../esp8266/rboot/esptool2/esptool2.c | 561 - .../esp8266/rboot/esptool2/esptool2.h | 44 - .../esp8266/rboot/esptool2/esptool2_elf.c | 183 - .../esp8266/rboot/esptool2/esptool2_elf.h | 48 - .../esp8266/rboot/esptool2/license.txt | 674 - .../esp8266/rboot/esptool2/readme.txt | 19 - .../esp8266/rboot/rboot/appcode/rboot-api.c | 152 - .../esp8266/rboot/rboot/appcode/rboot-api.h | 38 - .../rboot/rboot/appcode/rboot-bigflash.c | 61 - .../esp8266/rboot/rboot/firmware/rboot.bin | Bin 2464 -> 0 bytes .../platforms/esp8266/rboot/rboot/license.txt | 21 - .../esp8266/rboot/rboot/rboot-private.h | 74 - .../esp8266/rboot/rboot/rboot-stage2a.c | 80 - .../esp8266/rboot/rboot/rboot-stage2a.ld | 211 - .../platforms/esp8266/rboot/rboot/rboot.c | 414 - .../platforms/esp8266/rboot/rboot/rboot.h | 82 - .../platforms/esp8266/rboot/rboot/rboot.ld | 2 - .../platforms/esp8266/rboot/rboot/rboot.mk | 89 - .../esp8266/rboot/rboot/rboot_rom.ld | 8 - .../esp8266/rboot/rboot/readme-api.txt | 40 - .../platforms/esp8266/rboot/rboot/readme.txt | 218 - .../platforms/esp8266/rom/.gitattributes | 2 - .../common/platforms/esp8266/rom/disasm.sh | 4 - .../src/common/platforms/esp8266/rom/rom.S | 101 - .../src/common/platforms/esp8266/rom/rom.bin | Bin 65536 -> 0 bytes .../src/common/platforms/esp8266/rom/rom.elf | Bin 79984 -> 0 bytes .../platforms/esp8266/rom/rom_functions.S | 347 - .../common/platforms/esp8266/stubs/Makefile | 50 - .../common/platforms/esp8266/stubs/README.md | 13 - .../common/platforms/esp8266/stubs/chop80.py | 14 - .../platforms/esp8266/stubs/rom_functions.h | 91 - .../common/platforms/esp8266/stubs/stub.ld | 43 - .../platforms/esp8266/stubs/stub_hello.c | 23 - .../src/common/platforms/esp8266/stubs/uart.c | 14 - .../src/common/platforms/esp8266/stubs/uart.h | 35 - .../esp8266/tools/malloc_analyzer.py | 23 - .../common/platforms/esp8266/uart_register.h | 136 - .../src/common/platforms/lwip/mg_lwip.h | 69 - .../common/platforms/lwip/mg_lwip_ev_mgr.c | 243 - .../common/platforms/lwip/mg_lwip_net_if.c | 779 - .../common/platforms/lwip/mg_lwip_net_if.h | 53 - .../common/platforms/lwip/mg_lwip_ssl_if.c | 197 - .../src/common/platforms/mbed/mbed_libc.c | 42 - .../src/common/platforms/msp432/msp432_libc.c | 18 - .../src/common/platforms/nrf5/nrf5_libc.c | 14 - .../src/common/platforms/pic32/pic32_net_if.c | 292 - .../src/common/platforms/pic32/pic32_net_if.h | 25 - .../src/common/platforms/pic32/xc32.mk | 51 - .../src/common/platforms/platform_cc3100.h | 46 - .../src/common/platforms/platform_cc3200.h | 122 - .../src/common/platforms/platform_cc3220.h | 107 - .../src/common/platforms/platform_esp32.h | 42 - .../src/common/platforms/platform_esp8266.h | 62 - .../src/common/platforms/platform_mbed.h | 81 - .../src/common/platforms/platform_msp432.h | 105 - .../src/common/platforms/platform_nrf51.h | 42 - .../src/common/platforms/platform_nrf52.h | 45 - .../common/platforms/platform_nxp_kinetis.h | 31 - .../src/common/platforms/platform_nxp_lpc.h | 53 - .../src/common/platforms/platform_pic32.h | 37 - .../src/common/platforms/platform_stm32.h | 39 - .../src/common/platforms/platform_tm4c129.h | 57 - .../src/common/platforms/platform_unix.h | 145 - .../src/common/platforms/platform_wince.h | 201 - .../src/common/platforms/platform_windows.h | 183 - .../platforms/simplelink/cs_simplelink.h | 164 - .../src/common/platforms/simplelink/sl_fs.c | 411 - .../common/platforms/simplelink/sl_fs_slfs.c | 246 - .../common/platforms/simplelink/sl_fs_slfs.h | 34 - .../common/platforms/simplelink/sl_mg_task.c | 52 - .../common/platforms/simplelink/sl_net_if.c | 480 - .../common/platforms/simplelink/sl_net_if.h | 25 - .../common/platforms/simplelink/sl_socket.c | 48 - .../common/platforms/simplelink/sl_ssl_if.c | 211 - .../src/common/platforms/stm32/LICENSE | 29 - .../src/common/platforms/stm32/common.c | 869 -- .../src/common/platforms/stm32/flash.c | 93 - .../src/common/platforms/stm32/flash_loader.c | 93 - .../src/common/platforms/stm32/flash_loader.h | 30 - .../src/common/platforms/stm32/flasher.go | 38 - .../src/common/platforms/stm32/stlink.h | 198 - .../src/common/platforms/stm32/stm32_flash.h | 6 - .../src/common/platforms/stm32/usb.c | 773 - .../src/common/platforms/stm32/usb.h | 76 - .../src/common/platforms/wince/wince_libc.c | 74 - .../common/platforms/windows/windows_direct.c | 17 - src/mongoose-6.11/src/common/test_util.c | 93 - src/mongoose-6.11/src/common/test_util.h | 193 - src/mongoose-6.11/src/mg_coap.c | 597 - src/mongoose-6.11/src/mg_coap.h | 165 - src/mongoose-6.11/src/mg_common.h | 30 - src/mongoose-6.11/src/mg_dns.c | 377 - src/mongoose-6.11/src/mg_dns.h | 164 - src/mongoose-6.11/src/mg_dns_server.c | 71 - src/mongoose-6.11/src/mg_dns_server.h | 94 - src/mongoose-6.11/src/mg_features.h | 176 - src/mongoose-6.11/src/mg_http.c | 3077 ---- src/mongoose-6.11/src/mg_http.h | 364 - src/mongoose-6.11/src/mg_http_cgi.c | 514 - src/mongoose-6.11/src/mg_http_client.h | 62 - src/mongoose-6.11/src/mg_http_server.h | 561 - src/mongoose-6.11/src/mg_http_ssi.c | 193 - src/mongoose-6.11/src/mg_http_webdav.c | 269 - src/mongoose-6.11/src/mg_http_websocket.c | 517 - src/mongoose-6.11/src/mg_internal.h | 157 - src/mongoose-6.11/src/mg_modules.mk | 103 - src/mongoose-6.11/src/mg_mqtt.c | 466 - src/mongoose-6.11/src/mg_mqtt.h | 228 - src/mongoose-6.11/src/mg_mqtt_client.h | 8 - src/mongoose-6.11/src/mg_mqtt_server.c | 194 - src/mongoose-6.11/src/mg_mqtt_server.h | 104 - src/mongoose-6.11/src/mg_net.c | 1008 -- src/mongoose-6.11/src/mg_net.h | 591 - src/mongoose-6.11/src/mg_net_if.c | 41 - src/mongoose-6.11/src/mg_net_if.h | 130 - src/mongoose-6.11/src/mg_net_if_socket.c | 749 - src/mongoose-6.11/src/mg_net_if_socket.h | 25 - src/mongoose-6.11/src/mg_net_if_socks.c | 209 - src/mongoose-6.11/src/mg_net_if_socks.h | 22 - src/mongoose-6.11/src/mg_resolv.c | 292 - src/mongoose-6.11/src/mg_resolv.h | 81 - src/mongoose-6.11/src/mg_sntp.c | 288 - src/mongoose-6.11/src/mg_sntp.h | 54 - src/mongoose-6.11/src/mg_socks.c | 159 - src/mongoose-6.11/src/mg_socks.h | 66 - src/mongoose-6.11/src/mg_ssl_if.h | 55 - src/mongoose-6.11/src/mg_ssl_if_mbedtls.c | 478 - src/mongoose-6.11/src/mg_ssl_if_openssl.c | 394 - src/mongoose-6.11/src/mg_uri.c | 261 - src/mongoose-6.11/src/mg_uri.h | 67 - src/mongoose-6.11/src/mg_util.c | 336 - src/mongoose-6.11/src/mg_util.h | 208 - src/{mongoose-6.11 => mongoose}/LICENSE | 0 src/{mongoose-6.11 => mongoose}/mongoose.c | 1 + src/{mongoose-6.11 => mongoose}/mongoose.h | 0 src/mpd_client.c | 18 +- src/mympd.c | 12 +- 197 files changed, 34 insertions(+), 44735 deletions(-) delete mode 120000 src/mongoose delete mode 100644 src/mongoose-6.11/.mbedignore delete mode 100644 src/mongoose-6.11/CONTRIBUTING.md delete mode 100644 src/mongoose-6.11/README.md delete mode 100644 src/mongoose-6.11/jni/Android.mk delete mode 100644 src/mongoose-6.11/src/CPPLINT.cfg delete mode 100644 src/mongoose-6.11/src/common/cs_dbg.c delete mode 100644 src/mongoose-6.11/src/common/cs_dbg.h delete mode 100644 src/mongoose-6.11/src/common/cs_md5.c delete mode 100644 src/mongoose-6.11/src/common/cs_md5.h delete mode 100644 src/mongoose-6.11/src/common/cs_time.c delete mode 100644 src/mongoose-6.11/src/common/cs_time.h delete mode 100644 src/mongoose-6.11/src/common/mg_mem.h delete mode 100644 src/mongoose-6.11/src/common/platform.h delete mode 100644 src/mongoose-6.11/src/common/platforms/arm/arm_exc.c delete mode 100644 src/mongoose-6.11/src/common/platforms/arm/arm_exc_top.S delete mode 100644 src/mongoose-6.11/src/common/platforms/cc3200/cc3200.ld delete mode 100644 src/mongoose-6.11/src/common/platforms/cc3200/cc3200_libc.c delete mode 100644 src/mongoose-6.11/src/common/platforms/cc3200/cc3200v1p32.cmd delete mode 100644 src/mongoose-6.11/src/common/platforms/cc3200/gcc.mk delete mode 100644 src/mongoose-6.11/src/common/platforms/cc3200/ti.mk delete mode 100755 src/mongoose-6.11/src/common/platforms/esp/esptool.py delete mode 100644 src/mongoose-6.11/src/common/platforms/esp/slip.c delete mode 100644 src/mongoose-6.11/src/common/platforms/esp/slip.h delete mode 100644 src/mongoose-6.11/src/common/platforms/esp/stub_flasher.c delete mode 100644 src/mongoose-6.11/src/common/platforms/esp/stub_flasher.h delete mode 100644 src/mongoose-6.11/src/common/platforms/esp31/rom/.gitattributes delete mode 100644 src/mongoose-6.11/src/common/platforms/esp31/rom/ESP31B_ROM.txt delete mode 100755 src/mongoose-6.11/src/common/platforms/esp31/rom/disasm.sh delete mode 100644 src/mongoose-6.11/src/common/platforms/esp31/rom/notes.c delete mode 100644 src/mongoose-6.11/src/common/platforms/esp31/rom/rom.S delete mode 100644 src/mongoose-6.11/src/common/platforms/esp31/rom/rom.bin delete mode 100644 src/mongoose-6.11/src/common/platforms/esp31/rom/rom_functions.S delete mode 100644 src/mongoose-6.11/src/common/platforms/esp32/rom/.gitattributes delete mode 100755 src/mongoose-6.11/src/common/platforms/esp32/rom/disasm.sh delete mode 100644 src/mongoose-6.11/src/common/platforms/esp32/rom/rom.S delete mode 100644 src/mongoose-6.11/src/common/platforms/esp32/rom/rom.bin delete mode 100755 src/mongoose-6.11/src/common/platforms/esp32/rom/rom.elf delete mode 100644 src/mongoose-6.11/src/common/platforms/esp32/rom/rom_functions.S delete mode 100644 src/mongoose-6.11/src/common/platforms/esp32/stubs/Makefile delete mode 100644 src/mongoose-6.11/src/common/platforms/esp32/stubs/README.md delete mode 100644 src/mongoose-6.11/src/common/platforms/esp32/stubs/led.c delete mode 100644 src/mongoose-6.11/src/common/platforms/esp32/stubs/led.h delete mode 100644 src/mongoose-6.11/src/common/platforms/esp32/stubs/rom_functions.h delete mode 100644 src/mongoose-6.11/src/common/platforms/esp32/stubs/sdkconfig.h delete mode 100644 src/mongoose-6.11/src/common/platforms/esp32/stubs/stub.ld delete mode 100644 src/mongoose-6.11/src/common/platforms/esp32/stubs/stub_hello.c delete mode 100644 src/mongoose-6.11/src/common/platforms/esp32/stubs/uart.c delete mode 100644 src/mongoose-6.11/src/common/platforms/esp32/stubs/uart.h delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/common.mk delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/esp_crypto.c delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/esp_hw_wdt_register.h delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/esp_missing_includes.h delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/esp_ssl_krypton.h delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/esp_umm_malloc.c delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/esp_umm_malloc.h delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/Makefile delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/elf.h delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2.c delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2.h delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2_elf.c delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2_elf.h delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/license.txt delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/readme.txt delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-api.c delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-api.h delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-bigflash.c delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/firmware/rboot.bin delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/license.txt delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot-private.h delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot-stage2a.c delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot-stage2a.ld delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.c delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.h delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.ld delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.mk delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot_rom.ld delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/readme-api.txt delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/readme.txt delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rom/.gitattributes delete mode 100755 src/mongoose-6.11/src/common/platforms/esp8266/rom/disasm.sh delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.S delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.bin delete mode 100755 src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.elf delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/rom/rom_functions.S delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/stubs/Makefile delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/stubs/README.md delete mode 100755 src/mongoose-6.11/src/common/platforms/esp8266/stubs/chop80.py delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/stubs/rom_functions.h delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/stubs/stub.ld delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/stubs/stub_hello.c delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/stubs/uart.c delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/stubs/uart.h delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/tools/malloc_analyzer.py delete mode 100644 src/mongoose-6.11/src/common/platforms/esp8266/uart_register.h delete mode 100644 src/mongoose-6.11/src/common/platforms/lwip/mg_lwip.h delete mode 100644 src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_ev_mgr.c delete mode 100644 src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_net_if.c delete mode 100644 src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_net_if.h delete mode 100644 src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_ssl_if.c delete mode 100644 src/mongoose-6.11/src/common/platforms/mbed/mbed_libc.c delete mode 100644 src/mongoose-6.11/src/common/platforms/msp432/msp432_libc.c delete mode 100644 src/mongoose-6.11/src/common/platforms/nrf5/nrf5_libc.c delete mode 100644 src/mongoose-6.11/src/common/platforms/pic32/pic32_net_if.c delete mode 100644 src/mongoose-6.11/src/common/platforms/pic32/pic32_net_if.h delete mode 100644 src/mongoose-6.11/src/common/platforms/pic32/xc32.mk delete mode 100644 src/mongoose-6.11/src/common/platforms/platform_cc3100.h delete mode 100644 src/mongoose-6.11/src/common/platforms/platform_cc3200.h delete mode 100644 src/mongoose-6.11/src/common/platforms/platform_cc3220.h delete mode 100644 src/mongoose-6.11/src/common/platforms/platform_esp32.h delete mode 100644 src/mongoose-6.11/src/common/platforms/platform_esp8266.h delete mode 100644 src/mongoose-6.11/src/common/platforms/platform_mbed.h delete mode 100644 src/mongoose-6.11/src/common/platforms/platform_msp432.h delete mode 100644 src/mongoose-6.11/src/common/platforms/platform_nrf51.h delete mode 100644 src/mongoose-6.11/src/common/platforms/platform_nrf52.h delete mode 100644 src/mongoose-6.11/src/common/platforms/platform_nxp_kinetis.h delete mode 100644 src/mongoose-6.11/src/common/platforms/platform_nxp_lpc.h delete mode 100644 src/mongoose-6.11/src/common/platforms/platform_pic32.h delete mode 100644 src/mongoose-6.11/src/common/platforms/platform_stm32.h delete mode 100644 src/mongoose-6.11/src/common/platforms/platform_tm4c129.h delete mode 100644 src/mongoose-6.11/src/common/platforms/platform_unix.h delete mode 100644 src/mongoose-6.11/src/common/platforms/platform_wince.h delete mode 100644 src/mongoose-6.11/src/common/platforms/platform_windows.h delete mode 100644 src/mongoose-6.11/src/common/platforms/simplelink/cs_simplelink.h delete mode 100644 src/mongoose-6.11/src/common/platforms/simplelink/sl_fs.c delete mode 100644 src/mongoose-6.11/src/common/platforms/simplelink/sl_fs_slfs.c delete mode 100644 src/mongoose-6.11/src/common/platforms/simplelink/sl_fs_slfs.h delete mode 100644 src/mongoose-6.11/src/common/platforms/simplelink/sl_mg_task.c delete mode 100644 src/mongoose-6.11/src/common/platforms/simplelink/sl_net_if.c delete mode 100644 src/mongoose-6.11/src/common/platforms/simplelink/sl_net_if.h delete mode 100644 src/mongoose-6.11/src/common/platforms/simplelink/sl_socket.c delete mode 100644 src/mongoose-6.11/src/common/platforms/simplelink/sl_ssl_if.c delete mode 100644 src/mongoose-6.11/src/common/platforms/stm32/LICENSE delete mode 100644 src/mongoose-6.11/src/common/platforms/stm32/common.c delete mode 100644 src/mongoose-6.11/src/common/platforms/stm32/flash.c delete mode 100644 src/mongoose-6.11/src/common/platforms/stm32/flash_loader.c delete mode 100644 src/mongoose-6.11/src/common/platforms/stm32/flash_loader.h delete mode 100644 src/mongoose-6.11/src/common/platforms/stm32/flasher.go delete mode 100644 src/mongoose-6.11/src/common/platforms/stm32/stlink.h delete mode 100644 src/mongoose-6.11/src/common/platforms/stm32/stm32_flash.h delete mode 100644 src/mongoose-6.11/src/common/platforms/stm32/usb.c delete mode 100644 src/mongoose-6.11/src/common/platforms/stm32/usb.h delete mode 100644 src/mongoose-6.11/src/common/platforms/wince/wince_libc.c delete mode 100644 src/mongoose-6.11/src/common/platforms/windows/windows_direct.c delete mode 100644 src/mongoose-6.11/src/common/test_util.c delete mode 100644 src/mongoose-6.11/src/common/test_util.h delete mode 100644 src/mongoose-6.11/src/mg_coap.c delete mode 100644 src/mongoose-6.11/src/mg_coap.h delete mode 100644 src/mongoose-6.11/src/mg_common.h delete mode 100644 src/mongoose-6.11/src/mg_dns.c delete mode 100644 src/mongoose-6.11/src/mg_dns.h delete mode 100644 src/mongoose-6.11/src/mg_dns_server.c delete mode 100644 src/mongoose-6.11/src/mg_dns_server.h delete mode 100644 src/mongoose-6.11/src/mg_features.h delete mode 100644 src/mongoose-6.11/src/mg_http.c delete mode 100644 src/mongoose-6.11/src/mg_http.h delete mode 100644 src/mongoose-6.11/src/mg_http_cgi.c delete mode 100644 src/mongoose-6.11/src/mg_http_client.h delete mode 100644 src/mongoose-6.11/src/mg_http_server.h delete mode 100644 src/mongoose-6.11/src/mg_http_ssi.c delete mode 100644 src/mongoose-6.11/src/mg_http_webdav.c delete mode 100644 src/mongoose-6.11/src/mg_http_websocket.c delete mode 100644 src/mongoose-6.11/src/mg_internal.h delete mode 100644 src/mongoose-6.11/src/mg_modules.mk delete mode 100644 src/mongoose-6.11/src/mg_mqtt.c delete mode 100644 src/mongoose-6.11/src/mg_mqtt.h delete mode 100644 src/mongoose-6.11/src/mg_mqtt_client.h delete mode 100644 src/mongoose-6.11/src/mg_mqtt_server.c delete mode 100644 src/mongoose-6.11/src/mg_mqtt_server.h delete mode 100644 src/mongoose-6.11/src/mg_net.c delete mode 100644 src/mongoose-6.11/src/mg_net.h delete mode 100644 src/mongoose-6.11/src/mg_net_if.c delete mode 100644 src/mongoose-6.11/src/mg_net_if.h delete mode 100644 src/mongoose-6.11/src/mg_net_if_socket.c delete mode 100644 src/mongoose-6.11/src/mg_net_if_socket.h delete mode 100644 src/mongoose-6.11/src/mg_net_if_socks.c delete mode 100644 src/mongoose-6.11/src/mg_net_if_socks.h delete mode 100644 src/mongoose-6.11/src/mg_resolv.c delete mode 100644 src/mongoose-6.11/src/mg_resolv.h delete mode 100644 src/mongoose-6.11/src/mg_sntp.c delete mode 100644 src/mongoose-6.11/src/mg_sntp.h delete mode 100644 src/mongoose-6.11/src/mg_socks.c delete mode 100644 src/mongoose-6.11/src/mg_socks.h delete mode 100644 src/mongoose-6.11/src/mg_ssl_if.h delete mode 100644 src/mongoose-6.11/src/mg_ssl_if_mbedtls.c delete mode 100644 src/mongoose-6.11/src/mg_ssl_if_openssl.c delete mode 100644 src/mongoose-6.11/src/mg_uri.c delete mode 100644 src/mongoose-6.11/src/mg_uri.h delete mode 100644 src/mongoose-6.11/src/mg_util.c delete mode 100644 src/mongoose-6.11/src/mg_util.h rename src/{mongoose-6.11 => mongoose}/LICENSE (100%) rename src/{mongoose-6.11 => mongoose}/mongoose.c (99%) rename src/{mongoose-6.11 => mongoose}/mongoose.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 94be1bd..e09ef62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,12 +3,13 @@ cmake_minimum_required(VERSION 2.6) project (mympd C) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") set(CPACK_PACKAGE_VERSION_MAJOR "2") -set(CPACK_PACKAGE_VERSION_MINOR "2") +set(CPACK_PACKAGE_VERSION_MINOR "3") set(CPACK_PACKAGE_VERSION_PATCH "0") if(CMAKE_BUILD_TYPE MATCHES RELEASE) set(ASSETS_PATH "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}/htdocs") else() set(ASSETS_PATH "${PROJECT_SOURCE_DIR}/htdocs") + set(DEBUG ON) endif() option(WITH_IPV6 "enable IPv6 support" ON) @@ -22,8 +23,9 @@ include_directories(${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR} ${LIBMPDCLIENT_I include(CheckCSourceCompiles) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -ggdb -pedantic -D_FORTIFY_SOURCE=2 -fstack-protector -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=shift -fsanitize=integer-divide-by-zero -fsanitize=unreachable -fsanitize=vla-bound -fsanitize=null -fsanitize=return -fsanitize=signed-integer-overflow -fsanitize=bounds -fsanitize=bounds-strict -fsanitize=alignment -fsanitize=object-size -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fsanitize=nonnull-attribute -fsanitize=returns-nonnull-attribute -fsanitize=bool -fsanitize=enum -fsanitize=vptr -static-libasan") -set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -pedantic") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -pedantic") +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -pedantic -D_FORTIFY_SOURCE=2 -fstack-protector -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=shift -fsanitize=integer-divide-by-zero -fsanitize=unreachable -fsanitize=vla-bound -fsanitize=null -fsanitize=return -fsanitize=signed-integer-overflow -fsanitize=bounds -fsanitize=bounds-strict -fsanitize=alignment -fsanitize=object-size -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fsanitize=nonnull-attribute -fsanitize=returns-nonnull-attribute -fsanitize=bool -fsanitize=enum -fsanitize=vptr -static-libasan") +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb") if(WITH_IPV6) set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS NS_ENABLE_IPV6) endif() diff --git a/src/config.h.in b/src/config.h.in index 5cb0adc..d5c41cd 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -30,4 +30,5 @@ #define MYMPD_VERSION_PATCH ${CPACK_PACKAGE_VERSION_PATCH} #define MYMPD_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}" #define SRC_PATH "${ASSETS_PATH}" +#cmakedefine DEBUG #endif diff --git a/src/mongoose b/src/mongoose deleted file mode 120000 index 978d60b..0000000 --- a/src/mongoose +++ /dev/null @@ -1 +0,0 @@ -mongoose-6.11/ \ No newline at end of file diff --git a/src/mongoose-6.11/.mbedignore b/src/mongoose-6.11/.mbedignore deleted file mode 100644 index e966095..0000000 --- a/src/mongoose-6.11/.mbedignore +++ /dev/null @@ -1,9 +0,0 @@ -binary/ -deps/ -docker/ -docs/ -examples/ -platforms/ -src/ -test/ -multilingual/ diff --git a/src/mongoose-6.11/CONTRIBUTING.md b/src/mongoose-6.11/CONTRIBUTING.md deleted file mode 100644 index 4581b01..0000000 --- a/src/mongoose-6.11/CONTRIBUTING.md +++ /dev/null @@ -1,8 +0,0 @@ -People who have agreed to the -[Cesanta CLA](https://docs.cesanta.com/contributors_la.shtml) -can make contributions. Note that the CLA isn't a copyright -_assigment_ but rather a copyright _license_. -You retain the copyright on your contributions. - -We follow the Google C/C++ style guide: https://google.github.io/styleguide/cppguide.html -We'd appreciate if your contribution follows the same style guide. diff --git a/src/mongoose-6.11/README.md b/src/mongoose-6.11/README.md deleted file mode 100644 index 63d8f8e..0000000 --- a/src/mongoose-6.11/README.md +++ /dev/null @@ -1,68 +0,0 @@ -# Mongoose - Embedded Web Server / Embedded Networking Library - -![](https://img.shields.io/badge/license-GPL_2-green.svg "License") - -Mongoose is ideal for embedded environments. It has been designed -for connecting devices and bringing them online. On the market since 2004, -used by vast number of open source and -commercial products - it even runs on the International Space station! -Mongoose makes embedded network programming fast, robust, and easy. - -- [Download Mongoose Source Code here](https://www.cesanta.com/download.html) - -Looking for a complete IoT firmware solution? - -Check out [Mongoose OS](https://mongoose-os.com) - open source embedded operating system for low-power connected microcontrollers. Secure, designed for Internet of Things, complete environment for prototyping, development and managing. - -# Support -- [Study mongoose example code](https://github.com/cesanta/mongoose/tree/master/examples) -- [Read User Guide and API reference](https://cesanta.com/docs/overview/intro.html) -- [Support Forum - ask your technical questions here](https://forum.mongoose-os.com/categories/mongoose) -- [Commercial licensing and support available](https://www.cesanta.com/licensing.html) -- [Check our latest releases](https://github.com/cesanta/mongoose/releases) - -# Features - -* Cross-platform: works on Linux/UNIX, MacOS, QNX, eCos, Windows, Android, - iPhone, FreeRTOS (TI CC3200, ESP8266), etc -* Supported hardware platforms: TI CC3200, TI MSP432, NRF52, STM32, PIC32, ESP8266, ESP32 and more -* Builtin protocols: - - plain TCP, plain UDP, SSL/TLS (over TCP, one-way or two-way) - - HTTP client, HTTP server - - WebSocket client, WebSocket server - - MQTT client, MQTT broker - - CoAP client, CoAP server - - DNS client, DNS server, async DNS resolver -* Single-threaded, asynchronous, non-blocking core with simple event-based API -* Native support for [PicoTCP embedded TCP/IP stack](http://www.picotcp.com), - [LWIP embedded TCP/IP stack](https://en.wikipedia.org/wiki/LwIP) -* Tiny static and run-time footprint -* Source code is both ISO C and ISO C++ compliant -* Very easy to integrate: just copy - [mongoose.c](https://raw.githubusercontent.com/cesanta/mongoose/master/mongoose.c) and - [mongoose.h](https://raw.githubusercontent.com/cesanta/mongoose/master/mongoose.h) - files to your build tree - -# Licensing - -Mongoose is released under Commercial and [GNU GPL v.2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) open source licenses. - -Commercial Projects: [Contact us for commercial license.](https://www.cesanta.com/contact.html) - -# Dashboard Example - -Mongoose is often used to implement device dashboards and real-time -data exchange over Websocket. Here is a dashboard example that illustrates -the functionality: - -![](http://www.cesanta.com/images/dashboard.png) - -[Developing a new product? Contact us today to discuss how Mongoose can help.](https://www.cesanta.com/contact.html) - -# Contributions - -To submit contributions, sign [Cesanta CLA](https://cesanta.com/cla.html) -and send GitHub pull request. You retain the copyright on your contributions. - -# Looking for a pre-compiled Mongoose web server Windows or Mac binary? -- [Download pre-compiled Mongoose web server binary.](https://www.cesanta.com/binary.html) diff --git a/src/mongoose-6.11/jni/Android.mk b/src/mongoose-6.11/jni/Android.mk deleted file mode 100644 index a2e67b5..0000000 --- a/src/mongoose-6.11/jni/Android.mk +++ /dev/null @@ -1,8 +0,0 @@ -LOCAL_PATH := $(call my-dir)/.. -include $(CLEAR_VARS) - -LOCAL_CFLAGS := -std=c99 -O2 -W -Wall -pthread -pipe $(COPT) -LOCAL_MODULE := mongoose -LOCAL_SRC_FILES := examples/simplest_web_server/simplest_web_server.c mongoose.c - -include $(BUILD_EXECUTABLE) diff --git a/src/mongoose-6.11/src/CPPLINT.cfg b/src/mongoose-6.11/src/CPPLINT.cfg deleted file mode 100644 index 6809e55..0000000 --- a/src/mongoose-6.11/src/CPPLINT.cfg +++ /dev/null @@ -1 +0,0 @@ -exclude_files=sha1\.c diff --git a/src/mongoose-6.11/src/common/cs_dbg.c b/src/mongoose-6.11/src/common/cs_dbg.c deleted file mode 100644 index da1bc2e..0000000 --- a/src/mongoose-6.11/src/common/cs_dbg.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#include "common/cs_dbg.h" - -#include -#include -#include - -#include "common/cs_time.h" -#include "common/str_util.h" - -enum cs_log_level cs_log_threshold WEAK = -#if CS_ENABLE_DEBUG - LL_VERBOSE_DEBUG; -#else - LL_ERROR; -#endif - -static char *s_filter_pattern = NULL; -static size_t s_filter_pattern_len; - -void cs_log_set_filter(const char *pattern) WEAK; - -#if CS_ENABLE_STDIO - -FILE *cs_log_file WEAK = NULL; - -#if CS_LOG_ENABLE_TS_DIFF -double cs_log_ts WEAK; -#endif - -enum cs_log_level cs_log_cur_msg_level WEAK = LL_NONE; - -void cs_log_set_filter(const char *pattern) { - free(s_filter_pattern); - if (pattern != NULL) { - s_filter_pattern = strdup(pattern); - s_filter_pattern_len = strlen(pattern); - } else { - s_filter_pattern = NULL; - s_filter_pattern_len = 0; - } -} - -int cs_log_print_prefix(enum cs_log_level, const char *, const char *) WEAK; -int cs_log_print_prefix(enum cs_log_level level, const char *func, - const char *filename) { - char prefix[21]; - - if (level > cs_log_threshold) return 0; - if (s_filter_pattern != NULL && - mg_match_prefix(s_filter_pattern, s_filter_pattern_len, func) == 0 && - mg_match_prefix(s_filter_pattern, s_filter_pattern_len, filename) == 0) { - return 0; - } - - strncpy(prefix, func, 20); - prefix[20] = '\0'; - if (cs_log_file == NULL) cs_log_file = stderr; - cs_log_cur_msg_level = level; - fprintf(cs_log_file, "%-20s ", prefix); -#if CS_LOG_ENABLE_TS_DIFF - { - double now = cs_time(); - fprintf(cs_log_file, "%7u ", (unsigned int) ((now - cs_log_ts) * 1000000)); - cs_log_ts = now; - } -#endif - return 1; -} - -void cs_log_printf(const char *fmt, ...) WEAK; -void cs_log_printf(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - vfprintf(cs_log_file, fmt, ap); - va_end(ap); - fputc('\n', cs_log_file); - fflush(cs_log_file); - cs_log_cur_msg_level = LL_NONE; -} - -void cs_log_set_file(FILE *file) WEAK; -void cs_log_set_file(FILE *file) { - cs_log_file = file; -} - -#else - -void cs_log_set_filter(const char *pattern) { - (void) pattern; -} - -#endif /* CS_ENABLE_STDIO */ - -void cs_log_set_level(enum cs_log_level level) WEAK; -void cs_log_set_level(enum cs_log_level level) { - cs_log_threshold = level; -#if CS_LOG_ENABLE_TS_DIFF && CS_ENABLE_STDIO - cs_log_ts = cs_time(); -#endif -} diff --git a/src/mongoose-6.11/src/common/cs_dbg.h b/src/mongoose-6.11/src/common/cs_dbg.h deleted file mode 100644 index 24a7022..0000000 --- a/src/mongoose-6.11/src/common/cs_dbg.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_CS_DBG_H_ -#define CS_COMMON_CS_DBG_H_ - -#include "common/platform.h" - -#if CS_ENABLE_STDIO -#include -#endif - -#ifndef CS_ENABLE_DEBUG -#define CS_ENABLE_DEBUG 0 -#endif - -#ifndef CS_LOG_ENABLE_TS_DIFF -#define CS_LOG_ENABLE_TS_DIFF 0 -#endif - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* - * Log level; `LL_INFO` is the default. Use `cs_log_set_level()` to change it. - */ -enum cs_log_level { - LL_NONE = -1, - LL_ERROR = 0, - LL_WARN = 1, - LL_INFO = 2, - LL_DEBUG = 3, - LL_VERBOSE_DEBUG = 4, - - _LL_MIN = -2, - _LL_MAX = 5, -}; - -/* - * Set max log level to print; messages with the level above the given one will - * not be printed. - */ -void cs_log_set_level(enum cs_log_level level); - -/* - * Set log filter. NULL (a default) logs everything. - * Otherwise, function name and file name will be tested against the given - * pattern, and only matching messages will be printed. - * - * For the pattern syntax, refer to `mg_match_prefix()` in `str_util.h`. - * - * Example: - * ```c - * void foo(void) { - * LOG(LL_INFO, ("hello from foo")); - * } - * - * void bar(void) { - * LOG(LL_INFO, ("hello from bar")); - * } - * - * void test(void) { - * cs_log_set_filter(NULL); - * foo(); - * bar(); - * - * cs_log_set_filter("f*"); - * foo(); - * bar(); // Will NOT print anything - * - * cs_log_set_filter("bar"); - * foo(); // Will NOT print anything - * bar(); - * } - * ``` - */ -void cs_log_set_filter(const char *pattern); - -/* - * Helper function which prints message prefix with the given `level`, function - * name `func` and `filename`. If message should be printed (accordingly to the - * current log level and filter), prints the prefix and returns 1, otherwise - * returns 0. - * - * Clients should typically just use `LOG()` macro. - */ -int cs_log_print_prefix(enum cs_log_level level, const char *func, - const char *filename); - -extern enum cs_log_level cs_log_threshold; - -#if CS_ENABLE_STDIO - -/* - * Set file to write logs into. If `NULL`, logs go to `stderr`. - */ -void cs_log_set_file(FILE *file); - -/* - * Prints log to the current log file, appends "\n" in the end and flushes the - * stream. - */ -void cs_log_printf(const char *fmt, ...) -#ifdef __GNUC__ - __attribute__((format(printf, 1, 2))) -#endif - ; - -/* - * Format and print message `x` with the given level `l`. Example: - * - * ```c - * LOG(LL_INFO, ("my info message: %d", 123)); - * LOG(LL_DEBUG, ("my debug message: %d", 123)); - * ``` - */ -#define LOG(l, x) \ - do { \ - if (cs_log_print_prefix(l, __func__, __FILE__)) cs_log_printf x; \ - } while (0) - -#ifndef CS_NDEBUG - -/* - * Shortcut for `LOG(LL_VERBOSE_DEBUG, (...))` - */ -#define DBG(x) LOG(LL_VERBOSE_DEBUG, x) - -#else /* NDEBUG */ - -#define DBG(x) - -#endif - -#else /* CS_ENABLE_STDIO */ - -#define LOG(l, x) -#define DBG(x) - -#endif - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* CS_COMMON_CS_DBG_H_ */ diff --git a/src/mongoose-6.11/src/common/cs_md5.c b/src/mongoose-6.11/src/common/cs_md5.c deleted file mode 100644 index f8c8b68..0000000 --- a/src/mongoose-6.11/src/common/cs_md5.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * This code implements the MD5 message-digest algorithm. - * The algorithm is due to Ron Rivest. This code was - * written by Colin Plumb in 1993, no copyright is claimed. - * This code is in the public domain; do with it what you wish. - * - * Equivalent code is available from RSA Data Security, Inc. - * This code has been tested against that, and is equivalent, - * except that you don't need to include two pages of legalese - * with every copy. - * - * To compute the message digest of a chunk of bytes, declare an - * MD5Context structure, pass it to MD5Init, call MD5Update as - * needed on buffers full of bytes, and then call MD5Final, which - * will fill a supplied 16-byte array with the digest. - */ - -#include "common/cs_md5.h" -#include "common/str_util.h" - -#if !defined(EXCLUDE_COMMON) -#if !CS_DISABLE_MD5 - -#include "common/cs_endian.h" - -static void byteReverse(unsigned char *buf, unsigned longs) { -/* Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN */ -#if BYTE_ORDER == BIG_ENDIAN - do { - uint32_t t = (uint32_t)((unsigned) buf[3] << 8 | buf[2]) << 16 | - ((unsigned) buf[1] << 8 | buf[0]); - *(uint32_t *) buf = t; - buf += 4; - } while (--longs); -#else - (void) buf; - (void) longs; -#endif -} - -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -#define MD5STEP(f, w, x, y, z, data, s) \ - (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x) - -/* - * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious - * initialization constants. - */ -void cs_md5_init(cs_md5_ctx *ctx) { - ctx->buf[0] = 0x67452301; - ctx->buf[1] = 0xefcdab89; - ctx->buf[2] = 0x98badcfe; - ctx->buf[3] = 0x10325476; - - ctx->bits[0] = 0; - ctx->bits[1] = 0; -} - -static void cs_md5_transform(uint32_t buf[4], uint32_t const in[16]) { - register uint32_t a, b, c, d; - - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; - - MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; -} - -void cs_md5_update(cs_md5_ctx *ctx, const unsigned char *buf, size_t len) { - uint32_t t; - - t = ctx->bits[0]; - if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) ctx->bits[1]++; - ctx->bits[1] += (uint32_t) len >> 29; - - t = (t >> 3) & 0x3f; - - if (t) { - unsigned char *p = (unsigned char *) ctx->in + t; - - t = 64 - t; - if (len < t) { - memcpy(p, buf, len); - return; - } - memcpy(p, buf, t); - byteReverse(ctx->in, 16); - cs_md5_transform(ctx->buf, (uint32_t *) ctx->in); - buf += t; - len -= t; - } - - while (len >= 64) { - memcpy(ctx->in, buf, 64); - byteReverse(ctx->in, 16); - cs_md5_transform(ctx->buf, (uint32_t *) ctx->in); - buf += 64; - len -= 64; - } - - memcpy(ctx->in, buf, len); -} - -void cs_md5_final(unsigned char digest[16], cs_md5_ctx *ctx) { - unsigned count; - unsigned char *p; - uint32_t *a; - - count = (ctx->bits[0] >> 3) & 0x3F; - - p = ctx->in + count; - *p++ = 0x80; - count = 64 - 1 - count; - if (count < 8) { - memset(p, 0, count); - byteReverse(ctx->in, 16); - cs_md5_transform(ctx->buf, (uint32_t *) ctx->in); - memset(ctx->in, 0, 56); - } else { - memset(p, 0, count - 8); - } - byteReverse(ctx->in, 14); - - a = (uint32_t *) ctx->in; - a[14] = ctx->bits[0]; - a[15] = ctx->bits[1]; - - cs_md5_transform(ctx->buf, (uint32_t *) ctx->in); - byteReverse((unsigned char *) ctx->buf, 4); - memcpy(digest, ctx->buf, 16); - memset((char *) ctx, 0, sizeof(*ctx)); -} - -#endif /* CS_DISABLE_MD5 */ -#endif /* EXCLUDE_COMMON */ diff --git a/src/mongoose-6.11/src/common/cs_md5.h b/src/mongoose-6.11/src/common/cs_md5.h deleted file mode 100644 index 1450fd2..0000000 --- a/src/mongoose-6.11/src/common/cs_md5.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_MD5_H_ -#define CS_COMMON_MD5_H_ - -#include "common/platform.h" - -#ifndef CS_DISABLE_MD5 -#define CS_DISABLE_MD5 0 -#endif - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -typedef struct { - uint32_t buf[4]; - uint32_t bits[2]; - unsigned char in[64]; -} cs_md5_ctx; - -void cs_md5_init(cs_md5_ctx *c); -void cs_md5_update(cs_md5_ctx *c, const unsigned char *data, size_t len); -void cs_md5_final(unsigned char *md, cs_md5_ctx *c); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* CS_COMMON_MD5_H_ */ diff --git a/src/mongoose-6.11/src/common/cs_time.c b/src/mongoose-6.11/src/common/cs_time.c deleted file mode 100644 index f21845c..0000000 --- a/src/mongoose-6.11/src/common/cs_time.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#include "common/cs_time.h" - -#ifndef _WIN32 -#include -/* - * There is no sys/time.h on ARMCC. - */ -#if !(defined(__ARMCC_VERSION) || defined(__ICCARM__)) && \ - !defined(__TI_COMPILER_VERSION__) && \ - (!defined(CS_PLATFORM) || CS_PLATFORM != CS_P_NXP_LPC) -#include -#endif -#else -#include -#endif - -double cs_time(void) WEAK; -double cs_time(void) { - double now; -#ifndef _WIN32 - struct timeval tv; - if (gettimeofday(&tv, NULL /* tz */) != 0) return 0; - now = (double) tv.tv_sec + (((double) tv.tv_usec) / 1000000.0); -#else - SYSTEMTIME sysnow; - FILETIME ftime; - GetLocalTime(&sysnow); - SystemTimeToFileTime(&sysnow, &ftime); - /* - * 1. VC 6.0 doesn't support conversion uint64 -> double, so, using int64 - * This should not cause a problems in this (21th) century - * 2. Windows FILETIME is a number of 100-nanosecond intervals since January - * 1, 1601 while time_t is a number of _seconds_ since January 1, 1970 UTC, - * thus, we need to convert to seconds and adjust amount (subtract 11644473600 - * seconds) - */ - now = (double) (((int64_t) ftime.dwLowDateTime + - ((int64_t) ftime.dwHighDateTime << 32)) / - 10000000.0) - - 11644473600; -#endif /* _WIN32 */ - return now; -} - -double cs_timegm(const struct tm *tm) { - /* Month-to-day offset for non-leap-years. */ - static const int month_day[12] = {0, 31, 59, 90, 120, 151, - 181, 212, 243, 273, 304, 334}; - - /* Most of the calculation is easy; leap years are the main difficulty. */ - int month = tm->tm_mon % 12; - int year = tm->tm_year + tm->tm_mon / 12; - int year_for_leap; - int64_t rt; - - if (month < 0) { /* Negative values % 12 are still negative. */ - month += 12; - --year; - } - - /* This is the number of Februaries since 1900. */ - year_for_leap = (month > 1) ? year + 1 : year; - - rt = - tm->tm_sec /* Seconds */ - + - 60 * - (tm->tm_min /* Minute = 60 seconds */ - + - 60 * (tm->tm_hour /* Hour = 60 minutes */ - + - 24 * (month_day[month] + tm->tm_mday - 1 /* Day = 24 hours */ - + 365 * (year - 70) /* Year = 365 days */ - + (year_for_leap - 69) / 4 /* Every 4 years is leap... */ - - (year_for_leap - 1) / 100 /* Except centuries... */ - + (year_for_leap + 299) / 400))); /* Except 400s. */ - return rt < 0 ? -1 : (double) rt; -} diff --git a/src/mongoose-6.11/src/common/cs_time.h b/src/mongoose-6.11/src/common/cs_time.h deleted file mode 100644 index 3b59073..0000000 --- a/src/mongoose-6.11/src/common/cs_time.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_CS_TIME_H_ -#define CS_COMMON_CS_TIME_H_ - -#include - -#include "common/platform.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* Sub-second granularity time(). */ -double cs_time(void); - -/* - * Similar to (non-standard) timegm, converts broken-down time into the number - * of seconds since Unix Epoch. - */ -double cs_timegm(const struct tm *tm); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* CS_COMMON_CS_TIME_H_ */ diff --git a/src/mongoose-6.11/src/common/mg_mem.h b/src/mongoose-6.11/src/common/mg_mem.h deleted file mode 100644 index 65d71c7..0000000 --- a/src/mongoose-6.11/src/common/mg_mem.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_MG_MEM_H_ -#define CS_COMMON_MG_MEM_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef MG_MALLOC -#define MG_MALLOC malloc -#endif - -#ifndef MG_CALLOC -#define MG_CALLOC calloc -#endif - -#ifndef MG_REALLOC -#define MG_REALLOC realloc -#endif - -#ifndef MG_FREE -#define MG_FREE free -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* CS_COMMON_MG_MEM_H_ */ diff --git a/src/mongoose-6.11/src/common/platform.h b/src/mongoose-6.11/src/common/platform.h deleted file mode 100644 index b517cab..0000000 --- a/src/mongoose-6.11/src/common/platform.h +++ /dev/null @@ -1,121 +0,0 @@ -#ifndef CS_COMMON_PLATFORM_H_ -#define CS_COMMON_PLATFORM_H_ - -/* - * For the "custom" platform, includes and dependencies can be - * provided through mg_locals.h. - */ -#define CS_P_CUSTOM 0 -#define CS_P_UNIX 1 -#define CS_P_WINDOWS 2 -#define CS_P_ESP32 15 -#define CS_P_ESP8266 3 -#define CS_P_CC3100 6 -#define CS_P_CC3200 4 -#define CS_P_CC3220 17 -#define CS_P_MSP432 5 -#define CS_P_TM4C129 14 -#define CS_P_MBED 7 -#define CS_P_WINCE 8 -#define CS_P_NXP_LPC 13 -#define CS_P_NXP_KINETIS 9 -#define CS_P_NRF51 12 -#define CS_P_NRF52 10 -#define CS_P_PIC32 11 -#define CS_P_STM32 16 -/* Next id: 18 */ - -/* If not specified explicitly, we guess platform by defines. */ -#ifndef CS_PLATFORM - -#if defined(TARGET_IS_MSP432P4XX) || defined(__MSP432P401R__) -#define CS_PLATFORM CS_P_MSP432 -#elif defined(cc3200) || defined(TARGET_IS_CC3200) -#define CS_PLATFORM CS_P_CC3200 -#elif defined(cc3220) || defined(TARGET_IS_CC3220) -#define CS_PLATFORM CS_P_CC3220 -#elif defined(__unix__) || defined(__APPLE__) -#define CS_PLATFORM CS_P_UNIX -#elif defined(WINCE) -#define CS_PLATFORM CS_P_WINCE -#elif defined(_WIN32) -#define CS_PLATFORM CS_P_WINDOWS -#elif defined(__MBED__) -#define CS_PLATFORM CS_P_MBED -#elif defined(__USE_LPCOPEN) -#define CS_PLATFORM CS_P_NXP_LPC -#elif defined(FRDM_K64F) || defined(FREEDOM) -#define CS_PLATFORM CS_P_NXP_KINETIS -#elif defined(PIC32) -#define CS_PLATFORM CS_P_PIC32 -#elif defined(ESP_PLATFORM) -#define CS_PLATFORM CS_P_ESP32 -#elif defined(ICACHE_FLASH) -#define CS_PLATFORM CS_P_ESP8266 -#elif defined(TARGET_IS_TM4C129_RA0) || defined(TARGET_IS_TM4C129_RA1) || \ - defined(TARGET_IS_TM4C129_RA2) -#define CS_PLATFORM CS_P_TM4C129 -#elif defined(STM32) -#define CS_PLATFORM CS_P_STM32 -#endif - -#ifndef CS_PLATFORM -#error "CS_PLATFORM is not specified and we couldn't guess it." -#endif - -#endif /* !defined(CS_PLATFORM) */ - -#define MG_NET_IF_SOCKET 1 -#define MG_NET_IF_SIMPLELINK 2 -#define MG_NET_IF_LWIP_LOW_LEVEL 3 -#define MG_NET_IF_PIC32 4 - -#define MG_SSL_IF_OPENSSL 1 -#define MG_SSL_IF_MBEDTLS 2 -#define MG_SSL_IF_SIMPLELINK 3 - -#include "common/platforms/platform_unix.h" -#include "common/platforms/platform_windows.h" -#include "common/platforms/platform_esp32.h" -#include "common/platforms/platform_esp8266.h" -#include "common/platforms/platform_cc3100.h" -#include "common/platforms/platform_cc3200.h" -#include "common/platforms/platform_cc3220.h" -#include "common/platforms/platform_mbed.h" -#include "common/platforms/platform_nrf51.h" -#include "common/platforms/platform_nrf52.h" -#include "common/platforms/platform_wince.h" -#include "common/platforms/platform_nxp_lpc.h" -#include "common/platforms/platform_nxp_kinetis.h" -#include "common/platforms/platform_pic32.h" -#include "common/platforms/platform_stm32.h" - -/* Common stuff */ - -#if !defined(WEAK) -#if (defined(__GNUC__) || defined(__TI_COMPILER_VERSION__)) && !defined(_WIN32) -#define WEAK __attribute__((weak)) -#else -#define WEAK -#endif -#endif - -#ifdef __GNUC__ -#define NORETURN __attribute__((noreturn)) -#define NOINLINE __attribute__((noinline)) -#define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) -#define NOINSTR __attribute__((no_instrument_function)) -#define DO_NOT_WARN_UNUSED __attribute__((unused)) -#else -#define NORETURN -#define NOINLINE -#define WARN_UNUSED_RESULT -#define NOINSTR -#define DO_NOT_WARN_UNUSED -#endif /* __GNUC__ */ - -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) -#endif - -#endif /* CS_COMMON_PLATFORM_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/arm/arm_exc.c b/src/mongoose-6.11/src/common/platforms/arm/arm_exc.c deleted file mode 100644 index 60b50c8..0000000 --- a/src/mongoose-6.11/src/common/platforms/arm/arm_exc.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2014-2017 Cesanta Software Limited - * All rights reserved - */ - -#include -#include -#include -#include - -#include "FreeRTOS.h" - -#include "common/platform.h" - -#include "mgos_core_dump.h" -#include "mgos_hal.h" - -struct arm_exc_frame { - uint32_t r0; - uint32_t r1; - uint32_t r2; - uint32_t r3; - uint32_t r12; - uint32_t lr; - uint32_t pc; - uint32_t xpsr; -#ifdef ARM_HAVE_FPU - uint32_t s[16]; - uint32_t fpscr; - uint32_t reserved; -#endif -} __attribute__((packed)); - -struct arm_gdb_reg_file { - uint32_t r[13]; - uint32_t sp; - uint32_t lr; - uint32_t pc; - uint32_t cpsr; - uint64_t d[16]; - uint32_t fpscr; -} __attribute__((packed)); - -#if ARM_HAVE_FPU -static void save_s16_s31(uint32_t *dst) { - __asm volatile( - "\ - vmov r1, s16\n str r1, [%0, 0]\n\ - vmov r1, s17\n str r1, [%0, 4]\n\ - vmov r1, s18\n str r1, [%0, 8]\n\ - vmov r1, s19\n str r1, [%0, 12]\n\ - vmov r1, s20\n str r1, [%0, 16]\n\ - vmov r1, s21\n str r1, [%0, 20]\n\ - vmov r1, s22\n str r1, [%0, 24]\n\ - vmov r1, s23\n str r1, [%0, 28]\n\ - vmov r1, s24\n str r1, [%0, 32]\n\ - vmov r1, s25\n str r1, [%0, 36]\n\ - vmov r1, s26\n str r1, [%0, 40]\n\ - vmov r1, s27\n str r1, [%0, 44]\n\ - vmov r1, s28\n str r1, [%0, 48]\n\ - vmov r1, s29\n str r1, [%0, 52]\n\ - vmov r1, s30\n str r1, [%0, 56]\n\ - vmov r1, s31\n str r1, [%0, 60]\n\ - " - : - : "r"(dst) - : "r1"); -} - -static void print_fpu_regs(const uint32_t *regs, int off, int n) { - for (int i = 0, j = off; i < n; i++, j++) { - if (j % 4 == 0) mgos_cd_putc('\n'); - mgos_cd_printf(" S%d: %s0x%08lx", j, (j < 10 ? " " : ""), regs[i]); - } -} -#endif - -void arm_exc_handler_bottom(uint8_t isr_no, struct arm_exc_frame *ef, - struct arm_gdb_reg_file *rf) { - char buf[8]; - const char *name; - portDISABLE_INTERRUPTS(); - switch (isr_no) { - case 0: - name = "ThreadMode"; - break; - - case 1: - case 7: - case 8: - case 9: - case 10: - case 13: - name = "Reserved"; - break; - - case 2: - name = "NMI"; - break; - case 3: - name = "HardFault"; - break; - case 4: - name = "MemManage"; - break; - case 5: - name = "BusFault"; - break; - case 6: - name = "UsageFault"; - break; - case 11: - name = "SVCall"; - break; - case 12: - name = "ReservedDebug"; - break; - case 14: - name = "PendSV"; - break; - case 15: - name = "SysTick"; - break; - default: { - sprintf(buf, "IRQ%u", isr_no - 16); - name = buf; - } - } - mgos_cd_printf("\n\n--- Exception %u (%s) ---\n", isr_no, name); - if (rf != NULL) { - mgos_cd_printf(" R0: 0x%08lx R1: 0x%08lx R2: 0x%08lx R3: 0x%08lx\n", - rf->r[0], rf->r[1], rf->r[2], rf->r[3]); - mgos_cd_printf(" R4: 0x%08lx R5: 0x%08lx R6: 0x%08lx R7: 0x%08lx\n", - rf->r[4], rf->r[5], rf->r[6], rf->r[7]); - mgos_cd_printf(" R8: 0x%08lx R9: 0x%08lx R10: 0x%08lx R11: 0x%08lx\n", - rf->r[8], rf->r[9], rf->r[10], rf->r[11]); - mgos_cd_printf(" R12: 0x%08lx SP: 0x%08lx LR: 0x%08lx PC: 0x%08lx\n", - rf->r[12], rf->sp, rf->lr, rf->pc); - mgos_cd_printf(" PSR: 0x%08lx\n", rf->cpsr); - } - memset(rf->d, 0, sizeof(rf->d)); -#if ARM_HAVE_FPU - rf->fpscr = ef->fpscr; - memcpy((uint8_t *) rf->d, ef->s, sizeof(ef->s)); - print_fpu_regs((uint32_t *) rf->d, 0, ARRAY_SIZE(ef->s)); - save_s16_s31(ef->s); - memcpy(((uint8_t *) rf->d) + sizeof(ef->s), ef->s, sizeof(ef->s)); - print_fpu_regs((uint32_t *) (((uint8_t *) rf->d) + sizeof(ef->s)), 16, - ARRAY_SIZE(ef->s)); - mgos_cd_putc('\n'); - mgos_cd_printf("FPSCR: 0x%08lx\n", rf->fpscr); -#else - rf->fpscr = 0; -#endif - mgos_cd_emit_header(); - mgos_cd_emit_section(MGOS_CORE_DUMP_SECTION_REGS, rf, sizeof(*rf)); - mgos_cd_emit_section("SRAM", (void *) SRAM_BASE_ADDR, SRAM_SIZE); - mgos_cd_emit_footer(); -#ifdef MGOS_HALT_ON_EXCEPTION - mgos_cd_printf("Halting\n"); - while (1) { - mgos_wdt_feed(); - } -#else - mgos_cd_printf("Rebooting\n"); - mgos_dev_system_restart(); -#endif -} diff --git a/src/mongoose-6.11/src/common/platforms/arm/arm_exc_top.S b/src/mongoose-6.11/src/common/platforms/arm/arm_exc_top.S deleted file mode 100644 index 9ffade4..0000000 --- a/src/mongoose-6.11/src/common/platforms/arm/arm_exc_top.S +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2014-2017 Cesanta Software Limited - * All rights reserved - */ - -.arch armv7e-m -.syntax unified -.thumb - -/* These are required to satisfy TI linker. */ -.eabi_attribute Tag_ABI_align_needed, 1 -.eabi_attribute Tag_ABI_align_preserved, 1 - -.global arm_exc_handler_top -.global arm_exc_handler_bottom - -/* - * Determines the stack pointer, populates most of the GDB frame - * and hands off to the C routine. - */ -.section .text.arm_exc_handler_top -.type arm_exc_handler_top, %function -.align 8 - -arm_exc_handler_top: - tst lr, #4 - ite eq - mrseq r1, msp - mrsne r1, psp - // r1 -> arm_exc_frame prepared for us by the CPU -#if ARM_HAVE_FPU - add r0, r1, #104 // sizeof(arm_exc_frame) - sub sp, #328 // sizeof(arm_gdb_reg_file) -#else - add r0, r1, #32 // sizeof(arm_exc_frame) - sub sp, #328 // sizeof(arm_gdb_reg_file) -#endif - mov r2, sp - // r0 -> original sp, r2 -> arm_gdb_reg_file to fill - // r3 - scratch - ldr r3, [r1, #0] // r0 - str r3, [r2, #0] - ldr r3, [r1, #4] // r2 - str r3, [r2, #4] - ldr r3, [r1, #8] // r1 - str r3, [r2, #8] - ldr r3, [r1, #12] // r3 - str r3, [r2, #12] - str r4, [r2, #16] // r4 - str r5, [r2, #20] // r5 - str r6, [r2, #24] // r6 - str r7, [r2, #28] // r7 - str r8, [r2, #32] // r8 - str r9, [r2, #36] // r9 - str r10, [r2, #40] // r10 - str r11, [r2, #44] // r11 - ldr r3, [r1, #16] // r12 - str r3, [r2, #48] - str r0, [r2, #52] // sp - ldr r3, [r1, #20] // lr - str r3, [r2, #56] - ldr r3, [r1, #24] // pc - str r3, [r2, #60] - ldr r3, [r1, #28] // xpsr - str r3, [r2, #64] - - mrs r0, ipsr - b arm_exc_handler_bottom -.size arm_exc_handler_top, . - arm_exc_handler_top diff --git a/src/mongoose-6.11/src/common/platforms/cc3200/cc3200.ld b/src/mongoose-6.11/src/common/platforms/cc3200/cc3200.ld deleted file mode 100644 index a2df648..0000000 --- a/src/mongoose-6.11/src/common/platforms/cc3200/cc3200.ld +++ /dev/null @@ -1,92 +0,0 @@ -/***************************************************************************** -* -* GCC Linker script for CC3200. Based on TI's example "blinky.ld". -* -* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ -* -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* -* Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* -* Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the -* distribution. -* -* Neither the name of Texas Instruments Incorporated nor the names of -* its contributors may be used to endorse or promote products derived -* from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -******************************************************************************/ - -ORG = DEFINED(ORG) ? ORG : 0x20004000; -RAM_SIZE = DEFINED(RAM_SIZE) ? RAM_SIZE : 0x3C000; - -MEMORY -{ - /* SRAM size of 240KB for cc3200 ES 1.33 device onward */ - SRAM (rwx) : ORIGIN = ORG, LENGTH = RAM_SIZE -} - -SECTIONS -{ - .text : - { - _text = .; - KEEP(*(.intvecs)) - *(.text*) - *(.rodata*) - *(.ARM.extab* .gnu.linkonce.armextab.*) - . = ALIGN(8); - _etext = .; - } > SRAM - - .ARM : { - __exidx_start = .; - *(.ARM.exidx*) - __exidx_end = .; - } > SRAM - - __init_data = .; - - .data : AT(__init_data) - { - _data = .; - *(.data*) - . = ALIGN (8); - _edata = .; - } > SRAM - - .bss : - { - _bss = .; - *(.bss*) - *(COMMON) - _ebss = .; - } > SRAM - - .heap : - { - _heap = .; - . = . + (LENGTH(SRAM) - SIZEOF(.text) - SIZEOF(.ARM) - SIZEOF(.data) - SIZEOF(.bss)); - . = ALIGN(8); - _eheap = .; - - } > SRAM -} diff --git a/src/mongoose-6.11/src/common/platforms/cc3200/cc3200_libc.c b/src/mongoose-6.11/src/common/platforms/cc3200/cc3200_libc.c deleted file mode 100644 index f488725..0000000 --- a/src/mongoose-6.11/src/common/platforms/cc3200/cc3200_libc.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#if CS_PLATFORM == CS_P_CC3200 - -#include "common/mg_mem.h" -#include -#include - -#ifndef __TI_COMPILER_VERSION__ -#include -#include -#include -#include -#endif - -#include -#include -#include -#include -#include -#include -#include - -#define CONSOLE_UART UARTA0_BASE - -#ifdef __TI_COMPILER_VERSION__ -int asprintf(char **strp, const char *fmt, ...) { - va_list ap; - int len; - - *strp = MG_MALLOC(BUFSIZ); - if (*strp == NULL) return -1; - - va_start(ap, fmt); - len = vsnprintf(*strp, BUFSIZ, fmt, ap); - va_end(ap); - - if (len > 0) { - *strp = MG_REALLOC(*strp, len + 1); - if (*strp == NULL) return -1; - } - - if (len >= BUFSIZ) { - va_start(ap, fmt); - len = vsnprintf(*strp, len + 1, fmt, ap); - va_end(ap); - } - - return len; -} - -#if MG_TI_NO_HOST_INTERFACE -time_t HOSTtime() { - struct timeval tp; - gettimeofday(&tp, NULL); - return tp.tv_sec; -} -#endif - -#endif /* __TI_COMPILER_VERSION__ */ - -void fprint_str(FILE *fp, const char *str) { - while (*str != '\0') { - if (*str == '\n') MAP_UARTCharPut(CONSOLE_UART, '\r'); - MAP_UARTCharPut(CONSOLE_UART, *str++); - } -} - -void _exit(int status) { - fprint_str(stderr, "_exit\n"); - /* cause an unaligned access exception, that will drop you into gdb */ - *(int *) 1 = status; - while (1) - ; /* avoid gcc warning because stdlib abort() has noreturn attribute */ -} - -void _not_implemented(const char *what) { - fprint_str(stderr, what); - fprint_str(stderr, " is not implemented\n"); - _exit(42); -} - -int _kill(int pid, int sig) { - (void) pid; - (void) sig; - _not_implemented("_kill"); - return -1; -} - -int _getpid() { - fprint_str(stderr, "_getpid is not implemented\n"); - return 42; -} - -int _isatty(int fd) { - /* 0, 1 and 2 are TTYs. */ - return fd < 2; -} - -#endif /* CS_PLATFORM == CS_P_CC3200 */ diff --git a/src/mongoose-6.11/src/common/platforms/cc3200/cc3200v1p32.cmd b/src/mongoose-6.11/src/common/platforms/cc3200/cc3200v1p32.cmd deleted file mode 100644 index 3b17d07..0000000 --- a/src/mongoose-6.11/src/common/platforms/cc3200/cc3200v1p32.cmd +++ /dev/null @@ -1,82 +0,0 @@ -//***************************************************************************** -// cc3200v1p32.cmd -// -// CCS linker configuration file for cc3200 ES 1.32. -// -// Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ -// -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the -// distribution. -// -// Neither the name of Texas Instruments Incorporated nor the names of -// its contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -//***************************************************************************** - - ---retain=g_pfnVectors - -//***************************************************************************** -// The following command line options are set as part of the CCS project. -// If you are building using the command line, or for some reason want to -// define them here, you can uncomment and modify these lines as needed. -// If you are using CCS for building, it is probably better to make any such -// modifications in your CCS project and leave this file alone. -//***************************************************************************** - - -//***************************************************************************** -// The starting address of the application. Normally the interrupt vectors -// must be located at the beginning of the application. -//***************************************************************************** -#define RAM_BASE 0x20004000 - -/* System memory map */ - -MEMORY -{ - /* Application uses internal RAM for program and data */ - SRAM_CODE (RWX) : origin = 0x20004000, length = 0x2F000 - SRAM_DATA (RWX) : origin = 0x20033000, length = 0xD000 -} - -/* Section allocation in memory */ - -SECTIONS -{ - .intvecs: > RAM_BASE - .init_array : > SRAM_CODE - .vtable : > SRAM_CODE - .text : > SRAM_CODE - .const : > SRAM_CODE - .cinit : > SRAM_CODE - .pinit : > SRAM_CODE - .data : > SRAM_DATA - .bss : > SRAM_DATA - .sysmem : > SRAM_DATA - .stack : > SRAM_DATA(HIGH) -} - diff --git a/src/mongoose-6.11/src/common/platforms/cc3200/gcc.mk b/src/mongoose-6.11/src/common/platforms/cc3200/gcc.mk deleted file mode 100644 index ec98b75..0000000 --- a/src/mongoose-6.11/src/common/platforms/cc3200/gcc.mk +++ /dev/null @@ -1,52 +0,0 @@ -APP_LDFLAGS ?= -CC_WRAPPER ?= -GENFILES_LIST ?= -CC = arm-none-eabi-gcc -CXX = arm-none-eabi-g++ -AR = arm-none-eabi-ar -NM = arm-none-eabi-nm - -IPATH += $(SDK_PATH)/third_party/FreeRTOS/source/portable/GCC/ARM_CM4 -VPATH += $(SDK_PATH)/third_party/FreeRTOS/source/portable/GCC/ARM_CM4 - -C_CXX_FLAGS = -mthumb -mcpu=cortex-m4 -ffunction-sections -fdata-sections \ - -MD -Os -ggdb -Wall -Werror -Dgcc -CFLAGS += -std=c99 $(C_CXX_FLAGS) -CXXFLAGS += -std=g++11 $(C_CXX_FLAGS) - -AR = arm-none-eabi-ar -LD = arm-none-eabi-ld -OBJCOPY = arm-none-eabi-objcopy -LIBGCC := ${shell ${CC} -mthumb ${CFLAGS} -print-libgcc-file-name} -LIBC := ${shell ${CC} ${CFLAGS} -print-file-name=libc.a} -LIBM := ${shell ${CC} ${CFLAGS} -print-file-name=libm.a} - -# Disable certain warnings on SDK sources, we have no control over them anyway. -# We also force-include platform.h which resolves some symbol conflicts -# between system includes and simplelink.h -$(SDK_OBJS): CFLAGS += -Wno-missing-braces -Wno-strict-aliasing -Wno-parentheses -Wno-unused-variable -Wno-builtin-macro-redefined -$(SDK_OBJS): CFLAGS += -include common/platform.h - -# cc flags,file -define cc - $(vecho) "GCC $2 -> $@" - $(Q) $(CC_WRAPPER) $(CC) -c $1 -o $@ $2 -endef -define cxx - $(vecho) "G++ $2 -> $@" - $(Q) $(CC_WRAPPER) $(CXX) -c $1 -o $@ $2 -endef - -# ar files -define ar - $(vecho) "AR $@" - $(Q) $(AR) cru $@ $1 -endef - -# link script,flags,objs -define link - $(vecho) "LD $@" - $(Q) $(CC_WRAPPER) $(LD) \ - --gc-sections -o $@ -T $1 $2 $3 \ - $(LIBM) $(LIBC) $(LIBGCC) -endef diff --git a/src/mongoose-6.11/src/common/platforms/cc3200/ti.mk b/src/mongoose-6.11/src/common/platforms/cc3200/ti.mk deleted file mode 100644 index b50407f..0000000 --- a/src/mongoose-6.11/src/common/platforms/cc3200/ti.mk +++ /dev/null @@ -1,52 +0,0 @@ -IPATH += $(SDK_PATH)/third_party/FreeRTOS/source/portable/CCS/ARM_CM3 -VPATH += $(SDK_PATH)/third_party/FreeRTOS/source/portable/CCS/ARM_CM3 - -CC_WRAPPER ?= -CC = $(TOOLCHAIN)/bin/armcl -AR = $(TOOLCHAIN)/bin/armar -NM = nm -GENFILES_LIST ?= - -C_CXX_FLAGS = -Dccs -I$(TOOLCHAIN)/include -TI_C_CXX_FLAGS = -mv7M4 --little_endian --code_state=16 --float_support=vfplib --abi=eabi \ - -O4 --opt_for_speed=0 --unaligned_access=on --small_enum \ - --gen_func_subsections=on --diag_wrap=off --display_error_number \ - --emit_warnings_as_errors -CFLAGS += --c99 $(TI_C_CXX_FLAGS) $(C_CXX_FLAGS) -CXXFLAGS += $(TI_C_CXX_FLAGS) $(C_CXX_FLAGS) - -# cc flags,file -define cc - $(vecho) "TICC $2 -> $@" - $(Q) $(CC_WRAPPER) $(CC) -c --preproc_with_compile -ppd=$@.d $1 --output_file=$@ $2 -endef -define cxx - $(vecho) "TICXX $2 -> $@" - $(Q) $(CC_WRAPPER) $(CC) -c --preproc_with_compile -ppd=$@.d $1 --output_file=$@ $2 -endef - -# asm flags,file -define asm - $(vecho) "TIASM $2 -> $@" - $(Q) $(CC_WRAPPER) $(CC) -c $1 --output_file=$@ $2 -endef - -# ar files -define ar - $(vecho) "TIAR $@" - $(Q) $(AR) qru $@ $1 -endef - -# link script,flags,objs -define link - $(vecho) "TILD $@" - $(Q) $(CC_WRAPPER) $(CC) \ - -mv7M4 --code_state=16 --float_support=vfplib --abi=eabi --little_endian \ - --run_linker \ - --generate_dead_funcs_list=$@.garbage.xml \ - -i $(TOOLCHAIN)/lib \ - --reread_libs --warn_sections --display_error_number \ - --unused_section_elimination=on \ - -o $@ --map_file=$@.map --xml_link_info=$@.map.xml \ - $2 $1 $3 -endef diff --git a/src/mongoose-6.11/src/common/platforms/esp/esptool.py b/src/mongoose-6.11/src/common/platforms/esp/esptool.py deleted file mode 100755 index 9592f6d..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp/esptool.py +++ /dev/null @@ -1,2133 +0,0 @@ -#!/usr/bin/env python -# NB: Before sending a PR to change the above line to '#!/usr/bin/env python2', please read https://github.com/themadinventor/esptool/issues/21 -# -# ESP8266 & ESP32 ROM Bootloader Utility -# https://github.com/themadinventor/esptool -# -# Copyright (C) 2014-2016 Fredrik Ahlberg, Angus Gratton, Espressif Systems (Shanghai) PTE LTD, other contributors as noted. -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin -# Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import argparse -import hashlib -import inspect -import json -import os -import serial -import struct -import subprocess -import sys -import time -import base64 -import zlib - -__version__ = "2.0-dev" - - -MAX_UINT32 = 0xffffffff -MAX_UINT24 = 0xffffff - - -def check_supported_function(func, check_func): - """ - Decorator implementation that wraps a check around an ESPLoader - bootloader function to check if it's supported. - - This is used to capture the multidimensional differences in - functionality between the ESP8266 & ESP32 ROM loaders, and the - software stub that runs on both. Not possible to do this cleanly - via inheritance alone. - """ - def inner(*args, **kwargs): - obj = args[0] - if check_func(obj): - return func(*args, **kwargs) - else: - raise NotImplementedInROMError(obj) - return inner - - -def stub_function_only(func): - """ Attribute for a function only supported in the software stub loader """ - return check_supported_function(func, lambda o: o.IS_STUB) - - -def stub_and_esp32_function_only(func): - """ Attribute for a function only supported by software stubs or ESP32 ROM """ - return check_supported_function(func, lambda o: o.IS_STUB or o.CHIP_NAME == "ESP32") - - -def esp8266_function_only(func): - """ Attribute for a function only supported on ESP8266 """ - return check_supported_function(func, lambda o: o.CHIP_NAME == "ESP8266") - - -class ESPLoader(object): - """ Base class providing access to ESP ROM & softtware stub bootloaders. - Subclasses provide ESP8266 & ESP32 specific functionality. - - Don't instantiate this base class directly, either instantiate a subclass or - call ESPLoader.detect_chip() which will interrogate the chip and return the - appropriate subclass instance. - - """ - CHIP_NAME = "Espressif device" - IS_STUB = False - - DEFAULT_PORT = "/dev/ttyUSB0" - - # Commands supported by ESP8266 ROM bootloader - ESP_FLASH_BEGIN = 0x02 - ESP_FLASH_DATA = 0x03 - ESP_FLASH_END = 0x04 - ESP_MEM_BEGIN = 0x05 - ESP_MEM_END = 0x06 - ESP_MEM_DATA = 0x07 - ESP_SYNC = 0x08 - ESP_WRITE_REG = 0x09 - ESP_READ_REG = 0x0a - - # Some comands supported by ESP32 ROM bootloader (or -8266 w/ stub) - ESP_SPI_SET_PARAMS = 0x0B - ESP_SPI_ATTACH = 0x0D - ESP_CHANGE_BAUDRATE = 0x0F - ESP_FLASH_DEFL_BEGIN = 0x10 - ESP_FLASH_DEFL_DATA = 0x11 - ESP_FLASH_DEFL_END = 0x12 - ESP_SPI_FLASH_MD5 = 0x13 - - # Some commands supported by stub only - ESP_ERASE_FLASH = 0xD0 - ESP_ERASE_REGION = 0xD1 - ESP_READ_FLASH = 0xD2 - ESP_GET_FLASH_ID = 0xD3 - - # Maximum block sized for RAM and Flash writes, respectively. - ESP_RAM_BLOCK = 0x1800 - ESP_FLASH_BLOCK = 0x400 - - FLASH_WRITE_SIZE = ESP_FLASH_BLOCK - - # Default baudrate. The ROM auto-bauds, so we can use more or less whatever we want. - ESP_ROM_BAUD = 115200 - - # First byte of the application image - ESP_IMAGE_MAGIC = 0xe9 - - # Initial state for the checksum routine - ESP_CHECKSUM_MAGIC = 0xef - - # Flash sector size, minimum unit of erase. - ESP_FLASH_SECTOR = 0x1000 - - UART_DATA_REG_ADDR = 0x60000078 - - # Memory addresses - IROM_MAP_START = 0x40200000 - IROM_MAP_END = 0x40300000 - - # The number of bytes in the UART response that signify command status - STATUS_BYTES_LENGTH = 2 - - def __init__(self, port=DEFAULT_PORT, baud=ESP_ROM_BAUD, do_connect=True): - """Base constructor for ESPLoader bootloader interaction - - Don't call this constructor, either instantiate ESP8266ROM - or ESP32ROM, or use ESPLoader.detect_chip(). - - This base class has all of the instance methods for bootloader - functionality supported across various chips & stub - loaders. Subclasses replace the functions they don't support - with ones which throw NotImplementedInROMError(). - - """ - if isinstance(port, serial.Serial): - self._port = port - else: - self._port = serial.Serial(port) - self._slip_reader = slip_reader(self._port) - # setting baud rate in a separate step is a workaround for - # CH341 driver on some Linux versions (this opens at 9600 then - # sets), shouldn't matter for other platforms/drivers. See - # https://github.com/themadinventor/esptool/issues/44#issuecomment-107094446 - self._port.baudrate = baud - if do_connect: - self.connect() - - @staticmethod - def detect_chip(port=DEFAULT_PORT, baud=ESP_ROM_BAUD): - """Use serial access to detect the chip type. - - We use the UART's datecode register for this, it's mapped at - the same address on ESP8266 & ESP32 so we can use one - memory read and compare to the datecode register for each chip - type. - - """ - detect_port = ESPLoader(port, baud, True) - sys.stdout.write('Detecting chip type... ') - date_reg = detect_port.read_reg(ESPLoader.UART_DATA_REG_ADDR) - - for cls in [ESP8266ROM, ESP32ROM]: - if date_reg == cls.DATE_REG_VALUE: - # don't connect a second time - inst = cls(detect_port._port, baud, False) - print '%s' % inst.CHIP_NAME - return inst - print '' - raise FatalError("Unexpected UART datecode value 0x%08x. Failed to autodetect chip type." % date_reg) - - """ Read a SLIP packet from the serial port """ - def read(self): - r = self._slip_reader.next() - return r - - """ Write bytes to the serial port while performing SLIP escaping """ - def write(self, packet): - buf = '\xc0' \ - + (packet.replace('\xdb','\xdb\xdd').replace('\xc0','\xdb\xdc')) \ - + '\xc0' - self._port.write(buf) - - """ Calculate checksum of a blob, as it is defined by the ROM """ - @staticmethod - def checksum(data, state=ESP_CHECKSUM_MAGIC): - for b in data: - state ^= ord(b) - return state - - """ Send a request and read the response """ - def command(self, op=None, data="", chk=0): - if op is not None: - pkt = struct.pack(' self.STATUS_BYTES_LENGTH: - return data[:-self.STATUS_BYTES_LENGTH] - else: # otherwise, just return the 'val' field which comes from the reply header (this is used by read_reg) - return val - - def flush_input(self): - self._port.flushInput() - self._slip_reader = slip_reader(self._port) - - def sync(self): - """ Perform a connection test """ - self.command(self.ESP_SYNC, '\x07\x07\x12\x20' + 32 * '\x55') - for i in xrange(7): - self.command() - - def connect(self): - """ Try connecting repeatedly until successful, or giving up """ - print 'Connecting...' - - for _ in xrange(10): - # issue reset-to-bootloader: - # RTS = either CH_PD or nRESET (both active low = chip in reset) - # DTR = GPIO0 (active low = boot to flasher) - self._port.setDTR(False) - self._port.setRTS(True) - time.sleep(0.05) - self._port.setDTR(True) - self._port.setRTS(False) - time.sleep(0.05) - self._port.setDTR(False) - - self._port.timeout = 0.1 - last_exception = None - for _ in xrange(4): - try: - self.flush_input() - self._port.flushOutput() - self.sync() - self._port.timeout = 5 - return - except FatalError as e: - last_exception = e - time.sleep(0.05) - raise FatalError('Failed to connect to %s: %s' % (self.CHIP_NAME, last_exception)) - - """ Read memory address in target """ - def read_reg(self, addr): - # we don't call check_command here because read_reg() function is called - # when detecting chip type, and the way we check for success (STATUS_BYTES_LENGTH) is different - # for different chip types (!) - val, data = self.command(self.ESP_READ_REG, struct.pack('>sys.stderr, 'Stub executed, reading response:' - while True: - p = self.read() - print hexify(p) - if p == '': - return - - def run_stub(self, stub=None): - if stub is None: - if self.IS_STUB: - raise FatalError("Not possible for a stub to load another stub (memory likely to overlap.)") - stub = self.STUB_CODE - - # Upload - print "Uploading stub..." - for field in ['text', 'data']: - if field in stub: - self._upload_ram(stub[field + "_start"], stub[field]) - print "Running stub..." - self.mem_finish(stub['entry']) - - p = self.read() - if p != 'OHAI': - raise FatalError("Failed to start stub. Unexpected response: %s" % p) - print "Stub running..." - return self.STUB_CLASS(self) - - @stub_and_esp32_function_only - def flash_defl_begin(self, size, compsize, offset): - """ Start downloading compressed data to Flash (performs an erase) """ - old_tmo = self._port.timeout - num_blocks = (compsize + self.ESP_FLASH_BLOCK - 1) / self.ESP_FLASH_BLOCK - erase_blocks = (size + self.ESP_FLASH_BLOCK - 1) / self.ESP_FLASH_BLOCK - - self._port.timeout = 20 - t = time.time() - print "Compressed %d bytes to %d..." % (size, compsize) - self.check_command("enter compressed flash mode", self.ESP_FLASH_DEFL_BEGIN, - struct.pack(' length: - raise FatalError('Read more than expected') - digest_frame = self.read() - if len(digest_frame) != 16: - raise FatalError('Expected digest, got: %s' % hexify(digest_frame)) - expected_digest = hexify(digest_frame).upper() - digest = hashlib.md5(data).hexdigest().upper() - if digest != expected_digest: - raise FatalError('Digest mismatch: expected %s, got %s' % (expected_digest, digest)) - return data - - def flash_spi_attach(self,is_hspi,is_legacy): - """Send SPI attach command to enable the SPI flash pins - - ESP8266 ROM does this when you send flash_begin, ESP32 ROM - has it as a SPI command. - """ - # last 3 bytes in ESP_SPI_ATTACH argument are reserved values - arg = struct.pack(' 0: - self.write_reg(SPI_MOSI_DLEN_REG, mosi_bits - 1) - if miso_bits > 0: - self.write_reg(SPI_MISO_DLEN_REG, miso_bits - 1) - else: - - def set_data_lengths(mosi_bits, miso_bits): - SPI_DATA_LEN_REG = SPI_USR1_REG - SPI_MOSI_BITLEN_S = 17 - SPI_MISO_BITLEN_S = 8 - mosi_mask = 0 if (mosi_bits == 0) else (mosi_bits - 1) - miso_mask = 0 if (miso_bits == 0) else (miso_bits - 1) - self.write_reg(SPI_DATA_LEN_REG, - (miso_mask << SPI_MISO_BITLEN_S) | ( - mosi_mask << SPI_MOSI_BITLEN_S)) - - # SPI peripheral "command" bitmasks for SPI_CMD_REG - SPI_CMD_USR = (1 << 18) - - # shift values - SPI_USR2_DLEN_SHIFT = 28 - - if read_bits > 32: - raise FatalError("Reading more than 32 bits back from a SPI flash operation is unsupported") - if len(data) > 64: - raise FatalError("Writing more than 64 bytes of data with one SPI command is unsupported") - - data_bits = len(data) * 8 - flags = SPI_USR_COMMAND - if read_bits > 0: - flags |= SPI_USR_MISO - if data_bits > 0: - flags |= SPI_USR_MOSI - set_data_lengths(data_bits, read_bits) - self.write_reg(SPI_USR_REG, flags) - self.write_reg(SPI_USR2_REG, - (7 << SPI_USR2_DLEN_SHIFT) | spiflash_command) - if data_bits == 0: - self.write_reg(SPI_W0_REG, 0) # clear data register before we read it - else: - if len(data) % 4 != 0: # pad to 32-bit multiple - data += b'\0' * (4 - (len(data) % 4)) - words = struct.unpack("I" * (len(data) / 4), data) - next_reg = SPI_W0_REG - for word in words: - self.write_reg(next_reg, word) - next_reg += 4 - self.write_reg(SPI_CMD_REG, SPI_CMD_USR) - - def wait_done(): - for _ in xrange(10): - if (self.read_reg(SPI_CMD_REG) & SPI_CMD_USR) == 0: - return - raise FatalError("SPI command did not complete in time") - wait_done() - - status = self.read_reg(SPI_W0_REG) - return status - - def read_status(self, num_bytes=2): - """Read up to 24 bits (num_bytes) of SPI flash status register contents - via RDSR, RDSR2, RDSR3 commands - - Not all SPI flash supports all three commands. The upper 1 or 2 - bytes may be 0xFF. - """ - SPIFLASH_RDSR = 0x05 - SPIFLASH_RDSR2 = 0x35 - SPIFLASH_RDSR3 = 0x15 - - status = 0 - shift = 0 - for cmd in [SPIFLASH_RDSR, SPIFLASH_RDSR2, SPIFLASH_RDSR3][0:num_bytes]: - status += self.run_spiflash_command(cmd, read_bits=8) << shift - shift += 8 - return status - - def write_status(self, new_status, num_bytes=2, set_non_volatile=False): - """Write up to 24 bits (num_bytes) of new status register - - num_bytes can be 1, 2 or 3. - - Not all flash supports the additional commands to write the - second and third byte of the status register. When writing 2 - bytes, esptool also sends a 16-byte WRSR command (as some - flash types use this instead of WRSR2.) - - If the set_non_volatile flag is set, non-volatile bits will - be set as well as volatile ones (WREN used instead of WEVSR). - - """ - SPIFLASH_WRSR = 0x01 - SPIFLASH_WRSR2 = 0x31 - SPIFLASH_WRSR3 = 0x11 - SPIFLASH_WEVSR = 0x50 - SPIFLASH_WREN = 0x06 - SPIFLASH_WRDI = 0x04 - - enable_cmd = SPIFLASH_WREN if set_non_volatile else SPIFLASH_WEVSR - - # try using a 16-bit WRSR (not supported by all chips) - # this may be redundant, but shouldn't hurt - if num_bytes == 2: - self.run_spiflash_command(enable_cmd) - self.run_spiflash_command(SPIFLASH_WRSR, struct.pack(">= 8 - - self.run_spiflash_command(SPIFLASH_WRDI) - - -class ESP8266ROM(ESPLoader): - """ Access class for ESP8266 ROM bootloader - """ - CHIP_NAME = "ESP8266" - IS_STUB = False - - DATE_REG_VALUE = 0x00062000 - - # OTP ROM addresses - ESP_OTP_MAC0 = 0x3ff00050 - ESP_OTP_MAC1 = 0x3ff00054 - ESP_OTP_MAC3 = 0x3ff0005c - - SPI_REG_BASE = 0x60000200 - SPI_W0_OFFS = 0x40 - SPI_HAS_MOSI_DLEN_REG = False - - FLASH_SIZES = { - '512KB':0x00, - '256KB':0x10, - '1MB':0x20, - '2MB':0x30, - '4MB':0x40, - '2MB-c1': 0x50, - '4MB-c1':0x60, - '4MB-c2':0x70} - - def flash_spi_attach(self, is_spi, is_legacy): - pass # not implemented in ROM, but OK to silently skip - - def flash_set_parameters(self, size): - pass # not implemented in ROM, but OK to silently skip - - def chip_id(self): - """ Read Chip ID from OTP ROM - see http://esp8266-re.foogod.com/wiki/System_get_chip_id_%28IoT_RTOS_SDK_0.9.9%29 """ - id0 = self.read_reg(self.ESP_OTP_MAC0) - id1 = self.read_reg(self.ESP_OTP_MAC1) - return (id0 >> 24) | ((id1 & MAX_UINT24) << 8) - - def read_mac(self): - """ Read MAC from OTP ROM """ - mac0 = self.read_reg(self.ESP_OTP_MAC0) - mac1 = self.read_reg(self.ESP_OTP_MAC1) - mac3 = self.read_reg(self.ESP_OTP_MAC3) - if (mac3 != 0): - oui = ((mac3 >> 16) & 0xff, (mac3 >> 8) & 0xff, mac3 & 0xff) - elif ((mac1 >> 16) & 0xff) == 0: - oui = (0x18, 0xfe, 0x34) - elif ((mac1 >> 16) & 0xff) == 1: - oui = (0xac, 0xd0, 0x74) - else: - raise FatalError("Unknown OUI") - return oui + ((mac1 >> 8) & 0xff, mac1 & 0xff, (mac0 >> 24) & 0xff) - - def get_erase_size(self, offset, size): - """ Calculate an erase size given a specific size in bytes. - - Provides a workaround for the bootloader erase bug.""" - - sectors_per_block = 16 - sector_size = self.ESP_FLASH_SECTOR - num_sectors = (size + sector_size - 1) / sector_size - start_sector = offset / sector_size - - head_sectors = sectors_per_block - (start_sector % sectors_per_block) - if num_sectors < head_sectors: - head_sectors = num_sectors - - if num_sectors < 2 * head_sectors: - return (num_sectors + 1) / 2 * sector_size - else: - return (num_sectors - head_sectors) * sector_size - - -class ESP8266StubLoader(ESP8266ROM): - """ Access class for ESP8266 stub loader, runs on top of ROM. - """ - FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c - IS_STUB = True - - def __init__(self, rom_loader): - self._port = rom_loader._port - self.flush_input() # resets _slip_reader - -ESP8266ROM.STUB_CLASS = ESP8266StubLoader - - -class ESP32ROM(ESPLoader): - """Access class for ESP32 ROM bootloader - - """ - CHIP_NAME = "ESP32" - IS_STUB = False - - DATE_REG_VALUE = 0x15122500 - - IROM_MAP_START = 0x400d0000 - IROM_MAP_END = 0x40400000 - DROM_MAP_START = 0x3F400000 - DROM_MAP_END = 0x3F700000 - - # ESP32 uses a 4 byte status reply - STATUS_BYTES_LENGTH = 4 - - SPI_REG_BASE = 0x60002000 - EFUSE_REG_BASE = 0x6001a000 - - SPI_W0_OFFS = 0x80 - SPI_HAS_MOSI_DLEN_REG = True - - FLASH_SIZES = { - '1MB':0x00, - '2MB':0x10, - '4MB':0x20, - '8MB':0x30, - '16MB':0x40 - } - - def read_efuse(self, n): - """ Read the nth word of the ESP3x EFUSE region. """ - return self.read_reg(self.EFUSE_REG_BASE + (4 * n)) - - def chip_id(self): - word16 = self.read_efuse(16) - word17 = self.read_efuse(17) - return ((word17 & MAX_UINT24) << 24) | (word16 >> 8) & MAX_UINT24 - - def read_mac(self): - """ Read MAC from EFUSE region """ - word16 = self.read_efuse(16) - word17 = self.read_efuse(17) - word18 = self.read_efuse(18) - word19 = self.read_efuse(19) - wifi_mac = (((word17 >> 16) & 0xff), ((word17 >> 8) & 0xff), ((word17 >> 0) & 0xff), - ((word16 >> 24) & 0xff), ((word16 >> 16) & 0xff), ((word16 >> 8) & 0xff)) - bt_mac = (((word19 >> 16) & 0xff), ((word19 >> 8) & 0xff), ((word19 >> 0) & 0xff), - ((word18 >> 24) & 0xff), ((word18 >> 16) & 0xff), ((word18 >> 8) & 0xff)) - return (wifi_mac,bt_mac) - - def get_erase_size(self, offset, size): - return size - - -class ESP32StubLoader(ESP32ROM): - """ Access class for ESP32 stub loader, runs on top of ROM. - """ - FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c - STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM - IS_STUB = True - - def __init__(self, rom_loader): - self._port = rom_loader._port - self.flush_input() # resets _slip_reader - -ESP32ROM.STUB_CLASS = ESP32StubLoader - - -class ESPBOOTLOADER(object): - """ These are constants related to software ESP bootloader, working with 'v2' image files """ - - # First byte of the "v2" application image - IMAGE_V2_MAGIC = 0xea - - # First 'segment' value in a "v2" application image, appears to be a constant version value? - IMAGE_V2_SEGMENT = 4 - - -def LoadFirmwareImage(chip, filename): - """ Load a firmware image. Can be for ESP8266 or ESP32. ESP8266 images will be examined to determine if they are - original ROM firmware images (ESPFirmwareImage) or "v2" OTA bootloader images. - - Returns a BaseFirmwareImage subclass, either ESPFirmwareImage (v1) or OTAFirmwareImage (v2). - """ - with open(filename, 'rb') as f: - if chip == 'esp32': - return ESP32FirmwareImage(f) - else: # Otherwise, ESP8266 so look at magic to determine the image type - magic = ord(f.read(1)) - f.seek(0) - if magic == ESPLoader.ESP_IMAGE_MAGIC: - return ESPFirmwareImage(f) - elif magic == ESPBOOTLOADER.IMAGE_V2_MAGIC: - return OTAFirmwareImage(f) - else: - raise FatalError("Invalid image magic number: %d" % magic) - - -class ImageSegment(object): - """ Wrapper class for a segment in an ESP image - (very similar to a section in an ELFImage also) """ - def __init__(self, addr, data, file_offs=None): - self.addr = addr - # pad all ImageSegments to at least 4 bytes length - pad_mod = len(data) % 4 - if pad_mod != 0: - data += "\x00" * (4 - pad_mod) - self.data = data - self.file_offs = file_offs - self.include_in_checksum = True - - def copy_with_new_addr(self, new_addr): - """ Return a new ImageSegment with same data, but mapped at - a new address. """ - return ImageSegment(new_addr, self.data, 0) - - def __repr__(self): - r = "len 0x%05x load 0x%08x" % (len(self.data), self.addr) - if self.file_offs is not None: - r += " file_offs 0x%08x" % (self.file_offs) - return r - - -class ELFSection(ImageSegment): - """ Wrapper class for a section in an ELF image, has a section - name as well as the common properties of an ImageSegment. """ - def __init__(self, name, addr, data): - super(ELFSection, self).__init__(addr, data) - self.name = name - - def __repr__(self): - return "%s %s" % (self.name, super(ELFSection, self).__repr__()) - - -class BaseFirmwareImage(object): - SEG_HEADER_LEN = 8 - - """ Base class with common firmware image functions """ - def __init__(self): - self.segments = [] - self.entrypoint = 0 - - def load_common_header(self, load_file, expected_magic): - (magic, segments, self.flash_mode, self.flash_size_freq, self.entrypoint) = struct.unpack(' 16: - raise FatalError('Invalid firmware image magic=%d segments=%d' % (magic, segments)) - return segments - - def load_segment(self, f, is_irom_segment=False): - """ Load the next segment from the image file """ - file_offs = f.tell() - (offset, size) = struct.unpack(' 0x40200000 or offset < 0x3ffe0000 or size > 65536: - print('WARNING: Suspicious segment 0x%x, length %d' % (offset, size)) - - def save_segment(self, f, segment, checksum=None): - """ Save the next segment to the image file, return next checksum value if provided """ - f.write(struct.pack(' 0: - if len(irom_segments) != 1: - raise FatalError('Found %d segments that could be irom0. Bad ELF file?' % len(irom_segments)) - return irom_segments[0] - return None - - def get_non_irom_segments(self): - irom_segment = self.get_irom_segment() - return [s for s in self.segments if s != irom_segment] - - -class ESPFirmwareImage(BaseFirmwareImage): - """ 'Version 1' firmware image, segments loaded directly by the ROM bootloader. """ - - ROM_LOADER = ESP8266ROM - - def __init__(self, load_file=None): - super(ESPFirmwareImage, self).__init__() - self.flash_mode = 0 - self.flash_size_freq = 0 - self.version = 1 - - if load_file is not None: - segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) - - for _ in xrange(segments): - self.load_segment(load_file) - self.checksum = self.read_checksum(load_file) - - def default_output_name(self, input_file): - """ Derive a default output name from the ELF name. """ - return input_file + '-' - - def save(self, basename): - """ Save a set of V1 images for flashing. Parameter is a base filename. """ - # IROM data goes in its own plain binary file - irom_segment = self.get_irom_segment() - if irom_segment is not None: - with open("%s0x%05x.bin" % (basename, irom_segment.addr - ESP8266ROM.IROM_MAP_START), "wb") as f: - f.write(irom_segment.data) - - # everything but IROM goes at 0x00000 in an image file - normal_segments = self.get_non_irom_segments() - with open("%s0x00000.bin" % basename, 'wb') as f: - self.write_common_header(f, normal_segments) - checksum = ESPLoader.ESP_CHECKSUM_MAGIC - for segment in normal_segments: - checksum = self.save_segment(f, segment, checksum) - self.append_checksum(f, checksum) - - -class OTAFirmwareImage(BaseFirmwareImage): - """ 'Version 2' firmware image, segments loaded by software bootloader stub - (ie Espressif bootloader or rboot) - """ - - ROM_LOADER = ESP8266ROM - - def __init__(self, load_file=None): - super(OTAFirmwareImage, self).__init__() - self.version = 2 - if load_file is not None: - segments = self.load_common_header(load_file, ESPBOOTLOADER.IMAGE_V2_MAGIC) - if segments != ESPBOOTLOADER.IMAGE_V2_SEGMENT: - # segment count is not really segment count here, but we expect to see '4' - print 'Warning: V2 header has unexpected "segment" count %d (usually 4)' % segments - - # irom segment comes before the second header - # - # the file is saved in the image with a zero load address - # in the header, so we need to calculate a load address - irom_segment = self.load_segment(load_file, True) - # for actual mapped addr, add ESP8266ROM.IROM_MAP_START + flashing_Addr + 8 - irom_segment.addr = 0 - irom_segment.include_in_checksum = False - - first_flash_mode = self.flash_mode - first_flash_size_freq = self.flash_size_freq - first_entrypoint = self.entrypoint - # load the second header - - segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) - - if first_flash_mode != self.flash_mode: - print('WARNING: Flash mode value in first header (0x%02x) disagrees with second (0x%02x). Using second value.' - % (first_flash_mode, self.flash_mode)) - if first_flash_size_freq != self.flash_size_freq: - print('WARNING: Flash size/freq value in first header (0x%02x) disagrees with second (0x%02x). Using second value.' - % (first_flash_size_freq, self.flash_size_freq)) - if first_entrypoint != self.entrypoint: - print('WARNING: Entrypoint address in first header (0x%08x) disagrees with second header (0x%08x). Using second value.' - % (first_entrypoint, self.entrypoint)) - - # load all the usual segments - for _ in xrange(segments): - self.load_segment(load_file) - self.checksum = self.read_checksum(load_file) - - def default_output_name(self, input_file): - """ Derive a default output name from the ELF name. """ - irom_segment = self.get_irom_segment() - if irom_segment is not None: - irom_offs = irom_segment.addr - ESP8266ROM.IROM_MAP_START - else: - irom_offs = 0 - return "%s-0x%05x.bin" % (os.path.splitext(input_file)[0], - irom_offs & ~(ESPLoader.ESP_FLASH_SECTOR - 1)) - - def save(self, filename): - with open(filename, 'wb') as f: - # Save first header for irom0 segment - f.write(struct.pack(' 0: - null = ImageSegment(0, '\x00' * pad_len, f.tell()) - checksum = self.save_segment(f, null, checksum) - padding_segments += 1 - # verify that after the 8 byte header is added, were are at the correct offset relative to the segment's vaddr - assert (f.tell() + 8) % IROM_ALIGN == segment.addr % IROM_ALIGN - checksum = self.save_segment(f, segment, checksum) - self.append_checksum(f, checksum) - # kinda hacky: go back to the initial header and write the new segment count - # that includes padding segments. Luckily(?) this header is not checksummed - f.seek(1) - f.write(chr(len(self.segments) + padding_segments)) - - -class ELFFile(object): - SEC_TYPE_PROGBITS = 0x01 - SEC_TYPE_STRTAB = 0x03 - - def __init__(self, name): - # Load sections from the ELF file - self.name = name - self.symbols = None - with open(self.name, 'rb') as f: - self._read_elf_file(f) - - def get_section(self, section_name): - for s in self.sections: - if s.name == section_name: - return s - raise ValueError("No section %s in ELF file" % section_name) - - def _read_elf_file(self, f): - # read the ELF file header - LEN_FILE_HEADER = 0x34 - try: - (ident,_type,machine,_version, - self.entrypoint,_phoff,shoff,_flags, - _ehsize, _phentsize,_phnum,_shentsize, - _shnum,shstrndx) = struct.unpack("<16sHHLLLLLHHHHHH", f.read(LEN_FILE_HEADER)) - except struct.error as e: - raise FatalError("Failed to read a valid ELF header from %s: %s" % (self.name, e)) - - if ident[0] != '\x7f' or ident[1:4] != 'ELF': - raise FatalError("%s has invalid ELF magic header" % self.name) - if machine != 0x5e: - raise FatalError("%s does not appear to be an Xtensa ELF file. e_machine=%04x" % (self.name, machine)) - self._read_sections(f, shoff, shstrndx) - - def _read_sections(self, f, section_header_offs, shstrndx): - f.seek(section_header_offs) - section_header = f.read() - LEN_SEC_HEADER = 0x28 - if len(section_header) == 0: - raise FatalError("No section header found at offset %04x in ELF file." % section_header_offs) - if len(section_header) % LEN_SEC_HEADER != 0: - print 'WARNING: Unexpected ELF section header length %04x is not mod-%02x' % (len(section_header),LEN_SEC_HEADER) - - # walk through the section header and extract all sections - section_header_offsets = range(0, len(section_header), LEN_SEC_HEADER) - - def read_section_header(offs): - name_offs,sec_type,_flags,lma,sec_offs,size = struct.unpack_from(", ) or a single -# argument. - - -def load_ram(esp, args): - image = LoadFirmwareImage(esp, args.filename) - - print 'RAM boot...' - for (offset, size, data) in image.segments: - print 'Downloading %d bytes at %08x...' % (size, offset), - sys.stdout.flush() - esp.mem_begin(size, div_roundup(size, esp.ESP_RAM_BLOCK), esp.ESP_RAM_BLOCK, offset) - - seq = 0 - while len(data) > 0: - esp.mem_block(data[0:esp.ESP_RAM_BLOCK], seq) - data = data[esp.ESP_RAM_BLOCK:] - seq += 1 - print 'done!' - - print 'All segments done, executing at %08x' % image.entrypoint - esp.mem_finish(image.entrypoint) - - -def read_mem(esp, args): - print '0x%08x = 0x%08x' % (args.address, esp.read_reg(args.address)) - - -def write_mem(esp, args): - esp.write_reg(args.address, args.value, args.mask, 0) - print 'Wrote %08x, mask %08x to %08x' % (args.value, args.mask, args.address) - - -def dump_mem(esp, args): - f = file(args.filename, 'wb') - for i in xrange(args.size / 4): - d = esp.read_reg(args.address + (i * 4)) - f.write(struct.pack(' flash_end: - raise FatalError(("File %s (length %d) at offset %d will not fit in %d bytes of flash. " + - "Use --flash-size argument, or change flashing address.") - % (argfile.name, argfile.tell(), address, flash_end)) - argfile.seek(0) - - for address, argfile in args.addr_filename: - print 'Erasing flash...' - image = argfile.read() - # Update header with flash parameters - if address == 0 and image[0] == '\xe9': - image = image[0:2] + flash_info + image[4:] - calcmd5 = hashlib.md5(image).hexdigest() - uncsize = len(image) - if args.compress: - uncimage = image - image = zlib.compress(uncimage, 9) - blocks = div_roundup(len(image), esp.FLASH_WRITE_SIZE) - esp.flash_defl_begin(len(uncimage),len(image), address) - else: - blocks = div_roundup(len(image), esp.FLASH_WRITE_SIZE) - esp.flash_begin(blocks * esp.FLASH_WRITE_SIZE, address) - argfile.seek(0) # in case we need it again - seq = 0 - written = 0 - t = time.time() - header_block = None - while len(image) > 0: - print '\rWriting at 0x%08x... (%d %%)' % (address + seq * esp.FLASH_WRITE_SIZE, 100 * (seq + 1) / blocks), - sys.stdout.flush() - block = image[0:esp.FLASH_WRITE_SIZE] - if args.compress: - esp.flash_defl_block(block, seq) - else: - # Pad the last block - block = block + '\xff' * (esp.FLASH_WRITE_SIZE - len(block)) - esp.flash_block(block, seq) - image = image[esp.FLASH_WRITE_SIZE:] - seq += 1 - written += len(block) - t = time.time() - t - speed_msg = "" - if args.compress: - if t > 0.0: - speed_msg = " (effective %.1f kbit/s)" % (uncsize / t * 8 / 1000) - print '\rWrote %d bytes (%d compressed) at 0x%08x in %.1f seconds%s...' % (uncsize, written, address, t, speed_msg) - else: - if t > 0.0: - speed_msg = " (%.1f kbit/s)" % (written / t * 8 / 1000) - print '\rWrote %d bytes at 0x%08x in %.1f seconds%s...' % (written, address, t, speed_msg) - res = esp.flash_md5sum(address, uncsize) - if res != calcmd5: - print 'File md5: %s' % calcmd5 - print 'Flash md5: %s' % res - raise FatalError("MD5 of file does not match data in flash!") - else: - print 'Hash of data verified.' - print '\nLeaving...' - if args.flash_mode == 'dio' and esp.CHIP_NAME == "ESP8266": - esp.flash_unlock_dio() - else: - esp.flash_begin(0, 0) - if args.compress: - esp.flash_defl_finish(False) - else: - esp.flash_finish(False) - if args.verify: - print 'Verifying just-written flash...' - verify_flash(esp, args, header_block) - - -def image_info(args): - image = LoadFirmwareImage(args.chip, args.filename) - print('Image version: %d' % image.version) - print('Entry point: %08x' % image.entrypoint) if image.entrypoint != 0 else 'Entry point not set' - print '%d segments' % len(image.segments) - print - idx = 0 - for seg in image.segments: - idx += 1 - print 'Segment %d: %r' % (idx, seg) - calc_checksum = image.calculate_checksum() - print 'Checksum: %02x (%s)' % (image.checksum, - 'valid' if image.checksum == calc_checksum else 'invalid - calculated %02x' % calc_checksum) - - -def make_image(args): - image = ESPFirmwareImage() - if len(args.segfile) == 0: - raise FatalError('No segments specified') - if len(args.segfile) != len(args.segaddr): - raise FatalError('Number of specified files does not match number of specified addresses') - for (seg, addr) in zip(args.segfile, args.segaddr): - data = file(seg, 'rb').read() - image.segments.append(ImageSegment(addr, data)) - image.entrypoint = args.entrypoint - image.save(args.output) - - -def elf2image(args): - e = ELFFile(args.input) - if args.chip == 'auto': # Default to ESP8266 for backwards compatibility - print "Creating image for ESP8266..." - args.chip == 'esp8266' - - if args.chip == 'esp32': - image = ESP32FirmwareImage() - elif args.version == '1': # ESP8266 - image = ESPFirmwareImage() - else: - image = OTAFirmwareImage() - image.entrypoint = e.entrypoint - image.segments = e.sections # ELFSection is a subclass of ImageSegment - image.flash_mode = {'qio':0, 'qout':1, 'dio':2, 'dout': 3}[args.flash_mode] - image.flash_size_freq = image.ROM_LOADER.FLASH_SIZES[args.flash_size] - image.flash_size_freq += {'40m':0, '26m':1, '20m':2, '80m': 0xf}[args.flash_freq] - - if args.output is None: - args.output = image.default_output_name(args.input) - image.save(args.output) - - -def read_mac(esp, args): - mac = esp.read_mac() - print 'MAC: %s' % ':'.join(map(lambda x: '%02x' % x, mac)) - - -def chip_id(esp, args): - chipid = esp.chip_id() - print 'Chip ID: 0x%08x' % chipid - - -def erase_flash(esp, args): - print 'Erasing flash (this may take a while)...' - t = time.time() - esp.erase_flash() - print 'Chip erase completed successfully in %.1fs' % (time.time() - t) - - -def erase_region(esp, args): - print 'Erasing region (may be slow depending on size)...' - t = time.time() - esp.erase_region(args.address, args.size) - print 'Erase completed successfully in %.1f seconds.' % (time.time() - t) - - -def run(esp, args): - esp.run() - - -def flash_id(esp, args): - flash_id = esp.flash_id() - print 'Manufacturer: %02x' % (flash_id & 0xff) - print 'Device: %02x%02x' % ((flash_id >> 8) & 0xff, (flash_id >> 16) & 0xff) - - -def read_flash(esp, args): - if args.no_progress: - flash_progress = None - else: - def flash_progress(progress, length): - msg = '%d (%d %%)' % (progress, progress * 100.0 / length) - padding = '\b' * len(msg) - if progress == length: - padding = '\n' - sys.stdout.write(msg + padding) - sys.stdout.flush() - t = time.time() - data = esp.read_flash(args.address, args.size, flash_progress) - t = time.time() - t - print ('\rRead %d bytes at 0x%x in %.1f seconds (%.1f kbit/s)...' - % (len(data), args.address, t, len(data) / t * 8 / 1000)) - file(args.filename, 'wb').write(data) - - -def verify_flash(esp, args, flash_params=None): - differences = False - for address, argfile in args.addr_filename: - image = argfile.read() - argfile.seek(0) # rewind in case we need it again - if address == 0 and image[0] == '\xe9' and flash_params is not None: - image = image[0:2] + flash_params + image[4:] - image_size = len(image) - print 'Verifying 0x%x (%d) bytes @ 0x%08x in flash against %s...' % (image_size, image_size, address, argfile.name) - # Try digest first, only read if there are differences. - digest = esp.flash_md5sum(address, image_size) - expected_digest = hashlib.md5(image).hexdigest() - if digest == expected_digest: - print '-- verify OK (digest matched)' - continue - else: - differences = True - if getattr(args, 'diff', 'no') != 'yes': - print '-- verify FAILED (digest mismatch)' - continue - - flash = esp.read_flash(address, image_size) - assert flash != image - diff = [i for i in xrange(image_size) if flash[i] != image[i]] - print '-- verify FAILED: %d differences, first @ 0x%08x' % (len(diff), address + diff[0]) - for d in diff: - print ' %08x %02x %02x' % (address + d, ord(flash[d]), ord(image[d])) - if differences: - raise FatalError("Verify failed.") - - -def read_flash_status(esp, args): - print ('Status value: 0x%04x' % esp.read_status(args.bytes)) - - -def write_flash_status(esp, args): - fmt = "0x%%0%dx" % (args.bytes * 2) - args.value = args.value & ((1 << (args.bytes * 8)) - 1) - print (('Initial flash status: ' + fmt) % esp.read_status(args.bytes)) - print (('Setting flash status: ' + fmt) % args.value) - esp.write_status(args.value, args.bytes, args.non_volatile) - print (('After flash status: ' + fmt) % esp.read_status(args.bytes)) - - -def version(args): - print __version__ - - -def wrap_stub(args): - e = ELFFile(args.input) - - stub = { - 'params_start': e.get_symbol_addr('_params_start'), - 'code': e.get_section('.text').data, - 'code_start': e.get_symbol_addr('_code_start'), - 'entry': e.get_symbol_addr(args.entry), - } - try: - stub['data'] = e.get_section('.data').data - stub['data_start'] = e.get_symbol_addr('_data_start') - except ValueError: - pass - # No data section, that's fine - bss_size, bss_start = 0, 0 - try: - bss_start = e.get_symbol_addr('_bss_start') - bss_size = e.get_symbol_addr('_bss_end') - bss_start - except ValueError: - pass - params_len = e.get_symbol_addr('_params_end') - stub['params_start'] - if params_len % 4 != 0: - raise FatalError('Params must be dwords') - stub['num_params'] = params_len / 4 - - # Pad code with NOPs to mod 4. - if len(stub['code']) % 4 != 0: - stub['code'] += (4 - (len(stub['code']) % 4)) * '\0' - - print >>sys.stderr, ( - 'Stub params: %d @ 0x%08x, code: %d @ 0x%08x, bss: %d @ 0x%08x, data: %d @ 0x%08x, entry: %s @ 0x%x' % ( - params_len, stub['params_start'], - len(stub['code']), stub['code_start'], - bss_size, bss_start, - len(stub.get('data', '')), stub.get('data_start', 0), - args.entry, stub['entry'])) - - jstub = dict(stub) - jstub['code'] = hexify(stub['code']) - if 'data' in stub: - jstub['data'] = hexify(stub['data']) - print json.dumps(jstub) - - -def run_stub(esp, args): - esp.run_custom_stub(json.load(open(args.input)), args.params, read_output=True) - -# -# End of operations functions -# - - -def main(): - parser = argparse.ArgumentParser(description='esptool.py v%s - ESP8266 ROM Bootloader Utility' % __version__, prog='esptool') - - parser.add_argument('--chip', '-c', - help='Target chip type', - choices=['auto', 'esp8266', 'esp31', 'esp32'], - default=os.environ.get('ESPTOOL_CHIP', 'auto')) - - parser.add_argument( - '--port', '-p', - help='Serial port device', - default=os.environ.get('ESPTOOL_PORT', ESPLoader.DEFAULT_PORT)) - - parser.add_argument( - '--baud', '-b', - help='Serial port baud rate used when flashing/reading', - type=arg_auto_int, - default=os.environ.get('ESPTOOL_BAUD', ESPLoader.ESP_ROM_BAUD)) - - parser.add_argument( - '--no-stub', - help="Disable launching the flasher stub, only talk to ROM bootloader. Some features will not be available.", - action='store_true') - - subparsers = parser.add_subparsers( - dest='operation', - help='Run esptool {command} -h for additional help') - - parser_load_ram = subparsers.add_parser( - 'load_ram', - help='Download an image to RAM and execute') - parser_load_ram.add_argument('filename', help='Firmware image') - - parser_dump_mem = subparsers.add_parser( - 'dump_mem', - help='Dump arbitrary memory to disk') - parser_dump_mem.add_argument('address', help='Base address', type=arg_auto_int) - parser_dump_mem.add_argument('size', help='Size of region to dump', type=arg_auto_int) - parser_dump_mem.add_argument('filename', help='Name of binary dump') - - parser_read_mem = subparsers.add_parser( - 'read_mem', - help='Read arbitrary memory location') - parser_read_mem.add_argument('address', help='Address to read', type=arg_auto_int) - - parser_write_mem = subparsers.add_parser( - 'write_mem', - help='Read-modify-write to arbitrary memory location') - parser_write_mem.add_argument('address', help='Address to write', type=arg_auto_int) - parser_write_mem.add_argument('value', help='Value', type=arg_auto_int) - parser_write_mem.add_argument('mask', help='Mask of bits to write', type=arg_auto_int) - - def add_spi_flash_subparsers(parent): - """ Add common parser arguments for SPI flash properties """ - parent.add_argument('--flash_freq', '-ff', help='SPI Flash frequency', - choices=['40m', '26m', '20m', '80m'], - default=os.environ.get('ESPTOOL_FF', '40m')) - parent.add_argument('--flash_mode', '-fm', help='SPI Flash mode', - choices=['qio', 'qout', 'dio', 'dout'], - default=os.environ.get('ESPTOOL_FM', 'qio')) - parent.add_argument('--flash_size', '-fs', help='SPI Flash size in MegaBytes (1MB, 2MB, 4MB, 8MB, 16M)' - ' plus ESP8266-only (256KB, 512KB, 2MB-c1, 4MB-c1, 4MB-2)', - action=FlashSizeAction, - default=os.environ.get('ESPTOOL_FS', '1MB')) - parent.add_argument('--ucIsHspi', '-ih', help='Config SPI PORT/PINS (Espressif internal feature)',action='store_true') - parent.add_argument('--ucIsLegacy', '-il', help='Config SPI LEGACY (Espressif internal feature)',action='store_true') - - parser_write_flash = subparsers.add_parser( - 'write_flash', - help='Write a binary blob to flash') - parser_write_flash.add_argument('addr_filename', metavar='
', help='Address followed by binary filename, separated by space', - action=AddrFilenamePairAction) - add_spi_flash_subparsers(parser_write_flash) - parser_write_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") - parser_write_flash.add_argument('--verify', help='Verify just-written data (only necessary if very cautious, data is already CRCed', action='store_true') - parser_write_flash.add_argument('--compress', '-z', help='Compress data in transfer',action="store_true") - - subparsers.add_parser( - 'run', - help='Run application code in flash') - - parser_image_info = subparsers.add_parser( - 'image_info', - help='Dump headers from an application image') - parser_image_info.add_argument('filename', help='Image file to parse') - - parser_make_image = subparsers.add_parser( - 'make_image', - help='Create an application image from binary files') - parser_make_image.add_argument('output', help='Output image file') - parser_make_image.add_argument('--segfile', '-f', action='append', help='Segment input file') - parser_make_image.add_argument('--segaddr', '-a', action='append', help='Segment base address', type=arg_auto_int) - parser_make_image.add_argument('--entrypoint', '-e', help='Address of entry point', type=arg_auto_int, default=0) - - parser_elf2image = subparsers.add_parser( - 'elf2image', - help='Create an application image from ELF file') - parser_elf2image.add_argument('input', help='Input ELF file') - parser_elf2image.add_argument('--output', '-o', help='Output filename prefix (for version 1 image), or filename (for version 2 single image)', type=str) - parser_elf2image.add_argument('--version', '-e', help='Output image version', choices=['1','2'], default='1') - add_spi_flash_subparsers(parser_elf2image) - - subparsers.add_parser( - 'read_mac', - help='Read MAC address from OTP ROM') - - subparsers.add_parser( - 'chip_id', - help='Read Chip ID from OTP ROM') - - subparsers.add_parser( - 'flash_id', - help='Read SPI flash manufacturer and device ID') - - parser_read_status = subparsers.add_parser( - 'read_flash_status', - help='Read SPI flash status register') - - parser_read_status.add_argument('--bytes', help='Number of bytes to read (1-3)', type=int, choices=[1,2,3], default=2) - - parser_write_status = subparsers.add_parser( - 'write_flash_status', - help='Write SPI flash status register') - - parser_write_status.add_argument('--non-volatile', help='Write non-volatile bits (use with caution)', action='store_true') - parser_write_status.add_argument('--bytes', help='Number of status bytes to write (1-3)', type=int, choices=[1,2,3], default=2) - parser_write_status.add_argument('value', help='New value', type=arg_auto_int) - - parser_read_flash = subparsers.add_parser( - 'read_flash', - help='Read SPI flash content') - parser_read_flash.add_argument('address', help='Start address', type=arg_auto_int) - parser_read_flash.add_argument('size', help='Size of region to dump', type=arg_auto_int) - parser_read_flash.add_argument('filename', help='Name of binary dump') - parser_read_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") - - parser_verify_flash = subparsers.add_parser( - 'verify_flash', - help='Verify a binary blob against flash') - parser_verify_flash.add_argument('addr_filename', help='Address and binary file to verify there, separated by space', - action=AddrFilenamePairAction) - parser_verify_flash.add_argument('--diff', '-d', help='Show differences', - choices=['no', 'yes'], default='no') - - subparsers.add_parser( - 'erase_flash', - help='Perform Chip Erase on SPI flash') - - parser_erase_region = subparsers.add_parser( - 'erase_region', - help='Erase a region of the flash') - parser_erase_region.add_argument('address', help='Start address (must be multiple of 4096)', type=arg_auto_int) - parser_erase_region.add_argument('size', help='Size of region to erase (must be multiple of 4096)', type=arg_auto_int) - - subparsers.add_parser( - 'version', help='Print esptool version') - - parser_wrap_stub = subparsers.add_parser('wrap_stub', help='Wrap stub and output a JSON object') - parser_wrap_stub.add_argument('input') - parser_wrap_stub.add_argument('--entry', default='stub_main') - - parser_run_stub = subparsers.add_parser('run_stub', help='Run stub on a device') - parser_run_stub.add_argument('--entry', default='stub_main') - parser_run_stub.add_argument('input') - parser_run_stub.add_argument('params', nargs='*', type=arg_auto_int) - - # internal sanity check - every operation matches a module function of the same name - for operation in subparsers.choices.keys(): - assert operation in globals(), "%s should be a module function" % operation - - args = parser.parse_args() - - # operation function can take 1 arg (args), 2 args (esp, arg) - # or be a member function of the ESPLoader class. - - operation_func = globals()[args.operation] - operation_args,_,_,_ = inspect.getargspec(operation_func) - if operation_args[0] == 'esp': # operation function takes an ESPLoader connection object - initial_baud = min(ESPLoader.ESP_ROM_BAUD, args.baud) # don't sync faster than the default baud rate - chip_constructor_fun = { - 'auto': ESPLoader.detect_chip, - 'esp8266': ESP8266ROM, - 'esp32': ESP32ROM, - }[args.chip] - esp = chip_constructor_fun(args.port, initial_baud) - - if not args.no_stub and args.operation != 'run_stub': - esp = esp.run_stub() - - if args.baud > initial_baud: - esp.change_baud(args.baud) - # TODO: handle a NotImplementedInROMError - - # override common SPI flash parameter stuff as required - if hasattr(args, "ucIsHspi"): - print "Attaching SPI flash..." - esp.flash_spi_attach(args.ucIsHspi,args.ucIsLegacy) - else: - esp.flash_spi_attach(0, 0) - if hasattr(args, "flash_size"): - print "Configuring flash size..." - esp.flash_set_parameters(flash_size_bytes(args.flash_size)) - - operation_func(esp, args) - else: - operation_func(args) - - -class FlashSizeAction(argparse.Action): - """ Custom flash size parser class to support backwards compatibility with megabit size arguments. - - (At next major relase, remove deprecated sizes and this can become a 'normal' choices= argument again.) - """ - def __init__(self, option_strings, dest, nargs=1, **kwargs): - super(FlashSizeAction, self).__init__(option_strings, dest, nargs, **kwargs) - - def __call__(self, parser, namespace, values, option_string=None): - try: - value = { - '2m': '256KB', - '4m': '512KB', - '8m': '1MB', - '16m': '2MB', - '32m': '4MB', - '16m-c1': '2MB-c1', - '32m-c1': '4MB-c1', - '32m-c2': '4MB-c2' - }[values[0]] - print("WARNING: Flash size arguments in megabits like '%s' are deprecated." % (values[0])) - print("Please use the equivalent size '%s'." % (value)) - print("Megabit arguments may be removed in a future release.") - except KeyError: - value = values[0] - - known_sizes = dict(ESP8266ROM.FLASH_SIZES) - known_sizes.update(ESP32ROM.FLASH_SIZES) - if value not in known_sizes: - raise argparse.ArgumentError(self, '%s is not a known flash size. Known sizes: %s' % (value, ", ".join(known_sizes.keys()))) - setattr(namespace, self.dest, value) - - -class AddrFilenamePairAction(argparse.Action): - """ Custom parser class for the address/filename pairs passed as arguments """ - def __init__(self, option_strings, dest, nargs='+', **kwargs): - super(AddrFilenamePairAction, self).__init__(option_strings, dest, nargs, **kwargs) - - def __call__(self, parser, namespace, values, option_string=None): - # validate pair arguments - pairs = [] - for i in range(0,len(values),2): - try: - address = int(values[i],0) - except ValueError as e: - raise argparse.ArgumentError(self,'Address "%s" must be a number' % values[i]) - try: - argfile = open(values[i + 1], 'rb') - except IOError as e: - raise argparse.ArgumentError(self, e) - except IndexError: - raise argparse.ArgumentError(self,'Must be pairs of an address and the binary filename to write there') - pairs.append((address, argfile)) - setattr(namespace, self.dest, pairs) - -# Binary stub code (see flasher_stub dir for source & details) -ESP8266ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" -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\ -"""))) -ESP32ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" -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\ -"""))) - -if __name__ == '__main__': - try: - main() - except FatalError as e: - print '\nA fatal error occurred: %s' % e - sys.exit(2) diff --git a/src/mongoose-6.11/src/common/platforms/esp/slip.c b/src/mongoose-6.11/src/common/platforms/esp/slip.c deleted file mode 100644 index 13b980e..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp/slip.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "rom_functions.h" - -void SLIP_send(uint8_t *pkt, uint32_t size) { - send_packet(pkt, size); -} - -uint32_t SLIP_recv(void *pkt, uint32_t max_len) { - uint8_t c; - uint32_t len = 0; - uint8_t *p = (uint8_t *) pkt; - do { - c = uart_rx_one_char_block(); - } while (c != '\xc0'); - while (len < max_len) { - c = uart_rx_one_char_block(); - if (c == '\xc0') return len; - if (c == '\xdb') { - c = uart_rx_one_char_block(); - if (c == '\xdc') { - c = '\xc0'; - } else if (c == '\xdd') { - c = '\xdb'; - } else { - len = 0; - break; /* Bad esc sequence. */ - } - } - *p++ = c; - len++; - } - do { - c = uart_rx_one_char_block(); - } while (c != '\xc0'); - return len; -} diff --git a/src/mongoose-6.11/src/common/platforms/esp/slip.h b/src/mongoose-6.11/src/common/platforms/esp/slip.h deleted file mode 100644 index 53100a0..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp/slip.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef CS_COMMON_PLATFORMS_ESP8266_STUBS_SLIP_H_ -#define CS_COMMON_PLATFORMS_ESP8266_STUBS_SLIP_H_ - -#include - -void SLIP_send(const void *pkt, uint32_t size); -uint32_t SLIP_recv(void *pkt, uint32_t max_len); - -#endif /* CS_COMMON_PLATFORMS_ESP8266_STUBS_SLIP_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp/stub_flasher.c b/src/mongoose-6.11/src/common/platforms/esp/stub_flasher.c deleted file mode 100644 index be7398c..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp/stub_flasher.c +++ /dev/null @@ -1,559 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - * - * Spiffy flasher. Implements strong checksums (MD5) and can use higher - * baud rates. Actual max baud rate will differe from device to device, - * but 921K seems to be common. - * - * SLIP protocol is used for communication. - * First packet is a single byte - command number. - * After that, a packet with a variable number of 32-bit (LE) arguments, - * depending on command. - * - * Then command produces variable number of packets of output, but first - * packet of length 1 is the response code: 0 for success, non-zero - error. - * - * See individual command description below. - */ - -#include "stub_flasher.h" - -#include -#include - -#include "rom_functions.h" - -#if defined(ESP8266) -#include "eagle_soc.h" -#include "ets_sys.h" -#include "../../../miniz.c" -#elif defined(ESP32) -#include "rom/efuse.h" -#include "rom/miniz.h" -#include "rom/spi_flash.h" -#include "soc/uart_reg.h" -#include "led.h" -#endif - -#include "slip.h" -#include "uart.h" - -/* Param: baud rate. */ -uint32_t params[1] __attribute__((section(".params"))); - -#define FLASH_BLOCK_SIZE 65536 -#define FLASH_SECTOR_SIZE 4096 -#define FLASH_PAGE_SIZE 256 - -/* These consts should be in sync with flasher_client.go */ -#define NUM_BUFS 4 -#define BUF_SIZE 4096 -#define FLASH_WRITE_SIZE BUF_SIZE - -#define UART_RX_INTS (UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA) - -extern uint32_t _bss_start, _bss_end; - -#ifdef ESP8266 -#define REG_SPI_BASE(i) (0x60000200 - i * 0x100) - -#define SPI_CMD_REG(i) (REG_SPI_BASE(i) + 0x0) -#define SPI_FLASH_WREN (BIT(30)) -#define SPI_FLASH_RDID (BIT(28)) -#define SPI_FLASH_SE (BIT(24)) -#define SPI_FLASH_BE (BIT(23)) - -#define SPI_ADDR_REG(i) (REG_SPI_BASE(i) + 0x4) - -#define SPI_USER_REG(i) (REG_SPI_BASE(i) + 0x1C) - -#define SPI_W0_REG(i) (REG_SPI_BASE(i) + 0x40) -#endif - -enum read_state { - READ_WAIT_START = 0, - READ_FLAGS, - READ_DATA, - READ_UNESCAPE, - READ_ERROR, -}; - -struct data_buf { - uint32_t len; - uint8_t data[BUF_SIZE]; - uint8_t flags; -}; - -#define FLAG_COMPRESSED 1 - -struct uart_buf { - enum read_state state; - struct data_buf bufs[NUM_BUFS]; - uint32_t bri, bwi; - uint32_t ps; -}; - -static inline uint32_t ccount(void) { - uint32_t r; - __asm volatile("rsr.ccount %0" : "=a"(r)); - return r; -} - -struct write_progress { - uint32_t num_written; - uint32_t buf_level; - uint8_t digest[16]; -}; - -struct write_result { - uint32_t wait_time; - uint32_t decomp_time; - uint32_t write_time; - uint32_t erase_time; - uint32_t total_time; - uint8_t digest[16]; -}; - -static struct uart_buf ub; -static uint32_t inflate_buf[TINFL_LZ_DICT_SIZE / sizeof(uint32_t)]; - -static void next_write_buf(void) { - ub.bwi++; - if (ub.bwi == NUM_BUFS) ub.bwi = 0; - ub.bufs[ub.bwi].len = 0; - ub.bufs[ub.bwi].flags = 0; -} - -static void add_byte(uint8_t byte) { - struct data_buf *buf = &ub.bufs[ub.bwi]; - if (buf->len < BUF_SIZE) { - buf->data[buf->len++] = byte; - ub.ps++; - } -} - -void uart_isr(void *arg) { - uint32_t int_st = READ_PERI_REG(UART_INT_ST_REG(0)); - uint8_t fifo_len, i; - while ((fifo_len = READ_PERI_REG(UART_STATUS_REG(0))) > 0) { - for (i = 0; i < fifo_len; i++) { - uint8_t byte = READ_PERI_REG(UART_FIFO_REG(0)); - switch (ub.state) { - case READ_WAIT_START: { - if (byte == 0xc0) { - ub.state = READ_FLAGS; - ub.ps = 0; - } - break; - } - case READ_FLAGS: - case READ_DATA: { - struct data_buf *buf = &ub.bufs[ub.bwi]; - if (byte == 0xdb) { - ub.state = READ_UNESCAPE; - } else if (byte == 0xc0) { - next_write_buf(); - if (ub.ps == 0) { - /* Empty packet, sender aborted. */ - ub.state = READ_ERROR; - SET_PERI_REG_MASK(UART_INT_ENA_REG(0), 0); - goto out; - } else { - ub.state = READ_WAIT_START; - } - } else { - if (ub.state == READ_FLAGS) { - buf->flags = byte; - ub.state = READ_DATA; - } else { - add_byte(byte); - } - } - break; - } - case READ_UNESCAPE: { - if (byte == 0xdc) { - byte = 0xc0; - } else if (byte == 0xdd) { - byte = 0xdb; - } else { - ub.state = READ_ERROR; - SET_PERI_REG_MASK(UART_INT_ENA_REG(0), 0); - goto out; - } - add_byte(byte); - ub.state = READ_DATA; - break; - } - case READ_ERROR: { - goto out; - } - } - } - } -out: - WRITE_PERI_REG(UART_INT_CLR_REG(0), int_st); - (void) arg; -} - -size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, - const void *pSrc_buf, size_t src_buf_len, - int flags); -#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) -#define TINFL_FLAG_PARSE_ZLIB_HEADER 1 - -#if defined(ESP8266) -int esp_rom_spiflash_erase_start(uint32_t addr, uint32_t cmd) { - SPI_write_enable(flashchip); - WRITE_PERI_REG(SPI_ADDR_REG(0), addr); - WRITE_PERI_REG(SPI_CMD_REG(0), cmd); - while (READ_PERI_REG(SPI_CMD_REG(0)) & cmd) { - } - return 0; -} -#elif defined(ESP32) -extern esp_rom_spiflash_chip_t g_rom_spiflash_chip; - -esp_rom_spiflash_result_t esp_rom_spiflash_erase_start(uint32_t addr, - uint32_t cmd) { - esp_rom_spiflash_chip_t *spi = &g_rom_spiflash_chip; - esp_rom_spiflash_wait_idle(spi); - - REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_DUMMY); - REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_ADDR_BITLEN, - ESP_ROM_SPIFLASH_W_SIO_ADDR_BITSLEN); - - WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, SPI_FLASH_WREN); - while (READ_PERI_REG(PERIPHS_SPI_FLASH_CMD) != 0) { - } - - WRITE_PERI_REG(PERIPHS_SPI_FLASH_ADDR, addr); - WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, cmd); - - return ESP_ROM_SPIFLASH_RESULT_OK; -} -#endif - -int do_flash_write(uint32_t addr, uint32_t len, uint32_t erase) { - int ret = 0; - uint32_t num_erased = 0; - struct MD5Context ctx; - MD5Init(&ctx); - - if (addr % FLASH_SECTOR_SIZE != 0) return 0x32; - if (len % FLASH_SECTOR_SIZE != 0) return 0x33; - if (esp_rom_spiflash_unlock() != 0) return 0x34; - - memset(&ub, 0, sizeof(ub)); - memset(&inflate_buf, 0, sizeof(inflate_buf)); - ets_isr_attach(ETS_UART0_INUM, uart_isr, &ub); - uint32_t saved_conf1 = READ_PERI_REG(UART_CONF1_REG(0)); - /* Reduce frequency of UART interrupts */ - WRITE_PERI_REG(UART_CONF1_REG(0), UART_RX_TOUT_EN | - (20 << UART_RX_TOUT_THRHD_S) | - (100 << UART_RXFIFO_FULL_THRHD_S)); - SET_PERI_REG_MASK(UART_INT_ENA_REG(0), UART_RX_INTS); - ets_isr_unmask(1 << ETS_UART0_INUM); - - struct write_result wr; - memset(&wr, 0, sizeof(wr)); - - struct write_progress wp = {.num_written = 0, .buf_level = 0}; - wp.buf_level = (uint32_t) &addr; - SLIP_send(&wp, sizeof(wp)); - wr.total_time = ccount(); - while (wp.num_written < len) { - /* Prepare the space ahead. */ - uint32_t start_count = ccount(); - while (erase && num_erased < wp.num_written + FLASH_WRITE_SIZE) { - const uint32_t num_left = (len - num_erased); - if (num_left >= FLASH_BLOCK_SIZE && addr % FLASH_BLOCK_SIZE == 0) { - if (esp_rom_spiflash_erase_start(addr, SPI_FLASH_BE) != 0) { - ret = 0x35; - goto out; - } - num_erased += FLASH_BLOCK_SIZE; - } else { - /* len % FLASH_SECTOR_SIZE == 0 is enforced, no further checks needed */ - if (esp_rom_spiflash_erase_start(addr, SPI_FLASH_SE) != 0) { - ret = 0x36; - goto out; - } - num_erased += FLASH_SECTOR_SIZE; - } - } - wr.erase_time += ccount() - start_count; - start_count = ccount(); - /* Wait for data to arrive. */ - wp.buf_level = 0; - for (int i = 0; i < NUM_BUFS; i++) wp.buf_level += ub.bufs[i].len; - volatile uint32_t *bwi = &ub.bwi; - while (*bwi == ub.bri && ub.state != READ_ERROR) { - } - struct data_buf *buf = &ub.bufs[ub.bri]; - if (ub.state == READ_ERROR) { - ret = 0x37; - goto out; - } - wr.wait_time += ccount() - start_count; - start_count = ccount(); - uint32_t *data = (uint32_t *) buf->data; - uint32_t write_len = buf->len; - if (buf->flags & FLAG_COMPRESSED) { - data = inflate_buf; - write_len = tinfl_decompress_mem_to_mem( - &inflate_buf[0], sizeof(inflate_buf), buf->data, write_len, - TINFL_FLAG_PARSE_ZLIB_HEADER); - if (write_len == TINFL_DECOMPRESS_MEM_TO_MEM_FAILED) { - ret = 0x40; - goto out; - } - } - wr.decomp_time += ccount() - start_count; - MD5Update(&ctx, (uint8_t *) data, write_len); - start_count = ccount(); - wr.erase_time += ccount() - start_count; - start_count = ccount(); - if (esp_rom_spiflash_write(addr, data, write_len) != 0) { - ret = 0x38; - goto out; - } - wr.write_time += ccount() - start_count; - buf->len = 0; - ub.bri++; - if (ub.bri == NUM_BUFS) ub.bri = 0; - addr += write_len; - wp.num_written += write_len; - struct MD5Context ctx2; - memcpy(&ctx2, &ctx, sizeof(ctx)); - MD5Final(wp.digest, &ctx2); - SLIP_send(&wp, sizeof(wp)); - } - - MD5Final(wr.digest, &ctx); - - wr.total_time = ccount() - wr.total_time; - SLIP_send(&wr, sizeof(wr)); - -out: - WRITE_PERI_REG(UART_CONF1_REG(0), saved_conf1); - ets_isr_mask(1 << ETS_UART0_INUM); - return ret; -} - -int do_flash_read(uint32_t addr, uint32_t len, uint32_t block_size, - uint32_t max_in_flight) { - uint8_t buf[FLASH_SECTOR_SIZE]; - uint8_t digest[16]; - struct MD5Context ctx; - uint32_t num_sent = 0, num_acked = 0; - if (block_size > sizeof(buf)) return 0x52; - MD5Init(&ctx); - while (num_acked < len) { - while (num_sent < len && num_sent - num_acked < max_in_flight) { - uint32_t n = len - num_sent; - if (n > block_size) n = block_size; - if (esp_rom_spiflash_read(addr, (uint32_t *) buf, n) != 0) return 0x53; - send_packet(buf, n); - MD5Update(&ctx, buf, n); - addr += n; - num_sent += n; - } - { - if (SLIP_recv(&num_acked, sizeof(num_acked)) != 4) return 0x54; - if (num_acked > num_sent) return 0x55; - } - } - MD5Final(digest, &ctx); - send_packet(digest, sizeof(digest)); - return 0; -} - -int do_flash_digest(uint32_t addr, uint32_t len, uint32_t digest_block_size) { - uint8_t buf[FLASH_SECTOR_SIZE]; - uint8_t digest[16]; - uint32_t read_block_size = - digest_block_size ? digest_block_size : sizeof(buf); - struct MD5Context ctx; - if (digest_block_size > sizeof(buf)) return 0x62; - MD5Init(&ctx); - while (len > 0) { - uint32_t n = len; - struct MD5Context block_ctx; - MD5Init(&block_ctx); - if (n > read_block_size) n = read_block_size; - if (esp_rom_spiflash_read(addr, (uint32_t *) buf, n) != 0) return 0x63; - MD5Update(&ctx, buf, n); - if (digest_block_size > 0) { - MD5Update(&block_ctx, buf, n); - MD5Final(digest, &block_ctx); - send_packet(digest, sizeof(digest)); - } - addr += n; - len -= n; - } - MD5Final(digest, &ctx); - send_packet(digest, sizeof(digest)); - return 0; -} - -int do_flash_read_chip_id(void) { - uint32_t chip_id = 0; - WRITE_PERI_REG(SPI_CMD_REG(0), SPI_FLASH_RDID); - while (READ_PERI_REG(SPI_CMD_REG(0)) & SPI_FLASH_RDID) { - } - chip_id = READ_PERI_REG(SPI_W0_REG(0)) & 0xFFFFFF; - send_packet((uint8_t *) &chip_id, sizeof(chip_id)); - return 0; -} - -uint8_t cmd_loop(void) { - uint8_t cmd = 0x55; - do { - /* Reset FIFO to re-sync */ - SET_PERI_REG_MASK(UART_CONF0_REG(0), UART_RXFIFO_RST); - CLEAR_PERI_REG_MASK(UART_CONF0_REG(0), UART_RXFIFO_RST); - uint32_t args[4]; - uint32_t len = SLIP_recv(&cmd, 1); - if (len != 1) { - continue; - } - uint8_t resp = 0xff; - switch (cmd) { - case CMD_FLASH_WRITE: { - len = SLIP_recv(args, sizeof(args)); - if (len == 12) { - resp = do_flash_write(args[0] /* addr */, args[1] /* len */, - args[2] /* erase */); - } else { - resp = 0x41; - } - break; - } - case CMD_FLASH_READ: { - len = SLIP_recv(args, sizeof(args)); - if (len == 16) { - resp = do_flash_read(args[0] /* addr */, args[1], /* len */ - args[2] /* block_size */, - args[3] /* max_in_flight */); - } else { - resp = 0x51; - } - break; - } - case CMD_FLASH_DIGEST: { - len = SLIP_recv(args, sizeof(args)); - if (len == 12) { - resp = do_flash_digest(args[0] /* addr */, args[1], /* len */ - args[2] /* digest_block_size */); - } else { - resp = 0x61; - } - break; - } - case CMD_FLASH_READ_CHIP_ID: { - resp = do_flash_read_chip_id(); - break; - } - case CMD_FLASH_ERASE_CHIP: { - resp = esp_rom_spiflash_erase_chip(); - break; - } - case CMD_BOOT_FW: - case CMD_REBOOT: { - resp = 0; - SLIP_send(&resp, 1); - return cmd; - } - case CMD_ECHO: { - len = SLIP_recv(args, sizeof(args)); - SLIP_send(args, len); - resp = 0; - break; - } - } - SLIP_send(&resp, 1); - } while (cmd != CMD_BOOT_FW && cmd != CMD_REBOOT); - return cmd; -} - -void stub_main1(void) { - uint32_t baud_rate = params[0]; - uint32_t greeting = 0x4941484f; /* OHAI */ - uint8_t last_cmd; - - /* This points at us right now, reset for next boot. */ - ets_set_user_start(0); - -/* Selects SPI functions for flash pins. */ -#if defined(ESP8266) - SelectSpiFunction(); - SET_PERI_REG_MASK(0x3FF00014, 1); /* Switch to 160 MHz */ -#elif defined(ESP32) - esp_rom_spiflash_attach(ets_efuse_get_spiconfig(), 0 /* legacy */); -#endif - - esp_rom_spiflash_config_param( - 0 /* deviceId */, 16 * 1024 * 1024 /* chip_size */, FLASH_BLOCK_SIZE, - FLASH_SECTOR_SIZE, FLASH_PAGE_SIZE, 0xffff /* status_mask */); - - if (baud_rate > 0) { - ets_delay_us(10000); - set_baud_rate(0, baud_rate); - } - - /* Give host time to get ready too. */ - ets_delay_us(50000); - -#ifdef BAUD_TEST - while (1) { - WRITE_PERI_REG(UART_FIFO_REG(0), 0x55); - } -#else - SLIP_send(&greeting, 4); -#endif - - last_cmd = cmd_loop(); - - ets_delay_us(10000); - - if (last_cmd == CMD_BOOT_FW) { -#if defined(ESP8266) - /* - * Find the return address in our own stack and change it. - * "flash_finish" it gets to the same point, except it doesn't need to - * patch up its RA: it returns from UartDwnLdProc, then from f_400011ac, - * then jumps to 0x4000108a, then checks strapping bits again (which will - * not have changed), and then proceeds to 0x400010a8. - */ - volatile uint32_t *sp = &baud_rate; - while (*sp != (uint32_t) 0x40001100) sp++; - *sp = 0x400010a8; - /* - * The following dummy asm fragment acts as a barrier, to make sure function - * epilogue, including return address loading, is added after our stack - * patching. - */ - __asm volatile("nop.n"); - return; /* To 0x400010a8 */ -#elif defined(ESP32) -/* TODO(rojer) */ -#endif - } else { - software_reset(); - } - /* Not reached */ -} - -/* miniz requires at least 12K of stack */ -uint32_t stack[3071]; -uint32_t stack_end; - -void stub_main(void) { - memset(&_bss_start, 0, (&_bss_end - &_bss_start)); - __asm volatile("movi a1, stack_end\n"); - stub_main1(); - // Keep the stack vars alive. - stack[0] = stack_end = 0xff; -} diff --git a/src/mongoose-6.11/src/common/platforms/esp/stub_flasher.h b/src/mongoose-6.11/src/common/platforms/esp/stub_flasher.h deleted file mode 100644 index 4bae4c6..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp/stub_flasher.h +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef CS_COMMON_PLATFORMS_ESP8266_STUBS_STUB_FLASHER_H_ -#define CS_COMMON_PLATFORMS_ESP8266_STUBS_STUB_FLASHER_H_ - -enum stub_cmd { - /* - * Write to the SPI flash. - * - * Args: addr, len, erase; addr and len must be SECTOR_SIZE-aligned. - * If erase != 0, perform erase before writing. - * Input: Stream of data to be written, note: no SLIP encapsulation here. - * Output: SLIP packets with number of bytes written after every write. - * This can (and should) be used for flow control. Flasher will - * write in 1K chunks but will buffer up to 4K of data - * Use this feedback to keep buffer above 1K but below 4K. - * Final packet will contain MD5 digest of the data written. - */ - CMD_FLASH_WRITE = 1, - - /* - * Read from the SPI flash. - * - * Args: addr, len, block_size; no alignment requirements, block_size <= 4K. - * Input: None. - * Output: Packets of up to block_size with data. An acknowledgement is - *expected - * after every packet, in the form of a packet with total number of - * bytes received so far. - * Last packet is the MD5 digest of the data sent. - * - * Note: No flow control is performed, it is assumed that the host can cope - * with the inbound stream. - */ - CMD_FLASH_READ = 2, - - /* - * Compute MD5 digest of the specified flash region. - * - * Args: addr, len, digest_block_size; no alignment requirements. - * Input: None. - * Output: If block digests are not enabled (digest_block_size == 0), - * only overall digest is produced. - * Otherwise, there will be a separate digest for each block, - * the remainder (if any) and the overall digest at the end. - */ - CMD_FLASH_DIGEST = 3, - - /* - * Read flash chip ID. - * This is the JEDEC ID, containinf manufactirer, SPI mode and capacity. - * - * Args: None. - * Input: None. - * Output: 32 bit chip id (only 24 bits are meaningful). - */ - CMD_FLASH_READ_CHIP_ID = 4, - - /* - * Zap the whole chip at once. - * - * Args: None. - * Input: None. - * Output: None. - */ - CMD_FLASH_ERASE_CHIP = 5, - - /* - * Boots the firmware from flash. - * - * Args: None. - * Input: None. - * Output: None. - */ - CMD_BOOT_FW = 6, - - /* - * Reboot the CPU. - * Since strapping settings are not reset, this will reboot into whatever mode - * got us here, most likely UART loader. - * - * Args: None. - * Input: None. - * Output: None. - */ - CMD_REBOOT = 7, - - /* - * Echo the arguments back to the host. - * - * Args: variable. - * Input: None. - * Output: arguments. - */ - CMD_ECHO = 8, -}; - -#endif /* CS_COMMON_PLATFORMS_ESP8266_STUBS_STUB_FLASHER_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/.gitattributes b/src/mongoose-6.11/src/common/platforms/esp31/rom/.gitattributes deleted file mode 100644 index 1260bec..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp31/rom/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -rom.bin -nodiff diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/ESP31B_ROM.txt b/src/mongoose-6.11/src/common/platforms/esp31/rom/ESP31B_ROM.txt deleted file mode 100644 index f4640cc..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp31/rom/ESP31B_ROM.txt +++ /dev/null @@ -1,11917 +0,0 @@ - -rom.elf: file format elf32-xtensa-le - - -Disassembly of section .text: - -40000000 <_start>: -40000000: 49c500 s32e a0, a5, -16 -40000003: 49d510 s32e a1, a5, -12 -40000006: 49e520 s32e a2, a5, -8 -40000009: 49f530 s32e a3, a5, -4 -4000000c: 003400 rfwo -4000000f: 412800 srli a2, a0, 8 -40000012: 5138 l32i.n a3, a1, 20 -40000014: 6148 l32i.n a4, a1, 24 -40000016: 01d112 addmi a1, a1, 0x100 -40000019: 13d100 wsr.excsave1 a0 -4000001c: 034800 rsr.windowbase a0 -4000001f: 4080f0 rotw -1 -40000022: 03e620 rsr.ps a2 -40000025: 343820 extui a3, a2, 8, 4 -40000028: 303340 xor a3, a3, a4 -4000002b: 000846 j 40000050 <_XX_ExcVec50> - ... - -40000040 <_XX_Vec40>: -40000040: 09c500 l32e a0, a5, -16 -40000043: 09d510 l32e a1, a5, -12 -40000046: 09e520 l32e a2, a5, -8 -40000049: 09f530 l32e a3, a5, -4 -4000004c: 003500 rfwu - ... - -40000050 <_XX_ExcVec50>: -40000050: 03d140 rsr.excsave1 a4 -40000053: 113380 slli a3, a3, 8 -40000056: 302230 xor a2, a2, a3 -40000059: 13e620 wsr.ps a2 -4000005c: 002010 rsync -4000005f: dd74f7 bbci a4, 31, 40000040 <_XX_Vec40> -40000062: 4080f0 rotw -1 -40000065: 5778e7 bbci a8, 30, 400000c0 <_XX_ExcVec80+0x40> -40000068: 4080f0 rotw -1 -4000006b: 003446 j 40000140 <_WindowUnderflowHandler> - ... - -40000080 <_XX_ExcVec80>: -40000080: 49c900 s32e a0, a9, -16 -40000083: 09d100 l32e a0, a1, -12 -40000086: 49d910 s32e a1, a9, -12 -40000089: 49e920 s32e a2, a9, -8 -4000008c: 49f930 s32e a3, a9, -4 -4000008f: 498040 s32e a4, a0, -32 -40000092: 499050 s32e a5, a0, -28 -40000095: 49a060 s32e a6, a0, -24 -40000098: 49b070 s32e a7, a0, -20 -4000009b: 003400 rfwo - ... -400000be: 000000 ill -400000c1: 09c9 s32i.n a12, a9, 0 -400000c3: 09d910 l32e a1, a9, -12 -400000c6: 09e920 l32e a2, a9, -8 -400000c9: 09d170 l32e a7, a1, -12 -400000cc: 09f930 l32e a3, a9, -4 -400000cf: 098740 l32e a4, a7, -32 -400000d2: 099750 l32e a5, a7, -28 -400000d5: 09a760 l32e a6, a7, -24 -400000d8: 09b770 l32e a7, a7, -20 -400000db: 003500 rfwu - ... - -40000100 <_WindowOverflowHandler>: -40000100: 49cd00 s32e a0, a13, -16 -40000103: 09d100 l32e a0, a1, -12 -40000106: 49dd10 s32e a1, a13, -12 -40000109: 49ed20 s32e a2, a13, -8 -4000010c: 49fd30 s32e a3, a13, -4 -4000010f: 494040 s32e a4, a0, -48 -40000112: 495050 s32e a5, a0, -44 -40000115: 496060 s32e a6, a0, -40 -40000118: 497070 s32e a7, a0, -36 -4000011b: 498080 s32e a8, a0, -32 -4000011e: 499090 s32e a9, a0, -28 -40000121: 49a0a0 s32e a10, a0, -24 -40000124: 49b0b0 s32e a11, a0, -20 -40000127: 003400 rfwo - ... - -40000140 <_WindowUnderflowHandler>: -40000140: 09cd00 l32e a0, a13, -16 -40000143: 09dd10 l32e a1, a13, -12 -40000146: 09ed20 l32e a2, a13, -8 -40000149: 09d1b0 l32e a11, a1, -12 -4000014c: 09fd30 l32e a3, a13, -4 -4000014f: 094b40 l32e a4, a11, -48 -40000152: 095b50 l32e a5, a11, -44 -40000155: 096b60 l32e a6, a11, -40 -40000158: 097b70 l32e a7, a11, -36 -4000015b: 098b80 l32e a8, a11, -32 -4000015e: 099b90 l32e a9, a11, -28 -40000161: 09aba0 l32e a10, a11, -24 -40000164: 09bbb0 l32e a11, a11, -20 -40000167: 003500 rfwu - ... -4000017e: 200000 or a0, a0, a0 -40000181: a061d2 s32i a13, a1, 0x280 -40000184: 000002 l8ui a0, a0, 0 - ... -400001bf: d32000 movt a2, a0, b0 -400001c2: 02a061 l32r a6, 3ffc0c44 <_start-0x3f3bc> - ... -400001fd: 000000 ill -40000200: 61d420 xsr.excsave4 a2 -40000203: 0002a0 jx a2 - ... -4000023e: 200000 or a0, a0, a0 -40000241: a061d5 call4 3ffa0860 <_start-0x5f7a0> -40000244: 000002 l8ui a0, a0, 0 - ... -4000027f: d62000 excw -40000282: 827c13 excw -40000285: 005100 simcall -40000288: ffff06 j 40000288 <_WindowUnderflowHandler+0x148> - ... -400002bf: 371000 excw - ... - -40000300 <_XX_Vec400>: -40000300: 004100 break 1, 0 -40000303: fffe46 j 40000300 <_XX_Vec400> - ... -4000033e: 120000 andbc b0, b0, b0 -40000341: 29ffd1 l32r a13, 3ffcab40 <_start-0x354c0> -40000344: 513941 l32r a4, 3ffd4828 <_start-0x2b7d8> -40000347: f27e31 l32r a3, 3fffcd40 <_start-0x32c0> -4000034a: 03e820 rsr.exccause a2 -4000034d: a03230 addx4 a3, a2, a3 -40000350: 0338 l32i.n a3, a3, 0 -40000352: 6149 s32i.n a4, a1, 24 -40000354: 0003a0 jx a3 - ... -400003bf: 414000 srli a4, a0, 0 -400003c2: fe4600 excw -400003c5: ff .byte 0xff - ... -400003fe: 7c0000 excw -40000401: 6100d0 xsr.lbeg a13 -40000404: 0dc613 excw - ... -4000040f: 211f40 srai a1, a4, 15 -40000412: 002222 l32i a2, a2, 0 -40000415: e00000 subx4 a0, a0, a0 -40000418: 000494 excw -4000041b: 70d440 excw -4000041e: dc4000 excw -40000421: 0009 s32i.n a0, a0, 0 -40000423: 0ab040 depbits a4, a0, 0, 12 -40000426: 804000 add a4, a0, a0 -40000429: 000b addi.n a0, a0, -1 -4000042b: c20840 quou a0, a8, a4 -4000042e: ff .byte 0xff -4000042f: 3f .byte 0x3f -40000430: 000cc0 callx0 a12 -40000433: c20c40 quou a0, a12, a4 -40000436: ff .byte 0xff -40000437: 3f .byte 0x3f - ... -40000440: 000c movi.n a0, 0 -40000442: 13e400 wsr.intenable a0 -40000445: fff021 l32r a2, 40000408 <_XX_Vec400+0x108> -40000448: 03eb30 rsr.prid a3 -4000044b: 743030 extui a3, a3, 0, 8 -4000044e: 228c beqz.n a2, 40000454 <_XX_Vec400+0x154> -40000450: 03cc bnez.n a3, 40000454 <_XX_Vec400+0x154> -40000452: 0209 s32i.n a0, a2, 0 -40000454: ffee21 l32r a2, 4000040c <_XX_Vec400+0x10c> -40000457: 13e720 wsr.vecbase a2 -4000045a: 531c movi.n a3, 21 -4000045c: 136330 wsr.atomctl a3 -4000045f: 006120 rsil a2, 1 -40000462: ffeb21 l32r a2, 40000410 <_XX_Vec400+0x110> -40000465: ffeb51 l32r a5, 40000414 <_XX_Vec400+0x114> -40000468: ffec61 l32r a6, 40000418 <_XX_Vec400+0x118> -4000046b: 030c movi.n a3, 0 -4000046d: 027d mov.n a7, a2 -4000046f: 106650 and a6, a6, a5 -40000472: 000786 j 40000494 <_XX_Vec400+0x194> - ... -4000047d: 000000 ill -40000480: 506340 witlb a4, a3 -40000483: 002000 isync -40000486: f03d nop.n -40000488: 0020f0 nop -4000048b: c03350 sub a3, a3, a5 -4000048e: 14b3b6 bltui a3, 16, 400004a6 <_XX_Vec400+0x1a6> -40000491: 417470 srli a7, a7, 4 -40000494: 344070 extui a4, a7, 0, 4 -40000497: e51367 beq a3, a6, 40000480 <_XX_Vec400+0x180> -4000049a: 506340 witlb a4, a3 -4000049d: c03350 sub a3, a3, a5 -400004a0: edb3f6 bgeui a3, 16, 40000491 <_XX_Vec400+0x191> -400004a3: 002000 isync -400004a6: ffdb51 l32r a5, 40000414 <_XX_Vec400+0x114> -400004a9: 030c movi.n a3, 0 -400004ab: 027d mov.n a7, a2 -400004ad: 344070 extui a4, a7, 0, 4 -400004b0: 50e340 wdtlb a4, a3 -400004b3: c03350 sub a3, a3, a5 -400004b6: 417470 srli a7, a7, 4 -400004b9: f0b3f6 bgeui a3, 16, 400004ad <_XX_Vec400+0x1ad> -400004bc: 002030 dsync -400004bf: 03a032 movi a3, 3 -400004c2: 036120 rsr.memctl a2 -400004c5: 202230 or a2, a2, a3 -400004c8: 136120 wsr.memctl a2 -400004cb: ffd421 l32r a2, 4000041c <_XX_Vec400+0x11c> -400004ce: 02ac beqz.n a2, 400004f2 <_XX_Vec400+0x1f2> -400004d0: 0238 l32i.n a3, a2, 0 -400004d2: 1248 l32i.n a4, a2, 4 -400004d4: 2258 l32i.n a5, a2, 8 -400004d6: 0cc222 addi a2, a2, 12 -400004d9: 0fb347 bgeu a3, a4, 400004ec <_XX_Vec400+0x1ec> -400004dc: 0568 l32i.n a6, a5, 0 -400004de: 554b addi.n a5, a5, 4 -400004e0: 0369 s32i.n a6, a3, 0 -400004e2: 334b addi.n a3, a3, 4 -400004e4: f43347 bltu a3, a4, 400004dc <_XX_Vec400+0x1dc> -400004e7: fff946 j 400004d0 <_XX_Vec400+0x1d0> -400004ea: 560000 excw -400004ed: 56fe03 excw -400004f0: 00fdd5 call4 400014d0 <_l_strap_0x0x11_loader+0x7> -400004f3: 0c0020 excw -400004f6: 491011 l32r a1, 3ffd2938 <_start-0x2d6c8> -400004f9: 480013 excw -400004fc: 201013 excw - ... - -40000500 <_X_ResetVector>: -40000500: 000c movi.n a0, 0 -40000502: ffc741 l32r a4, 40000420 <_XX_Vec400+0x120> -40000505: 13d240 wsr.excsave2 a4 -40000508: ffc741 l32r a4, 40000424 <_XX_Vec400+0x124> -4000050b: 13d340 wsr.excsave3 a4 -4000050e: ffc641 l32r a4, 40000428 <_XX_Vec400+0x128> -40000511: 13d440 wsr.excsave4 a4 -40000514: ffc651 l32r a5, 4000042c <_XX_Vec400+0x12c> -40000517: 006542 s32i a4, a5, 0 -4000051a: ffc541 l32r a4, 40000430 <_XX_Vec400+0x130> -4000051d: 13d540 wsr.excsave5 a4 -40000520: ffc551 l32r a5, 40000434 <_XX_Vec400+0x134> -40000523: 006542 s32i a4, a5, 0 -40000526: 002845 call0 400007ac <_X_start> - ... - -40000700 <_c_stack>: -40000700: 000000 ill -40000703: 002040 excw -40000706: 480004 mula.da.ll.ldinc m0, a0, m0, a0 - -40000708 <_c_bss_start>: -40000708: cd48 l32i.n a4, a13, 48 -4000070a: ff .byte 0xff -4000070b: 3f .byte 0x3f - -4000070c <_c_bss_end>: -4000070c: ffe1d0 excw -4000070f: 3f .byte 0x3f -40000710: 6668 l32i.n a6, a6, 24 -40000712: 084000 excw -40000715: 3fffc1 l32r a12, 3ffd0714 <_start-0x2f8ec> -40000718: 000e40 excw -4000071b: c00840 sub a0, a8, a4 -4000071e: ff .byte 0xff -4000071f: 3f .byte 0x3f -40000720: 66b8 l32i.n a11, a6, 24 -40000722: 234000 sext a4, a0, 7 -40000725: 000500 excw -40000728: 000007 bnone a0, a0, 4000072c <_c_bss_end+0x20> -4000072b: 000580 ret -4000072e: 000000 ill -40000731: c00000 sub a0, a0, a0 - -40000734 <_c_0x3fffc210>: -40000734: ffc210 excw -40000737: 3f .byte 0x3f - -40000738 <_c_0x80000000>: -40000738: 000000 ill -4000073b: c21880 quou a1, a8, a8 -4000073e: ff .byte 0xff -4000073f: 3f .byte 0x3f -40000740: 66c8 l32i.n a12, a6, 24 -40000742: 004000 break 0, 0 -40000745: 078000 excw -40000748: 000aa1 l32r a10, 3ffc0770 <_start-0x3f890> -4000074b: 800040 add a0, a0, a4 -4000074e: 723800 excw -40000751: 000b addi.n a0, a0, -1 -40000753: d15040 mul16s a5, a0, a4 -40000756: ff .byte 0xff -40000757: 3f .byte 0x3f -40000758: c208 l32i.n a0, a2, 48 -4000075a: ff .byte 0xff -4000075b: 3f .byte 0x3f -4000075c: 050024 excw - ... - -40000760 <_c_0x40000000>: -40000760: 000000 ill -40000763: d6b040 excw -40000766: ff .byte 0xff -40000767: 3f .byte 0x3f -40000768: c20c movi.n a2, 12 -4000076a: ff .byte 0xff -4000076b: 3f .byte 0x3f -4000076c: 050025 call8 40005770 <_XX_unk5734+0x3c> -4000076f: 000000 ill -40000772: 408001 l32r a0, 3ffd0974 <_start-0x2f68c> -40000775: ffc8 l32i.n a12, a15, 60 -40000777: 3f .byte 0x3f -40000778: f42d break.n 4 -4000077a: 2d5851 l32r a5, 3ffcbcdc <_start-0x34324> -4000077d: 7f .byte 0x7f -4000077e: ff4c95 call4 3ffffc48 <_start-0x3b8> - -40000780 <_c_0x7fffffff>: -40000780: ff .byte 0xff -40000781: ff .byte 0xff -40000782: ff .byte 0xff -40000783: 7f .byte 0x7f -40000784: ffca30 excw -40000787: 3f .byte 0x3f -40000788: 0066b0 rsil a11, 6 -4000078b: fff040 excw -4000078e: fffb addi.n a15, a15, 15 -40000790: ff .byte 0xff -40000791: 000000 ill -40000794: 00ff00 excw - ... - -40000798 <_c_0x00ff0000>: -40000798: ff0000 excw -4000079b: 000000 ill -4000079e: 40ff00 nsau a0, a15 -400007a1: 404040 excw -400007a4: 808080 add a8, a0, a8 -400007a7: feff80 excw -400007aa: fe .byte 0xfe -400007ab: fe .byte 0xfe - -400007ac <_X_start>: -400007ac: 00a002 movi a0, 0 -400007af: ffd411 l32r a1, 40000700 <_c_stack> -400007b2: ffd431 l32r a3, 40000704 <_c_stack+0x4> -400007b5: f03d nop.n -400007b7: 13e630 wsr.ps a3 -400007ba: 002010 rsync -400007bd: ffd261 l32r a6, 40000708 <_c_bss_start> -400007c0: ffd371 l32r a7, 4000070c <_c_bss_end> -400007c3: 06b677 bgeu a6, a7, 400007cd <_X_start+0x21> -400007c6: 0609 s32i.n a0, a6, 0 -400007c8: 664b addi.n a6, a6, 4 -400007ca: f83677 bltu a6, a7, 400007c6 <_X_start+0x1a> -400007cd: 0098d5 call4 4000115c <_X_main> -400007d0: 120c movi.n a2, 1 -400007d2: 005100 simcall -400007d5: 0041f0 break 1, 15 -400007d8: fffd06 j 400007d0 <_X_start+0x24> -400007db: 413600 srli a3, a0, 6 -400007de: d2f600 quos a15, a6, a0 -400007e1: 2e .byte 0x2e -400007e2: 090c movi.n a9, 0 -400007e4: ffcd61 l32r a6, 40000718 <_c_bss_end+0xc> -400007e7: ffce41 l32r a4, 40000720 <_c_bss_end+0x14> -400007ea: ffcc81 l32r a8, 4000071c <_c_bss_end+0x10> -400007ed: ffc8a1 l32r a10, 40000710 <_c_bss_end+0x4> -400007f0: ffc9b1 l32r a11, 40000714 <_c_bss_end+0x8> -400007f3: 1152e0 slli a5, a2, 2 -400007f6: b5ba add.n a11, a5, a11 -400007f8: 833a30 moveqz a3, a10, a3 -400007fb: 558a add.n a5, a5, a8 -400007fd: 0b28 l32i.n a2, a11, 0 -400007ff: c073a0 sub a7, a3, a10 -40000802: 934670 movnez a4, a6, a7 -40000805: 0b39 s32i.n a3, a11, 0 -40000807: 0549 s32i.n a4, a5, 0 -40000809: c0a2a0 sub a10, a2, a10 -4000080c: 8329a0 moveqz a2, a9, a10 -4000080f: f01d retw.n -40000811: 020c movi.n a2, 0 -40000813: f01d retw.n -40000815: 000000 ill -40000818: 03b130 rsr.epc1 a3 -4000081b: 030120 rsr.lend a2 -4000081e: 333b addi.n a3, a3, 3 -40000820: 0c9237 bne a2, a3, 40000830 <_X_start+0x84> -40000823: 030220 rsr.lcount a2 -40000826: 628c beqz.n a2, 40000830 <_X_start+0x84> -40000828: 220b addi.n a2, a2, -1 -4000082a: 130220 wsr.lcount a2 -4000082d: 030030 rsr.lbeg a3 -40000830: 4128 l32i.n a2, a1, 16 -40000832: 13b130 wsr.epc1 a3 -40000835: 5138 l32i.n a3, a1, 20 -40000837: 056256 bnez a2, 40000891 <_X_start+0xe5> -4000083a: 396132 s32i a3, a1, 228 -4000083d: 3a6142 s32i a4, a1, 232 -40000840: 3b6152 s32i a5, a1, 236 -40000843: ffb831 l32r a3, 40000724 <_c_bss_end+0x18> -40000846: 03e620 rsr.ps a2 -40000849: 13e630 wsr.ps a3 -4000084c: 03b130 rsr.epc1 a3 -4000084f: 01d112 addmi a1, a1, 0x100 -40000852: 002010 rsync -40000855: 040c movi.n a4, 0 -40000857: 008136 entry a1, 64 -4000085a: 0001f5 call12 40000878 <_X_start+0xcc> -4000085d: ffb2e1 l32r a14, 40000728 <_c_bss_end+0x1c> -40000860: 0cea add.n a0, a12, a14 -40000862: f01d retw.n -40000864: 13e620 wsr.ps a2 -40000867: 020c movi.n a2, 0 -40000869: a548 l32i.n a4, a5, 40 -4000086b: 002010 rsync -4000086e: 13b130 wsr.epc1 a3 -40000871: 9538 l32i.n a3, a5, 36 -40000873: b558 l32i.n a5, a5, 44 -40000875: 003000 rfe -40000878: 006136 entry a1, 48 -4000087b: ffacc1 l32r a12, 4000072c <_c_bss_end+0x20> -4000087e: c0ca add.n a12, a0, a12 -40000880: 006136 entry a1, 48 -40000883: 00cd mov.n a12, a0 -40000885: 006136 entry a1, 48 -40000888: 00cd mov.n a12, a0 -4000088a: 002136 entry a1, 16 -4000088d: 0bbd mov.n a11, a11 -4000088f: f01d retw.n -40000891: f27c movi.n a2, -1 -40000893: 01d112 addmi a1, a1, 0x100 -40000896: 003000 rfe -40000899: 000000 ill -4000089c: 7159 s32i.n a5, a1, 28 -4000089e: ffa121 l32r a2, 40000724 <_c_bss_end+0x18> -400008a1: 03b130 rsr.epc1 a3 -400008a4: 61e620 xsr.ps a2 -400008a7: 0139 s32i.n a3, a1, 0 -400008a9: 1129 s32i.n a2, a1, 4 -400008ab: 3b2122 l32i a2, a1, 236 -400008ae: 01d112 addmi a1, a1, 0x100 -400008b1: 002010 rsync -400008b4: ff9f41 l32r a4, 40000730 <_c_bss_end+0x24> -400008b7: 203430 or a3, a4, a3 -400008ba: 904430 addx2 a4, a4, a3 -400008bd: 020136 entry a1, 0x100 -400008c0: 0061f0 rsil a15, 1 -400008c3: ff9ce1 l32r a14, 40000734 <_c_0x3fffc210> -400008c6: 03e2f0 rsr.interrupt a15 -400008c9: 03e4c0 rsr.intenable a12 -400008cc: 1ed8 l32i.n a13, a14, 4 -400008ce: 10ffc0 and a15, a15, a12 -400008d1: 0303c0 rsr.sar a12 -400008d4: 31d9 s32i.n a13, a1, 12 -400008d6: 092f16 beqz a15, 4000096c <_X_start+0x1c0> -400008d9: 21c9 s32i.n a12, a1, 8 -400008db: 8129 s32i.n a2, a1, 32 -400008dd: ffaf22 movi a2, -1 -400008e0: 40ffc0 nsau a12, a15 -400008e3: ff95f1 l32r a15, 40000738 <_c_0x80000000> -400008e6: 400c00 ssr a12 -400008e9: 91f0f0 srl a15, a15 -400008ec: 3022f0 xor a2, a2, a15 -400008ef: ff93d1 l32r a13, 4000073c <_c_0x80000000+0x4> -400008f2: 13e3f0 wsr.intclear a15 -400008f5: b0ccd0 addx8 a12, a12, a13 -400008f8: 402cd2 l32i a13, a12, 0x100 -400008fb: 0066f0 rsil a15, 6 -400008fe: 0ef8 l32i.n a15, a14, 0 -40000900: 1ed9 s32i.n a13, a14, 4 -40000902: 10ffd0 and a15, a15, a13 -40000905: 13e4f0 wsr.intenable a15 -40000908: 002010 rsync -4000090b: 0060f0 rsil a15, 0 -4000090e: 0302d0 rsr.lcount a13 -40000911: 0300f0 rsr.lbeg a15 -40000914: 91d9 s32i.n a13, a1, 36 -40000916: 0301d0 rsr.lend a13 -40000919: a1f9 s32i.n a15, a1, 40 -4000091b: b1d9 s32i.n a13, a1, 44 -4000091d: 0310d0 rsr.acclo a13 -40000920: 0311f0 rsr.acchi a15 -40000923: c1d9 s32i.n a13, a1, 48 -40000925: d1f9 s32i.n a15, a1, 52 -40000927: 0cd8 l32i.n a13, a12, 0 -40000929: 01fd mov.n a15, a1 -4000092b: 012ce2 l32i a14, a12, 4 -4000092e: 000df0 callx12 a13 -40000931: 91d8 l32i.n a13, a1, 36 -40000933: a1e8 l32i.n a14, a1, 40 -40000935: b1f8 l32i.n a15, a1, 44 -40000937: 1302d0 wsr.lcount a13 -4000093a: 1300e0 wsr.lbeg a14 -4000093d: 1301f0 wsr.lend a15 -40000940: c1d8 l32i.n a13, a1, 48 -40000942: d1e8 l32i.n a14, a1, 52 -40000944: 1310d0 wsr.acclo a13 -40000947: 1311e0 wsr.acchi a14 -4000094a: ff7ae1 l32r a14, 40000734 <_c_0x3fffc210> -4000094d: 03e2f0 rsr.interrupt a15 -40000950: 0066d0 rsil a13, 6 -40000953: 0ed8 l32i.n a13, a14, 0 -40000955: 31c8 l32i.n a12, a1, 12 -40000957: 10ddc0 and a13, a13, a12 -4000095a: 10ffd0 and a15, a15, a13 -4000095d: 01ff56 bnez a15, 40000980 <_X_start+0x1d4> -40000960: 8128 l32i.n a2, a1, 32 -40000962: 1ec9 s32i.n a12, a14, 4 -40000964: 21c8 l32i.n a12, a1, 8 -40000966: 13e4d0 wsr.intenable a13 -40000969: 0063d0 rsil a13, 3 -4000096c: ff7501 l32r a0, 40000740 <_c_0x80000000+0x8> -4000096f: ff70d1 l32r a13, 40000730 <_c_bss_end+0x24> -40000972: 1303c0 wsr.sar a12 -40000975: 2000d0 or a0, a0, a13 -40000978: 900d00 addx2 a0, a13, a0 -4000097b: 0063d0 rsil a13, 3 -4000097e: f01d retw.n -40000980: 40ffd0 nsau a13, a15 -40000983: ff6ec1 l32r a12, 4000073c <_c_0x80000000+0x4> -40000986: b0cdc0 addx8 a12, a13, a12 -40000989: 412ce2 l32i a14, a12, 0x104 -4000098c: 10ff20 and a15, a15, a2 -4000098f: 10ffe0 and a15, a15, a14 -40000992: ff68e1 l32r a14, 40000734 <_c_0x3fffc210> -40000995: f47f56 bnez a15, 400008e0 <_X_start+0x134> -40000998: ff68f1 l32r a15, 40000738 <_c_0x80000000> -4000099b: 400d00 ssr a13 -4000099e: 91d0f0 srl a13, a15 -400009a1: 412cf2 l32i a15, a12, 0x104 -400009a4: 13e3d0 wsr.intclear a13 -400009a7: 2022f0 or a2, a2, a15 -400009aa: 3022d0 xor a2, a2, a13 -400009ad: ffd1c6 j 400008f8 <_X_start+0x14c> -400009b0: 002136 entry a1, 16 -400009b3: 13e620 wsr.ps a2 -400009b6: 002010 rsync -400009b9: f01d retw.n -400009bb: 213600 srai a3, a0, 6 -400009be: 023d00 andb b3, b13, b0 -400009c1: ff5c41 l32r a4, 40000734 <_c_0x3fffc210> -400009c4: 006670 rsil a7, 6 -400009c7: 1428 l32i.n a2, a4, 4 -400009c9: 0458 l32i.n a5, a4, 0 -400009cb: 1439 s32i.n a3, a4, 4 -400009cd: 105530 and a5, a5, a3 -400009d0: 13e450 wsr.intenable a5 -400009d3: 13e670 wsr.ps a7 -400009d6: 002010 rsync -400009d9: f01d retw.n -400009db: d22000 quos a2, a0, a0 -400009de: d11261 l32r a6, 3fff4e28 <_start-0xb1d8> -400009e1: ff .byte 0xff -400009e2: 4129 s32i.n a2, a1, 16 -400009e4: ff5021 l32r a2, 40000724 <_c_bss_end+0x18> -400009e7: 6149 s32i.n a4, a1, 24 -400009e9: 7159 s32i.n a5, a1, 28 -400009eb: 13e620 wsr.ps a2 -400009ee: 002010 rsync -400009f1: 3b2122 l32i a2, a1, 236 -400009f4: 01d112 addmi a1, a1, 0x100 -400009f7: 03b240 rsr.epc2 a4 -400009fa: ff4d51 l32r a5, 40000730 <_c_bss_end+0x24> -400009fd: 204540 or a4, a5, a4 -40000a00: 904540 addx2 a4, a5, a4 -40000a03: 020136 entry a1, 0x100 -40000a06: 0062f0 rsil a15, 2 -40000a09: 03e2f0 rsr.interrupt a15 -40000a0c: 03e4c0 rsr.intenable a12 -40000a0f: ff4dd1 l32r a13, 40000744 <_c_0x80000000+0xc> -40000a12: 10ffc0 and a15, a15, a12 -40000a15: 10ffd0 and a15, a15, a13 -40000a18: 0303e0 rsr.sar a14 -40000a1b: 06ef16 beqz a15, 40000a8d <_X_start+0x2e1> -40000a1e: 21e9 s32i.n a14, a1, 8 -40000a20: 0302d0 rsr.lcount a13 -40000a23: 0300e0 rsr.lbeg a14 -40000a26: 91d9 s32i.n a13, a1, 36 -40000a28: 0301d0 rsr.lend a13 -40000a2b: a1e9 s32i.n a14, a1, 40 -40000a2d: b1d9 s32i.n a13, a1, 44 -40000a2f: 0310d0 rsr.acclo a13 -40000a32: 0311e0 rsr.acchi a14 -40000a35: c1d9 s32i.n a13, a1, 48 -40000a37: d1e9 s32i.n a14, a1, 52 -40000a39: 60c0f0 neg a12, a15 -40000a3c: 10ccf0 and a12, a12, a15 -40000a3f: 13e3c0 wsr.intclear a12 -40000a42: ff3ed1 l32r a13, 4000073c <_c_0x80000000+0x4> -40000a45: fe1c movi.n a14, 31 -40000a47: 40fcc0 nsau a12, a12 -40000a4a: c0fec0 sub a15, a14, a12 -40000a4d: 60f0f0 neg a15, a15 -40000a50: 1fcff2 addi a15, a15, 31 -40000a53: b0cfd0 addx8 a12, a15, a13 -40000a56: 0cd8 l32i.n a13, a12, 0 -40000a58: 1ce8 l32i.n a14, a12, 4 -40000a5a: 20f110 or a15, a1, a1 -40000a5d: 000df0 callx12 a13 -40000a60: 03e2f0 rsr.interrupt a15 -40000a63: 03e4c0 rsr.intenable a12 -40000a66: ff37d1 l32r a13, 40000744 <_c_0x80000000+0xc> -40000a69: 10ffc0 and a15, a15, a12 -40000a6c: 10ffd0 and a15, a15, a13 -40000a6f: fc6f56 bnez a15, 40000a39 <_X_start+0x28d> -40000a72: 91d8 l32i.n a13, a1, 36 -40000a74: a1e8 l32i.n a14, a1, 40 -40000a76: b1f8 l32i.n a15, a1, 44 -40000a78: 1302d0 wsr.lcount a13 -40000a7b: 1300e0 wsr.lbeg a14 -40000a7e: 1301f0 wsr.lend a15 -40000a81: c1d8 l32i.n a13, a1, 48 -40000a83: d1e8 l32i.n a14, a1, 52 -40000a85: 1310d0 wsr.acclo a13 -40000a88: 1311e0 wsr.acchi a14 -40000a8b: 21e8 l32i.n a14, a1, 8 -40000a8d: ff2e01 l32r a0, 40000748 <_c_0x80000000+0x10> -40000a90: ff28d1 l32r a13, 40000730 <_c_bss_end+0x24> -40000a93: 1303e0 wsr.sar a14 -40000a96: 2000d0 or a0, a0, a13 -40000a99: 900d00 addx2 a0, a13, a0 -40000a9c: 0063e0 rsil a14, 3 -40000a9f: f01d retw.n -40000aa1: 3b6522 s32i a2, a5, 236 -40000aa4: 4528 l32i.n a2, a5, 16 -40000aa6: 6548 l32i.n a4, a5, 24 -40000aa8: 7558 l32i.n a5, a5, 28 -40000aaa: 003210 rfi 2 -40000aad: 000000 ill -40000ab0: 61d320 xsr.excsave3 a2 -40000ab3: ffd112 addmi a1, a1, 0xffffff00 -40000ab6: 4129 s32i.n a2, a1, 16 -40000ab8: ff1b21 l32r a2, 40000724 <_c_bss_end+0x18> -40000abb: 6149 s32i.n a4, a1, 24 -40000abd: 7159 s32i.n a5, a1, 28 -40000abf: 13e620 wsr.ps a2 -40000ac2: 002010 rsync -40000ac5: 3b2122 l32i a2, a1, 236 -40000ac8: 01d112 addmi a1, a1, 0x100 -40000acb: 03b340 rsr.epc3 a4 -40000ace: ff1851 l32r a5, 40000730 <_c_bss_end+0x24> -40000ad1: 204540 or a4, a5, a4 -40000ad4: 904540 addx2 a4, a5, a4 -40000ad7: 020136 entry a1, 0x100 -40000ada: 0063f0 rsil a15, 3 -40000add: 03e2f0 rsr.interrupt a15 -40000ae0: 03e4c0 rsr.intenable a12 -40000ae3: ff1ad1 l32r a13, 4000074c <_c_0x80000000+0x14> -40000ae6: 10ffc0 and a15, a15, a12 -40000ae9: 10ffd0 and a15, a15, a13 -40000aec: 0303e0 rsr.sar a14 -40000aef: 06ef16 beqz a15, 40000b61 <_X_start+0x3b5> -40000af2: 21e9 s32i.n a14, a1, 8 -40000af4: 0302d0 rsr.lcount a13 -40000af7: 0300e0 rsr.lbeg a14 -40000afa: 91d9 s32i.n a13, a1, 36 -40000afc: 0301d0 rsr.lend a13 -40000aff: a1e9 s32i.n a14, a1, 40 -40000b01: b1d9 s32i.n a13, a1, 44 -40000b03: 0310d0 rsr.acclo a13 -40000b06: 0311e0 rsr.acchi a14 -40000b09: c1d9 s32i.n a13, a1, 48 -40000b0b: d1e9 s32i.n a14, a1, 52 -40000b0d: 60c0f0 neg a12, a15 -40000b10: 10ccf0 and a12, a12, a15 -40000b13: 13e3c0 wsr.intclear a12 -40000b16: ff09d1 l32r a13, 4000073c <_c_0x80000000+0x4> -40000b19: fe1c movi.n a14, 31 -40000b1b: 40fcc0 nsau a12, a12 -40000b1e: c0fec0 sub a15, a14, a12 -40000b21: 60f0f0 neg a15, a15 -40000b24: 1fcff2 addi a15, a15, 31 -40000b27: b0cfd0 addx8 a12, a15, a13 -40000b2a: 0cd8 l32i.n a13, a12, 0 -40000b2c: 1ce8 l32i.n a14, a12, 4 -40000b2e: 20f110 or a15, a1, a1 -40000b31: 000df0 callx12 a13 -40000b34: 03e2f0 rsr.interrupt a15 -40000b37: 03e4c0 rsr.intenable a12 -40000b3a: ff04d1 l32r a13, 4000074c <_c_0x80000000+0x14> -40000b3d: 10ffc0 and a15, a15, a12 -40000b40: 10ffd0 and a15, a15, a13 -40000b43: fc6f56 bnez a15, 40000b0d <_X_start+0x361> -40000b46: 91d8 l32i.n a13, a1, 36 -40000b48: a1e8 l32i.n a14, a1, 40 -40000b4a: b1f8 l32i.n a15, a1, 44 -40000b4c: 1302d0 wsr.lcount a13 -40000b4f: 1300e0 wsr.lbeg a14 -40000b52: 1301f0 wsr.lend a15 -40000b55: c1d8 l32i.n a13, a1, 48 -40000b57: d1e8 l32i.n a14, a1, 52 -40000b59: 1310d0 wsr.acclo a13 -40000b5c: 1311e0 wsr.acchi a14 -40000b5f: 21e8 l32i.n a14, a1, 8 -40000b61: fefb01 l32r a0, 40000750 <_c_0x80000000+0x18> -40000b64: fef3d1 l32r a13, 40000730 <_c_bss_end+0x24> -40000b67: 1303e0 wsr.sar a14 -40000b6a: 2000d0 or a0, a0, a13 -40000b6d: 900d00 addx2 a0, a13, a0 -40000b70: f01d retw.n -40000b72: 3b6522 s32i a2, a5, 236 -40000b75: 4528 l32i.n a2, a5, 16 -40000b77: 6548 l32i.n a4, a5, 24 -40000b79: 7558 l32i.n a5, a5, 28 -40000b7b: 003310 rfi 3 -40000b7e: 210000 srai a0, a0, 0 -40000b81: 09fef5 call12 4000ab70 <__umoddi3+0x3d0c> -40000b84: f401d2 l8ui a13, a1, 244 -40000b87: fe .byte 0xfe -40000b88: e219 s32i.n a1, a2, 56 -40000b8a: 0008 l32i.n a0, a0, 0 -40000b8c: 106232 s32i a3, a2, 64 -40000b8f: 61d400 xsr.excsave4 a0 -40000b92: 116242 s32i a4, a2, 68 -40000b95: f209 s32i.n a0, a2, 60 -40000b97: 03b130 rsr.epc1 a3 -40000b9a: 03e840 rsr.exccause a4 -40000b9d: 3239 s32i.n a3, a2, 12 -40000b9f: 4249 s32i.n a4, a2, 16 -40000ba1: 03ee30 rsr.excvaddr a3 -40000ba4: 056232 s32i a3, a2, 20 -40000ba7: 03d140 rsr.excsave1 a4 -40000baa: 066242 s32i a4, a2, 24 -40000bad: 803c movi.n a0, 56 -40000baf: 126252 s32i a5, a2, 72 -40000bb2: 136262 s32i a6, a2, 76 -40000bb5: 146272 s32i a7, a2, 80 -40000bb8: 156282 s32i a8, a2, 84 -40000bbb: 166292 s32i a9, a2, 88 -40000bbe: 1762a2 s32i a10, a2, 92 -40000bc1: 1862b2 s32i a11, a2, 96 -40000bc4: 1962c2 s32i a12, a2, 100 -40000bc7: 1a62d2 s32i a13, a2, 104 -40000bca: 1b62e2 s32i a14, a2, 108 -40000bcd: 1c62f2 s32i a15, a2, 112 -40000bd0: f8c082 addi a8, a0, -8 -40000bd3: 20c2a2 addi a10, a2, 32 -40000bd6: 408020 rotw 2 -40000bd9: fdb056 bnez a0, 40000bb8 <_X_start+0x40c> -40000bdc: 408020 rotw 2 -40000bdf: 034930 rsr.windowstart a3 -40000be2: 034840 rsr.windowbase a4 -40000be5: 1239 s32i.n a3, a2, 4 -40000be7: 2249 s32i.n a4, a2, 8 -40000be9: 130c movi.n a3, 1 -40000beb: 040c movi.n a4, 0 -40000bed: 134930 wsr.windowstart a3 -40000bf0: 134840 wsr.windowbase a4 -40000bf3: 002010 rsync -40000bf6: fed711 l32r a1, 40000754 <_c_0x80000000+0x1c> -40000bf9: 000c movi.n a0, 0 -40000bfb: fed821 l32r a2, 4000075c <_c_0x80000000+0x24> -40000bfe: 13e620 wsr.ps a2 -40000c01: 002010 rsync -40000c04: 0303e0 rsr.sar a14 -40000c07: 01e9 s32i.n a14, a1, 0 -40000c09: 0302e0 rsr.lcount a14 -40000c0c: a1e9 s32i.n a14, a1, 40 -40000c0e: 0300e0 rsr.lbeg a14 -40000c11: b1e9 s32i.n a14, a1, 44 -40000c13: 0301e0 rsr.lend a14 -40000c16: c1e9 s32i.n a14, a1, 48 -40000c18: 0310e0 rsr.acclo a14 -40000c1b: 81e9 s32i.n a14, a1, 32 -40000c1d: 0311e0 rsr.acchi a14 -40000c20: 91e9 s32i.n a14, a1, 36 -40000c22: fecfd1 l32r a13, 40000760 <_c_0x40000000> -40000c25: fec5c1 l32r a12, 4000073c <_c_0x80000000+0x4> -40000c28: 13e3d0 wsr.intclear a13 -40000c2b: 2cd8 l32i.n a13, a12, 8 -40000c2d: 3c68 l32i.n a6, a12, 12 -40000c2f: 000dd0 callx4 a13 -40000c32: a1d8 l32i.n a13, a1, 40 -40000c34: b1e8 l32i.n a14, a1, 44 -40000c36: c1f8 l32i.n a15, a1, 48 -40000c38: 1302d0 wsr.lcount a13 -40000c3b: 1300e0 wsr.lbeg a14 -40000c3e: 1301f0 wsr.lend a15 -40000c41: 81d8 l32i.n a13, a1, 32 -40000c43: 91e8 l32i.n a14, a1, 36 -40000c45: 1310d0 wsr.acclo a13 -40000c48: 1311e0 wsr.acchi a14 -40000c4b: 01f8 l32i.n a15, a1, 0 -40000c4d: 1303f0 wsr.sar a15 -40000c50: 423c movi.n a2, 52 -40000c52: 13e620 wsr.ps a2 -40000c55: 002010 rsync -40000c58: 1128 l32i.n a2, a1, 4 -40000c5a: 2138 l32i.n a3, a1, 8 -40000c5c: 134920 wsr.windowstart a2 -40000c5f: 134830 wsr.windowbase a3 -40000c62: 002010 rsync -40000c65: febb11 l32r a1, 40000754 <_c_0x80000000+0x1c> -40000c68: 863c movi.n a6, 56 -40000c6a: e0c172 addi a7, a1, -32 -40000c6d: f8c6e2 addi a14, a6, -8 -40000c70: 20c7f2 addi a15, a7, 32 -40000c73: 112f42 l32i a4, a15, 68 -40000c76: 122f52 l32i a5, a15, 72 -40000c79: 132f62 l32i a6, a15, 76 -40000c7c: 142f72 l32i a7, a15, 80 -40000c7f: 152f82 l32i a8, a15, 84 -40000c82: 162f92 l32i a9, a15, 88 -40000c85: 172fa2 l32i a10, a15, 92 -40000c88: 182fb2 l32i a11, a15, 96 -40000c8b: 408020 rotw 2 -40000c8e: fdb656 bnez a6, 40000c6d <_X_start+0x4c1> -40000c91: 192742 l32i a4, a7, 100 -40000c94: 1a2752 l32i a5, a7, 104 -40000c97: 1b2762 l32i a6, a7, 108 -40000c9a: 1c2772 l32i a7, a7, 112 -40000c9d: 408020 rotw 2 -40000ca0: 3128 l32i.n a2, a1, 12 -40000ca2: 4138 l32i.n a3, a1, 16 -40000ca4: 13b120 wsr.epc1 a2 -40000ca7: 13e830 wsr.exccause a3 -40000caa: 5128 l32i.n a2, a1, 20 -40000cac: 13ee20 wsr.excvaddr a2 -40000caf: 6138 l32i.n a3, a1, 24 -40000cb1: 13d130 wsr.excsave1 a3 -40000cb4: d108 l32i.n a0, a1, 52 -40000cb6: f128 l32i.n a2, a1, 60 -40000cb8: 102132 l32i a3, a1, 64 -40000cbb: e118 l32i.n a1, a1, 56 -40000cbd: 003410 rfi 4 -40000cc0: fea921 l32r a2, 40000764 <_c_0x40000000+0x4> -40000cc3: d209 s32i.n a0, a2, 52 -40000cc5: fea801 l32r a0, 40000768 <_c_0x40000000+0x8> -40000cc8: e219 s32i.n a1, a2, 56 -40000cca: 0008 l32i.n a0, a0, 0 -40000ccc: 106232 s32i a3, a2, 64 -40000ccf: 61d500 xsr.excsave5 a0 -40000cd2: 116242 s32i a4, a2, 68 -40000cd5: f209 s32i.n a0, a2, 60 -40000cd7: 03b130 rsr.epc1 a3 -40000cda: 03e840 rsr.exccause a4 -40000cdd: 3239 s32i.n a3, a2, 12 -40000cdf: 4249 s32i.n a4, a2, 16 -40000ce1: 03ee30 rsr.excvaddr a3 -40000ce4: 056232 s32i a3, a2, 20 -40000ce7: 03d140 rsr.excsave1 a4 -40000cea: 6249 s32i.n a4, a2, 24 -40000cec: 38a002 movi a0, 56 -40000cef: 126252 s32i a5, a2, 72 -40000cf2: 136262 s32i a6, a2, 76 -40000cf5: 146272 s32i a7, a2, 80 -40000cf8: 156282 s32i a8, a2, 84 -40000cfb: 166292 s32i a9, a2, 88 -40000cfe: 1762a2 s32i a10, a2, 92 -40000d01: 1862b2 s32i a11, a2, 96 -40000d04: 1962c2 s32i a12, a2, 100 -40000d07: 1a62d2 s32i a13, a2, 104 -40000d0a: 1b62e2 s32i a14, a2, 108 -40000d0d: 1c62f2 s32i a15, a2, 112 -40000d10: f8c082 addi a8, a0, -8 -40000d13: 20c2a2 addi a10, a2, 32 -40000d16: 408020 rotw 2 -40000d19: fdb056 bnez a0, 40000cf8 <_X_start+0x54c> -40000d1c: 408020 rotw 2 -40000d1f: 034930 rsr.windowstart a3 -40000d22: 034840 rsr.windowbase a4 -40000d25: 1239 s32i.n a3, a2, 4 -40000d27: 2249 s32i.n a4, a2, 8 -40000d29: 130c movi.n a3, 1 -40000d2b: 040c movi.n a4, 0 -40000d2d: 134930 wsr.windowstart a3 -40000d30: 134840 wsr.windowbase a4 -40000d33: 002010 rsync -40000d36: fe8b11 l32r a1, 40000764 <_c_0x40000000+0x4> -40000d39: 000c movi.n a0, 0 -40000d3b: fe8c21 l32r a2, 4000076c <_c_0x40000000+0xc> -40000d3e: 13e620 wsr.ps a2 -40000d41: 002010 rsync -40000d44: 0303e0 rsr.sar a14 -40000d47: 01e9 s32i.n a14, a1, 0 -40000d49: 0302e0 rsr.lcount a14 -40000d4c: a1e9 s32i.n a14, a1, 40 -40000d4e: 0300e0 rsr.lbeg a14 -40000d51: b1e9 s32i.n a14, a1, 44 -40000d53: 0301e0 rsr.lend a14 -40000d56: c1e9 s32i.n a14, a1, 48 -40000d58: 0310e0 rsr.acclo a14 -40000d5b: 81e9 s32i.n a14, a1, 32 -40000d5d: 0311e0 rsr.acchi a14 -40000d60: 91e9 s32i.n a14, a1, 36 -40000d62: 03e2f0 rsr.interrupt a15 -40000d65: 03e4c0 rsr.intenable a12 -40000d68: fe82d1 l32r a13, 40000770 <_c_0x40000000+0x10> -40000d6b: 10ffc0 and a15, a15, a12 -40000d6e: 10ffd0 and a15, a15, a13 -40000d71: 02af16 beqz a15, 40000d9f <_X_start+0x5f3> -40000d74: 60e0f0 neg a14, a15 -40000d77: 10eef0 and a14, a14, a15 -40000d7a: 13e3e0 wsr.intclear a14 -40000d7d: fe6fc1 l32r a12, 4000073c <_c_0x80000000+0x4> -40000d80: 40fee0 nsau a14, a14 -40000d83: b0cec0 addx8 a12, a14, a12 -40000d86: 0cd8 l32i.n a13, a12, 0 -40000d88: 1c68 l32i.n a6, a12, 4 -40000d8a: 000dd0 callx4 a13 -40000d8d: 03e2f0 rsr.interrupt a15 -40000d90: 03e4c0 rsr.intenable a12 -40000d93: fe77d1 l32r a13, 40000770 <_c_0x40000000+0x10> -40000d96: 10ffc0 and a15, a15, a12 -40000d99: 10ffd0 and a15, a15, a13 -40000d9c: fd4f56 bnez a15, 40000d74 <_X_start+0x5c8> -40000d9f: a1d8 l32i.n a13, a1, 40 -40000da1: b1e8 l32i.n a14, a1, 44 -40000da3: c1f8 l32i.n a15, a1, 48 -40000da5: 1302d0 wsr.lcount a13 -40000da8: 1300e0 wsr.lbeg a14 -40000dab: 1301f0 wsr.lend a15 -40000dae: 81d8 l32i.n a13, a1, 32 -40000db0: 91e8 l32i.n a14, a1, 36 -40000db2: 1310d0 wsr.acclo a13 -40000db5: 1311e0 wsr.acchi a14 -40000db8: 01f8 l32i.n a15, a1, 0 -40000dba: 1303f0 wsr.sar a15 -40000dbd: 523c movi.n a2, 53 -40000dbf: 13e620 wsr.ps a2 -40000dc2: 002010 rsync -40000dc5: 1128 l32i.n a2, a1, 4 -40000dc7: 022132 l32i a3, a1, 8 -40000dca: 134920 wsr.windowstart a2 -40000dcd: 134830 wsr.windowbase a3 -40000dd0: 002010 rsync -40000dd3: fe6411 l32r a1, 40000764 <_c_0x40000000+0x4> -40000dd6: 38a062 movi a6, 56 -40000dd9: e0c172 addi a7, a1, -32 -40000ddc: f8c6e2 addi a14, a6, -8 -40000ddf: 20c7f2 addi a15, a7, 32 -40000de2: 112f42 l32i a4, a15, 68 -40000de5: 122f52 l32i a5, a15, 72 -40000de8: 132f62 l32i a6, a15, 76 -40000deb: 142f72 l32i a7, a15, 80 -40000dee: 152f82 l32i a8, a15, 84 -40000df1: 162f92 l32i a9, a15, 88 -40000df4: 172fa2 l32i a10, a15, 92 -40000df7: 182fb2 l32i a11, a15, 96 -40000dfa: 408020 rotw 2 -40000dfd: fdb656 bnez a6, 40000ddc <_X_start+0x630> -40000e00: 192742 l32i a4, a7, 100 -40000e03: 1a2752 l32i a5, a7, 104 -40000e06: 1b2762 l32i a6, a7, 108 -40000e09: 1c2772 l32i a7, a7, 112 -40000e0c: 408020 rotw 2 -40000e0f: 3128 l32i.n a2, a1, 12 -40000e11: 4138 l32i.n a3, a1, 16 -40000e13: 13b120 wsr.epc1 a2 -40000e16: 13e830 wsr.exccause a3 -40000e19: 5128 l32i.n a2, a1, 20 -40000e1b: 13ee20 wsr.excvaddr a2 -40000e1e: 6138 l32i.n a3, a1, 24 -40000e20: 13d130 wsr.excsave1 a3 -40000e23: d108 l32i.n a0, a1, 52 -40000e25: f128 l32i.n a2, a1, 60 -40000e27: 102132 l32i a3, a1, 64 -40000e2a: e118 l32i.n a1, a1, 56 -40000e2c: 003510 rfi 5 -40000e2f: 320c00 orbc b0, b12, b0 -40000e32: 000286 j 40000e40 <_X_start+0x694> -40000e35: 000000 ill -40000e38: fdc332 addi a3, a3, -3 -40000e3b: 332a add.n a3, a3, a2 -40000e3d: 0004c6 j 40000e54 <_X_start+0x6a8> -40000e40: 7159 s32i.n a5, a1, 28 -40000e42: 086122 s32i a2, a1, 32 -40000e45: 03e620 rsr.ps a2 -40000e48: fe3731 l32r a3, 40000724 <_c_bss_end+0x18> -40000e4b: 016122 s32i a2, a1, 4 -40000e4e: 342020 extui a2, a2, 0, 4 -40000e51: e342f6 bgeui a2, 4, 40000e38 <_X_start+0x68c> -40000e54: 03b120 rsr.epc1 a2 -40000e57: 13e630 wsr.ps a3 -40000e5a: 0129 s32i.n a2, a1, 0 -40000e5c: 3b2122 l32i a2, a1, 236 -40000e5f: 01d112 addmi a1, a1, 0x100 -40000e62: 002010 rsync -40000e65: fe3241 l32r a4, 40000730 <_c_bss_end+0x24> -40000e68: 203430 or a3, a4, a3 -40000e6b: 904430 addx2 a4, a4, a3 -40000e6e: 020136 entry a1, 0x100 -40000e71: 0303c0 rsr.sar a12 -40000e74: 11d8 l32i.n a13, a1, 4 -40000e76: 03e6e0 rsr.ps a14 -40000e79: 34d0d0 extui a13, a13, 0, 4 -40000e7c: 34f0e0 extui a15, a14, 0, 4 -40000e7f: 30eef0 xor a14, a14, a15 -40000e82: 30eed0 xor a14, a14, a13 -40000e85: 13e6e0 wsr.ps a14 -40000e88: fe23d1 l32r a13, 40000714 <_c_bss_end+0x8> -40000e8b: 81f8 l32i.n a15, a1, 32 -40000e8d: 21c9 s32i.n a12, a1, 8 -40000e8f: 0302c0 rsr.lcount a12 -40000e92: 0300e0 rsr.lbeg a14 -40000e95: 91c9 s32i.n a12, a1, 36 -40000e97: 0301c0 rsr.lend a12 -40000e9a: a1e9 s32i.n a14, a1, 40 -40000e9c: b1c9 s32i.n a12, a1, 44 -40000e9e: 0310c0 rsr.acclo a12 -40000ea1: 0311e0 rsr.acchi a14 -40000ea4: c1c9 s32i.n a12, a1, 48 -40000ea6: d1e9 s32i.n a14, a1, 52 -40000ea8: a0cfd0 addx4 a12, a15, a13 -40000eab: 0cc8 l32i.n a12, a12, 0 -40000ead: 01ed mov.n a14, a1 -40000eaf: 1c8c beqz.n a12, 40000eb4 <_X_start+0x708> -40000eb1: 000cf0 callx12 a12 -40000eb4: 91d8 l32i.n a13, a1, 36 -40000eb6: a1e8 l32i.n a14, a1, 40 -40000eb8: b1f8 l32i.n a15, a1, 44 -40000eba: 1302d0 wsr.lcount a13 -40000ebd: 1300e0 wsr.lbeg a14 -40000ec0: 1301f0 wsr.lend a15 -40000ec3: c1d8 l32i.n a13, a1, 48 -40000ec5: d1e8 l32i.n a14, a1, 52 -40000ec7: 1310d0 wsr.acclo a13 -40000eca: 1311e0 wsr.acchi a14 -40000ecd: 21e8 l32i.n a14, a1, 8 -40000ecf: 0063c0 rsil a12, 3 -40000ed2: 1303e0 wsr.sar a14 -40000ed5: fe1a01 l32r a0, 40000740 <_c_0x80000000+0x8> -40000ed8: fe16d1 l32r a13, 40000730 <_c_bss_end+0x24> -40000edb: 2000d0 or a0, a0, a13 -40000ede: 900d00 addx2 a0, a13, a0 -40000ee1: f01d retw.n -40000ee3: 413600 srli a3, a0, 6 -40000ee6: 234100 sext a4, a1, 7 -40000ee9: fe .byte 0xfe -40000eea: 0448 l32i.n a4, a4, 0 -40000eec: 030c movi.n a3, 0 -40000eee: 2a6422 s32i a2, a4, 168 -40000ef1: 2b6432 s32i a3, a4, 172 -40000ef4: f01d retw.n -40000ef6: 360000 excw -40000ef9: 610041 l32r a4, 3ffd92fc <_start-0x26d04> -40000efc: 1e .byte 0x1e -40000efd: fe .byte 0xfe -40000efe: fe1e81 l32r a8, 40000778 <_c_0x40000000+0x18> -40000f01: 0668 l32i.n a6, a6, 0 -40000f03: fe1e51 l32r a5, 4000077c <_c_0x40000000+0x1c> -40000f06: 2b2672 l32i a7, a6, 172 -40000f09: 2a2642 l32i a4, a6, 168 -40000f0c: 827750 mull a7, a7, a5 -40000f0f: 828480 mull a8, a4, a8 -40000f12: 822450 mull a2, a4, a5 -40000f15: a24450 muluh a4, a4, a5 -40000f18: 521b addi.n a5, a2, 1 -40000f1a: 448a add.n a4, a4, a8 -40000f1c: 447a add.n a4, a4, a7 -40000f1e: 0fb527 bgeu a5, a2, 40000f31 <_X_start+0x785> -40000f21: 2a6652 s32i a5, a6, 168 -40000f24: 241b addi.n a2, a4, 1 -40000f26: fe1631 l32r a3, 40000780 <_c_0x7fffffff> -40000f29: 2b6622 s32i a2, a6, 172 -40000f2c: 102230 and a2, a2, a3 -40000f2f: f01d retw.n -40000f31: 2a6652 s32i a5, a6, 168 -40000f34: fe1321 l32r a2, 40000780 <_c_0x7fffffff> -40000f37: 2b6642 s32i a4, a6, 172 -40000f3a: 102420 and a2, a4, a2 -40000f3d: f01d retw.n -40000f3f: d81000 excw -40000f42: ff .byte 0xff -40000f43: 3f .byte 0x3f -40000f44: 004136 entry a1, 32 -40000f47: fffe41 l32r a4, 40000f40 <_X_start+0x794> -40000f4a: 1439 s32i.n a3, a4, 4 -40000f4c: 0429 s32i.n a2, a4, 0 -40000f4e: f01d retw.n - -40000f50 <_c_0x3fffd820>: -40000f50: ffd820 excw -40000f53: 3f .byte 0x3f - -40000f54 <_X_ets_task>: -40000f54: 004136 entry a1, 32 -40000f57: 160c movi.n a6, 1 -40000f59: 090c movi.n a9, 0 -40000f5b: fffd71 l32r a7, 40000f50 <_c_0x3fffd820> -40000f5e: 1183c0 slli a8, a3, 4 -40000f61: 778a add.n a7, a7, a8 -40000f63: 830b addi.n a8, a3, -1 -40000f65: f0c772 addi a7, a7, -16 -40000f68: 0729 s32i.n a2, a7, 0 -40000f6a: 1749 s32i.n a4, a7, 4 -40000f6c: 084752 s8i a5, a7, 8 -40000f6f: 094792 s8i a9, a7, 9 -40000f72: 0a4792 s8i a9, a7, 10 -40000f75: 0b4792 s8i a9, a7, 11 -40000f78: 401800 ssl a8 -40000f7b: a16600 sll a6, a6 -40000f7e: 3769 s32i.n a6, a7, 12 -40000f80: f01d retw.n - ... - -40000f84 <_XX_unk0f84>: -40000f84: 004136 entry a1, 32 -40000f87: f01d retw.n -40000f89: 000000 ill -40000f8c: 004136 entry a1, 32 -40000f8f: ffff65 call8 40000f84 <_XX_unk0f84> -40000f92: 0008a5 call8 4000101c <_XX_unk0f96+0x84> -40000f95: f01d retw.n - ... - -40000f98 <_XX_unk0f96>: -40000f98: 004136 entry a1, 32 -40000f9b: 0010e5 call8 400010a8 <_X_ets_enter_critical> -40000f9e: ffeca1 l32r a10, 40000f50 <_c_0x3fffd820> -40000fa1: 11b2c0 slli a11, a2, 4 -40000fa4: aaba add.n a10, a10, a11 -40000fa6: ffdac2 addmi a12, a10, 0xffffff00 -40000fa9: f80cd2 l8ui a13, a12, 248 -40000fac: fb0c82 l8ui a8, a12, 251 -40000faf: f0cab2 addi a11, a10, -16 -40000fb2: 4eb8d7 bgeu a8, a13, 40001004 <_XX_unk0f96+0x6c> -40000fb5: 090b82 l8ui a8, a11, 9 -40000fb8: 1b98 l32i.n a9, a11, 4 -40000fba: f81b addi.n a15, a8, 1 -40000fbc: b08890 addx8 a8, a8, a9 -40000fbf: 0839 s32i.n a3, a8, 0 -40000fc1: 1849 s32i.n a4, a8, 4 -40000fc3: 094bf2 s8i a15, a11, 9 -40000fc6: f90ce2 l8ui a14, a12, 249 -40000fc9: ffdaa2 addmi a10, a10, 0xffffff00 -40000fcc: 049de7 bne a13, a14, 40000fd4 <_XX_unk0f96+0x3c> -40000fcf: 0c0c movi.n a12, 0 -40000fd1: 094bc2 s8i a12, a11, 9 -40000fd4: 0b0be2 l8ui a14, a11, 11 -40000fd7: 01cee2 addi a14, a14, 1 -40000fda: 0b4be2 s8i a14, a11, 11 -40000fdd: fb0ad2 l8ui a13, a10, 251 -40000fe0: 091d26 beqi a13, 1, 40000fed <_XX_unk0f96+0x55> -40000fe3: 000da5 call8 400010bc <_X_ets_exit_critical> -40000fe6: 00a022 movi a2, 0 -40000fe9: 000090 retw -40000fec: d48100 extui a8, a0, 1, 14 -40000fef: ff .byte 0xff -40000ff0: 3f2af2 l32i a15, a10, 252 -40000ff3: 022892 l32i a9, a8, 8 -40000ff6: 20ff90 or a15, a15, a9 -40000ff9: 0268f2 s32i a15, a8, 8 -40000ffc: 000be5 call8 400010bc <_X_ets_exit_critical> -40000fff: 00a022 movi a2, 0 -40001002: f01d retw.n -40001004: 000b65 call8 400010bc <_X_ets_exit_critical> -40001007: 120c movi.n a2, 1 -40001009: f01d retw.n -4000100b: 413600 srli a3, a0, 6 -4000100e: f23000 rems a3, a0, a0 -40001011: 022c40 andb b2, b12, b4 -40001014: c02230 sub a2, a2, a3 -40001017: 742020 extui a2, a2, 0, 8 -4000101a: f01d retw.n -4000101c: 004136 entry a1, 32 -4000101f: f77c movi.n a7, -1 -40001021: ffc731 l32r a3, 40000f40 <_X_start+0x794> -40001024: 060c movi.n a6, 0 -40001026: 10c352 addi a5, a3, 16 -40001029: 000406 j 4000103d <_XX_unk0f96+0xa5> -4000102c: 201110 or a1, a1, a1 -4000102f: 0008e5 call8 400010bc <_X_ets_exit_critical> -40001032: ffd282 addmi a8, a2, 0xffffff00 -40001035: 3c2882 l32i a8, a8, 240 -40001038: 04ad mov.n a10, a4 -4000103a: 0008e0 callx8 a8 -4000103d: 0006a5 call8 400010a8 <_X_ets_enter_critical> -40001040: 23a8 l32i.n a10, a3, 8 -40001042: fffca5 call8 4000100c <_XX_unk0f96+0x74> -40001045: 048a16 beqz a10, 40001091 <_XX_unk0f96+0xf9> -40001048: 112ac0 slli a2, a10, 4 -4000104b: 252a add.n a2, a5, a2 -4000104d: f0c2a2 addi a10, a2, -16 -40001050: ffd2b2 addmi a11, a2, 0xffffff00 -40001053: f80b92 l8ui a9, a11, 248 -40001056: 3d2b82 l32i a8, a11, 244 -40001059: 0a0ac2 l8ui a12, a10, 10 -4000105c: fa0b42 l8ui a4, a11, 250 -4000105f: cc1b addi.n a12, a12, 1 -40001061: 0a4ac2 s8i a12, a10, 10 -40001064: fa0bb2 l8ui a11, a11, 250 -40001067: b04480 addx8 a4, a4, a8 -4000106a: 0299b7 bne a9, a11, 40001070 <_XX_unk0f96+0xd8> -4000106d: 0a4a62 s8i a6, a10, 10 -40001070: 0b0ab2 l8ui a11, a10, 11 -40001073: ffd292 addmi a9, a2, 0xffffff00 -40001076: bb0b addi.n a11, a11, -1 -40001078: 0b4ab2 s8i a11, a10, 11 -4000107b: fb0982 l8ui a8, a9, 251 -4000107e: 3f29d2 l32i a13, a9, 252 -40001081: fa7856 bnez a8, 4000102c <_XX_unk0f96+0x94> -40001084: 23c8 l32i.n a12, a3, 8 -40001086: 30dd70 xor a13, a13, a7 -40001089: 10ccd0 and a12, a12, a13 -4000108c: 23c9 s32i.n a12, a3, 8 -4000108e: ffe686 j 4000102c <_XX_unk0f96+0x94> -40001091: 03b8 l32i.n a11, a3, 0 -40001093: 005b16 beqz a11, 4000109c <_XX_unk0f96+0x104> -40001096: 0123a2 l32i a10, a3, 4 -40001099: 000be0 callx8 a11 -4000109c: 000365 call8 400010d4 <_X_ets_exit_critical_and_wait_int> -4000109f: ffe686 j 4000103d <_XX_unk0f96+0xa5> - ... - -400010a4 <_c_ets_critical_level>: -400010a4: ffda20 excw -400010a7: 3f .byte 0x3f - -400010a8 <_X_ets_enter_critical>: -400010a8: 004136 entry a1, 32 -400010ab: fffe31 l32r a3, 400010a4 <_c_ets_critical_level> -400010ae: 006220 rsil a2, 2 -400010b1: 0388 l32i.n a8, a3, 0 -400010b3: 881b addi.n a8, a8, 1 -400010b5: 0389 s32i.n a8, a3, 0 -400010b7: f01d retw.n -400010b9: 000000 ill - -400010bc <_X_ets_exit_critical>: -400010bc: 004136 entry a1, 32 -400010bf: fff931 l32r a3, 400010a4 <_c_ets_critical_level> -400010c2: 0328 l32i.n a2, a3, 0 -400010c4: 220b addi.n a2, a2, -1 -400010c6: 0329 s32i.n a2, a3, 0 -400010c8: 028c beqz.n a2, 400010cc <_X_ets_exit_critical+0x10> -400010ca: f01d retw.n -400010cc: 006080 rsil a8, 0 -400010cf: f01d retw.n -400010d1: 000000 ill - -400010d4 <_X_ets_exit_critical_and_wait_int>: -400010d4: 004136 entry a1, 32 -400010d7: fff331 l32r a3, 400010a4 <_c_ets_critical_level> -400010da: 0328 l32i.n a2, a3, 0 -400010dc: 220b addi.n a2, a2, -1 -400010de: 0329 s32i.n a2, a3, 0 -400010e0: 007000 waiti 0 -400010e3: f01d retw.n -400010e5: 000000 ill - -400010e8 <_X_ets_isr_attach>: -400010e8: 004136 entry a1, 32 -400010eb: 04cd mov.n a12, a4 -400010ed: 03bd mov.n a11, a3 -400010ef: 02ad mov.n a10, a2 -400010f1: 048e65 call8 400059d8 <_X_xtos_set_interrupt_handler_arg> -400010f4: f01d retw.n - ... - -400010f8 <_X_ets_isr_mask>: -400010f8: 004136 entry a1, 32 -400010fb: 02ad mov.n a10, a2 -400010fd: 0495a5 call8 40005a58 <_X_xtos_ints_off> -40001100: f01d retw.n - ... - -40001104 <_X_ets_isr_unmask>: -40001104: 004136 entry a1, 32 -40001107: 02ad mov.n a10, a2 -40001109: 0492a5 call8 40005a34 <_X_xtos_ints_on> -4000110c: f01d retw.n - ... - -40001110 <_c_0x3fffda30>: -40001110: ffda30 excw -40001113: 3f .byte 0x3f - -40001114 <_XX_set_0x3fffda30_0>: -40001114: 004136 entry a1, 32 -40001117: fffe31 l32r a3, 40001110 <_c_0x3fffda30> -4000111a: 0329 s32i.n a2, a3, 0 -4000111c: f01d retw.n - ... - -40001120 <_XX_set_0x3fffda30_4>: -40001120: 004136 entry a1, 32 -40001123: fffb31 l32r a3, 40001110 <_c_0x3fffda30> -40001126: 1329 s32i.n a2, a3, 4 -40001128: f01d retw.n - ... - -4000112c <_c_0xfffdffff>: -4000112c: ff .byte 0xff -4000112d: ff .byte 0xff -4000112e: fffd excw - -40001130 <_c_0x60003e00>: -40001130: 003e00 excw -40001133: 820060 mull a0, a0, a6 - -40001134 <_c_0x60008200>: -40001134: 008200 any4 b0, b0:b1:b2:b3 -40001137: 7e0060 excw - -40001138 <_c_0x60007e00>: -40001138: 007e00 waiti 14 -4000113b: 100060 and a0, a0, a6 - -4000113c <_c_0x1000>: -4000113c: 001000 movsp a0, a0 - ... - -40001140 <_s_fw_build>: -40001140: ca7c movi.n a10, -4 -40001142: ff .byte 0xff -40001143: 3f .byte 0x3f - -40001144 <_s_boot_banner>: -40001144: ffca54 excw -40001147: 3f .byte 0x3f - -40001148 <_s_pct_s_pct_u>: -40001148: ca88 l32i.n a8, a10, 48 -4000114a: ff .byte 0xff -4000114b: 3f .byte 0x3f - -4000114c <_s_ets_main_c>: -4000114c: ffca90 excw -4000114f: 3f .byte 0x3f -40001150: 1d18 l32i.n a1, a13, 4 -40001152: 904000 addx2 a4, a0, a0 -40001155: 001d mov.n a1, a0 -40001157: ca9c40 depbits a4, a12, 12, 10 -4000115a: ff .byte 0xff -4000115b: 3f .byte 0x3f - -4000115c <_X_main>: -4000115c: 004136 entry a1, 32 -4000115f: 0177a5 call8 400028d8 <_X_get_rst_cause> -40001162: fff281 l32r a8, 4000112c <_c_0xfffdffff> -40001165: 00afe2 movi a14, 0xffffff00 -40001168: fff221 l32r a2, 40001130 <_c_0x60003e00> -4000116b: 871c movi.n a7, 24 -4000116d: fff1d1 l32r a13, 40001134 <_c_0x60008200> -40001170: 063c movi.n a6, 48 -40001172: 203aa0 or a3, a10, a10 -40001175: fff0b1 l32r a11, 40001138 <_c_0x60007e00> -40001178: 1aa0a2 movi a10, 26 -4000117b: 0020c0 memw -4000117e: a02bf2 l32i a15, a11, 0x280 -40001181: 10ff80 and a15, a15, a8 -40001184: 0020c0 memw -40001187: a06bf2 s32i a15, a11, 0x280 -4000118a: ffecc1 l32r a12, 4000113c <_c_0x1000> -4000118d: 0020c0 memw -40001190: a02b92 l32i a9, a11, 0x280 -40001193: 2099c0 or a9, a9, a12 -40001196: 0020c0 memw -40001199: a06b92 s32i a9, a11, 0x280 -4000119c: 0020c0 memw -4000119f: 8e2282 l32i a8, a2, 0x238 -400011a2: 174867 ball a8, a6, 400011bd <_X_main+0x61> -400011a5: 0020c0 memw -400011a8: 8e2292 l32i a9, a2, 0x238 -400011ab: 549090 extui a9, a9, 0, 6 -400011ae: 0b1977 beq a9, a7, 400011bd <_X_main+0x61> -400011b1: 0020c0 memw -400011b4: 8e22b2 l32i a11, a2, 0x238 -400011b7: 54b0b0 extui a11, a11, 0, 6 -400011ba: 0e9ba7 bne a11, a10, 400011cc <_X_main+0x70> -400011bd: 0020c0 memw -400011c0: 9d2dc2 l32i a12, a13, 0x274 -400011c3: 10cce0 and a12, a12, a14 -400011c6: 0020c0 memw -400011c9: 9d6dc2 s32i a12, a13, 0x274 -400011cc: 0020c0 memw -400011cf: 8e22f2 l32i a15, a2, 0x238 -400011d2: f03d nop.n -400011d4: 10ff60 and a15, a15, a6 -400011d7: 6bcf66 bnei a15, 32, 40001246 <_X_main+0xea> -400011da: fd6991 l32r a9, 40000780 <_c_0x7fffffff> -400011dd: 0020c0 memw -400011e0: a52dc2 l32i a12, a13, 0x294 -400011e3: 10cc90 and a12, a12, a9 -400011e6: 0020c0 memw -400011e9: a56dc2 s32i a12, a13, 0x294 -400011ec: 0020c0 memw -400011ef: a62db2 l32i a11, a13, 0x298 -400011f2: 10bb90 and a11, a11, a9 -400011f5: 0020c0 memw -400011f8: a66db2 s32i a11, a13, 0x298 -400011fb: 0020c0 memw -400011fe: a72d82 l32i a8, a13, 0x29c -40001201: 108890 and a8, a8, a9 -40001204: 0020c0 memw -40001207: a76d82 s32i a8, a13, 0x29c -4000120a: 0020c0 memw -4000120d: a82df2 l32i a15, a13, 0x2a0 -40001210: 10ff90 and a15, a15, a9 -40001213: 0020c0 memw -40001216: a86df2 s32i a15, a13, 0x2a0 -40001219: 0020c0 memw -4000121c: a92dc2 l32i a12, a13, 0x2a4 -4000121f: 10cc90 and a12, a12, a9 -40001222: 0020c0 memw -40001225: a96dc2 s32i a12, a13, 0x2a4 -40001228: 0020c0 memw -4000122b: aa2db2 l32i a11, a13, 0x2a8 -4000122e: 10bb90 and a11, a11, a9 -40001231: 0020c0 memw -40001234: aa6db2 s32i a11, a13, 0x2a8 -40001237: 0020c0 memw -4000123a: ab2d82 l32i a8, a13, 0x2ac -4000123d: 108890 and a8, a8, a9 -40001240: 0020c0 memw -40001243: ab6d82 s32i a8, a13, 0x2ac -40001246: 950c movi.n a5, 9 -40001248: b40c movi.n a4, 11 -4000124a: 8c2c movi.n a12, 40 -4000124c: eb2c movi.n a11, 46 -4000124e: 0020c0 memw -40001251: 8e22f2 l32i a15, a2, 0x238 -40001254: 020fc7 bnone a15, a12, 4000125a <_l_strap_0x0xxx> -40001257: 006a86 j 40001405 <_l_strap_NxNxxx> - -4000125a <_l_strap_0x0xxx>: -4000125a: 0020c0 memw -4000125d: 9d2d82 l32i a8, a13, 0x274 -40001260: 1088e0 and a8, a8, a14 -40001263: 0020c0 memw -40001266: 9d6d82 s32i a8, a13, 0x274 - -40001269 <_l_strap_init_uart0>: -40001269: 020da5 call8 40003344 <_X_uart_attach> -4000126c: 00a0a2 movi a10, 0 -4000126f: 022c65 call8 40003534 <_X_uart_init> -40001272: 015125 call8 40002784 <_X_ets_install_uart_printf> -40001275: ba2c movi.n a10, 43 -40001277: 0020c0 memw -4000127a: 8e2292 l32i a9, a2, 0x238 -4000127d: 1099a0 and a9, a9, a10 -40001280: fec992 addi a9, a9, -2 -40001283: 25f916 beqz a9, 400014e6 <_l_strap_0x0x10> -40001286: 0020c0 memw -40001289: 8e22b2 l32i a11, a2, 0x238 -4000128c: 54b0b0 extui a11, a11, 0, 6 -4000128f: c0bb40 sub a11, a11, a4 -40001292: 233b16 beqz a11, 400014c9 <_l_strap_0x0x11_loader> -40001295: 0020c0 memw -40001298: 8e22c2 l32i a12, a2, 0x238 -4000129b: 10cca0 and a12, a12, a10 -4000129e: fdccc2 addi a12, a12, -3 -400012a1: 224c16 beqz a12, 400014c9 <_l_strap_0x0x11_loader> -400012a4: 0020c0 memw -400012a7: 8e22d2 l32i a13, a2, 0x238 -400012aa: 340da7 bnone a13, a10, 400012e2 <_l_strap_0x0x00> -400012ad: 0020c0 memw -400012b0: 8e22e2 l32i a14, a2, 0x238 -400012b3: 54e0e0 extui a14, a14, 0, 6 -400012b6: c0ee50 sub a14, a14, a5 -400012b9: 217e16 beqz a14, 400014d4 <_l_strap_0x0x01> -400012bc: 0020c0 memw -400012bf: 8e22f2 l32i a15, a2, 0x238 -400012c2: 10ffa0 and a15, a15, a10 -400012c5: ff0b addi.n a15, a15, -1 -400012c7: 209f16 beqz a15, 400014d4 <_l_strap_0x0x01> -400012ca: 0020c0 memw -400012cd: 8e2282 l32i a8, a2, 0x238 -400012d0: 548080 extui a8, a8, 0, 6 -400012d3: 139866 bnei a8, 10, 400012ea <_l_boot> -400012d6: 04a0a2 movi a10, 4 -400012d9: 03e965 call8 40005170 <_X_sip_init_attach> -400012dc: 000286 j 400012ea <_l_boot> -400012df: 000000 ill - -400012e2 <_l_strap_0x0x00>: -400012e2: 0a0c movi.n a10, 0 -400012e4: 201110 or a1, a1, a1 -400012e7: 03e8a5 call8 40005170 <_X_sip_init_attach> - -400012ea <_l_boot>: -400012ea: 20c330 or a12, a3, a3 -400012ed: ff94b1 l32r a11, 40001140 <_s_fw_build> -400012f0: ff95a1 l32r a10, 40001144 <_s_boot_banner> -400012f3: 0020c0 memw -400012f6: 8e22d2 l32i a13, a2, 0x238 -400012f9: 54d0d0 extui a13, a13, 0, 6 -400012fc: 015065 call8 40002804 -400012ff: 009ea5 call8 40001ce8 <_X_print_mem_banner> -40001302: 0da0a2 movi a10, 13 -40001305: ff8251 l32r a5, 40001110 <_c_0x3fffda30> -40001308: 0233f6 bgeui a3, 3, 4000130e <_l_boot+0x24> -4000130b: 033356 bnez a3, 40001342 <_l_rst_cause_12> -4000130e: 0263f6 bgeui a3, 6, 40001314 <_l_boot+0x2a> -40001311: 2133f6 bgeui a3, 3, 40001336 <_l_rst_cause_345> -40001314: ae0c movi.n a14, 10 -40001316: 023e37 bltu a14, a3, 4000131c <_l_boot+0x32> -40001319: 1973f6 bgeui a3, 7, 40001336 <_l_rst_cause_345> -4000131c: 023a37 bltu a10, a3, 40001322 <_l_boot+0x38> -4000131f: 24b347 bgeu a3, a4, 40001347 <_l_rst_cause_12+0x5> -40001322: 4f1c movi.n a15, 20 -40001324: 0e13f7 beq a3, a15, 40001336 <_l_rst_cause_345> -40001327: ff88a1 l32r a10, 40001148 <_s_pct_s_pct_u> -4000132a: ff88b1 l32r a11, 4000114c <_s_ets_main_c> -4000132d: 31a1c2 movi a12, 0x131 -40001330: 014d25 call8 40002804 -40001333: ffff06 j 40001333 <_l_boot+0x49> - -40001336 <_l_rst_cause_345>: -40001336: 05ad mov.n a10, a5 -40001338: 03bd mov.n a11, a3 -4000133a: 0160e5 call8 40002948 <_XX_unk2948> -4000133d: 0588 l32i.n a8, a5, 0 -4000133f: 11b856 bnez a8, 4000145e <_l_strap_0010xx+0x12> - -40001342 <_l_rst_cause_12>: -40001342: 05ad mov.n a10, a5 -40001344: 001f65 call8 4000153c <_XX_unk153c> -40001347: 071347 beq a3, a4, 40001352 <_l_rst_cause_12+0x10> -4000134a: 04a326 beqi a3, 12, 40001352 <_l_rst_cause_12+0x10> -4000134d: d90c movi.n a9, 13 -4000134f: 4d9397 bne a3, a9, 400013a0 <_l_rst_cause_12+0x5e> -40001352: 1b2c movi.n a11, 33 -40001354: 0020c0 memw -40001357: 8e22a2 l32i a10, a2, 0x238 -4000135a: 02cab7 bnall a10, a11, 40001360 <_l_rst_cause_12+0x1e> -4000135d: 0058c6 j 400014c4 <_l_strap_001000_0x110x+0x14> -40001360: 0020c0 memw -40001363: 8e22c2 l32i a12, a2, 0x238 -40001366: 54c0c0 extui a12, a12, 0, 6 -40001369: c0cc70 sub a12, a12, a7 -4000136c: 154c16 beqz a12, 400014c4 <_l_strap_001000_0x110x+0x14> -4000136f: 0020c0 memw -40001372: 8e22d2 l32i a13, a2, 0x238 -40001375: 54d0d0 extui a13, a13, 0, 6 -40001378: e6cdd2 addi a13, a13, -26 -4000137b: 145d16 beqz a13, 400014c4 <_l_strap_001000_0x110x+0x14> -4000137e: 1b0c movi.n a11, 1 -40001380: 00a032 movi a3, 0 -40001383: 20a330 or a10, a3, a3 -40001386: 0020c0 memw -40001389: 8e22d2 l32i a13, a2, 0x238 -4000138c: 01a0c2 movi a12, 1 -4000138f: 10dd60 and a13, a13, a6 -40001392: e0cdd2 addi a13, a13, -32 -40001395: 83acd0 moveqz a10, a12, a13 -40001398: 74a0a0 extui a10, a10, 0, 8 -4000139b: 02fd65 call8 40004370 <_X_spi_flash_attach> -4000139e: 0539 s32i.n a3, a5, 0 -400013a0: 0020c0 memw -400013a3: 8e22e2 l32i a14, a2, 0x238 -400013a6: 46ee57 bbsi a14, 5, 400013f0 <_l_rst_cause_12+0xae> -400013a9: 0020c0 memw -400013ac: 8e22f2 l32i a15, a2, 0x238 -400013af: 54f0f0 extui a15, a15, 0, 6 -400013b2: 3a1f77 beq a15, a7, 400013f0 <_l_rst_cause_12+0xae> -400013b5: 0020c0 memw -400013b8: 8e2282 l32i a8, a2, 0x238 -400013bb: 1aa092 movi a9, 26 -400013be: 548080 extui a8, a8, 0, 6 -400013c1: 2b1897 beq a8, a9, 400013f0 <_l_rst_cause_12+0xae> -400013c4: 0020c0 memw -400013c7: 8e2292 l32i a9, a2, 0x238 -400013ca: 549090 extui a9, a9, 0, 6 -400013cd: 1a8926 beqi a9, 8, 400013eb <_l_rst_cause_12+0xa9> -400013d0: 0020c0 memw -400013d3: 8e22a2 l32i a10, a2, 0x238 -400013d6: 2ea0b2 movi a11, 46 -400013d9: 10aab0 and a10, a10, a11 -400013dc: 0baa26 beqi a10, 12, 400013eb <_l_rst_cause_12+0xa9> -400013df: 0020c0 memw -400013e2: 8e22c2 l32i a12, a2, 0x238 -400013e5: 28a0d2 movi a13, 40 -400013e8: 728cd7 bany a12, a13, 4000145e <_l_strap_0010xx+0x12> -400013eb: 05e8 l32i.n a14, a5, 0 -400013ed: 06de56 bnez a14, 4000145e <_l_strap_0010xx+0x12> -400013f0: 002f65 call8 400016e8 <_XX_unk153c+0x1ac> -400013f3: 067a16 beqz a10, 4000145e <_l_strap_0010xx+0x12> -400013f6: ff54a1 l32r a10, 40001148 <_s_pct_s_pct_u> -400013f9: ff54b1 l32r a11, 4000114c <_s_ets_main_c> -400013fc: 2ca1c2 movi a12, 0x12c -400013ff: 014065 call8 40002804 -40001402: ffff06 j 40001402 <_l_rst_cause_12+0xc0> - -40001405 <_l_strap_NxNxxx>: -40001405: 0020c0 memw -40001408: 8e22f2 l32i a15, a2, 0x238 -4000140b: f03d nop.n -4000140d: 54f0f0 extui a15, a15, 0, 6 -40001410: f8cff2 addi a15, a15, -8 -40001413: 099f16 beqz a15, 400014b0 <_l_strap_001000_0x110x> -40001416: 0020c0 memw -40001419: 8e2282 l32i a8, a2, 0x238 -4000141c: 1088b0 and a8, a8, a11 -4000141f: f4c882 addi a8, a8, -12 -40001422: 08a816 beqz a8, 400014b0 <_l_strap_001000_0x110x> -40001425: 0020c0 memw -40001428: 8e2292 l32i a9, a2, 0x238 -4000142b: 549090 extui a9, a9, 0, 6 -4000142e: 1a1947 beq a9, a4, 4000144c <_l_strap_0010xx> -40001431: 0020c0 memw -40001434: 8e22b2 l32i a11, a2, 0x238 -40001437: 54b0b0 extui a11, a11, 0, 6 -4000143a: 0e9b26 beqi a11, 10, 4000144c <_l_strap_0010xx> -4000143d: 0020c0 memw -40001440: 8e22c2 l32i a12, a2, 0x238 -40001443: 54c0c0 extui a12, a12, 0, 6 -40001446: c0cc50 sub a12, a12, a5 -40001449: e1cc56 bnez a12, 40001269 <_l_strap_init_uart0> - -4000144c <_l_strap_0010xx>: -4000144c: 0020c0 memw -4000144f: 9d2df2 l32i a15, a13, 0x274 -40001452: 10ffe0 and a15, a15, a14 -40001455: 0020c0 memw -40001458: 9d6df2 s32i a15, a13, 0x274 -4000145b: ff8286 j 40001269 <_l_strap_init_uart0> -4000145e: 09a0a2 movi a10, 9 -40001461: ff3bb1 l32r a11, 40001150 <_s_ets_main_c+0x4> -40001464: ff3765 call8 400007dc <_X_start+0x30> -40001467: ff3b21 l32r a2, 40001154 <_s_ets_main_c+0x8> -4000146a: 0a0c movi.n a10, 0 -4000146c: 02bd mov.n a11, a2 -4000146e: ff36e5 call8 400007dc <_X_start+0x30> -40001471: 2a0c movi.n a10, 2 -40001473: 02bd mov.n a11, a2 -40001475: ff3665 call8 400007dc <_X_start+0x30> -40001478: 3a0c movi.n a10, 3 -4000147a: 02bd mov.n a11, a2 -4000147c: ff35e5 call8 400007dc <_X_start+0x30> -4000147f: ca1c movi.n a10, 28 -40001481: 02bd mov.n a11, a2 -40001483: ff35a5 call8 400007dc <_X_start+0x30> -40001486: da1c movi.n a10, 29 -40001488: 02bd mov.n a11, a2 -4000148a: ff3525 call8 400007dc <_X_start+0x30> -4000148d: 02bd mov.n a11, a2 -4000148f: 08a0a2 movi a10, 8 -40001492: ff34a5 call8 400007dc <_X_start+0x30> -40001495: 05a8 l32i.n a10, a5, 0 -40001497: 1a8c beqz.n a10, 4000149c <_l_strap_0010xx+0x50> -40001499: 000ae0 callx8 a10 -4000149c: ff2fa1 l32r a10, 40001158 <_s_ets_main_c+0xc> -4000149f: 013665 call8 40002804 -400014a2: 0125a2 l32i a10, a5, 4 -400014a5: 033a16 beqz a10, 400014dc <_l_strap_0x0x01+0x8> -400014a8: 000ae0 callx8 a10 -400014ab: 00a022 movi a2, 0 -400014ae: f01d retw.n - -400014b0 <_l_strap_001000_0x110x>: -400014b0: c97c movi.n a9, -4 -400014b2: 0020c0 memw -400014b5: 9d2d82 l32i a8, a13, 0x274 -400014b8: 108890 and a8, a8, a9 -400014bb: 0020c0 memw -400014be: 9d6d82 s32i a8, a13, 0x274 -400014c1: ff6906 j 40001269 <_l_strap_init_uart0> -400014c4: 0b0c movi.n a11, 0 -400014c6: ffad86 j 40001380 <_l_rst_cause_12+0x3e> - -400014c9 <_l_strap_0x0x11_loader>: -400014c9: 3a0c movi.n a10, 3 -400014cb: 03ca65 call8 40005170 <_X_sip_init_attach> -400014ce: ff8606 j 400012ea <_l_boot> -400014d1: 000000 ill - -400014d4 <_l_strap_0x0x01>: -400014d4: 1a0c movi.n a10, 1 -400014d6: 03c9a5 call8 40005170 <_X_sip_init_attach> -400014d9: ff8346 j 400012ea <_l_boot> -400014dc: ffaae5 call8 40000f8c <_XX_unk0f84+0x8> -400014df: 020c movi.n a2, 0 -400014e1: f01d retw.n -400014e3: 000000 ill - -400014e6 <_l_strap_0x0x10>: -400014e6: 2a0c movi.n a10, 2 -400014e8: 03c865 call8 40005170 <_X_sip_init_attach> -400014eb: ff7ec6 j 400012ea <_l_boot> - ... - -400014f0 <_c_0xffff8fff>: -400014f0: ff .byte 0xff -400014f1: 8f .byte 0x8f -400014f2: ff .byte 0xff -400014f3: ff .byte 0xff - -400014f4 <_c_0x60008e00>: -400014f4: 008e00 any4 b0, b12:b13:b14:b15 -400014f7: 413660 srli a3, a6, 6 -400014fa: fdb100 excw -400014fd: ff .byte 0xff -400014fe: fffd91 l32r a9, 400014f4 <_c_0x60008e00> -40001501: 0020c0 memw -40001504: 9a29a2 l32i a10, a9, 0x268 -40001507: 10aab0 and a10, a10, a11 -4000150a: 0020c0 memw -4000150d: 9a69a2 s32i a10, a9, 0x268 -40001510: 0020c0 memw -40001513: 9a2982 l32i a8, a9, 0x268 -40001516: 0020c0 memw -40001519: 9a6982 s32i a8, a9, 0x268 -4000151c: 000106 j 40001524 <_c_0x60008e00+0x30> -4000151f: 000000 ill -40001522: 250070 extui a0, a7, 16, 3 -40001525: 6603c3 excw -40001528: f62a add.n a15, a6, a2 -4000152a: f01d retw.n - -4000152c <_s_waiting_for_host>: -4000152c: caac beqz.n a10, 4000155c <_XX_unk153c+0x20> -4000152e: ff .byte 0xff -4000152f: 3f .byte 0x3f -40001530: ffa000 excw -40001533: 3f .byte 0x3f -40001534: 002000 isync -40001537: 000000 ill -4000153a: 364004 excw - -4000153c <_XX_unk153c>: -4000153c: 004136 entry a1, 32 -4000153f: fefc31 l32r a3, 40001130 <_c_0x60003e00> -40001542: 1d0c movi.n a13, 1 -40001544: 8b1c movi.n a11, 24 -40001546: ac1c movi.n a12, 26 -40001548: 040c movi.n a4, 0 -4000154a: 852c movi.n a5, 40 -4000154c: 04ad mov.n a10, a4 -4000154e: 0020c0 memw -40001551: 8e2392 l32i a9, a3, 0x238 -40001554: 109950 and a9, a9, a5 -40001557: 83ad90 moveqz a10, a13, a9 -4000155a: 0020c0 memw -4000155d: 8e2382 l32i a8, a3, 0x238 -40001560: 048580 extui a8, a8, 5, 1 -40001563: 0ad856 bnez a8, 40001614 <_XX_unk153c+0xd8> -40001566: 0020c0 memw -40001569: 8e23e2 l32i a14, a3, 0x238 -4000156c: 54e0e0 extui a14, a14, 0, 6 -4000156f: c0eeb0 sub a14, a14, a11 -40001572: 09ee16 beqz a14, 40001614 <_XX_unk153c+0xd8> -40001575: 0020c0 memw -40001578: 8e23f2 l32i a15, a3, 0x238 -4000157b: 54f0f0 extui a15, a15, 0, 6 -4000157e: c0ffc0 sub a15, a15, a12 -40001581: 08ff16 beqz a15, 40001614 <_XX_unk153c+0xd8> -40001584: 0020c0 memw -40001587: 8e2382 l32i a8, a3, 0x238 -4000158a: 548080 extui a8, a8, 0, 6 -4000158d: e5c882 addi a8, a8, -27 -40001590: 124816 beqz a8, 400016b8 <_XX_unk153c+0x17c> -40001593: 0020c0 memw -40001596: 8e2392 l32i a9, a3, 0x238 -40001599: ec2c movi.n a12, 46 -4000159b: 549090 extui a9, a9, 0, 6 -4000159e: f8c992 addi a9, a9, -8 -400015a1: 0f5916 beqz a9, 4000169a <_XX_unk153c+0x15e> -400015a4: 0020c0 memw -400015a7: 8e23b2 l32i a11, a3, 0x238 -400015aa: 040c movi.n a4, 0 -400015ac: 10bbc0 and a11, a11, a12 -400015af: f4cbb2 addi a11, a11, -12 -400015b2: 0e4b16 beqz a11, 4000169a <_XX_unk153c+0x15e> -400015b5: 0ba0e2 movi a14, 11 -400015b8: 0020c0 memw -400015bb: 8e23d2 l32i a13, a3, 0x238 -400015be: 54d0d0 extui a13, a13, 0, 6 -400015c1: 1a1de7 beq a13, a14, 400015df <_XX_unk153c+0xa3> -400015c4: 0020c0 memw -400015c7: 8e23e2 l32i a14, a3, 0x238 -400015ca: 09a082 movi a8, 9 -400015cd: 54e0e0 extui a14, a14, 0, 6 -400015d0: 0b9e26 beqi a14, 10, 400015df <_XX_unk153c+0xa3> -400015d3: 0020c0 memw -400015d6: 8e23f2 l32i a15, a3, 0x238 -400015d9: 54f0f0 extui a15, a15, 0, 6 -400015dc: 089f87 bne a15, a8, 400015e8 <_XX_unk153c+0xac> -400015df: ffd3a1 l32r a10, 4000152c <_s_waiting_for_host> -400015e2: 012225 call8 40002804 -400015e5: 01a042 movi a4, 1 -400015e8: 0020c0 memw -400015eb: 8e2382 l32i a8, a3, 0x238 -400015ee: 178857 bany a8, a5, 40001609 <_XX_unk153c+0xcd> -400015f1: 01a0a2 movi a10, 1 -400015f4: 01f3e5 call8 40003534 <_X_uart_init> -400015f7: 0a0c movi.n a10, 0 -400015f9: 020a25 call8 4000369c <_X_uart_wait_tx_empty> -400015fc: 0a2c movi.n a10, 32 -400015fe: ffb065 call8 40001104 <_X_ets_isr_unmask> -40001601: 06b416 beqz a4, 40001670 <_XX_unk153c+0x134> -40001604: 0a2c movi.n a10, 32 -40001606: ffaf25 call8 400010f8 <_X_ets_isr_mask> -40001609: 532426 beqi a4, 2, 40001660 <_XX_unk153c+0x124> -4000160c: ffeea5 call8 400014f8 <_c_0x60008e00+0x4> -4000160f: f01d retw.n -40001611: 000000 ill -40001614: 1a2c movi.n a10, 33 -40001616: 0020c0 memw -40001619: 8e2392 l32i a9, a3, 0x238 -4000161c: 3949a7 ball a9, a10, 40001659 <_XX_unk153c+0x11d> -4000161f: 0020c0 memw -40001622: 8e23e2 l32i a14, a3, 0x238 -40001625: 54e0e0 extui a14, a14, 0, 6 -40001628: 2d1eb7 beq a14, a11, 40001659 <_XX_unk153c+0x11d> -4000162b: 0020c0 memw -4000162e: 8e23f2 l32i a15, a3, 0x238 -40001631: 1b0c movi.n a11, 1 -40001633: 54f0f0 extui a15, a15, 0, 6 -40001636: 1f1fc7 beq a15, a12, 40001659 <_XX_unk153c+0x11d> -40001639: 30a0e2 movi a14, 48 -4000163c: 20a440 or a10, a4, a4 -4000163f: 0020c0 memw -40001642: 8e23c2 l32i a12, a3, 0x238 -40001645: 10cce0 and a12, a12, a14 -40001648: e0ccc2 addi a12, a12, -32 -4000164b: 83adc0 moveqz a10, a13, a12 -4000164e: 74a0a0 extui a10, a10, 0, 8 -40001651: 02d1e5 call8 40004370 <_X_spi_flash_attach> -40001654: 006242 s32i a4, a2, 0 -40001657: f01d retw.n -40001659: 0b0c movi.n a11, 0 -4000165b: fff686 j 40001639 <_XX_unk153c+0xfd> -4000165e: a10000 sll a0, a0 -40001661: b1ffb4 excw -40001664: cdffb4 excw -40001667: 902502 l32i a0, a5, 0x240 -4000166a: f01d01 l32r a0, 3fffd6e0 <_start-0x2920> -4000166d: 000000 ill -40001670: 00a0a2 movi a10, 0 -40001673: 017e65 call8 40002e58 <_XX_unk2e58> -40001676: 7440a0 extui a4, a10, 0, 8 -40001679: f87456 bnez a4, 40001604 <_XX_unk153c+0xc8> -4000167c: 1a0c movi.n a10, 1 -4000167e: 017da5 call8 40002e58 <_XX_unk2e58> -40001681: 7440a0 extui a4, a10, 0, 8 -40001684: f7c456 bnez a4, 40001604 <_XX_unk153c+0xc8> -40001687: 03ad25 call8 40005158 <_XX_unk500c+0x14c> -4000168a: feca82 addi a8, a10, -2 -4000168d: 1f0c movi.n a15, 1 -4000168f: 040c movi.n a4, 0 -40001691: 834f80 moveqz a4, a15, a8 -40001694: fd8416 beqz a4, 40001670 <_XX_unk153c+0x134> -40001697: ffda46 j 40001604 <_XX_unk153c+0xc8> -4000169a: 00a0b2 movi a11, 0 -4000169d: 7440a0 extui a4, a10, 0, 8 -400016a0: 20a440 or a10, a4, a4 -400016a3: 01daa5 call8 4000344c <_c_0x000fffff+0x4> -400016a6: 20baa0 or a11, a10, a10 -400016a9: 04ad mov.n a10, a4 -400016ab: f4b0b0 extui a11, a11, 0, 16 -400016ae: 01e3a5 call8 400034e8 -400016b1: 240c movi.n a4, 2 -400016b3: ffbf86 j 400015b5 <_XX_unk153c+0x79> -400016b6: c10000 mul16u a0, a0, a0 -400016b9: c9ffa0 excw -400016bc: f01d02 l16ui a0, a13, 0x1e0 -400016bf: cae400 depbits a0, a4, 12, 15 -400016c2: ff .byte 0xff -400016c3: 3f .byte 0x3f -400016c4: cb18 l32i.n a1, a11, 48 -400016c6: ff .byte 0xff -400016c7: 3f .byte 0x3f -400016c8: ffcb24 excw -400016cb: 3f .byte 0x3f -400016cc: ffcb04 excw -400016cf: 3f .byte 0x3f -400016d0: ffc880 excw -400016d3: 3f .byte 0x3f -400016d4: ffcb50 excw -400016d7: 3f .byte 0x3f -400016d8: ffcac0 excw -400016db: 3f .byte 0x3f -400016dc: ffcad0 excw -400016df: 3f .byte 0x3f -400016e0: ffcb34 excw -400016e3: 3f .byte 0x3f -400016e4: ffcb44 excw -400016e7: 3f .byte 0x3f -400016e8: 00e136 entry a1, 112 -400016eb: 1a2c movi.n a10, 33 -400016ed: fe9081 l32r a8, 40001130 <_c_0x60003e00> -400016f0: 0020c0 memw -400016f3: 8e2892 l32i a9, a8, 0x238 -400016f6: 4249a7 ball a9, a10, 4000173c <_XX_unk153c+0x200> -400016f9: 8c1c movi.n a12, 24 -400016fb: 0020c0 memw -400016fe: 8e28b2 l32i a11, a8, 0x238 -40001701: 54b0b0 extui a11, a11, 0, 6 -40001704: 341bc7 beq a11, a12, 4000173c <_XX_unk153c+0x200> -40001707: 1aa0d2 movi a13, 26 -4000170a: 0020c0 memw -4000170d: 8e28c2 l32i a12, a8, 0x238 -40001710: 54c0c0 extui a12, a12, 0, 6 -40001713: 251cd7 beq a12, a13, 4000173c <_XX_unk153c+0x200> -40001716: 01a022 movi a2, 1 -40001719: 00a0a2 movi a10, 0 -4000171c: 20b110 or a11, a1, a1 -4000171f: 10a0c2 movi a12, 16 -40001722: 031765 call8 40004898 -40001725: facc bnez.n a10, 40001738 <_XX_unk153c+0x1fc> -40001727: 010162 l8ui a6, a1, 1 -4000172a: 030132 l8ui a3, a1, 3 -4000172d: 11c8 l32i.n a12, a1, 4 -4000172f: 0001d2 l8ui a13, a1, 0 -40001732: e9a0e2 movi a14, 233 -40001735: 081de7 beq a13, a14, 40001741 <_XX_unk153c+0x205> -40001738: 120c movi.n a2, 1 -4000173a: f01d retw.n -4000173c: 020c movi.n a2, 0 -4000173e: fff5c6 j 40001719 <_XX_unk153c+0x1dd> -40001741: 20b220 or a11, a2, a2 -40001744: 0201a2 l8ui a10, a1, 2 -40001747: 0e61c2 s32i a12, a1, 56 -4000174a: 02dee5 call8 40004538 <_X_SPIReadModeConfig> -4000174d: 348030 extui a8, a3, 0, 4 -40001750: 0fa032 movi a3, 15 -40001753: 0738f6 bgeui a8, 3, 4000175e <_XX_unk153c+0x222> -40001756: a82b addi.n a10, a8, 2 -40001758: 74a0a0 extui a10, a10, 0, 8 -4000175b: 000046 j 40001760 <_XX_unk153c+0x224> -4000175e: 1a0c movi.n a10, 1 -40001760: 02f165 call8 40004678 <_X_SPIReadModeConfig+0x140> -40001763: 0c0182 l8ui a8, a1, 12 -40001766: 0e0192 l8ui a9, a1, 14 -40001769: 0f0152 l8ui a5, a1, 15 -4000176c: 0b0122 l8ui a2, a1, 11 -4000176f: 115580 slli a5, a5, 8 -40001772: 112280 slli a2, a2, 8 -40001775: 205590 or a5, a5, a9 -40001778: 0d0192 l8ui a9, a1, 13 -4000177b: 115580 slli a5, a5, 8 -4000177e: 205590 or a5, a5, a9 -40001781: 115580 slli a5, a5, 8 -40001784: 0a0192 l8ui a9, a1, 10 -40001787: 205580 or a5, a5, a8 -4000178a: 080182 l8ui a8, a1, 8 -4000178d: 202290 or a2, a2, a9 -40001790: 090192 l8ui a9, a1, 9 -40001793: 112280 slli a2, a2, 8 -40001796: 202290 or a2, a2, a9 -40001799: 112280 slli a2, a2, 8 -4000179c: 202280 or a2, a2, a8 -4000179f: 3c1296 bltz a2, 40001b64 <_XX_unk153c+0x628> -400017a2: 031c movi.n a3, 16 -400017a4: 20a330 or a10, a3, a3 -400017a7: 20b110 or a11, a1, a1 -400017aa: 10a0c2 movi a12, 16 -400017ad: 030ea5 call8 40004898 -400017b0: f84a56 bnez a10, 40001738 <_XX_unk153c+0x1fc> -400017b3: 10c332 addi a3, a3, 16 -400017b6: 278616 beqz a6, 40001a32 <_XX_unk153c+0x4f6> -400017b9: f119 s32i.n a1, a1, 60 -400017bb: efa042 movi a4, 239 -400017be: 017d mov.n a7, a1 -400017c0: c159 s32i.n a5, a1, 48 -400017c2: 6169 s32i.n a6, a1, 24 -400017c4: 02bd mov.n a11, a2 -400017c6: 0d0c movi.n a13, 0 -400017c8: d1d9 s32i.n a13, a1, 52 -400017ca: 0b2d mov.n a2, a11 -400017cc: ffbda1 l32r a10, 400016c0 <_XX_unk153c+0x184> -400017cf: c1c8 l32i.n a12, a1, 48 -400017d1: d188 l32i.n a8, a1, 52 -400017d3: 051c movi.n a5, 16 -400017d5: c05580 sub a5, a5, a8 -400017d8: 745050 extui a5, a5, 0, 8 -400017db: 05dd mov.n a13, a5 -400017dd: 010265 call8 40002804 -400017e0: 1a0c movi.n a10, 1 -400017e2: c198 l32i.n a9, a1, 48 -400017e4: a1a9 s32i.n a10, a1, 40 -400017e6: 03b957 bgeu a9, a5, 400017ed <_XX_unk153c+0x2b1> -400017e9: 0b0c movi.n a11, 0 -400017eb: a1b9 s32i.n a11, a1, 40 -400017ed: 0f0c movi.n a15, 0 -400017ef: d1e8 l32i.n a14, a1, 52 -400017f1: a1c8 l32i.n a12, a1, 40 -400017f3: 81c9 s32i.n a12, a1, 32 -400017f5: 93efc0 movnez a14, a15, a12 -400017f8: 91e9 s32i.n a14, a1, 36 -400017fa: 3b2c16 beqz a12, 40001bb0 <_XX_unk153c+0x674> -400017fd: 380c movi.n a8, 3 -400017ff: 028857 bany a8, a5, 40001805 <_XX_unk153c+0x2c9> -40001802: 008d86 j 40001a3c <_XX_unk153c+0x500> -40001805: 0215e6 bgei a5, 1, 4000180b <_XX_unk153c+0x2cf> -40001808: 002206 j 40001894 <_XX_unk153c+0x358> -4000180b: 249050 extui a9, a5, 0, 3 -4000180e: 139976 loopnez a9, 40001825 <_XX_unk153c+0x2e9> -40001811: f188 l32i.n a8, a1, 60 -40001813: 000892 l8ui a9, a8, 0 -40001816: 004292 s8i a9, a2, 0 -40001819: 881b addi.n a8, a8, 1 -4000181b: f189 s32i.n a8, a1, 60 -4000181d: 221b addi.n a2, a2, 1 -4000181f: 304940 xor a4, a9, a4 -40001822: 744040 extui a4, a4, 0, 8 -40001825: f188 l32i.n a8, a1, 60 -40001827: 419350 srli a9, a5, 3 -4000182a: 639976 loopnez a9, 40001891 <_XX_unk153c+0x355> -4000182d: 000892 l8ui a9, a8, 0 -40001830: 004292 s8i a9, a2, 0 -40001833: 309940 xor a9, a9, a4 -40001836: 749090 extui a9, a9, 0, 8 -40001839: 010842 l8ui a4, a8, 1 -4000183c: 014242 s8i a4, a2, 1 -4000183f: 309490 xor a9, a4, a9 -40001842: 749090 extui a9, a9, 0, 8 -40001845: 020842 l8ui a4, a8, 2 -40001848: 024242 s8i a4, a2, 2 -4000184b: 309490 xor a9, a4, a9 -4000184e: 749090 extui a9, a9, 0, 8 -40001851: 030842 l8ui a4, a8, 3 -40001854: 034242 s8i a4, a2, 3 -40001857: 309490 xor a9, a4, a9 -4000185a: 749090 extui a9, a9, 0, 8 -4000185d: 040842 l8ui a4, a8, 4 -40001860: 044242 s8i a4, a2, 4 -40001863: 309490 xor a9, a4, a9 -40001866: 749090 extui a9, a9, 0, 8 -40001869: 050842 l8ui a4, a8, 5 -4000186c: 054242 s8i a4, a2, 5 -4000186f: 309490 xor a9, a4, a9 -40001872: 749090 extui a9, a9, 0, 8 -40001875: 060842 l8ui a4, a8, 6 -40001878: 064242 s8i a4, a2, 6 -4000187b: 309490 xor a9, a4, a9 -4000187e: 749090 extui a9, a9, 0, 8 -40001881: 070842 l8ui a4, a8, 7 -40001884: 074242 s8i a4, a2, 7 -40001887: 309490 xor a9, a4, a9 -4000188a: 888b addi.n a8, a8, 8 -4000188c: 228b addi.n a2, a2, 8 -4000188e: 744090 extui a4, a9, 0, 8 -40001891: 0f6182 s32i a8, a1, 60 -40001894: c168 l32i.n a6, a1, 48 -40001896: a1a8 l32i.n a10, a1, 40 -40001898: c06650 sub a6, a6, a5 -4000189b: 416460 srli a6, a6, 4 -4000189e: 35ea16 beqz a10, 40001c00 <_XX_unk153c+0x6c4> -400018a1: d198 l32i.n a9, a1, 52 -400018a3: c188 l32i.n a8, a1, 48 -400018a5: 889a add.n a8, a8, a9 -400018a7: f0c882 addi a8, a8, -16 -400018aa: 348080 extui a8, a8, 0, 4 -400018ad: 076182 s32i a8, a1, 28 -400018b0: 91a8 l32i.n a10, a1, 36 -400018b2: 74a0a0 extui a10, a10, 0, 8 -400018b5: d1a9 s32i.n a10, a1, 52 -400018b7: 3e16a6 blti a6, 1, 400018f9 <_XX_unk153c+0x3bd> -400018ba: 050c movi.n a5, 0 -400018bc: 20a330 or a10, a3, a3 -400018bf: 20b110 or a11, a1, a1 -400018c2: 10a0c2 movi a12, 16 -400018c5: 02fd25 call8 40004898 -400018c8: 1fca56 bnez a10, 40001ac8 <_XX_unk153c+0x58c> -400018cb: 080c movi.n a8, 0 -400018cd: 10c332 addi a3, a3, 16 -400018d0: 091c movi.n a9, 16 -400018d2: 10a976 loopgtz a9, 400018e6 <_XX_unk153c+0x3aa> -400018d5: 987a add.n a9, a8, a7 -400018d7: 01c882 addi a8, a8, 1 -400018da: 000992 l8ui a9, a9, 0 -400018dd: 748080 extui a8, a8, 0, 8 -400018e0: 309940 xor a9, a9, a4 -400018e3: 744090 extui a4, a9, 0, 8 -400018e6: 20a220 or a10, a2, a2 -400018e9: 20b110 or a11, a1, a1 -400018ec: 0c1c movi.n a12, 16 -400018ee: 050865 call8 40006974 -400018f1: 10c222 addi a2, a2, 16 -400018f4: 551b addi.n a5, a5, 1 -400018f6: c29567 bne a5, a6, 400018bc <_XX_unk153c+0x380> -400018f9: 0821b2 l32i a11, a1, 32 -400018fc: 011b16 beqz a11, 40001911 <_XX_unk153c+0x3d5> -400018ff: 20a330 or a10, a3, a3 -40001902: 01bd mov.n a11, a1 -40001904: 0c1c movi.n a12, 16 -40001906: 02f925 call8 40004898 -40001909: 3afa56 bnez a10, 40001cbc <_XX_unk153c+0x780> -4000190c: 10c332 addi a3, a3, 16 -4000190f: f119 s32i.n a1, a1, 60 -40001911: 072152 l32i a5, a1, 28 -40001914: ff6ca1 l32r a10, 400016c4 <_XX_unk153c+0x188> -40001917: 745050 extui a5, a5, 0, 8 -4000191a: 20b550 or a11, a5, a5 -4000191d: 00ee65 call8 40002804 -40001920: 03a082 movi a8, 3 -40001923: 028857 bany a8, a5, 40001929 <_XX_unk153c+0x3ed> -40001926: 006b86 j 40001ad8 <_XX_unk153c+0x59c> -40001929: 0215e6 bgei a5, 1, 4000192f <_XX_unk153c+0x3f3> -4000192c: 002206 j 400019b8 <_XX_unk153c+0x47c> -4000192f: 249050 extui a9, a5, 0, 3 -40001932: 139976 loopnez a9, 40001949 <_XX_unk153c+0x40d> -40001935: f188 l32i.n a8, a1, 60 -40001937: 000892 l8ui a9, a8, 0 -4000193a: 004292 s8i a9, a2, 0 -4000193d: 881b addi.n a8, a8, 1 -4000193f: f189 s32i.n a8, a1, 60 -40001941: 221b addi.n a2, a2, 1 -40001943: 304940 xor a4, a9, a4 -40001946: 744040 extui a4, a4, 0, 8 -40001949: f188 l32i.n a8, a1, 60 -4000194b: 419350 srli a9, a5, 3 -4000194e: 639976 loopnez a9, 400019b5 <_XX_unk153c+0x479> -40001951: 000892 l8ui a9, a8, 0 -40001954: 004292 s8i a9, a2, 0 -40001957: 309940 xor a9, a9, a4 -4000195a: 749090 extui a9, a9, 0, 8 -4000195d: 010842 l8ui a4, a8, 1 -40001960: 014242 s8i a4, a2, 1 -40001963: 309490 xor a9, a4, a9 -40001966: 749090 extui a9, a9, 0, 8 -40001969: 020842 l8ui a4, a8, 2 -4000196c: 024242 s8i a4, a2, 2 -4000196f: 309490 xor a9, a4, a9 -40001972: 749090 extui a9, a9, 0, 8 -40001975: 030842 l8ui a4, a8, 3 -40001978: 034242 s8i a4, a2, 3 -4000197b: 309490 xor a9, a4, a9 -4000197e: 749090 extui a9, a9, 0, 8 -40001981: 040842 l8ui a4, a8, 4 -40001984: 044242 s8i a4, a2, 4 -40001987: 309490 xor a9, a4, a9 -4000198a: 749090 extui a9, a9, 0, 8 -4000198d: 050842 l8ui a4, a8, 5 -40001990: 054242 s8i a4, a2, 5 -40001993: 309490 xor a9, a4, a9 -40001996: 749090 extui a9, a9, 0, 8 -40001999: 060842 l8ui a4, a8, 6 -4000199c: 064242 s8i a4, a2, 6 -4000199f: 309490 xor a9, a4, a9 -400019a2: 749090 extui a9, a9, 0, 8 -400019a5: 070842 l8ui a4, a8, 7 -400019a8: 074242 s8i a4, a2, 7 -400019ab: 309490 xor a9, a4, a9 -400019ae: 888b addi.n a8, a8, 8 -400019b0: 228b addi.n a2, a2, 8 -400019b2: 744090 extui a4, a9, 0, 8 -400019b5: 0f6182 s32i a8, a1, 60 -400019b8: ff44a1 l32r a10, 400016c8 <_XX_unk153c+0x18c> -400019bb: 04bd mov.n a11, a4 -400019bd: 00e465 call8 40002804 -400019c0: 61a8 l32i.n a10, a1, 24 -400019c2: aa0b addi.n a10, a10, -1 -400019c4: 2d5a16 beqz a10, 40001c9d <_XX_unk153c+0x761> -400019c7: d128 l32i.n a2, a1, 52 -400019c9: 061c movi.n a6, 16 -400019cb: 252a add.n a2, a5, a2 -400019cd: c06620 sub a6, a6, a2 -400019d0: 746060 extui a6, a6, 0, 8 -400019d3: 0286f6 bgeui a6, 8, 400019d9 <_XX_unk153c+0x49d> -400019d6: 0078c6 j 40001bbd <_XX_unk153c+0x681> -400019d9: f158 l32i.n a5, a1, 60 -400019db: a28b addi.n a10, a2, 8 -400019dd: 74a0a0 extui a10, a10, 0, 8 -400019e0: d1a9 s32i.n a10, a1, 52 -400019e2: 040562 l8ui a6, a5, 4 -400019e5: 0005c2 l8ui a12, a5, 0 -400019e8: 050582 l8ui a8, a5, 5 -400019eb: 060592 l8ui a9, a5, 6 -400019ee: 0105d2 l8ui a13, a5, 1 -400019f1: 0305b2 l8ui a11, a5, 3 -400019f4: 0205e2 l8ui a14, a5, 2 -400019f7: 11bb80 slli a11, a11, 8 -400019fa: 20bbe0 or a11, a11, a14 -400019fd: 11bb80 slli a11, a11, 8 -40001a00: 20bbd0 or a11, a11, a13 -40001a03: 11bb80 slli a11, a11, 8 -40001a06: 070552 l8ui a5, a5, 7 -40001a09: 20bbc0 or a11, a11, a12 -40001a0c: 115580 slli a5, a5, 8 -40001a0f: 205590 or a5, a5, a9 -40001a12: 115580 slli a5, a5, 8 -40001a15: 205580 or a5, a5, a8 -40001a18: 115580 slli a5, a5, 8 -40001a1b: 205560 or a5, a5, a6 -40001a1e: c159 s32i.n a5, a1, 48 -40001a20: 6188 l32i.n a8, a1, 24 -40001a22: d198 l32i.n a9, a1, 52 -40001a24: a80b addi.n a10, a8, -1 -40001a26: 997a add.n a9, a9, a7 -40001a28: f199 s32i.n a9, a1, 60 -40001a2a: 7480a0 extui a8, a10, 0, 8 -40001a2d: 6189 s32i.n a8, a1, 24 -40001a2f: d97856 bnez a8, 400017ca <_XX_unk153c+0x28e> -40001a32: e1a8 l32i.n a10, a1, 56 -40001a34: ff6de5 call8 40001114 <_XX_set_0x3fffda30_0> -40001a37: 020c movi.n a2, 0 -40001a39: f01d retw.n -40001a3b: 451600 extui a1, a0, 22, 5 -40001a3e: 02ade5 call8 4000451c -40001a41: 41d250 srli a13, a5, 2 -40001a44: 7a1da6 blti a13, 1, 40001ac2 <_XX_unk153c+0x586> -40001a47: 276527 bbci a5, 2, 40001a72 <_XX_unk153c+0x536> -40001a4a: f198 l32i.n a9, a1, 60 -40001a4c: a24b addi.n a10, a2, 4 -40001a4e: 0009c2 l8ui a12, a9, 0 -40001a51: 0988 l32i.n a8, a9, 0 -40001a53: 0289 s32i.n a8, a2, 0 -40001a55: 75b080 extui a11, a8, 16, 8 -40001a58: 74e880 extui a14, a8, 8, 8 -40001a5b: 756880 extui a6, a8, 24, 8 -40001a5e: 994b addi.n a9, a9, 4 -40001a60: 0f6192 s32i a9, a1, 60 -40001a63: 306640 xor a6, a6, a4 -40001a66: 304ce0 xor a4, a12, a14 -40001a69: 304b40 xor a4, a11, a4 -40001a6c: 304460 xor a4, a4, a6 -40001a6f: 744040 extui a4, a4, 0, 8 -40001a72: 0afd mov.n a15, a10 -40001a74: f188 l32i.n a8, a1, 60 -40001a76: 4121d0 srli a2, a13, 1 -40001a79: 419276 loopnez a2, 40001abe <_XX_unk153c+0x582> -40001a7c: 030892 l8ui a9, a8, 3 -40001a7f: 0108b2 l8ui a11, a8, 1 -40001a82: 0008c2 l8ui a12, a8, 0 -40001a85: 0208d2 l8ui a13, a8, 2 -40001a88: 08e8 l32i.n a14, a8, 0 -40001a8a: 309940 xor a9, a9, a4 -40001a8d: 30bcb0 xor a11, a12, a11 -40001a90: 0fe9 s32i.n a14, a15, 0 -40001a92: 30bdb0 xor a11, a13, a11 -40001a95: 0708c2 l8ui a12, a8, 7 -40001a98: 0508d2 l8ui a13, a8, 5 -40001a9b: 0408e2 l8ui a14, a8, 4 -40001a9e: 060842 l8ui a4, a8, 6 -40001aa1: 1828 l32i.n a2, a8, 4 -40001aa3: 309b90 xor a9, a11, a9 -40001aa6: 30bed0 xor a11, a14, a13 -40001aa9: 1f29 s32i.n a2, a15, 4 -40001aab: 888b addi.n a8, a8, 8 -40001aad: 749090 extui a9, a9, 0, 8 -40001ab0: 30b4b0 xor a11, a4, a11 -40001ab3: ff8b addi.n a15, a15, 8 -40001ab5: 309c90 xor a9, a12, a9 -40001ab8: 309b90 xor a9, a11, a9 -40001abb: 744090 extui a4, a9, 0, 8 -40001abe: f189 s32i.n a8, a1, 60 -40001ac0: 0fad mov.n a10, a15 -40001ac2: 0a2d mov.n a2, a10 -40001ac4: ff7306 j 40001894 <_XX_unk153c+0x358> -40001ac7: 01a100 slli a10, a1, 32 -40001aca: ff .byte 0xff -40001acb: ff01b1 l32r a11, 400016d0 <_XX_unk153c+0x194> -40001ace: 00d365 call8 40002804 -40001ad1: 120c movi.n a2, 1 -40001ad3: f01d retw.n -40001ad5: 000000 ill -40001ad8: edc516 beqz a5, 400019b8 <_XX_unk153c+0x47c> -40001adb: 02ad mov.n a10, a2 -40001add: 41d250 srli a13, a5, 2 -40001ae0: 021de6 bgei a13, 1, 40001ae6 <_XX_unk153c+0x5aa> -40001ae3: ffb446 j 400019b8 <_XX_unk153c+0x47c> -40001ae6: 286527 bbci a5, 2, 40001b12 <_XX_unk153c+0x5d6> -40001ae9: f198 l32i.n a9, a1, 60 -40001aeb: a24b addi.n a10, a2, 4 -40001aed: 0009c2 l8ui a12, a9, 0 -40001af0: 0988 l32i.n a8, a9, 0 -40001af2: 0289 s32i.n a8, a2, 0 -40001af4: 75b080 extui a11, a8, 16, 8 -40001af7: 74e880 extui a14, a8, 8, 8 -40001afa: 756880 extui a6, a8, 24, 8 -40001afd: 04c992 addi a9, a9, 4 -40001b00: 0f6192 s32i a9, a1, 60 -40001b03: 306640 xor a6, a6, a4 -40001b06: 304ce0 xor a4, a12, a14 -40001b09: 304b40 xor a4, a11, a4 -40001b0c: 304460 xor a4, a4, a6 -40001b0f: 744040 extui a4, a4, 0, 8 -40001b12: 0afd mov.n a15, a10 -40001b14: f188 l32i.n a8, a1, 60 -40001b16: 4121d0 srli a2, a13, 1 -40001b19: 419276 loopnez a2, 40001b5e <_XX_unk153c+0x622> -40001b1c: 030892 l8ui a9, a8, 3 -40001b1f: 0108b2 l8ui a11, a8, 1 -40001b22: 0008c2 l8ui a12, a8, 0 -40001b25: 0208d2 l8ui a13, a8, 2 -40001b28: 08e8 l32i.n a14, a8, 0 -40001b2a: 309940 xor a9, a9, a4 -40001b2d: 30bcb0 xor a11, a12, a11 -40001b30: 0fe9 s32i.n a14, a15, 0 -40001b32: 30bdb0 xor a11, a13, a11 -40001b35: 0708c2 l8ui a12, a8, 7 -40001b38: 0508d2 l8ui a13, a8, 5 -40001b3b: 0408e2 l8ui a14, a8, 4 -40001b3e: 060842 l8ui a4, a8, 6 -40001b41: 1828 l32i.n a2, a8, 4 -40001b43: 309b90 xor a9, a11, a9 -40001b46: 30bed0 xor a11, a14, a13 -40001b49: 1f29 s32i.n a2, a15, 4 -40001b4b: 888b addi.n a8, a8, 8 -40001b4d: 749090 extui a9, a9, 0, 8 -40001b50: 30b4b0 xor a11, a4, a11 -40001b53: ff8b addi.n a15, a15, 8 -40001b55: 309c90 xor a9, a12, a9 -40001b58: 309b90 xor a9, a11, a9 -40001b5b: 744090 extui a4, a9, 0, 8 -40001b5e: f189 s32i.n a8, a1, 60 -40001b60: ff9506 j 400019b8 <_XX_unk153c+0x47c> -40001b63: 402000 ssa8l a0 -40001b66: c58275 call12 3ffc738c <_start-0x38c74> -40001b69: 87f8 l32i.n a15, a7, 32 -40001b6b: 060203 excw -40001b6e: 500032 l8ui a3, a0, 80 -40001b71: 16f474 excw -40001b74: 0c0917 bnone a9, a1, 40001b84 <_XX_unk153c+0x648> -40001b77: 031c05 call0 40004d38 -40001b7a: 918b addi.n a9, a1, 8 -40001b7c: b199 s32i.n a9, a1, 44 -40001b7e: 03ad mov.n a10, a3 -40001b80: 01bd mov.n a11, a1 -40001b82: 0c1c movi.n a12, 16 -40001b84: 02d125 call8 40004898 -40001b87: ea7a56 bnez a10, 40001a32 <_XX_unk153c+0x4f6> -40001b8a: 10c332 addi a3, a3, 16 -40001b8d: 449c beqz.n a4, 40001ba5 <_XX_unk153c+0x669> -40001b8f: 01ad mov.n a10, a1 -40001b91: 0247e5 call8 40004010 <_XX_unk4010> -40001b94: 440b addi.n a4, a4, -1 -40001b96: f44040 extui a4, a4, 0, 16 -40001b99: 848c beqz.n a4, 40001ba5 <_XX_unk153c+0x669> -40001b9b: b1a8 l32i.n a10, a1, 44 -40001b9d: 024725 call8 40004010 <_XX_unk4010> -40001ba0: 440b addi.n a4, a4, -1 -40001ba2: f44040 extui a4, a4, 0, 16 -40001ba5: 551b addi.n a5, a5, 1 -40001ba7: f45050 extui a5, a5, 0, 16 -40001baa: d09757 bne a7, a5, 40001b7e <_XX_unk153c+0x642> -40001bad: 001646 j 40001c0a <_XX_unk153c+0x6ce> -40001bb0: 7480e0 extui a8, a14, 0, 8 -40001bb3: c198 l32i.n a9, a1, 48 -40001bb5: 7199 s32i.n a9, a1, 28 -40001bb7: d189 s32i.n a8, a1, 52 -40001bb9: ff4f06 j 400018f9 <_XX_unk153c+0x3bd> -40001bbc: 05cd00 extui a12, a0, 29, 1 -40001bbf: fec5a1 l32r a10, 400016d4 <_XX_unk153c+0x198> -40001bc2: d1b8 l32i.n a11, a1, 52 -40001bc4: 06dd mov.n a13, a6 -40001bc6: 00c3e5 call8 40002804 -40001bc9: b27a add.n a11, a2, a7 -40001bcb: 10c1a2 addi a10, a1, 16 -40001bce: 06cd mov.n a12, a6 -40001bd0: 04da25 call8 40006974 -40001bd3: 03ad mov.n a10, a3 -40001bd5: 01bd mov.n a11, a1 -40001bd7: 0c1c movi.n a12, 16 -40001bd9: 02cbe5 call8 40004898 -40001bdc: 0eca56 bnez a10, 40001ccc <_XX_unk153c+0x790> -40001bdf: 10c332 addi a3, a3, 16 -40001be2: 01bd mov.n a11, a1 -40001be4: 8c0c movi.n a12, 8 -40001be6: 10c1a2 addi a10, a1, 16 -40001be9: a6aa add.n a10, a6, a10 -40001beb: c0cc60 sub a12, a12, a6 -40001bee: 74c0c0 extui a12, a12, 0, 8 -40001bf1: d1c9 s32i.n a12, a1, 52 -40001bf3: 04d825 call8 40006974 -40001bf6: 41b8 l32i.n a11, a1, 16 -40001bf8: 51c8 l32i.n a12, a1, 20 -40001bfa: c1c9 s32i.n a12, a1, 48 -40001bfc: ff8806 j 40001a20 <_XX_unk153c+0x4e4> -40001bff: c1d800 mul16u a13, a8, a0 -40001c02: 71d9 s32i.n a13, a1, 28 -40001c04: ff2a06 j 400018b0 <_XX_unk153c+0x374> -40001c07: 031c00 excw -40001c0a: 03ad mov.n a10, a3 -40001c0c: 01bd mov.n a11, a1 -40001c0e: 0c1c movi.n a12, 16 -40001c10: 02c865 call8 40004898 -40001c13: b21a56 bnez a10, 40001738 <_XX_unk153c+0x1fc> -40001c16: 10c332 addi a3, a3, 16 -40001c19: 448c beqz.n a4, 40001c21 <_XX_unk153c+0x6e5> -40001c1b: 20a110 or a10, a1, a1 -40001c1e: 023f25 call8 40004010 <_XX_unk4010> -40001c21: 744020 extui a4, a2, 0, 8 -40001c24: 04ad mov.n a10, a4 -40001c26: 0299a5 call8 400045c0 <_X_SPIReadModeConfig+0x88> -40001c29: 348820 extui a8, a2, 8, 4 -40001c2c: 1838f6 bgeui a8, 3, 40001c48 <_XX_unk153c+0x70c> -40001c2f: 282b addi.n a2, a8, 2 -40001c31: 742020 extui a2, a2, 0, 8 -40001c34: 000486 j 40001c4a <_XX_unk153c+0x70e> -40001c37: a10000 sll a0, a0 -40001c3a: 50fea7 bbsi a14, 26, 40001c8e <_XX_unk153c+0x752> -40001c3d: 6520b5 call12 40066e48 <__bss_start+0x56e48> -40001c40: 00bc beqz.n a0, 40001c74 <_XX_unk153c+0x738> -40001c42: 01a022 movi a2, 1 -40001c45: 000090 retw -40001c48: 120c movi.n a2, 1 -40001c4a: 20c440 or a12, a4, a4 -40001c4d: fea3a1 l32r a10, 400016dc <_XX_unk153c+0x1a0> -40001c50: 20b220 or a11, a2, a2 -40001c53: 00bb25 call8 40002804 -40001c56: 20a220 or a10, a2, a2 -40001c59: 02a1e5 call8 40004678 <_X_SPIReadModeConfig+0x140> -40001c5c: 0e0192 l8ui a9, a1, 14 -40001c5f: 080152 l8ui a5, a1, 8 -40001c62: 0b0122 l8ui a2, a1, 11 -40001c65: 0a0182 l8ui a8, a1, 10 -40001c68: 112280 slli a2, a2, 8 -40001c6b: 202280 or a2, a2, a8 -40001c6e: 090182 l8ui a8, a1, 9 -40001c71: 112280 slli a2, a2, 8 -40001c74: 202280 or a2, a2, a8 -40001c77: 112280 slli a2, a2, 8 -40001c7a: 202250 or a2, a2, a5 -40001c7d: 0f0152 l8ui a5, a1, 15 -40001c80: 0c0182 l8ui a8, a1, 12 -40001c83: 115580 slli a5, a5, 8 -40001c86: 205590 or a5, a5, a9 -40001c89: 0d0192 l8ui a9, a1, 13 -40001c8c: 115580 slli a5, a5, 8 -40001c8f: 205590 or a5, a5, a9 -40001c92: 115580 slli a5, a5, 8 -40001c95: 205580 or a5, a5, a8 -40001c98: fec206 j 400017a4 <_XX_unk153c+0x268> -40001c9b: a10000 sll a0, a0 -40001c9e: 40fe90 nsau a9, a14 -40001ca1: 2520b4 excw -40001ca4: 8200b6 bltui a0, 0x8000, 40001c2a <_XX_unk153c+0x6ee> -40001ca7: 400f01 l32r a0, 3ffd1ce4 <_start-0x2e31c> -40001caa: c088 l32i.n a8, a0, 48 -40001cac: d82816 beqz a8, 40001a32 <_XX_unk153c+0x4f6> -40001caf: fe8da1 l32r a10, 400016e4 <_XX_unk153c+0x1a8> -40001cb2: 00b525 call8 40002804 -40001cb5: 01a022 movi a2, 1 -40001cb8: f01d retw.n -40001cba: a10000 sll a0, a0 -40001cbd: b2fe84 excw -40001cc0: 25efa2 s32c1i a10, a15, 148 -40001cc3: 0c00b4 excw -40001cc6: f01d12 l16ui a1, a13, 0x1e0 -40001cc9: 000000 ill -40001ccc: fe80a1 l32r a10, 400016cc <_XX_unk153c+0x190> -40001ccf: 2ca3b2 movi a11, 0x32c -40001cd2: 00b325 call8 40002804 -40001cd5: ff5646 j 40001a32 <_XX_unk153c+0x4f6> - -40001cd8 <_c_data_end>: -40001cd8: ffc864 excw -40001cdb: 3f .byte 0x3f - -40001cdc <_s_mem_banner>: -40001cdc: cb68 l32i.n a6, a11, 48 -40001cde: ff .byte 0xff -40001cdf: 3f .byte 0x3f - -40001ce0 <_c_stack_sentry>: -40001ce0: ffe1d0 excw -40001ce3: 3f .byte 0x3f - -40001ce4 <_c_data_start>: -40001ce4: ffc000 excw -40001ce7: 3f .byte 0x3f - -40001ce8 <_X_print_mem_banner>: -40001ce8: 006136 entry a1, 48 -40001ceb: fffca1 l32r a10, 40001cdc <_s_mem_banner> -40001cee: fffcb1 l32r a11, 40001ce0 <_c_stack_sentry> -40001cf1: fa83c1 l32r a12, 40000700 <_c_stack> -40001cf4: fa85d1 l32r a13, 40000708 <_c_bss_start> -40001cf7: fa85e1 l32r a14, 4000070c <_c_bss_end> -40001cfa: fffaf1 l32r a15, 40001ce4 <_c_data_start> -40001cfd: fff681 l32r a8, 40001cd8 <_c_data_end> -40001d00: 0189 s32i.n a8, a1, 0 -40001d02: 00b025 call8 40002804 -40001d05: f01d retw.n -40001d07: da4000 depbits a0, a0, 13, 5 -40001d0a: ff .byte 0xff -40001d0b: 3f .byte 0x3f - -40001d0c <_s_exc_sp_fmt>: -40001d0c: ffcbe4 excw -40001d0f: 3f .byte 0x3f - -40001d10 <_s_exc_sf_dump_fmt>: -40001d10: cbec bnez.n a11, 40001d40 <_X_exc_handler+0x28> -40001d12: ff .byte 0xff -40001d13: 3f .byte 0x3f - -40001d14 <_s_exc_regs_fmt>: -40001d14: cc2c movi.n a12, 44 -40001d16: ff .byte 0xff -40001d17: 3f .byte 0x3f - -40001d18 <_X_exc_handler>: -40001d18: 004136 entry a1, 32 -40001d1b: 012d mov.n a2, a1 -40001d1d: 03e765 call8 40005b94 <_XX_xtos_exc_unk5b94> -40001d20: 030c movi.n a3, 0 -40001d22: f8add2 movi a13, 0xfffffdf8 -40001d25: fff841 l32r a4, 40001d08 <_X_print_mem_banner+0x20> -40001d28: 580c movi.n a8, 5 -40001d2a: 209440 or a9, a4, a4 -40001d2d: 1ea876 loopgtz a8, 40001d4f <_X_exc_handler+0x37> -40001d30: c2da add.n a12, a2, a13 -40001d32: 7e2cb2 l32i a11, a12, 0x1f8 -40001d35: 09b9 s32i.n a11, a9, 0 -40001d37: 7f2ca2 l32i a10, a12, 0x1fc -40001d3a: 19a9 s32i.n a10, a9, 4 -40001d3c: 802ce2 l32i a14, a12, 0x200 -40001d3f: 29e9 s32i.n a14, a9, 8 -40001d41: 812cc2 l32i a12, a12, 0x204 -40001d44: 39c9 s32i.n a12, a9, 12 -40001d46: 5b8c beqz.n a11, 40001d4f <_X_exc_handler+0x37> -40001d48: 10c992 addi a9, a9, 16 -40001d4b: 0a2d mov.n a2, a10 -40001d4d: 331b addi.n a3, a3, 1 -40001d4f: ffefa1 l32r a10, 40001d0c <_s_exc_sp_fmt> -40001d52: 02bd mov.n a11, a2 -40001d54: 00aae5 call8 40002804 -40001d57: 020c movi.n a2, 0 -40001d59: 1713a6 blti a3, 1, 40001d74 <_X_exc_handler+0x5c> -40001d5c: ffeda1 l32r a10, 40001d10 <_s_exc_sf_dump_fmt> -40001d5f: 02bd mov.n a11, a2 -40001d61: 04c8 l32i.n a12, a4, 0 -40001d63: 14d8 l32i.n a13, a4, 4 -40001d65: 24e8 l32i.n a14, a4, 8 -40001d67: 34f8 l32i.n a15, a4, 12 -40001d69: 00a9a5 call8 40002804 -40001d6c: 10c442 addi a4, a4, 16 -40001d6f: 221b addi.n a2, a2, 1 -40001d71: e79237 bne a2, a3, 40001d5c <_X_exc_handler+0x44> -40001d74: ffe8a1 l32r a10, 40001d14 <_s_exc_regs_fmt> -40001d77: 03b1b0 rsr.epc1 a11 -40001d7a: 03b2c0 rsr.epc2 a12 -40001d7d: 03b3d0 rsr.epc3 a13 -40001d80: 03eee0 rsr.excvaddr a14 -40001d83: 03c0f0 rsr.depc a15 -40001d86: 00a7e5 call8 40002804 -40001d89: ffff06 j 40001d89 <_X_exc_handler+0x71> -40001d8c: ffcc74 excw -40001d8f: 3f .byte 0x3f - -40001d90 <_XX_unk1d90>: -40001d90: 004136 entry a1, 32 -40001d93: 03e8b0 rsr.exccause a11 -40001d96: fffda1 l32r a10, 40001d8c <_X_exc_handler+0x74> -40001d99: 00a6a5 call8 40002804 -40001d9c: ffdea1 l32r a10, 40001d14 <_s_exc_regs_fmt> -40001d9f: 03b1b0 rsr.epc1 a11 -40001da2: 03b2c0 rsr.epc2 a12 -40001da5: 03b3d0 rsr.epc3 a13 -40001da8: 03eee0 rsr.excvaddr a14 -40001dab: 03c0f0 rsr.depc a15 -40001dae: 00a565 call8 40002804 -40001db1: ffff06 j 40001db1 <_XX_unk1d90+0x21> - -40001db4 <_X_ets_memset>: -40001db4: 004136 entry a1, 32 -40001db7: 04cd mov.n a12, a4 -40001db9: 03bd mov.n a11, a3 -40001dbb: 02ad mov.n a10, a2 -40001dbd: 04e265 call8 40006be4 -40001dc0: 0a2d mov.n a2, a10 -40001dc2: f01d retw.n - -40001dc4 <_X_ets_memcpy>: -40001dc4: 004136 entry a1, 32 -40001dc7: 04cd mov.n a12, a4 -40001dc9: 03bd mov.n a11, a3 -40001dcb: 02ad mov.n a10, a2 -40001dcd: 04ba65 call8 40006974 -40001dd0: 0a2d mov.n a2, a10 -40001dd2: f01d retw.n - -40001dd4 <_X_ets_memmove>: -40001dd4: 004136 entry a1, 32 -40001dd7: 04cd mov.n a12, a4 -40001dd9: 03bd mov.n a11, a3 -40001ddb: 02ad mov.n a10, a2 -40001ddd: 04c8e5 call8 40006a6c -40001de0: 0a2d mov.n a2, a10 -40001de2: f01d retw.n - -40001de4 <_X_ets_memcmp>: -40001de4: 004136 entry a1, 32 -40001de7: 04cd mov.n a12, a4 -40001de9: 03bd mov.n a11, a3 -40001deb: 02ad mov.n a10, a2 -40001ded: 04afe5 call8 400068ec -40001df0: 0a2d mov.n a2, a10 -40001df2: f01d retw.n -40001df4: ffda90 excw -40001df7: 3f .byte 0x3f -40001df8: 004136 entry a1, 32 -40001dfb: 000292 l8ui a9, a2, 0 -40001dfe: fffd81 l32r a8, 40001df4 <_X_ets_memcmp+0x10> -40001e01: 010262 l8ui a6, a2, 1 -40001e04: 0858 l32i.n a5, a8, 0 -40001e06: 116680 slli a6, a6, 8 -40001e09: 206690 or a6, a6, a9 -40001e0c: 665a add.n a6, a6, a5 -40001e0e: 071267 beq a2, a6, 40001e19 <_X_ets_memcmp+0x35> -40001e11: 040692 l8ui a9, a6, 4 -40001e14: 18e8 l32i.n a14, a8, 4 -40001e16: 04e916 beqz a9, 40001e68 <_X_ets_memcmp+0x84> -40001e19: 030272 l8ui a7, a2, 3 -40001e1c: 020292 l8ui a9, a2, 2 -40001e1f: 117780 slli a7, a7, 8 -40001e22: 207790 or a7, a7, a9 -40001e25: 675a add.n a6, a7, a5 -40001e27: 391267 beq a2, a6, 40001e64 <_X_ets_memcmp+0x80> -40001e2a: 040692 l8ui a9, a6, 4 -40001e2d: 28a8 l32i.n a10, a8, 8 -40001e2f: 19fc bnez.n a9, 40001e64 <_X_ets_memcmp+0x80> -40001e31: 41b870 srli a11, a7, 8 -40001e34: 0102d2 l8ui a13, a2, 1 -40001e37: 0002e2 l8ui a14, a2, 0 -40001e3a: 11dd80 slli a13, a13, 8 -40001e3d: 20dde0 or a13, a13, a14 -40001e40: 0046d2 s8i a13, a6, 0 -40001e43: 41d8d0 srli a13, a13, 8 -40001e46: 0146d2 s8i a13, a6, 1 -40001e49: 0102c2 l8ui a12, a2, 1 -40001e4c: 0002d2 l8ui a13, a2, 0 -40001e4f: 11cc80 slli a12, a12, 8 -40001e52: 20ccd0 or a12, a12, a13 -40001e55: cc5a add.n a12, a12, a5 -40001e57: 024c72 s8i a7, a12, 2 -40001e5a: 034cb2 s8i a11, a12, 3 -40001e5d: 019a27 bne a10, a2, 40001e62 <_X_ets_memcmp+0x7e> -40001e60: 2869 s32i.n a6, a8, 8 -40001e62: f01d retw.n -40001e64: f01d retw.n -40001e66: 670000 excw -40001e69: 1e .byte 0x1e -40001e6a: 50ad excw -40001e6c: a2c032 addi a3, a0, -94 -40001e6f: 920006 j 3ffe6673 <_start-0x1998d> -40001e72: f20106 j 3fffe67a <_start-0x1986> -40001e75: 0228 l32i.n a2, a2, 0 -40001e77: 119980 slli a9, a9, 8 -40001e7a: 029f67 bne a15, a6, 40001e80 <_X_ets_memcmp+0x9c> -40001e7d: 026822 s32i a2, a8, 8 -40001e80: 2099a0 or a9, a9, a10 -40001e83: 004292 s8i a9, a2, 0 -40001e86: 419890 srli a9, a9, 8 -40001e89: 014292 s8i a9, a2, 1 -40001e8c: 010642 l8ui a4, a6, 1 -40001e8f: 000692 l8ui a9, a6, 0 -40001e92: 114480 slli a4, a4, 8 -40001e95: 204490 or a4, a4, a9 -40001e98: 445a add.n a4, a4, a5 -40001e9a: 024432 s8i a3, a4, 2 -40001e9d: 413830 srli a3, a3, 8 -40001ea0: 034432 s8i a3, a4, 3 -40001ea3: ffdc86 j 40001e19 <_X_ets_memcmp+0x35> -40001ea6: 360000 excw -40001ea9: 1c0041 l32r a4, 3ffc8eac <_start-0x37154> -40001eac: 051c03 excw -40001eaf: 180c movi.n a8, 1 -40001eb1: fca291 l32r a9, 4000113c <_c_0x1000> -40001eb4: 0d0c movi.n a13, 0 -40001eb6: 0c1c movi.n a12, 16 -40001eb8: cb7c movi.n a11, -4 -40001eba: a23b addi.n a10, a2, 3 -40001ebc: 10aab0 and a10, a10, a11 -40001ebf: 014ac2 s8i a12, a10, 1 -40001ec2: 024ad2 s8i a13, a10, 2 -40001ec5: 004a92 s8i a9, a10, 0 -40001ec8: 034ad2 s8i a13, a10, 3 -40001ecb: 044ad2 s8i a13, a10, 4 -40001ece: ffc9b1 l32r a11, 40001df4 <_X_ets_memcmp+0x10> -40001ed1: 10da42 addmi a4, a10, 0x1000 -40001ed4: 0ba9 s32i.n a10, a11, 0 -40001ed6: 2ba9 s32i.n a10, a11, 8 -40001ed8: 004492 s8i a9, a4, 0 -40001edb: 1b49 s32i.n a4, a11, 4 -40001edd: 024492 s8i a9, a4, 2 -40001ee0: 044482 s8i a8, a4, 4 -40001ee3: 014452 s8i a5, a4, 1 -40001ee6: 034432 s8i a3, a4, 3 -40001ee9: f01d retw.n -40001eeb: 413600 srli a3, a0, 6 -40001eee: b28c00 mulsh a8, a12, a0 -40001ef1: ffc0a1 l32r a10, 40001df4 <_X_ets_memcmp+0x10> -40001ef4: 0a88 l32i.n a8, a10, 0 -40001ef6: 1a98 l32i.n a9, a10, 4 -40001ef8: 023287 bltu a2, a8, 40001efe <_X_ets_memcmp+0x11a> -40001efb: 013297 bltu a2, a9, 40001f00 <_X_ets_memcmp+0x11c> -40001efe: f01d retw.n -40001f00: 0c0c movi.n a12, 0 -40001f02: 2ad8 l32i.n a13, a10, 8 -40001f04: f8c2b2 addi a11, a2, -8 -40001f07: 63ddb0 minu a13, a13, a11 -40001f0a: 044bc2 s8i a12, a11, 4 -40001f0d: 2ad9 s32i.n a13, a10, 8 -40001f0f: 0bad mov.n a10, a11 -40001f11: ffee65 call8 40001df8 <_X_ets_memcmp+0x14> -40001f14: f01d retw.n -40001f16: fc0000 excw -40001f19: ff .byte 0xff -40001f1a: 360000 excw -40001f1d: c10041 l32r a4, 3fff2320 <_start-0xdce0> -40001f20: 51fc87 bbsi a12, 24, 40001f75 <_X_ets_memcmp+0x191> -40001f23: fffd excw -40001f25: 433b addi.n a4, a3, 3 -40001f27: 104450 and a4, a4, a5 -40001f2a: c50c movi.n a5, 12 -40001f2c: 734450 maxu a4, a4, a5 -40001f2f: f43040 extui a3, a4, 0, 16 -40001f32: 02bc37 bgeu a12, a3, 40001f38 <_X_ets_memcmp+0x154> -40001f35: 002246 j 40001fc2 <_X_ets_memcmp+0x1de> -40001f38: ffaf91 l32r a9, 40001df4 <_X_ets_memcmp+0x10> -40001f3b: 0968 l32i.n a6, a9, 0 -40001f3d: 1988 l32i.n a8, a9, 4 -40001f3f: 7d3267 bltu a2, a6, 40001fc0 <_X_ets_memcmp+0x1dc> -40001f42: 7ab287 bgeu a2, a8, 40001fc0 <_X_ets_memcmp+0x1dc> -40001f45: f8c2b2 addi a11, a2, -8 -40001f48: 010b82 l8ui a8, a11, 1 -40001f4b: 000ba2 l8ui a10, a11, 0 -40001f4e: 118880 slli a8, a8, 8 -40001f51: 2088a0 or a8, a8, a10 -40001f54: c0a260 sub a10, a2, a6 -40001f57: f8caa2 addi a10, a10, -8 -40001f5a: c078a0 sub a7, a8, a10 -40001f5d: f8c772 addi a7, a7, -8 -40001f60: f47070 extui a7, a7, 0, 16 -40001f63: 5b3737 bltu a7, a3, 40001fc2 <_X_ets_memcmp+0x1de> -40001f66: 561737 beq a7, a3, 40001fc0 <_X_ets_memcmp+0x1dc> -40001f69: d86a add.n a13, a8, a6 -40001f6b: 040df2 l8ui a15, a13, 4 -40001f6e: 0e0c movi.n a14, 0 -40001f70: 052f16 beqz a15, 40001fc6 <_X_ets_memcmp+0x1e2> -40001f73: 14c342 addi a4, a3, 20 -40001f76: 463747 bltu a7, a4, 40001fc0 <_X_ets_memcmp+0x1dc> -40001f79: 2958 l32i.n a5, a9, 8 -40001f7b: 41f880 srli a15, a8, 8 -40001f7e: 4148a0 srli a4, a10, 8 -40001f81: 3a3a add.n a3, a10, a3 -40001f83: 338b addi.n a3, a3, 8 -40001f85: f4d030 extui a13, a3, 0, 16 -40001f88: d6da add.n a13, a6, a13 -40001f8a: 034d42 s8i a4, a13, 3 -40001f8d: 024da2 s8i a10, a13, 2 -40001f90: 014df2 s8i a15, a13, 1 -40001f93: 044de2 s8i a14, a13, 4 -40001f96: 004d82 s8i a8, a13, 0 -40001f99: 6355d0 minu a5, a5, a13 -40001f9c: 2959 s32i.n a5, a9, 8 -40001f9e: 418830 srli a8, a3, 8 -40001fa1: 004b32 s8i a3, a11, 0 -40001fa4: 014b82 s8i a8, a11, 1 -40001fa7: 010d72 l8ui a7, a13, 1 -40001faa: 000dd2 l8ui a13, a13, 0 -40001fad: 117780 slli a7, a7, 8 -40001fb0: 2077d0 or a7, a7, a13 -40001fb3: 0917c7 beq a7, a12, 40001fc0 <_X_ets_memcmp+0x1dc> -40001fb6: 476a add.n a4, a7, a6 -40001fb8: 024432 s8i a3, a4, 2 -40001fbb: 034482 s8i a8, a4, 3 -40001fbe: f01d retw.n -40001fc0: f01d retw.n -40001fc2: 020c movi.n a2, 0 -40001fc4: f01d retw.n -40001fc6: 2958 l32i.n a5, a9, 8 -40001fc8: 4148a0 srli a4, a10, 8 -40001fcb: 000db2 l8ui a11, a13, 0 -40001fce: 010d82 l8ui a8, a13, 1 -40001fd1: 3a3a add.n a3, a10, a3 -40001fd3: 338b addi.n a3, a3, 8 -40001fd5: f47030 extui a7, a3, 0, 16 -40001fd8: 118880 slli a8, a8, 8 -40001fdb: 2088b0 or a8, a8, a11 -40001fde: 807670 add a7, a6, a7 -40001fe1: 0247a2 s8i a10, a7, 2 -40001fe4: 0447e2 s8i a14, a7, 4 -40001fe7: 034742 s8i a4, a7, 3 -40001fea: 41f880 srli a15, a8, 8 -40001fed: 004782 s8i a8, a7, 0 -40001ff0: 0147f2 s8i a15, a7, 1 -40001ff3: 418830 srli a8, a3, 8 -40001ff6: 0295d7 bne a5, a13, 40001ffc <_X_ets_memcmp+0x218> -40001ff9: 026972 s32i a7, a9, 8 -40001ffc: f8c2d2 addi a13, a2, -8 -40001fff: 014d82 s8i a8, a13, 1 -40002002: 004d32 s8i a3, a13, 0 -40002005: 0107b2 l8ui a11, a7, 1 -40002008: 000792 l8ui a9, a7, 0 -4000200b: 117b80 slli a7, a11, 8 -4000200e: 207790 or a7, a7, a9 -40002011: ab17c7 beq a7, a12, 40001fc0 <_X_ets_memcmp+0x1dc> -40002014: 976a add.n a9, a7, a6 -40002016: 024932 s8i a3, a9, 2 -40002019: 034982 s8i a8, a9, 3 -4000201c: f01d retw.n -4000201e: 360000 excw -40002021: 160041 l32r a4, 3ffc7824 <_start-0x387dc> -40002024: b105d2 l8ui a13, a5, 177 -40002027: ffbc beqz.n a15, 4000206a <_X_ets_memcmp+0x286> -40002029: a23b addi.n a10, a2, 3 -4000202b: 10aab0 and a10, a10, a11 -4000202e: cb0c movi.n a11, 12 -40002030: 73aab0 maxu a10, a10, a11 -40002033: fc42b1 l32r a11, 4000113c <_c_0x1000> -40002036: f4a0a0 extui a10, a10, 0, 16 -40002039: 473ba7 bltu a11, a10, 40002084 <_X_ets_memcmp+0x2a0> -4000203c: ff6ed1 l32r a13, 40001df4 <_X_ets_memcmp+0x10> -4000203f: 002d62 l32i a6, a13, 0 -40002042: 022d92 l32i a9, a13, 8 -40002045: c07ba0 sub a7, a11, a10 -40002048: c02960 sub a2, a9, a6 -4000204b: f42020 extui a2, a2, 0, 16 -4000204e: 32a277 bge a2, a7, 40002084 <_X_ets_memcmp+0x2a0> -40002051: 188076 loop a0, 4000206d <_X_ets_memcmp+0x289> -40002054: 862a add.n a8, a6, a2 -40002056: 040832 l8ui a3, a8, 4 -40002059: 010852 l8ui a5, a8, 1 -4000205c: 0008c2 l8ui a12, a8, 0 -4000205f: 115580 slli a5, a5, 8 -40002062: 2055c0 or a5, a5, a12 -40002065: 007316 beqz a3, 40002070 <_X_ets_memcmp+0x28c> -40002068: 052d mov.n a2, a5 -4000206a: 16a577 bge a5, a7, 40002084 <_X_ets_memcmp+0x2a0> -4000206d: fff806 j 40002051 <_X_ets_memcmp+0x26d> -40002070: c0c520 sub a12, a5, a2 -40002073: f8ccc2 addi a12, a12, -8 -40002076: ee3ca7 bltu a12, a10, 40002068 <_X_ets_memcmp+0x284> -40002079: 1e0c movi.n a14, 1 -4000207b: 14caf2 addi a15, a10, 20 -4000207e: 4f3cf7 bltu a12, a15, 400020d1 <_X_ets_memcmp+0x2ed> -40002081: 0000c6 j 40002088 <_X_ets_memcmp+0x2a4> -40002084: 020c movi.n a2, 0 -40002086: f01d retw.n -40002088: 41f820 srli a15, a2, 8 -4000208b: 41c850 srli a12, a5, 8 -4000208e: 040c movi.n a4, 0 -40002090: 72aa add.n a7, a2, a10 -40002092: 778b addi.n a7, a7, 8 -40002094: f43070 extui a3, a7, 0, 16 -40002097: 363a add.n a3, a6, a3 -40002099: 044342 s8i a4, a3, 4 -4000209c: 0143c2 s8i a12, a3, 1 -4000209f: 024322 s8i a2, a3, 2 -400020a2: 004352 s8i a5, a3, 0 -400020a5: 0343f2 s8i a15, a3, 3 -400020a8: 415870 srli a5, a7, 8 -400020ab: 0448e2 s8i a14, a8, 4 -400020ae: 004872 s8i a7, a8, 0 -400020b1: 014852 s8i a5, a8, 1 -400020b4: 010322 l8ui a2, a3, 1 -400020b7: 000332 l8ui a3, a3, 0 -400020ba: 112280 slli a2, a2, 8 -400020bd: 202230 or a2, a2, a3 -400020c0: 1012b7 beq a2, a11, 400020d4 <_X_ets_memcmp+0x2f0> -400020c3: a62a add.n a10, a6, a2 -400020c5: 024a72 s8i a7, a10, 2 -400020c8: 034a52 s8i a5, a10, 3 -400020cb: 000146 j 400020d4 <_X_ets_memcmp+0x2f0> -400020ce: 000000 ill -400020d1: 0448e2 s8i a14, a8, 4 -400020d4: 219987 bne a9, a8, 400020f9 <_X_ets_memcmp+0x315> -400020d7: 0409b2 l8ui a11, a9, 4 -400020da: bb9c beqz.n a11, 400020f9 <_X_ets_memcmp+0x315> -400020dc: 1d28 l32i.n a2, a13, 4 -400020de: 171297 beq a2, a9, 400020f9 <_X_ets_memcmp+0x315> -400020e1: 0109b2 l8ui a11, a9, 1 -400020e4: 0009a2 l8ui a10, a9, 0 -400020e7: 119b80 slli a9, a11, 8 -400020ea: 2099a0 or a9, a9, a10 -400020ed: 969a add.n a9, a6, a9 -400020ef: 0409c2 l8ui a12, a9, 4 -400020f2: 2d99 s32i.n a9, a13, 8 -400020f4: 1c8c beqz.n a12, 400020f9 <_X_ets_memcmp+0x315> -400020f6: e79297 bne a2, a9, 400020e1 <_X_ets_memcmp+0x2fd> -400020f9: 288b addi.n a2, a8, 8 -400020fb: f01d retw.n -400020fd: 000000 ill -40002100: 004136 entry a1, 32 -40002103: c13230 mul16u a3, a2, a3 -40002106: f4a030 extui a10, a3, 0, 16 -40002109: fff165 call8 40002020 <_X_ets_memcmp+0x23c> -4000210c: 202aa0 or a2, a10, a10 -4000210f: 007a16 beqz a10, 4000211a <_X_ets_memcmp+0x336> -40002112: 20c330 or a12, a3, a3 -40002115: 0b0c movi.n a11, 0 -40002117: ffc9e5 call8 40001db4 <_X_ets_memset> -4000211a: f01d retw.n -4000211c: 004136 entry a1, 32 -4000211f: 02bd mov.n a11, a2 -40002121: 1a0c movi.n a10, 1 -40002123: fffde5 call8 40002100 <_X_ets_memcmp+0x31c> -40002126: 0a2d mov.n a2, a10 -40002128: f01d retw.n -4000212a: 360000 excw -4000212d: 4d0041 l32r a4, 3ffd5530 <_start-0x2aad0> -40002130: 03ad02 movi a0, 0xfffffd03 -40002133: ffeee5 call8 40002020 <_X_ets_memcmp+0x23c> -40002136: 0a2d mov.n a2, a10 -40002138: 2acc bnez.n a10, 4000213e <_X_ets_memcmp+0x35a> -4000213a: f01d retw.n -4000213c: 300000 xor a0, a0, a0 -4000213f: 4020c3 excw -40002142: e520b4 excw -40002145: 40ffc7 bbsi a15, 28, 40002189 <_st_0x3fffda9c+0x39> -40002148: 2520a4 excw -4000214b: ffda add.n a15, a15, a13 -4000214d: f01d retw.n - ... - -40002150 <_st_0x3fffda9c>: -40002150: da9c beqz.n a10, 40002171 <_st_0x3fffda9c+0x21> -40002152: ff .byte 0xff -40002153: 3f .byte 0x3f -40002154: 00fff2 s32ri a15, a15, 0 -40002157: 413600 srli a3, a0, 6 -4000215a: fd4100 excw -4000215d: ff .byte 0xff -4000215e: 1258 l32i.n a5, a2, 4 -40002160: 0429 s32i.n a2, a4, 0 -40002162: 55eb addi.n a5, a5, 14 -40002164: 1459 s32i.n a5, a4, 4 -40002166: fffb51 l32r a5, 40002154 <_st_0x3fffda9c+0x4> -40002169: 0020c0 memw -4000216c: 0238 l32i.n a3, a2, 0 -4000216e: b43030 extui a3, a3, 0, 12 -40002171: 335a add.n a3, a3, a5 -40002173: 045432 s16i a3, a4, 8 -40002176: f01d retw.n -40002178: cc8c beqz.n a12, 40002188 <_st_0x3fffda9c+0x38> -4000217a: ff .byte 0xff -4000217b: 3f .byte 0x3f -4000217c: 004136 entry a1, 32 -4000217f: fff4b1 l32r a11, 40002150 <_st_0x3fffda9c> -40002182: 041b92 l16ui a9, a11, 8 -40002185: 1b88 l32i.n a8, a11, 4 -40002187: d98c beqz.n a9, 40002198 <_st_0x3fffda9c+0x48> -40002189: 004822 s8i a2, a8, 0 -4000218c: a90b addi.n a10, a9, -1 -4000218e: 045ba2 s16i a10, a11, 8 -40002191: 881b addi.n a8, a8, 1 -40002193: 1b89 s32i.n a8, a11, 4 -40002195: f01d retw.n -40002197: f8a100 excw -4000219a: ff .byte 0xff -4000219b: 0066a5 call8 40002804 -4000219e: f01d retw.n -400021a0: 217c movi.n a1, -14 -400021a2: 364000 excw -400021a5: bd00a1 l32r a10, 3fff15a8 <_start-0xea58> -400021a8: 412902 l32i a0, a9, 0x104 -400021ab: 5139 s32i.n a3, a1, 20 -400021ad: 6149 s32i.n a4, a1, 24 -400021af: 7159 s32i.n a5, a1, 28 -400021b1: 8169 s32i.n a6, a1, 32 -400021b3: 9179 s32i.n a7, a1, 36 -400021b5: fffaa1 l32r a10, 400021a0 <_st_0x3fffda9c+0x50> -400021b8: 30c1c2 addi a12, a1, 48 -400021bb: 4e0c movi.n a14, 4 -400021bd: 10c1d2 addi a13, a1, 16 -400021c0: 11d9 s32i.n a13, a1, 4 -400021c2: 21e9 s32i.n a14, a1, 8 -400021c4: 01c9 s32i.n a12, a1, 0 -400021c6: 0015e5 call8 40002324 <_X_ets_unk225c+0xc8> -400021c9: 0a2d mov.n a2, a10 -400021cb: f01d retw.n -400021cd: 000000 ill -400021d0: 004136 entry a1, 32 -400021d3: ffdfa1 l32r a10, 40002150 <_st_0x3fffda9c> -400021d6: 002ab2 l32i a11, a10, 0 -400021d9: 012bd2 l32i a13, a11, 4 -400021dc: 0ccd92 addi a9, a13, 12 -400021df: 0020c0 memw -400021e2: f0a0f2 movi a15, 240 -400021e5: 0be8 l32i.n a14, a11, 0 -400021e7: cd2b addi.n a12, a13, 2 -400021e9: b480e0 extui a8, a14, 0, 12 -400021ec: 34e8e0 extui a14, a14, 8, 4 -400021ef: 004c82 s8i a8, a12, 0 -400021f2: 014ce2 s8i a14, a12, 1 -400021f5: 8c0c movi.n a12, 8 -400021f7: 000de2 l8ui a14, a13, 0 -400021fa: 014dc2 s8i a12, a13, 1 -400021fd: 10eef0 and a14, a14, a15 -40002200: 004de2 s8i a14, a13, 0 -40002203: 041aa2 l16ui a10, a10, 8 -40002206: 0020c0 memw -40002209: 0b88 l32i.n a8, a11, 0 -4000220b: b48080 extui a8, a8, 0, 12 -4000220e: c088a0 sub a8, a8, a10 -40002211: 0bad mov.n a10, a11 -40002213: f2c882 addi a8, a8, -14 -40002216: 005982 s16i a8, a9, 0 -40002219: 02fb25 call8 400051cc <_XX_unk51ac+0x20> -4000221c: f01d retw.n -4000221e: 360000 excw -40002221: 310041 l32r a4, 3ffce624 <_start-0x319dc> -40002224: ffcb addi.n a15, a15, 12 -40002226: 33b8 l32i.n a11, a3, 12 -40002228: 4b8c beqz.n a11, 40002230 <_st_0x3fffda9c+0xe0> -4000222a: 20a220 or a10, a2, a2 -4000222d: 000be0 callx8 a11 -40002230: 43b8 l32i.n a11, a3, 16 -40002232: 3b8c beqz.n a11, 40002239 <_st_0x3fffda9c+0xe9> -40002234: 02ad mov.n a10, a2 -40002236: 000be0 callx8 a11 -40002239: f01d retw.n - ... - -4000223c <_X_ets_uart_putc>: -4000223c: 004136 entry a1, 32 -4000223f: 0b9226 beqi a2, 10, 4000224e <_X_ets_uart_putc+0x12> -40002242: d80c movi.n a8, 13 -40002244: 041287 beq a2, a8, 4000224c <_X_ets_uart_putc+0x10> -40002247: 02ad mov.n a10, a2 -40002249: 013e25 call8 4000362c -4000224c: f01d retw.n -4000224e: 0da0a2 movi a10, 13 -40002251: 013da5 call8 4000362c -40002254: 0aa0a2 movi a10, 10 -40002257: 013d65 call8 4000362c -4000225a: f01d retw.n - -4000225c <_X_ets_unk225c>: -4000225c: 00a136 entry a1, 80 -4000225f: 068d mov.n a8, a6 -40002261: 090c movi.n a9, 0 -40002263: 1b0c movi.n a11, 1 -40002265: 0a0c movi.n a10, 0 -40002267: 83ab30 moveqz a10, a11, a3 -4000226a: 839b20 moveqz a9, a11, a2 -4000226d: 0909a7 bnone a9, a10, 4000227a <_X_ets_unk225c+0x1e> -40002270: 711b addi.n a7, a1, 1 -40002272: 0c3c movi.n a12, 48 -40002274: 0041c2 s8i a12, a1, 0 -40002277: 001046 j 400022bc <_X_ets_unk225c+0x60> -4000227a: 017d mov.n a7, a1 -4000227c: 316f50 srai a6, a5, 31 -4000227f: 086182 s32i a8, a1, 32 -40002282: 20b330 or a11, a3, a3 -40002285: 20a220 or a10, a2, a2 -40002288: 06dd mov.n a13, a6 -4000228a: 05cd mov.n a12, a5 -4000228c: 04bd65 call8 40006e64 <__umoddi3> -4000228f: 03bd mov.n a11, a3 -40002291: 81d8 l32i.n a13, a1, 32 -40002293: 05cd mov.n a12, a5 -40002295: ddaa add.n a13, a13, a10 -40002297: 000dd2 l8ui a13, a13, 0 -4000229a: 02ad mov.n a10, a2 -4000229c: 0047d2 s8i a13, a7, 0 -4000229f: 771b addi.n a7, a7, 1 -400022a1: 06dd mov.n a13, a6 -400022a3: 0498e5 call8 40006c30 <__udivdi3> -400022a6: 0b3d mov.n a3, a11 -400022a8: 0a2d mov.n a2, a10 -400022aa: 0aed mov.n a14, a10 -400022ac: 180c movi.n a8, 1 -400022ae: 0bfd mov.n a15, a11 -400022b0: 93f8b0 movnez a15, a8, a11 -400022b3: 93e8a0 movnez a14, a8, a10 -400022b6: 20eef0 or a14, a14, a15 -400022b9: fc5e56 bnez a14, 40002282 <_X_ets_unk225c+0x26> -400022bc: 441717 beq a7, a1, 40002304 <_X_ets_unk225c+0xa8> -400022bf: c02710 sub a2, a7, a1 -400022c2: 149020 extui a9, a2, 0, 2 -400022c5: 099976 loopnez a9, 400022d2 <_X_ets_unk225c+0x76> -400022c8: 770b addi.n a7, a7, -1 -400022ca: 0007a2 l8ui a10, a7, 0 -400022cd: 0044a2 s8i a10, a4, 0 -400022d0: 441b addi.n a4, a4, 1 -400022d2: 419220 srli a9, a2, 2 -400022d5: 249976 loopnez a9, 400022fd <_X_ets_unk225c+0xa1> -400022d8: 970b addi.n a9, a7, -1 -400022da: fec7b2 addi a11, a7, -2 -400022dd: fdc7c2 addi a12, a7, -3 -400022e0: 000992 l8ui a9, a9, 0 -400022e3: 000bb2 l8ui a11, a11, 0 -400022e6: 000cc2 l8ui a12, a12, 0 -400022e9: fcc772 addi a7, a7, -4 -400022ec: 004492 s8i a9, a4, 0 -400022ef: 0144b2 s8i a11, a4, 1 -400022f2: 0244c2 s8i a12, a4, 2 -400022f5: 000792 l8ui a9, a7, 0 -400022f8: 034492 s8i a9, a4, 3 -400022fb: 444b addi.n a4, a4, 4 -400022fd: 0b0c movi.n a11, 0 -400022ff: 0044b2 s8i a11, a4, 0 -40002302: f01d retw.n -40002304: 020c movi.n a2, 0 -40002306: 0c0c movi.n a12, 0 -40002308: 0044c2 s8i a12, a4, 0 -4000230b: f01d retw.n -4000230d: 000000 ill -40002310: ffc8a0 excw -40002313: 3f .byte 0x3f -40002314: cca8 l32i.n a10, a12, 48 -40002316: ff .byte 0xff -40002317: 3f .byte 0x3f -40002318: cc9c beqz.n a12, 40002338 <_X_ets_unk225c+0xdc> -4000231a: ff .byte 0xff -4000231b: 3f .byte 0x3f -4000231c: ffccd0 excw -4000231f: 3f .byte 0x3f -40002320: ccbc beqz.n a12, 40002360 <_X_ets_unk225c+0x104> -40002322: ff .byte 0xff -40002323: 3f .byte 0x3f -40002324: 00e136 entry a1, 112 -40002327: 106162 s32i a6, a1, 64 -4000232a: 04dd mov.n a13, a4 -4000232c: 05cd mov.n a12, a5 -4000232e: 000352 l8ui a5, a3, 0 -40002331: 431b addi.n a4, a3, 1 -40002333: 42b516 beqz a5, 40002762 <_X_ets_unk225c+0x506> -40002336: 030c movi.n a3, 0 -40002338: a1d9 s32i.n a13, a1, 40 -4000233a: 91c9 s32i.n a12, a1, 36 -4000233c: 080c movi.n a8, 0 -4000233e: 060c movi.n a6, 0 -40002340: 090c movi.n a9, 0 -40002342: 116192 s32i a9, a1, 68 -40002345: d169 s32i.n a6, a1, 52 -40002347: e189 s32i.n a8, a1, 56 -40002349: 016d mov.n a6, a1 -4000234b: 582c movi.n a8, 37 -4000234d: 1b1587 beq a5, a8, 4000236c <_X_ets_unk225c+0x110> -40002350: 05ad mov.n a10, a5 -40002352: 0002e0 callx8 a2 -40002355: 1121a2 l32i a10, a1, 68 -40002358: aa1b addi.n a10, a10, 1 -4000235a: 1161a2 s32i a10, a1, 68 -4000235d: 000452 l8ui a5, a4, 0 -40002360: 01c442 addi a4, a4, 1 -40002363: 19e516 beqz a5, 40002505 <_X_ets_unk225c+0x2a9> -40002366: 25a0b2 movi a11, 37 -40002369: e395b7 bne a5, a11, 40002350 <_X_ets_unk225c+0xf4> -4000236c: 000452 l8ui a5, a4, 0 -4000236f: 441b addi.n a4, a4, 1 -40002371: d3c5c2 addi a12, a5, -45 -40002374: 192c16 beqz a12, 4000250a <_X_ets_unk225c+0x2ae> -40002377: d0c5d2 addi a13, a5, -48 -4000237a: 0e0c movi.n a14, 0 -4000237c: f1e9 s32i.n a14, a1, 60 -4000237e: 197d16 beqz a13, 40002519 <_X_ets_unk225c+0x2bd> -40002381: 0f0c movi.n a15, 0 -40002383: c1f9 s32i.n a15, a1, 48 -40002385: f82c movi.n a8, 47 -40002387: 27b857 bgeu a8, a5, 400023b2 <_X_ets_unk225c+0x156> -4000238a: 993c movi.n a9, 57 -4000238c: 223957 bltu a9, a5, 400023b2 <_X_ets_unk225c+0x156> -4000238f: 070c movi.n a7, 0 -40002391: 178076 loop a0, 400023ac <_X_ets_unk225c+0x150> -40002394: fc2c movi.n a12, 47 -40002396: a07770 addx4 a7, a7, a7 -40002399: 907750 addx2 a7, a7, a5 -4000239c: d0c772 addi a7, a7, -48 -4000239f: 000452 l8ui a5, a4, 0 -400023a2: 441b addi.n a4, a4, 1 -400023a4: 0cbc57 bgeu a12, a5, 400023b4 <_X_ets_unk225c+0x158> -400023a7: 983c movi.n a8, 57 -400023a9: 073857 bltu a8, a5, 400023b4 <_X_ets_unk225c+0x158> -400023ac: fff846 j 40002391 <_X_ets_unk225c+0x135> -400023af: 000000 ill -400023b2: 070c movi.n a7, 0 -400023b4: d2c592 addi a9, a5, -46 -400023b7: 301916 beqz a9, 400026bc <_X_ets_unk225c+0x460> -400023ba: 94c5a2 addi a10, a5, -108 -400023bd: 21ca16 beqz a10, 400025dd <_X_ets_unk225c+0x381> -400023c0: 4b4c movi.n a11, 68 -400023c2: 4615b7 beq a5, a11, 4000240c <_X_ets_unk225c+0x1b0> -400023c5: 5c5c movi.n a12, 85 -400023c7: 4115c7 beq a5, a12, 4000240c <_X_ets_unk225c+0x1b0> -400023ca: 8d5c movi.n a13, 88 -400023cc: 3c15d7 beq a5, a13, 4000240c <_X_ets_unk225c+0x1b0> -400023cf: 64a0e2 movi a14, 100 -400023d2: 3615e7 beq a5, a14, 4000240c <_X_ets_unk225c+0x1b0> -400023d5: 0e0c movi.n a14, 0 -400023d7: 0c0c movi.n a12, 0 -400023d9: 90c5f2 addi a15, a5, -112 -400023dc: 30cf16 beqz a15, 400026ec <_X_ets_unk225c+0x490> -400023df: 75a082 movi a8, 117 -400023e2: 051587 beq a5, a8, 400023eb <_X_ets_unk225c+0x18f> -400023e5: 78a092 movi a9, 120 -400023e8: 649597 bne a5, a9, 40002450 <_X_ets_unk225c+0x1f4> -400023eb: 31ac16 beqz a12, 40002709 <_X_ets_unk225c+0x4ad> -400023ee: 1021c2 l32i a12, a1, 64 -400023f1: 8b7c movi.n a11, -8 -400023f3: ac7b addi.n a10, a12, 7 -400023f5: 10aab0 and a10, a10, a11 -400023f8: 8b1c movi.n a11, 24 -400023fa: ca8b addi.n a12, a10, 8 -400023fc: 1061c2 s32i a12, a1, 64 -400023ff: 02abc7 bge a11, a12, 40002405 <_X_ets_unk225c+0x1a9> -40002402: 006106 j 4000258a <_X_ets_unk225c+0x32e> -40002405: 9198 l32i.n a9, a1, 36 -40002407: 0062c6 j 40002596 <_X_ets_unk225c+0x33a> -4000240a: d20000 quos a0, a0, a0 -4000240d: 1c1021 l32r a2, 3ffc9450 <_start-0x36bb0> -40002410: ad8c beqz.n a13, 4000241e <_X_ets_unk225c+0x1c2> -40002412: 4b0d excw -40002414: d2dd excw -40002416: d71061 l32r a6, 3fff8058 <_start-0x7fa8> -40002419: 042c movi.n a4, 32 -4000241b: 9198 l32i.n a9, a1, 36 -4000241d: 0002c6 j 4000242c <_X_ets_unk225c+0x1d0> -40002420: 8e1c movi.n a14, 24 -40002422: 042ea7 blt a14, a10, 4000242a <_X_ets_unk225c+0x1ce> -40002425: 4f2c movi.n a15, 36 -40002427: 1061f2 s32i a15, a1, 64 -4000242a: a198 l32i.n a9, a1, 40 -4000242c: 102182 l32i a8, a1, 64 -4000242f: 889a add.n a8, a8, a9 -40002431: ffd882 addmi a8, a8, 0xffffff00 -40002434: 3f2882 l32i a8, a8, 252 -40002437: 0d6182 s32i a8, a1, 52 -4000243a: 318f80 srai a8, a8, 31 -4000243d: 0e6182 s32i a8, a1, 56 -40002440: 9cc592 addi a9, a5, -100 -40002443: 167916 beqz a9, 400025ae <_X_ets_unk225c+0x352> -40002446: bcc5a2 addi a10, a5, -68 -40002449: 161a16 beqz a10, 400025ae <_X_ets_unk225c+0x352> -4000244c: 0b0c movi.n a11, 0 -4000244e: e1b9 s32i.n a11, a1, 56 -40002450: 0f0c movi.n a15, 0 -40002452: 81f9 s32i.n a15, a1, 32 -40002454: 485c movi.n a8, 84 -40002456: dbc5c2 addi a12, a5, -37 -40002459: 0bbc87 bgeu a12, a8, 40002468 <_X_ets_unk225c+0x20c> -4000245c: ffad91 l32r a9, 40002310 <_X_ets_unk225c+0xb4> -4000245f: a09c90 addx4 a9, a12, a9 -40002462: 0998 l32i.n a9, a9, 0 -40002464: 0009a0 jx a9 -40002467: 5a2c00 depbits a0, a12, 5, 3 -4000246a: 0002e0 callx8 a2 -4000246d: 05ad mov.n a10, a5 -4000246f: 0002e0 callx8 a2 -40002472: 1121a2 l32i a10, a1, 68 -40002475: aa2b addi.n a10, a10, 2 -40002477: 1161a2 s32i a10, a1, 68 -4000247a: b139 s32i.n a3, a1, 44 -4000247c: c1b8 l32i.n a11, a1, 48 -4000247e: 81c8 l32i.n a12, a1, 32 -40002480: c07730 sub a7, a7, a3 -40002483: ffc7e2 addi a14, a7, -1 -40002486: 937ec0 movnez a7, a14, a12 -40002489: 17cb16 beqz a11, 40002609 <_X_ets_unk225c+0x3ad> -4000248c: 00ec16 beqz a12, 4000249e <_X_ets_unk225c+0x242> -4000248f: 0cad mov.n a10, a12 -40002491: 0002e0 callx8 a2 -40002494: 1121f2 l32i a15, a1, 68 -40002497: 0c0c movi.n a12, 0 -40002499: ff1b addi.n a15, a15, 1 -4000249b: 1161f2 s32i a15, a1, 68 -4000249e: 053c movi.n a5, 48 -400024a0: f188 l32i.n a8, a1, 60 -400024a2: 0861c2 s32i a12, a1, 32 -400024a5: 167816 beqz a8, 40002610 <_X_ets_unk225c+0x3b4> -400024a8: 81c8 l32i.n a12, a1, 32 -400024aa: bc8c beqz.n a12, 400024b9 <_X_ets_unk225c+0x25d> -400024ac: 0cad mov.n a10, a12 -400024ae: 0002e0 callx8 a2 -400024b1: 112192 l32i a9, a1, 68 -400024b4: 991b addi.n a9, a9, 1 -400024b6: 116192 s32i a9, a1, 68 -400024b9: b1a8 l32i.n a10, a1, 44 -400024bb: ffca52 addi a5, a10, -1 -400024be: 203550 or a3, a5, a5 -400024c1: 181aa6 blti a10, 1, 400024dd <_X_ets_unk225c+0x281> -400024c4: 0006a2 l8ui a10, a6, 0 -400024c7: 661b addi.n a6, a6, 1 -400024c9: 0002e0 callx8 a2 -400024cc: 330b addi.n a3, a3, -1 -400024ce: f20366 bnei a3, -1, 400024c4 <_X_ets_unk225c+0x268> -400024d1: f37c movi.n a3, -1 -400024d3: 1121b2 l32i a11, a1, 68 -400024d6: c51b addi.n a12, a5, 1 -400024d8: bbca add.n a11, a11, a12 -400024da: 1161b2 s32i a11, a1, 68 -400024dd: f1d8 l32i.n a13, a1, 60 -400024df: e7ad16 beqz a13, 4000235d <_X_ets_unk225c+0x101> -400024e2: 07ed mov.n a14, a7 -400024e4: 570b addi.n a5, a7, -1 -400024e6: 057d mov.n a7, a5 -400024e8: 021ee6 bgei a14, 1, 400024ee <_X_ets_unk225c+0x292> -400024eb: ff9b86 j 4000235d <_X_ets_unk225c+0x101> -400024ee: 0a2c movi.n a10, 32 -400024f0: 0002e0 callx8 a2 -400024f3: 770b addi.n a7, a7, -1 -400024f5: f50766 bnei a7, -1, 400024ee <_X_ets_unk225c+0x292> -400024f8: 1121f2 l32i a15, a1, 68 -400024fb: 851b addi.n a8, a5, 1 -400024fd: ff8a add.n a15, a15, a8 -400024ff: 1161f2 s32i a15, a1, 68 -40002502: ff95c6 j 4000235d <_X_ets_unk225c+0x101> -40002505: 112122 l32i a2, a1, 68 -40002508: f01d retw.n -4000250a: 1a0c movi.n a10, 1 -4000250c: 000452 l8ui a5, a4, 0 -4000250f: 441b addi.n a4, a4, 1 -40002511: f1a9 s32i.n a10, a1, 60 -40002513: d0c592 addi a9, a5, -48 -40002516: e67956 bnez a9, 40002381 <_X_ets_unk225c+0x125> -40002519: 000452 l8ui a5, a4, 0 -4000251c: 1b0c movi.n a11, 1 -4000251e: 441b addi.n a4, a4, 1 -40002520: c1b9 s32i.n a11, a1, 48 -40002522: ff97c6 j 40002385 <_X_ets_unk225c+0x129> -40002525: 3c0000 excw -40002528: e00a add.n a14, a0, a0 -4000252a: a20002 l8ui a0, a0, 162 -4000252d: e078a0 subx4 a7, a8, a10 -40002530: 0c0002 l8ui a0, a0, 12 -40002533: 1c0c87 bnone a12, a8, 40002553 <_X_ets_unk225c+0x2f7> -40002536: c1c9 s32i.n a12, a1, 48 -40002538: 4d4c movi.n a13, 68 -4000253a: 3615d7 beq a5, a13, 40002574 <_X_ets_unk225c+0x318> -4000253d: 5e5c movi.n a14, 85 -4000253f: 3115e7 beq a5, a14, 40002574 <_X_ets_unk225c+0x318> -40002542: a8c5f2 addi a15, a5, -88 -40002545: 201f16 beqz a15, 4000274a <_X_ets_unk225c+0x4ee> -40002548: 64a082 movi a8, 100 -4000254b: 251587 beq a5, a8, 40002574 <_X_ets_unk225c+0x318> -4000254e: 70a092 movi a9, 112 -40002551: 0b1597 beq a5, a9, 40002560 <_X_ets_unk225c+0x304> -40002554: 75a0a2 movi a10, 117 -40002557: 1915a7 beq a5, a10, 40002574 <_X_ets_unk225c+0x318> -4000255a: 78a0b2 movi a11, 120 -4000255d: 2395b7 bne a5, a11, 40002584 <_X_ets_unk225c+0x328> -40002560: e1b8 l32i.n a11, a1, 56 -40002562: d1a8 l32i.n a10, a1, 52 -40002564: 01cd mov.n a12, a1 -40002566: 0d1c movi.n a13, 16 -40002568: ff6be1 l32r a14, 40002314 <_X_ets_unk225c+0xb8> -4000256b: ffcf25 call8 4000225c <_X_ets_unk225c> -4000256e: 0a3d mov.n a3, a10 -40002570: 000406 j 40002584 <_X_ets_unk225c+0x328> -40002573: e1b800 excw -40002576: d1a8 l32i.n a10, a1, 52 -40002578: 01cd mov.n a12, a1 -4000257a: ad0c movi.n a13, 10 -4000257c: ff67e1 l32r a14, 40002318 <_X_ets_unk225c+0xbc> -4000257f: ffcde5 call8 4000225c <_X_ets_unk225c> -40002582: 0a3d mov.n a3, a10 -40002584: 016d mov.n a6, a1 -40002586: ffbc06 j 4000247a <_X_ets_unk225c+0x21e> -40002589: 8c1c00 excw -4000258c: 042ca7 blt a12, a10, 40002594 <_X_ets_unk225c+0x338> -4000258f: 8d2c movi.n a13, 40 -40002591: 1061d2 s32i a13, a1, 64 -40002594: a198 l32i.n a9, a1, 40 -40002596: 1021f2 l32i a15, a1, 64 -40002599: 9cc5e2 addi a14, a5, -100 -4000259c: ff9a add.n a15, a15, a9 -4000259e: ffdff2 addmi a15, a15, 0xffffff00 -400025a1: 3f2f82 l32i a8, a15, 252 -400025a4: e189 s32i.n a8, a1, 56 -400025a6: 3e2ff2 l32i a15, a15, 248 -400025a9: d1f9 s32i.n a15, a1, 52 -400025ab: e97e56 bnez a14, 40002446 <_X_ets_unk225c+0x1ea> -400025ae: d1a8 l32i.n a10, a1, 52 -400025b0: 0d0c movi.n a13, 0 -400025b2: e188 l32i.n a8, a1, 56 -400025b4: 0c0c movi.n a12, 0 -400025b6: 0218e6 bgei a8, 1, 400025bc <_X_ets_unk225c+0x360> -400025b9: 006146 j 40002742 <_X_ets_unk225c+0x4e6> -400025bc: e90c16 beqz a12, 40002450 <_X_ets_unk225c+0x1f4> -400025bf: d1a8 l32i.n a10, a1, 52 -400025c1: fe7c movi.n a14, -1 -400025c3: e1c8 l32i.n a12, a1, 56 -400025c5: d92c movi.n a9, 45 -400025c7: 8199 s32i.n a9, a1, 32 -400025c9: 30cce0 xor a12, a12, a14 -400025cc: 60b0a0 neg a11, a10 -400025cf: d1b9 s32i.n a11, a1, 52 -400025d1: dc1b addi.n a13, a12, 1 -400025d3: 83cda0 moveqz a12, a13, a10 -400025d6: e1c9 s32i.n a12, a1, 56 -400025d8: ff9e06 j 40002454 <_X_ets_unk225c+0x1f8> -400025db: 520000 excw -400025de: 1b0004 mula.dd.hh.lddec m0, a0, m0, m2 -400025e1: c5f244 excw -400025e4: ff1694 excw -400025e7: 0e .byte 0xe -400025e8: 0c0c movi.n a12, 0 -400025ea: bcc582 addi a8, a5, -68 -400025ed: 0f4816 beqz a8, 400026e5 <_X_ets_unk225c+0x489> -400025f0: abc592 addi a9, a5, -85 -400025f3: 0ee916 beqz a9, 400026e5 <_X_ets_unk225c+0x489> -400025f6: a8c5a2 addi a10, a5, -88 -400025f9: 0e8a16 beqz a10, 400026e5 <_X_ets_unk225c+0x489> -400025fc: 9cc5b2 addi a11, a5, -100 -400025ff: 0e2b16 beqz a11, 400026e5 <_X_ets_unk225c+0x489> -40002602: 1e0c movi.n a14, 1 -40002604: ff7446 j 400023d9 <_X_ets_unk225c+0x17d> -40002607: c80000 excw -4000260a: 052cf1 l32r a15, 3ffc3abc <_start-0x3c544> -4000260d: e97c56 bnez a12, 400024a8 <_X_ets_unk225c+0x24c> -40002610: 07dd mov.n a13, a7 -40002612: 370b addi.n a3, a7, -1 -40002614: 037d mov.n a7, a3 -40002616: 021de6 bgei a13, 1, 4000261c <_X_ets_unk225c+0x3c0> -40002619: ffa2c6 j 400024a8 <_X_ets_unk225c+0x24c> -4000261c: 05ad mov.n a10, a5 -4000261e: 0002e0 callx8 a2 -40002621: 770b addi.n a7, a7, -1 -40002623: f50766 bnei a7, -1, 4000261c <_X_ets_unk225c+0x3c0> -40002626: f77c movi.n a7, -1 -40002628: 1121e2 l32i a14, a1, 68 -4000262b: f31b addi.n a15, a3, 1 -4000262d: eefa add.n a14, a14, a15 -4000262f: 1161e2 s32i a14, a1, 68 -40002632: ff9c86 j 400024a8 <_X_ets_unk225c+0x24c> -40002635: 102192 l32i a9, a1, 64 -40002638: 881c movi.n a8, 24 -4000263a: 09cd mov.n a12, a9 -4000263c: 994b addi.n a9, a9, 4 -4000263e: 106192 s32i a9, a1, 64 -40002641: 052897 blt a8, a9, 4000264a <_X_ets_unk225c+0x3ee> -40002644: 9198 l32i.n a9, a1, 36 -40002646: 000306 j 40002656 <_X_ets_unk225c+0x3fa> -40002649: 8a1c00 depbits a0, a12, 8, 2 -4000264c: 042ac7 blt a10, a12, 40002654 <_X_ets_unk225c+0x3f8> -4000264f: 4b2c movi.n a11, 36 -40002651: 1061b2 s32i a11, a1, 64 -40002654: a198 l32i.n a9, a1, 40 -40002656: 102162 l32i a6, a1, 64 -40002659: ff30d1 l32r a13, 4000231c <_X_ets_unk225c+0xc0> -4000265c: 669a add.n a6, a6, a9 -4000265e: ffd662 addmi a6, a6, 0xffffff00 -40002661: 3f2662 l32i a6, a6, 252 -40002664: 836d60 moveqz a6, a13, a6 -40002667: 0006c2 l8ui a12, a6, 0 -4000266a: 0efc16 beqz a12, 4000275d <_X_ets_unk225c+0x501> -4000266d: 069d mov.n a9, a6 -4000266f: 030c movi.n a3, 0 -40002671: 098076 loop a0, 4000267e <_X_ets_unk225c+0x422> -40002674: 331b addi.n a3, a3, 1 -40002676: 010982 l8ui a8, a9, 1 -40002679: 991b addi.n a9, a9, 1 -4000267b: dfb816 beqz a8, 4000247a <_X_ets_unk225c+0x21e> -4000267e: fffbc6 j 40002671 <_X_ets_unk225c+0x415> -40002681: 1021a2 l32i a10, a1, 64 -40002684: 891c movi.n a9, 24 -40002686: 0acd mov.n a12, a10 -40002688: aa4b addi.n a10, a10, 4 -4000268a: 1061a2 s32i a10, a1, 64 -4000268d: 0529a7 blt a9, a10, 40002696 <_X_ets_unk225c+0x43a> -40002690: 91c8 l32i.n a12, a1, 36 -40002692: 000306 j 400026a2 <_X_ets_unk225c+0x446> -40002695: 8b1c00 depbits a0, a12, 24, 2 -40002698: 042bc7 blt a11, a12, 400026a0 <_X_ets_unk225c+0x444> -4000269b: 4c2c movi.n a12, 36 -4000269d: 1061c2 s32i a12, a1, 64 -400026a0: a1c8 l32i.n a12, a1, 40 -400026a2: 1021a2 l32i a10, a1, 64 -400026a5: aaca add.n a10, a10, a12 -400026a7: ffdaa2 addmi a10, a10, 0xffffff00 -400026aa: fc0aa2 l8ui a10, a10, 252 -400026ad: 0002e0 callx8 a2 -400026b0: ff2846 j 40002355 <_X_ets_unk225c+0xf9> -400026b3: 5a2c movi.n a10, 37 -400026b5: 0002e0 callx8 a2 -400026b8: ff6f86 j 4000247a <_X_ets_unk225c+0x21e> -400026bb: fb2c00 depbits a0, a12, 31, 3 -400026be: 000452 l8ui a5, a4, 0 -400026c1: c1c8 l32i.n a12, a1, 48 -400026c3: 441b addi.n a4, a4, 1 -400026c5: cc1b addi.n a12, a12, 1 -400026c7: c1c9 s32i.n a12, a1, 48 -400026c9: 023b57 bltu a11, a5, 400026cf <_X_ets_unk225c+0x473> -400026cc: ff3a86 j 400023ba <_X_ets_unk225c+0x15e> -400026cf: 9d3c movi.n a13, 57 -400026d1: 25bd57 bgeu a13, a5, 400026fa <_X_ets_unk225c+0x49e> -400026d4: ff3886 j 400023ba <_X_ets_unk225c+0x15e> -400026d7: 1c0c00 excw -400026da: 000452 l8ui a5, a4, 0 -400026dd: 441b addi.n a4, a4, 1 -400026df: bcc5e2 addi a14, a5, -68 -400026e2: f0ae56 bnez a14, 400025f0 <_X_ets_unk225c+0x394> -400026e5: d05c56 bnez a12, 400023ee <_X_ets_unk225c+0x192> -400026e8: 000806 j 4000270c <_X_ets_unk225c+0x4b0> -400026eb: ec5600 excw -400026ee: cf .byte 0xcf -400026ef: 000646 j 4000270c <_X_ets_unk225c+0x4b0> -400026f2: 9f3c movi.n a15, 57 -400026f4: 02bf57 bgeu a15, a5, 400026fa <_X_ets_unk225c+0x49e> -400026f7: ff2fc6 j 400023ba <_X_ets_unk225c+0x15e> -400026fa: f82c movi.n a8, 47 -400026fc: 000452 l8ui a5, a4, 0 -400026ff: 441b addi.n a4, a4, 1 -40002701: ed3857 bltu a8, a5, 400026f2 <_X_ets_unk225c+0x496> -40002704: ff2c86 j 400023ba <_X_ets_unk225c+0x15e> -40002707: 160000 excw -4000270a: fe .byte 0xfe -4000270b: cf .byte 0xcf -4000270c: 1021b2 l32i a11, a1, 64 -4000270f: 891c movi.n a9, 24 -40002711: 0bad mov.n a10, a11 -40002713: bb4b addi.n a11, a11, 4 -40002715: 1061b2 s32i a11, a1, 64 -40002718: 0429b7 blt a9, a11, 40002720 <_X_ets_unk225c+0x4c4> -4000271b: 9198 l32i.n a9, a1, 36 -4000271d: 0002c6 j 4000272c <_X_ets_unk225c+0x4d0> -40002720: 8c1c movi.n a12, 24 -40002722: 042ca7 blt a12, a10, 4000272a <_X_ets_unk225c+0x4ce> -40002725: 4d2c movi.n a13, 36 -40002727: 1061d2 s32i a13, a1, 64 -4000272a: a198 l32i.n a9, a1, 40 -4000272c: 1021e2 l32i a14, a1, 64 -4000272f: ee9a add.n a14, a14, a9 -40002731: ffdee2 addmi a14, a14, 0xffffff00 -40002734: 3f2ee2 l32i a14, a14, 252 -40002737: d1e9 s32i.n a14, a1, 52 -40002739: 31efe0 srai a14, a14, 31 -4000273c: e1e9 s32i.n a14, a1, 56 -4000273e: ff3f86 j 40002440 <_X_ets_unk225c+0x1e4> -40002741: 28ac00 excw -40002744: 1c0c movi.n a12, 1 -40002746: ff9c86 j 400025bc <_X_ets_unk225c+0x360> -40002749: e1b800 excw -4000274c: d1a8 l32i.n a10, a1, 52 -4000274e: 01cd mov.n a12, a1 -40002750: 0d1c movi.n a13, 16 -40002752: fef3e1 l32r a14, 40002320 <_X_ets_unk225c+0xc4> -40002755: ffb065 call8 4000225c <_X_ets_unk225c> -40002758: 0a3d mov.n a3, a10 -4000275a: ff8986 j 40002584 <_X_ets_unk225c+0x328> -4000275d: 030c movi.n a3, 0 -4000275f: ff45c6 j 4000247a <_X_ets_unk225c+0x21e> -40002762: 020c movi.n a2, 0 -40002764: f01d retw.n -40002766: d70000 excw -40002769: 023a add.n a0, a2, a3 -4000276b: ff9346 j 400025bc <_X_ets_unk225c+0x360> -4000276e: fff486 j 40002744 <_X_ets_unk225c+0x4e8> -40002771: 000000 ill - -40002774 : -40002774: 004136 entry a1, 32 -40002777: fe7631 l32r a3, 40002150 <_st_0x3fffda9c> -4000277a: 3329 s32i.n a2, a3, 12 -4000277c: f01d retw.n - ... - -40002780 <_c_0x4000223c_ets_uart_putc>: -40002780: 223c movi.n a2, 50 -40002782: 364000 excw - -40002784 <_X_ets_install_uart_printf>: -40002784: 004136 entry a1, 32 -40002787: fffea1 l32r a10, 40002780 <_c_0x4000223c_ets_uart_putc> -4000278a: fffea5 call8 40002774 -4000278d: f01d retw.n - ... - -40002790 <_c_0x400027dc>: -40002790: 27dc bnez.n a7, 400027a6 <_X_ets_install_external_printf+0x12> -40002792: 364000 excw - -40002794 <_X_ets_install_external_printf>: -40002794: 004136 entry a1, 32 -40002797: fe6e51 l32r a5, 40002150 <_st_0x3fffda9c> -4000279a: 5529 s32i.n a2, a5, 20 -4000279c: 838c beqz.n a3, 400027a8 <_X_ets_install_external_printf+0x14> -4000279e: 20a330 or a10, a3, a3 -400027a1: 000125 call8 400027b4 <_X_ets_install_putc2> -400027a4: 6549 s32i.n a4, a5, 24 -400027a6: f01d retw.n -400027a8: fffaa1 l32r a10, 40002790 <_c_0x400027dc> -400027ab: 0000a5 call8 400027b4 <_X_ets_install_putc2> -400027ae: 6549 s32i.n a4, a5, 24 -400027b0: f01d retw.n - ... - -400027b4 <_X_ets_install_putc2>: -400027b4: 004136 entry a1, 32 -400027b7: fe6631 l32r a3, 40002150 <_st_0x3fffda9c> -400027ba: 4329 s32i.n a2, a3, 16 -400027bc: f01d retw.n - ... - -400027c0 <_X_ets_get_printf_buf_remain_len>: -400027c0: 004136 entry a1, 32 -400027c3: fe6321 l32r a2, 40002150 <_st_0x3fffda9c> -400027c6: 0e1222 l16ui a2, a2, 28 -400027c9: f01d retw.n - ... - -400027cc <_X_ets_reset_printf_buf_len>: -400027cc: 004136 entry a1, 32 -400027cf: fe6031 l32r a3, 40002150 <_st_0x3fffda9c> -400027d2: 020c movi.n a2, 0 -400027d4: 0e5322 s16i a2, a3, 28 -400027d7: f01d retw.n -400027d9: 000000 ill - -400027dc <_X_ets_putc>: -400027dc: 004136 entry a1, 32 -400027df: fe5c61 l32r a6, 40002150 <_st_0x3fffda9c> -400027e2: 0e1642 l16ui a4, a6, 28 -400027e5: 8638 l32i.n a3, a6, 32 -400027e7: d48c beqz.n a4, 400027f8 <_X_ets_putc+0x1c> -400027e9: 004322 s8i a2, a3, 0 -400027ec: 540b addi.n a5, a4, -1 -400027ee: 331b addi.n a3, a3, 1 -400027f0: 0e5652 s16i a5, a6, 28 -400027f3: 8639 s32i.n a3, a6, 32 -400027f5: f01d retw.n -400027f7: f01d00 subx8 a1, a13, a0 -400027fa: bc0000 excw -400027fd: ffda add.n a15, a15, a13 -400027ff: 3f .byte 0x3f -40002800: 002220 excw -40002803: a13640 excw - -40002804 : -40002804: 00a136 entry a1, 80 -40002807: 4129 s32i.n a2, a1, 16 -40002809: 5139 s32i.n a3, a1, 20 -4000280b: 6149 s32i.n a4, a1, 24 -4000280d: 7159 s32i.n a5, a1, 28 -4000280f: 079d mov.n a9, a7 -40002811: fe4f71 l32r a7, 40002150 <_st_0x3fffda9c> -40002814: 8169 s32i.n a6, a1, 32 -40002816: 3788 l32i.n a8, a7, 12 -40002818: 9199 s32i.n a9, a1, 36 -4000281a: 38cc bnez.n a8, 40002821 -4000281c: 0427b2 l32i a11, a7, 16 -4000281f: 5bbc beqz.n a11, 40002858 -40002821: 57d8 l32i.n a13, a7, 20 -40002823: 00dd16 beqz a13, 40002834 -40002826: fff5a1 l32r a10, 400027fc <_X_ets_putc+0x20> -40002829: fccab2 addi a11, a10, -4 -4000282c: 04cac2 addi a12, a10, 4 -4000282f: 000de0 callx8 a13 -40002832: 4128 l32i.n a2, a1, 16 -40002834: fff3a1 l32r a10, 40002800 <_X_ets_putc+0x24> -40002837: 10c1d2 addi a13, a1, 16 -4000283a: 30c1c2 addi a12, a1, 48 -4000283d: 4e0c movi.n a14, 4 -4000283f: 02bd mov.n a11, a2 -40002841: 21e9 s32i.n a14, a1, 8 -40002843: 01c9 s32i.n a12, a1, 0 -40002845: 11d9 s32i.n a13, a1, 4 -40002847: ffade5 call8 40002324 <_X_ets_unk225c+0xc8> -4000284a: 67b8 l32i.n a11, a7, 24 -4000284c: 0a2d mov.n a2, a10 -4000284e: 3b8c beqz.n a11, 40002855 -40002850: 97a8 l32i.n a10, a7, 36 -40002852: 000be0 callx8 a11 -40002855: f01d retw.n -40002857: 020c00 andb b0, b12, b0 -4000285a: f01d retw.n -4000285c: 00a136 entry a1, 80 -4000285f: 02bd mov.n a11, a2 -40002861: 4129 s32i.n a2, a1, 16 -40002863: 5139 s32i.n a3, a1, 20 -40002865: 6149 s32i.n a4, a1, 24 -40002867: 7159 s32i.n a5, a1, 28 -40002869: 8169 s32i.n a6, a1, 32 -4000286b: 9179 s32i.n a7, a1, 36 -4000286d: ffc4a1 l32r a10, 40002780 <_c_0x4000223c_ets_uart_putc> -40002870: 30c1c2 addi a12, a1, 48 -40002873: 4e0c movi.n a14, 4 -40002875: 10c1d2 addi a13, a1, 16 -40002878: 11d9 s32i.n a13, a1, 4 -4000287a: 21e9 s32i.n a14, a1, 8 -4000287c: 01c9 s32i.n a12, a1, 0 -4000287e: ffaa65 call8 40002324 <_X_ets_unk225c+0xc8> -40002881: 0a2d mov.n a2, a10 -40002883: f01d retw.n -40002885: 000000 ill -40002888: 00a136 entry a1, 80 -4000288b: 5139 s32i.n a3, a1, 20 -4000288d: 6149 s32i.n a4, a1, 24 -4000288f: 7159 s32i.n a5, a1, 28 -40002891: 8169 s32i.n a6, a1, 32 -40002893: 02bd mov.n a11, a2 -40002895: fe2e21 l32r a2, 40002150 <_st_0x3fffda9c> -40002898: 9179 s32i.n a7, a1, 36 -4000289a: 52d8 l32i.n a13, a2, 20 -4000289c: 0461b2 s32i a11, a1, 16 -4000289f: 00ed16 beqz a13, 400028b1 -400028a2: 20c2a2 addi a10, a2, 32 -400028a5: 1cc2b2 addi a11, a2, 28 -400028a8: 24c2c2 addi a12, a2, 36 -400028ab: 000de0 callx8 a13 -400028ae: 0421b2 l32i a11, a1, 16 -400028b1: ffb7a1 l32r a10, 40002790 <_c_0x400027dc> -400028b4: 10c1d2 addi a13, a1, 16 -400028b7: 30c1c2 addi a12, a1, 48 -400028ba: 4e0c movi.n a14, 4 -400028bc: 21e9 s32i.n a14, a1, 8 -400028be: 01c9 s32i.n a12, a1, 0 -400028c0: 11d9 s32i.n a13, a1, 4 -400028c2: ffa625 call8 40002324 <_X_ets_unk225c+0xc8> -400028c5: 62b8 l32i.n a11, a2, 24 -400028c7: 0a3d mov.n a3, a10 -400028c9: 3b8c beqz.n a11, 400028d0 -400028cb: 92a8 l32i.n a10, a2, 36 -400028cd: 000be0 callx8 a11 -400028d0: 032d mov.n a2, a3 -400028d2: f01d retw.n - -400028d4 <_c_0xdfffffff>: -400028d4: ff .byte 0xff -400028d5: ff .byte 0xff -400028d6: ff .byte 0xff -400028d7: df .byte 0xdf - -400028d8 <_X_get_rst_cause>: -400028d8: 004136 entry a1, 32 -400028db: fa1741 l32r a4, 40001138 <_c_0x60007e00> -400028de: 0020c0 memw -400028e1: 8d2422 l32i a2, a4, 0x234 -400028e4: fffcc1 l32r a12, 400028d4 <_c_0xdfffffff> -400028e7: 542020 extui a2, a2, 0, 6 -400028ea: 325266 bnei a2, 5, 40002920 <_X_get_rst_cause+0x48> -400028ed: 0020c0 memw -400028f0: 8d2432 l32i a3, a4, 0x234 -400028f3: 943c30 extui a3, a3, 12, 10 -400028f6: 371326 beqi a3, 1, 40002931 <_X_get_rst_cause+0x59> -400028f9: 0020c0 memw -400028fc: fff681 l32r a8, 400028d4 <_c_0xdfffffff> -400028ff: 8d24a2 l32i a10, a4, 0x234 -40002902: 090c movi.n a9, 0 -40002904: 94aca0 extui a10, a10, 12, 10 -40002907: f8caa2 addi a10, a10, -8 -4000290a: 9329a0 movnez a2, a9, a10 -4000290d: 0020c0 memw -40002910: 862452 l32i a5, a4, 0x218 -40002913: 105580 and a5, a5, a8 -40002916: 0020c0 memw -40002919: 866452 s32i a5, a4, 0x218 -4000291c: f01d retw.n -4000291e: c00000 sub a0, a0, a0 -40002921: b20020 mulsh a0, a0, a2 -40002924: c08624 excw -40002927: 10bb addi.n a1, a0, 11 -40002929: 0020c0 memw -4000292c: 8664b2 s32i a11, a4, 0x218 -4000292f: f01d retw.n -40002931: ffe8e1 l32r a14, 400028d4 <_c_0xdfffffff> -40002934: 421c movi.n a2, 20 -40002936: 0020c0 memw -40002939: 8624d2 l32i a13, a4, 0x218 -4000293c: 10dde0 and a13, a13, a14 -4000293f: 0020c0 memw -40002942: 8664d2 s32i a13, a4, 0x218 -40002945: f01d retw.n - ... - -40002948 <_XX_unk2948>: -40002948: 004136 entry a1, 32 -4000294b: 070c movi.n a7, 0 -4000294d: f9fa51 l32r a5, 40001138 <_c_0x60007e00> -40002950: 0020c0 memw -40002953: a82562 l32i a6, a5, 0x2a0 -40002956: 0269 s32i.n a6, a2, 0 -40002958: 0020c0 memw -4000295b: a92542 l32i a4, a5, 0x2a4 -4000295e: 400300 ssr a3 -40002961: 914040 srl a4, a4 -40002964: 086407 bbci a4, 0, 40002970 <_l_2970> -40002967: 0020c0 memw -4000296a: a86572 s32i a7, a5, 0x2a0 -4000296d: f01d retw.n - ... - -40002970 <_l_2970>: -40002970: 0279 s32i.n a7, a2, 0 -40002972: 0020c0 memw -40002975: a86572 s32i a7, a5, 0x2a0 -40002978: f01d retw.n - ... - -4000297c <_X_sw_sys_rst>: -4000297c: 004136 entry a1, 32 -4000297f: f76e41 l32r a4, 40000738 <_c_0x80000000> -40002982: f9ed31 l32r a3, 40001138 <_c_0x60007e00> -40002985: 0020c0 memw -40002988: 802322 l32i a2, a3, 0x200 -4000298b: 202240 or a2, a2, a4 -4000298e: 0020c0 memw -40002991: 806322 s32i a2, a3, 0x200 -40002994: f01d retw.n - ... - -40002998 : -40002998: 004136 entry a1, 32 -4000299b: 80a042 movi a4, 128 -4000299e: f9e631 l32r a3, 40001138 <_c_0x60007e00> -400029a1: 0020c0 memw -400029a4: 802322 l32i a2, a3, 0x200 -400029a7: 202240 or a2, a2, a4 -400029aa: 0020c0 memw -400029ad: 806322 s32i a2, a3, 0x200 -400029b0: f01d retw.n - ... - -400029b4 <_c_0x00400000>: -400029b4: 400000 ssr a0 - ... - -400029b8 <_c_0xffbfffff>: -400029b8: ff .byte 0xff -400029b9: ff .byte 0xff -400029ba: bf .byte 0xbf -400029bb: ff .byte 0xff - -400029bc <_XX_apb_bridge_toggle>: -400029bc: 004136 entry a1, 32 -400029bf: f9de41 l32r a4, 40001138 <_c_0x60007e00> -400029c2: 229c beqz.n a2, 400029d8 <_XX_apb_bridge_toggle+0x1c> -400029c4: fffc31 l32r a3, 400029b4 <_c_0x00400000> -400029c7: 0020c0 memw -400029ca: 862422 l32i a2, a4, 0x218 -400029cd: 202230 or a2, a2, a3 -400029d0: 0020c0 memw -400029d3: 866422 s32i a2, a4, 0x218 -400029d6: f01d retw.n -400029d8: fff891 l32r a9, 400029b8 <_c_0xffbfffff> -400029db: 0020c0 memw -400029de: 862482 l32i a8, a4, 0x218 -400029e1: 108890 and a8, a8, a9 -400029e4: 0020c0 memw -400029e7: 866482 s32i a8, a4, 0x218 -400029ea: f01d retw.n - -400029ec <_X_ets_strcpy>: -400029ec: 004136 entry a1, 32 -400029ef: 20b330 or a11, a3, a3 -400029f2: 20a220 or a10, a2, a2 -400029f5: 032e65 call8 40005cdc -400029f8: 202aa0 or a2, a10, a10 -400029fb: f01d retw.n -400029fd: 000000 ill - -40002a00 <_X_ets_strncpy>: -40002a00: 004136 entry a1, 32 -40002a03: 04cd mov.n a12, a4 -40002a05: 03bd mov.n a11, a3 -40002a07: 02ad mov.n a10, a2 -40002a09: 034865 call8 40005e90 -40002a0c: 0a2d mov.n a2, a10 -40002a0e: f01d retw.n - -40002a10 <_X_ets_strcmp>: -40002a10: 004136 entry a1, 32 -40002a13: 20b330 or a11, a3, a3 -40002a16: 20a220 or a10, a2, a2 -40002a19: 0319e5 call8 40005bb8 -40002a1c: 202aa0 or a2, a10, a10 -40002a1f: f01d retw.n -40002a21: 000000 ill - -40002a24 <_X_ets_strncmp>: -40002a24: 004136 entry a1, 32 -40002a27: 04cd mov.n a12, a4 -40002a29: 03bd mov.n a11, a3 -40002a2b: 02ad mov.n a10, a2 -40002a2d: 033a25 call8 40005dd0 -40002a30: 0a2d mov.n a2, a10 -40002a32: f01d retw.n - -40002a34 <_X_ets_strlen>: -40002a34: 004136 entry a1, 32 -40002a37: 02ad mov.n a10, a2 -40002a39: 033325 call8 40005d6c -40002a3c: 0a2d mov.n a2, a10 -40002a3e: f01d retw.n - -40002a40 <_X_ets_strstr>: -40002a40: 004136 entry a1, 32 -40002a43: 20b330 or a11, a3, a3 -40002a46: 20a220 or a10, a2, a2 -40002a49: 035225 call8 40005f6c -40002a4c: 202aa0 or a2, a10, a10 -40002a4f: f01d retw.n -40002a51: 000000 ill - -40002a54 : -40002a54: 004136 entry a1, 32 -40002a57: 20b330 or a11, a3, a3 -40002a5a: 20a220 or a10, a2, a2 -40002a5d: 03e4e5 call8 400068ac -40002a60: 000090 retw - ... - -40002a64 : -40002a64: 004136 entry a1, 32 -40002a67: 0003a2 l8ui a10, a3, 0 -40002a6a: 9b0c movi.n a11, 9 -40002a6c: 02ca26 beqi a10, 32, 40002a72 -40002a6f: 139ab7 bne a10, a11, 40002a86 -40002a72: 0a8076 loop a0, 40002a80 -40002a75: 0103a2 l8ui a10, a3, 1 -40002a78: 331b addi.n a3, a3, 1 -40002a7a: f7ca26 beqi a10, 32, 40002a75 -40002a7d: 059ab7 bne a10, a11, 40002a86 -40002a80: fffb86 j 40002a72 -40002a83: 000000 ill -40002a86: 040c movi.n a4, 0 -40002a88: 0003a2 l8ui a10, a3, 0 -40002a8b: 0002e5 call8 40002ab8 -40002a8e: 0a5d mov.n a5, a10 -40002a90: 01ea96 bltz a10, 40002ab2 -40002a93: 0103a2 l8ui a10, a3, 1 -40002a96: 000225 call8 40002ab8 -40002a99: 015a96 bltz a10, 40002ab2 -40002a9c: 333b addi.n a3, a3, 3 -40002a9e: 924a add.n a9, a2, a4 -40002aa0: 1185c0 slli a8, a5, 4 -40002aa3: 441b addi.n a4, a4, 1 -40002aa5: 8a8a add.n a8, a10, a8 -40002aa7: 004982 s8i a8, a9, 0 -40002aaa: 744040 extui a4, a4, 0, 8 -40002aad: d76466 bnei a4, 6, 40002a88 -40002ab0: f01d retw.n -40002ab2: 020c movi.n a2, 0 -40002ab4: f01d retw.n -40002ab6: 360000 excw -40002ab9: 3c0041 l32r a4, 3ffd1abc <_start-0x2e544> -40002abc: f62c95 call4 3fff8d88 <_start-0x7278> -40002abf: 02b627 bgeu a6, a2, 40002ac5 -40002ac2: 3ab527 bgeu a5, a2, 40002b00 -40002ac5: 60a032 movi a3, 96 -40002ac8: 05b327 bgeu a3, a2, 40002ad1 -40002acb: 66a042 movi a4, 102 -40002ace: 0ab427 bgeu a4, a2, 40002adc -40002ad1: 084c movi.n a8, 64 -40002ad3: 1db827 bgeu a8, a2, 40002af4 -40002ad6: 46a092 movi a9, 70 -40002ad9: 173927 bltu a9, a2, 40002af4 -40002adc: 02b627 bgeu a6, a2, 40002ae2 -40002adf: 1db527 bgeu a5, a2, 40002b00 -40002ae2: 0a4c movi.n a10, 64 -40002ae4: 10ba27 bgeu a10, a2, 40002af8 -40002ae7: ab5c movi.n a11, 90 -40002ae9: 0b3b27 bltu a11, a2, 40002af8 -40002aec: c9c222 addi a2, a2, -55 -40002aef: 232200 sext a2, a2, 7 -40002af2: f01d retw.n -40002af4: f27c movi.n a2, -1 -40002af6: f01d retw.n -40002af8: a9c222 addi a2, a2, -87 -40002afb: 232200 sext a2, a2, 7 -40002afe: f01d retw.n -40002b00: d0c222 addi a2, a2, -48 -40002b03: 232200 sext a2, a2, 7 -40002b06: f01d retw.n -40002b08: 004136 entry a1, 32 -40002b0b: 00bbe5 call8 400036c8 <_X_uart_wait_tx_empty+0x2c> -40002b0e: 0042a2 s8i a10, a2, 0 -40002b11: f01d retw.n - ... - -40002b14 : -40002b14: 004136 entry a1, 32 -40002b17: 02ad mov.n a10, a2 -40002b19: 00b125 call8 4000362c -40002b1c: f01d retw.n -40002b1e: 000000 ill -40002b21: 6e .byte 0x6e -40002b22: 366000 excw -40002b25: 310041 l32r a4, 3ffcef28 <_start-0x310d8> -40002b28: fe .byte 0xfe -40002b29: ff .byte 0xff -40002b2a: 0020c0 memw -40002b2d: 8c6322 s32i a2, a3, 0x230 -40002b30: f01d retw.n -40002b32: 360000 excw -40002b35: c20041 l32r a4, 3fff3338 <_start-0xccc8> -40002b38: b180a2 excw -40002b3b: fff9 s32i.n a15, a15, 60 -40002b3d: 0020c0 memw -40002b40: 892bb2 l32i a11, a11, 0x224 -40002b43: c0a2b0 sub a10, a2, a11 -40002b46: 0e2ca7 blt a12, a10, 40002b58 -40002b49: 131aa6 blti a10, 1, 40002b60 -40002b4c: 03d2a2 addmi a10, a2, 0x300 -40002b4f: 84caa2 addi a10, a10, -124 -40002b52: fffd25 call8 40002b24 -40002b55: f01d retw.n -40002b57: 02ad00 andb b10, b13, b0 -40002b5a: fffca5 call8 40002b24 -40002b5d: f01d retw.n -40002b5f: abca00 depbits a0, a10, 26, 13 -40002b62: fffc25 call8 40002b24 -40002b65: f01d retw.n -40002b67: 413600 srli a3, a0, 6 -40002b6a: 323900 orbc b3, b9, b0 -40002b6d: 4249 s32i.n a4, a2, 16 -40002b6f: 080c movi.n a8, 0 -40002b71: f57c movi.n a5, -1 -40002b73: 0259 s32i.n a5, a2, 0 -40002b75: 1289 s32i.n a8, a2, 4 -40002b77: 2289 s32i.n a8, a2, 8 -40002b79: f01d retw.n -40002b7b: dad000 depbits a0, a0, 13, 14 -40002b7e: ff .byte 0xff -40002b7f: 3f .byte 0x3f -40002b80: ca88 l32i.n a8, a10, 48 -40002b82: ff .byte 0xff -40002b83: 3f .byte 0x3f -40002b84: ccd8 l32i.n a13, a12, 48 -40002b86: ff .byte 0xff -40002b87: 3f .byte 0x3f -40002b88: 004136 entry a1, 32 -40002b8b: fffcb1 l32r a11, 40002b7c -40002b8e: 0b98 l32i.n a9, a11, 0 -40002b90: 69bc beqz.n a9, 40002bca -40002b92: 00a0a2 movi a10, 0 -40002b95: 0d8076 loop a0, 40002ba6 -40002b98: 1988 l32i.n a8, a9, 4 -40002b9a: c08280 sub a8, a2, a8 -40002b9d: 0818a6 blti a8, 1, 40002ba9 -40002ba0: 09ad mov.n a10, a9 -40002ba2: 0998 l32i.n a9, a9, 0 -40002ba4: 198c beqz.n a9, 40002ba9 -40002ba6: fffac6 j 40002b95 -40002ba9: 0399 s32i.n a9, a3, 0 -40002bab: 1329 s32i.n a2, a3, 4 -40002bad: da9c beqz.n a10, 40002bce -40002baf: 0a39 s32i.n a3, a10, 0 -40002bb1: 0398 l32i.n a9, a3, 0 -40002bb3: 119937 bne a9, a3, 40002bc8 -40002bb6: fff2a1 l32r a10, 40002b80 -40002bb9: fff2b1 l32r a11, 40002b84 -40002bbc: b1a0c2 movi a12, 177 -40002bbf: 201110 or a1, a1, a1 -40002bc2: ffc425 call8 40002804 -40002bc5: ffff06 j 40002bc5 -40002bc8: f01d retw.n -40002bca: 0399 s32i.n a9, a3, 0 -40002bcc: 1329 s32i.n a2, a3, 4 -40002bce: 02ad mov.n a10, a2 -40002bd0: 0b39 s32i.n a3, a11, 0 -40002bd2: fff625 call8 40002b34 -40002bd5: fff606 j 40002bb1 -40002bd8: 1388 l32i.n a8, a3, 4 -40002bda: 360000 excw -40002bdd: 880041 l32r a4, 3ffe4be0 <_start-0x1b420> -40002be0: fd9102 l16si a0, a1, 0x1fa -40002be3: ff .byte 0xff -40002be4: 110826 beqi a8, -1, 40002bf9 -40002be7: ffe6a1 l32r a10, 40002b80 -40002bea: ffe6b1 l32r a11, 40002b84 -40002bed: bda0c2 movi a12, 189 -40002bf0: ffc125 call8 40002804 -40002bf3: ffff06 j 40002bf3 -40002bf6: 000000 ill -40002bf9: 823390 mull a3, a3, a9 -40002bfc: 148c beqz.n a4, 40002c01 -40002bfe: 026232 s32i a3, a2, 8 -40002c01: fe4a65 call8 400010a8 <_X_ets_enter_critical> -40002c04: 20b220 or a11, a2, a2 -40002c07: ffc6a1 l32r a10, 40002b20 -40002c0a: 0020c0 memw -40002c0d: 892aa2 l32i a10, a10, 0x224 -40002c10: f03d nop.n -40002c12: 80a3a0 add a10, a3, a10 -40002c15: fff725 call8 40002b88 -40002c18: fe4a25 call8 400010bc <_X_ets_exit_critical> -40002c1b: f01d retw.n -40002c1d: 000000 ill -40002c20: 004136 entry a1, 32 -40002c23: 0288 l32i.n a8, a2, 0 -40002c25: a03330 addx4 a3, a3, a3 -40002c28: 110826 beqi a8, -1, 40002c3d -40002c2b: ffd5a1 l32r a10, 40002b80 -40002c2e: ffd5b1 l32r a11, 40002b84 -40002c31: d3a0c2 movi a12, 211 -40002c34: ffbce5 call8 40002804 -40002c37: ffff06 j 40002c37 -40002c3a: 000000 ill -40002c3d: 048c beqz.n a4, 40002c41 -40002c3f: 2239 s32i.n a3, a2, 8 -40002c41: fe4665 call8 400010a8 <_X_ets_enter_critical> -40002c44: 20b220 or a11, a2, a2 -40002c47: ffb6a1 l32r a10, 40002b20 -40002c4a: 0020c0 memw -40002c4d: 892aa2 l32i a10, a10, 0x224 -40002c50: 80a3a0 add a10, a3, a10 -40002c53: fff365 call8 40002b88 -40002c56: fe4665 call8 400010bc <_X_ets_exit_critical> -40002c59: 000090 retw -40002c5c: 004136 entry a1, 32 -40002c5f: fe44a5 call8 400010a8 <_X_ets_enter_critical> -40002c62: ffc6c1 l32r a12, 40002b7c -40002c65: 0020f0 nop -40002c68: 002ca2 l32i a10, a12, 0 -40002c6b: 019a16 beqz a10, 40002c88 -40002c6e: 00a0b2 movi a11, 0 -40002c71: 088076 loop a0, 40002c7d -40002c74: 0812a7 beq a2, a10, 40002c80 -40002c77: 0abd mov.n a11, a10 -40002c79: 0aa8 l32i.n a10, a10, 0 -40002c7b: 9a8c beqz.n a10, 40002c88 -40002c7d: fffc06 j 40002c71 -40002c80: 4a8c beqz.n a10, 40002c88 -40002c82: 2b9c beqz.n a11, 40002c98 -40002c84: 0a88 l32i.n a8, a10, 0 -40002c86: 0b89 s32i.n a8, a11, 0 -40002c88: fe4325 call8 400010bc <_X_ets_exit_critical> -40002c8b: 00a092 movi a9, 0 -40002c8e: ffafa2 movi a10, -1 -40002c91: 0062a2 s32i a10, a2, 0 -40002c94: 2299 s32i.n a9, a2, 8 -40002c96: f01d retw.n -40002c98: 0ab8 l32i.n a11, a10, 0 -40002c9a: 0cb9 s32i.n a11, a12, 0 -40002c9c: fffa06 j 40002c88 -40002c9f: 413600 srli a3, a0, 6 -40002ca2: 028800 andb b8, b8, b0 -40002ca5: 0f0826 beqi a8, -1, 40002cb8 -40002ca8: ffb6a1 l32r a10, 40002b80 -40002cab: ffb6b1 l32r a11, 40002b84 -40002cae: 02a1c2 movi a12, 0x102 -40002cb1: ffb525 call8 40002804 -40002cb4: ffff06 j 40002cb4 -40002cb7: 02ad00 andb b10, b13, b0 -40002cba: 0b0c movi.n a11, 0 -40002cbc: 0c0c movi.n a12, 0 -40002cbe: ffeaa5 call8 40002b68 -40002cc1: f01d retw.n -40002cc3: 413600 srli a3, a0, 6 -40002cc6: 3e2500 excw -40002cc9: fe .byte 0xfe -40002cca: ff9521 l32r a2, 40002b20 -40002ccd: ffab31 l32r a3, 40002b7c -40002cd0: 0020c0 memw -40002cd3: 03b8 l32i.n a11, a3, 0 -40002cd5: 892292 l32i a9, a2, 0x224 -40002cd8: 0b9c beqz.n a11, 40002cec -40002cda: 1b88 l32i.n a8, a11, 4 -40002cdc: ffaf42 movi a4, -1 -40002cdf: c08890 sub a8, a8, a9 -40002ce2: 0e18a6 blti a8, 1, 40002cf4 -40002ce5: 3b8c beqz.n a11, 40002cec -40002ce7: 1ba8 l32i.n a10, a11, 4 -40002ce9: ffe4a5 call8 40002b34 -40002cec: fe3ce5 call8 400010bc <_X_ets_exit_critical> -40002cef: f01d retw.n -40002cf1: 000000 ill -40002cf4: 000506 j 40002d0c -40002cf7: c00000 sub a0, a0, a0 -40002cfa: b80020 excw -40002cfd: 229203 excw -40002d00: 1689 s32i.n a8, a6, 4 -40002d02: fe7b addi.n a15, a14, 7 -40002d04: 1ba8 l32i.n a10, a11, 4 -40002d06: c0aa90 sub a10, a10, a9 -40002d09: d81ae6 bgei a10, 1, 40002ce5 -40002d0c: 0b5d mov.n a5, a11 -40002d0e: 13b9 s32i.n a11, a3, 4 -40002d10: 3bd8 l32i.n a13, a11, 12 -40002d12: 23d9 s32i.n a13, a3, 8 -40002d14: 0bc8 l32i.n a12, a11, 0 -40002d16: 03c9 s32i.n a12, a3, 0 -40002d18: 0b49 s32i.n a4, a11, 0 -40002d1a: fe3a25 call8 400010bc <_X_ets_exit_critical> -40002d1d: 3588 l32i.n a8, a5, 12 -40002d1f: 45a8 l32i.n a10, a5, 16 -40002d21: 0008e0 callx8 a8 -40002d24: fe3825 call8 400010a8 <_X_ets_enter_critical> -40002d27: 0598 l32i.n a9, a5, 0 -40002d29: 25a8 l32i.n a10, a5, 8 -40002d2b: ca0966 bnei a9, -1, 40002cf9 -40002d2e: fc7a16 beqz a10, 40002cf9 -40002d31: 15b8 l32i.n a11, a5, 4 -40002d33: abaa add.n a10, a11, a10 -40002d35: 05bd mov.n a11, a5 -40002d37: 15a9 s32i.n a10, a5, 4 -40002d39: ffe4e5 call8 40002b88 -40002d3c: ffee46 j 40002cf9 -40002d3f: 413600 srli a3, a0, 6 -40002d42: fa1c00 depbits a0, a12, 15, 2 -40002d45: 0b0c movi.n a11, 0 -40002d47: 0c0c movi.n a12, 0 -40002d49: fe24e5 call8 40000f98 <_XX_unk0f96> -40002d4c: f01d retw.n -40002d4e: 360000 excw -40002d51: 880041 l32r a4, 3ffe4d54 <_start-0x1b2ac> -40002d54: 088c02 excw -40002d57: f01d retw.n -40002d59: 0a0c movi.n a10, 0 -40002d5b: fff6a5 call8 40002cc4 -40002d5e: f01d retw.n -40002d60: 002d40 excw -40002d63: 2d5040 excw -40002d66: e04000 subx4 a4, a0, a0 -40002d69: ffda add.n a15, a15, a13 -40002d6b: 3f .byte 0x3f -40002d6c: 004136 entry a1, 32 -40002d6f: 0aa0a2 movi a10, 10 -40002d72: fffbb1 l32r a11, 40002d60 -40002d75: ff8181 l32r a8, 40002b7c -40002d78: 020c movi.n a2, 0 -40002d7a: 02cd mov.n a12, a2 -40002d7c: 006822 s32i a2, a8, 0 -40002d7f: fe36a5 call8 400010e8 <_X_ets_isr_attach> -40002d82: 00a4a2 movi a10, 0x400 -40002d85: fe37e5 call8 40001104 <_X_ets_isr_unmask> -40002d88: fff7a1 l32r a10, 40002d64 -40002d8b: fb1c movi.n a11, 31 -40002d8d: fff6c1 l32r a12, 40002d68 -40002d90: 4d0c movi.n a13, 4 -40002d92: fe1c25 call8 40000f54 <_X_ets_task> -40002d95: ff6291 l32r a9, 40002b20 -40002d98: 0020c0 memw -40002d9b: 8c6922 s32i a2, a9, 0x230 -40002d9e: 88a0a2 movi a10, 136 -40002da1: 0020c0 memw -40002da4: 8a69a2 s32i a10, a9, 0x228 -40002da7: 0020c0 memw -40002daa: 886922 s32i a2, a9, 0x220 -40002dad: f01d retw.n -40002daf: c84400 excw -40002db2: ff .byte 0xff -40002db3: 3f .byte 0x3f - -40002db4 : -40002db4: 004136 entry a1, 32 -40002db7: 0392e5 call8 400066e4 <_xtos_set_intlevel+0x74> -40002dba: fffd81 l32r a8, 40002db0 -40002dbd: 002882 l32i a8, a8, 0 -40002dc0: 0a3d mov.n a3, a10 -40002dc2: 822820 mull a2, a8, a2 -40002dc5: 0391e5 call8 400066e4 <_xtos_set_intlevel+0x74> -40002dc8: c09a30 sub a9, a10, a3 -40002dcb: 08b927 bgeu a9, a2, 40002dd7 -40002dce: 039165 call8 400066e4 <_xtos_set_intlevel+0x74> -40002dd1: c0aa30 sub a10, a10, a3 -40002dd4: f63a27 bltu a10, a2, 40002dce -40002dd7: f01d retw.n -40002dd9: 000000 ill - -40002ddc : -40002ddc: 004136 entry a1, 32 -40002ddf: fff431 l32r a3, 40002db0 -40002de2: 0329 s32i.n a2, a3, 0 -40002de4: f01d retw.n - ... - -40002de8 : -40002de8: 004136 entry a1, 32 -40002deb: fff121 l32r a2, 40002db0 -40002dee: 0228 l32i.n a2, a2, 0 -40002df0: f01d retw.n -40002df2: 360000 excw -40002df5: a60041 l32r a4, 3ffec5f8 <_start-0x13a08> -40002df8: 424913 excw -40002dfb: 30efa0 xor a14, a15, a10 -40002dfe: 6d1450 excw -40002e01: 326003 excw -40002e04: a57621 l32r a2, 3ffec3dc <_start-0x13c24> -40002e07: 520a add.n a5, a2, a0 -40002e09: 1b0002 l8ui a0, a0, 27 -40002e0c: 454022 s8i a2, a0, 69 -40002e0f: 404030 excw -40002e12: f03d74 excw -40002e15: 25a376 loopgtz a3, 40002e3e -40002e18: 000232 l8ui a3, a2, 0 -40002e1b: 010252 l8ui a5, a2, 1 -40002e1e: 020262 l8ui a6, a2, 2 -40002e21: 030272 l8ui a7, a2, 3 -40002e24: 303340 xor a3, a3, a4 -40002e27: 224b addi.n a2, a2, 4 -40002e29: 743030 extui a3, a3, 0, 8 -40002e2c: 303530 xor a3, a5, a3 -40002e2f: 743030 extui a3, a3, 0, 8 -40002e32: 303630 xor a3, a6, a3 -40002e35: 743030 extui a3, a3, 0, 8 -40002e38: 303730 xor a3, a7, a3 -40002e3b: 744030 extui a4, a3, 0, 8 -40002e3e: 042d mov.n a2, a4 -40002e40: f01d retw.n -40002e42: 220000 orb b0, b0, b0 -40002e45: 1defa0 excw -40002e48: 0000f0 callx12 a0 - ... - -40002e4c <_st_0x3fffdb10_uartdev>: -40002e4c: ffdb10 excw -40002e4f: 3f .byte 0x3f - -40002e50 <_c_0x3fffdb00>: -40002e50: ffdb00 excw -40002e53: 3f .byte 0x3f -40002e54: 004000 break 0, 0 - ... - -40002e58 <_XX_unk2e58>: -40002e58: 006136 entry a1, 48 -40002e5b: 02ad mov.n a10, a2 -40002e5d: 005765 call8 400033d4 <_c_0x0000ffff+0x4> -40002e60: fffb41 l32r a4, 40002e4c <_st_0x3fffdb10_uartdev> -40002e63: c498 l32i.n a9, a4, 48 -40002e65: 230c movi.n a3, 2 -40002e67: 39ac beqz.n a9, 40002e8e <_XX_unk2e58+0x36> -40002e69: 211926 beqi a9, 1, 40002e8e <_XX_unk2e58+0x36> -40002e6c: f9b1a1 l32r a10, 40001530 <_s_waiting_for_host+0x4> -40002e6f: f9b1b1 l32r a11, 40001534 <_s_waiting_for_host+0x8> -40002e72: 01a0c2 movi a12, 1 -40002e75: 00b125 call8 40003988 <_X_RcvMsg> -40002e78: 00ea56 bnez a10, 40002e8a <_XX_unk2e58+0x32> -40002e7b: f9ada1 l32r a10, 40001530 <_s_waiting_for_host+0x4> -40002e7e: 000a82 l8ui a8, a10, 0 -40002e81: 010a92 l8ui a9, a10, 1 -40002e84: 002856 bnez a8, 40002e8a <_XX_unk2e58+0x32> -40002e87: 1e8926 beqi a9, 8, 40002ea9 <_XX_unk2e58+0x51> -40002e8a: 020c movi.n a2, 0 -40002e8c: f01d retw.n -40002e8e: 20a220 or a10, a2, a2 -40002e91: 01a0b2 movi a11, 1 -40002e94: 005b65 call8 4000344c <_c_0x000fffff+0x4> -40002e97: f4b0a0 extui a11, a10, 0, 16 -40002e9a: fecb16 beqz a11, 40002e8a <_XX_unk2e58+0x32> -40002e9d: 02ad mov.n a10, a2 -40002e9f: 0064a5 call8 400034e8 -40002ea2: c439 s32i.n a3, a4, 48 -40002ea4: fff106 j 40002e6c <_XX_unk2e58+0x14> -40002ea7: 0c0000 excw -40002eaa: 411c movi.n a1, 20 -40002eac: ffe9 s32i.n a14, a15, 60 -40002eae: 8b0c movi.n a11, 8 -40002eb0: 015432 s16i a3, a4, 2 -40002eb3: 0144b2 s8i a11, a4, 1 -40002eb6: 0044c2 s8i a12, a4, 0 -40002eb9: 01bd mov.n a11, a1 -40002ebb: c44b addi.n a12, a4, 4 -40002ebd: 004065 call8 400032c4 <_X_UartConnectProc> -40002ec0: f98d61 l32r a6, 400014f4 <_c_0x60008e00> -40002ec3: f98b51 l32r a5, 400014f0 <_c_0xffff8fff> -40002ec6: 00a0d2 movi a13, 0 -40002ec9: 0844d2 s8i a13, a4, 8 -40002ecc: 0944d2 s8i a13, a4, 9 -40002ecf: 053216 beqz a2, 40002f26 <_XX_unk2e58+0xce> -40002ed2: f897f1 l32r a15, 40001130 <_c_0x60003e00> -40002ed5: ffdfa1 l32r a10, 40002e54 <_c_0x3fffdb00+0x4> -40002ed8: 0020c0 memw -40002edb: 9626d2 l32i a13, a6, 0x258 -40002ede: 10dd50 and a13, a13, a5 -40002ee1: 0020c0 memw -40002ee4: 9666d2 s32i a13, a6, 0x258 -40002ee7: 0020c0 memw -40002eea: 9626c2 l32i a12, a6, 0x258 -40002eed: 20cca0 or a12, a12, a10 -40002ef0: 0020c0 memw -40002ef3: 9666c2 s32i a12, a6, 0x258 -40002ef6: 0020c0 memw -40002ef9: 9526b2 l32i a11, a6, 0x254 -40002efc: 10bb50 and a11, a11, a5 -40002eff: 0020c0 memw -40002f02: 9566b2 s32i a11, a6, 0x254 -40002f05: 0020c0 memw -40002f08: 952692 l32i a9, a6, 0x254 -40002f0b: 2099a0 or a9, a9, a10 -40002f0e: 0020c0 memw -40002f11: 956692 s32i a9, a6, 0x254 -40002f14: f88681 l32r a8, 4000112c <_c_0xfffdffff> -40002f17: 0020c0 memw -40002f1a: f02fe2 l32i a14, a15, 0x3c0 -40002f1d: 10ee80 and a14, a14, a8 -40002f20: 0020c0 memw -40002f23: f06fe2 s32i a14, a15, 0x3c0 -40002f26: 830c movi.n a3, 8 -40002f28: 20a440 or a10, a4, a4 -40002f2b: 0aa0b2 movi a11, 10 -40002f2e: 008fa5 call8 40003828 <_X_SendMsg> -40002f31: 0b1a26 beqi a10, 1, 40002f40 <_XX_unk2e58+0xe8> -40002f34: ffc332 addi a3, a3, -1 -40002f37: fed356 bnez a3, 40002f28 <_XX_unk2e58+0xd0> -40002f3a: 220c movi.n a2, 2 -40002f3c: f01d retw.n -40002f3e: 160000 excw -40002f41: c0f462 s32ri a6, a4, 0x300 -40002f44: f20020 rems a0, a0, a2 -40002f47: 509626 beqi a6, 10, 40002f9b <_X_UartDwnLdProc+0x2f> -40002f4a: ff .byte 0xff -40002f4b: 20c010 or a12, a0, a1 -40002f4e: 66f200 excw -40002f51: 20c096 bltz a0, 40003161 <_XX_unk313c+0x25> -40002f54: 26e200 excw -40002f57: 20c096 bltz a0, 40003167 <_XX_unk313c+0x2b> -40002f5a: 66e200 excw -40002f5d: 020c96 bltz a12, 40002f81 <_X_UartDwnLdProc+0x15> -40002f60: f01d retw.n - ... - -40002f64 <_c_0x3fffdb04>: -40002f64: ffdb04 excw -40002f67: 3f .byte 0x3f -40002f68: ffc9f0 excw -40002f6b: 3f .byte 0x3f - -40002f6c <_X_UartDwnLdProc>: -40002f6c: 00a136 entry a1, 80 -40002f6f: 0a2c movi.n a10, 32 -40002f71: 280c movi.n a8, 2 -40002f73: ffb751 l32r a5, 40002e50 <_c_0x3fffdb00> -40002f76: 046d mov.n a6, a4 -40002f78: 070c movi.n a7, 0 -40002f7a: 6179 s32i.n a7, a1, 24 -40002f7c: 140c movi.n a4, 1 -40002f7e: 004542 s8i a4, a5, 0 -40002f81: 015582 s16i a8, a5, 2 -40002f84: fe17e5 call8 40001104 <_X_ets_isr_unmask> -40002f87: 6198 l32i.n a9, a1, 24 -40002f89: 5c2926 beqi a9, 2, 40002fe9 <_X_UartDwnLdProc+0x7d> -40002f8c: f43030 extui a3, a3, 0, 16 -40002f8f: 870c movi.n a7, 8 -40002f91: 8169 s32i.n a6, a1, 32 -40002f93: fff491 l32r a9, 40002f64 <_c_0x3fffdb04> -40002f96: 9199 s32i.n a9, a1, 36 -40002f98: 20b330 or a11, a3, a3 -40002f9b: 20a220 or a10, a2, a2 -40002f9e: 00a0c2 movi a12, 0 -40002fa1: 009e65 call8 40003988 <_X_RcvMsg> -40002fa4: aa0b addi.n a10, a10, -1 -40002fa6: 134a16 beqz a10, 400030de <_X_UartDwnLdProc+0x172> -40002fa9: 0002b2 l8ui a11, a2, 0 -40002fac: 1a0c movi.n a10, 1 -40002fae: 04fb16 beqz a11, 40003001 <_X_UartDwnLdProc+0x95> -40002fb1: 590c movi.n a9, 5 -40002fb3: 7199 s32i.n a9, a1, 28 -40002fb5: 094592 s8i a9, a5, 9 -40002fb8: 04cd mov.n a12, a4 -40002fba: 0105d2 l8ui a13, a5, 1 -40002fbd: 01a0e2 movi a14, 1 -40002fc0: 93aea0 movnez a10, a14, a10 -40002fc3: 0845a2 s8i a10, a5, 8 -40002fc6: f8cdd2 addi a13, a13, -8 -40002fc9: 83c7d0 moveqz a12, a7, a13 -40002fcc: 206cc0 or a6, a12, a12 -40002fcf: 011c16 beqz a12, 40002fe4 <_X_UartDwnLdProc+0x78> -40002fd2: 20a550 or a10, a5, a5 -40002fd5: 0aa0b2 movi a11, 10 -40002fd8: 0084e5 call8 40003828 <_X_SendMsg> -40002fdb: 161a26 beqi a10, 1, 40002ff5 <_X_UartDwnLdProc+0x89> -40002fde: ffc662 addi a6, a6, -1 -40002fe1: fed656 bnez a6, 40002fd2 <_X_UartDwnLdProc+0x66> -40002fe4: 61f8 l32i.n a15, a1, 24 -40002fe6: ae2f66 bnei a15, 2, 40002f98 <_X_UartDwnLdProc+0x2c> -40002fe9: 20a0a2 movi a10, 32 -40002fec: fe10a5 call8 400010f8 <_X_ets_isr_mask> -40002fef: 00a022 movi a2, 0 -40002ff2: 000090 retw -40002ff5: 20a0a2 movi a10, 32 -40002ff8: fe0fe5 call8 400010f8 <_X_ets_isr_mask> -40002ffb: 01a022 movi a2, 1 -40002ffe: 000090 retw -40003001: 010292 l8ui a9, a2, 1 -40003004: ffd981 l32r a8, 40002f68 <_c_0x3fffdb04+0x4> -40003007: 07a9f6 bgeui a9, 12, 40003012 <_X_UartDwnLdProc+0xa6> -4000300a: a08980 addx4 a8, a9, a8 -4000300d: 0888 l32i.n a8, a8, 0 -4000300f: 0008a0 jx a8 -40003012: 1a0c movi.n a10, 1 -40003014: 590c movi.n a9, 5 -40003016: 7199 s32i.n a9, a1, 28 -40003018: 7198 l32i.n a9, a1, 28 -4000301a: ffe5c6 j 40002fb5 <_X_UartDwnLdProc+0x49> -4000301d: 02ad00 andb b10, b13, b0 -40003020: 1cc1b2 addi a11, a1, 28 -40003023: 01cd mov.n a12, a1 -40003025: 001ea5 call8 40003210 <_XX_unk3210> -40003028: 5b0c movi.n a11, 5 -4000302a: 0145b2 s8i a11, a5, 1 -4000302d: fff9c6 j 40003018 <_X_UartDwnLdProc+0xac> -40003030: 02ad mov.n a10, a2 -40003032: 1cc1b2 addi a11, a1, 28 -40003035: 001ae5 call8 400031e4 <_XX_unk31e4> -40003038: bc0c movi.n a12, 11 -4000303a: 0145c2 s8i a12, a5, 1 -4000303d: fff5c6 j 40003018 <_X_UartDwnLdProc+0xac> -40003040: 02ad mov.n a10, a2 -40003042: 1cc1b2 addi a11, a1, 28 -40003045: 91c8 l32i.n a12, a1, 36 -40003047: 002d25 call8 40003318 <_X_UartRegReadProc> -4000304a: ad0c movi.n a13, 10 -4000304c: 0145d2 s8i a13, a5, 1 -4000304f: fff146 j 40003018 <_X_UartDwnLdProc+0xac> -40003052: 02ad mov.n a10, a2 -40003054: 1cc1b2 addi a11, a1, 28 -40003057: 0027e5 call8 400032d4 <_X_UartRegWriteProc> -4000305a: 9e0c movi.n a14, 9 -4000305c: 0145e2 s8i a14, a5, 1 -4000305f: ffed46 j 40003018 <_X_UartDwnLdProc+0xac> -40003062: 02ad mov.n a10, a2 -40003064: 1cc1b2 addi a11, a1, 28 -40003067: 91c8 l32i.n a12, a1, 36 -40003069: 0025a5 call8 400032c4 <_X_UartConnectProc> -4000306c: 014572 s8i a7, a5, 1 -4000306f: ffe946 j 40003018 <_X_UartDwnLdProc+0xac> -40003072: 02ad mov.n a10, a2 -40003074: 1cc1b2 addi a11, a1, 28 -40003077: 01cd mov.n a12, a1 -40003079: 001c65 call8 40003240 <_XX_unk3240> -4000307c: 7f0c movi.n a15, 7 -4000307e: 0145f2 s8i a15, a5, 1 -40003081: ffe4c6 j 40003018 <_X_UartDwnLdProc+0xac> -40003084: 02ad mov.n a10, a2 -40003086: 1cc1b2 addi a11, a1, 28 -40003089: 01cd mov.n a12, a1 -4000308b: 81d8 l32i.n a13, a1, 32 -4000308d: 0020e5 call8 4000329c <_X_MemDwnLdStopReqMsgProc> -40003090: 680c movi.n a8, 6 -40003092: 014582 s8i a8, a5, 1 -40003095: ffdfc6 j 40003018 <_X_UartDwnLdProc+0xac> -40003098: 02ad mov.n a10, a2 -4000309a: 1cc1b2 addi a11, a1, 28 -4000309d: 01cd mov.n a12, a1 -4000309f: 0011e5 call8 400031bc <_XX_unk31bc> -400030a2: 490c movi.n a9, 4 -400030a4: 81c8 l32i.n a12, a1, 32 -400030a6: 0b0c movi.n a11, 0 -400030a8: 0cb9 s32i.n a11, a12, 0 -400030aa: 014592 s8i a9, a5, 1 -400030ad: ffd9c6 j 40003018 <_X_UartDwnLdProc+0xac> -400030b0: 02ad mov.n a10, a2 -400030b2: 1cc1b2 addi a11, a1, 28 -400030b5: 01cd mov.n a12, a1 -400030b7: 000865 call8 4000313c <_XX_unk313c> -400030ba: 3d0c movi.n a13, 3 -400030bc: 0145d2 s8i a13, a5, 1 -400030bf: ffd546 j 40003018 <_X_UartDwnLdProc+0xac> -400030c2: 00a0a2 movi a10, 0 -400030c5: 00a0b2 movi a11, 0 -400030c8: 012a65 call8 40004370 <_X_spi_flash_attach> -400030cb: 20a220 or a10, a2, a2 -400030ce: 1cc1b2 addi a11, a1, 28 -400030d1: 01cd mov.n a12, a1 -400030d3: 0001e5 call8 400030f0 <_X_FlashDwnLdStartMsgProc> -400030d6: 2e0c movi.n a14, 2 -400030d8: 0145e2 s8i a14, a5, 1 -400030db: ffce46 j 40003018 <_X_UartDwnLdProc+0xac> -400030de: 20a0a2 movi a10, 32 -400030e1: fe0165 call8 400010f8 <_X_ets_isr_mask> -400030e4: 01a022 movi a2, 1 -400030e7: 000090 retw - ... - -400030ec <_c_0x00001800>: -400030ec: 001800 movsp a0, a8 - ... - -400030f0 <_X_FlashDwnLdStartMsgProc>: -400030f0: 004136 entry a1, 32 -400030f3: 011282 l16ui a8, a2, 2 -400030f6: 05a0a2 movi a10, 5 -400030f9: 05b826 beqi a8, 16, 40003102 <_X_FlashDwnLdStartMsgProc+0x12> -400030fc: 120c movi.n a2, 1 -400030fe: 03a9 s32i.n a10, a3, 0 -40003100: f01d retw.n -40003102: 0422b2 l32i a11, a2, 16 -40003105: fff991 l32r a9, 400030ec <_c_0x00001800> -40003108: f039b7 bltu a9, a11, 400030fc <_X_FlashDwnLdStartMsgProc+0xc> -4000310b: 0264b2 s32i a11, a4, 8 -4000310e: 0a0c movi.n a10, 0 -40003110: 32c8 l32i.n a12, a2, 12 -40003112: 22d8 l32i.n a13, a2, 8 -40003114: 04d9 s32i.n a13, a4, 0 -40003116: 14c9 s32i.n a12, a4, 4 -40003118: 34a9 s32i.n a10, a4, 12 -4000311a: 44a9 s32i.n a10, a4, 16 -4000311c: 52b8 l32i.n a11, a2, 20 -4000311e: 54b9 s32i.n a11, a4, 20 -40003120: 0132a5 call8 4000444c -40003123: 54a8 l32i.n a10, a4, 20 -40003125: 04b8 l32i.n a11, a4, 0 -40003127: 0178e5 call8 400048b4 <_X_SPIEraseArea> -4000312a: 6a8c beqz.n a10, 40003134 <_X_FlashDwnLdStartMsgProc+0x44> -4000312c: 120c movi.n a2, 1 -4000312e: 6e0c movi.n a14, 6 -40003130: 03e9 s32i.n a14, a3, 0 -40003132: f01d retw.n -40003134: 020c movi.n a2, 0 -40003136: f01d retw.n -40003138: 001810 movsp a1, a8 - ... - -4000313c <_XX_unk313c>: -4000313c: 004136 entry a1, 32 -4000313f: 011292 l16ui a9, a2, 2 -40003142: fffd81 l32r a8, 40003138 <_X_FlashDwnLdStartMsgProc+0x48> -40003145: 5a0c movi.n a10, 5 -40003147: 07b897 bgeu a8, a9, 40003152 <_XX_unk313c+0x16> -4000314a: 120c movi.n a2, 1 -4000314c: 03a9 s32i.n a10, a3, 0 -4000314e: f01d retw.n -40003150: c80000 excw -40003153: 32b834 excw -40003156: 24d8 l32i.n a13, a4, 8 -40003158: ee9bc7 bne a11, a12, 4000314a <_XX_unk313c+0xe> -4000315b: 22b8 l32i.n a11, a2, 8 -4000315d: 3e0c movi.n a14, 3 -4000315f: e73db7 bltu a13, a11, 4000314a <_XX_unk313c+0xe> -40003162: e48eb7 bany a14, a11, 4000314a <_XX_unk313c+0xe> -40003165: 040252 l8ui a5, a2, 4 -40003168: 18c262 addi a6, a2, 24 -4000316b: fc1b addi.n a15, a12, 1 -4000316d: 34f9 s32i.n a15, a4, 12 -4000316f: 06ad mov.n a10, a6 -40003171: ffc825 call8 40002df4 -40003174: 0915a7 beq a5, a10, 40003181 <_XX_unk313c+0x45> -40003177: 720c movi.n a2, 7 -40003179: 0329 s32i.n a2, a3, 0 -4000317b: 120c movi.n a2, 1 -4000317d: f01d retw.n -4000317f: a20000 muluh a0, a0, a0 -40003182: 600524 excw -40003185: c820b6 bltui a0, 2, 40003151 <_XX_unk313c+0x15> -40003188: 5ae522 s32c1i a2, a5, 0x168 -4000318b: 54a801 l32r a0, 3ffd842c <_start-0x27bd4> -4000318e: 06bd mov.n a11, a6 -40003190: 22c8 l32i.n a12, a2, 8 -40003192: 017065 call8 40004898 -40003195: 06ad mov.n a10, a6 -40003197: 22b8 l32i.n a11, a2, 8 -40003199: ffc5a5 call8 40002df4 -4000319c: 0815a7 beq a5, a10, 400031a8 <_XX_unk313c+0x6c> -4000319f: 120c movi.n a2, 1 -400031a1: 880c movi.n a8, 8 -400031a3: 0389 s32i.n a8, a3, 0 -400031a5: f01d retw.n -400031a7: 44b800 extui a11, a0, 8, 5 -400031aa: 5498 l32i.n a9, a4, 20 -400031ac: 22a8 l32i.n a10, a2, 8 -400031ae: 020c movi.n a2, 0 -400031b0: 99aa add.n a9, a9, a10 -400031b2: bbaa add.n a11, a11, a10 -400031b4: 44b9 s32i.n a11, a4, 16 -400031b6: 5499 s32i.n a9, a4, 20 -400031b8: f01d retw.n - ... - -400031bc <_XX_unk31bc>: -400031bc: 004136 entry a1, 32 -400031bf: 3498 l32i.n a9, a4, 12 -400031c1: 2a0c movi.n a10, 2 -400031c3: 1488 l32i.n a8, a4, 4 -400031c5: 64a9 s32i.n a10, a4, 24 -400031c7: 6a0c movi.n a10, 6 -400031c9: 079897 bne a8, a9, 400031d4 <_XX_unk31bc+0x18> -400031cc: 44c8 l32i.n a12, a4, 16 -400031ce: 0024b2 l32i a11, a4, 0 -400031d1: 071bc7 beq a11, a12, 400031dc <_XX_unk31bc+0x20> -400031d4: 120c movi.n a2, 1 -400031d6: 03a9 s32i.n a10, a3, 0 -400031d8: f01d retw.n -400031da: 650000 extui a0, a0, 16, 7 -400031dd: 0c0131 l32r a3, 3ffc61e4 <_start-0x39e1c> -400031e0: f01d02 l16ui a0, a13, 0x1e0 - ... - -400031e4 <_XX_unk31e4>: -400031e4: 004136 entry a1, 32 -400031e7: 011282 l16ui a8, a2, 2 -400031ea: 891c movi.n a9, 24 -400031ec: 081897 beq a8, a9, 400031f8 <_XX_unk31e4+0x14> -400031ef: 120c movi.n a2, 1 -400031f1: 5a0c movi.n a10, 5 -400031f3: 03a9 s32i.n a10, a3, 0 -400031f5: f01d retw.n -400031f7: 22a200 orb b10, b2, b0 -400031fa: 22b202 l32ai a0, a2, 136 -400031fd: 22c203 excw -40003200: 52d804 excw -40003203: 62e8 l32i.n a14, a2, 24 -40003205: 72f8 l32i.n a15, a2, 28 -40003207: 0173a5 call8 40004940 <_XX_unk4940> -4000320a: 020c movi.n a2, 0 -4000320c: f01d retw.n - ... - -40003210 <_XX_unk3210>: -40003210: 004136 entry a1, 32 -40003213: 011252 l16ui a5, a2, 2 -40003216: 05a072 movi a7, 5 -40003219: 05b526 beqi a5, 16, 40003222 <_XX_unk3210+0x12> -4000321c: 120c movi.n a2, 1 -4000321e: 0379 s32i.n a7, a3, 0 -40003220: f01d retw.n -40003222: 4288 l32i.n a8, a2, 16 -40003224: ffb261 l32r a6, 400030ec <_c_0x00001800> -40003227: 5298 l32i.n a9, a2, 20 -40003229: ef3687 bltu a6, a8, 4000321c <_XX_unk3210+0xc> -4000322c: 2489 s32i.n a8, a4, 8 -4000322e: 5499 s32i.n a9, a4, 20 -40003230: 32a8 l32i.n a10, a2, 12 -40003232: 22b8 l32i.n a11, a2, 8 -40003234: 04b9 s32i.n a11, a4, 0 -40003236: 14a9 s32i.n a10, a4, 4 -40003238: 020c movi.n a2, 0 -4000323a: 3429 s32i.n a2, a4, 12 -4000323c: 4429 s32i.n a2, a4, 16 -4000323e: f01d retw.n - -40003240 <_XX_unk3240>: -40003240: 004136 entry a1, 32 -40003243: 011292 l16ui a9, a2, 2 -40003246: ffbc81 l32r a8, 40003138 <_X_FlashDwnLdStartMsgProc+0x48> -40003249: 5a0c movi.n a10, 5 -4000324b: 05b897 bgeu a8, a9, 40003254 <_XX_unk3240+0x14> -4000324e: 120c movi.n a2, 1 -40003250: 03a9 s32i.n a10, a3, 0 -40003252: f01d retw.n -40003254: 34c8 l32i.n a12, a4, 12 -40003256: 32b8 l32i.n a11, a2, 12 -40003258: 24d8 l32i.n a13, a4, 8 -4000325a: f09bc7 bne a11, a12, 4000324e <_XX_unk3240+0xe> -4000325d: 22b8 l32i.n a11, a2, 8 -4000325f: eb3db7 bltu a13, a11, 4000324e <_XX_unk3240+0xe> -40003262: 040252 l8ui a5, a2, 4 -40003265: 18c262 addi a6, a2, 24 -40003268: ec1b addi.n a14, a12, 1 -4000326a: 34e9 s32i.n a14, a4, 12 -4000326c: 06ad mov.n a10, a6 -4000326e: ffb865 call8 40002df4 -40003271: 0715a7 beq a5, a10, 4000327c <_XX_unk3240+0x3c> -40003274: 120c movi.n a2, 1 -40003276: 7f0c movi.n a15, 7 -40003278: 03f9 s32i.n a15, a3, 0 -4000327a: f01d retw.n -4000327c: 20b660 or a11, a6, a6 -4000327f: 0524a2 l32i a10, a4, 20 -40003282: 0222c2 l32i a12, a2, 8 -40003285: feb3e5 call8 40001dc4 <_X_ets_memcpy> -40003288: 4498 l32i.n a9, a4, 16 -4000328a: 2288 l32i.n a8, a2, 8 -4000328c: 5428 l32i.n a2, a4, 20 -4000328e: 998a add.n a9, a9, a8 -40003290: 228a add.n a2, a2, a8 -40003292: 5429 s32i.n a2, a4, 20 -40003294: 4499 s32i.n a9, a4, 16 -40003296: 020c movi.n a2, 0 -40003298: f01d retw.n - ... - -4000329c <_X_MemDwnLdStopReqMsgProc>: -4000329c: 004136 entry a1, 32 -4000329f: 011272 l16ui a7, a2, 2 -400032a2: 860c movi.n a6, 8 -400032a4: 08b677 bgeu a6, a7, 400032b0 <_X_MemDwnLdStopReqMsgProc+0x14> -400032a7: 120c movi.n a2, 1 -400032a9: 580c movi.n a8, 5 -400032ab: 0389 s32i.n a8, a3, 0 -400032ad: f01d retw.n -400032af: 2b0c00 depbits a0, a12, 18, 1 -400032b2: 0a0c movi.n a10, 0 -400032b4: 3298 l32i.n a9, a2, 12 -400032b6: 0802c2 l8ui a12, a2, 8 -400032b9: 020c movi.n a2, 0 -400032bb: 83abc0 moveqz a10, a11, a12 -400032be: 64a9 s32i.n a10, a4, 24 -400032c0: 0599 s32i.n a9, a5, 0 -400032c2: f01d retw.n - -400032c4 <_X_UartConnectProc>: -400032c4: 004136 entry a1, 32 -400032c7: 2228 l32i.n a2, a2, 8 -400032c9: 0429 s32i.n a2, a4, 0 -400032cb: 020c movi.n a2, 0 -400032cd: 0329 s32i.n a2, a3, 0 -400032cf: f01d retw.n -400032d1: 000000 ill - -400032d4 <_X_UartRegWriteProc>: -400032d4: 004136 entry a1, 32 -400032d7: 011242 l16ui a4, a2, 2 -400032da: 228b addi.n a2, a2, 8 -400032dc: 414440 srli a4, a4, 4 -400032df: e4ac beqz.n a4, 40003311 <_X_UartRegWriteProc+0x3d> -400032e1: ffaf52 movi a5, -1 -400032e4: 0612a2 l16ui a10, a2, 12 -400032e7: fface5 call8 40002db4 -400032ea: 002292 l32i a9, a2, 0 -400032ed: 0020c0 memw -400032f0: 012282 l32i a8, a2, 4 -400032f3: 0222b2 l32i a11, a2, 8 -400032f6: 09a8 l32i.n a10, a9, 0 -400032f8: 30cb50 xor a12, a11, a5 -400032fb: 10aac0 and a10, a10, a12 -400032fe: 1088b0 and a8, a8, a11 -40003301: 2088a0 or a8, a8, a10 -40003304: 0020c0 memw -40003307: 0989 s32i.n a8, a9, 0 -40003309: 10c222 addi a2, a2, 16 -4000330c: 440b addi.n a4, a4, -1 -4000330e: d214e6 bgei a4, 1, 400032e4 <_X_UartRegWriteProc+0x10> -40003311: 020c movi.n a2, 0 -40003313: 0329 s32i.n a2, a3, 0 -40003315: f01d retw.n - ... - -40003318 <_X_UartRegReadProc>: -40003318: 004136 entry a1, 32 -4000331b: 2228 l32i.n a2, a2, 8 -4000331d: 0020c0 memw -40003320: 0228 l32i.n a2, a2, 0 -40003322: 0429 s32i.n a2, a4, 0 -40003324: 020c movi.n a2, 0 -40003326: 0329 s32i.n a2, a3, 0 -40003328: f01d retw.n - ... - -4000332c <_c_115200>: -4000332c: 01c200 slli a12, a2, 32 - ... - -40003330 <_c_0x3feffe00>: -40003330: effe00 excw -40003333: 3f .byte 0x3f - -40003334 <_c_0xffff83ff>: -40003334: ff .byte 0xff -40003335: ffff83 excw - -40003338 <_c_0x00001400>: -40003338: 001400 movsp a0, a4 - ... - -4000333c <_c_0x40003728_uart_int_handler>: -4000333c: 3728 l32i.n a2, a7, 12 -4000333e: 2c4000 excw - -40003340 <_c_0x3fffdb2c_uart_int_handler_arg>: -40003340: db2c movi.n a11, 45 -40003342: ff .byte 0xff -40003343: 3f .byte 0x3f - -40003344 <_X_uart_attach>: -40003344: 004136 entry a1, 32 -40003347: 03a0a2 movi a10, 3 -4000334a: 01a082 movi a8, 1 -4000334d: fff7b1 l32r a11, 4000332c <_c_115200> -40003350: febf91 l32r a9, 40002e4c <_st_0x3fffdb10_uartdev> -40003353: 0d0c movi.n a13, 0 -40003355: 59d9 s32i.n a13, a9, 20 -40003357: 29d9 s32i.n a13, a9, 8 -40003359: 39d9 s32i.n a13, a9, 12 -4000335b: 49d9 s32i.n a13, a9, 16 -4000335d: b9d9 s32i.n a13, a9, 44 -4000335f: c9d9 s32i.n a13, a9, 48 -40003361: d9d9 s32i.n a13, a9, 52 -40003363: 1849d2 s8i a13, a9, 24 -40003366: 1949d2 s8i a13, a9, 25 -40003369: 09b9 s32i.n a11, a9, 0 -4000336b: 284982 s8i a8, a9, 40 -4000336e: 19a9 s32i.n a10, a9, 4 -40003370: 40c9c2 addi a12, a9, 64 -40003373: 79c9 s32i.n a12, a9, 28 -40003375: 89c9 s32i.n a12, a9, 32 -40003377: 99c9 s32i.n a12, a9, 36 -40003379: 20a0a2 movi a10, 32 -4000337c: fdd7a5 call8 400010f8 <_X_ets_isr_mask> -4000337f: 1facc2 movi a12, 0xfffffc1f -40003382: a0a0b2 movi a11, 160 -40003385: ffeaf1 l32r a15, 40003330 <_c_0x3feffe00> -40003388: 0020c0 memw -4000338b: 9e2fa2 l32i a10, a15, 0x278 -4000338e: 10aac0 and a10, a10, a12 -40003391: 20aab0 or a10, a10, a11 -40003394: 0020c0 memw -40003397: 9e6fa2 s32i a10, a15, 0x278 -4000339a: ffe691 l32r a9, 40003334 <_c_0xffff83ff> -4000339d: ffe681 l32r a8, 40003338 <_c_0x00001400> -400033a0: 0020c0 memw -400033a3: 9e2fe2 l32i a14, a15, 0x278 -400033a6: 10ee90 and a14, a14, a9 -400033a9: 20ee80 or a14, a14, a8 -400033ac: 0020c0 memw -400033af: 9e6fe2 s32i a14, a15, 0x278 -400033b2: 5a0c movi.n a10, 5 -400033b4: ffe2b1 l32r a11, 4000333c <_c_0x40003728_uart_int_handler> -400033b7: ffe2c1 l32r a12, 40003340 <_c_0x3fffdb2c_uart_int_handler_arg> -400033ba: fdd2e5 call8 400010e8 <_X_ets_isr_attach> -400033bd: f01d retw.n - ... - -400033c0 <_XX_uart_set_unk33c0>: -400033c0: 004136 entry a1, 32 -400033c3: fea231 l32r a3, 40002e4c <_st_0x3fffdb10_uartdev> -400033c6: 194322 s8i a2, a3, 25 -400033c9: f01d retw.n - ... - -400033cc <_c_0x5ffffe00>: -400033cc: fffe00 excw -400033cf: 5f .byte 0x5f - -400033d0 <_c_0x0000ffff>: -400033d0: ff .byte 0xff -400033d1: ff .byte 0xff -400033d2: 360000 excw -400033d5: 510061 l32r a6, 3ffd77d8 <_start-0x28828> -400033d8: fe .byte 0xfe -400033d9: ff .byte 0xff -400033da: 190c movi.n a9, 1 -400033dc: 080c movi.n a8, 0 -400033de: fffb41 l32r a4, 400033cc <_c_0x5ffffe00> -400033e1: 838920 moveqz a8, a9, a2 -400033e4: 118800 slli a8, a8, 16 -400033e7: 884a add.n a8, a8, a4 -400033e9: 0020c0 memw -400033ec: 846852 s32i a5, a8, 0x210 -400033ef: e97c movi.n a9, -2 -400033f1: 0020c0 memw -400033f4: 832832 l32i a3, a8, 0x20c -400033f7: 103390 and a3, a3, a9 -400033fa: 0020c0 memw -400033fd: 836832 s32i a3, a8, 0x20c -40003400: ffd031 l32r a3, 40003340 <_c_0x3fffdb2c_uart_int_handler_arg> -40003403: 01bd mov.n a11, a1 -40003405: 03ad mov.n a10, a3 -40003407: 0059a5 call8 400039a0 <_X_uart_rx_readbuff> -4000340a: 7aac beqz.n a10, 40003435 <_c_0x0000ffff+0x65> -4000340c: fe90a1 l32r a10, 40002e4c <_st_0x3fffdb10_uartdev> -4000340f: 0d0c movi.n a13, 0 -40003411: bad9 s32i.n a13, a10, 44 -40003413: 11c200 slli a12, a2, 16 -40003416: cc4a add.n a12, a12, a4 -40003418: 0020c0 memw -4000341b: 846c52 s32i a5, a12, 0x210 -4000341e: 1d0c movi.n a13, 1 -40003420: 0020c0 memw -40003423: 832cb2 l32i a11, a12, 0x20c -40003426: 20bbd0 or a11, a11, a13 -40003429: 0020c0 memw -4000342c: 836cb2 s32i a11, a12, 0x20c -4000342f: 184a22 s8i a2, a10, 24 -40003432: f01d retw.n -40003434: 03ad00 excw -40003437: 01bd mov.n a11, a1 -40003439: 005665 call8 400039a0 <_X_uart_rx_readbuff> -4000343c: ff5a16 beqz a10, 40003435 <_c_0x0000ffff+0x65> -4000343f: fff246 j 4000340c <_c_0x0000ffff+0x3c> -40003442: 010000 slli a0, a0, 32 -40003445: 0008 l32i.n a0, a0, 0 - ... - -40003448 <_c_0x000fffff>: -40003448: ff .byte 0xff -40003449: ff .byte 0xff -4000344a: 0f .byte 0xf -4000344b: 413600 srli a3, a0, 6 -4000344e: 7fc100 excw -40003451: fe .byte 0xfe -40003452: e47c movi.n a4, -2 -40003454: ccb8 l32i.n a11, a12, 48 -40003456: 01a0d2 movi a13, 1 -40003459: 83bdb0 moveqz a11, a13, a11 -4000345c: 0c6cb2 s32i a11, a12, 48 -4000345f: ffdba1 l32r a10, 400033cc <_c_0x5ffffe00> -40003462: 119200 slli a9, a2, 16 -40003465: 29aa add.n a2, a9, a10 -40003467: 0020c0 memw -4000346a: 862282 l32i a8, a2, 0x218 -4000346d: 17e807 bbsi a8, 0, 40003488 <_c_0x000fffff+0x40> -40003470: 0020c0 memw -40003473: 8622f2 l32i a15, a2, 0x218 -40003476: 10ff40 and a15, a15, a4 -40003479: 0020c0 memw -4000347c: 8662f2 s32i a15, a2, 0x218 -4000347f: fff1e1 l32r a14, 40003444 <_c_0x0000ffff+0x74> -40003482: 0020c0 memw -40003485: 8662e2 s32i a14, a2, 0x218 -40003488: 0020c0 memw -4000348b: 8c2282 l32i a8, a2, 0x230 -4000348e: 13e8f6 bgeui a8, 128, 400034a5 <_c_0x000fffff+0x5d> -40003491: 045356 bnez a3, 400034da <_c_0x000fffff+0x92> -40003494: e8a3a2 movi a10, 0x3e8 -40003497: ff91e5 call8 40002db4 -4000349a: 0020c0 memw -4000349d: 8c2292 l32i a9, a2, 0x230 -400034a0: f03d nop.n -400034a2: ebe9b6 bltui a9, 128, 40003491 <_c_0x000fffff+0x49> -400034a5: ffe8a1 l32r a10, 40003448 <_c_0x000fffff> -400034a8: 0020c0 memw -400034ab: 8a2282 l32i a8, a2, 0x228 -400034ae: 1088a0 and a8, a8, a10 -400034b1: 0020c0 memw -400034b4: 8b2292 l32i a9, a2, 0x22c -400034b7: 1099a0 and a9, a9, a10 -400034ba: 0020c0 memw -400034bd: 8622a2 l32i a10, a2, 0x218 -400034c0: 10aa40 and a10, a10, a4 -400034c3: 0020c0 memw -400034c6: 8662a2 s32i a10, a2, 0x218 -400034c9: 289a add.n a2, a8, a9 -400034cb: 080c movi.n a8, 0 -400034cd: 212120 srai a2, a2, 1 -400034d0: 532280 max a2, a2, a8 -400034d3: 223b addi.n a2, a2, 3 -400034d5: f01d retw.n -400034d7: 000000 ill -400034da: 020c movi.n a2, 0 -400034dc: f01d retw.n - ... - -400034e0 <_c_0x00060000>: -400034e0: 060000 excw - ... - -400034e4 <_c_0xfff9ffff>: -400034e4: ff .byte 0xff -400034e5: ff .byte 0xff -400034e6: fff9 s32i.n a15, a15, 60 - -400034e8 : -400034e8: 004136 entry a1, 32 -400034eb: ffb861 l32r a6, 400033cc <_c_0x5ffffe00> -400034ee: 115200 slli a5, a2, 16 -400034f1: 556a add.n a5, a5, a6 -400034f3: 0020c0 memw -400034f6: 856532 s32i a3, a5, 0x214 -400034f9: fff991 l32r a9, 400034e0 <_c_0x00060000> -400034fc: 0020c0 memw -400034ff: 882582 l32i a8, a5, 0x220 -40003502: 208890 or a8, a8, a9 -40003505: 0020c0 memw -40003508: 886582 s32i a8, a5, 0x220 -4000350b: fff661 l32r a6, 400034e4 <_c_0xfff9ffff> -4000350e: 0020c0 memw -40003511: 882542 l32i a4, a5, 0x220 -40003514: 104460 and a4, a4, a6 -40003517: 0020c0 memw -4000351a: 886542 s32i a4, a5, 0x220 -4000351d: f01d retw.n - ... - -40003520 <_c_0xfffc0fff>: -40003520: ff .byte 0xff -40003521: 0f .byte 0xf -40003522: fffc bnez.n a15, 40003565 <_X_uart_init+0x31> - -40003524 <_c_0x00009000>: -40003524: 009000 all4 b0, b0:b1:b2:b3 - ... - -40003528 <_c_0x00020000>: -40003528: 020000 andb b0, b0, b0 - ... - -4000352c <_c_13000000>: -4000352c: c65d40 excw - ... - -40003530 <_c_0x08000000>: -40003530: 000000 ill -40003533: 3608 l32i.n a0, a6, 12 - -40003534 <_X_uart_init>: -40003534: 004136 entry a1, 32 -40003537: 0b9216 beqz a2, 400035f4 <_l_35f4> -4000353a: fff9d1 l32r a13, 40003520 <_c_0xfffc0fff> -4000353d: fff9c1 l32r a12, 40003524 <_c_0x00009000> -40003540: f6fc91 l32r a9, 40001130 <_c_0x60003e00> -40003543: 0020c0 memw -40003546: cf29b2 l32i a11, a9, 0x33c -40003549: f03d nop.n -4000354b: 10bbd0 and a11, a11, a13 -4000354e: 20bbc0 or a11, a11, a12 -40003551: 0020c0 memw -40003554: cf69b2 s32i a11, a9, 0x33c -40003557: fff4a1 l32r a10, 40003528 <_c_0x00020000> -4000355a: 0020c0 memw -4000355d: f02982 l32i a8, a9, 0x3c0 -40003560: 2088a0 or a8, a8, a10 -40003563: 0020c0 memw -40003566: f06982 s32i a8, a9, 0x3c0 -40003569: fe3831 l32r a3, 40002e4c <_st_0x3fffdb10_uartdev> -4000356c: fff0b1 l32r a11, 4000352c <_c_13000000> -4000356f: 03c8 l32i.n a12, a3, 0 -40003571: 02ad mov.n a10, a2 -40003573: c2bbc0 quou a11, a11, a12 -40003576: f4b0b0 extui a11, a11, 0, 16 -40003579: fff6e5 call8 400034e8 -4000357c: 3398 l32i.n a9, a3, 12 -4000357e: ff93f1 l32r a15, 400033cc <_c_0x5ffffe00> -40003581: 11e200 slli a14, a2, 16 -40003584: 4388 l32i.n a8, a3, 16 -40003586: eefa add.n a14, a14, a15 -40003588: 23f8 l32i.n a15, a3, 8 -4000358a: 1188c0 slli a8, a8, 4 -4000358d: 20ff90 or a15, a15, a9 -40003590: 1398 l32i.n a9, a3, 4 -40003592: 20ff80 or a15, a15, a8 -40003595: 1199e0 slli a9, a9, 2 -40003598: ffe681 l32r a8, 40003530 <_c_0x08000000> -4000359b: 20ff90 or a15, a15, a9 -4000359e: 20ff80 or a15, a15, a8 -400035a1: 0020c0 memw -400035a4: 886ef2 s32i a15, a14, 0x220 -400035a7: ffced1 l32r a13, 400034e0 <_c_0x00060000> -400035aa: 0020c0 memw -400035ad: 882ec2 l32i a12, a14, 0x220 -400035b0: 20ccd0 or a12, a12, a13 -400035b3: 0020c0 memw -400035b6: 886ec2 s32i a12, a14, 0x220 -400035b9: ffcab1 l32r a11, 400034e4 <_c_0xfff9ffff> -400035bc: 0020c0 memw -400035bf: 882ea2 l32i a10, a14, 0x220 -400035c2: 10aab0 and a10, a10, a11 -400035c5: 0020c0 memw -400035c8: 886ea2 s32i a10, a14, 0x220 -400035cb: 280392 l8ui a9, a3, 40 -400035ce: 649090 extui a9, a9, 0, 7 -400035d1: 0020c0 memw -400035d4: 896e92 s32i a9, a14, 0x224 -400035d7: ff7e81 l32r a8, 400033d0 <_c_0x0000ffff> -400035da: 0020c0 memw -400035dd: 846e82 s32i a8, a14, 0x210 -400035e0: 1f0c movi.n a15, 1 -400035e2: 0020c0 memw -400035e5: 832ed2 l32i a13, a14, 0x20c -400035e8: 20ddf0 or a13, a13, a15 -400035eb: 0020c0 memw -400035ee: 836ed2 s32i a13, a14, 0x20c -400035f1: f01d retw.n - ... - -400035f4 <_l_35f4>: -400035f4: 7d7c movi.n a13, -9 -400035f6: f7bf91 l32r a9, 400014f4 <_c_0x60008e00> -400035f9: 0020c0 memw -400035fc: a229c2 l32i a12, a9, 0x288 -400035ff: 10ccd0 and a12, a12, a13 -40003602: 0020c0 memw -40003605: a269c2 s32i a12, a9, 0x288 -40003608: f7bab1 l32r a11, 400014f0 <_c_0xffff8fff> -4000360b: 0020c0 memw -4000360e: a229a2 l32i a10, a9, 0x288 -40003611: 10aab0 and a10, a10, a11 -40003614: 0020c0 memw -40003617: a269a2 s32i a10, a9, 0x288 -4000361a: 0020c0 memw -4000361d: a22982 l32i a8, a9, 0x288 -40003620: 0020c0 memw -40003623: a26982 s32i a8, a9, 0x288 -40003626: ffcfc6 j 40003569 <_X_uart_init+0x35> -40003629: 000000 ill - -4000362c : -4000362c: 004136 entry a1, 32 -4000362f: fe0741 l32r a4, 40002e4c <_st_0x3fffdb10_uartdev> -40003632: 0020f0 nop -40003635: 190442 l8ui a4, a4, 25 -40003638: ff6581 l32r a8, 400033cc <_c_0x5ffffe00> -4000363b: 114400 slli a4, a4, 16 -4000363e: 448a add.n a4, a4, a8 -40003640: 0020c0 memw -40003643: 872432 l32i a3, a4, 0x21c -40003646: 0e7377 bbci a3, 23, 40003658 -40003649: 088076 loop a0, 40003655 -4000364c: 0020c0 memw -4000364f: 872482 l32i a8, a4, 0x21c -40003652: 027877 bbci a8, 23, 40003658 -40003655: fffc06 j 40003649 -40003658: 0020c0 memw -4000365b: 806422 s32i a2, a4, 0x200 -4000365e: 020c movi.n a2, 0 -40003660: f01d retw.n - ... - -40003664 <_X_uart_tx_one_char2>: -40003664: 004136 entry a1, 32 -40003667: fdf941 l32r a4, 40002e4c <_st_0x3fffdb10_uartdev> -4000366a: 0020f0 nop -4000366d: 180442 l8ui a4, a4, 24 -40003670: ff5781 l32r a8, 400033cc <_c_0x5ffffe00> -40003673: 114400 slli a4, a4, 16 -40003676: 448a add.n a4, a4, a8 -40003678: 0020c0 memw -4000367b: 872432 l32i a3, a4, 0x21c -4000367e: 0e7377 bbci a3, 23, 40003690 <_X_uart_tx_one_char2+0x2c> -40003681: 088076 loop a0, 4000368d <_X_uart_tx_one_char2+0x29> -40003684: 0020c0 memw -40003687: 872482 l32i a8, a4, 0x21c -4000368a: 027877 bbci a8, 23, 40003690 <_X_uart_tx_one_char2+0x2c> -4000368d: fffc06 j 40003681 <_X_uart_tx_one_char2+0x1d> -40003690: 0020c0 memw -40003693: 806422 s32i a2, a4, 0x200 -40003696: 020c movi.n a2, 0 -40003698: f01d retw.n - ... - -4000369c <_X_uart_wait_tx_empty>: -4000369c: 004136 entry a1, 32 -4000369f: 115200 slli a5, a2, 16 -400036a2: ff4a81 l32r a8, 400033cc <_c_0x5ffffe00> -400036a5: f43c21 l32r a2, 40000798 <_c_0x00ff0000> -400036a8: 805580 add a5, a5, a8 -400036ab: 0020c0 memw -400036ae: 872532 l32i a3, a5, 0x21c -400036b1: 100237 bnone a2, a3, 400036c5 <_X_uart_wait_tx_empty+0x29> -400036b4: f03d nop.n -400036b6: 088076 loop a0, 400036c2 <_X_uart_wait_tx_empty+0x26> -400036b9: 0020c0 memw -400036bc: 872592 l32i a9, a5, 0x21c -400036bf: 020927 bnone a9, a2, 400036c5 <_X_uart_wait_tx_empty+0x29> -400036c2: fffb86 j 400036b4 <_X_uart_wait_tx_empty+0x18> -400036c5: f01d retw.n -400036c7: 413600 srli a3, a0, 6 -400036ca: e02100 subx4 a2, a1, a0 -400036cd: 52fd excw -400036cf: 22ffa0 orb b15, b15, b10 -400036d2: 311802 l16ui a0, a8, 98 -400036d5: 3e .byte 0x3e -400036d6: ff .byte 0xff -400036d7: 112200 slli a2, a2, 16 -400036da: 802230 add a2, a2, a3 -400036dd: 088076 loop a0, 400036e9 <_X_uart_wait_tx_empty+0x4d> -400036e0: 0020c0 memw -400036e3: 872232 l32i a3, a2, 0x21c -400036e6: 028537 bany a5, a3, 400036ec <_X_uart_wait_tx_empty+0x50> -400036e9: fffc06 j 400036dd <_X_uart_wait_tx_empty+0x41> -400036ec: 0020c0 memw -400036ef: 802222 l32i a2, a2, 0x200 -400036f2: 742020 extui a2, a2, 0, 8 -400036f5: f01d retw.n -400036f7: 413600 srli a3, a0, 6 -400036fa: d44100 extui a4, a0, 1, 14 -400036fd: 42fd excw -400036ff: 811804 excw -40003702: 00ff32 s32ri a3, a15, 0 -40003705: 8a1144 excw -40003708: 20c044 excw -4000370b: 243200 extui a3, a0, 2, 3 -4000370e: 303087 bltu a0, a8, 40003742 <_X_uart_int_handler+0x1a> -40003711: b38c74 excw -40003714: 0020c0 memw -40003717: 802482 l32i a8, a4, 0x200 -4000371a: 004282 s8i a8, a2, 0 -4000371d: 020c movi.n a2, 0 -4000371f: f01d retw.n -40003721: 120c movi.n a2, 1 -40003723: f01d retw.n -40003725: 000000 ill - -40003728 <_X_uart_int_handler>: -40003728: 004136 entry a1, 32 -4000372b: fdc861 l32r a6, 40002e4c <_st_0x3fffdb10_uartdev> -4000372e: 180c movi.n a8, 1 -40003730: 180662 l8ui a6, a6, 24 -40003733: ff2671 l32r a7, 400033cc <_c_0x5ffffe00> -40003736: 115600 slli a5, a6, 16 -40003739: 557a add.n a5, a5, a7 -4000373b: 0020c0 memw -4000373e: 822532 l32i a3, a5, 0x208 -40003741: 546307 bbci a3, 0, 40003799 <_X_uart_int_handler+0x71> -40003744: ffa092 movi a9, 255 -40003747: 0020c0 memw -4000374a: 846582 s32i a8, a5, 0x210 -4000374d: 116600 slli a6, a6, 16 -40003750: 667a add.n a6, a6, a7 -40003752: 0020c0 memw -40003755: 8726a2 l32i a10, a6, 0x21c -40003758: 3d09a7 bnone a9, a10, 40003799 <_X_uart_int_handler+0x71> -4000375b: 1258 l32i.n a5, a2, 4 -4000375d: 290c movi.n a9, 2 -4000375f: d80c movi.n a8, 13 -40003761: 2b8076 loop a0, 40003790 <_X_uart_int_handler+0x68> -40003764: 0020c0 memw -40003767: 8026a2 l32i a10, a6, 0x200 -4000376a: 74a0a0 extui a10, a10, 0, 8 -4000376d: 0045a2 s8i a10, a5, 0 -40003770: 0278 l32i.n a7, a2, 0 -40003772: 1258 l32i.n a5, a2, 4 -40003774: 01d7b2 addmi a11, a7, 0x100 -40003777: 551b addi.n a5, a5, 1 -40003779: 171a87 beq a10, a8, 40003794 <_X_uart_int_handler+0x6c> -4000377c: 1259 s32i.n a5, a2, 4 -4000377e: 0395b7 bne a5, a11, 40003785 <_X_uart_int_handler+0x5d> -40003781: 075d mov.n a5, a7 -40003783: 1279 s32i.n a7, a2, 4 -40003785: 0020c0 memw -40003788: 8726a2 l32i a10, a6, 0x21c -4000378b: 74a0a0 extui a10, a10, 0, 8 -4000378e: 7a8c beqz.n a10, 40003799 <_X_uart_int_handler+0x71> -40003790: fff346 j 40003761 <_X_uart_int_handler+0x39> -40003793: 429900 xorb b9, b9, b0 -40003796: fff886 j 4000377c <_X_uart_int_handler+0x54> -40003799: f01d retw.n -4000379b: 413600 srli a3, a0, 6 -4000379e: 23b600 sext a11, a6, 7 -400037a1: 140c26 beqi a12, -1, 400037b9 <_X_uart_int_handler+0x91> -400037a4: d50c movi.n a5, 13 -400037a6: 02ad mov.n a10, a2 -400037a8: fff4e5 call8 400036f8 <_X_uart_wait_tx_empty+0x5c> -400037ab: 071a66 bnei a10, 1, 400037b6 <_X_uart_int_handler+0x8e> -400037ae: 02ad mov.n a10, a2 -400037b0: fff465 call8 400036f8 <_X_uart_wait_tx_empty+0x5c> -400037b3: f71a26 beqi a10, 1, 400037ae <_X_uart_int_handler+0x86> -400037b6: 0002a2 l8ui a10, a2, 0 -400037b9: 0c9a26 beqi a10, 10, 400037c9 <_X_uart_int_handler+0xa1> -400037bc: 091a57 beq a10, a5, 400037c9 <_X_uart_int_handler+0xa1> -400037bf: 221b addi.n a2, a2, 1 -400037c1: 441b addi.n a4, a4, 1 -400037c3: 744040 extui a4, a4, 0, 8 -400037c6: dc9347 bne a3, a4, 400037a6 <_X_uart_int_handler+0x7e> -400037c9: 0d0c movi.n a13, 0 -400037cb: 0142d2 s8i a13, a2, 1 -400037ce: 0d2d mov.n a2, a13 -400037d0: f01d retw.n - ... - -400037d4 <_X_send_packet>: -400037d4: 004136 entry a1, 32 -400037d7: c0a0a2 movi a10, 192 -400037da: ffe8a5 call8 40003664 <_X_uart_tx_one_char2> -400037dd: 330b addi.n a3, a3, -1 -400037df: 3d0326 beqi a3, -1, 40003820 <_X_send_packet+0x4c> -400037e2: dba052 movi a5, 219 -400037e5: c0a042 movi a4, 192 -400037e8: 000306 j 400037f8 <_X_send_packet+0x24> -400037eb: 650000 extui a0, a0, 16, 7 -400037ee: 1bffe7 bbsi a15, 30, 4000380d <_X_send_packet+0x39> -400037f1: c33222 excw -400037f4: ff .byte 0xff -400037f5: 027396 bltz a3, 40003820 <_X_send_packet+0x4c> -400037f8: 0002a2 l8ui a10, a2, 0 -400037fb: 111a47 beq a10, a4, 40003810 <_X_send_packet+0x3c> -400037fe: eb9a57 bne a10, a5, 400037ed <_X_send_packet+0x19> -40003801: dba0a2 movi a10, 219 -40003804: ffe5e5 call8 40003664 <_X_uart_tx_one_char2> -40003807: dda0a2 movi a10, 221 -4000380a: ffe5a5 call8 40003664 <_X_uart_tx_one_char2> -4000380d: fff7c6 j 400037f0 <_X_send_packet+0x1c> -40003810: dba0a2 movi a10, 219 -40003813: ffe525 call8 40003664 <_X_uart_tx_one_char2> -40003816: dca0a2 movi a10, 220 -40003819: ffe4a5 call8 40003664 <_X_uart_tx_one_char2> -4000381c: fff406 j 400037f0 <_X_send_packet+0x1c> -4000381f: a0a200 addx4 a10, a2, a0 -40003822: e425c0 extui a2, a12, 5, 15 -40003825: ff .byte 0xff -40003826: f01d retw.n - -40003828 <_X_SendMsg>: -40003828: 004136 entry a1, 32 -4000382b: 03bd mov.n a11, a3 -4000382d: 02ad mov.n a10, a2 -4000382f: fffa65 call8 400037d4 <_X_send_packet> -40003832: 020c movi.n a2, 0 -40003834: f01d retw.n -40003836: c80000 excw -40003839: ffc8 l32i.n a12, a15, 60 -4000383b: 3f .byte 0x3f - -4000383c <_X_recv_packet>: -4000383c: 006136 entry a1, 48 -4000383f: 180c movi.n a8, 1 -40003841: 047d mov.n a7, a4 -40003843: fd8261 l32r a6, 40002e4c <_st_0x3fffdb10_uartdev> -40003846: 029d mov.n a9, a2 -40003848: 1199 s32i.n a9, a1, 4 -4000384a: 020c movi.n a2, 0 -4000384c: 004122 s8i a2, a1, 0 -4000384f: d658 l32i.n a5, a6, 52 -40003851: 937840 movnez a7, a8, a4 -40003854: 2179 s32i.n a7, a1, 8 -40003856: 932570 movnez a2, a5, a7 -40003859: c0a072 movi a7, 192 -4000385c: 1cc652 addi a5, a6, 28 -4000385f: 0d3416 beqz a4, 40003936 <_X_recv_packet+0xfa> -40003862: c698 l32i.n a9, a6, 48 -40003864: 1e2966 bnei a9, 2, 40003886 <_X_recv_packet+0x4a> -40003867: 000f86 j 400038a9 <_X_recv_packet+0x6d> -4000386a: 648c beqz.n a4, 40003874 <_X_recv_packet+0x38> -4000386c: c6e8 l32i.n a14, a6, 48 -4000386e: 024e66 bnei a14, 4, 40003874 <_X_recv_packet+0x38> -40003871: 002406 j 40003905 <_X_recv_packet+0xc9> -40003874: 0001a2 l8ui a10, a1, 0 -40003877: 1d1a77 beq a10, a7, 40003898 <_X_recv_packet+0x5c> -4000387a: dba0f2 movi a15, 219 -4000387d: 511af7 beq a10, a15, 400038d2 <_X_recv_packet+0x96> -40003880: 02a237 bge a2, a3, 40003886 <_X_recv_packet+0x4a> -40003883: 002746 j 40003924 <_X_recv_packet+0xe8> -40003886: 05ad mov.n a10, a5 -40003888: 01bd mov.n a11, a1 -4000388a: 001165 call8 400039a0 <_X_uart_rx_readbuff> -4000388d: fd9a16 beqz a10, 4000386a <_X_recv_packet+0x2e> -40003890: ff2416 beqz a4, 40003886 <_X_recv_packet+0x4a> -40003893: 020c movi.n a2, 0 -40003895: f01d retw.n -40003897: a21600 muluh a1, a6, a0 -4000389a: fe .byte 0xfe -4000389b: 0e7416 beqz a4, 40003986 <_X_recv_packet+0x14a> -4000389e: 280c movi.n a8, 2 -400038a0: 090c movi.n a9, 0 -400038a2: d699 s32i.n a9, a6, 52 -400038a4: c689 s32i.n a8, a6, 48 -400038a6: f01d retw.n -400038a8: 05ad00 extui a10, a0, 29, 1 -400038ab: 01bd mov.n a11, a1 -400038ad: 000f25 call8 400039a0 <_X_uart_rx_readbuff> -400038b0: 4adc bnez.n a10, 400038c8 <_X_recv_packet+0x8c> -400038b2: 0001a2 l8ui a10, a1, 0 -400038b5: 0020f0 nop -400038b8: c0ba70 sub a11, a10, a7 -400038bb: 09db16 beqz a11, 4000395c <_X_recv_packet+0x120> -400038be: 05ad mov.n a10, a5 -400038c0: 01bd mov.n a11, a1 -400038c2: 000de5 call8 400039a0 <_X_uart_rx_readbuff> -400038c5: fe9a16 beqz a10, 400038b2 <_X_recv_packet+0x76> -400038c8: 0001c2 l8ui a12, a1, 0 -400038cb: c49c77 bne a12, a7, 40003893 <_X_recv_packet+0x57> -400038ce: ffed06 j 40003886 <_X_recv_packet+0x4a> -400038d1: e4bc00 extui a11, a0, 12, 15 -400038d4: c698 l32i.n a9, a6, 48 -400038d6: 0101d2 l8ui a13, a1, 1 -400038d9: 023966 bnei a9, 3, 400038df <_X_recv_packet+0xa3> -400038dc: 002186 j 40003966 <_X_recv_packet+0x12a> -400038df: 0041d2 s8i a13, a1, 0 -400038e2: 24afe2 movi a14, -220 -400038e5: 0001a2 l8ui a10, a1, 0 -400038e8: 21d8 l32i.n a13, a1, 8 -400038ea: 3c0c movi.n a12, 3 -400038ec: 939cd0 movnez a9, a12, a13 -400038ef: c699 s32i.n a9, a6, 48 -400038f1: eaea add.n a14, a10, a14 -400038f3: 0a2ef6 bgeui a14, 2, 40003901 <_X_recv_packet+0xc5> -400038f6: ffd0b1 l32r a11, 40003838 <_X_SendMsg+0x10> -400038f9: aaba add.n a10, a10, a11 -400038fb: 7c0aa2 l8ui a10, a10, 124 -400038fe: 0041a2 s8i a10, a1, 0 -40003901: ffdec6 j 40003880 <_X_recv_packet+0x44> -40003904: a0e200 addx4 a14, a2, a0 -40003907: f2db addi.n a15, a2, 13 -40003909: f20001 l32r a0, 4000010c <_WindowOverflowHandler+0xc> -4000390c: e20141 l32r a4, 3fffc110 <_start-0x3ef0> -4000390f: 560041 l32r a4, 3ffd9110 <_start-0x26ef0> -40003912: adfbf4 excw -40003915: b11005 call0 3ffb4a18 <_start-0x4b5e8> -40003918: 086520 excw -4000391b: 1a2600 depbits a0, a6, 1, 3 -4000391e: 9859 s32i.n a5, a8, 36 -40003920: ef46c6 j 3ffff63f <_start-0x9c1> -40003923: ff .byte 0xff -40003924: 1198 l32i.n a9, a1, 4 -40003926: 929a add.n a9, a2, a9 -40003928: 0049a2 s8i a10, a9, 0 -4000392b: d688 l32i.n a8, a6, 52 -4000392d: 221b addi.n a2, a2, 1 -4000392f: 881b addi.n a8, a8, 1 -40003931: d689 s32i.n a8, a6, 52 -40003933: ffd3c6 j 40003886 <_X_recv_packet+0x4a> -40003936: 05ad mov.n a10, a5 -40003938: 01bd mov.n a11, a1 -4000393a: 000665 call8 400039a0 <_X_uart_rx_readbuff> -4000393d: 081a26 beqi a10, 1, 40003949 <_X_recv_packet+0x10d> -40003940: 0001a2 l8ui a10, a1, 0 -40003943: c0aa70 sub a10, a10, a7 -40003946: f3ca16 beqz a10, 40003886 <_X_recv_packet+0x4a> -40003949: 05ad mov.n a10, a5 -4000394b: 01bd mov.n a11, a1 -4000394d: 000525 call8 400039a0 <_X_uart_rx_readbuff> -40003950: f51a26 beqi a10, 1, 40003949 <_X_recv_packet+0x10d> -40003953: 0001b2 l8ui a11, a1, 0 -40003956: ef9b77 bne a11, a7, 40003949 <_X_recv_packet+0x10d> -40003959: ffca46 j 40003886 <_X_recv_packet+0x4a> -4000395c: 3d0c movi.n a13, 3 -4000395e: c6d9 s32i.n a13, a6, 48 -40003960: f2fb56 bnez a11, 40003893 <_X_recv_packet+0x57> -40003963: ffc7c6 j 40003886 <_X_recv_packet+0x4a> -40003966: 05ad mov.n a10, a5 -40003968: 01bd mov.n a11, a1 -4000396a: 4e0c movi.n a14, 4 -4000396c: c6e9 s32i.n a14, a6, 48 -4000396e: 000325 call8 400039a0 <_X_uart_rx_readbuff> -40003971: aa1a66 bnei a10, 1, 4000391f <_X_recv_packet+0xe3> -40003974: ffc6c6 j 40003893 <_X_recv_packet+0x57> -40003977: ad0000 excw -4000397a: 01bd05 call0 4000554c <_X_slc_init_attach+0x64> -4000397d: 000225 call8 400039a0 <_X_uart_rx_readbuff> -40003980: f51a26 beqi a10, 1, 40003979 <_X_recv_packet+0x13d> -40003983: ffe606 j 4000391f <_X_recv_packet+0xe3> -40003986: f01d retw.n - -40003988 <_X_RcvMsg>: -40003988: 004136 entry a1, 32 -4000398b: 04cd mov.n a12, a4 -4000398d: 03bd mov.n a11, a3 -4000398f: 02ad mov.n a10, a2 -40003991: ffeaa5 call8 4000383c <_X_recv_packet> -40003994: 2a8c beqz.n a10, 4000399a <_X_RcvMsg+0x12> -40003996: 020c movi.n a2, 0 -40003998: f01d retw.n -4000399a: 120c movi.n a2, 1 -4000399c: f01d retw.n - ... - -400039a0 <_X_uart_rx_readbuff>: -400039a0: 004136 entry a1, 32 -400039a3: 2268 l32i.n a6, a2, 8 -400039a5: 1248 l32i.n a4, a2, 4 -400039a7: 059467 bne a4, a6, 400039b0 <_X_uart_rx_readbuff+0x10> -400039aa: 120c movi.n a2, 1 -400039ac: f01d retw.n -400039ae: 820000 mull a0, a0, a0 -400039b1: 820006 j 3ffe41b5 <_start-0x1be4b> -400039b4: 380043 excw -400039b7: 225802 s16i a0, a8, 68 -400039ba: 01d382 addmi a8, a3, 0x100 -400039bd: 551b addi.n a5, a5, 1 -400039bf: 2259 s32i.n a5, a2, 8 -400039c1: 059587 bne a5, a8, 400039ca <_X_uart_rx_readbuff+0x2a> -400039c4: 2239 s32i.n a3, a2, 8 -400039c6: 020c movi.n a2, 0 -400039c8: f01d retw.n -400039ca: 020c movi.n a2, 0 -400039cc: f01d retw.n -400039ce: 360000 excw -400039d1: 410061 l32r a6, 3ffd3dd4 <_start-0x2c22c> -400039d4: 1e .byte 0x1e -400039d5: 88fd excw -400039d7: 2826b4 excw -400039da: 120c04 excw -400039dd: f01d retw.n -400039df: 01bd00 slli a11, a13, 32 -400039e2: 1cc432 addi a3, a4, 28 -400039e5: 03ad mov.n a10, a3 -400039e7: fffba5 call8 400039a0 <_X_uart_rx_readbuff> -400039ea: ba8c beqz.n a10, 400039f9 <_X_uart_rx_readbuff+0x59> -400039ec: 090c movi.n a9, 0 -400039ee: 004292 s8i a9, a2, 0 -400039f1: 092d mov.n a2, a9 -400039f3: b429 s32i.n a2, a4, 44 -400039f5: f01d retw.n -400039f7: ad0000 excw -400039fa: 019203 excw -400039fd: 01bd00 slli a11, a13, 32 -40003a00: 004292 s8i a9, a2, 0 -40003a03: 221b addi.n a2, a2, 1 -40003a05: fff9a5 call8 400039a0 <_X_uart_rx_readbuff> -40003a08: feda16 beqz a10, 400039f9 <_X_uart_rx_readbuff+0x59> -40003a0b: fff746 j 400039ec <_X_uart_rx_readbuff+0x4c> -40003a0e: 360000 excw -40003a11: 210041 l32r a4, 3ffcbe14 <_start-0x341ec> -40003a14: 0e .byte 0xe -40003a15: 1dfd excw -40003a17: 4e00f0 excw - -40003a18 <_c_0x60004e00>: -40003a18: 004e00 break 14, 0 -40003a1b: 413660 srli a3, a6, 6 - -40003a1c <_X_SelectSpiFunction>: -40003a1c: 004136 entry a1, 32 -40003a1f: 787c movi.n a8, -9 -40003a21: f6b371 l32r a7, 400014f0 <_c_0xffff8fff> -40003a24: f5c641 l32r a4, 4000113c <_c_0x1000> -40003a27: fffc51 l32r a5, 40003a18 <_c_0x60004e00> -40003a2a: f6b261 l32r a6, 400014f4 <_c_0x60008e00> -40003a2d: 123216 beqz a2, 40003b54 <_X_SelectSpiFunction+0x138> -40003a30: 0020c0 memw -40003a33: 8c2632 l32i a3, a6, 0x230 -40003a36: 103380 and a3, a3, a8 -40003a39: 0020c0 memw -40003a3c: 8c6632 s32i a3, a6, 0x230 -40003a3f: 0020c0 memw -40003a42: 8c2622 l32i a2, a6, 0x230 -40003a45: 102270 and a2, a2, a7 -40003a48: 0020c0 memw -40003a4b: 8c6622 s32i a2, a6, 0x230 -40003a4e: 0020c0 memw -40003a51: 8c26f2 l32i a15, a6, 0x230 -40003a54: 20ff40 or a15, a15, a4 -40003a57: 0020c0 memw -40003a5a: 8c66f2 s32i a15, a6, 0x230 -40003a5d: 0020c0 memw -40003a60: 8d26e2 l32i a14, a6, 0x234 -40003a63: 10ee80 and a14, a14, a8 -40003a66: 0020c0 memw -40003a69: 8d66e2 s32i a14, a6, 0x234 -40003a6c: 0020c0 memw -40003a6f: 8d26d2 l32i a13, a6, 0x234 -40003a72: 10dd70 and a13, a13, a7 -40003a75: 0020c0 memw -40003a78: 8d66d2 s32i a13, a6, 0x234 -40003a7b: 0020c0 memw -40003a7e: 8d26c2 l32i a12, a6, 0x234 -40003a81: 20cc40 or a12, a12, a4 -40003a84: 0020c0 memw -40003a87: 8d66c2 s32i a12, a6, 0x234 -40003a8a: 0020c0 memw -40003a8d: 8e26b2 l32i a11, a6, 0x238 -40003a90: 10bb80 and a11, a11, a8 -40003a93: 0020c0 memw -40003a96: 8e66b2 s32i a11, a6, 0x238 -40003a99: 0020c0 memw -40003a9c: 8e26a2 l32i a10, a6, 0x238 -40003a9f: 10aa70 and a10, a10, a7 -40003aa2: 0020c0 memw -40003aa5: 8e66a2 s32i a10, a6, 0x238 -40003aa8: 0020c0 memw -40003aab: 8e2692 l32i a9, a6, 0x238 -40003aae: 209940 or a9, a9, a4 -40003ab1: 0020c0 memw -40003ab4: 8e6692 s32i a9, a6, 0x238 -40003ab7: 0020c0 memw -40003aba: 8f2632 l32i a3, a6, 0x23c -40003abd: 103380 and a3, a3, a8 -40003ac0: 0020c0 memw -40003ac3: 8f6632 s32i a3, a6, 0x23c -40003ac6: 0020c0 memw -40003ac9: 8f2622 l32i a2, a6, 0x23c -40003acc: 102270 and a2, a2, a7 -40003acf: 0020c0 memw -40003ad2: 8f6622 s32i a2, a6, 0x23c -40003ad5: 0020c0 memw -40003ad8: 8f26f2 l32i a15, a6, 0x23c -40003adb: 20ff40 or a15, a15, a4 -40003ade: 0020c0 memw -40003ae1: 8f66f2 s32i a15, a6, 0x23c -40003ae4: 0020c0 memw -40003ae7: 9026e2 l32i a14, a6, 0x240 -40003aea: 10ee80 and a14, a14, a8 -40003aed: 0020c0 memw -40003af0: 9066e2 s32i a14, a6, 0x240 -40003af3: 0020c0 memw -40003af6: 9026d2 l32i a13, a6, 0x240 -40003af9: 10dd70 and a13, a13, a7 -40003afc: 0020c0 memw -40003aff: 9066d2 s32i a13, a6, 0x240 -40003b02: 0020c0 memw -40003b05: 9026c2 l32i a12, a6, 0x240 -40003b08: 20cc40 or a12, a12, a4 -40003b0b: 0020c0 memw -40003b0e: 9066c2 s32i a12, a6, 0x240 -40003b11: 0020c0 memw -40003b14: 9226b2 l32i a11, a6, 0x248 -40003b17: 10bb80 and a11, a11, a8 -40003b1a: 0020c0 memw -40003b1d: 9266b2 s32i a11, a6, 0x248 -40003b20: 0020c0 memw -40003b23: 9226a2 l32i a10, a6, 0x248 -40003b26: 10aa70 and a10, a10, a7 -40003b29: 0020c0 memw -40003b2c: 9266a2 s32i a10, a6, 0x248 -40003b2f: 0020c0 memw -40003b32: 922692 l32i a9, a6, 0x248 -40003b35: 209940 or a9, a9, a4 -40003b38: 0020c0 memw -40003b3b: 926692 s32i a9, a6, 0x248 -40003b3e: f2fe31 l32r a3, 40000738 <_c_0x80000000> -40003b41: 0020c0 memw -40003b44: 492522 l32i a2, a5, 0x124 -40003b47: 202230 or a2, a2, a3 -40003b4a: 0020c0 memw -40003b4d: 496522 s32i a2, a5, 0x124 -40003b50: f01d retw.n -40003b52: c00000 sub a0, a0, a0 -40003b55: a20020 muluh a0, a0, a2 -40003b58: 809826 beqi a8, 10, 40003adc <_X_SelectSpiFunction+0xc0> -40003b5b: 10aa add.n a1, a0, a10 -40003b5d: 0020c0 memw -40003b60: 9866a2 s32i a10, a6, 0x260 -40003b63: 0020c0 memw -40003b66: 982692 l32i a9, a6, 0x260 -40003b69: 109970 and a9, a9, a7 -40003b6c: 0020c0 memw -40003b6f: 986692 s32i a9, a6, 0x260 -40003b72: 0020c0 memw -40003b75: 982632 l32i a3, a6, 0x260 -40003b78: 203340 or a3, a3, a4 -40003b7b: 0020c0 memw -40003b7e: 986632 s32i a3, a6, 0x260 -40003b81: 0020c0 memw -40003b84: 992622 l32i a2, a6, 0x264 -40003b87: 102280 and a2, a2, a8 -40003b8a: 0020c0 memw -40003b8d: 996622 s32i a2, a6, 0x264 -40003b90: 0020c0 memw -40003b93: 9926f2 l32i a15, a6, 0x264 -40003b96: 10ff70 and a15, a15, a7 -40003b99: 0020c0 memw -40003b9c: 9966f2 s32i a15, a6, 0x264 -40003b9f: 0020c0 memw -40003ba2: 9926e2 l32i a14, a6, 0x264 -40003ba5: 20ee40 or a14, a14, a4 -40003ba8: 0020c0 memw -40003bab: 9966e2 s32i a14, a6, 0x264 -40003bae: 0020c0 memw -40003bb1: 9a26d2 l32i a13, a6, 0x268 -40003bb4: 10dd80 and a13, a13, a8 -40003bb7: 0020c0 memw -40003bba: 9a66d2 s32i a13, a6, 0x268 -40003bbd: 0020c0 memw -40003bc0: 9a26c2 l32i a12, a6, 0x268 -40003bc3: 10cc70 and a12, a12, a7 -40003bc6: 0020c0 memw -40003bc9: 9a66c2 s32i a12, a6, 0x268 -40003bcc: 0020c0 memw -40003bcf: 9a26b2 l32i a11, a6, 0x268 -40003bd2: 20bb40 or a11, a11, a4 -40003bd5: 0020c0 memw -40003bd8: 9a66b2 s32i a11, a6, 0x268 -40003bdb: 0020c0 memw -40003bde: 9526a2 l32i a10, a6, 0x254 -40003be1: 10aa80 and a10, a10, a8 -40003be4: 0020c0 memw -40003be7: 9566a2 s32i a10, a6, 0x254 -40003bea: 0020c0 memw -40003bed: 952692 l32i a9, a6, 0x254 -40003bf0: 109970 and a9, a9, a7 -40003bf3: 0020c0 memw -40003bf6: 956692 s32i a9, a6, 0x254 -40003bf9: 0020c0 memw -40003bfc: 952632 l32i a3, a6, 0x254 -40003bff: 203340 or a3, a3, a4 -40003c02: 0020c0 memw -40003c05: 956632 s32i a3, a6, 0x254 -40003c08: 0020c0 memw -40003c0b: 962622 l32i a2, a6, 0x258 -40003c0e: 102280 and a2, a2, a8 -40003c11: 0020c0 memw -40003c14: 966622 s32i a2, a6, 0x258 -40003c17: 0020c0 memw -40003c1a: 9626f2 l32i a15, a6, 0x258 -40003c1d: 10ff70 and a15, a15, a7 -40003c20: 0020c0 memw -40003c23: 9666f2 s32i a15, a6, 0x258 -40003c26: 0020c0 memw -40003c29: 9626e2 l32i a14, a6, 0x258 -40003c2c: 20ee40 or a14, a14, a4 -40003c2f: 0020c0 memw -40003c32: 9666e2 s32i a14, a6, 0x258 -40003c35: 0020c0 memw -40003c38: 9726d2 l32i a13, a6, 0x25c -40003c3b: 10dd80 and a13, a13, a8 -40003c3e: 0020c0 memw -40003c41: 9766d2 s32i a13, a6, 0x25c -40003c44: 0020c0 memw -40003c47: 9726c2 l32i a12, a6, 0x25c -40003c4a: 10cc70 and a12, a12, a7 -40003c4d: 0020c0 memw -40003c50: 9766c2 s32i a12, a6, 0x25c -40003c53: 0020c0 memw -40003c56: 9726b2 l32i a11, a6, 0x25c -40003c59: 20bb40 or a11, a11, a4 -40003c5c: 0020c0 memw -40003c5f: 9766b2 s32i a11, a6, 0x25c -40003c62: f2c7a1 l32r a10, 40000780 <_c_0x7fffffff> -40003c65: 0020c0 memw -40003c68: 492592 l32i a9, a5, 0x124 -40003c6b: 1099a0 and a9, a9, a10 -40003c6e: 0020c0 memw -40003c71: 496592 s32i a9, a5, 0x124 -40003c74: f01d retw.n - ... - -40003c78 <_c_0x60002e00>: -40003c78: 002e00 excw -40003c7b: 413660 srli a3, a6, 6 - -40003c7c <_X_SPI_chip_erase>: -40003c7c: 004136 entry a1, 32 -40003c7f: 20a220 or a10, a2, a2 -40003c82: 005865 call8 40004208 <_X_Wait_SPI_Idle> -40003c85: fb4ba1 l32r a10, 400029b4 <_c_0x00400000> -40003c88: fffc91 l32r a9, 40003c78 <_c_0x60002e00> -40003c8b: 0020c0 memw -40003c8e: 8069a2 s32i a10, a9, 0x200 -40003c91: 0020c0 memw -40003c94: 802982 l32i a8, a9, 0x200 -40003c97: d88c beqz.n a8, 40003ca8 <_X_SPI_chip_erase+0x2c> -40003c99: 078076 loop a0, 40003ca4 <_X_SPI_chip_erase+0x28> -40003c9c: 0020c0 memw -40003c9f: 8029b2 l32i a11, a9, 0x200 -40003ca2: 2b8c beqz.n a11, 40003ca8 <_X_SPI_chip_erase+0x2c> -40003ca4: fffc46 j 40003c99 <_X_SPI_chip_erase+0x1d> -40003ca7: 02ad00 andb b10, b13, b0 -40003caa: 0055e5 call8 40004208 <_X_Wait_SPI_Idle> -40003cad: 020c movi.n a2, 0 -40003caf: f01d retw.n -40003cb1: 000000 ill - -40003cb4 <_c_0x00ffffff>: -40003cb4: ff .byte 0xff -40003cb5: ff .byte 0xff -40003cb6: ff .byte 0xff - ... - -40003cb8 <_c_0x01000000>: -40003cb8: 000000 ill -40003cbb: 413601 l32r a0, 3ffd4194 <_start-0x2be6c> - -40003cbc <_XX_unk3cbc>: -40003cbc: 004136 entry a1, 32 -40003cbf: b48030 extui a8, a3, 0, 12 -40003cc2: 288c beqz.n a8, 40003cc8 <_XX_unk3cbc+0xc> -40003cc4: 120c movi.n a2, 1 -40003cc6: f01d retw.n -40003cc8: 02ad mov.n a10, a2 -40003cca: 0053e5 call8 40004208 <_X_Wait_SPI_Idle> -40003ccd: fff9c1 l32r a12, 40003cb4 <_c_0x00ffffff> -40003cd0: ffea91 l32r a9, 40003c78 <_c_0x60002e00> -40003cd3: 10c3c0 and a12, a3, a12 -40003cd6: 0020c0 memw -40003cd9: 8169c2 s32i a12, a9, 0x204 -40003cdc: fff7b1 l32r a11, 40003cb8 <_c_0x01000000> -40003cdf: 0020c0 memw -40003ce2: 8069b2 s32i a11, a9, 0x200 -40003ce5: 0020c0 memw -40003ce8: 8029a2 l32i a10, a9, 0x200 -40003ceb: 7a8c beqz.n a10, 40003cf6 <_XX_unk3cbc+0x3a> -40003ced: 0020c0 memw -40003cf0: 8029d2 l32i a13, a9, 0x200 -40003cf3: ff6d56 bnez a13, 40003ced <_XX_unk3cbc+0x31> -40003cf6: 02ad mov.n a10, a2 -40003cf8: 0050e5 call8 40004208 <_X_Wait_SPI_Idle> -40003cfb: 020c movi.n a2, 0 -40003cfd: f01d retw.n - ... - -40003d00 <_c_0x00800000>: -40003d00: 800000 add a0, a0, a0 -40003d03: 413600 srli a3, a0, 6 -40003d06: a22000 muluh a2, a0, a0 -40003d09: 4fe520 excw -40003d0c: e9b100 excw -40003d0f: ff .byte 0xff -40003d10: ffda91 l32r a9, 40003c78 <_c_0x60002e00> -40003d13: 10b3b0 and a11, a3, a11 -40003d16: 0020c0 memw -40003d19: 8169b2 s32i a11, a9, 0x204 -40003d1c: fff9a1 l32r a10, 40003d00 <_c_0x00800000> -40003d1f: 0020c0 memw -40003d22: 8069a2 s32i a10, a9, 0x200 -40003d25: 0020c0 memw -40003d28: 802982 l32i a8, a9, 0x200 -40003d2b: d88c beqz.n a8, 40003d3c <_c_0x00800000+0x3c> -40003d2d: 078076 loop a0, 40003d38 <_c_0x00800000+0x38> -40003d30: 0020c0 memw -40003d33: 8029c2 l32i a12, a9, 0x200 -40003d36: 2c8c beqz.n a12, 40003d3c <_c_0x00800000+0x3c> -40003d38: fffc46 j 40003d2d <_c_0x00800000+0x2d> -40003d3b: 02ad00 andb b10, b13, b0 -40003d3e: 004ca5 call8 40004208 <_X_Wait_SPI_Idle> -40003d41: 020c movi.n a2, 0 -40003d43: f01d retw.n -40003d45: 000000 ill -40003d48: 000000 ill -40003d4b: 000020 excw - -40003d4c <_c_0x02000000>: -40003d4c: 000000 ill -40003d4f: 413602 excw -40003d52: 380c00 excw -40003d55: 1f8857 bany a8, a5, 40003d78 <_c_0x02000000+0x2c> -40003d58: 4298 l32i.n a9, a2, 16 -40003d5a: e2a390 remu a10, a3, a9 -40003d5d: a5aa add.n a10, a5, a10 -40003d5f: 1539a7 bltu a9, a10, 40003d78 <_c_0x02000000+0x2c> -40003d62: 20a220 or a10, a2, a2 -40003d65: 004a25 call8 40004208 <_X_Wait_SPI_Idle> -40003d68: 0215e6 bgei a5, 1, 40003d6e <_c_0x02000000+0x22> -40003d6b: 002b86 j 40003e1d <_c_0x02000000+0xd1> -40003d6e: ffc261 l32r a6, 40003c78 <_c_0x60002e00> -40003d71: ffd071 l32r a7, 40003cb4 <_c_0x00ffffff> -40003d74: 0003c6 j 40003d87 <_c_0x02000000+0x3b> -40003d77: 120c00 andbc b0, b12, b0 -40003d7a: f01d retw.n -40003d7c: 02ad mov.n a10, a2 -40003d7e: 0048a5 call8 40004208 <_X_Wait_SPI_Idle> -40003d81: 0215e6 bgei a5, 1, 40003d87 <_c_0x02000000+0x3b> -40003d84: 002546 j 40003e1d <_c_0x02000000+0xd1> -40003d87: 10a370 and a10, a3, a7 -40003d8a: 56c5a6 blti a5, 32, 40003de4 <_c_0x02000000+0x98> -40003d8d: ffeed1 l32r a13, 40003d48 <_c_0x00800000+0x48> -40003d90: 20dad0 or a13, a10, a13 -40003d93: 0020c0 memw -40003d96: 8166d2 s32i a13, a6, 0x204 -40003d99: 080c movi.n a8, 0 -40003d9b: 890c movi.n a9, 8 -40003d9d: 11a976 loopgtz a9, 40003db2 <_c_0x02000000+0x66> -40003da0: a09860 addx4 a9, a8, a6 -40003da3: 04b8 l32i.n a11, a4, 0 -40003da5: 0020c0 memw -40003da8: 9069b2 s32i a11, a9, 0x240 -40003dab: 444b addi.n a4, a4, 4 -40003dad: 981b addi.n a9, a8, 1 -40003daf: 748090 extui a8, a9, 0, 8 -40003db2: e0c552 addi a5, a5, -32 -40003db5: 20c332 addi a3, a3, 32 -40003db8: 02ad mov.n a10, a2 -40003dba: 004025 call8 400041bc <_X_SPI_write_enable> -40003dbd: fb7a56 bnez a10, 40003d78 <_c_0x02000000+0x2c> -40003dc0: 0020c0 memw -40003dc3: ffe2f1 l32r a15, 40003d4c <_c_0x02000000> -40003dc6: 8066f2 s32i a15, a6, 0x200 -40003dc9: 0020c0 memw -40003dcc: 8026e2 l32i a14, a6, 0x200 -40003dcf: 0020f0 nop -40003dd2: fa6e16 beqz a14, 40003d7c <_c_0x02000000+0x30> -40003dd5: 088076 loop a0, 40003de1 <_c_0x02000000+0x95> -40003dd8: 0020c0 memw -40003ddb: 802682 l32i a8, a6, 0x200 -40003dde: f9a816 beqz a8, 40003d7c <_c_0x02000000+0x30> -40003de1: fffc06 j 40003dd5 <_c_0x02000000+0x89> -40003de4: 03bd mov.n a11, a3 -40003de6: 8b75b0 depbits a11, a5, 24, 8 -40003de9: 0020c0 memw -40003dec: 8166b2 s32i a11, a6, 0x204 -40003def: 219250 srai a9, a5, 2 -40003df2: 14a050 extui a10, a5, 0, 2 -40003df5: fa9c beqz.n a10, 40003e18 <_c_0x02000000+0xcc> -40003df7: a91b addi.n a10, a9, 1 -40003df9: 080c movi.n a8, 0 -40003dfb: 7490a0 extui a9, a10, 0, 8 -40003dfe: 119976 loopnez a9, 40003e13 <_c_0x02000000+0xc7> -40003e01: a09860 addx4 a9, a8, a6 -40003e04: 04b8 l32i.n a11, a4, 0 -40003e06: 0020c0 memw -40003e09: 9069b2 s32i a11, a9, 0x240 -40003e0c: 444b addi.n a4, a4, 4 -40003e0e: 981b addi.n a9, a8, 1 -40003e10: 748090 extui a8, a9, 0, 8 -40003e13: 050c movi.n a5, 0 -40003e15: ffe7c6 j 40003db8 <_c_0x02000000+0x6c> -40003e18: 09ad mov.n a10, a9 -40003e1a: fff6c6 j 40003df9 <_c_0x02000000+0xad> -40003e1d: 020c movi.n a2, 0 -40003e1f: f01d retw.n -40003e21: 000000 ill - -40003e24 <_XX_unk3e24>: -40003e24: 004136 entry a1, 32 -40003e27: 1288 l32i.n a8, a2, 4 -40003e29: 953a add.n a9, a5, a3 -40003e2b: 03b897 bgeu a8, a9, 40003e32 <_XX_unk3e24+0xe> -40003e2e: 120c movi.n a2, 1 -40003e30: f01d retw.n -40003e32: 02ad mov.n a10, a2 -40003e34: 003d25 call8 40004208 <_X_Wait_SPI_Idle> -40003e37: 0215e6 bgei a5, 1, 40003e3d <_XX_unk3e24+0x19> -40003e3a: 002e06 j 40003ef6 <_XX_unk3e24+0xd2> -40003e3d: ffc2f1 l32r a15, 40003d48 <_c_0x00800000+0x48> -40003e40: f23ee1 l32r a14, 40000738 <_c_0x80000000> -40003e43: ff8dd1 l32r a13, 40003c78 <_c_0x60002e00> -40003e46: 000b46 j 40003e77 <_XX_unk3e24+0x53> -40003e49: 219250 srai a9, a5, 2 -40003e4c: 14a050 extui a10, a5, 0, 2 -40003e4f: 09ea16 beqz a10, 40003ef1 <_XX_unk3e24+0xcd> -40003e52: a91b addi.n a10, a9, 1 -40003e54: 080c movi.n a8, 0 -40003e56: 7490a0 extui a9, a10, 0, 8 -40003e59: 119976 loopnez a9, 40003e6e <_XX_unk3e24+0x4a> -40003e5c: a098d0 addx4 a9, a8, a13 -40003e5f: 0020c0 memw -40003e62: 902992 l32i a9, a9, 0x240 -40003e65: 881b addi.n a8, a8, 1 -40003e67: 0499 s32i.n a9, a4, 0 -40003e69: 748080 extui a8, a8, 0, 8 -40003e6c: 444b addi.n a4, a4, 4 -40003e6e: 00a052 movi a5, 0 -40003e71: 0215e6 bgei a5, 1, 40003e77 <_XX_unk3e24+0x53> -40003e74: 001f86 j 40003ef6 <_XX_unk3e24+0xd2> -40003e77: 4ac5a6 blti a5, 32, 40003ec5 <_XX_unk3e24+0xa1> -40003e7a: 20c3f0 or a12, a3, a15 -40003e7d: 0020c0 memw -40003e80: 816dc2 s32i a12, a13, 0x204 -40003e83: 0020c0 memw -40003e86: 806de2 s32i a14, a13, 0x200 -40003e89: 0020c0 memw -40003e8c: 802db2 l32i a11, a13, 0x200 -40003e8f: eb8c beqz.n a11, 40003ea1 <_XX_unk3e24+0x7d> -40003e91: 078076 loop a0, 40003e9c <_XX_unk3e24+0x78> -40003e94: 0020c0 memw -40003e97: 802d82 l32i a8, a13, 0x200 -40003e9a: 388c beqz.n a8, 40003ea1 <_XX_unk3e24+0x7d> -40003e9c: fffc46 j 40003e91 <_XX_unk3e24+0x6d> -40003e9f: 0c0000 excw -40003ea2: 0c08 l32i.n a0, a12, 0 -40003ea4: 7689 s32i.n a8, a6, 28 -40003ea6: 11a9 s32i.n a10, a1, 4 -40003ea8: a098d0 addx4 a9, a8, a13 -40003eab: 0020c0 memw -40003eae: 902992 l32i a9, a9, 0x240 -40003eb1: 881b addi.n a8, a8, 1 -40003eb3: 0499 s32i.n a9, a4, 0 -40003eb5: 748080 extui a8, a8, 0, 8 -40003eb8: 444b addi.n a4, a4, 4 -40003eba: e0c552 addi a5, a5, -32 -40003ebd: 20c332 addi a3, a3, 32 -40003ec0: ffeb46 j 40003e71 <_XX_unk3e24+0x4d> -40003ec3: 800000 add a0, a0, a0 -40003ec6: a001a5 call8 3ffa3ee0 <_start-0x5c120> -40003ec9: c020a3 excw -40003ecc: a20020 muluh a0, a0, a2 -40003ecf: 816d excw -40003ed1: 0020c0 memw -40003ed4: 806de2 s32i a14, a13, 0x200 -40003ed7: 0020c0 memw -40003eda: 802d92 l32i a9, a13, 0x200 -40003edd: f68916 beqz a9, 40003e49 <_XX_unk3e24+0x25> -40003ee0: f03d nop.n -40003ee2: 088076 loop a0, 40003eee <_XX_unk3e24+0xca> -40003ee5: 0020c0 memw -40003ee8: 802db2 l32i a11, a13, 0x200 -40003eeb: f5ab16 beqz a11, 40003e49 <_XX_unk3e24+0x25> -40003eee: fffb86 j 40003ee0 <_XX_unk3e24+0xbc> -40003ef1: 09ad mov.n a10, a9 -40003ef3: ffd746 j 40003e54 <_XX_unk3e24+0x30> -40003ef6: 020c movi.n a2, 0 -40003ef8: f01d retw.n - ... - -40003efc <_X_SPI_read_status>: -40003efc: 004136 entry a1, 32 -40003eff: fd8c61 l32r a6, 40003530 <_c_0x08000000> -40003f02: ff5d51 l32r a5, 40003c78 <_c_0x60002e00> -40003f05: 080c movi.n a8, 0 -40003f07: 000346 j 40003f18 <_X_SPI_read_status+0x1c> -40003f0a: 5298 l32i.n a9, a2, 20 -40003f0c: 0020c0 memw -40003f0f: 842572 l32i a7, a5, 0x210 -40003f12: 107790 and a7, a7, a9 -40003f15: 236707 bbci a7, 0, 40003f3c <_X_SPI_read_status+0x40> -40003f18: 0020c0 memw -40003f1b: 846582 s32i a8, a5, 0x210 -40003f1e: 0020c0 memw -40003f21: 806562 s32i a6, a5, 0x200 -40003f24: 0020c0 memw -40003f27: 8025a2 l32i a10, a5, 0x200 -40003f2a: fdca16 beqz a10, 40003f0a <_X_SPI_read_status+0xe> -40003f2d: 088076 loop a0, 40003f39 <_X_SPI_read_status+0x3d> -40003f30: 0020c0 memw -40003f33: 8025b2 l32i a11, a5, 0x200 -40003f36: fd0b16 beqz a11, 40003f0a <_X_SPI_read_status+0xe> -40003f39: fffc06 j 40003f2d <_X_SPI_read_status+0x31> -40003f3c: 0379 s32i.n a7, a3, 0 -40003f3e: 020c movi.n a2, 0 -40003f40: f01d retw.n -40003f42: 480000 excw -40003f45: ffc8 l32i.n a12, a15, 60 -40003f47: 3f .byte 0x3f - -40003f48 <_c_0x90000000>: -40003f48: 000000 ill -40003f4b: 003590 excw - -40003f4c <_c_0x70000035>: -40003f4c: 000035 call12 40003f50 <_c_0x00040000> -40003f4f: 000070 excw - -40003f50 <_c_0x00040000>: -40003f50: 040000 extui a0, a0, 0, 1 - ... - -40003f54 <_XX_unk3f54>: -40003f54: 004136 entry a1, 32 -40003f57: fffba1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> -40003f5a: 0020f0 nop -40003f5d: 0aa8 l32i.n a10, a10, 0 -40003f5f: 002aa5 call8 40004208 <_X_Wait_SPI_Idle> -40003f62: 0d0c movi.n a13, 0 -40003f64: ff4591 l32r a9, 40003c78 <_c_0x60002e00> -40003f67: 0020c0 memw -40003f6a: 8229a2 l32i a10, a9, 0x208 -40003f6d: 0020c0 memw -40003f70: 8269d2 s32i a13, a9, 0x208 -40003f73: 00a782 movi a8, 0x700 -40003f76: 0020c0 memw -40003f79: 886982 s32i a8, a9, 0x220 -40003f7c: fff3f1 l32r a15, 40003f48 <_c_0x90000000> -40003f7f: 0020c0 memw -40003f82: 8769f2 s32i a15, a9, 0x21c -40003f85: fff1e1 l32r a14, 40003f4c <_c_0x70000035> -40003f88: 0020c0 memw -40003f8b: 8969e2 s32i a14, a9, 0x224 -40003f8e: 0020c0 memw -40003f91: 9069d2 s32i a13, a9, 0x240 -40003f94: ffefc1 l32r a12, 40003f50 <_c_0x00040000> -40003f97: 0020c0 memw -40003f9a: 8069c2 s32i a12, a9, 0x200 -40003f9d: 0020c0 memw -40003fa0: 8029b2 l32i a11, a9, 0x200 -40003fa3: db8c beqz.n a11, 40003fb4 <_XX_unk3f54+0x60> -40003fa5: 078076 loop a0, 40003fb0 <_XX_unk3f54+0x5c> -40003fa8: 0020c0 memw -40003fab: 8029b2 l32i a11, a9, 0x200 -40003fae: 2b8c beqz.n a11, 40003fb4 <_XX_unk3f54+0x60> -40003fb0: fffc46 j 40003fa5 <_XX_unk3f54+0x51> -40003fb3: 20c000 or a12, a0, a0 -40003fb6: 29c200 excw -40003fb9: c0c090 sub a12, a0, a9 -40003fbc: cc8074 excw -40003fbf: 03c911 l32r a1, 3ffc4ee4 <_start-0x3b11c> -40003fc2: 0020c0 memw -40003fc5: 8269a2 s32i a10, a9, 0x208 -40003fc8: 020c movi.n a2, 0 -40003fca: f01d retw.n - -40003fcc <_c_0x04000000>: -40003fcc: 000000 ill -40003fcf: 413604 excw -40003fd2: 02ad00 andb b10, b13, b0 -40003fd5: 002325 call8 40004208 <_X_Wait_SPI_Idle> -40003fd8: ff2891 l32r a9, 40003c78 <_c_0x60002e00> -40003fdb: 0020c0 memw -40003fde: 846932 s32i a3, a9, 0x210 -40003fe1: fffaa1 l32r a10, 40003fcc <_c_0x04000000> -40003fe4: 0020c0 memw -40003fe7: 8069a2 s32i a10, a9, 0x200 -40003fea: 0020c0 memw -40003fed: 802982 l32i a8, a9, 0x200 -40003ff0: 0020f0 nop -40003ff3: d88c beqz.n a8, 40004004 <_c_0x04000000+0x38> -40003ff5: 078076 loop a0, 40004000 <_c_0x04000000+0x34> -40003ff8: 0020c0 memw -40003ffb: 8029b2 l32i a11, a9, 0x200 -40003ffe: 2b8c beqz.n a11, 40004004 <_c_0x04000000+0x38> -40004000: fffc46 j 40003ff5 <_c_0x04000000+0x29> -40004003: 020c00 andb b0, b12, b0 -40004006: f01d retw.n -40004008: 000000 ill -4000400b: 000070 excw -4000400e: 368800 excw - -40004010 <_XX_unk4010>: -40004010: 004136 entry a1, 32 -40004013: 000232 l8ui a3, a2, 0 -40004016: 0020f0 nop -40004019: 144230 extui a4, a3, 2, 2 -4000401c: 145430 extui a5, a3, 4, 2 -4000401f: 143030 extui a3, a3, 0, 2 -40004022: 09a316 beqz a3, 400040c0 <_XX_unk4010+0xb0> -40004025: 0233b6 bltui a3, 3, 4000402b <_XX_unk4010+0x1b> -40004028: 002506 j 400040c0 <_XX_unk4010+0xb0> -4000402b: 022366 bnei a3, 2, 40004031 <_XX_unk4010+0x21> -4000402e: 002186 j 400040b8 <_XX_unk4010+0xa8> -40004031: 0b1366 bnei a3, 1, 40004040 <_XX_unk4010+0x30> -40004034: 0224b6 bltui a4, 2, 4000403a <_XX_unk4010+0x2a> -40004037: 002146 j 400040c0 <_XX_unk4010+0xb0> -4000403a: 808340 add a8, a3, a4 -4000403d: 7f9587 bne a5, a8, 400040c0 <_XX_unk4010+0xb0> -40004040: ffc1a1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> -40004043: f03d nop.n -40004045: 0aa8 l32i.n a10, a10, 0 -40004047: 001c25 call8 40004208 <_X_Wait_SPI_Idle> -4000404a: 0f0c movi.n a15, 0 -4000404c: ffefc1 l32r a12, 40004008 <_c_0x04000000+0x3c> -4000404f: ffc0b1 l32r a11, 40003f50 <_c_0x00040000> -40004052: ff09a1 l32r a10, 40003c78 <_c_0x60002e00> -40004055: 0020c0 memw -40004058: 822ad2 l32i a13, a10, 0x208 -4000405b: 0020c0 memw -4000405e: 0212e2 l16ui a14, a2, 4 -40004061: 826af2 s32i a15, a10, 0x208 -40004064: 10ce16 beqz a14, 40004174 <_XX_unk4010+0x164> -40004067: f87c movi.n a8, -1 -40004069: b09580 addx8 a9, a5, a8 -4000406c: ffb751 l32r a5, 40003f48 <_c_0x90000000> -4000406f: b08380 addx8 a8, a3, a8 -40004072: 0199f0 slli a9, a9, 17 -40004075: 118880 slli a8, a8, 8 -40004078: 208890 or a8, a8, a9 -4000407b: 0c9d mov.n a9, a12 -4000407d: 0020c0 memw -40004080: 886a82 s32i a8, a10, 0x220 -40004083: 0020c0 memw -40004086: 876a52 s32i a5, a10, 0x21c -40004089: 0102e2 l8ui a14, a2, 1 -4000408c: 0a7e90 depbits a9, a14, 0, 8 -4000408f: 0020c0 memw -40004092: 896a92 s32i a9, a10, 0x224 -40004095: 0020c0 memw -40004098: 906af2 s32i a15, a10, 0x240 -4000409b: 0020c0 memw -4000409e: 806ab2 s32i a11, a10, 0x200 -400040a1: 0020c0 memw -400040a4: 802a82 l32i a8, a10, 0x200 -400040a7: a89c beqz.n a8, 400040c5 <_XX_unk4010+0xb5> -400040a9: 078076 loop a0, 400040b4 <_XX_unk4010+0xa4> -400040ac: 0020c0 memw -400040af: 802a92 l32i a9, a10, 0x200 -400040b2: f98c beqz.n a9, 400040c5 <_XX_unk4010+0xb5> -400040b4: fffc46 j 400040a9 <_XX_unk4010+0x99> -400040b7: 44cc00 extui a12, a0, 12, 5 -400040ba: 022566 bnei a5, 2, 400040c0 <_XX_unk4010+0xb0> -400040bd: ffdc06 j 40004031 <_XX_unk4010+0x21> -400040c0: f01d retw.n -400040c2: 000000 ill -400040c5: 0020c0 memw -400040c8: 902ae2 l32i a14, a10, 0x240 -400040cb: 930b addi.n a9, a3, -1 -400040cd: 7480e0 extui a8, a14, 0, 8 -400040d0: f4e0e0 extui a14, a14, 0, 16 -400040d3: 83e890 moveqz a14, a8, a9 -400040d6: 0ca416 beqz a4, 400041a4 <_XX_unk4010+0x194> -400040d9: 0c9d mov.n a9, a12 -400040db: 0020c0 memw -400040de: 876a52 s32i a5, a10, 0x21c -400040e1: 020282 l8ui a8, a2, 2 -400040e4: 0a7890 depbits a9, a8, 0, 8 -400040e7: 0020c0 memw -400040ea: 896a92 s32i a9, a10, 0x224 -400040ed: 0020c0 memw -400040f0: 906af2 s32i a15, a10, 0x240 -400040f3: 0020c0 memw -400040f6: 806ab2 s32i a11, a10, 0x200 -400040f9: 0020c0 memw -400040fc: 802a82 l32i a8, a10, 0x200 -400040ff: d88c beqz.n a8, 40004110 <_XX_unk4010+0x100> -40004101: 078076 loop a0, 4000410c <_XX_unk4010+0xfc> -40004104: 0020c0 memw -40004107: 802a92 l32i a9, a10, 0x200 -4000410a: 298c beqz.n a9, 40004110 <_XX_unk4010+0x100> -4000410c: fffc46 j 40004101 <_XX_unk4010+0xf1> -4000410f: 20c000 or a12, a0, a0 -40004112: 128200 andbc b8, b2, b0 -40004115: 2a9202 l16si a0, a2, 84 -40004118: 12f290 andbc b15, b2, b9 -4000411b: 909003 excw -4000411e: ff8074 excw -40004121: 998010 excw -40004124: 9e9011 l32r a1, 3ffebb64 <_start-0x1449c> -40004127: 909020 addx2 a9, a0, a2 -4000412a: 8890f4 excw -4000412d: 1f8710 excw -40004130: 913a add.n a9, a1, a3 -40004132: c0ffb6 bltui a15, 0x100, 400040f6 <_XX_unk4010+0xe6> -40004135: 920020 excw -40004138: 876a add.n a8, a7, a6 -4000413a: 030282 l8ui a8, a2, 3 -4000413d: 0a78c0 depbits a12, a8, 0, 8 -40004140: 0020c0 memw -40004143: 896ac2 s32i a12, a10, 0x224 -40004146: 0312f2 l16ui a15, a2, 6 -40004149: 0020c0 memw -4000414c: 906af2 s32i a15, a10, 0x240 -4000414f: 0020c0 memw -40004152: 806ab2 s32i a11, a10, 0x200 -40004155: 0020c0 memw -40004158: 802ae2 l32i a14, a10, 0x200 -4000415b: de8c beqz.n a14, 4000416c <_XX_unk4010+0x15c> -4000415d: 078076 loop a0, 40004168 <_XX_unk4010+0x158> -40004160: 0020c0 memw -40004163: 802ab2 l32i a11, a10, 0x200 -40004166: 2b8c beqz.n a11, 4000416c <_XX_unk4010+0x15c> -40004168: fffc46 j 4000415d <_XX_unk4010+0x14d> -4000416b: 20c000 or a12, a0, a0 -4000416e: 6ad200 depbits a0, a2, 6, 14 -40004171: f01d82 l16ui a8, a13, 0x1e0 -40004174: f17181 l32r a8, 40000738 <_c_0x80000000> -40004177: 0020c0 memw -4000417a: 876a82 s32i a8, a10, 0x21c -4000417d: 0102f2 l8ui a15, a2, 1 -40004180: 0a7fc0 depbits a12, a15, 0, 8 -40004183: 0020c0 memw -40004186: 896ac2 s32i a12, a10, 0x224 -40004189: 0020c0 memw -4000418c: 806ab2 s32i a11, a10, 0x200 -4000418f: 0020c0 memw -40004192: 802ae2 l32i a14, a10, 0x200 -40004195: fd3e16 beqz a14, 4000416c <_XX_unk4010+0x15c> -40004198: 0020c0 memw -4000419b: 802a92 l32i a9, a10, 0x200 -4000419e: ff6956 bnez a9, 40004198 <_XX_unk4010+0x188> -400041a1: fff1c6 j 4000416c <_XX_unk4010+0x15c> -400041a4: 021292 l16ui a9, a2, 4 -400041a7: 0312f2 l16ui a15, a2, 6 -400041aa: 108e90 and a8, a14, a9 -400041ad: 10ff90 and a15, a15, a9 -400041b0: 021f87 beq a15, a8, 400041b6 <_XX_unk4010+0x1a6> -400041b3: ffde86 j 40004131 <_XX_unk4010+0x121> -400041b6: ffec86 j 4000416c <_XX_unk4010+0x15c> -400041b9: 000000 ill - -400041bc <_X_SPI_write_enable>: -400041bc: 006136 entry a1, 48 -400041bf: 02ad mov.n a10, a2 -400041c1: 080c movi.n a8, 0 -400041c3: 006182 s32i a8, a1, 0 -400041c6: 000425 call8 40004208 <_X_Wait_SPI_Idle> -400041c9: f165b1 l32r a11, 40000760 <_c_0x40000000> -400041cc: feab91 l32r a9, 40003c78 <_c_0x60002e00> -400041cf: 0020c0 memw -400041d2: 8069b2 s32i a11, a9, 0x200 -400041d5: 0020c0 memw -400041d8: 8029a2 l32i a10, a9, 0x200 -400041db: fa8c beqz.n a10, 400041ee <_X_SPI_write_enable+0x32> -400041dd: 078076 loop a0, 400041e8 <_X_SPI_write_enable+0x2c> -400041e0: 0020c0 memw -400041e3: 8029c2 l32i a12, a9, 0x200 -400041e6: 4c8c beqz.n a12, 400041ee <_X_SPI_write_enable+0x32> -400041e8: fffc46 j 400041dd <_X_SPI_write_enable+0x21> -400041eb: 000000 ill -400041ee: 01d8 l32i.n a13, a1, 0 -400041f0: 230c movi.n a3, 2 -400041f2: 0bed17 bbsi a13, 1, 40004201 <_X_SPI_write_enable+0x45> -400041f5: 02ad mov.n a10, a2 -400041f7: 01bd mov.n a11, a1 -400041f9: ffd025 call8 40003efc <_X_SPI_read_status> -400041fc: 01e8 l32i.n a14, a1, 0 -400041fe: f30e37 bnone a14, a3, 400041f5 <_X_SPI_write_enable+0x39> -40004201: 020c movi.n a2, 0 -40004203: f01d retw.n -40004205: 000000 ill - -40004208 <_X_Wait_SPI_Idle>: -40004208: 006136 entry a1, 48 -4000420b: 20a220 or a10, a2, a2 -4000420e: fe9a91 l32r a9, 40003c78 <_c_0x60002e00> -40004211: 0020c0 memw -40004214: be2982 l32i a8, a9, 0x2f8 -40004217: d88c beqz.n a8, 40004228 <_l_4228> -40004219: 078076 loop a0, 40004224 <_X_Wait_SPI_Idle+0x1c> -4000421c: 0020c0 memw -4000421f: be29b2 l32i a11, a9, 0x2f8 -40004222: 2b8c beqz.n a11, 40004228 <_l_4228> -40004224: fffc46 j 40004219 <_X_Wait_SPI_Idle+0x11> - ... - -40004228 <_l_4228>: -40004228: 01bd mov.n a11, a1 -4000422a: ffcd25 call8 40003efc <_X_SPI_read_status> -4000422d: 3a8c beqz.n a10, 40004234 <_l_4234> -4000422f: 120c movi.n a2, 1 -40004231: f01d retw.n - ... - -40004234 <_l_4234>: -40004234: 020c movi.n a2, 0 -40004236: f01d retw.n - -40004238 <_XX_unk4238>: -40004238: 006136 entry a1, 48 -4000423b: 02ad mov.n a10, a2 -4000423d: 01bd mov.n a11, a1 -4000423f: ffd165 call8 40003f54 <_XX_unk3f54> -40004242: 2a8c beqz.n a10, 40004248 <_XX_unk4238+0x10> -40004244: 120c movi.n a2, 1 -40004246: f01d retw.n -40004248: 0188 l32i.n a8, a1, 0 -4000424a: 036897 bbci a8, 9, 40004251 <_XX_unk4238+0x19> -4000424d: 020c movi.n a2, 0 -4000424f: f01d retw.n -40004251: f9d8b1 l32r a11, 400029b4 <_c_0x00400000> -40004254: fe89a1 l32r a10, 40003c78 <_c_0x60002e00> -40004257: 0020c0 memw -4000425a: 822a92 l32i a9, a10, 0x208 -4000425d: f03d nop.n -4000425f: 2099b0 or a9, a9, a11 -40004262: 0020c0 memw -40004265: 826a92 s32i a9, a10, 0x208 -40004268: 02ad mov.n a10, a2 -4000426a: fff525 call8 400041bc <_X_SPI_write_enable> -4000426d: fd3a56 bnez a10, 40004244 <_XX_unk4238+0xc> -40004270: 02ad mov.n a10, a2 -40004272: 00a2b2 movi a11, 0x200 -40004275: ffd5a5 call8 40003fd0 <_c_0x04000000+0x4> -40004278: fc8a56 bnez a10, 40004244 <_XX_unk4238+0xc> -4000427b: fff386 j 4000424d <_XX_unk4238+0x15> -4000427e: 360000 excw -40004281: ad0061 l32r a6, 3ffef684 <_start-0x1097c> -40004284: 01bd02 l32ai a0, a13, 4 -40004287: ffcce5 call8 40003f54 <_XX_unk3f54> -4000428a: 2a8c beqz.n a10, 40004290 <_XX_unk4238+0x58> -4000428c: 120c movi.n a2, 1 -4000428e: f01d retw.n -40004290: 0188 l32i.n a8, a1, 0 -40004292: 03e897 bbsi a8, 9, 40004299 <_XX_unk4238+0x61> -40004295: 020c movi.n a2, 0 -40004297: f01d retw.n -40004299: f9c6b1 l32r a11, 400029b4 <_c_0x00400000> -4000429c: fe77a1 l32r a10, 40003c78 <_c_0x60002e00> -4000429f: 0020c0 memw -400042a2: 822a92 l32i a9, a10, 0x208 -400042a5: f03d nop.n -400042a7: 2099b0 or a9, a9, a11 -400042aa: 0020c0 memw -400042ad: 826a92 s32i a9, a10, 0x208 -400042b0: 02ad mov.n a10, a2 -400042b2: fff0a5 call8 400041bc <_X_SPI_write_enable> -400042b5: fd3a56 bnez a10, 4000428c <_XX_unk4238+0x54> -400042b8: 02ad mov.n a10, a2 -400042ba: 04c1b2 addi a11, a1, 4 -400042bd: ffc3e5 call8 40003efc <_X_SPI_read_status> -400042c0: 02ad mov.n a10, a2 -400042c2: 0401b2 l8ui a11, a1, 4 -400042c5: ffd0a5 call8 40003fd0 <_c_0x04000000+0x4> -400042c8: fc0a56 bnez a10, 4000428c <_XX_unk4238+0x54> -400042cb: fff186 j 40004295 <_XX_unk4238+0x5d> -400042ce: 000000 ill -400042d1: 002880 excw -400042d4: 100000 and a0, a0, a0 - ... - -400042d8 <_X_SPIFlashModeConfig>: -400042d8: 004136 entry a1, 32 -400042db: 069216 beqz a2, 40004348 <_X_SPIFlashModeConfig+0x70> -400042de: 6e1226 beqi a2, 1, 40004350 <_X_SPIFlashModeConfig+0x78> -400042e1: 732226 beqi a2, 2, 40004358 <_X_SPIFlashModeConfig+0x80> -400042e4: 783226 beqi a2, 3, 40004360 <_X_SPIFlashModeConfig+0x88> -400042e7: 7d4226 beqi a2, 4, 40004368 <_X_SPIFlashModeConfig+0x90> -400042ea: 050c movi.n a5, 0 -400042ec: fe6321 l32r a2, 40003c78 <_c_0x60002e00> -400042ef: 0b23f6 bgeui a3, 2, 400042fe <_X_SPIFlashModeConfig+0x26> -400042f2: f11141 l32r a4, 40000738 <_c_0x80000000> -400042f5: 0020c0 memw -400042f8: 866242 s32i a4, a2, 0x218 -400042fb: 000646 j 40004318 <_X_SPIFlashModeConfig+0x40> -400042fe: 630b addi.n a6, a3, -1 -40004300: 118640 slli a8, a6, 12 -40004303: 206680 or a6, a6, a8 -40004306: 418130 srli a8, a3, 1 -40004309: ffc882 addi a8, a8, -1 -4000430c: 1188a0 slli a8, a8, 6 -4000430f: 206680 or a6, a6, a8 -40004312: 0020c0 memw -40004315: 866262 s32i a6, a2, 0x218 -40004318: ffeeb1 l32r a11, 400042d0 <_XX_unk4238+0x98> -4000431b: 20b5b0 or a11, a5, a11 -4000431e: 0020c0 memw -40004321: 8262b2 s32i a11, a2, 0x208 -40004324: ffeca1 l32r a10, 400042d4 <_XX_unk4238+0x9c> -40004327: 0020c0 memw -4000432a: 8062a2 s32i a10, a2, 0x200 -4000432d: 0020c0 memw -40004330: 802292 l32i a9, a2, 0x200 -40004333: d98c beqz.n a9, 40004344 <_X_SPIFlashModeConfig+0x6c> -40004335: 078076 loop a0, 40004340 <_X_SPIFlashModeConfig+0x68> -40004338: 0020c0 memw -4000433b: 8022c2 l32i a12, a2, 0x200 -4000433e: 2c8c beqz.n a12, 40004344 <_X_SPIFlashModeConfig+0x6c> -40004340: fffc46 j 40004335 <_X_SPIFlashModeConfig+0x5d> -40004343: f01d00 subx8 a1, a13, a0 -40004346: 510000 excw -40004349: fe5c movi.n a14, 95 -4000434b: ffe746 j 400042ec <_X_SPIFlashModeConfig+0x14> -4000434e: 510000 excw -40004351: 46ffe1 l32r a14, 3ffd5f50 <_start-0x2a0b0> -40004354: 00ffe5 call8 40005354 <_XX_unk51ac+0x1a8> -40004357: 6a5100 depbits a0, a1, 6, 6 -4000435a: fe .byte 0xfe -4000435b: ffe346 j 400042ec <_X_SPIFlashModeConfig+0x14> -4000435e: 510000 excw -40004361: fabd excw -40004363: ffe146 j 400042ec <_X_SPIFlashModeConfig+0x14> -40004366: 510000 excw -40004369: 46f473 excw -4000436c: df .byte 0xdf -4000436d: ff .byte 0xff - ... - -40004370 <_X_spi_flash_attach>: -40004370: 004136 entry a1, 32 -40004373: 20a220 or a10, a2, a2 -40004376: ff6a65 call8 40003a1c <_X_SelectSpiFunction> -40004379: 5a0c movi.n a10, 5 -4000437b: 04a0b2 movi a11, 4 -4000437e: fff5a5 call8 400042d8 <_X_SPIFlashModeConfig> -40004381: 03bd mov.n a11, a3 -40004383: 5a0c movi.n a10, 5 -40004385: 001b25 call8 40004538 <_X_SPIReadModeConfig> -40004388: f01d retw.n - ... - -4000438c : -4000438c: 004136 entry a1, 32 -4000438f: feeda1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> -40004392: 002aa2 l32i a10, a10, 0 -40004395: ffe725 call8 40004208 <_X_Wait_SPI_Idle> -40004398: 0b2c movi.n a11, 32 -4000439a: fd6c movi.n a13, -17 -4000439c: fe37c1 l32r a12, 40003c78 <_c_0x60002e00> -4000439f: f7af92 movi a9, -9 -400043a2: fbe3a1 l32r a10, 40003330 <_c_0x3feffe00> -400043a5: 0020c0 memw -400043a8: 822a82 l32i a8, a10, 0x208 -400043ab: 108890 and a8, a8, a9 -400043ae: 0020c0 memw -400043b1: 826a82 s32i a8, a10, 0x208 -400043b4: f35ef1 l32r a15, 4000112c <_c_0xfffdffff> -400043b7: 0020c0 memw -400043ba: 822ce2 l32i a14, a12, 0x208 -400043bd: 10eef0 and a14, a14, a15 -400043c0: 0020c0 memw -400043c3: 826ce2 s32i a14, a12, 0x208 -400043c6: 0020c0 memw -400043c9: 822a92 l32i a9, a10, 0x208 -400043cc: 1099d0 and a9, a9, a13 -400043cf: 0020c0 memw -400043d2: 826a92 s32i a9, a10, 0x208 -400043d5: 10a082 movi a8, 16 -400043d8: 0020c0 memw -400043db: 822af2 l32i a15, a10, 0x208 -400043de: f03d nop.n -400043e0: 20ff80 or a15, a15, a8 -400043e3: 0020c0 memw -400043e6: 826af2 s32i a15, a10, 0x208 -400043e9: 0020c0 memw -400043ec: 822ae2 l32i a14, a10, 0x208 -400043ef: 0020f0 nop -400043f2: 0eee57 bbsi a14, 5, 40004404 -400043f5: 088076 loop a0, 40004401 -400043f8: 0020c0 memw -400043fb: 822ae2 l32i a14, a10, 0x208 -400043fe: 028eb7 bany a14, a11, 40004404 -40004401: fffc06 j 400043f5 -40004404: 0020c0 memw -40004407: 822a82 l32i a8, a10, 0x208 -4000440a: 1088d0 and a8, a8, a13 -4000440d: 0020c0 memw -40004410: 826a82 s32i a8, a10, 0x208 -40004413: 7f0c movi.n a15, 7 -40004415: 0020c0 memw -40004418: 822ae2 l32i a14, a10, 0x208 -4000441b: 20eef0 or a14, a14, a15 -4000441e: 0020c0 memw -40004421: 826ae2 s32i a14, a10, 0x208 -40004424: fc41b1 l32r a11, 40003528 <_c_0x00020000> -40004427: 0020c0 memw -4000442a: 822c92 l32i a9, a12, 0x208 -4000442d: 2099b0 or a9, a9, a11 -40004430: 0020c0 memw -40004433: 826c92 s32i a9, a12, 0x208 -40004436: 880c movi.n a8, 8 -40004438: 0020c0 memw -4000443b: 822af2 l32i a15, a10, 0x208 -4000443e: 20ff80 or a15, a15, a8 -40004441: 0020c0 memw -40004444: 826af2 s32i a15, a10, 0x208 -40004447: f01d retw.n -40004449: 000000 ill - -4000444c : -4000444c: 004136 entry a1, 32 -4000444f: 052c movi.n a5, 32 -40004451: f76c movi.n a7, -17 -40004453: fe09a1 l32r a10, 40003c78 <_c_0x60002e00> -40004456: 7d7c movi.n a13, -9 -40004458: fbb641 l32r a4, 40003330 <_c_0x3feffe00> -4000445b: 0020c0 memw -4000445e: 8224c2 l32i a12, a4, 0x208 -40004461: 10ccd0 and a12, a12, a13 -40004464: 0020c0 memw -40004467: 8264c2 s32i a12, a4, 0x208 -4000446a: f330b1 l32r a11, 4000112c <_c_0xfffdffff> -4000446d: 0020c0 memw -40004470: 822a92 l32i a9, a10, 0x208 -40004473: 1099b0 and a9, a9, a11 -40004476: 0020c0 memw -40004479: 826a92 s32i a9, a10, 0x208 -4000447c: 0020c0 memw -4000447f: 822482 l32i a8, a4, 0x208 -40004482: 108870 and a8, a8, a7 -40004485: 0020c0 memw -40004488: 826482 s32i a8, a4, 0x208 -4000448b: 061c movi.n a6, 16 -4000448d: 0020c0 memw -40004490: 822432 l32i a3, a4, 0x208 -40004493: 203360 or a3, a3, a6 -40004496: 0020c0 memw -40004499: 826432 s32i a3, a4, 0x208 -4000449c: 0020c0 memw -4000449f: 822422 l32i a2, a4, 0x208 -400044a2: 0ee257 bbsi a2, 5, 400044b4 -400044a5: 088076 loop a0, 400044b1 -400044a8: 0020c0 memw -400044ab: 8224e2 l32i a14, a4, 0x208 -400044ae: 028e57 bany a14, a5, 400044b4 -400044b1: fffc06 j 400044a5 -400044b4: 0020c0 memw -400044b7: 8224f2 l32i a15, a4, 0x208 -400044ba: 10ff70 and a15, a15, a7 -400044bd: 0020c0 memw -400044c0: 8264f2 s32i a15, a4, 0x208 -400044c3: f01d retw.n -400044c5: 000000 ill -400044c8: 006136 entry a1, 48 -400044cb: fe9e21 l32r a2, 40003f44 <_X_SPI_read_status+0x48> -400044ce: 01bd mov.n a11, a1 -400044d0: 0022a2 l32i a10, a2, 0 -400044d3: ffa825 call8 40003f54 <_XX_unk3f54> -400044d6: 2a8c beqz.n a10, 400044dc -400044d8: 120c movi.n a2, 1 -400044da: f01d retw.n -400044dc: 02a8 l32i.n a10, a2, 0 -400044de: ffcde5 call8 400041bc <_X_SPI_write_enable> -400044e1: ff3a56 bnez a10, 400044d8 -400044e4: 02a8 l32i.n a10, a2, 0 -400044e6: 0021b2 l32i a11, a1, 0 -400044e9: ffae65 call8 40003fd0 <_c_0x04000000+0x4> -400044ec: fe8a56 bnez a10, 400044d8 -400044ef: 020c movi.n a2, 0 -400044f1: f01d retw.n -400044f3: 613600 excw -400044f6: 932100 movnez a2, a1, a0 -400044f9: fe .byte 0xfe -400044fa: 01bd mov.n a11, a1 -400044fc: 0022a2 l32i a10, a2, 0 -400044ff: ffa565 call8 40003f54 <_XX_unk3f54> -40004502: 2a8c beqz.n a10, 40004508 -40004504: 120c movi.n a2, 1 -40004506: f01d retw.n -40004508: 02a8 l32i.n a10, a2, 0 -4000450a: ffcb25 call8 400041bc <_X_SPI_write_enable> -4000450d: ff3a56 bnez a10, 40004504 -40004510: 02a8 l32i.n a10, a2, 0 -40004512: 01b8 l32i.n a11, a1, 0 -40004514: cc1c movi.n a12, 28 -40004516: 20bbc0 or a11, a11, a12 -40004519: ffab65 call8 40003fd0 <_c_0x04000000+0x4> -4000451c: fe4a56 bnez a10, 40004504 -4000451f: 020c movi.n a2, 0 -40004521: f01d retw.n -40004523: 9fff00 excw -40004526: 6f .byte 0x6f -40004527: fe .byte 0xfe -40004528: 002000 isync -4000452b: 200001 l32r a0, 3ffcc52c <_start-0x33ad4> -4000452e: 000010 excw -40004531: 008020 any4 b2, b0:b1:b2:b3 -40004534: 006000 rsil a0, 0 - ... - -40004538 <_X_SPIReadModeConfig>: -40004538: 004136 entry a1, 32 -4000453b: fffa91 l32r a9, 40004524 -4000453e: fe81a1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> -40004541: fdcd51 l32r a5, 40003c78 <_c_0x60002e00> -40004544: 0020c0 memw -40004547: 822582 l32i a8, a5, 0x208 -4000454a: f03d nop.n -4000454c: 108890 and a8, a8, a9 -4000454f: 0020c0 memw -40004552: 826582 s32i a8, a5, 0x208 -40004555: c2ac beqz.n a2, 40004585 <_X_SPIReadModeConfig+0x4d> -40004557: 451226 beqi a2, 1, 400045a0 <_X_SPIReadModeConfig+0x68> -4000455a: 4a2226 beqi a2, 2, 400045a8 <_X_SPIReadModeConfig+0x70> -4000455d: 4f3226 beqi a2, 3, 400045b0 <_X_SPIReadModeConfig+0x78> -40004560: 544226 beqi a2, 4, 400045b8 <_X_SPIReadModeConfig+0x80> -40004563: 040c movi.n a4, 0 -40004565: 1f1226 beqi a2, 1, 40004588 <_X_SPIReadModeConfig+0x50> -40004568: 438c beqz.n a3, 40004570 <_X_SPIReadModeConfig+0x38> -4000456a: 002aa2 l32i a10, a10, 0 -4000456d: ffd125 call8 40004280 <_XX_unk4238+0x48> -40004570: 0020c0 memw -40004573: 8225a2 l32i a10, a5, 0x208 -40004576: 20aa40 or a10, a10, a4 -40004579: 0020c0 memw -4000457c: 8265a2 s32i a10, a5, 0x208 -4000457f: 020c movi.n a2, 0 -40004581: f01d retw.n -40004583: 410000 srli a0, a0, 0 -40004586: ffe8 l32i.n a14, a15, 60 -40004588: 0aa8 l32i.n a10, a10, 0 -4000458a: ffcae5 call8 40004238 <_XX_unk4238> -4000458d: 0020c0 memw -40004590: 8225b2 l32i a11, a5, 0x208 -40004593: 20bb40 or a11, a11, a4 -40004596: 0020c0 memw -40004599: 8265b2 s32i a11, a5, 0x208 -4000459c: 020c movi.n a2, 0 -4000459e: f01d retw.n -400045a0: ffe341 l32r a4, 4000452c -400045a3: ffef86 j 40004565 <_X_SPIReadModeConfig+0x2d> -400045a6: 410000 srli a0, a0, 0 -400045a9: 86ffe2 s32ri a14, a15, 0x218 -400045ac: ffed excw -400045ae: 410000 srli a0, a0, 0 -400045b1: 86ffe1 l32r a14, 3ffe61b0 <_start-0x19e50> -400045b4: ffeb addi.n a15, a15, 14 -400045b6: 410000 srli a0, a0, 0 -400045b9: df .byte 0xdf -400045ba: e986f3 excw -400045bd: ff .byte 0xff -400045be: 360000 excw -400045c1: a10041 l32r a4, 3ffec9c4 <_start-0x1363c> -400045c4: a2fe60 muluh a15, a14, a6 -400045c7: 002a add.n a0, a0, a2 -400045c9: ffc3e5 call8 40004208 <_X_Wait_SPI_Idle> -400045cc: ffd6c1 l32r a12, 40004524 -400045cf: fdaaa1 l32r a10, 40003c78 <_c_0x60002e00> -400045d2: 0020c0 memw -400045d5: 822ab2 l32i a11, a10, 0x208 -400045d8: 10bbc0 and a11, a11, a12 -400045db: 0020c0 memw -400045de: 826ab2 s32i a11, a10, 0x208 -400045e1: 32bc beqz.n a2, 40004618 <_X_SPIReadModeConfig+0xe0> -400045e3: 491226 beqi a2, 1, 40004630 <_X_SPIReadModeConfig+0xf8> -400045e6: 5e2226 beqi a2, 2, 40004648 <_X_SPIReadModeConfig+0x110> -400045e9: 733226 beqi a2, 3, 40004660 <_X_SPIReadModeConfig+0x128> -400045ec: 104226 beqi a2, 4, 40004600 <_X_SPIReadModeConfig+0xc8> -400045ef: 0020c0 memw -400045f2: 822ad2 l32i a13, a10, 0x208 -400045f5: 0020c0 memw -400045f8: 826ad2 s32i a13, a10, 0x208 -400045fb: 020c movi.n a2, 0 -400045fd: f01d retw.n -400045ff: cdf100 excw -40004602: 20c0f3 excw -40004605: 2ae200 depbits a0, a2, 2, 15 -40004608: eef082 s32ri a8, a0, 0x3b8 -4000460b: 20c020 or a12, a0, a2 -4000460e: 6ae200 depbits a0, a2, 6, 15 -40004611: 020c82 l8ui a8, a12, 2 -40004614: f01d retw.n -40004616: 910000 srl a0, a0 -40004619: c0ffc4 excw -4000461c: 220020 orb b0, b0, b2 -4000461f: 822a add.n a8, a2, a2 -40004621: 202290 or a2, a2, a9 -40004624: 0020c0 memw -40004627: 826a22 s32i a2, a10, 0x208 -4000462a: 020c movi.n a2, 0 -4000462c: f01d retw.n -4000462e: 910000 srl a0, a0 -40004631: bf .byte 0xbf -40004632: ff .byte 0xff -40004633: 0020c0 memw -40004636: 822a82 l32i a8, a10, 0x208 -40004639: 208890 or a8, a8, a9 -4000463c: 0020c0 memw -4000463f: 826a82 s32i a8, a10, 0x208 -40004642: 020c movi.n a2, 0 -40004644: f01d retw.n -40004646: c10000 mul16u a0, a0, a0 -40004649: ffba add.n a15, a15, a11 -4000464b: 0020c0 memw -4000464e: 822ab2 l32i a11, a10, 0x208 -40004651: 20bbc0 or a11, a11, a12 -40004654: 0020c0 memw -40004657: 826ab2 s32i a11, a10, 0x208 -4000465a: 020c movi.n a2, 0 -4000465c: f01d retw.n -4000465e: 910000 srl a0, a0 -40004661: c0ffb5 call12 3ffc565c <_start-0x3a9a4> -40004664: 220020 orb b0, b0, b2 -40004667: 822a add.n a8, a2, a2 -40004669: 202290 or a2, a2, a9 -4000466c: 0020c0 memw -4000466f: 826a22 s32i a2, a10, 0x208 -40004672: 020c movi.n a2, 0 -40004674: f01d retw.n -40004676: 360000 excw -40004679: a10041 l32r a4, 3ffeca7c <_start-0x13584> -4000467c: a2fe32 s32ri a3, a14, 0x288 -4000467f: 002a add.n a0, a0, a2 -40004681: ffb865 call8 40004208 <_X_Wait_SPI_Idle> -40004684: fd7d91 l32r a9, 40003c78 <_c_0x60002e00> -40004687: 0d22f6 bgeui a2, 2, 40004698 <_X_SPIReadModeConfig+0x160> -4000468a: f02bb1 l32r a11, 40000738 <_c_0x80000000> -4000468d: 0020c0 memw -40004690: 8669b2 s32i a11, a9, 0x218 -40004693: 020c movi.n a2, 0 -40004695: f01d retw.n -40004697: c20b00 quou a0, a11, a0 -4000469a: 11dc40 slli a13, a12, 12 -4000469d: 20ccd0 or a12, a12, a13 -400046a0: 41d120 srli a13, a2, 1 -400046a3: dd0b addi.n a13, a13, -1 -400046a5: 11dda0 slli a13, a13, 6 -400046a8: 20ccd0 or a12, a12, a13 -400046ab: 0020c0 memw -400046ae: 8669c2 s32i a12, a9, 0x218 -400046b1: 020c movi.n a2, 0 -400046b3: f01d retw.n -400046b5: 000000 ill -400046b8: 004136 entry a1, 32 -400046bb: fe2221 l32r a2, 40003f44 <_X_SPI_read_status+0x48> -400046be: f03d nop.n -400046c0: 0022a2 l32i a10, a2, 0 -400046c3: ffafa5 call8 400041bc <_X_SPI_write_enable> -400046c6: 2a8c beqz.n a10, 400046cc <_X_SPIReadModeConfig+0x194> -400046c8: 120c movi.n a2, 1 -400046ca: f01d retw.n -400046cc: 02a8 l32i.n a10, a2, 0 -400046ce: ff5ae5 call8 40003c7c <_X_SPI_chip_erase> -400046d1: ff3a56 bnez a10, 400046c8 <_X_SPIReadModeConfig+0x190> -400046d4: 020c movi.n a2, 0 -400046d6: f01d retw.n -400046d8: 004136 entry a1, 32 -400046db: fe1a31 l32r a3, 40003f44 <_X_SPI_read_status+0x48> -400046de: 03a8 l32i.n a10, a3, 0 -400046e0: 2a98 l32i.n a9, a10, 8 -400046e2: 1a88 l32i.n a8, a10, 4 -400046e4: c28890 quou a8, a8, a9 -400046e7: 063287 bltu a2, a8, 400046f1 <_X_SPIReadModeConfig+0x1b9> -400046ea: 120c movi.n a2, 1 -400046ec: f01d retw.n -400046ee: 000000 ill -400046f1: ffaca5 call8 400041bc <_X_SPI_write_enable> -400046f4: ff2a56 bnez a10, 400046ea <_X_SPIReadModeConfig+0x1b2> -400046f7: 03a8 l32i.n a10, a3, 0 -400046f9: 2ab8 l32i.n a11, a10, 8 -400046fb: 82bb20 mull a11, a11, a2 -400046fe: ff6065 call8 40003d04 <_c_0x00800000+0x4> -40004701: fe5a56 bnez a10, 400046ea <_X_SPIReadModeConfig+0x1b2> -40004704: 020c movi.n a2, 0 -40004706: f01d retw.n - -40004708 : -40004708: 004136 entry a1, 32 -4000470b: fe0e31 l32r a3, 40003f44 <_X_SPI_read_status+0x48> -4000470e: 03a8 l32i.n a10, a3, 0 -40004710: 3a98 l32i.n a9, a10, 12 -40004712: 1a88 l32i.n a8, a10, 4 -40004714: c28890 quou a8, a8, a9 -40004717: 063287 bltu a2, a8, 40004721 -4000471a: 120c movi.n a2, 1 -4000471c: f01d retw.n -4000471e: 000000 ill -40004721: ffa9a5 call8 400041bc <_X_SPI_write_enable> -40004724: ff2a56 bnez a10, 4000471a -40004727: 03a8 l32i.n a10, a3, 0 -40004729: 3ab8 l32i.n a11, a10, 12 -4000472b: 82bb20 mull a11, a11, a2 -4000472e: ff58e5 call8 40003cbc <_XX_unk3cbc> -40004731: fe5a56 bnez a10, 4000471a -40004734: 020c movi.n a2, 0 -40004736: f01d retw.n - -40004738 : -40004738: 006136 entry a1, 48 -4000473b: fe02c1 l32r a12, 40003f44 <_X_SPI_read_status+0x48> -4000473e: 046d mov.n a6, a4 -40004740: 0ca8 l32i.n a10, a12, 0 -40004742: 942a add.n a9, a4, a2 -40004744: 1a88 l32i.n a8, a10, 4 -40004746: 4a78 l32i.n a7, a10, 16 -40004748: 143897 bltu a8, a9, 40004760 -4000474b: e25270 remu a5, a2, a7 -4000474e: c05750 sub a5, a7, a5 -40004751: 0fb457 bgeu a4, a5, 40004764 -40004754: 04dd mov.n a13, a4 -40004756: 03cd mov.n a12, a3 -40004758: 02bd mov.n a11, a2 -4000475a: ff5f65 call8 40003d50 <_c_0x02000000+0x4> -4000475d: 05fa16 beqz a10, 400047c0 -40004760: 120c movi.n a2, 1 -40004762: f01d retw.n -40004764: 02bd mov.n a11, a2 -40004766: 03cd mov.n a12, a3 -40004768: 05dd mov.n a13, a5 -4000476a: ff5e65 call8 40003d50 <_c_0x02000000+0x4> -4000476d: fefa56 bnez a10, 40004760 -40004770: 0129 s32i.n a2, a1, 0 -40004772: 1169 s32i.n a6, a1, 4 -40004774: c04650 sub a4, a6, a5 -40004777: c24470 quou a4, a4, a7 -4000477a: 74ac beqz.n a4, 400047a5 -4000477c: 806520 add a6, a5, a2 -4000477f: 00a022 movi a2, 0 -40004782: 06bd mov.n a11, a6 -40004784: 07dd mov.n a13, a7 -40004786: fdefa1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> -40004789: 41c250 srli a12, a5, 2 -4000478c: a0cc30 addx4 a12, a12, a3 -4000478f: 002aa2 l32i a10, a10, 0 -40004792: ff5be5 call8 40003d50 <_c_0x02000000+0x4> -40004795: fc7a56 bnez a10, 40004760 -40004798: 667a add.n a6, a6, a7 -4000479a: 575a add.n a5, a7, a5 -4000479c: 01c222 addi a2, a2, 1 -4000479f: 742020 extui a2, a2, 0, 8 -400047a2: dc9427 bne a4, a2, 40004782 -400047a5: fde7a1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> -400047a8: 11d8 l32i.n a13, a1, 4 -400047aa: 01b8 l32i.n a11, a1, 0 -400047ac: 41c250 srli a12, a5, 2 -400047af: a0cc30 addx4 a12, a12, a3 -400047b2: bb5a add.n a11, a11, a5 -400047b4: c0dd50 sub a13, a13, a5 -400047b7: 002aa2 l32i a10, a10, 0 -400047ba: ff5965 call8 40003d50 <_c_0x02000000+0x4> -400047bd: f9fa56 bnez a10, 40004760 -400047c0: 020c movi.n a2, 0 -400047c2: f01d retw.n -400047c4: 011000 slli a1, a0, 32 -400047c7: 413600 srli a3, a0, 6 -400047ca: fe4100 excw -400047cd: ff .byte 0xff -400047ce: fad831 l32r a3, 40003330 <_c_0x3feffe00> -400047d1: 0020c0 memw -400047d4: 9a2322 l32i a2, a3, 0x268 -400047d7: 202240 or a2, a2, a4 -400047da: 0020c0 memw -400047dd: 9a6322 s32i a2, a3, 0x268 -400047e0: f01d retw.n -400047e2: 000000 ill -400047e5: ae .byte 0xae -400047e6: 366001 l32r a0, 3ffd2168 <_start-0x2de98> -400047e9: 4d0041 l32r a4, 3ffd7bec <_start-0x28414> -400047ec: f51c03 excw -400047ef: 458527 bany a5, a2, 40004838 -400047f2: fffc61 l32r a6, 400047e4 -400047f5: 0020c0 memw -400047f8: 896622 s32i a2, a6, 0x224 -400047fb: 08a032 movi a3, 8 -400047fe: 00a022 movi a2, 0 -40004801: 0ea376 loopgtz a3, 40004813 -40004804: a03260 addx4 a3, a2, a6 -40004807: 0458 l32i.n a5, a4, 0 -40004809: 0020c0 memw -4000480c: 806352 s32i a5, a3, 0x200 -4000480f: 444b addi.n a4, a4, 4 -40004811: 221b addi.n a2, a2, 1 -40004813: 01a0a2 movi a10, 1 -40004816: 0020c0 memw -40004819: 8866a2 s32i a10, a6, 0x220 -4000481c: 0020c0 memw -4000481f: 882692 l32i a9, a6, 0x220 -40004822: 0ee907 bbsi a9, 0, 40004834 -40004825: 088076 loop a0, 40004831 -40004828: 0020c0 memw -4000482b: 8826b2 l32i a11, a6, 0x220 -4000482e: 02eb07 bbsi a11, 0, 40004834 -40004831: fffc06 j 40004825 -40004834: 020c movi.n a2, 0 -40004836: f01d retw.n -40004838: 120c movi.n a2, 1 -4000483a: f01d retw.n -4000483c: ff .byte 0xff -4000483d: ef .byte 0xef -4000483e: fe .byte 0xfe -4000483f: ff .byte 0xff -40004840: 004136 entry a1, 32 -40004843: fffe41 l32r a4, 4000483c -40004846: faba31 l32r a3, 40003330 <_c_0x3feffe00> -40004849: 0020c0 memw -4000484c: 9a2322 l32i a2, a3, 0x268 -4000484f: 102240 and a2, a2, a4 -40004852: 0020c0 memw -40004855: 9a6322 s32i a2, a3, 0x268 -40004858: f01d retw.n -4000485a: 360000 excw -4000485d: 1c0041 l32r a4, 3ffcb860 <_start-0x347a0> -40004860: 20fa add.n a2, a0, a15 -40004862: cc4480 excw -40004865: 4718 l32i.n a1, a7, 16 -40004867: 030a add.n a0, a3, a0 -40004869: 120c movi.n a2, 1 -4000486b: f01d retw.n -4000486d: fff5a5 call8 400047c8 -40004870: 415540 srli a5, a4, 5 -40004873: a59c beqz.n a5, 40004891 -40004875: 00a042 movi a4, 0 -40004878: 02ad mov.n a10, a2 -4000487a: 11b4d0 slli a11, a4, 3 -4000487d: a0bb30 addx4 a11, a11, a3 -40004880: fff665 call8 400047e8 -40004883: 02ad mov.n a10, a2 -40004885: 03bd mov.n a11, a3 -40004887: 0c2c movi.n a12, 32 -40004889: ffeae5 call8 40004738 -4000488c: 441b addi.n a4, a4, 1 -4000488e: e69457 bne a4, a5, 40004878 -40004891: fffae5 call8 40004840 -40004894: f01d retw.n - ... - -40004898 : -40004898: 004136 entry a1, 32 -4000489b: 04dd mov.n a13, a4 -4000489d: 03cd mov.n a12, a3 -4000489f: fda9a1 l32r a10, 40003f44 <_X_SPI_read_status+0x48> -400048a2: 02bd mov.n a11, a2 -400048a4: 0aa8 l32i.n a10, a10, 0 -400048a6: ff57e5 call8 40003e24 <_XX_unk3e24> -400048a9: 3a8c beqz.n a10, 400048b0 -400048ab: 120c movi.n a2, 1 -400048ad: f01d retw.n -400048af: 020c00 andb b0, b12, b0 -400048b2: f01d retw.n - -400048b4 <_X_SPIEraseArea>: -400048b4: 004136 entry a1, 32 -400048b7: 05a0a2 movi a10, 5 -400048ba: 01a0b2 movi a11, 1 -400048bd: ffc7a5 call8 40004538 <_X_SPIReadModeConfig> -400048c0: fda141 l32r a4, 40003f44 <_X_SPI_read_status+0x48> -400048c3: 04a8 l32i.n a10, a4, 0 -400048c5: 923a add.n a9, a2, a3 -400048c7: 1a88 l32i.n a8, a10, 4 -400048c9: 3ab8 l32i.n a11, a10, 12 -400048cb: 053897 bltu a8, a9, 400048d4 <_X_SPIEraseArea+0x20> -400048ce: e2b2b0 remu a11, a2, a11 -400048d1: 004b16 beqz a11, 400048d9 <_X_SPIEraseArea+0x25> -400048d4: 120c movi.n a2, 1 -400048d6: f01d retw.n -400048d8: bee500 excw -400048db: ff .byte 0xff -400048dc: ff4a56 bnez a10, 400048d4 <_X_SPIEraseArea+0x20> -400048df: 0448 l32i.n a4, a4, 0 -400048e1: 3488 l32i.n a8, a4, 12 -400048e3: 2448 l32i.n a4, a4, 8 -400048e5: c29380 quou a9, a3, a8 -400048e8: e2c380 remu a12, a3, a8 -400048eb: c22280 quou a2, a2, a8 -400048ee: c24480 quou a4, a4, a8 -400048f1: e25240 remu a5, a2, a4 -400048f4: 1c8c beqz.n a12, 400048f9 <_X_SPIEraseArea+0x45> -400048f6: 01c992 addi a9, a9, 1 -400048f9: c05450 sub a5, a4, a5 -400048fc: 435950 min a5, a9, a5 -400048ff: c03950 sub a3, a9, a5 -40004902: e58c beqz.n a5, 40004914 <_X_SPIEraseArea+0x60> -40004904: 02ad mov.n a10, a2 -40004906: ffe025 call8 40004708 -40004909: fc7a56 bnez a10, 400048d4 <_X_SPIEraseArea+0x20> -4000490c: 221b addi.n a2, a2, 1 -4000490e: ffc552 addi a5, a5, -1 -40004911: fef556 bnez a5, 40004904 <_X_SPIEraseArea+0x50> -40004914: 11b437 bgeu a4, a3, 40004929 <_X_SPIEraseArea+0x75> -40004917: c2a240 quou a10, a2, a4 -4000491a: ffdbe5 call8 400046d8 <_X_SPIReadModeConfig+0x1a0> -4000491d: fb3a56 bnez a10, 400048d4 <_X_SPIEraseArea+0x20> -40004920: 802240 add a2, a2, a4 -40004923: c03340 sub a3, a3, a4 -40004926: ed3437 bltu a4, a3, 40004917 <_X_SPIEraseArea+0x63> -40004929: 0f13a6 blti a3, 1, 4000493c <_X_SPIEraseArea+0x88> -4000492c: 02ad mov.n a10, a2 -4000492e: ffdda5 call8 40004708 -40004931: f9fa56 bnez a10, 400048d4 <_X_SPIEraseArea+0x20> -40004934: 221b addi.n a2, a2, 1 -40004936: ffc332 addi a3, a3, -1 -40004939: fef356 bnez a3, 4000492c <_X_SPIEraseArea+0x78> -4000493c: 020c movi.n a2, 0 -4000493e: f01d retw.n - -40004940 <_XX_unk4940>: -40004940: 004136 entry a1, 32 -40004943: fd8081 l32r a8, 40003f44 <_X_SPI_read_status+0x48> -40004946: 0888 l32i.n a8, a8, 0 -40004948: 1839 s32i.n a3, a8, 4 -4000494a: 2849 s32i.n a4, a8, 8 -4000494c: 3859 s32i.n a5, a8, 12 -4000494e: 4869 s32i.n a6, a8, 16 -40004950: 0829 s32i.n a2, a8, 0 -40004952: 5879 s32i.n a7, a8, 20 -40004954: 020c movi.n a2, 0 -40004956: f01d retw.n -40004958: 4bac beqz.n a11, 40004980 <_XX_unk4940+0x40> -4000495a: 884000 excw -4000495d: 600040 neg a0, a4 -40004960: 4128 l32i.n a2, a1, 16 -40004962: 506000 witlb a0, a0 -40004965: ffdc bnez.n a15, 40004988 <_XX_unk4940+0x48> -40004967: 3f .byte 0x3f -40004968: 004136 entry a1, 32 -4000496b: 04a0a2 movi a10, 4 -4000496e: fffab1 l32r a11, 40004958 <_XX_unk4940+0x18> -40004971: 00a0c2 movi a12, 0 -40004974: fc7725 call8 400010e8 <_X_ets_isr_attach> -40004977: 10a0a2 movi a10, 16 -4000497a: fc77e5 call8 400010f8 <_X_ets_isr_mask> -4000497d: 0a0c movi.n a10, 0 -4000497f: 0b0c movi.n a11, 0 -40004981: 0c0c movi.n a12, 0 -40004983: fd7c movi.n a13, -1 -40004985: 000525 call8 400049d8 -40004988: 0a0c movi.n a10, 0 -4000498a: 0b0c movi.n a11, 0 -4000498c: 0c0c movi.n a12, 0 -4000498e: ffa0d2 movi a13, 255 -40004991: 000665 call8 400049f8 -40004994: fff221 l32r a2, 4000495c <_XX_unk4940+0x1c> -40004997: f1e631 l32r a3, 40001130 <_c_0x60003e00> -4000499a: fff151 l32r a5, 40004960 <_XX_unk4940+0x20> -4000499d: fff141 l32r a4, 40004964 <_XX_unk4940+0x24> -400049a0: 080c movi.n a8, 0 -400049a2: 0489 s32i.n a8, a4, 0 -400049a4: 1489 s32i.n a8, a4, 4 -400049a6: 02ad mov.n a10, a2 -400049a8: 0b0c movi.n a11, 0 -400049aa: 000925 call8 40004a3c -400049ad: 0020c0 memw -400049b0: 224b addi.n a2, a2, 4 -400049b2: 441b addi.n a4, a4, 1 -400049b4: a22392 l32i a9, a3, 0x288 -400049b7: 334b addi.n a3, a3, 4 -400049b9: 249790 extui a9, a9, 7, 3 -400049bc: 0f4492 s8i a9, a4, 15 -400049bf: e39257 bne a2, a5, 400049a6 <_XX_unk4940+0x66> -400049c2: fc7c movi.n a12, -1 -400049c4: f1dbb1 l32r a11, 40001130 <_c_0x60003e00> -400049c7: 0020c0 memw -400049ca: 936bc2 s32i a12, a11, 0x24c -400049cd: ffa0a2 movi a10, 255 -400049d0: 0020c0 memw -400049d3: 966ba2 s32i a10, a11, 0x258 -400049d6: f01d retw.n - -400049d8 : -400049d8: 004136 entry a1, 32 -400049db: f1d561 l32r a6, 40001130 <_c_0x60003e00> -400049de: 0020c0 memw -400049e1: 826622 s32i a2, a6, 0x208 -400049e4: 0020c0 memw -400049e7: 836632 s32i a3, a6, 0x20c -400049ea: 0020c0 memw -400049ed: 896642 s32i a4, a6, 0x224 -400049f0: 0020c0 memw -400049f3: 8a6652 s32i a5, a6, 0x228 -400049f6: f01d retw.n - -400049f8 : -400049f8: 004136 entry a1, 32 -400049fb: f1cd61 l32r a6, 40001130 <_c_0x60003e00> -400049fe: 0020c0 memw -40004a01: 856622 s32i a2, a6, 0x214 -40004a04: 0020c0 memw -40004a07: 866632 s32i a3, a6, 0x218 -40004a0a: 0020c0 memw -40004a0d: 8c6642 s32i a4, a6, 0x230 -40004a10: 0020c0 memw -40004a13: 8d6652 s32i a5, a6, 0x234 -40004a16: f01d retw.n -40004a18: 004136 entry a1, 32 -40004a1b: f1c521 l32r a2, 40001130 <_c_0x60003e00> -40004a1e: 0020c0 memw -40004a21: 8f2222 l32i a2, a2, 0x23c -40004a24: f01d retw.n -40004a26: 360000 excw -40004a29: 210041 l32r a4, 3ffcce2c <_start-0x331d4> -40004a2c: c0f1c1 l32r a12, 3fff4df0 <_start-0xb210> -40004a2f: 220020 orb b0, b0, b2 -40004a32: 1d9022 l16si a2, a0, 58 -40004a35: 0000f0 callx12 a0 -40004a38: ffdc60 excw -40004a3b: 3f .byte 0x3f -40004a3c: 004136 entry a1, 32 -40004a3f: fc66a5 call8 400010a8 <_X_ets_enter_critical> -40004a42: ffc6a1 l32r a10, 4000495c <_XX_unk4940+0x1c> -40004a45: 1192e0 slli a9, a2, 2 -40004a48: 8099a0 add a9, a9, a10 -40004a4b: 0020c0 memw -40004a4e: 006932 s32i a3, a9, 0 -40004a51: 27a082 movi a8, 39 -40004a54: 3e3827 bltu a8, a2, 40004a96 -40004a57: fff8a1 l32r a10, 40004a38 -40004a5a: 24b730 extui a11, a3, 7, 3 -40004a5d: c2aa add.n a12, a2, a10 -40004a5f: 004cb2 s8i a11, a12, 0 -40004a62: 0bfc bnez.n a11, 40004a96 -40004a64: 1c0c movi.n a12, 1 -40004a66: fb7c movi.n a11, -1 -40004a68: f0caa2 addi a10, a10, -16 -40004a6b: 12c2f6 bgeui a2, 32, 40004a81 -40004a6e: 0ad8 l32i.n a13, a10, 0 -40004a70: 401200 ssl a2 -40004a73: a1ec00 sll a14, a12 -40004a76: 30eeb0 xor a14, a14, a11 -40004a79: 10dde0 and a13, a13, a14 -40004a7c: 0ad9 s32i.n a13, a10, 0 -40004a7e: 000506 j 40004a96 -40004a81: 012af2 l32i a15, a10, 4 -40004a84: e0c292 addi a9, a2, -32 -40004a87: 401900 ssl a9 -40004a8a: a18c00 sll a8, a12 -40004a8d: 3088b0 xor a8, a8, a11 -40004a90: 10ff80 and a15, a15, a8 -40004a93: 016af2 s32i a15, a10, 4 -40004a96: fc6265 call8 400010bc <_X_ets_exit_critical> -40004a99: 000090 retw -40004a9c: 004136 entry a1, 32 -40004a9f: ffaf81 l32r a8, 4000495c <_XX_unk4940+0x1c> -40004aa2: 1152e0 slli a5, a2, 2 -40004aa5: 558a add.n a5, a5, a8 -40004aa7: 0020c0 memw -40004aaa: 0558 l32i.n a5, a5, 0 -40004aac: 732c movi.n a3, 39 -40004aae: 163327 bltu a3, a2, 40004ac8 -40004ab1: ffe131 l32r a3, 40004a38 -40004ab4: 223a add.n a2, a2, a3 -40004ab6: 000222 l8ui a2, a2, 0 -40004ab9: 7fac32 movi a3, 0xfffffc7f -40004abc: 103530 and a3, a5, a3 -40004abf: 112290 slli a2, a2, 7 -40004ac2: 202230 or a2, a2, a3 -40004ac5: f01d retw.n -40004ac7: 052d00 extui a2, a0, 29, 1 -40004aca: f01d retw.n -40004acc: 004136 entry a1, 32 -40004acf: ffa521 l32r a2, 40004964 <_XX_unk4940+0x24> -40004ad2: 0228 l32i.n a2, a2, 0 -40004ad4: f01d retw.n -40004ad6: 360000 excw -40004ad9: 210041 l32r a4, 3ffccedc <_start-0x33124> -40004adc: 28ffa2 s32ri a10, a15, 160 -40004adf: f01d12 l16ui a1, a13, 0x1e0 -40004ae2: 360000 excw -40004ae5: 250041 l32r a4, 3ffcdee8 <_start-0x32118> -40004ae8: fc5c movi.n a12, 95 -40004aea: f19191 l32r a9, 40001130 <_c_0x60003e00> -40004aed: a09290 addx4 a9, a2, a9 -40004af0: 0020c0 memw -40004af3: 7facb2 movi a11, 0xfffffc7f -40004af6: a22982 l32i a8, a9, 0x288 -40004af9: 11a390 slli a10, a3, 7 -40004afc: 1088b0 and a8, a8, a11 -40004aff: 2088a0 or a8, a8, a10 -40004b02: 0020c0 memw -40004b05: a26982 s32i a8, a9, 0x288 -40004b08: fc5b25 call8 400010bc <_X_ets_exit_critical> -40004b0b: f01d retw.n -40004b0d: 000000 ill -40004b10: 004136 entry a1, 32 -40004b13: fc5965 call8 400010a8 <_X_ets_enter_critical> -40004b16: ff93b1 l32r a11, 40004964 <_XX_unk4940+0x24> -40004b19: ffaf92 movi a9, -1 -40004b1c: 002b82 l32i a8, a11, 0 -40004b1f: 309290 xor a9, a2, a9 -40004b22: 10a890 and a10, a8, a9 -40004b25: 006ba2 s32i a10, a11, 0 -40004b28: 0c0897 bnone a8, a9, 40004b38 -40004b2b: f181d1 l32r a13, 40001130 <_c_0x60003e00> -40004b2e: 0020c0 memw -40004b31: 926da2 s32i a10, a13, 0x248 -40004b34: 0c0c movi.n a12, 0 -40004b36: 0bc9 s32i.n a12, a11, 0 -40004b38: ffc041 l32r a4, 40004a38 -40004b3b: 030c movi.n a3, 0 -40004b3d: 075237 bbc a2, a3, 40004b48 -40004b40: 03ad mov.n a10, a3 -40004b42: 0004b2 l8ui a11, a4, 0 -40004b45: fff9e5 call8 40004ae4 -40004b48: 01c442 addi a4, a4, 1 -40004b4b: 01c332 addi a3, a3, 1 -40004b4e: ebc366 bnei a3, 32, 40004b3d -40004b51: fc56a5 call8 400010bc <_X_ets_exit_critical> -40004b54: 000090 retw -40004b57: 413600 srli a3, a0, 6 -40004b5a: 54e500 extui a14, a0, 5, 6 -40004b5d: b1fc bnez.n a1, 40004b9c -40004b5f: 92ff81 l32r a8, 3ffe975c <_start-0x168a4> -40004b62: af .byte 0xaf -40004b63: ff .byte 0xff -40004b64: 012b82 l32i a8, a11, 4 -40004b67: 309290 xor a9, a2, a9 -40004b6a: 10a890 and a10, a8, a9 -40004b6d: 016ba2 s32i a10, a11, 4 -40004b70: 0c0897 bnone a8, a9, 40004b80 -40004b73: f16fd1 l32r a13, 40001130 <_c_0x60003e00> -40004b76: 0020c0 memw -40004b79: 956da2 s32i a10, a13, 0x254 -40004b7c: 0c0c movi.n a12, 0 -40004b7e: 1bc9 s32i.n a12, a11, 4 -40004b80: ffae41 l32r a4, 40004a38 -40004b83: 030c movi.n a3, 0 -40004b85: 085237 bbc a2, a3, 40004b91 -40004b88: 20c3a2 addi a10, a3, 32 -40004b8b: 2004b2 l8ui a11, a4, 32 -40004b8e: fff565 call8 40004ae4 -40004b91: 01c442 addi a4, a4, 1 -40004b94: 01c332 addi a3, a3, 1 -40004b97: ea8366 bnei a3, 8, 40004b85 -40004b9a: fc5225 call8 400010bc <_X_ets_exit_critical> -40004b9d: 000090 retw -40004ba0: 004136 entry a1, 32 -40004ba3: ff7041 l32r a4, 40004964 <_XX_unk4940+0x24> -40004ba6: 3439 s32i.n a3, a4, 12 -40004ba8: 2429 s32i.n a2, a4, 8 -40004baa: f01d retw.n -40004bac: 004136 entry a1, 32 -40004baf: f16051 l32r a5, 40001130 <_c_0x60003e00> -40004bb2: 0020c0 memw -40004bb5: 912532 l32i a3, a5, 0x244 -40004bb8: 0020c0 memw -40004bbb: 942522 l32i a2, a5, 0x250 -40004bbe: 040c movi.n a4, 0 -40004bc0: 065347 bbc a3, a4, 40004bca -40004bc3: 04ad mov.n a10, a4 -40004bc5: 0b0c movi.n a11, 0 -40004bc7: fff1e5 call8 40004ae4 -40004bca: 441b addi.n a4, a4, 1 -40004bcc: f0c466 bnei a4, 32, 40004bc0 -40004bcf: ff6561 l32r a6, 40004964 <_XX_unk4940+0x24> -40004bd2: 0020c0 memw -40004bd5: 936532 s32i a3, a5, 0x24c -40004bd8: 0698 l32i.n a9, a6, 0 -40004bda: 89ac beqz.n a9, 40004c06 -40004bdc: 208390 or a8, a3, a9 -40004bdf: 006682 s32i a8, a6, 0 -40004be2: 040c movi.n a4, 0 -40004be4: 085247 bbc a2, a4, 40004bf0 -40004be7: 20c4a2 addi a10, a4, 32 -40004bea: 00a0b2 movi a11, 0 -40004bed: ffef65 call8 40004ae4 -40004bf0: 441b addi.n a4, a4, 1 -40004bf2: ee8466 bnei a4, 8, 40004be4 -40004bf5: 0020c0 memw -40004bf8: 966522 s32i a2, a5, 0x258 -40004bfb: 1698 l32i.n a9, a6, 4 -40004bfd: 499c beqz.n a9, 40004c15 -40004bff: 20a290 or a10, a2, a9 -40004c02: 16a9 s32i.n a10, a6, 4 -40004c04: f01d retw.n -40004c06: 03ad mov.n a10, a3 -40004c08: 0639 s32i.n a3, a6, 0 -40004c0a: 2688 l32i.n a8, a6, 8 -40004c0c: 36b8 l32i.n a11, a6, 12 -40004c0e: 0008e0 callx8 a8 -40004c11: fff346 j 40004be2 -40004c14: 02ad00 andb b10, b13, b0 -40004c17: 1629 s32i.n a2, a6, 4 -40004c19: 2688 l32i.n a8, a6, 8 -40004c1b: 36b8 l32i.n a11, a6, 12 -40004c1d: 0008e0 callx8 a8 -40004c20: f01d retw.n -40004c22: 360000 excw -40004c25: 250041 l32r a4, 3ffce028 <_start-0x31fd8> -40004c28: fc48 l32i.n a4, a12, 60 -40004c2a: f14191 l32r a9, 40001130 <_c_0x60003e00> -40004c2d: a09290 addx4 a9, a2, a9 -40004c30: 0020c0 memw -40004c33: 7facb2 movi a11, 0xfffffc7f -40004c36: a22982 l32i a8, a9, 0x288 -40004c39: 11a390 slli a10, a3, 7 -40004c3c: 1088b0 and a8, a8, a11 -40004c3f: 2088a0 or a8, a8, a10 -40004c42: 00a4a2 movi a10, 0x400 -40004c45: 2088a0 or a8, a8, a10 -40004c48: 0020c0 memw -40004c4b: a26982 s32i a8, a9, 0x288 -40004c4e: fc46e5 call8 400010bc <_X_ets_exit_critical> -40004c51: f01d retw.n -40004c53: 413600 srli a3, a0, 6 -40004c56: 030c00 rsr.scompare1 a0 -40004c59: 7fa872 movi a7, 0xfffff87f -40004c5c: f13541 l32r a4, 40001130 <_c_0x60003e00> -40004c5f: 822c movi.n a2, 40 -40004c61: 19a276 loopgtz a2, 40004c7e -40004c64: a06340 addx4 a6, a3, a4 -40004c67: 0020c0 memw -40004c6a: a22652 l32i a5, a6, 0x288 -40004c6d: 0865a7 bbci a5, 10, 40004c79 -40004c70: 108570 and a8, a5, a7 -40004c73: 0020c0 memw -40004c76: a26682 s32i a8, a6, 0x288 -40004c79: 331b addi.n a3, a3, 1 -40004c7b: 743030 extui a3, a3, 0, 8 -40004c7e: f01d retw.n - -40004c80 <_c_0xbfffffff>: -40004c80: ff .byte 0xff -40004c81: ff .byte 0xff -40004c82: ff .byte 0xff -40004c83: bf .byte 0xbf -40004c84: fff000 excw -40004c87: ff .byte 0xff - -40004c88 <_c_0xff000fff>: -40004c88: ff .byte 0xff -40004c89: 0f .byte 0xf -40004c8a: 36ff00 excw - -40004c8c : -40004c8c: 006136 entry a1, 48 -40004c8f: b4f060 extui a15, a6, 0, 12 -40004c92: 00a092 movi a9, 0 -40004c95: fffae1 l32r a14, 40004c80 <_c_0xbfffffff> -40004c98: eebad1 l32r a13, 40000780 <_c_0x7fffffff> -40004c9b: eea7c1 l32r a12, 40000738 <_c_0x80000000> -40004c9e: f70db1 l32r a11, 400028d4 <_c_0xdfffffff> -40004ca1: 036172 s32i a7, a1, 12 -40004ca4: fff8a1 l32r a10, 40004c84 <_c_0xbfffffff+0x4> -40004ca7: c27560 quou a7, a5, a6 -40004caa: 205110 or a5, a1, a1 -40004cad: 628076 loop a0, 40004d13 -40004cb0: 1249 s32i.n a4, a2, 4 -40004cb2: 0020c0 memw -40004cb5: 0238 l32i.n a3, a2, 0 -40004cb7: 1033a0 and a3, a3, a10 -40004cba: 2033f0 or a3, a3, a15 -40004cbd: 0020c0 memw -40004cc0: 0239 s32i.n a3, a2, 0 -40004cc2: 0020c0 memw -40004cc5: 0288 l32i.n a8, a2, 0 -40004cc7: 1088d0 and a8, a8, a13 -40004cca: 2088c0 or a8, a8, a12 -40004ccd: 0020c0 memw -40004cd0: 3138 l32i.n a3, a1, 12 -40004cd2: 0289 s32i.n a8, a2, 0 -40004cd4: 040316 beqz a3, 40004d18 -40004cd7: 0020c0 memw -40004cda: 0288 l32i.n a8, a2, 0 -40004cdc: 0020c0 memw -40004cdf: 0238 l32i.n a3, a2, 0 -40004ce1: b43030 extui a3, a3, 0, 12 -40004ce4: cab380 depbits a8, a3, 12, 12 -40004ce7: 0020c0 memw -40004cea: 0289 s32i.n a8, a2, 0 -40004cec: 0020c0 memw -40004cef: 0288 l32i.n a8, a2, 0 -40004cf1: 1088e0 and a8, a8, a14 -40004cf4: 0020c0 memw -40004cf7: 0289 s32i.n a8, a2, 0 -40004cf9: 0020c0 memw -40004cfc: 0288 l32i.n a8, a2, 0 -40004cfe: 1088b0 and a8, a8, a11 -40004d01: 0020c0 memw -40004d04: 464a add.n a4, a6, a4 -40004d06: 991b addi.n a9, a9, 1 -40004d08: 0289 s32i.n a8, a2, 0 -40004d0a: 2529 s32i.n a2, a5, 8 -40004d0c: 2558 l32i.n a5, a5, 8 -40004d0e: 22cb addi.n a2, a2, 12 -40004d10: 18b977 bgeu a9, a7, 40004d2c -40004d13: ffe586 j 40004cad -40004d16: c00000 sub a0, a0, a0 -40004d19: 810020 src a0, a0, a2 -40004d1c: ffdb addi.n a15, a15, 13 -40004d1e: 0238 l32i.n a3, a2, 0 -40004d20: 103380 and a3, a3, a8 -40004d23: 0020c0 memw -40004d26: 0239 s32i.n a3, a2, 0 -40004d28: fff006 j 40004cec -40004d2b: d19800 mul16s a9, a8, a0 -40004d2e: c1b8 l32i.n a11, a1, 48 -40004d30: 0a0c movi.n a10, 0 -40004d32: 25a9 s32i.n a10, a5, 8 -40004d34: 21a8 l32i.n a10, a1, 8 -40004d36: 0ba9 s32i.n a10, a11, 0 -40004d38: 0959 s32i.n a5, a9, 0 -40004d3a: f01d retw.n -40004d3c: 004136 entry a1, 32 -40004d3f: ffc332 addi a3, a3, -1 -40004d42: f43030 extui a3, a3, 0, 16 -40004d45: 019376 loopnez a3, 40004d4a -40004d48: 2228 l32i.n a2, a2, 8 -40004d4a: f01d retw.n -40004d4c: 004136 entry a1, 32 -40004d4f: 015410 slli a5, a4, 31 -40004d52: ee8b81 l32r a8, 40000780 <_c_0x7fffffff> -40004d55: 0020c0 memw -40004d58: 0268 l32i.n a6, a2, 0 -40004d5a: fb0460 depbits a6, a4, 31, 1 -40004d5d: 084d mov.n a4, a8 -40004d5f: 0020c0 memw -40004d62: 0269 s32i.n a6, a2, 0 -40004d64: 330b addi.n a3, a3, -1 -40004d66: f43030 extui a3, a3, 0, 16 -40004d69: 119376 loopnez a3, 40004d7e -40004d6c: 2228 l32i.n a2, a2, 8 -40004d6e: 0020c0 memw -40004d71: 0238 l32i.n a3, a2, 0 -40004d73: 103340 and a3, a3, a4 -40004d76: 203350 or a3, a3, a5 -40004d79: 0020c0 memw -40004d7c: 0239 s32i.n a3, a2, 0 -40004d7e: f01d retw.n - -40004d80 <_st_0x3fffdc90>: -40004d80: ffdc90 excw -40004d83: 3f .byte 0x3f -40004d84: ffdd50 excw -40004d87: 3f .byte 0x3f - -40004d88 <_XX_unk4d88>: -40004d88: 008136 entry a1, 64 -40004d8b: fffec1 l32r a12, 40004d84 <_st_0x3fffdc90+0x4> -40004d8e: 00a2d2 movi a13, 0x200 -40004d91: 14c182 addi a8, a1, 20 -40004d94: 10c192 addi a9, a1, 16 -40004d97: fffa21 l32r a2, 40004d80 <_st_0x3fffdc90> -40004d9a: 00a0f2 movi a15, 0 -40004d9d: 40a0b2 movi a11, 64 -40004da0: 20ebb0 or a14, a11, a11 -40004da3: 22f9 s32i.n a15, a2, 8 -40004da5: 0052b2 s16i a11, a2, 0 -40004da8: a28b addi.n a10, a2, 8 -40004daa: 32a9 s32i.n a10, a2, 12 -40004dac: 60a0b2 movi a11, 96 -40004daf: 60c2a2 addi a10, a2, 96 -40004db2: 0199 s32i.n a9, a1, 0 -40004db4: 1189 s32i.n a8, a1, 4 -40004db6: ffed65 call8 40004c8c -40004db9: 51c8 l32i.n a12, a1, 20 -40004dbb: 41d8 l32i.n a13, a1, 16 -40004dbd: 22d9 s32i.n a13, a2, 8 -40004dbf: cc8b addi.n a12, a12, 8 -40004dc1: 32c9 s32i.n a12, a2, 12 -40004dc3: f01d retw.n -40004dc5: 000000 ill -40004dc8: dc98 l32i.n a9, a12, 52 -40004dca: ff .byte 0xff -40004dcb: 3f .byte 0x3f -40004dcc: 004136 entry a1, 32 -40004dcf: ffec51 l32r a5, 40004d80 <_st_0x3fffdc90> -40004dd2: 2588 l32i.n a8, a5, 8 -40004dd4: 058816 beqz a8, 40004e30 <_XX_unk4d88+0xa8> -40004dd7: fc2d25 call8 400010a8 <_X_ets_enter_critical> -40004dda: 2548 l32i.n a4, a5, 8 -40004ddc: 2498 l32i.n a9, a4, 8 -40004dde: 026592 s32i a9, a5, 8 -40004de1: 04f916 beqz a9, 40004e34 <_XX_unk4d88+0xac> -40004de4: fc2d65 call8 400010bc <_X_ets_exit_critical> -40004de7: 00a0a2 movi a10, 0 -40004dea: 0ca0b2 movi a11, 12 -40004ded: 24a9 s32i.n a10, a4, 8 -40004def: 14a8 l32i.n a10, a4, 4 -40004df1: fdc625 call8 40002a54 -40004df4: f0a0f2 movi a15, 240 -40004df7: 14d8 l32i.n a13, a4, 4 -40004df9: 22cb addi.n a2, a2, 12 -40004dfb: 418820 srli a8, a2, 8 -40004dfe: 034d82 s8i a8, a13, 3 -40004e01: 024d22 s8i a2, a13, 2 -40004e04: 000de2 l8ui a14, a13, 0 -40004e07: 014d32 s8i a3, a13, 1 -40004e0a: 10eef0 and a14, a14, a15 -40004e0d: 004de2 s8i a14, a13, 0 -40004e10: 0020c0 memw -40004e13: dd2b addi.n a13, a13, 2 -40004e15: 04b8 l32i.n a11, a4, 0 -40004e17: 010dc2 l8ui a12, a13, 1 -40004e1a: 000dd2 l8ui a13, a13, 0 -40004e1d: 11cc80 slli a12, a12, 8 -40004e20: 20ccd0 or a12, a12, a13 -40004e23: cabcb0 depbits a11, a12, 12, 12 -40004e26: 0020c0 memw -40004e29: 04b9 s32i.n a11, a4, 0 -40004e2b: 042d mov.n a2, a4 -40004e2d: f01d retw.n -40004e2f: 020c00 andb b0, b12, b0 -40004e32: f01d retw.n -40004e34: ffe591 l32r a9, 40004dc8 <_XX_unk4d88+0x40> -40004e37: 3599 s32i.n a9, a5, 12 -40004e39: ffe9c6 j 40004de4 <_XX_unk4d88+0x5c> -40004e3c: 004136 entry a1, 32 -40004e3f: ee5081 l32r a8, 40000780 <_c_0x7fffffff> -40004e42: ee3df1 l32r a15, 40000738 <_c_0x80000000> -40004e45: 00a032 movi a3, 0 -40004e48: 0020c0 memw -40004e4b: 0022e2 l32i a14, a2, 0 -40004e4e: 10ee80 and a14, a14, a8 -40004e51: 20eef0 or a14, a14, a15 -40004e54: 0020c0 memw -40004e57: 02e9 s32i.n a14, a2, 0 -40004e59: ff89d1 l32r a13, 40004c80 <_c_0xbfffffff> -40004e5c: 0020c0 memw -40004e5f: 02c8 l32i.n a12, a2, 0 -40004e61: 10ccd0 and a12, a12, a13 -40004e64: 0020c0 memw -40004e67: 02c9 s32i.n a12, a2, 0 -40004e69: f69ab1 l32r a11, 400028d4 <_c_0xdfffffff> -40004e6c: 0020c0 memw -40004e6f: 02a8 l32i.n a10, a2, 0 -40004e71: 10aab0 and a10, a10, a11 -40004e74: 0020c0 memw -40004e77: 02a9 s32i.n a10, a2, 0 -40004e79: ff8391 l32r a9, 40004c88 <_c_0xff000fff> -40004e7c: 0020c0 memw -40004e7f: 0288 l32i.n a8, a2, 0 -40004e81: 108890 and a8, a8, a9 -40004e84: 0020c0 memw -40004e87: 0289 s32i.n a8, a2, 0 -40004e89: fc21e5 call8 400010a8 <_X_ets_enter_critical> -40004e8c: ffbda1 l32r a10, 40004d80 <_st_0x3fffdc90> -40004e8f: 2239 s32i.n a3, a2, 8 -40004e91: 3ab8 l32i.n a11, a10, 12 -40004e93: 928b addi.n a9, a2, 8 -40004e95: 0b29 s32i.n a2, a11, 0 -40004e97: 3a99 s32i.n a9, a10, 12 -40004e99: fc2225 call8 400010bc <_X_ets_exit_critical> -40004e9c: f01d retw.n -40004e9e: 360000 excw -40004ea1: cd0041 l32r a4, 3fff82a4 <_start-0x7d5c> -40004ea4: 03bd04 excw -40004ea7: 02ad mov.n a10, a2 -40004ea9: 209220 or a9, a2, a2 -40004eac: 05c216 beqz a2, 40004f0c <_XX_unk4d88+0x184> -40004eaf: ff7631 l32r a3, 40004c88 <_c_0xff000fff> -40004eb2: ff7321 l32r a2, 40004c80 <_c_0xbfffffff> -40004eb5: ee32f1 l32r a15, 40000780 <_c_0x7fffffff> -40004eb8: ee20e1 l32r a14, 40000738 <_c_0x80000000> -40004ebb: f686d1 l32r a13, 400028d4 <_c_0xdfffffff> -40004ebe: 00a042 movi a4, 0 -40004ec1: 438076 loop a0, 40004f08 <_XX_unk4d88+0x180> -40004ec4: 0020c0 memw -40004ec7: 0988 l32i.n a8, a9, 0 -40004ec9: 1088f0 and a8, a8, a15 -40004ecc: 2088e0 or a8, a8, a14 -40004ecf: 0020c0 memw -40004ed2: 0989 s32i.n a8, a9, 0 -40004ed4: 0020c0 memw -40004ed7: 0988 l32i.n a8, a9, 0 -40004ed9: 108820 and a8, a8, a2 -40004edc: 208840 or a8, a8, a4 -40004edf: 0020c0 memw -40004ee2: 0989 s32i.n a8, a9, 0 -40004ee4: 0020c0 memw -40004ee7: 0988 l32i.n a8, a9, 0 -40004ee9: 1088d0 and a8, a8, a13 -40004eec: 208840 or a8, a8, a4 -40004eef: 0020c0 memw -40004ef2: 0989 s32i.n a8, a9, 0 -40004ef4: 0020c0 memw -40004ef7: 0988 l32i.n a8, a9, 0 -40004ef9: 108830 and a8, a8, a3 -40004efc: 208840 or a8, a8, a4 -40004eff: 0020c0 memw -40004f02: 0989 s32i.n a8, a9, 0 -40004f04: 2998 l32i.n a9, a9, 8 -40004f06: 298c beqz.n a9, 40004f0c <_XX_unk4d88+0x184> -40004f08: ffed46 j 40004ec1 <_XX_unk4d88+0x139> -40004f0b: 8aa500 depbits a0, a5, 8, 11 -40004f0e: f01d00 subx8 a1, a13, a0 -40004f11: 000000 ill - -40004f14 <_XX_unk4f14>: -40004f14: 004136 entry a1, 32 -40004f17: 20b220 or a11, a2, a2 -40004f1a: ee1991 l32r a9, 40000780 <_c_0x7fffffff> -40004f1d: ee0681 l32r a8, 40000738 <_c_0x80000000> -40004f20: 0020c0 memw -40004f23: 0022f2 l32i a15, a2, 0 -40004f26: 10ff90 and a15, a15, a9 -40004f29: 20ff80 or a15, a15, a8 -40004f2c: 0020c0 memw -40004f2f: 0062f2 s32i a15, a2, 0 -40004f32: ff53e1 l32r a14, 40004c80 <_c_0xbfffffff> -40004f35: 0020c0 memw -40004f38: 02d8 l32i.n a13, a2, 0 -40004f3a: 10dde0 and a13, a13, a14 -40004f3d: 0020c0 memw -40004f40: 02d9 s32i.n a13, a2, 0 -40004f42: f664c1 l32r a12, 400028d4 <_c_0xdfffffff> -40004f45: 0020c0 memw -40004f48: 02a8 l32i.n a10, a2, 0 -40004f4a: 10aac0 and a10, a10, a12 -40004f4d: 0020c0 memw -40004f50: 02a9 s32i.n a10, a2, 0 -40004f52: ff4d91 l32r a9, 40004c88 <_c_0xff000fff> -40004f55: 02ad mov.n a10, a2 -40004f57: 0020c0 memw -40004f5a: 0288 l32i.n a8, a2, 0 -40004f5c: 108890 and a8, a8, a9 -40004f5f: 0020c0 memw -40004f62: 1c0c movi.n a12, 1 -40004f64: 0289 s32i.n a8, a2, 0 -40004f66: 008525 call8 400057b8 <_XX_unk57b8> -40004f69: f01d retw.n - ... - -40004f6c <_XX_unk4f6c>: -40004f6c: 006136 entry a1, 48 -40004f6f: 20a110 or a10, a1, a1 -40004f72: 04c1b2 addi a11, a1, 4 -40004f75: 0087e5 call8 400057f4 <_XX_unk57f4> -40004f78: 0021c2 l32i a12, a1, 0 -40004f7b: ff81a1 l32r a10, 40004d80 <_st_0x3fffdc90> -40004f7e: ac9c beqz.n a12, 40004f9c <_XX_unk4f6c+0x30> -40004f80: 6a98 l32i.n a9, a10, 24 -40004f82: 698c beqz.n a9, 40004f8c <_XX_unk4f6c+0x20> -40004f84: 29c9 s32i.n a12, a9, 8 -40004f86: 1188 l32i.n a8, a1, 4 -40004f88: 6a89 s32i.n a8, a10, 24 -40004f8a: f01d retw.n -40004f8c: 00a0b2 movi a11, 0 -40004f8f: 012192 l32i a9, a1, 4 -40004f92: 056ac2 s32i a12, a10, 20 -40004f95: 6a99 s32i.n a9, a10, 24 -40004f97: da1c movi.n a10, 29 -40004f99: fbffe5 call8 40000f98 <_XX_unk0f96> -40004f9c: f01d retw.n -40004f9e: 360000 excw -40004fa1: 410041 l32r a4, 3ffd53a4 <_start-0x2ac5c> -40004fa4: 32ff77 bbsi a15, 23, 40004fda <_XX_unk4fc8+0x12> -40004fa7: 201424 excw -40004faa: 329332 l16si a3, a3, 100 -40004fad: 1d1464 excw -40004fb0: 0000f0 callx12 a0 -40004fb3: 413600 srli a3, a0, 6 -40004fb6: 724100 excw -40004fb9: ff .byte 0xff -40004fba: 152432 l32i a3, a4, 84 -40004fbd: 933220 movnez a3, a2, a2 -40004fc0: 156432 s32i a3, a4, 84 -40004fc3: f01d retw.n -40004fc5: 000000 ill - -40004fc8 <_XX_unk4fc8>: -40004fc8: 006136 entry a1, 48 -40004fcb: 01ad mov.n a10, a1 -40004fcd: b14b addi.n a11, a1, 4 -40004fcf: 0087a5 call8 40005848 <_XX_unk5848> -40004fd2: 2e0a26 beqi a10, -1, 40005004 <_XX_unk4fc8+0x3c> -40004fd5: ff6aa1 l32r a10, 40004d80 <_st_0x3fffdc90> -40004fd8: 01c8 l32i.n a12, a1, 0 -40004fda: 162ad2 l32i a13, a10, 88 -40004fdd: 8a98 l32i.n a9, a10, 32 -40004fdf: 7d8c beqz.n a13, 40004fea <_XX_unk4fc8+0x22> -40004fe1: 0cad mov.n a10, a12 -40004fe3: 11b8 l32i.n a11, a1, 4 -40004fe5: 000de0 callx8 a13 -40004fe8: f01d retw.n -40004fea: 698c beqz.n a9, 40004ff4 <_XX_unk4fc8+0x2c> -40004fec: 29c9 s32i.n a12, a9, 8 -40004fee: 1188 l32i.n a8, a1, 4 -40004ff0: 8a89 s32i.n a8, a10, 32 -40004ff2: f01d retw.n -40004ff4: 02a0b2 movi a11, 2 -40004ff7: 012192 l32i a9, a1, 4 -40004ffa: 076ac2 s32i a12, a10, 28 -40004ffd: 8a99 s32i.n a9, a10, 32 -40004fff: da1c movi.n a10, 29 -40005001: fbf965 call8 40000f98 <_XX_unk0f96> -40005004: f01d retw.n - ... - -40005008 <_s_no_rds>: -40005008: ffcce4 excw -4000500b: 3f .byte 0x3f - -4000500c <_XX_unk500c>: -4000500c: 004136 entry a1, 32 -4000500f: 12a8 l32i.n a10, a2, 4 -40005011: 070a92 l8ui a9, a10, 7 -40005014: 060ab2 l8ui a11, a10, 6 -40005017: 119980 slli a9, a9, 8 -4000501a: 2099b0 or a9, a9, a11 -4000501d: 050ab2 l8ui a11, a10, 5 -40005020: 119980 slli a9, a9, 8 -40005023: 2099b0 or a9, a9, a11 -40005026: 040ab2 l8ui a11, a10, 4 -40005029: 119980 slli a9, a9, 8 -4000502c: 2099b0 or a9, a9, a11 -4000502f: 890b addi.n a8, a9, -1 -40005031: 0a8816 beqz a8, 400050dd <_XX_unk500c+0xd1> -40005034: 603926 beqi a9, 3, 40005098 <_XX_unk500c+0x8c> -40005037: fbc9b2 addi a11, a9, -5 -4000503a: 0e6b16 beqz a11, 40005124 <_XX_unk500c+0x118> -4000503d: 07a926 beqi a9, 12, 40005048 <_XX_unk500c+0x3c> -40005040: 02ad mov.n a10, a2 -40005042: ffed25 call8 40004f14 <_XX_unk4f14> -40005045: f01d retw.n -40005047: ca3200 depbits a0, a2, 12, 4 -4000504a: 420c movi.n a2, 4 -4000504c: 18ca add.n a1, a8, a12 -4000504e: 07a0b2 movi a11, 7 -40005051: 0ca0a2 movi a10, 12 -40005054: ffd765 call8 40004dcc <_XX_unk4d88+0x44> -40005057: 0a5d mov.n a5, a10 -40005059: 7acc bnez.n a10, 40005064 <_XX_unk500c+0x58> -4000505b: ffeba1 l32r a10, 40005008 <_s_no_rds> -4000505e: fd7a65 call8 40002804 -40005061: fff6c6 j 40005040 <_XX_unk500c+0x34> -40005064: 20b440 or a11, a4, a4 -40005067: 0603d2 l8ui a13, a3, 6 -4000506a: 0703c2 l8ui a12, a3, 7 -4000506d: 012aa2 l32i a10, a10, 4 -40005070: 11cc80 slli a12, a12, 8 -40005073: 0ccaa2 addi a10, a10, 12 -40005076: 20ccd0 or a12, a12, a13 -40005079: 0503d2 l8ui a13, a3, 5 -4000507c: 11cc80 slli a12, a12, 8 -4000507f: 20ccd0 or a12, a12, a13 -40005082: 0403d2 l8ui a13, a3, 4 -40005085: 11cc80 slli a12, a12, 8 -40005088: 20ccd0 or a12, a12, a13 -4000508b: fcd3a5 call8 40001dc4 <_X_ets_memcpy> -4000508e: 05bd mov.n a11, a5 -40005090: 05ad mov.n a10, a5 -40005092: 0013a5 call8 400051cc <_XX_unk51ac+0x20> -40005095: ffe9c6 j 40005040 <_XX_unk500c+0x34> -40005098: 0d0a82 l8ui a8, a10, 13 -4000509b: 0e0af2 l8ui a15, a10, 14 -4000509e: 0f0ae2 l8ui a14, a10, 15 -400050a1: 130ad2 l8ui a13, a10, 19 -400050a4: 11ee80 slli a14, a14, 8 -400050a7: 11dd80 slli a13, a13, 8 -400050aa: 20eef0 or a14, a14, a15 -400050ad: 120af2 l8ui a15, a10, 18 -400050b0: 11ee80 slli a14, a14, 8 -400050b3: 20ee80 or a14, a14, a8 -400050b6: 20ddf0 or a13, a13, a15 -400050b9: 110af2 l8ui a15, a10, 17 -400050bc: 11dd80 slli a13, a13, 8 -400050bf: 20ddf0 or a13, a13, a15 -400050c2: 100af2 l8ui a15, a10, 16 -400050c5: 11dd80 slli a13, a13, 8 -400050c8: 20ddf0 or a13, a13, a15 -400050cb: 0c0af2 l8ui a15, a10, 12 -400050ce: 11ee80 slli a14, a14, 8 -400050d1: 20eef0 or a14, a14, a15 -400050d4: 0020c0 memw -400050d7: 0ed9 s32i.n a13, a14, 0 -400050d9: ffd8c6 j 40005040 <_XX_unk500c+0x34> -400050dc: cab200 depbits a0, a2, 12, 12 -400050df: 0ae214 excw -400050e2: 0e .byte 0xe -400050e3: 110af2 l8ui a15, a10, 17 -400050e6: 130ac2 l8ui a12, a10, 19 -400050e9: 120ad2 l8ui a13, a10, 18 -400050ec: 11cc80 slli a12, a12, 8 -400050ef: 20ccd0 or a12, a12, a13 -400050f2: 11cc80 slli a12, a12, 8 -400050f5: 0f0ad2 l8ui a13, a10, 15 -400050f8: 20ccf0 or a12, a12, a15 -400050fb: 100af2 l8ui a15, a10, 16 -400050fe: 11dd80 slli a13, a13, 8 -40005101: 11cc80 slli a12, a12, 8 -40005104: 20dde0 or a13, a13, a14 -40005107: 0d0ae2 l8ui a14, a10, 13 -4000510a: 11dd80 slli a13, a13, 8 -4000510d: 20ccf0 or a12, a12, a15 -40005110: 20dde0 or a13, a13, a14 -40005113: 0c0ae2 l8ui a14, a10, 12 -40005116: 11dd80 slli a13, a13, 8 -40005119: 20dde0 or a13, a13, a14 -4000511c: 0dad mov.n a10, a13 -4000511e: fcca65 call8 40001dc4 <_X_ets_memcpy> -40005121: ffc6c6 j 40005040 <_XX_unk500c+0x34> -40005124: 0c0ab2 l8ui a11, a10, 12 -40005127: 0f0ac2 l8ui a12, a10, 15 -4000512a: 0e0ad2 l8ui a13, a10, 14 -4000512d: 11cc80 slli a12, a12, 8 -40005130: 20ccd0 or a12, a12, a13 -40005133: 0d0ad2 l8ui a13, a10, 13 -40005136: 11cc80 slli a12, a12, 8 -40005139: 20ccd0 or a12, a12, a13 -4000513c: 11ac80 slli a10, a12, 8 -4000513f: 20aab0 or a10, a10, a11 -40005142: fbfd25 call8 40001114 <_XX_set_0x3fffda30_0> -40005145: 02a0d2 movi a13, 2 -40005148: ff0ee1 l32r a14, 40004d80 <_st_0x3fffdc90> -4000514b: 00a0f2 movi a15, 0 -4000514e: 166ef2 s32i a15, a14, 88 -40005151: 0b6ed2 s32i a13, a14, 44 -40005154: ffba06 j 40005040 <_XX_unk500c+0x34> -40005157: 413600 srli a3, a0, 6 -4000515a: 092100 l32e a0, a1, -56 -4000515d: ff .byte 0xff -4000515e: b228 l32i.n a2, a2, 44 -40005160: f01d retw.n - ... - -40005164 <_fp_0x40004f6c>: -40005164: 4f6c movi.n a15, -28 -40005166: c84000 excw - -40005168 <_fp_0x40004fc8>: -40005168: 4fc8 l32i.n a12, a15, 16 -4000516a: 0c4000 excw -4000516d: 400050 excw - -40005170 <_X_sip_init_attach>: -40005170: 004136 entry a1, 32 -40005173: 02dd mov.n a13, a2 -40005175: fffba1 l32r a10, 40005164 <_fp_0x40004f6c> -40005178: fffcb1 l32r a11, 40005168 <_fp_0x40004fc8> -4000517b: ff0131 l32r a3, 40004d80 <_st_0x3fffdc90> -4000517e: 080c movi.n a8, 0 -40005180: 03cd mov.n a12, a3 -40005182: b389 s32i.n a8, a3, 44 -40005184: 003625 call8 400054e8 <_X_slc_init_attach> -40005187: c3a9 s32i.n a10, a3, 48 -40005189: ffbfe5 call8 40004d88 <_XX_unk4d88> -4000518c: c398 l32i.n a9, a3, 48 -4000518e: fff7a1 l32r a10, 4000516c <_fp_0x40004fc8+0x4> -40005191: 1663a2 s32i a10, a3, 88 -40005194: 001992 l16ui a9, a9, 0 -40005197: 015392 s16i a9, a3, 2 -4000519a: 004de5 call8 40005678 <_X_slc_enable> -4000519d: 1b0c movi.n a11, 1 -4000519f: b3b9 s32i.n a11, a3, 44 -400051a1: f01d retw.n -400051a3: 544400 extui a4, a0, 4, 6 -400051a6: 504000 iitlb a0 -400051a9: df .byte 0xdf -400051aa: ff .byte 0xff -400051ab: 3f .byte 0x3f - -400051ac <_XX_unk51ac>: -400051ac: 004136 entry a1, 32 -400051af: fffda1 l32r a10, 400051a4 <_X_sip_init_attach+0x34> -400051b2: 1da0b2 movi a11, 29 -400051b5: fffcc1 l32r a12, 400051a8 <_X_sip_init_attach+0x38> -400051b8: 04a0d2 movi a13, 4 -400051bb: fef191 l32r a9, 40004d80 <_st_0x3fffdc90> -400051be: 00a082 movi a8, 0 -400051c1: a989 s32i.n a8, a9, 40 -400051c3: 9989 s32i.n a8, a9, 36 -400051c5: fbd8e5 call8 40000f54 <_X_ets_task> -400051c8: f01d retw.n -400051ca: 360000 excw -400051cd: ad0041 l32r a4, 3fff05d0 <_start-0xfa30> -400051d0: eb8102 excw -400051d3: fe .byte 0xfe -400051d4: 1298 l32i.n a9, a2, 4 -400051d6: a8f8 l32i.n a15, a8, 40 -400051d8: 0849f2 s8i a15, a9, 8 -400051db: 41c8f0 srli a12, a15, 8 -400051de: 0949c2 s8i a12, a9, 9 -400051e1: ff1b addi.n a15, a15, 1 -400051e3: 41c8c0 srli a12, a12, 8 -400051e6: 0a49c2 s8i a12, a9, 10 -400051e9: 41c8c0 srli a12, a12, 8 -400051ec: 0b49c2 s8i a12, a9, 11 -400051ef: a8f9 s32i.n a15, a8, 40 -400051f1: 03bd mov.n a11, a3 -400051f3: fea3e1 l32r a14, 40004c80 <_c_0xbfffffff> -400051f6: ed5ad1 l32r a13, 40000760 <_c_0x40000000> -400051f9: 0020c0 memw -400051fc: 03c8 l32i.n a12, a3, 0 -400051fe: 10cce0 and a12, a12, a14 -40005201: 20ccd0 or a12, a12, a13 -40005204: 0020c0 memw -40005207: 03c9 s32i.n a12, a3, 0 -40005209: 994b addi.n a9, a9, 4 -4000520b: 0d0c movi.n a13, 0 -4000520d: 0020c0 memw -40005210: 0049d2 s8i a13, a9, 0 -40005213: 0020c0 memw -40005216: 0149d2 s8i a13, a9, 1 -40005219: 0c0c movi.n a12, 0 -4000521b: 0020c0 memw -4000521e: 0249c2 s8i a12, a9, 2 -40005221: 080c movi.n a8, 0 -40005223: 0020c0 memw -40005226: 034982 s8i a8, a9, 3 -40005229: 0050a5 call8 40005734 <_XX_unk5734> -4000522c: 020c movi.n a2, 0 -4000522e: f01d retw.n -40005230: 004136 entry a1, 32 -40005233: fed321 l32r a2, 40004d80 <_st_0x3fffdc90> -40005236: f01d retw.n -40005238: 004136 entry a1, 32 -4000523b: fed141 l32r a4, 40004d80 <_st_0x3fffdc90> -4000523e: 5488 l32i.n a8, a4, 20 -40005240: 010816 beqz a8, 40005254 <_XX_unk51ac+0xa8> -40005243: fbe665 call8 400010a8 <_X_ets_enter_critical> -40005246: 5428 l32i.n a2, a4, 20 -40005248: 050c movi.n a5, 0 -4000524a: 6459 s32i.n a5, a4, 24 -4000524c: 5459 s32i.n a5, a4, 20 -4000524e: fbe6e5 call8 400010bc <_X_ets_exit_critical> -40005251: 057256 bnez a2, 400052ac <_XX_unk51ac+0x100> -40005254: f01d retw.n -40005256: ffc9b2 addi a11, a9, -1 -40005259: 083b16 beqz a11, 400052e0 <_XX_unk51ac+0x134> -4000525c: fdc9c2 addi a12, a9, -3 -4000525f: 07dc16 beqz a12, 400052e0 <_XX_unk51ac+0x134> -40005262: 432966 bnei a9, 2, 400052a9 <_XX_unk51ac+0xfd> -40005265: 030ac2 l8ui a12, a10, 3 -40005268: 020ab2 l8ui a11, a10, 2 -4000526b: 11ac80 slli a10, a12, 8 -4000526e: 20aab0 or a10, a10, a11 -40005271: 0314b2 l16ui a11, a4, 6 -40005274: 0162a5 call8 400068a0 -40005277: ffca62 addi a6, a10, -1 -4000527a: 02ad mov.n a10, a2 -4000527c: f46060 extui a6, a6, 0, 16 -4000527f: 06bd mov.n a11, a6 -40005281: ffaba5 call8 40004d3c -40005284: 2a28 l32i.n a2, a10, 8 -40005286: 2a59 s32i.n a5, a10, 8 -40005288: 1224d2 l32i a13, a4, 72 -4000528b: 0abd mov.n a11, a10 -4000528d: 5d8c beqz.n a13, 40005296 <_XX_unk51ac+0xea> -4000528f: 06cd mov.n a12, a6 -40005291: 03ad mov.n a10, a3 -40005293: 000de0 callx8 a13 -40005296: 02bd mov.n a11, a2 -40005298: 2228 l32i.n a2, a2, 8 -4000529a: 2b59 s32i.n a5, a11, 8 -4000529c: 1324d2 l32i a13, a4, 76 -4000529f: 6d8c beqz.n a13, 400052a9 <_XX_unk51ac+0xfd> -400052a1: 0bad mov.n a10, a11 -400052a3: 01a0c2 movi a12, 1 -400052a6: 000de0 callx8 a13 -400052a9: fa7216 beqz a2, 40005254 <_XX_unk51ac+0xa8> -400052ac: 12a8 l32i.n a10, a2, 4 -400052ae: 000a92 l8ui a9, a10, 0 -400052b1: 023d mov.n a3, a2 -400052b3: 349090 extui a9, a9, 0, 4 -400052b6: f9c956 bnez a9, 40005256 <_XX_unk51ac+0xaa> -400052b9: 2228 l32i.n a2, a2, 8 -400052bb: 2359 s32i.n a5, a3, 8 -400052bd: 010ab2 l8ui a11, a10, 1 -400052c0: 1324d2 l32i a13, a4, 76 -400052c3: 118b66 bnei a11, 8, 400052d8 <_XX_unk51ac+0x12c> -400052c6: fdfd16 beqz a13, 400052a9 <_XX_unk51ac+0xfd> -400052c9: 03bd mov.n a11, a3 -400052cb: 20a330 or a10, a3, a3 -400052ce: 01a0c2 movi a12, 1 -400052d1: 000de0 callx8 a13 -400052d4: fff446 j 400052a9 <_XX_unk51ac+0xfd> -400052d7: 03ad00 excw -400052da: ffb625 call8 40004e3c <_XX_unk4d88+0xb4> -400052dd: fff206 j 400052a9 <_XX_unk51ac+0xfd> -400052e0: 030ac2 l8ui a12, a10, 3 -400052e3: 020ab2 l8ui a11, a10, 2 -400052e6: 11ac80 slli a10, a12, 8 -400052e9: 20aab0 or a10, a10, a11 -400052ec: 0314b2 l16ui a11, a4, 6 -400052ef: 015b25 call8 400068a0 -400052f2: f460a0 extui a6, a10, 0, 16 -400052f5: 20a330 or a10, a3, a3 -400052f8: 20b660 or a11, a6, a6 -400052fb: ffa425 call8 40004d3c -400052fe: 2a28 l32i.n a2, a10, 8 -40005300: 2a59 s32i.n a5, a10, 8 -40005302: 1224d2 l32i a13, a4, 72 -40005305: 0abd mov.n a11, a10 -40005307: f9ed16 beqz a13, 400052a9 <_XX_unk51ac+0xfd> -4000530a: 06cd mov.n a12, a6 -4000530c: 03ad mov.n a10, a3 -4000530e: 000de0 callx8 a13 -40005311: ffe506 j 400052a9 <_XX_unk51ac+0xfd> -40005314: ccec bnez.n a12, 40005344 <_XX_unk51ac+0x198> -40005316: ff .byte 0xff -40005317: 3f .byte 0x3f -40005318: 004136 entry a1, 32 -4000531b: fe9961 l32r a6, 40004d80 <_st_0x3fffdc90> -4000531e: 072682 l32i a8, a6, 28 -40005321: 014816 beqz a8, 40005339 <_XX_unk51ac+0x18d> -40005324: fbd825 call8 400010a8 <_X_ets_enter_critical> -40005327: 7628 l32i.n a2, a6, 28 -40005329: 030c movi.n a3, 0 -4000532b: 8639 s32i.n a3, a6, 32 -4000532d: 7639 s32i.n a3, a6, 28 -4000532f: fbd8e5 call8 400010bc <_X_ets_exit_critical> -40005332: 328c beqz.n a2, 40005339 <_XX_unk51ac+0x18d> -40005334: 050c movi.n a5, 0 -40005336: 001246 j 40005383 <_XX_unk51ac+0x1d7> -40005339: f01d retw.n -4000533b: 370000 excw -4000533e: 1c69 s32i.n a6, a12, 4 -40005340: 0b03a2 l8ui a10, a3, 11 -40005343: 11aa80 slli a10, a10, 8 -40005346: 20aab0 or a10, a10, a11 -40005349: 0903b2 l8ui a11, a3, 9 -4000534c: 11aa80 slli a10, a10, 8 -4000534f: 20aab0 or a10, a10, a11 -40005352: 0803b2 l8ui a11, a3, 8 -40005355: 11aa80 slli a10, a10, 8 -40005358: 20aab0 or a10, a10, a11 -4000535b: 96a9 s32i.n a10, a6, 36 -4000535d: 010556 bnez a5, 40005371 <_XX_unk51ac+0x1c5> -40005360: 0103b2 l8ui a11, a3, 1 -40005363: 0a6b17 bbci a11, 1, 40005371 <_XX_unk51ac+0x1c5> -40005366: 001165 call8 4000547c <_c_0x60017e00+0x4> -40005369: 4acc bnez.n a10, 40005371 <_XX_unk51ac+0x1c5> -4000536b: 000f65 call8 40005460 <_XX_unk51ac+0x2b4> -4000536e: 01a052 movi a5, 1 -40005371: 000392 l8ui a9, a3, 0 -40005374: 349090 extui a9, a9, 0, 4 -40005377: 04d916 beqz a9, 400053c8 <_XX_unk51ac+0x21c> -4000537a: 621926 beqi a9, 1, 400053e0 <_XX_unk51ac+0x234> -4000537d: 5f2926 beqi a9, 2, 400053e0 <_XX_unk51ac+0x234> -40005380: fb5216 beqz a2, 40005339 <_XX_unk51ac+0x18d> -40005383: 1238 l32i.n a3, a2, 4 -40005385: 024d mov.n a4, a2 -40005387: 010392 l8ui a9, a3, 1 -4000538a: 0a03b2 l8ui a11, a3, 10 -4000538d: 14a290 extui a10, a9, 2, 2 -40005390: fa9a56 bnez a10, 4000533d <_XX_unk51ac+0x191> -40005393: 96c8 l32i.n a12, a6, 36 -40005395: bc1b addi.n a11, a12, 1 -40005397: 96b9 s32i.n a11, a6, 36 -40005399: 0b03a2 l8ui a10, a3, 11 -4000539c: 0a03b2 l8ui a11, a3, 10 -4000539f: 11aa80 slli a10, a10, 8 -400053a2: 20aab0 or a10, a10, a11 -400053a5: 0903b2 l8ui a11, a3, 9 -400053a8: 11aa80 slli a10, a10, 8 -400053ab: 20aab0 or a10, a10, a11 -400053ae: 0803b2 l8ui a11, a3, 8 -400053b1: 11aa80 slli a10, a10, 8 -400053b4: 20aab0 or a10, a10, a11 -400053b7: a21ac7 beq a10, a12, 4000535d <_XX_unk51ac+0x1b1> -400053ba: 0abd mov.n a11, a10 -400053bc: 02dd mov.n a13, a2 -400053be: ffd5a1 l32r a10, 40005314 <_XX_unk51ac+0x168> -400053c1: fd4425 call8 40002804 -400053c4: ffe546 j 4000535d <_XX_unk51ac+0x1b1> -400053c7: 04ad00 extui a10, a0, 13, 1 -400053ca: 04bd mov.n a11, a4 -400053cc: 2428 l32i.n a2, a4, 8 -400053ce: 090c movi.n a9, 0 -400053d0: 142682 l32i a8, a6, 80 -400053d3: 2499 s32i.n a9, a4, 8 -400053d5: 0008e0 callx8 a8 -400053d8: 04ad mov.n a10, a4 -400053da: ffb3a5 call8 40004f14 <_XX_unk4f14> -400053dd: ffe7c6 j 40005380 <_XX_unk51ac+0x1d4> -400053e0: 0303a2 l8ui a10, a3, 3 -400053e3: 0203b2 l8ui a11, a3, 2 -400053e6: 11aa80 slli a10, a10, 8 -400053e9: 20aab0 or a10, a10, a11 -400053ec: 0216b2 l16ui a11, a6, 4 -400053ef: 014b25 call8 400068a0 -400053f2: 0a7d mov.n a7, a10 -400053f4: 04ad mov.n a10, a4 -400053f6: f4b070 extui a11, a7, 0, 16 -400053f9: ff9425 call8 40004d3c -400053fc: 2a28 l32i.n a2, a10, 8 -400053fe: 0d0c movi.n a13, 0 -40005400: 2ad9 s32i.n a13, a10, 8 -40005402: 0abd mov.n a11, a10 -40005404: 0020c0 memw -40005407: 0a98 l32i.n a9, a10, 0 -40005409: c32b addi.n a12, a3, 2 -4000540b: 0216d2 l16ui a13, a6, 4 -4000540e: 000ce2 l8ui a14, a12, 0 -40005411: 010cc2 l8ui a12, a12, 1 -40005414: 82d7d0 mull a13, a7, a13 -40005417: 11cc80 slli a12, a12, 8 -4000541a: 20cce0 or a12, a12, a14 -4000541d: c0ccd0 sub a12, a12, a13 -40005420: 0020c0 memw -40005423: fa24d1 l32r a13, 40003cb4 <_c_0x00ffffff> -40005426: 0aa8 l32i.n a10, a10, 0 -40005428: 10aad0 and a10, a10, a13 -4000542b: 41aca0 srli a10, a10, 12 -4000542e: aaca add.n a10, a10, a12 -40005430: caba90 depbits a9, a10, 12, 12 -40005433: 04ad mov.n a10, a4 -40005435: 0020c0 memw -40005438: 152682 l32i a8, a6, 84 -4000543b: 0b99 s32i.n a9, a11, 0 -4000543d: 0008e0 callx8 a8 -40005440: ffcf06 j 40005380 <_XX_unk51ac+0x1d4> -40005443: 413600 srli a3, a0, 6 -40005446: 029800 andb b9, b8, b0 -40005449: 398c beqz.n a9, 40005450 <_XX_unk51ac+0x2a4> -4000544b: 092926 beqi a9, 2, 40005458 <_XX_unk51ac+0x2ac> -4000544e: f01d retw.n -40005450: 12a8 l32i.n a10, a2, 4 -40005452: ffde65 call8 40005238 <_XX_unk51ac+0x8c> -40005455: f01d retw.n -40005457: 12a800 andbc b10, b8, b0 -4000545a: ffebe5 call8 40005318 <_XX_unk51ac+0x16c> -4000545d: f01d retw.n -4000545f: 413600 srli a3, a0, 6 -40005462: a0a200 addx4 a10, a2, a0 -40005465: a0b200 addx4 a11, a2, a0 -40005468: 962505 call0 3ff9b6bc <_start-0x64944> -4000546b: ff .byte 0xff -4000546c: 0abd mov.n a11, a10 -4000546e: ffd5e5 call8 400051cc <_XX_unk51ac+0x20> -40005471: f01d retw.n -40005473: 4e0000 excw -40005476: 006001 l32r a0, 3ffc55f8 <_start-0x3aa08> - -40005478 <_c_0x60017e00>: -40005478: 017e00 slli a7, a14, 32 -4000547b: 413660 srli a3, a6, 6 -4000547e: fd3100 excw -40005481: ff .byte 0xff -40005482: 0020c0 memw -40005485: 942332 l32i a3, a3, 0x250 -40005488: 181c movi.n a8, 17 -4000548a: 0ff377 bbsi a3, 23, 4000549d <_c_0x60017e00+0x25> -4000548d: fffa41 l32r a4, 40005478 <_c_0x60017e00> -40005490: 0020c0 memw -40005493: a52442 l32i a4, a4, 0x294 -40005496: 020c movi.n a2, 0 -40005498: 01c487 bnall a4, a8, 4000549d <_c_0x60017e00+0x25> -4000549b: f01d retw.n -4000549d: 120c movi.n a2, 1 -4000549f: f01d retw.n -400054a1: 000000 ill - -400054a4 <_st_0x3fffdf70>: -400054a4: ffdf70 excw -400054a7: 3f .byte 0x3f -400054a8: 004136 entry a1, 32 -400054ab: fffe51 l32r a5, 400054a4 <_st_0x3fffdf70> -400054ae: b549 s32i.n a4, a5, 44 -400054b0: 6539 s32i.n a3, a5, 24 -400054b2: 5529 s32i.n a2, a5, 20 -400054b4: f01d retw.n - ... - -400054b8 <_c_0x6000ae00>: -400054b8: 00ae00 any8 b0, b8:b9:b10:b11:b12:b13:b14:b15 -400054bb: 000060 excw - -400054bc <_c_0xf0000000>: -400054bc: 000000 ill -400054bf: 0017f0 movsp a15, a7 - -400054c0 <_c_0x02320017>: -400054c0: 320017 bnone a0, a1, 400054f6 <_X_slc_init_attach+0xe> -400054c3: 666602 s32i a0, a6, 0x198 - -400054c4 <_c_0x11116666>: -400054c4: 116666 bnei a6, 6, 400054d9 <_c_0x11116666+0x15> -400054c7: bfff11 l32r a1, 3fff54c4 <_start-0xab3c> -400054ca: ff .byte 0xff -400054cb: ff .byte 0xff -400054cc: ff .byte 0xff -400054cd: fffc01 l32r a0, 400054c0 <_c_0x02320017> -400054d0: 00c800 excw -400054d3: dfb000 excw -400054d6: ff .byte 0xff -400054d7: 3f .byte 0x3f -400054d8: 030003 excw -400054db: fffc00 excw -400054de: fffc bnez.n a15, 40005521 <_X_slc_init_attach+0x39> -400054e0: 800040 add a0, a0, a4 - ... - -400054e4 <_c_0x01110013>: -400054e4: 110013 excw -400054e7: 813601 l32r a0, 3ffe59c0 <_start-0x1a640> - -400054e8 <_X_slc_init_attach>: -400054e8: 008136 entry a1, 64 -400054eb: 029d mov.n a9, a2 -400054ed: 0e2c movi.n a14, 32 -400054ef: ffed21 l32r a2, 400054a4 <_st_0x3fffdf70> -400054f2: 0f0c movi.n a15, 0 -400054f4: 92f9 s32i.n a15, a2, 36 -400054f6: 82f9 s32i.n a15, a2, 32 -400054f8: 72f9 s32i.n a15, a2, 28 -400054fa: b249 s32i.n a4, a2, 44 -400054fc: 6239 s32i.n a3, a2, 24 -400054fe: 056292 s32i a9, a2, 20 -40005501: ffeda1 l32r a10, 400054b8 <_c_0x6000ae00> -40005504: f78bb1 l32r a11, 40003330 <_c_0x3feffe00> -40005507: ffed91 l32r a9, 400054bc <_c_0xf0000000> -4000550a: 0020c0 memw -4000550d: 952bd2 l32i a13, a11, 0x254 -40005510: 20dde0 or a13, a13, a14 -40005513: 0020c0 memw -40005516: 956bd2 s32i a13, a11, 0x254 -40005519: dfafc2 movi a12, -33 -4000551c: 0020c0 memw -4000551f: 952b82 l32i a8, a11, 0x254 -40005522: 1088c0 and a8, a8, a12 -40005525: 0020c0 memw -40005528: 956b82 s32i a8, a11, 0x254 -4000552b: 0545b6 bltui a5, 4, 40005534 <_X_slc_init_attach+0x4c> -4000552e: fcc582 addi a8, a5, -4 -40005531: 11f816 beqz a8, 40005654 <_l_slc_boot_mode_4> -40005534: 0020c0 memw -40005537: 812ab2 l32i a11, a10, 0x204 -4000553a: ffe1c1 l32r a12, 400054c0 <_c_0x02320017> -4000553d: 10bb90 and a11, a11, a9 -40005540: 20bbc0 or a11, a11, a12 -40005543: 0020c0 memw -40005546: 816ab2 s32i a11, a10, 0x204 -40005549: ffdec1 l32r a12, 400054c4 <_c_0x11116666> -4000554c: 0020c0 memw -4000554f: 806ac2 s32i a12, a10, 0x200 -40005552: 0043a5 call8 4000598c <_X_slc_set_host_io_max_window> -40005555: 3c0c movi.n a12, 3 -40005557: ffc741 l32r a4, 40005474 <_XX_unk51ac+0x2c8> -4000555a: ffc731 l32r a3, 40005478 <_c_0x60017e00> -4000555d: 0020c0 memw -40005560: 8023b2 l32i a11, a3, 0x200 -40005563: 20bbc0 or a11, a11, a12 -40005566: 0020c0 memw -40005569: 8063b2 s32i a11, a3, 0x200 -4000556c: ca7c movi.n a10, -4 -4000556e: 0020c0 memw -40005571: 802392 l32i a9, a3, 0x200 -40005574: 1099a0 and a9, a9, a10 -40005577: 0020c0 memw -4000557a: 806392 s32i a9, a3, 0x200 -4000557d: 10a382 movi a8, 0x310 -40005580: 0020c0 memw -40005583: 8023f2 l32i a15, a3, 0x200 -40005586: 20ff80 or a15, a15, a8 -40005589: 0020c0 memw -4000558c: 8063f2 s32i a15, a3, 0x200 -4000558f: ffcee1 l32r a14, 400054c8 <_c_0x11116666+0x4> -40005592: 0020c0 memw -40005595: 8023d2 l32i a13, a3, 0x200 -40005598: 10dde0 and a13, a13, a14 -4000559b: 0020c0 memw -4000559e: 8063d2 s32i a13, a3, 0x200 -400055a1: 00a1c2 movi a12, 0x100 -400055a4: 0020c0 memw -400055a7: 9e24b2 l32i a11, a4, 0x278 -400055aa: 20bbc0 or a11, a11, a12 -400055ad: 0020c0 memw -400055b0: 9e64b2 s32i a11, a4, 0x278 -400055b3: ffc6a1 l32r a10, 400054cc <_c_0x11116666+0x8> -400055b6: ffc691 l32r a9, 400054d0 <_c_0x11116666+0xc> -400055b9: 0020c0 memw -400055bc: c02482 l32i a8, a4, 0x300 -400055bf: 1088a0 and a8, a8, a10 -400055c2: 208890 or a8, a8, a9 -400055c5: 0020c0 memw -400055c8: cb0c movi.n a11, 12 -400055ca: 10c1f2 addi a15, a1, 16 -400055cd: 14c1e2 addi a14, a1, 20 -400055d0: c06482 s32i a8, a4, 0x300 -400055d3: ffc0a1 l32r a10, 400054d4 <_c_0x11116666+0x10> -400055d6: 10a2d2 movi a13, 0x210 -400055d9: 10cac2 addi a12, a10, 16 -400055dc: 0052d2 s16i a13, a2, 0 -400055df: 11e9 s32i.n a14, a1, 4 -400055e1: 01f9 s32i.n a15, a1, 0 -400055e3: 10a2e2 movi a14, 0x210 -400055e6: 01a0f2 movi a15, 1 -400055e9: ff6a25 call8 40004c8c -400055ec: ffbb81 l32r a8, 400054d8 <_c_0x11116666+0x14> -400055ef: 0020c0 memw -400055f2: 8023f2 l32i a15, a3, 0x200 -400055f5: 20ff80 or a15, a15, a8 -400055f8: 0020c0 memw -400055fb: 8063f2 s32i a15, a3, 0x200 -400055fe: ffb7e1 l32r a14, 400054dc <_c_0x11116666+0x18> -40005601: 0020c0 memw -40005604: 8023d2 l32i a13, a3, 0x200 -40005607: 10dde0 and a13, a13, a14 -4000560a: 0020c0 memw -4000560d: 8063d2 s32i a13, a3, 0x200 -40005610: 41a8 l32i.n a10, a1, 16 -40005612: 51b8 l32i.n a11, a1, 20 -40005614: 1c0c movi.n a12, 1 -40005616: 001a25 call8 400057b8 <_XX_unk57b8> -40005619: f9b9e1 l32r a14, 40003d00 <_c_0x00800000> -4000561c: fc7c movi.n a12, -1 -4000561e: 0020c0 memw -40005621: b864e2 s32i a14, a4, 0x2e0 -40005624: ffafd1 l32r a13, 400054e0 <_c_0x11116666+0x1c> -40005627: 0020c0 memw -4000562a: b464d2 s32i a13, a4, 0x2d0 -4000562d: 0020c0 memw -40005630: b264c2 s32i a12, a4, 0x2c8 -40005633: 0020c0 memw -40005636: 8463c2 s32i a12, a3, 0x210 -40005639: 00acb2 movi a11, 0xfffffc00 -4000563c: 0a1c movi.n a10, 16 -4000563e: 0020c0 memw -40005641: c62392 l32i a9, a3, 0x318 -40005644: 1099b0 and a9, a9, a11 -40005647: 2099a0 or a9, a9, a10 -4000564a: 0020c0 memw -4000564d: c66392 s32i a9, a3, 0x318 -40005650: f01d retw.n - ... - -40005654 <_l_slc_boot_mode_4>: -40005654: 0020c0 memw -40005657: 812af2 l32i a15, a10, 0x204 -4000565a: ffa281 l32r a8, 400054e4 <_c_0x01110013> -4000565d: 10ff90 and a15, a15, a9 -40005660: 20ff80 or a15, a15, a8 -40005663: 0020c0 memw -40005666: 816af2 s32i a15, a10, 0x204 -40005669: ffb706 j 40005549 <_X_slc_init_attach+0x61> -4000566c: ff .byte 0xff -4000566d: ff .byte 0xff -4000566e: cf .byte 0xcf -4000566f: ff .byte 0xff -40005670: 0058e0 excw -40005673: 840040 extui a0, a4, 0, 9 -40005676: 040a add.n a0, a4, a0 - -40005678 <_X_slc_enable>: -40005678: 004136 entry a1, 32 -4000567b: fffcc1 l32r a12, 4000566c <_l_slc_boot_mode_4+0x18> -4000567e: f72c91 l32r a9, 40003330 <_c_0x3feffe00> -40005681: ff7d21 l32r a2, 40005478 <_c_0x60017e00> -40005684: 0020c0 memw -40005687: 9822b2 l32i a11, a2, 0x260 -4000568a: 10bbc0 and a11, a11, a12 -4000568d: 0020c0 memw -40005690: 9862b2 s32i a11, a2, 0x260 -40005693: 20a0a2 movi a10, 32 -40005696: 0020c0 memw -40005699: 982282 l32i a8, a2, 0x260 -4000569c: 2088a0 or a8, a8, a10 -4000569f: 0020c0 memw -400056a2: 986282 s32i a8, a2, 0x260 -400056a5: 40a0f2 movi a15, 64 -400056a8: 0020c0 memw -400056ab: 9822e2 l32i a14, a2, 0x260 -400056ae: 20eef0 or a14, a14, a15 -400056b1: 0020c0 memw -400056b4: 9862e2 s32i a14, a2, 0x260 -400056b7: fd6c movi.n a13, -17 -400056b9: 0020c0 memw -400056bc: 9822c2 l32i a12, a2, 0x260 -400056bf: 10ccd0 and a12, a12, a13 -400056c2: 0020c0 memw -400056c5: 9862c2 s32i a12, a2, 0x260 -400056c8: f71bb1 l32r a11, 40003334 <_c_0xffff83ff> -400056cb: 00a4a2 movi a10, 0x400 -400056ce: 0020c0 memw -400056d1: 9c2982 l32i a8, a9, 0x270 -400056d4: 1088b0 and a8, a8, a11 -400056d7: 2088a0 or a8, a8, a10 -400056da: 0020c0 memw -400056dd: 1a0c movi.n a10, 1 -400056df: ffe4b1 l32r a11, 40005670 <_l_slc_boot_mode_4+0x1c> -400056e2: 00a0c2 movi a12, 0 -400056e5: 9c6982 s32i a8, a9, 0x270 -400056e8: fb9fe5 call8 400010e8 <_X_ets_isr_attach> -400056eb: ffe2d1 l32r a13, 40005674 <_l_slc_boot_mode_4+0x20> -400056ee: 0020c0 memw -400056f1: 8362d2 s32i a13, a2, 0x20c -400056f4: 2a0c movi.n a10, 2 -400056f6: fba0e5 call8 40001104 <_X_ets_isr_unmask> -400056f9: f01d retw.n - ... - -400056fc <_X_slc_select_tohost_gpio_mode>: -400056fc: 004136 entry a1, 32 -400056ff: ff6931 l32r a3, 400054a4 <_st_0x3fffdf70> -40005702: d329 s32i.n a2, a3, 52 -40005704: f01d retw.n - ... - -40005708 <_X_slc_select_tohost_gpio>: -40005708: 004136 entry a1, 32 -4000570b: 11b2f6 bgeui a2, 16, 40005720 <_X_slc_select_tohost_gpio+0x18> -4000570e: ff6541 l32r a4, 400054a4 <_st_0x3fffdf70> -40005711: 401200 ssl a2 -40005714: 130c movi.n a3, 1 -40005716: a13300 sll a3, a3 -40005719: 1c5432 s16i a3, a4, 56 -4000571c: f01d retw.n -4000571e: 910000 srl a0, a0 -40005721: 0cff61 l32r a6, 3ffc8b20 <_start-0x374e0> -40005724: 8208 l32i.n a0, a2, 32 -40005726: 1c59 s32i.n a5, a12, 4 -40005728: f01d retw.n -4000572a: 000000 ill -4000572d: 00c000 excw - -40005730 <_c_0xff300000>: -40005730: 300000 xor a0, a0, a0 -40005733: ff .byte 0xff - -40005734 <_XX_unk5734>: -40005734: 006136 entry a1, 48 -40005737: 026d mov.n a6, a2 -40005739: 040c movi.n a4, 0 -4000573b: 0020c0 memw -4000573e: 0149 s32i.n a4, a1, 0 -40005740: 06c216 beqz a2, 400057b0 <_XX_unk5734+0x7c> -40005743: 00a052 movi a5, 0 -40005746: f95b71 l32r a7, 40003cb4 <_c_0x00ffffff> -40005749: 108076 loop a0, 4000575d <_XX_unk5734+0x29> -4000574c: 0020c0 memw -4000574f: 0688 l32i.n a8, a6, 0 -40005751: 2668 l32i.n a6, a6, 8 -40005753: 108870 and a8, a8, a7 -40005756: 418c80 srli a8, a8, 12 -40005759: 585a add.n a5, a8, a5 -4000575b: 268c beqz.n a6, 40005761 <_XX_unk5734+0x2d> -4000575d: fffa06 j 40005749 <_XX_unk5734+0x15> -40005760: 20c000 or a12, a0, a0 -40005763: 615200 excw -40005766: 446100 extui a6, a0, 1, 5 -40005769: ff .byte 0xff -4000576a: f96571 l32r a7, 40003d00 <_c_0x00800000> -4000576d: 088076 loop a0, 40005779 <_XX_unk5734+0x45> -40005770: 0020c0 memw -40005773: b92692 l32i a9, a6, 0x2e4 -40005776: 020977 bnone a9, a7, 4000577c <_XX_unk5734+0x48> -40005779: fffc06 j 4000576d <_XX_unk5734+0x39> -4000577c: 0020c0 memw -4000577f: bd6622 s32i a2, a6, 0x2f4 -40005782: 0020c0 memw -40005785: be6632 s32i a3, a6, 0x2f8 -40005788: ffe9b1 l32r a11, 4000572c <_X_slc_select_tohost_gpio+0x24> -4000578b: f72fd1 l32r a13, 40003448 <_c_0x000fffff> -4000578e: 0020c0 memw -40005791: 01c8 l32i.n a12, a1, 0 -40005793: 10ccd0 and a12, a12, a13 -40005796: ffe6d1 l32r a13, 40005730 <_c_0xff300000> -40005799: 0020c0 memw -4000579c: b926a2 l32i a10, a6, 0x2e4 -4000579f: 10aad0 and a10, a10, a13 -400057a2: 20aac0 or a10, a10, a12 -400057a5: 20aab0 or a10, a10, a11 -400057a8: 0020c0 memw -400057ab: b966a2 s32i a10, a6, 0x2e4 -400057ae: f01d retw.n -400057b0: 050c movi.n a5, 0 -400057b2: ffeac6 j 40005761 <_XX_unk5734+0x2d> -400057b5: 000000 ill - -400057b8 <_XX_unk57b8>: -400057b8: 004136 entry a1, 32 -400057bb: ff2fb1 l32r a11, 40005478 <_c_0x60017e00> -400057be: f93ea1 l32r a10, 40003cb8 <_c_0x01000000> -400057c1: 088076 loop a0, 400057cd <_XX_unk57b8+0x15> -400057c4: 0020c0 memw -400057c7: b92b82 l32i a8, a11, 0x2e4 -400057ca: 0208a7 bnone a8, a10, 400057d0 <_XX_unk57b8+0x18> -400057cd: fffc06 j 400057c1 <_XX_unk57b8+0x9> -400057d0: 0020c0 memw -400057d3: bb6b22 s32i a2, a11, 0x2ec -400057d6: 0020c0 memw -400057d9: bc6b32 s32i a3, a11, 0x2f0 -400057dc: 0020c0 memw -400057df: b92b92 l32i a9, a11, 0x2e4 -400057e2: 2099a0 or a9, a9, a10 -400057e5: 0020c0 memw -400057e8: b96b92 s32i a9, a11, 0x2e4 -400057eb: f4a040 extui a10, a4, 0, 16 -400057ee: 001d65 call8 400059c4 <_X_slc_add_credits> -400057f1: f01d retw.n - ... - -400057f4 <_XX_unk57f4>: -400057f4: 004136 entry a1, 32 -400057f7: 00a082 movi a8, 0 -400057fa: ff1f61 l32r a6, 40005478 <_c_0x60017e00> -400057fd: 0020c0 memw -40005800: b92642 l32i a4, a6, 0x2e4 -40005803: 3974b7 bbci a4, 27, 40005840 <_XX_unk57f4+0x4c> -40005806: f951a1 l32r a10, 40003d4c <_c_0x02000000> -40005809: 0020c0 memw -4000580c: b92692 l32i a9, a6, 0x2e4 -4000580f: 2099a0 or a9, a9, a10 -40005812: 0020c0 memw -40005815: b96692 s32i a9, a6, 0x2e4 -40005818: 0020c0 memw -4000581b: c12672 l32i a7, a6, 0x304 -4000581e: 006272 s32i a7, a2, 0 -40005821: 0020c0 memw -40005824: f93721 l32r a2, 40003d00 <_c_0x00800000> -40005827: c22672 l32i a7, a6, 0x308 -4000582a: 006372 s32i a7, a3, 0 -4000582d: 088076 loop a0, 40005839 <_XX_unk57f4+0x45> -40005830: 0020c0 memw -40005833: b926b2 l32i a11, a6, 0x2e4 -40005836: 020b27 bnone a11, a2, 4000583c <_XX_unk57f4+0x48> -40005839: fffc06 j 4000582d <_XX_unk57f4+0x39> -4000583c: 2789 s32i.n a8, a7, 8 -4000583e: f01d retw.n -40005840: 0289 s32i.n a8, a2, 0 -40005842: 0389 s32i.n a8, a3, 0 -40005844: f01d retw.n - ... - -40005848 <_XX_unk5848>: -40005848: 004136 entry a1, 32 -4000584b: ff0b61 l32r a6, 40005478 <_c_0x60017e00> -4000584e: 0020c0 memw -40005851: b92642 l32i a4, a6, 0x2e4 -40005854: 7474c7 bbci a4, 28, 400058cc <_XX_unk5848+0x84> -40005857: f9dd91 l32r a9, 40003fcc <_c_0x04000000> -4000585a: 0020c0 memw -4000585d: b92682 l32i a8, a6, 0x2e4 -40005860: 208890 or a8, a8, a9 -40005863: 0020c0 memw -40005866: b96682 s32i a8, a6, 0x2e4 -40005869: 0020c0 memw -4000586c: bf2672 l32i a7, a6, 0x2fc -4000586f: 0279 s32i.n a7, a2, 0 -40005871: 0020c0 memw -40005874: f91171 l32r a7, 40003cb8 <_c_0x01000000> -40005877: c02682 l32i a8, a6, 0x300 -4000587a: 006382 s32i a8, a3, 0 -4000587d: 088076 loop a0, 40005889 <_XX_unk5848+0x41> -40005880: 0020c0 memw -40005883: b926a2 l32i a10, a6, 0x2e4 -40005886: 040a77 bnone a10, a7, 4000588e <_XX_unk5848+0x46> -40005889: fffc06 j 4000587d <_XX_unk5848+0x35> -4000588c: 0c0000 excw -4000588f: 286906 j 4000fa37 <__umoddi3+0x8bd3> -40005892: 002222 l32i a2, a2, 0 -40005895: f2ac beqz.n a2, 400058c8 <_XX_unk5848+0x80> -40005897: fcfa51 l32r a5, 40004c80 <_c_0xbfffffff> -4000589a: ebb931 l32r a3, 40000780 <_c_0x7fffffff> -4000589d: 118076 loop a0, 400058b2 <_XX_unk5848+0x6a> -400058a0: 0020c0 memw -400058a3: 02b8 l32i.n a11, a2, 0 -400058a5: 10bb30 and a11, a11, a3 -400058a8: 15beb0 extui a11, a11, 30, 2 -400058ab: 061b26 beqi a11, 1, 400058b5 <_XX_unk5848+0x6d> -400058ae: 2228 l32i.n a2, a2, 8 -400058b0: 429c beqz.n a2, 400058c8 <_XX_unk5848+0x80> -400058b2: fff9c6 j 4000589d <_XX_unk5848+0x55> -400058b5: 0020c0 memw -400058b8: 02c8 l32i.n a12, a2, 0 -400058ba: 10cc50 and a12, a12, a5 -400058bd: 20cc60 or a12, a12, a6 -400058c0: 0020c0 memw -400058c3: 02c9 s32i.n a12, a2, 0 -400058c5: fff946 j 400058ae <_XX_unk5848+0x66> -400058c8: 020c movi.n a2, 0 -400058ca: f01d retw.n -400058cc: f27c movi.n a2, -1 -400058ce: f01d retw.n -400058d0: ff .byte 0xff -400058d1: ff .byte 0xff -400058d2: fe .byte 0xfe -400058d3: ff .byte 0xff -400058d4: 020000 andb b0, b0, b0 -400058d7: 000004 excw -400058da: 000001 l32r a0, 3ffc58dc <_start-0x3a724> -400058dd: 000080 ret -400058e0: 004136 entry a1, 32 -400058e3: fffb71 l32r a7, 400058d0 <_XX_unk5848+0x88> -400058e6: fffb51 l32r a5, 400058d4 <_XX_unk5848+0x8c> -400058e9: fffb61 l32r a6, 400058d8 <_XX_unk5848+0x90> -400058ec: fee331 l32r a3, 40005478 <_c_0x60017e00> -400058ef: feed41 l32r a4, 400054a4 <_st_0x3fffdf70> -400058f2: 0020c0 memw -400058f5: 822322 l32i a2, a3, 0x208 -400058f8: 74b8 l32i.n a11, a4, 28 -400058fa: 07e216 beqz a2, 4000597c <_XX_unk5848+0x134> -400058fd: 4b8c beqz.n a11, 40005905 <_XX_unk5848+0xbd> -400058ff: 20a220 or a10, a2, a2 -40005902: 000be0 callx8 a11 -40005905: 0c0527 bnone a5, a2, 40005915 <_XX_unk5848+0xcd> -40005908: 0020c0 memw -4000590b: 846352 s32i a5, a3, 0x210 -4000590e: 5488 l32i.n a8, a4, 20 -40005910: b4a8 l32i.n a10, a4, 44 -40005912: 0008e0 callx8 a8 -40005915: 1062f7 bbci a2, 15, 40005929 <_XX_unk5848+0xe1> -40005918: 0020c0 memw -4000591b: fff091 l32r a9, 400058dc <_XX_unk5848+0x94> -4000591e: 846392 s32i a9, a3, 0x210 -40005921: 6488 l32i.n a8, a4, 24 -40005923: 0b24a2 l32i a10, a4, 44 -40005926: 0008e0 callx8 a8 -40005929: 1c7207 bbci a2, 16, 40005949 <_XX_unk5848+0x101> -4000592c: d498 l32i.n a9, a4, 52 -4000592e: 2a2926 beqi a9, 2, 4000595c <_XX_unk5848+0x114> -40005931: 373926 beqi a9, 3, 4000596c <_XX_unk5848+0x124> -40005934: 0020c0 memw -40005937: 8323a2 l32i a10, a3, 0x20c -4000593a: 10aa70 and a10, a10, a7 -4000593d: 0020c0 memw -40005940: 8363a2 s32i a10, a3, 0x20c -40005943: 0020c0 memw -40005946: 846362 s32i a6, a3, 0x210 -40005949: 2ff237 bbsi a2, 19, 4000597c <_XX_unk5848+0x134> -4000594c: 2ce2a7 bbsi a2, 10, 4000597c <_XX_unk5848+0x134> -4000594f: 84b8 l32i.n a11, a4, 32 -40005951: f9db16 beqz a11, 400058f2 <_XX_unk5848+0xaa> -40005954: 02ad mov.n a10, a2 -40005956: 000be0 callx8 a11 -40005959: ffe546 j 400058f2 <_XX_unk5848+0xaa> -4000595c: 1c14b2 l16ui a11, a4, 56 -4000595f: 0020c0 memw -40005962: edf3c1 l32r a12, 40001130 <_c_0x60003e00> -40005965: 836cb2 s32i a11, a12, 0x20c -40005968: fff206 j 40005934 <_XX_unk5848+0xec> -4000596b: 14d200 extui a13, a0, 2, 2 -4000596e: c01c movi.n a0, 28 -40005970: e10020 excw -40005973: ef .byte 0xef -40005974: d2ed excw -40005976: 6e .byte 0x6e -40005977: ee0682 l8ui a8, a6, 238 -4000597a: ff .byte 0xff -4000597b: 94b800 extui a11, a0, 8, 10 -4000597e: 3b8c beqz.n a11, 40005985 <_XX_unk5848+0x13d> -40005980: 0a0c movi.n a10, 0 -40005982: 000be0 callx8 a11 -40005985: f01d retw.n - ... - -40005988 <_c_0xfffff0c0>: -40005988: fff0c0 excw -4000598b: ff .byte 0xff - -4000598c <_X_slc_set_host_io_max_window>: -4000598c: 004136 entry a1, 32 -4000598f: feba31 l32r a3, 40005478 <_c_0x60017e00> -40005992: 0020c0 memw -40005995: fffc41 l32r a4, 40005988 <_c_0xfffff0c0> -40005998: 9d2322 l32i a2, a3, 0x274 -4000599b: 102240 and a2, a2, a4 -4000599e: 20a742 movi a4, 0x720 -400059a1: 202240 or a2, a2, a4 -400059a4: 0020c0 memw -400059a7: 9d6322 s32i a2, a3, 0x274 -400059aa: f01d retw.n - -400059ac <_X_slc_init_credit>: -400059ac: 004136 entry a1, 32 -400059af: ede381 l32r a8, 4000113c <_c_0x1000> -400059b2: feb191 l32r a9, 40005478 <_c_0x60017e00> -400059b5: 0020c0 memw -400059b8: 1a0c movi.n a10, 1 -400059ba: 956982 s32i a8, a9, 0x254 -400059bd: 000065 call8 400059c4 <_X_slc_add_credits> -400059c0: f01d retw.n - ... - -400059c4 <_X_slc_add_credits>: -400059c4: 004136 entry a1, 32 -400059c7: f52331 l32r a3, 40002e54 <_c_0x3fffdb00+0x4> -400059ca: feab41 l32r a4, 40005478 <_c_0x60017e00> -400059cd: 0ab230 depbits a3, a2, 0, 12 -400059d0: 0020c0 memw -400059d3: 956432 s32i a3, a4, 0x254 -400059d6: f01d retw.n - -400059d8 <_X_xtos_set_interrupt_handler_arg>: -400059d8: 004136 entry a1, 32 -400059db: 02e296 bltz a2, 40005a0d <_X_xtos_set_interrupt_handler_arg+0x35> -400059de: 2bc2e6 bgei a2, 32, 40005a0d <_X_xtos_set_interrupt_handler_arg+0x35> -400059e1: eb6851 l32r a5, 40000784 <_c_0x7fffffff+0x4> -400059e4: 1182d0 slli a8, a2, 3 -400059e7: 525a add.n a5, a2, a5 -400059e9: 000552 l8ui a5, a5, 0 -400059ec: eb5471 l32r a7, 4000073c <_c_0x80000000+0x4> -400059ef: 1a75f6 bgeui a5, 7, 40005a0d <_X_xtos_set_interrupt_handler_arg+0x35> -400059f2: c07780 sub a7, a7, a8 -400059f5: eb6481 l32r a8, 40000788 <_c_0x7fffffff+0x8> -400059f8: 3e2762 l32i a6, a7, 248 -400059fb: 23dc bnez.n a3, 40005a11 <_X_xtos_set_interrupt_handler_arg+0x39> -400059fd: 3e6782 s32i a8, a7, 248 -40005a00: 3f6722 s32i a2, a7, 252 -40005a03: c09680 sub a9, a6, a8 -40005a06: 020c movi.n a2, 0 -40005a08: 932690 movnez a2, a6, a9 -40005a0b: f01d retw.n -40005a0d: 020c movi.n a2, 0 -40005a0f: f01d retw.n -40005a11: 3e6732 s32i a3, a7, 248 -40005a14: 3f6742 s32i a4, a7, 252 -40005a17: c0a680 sub a10, a6, a8 -40005a1a: 020c movi.n a2, 0 -40005a1c: 9326a0 movnez a2, a6, a10 -40005a1f: f01d retw.n -40005a21: 000000 ill - -40005a24 <_X_xtos_set_interrupt_handler>: -40005a24: 004136 entry a1, 32 -40005a27: 03bd mov.n a11, a3 -40005a29: 02ad mov.n a10, a2 -40005a2b: 02cd mov.n a12, a2 -40005a2d: fffaa5 call8 400059d8 <_X_xtos_set_interrupt_handler_arg> -40005a30: 0a2d mov.n a2, a10 -40005a32: f01d retw.n - -40005a34 <_X_xtos_ints_on>: -40005a34: 002136 entry a1, 16 -40005a37: eb3f41 l32r a4, 40000734 <_c_0x3fffc210> -40005a3a: 006670 rsil a7, 6 -40005a3d: 0438 l32i.n a3, a4, 0 -40005a3f: 1468 l32i.n a6, a4, 4 -40005a41: 205320 or a5, a3, a2 -40005a44: 0459 s32i.n a5, a4, 0 -40005a46: 105560 and a5, a5, a6 -40005a49: 13e450 wsr.intenable a5 -40005a4c: 13e670 wsr.ps a7 -40005a4f: 002010 rsync -40005a52: 032d mov.n a2, a3 -40005a54: f01d retw.n - ... - -40005a58 <_X_xtos_ints_off>: -40005a58: 002136 entry a1, 16 -40005a5b: eb3641 l32r a4, 40000734 <_c_0x3fffc210> -40005a5e: 006670 rsil a7, 6 -40005a61: 0438 l32i.n a3, a4, 0 -40005a63: 1468 l32i.n a6, a4, 4 -40005a65: 205320 or a5, a3, a2 -40005a68: 305520 xor a5, a5, a2 -40005a6b: 0459 s32i.n a5, a4, 0 -40005a6d: 105560 and a5, a5, a6 -40005a70: 13e450 wsr.intenable a5 -40005a73: 13e670 wsr.ps a7 -40005a76: 002010 rsync -40005a79: 032d mov.n a2, a3 -40005a7b: f01d retw.n -40005a7d: 000000 ill - -40005a80 <_XX_xtos_exc_unk5a80>: -40005a80: 034820 rsr.windowbase a2 -40005a83: 221b addi.n a2, a2, 1 -40005a85: 400200 ssr a2 -40005a88: 034930 rsr.windowstart a3 -40005a8b: 912030 srl a2, a3 -40005a8e: a13300 sll a3, a3 -40005a91: 0bf3d6 bgez a3, 40005b54 <_XX_xtos_exc_unk5a80+0xd4> -40005a94: f53030 extui a3, a3, 16, 16 -40005a97: 202230 or a2, a2, a3 -40005a9a: 603020 neg a3, a2 -40005a9d: 103320 and a3, a3, a2 -40005aa0: 40f330 nsau a3, a3 -40005aa3: 401300 ssl a3 -40005aa6: 912020 srl a2, a2 -40005aa9: 134920 wsr.windowstart a2 -40005aac: 034820 rsr.windowbase a2 -40005aaf: 1fc222 addi a2, a2, 31 -40005ab2: c03230 sub a3, a2, a3 -40005ab5: 134830 wsr.windowbase a3 -40005ab8: 002010 rsync -40005abb: 034920 rsr.windowstart a2 -40005abe: 07a216 beqz a2, 40005b3c <_XX_xtos_exc_unk5a80+0xbc> -40005ac1: 08e207 bbsi a2, 0, 40005acd <_XX_xtos_exc_unk5a80+0x4d> -40005ac4: 19e217 bbsi a2, 1, 40005ae1 <_XX_xtos_exc_unk5a80+0x61> -40005ac7: 3ae227 bbsi a2, 2, 40005b05 <_XX_xtos_exc_unk5a80+0x85> -40005aca: 0022c6 j 40005b59 <_XX_xtos_exc_unk5a80+0xd9> -40005acd: f0c932 addi a3, a9, -16 -40005ad0: 0349 s32i.n a4, a3, 0 -40005ad2: 1359 s32i.n a5, a3, 4 -40005ad4: 2369 s32i.n a6, a3, 8 -40005ad6: 3379 s32i.n a7, a3, 12 -40005ad8: 416120 srli a6, a2, 1 -40005adb: 408010 rotw 1 -40005ade: fff706 j 40005abe <_XX_xtos_exc_unk5a80+0x3e> -40005ae1: f0cd32 addi a3, a13, -16 -40005ae4: 0349 s32i.n a4, a3, 0 -40005ae6: 1359 s32i.n a5, a3, 4 -40005ae8: 2369 s32i.n a6, a3, 8 -40005aea: 3379 s32i.n a7, a3, 12 -40005aec: f4c532 addi a3, a5, -12 -40005aef: 0338 l32i.n a3, a3, 0 -40005af1: e0c332 addi a3, a3, -32 -40005af4: 0389 s32i.n a8, a3, 0 -40005af6: 1399 s32i.n a9, a3, 4 -40005af8: 23a9 s32i.n a10, a3, 8 -40005afa: 33b9 s32i.n a11, a3, 12 -40005afc: 41a220 srli a10, a2, 2 -40005aff: 408020 rotw 2 -40005b02: ffee06 j 40005abe <_XX_xtos_exc_unk5a80+0x3e> -40005b05: 408010 rotw 1 -40005b08: f0cdd2 addi a13, a13, -16 -40005b0b: 0d09 s32i.n a0, a13, 0 -40005b0d: 1d19 s32i.n a1, a13, 4 -40005b0f: 2d29 s32i.n a2, a13, 8 -40005b11: 3d39 s32i.n a3, a13, 12 -40005b13: f4c132 addi a3, a1, -12 -40005b16: 0338 l32i.n a3, a3, 0 -40005b18: 10cdd2 addi a13, a13, 16 -40005b1b: d0c332 addi a3, a3, -48 -40005b1e: 0349 s32i.n a4, a3, 0 -40005b20: 1359 s32i.n a5, a3, 4 -40005b22: 2369 s32i.n a6, a3, 8 -40005b24: 3379 s32i.n a7, a3, 12 -40005b26: 4389 s32i.n a8, a3, 16 -40005b28: 5399 s32i.n a9, a3, 20 -40005b2a: 63a9 s32i.n a10, a3, 24 -40005b2c: 73b9 s32i.n a11, a3, 28 -40005b2e: 4080f0 rotw -1 -40005b31: 41e320 srli a14, a2, 3 -40005b34: 408030 rotw 3 -40005b37: ffe0c6 j 40005abe <_XX_xtos_exc_unk5a80+0x3e> -40005b3a: 100000 and a0, a0, a0 -40005b3d: 204080 or a4, a0, a8 -40005b40: 0348 l32i.n a4, a3, 0 -40005b42: 401200 ssl a2 -40005b45: 120c movi.n a2, 1 -40005b47: a12200 sll a2, a2 -40005b4a: 134920 wsr.windowstart a2 -40005b4d: 002010 rsync -40005b50: 020c movi.n a2, 0 -40005b52: f00d ret.n -40005b54: 120c movi.n a2, 1 -40005b56: f00d ret.n -40005b58: 22f000 orb b15, b0, b0 -40005b5b: c22211 l32r a1, 3fff63e4 <_start-0x9c1c> -40005b5e: 483001 l32r a0, 3ffd7c20 <_start-0x283e0> -40005b61: e2f703 excw -40005b64: 0b0a add.n a0, a11, a0 -40005b66: 22f033 excw -40005b69: 62f711 l32r a1, 3ffde748 <_start-0x218b8> -40005b6c: 3030f7 bltu a0, a15, 40005ba0 <_XX_xtos_exc_unk5b94+0xc> -40005b6f: 130034 excw -40005b72: 320040 orbc b0, b0, b4 -40005b75: 223011 l32r a1, 3ffce438 <_start-0x31bc8> -40005b78: 202081 l32r a8, 3ffcdbf8 <_start-0x32408> -40005b7b: 4920f4 mula.da.hl.ldinc m2, a0, m0, a15 -40005b7e: 033013 excw -40005b81: 022c03 excw -40005b84: c03230 sub a3, a2, a3 -40005b87: 134830 wsr.windowbase a3 -40005b8a: 002010 rsync -40005b8d: 220c movi.n a2, 2 -40005b8f: f00d ret.n -40005b91: 000000 ill - -40005b94 <_XX_xtos_exc_unk5b94>: -40005b94: 002136 entry a1, 16 -40005b97: eafd61 l32r a6, 4000078c <_c_0x7fffffff+0xc> -40005b9a: 03e650 rsr.ps a5 -40005b9d: 004d mov.n a4, a0 -40005b9f: 102560 and a2, a5, a6 -40005ba2: 223b addi.n a2, a2, 3 -40005ba4: 13e620 wsr.ps a2 -40005ba7: 002010 rsync -40005baa: ffed45 call0 40005a80 <_XX_xtos_exc_unk5a80> -40005bad: 040d mov.n a0, a4 -40005baf: 13e650 wsr.ps a5 -40005bb2: 002010 rsync -40005bb5: f01d retw.n - ... - -40005bb8 : -40005bb8: 002136 entry a1, 16 -40005bbb: 000282 l8ui a8, a2, 0 -40005bbe: 000392 l8ui a9, a3, 0 -40005bc1: 3a0c movi.n a10, 3 -40005bc3: 529897 bne a8, a9, 40005c19 -40005bc6: 20b230 or a11, a2, a3 -40005bc9: 530ba7 bnone a11, a10, 40005c20 -40005bcc: 30b230 xor a11, a2, a3 -40005bcf: 318ba7 bany a11, a10, 40005c04 -40005bd2: 221b addi.n a2, a2, 1 -40005bd4: 0b2816 beqz a8, 40005c8a -40005bd7: 331b addi.n a3, a3, 1 -40005bd9: 4302a7 bnone a2, a10, 40005c20 -40005bdc: 000282 l8ui a8, a2, 0 -40005bdf: 000392 l8ui a9, a3, 0 -40005be2: 221b addi.n a2, a2, 1 -40005be4: 319897 bne a8, a9, 40005c19 -40005be7: 09f816 beqz a8, 40005c8a -40005bea: 331b addi.n a3, a3, 1 -40005bec: 3002a7 bnone a2, a10, 40005c20 -40005bef: 000282 l8ui a8, a2, 0 -40005bf2: 000392 l8ui a9, a3, 0 -40005bf5: 221b addi.n a2, a2, 1 -40005bf7: 1e9897 bne a8, a9, 40005c19 -40005bfa: 08c816 beqz a8, 40005c8a -40005bfd: 331b addi.n a3, a3, 1 -40005bff: 000746 j 40005c20 -40005c02: 0c0000 excw -40005c05: 7608 l32i.n a0, a6, 28 -40005c07: 0f88 l32i.n a8, a15, 0 -40005c09: 000282 l8ui a8, a2, 0 -40005c0c: 000392 l8ui a9, a3, 0 -40005c0f: 221b addi.n a2, a2, 1 -40005c11: 049897 bne a8, a9, 40005c19 -40005c14: 331b addi.n a3, a3, 1 -40005c16: fff816 beqz a8, 40005c19 -40005c19: c02890 sub a2, a8, a9 -40005c1c: f01d retw.n -40005c1e: 410000 srli a0, a0, 0 -40005c21: eadc bnez.n a10, 40005c43 -40005c23: eadf71 l32r a7, 400007a0 <_c_0x00ff0000+0x8> -40005c26: 238076 loop a0, 40005c4d -40005c29: 0288 l32i.n a8, a2, 0 -40005c2b: 0398 l32i.n a9, a3, 0 -40005c2d: 1158f0 slli a5, a8, 1 -40005c30: 5c9897 bne a8, a9, 40005c90 -40005c33: 209850 or a9, a8, a5 -40005c36: 1ac977 bnall a9, a7, 40005c54 -40005c39: 1288 l32i.n a8, a2, 4 -40005c3b: 1398 l32i.n a9, a3, 4 -40005c3d: 1158f0 slli a5, a8, 1 -40005c40: 4c9897 bne a8, a9, 40005c90 -40005c43: 209850 or a9, a8, a5 -40005c46: 06c977 bnall a9, a7, 40005c50 -40005c49: 228b addi.n a2, a2, 8 -40005c4b: 338b addi.n a3, a3, 8 -40005c4d: fff546 j 40005c26 -40005c50: 224b addi.n a2, a2, 4 -40005c52: 334b addi.n a3, a3, 4 -40005c54: 320847 bnone a8, a4, 40005c8a -40005c57: eacf51 l32r a5, 40000794 <_c_0x7fffffff+0x14> -40005c5a: eacf61 l32r a6, 40000798 <_c_0x00ff0000> -40005c5d: 290857 bnone a8, a5, 40005c8a -40005c60: eacf71 l32r a7, 4000079c <_c_0x00ff0000+0x4> -40005c63: 230867 bnone a8, a6, 40005c8a -40005c66: 200877 bnone a8, a7, 40005c8a -40005c69: 224b addi.n a2, a2, 4 -40005c6b: 334b addi.n a3, a3, 4 -40005c6d: 198076 loop a0, 40005c8a -40005c70: 0288 l32i.n a8, a2, 0 -40005c72: 0398 l32i.n a9, a3, 0 -40005c74: 224b addi.n a2, a2, 4 -40005c76: 169897 bne a8, a9, 40005c90 -40005c79: 0d0847 bnone a8, a4, 40005c8a -40005c7c: 0a0857 bnone a8, a5, 40005c8a -40005c7f: 070867 bnone a8, a6, 40005c8a -40005c82: 040877 bnone a8, a7, 40005c8a -40005c85: 334b addi.n a3, a3, 4 -40005c87: fff886 j 40005c6d -40005c8a: 020c movi.n a2, 0 -40005c8c: f01d retw.n -40005c8e: 900000 addx2 a0, a0, a0 -40005c91: 3028 l32i.n a2, a0, 12 -40005c93: 218247 bany a2, a4, 40005cb8 -40005c96: eabf51 l32r a5, 40000794 <_c_0x7fffffff+0x14> -40005c99: ed0847 bnone a8, a4, 40005c8a -40005c9c: 248257 bany a2, a5, 40005cc4 -40005c9f: eabe61 l32r a6, 40000798 <_c_0x00ff0000> -40005ca2: e40857 bnone a8, a5, 40005c8a -40005ca5: 278267 bany a2, a6, 40005cd0 -40005ca8: de0867 bnone a8, a6, 40005c8a -40005cab: 75a880 extui a10, a8, 24, 8 -40005cae: 75b890 extui a11, a9, 24, 8 -40005cb1: c02ab0 sub a2, a10, a11 -40005cb4: f01d retw.n -40005cb6: 800000 add a0, a0, a0 -40005cb9: 9074a0 addx2 a7, a4, a10 -40005cbc: b074b0 addx8 a7, a4, a11 -40005cbf: c02a add.n a12, a0, a2 -40005cc1: f01d retw.n -40005cc3: a88000 excw -40005cc6: b89074 excw -40005cc9: 2ab074 excw -40005ccc: f01dc0 subx8 a1, a13, a12 -40005ccf: a08000 addx4 a8, a0, a0 -40005cd2: b09075 call12 3ffb65d8 <_start-0x49a28> -40005cd5: 2ab075 call12 400307dc <__bss_start+0x207dc> -40005cd8: f01dc0 subx8 a1, a13, a12 - ... - -40005cdc : -40005cdc: 002136 entry a1, 16 -40005cdf: 20a220 or a10, a2, a2 -40005ce2: ffa042 movi a4, 255 -40005ce5: eaab51 l32r a5, 40000794 <_c_0x7fffffff+0x14> -40005ce8: eaac61 l32r a6, 40000798 <_c_0x00ff0000> -40005ceb: eaac71 l32r a7, 4000079c <_c_0x00ff0000+0x4> -40005cee: 0be307 bbsi a3, 0, 40005cfd -40005cf1: 17e317 bbsi a3, 1, 40005d0c -40005cf4: 380c movi.n a8, 3 -40005cf6: 2a0a87 bnone a10, a8, 40005d24 -40005cf9: 0016c6 j 40005d58 -40005cfc: 038200 excw -40005cff: 331b00 clamps a1, a11, 7 -40005d02: 004a82 s8i a8, a10, 0 -40005d05: 889c beqz.n a8, 40005d21 -40005d07: aa1b addi.n a10, a10, 1 -40005d09: e76317 bbci a3, 1, 40005cf4 -40005d0c: 000382 l8ui a8, a3, 0 -40005d0f: 004a82 s8i a8, a10, 0 -40005d12: b88c beqz.n a8, 40005d21 -40005d14: 010382 l8ui a8, a3, 1 -40005d17: 332b addi.n a3, a3, 2 -40005d19: 014a82 s8i a8, a10, 1 -40005d1c: aa2b addi.n a10, a10, 2 -40005d1e: fd2856 bnez a8, 40005cf4 -40005d21: f01d retw.n -40005d23: 080c00 excw -40005d26: 138876 loop a8, 40005d3d -40005d29: 0388 l32i.n a8, a3, 0 -40005d2b: 334b addi.n a3, a3, 4 -40005d2d: 0f0847 bnone a8, a4, 40005d40 -40005d30: 110857 bnone a8, a5, 40005d45 -40005d33: 150867 bnone a8, a6, 40005d4c -40005d36: 0a89 s32i.n a8, a10, 0 -40005d38: 010877 bnone a8, a7, 40005d3d -40005d3b: aa4b addi.n a10, a10, 4 -40005d3d: f01d retw.n -40005d3f: 4a8200 depbits a0, a2, 4, 9 -40005d42: f01d00 subx8 a1, a13, a0 -40005d45: 005a82 s16i a8, a10, 0 -40005d48: f01d retw.n -40005d4a: 820000 mull a0, a0, a0 -40005d4d: 005a add.n a0, a0, a5 -40005d4f: 080c movi.n a8, 0 -40005d51: 024a82 s8i a8, a10, 2 -40005d54: f01d retw.n -40005d56: 0c0000 excw -40005d59: 7608 l32i.n a0, a6, 28 -40005d5b: 0c88 l32i.n a8, a12, 0 -40005d5d: 000382 l8ui a8, a3, 0 -40005d60: 331b addi.n a3, a3, 1 -40005d62: 004a82 s8i a8, a10, 0 -40005d65: aa1b addi.n a10, a10, 1 -40005d67: fff816 beqz a8, 40005d6a -40005d6a: f01d retw.n - -40005d6c : -40005d6c: 002136 entry a1, 16 -40005d6f: fcc232 addi a3, a2, -4 -40005d72: ffa042 movi a4, 255 -40005d75: ea8751 l32r a5, 40000794 <_c_0x7fffffff+0x14> -40005d78: ea8861 l32r a6, 40000798 <_c_0x00ff0000> -40005d7b: ea8871 l32r a7, 4000079c <_c_0x00ff0000+0x4> -40005d7e: 06e207 bbsi a2, 0, 40005d88 -40005d81: 0de217 bbsi a2, 1, 40005d92 -40005d84: 000706 j 40005da4 -40005d87: 038200 excw -40005d8a: 331b04 excw -40005d8d: 88ac beqz.n a8, 40005db9 -40005d8f: 116317 bbci a3, 1, 40005da4 -40005d92: 332b addi.n a3, a3, 2 -40005d94: 0388 l32i.n a8, a3, 0 -40005d96: 2e0867 bnone a8, a6, 40005dc8 -40005d99: 078877 bany a8, a7, 40005da4 -40005d9c: 333b addi.n a3, a3, 3 -40005d9e: c02320 sub a2, a3, a2 -40005da1: f01d retw.n -40005da3: 080c00 excw -40005da6: 0f8876 loop a8, 40005db9 -40005da9: 1388 l32i.n a8, a3, 4 -40005dab: 334b addi.n a3, a3, 4 -40005dad: 0a0847 bnone a8, a4, 40005dbb -40005db0: 0c0857 bnone a8, a5, 40005dc0 -40005db3: 110867 bnone a8, a6, 40005dc8 -40005db6: ff0877 bnone a8, a7, 40005db9 -40005db9: 333b addi.n a3, a3, 3 -40005dbb: c02320 sub a2, a3, a2 -40005dbe: f01d retw.n -40005dc0: 331b addi.n a3, a3, 1 -40005dc2: c02320 sub a2, a3, a2 -40005dc5: f01d retw.n -40005dc7: 332b00 clamps a2, a11, 7 -40005dca: c02320 sub a2, a3, a2 -40005dcd: f01d retw.n - ... - -40005dd0 : -40005dd0: 004136 entry a1, 32 -40005dd3: a4bc beqz.n a4, 40005e11 -40005dd5: 205320 or a5, a3, a2 -40005dd8: 145050 extui a5, a5, 0, 2 -40005ddb: 041516 beqz a5, 40005e20 -40005dde: 000272 l8ui a7, a2, 0 -40005de1: 000382 l8ui a8, a3, 0 -40005de4: 049d mov.n a9, a4 -40005de6: 440b addi.n a4, a4, -1 -40005de8: 09ac beqz.n a9, 40005e0c -40005dea: 1e9787 bne a7, a8, 40005e0c -40005ded: 168076 loop a0, 40005e07 -40005df0: 046d mov.n a6, a4 -40005df2: b49c beqz.n a4, 40005e11 -40005df4: 979c beqz.n a7, 40005e11 -40005df6: 440b addi.n a4, a4, -1 -40005df8: 331b addi.n a3, a3, 1 -40005dfa: 221b addi.n a2, a2, 1 -40005dfc: 569c beqz.n a6, 40005e15 -40005dfe: 000272 l8ui a7, a2, 0 -40005e01: 000382 l8ui a8, a3, 0 -40005e04: 049877 bne a8, a7, 40005e0c -40005e07: fff886 j 40005ded -40005e0a: 800000 add a0, a0, a0 -40005e0d: 1dc027 bnall a0, a2, 40005e2e -40005e10: 020cf0 andb b0, b12, b15 -40005e13: f01d retw.n -40005e15: 000352 l8ui a5, a3, 0 -40005e18: 000222 l8ui a2, a2, 0 -40005e1b: c02250 sub a2, a2, a5 -40005e1e: f01d retw.n -40005e20: ba44b6 bltui a4, 4, 40005dde -40005e23: 0278 l32i.n a7, a2, 0 -40005e25: 002362 l32i a6, a3, 0 -40005e28: b29677 bne a6, a7, 40005dde -40005e2b: ea5e91 l32r a9, 400007a4 <_c_0x00ff0000+0xc> -40005e2e: ffafa2 movi a10, -1 -40005e31: ea5d81 l32r a8, 400007a8 <_c_0x00ff0000+0x10> -40005e34: fcc442 addi a4, a4, -4 -40005e37: fd6416 beqz a4, 40005e11 -40005e3a: 30c7a0 xor a12, a7, a10 -40005e3d: b78a add.n a11, a7, a8 -40005e3f: 10bbc0 and a11, a11, a12 -40005e42: cb89b7 bany a9, a11, 40005e11 -40005e45: 334b addi.n a3, a3, 4 -40005e47: 224b addi.n a2, a2, 4 -40005e49: 9144b6 bltui a4, 4, 40005dde -40005e4c: 0278 l32i.n a7, a2, 0 -40005e4e: 03d8 l32i.n a13, a3, 0 -40005e50: e01d77 beq a13, a7, 40005e34 -40005e53: ffe1c6 j 40005dde -40005e56: 820000 mull a0, a0, a0 -40005e59: 1b0003 excw -40005e5c: 4a8233 excw -40005e5f: 440b00 extui a0, a0, 11, 5 -40005e62: 74ac beqz.n a4, 40005e8d -40005e64: aa1b addi.n a10, a10, 1 -40005e66: 048816 beqz a8, 40005eb2 -40005e69: 3d6317 bbci a3, 1, 40005eaa -40005e6c: 000382 l8ui a8, a3, 0 -40005e6f: 440b addi.n a4, a4, -1 -40005e71: 004a82 s8i a8, a10, 0 -40005e74: 549c beqz.n a4, 40005e8d -40005e76: aa1b addi.n a10, a10, 1 -40005e78: 68bc beqz.n a8, 40005eb2 -40005e7a: 010382 l8ui a8, a3, 1 -40005e7d: 332b addi.n a3, a3, 2 -40005e7f: 004a82 s8i a8, a10, 0 -40005e82: 440b addi.n a4, a4, -1 -40005e84: 548c beqz.n a4, 40005e8d -40005e86: aa1b addi.n a10, a10, 1 -40005e88: e8dc bnez.n a8, 40005eaa -40005e8a: 000906 j 40005eb2 -40005e8d: f01d retw.n - ... - -40005e90 : -40005e90: 002136 entry a1, 16 -40005e93: 02ad mov.n a10, a2 -40005e95: ff4416 beqz a4, 40005e8d -40005e98: ffa0b2 movi a11, 255 -40005e9b: ea3e51 l32r a5, 40000794 <_c_0x7fffffff+0x14> -40005e9e: ea3e61 l32r a6, 40000798 <_c_0x00ff0000> -40005ea1: ea3e71 l32r a7, 4000079c <_c_0x00ff0000+0x4> -40005ea4: b0e307 bbsi a3, 0, 40005e58 -40005ea7: c1e317 bbsi a3, 1, 40005e6c -40005eaa: 380c movi.n a8, 3 -40005eac: 540a87 bnone a10, a8, 40005f04 -40005eaf: 002746 j 40005f50 -40005eb2: 090c movi.n a9, 0 -40005eb4: 28ea07 bbsi a10, 0, 40005ee0 -40005eb7: 32ea17 bbsi a10, 1, 40005eed -40005eba: 1344a6 blti a4, 4, 40005ed1 -40005ebd: 218240 srai a8, a4, 2 -40005ec0: f03d nop.n -40005ec2: 038876 loop a8, 40005ec9 -40005ec5: 0a99 s32i.n a9, a10, 0 -40005ec7: aa4b addi.n a10, a10, 4 -40005ec9: 1188e0 slli a8, a8, 2 -40005ecc: c04480 sub a4, a4, a8 -40005ecf: 948c beqz.n a4, 40005edc -40005ed1: 004a92 s8i a9, a10, 0 -40005ed4: 440b addi.n a4, a4, -1 -40005ed6: 01caa2 addi a10, a10, 1 -40005ed9: ff4456 bnez a4, 40005ed1 -40005edc: f01d retw.n -40005ede: 920000 excw -40005ee1: 004a add.n a0, a0, a4 -40005ee3: 440b addi.n a4, a4, -1 -40005ee5: ff3416 beqz a4, 40005edc -40005ee8: aa1b addi.n a10, a10, 1 -40005eea: cc6a17 bbci a10, 1, 40005eba -40005eed: 004a92 s8i a9, a10, 0 -40005ef0: 440b addi.n a4, a4, -1 -40005ef2: fe6416 beqz a4, 40005edc -40005ef5: 014a92 s8i a9, a10, 1 -40005ef8: 440b addi.n a4, a4, -1 -40005efa: fde416 beqz a4, 40005edc -40005efd: aa2b addi.n a10, a10, 2 -40005eff: ffedc6 j 40005eba -40005f02: 0c0000 excw -40005f05: 7608 l32i.n a0, a6, 28 -40005f07: 1988 l32i.n a8, a9, 4 -40005f09: 4354a6 blti a4, 5, 40005f50 -40005f0c: 0388 l32i.n a8, a3, 0 -40005f0e: 334b addi.n a3, a3, 4 -40005f10: 1408b7 bnone a8, a11, 40005f28 -40005f13: 1d0857 bnone a8, a5, 40005f34 -40005f16: 260867 bnone a8, a6, 40005f40 -40005f19: 0a89 s32i.n a8, a10, 0 -40005f1b: fcc442 addi a4, a4, -4 -40005f1e: aa4b addi.n a10, a10, 4 -40005f20: 8e0877 bnone a8, a7, 40005eb2 -40005f23: ffe2c6 j 40005eb2 -40005f26: 820000 mull a0, a0, a0 -40005f29: 004a add.n a0, a0, a4 -40005f2b: 440b addi.n a4, a4, -1 -40005f2d: aa1b addi.n a10, a10, 1 -40005f2f: ffdfc6 j 40005eb2 -40005f32: 820000 mull a0, a0, a0 -40005f35: 005a add.n a0, a0, a5 -40005f37: fec442 addi a4, a4, -2 -40005f3a: aa2b addi.n a10, a10, 2 -40005f3c: ffdc86 j 40005eb2 -40005f3f: 5a8200 depbits a0, a2, 5, 9 -40005f42: 080c00 excw -40005f45: 024a82 s8i a8, a10, 2 -40005f48: fdc442 addi a4, a4, -3 -40005f4b: aa3b addi.n a10, a10, 3 -40005f4d: ffd846 j 40005eb2 -40005f50: 080c movi.n a8, 0 -40005f52: 108876 loop a8, 40005f66 -40005f55: 000382 l8ui a8, a3, 0 -40005f58: 331b addi.n a3, a3, 1 -40005f5a: 004a82 s8i a8, a10, 0 -40005f5d: 440b addi.n a4, a4, -1 -40005f5f: 648c beqz.n a4, 40005f69 -40005f61: aa1b addi.n a10, a10, 1 -40005f63: fff816 beqz a8, 40005f66 -40005f66: ffd206 j 40005eb2 -40005f69: f01d retw.n - ... - -40005f6c : -40005f6c: 090136 entry a1, 0x480 -40005f6f: 50c172 addi a7, a1, 80 -40005f72: 025d mov.n a5, a2 -40005f74: 038d mov.n a8, a3 -40005f76: 20c220 or a12, a2, a2 -40005f79: 0003b2 l8ui a11, a3, 0 -40005f7c: 0002e2 l8ui a14, a2, 0 -40005f7f: 209bb0 or a9, a11, a11 -40005f82: eebc beqz.n a14, 40005fc4 -40005f84: cbac beqz.n a11, 40005fb4 -40005f86: 01a0a2 movi a10, 1 -40005f89: 228076 loop a0, 40005faf -40005f8c: cc1b addi.n a12, a12, 1 -40005f8e: 1d0c movi.n a13, 1 -40005f90: 74f0e0 extui a15, a14, 0, 8 -40005f93: 040c movi.n a4, 0 -40005f95: 74e090 extui a14, a9, 0, 8 -40005f98: c0eef0 sub a14, a14, a15 -40005f9b: 834de0 moveqz a4, a13, a14 -40005f9e: 10aa40 and a10, a10, a4 -40005fa1: 000ce2 l8ui a14, a12, 0 -40005fa4: 481b addi.n a4, a8, 1 -40005fa6: 048d mov.n a8, a4 -40005fa8: ce8c beqz.n a14, 40005fb8 -40005faa: 000492 l8ui a9, a4, 0 -40005fad: 198c beqz.n a9, 40005fb2 -40005faf: fff586 j 40005f89 -40005fb2: 4a9c beqz.n a10, 40005fca -40005fb4: 052d mov.n a2, a5 -40005fb6: f01d retw.n -40005fb8: 000482 l8ui a8, a4, 0 -40005fbb: ff3816 beqz a8, 40005fb2 -40005fbe: 020c movi.n a2, 0 -40005fc0: f01d retw.n -40005fc2: 560000 excw -40005fc5: ff6b addi.n a15, a15, 6 -40005fc7: fffa46 j 40005fb4 -40005fca: 01c5a2 addi a10, a5, 1 -40005fcd: 005aa5 call8 40006578 -40005fd0: 202aa0 or a2, a10, a10 -40005fd3: 271a16 beqz a10, 40006248 -40005fd6: c04430 sub a4, a4, a3 -40005fd9: ffc492 addi a9, a4, -1 -40005fdc: 268916 beqz a9, 40006248 -40005fdf: 845a add.n a8, a4, a5 -40005fe1: 0538a7 bltu a8, a10, 40005fea -40005fe4: c088a0 sub a8, a8, a10 -40005fe7: 000046 j 40005fec -40005fea: 180c movi.n a8, 1 -40005fec: 18c4f6 bgeui a4, 32, 40006008 -40005fef: 085d mov.n a5, a8 -40005ff1: 0224f6 bgeui a4, 2, 40005ff7 -40005ff4: 002f06 j 400060b4 -40005ff7: 190c movi.n a9, 1 -40005ff9: 180c movi.n a8, 1 -40005ffb: 1f0c movi.n a15, 1 -40005ffd: 0a0c movi.n a10, 0 -40005fff: f67c movi.n a6, -1 -40006001: 5159 s32i.n a5, a1, 20 -40006003: 0008c6 j 4000602a -40006006: 820000 mull a0, a0, a0 -40006009: f61061 l32r a6, 4000384c <_X_recv_packet+0x10> -4000600c: 860224 excw -4000600f: 0c0067 bnone a0, a6, 4000601f -40006012: 0c19 s32i.n a1, a12, 0 -40006014: 0c18 l32i.n a1, a12, 0 -40006016: 1f .byte 0x1f -40006017: 0a0c movi.n a10, 0 -40006019: f67c movi.n a6, -1 -4000601b: 0042c6 j 4000612a -4000601e: 09ad mov.n a10, a9 -40006020: c0f960 sub a15, a9, a6 -40006023: 180c movi.n a8, 1 -40006025: 98aa add.n a9, a8, a10 -40006027: 2db947 bgeu a9, a4, 40006058 -4000602a: b86a add.n a11, a8, a6 -4000602c: c93a add.n a12, a9, a3 -4000602e: 000cc2 l8ui a12, a12, 0 -40006031: b3ba add.n a11, a3, a11 -40006033: 000bb2 l8ui a11, a11, 0 -40006036: e43cb7 bltu a12, a11, 4000601e -40006039: 0f9cb7 bne a12, a11, 4000604c -4000603c: 0418f7 beq a8, a15, 40006044 -4000603f: 881b addi.n a8, a8, 1 -40006041: fff806 j 40006025 -40006044: afaa add.n a10, a15, a10 -40006046: 180c movi.n a8, 1 -40006048: fff646 j 40006025 -4000604b: 180c00 excw -4000604e: 0a6d mov.n a6, a10 -40006050: 1f0c movi.n a15, 1 -40006052: aa1b addi.n a10, a10, 1 -40006054: fff346 j 40006025 -40006057: 190c00 excw -4000605a: f57c movi.n a5, -1 -4000605c: 180c movi.n a8, 1 -4000605e: 11f9 s32i.n a15, a1, 4 -40006060: 0a0c movi.n a10, 0 -40006062: 1f0c movi.n a15, 1 -40006064: 000346 j 40006075 -40006067: ad0000 excw -4000606a: 5009 s32i.n a0, a0, 20 -4000606c: c0f9 s32i.n a15, a0, 48 -4000606e: 180c movi.n a8, 1 -40006070: 98aa add.n a9, a8, a10 -40006072: 2eb947 bgeu a9, a4, 400060a4 -40006075: b58a add.n a11, a5, a8 -40006077: c93a add.n a12, a9, a3 -40006079: 000cc2 l8ui a12, a12, 0 -4000607c: b3ba add.n a11, a3, a11 -4000607e: 000bb2 l8ui a11, a11, 0 -40006081: e43bc7 bltu a11, a12, 40006069 -40006084: 109cb7 bne a12, a11, 40006098 -40006087: 0518f7 beq a8, a15, 40006090 -4000608a: 881b addi.n a8, a8, 1 -4000608c: fff806 j 40006070 -4000608f: afaa00 excw -40006092: 180c movi.n a8, 1 -40006094: fff606 j 40006070 -40006097: 180c00 excw -4000609a: 0a5d mov.n a5, a10 -4000609c: 1f0c movi.n a15, 1 -4000609e: aa1b addi.n a10, a10, 1 -400060a0: fff306 j 40006070 -400060a3: 961b00 excw -400060a6: c51b addi.n a12, a5, 1 -400060a8: 5158 l32i.n a5, a1, 20 -400060aa: 0abc97 bgeu a12, a9, 400060b8 -400060ad: 1168 l32i.n a6, a1, 4 -400060af: 09cd mov.n a12, a9 -400060b1: 000146 j 400060ba -400060b4: 0c0c movi.n a12, 0 -400060b6: 1f0c movi.n a15, 1 -400060b8: 0f6d mov.n a6, a15 -400060ba: 81c9 s32i.n a12, a1, 32 -400060bc: 03ad mov.n a10, a3 -400060be: b36a add.n a11, a3, a6 -400060c0: 0082a5 call8 400068ec -400060c3: 5159 s32i.n a5, a1, 20 -400060c5: 213a16 beqz a10, 400062dc -400060c8: 81e8 l32i.n a14, a1, 32 -400060ca: 6e0b addi.n a6, a14, -1 -400060cc: c0f4e0 sub a15, a4, a14 -400060cf: 6169 s32i.n a6, a1, 24 -400060d1: 00a062 movi a6, 0 -400060d4: 73eef0 maxu a14, a14, a15 -400060d7: 21e9 s32i.n a14, a1, 8 -400060d9: a25a add.n a10, a2, a5 -400060db: c46a add.n a12, a4, a6 -400060dd: 00a0b2 movi a11, 0 -400060e0: 0761c2 s32i a12, a1, 28 -400060e3: c0cc50 sub a12, a12, a5 -400060e6: 0041a5 call8 40006500 -400060e9: 15da56 bnez a10, 4000624a -400060ec: 71d8 l32i.n a13, a1, 28 -400060ee: 81e8 l32i.n a14, a1, 32 -400060f0: 0d5d mov.n a5, a13 -400060f2: 154d16 beqz a13, 4000624a -400060f5: 0e8d mov.n a8, a14 -400060f7: 11be47 bgeu a14, a4, 4000610c -400060fa: fe6a add.n a15, a14, a6 -400060fc: 9e3a add.n a9, a14, a3 -400060fe: 000992 l8ui a9, a9, 0 -40006101: f2fa add.n a15, a2, a15 -40006103: 000ff2 l8ui a15, a15, 0 -40006106: c0ff90 sub a15, a15, a9 -40006109: 154f16 beqz a15, 40006261 -4000610c: 023847 bltu a8, a4, 40006112 -4000610f: 0054c6 j 40006266 -40006112: 81a8 l32i.n a10, a1, 32 -40006114: c0a8a0 sub a10, a8, a10 -40006117: aa1b addi.n a10, a10, 1 -40006119: 66aa add.n a6, a6, a10 -4000611b: ffee86 j 400060d9 -4000611e: 09ad mov.n a10, a9 -40006120: c0f960 sub a15, a9, a6 -40006123: 180c movi.n a8, 1 -40006125: 98aa add.n a9, a8, a10 -40006127: 2db947 bgeu a9, a4, 40006158 -4000612a: b86a add.n a11, a8, a6 -4000612c: c93a add.n a12, a9, a3 -4000612e: 000cc2 l8ui a12, a12, 0 -40006131: b3ba add.n a11, a3, a11 -40006133: 000bb2 l8ui a11, a11, 0 -40006136: e43cb7 bltu a12, a11, 4000611e -40006139: 0f9cb7 bne a12, a11, 4000614c -4000613c: 0418f7 beq a8, a15, 40006144 -4000613f: 881b addi.n a8, a8, 1 -40006141: fff806 j 40006125 -40006144: afaa add.n a10, a15, a10 -40006146: 180c movi.n a8, 1 -40006148: fff646 j 40006125 -4000614b: 180c00 excw -4000614e: 0a6d mov.n a6, a10 -40006150: 1f0c movi.n a15, 1 -40006152: aa1b addi.n a10, a10, 1 -40006154: fff346 j 40006125 -40006157: 190c00 excw -4000615a: f57c movi.n a5, -1 -4000615c: 180c movi.n a8, 1 -4000615e: f1f9 s32i.n a15, a1, 60 -40006160: 0a0c movi.n a10, 0 -40006162: 1f0c movi.n a15, 1 -40006164: 000346 j 40006175 -40006167: ad0000 excw -4000616a: 5009 s32i.n a0, a0, 20 -4000616c: c0f9 s32i.n a15, a0, 48 -4000616e: 180c movi.n a8, 1 -40006170: 98aa add.n a9, a8, a10 -40006172: 2eb947 bgeu a9, a4, 400061a4 -40006175: b58a add.n a11, a5, a8 -40006177: c93a add.n a12, a9, a3 -40006179: 000cc2 l8ui a12, a12, 0 -4000617c: b3ba add.n a11, a3, a11 -4000617e: 000bb2 l8ui a11, a11, 0 -40006181: e43bc7 bltu a11, a12, 40006169 -40006184: 109cb7 bne a12, a11, 40006198 -40006187: 0518f7 beq a8, a15, 40006190 -4000618a: 881b addi.n a8, a8, 1 -4000618c: fff806 j 40006170 -4000618f: afaa00 excw -40006192: 180c movi.n a8, 1 -40006194: fff606 j 40006170 -40006197: 180c00 excw -4000619a: 0a5d mov.n a5, a10 -4000619c: 1f0c movi.n a15, 1 -4000619e: aa1b addi.n a10, a10, 1 -400061a0: fff306 j 40006170 -400061a3: 961b00 excw -400061a6: c51b addi.n a12, a5, 1 -400061a8: 08bc97 bgeu a12, a9, 400061b4 -400061ab: 09ed mov.n a14, a9 -400061ad: 0001c6 j 400061b8 -400061b0: 0c0c movi.n a12, 0 -400061b2: 1f0c movi.n a15, 1 -400061b4: 0ced mov.n a14, a12 -400061b6: f1f9 s32i.n a15, a1, 60 -400061b8: 0a2c movi.n a10, 32 -400061ba: 10c762 addi a6, a7, 16 -400061bd: 068d mov.n a8, a6 -400061bf: 12aa76 loopgtz a10, 400061d5 -400061c2: 0849 s32i.n a4, a8, 0 -400061c4: 1849 s32i.n a4, a8, 4 -400061c6: 2849 s32i.n a4, a8, 8 -400061c8: 3849 s32i.n a4, a8, 12 -400061ca: 4849 s32i.n a4, a8, 16 -400061cc: 5849 s32i.n a4, a8, 20 -400061ce: 6849 s32i.n a4, a8, 24 -400061d0: 7849 s32i.n a4, a8, 28 -400061d2: 20c882 addi a8, a8, 32 -400061d5: 080c movi.n a8, 0 -400061d7: a40b addi.n a10, a4, -1 -400061d9: e1a9 s32i.n a10, a1, 56 -400061db: 10a476 loopgtz a4, 400061ef -400061de: 938a add.n a9, a3, a8 -400061e0: 000992 l8ui a9, a9, 0 -400061e3: 01c882 addi a8, a8, 1 -400061e6: a09960 addx4 a9, a9, a6 -400061e9: 0069a2 s32i a10, a9, 0 -400061ec: ffcaa2 addi a10, a10, -1 -400061ef: 0ecd mov.n a12, a14 -400061f1: d1e9 s32i.n a14, a1, 52 -400061f3: f1b8 l32i.n a11, a1, 60 -400061f5: 03ad mov.n a10, a3 -400061f7: b3ba add.n a11, a3, a11 -400061f9: 006f25 call8 400068ec -400061fc: 174a16 beqz a10, 40006374 -400061ff: d1d8 l32i.n a13, a1, 52 -40006201: 050c movi.n a5, 0 -40006203: c0e4d0 sub a14, a4, a13 -40006206: 73ede0 maxu a14, a13, a14 -40006209: a1e9 s32i.n a14, a1, 40 -4000620b: dd0b addi.n a13, a13, -1 -4000620d: b1d9 s32i.n a13, a1, 44 -4000620f: 000686 j 4000622d -40006212: c1f8 l32i.n a15, a1, 48 -40006214: 1061f2 s32i a15, a1, 64 -40006217: 9fac beqz.n a15, 40006244 -40006219: 8092f0 add a9, a2, a15 -4000621c: ffd992 addmi a9, a9, 0xffffff00 -4000621f: ff0992 l8ui a9, a9, 255 -40006222: a09960 addx4 a9, a9, a6 -40006225: 0998 l32i.n a9, a9, 0 -40006227: 07a916 beqz a9, 400062a5 -4000622a: 805950 add a5, a9, a5 -4000622d: 00a0b2 movi a11, 0 -40006230: 1021d2 l32i a13, a1, 64 -40006233: 80c450 add a12, a4, a5 -40006236: 0c61c2 s32i a12, a1, 48 -40006239: a2da add.n a10, a2, a13 -4000623b: c0ccd0 sub a12, a12, a13 -4000623e: 002c25 call8 40006500 -40006241: fcda16 beqz a10, 40006212 -40006244: 020c movi.n a2, 0 -40006246: f01d retw.n -40006248: f01d retw.n -4000624a: 020c movi.n a2, 0 -4000624c: f01d retw.n -4000624e: e86a add.n a14, a8, a6 -40006250: f38a add.n a15, a3, a8 -40006252: 000ff2 l8ui a15, a15, 0 -40006255: 80e2e0 add a14, a2, a14 -40006258: 000ee2 l8ui a14, a14, 0 -4000625b: c0eef0 sub a14, a14, a15 -4000625e: eaae56 bnez a14, 4000610c -40006261: 881b addi.n a8, a8, 1 -40006263: e73847 bltu a8, a4, 4000624e -40006266: 8198 l32i.n a9, a1, 32 -40006268: 6188 l32i.n a8, a1, 24 -4000626a: 69ac beqz.n a9, 40006294 -4000626c: a86a add.n a10, a8, a6 -4000626e: b83a add.n a11, a8, a3 -40006270: 000bb2 l8ui a11, a11, 0 -40006273: 80a2a0 add a10, a2, a10 -40006276: 000aa2 l8ui a10, a10, 0 -40006279: 179ab7 bne a10, a11, 40006294 -4000627c: 126162 s32i a6, a1, 72 -4000627f: 880b addi.n a8, a8, -1 -40006281: 4f0826 beqi a8, -1, 400062d4 -40006284: c86a add.n a12, a8, a6 -40006286: d38a add.n a13, a3, a8 -40006288: 000dd2 l8ui a13, a13, 0 -4000628b: 80c2c0 add a12, a2, a12 -4000628e: 000cc2 l8ui a12, a12, 0 -40006291: e71cd7 beq a12, a13, 4000627c -40006294: 126162 s32i a6, a1, 72 -40006297: 390826 beqi a8, -1, 400062d4 -4000629a: 21e8 l32i.n a14, a1, 8 -4000629c: ee1b addi.n a14, a14, 1 -4000629e: 66ea add.n a6, a6, a14 -400062a0: ff8d46 j 400060d9 -400062a3: f80000 excw -400062a6: e198d1 l32r a13, 3fffe908 <_start-0x16f8> -400062a9: 0f8d mov.n a8, a15 -400062ab: 11bf97 bgeu a15, a9, 400062c0 -400062ae: af5a add.n a10, a15, a5 -400062b0: bf3a add.n a11, a15, a3 -400062b2: 000bb2 l8ui a11, a11, 0 -400062b5: a2aa add.n a10, a2, a10 -400062b7: 000aa2 l8ui a10, a10, 0 -400062ba: c0aab0 sub a10, a10, a11 -400062bd: 145a16 beqz a10, 40006406 -400062c0: e1c8 l32i.n a12, a1, 56 -400062c2: 0238c7 bltu a8, a12, 400062c8 -400062c5: 005106 j 4000640d -400062c8: d1d8 l32i.n a13, a1, 52 -400062ca: c0d8d0 sub a13, a8, a13 -400062cd: dd1b addi.n a13, a13, 1 -400062cf: 55da add.n a5, a5, a13 -400062d1: ffd606 j 4000622d -400062d4: 1221e2 l32i a14, a1, 72 -400062d7: 22ea add.n a2, a2, a14 -400062d9: f01d retw.n -400062db: 714900 excw -400062de: 090c movi.n a9, 0 -400062e0: 8158 l32i.n a5, a1, 32 -400062e2: c0f460 sub a15, a4, a6 -400062e5: 31f9 s32i.n a15, a1, 12 -400062e7: 550b addi.n a5, a5, -1 -400062e9: 6159 s32i.n a5, a1, 24 -400062eb: 050c movi.n a5, 0 -400062ed: 00a0b2 movi a11, 0 -400062f0: 116192 s32i a9, a1, 68 -400062f3: 0521d2 l32i a13, a1, 20 -400062f6: 0721c2 l32i a12, a1, 28 -400062f9: a2da add.n a10, a2, a13 -400062fb: c0ccd0 sub a12, a12, a13 -400062fe: 002025 call8 40006500 -40006301: 112192 l32i a9, a1, 68 -40006304: f42a56 bnez a10, 4000624a -40006307: 71e8 l32i.n a14, a1, 28 -40006309: 51e9 s32i.n a14, a1, 20 -4000630b: f3be16 beqz a14, 4000624a -4000630e: 8188 l32i.n a8, a1, 32 -40006310: 738890 maxu a8, a8, a9 -40006313: 2ab847 bgeu a8, a4, 40006341 -40006316: a85a add.n a10, a8, a5 -40006318: b83a add.n a11, a8, a3 -4000631a: 000bb2 l8ui a11, a11, 0 -4000631d: a2aa add.n a10, a2, a10 -4000631f: 000aa2 l8ui a10, a10, 0 -40006322: c0aab0 sub a10, a10, a11 -40006325: 3a9c beqz.n a10, 4000633c -40006327: 004746 j 40006448 -4000632a: c85a add.n a12, a8, a5 -4000632c: d38a add.n a13, a3, a8 -4000632e: 000dd2 l8ui a13, a13, 0 -40006331: c2ca add.n a12, a2, a12 -40006333: 000cc2 l8ui a12, a12, 0 -40006336: c0ccd0 sub a12, a12, a13 -40006339: 10bc56 bnez a12, 40006448 -4000633c: 881b addi.n a8, a8, 1 -4000633e: e83847 bltu a8, a4, 4000632a -40006341: 81e8 l32i.n a14, a1, 32 -40006343: 6188 l32i.n a8, a1, 24 -40006345: 11b9e7 bgeu a9, a14, 4000635a -40006348: f85a add.n a15, a8, a5 -4000634a: a83a add.n a10, a8, a3 -4000634c: 000aa2 l8ui a10, a10, 0 -4000634f: f2fa add.n a15, a2, a15 -40006351: 000ff2 l8ui a15, a15, 0 -40006354: c0ffa0 sub a15, a15, a10 -40006357: 117f16 beqz a15, 40006472 -4000635a: 81a8 l32i.n a10, a1, 32 -4000635c: 126152 s32i a5, a1, 72 -4000635f: b91b addi.n a11, a9, 1 -40006361: 02bab7 bgeu a10, a11, 40006367 -40006364: ffdb06 j 400062d4 -40006367: 556a add.n a5, a5, a6 -40006369: 71c8 l32i.n a12, a1, 28 -4000636b: 3198 l32i.n a9, a1, 12 -4000636d: cc6a add.n a12, a12, a6 -4000636f: 71c9 s32i.n a12, a1, 28 -40006371: ffde06 j 400062ed -40006374: c149 s32i.n a4, a1, 48 -40006376: 050c movi.n a5, 0 -40006378: d1f8 l32i.n a15, a1, 52 -4000637a: f1e8 l32i.n a14, a1, 60 -4000637c: 0d0c movi.n a13, 0 -4000637e: 91d9 s32i.n a13, a1, 36 -40006380: c0e4e0 sub a14, a4, a14 -40006383: ff0b addi.n a15, a15, -1 -40006385: b1f9 s32i.n a15, a1, 44 -40006387: 41e9 s32i.n a14, a1, 16 -40006389: 000546 j 400063a2 -4000638c: 919800 excw -4000638f: 598c beqz.n a9, 40006398 -40006391: f1a8 l32i.n a10, a1, 60 -40006393: 01b8a7 bgeu a8, a10, 40006398 -40006396: 4188 l32i.n a8, a1, 16 -40006398: 585a add.n a5, a8, a5 -4000639a: 0c0c movi.n a12, 0 -4000639c: 91c9 s32i.n a12, a1, 36 -4000639e: b45a add.n a11, a4, a5 -400063a0: c1b9 s32i.n a11, a1, 48 -400063a2: 00a0b2 movi a11, 0 -400063a5: 1021d2 l32i a13, a1, 64 -400063a8: 0c21c2 l32i a12, a1, 48 -400063ab: 80a2d0 add a10, a2, a13 -400063ae: c0ccd0 sub a12, a12, a13 -400063b1: 0014e5 call8 40006500 -400063b4: e8ca56 bnez a10, 40006244 -400063b7: c1e8 l32i.n a14, a1, 48 -400063b9: 1061e2 s32i a14, a1, 64 -400063bc: e84e16 beqz a14, 40006244 -400063bf: 82ea add.n a8, a2, a14 -400063c1: ffd882 addmi a8, a8, 0xffffff00 -400063c4: ff0882 l8ui a8, a8, 255 -400063c7: a08860 addx4 a8, a8, a6 -400063ca: 0888 l32i.n a8, a8, 0 -400063cc: fbd856 bnez a8, 4000638d -400063cf: 9198 l32i.n a9, a1, 36 -400063d1: d188 l32i.n a8, a1, 52 -400063d3: 738890 maxu a8, a8, a9 -400063d6: e198 l32i.n a9, a1, 56 -400063d8: 023897 bltu a8, a9, 400063de -400063db: 002d46 j 40006494 -400063de: a85a add.n a10, a8, a5 -400063e0: b83a add.n a11, a8, a3 -400063e2: 000bb2 l8ui a11, a11, 0 -400063e5: a2aa add.n a10, a2, a10 -400063e7: 000aa2 l8ui a10, a10, 0 -400063ea: c0aab0 sub a10, a10, a11 -400063ed: 09ca16 beqz a10, 4000648d -400063f0: 003586 j 400064ca -400063f3: c58a00 extui a8, a0, 26, 13 -400063f6: d38a add.n a13, a3, a8 -400063f8: 000dd2 l8ui a13, a13, 0 -400063fb: c2ca add.n a12, a2, a12 -400063fd: 000cc2 l8ui a12, a12, 0 -40006400: c0ccd0 sub a12, a12, a13 -40006403: eb9c56 bnez a12, 400062c0 -40006406: e1e8 l32i.n a14, a1, 56 -40006408: 881b addi.n a8, a8, 1 -4000640a: e638e7 bltu a8, a14, 400063f4 -4000640d: d1f8 l32i.n a15, a1, 52 -4000640f: b188 l32i.n a8, a1, 44 -40006411: 3fac beqz.n a15, 40006438 -40006413: 985a add.n a9, a8, a5 -40006415: a83a add.n a10, a8, a3 -40006417: 000aa2 l8ui a10, a10, 0 -4000641a: 929a add.n a9, a2, a9 -4000641c: 000992 l8ui a9, a9, 0 -4000641f: 1599a7 bne a9, a10, 40006438 -40006422: 880b addi.n a8, a8, -1 -40006424: b81b addi.n a11, a8, 1 -40006426: 09cb16 beqz a11, 400064c6 -40006429: c58a add.n a12, a5, a8 -4000642b: d38a add.n a13, a3, a8 -4000642d: 000dd2 l8ui a13, a13, 0 -40006430: c2ca add.n a12, a2, a12 -40006432: 000cc2 l8ui a12, a12, 0 -40006435: e91cd7 beq a12, a13, 40006422 -40006438: 020866 bnei a8, -1, 4000643e -4000643b: 0021c6 j 400064c6 -4000643e: a1e8 l32i.n a14, a1, 40 -40006440: ee1b addi.n a14, a14, 1 -40006442: 55ea add.n a5, a5, a14 -40006444: ff7946 j 4000622d -40006447: 384700 excw -4000644a: bc8602 excw -4000644d: ff .byte 0xff -4000644e: 8198 l32i.n a9, a1, 32 -40006450: c09890 sub a9, a8, a9 -40006453: 991b addi.n a9, a9, 1 -40006455: 559a add.n a5, a5, a9 -40006457: 090c movi.n a9, 0 -40006459: f45a add.n a15, a4, a5 -4000645b: 71f9 s32i.n a15, a1, 28 -4000645d: ffa306 j 400062ed -40006460: b85a add.n a11, a8, a5 -40006462: c38a add.n a12, a3, a8 -40006464: 000cc2 l8ui a12, a12, 0 -40006467: b2ba add.n a11, a2, a11 -40006469: 000bb2 l8ui a11, a11, 0 -4000646c: 021bc7 beq a11, a12, 40006472 -4000646f: ffba46 j 4000635c -40006472: 08ad mov.n a10, a8 -40006474: 880b addi.n a8, a8, -1 -40006476: e639a7 bltu a9, a10, 40006460 -40006479: ffb7c6 j 4000635c -4000647c: 8a0000 depbits a0, a0, 8, 1 -4000647f: e38ad5 call4 3ffe9d2c <_start-0x162d4> -40006482: 000ee2 l8ui a14, a14, 0 -40006485: d2da add.n a13, a2, a13 -40006487: 000dd2 l8ui a13, a13, 0 -4000648a: 3c9de7 bne a13, a14, 400064ca -4000648d: e1f8 l32i.n a15, a1, 56 -4000648f: 881b addi.n a8, a8, 1 -40006491: e938f7 bltu a8, a15, 4000647e -40006494: d1a8 l32i.n a10, a1, 52 -40006496: 9198 l32i.n a9, a1, 36 -40006498: b188 l32i.n a8, a1, 44 -4000649a: 0eb9a7 bgeu a9, a10, 400064ac -4000649d: b85a add.n a11, a8, a5 -4000649f: c83a add.n a12, a8, a3 -400064a1: 000cc2 l8ui a12, a12, 0 -400064a4: b2ba add.n a11, a2, a11 -400064a6: 000bb2 l8ui a11, a11, 0 -400064a9: 471bc7 beq a11, a12, 400064f4 -400064ac: d198 l32i.n a9, a1, 52 -400064ae: 91d8 l32i.n a13, a1, 36 -400064b0: dd1b addi.n a13, a13, 1 -400064b2: 1039d7 bltu a9, a13, 400064c6 -400064b5: c1f8 l32i.n a15, a1, 48 -400064b7: f188 l32i.n a8, a1, 60 -400064b9: 41e8 l32i.n a14, a1, 16 -400064bb: 91e9 s32i.n a14, a1, 36 -400064bd: 558a add.n a5, a5, a8 -400064bf: ff8a add.n a15, a15, a8 -400064c1: c1f9 s32i.n a15, a1, 48 -400064c3: ffb6c6 j 400063a2 -400064c6: 225a add.n a2, a2, a5 -400064c8: f01d retw.n -400064ca: e198 l32i.n a9, a1, 56 -400064cc: c4b897 bgeu a8, a9, 40006494 -400064cf: d1c8 l32i.n a12, a1, 52 -400064d1: 0b0c movi.n a11, 0 -400064d3: 91b9 s32i.n a11, a1, 36 -400064d5: c0c8c0 sub a12, a8, a12 -400064d8: cc1b addi.n a12, a12, 1 -400064da: 55ca add.n a5, a5, a12 -400064dc: a45a add.n a10, a4, a5 -400064de: c1a9 s32i.n a10, a1, 48 -400064e0: ffaf86 j 400063a2 -400064e3: 8a0000 depbits a0, a0, 8, 1 -400064e6: e38ad5 call4 3ffe9d94 <_start-0x1626c> -400064e9: 000ee2 l8ui a14, a14, 0 -400064ec: d2da add.n a13, a2, a13 -400064ee: 000dd2 l8ui a13, a13, 0 -400064f1: b99de7 bne a13, a14, 400064ae -400064f4: 089d mov.n a9, a8 -400064f6: 91f8 l32i.n a15, a1, 36 -400064f8: 880b addi.n a8, a8, -1 -400064fa: e73f97 bltu a15, a9, 400064e5 -400064fd: ffeb46 j 400064ae -40006500: 004136 entry a1, 32 -40006503: 03a052 movi a5, 3 -40006506: 1e0527 bnone a5, a2, 40006528 -40006509: 747030 extui a7, a3, 0, 8 -4000650c: f03d nop.n -4000650e: 118076 loop a0, 40006523 -40006511: 440b addi.n a4, a4, -1 -40006513: 5d0426 beqi a4, -1, 40006574 -40006516: 000262 l8ui a6, a2, 0 -40006519: 551677 beq a6, a7, 40006572 -4000651c: 221b addi.n a2, a2, 1 -4000651e: 148020 extui a8, a2, 0, 2 -40006521: 388c beqz.n a8, 40006528 -40006523: fff946 j 4000650c -40006526: b60000 excw -40006529: 812b44 excw -4000652c: 9e .byte 0x9e -4000652d: 7ce8 l32i.n a14, a12, 28 -4000652f: 71fa add.n a7, a1, a15 -40006531: 9e .byte 0x9e -40006532: 40e8 l32i.n a14, a0, 16 -40006534: 3041b2 s8i a11, a1, 48 -40006537: 907490 addx2 a7, a4, a9 -4000653a: 908a73 excw -4000653d: 0bf9 s32i.n a15, a11, 0 -4000653f: 149b76 loopnez a11, 40006557 -40006542: 02c8 l32i.n a12, a2, 0 -40006544: 30cc90 xor a12, a12, a9 -40006547: 30dca0 xor a13, a12, a10 -4000654a: cc7a add.n a12, a12, a7 -4000654c: 10ccd0 and a12, a12, a13 -4000654f: 048c87 bany a12, a8, 40006557 -40006552: fcc442 addi a4, a4, -4 -40006555: 224b addi.n a2, a2, 4 -40006557: 046d mov.n a6, a4 -40006559: 440b addi.n a4, a4, -1 -4000655b: 150426 beqi a4, -1, 40006574 -4000655e: 747030 extui a7, a3, 0, 8 -40006561: 099676 loopnez a6, 4000656e -40006564: 0002d2 l8ui a13, a2, 0 -40006567: 440b addi.n a4, a4, -1 -40006569: 051d77 beq a13, a7, 40006572 -4000656c: 221b addi.n a2, a2, 1 -4000656e: 020c movi.n a2, 0 -40006570: f01d retw.n -40006572: f01d retw.n -40006574: 020c movi.n a2, 0 -40006576: f01d retw.n -40006578: 004136 entry a1, 32 -4000657b: e88b81 l32r a8, 400007a8 <_c_0x00ff0000+0x10> -4000657e: ffaf92 movi a9, -1 -40006581: e88871 l32r a7, 400007a4 <_c_0x00ff0000+0xc> -40006584: 746030 extui a6, a3, 0, 8 -40006587: 03a032 movi a3, 3 -4000658a: 08f616 beqz a6, 4000661d -4000658d: 180327 bnone a3, a2, 400065a9 -40006590: f03d nop.n -40006592: 0f8076 loop a0, 400065a5 -40006595: 000232 l8ui a3, a2, 0 -40006598: 0c8316 beqz a3, 40006664 -4000659b: 791637 beq a6, a3, 40006618 -4000659e: 221b addi.n a2, a2, 1 -400065a0: 144020 extui a4, a2, 0, 2 -400065a3: 248c beqz.n a4, 400065a9 -400065a5: fff9c6 j 40006590 -400065a8: 063d00 excw -400065ab: 850c movi.n a5, 8 -400065ad: 0e8076 loop a0, 400065bf -400065b0: 401500 ssl a5 -400065b3: 1155f0 slli a5, a5, 1 -400065b6: a1a300 sll a10, a3 -400065b9: 2033a0 or a3, a3, a10 -400065bc: 02c5f6 bgeui a5, 32, 400065c2 -400065bf: fffa86 j 400065ad -400065c2: 02a8 l32i.n a10, a2, 0 -400065c4: 30ca90 xor a12, a10, a9 -400065c7: ba8a add.n a11, a10, a8 -400065c9: 10bbc0 and a11, a11, a12 -400065cc: 2d8b77 bany a11, a7, 400065fd -400065cf: 30da30 xor a13, a10, a3 -400065d2: 30ed90 xor a14, a13, a9 -400065d5: dd8a add.n a13, a13, a8 -400065d7: 10dde0 and a13, a13, a14 -400065da: 100d77 bnone a13, a7, 400065ee -400065dd: 000706 j 400065fd -400065e0: 30ea30 xor a14, a10, a3 -400065e3: 30fe90 xor a15, a14, a9 -400065e6: ee8a add.n a14, a14, a8 -400065e8: 10eef0 and a14, a14, a15 -400065eb: 0e8e77 bany a14, a7, 400065fd -400065ee: 12a8 l32i.n a10, a2, 4 -400065f0: 224b addi.n a2, a2, 4 -400065f2: 304a90 xor a4, a10, a9 -400065f5: fa8a add.n a15, a10, a8 -400065f7: 10ff40 and a15, a15, a4 -400065fa: e20f77 bnone a15, a7, 400065e0 -400065fd: 000232 l8ui a3, a2, 0 -40006600: 139c beqz.n a3, 40006615 -40006602: 121637 beq a6, a3, 40006618 -40006605: 098076 loop a0, 40006612 -40006608: 010232 l8ui a3, a2, 1 -4000660b: 221b addi.n a2, a2, 1 -4000660d: 438c beqz.n a3, 40006615 -4000660f: 051637 beq a6, a3, 40006618 -40006612: fffbc6 j 40006605 -40006615: 4b9637 bne a6, a3, 40006664 -40006618: f01d retw.n -4000661a: 000000 ill -4000661d: 0d0327 bnone a3, a2, 4000662e -40006620: 0002a2 l8ui a10, a2, 0 -40006623: ff1a16 beqz a10, 40006618 -40006626: 221b addi.n a2, a2, 1 -40006628: 14b020 extui a11, a2, 0, 2 -4000662b: ff1b56 bnez a11, 40006620 -4000662e: 02c8 l32i.n a12, a2, 0 -40006630: f03d nop.n -40006632: 30dc90 xor a13, a12, a9 -40006635: cc8a add.n a12, a12, a8 -40006637: 10ccd0 and a12, a12, a13 -4000663a: 0e8c77 bany a12, a7, 4000664c -4000663d: 12d8 l32i.n a13, a2, 4 -4000663f: 224b addi.n a2, a2, 4 -40006641: 30ed90 xor a14, a13, a9 -40006644: dd8a add.n a13, a13, a8 -40006646: 10dde0 and a13, a13, a14 -40006649: f00d77 bnone a13, a7, 4000663d -4000664c: 0002e2 l8ui a14, a2, 0 -4000664f: 0020f0 nop -40006652: fc2e16 beqz a14, 40006618 -40006655: 078076 loop a0, 40006660 -40006658: 0102f2 l8ui a15, a2, 1 -4000665b: 221b addi.n a2, a2, 1 -4000665d: fb7f16 beqz a15, 40006618 -40006660: fffc46 j 40006655 -40006663: 020c00 andb b0, b12, b0 -40006666: f01d retw.n -40006668: 002136 entry a1, 16 -4000666b: f01d retw.n -4000666d: 000000 ill - -40006670 <_xtos_set_intlevel>: -40006670: 002136 entry a1, 16 -40006673: 03e630 rsr.ps a3 -40006676: 047c movi.n a4, -16 -40006678: 342020 extui a2, a2, 0, 4 -4000667b: 104430 and a4, a4, a3 -4000667e: 204420 or a4, a4, a2 -40006681: 13e640 wsr.ps a4 -40006684: 342030 extui a2, a3, 0, 4 -40006687: 002010 rsync -4000668a: f01d retw.n -4000668c: 002136 entry a1, 16 -4000668f: 343020 extui a3, a2, 0, 4 -40006692: 03e620 rsr.ps a2 -40006695: 047c movi.n a4, -16 -40006697: 345020 extui a5, a2, 0, 4 -4000669a: c05350 sub a5, a3, a5 -4000669d: 104420 and a4, a4, a2 -400066a0: 204430 or a4, a4, a3 -400066a3: a34250 movltz a4, a2, a5 -400066a6: 13e640 wsr.ps a4 -400066a9: 002010 rsync -400066ac: f01d retw.n -400066ae: 360000 excw -400066b1: f00021 l32r a2, 400026b4 <_X_ets_unk225c+0x458> -400066b4: 1d0041 l32r a4, 3ffcdab4 <_start-0x3254c> -400066b7: 4128f0 srli a2, a15, 8 -400066ba: 5138 l32i.n a3, a1, 20 -400066bc: 01d112 addmi a1, a1, 0x100 -400066bf: 004110 break 1, 1 -400066c2: 003000 rfe -400066c5: 000000 ill -400066c8: 3b6522 s32i a2, a5, 236 -400066cb: 1528 l32i.n a2, a5, 4 -400066cd: 0538 l32i.n a3, a5, 0 -400066cf: 13e620 wsr.ps a2 -400066d2: 4528 l32i.n a2, a5, 16 -400066d4: 6548 l32i.n a4, a5, 24 -400066d6: 002010 rsync -400066d9: 13b130 wsr.epc1 a3 -400066dc: 5538 l32i.n a3, a5, 20 -400066de: 7558 l32i.n a5, a5, 28 -400066e0: 003000 rfe -400066e3: 213600 srai a3, a0, 6 -400066e6: ea2000 depbits a0, a0, 14, 3 -400066e9: f01d03 excw -400066ec: 002136 entry a1, 16 -400066ef: 52cc bnez.n a2, 400066f8 <_xtos_set_intlevel+0x88> -400066f1: 13f030 wsr.ccompare0 a3 -400066f4: 0003c6 j 40006707 <_xtos_set_intlevel+0x97> -400066f7: 22f600 orb b15, b6, b0 -400066fa: f13005 call0 3fff79fc <_start-0x8604> -400066fd: 014613 excw -40006700: 32f600 orbc b15, b6, b0 -40006703: f23005 call0 3fff8a04 <_start-0x75fc> -40006706: 200013 excw -40006709: f01d00 subx8 a1, a13, a0 -4000670c: 002136 entry a1, 16 -4000670f: 52cc bnez.n a2, 40006718 <_xtos_set_intlevel+0xa8> -40006711: 03f020 rsr.ccompare0 a2 -40006714: f01d retw.n -40006716: f60000 excw -40006719: 200422 l8ui a2, a4, 32 -4000671c: 1d03f1 l32r a15, 3ffcdb28 <_start-0x324d8> -4000671f: 32f6f0 orbc b15, b6, b15 -40006722: f22004 excw -40006725: f01d03 excw -40006728: 020c movi.n a2, 0 -4000672a: f01d retw.n -4000672c: 004136 entry a1, 32 -4000672f: 035d mov.n a5, a3 -40006731: 023d mov.n a3, a2 -40006733: 052d mov.n a2, a5 -40006735: 000706 j 40006755 <_xtos_set_intlevel+0xe5> -40006738: 05ad mov.n a10, a5 -4000673a: 03bd mov.n a11, a3 -4000673c: 4c0c movi.n a12, 4 -4000673e: c06c60 sub a6, a12, a6 -40006741: 06cd mov.n a12, a6 -40006743: 000765 call8 400067b8 <_xtos_set_intlevel+0x148> -40006746: 556a add.n a5, a5, a6 -40006748: 336a add.n a3, a3, a6 -4000674a: c04460 sub a4, a4, a6 -4000674d: 000346 j 4000675e <_xtos_set_intlevel+0xee> -40006750: 004136 entry a1, 32 -40006753: 025d mov.n a5, a2 -40006755: 6244b6 bltui a4, 4, 400067bb <_xtos_set_intlevel+0x14b> -40006758: 146020 extui a6, a2, 0, 2 -4000675b: fd9656 bnez a6, 40006738 <_xtos_set_intlevel+0xc8> -4000675e: 417440 srli a7, a4, 4 -40006761: 14b030 extui a11, a3, 0, 2 -40006764: 09cb56 bnez a11, 40006804 <_xtos_set_intlevel+0x194> -40006767: 159776 loopnez a7, 40006780 <_xtos_set_intlevel+0x110> -4000676a: 0368 l32i.n a6, a3, 0 -4000676c: 1378 l32i.n a7, a3, 4 -4000676e: 0569 s32i.n a6, a5, 0 -40006770: 2368 l32i.n a6, a3, 8 -40006772: 1579 s32i.n a7, a5, 4 -40006774: 3378 l32i.n a7, a3, 12 -40006776: 2569 s32i.n a6, a5, 8 -40006778: 10c332 addi a3, a3, 16 -4000677b: 3579 s32i.n a7, a5, 12 -4000677d: 10c552 addi a5, a5, 16 -40006780: 0d6437 bbci a4, 3, 40006791 <_xtos_set_intlevel+0x121> -40006783: 0368 l32i.n a6, a3, 0 -40006785: 1378 l32i.n a7, a3, 4 -40006787: 338b addi.n a3, a3, 8 -40006789: 0569 s32i.n a6, a5, 0 -4000678b: 016572 s32i a7, a5, 4 -4000678e: 08c552 addi a5, a5, 8 -40006791: 076427 bbci a4, 2, 4000679c <_xtos_set_intlevel+0x12c> -40006794: 0368 l32i.n a6, a3, 0 -40006796: 334b addi.n a3, a3, 4 -40006798: 0569 s32i.n a6, a5, 0 -4000679a: 554b addi.n a5, a5, 4 -4000679c: 144040 extui a4, a4, 0, 2 -4000679f: 349c beqz.n a4, 400067b6 <_xtos_set_intlevel+0x146> -400067a1: 0368 l32i.n a6, a3, 0 -400067a3: 0578 l32i.n a7, a5, 0 -400067a5: 402400 ssa8l a4 -400067a8: a16600 sll a6, a6 -400067ab: 917070 srl a7, a7 -400067ae: 403400 ssa8b a4 -400067b1: 817760 src a7, a7, a6 -400067b4: 0579 s32i.n a7, a5, 0 -400067b6: f01d retw.n -400067b8: 002136 entry a1, 16 -400067bb: 041416 beqz a4, 40006800 <_xtos_set_intlevel+0x190> -400067be: c57c movi.n a5, -4 -400067c0: 105350 and a5, a3, a5 -400067c3: 0568 l32i.n a6, a5, 0 -400067c5: 1578 l32i.n a7, a5, 4 -400067c7: 402300 ssa8l a3 -400067ca: 813760 src a3, a7, a6 -400067cd: 14a020 extui a10, a2, 0, 2 -400067d0: c052a0 sub a5, a2, a10 -400067d3: 0588 l32i.n a8, a5, 0 -400067d5: 6a4a add.n a6, a10, a4 -400067d7: 402200 ssa8l a2 -400067da: a1a800 sll a10, a8 -400067dd: 0b46b6 bltui a6, 4, 400067ec <_xtos_set_intlevel+0x17c> -400067e0: 1588 l32i.n a8, a5, 4 -400067e2: 403200 ssa8b a2 -400067e5: 81a3a0 src a10, a3, a10 -400067e8: 05a9 s32i.n a10, a5, 0 -400067ea: 554b addi.n a5, a5, 4 -400067ec: 402400 ssa8l a4 -400067ef: 8133a0 src a3, a3, a10 -400067f2: 402600 ssa8l a6 -400067f5: 918080 srl a8, a8 -400067f8: 403600 ssa8b a6 -400067fb: 818830 src a8, a8, a3 -400067fe: 0589 s32i.n a8, a5, 0 -40006800: f01d retw.n -40006802: 000000 ill -40006805: 304023 excw -40006808: b014b0 addx8 a1, a4, a11 -4000680b: 68c033 excw -4000680e: 977603 excw -40006811: 137821 l32r a2, 3ffcb5f4 <_start-0x34a0c> -40006814: 2388 l32i.n a8, a3, 8 -40006816: 816760 src a6, a7, a6 -40006819: 0569 s32i.n a6, a5, 0 -4000681b: 3398 l32i.n a9, a3, 12 -4000681d: 817870 src a7, a8, a7 -40006820: 1579 s32i.n a7, a5, 4 -40006822: 4368 l32i.n a6, a3, 16 -40006824: 818980 src a8, a9, a8 -40006827: 2589 s32i.n a8, a5, 8 -40006829: 10c332 addi a3, a3, 16 -4000682c: 819690 src a9, a6, a9 -4000682f: 3599 s32i.n a9, a5, 12 -40006831: 10c552 addi a5, a5, 16 -40006834: 146437 bbci a4, 3, 4000684c <_xtos_set_intlevel+0x1dc> -40006837: 1378 l32i.n a7, a3, 4 -40006839: 2388 l32i.n a8, a3, 8 -4000683b: 816760 src a6, a7, a6 -4000683e: 0569 s32i.n a6, a5, 0 -40006840: 338b addi.n a3, a3, 8 -40006842: 817870 src a7, a8, a7 -40006845: 1579 s32i.n a7, a5, 4 -40006847: 558b addi.n a5, a5, 8 -40006849: 206880 or a6, a8, a8 -4000684c: 0c6427 bbci a4, 2, 4000685c <_xtos_set_intlevel+0x1ec> -4000684f: 1378 l32i.n a7, a3, 4 -40006851: 334b addi.n a3, a3, 4 -40006853: 816760 src a6, a7, a6 -40006856: 0569 s32i.n a6, a5, 0 -40006858: 554b addi.n a5, a5, 4 -4000685a: 076d mov.n a6, a7 -4000685c: 1378 l32i.n a7, a3, 4 -4000685e: 0538 l32i.n a3, a5, 0 -40006860: 816760 src a6, a7, a6 -40006863: 402400 ssa8l a4 -40006866: a16600 sll a6, a6 -40006869: 913030 srl a3, a3 -4000686c: 403400 ssa8b a4 -4000686f: 813360 src a3, a3, a6 -40006872: 0539 s32i.n a3, a5, 0 -40006874: f01d retw.n -40006876: 360000 excw -40006879: 200021 l32r a2, 3ffce87c <_start-0x31784> -4000687c: 1d03e2 l8ui a14, a3, 29 -4000687f: 2136f0 srai a3, a15, 6 -40006882: e32000 excw -40006885: f01d13 excw - -40006888 <__divsi3>: -40006888: 002136 entry a1, 16 -4000688b: d22230 quos a2, a2, a3 -4000688e: f01d retw.n - -40006890 : -40006890: 004136 entry a1, 32 -40006893: 604030 neg a4, a3 -40006896: 223a add.n a2, a2, a3 -40006898: 220b addi.n a2, a2, -1 -4000689a: 102240 and a2, a2, a4 -4000689d: f01d retw.n - ... - -400068a0 : -400068a0: 004136 entry a1, 32 -400068a3: 223a add.n a2, a2, a3 -400068a5: 220b addi.n a2, a2, -1 -400068a7: c22230 quou a2, a2, a3 -400068aa: f01d retw.n -400068ac: 004136 entry a1, 32 -400068af: 430b addi.n a4, a3, -1 -400068b1: 320426 beqi a4, -1, 400068e7 -400068b4: 206330 or a6, a3, a3 -400068b7: 248030 extui a8, a3, 0, 3 -400068ba: 00a032 movi a3, 0 -400068bd: 049876 loopnez a8, 400068c5 -400068c0: 004232 s8i a3, a2, 0 -400068c3: 221b addi.n a2, a2, 1 -400068c5: 414360 srli a4, a6, 3 -400068c8: f03d nop.n -400068ca: 199476 loopnez a4, 400068e7 -400068cd: 004232 s8i a3, a2, 0 -400068d0: 014232 s8i a3, a2, 1 -400068d3: 024232 s8i a3, a2, 2 -400068d6: 034232 s8i a3, a2, 3 -400068d9: 044232 s8i a3, a2, 4 -400068dc: 054232 s8i a3, a2, 5 -400068df: 064232 s8i a3, a2, 6 -400068e2: 074232 s8i a3, a2, 7 -400068e5: 228b addi.n a2, a2, 8 -400068e7: f01d retw.n -400068e9: 000000 ill - -400068ec : -400068ec: 004136 entry a1, 32 -400068ef: 1b44b6 bltui a4, 4, 4000690e -400068f2: 205320 or a5, a3, a2 -400068f5: 145050 extui a5, a5, 0, 2 -400068f8: 25dc bnez.n a5, 4000690e -400068fa: 418240 srli a8, a4, 2 -400068fd: 0d9876 loopnez a8, 4000690e -40006900: 03a8 l32i.n a10, a3, 0 -40006902: 0298 l32i.n a9, a2, 0 -40006904: 0699a7 bne a9, a10, 4000690e -40006907: 224b addi.n a2, a2, 4 -40006909: 334b addi.n a3, a3, 4 -4000690b: fcc442 addi a4, a4, -4 -4000690e: 046d mov.n a6, a4 -40006910: 440b addi.n a4, a4, -1 -40006912: 110426 beqi a4, -1, 40006927 -40006915: 0e9676 loopnez a6, 40006927 -40006918: 000372 l8ui a7, a3, 0 -4000691b: 000282 l8ui a8, a2, 0 -4000691e: 221b addi.n a2, a2, 1 -40006920: 089877 bne a8, a7, 4000692c -40006923: 440b addi.n a4, a4, -1 -40006925: 331b addi.n a3, a3, 1 -40006927: 020c movi.n a2, 0 -40006929: f01d retw.n -4000692b: 287000 excw -4000692e: f01dc0 subx8 a1, a13, a12 -40006931: 000000 ill -40006934: 947600 extui a7, a0, 6, 10 -40006937: 6209 s32i.n a0, a2, 24 -40006939: 1b0003 excw -4000693c: 456233 excw -4000693f: 551b00 extui a1, a0, 27, 6 -40006942: f01d retw.n -40006944: ed74b6 bltui a4, 7, 40006935 -40006947: 000362 l8ui a6, a3, 0 -4000694a: 331b addi.n a3, a3, 1 -4000694c: ffc442 addi a4, a4, -1 -4000694f: 004562 s8i a6, a5, 0 -40006952: 01c552 addi a5, a5, 1 -40006955: 276517 bbci a5, 1, 40006980 -40006958: d964b6 bltui a4, 6, 40006935 -4000695b: 000362 l8ui a6, a3, 0 -4000695e: 010372 l8ui a7, a3, 1 -40006961: 332b addi.n a3, a3, 2 -40006963: fec442 addi a4, a4, -2 -40006966: 004562 s8i a6, a5, 0 -40006969: 014572 s8i a7, a5, 1 -4000696c: 552b addi.n a5, a5, 2 -4000696e: 000386 j 40006980 -40006971: 000000 ill - -40006974 : -40006974: 002136 entry a1, 16 -40006977: 205220 or a5, a2, a2 -4000697a: c6e207 bbsi a2, 0, 40006944 -4000697d: d7e217 bbsi a2, 1, 40006958 -40006980: 417440 srli a7, a4, 4 -40006983: 018320 slli a8, a3, 30 -40006986: 05a856 bnez a8, 400069e4 -40006989: 159776 loopnez a7, 400069a2 -4000698c: 0368 l32i.n a6, a3, 0 -4000698e: 1378 l32i.n a7, a3, 4 -40006990: 0569 s32i.n a6, a5, 0 -40006992: 2368 l32i.n a6, a3, 8 -40006994: 1579 s32i.n a7, a5, 4 -40006996: 3378 l32i.n a7, a3, 12 -40006998: 2569 s32i.n a6, a5, 8 -4000699a: 10c332 addi a3, a3, 16 -4000699d: 3579 s32i.n a7, a5, 12 -4000699f: 10c552 addi a5, a5, 16 -400069a2: 0b6437 bbci a4, 3, 400069b1 -400069a5: 0368 l32i.n a6, a3, 0 -400069a7: 1378 l32i.n a7, a3, 4 -400069a9: 338b addi.n a3, a3, 8 -400069ab: 0569 s32i.n a6, a5, 0 -400069ad: 1579 s32i.n a7, a5, 4 -400069af: 558b addi.n a5, a5, 8 -400069b1: 07e427 bbsi a4, 2, 400069bc -400069b4: 14e417 bbsi a4, 1, 400069cc -400069b7: 21e407 bbsi a4, 0, 400069dc -400069ba: f01d retw.n -400069bc: 0368 l32i.n a6, a3, 0 -400069be: 334b addi.n a3, a3, 4 -400069c0: 0569 s32i.n a6, a5, 0 -400069c2: 554b addi.n a5, a5, 4 -400069c4: 04e417 bbsi a4, 1, 400069cc -400069c7: 11e407 bbsi a4, 0, 400069dc -400069ca: f01d retw.n -400069cc: 001362 l16ui a6, a3, 0 -400069cf: 332b addi.n a3, a3, 2 -400069d1: 005562 s16i a6, a5, 0 -400069d4: 552b addi.n a5, a5, 2 -400069d6: 02e407 bbsi a4, 0, 400069dc -400069d9: f01d retw.n -400069db: 036200 excw -400069de: 456200 extui a6, a0, 18, 5 -400069e1: f01d00 subx8 a1, a13, a0 -400069e4: ffa416 beqz a4, 400069e2 -400069e7: 402300 ssa8l a3 -400069ea: 15be80 extui a11, a8, 30, 2 -400069ed: c033b0 sub a3, a3, a11 -400069f0: 0368 l32i.n a6, a3, 0 -400069f2: 219776 loopnez a7, 40006a17 -400069f5: 1378 l32i.n a7, a3, 4 -400069f7: 2388 l32i.n a8, a3, 8 -400069f9: 816760 src a6, a7, a6 -400069fc: 0569 s32i.n a6, a5, 0 -400069fe: 3398 l32i.n a9, a3, 12 -40006a00: 817870 src a7, a8, a7 -40006a03: 1579 s32i.n a7, a5, 4 -40006a05: 4368 l32i.n a6, a3, 16 -40006a07: 818980 src a8, a9, a8 -40006a0a: 2589 s32i.n a8, a5, 8 -40006a0c: 10c332 addi a3, a3, 16 -40006a0f: 819690 src a9, a6, a9 -40006a12: 3599 s32i.n a9, a5, 12 -40006a14: 10c552 addi a5, a5, 16 -40006a17: 156437 bbci a4, 3, 40006a30 -40006a1a: 1378 l32i.n a7, a3, 4 -40006a1c: 2388 l32i.n a8, a3, 8 -40006a1e: 816760 src a6, a7, a6 -40006a21: 0569 s32i.n a6, a5, 0 -40006a23: 338b addi.n a3, a3, 8 -40006a25: 817870 src a7, a8, a7 -40006a28: 1579 s32i.n a7, a5, 4 -40006a2a: 08c552 addi a5, a5, 8 -40006a2d: 206880 or a6, a8, a8 -40006a30: 0c6427 bbci a4, 2, 40006a40 -40006a33: 1378 l32i.n a7, a3, 4 -40006a35: 334b addi.n a3, a3, 4 -40006a37: 816760 src a6, a7, a6 -40006a3a: 0569 s32i.n a6, a5, 0 -40006a3c: 554b addi.n a5, a5, 4 -40006a3e: 076d mov.n a6, a7 -40006a40: 33ba add.n a3, a3, a11 -40006a42: 06e417 bbsi a4, 1, 40006a4c -40006a45: 18e407 bbsi a4, 0, 40006a61 -40006a48: f01d retw.n -40006a4a: 620000 excw -40006a4d: 720003 excw -40006a50: 2b0103 excw -40006a53: 456233 excw -40006a56: 457200 extui a7, a0, 18, 5 -40006a59: 552b01 l32r a0, 3ffdbf08 <_start-0x240f8> -40006a5c: 01e407 bbsi a4, 0, 40006a61 -40006a5f: f01d retw.n -40006a61: 000362 l8ui a6, a3, 0 -40006a64: 004562 s8i a6, a5, 0 -40006a67: f01d retw.n -40006a69: 000000 ill - -40006a6c : -40006a6c: 004136 entry a1, 32 -40006a6f: 049d mov.n a9, a4 -40006a71: 028d mov.n a8, a2 -40006a73: 04ad mov.n a10, a4 -40006a75: 032d mov.n a2, a3 -40006a77: 087d mov.n a7, a8 -40006a79: 1bb387 bgeu a3, a8, 40006a98 -40006a7c: 343a add.n a3, a4, a3 -40006a7e: 16b837 bgeu a8, a3, 40006a98 -40006a81: 748a add.n a7, a4, a8 -40006a83: 131416 beqz a4, 40006bb8 -40006a86: 099476 loopnez a4, 40006a93 -40006a89: 770b addi.n a7, a7, -1 -40006a8b: 330b addi.n a3, a3, -1 -40006a8d: 000342 l8ui a4, a3, 0 -40006a90: 004742 s8i a4, a7, 0 -40006a93: 082d mov.n a2, a8 -40006a95: f01d retw.n -40006a97: b9f600 excw -40006a9a: 320602 l8ui a0, a6, 50 -40006a9d: b28000 mulsh a8, a0, a0 -40006aa0: b0b020 addx8 a11, a0, a2 -40006aa3: fb5614 excw -40006aa6: 7d0b addi.n a7, a13, -1 -40006aa8: 9008 l32i.n a0, a0, 36 -40006aaa: 9014c4 excw -40006aad: 4d4164 excw -40006ab0: 9c7606 j 3ffedc8c <_start-0x12374> -40006ab3: 025815 call4 40009034 <__umoddi3+0x21d0> -40006ab6: 0759 s32i.n a5, a7, 0 -40006ab8: 12f8 l32i.n a15, a2, 4 -40006aba: 17f9 s32i.n a15, a7, 4 -40006abc: 22e8 l32i.n a14, a2, 8 -40006abe: 27e9 s32i.n a14, a7, 8 -40006ac0: 32d8 l32i.n a13, a2, 12 -40006ac2: 37d9 s32i.n a13, a7, 12 -40006ac4: 10c222 addi a2, a2, 16 -40006ac7: 10c772 addi a7, a7, 16 -40006aca: 413240 srli a3, a4, 2 -40006acd: 459376 loopnez a3, 40006b16 -40006ad0: 0238 l32i.n a3, a2, 0 -40006ad2: 0739 s32i.n a3, a7, 0 -40006ad4: 1238 l32i.n a3, a2, 4 -40006ad6: 1739 s32i.n a3, a7, 4 -40006ad8: 2238 l32i.n a3, a2, 8 -40006ada: 2739 s32i.n a3, a7, 8 -40006adc: 3238 l32i.n a3, a2, 12 -40006ade: 3739 s32i.n a3, a7, 12 -40006ae0: 4238 l32i.n a3, a2, 16 -40006ae2: 4739 s32i.n a3, a7, 16 -40006ae4: 5238 l32i.n a3, a2, 20 -40006ae6: 5739 s32i.n a3, a7, 20 -40006ae8: 6238 l32i.n a3, a2, 24 -40006aea: 6739 s32i.n a3, a7, 24 -40006aec: 7238 l32i.n a3, a2, 28 -40006aee: 7739 s32i.n a3, a7, 28 -40006af0: 8238 l32i.n a3, a2, 32 -40006af2: 8739 s32i.n a3, a7, 32 -40006af4: 9238 l32i.n a3, a2, 36 -40006af6: 9739 s32i.n a3, a7, 36 -40006af8: a238 l32i.n a3, a2, 40 -40006afa: a739 s32i.n a3, a7, 40 -40006afc: b238 l32i.n a3, a2, 44 -40006afe: b739 s32i.n a3, a7, 44 -40006b00: c238 l32i.n a3, a2, 48 -40006b02: c739 s32i.n a3, a7, 48 -40006b04: d238 l32i.n a3, a2, 52 -40006b06: d739 s32i.n a3, a7, 52 -40006b08: e238 l32i.n a3, a2, 56 -40006b0a: e739 s32i.n a3, a7, 56 -40006b0c: f238 l32i.n a3, a2, 60 -40006b0e: f739 s32i.n a3, a7, 60 -40006b10: 40c222 addi a2, a2, 64 -40006b13: 40c772 addi a7, a7, 64 -40006b16: 1166c0 slli a6, a6, 4 -40006b19: c06960 sub a6, a9, a6 -40006b1c: 06ad mov.n a10, a6 -40006b1e: 4546b6 bltui a6, 4, 40006b67 -40006b21: 24b260 extui a11, a6, 2, 3 -40006b24: 419260 srli a9, a6, 2 -40006b27: 204990 or a4, a9, a9 -40006b2a: 413340 srli a3, a4, 3 -40006b2d: 079b76 loopnez a11, 40006b38 -40006b30: 02c8 l32i.n a12, a2, 0 -40006b32: 07c9 s32i.n a12, a7, 0 -40006b34: 224b addi.n a2, a2, 4 -40006b36: 774b addi.n a7, a7, 4 -40006b38: 11a9e0 slli a10, a9, 2 -40006b3b: 259376 loopnez a3, 40006b64 -40006b3e: 0238 l32i.n a3, a2, 0 -40006b40: 0739 s32i.n a3, a7, 0 -40006b42: 1238 l32i.n a3, a2, 4 -40006b44: 1739 s32i.n a3, a7, 4 -40006b46: 2238 l32i.n a3, a2, 8 -40006b48: 2739 s32i.n a3, a7, 8 -40006b4a: 3238 l32i.n a3, a2, 12 -40006b4c: 3739 s32i.n a3, a7, 12 -40006b4e: 4238 l32i.n a3, a2, 16 -40006b50: 4739 s32i.n a3, a7, 16 -40006b52: 5238 l32i.n a3, a2, 20 -40006b54: 5739 s32i.n a3, a7, 20 -40006b56: 6238 l32i.n a3, a2, 24 -40006b58: 6739 s32i.n a3, a7, 24 -40006b5a: 7238 l32i.n a3, a2, 28 -40006b5c: 7739 s32i.n a3, a7, 28 -40006b5e: 20c222 addi a2, a2, 32 -40006b61: 20c772 addi a7, a7, 32 -40006b64: c0a6a0 sub a10, a6, a10 -40006b67: ba0b addi.n a11, a10, -1 -40006b69: 4b0b26 beqi a11, -1, 40006bb8 -40006b6c: 4133a0 srli a3, a10, 3 -40006b6f: 24c0a0 extui a12, a10, 0, 3 -40006b72: 099c76 loopnez a12, 40006b7f -40006b75: 0002d2 l8ui a13, a2, 0 -40006b78: 0047d2 s8i a13, a7, 0 -40006b7b: 221b addi.n a2, a2, 1 -40006b7d: 771b addi.n a7, a7, 1 -40006b7f: f03d nop.n -40006b81: 339376 loopnez a3, 40006bb8 -40006b84: 000232 l8ui a3, a2, 0 -40006b87: 004732 s8i a3, a7, 0 -40006b8a: 010232 l8ui a3, a2, 1 -40006b8d: 014732 s8i a3, a7, 1 -40006b90: 020232 l8ui a3, a2, 2 -40006b93: 024732 s8i a3, a7, 2 -40006b96: 030232 l8ui a3, a2, 3 -40006b99: 034732 s8i a3, a7, 3 -40006b9c: 040232 l8ui a3, a2, 4 -40006b9f: 044732 s8i a3, a7, 4 -40006ba2: 050232 l8ui a3, a2, 5 -40006ba5: 054732 s8i a3, a7, 5 -40006ba8: 060232 l8ui a3, a2, 6 -40006bab: 064732 s8i a3, a7, 6 -40006bae: 070232 l8ui a3, a2, 7 -40006bb1: 074732 s8i a3, a7, 7 -40006bb4: 228b addi.n a2, a2, 8 -40006bb6: 778b addi.n a7, a7, 8 -40006bb8: 082d mov.n a2, a8 -40006bba: f01d retw.n -40006bbc: 947600 extui a7, a0, 6, 10 -40006bbf: 453204 excw -40006bc2: 551b00 extui a1, a0, 27, 6 -40006bc5: f01d retw.n -40006bc7: 84b600 extui a11, a0, 6, 9 -40006bca: 4532f1 l32r a15, 3ffd8094 <_start-0x27f6c> -40006bcd: 551b00 extui a1, a0, 27, 6 -40006bd0: 440b addi.n a4, a4, -1 -40006bd2: 286517 bbci a5, 1, 40006bfe -40006bd5: e484b6 bltui a4, 8, 40006bbd -40006bd8: 005532 s16i a3, a5, 0 -40006bdb: 552b addi.n a5, a5, 2 -40006bdd: fec442 addi a4, a4, -2 -40006be0: 000686 j 40006bfe - ... - -40006be4 : -40006be4: 002136 entry a1, 16 -40006be7: 743030 extui a3, a3, 0, 8 -40006bea: 117380 slli a7, a3, 8 -40006bed: 203370 or a3, a3, a7 -40006bf0: 117300 slli a7, a3, 16 -40006bf3: 203370 or a3, a3, a7 -40006bf6: 025d mov.n a5, a2 -40006bf8: cce207 bbsi a2, 0, 40006bc8 -40006bfb: d6e217 bbsi a2, 1, 40006bd5 -40006bfe: 417440 srli a7, a4, 4 -40006c01: 0a9776 loopnez a7, 40006c0f -40006c04: 0539 s32i.n a3, a5, 0 -40006c06: 1539 s32i.n a3, a5, 4 -40006c08: 2539 s32i.n a3, a5, 8 -40006c0a: 3539 s32i.n a3, a5, 12 -40006c0c: 10c552 addi a5, a5, 16 -40006c0f: 066437 bbci a4, 3, 40006c19 -40006c12: 0539 s32i.n a3, a5, 0 -40006c14: 1539 s32i.n a3, a5, 4 -40006c16: 08c552 addi a5, a5, 8 -40006c19: 036427 bbci a4, 2, 40006c20 -40006c1c: 0539 s32i.n a3, a5, 0 -40006c1e: 554b addi.n a5, a5, 4 -40006c20: 046417 bbci a4, 1, 40006c28 -40006c23: 005532 s16i a3, a5, 0 -40006c26: 552b addi.n a5, a5, 2 -40006c28: 026407 bbci a4, 0, 40006c2e -40006c2b: 004532 s8i a3, a5, 0 -40006c2e: f01d retw.n - -40006c30 <__udivdi3>: -40006c30: 008136 entry a1, 64 -40006c33: 02ad mov.n a10, a2 -40006c35: 038d mov.n a8, a3 -40006c37: 0b2c movi.n a11, 32 -40006c39: 049d mov.n a9, a4 -40006c3b: 0a2516 beqz a5, 40006ce1 <__udivdi3+0xb1> -40006c3e: 07b357 bgeu a3, a5, 40006c49 <__udivdi3+0x19> -40006c41: 020c movi.n a2, 0 -40006c43: 030c movi.n a3, 0 -40006c45: f01d retw.n -40006c47: 900000 addx2 a0, a0, a0 -40006c4a: 1640f5 call12 4001d058 <__bss_start+0xd058> -40006c4d: 1e89 s32i.n a8, a14, 4 -40006c4f: c02b90 sub a2, a11, a9 -40006c52: 400200 ssr a2 -40006c55: 91f030 srl a15, a3 -40006c58: 401900 ssl a9 -40006c5b: 81c3a0 src a12, a3, a10 -40006c5e: 81b540 src a11, a5, a4 -40006c61: f480b0 extui a8, a11, 0, 16 -40006c64: f5d0b0 extui a13, a11, 16, 16 -40006c67: 404010 ssai 16 -40006c6a: c2efd0 quou a14, a15, a13 -40006c6d: 0e5d mov.n a5, a14 -40006c6f: 8238e0 mull a3, a8, a14 -40006c72: e2ffd0 remu a15, a15, a13 -40006c75: 81ffc0 src a15, a15, a12 -40006c78: 0f2d mov.n a2, a15 -40006c7a: 0ebf37 bgeu a15, a3, 40006c8c <__udivdi3+0x5c> -40006c7d: 5e0b addi.n a5, a14, -1 -40006c7f: 2bfa add.n a2, a11, a15 -40006c81: 0732b7 bltu a2, a11, 40006c8c <__udivdi3+0x5c> -40006c84: 04b237 bgeu a2, a3, 40006c8c <__udivdi3+0x5c> -40006c87: fece52 addi a5, a14, -2 -40006c8a: 2b2a add.n a2, a11, a2 -40006c8c: 115500 slli a5, a5, 16 -40006c8f: c03230 sub a3, a2, a3 -40006c92: 401900 ssl a9 -40006c95: a14400 sll a4, a4 -40006c98: c2e3d0 quou a14, a3, a13 -40006c9b: 0e2d mov.n a2, a14 -40006c9d: 8288e0 mull a8, a8, a14 -40006ca0: e233d0 remu a3, a3, a13 -40006ca3: 0bf3c0 depbits a12, a3, 16, 16 -40006ca6: 0c3d mov.n a3, a12 -40006ca8: 11bc87 bgeu a12, a8, 40006cbd <__udivdi3+0x8d> -40006cab: ffce22 addi a2, a14, -1 -40006cae: 803bc0 add a3, a11, a12 -40006cb1: 0833b7 bltu a3, a11, 40006cbd <__udivdi3+0x8d> -40006cb4: 05b387 bgeu a3, a8, 40006cbd <__udivdi3+0x8d> -40006cb7: 803b30 add a3, a11, a3 -40006cba: fece22 addi a2, a14, -2 -40006cbd: 202250 or a2, a2, a5 -40006cc0: 825240 mull a5, a2, a4 -40006cc3: a24240 muluh a4, a2, a4 -40006cc6: c03380 sub a3, a3, a8 -40006cc9: 0b3347 bltu a3, a4, 40006cd8 <__udivdi3+0xa8> -40006ccc: c06340 sub a6, a3, a4 -40006ccf: 083656 bnez a6, 40006d56 <__udivdi3+0x126> -40006cd2: a17a00 sll a7, a10 -40006cd5: 7db757 bgeu a7, a5, 40006d56 <__udivdi3+0x126> -40006cd8: 220b addi.n a2, a2, -1 -40006cda: 030c movi.n a3, 0 -40006cdc: f01d retw.n -40006cde: 000000 ill -40006ce1: 75b347 bgeu a3, a4, 40006d5a <__udivdi3+0x12a> -40006ce4: 40f450 nsau a5, a4 -40006ce7: a58c beqz.n a5, 40006cf5 <__udivdi3+0xc5> -40006ce9: 401500 ssl a5 -40006cec: a19400 sll a9, a4 -40006cef: 813320 src a3, a3, a2 -40006cf2: a12200 sll a2, a2 -40006cf5: f44090 extui a4, a9, 0, 16 -40006cf8: 404010 ssai 16 -40006cfb: f55090 extui a5, a9, 16, 16 -40006cfe: e2c350 remu a12, a3, a5 -40006d01: c2b350 quou a11, a3, a5 -40006d04: 0bad mov.n a10, a11 -40006d06: 81cc20 src a12, a12, a2 -40006d09: 8284b0 mull a8, a4, a11 -40006d0c: 0c3d mov.n a3, a12 -40006d0e: 0ebc87 bgeu a12, a8, 40006d20 <__udivdi3+0xf0> -40006d11: ab0b addi.n a10, a11, -1 -40006d13: 3c9a add.n a3, a12, a9 -40006d15: 073397 bltu a3, a9, 40006d20 <__udivdi3+0xf0> -40006d18: 04b387 bgeu a3, a8, 40006d20 <__udivdi3+0xf0> -40006d1b: fecba2 addi a10, a11, -2 -40006d1e: 393a add.n a3, a9, a3 -40006d20: c0d380 sub a13, a3, a8 -40006d23: c28d50 quou a8, a13, a5 -40006d26: 083d mov.n a3, a8 -40006d28: 824480 mull a4, a4, a8 -40006d2b: e2dd50 remu a13, a13, a5 -40006d2e: 0bfd20 depbits a2, a13, 16, 16 -40006d31: 17b247 bgeu a2, a4, 40006d4c <__udivdi3+0x11c> -40006d34: 380b addi.n a3, a8, -1 -40006d36: 229a add.n a2, a2, a9 -40006d38: 103297 bltu a2, a9, 40006d4c <__udivdi3+0x11c> -40006d3b: 0db247 bgeu a2, a4, 40006d4c <__udivdi3+0x11c> -40006d3e: 112a00 slli a2, a10, 16 -40006d41: fec832 addi a3, a8, -2 -40006d44: 202320 or a2, a3, a2 -40006d47: 030c movi.n a3, 0 -40006d49: f01d retw.n -40006d4b: 2a0000 depbits a0, a0, 2, 1 -40006d4e: 232011 l32r a1, 3ffcf9d0 <_start-0x30630> -40006d51: 030c20 rsr.scompare1 a2 -40006d54: f01d retw.n -40006d56: 030c movi.n a3, 0 -40006d58: f01d retw.n -40006d5a: 34cc bnez.n a4, 40006d61 <__udivdi3+0x131> -40006d5c: 190c movi.n a9, 1 -40006d5e: c29940 quou a9, a9, a4 -40006d61: 40f940 nsau a4, a9 -40006d64: 0ec416 beqz a4, 40006e54 <__udivdi3+0x224> -40006d67: 401400 ssl a4 -40006d6a: c06b40 sub a6, a11, a4 -40006d6d: a19900 sll a9, a9 -40006d70: 400600 ssr a6 -40006d73: 91d080 srl a13, a8 -40006d76: 401400 ssl a4 -40006d79: a15800 sll a5, a8 -40006d7c: 400600 ssr a6 -40006d7f: 9130a0 srl a3, a10 -40006d82: 401400 ssl a4 -40006d85: 203350 or a3, a3, a5 -40006d88: f44090 extui a4, a9, 0, 16 -40006d8b: a12a00 sll a2, a10 -40006d8e: f55090 extui a5, a9, 16, 16 -40006d91: 404010 ssai 16 -40006d94: c2cd50 quou a12, a13, a5 -40006d97: 0cbd mov.n a11, a12 -40006d99: e2dd50 remu a13, a13, a5 -40006d9c: 81dd30 src a13, a13, a3 -40006d9f: 82a4c0 mull a10, a4, a12 -40006da2: 0d8d mov.n a8, a13 -40006da4: 10bda7 bgeu a13, a10, 40006db8 <__udivdi3+0x188> -40006da7: ffccb2 addi a11, a12, -1 -40006daa: 808d90 add a8, a13, a9 -40006dad: 073897 bltu a8, a9, 40006db8 <__udivdi3+0x188> -40006db0: 04b8a7 bgeu a8, a10, 40006db8 <__udivdi3+0x188> -40006db3: feccb2 addi a11, a12, -2 -40006db6: 898a add.n a8, a9, a8 -40006db8: c0d8a0 sub a13, a8, a10 -40006dbb: e2ed50 remu a14, a13, a5 -40006dbe: 0bfe30 depbits a3, a14, 16, 16 -40006dc1: c2dd50 quou a13, a13, a5 -40006dc4: 0dad mov.n a10, a13 -40006dc6: 82c4d0 mull a12, a4, a13 -40006dc9: 038d mov.n a8, a3 -40006dcb: 0eb3c7 bgeu a3, a12, 40006ddd <__udivdi3+0x1ad> -40006dce: ad0b addi.n a10, a13, -1 -40006dd0: 839a add.n a8, a3, a9 -40006dd2: 073897 bltu a8, a9, 40006ddd <__udivdi3+0x1ad> -40006dd5: 04b8c7 bgeu a8, a12, 40006ddd <__udivdi3+0x1ad> -40006dd8: fecda2 addi a10, a13, -2 -40006ddb: 898a add.n a8, a9, a8 -40006ddd: c038c0 sub a3, a8, a12 -40006de0: 11bb00 slli a11, a11, 16 -40006de3: 20bab0 or a11, a10, a11 -40006de6: 404010 ssai 16 -40006de9: e2d350 remu a13, a3, a5 -40006dec: c2c350 quou a12, a3, a5 -40006def: 8284c0 mull a8, a4, a12 -40006df2: 0c3d mov.n a3, a12 -40006df4: 81dd20 src a13, a13, a2 -40006df7: 0dad mov.n a10, a13 -40006df9: 0fbd87 bgeu a13, a8, 40006e0c <__udivdi3+0x1dc> -40006dfc: 3c0b addi.n a3, a12, -1 -40006dfe: ad9a add.n a10, a13, a9 -40006e00: 083a97 bltu a10, a9, 40006e0c <__udivdi3+0x1dc> -40006e03: 05ba87 bgeu a10, a8, 40006e0c <__udivdi3+0x1dc> -40006e06: fecc32 addi a3, a12, -2 -40006e09: 80a9a0 add a10, a9, a10 -40006e0c: c0ea80 sub a14, a10, a8 -40006e0f: c28e50 quou a8, a14, a5 -40006e12: e2ee50 remu a14, a14, a5 -40006e15: 0bfe20 depbits a2, a14, 16, 16 -40006e18: 825480 mull a5, a4, a8 -40006e1b: 084d mov.n a4, a8 -40006e1d: 23b257 bgeu a2, a5, 40006e44 <__udivdi3+0x214> -40006e20: 480b addi.n a4, a8, -1 -40006e22: 229a add.n a2, a2, a9 -40006e24: 1c3297 bltu a2, a9, 40006e44 <__udivdi3+0x214> -40006e27: 19b257 bgeu a2, a5, 40006e44 <__udivdi3+0x214> -40006e2a: 113300 slli a3, a3, 16 -40006e2d: fec822 addi a2, a8, -2 -40006e30: 202230 or a2, a2, a3 -40006e33: 0b3d mov.n a3, a11 -40006e35: f01d retw.n -40006e37: 353700 extui a3, a0, 23, 4 -40006e3a: 324702 s8i a0, a7, 50 -40006e3d: 0f .byte 0xf -40006e3e: 120c movi.n a2, 1 -40006e40: 030c movi.n a3, 0 -40006e42: f01d retw.n -40006e44: 112300 slli a2, a3, 16 -40006e47: 202420 or a2, a4, a2 -40006e4a: 0b3d mov.n a3, a11 -40006e4c: f01d retw.n -40006e4e: 020c movi.n a2, 0 -40006e50: 030c movi.n a3, 0 -40006e52: f01d retw.n -40006e54: c03890 sub a3, a8, a9 -40006e57: f55090 extui a5, a9, 16, 16 -40006e5a: f44090 extui a4, a9, 0, 16 -40006e5d: 1b0c movi.n a11, 1 -40006e5f: ffe0c6 j 40006de6 <__udivdi3+0x1b6> - ... - -40006e64 <__umoddi3>: -40006e64: 00a136 entry a1, 80 -40006e67: 029d mov.n a9, a2 -40006e69: 03ad mov.n a10, a3 -40006e6b: 0b2c movi.n a11, 32 -40006e6d: 048d mov.n a8, a4 -40006e6f: 0d2516 beqz a5, 40006f45 <__umoddi3+0xe1> -40006e72: 02b357 bgeu a3, a5, 40006e78 <__umoddi3+0x14> -40006e75: 0031c6 j 40006f40 <__umoddi3+0xdc> -40006e78: 40f580 nsau a8, a5 -40006e7b: 151816 beqz a8, 40006fd0 <__umoddi3+0x16c> -40006e7e: c0eb80 sub a14, a11, a8 -40006e81: 400e00 ssr a14 -40006e84: 916030 srl a6, a3 -40006e87: 401800 ssl a8 -40006e8a: 81c320 src a12, a3, a2 -40006e8d: 815540 src a5, a5, a4 -40006e90: f4b050 extui a11, a5, 0, 16 -40006e93: f5d050 extui a13, a5, 16, 16 -40006e96: 404010 ssai 16 -40006e99: c2f6d0 quou a15, a6, a13 -40006e9c: 0f9d mov.n a9, a15 -40006e9e: 82abf0 mull a10, a11, a15 -40006ea1: e266d0 remu a6, a6, a13 -40006ea4: 8166c0 src a6, a6, a12 -40006ea7: 063d mov.n a3, a6 -40006ea9: 0fb6a7 bgeu a6, a10, 40006ebc <__umoddi3+0x58> -40006eac: 9f0b addi.n a9, a15, -1 -40006eae: 356a add.n a3, a5, a6 -40006eb0: 083357 bltu a3, a5, 40006ebc <__umoddi3+0x58> -40006eb3: 05b3a7 bgeu a3, a10, 40006ebc <__umoddi3+0x58> -40006eb6: fecf92 addi a9, a15, -2 -40006eb9: 803530 add a3, a5, a3 -40006ebc: 401800 ssl a8 -40006ebf: 119900 slli a9, a9, 16 -40006ec2: c063a0 sub a6, a3, a10 -40006ec5: c2f6d0 quou a15, a6, a13 -40006ec8: 0fad mov.n a10, a15 -40006eca: 82bbf0 mull a11, a11, a15 -40006ecd: e266d0 remu a6, a6, a13 -40006ed0: 0bf6c0 depbits a12, a6, 16, 16 -40006ed3: 0c3d mov.n a3, a12 -40006ed5: 0fbcb7 bgeu a12, a11, 40006ee8 <__umoddi3+0x84> -40006ed8: af0b addi.n a10, a15, -1 -40006eda: 35ca add.n a3, a5, a12 -40006edc: 083357 bltu a3, a5, 40006ee8 <__umoddi3+0x84> -40006edf: 05b3b7 bgeu a3, a11, 40006ee8 <__umoddi3+0x84> -40006ee2: fecfa2 addi a10, a15, -2 -40006ee5: 803530 add a3, a5, a3 -40006ee8: c033b0 sub a3, a3, a11 -40006eeb: 209a90 or a9, a10, a9 -40006eee: a1b400 sll a11, a4 -40006ef1: 8249b0 mull a4, a9, a11 -40006ef4: a299b0 muluh a9, a9, a11 -40006ef7: c0b4b0 sub a11, a4, a11 -40006efa: 09ad mov.n a10, a9 -40006efc: 0d3397 bltu a3, a9, 40006f0d <__umoddi3+0xa9> -40006eff: a12200 sll a2, a2 -40006f02: 199397 bne a3, a9, 40006f1f <__umoddi3+0xbb> -40006f05: 16b247 bgeu a2, a4, 40006f1f <__umoddi3+0xbb> -40006f08: 000106 j 40006f10 <__umoddi3+0xac> -40006f0b: 000000 ill -40006f0e: 0ca122 movi a2, 0x10c -40006f11: 5019 s32i.n a1, a0, 20 -40006f13: c0ca add.n a12, a0, a12 -40006f15: 0134b7 bltu a4, a11, 40006f1a <__umoddi3+0xb6> -40006f18: 090c movi.n a9, 0 -40006f1a: 0b4d mov.n a4, a11 -40006f1c: c09c90 sub a9, a12, a9 -40006f1f: c03390 sub a3, a3, a9 -40006f22: c04240 sub a4, a2, a4 -40006f25: 02b247 bgeu a2, a4, 40006f2b <__umoddi3+0xc7> -40006f28: 002346 j 40006fb9 <__umoddi3+0x155> -40006f2b: 401e00 ssl a14 -40006f2e: a16300 sll a6, a3 -40006f31: 400800 ssr a8 -40006f34: 913030 srl a3, a3 -40006f37: 912040 srl a2, a4 -40006f3a: 202260 or a2, a2, a6 -40006f3d: f01d retw.n -40006f3f: f01d00 subx8 a1, a13, a0 -40006f42: 000000 ill -40006f45: 023347 bltu a3, a4, 40006f4b <__umoddi3+0xe7> -40006f48: 002986 j 40006ff2 <__umoddi3+0x18e> -40006f4b: 40f450 nsau a5, a4 -40006f4e: a58c beqz.n a5, 40006f5c <__umoddi3+0xf8> -40006f50: 401500 ssl a5 -40006f53: a18400 sll a8, a4 -40006f56: 813320 src a3, a3, a2 -40006f59: a19200 sll a9, a2 -40006f5c: f42080 extui a2, a8, 0, 16 -40006f5f: 404010 ssai 16 -40006f62: f54080 extui a4, a8, 16, 16 -40006f65: c27340 quou a7, a3, a4 -40006f68: e2b340 remu a11, a3, a4 -40006f6b: 81bb90 src a11, a11, a9 -40006f6e: 823270 mull a3, a2, a7 -40006f71: 0bad mov.n a10, a11 -40006f73: 09bb37 bgeu a11, a3, 40006f80 <__umoddi3+0x11c> -40006f76: ab8a add.n a10, a11, a8 -40006f78: 043a87 bltu a10, a8, 40006f80 <__umoddi3+0x11c> -40006f7b: 01ba37 bgeu a10, a3, 40006f80 <__umoddi3+0x11c> -40006f7e: a8aa add.n a10, a8, a10 -40006f80: c06a30 sub a6, a10, a3 -40006f83: e2c640 remu a12, a6, a4 -40006f86: 0bfc90 depbits a9, a12, 16, 16 -40006f89: c24640 quou a4, a6, a4 -40006f8c: 824240 mull a4, a2, a4 -40006f8f: 093d mov.n a3, a9 -40006f91: 17b947 bgeu a9, a4, 40006fac <__umoddi3+0x148> -40006f94: 398a add.n a3, a9, a8 -40006f96: 123387 bltu a3, a8, 40006fac <__umoddi3+0x148> -40006f99: 0fb347 bgeu a3, a4, 40006fac <__umoddi3+0x148> -40006f9c: 400500 ssr a5 -40006f9f: 383a add.n a3, a8, a3 -40006fa1: c02340 sub a2, a3, a4 -40006fa4: 912020 srl a2, a2 -40006fa7: 030c movi.n a3, 0 -40006fa9: f01d retw.n -40006fab: 234000 sext a4, a0, 7 -40006fae: 0500c0 extui a0, a12, 16, 1 -40006fb1: 030c40 rsr.scompare1 a4 -40006fb4: 912020 srl a2, a2 -40006fb7: f01d retw.n -40006fb9: 401e00 ssl a14 -40006fbc: 330b addi.n a3, a3, -1 -40006fbe: a16300 sll a6, a3 -40006fc1: 400800 ssr a8 -40006fc4: 913030 srl a3, a3 -40006fc7: 912040 srl a2, a4 -40006fca: 202260 or a2, a2, a6 -40006fcd: f01d retw.n -40006fcf: 353700 extui a3, a0, 23, 4 -40006fd2: b24705 call0 3ffb9444 <_start-0x46bbc> -40006fd5: 3ac602 addi a0, a6, 58 -40006fd8: 424000 xorb b4, b0, b0 -40006fdb: 3247c0 orbc b4, b7, b12 -40006fde: 2d08 l32i.n a0, a13, 8 -40006fe0: 6a5004 excw -40006fe3: 063dc0 excw -40006fe6: f01d retw.n -40006fe8: 042d mov.n a2, a4 -40006fea: c06a50 sub a6, a10, a5 -40006fed: 360b addi.n a3, a6, -1 -40006fef: f01d retw.n -40006ff1: 34cc00 extui a12, a0, 12, 4 -40006ff4: 180c movi.n a8, 1 -40006ff6: c28840 quou a8, a8, a4 -40006ff9: 40f850 nsau a5, a8 -40006ffc: 0c8516 beqz a5, 400070c8 <__umoddi3+0x264> -40006fff: 401500 ssl a5 -40007002: c06b50 sub a6, a11, a5 -40007005: a18800 sll a8, a8 -40007008: 400600 ssr a6 -4000700b: 91c0a0 srl a12, a10 -4000700e: 401500 ssl a5 -40007011: a14a00 sll a4, a10 -40007014: 400600 ssr a6 -40007017: 913020 srl a3, a2 -4000701a: 203340 or a3, a3, a4 -4000701d: 401500 ssl a5 -40007020: a19200 sll a9, a2 -40007023: f54080 extui a4, a8, 16, 16 -40007026: f42080 extui a2, a8, 0, 16 -40007029: c2bc40 quou a11, a12, a4 -4000702c: 404010 ssai 16 -4000702f: e2cc40 remu a12, a12, a4 -40007032: 81cc30 src a12, a12, a3 -40007035: 82b2b0 mull a11, a2, a11 -40007038: 0cad mov.n a10, a12 -4000703a: 0abcb7 bgeu a12, a11, 40007048 <__umoddi3+0x1e4> -4000703d: 80ac80 add a10, a12, a8 -40007040: 043a87 bltu a10, a8, 40007048 <__umoddi3+0x1e4> -40007043: 01bab7 bgeu a10, a11, 40007048 <__umoddi3+0x1e4> -40007046: a8aa add.n a10, a8, a10 -40007048: c0bab0 sub a11, a10, a11 -4000704b: e2cb40 remu a12, a11, a4 -4000704e: 0bfc30 depbits a3, a12, 16, 16 -40007051: c2bb40 quou a11, a11, a4 -40007054: 82b2b0 mull a11, a2, a11 -40007057: 20a330 or a10, a3, a3 -4000705a: 0bb3b7 bgeu a3, a11, 40007069 <__umoddi3+0x205> -4000705d: 80a380 add a10, a3, a8 -40007060: 053a87 bltu a10, a8, 40007069 <__umoddi3+0x205> -40007063: 02bab7 bgeu a10, a11, 40007069 <__umoddi3+0x205> -40007066: 80a8a0 add a10, a8, a10 -40007069: c03ab0 sub a3, a10, a11 -4000706c: e2b340 remu a11, a3, a4 -4000706f: 404010 ssai 16 -40007072: c23340 quou a3, a3, a4 -40007075: 81bb90 src a11, a11, a9 -40007078: 823230 mull a3, a2, a3 -4000707b: 0bad mov.n a10, a11 -4000707d: 0bbb37 bgeu a11, a3, 4000708c <__umoddi3+0x228> -40007080: 80ab80 add a10, a11, a8 -40007083: 053a87 bltu a10, a8, 4000708c <__umoddi3+0x228> -40007086: 02ba37 bgeu a10, a3, 4000708c <__umoddi3+0x228> -40007089: 80a8a0 add a10, a8, a10 -4000708c: c07a30 sub a7, a10, a3 -4000708f: e26740 remu a6, a7, a4 -40007092: 0bf690 depbits a9, a6, 16, 16 -40007095: c24740 quou a4, a7, a4 -40007098: 824240 mull a4, a2, a4 -4000709b: 093d mov.n a3, a9 -4000709d: 17b947 bgeu a9, a4, 400070b8 <__umoddi3+0x254> -400070a0: 398a add.n a3, a9, a8 -400070a2: 123387 bltu a3, a8, 400070b8 <__umoddi3+0x254> -400070a5: 0fb347 bgeu a3, a4, 400070b8 <__umoddi3+0x254> -400070a8: 400500 ssr a5 -400070ab: 383a add.n a3, a8, a3 -400070ad: c02340 sub a2, a3, a4 -400070b0: 912020 srl a2, a2 -400070b3: 030c movi.n a3, 0 -400070b5: f01d retw.n -400070b7: 234000 sext a4, a0, 7 -400070ba: 0500c0 extui a0, a12, 16, 1 -400070bd: 030c40 rsr.scompare1 a4 -400070c0: 912020 srl a2, a2 -400070c3: f01d retw.n -400070c5: f01d retw.n -400070c7: 3a8000 depbits a0, a0, 3, 9 -400070ca: 4080c0 rotw -4 -400070cd: 2080f5 call12 400278dc <__bss_start+0x178dc> -400070d0: e5c6f4 excw -400070d3: ff .byte 0xff -400070d4: ffc000 excw -400070d7: 3f .byte 0x3f -400070d8: ffc864 excw -400070db: 3f .byte 0x3f -400070dc: 007104 excw -400070df: c87040 excw -400070e2: ff .byte 0xff -400070e3: 3f .byte 0x3f -400070e4: ffcd40 excw -400070e7: 3f .byte 0x3f -400070e8: 007974 excw -400070eb: cd4040 excw -400070ee: ff .byte 0xff -400070ef: 3f .byte 0x3f -400070f0: ffcd44 excw -400070f3: 3f .byte 0x3f -400070f4: 007e44 excw -400070f7: 000040 excw - ... -4000710a: b80000 excw -4000710d: 400066 bnei a0, -1, 40007151 <__umoddi3+0x2ed> -40007110: 0818 l32i.n a1, a8, 0 -40007112: b84000 excw -40007115: 400066 bnei a0, -1, 40007159 <__umoddi3+0x2f5> -40007118: 66b8 l32i.n a11, a6, 24 -4000711a: 9c4000 excw -4000711d: 0008 l32i.n a0, a0, 0 -4000711f: 001040 movsp a4, a0 -40007122: b84000 excw -40007125: 400066 bnei a0, -1, 40007169 <__umoddi3+0x305> -40007128: 66b8 l32i.n a11, a6, 24 -4000712a: b84000 excw -4000712d: 400066 bnei a0, -1, 40007171 <__umoddi3+0x30d> -40007130: 66b8 l32i.n a11, a6, 24 -40007132: b84000 excw -40007135: 400066 bnei a0, -1, 40007179 <__umoddi3+0x315> -40007138: 66b8 l32i.n a11, a6, 24 -4000713a: 304000 xor a4, a0, a0 -4000713d: 0e .byte 0xe -4000713e: 304000 xor a4, a0, a0 -40007141: 0e .byte 0xe -40007142: 304000 xor a4, a0, a0 -40007145: 0e .byte 0xe -40007146: 304000 xor a4, a0, a0 -40007149: 0e .byte 0xe -4000714a: b84000 excw -4000714d: 400066 bnei a0, -1, 40007191 <__umoddi3+0x32d> -40007150: 66b8 l32i.n a11, a6, 24 -40007152: b84000 excw -40007155: 400066 bnei a0, -1, 40007199 <__umoddi3+0x335> -40007158: 66b8 l32i.n a11, a6, 24 -4000715a: b84000 excw -4000715d: 400066 bnei a0, -1, 400071a1 <__umoddi3+0x33d> -40007160: 66b8 l32i.n a11, a6, 24 -40007162: b84000 excw -40007165: 400066 bnei a0, -1, 400071a9 <__umoddi3+0x345> -40007168: 66b8 l32i.n a11, a6, 24 -4000716a: b84000 excw -4000716d: 400066 bnei a0, -1, 400071b1 <__umoddi3+0x34d> -40007170: 66b8 l32i.n a11, a6, 24 -40007172: b84000 excw -40007175: 400066 bnei a0, -1, 400071b9 <__umoddi3+0x355> -40007178: 66b8 l32i.n a11, a6, 24 -4000717a: b84000 excw -4000717d: 400066 bnei a0, -1, 400071c1 <__umoddi3+0x35d> -40007180: 66b8 l32i.n a11, a6, 24 -40007182: b84000 excw -40007185: 400066 bnei a0, -1, 400071c9 <__umoddi3+0x365> -40007188: 66b8 l32i.n a11, a6, 24 -4000718a: b84000 excw -4000718d: 400066 bnei a0, -1, 400071d1 <__umoddi3+0x36d> -40007190: 66b8 l32i.n a11, a6, 24 -40007192: b84000 excw -40007195: 400066 bnei a0, -1, 400071d9 <__umoddi3+0x375> -40007198: 66b8 l32i.n a11, a6, 24 -4000719a: b84000 excw -4000719d: 400066 bnei a0, -1, 400071e1 <__umoddi3+0x37d> -400071a0: 66b8 l32i.n a11, a6, 24 -400071a2: b84000 excw -400071a5: 400066 bnei a0, -1, 400071e9 <__umoddi3+0x385> -400071a8: 66b8 l32i.n a11, a6, 24 -400071aa: b84000 excw -400071ad: 400066 bnei a0, -1, 400071f1 <__umoddi3+0x38d> -400071b0: 66b8 l32i.n a11, a6, 24 -400071b2: b84000 excw -400071b5: 400066 bnei a0, -1, 400071f9 <__umoddi3+0x395> -400071b8: 66b8 l32i.n a11, a6, 24 -400071ba: b84000 excw -400071bd: 400066 bnei a0, -1, 40007201 <__umoddi3+0x39d> -400071c0: 66b8 l32i.n a11, a6, 24 -400071c2: b84000 excw -400071c5: 400066 bnei a0, -1, 40007209 <__umoddi3+0x3a5> -400071c8: 66b8 l32i.n a11, a6, 24 -400071ca: b84000 excw -400071cd: 400066 bnei a0, -1, 40007211 <__umoddi3+0x3ad> -400071d0: 66b8 l32i.n a11, a6, 24 -400071d2: b84000 excw -400071d5: 400066 bnei a0, -1, 40007219 <__umoddi3+0x3b5> -400071d8: 66b8 l32i.n a11, a6, 24 -400071da: b84000 excw -400071dd: 400066 bnei a0, -1, 40007221 <__umoddi3+0x3bd> -400071e0: 66b8 l32i.n a11, a6, 24 -400071e2: b84000 excw -400071e5: 400066 bnei a0, -1, 40007229 <__umoddi3+0x3c5> -400071e8: 66b8 l32i.n a11, a6, 24 -400071ea: b84000 excw -400071ed: 400066 bnei a0, -1, 40007231 <__umoddi3+0x3cd> -400071f0: 66b8 l32i.n a11, a6, 24 -400071f2: b84000 excw -400071f5: 400066 bnei a0, -1, 40007239 <__umoddi3+0x3d5> -400071f8: 66b8 l32i.n a11, a6, 24 -400071fa: b84000 excw -400071fd: 400066 bnei a0, -1, 40007241 <__umoddi3+0x3dd> -40007200: 66b8 l32i.n a11, a6, 24 -40007202: b84000 excw -40007205: 400066 bnei a0, -1, 40007249 <__umoddi3+0x3e5> -40007208: 66b8 l32i.n a11, a6, 24 -4000720a: 684000 excw -4000720d: 400066 bnei a0, -1, 40007251 <__umoddi3+0x3ed> -40007210: 6668 l32i.n a6, a6, 24 -40007212: 684000 excw -40007215: 400066 bnei a0, -1, 40007259 <__umoddi3+0x3f5> -40007218: 6668 l32i.n a6, a6, 24 -4000721a: 684000 excw -4000721d: 400066 bnei a0, -1, 40007261 <__umoddi3+0x3fd> -40007220: 6668 l32i.n a6, a6, 24 -40007222: 684000 excw -40007225: 400066 bnei a0, -1, 40007269 <__umoddi3+0x405> -40007228: 6668 l32i.n a6, a6, 24 -4000722a: 684000 excw -4000722d: 400066 bnei a0, -1, 40007271 <__umoddi3+0x40d> -40007230: 6668 l32i.n a6, a6, 24 -40007232: 684000 excw -40007235: 400066 bnei a0, -1, 40007279 <__umoddi3+0x415> -40007238: 6668 l32i.n a6, a6, 24 -4000723a: 684000 excw -4000723d: 400066 bnei a0, -1, 40007281 <__umoddi3+0x41d> -40007240: 6668 l32i.n a6, a6, 24 -40007242: 684000 excw -40007245: 400066 bnei a0, -1, 40007289 <__umoddi3+0x425> -40007248: 6668 l32i.n a6, a6, 24 -4000724a: 684000 excw -4000724d: 400066 bnei a0, -1, 40007291 <__umoddi3+0x42d> -40007250: 6668 l32i.n a6, a6, 24 -40007252: 684000 excw -40007255: 400066 bnei a0, -1, 40007299 <__umoddi3+0x435> -40007258: 6668 l32i.n a6, a6, 24 -4000725a: 684000 excw -4000725d: 400066 bnei a0, -1, 400072a1 <__umoddi3+0x43d> -40007260: 6668 l32i.n a6, a6, 24 -40007262: 684000 excw -40007265: 400066 bnei a0, -1, 400072a9 <__umoddi3+0x445> -40007268: 6668 l32i.n a6, a6, 24 -4000726a: 684000 excw -4000726d: 400066 bnei a0, -1, 400072b1 <__umoddi3+0x44d> -40007270: 6668 l32i.n a6, a6, 24 -40007272: 684000 excw -40007275: 400066 bnei a0, -1, 400072b9 <__umoddi3+0x455> -40007278: 6668 l32i.n a6, a6, 24 -4000727a: 684000 excw -4000727d: 400066 bnei a0, -1, 400072c1 <__umoddi3+0x45d> -40007280: 6668 l32i.n a6, a6, 24 -40007282: 684000 excw -40007285: 400066 bnei a0, -1, 400072c9 <__umoddi3+0x465> -40007288: 6668 l32i.n a6, a6, 24 -4000728a: 684000 excw -4000728d: 400066 bnei a0, -1, 400072d1 <__umoddi3+0x46d> -40007290: 6668 l32i.n a6, a6, 24 -40007292: 684000 excw -40007295: 400066 bnei a0, -1, 400072d9 <__umoddi3+0x475> -40007298: 6668 l32i.n a6, a6, 24 -4000729a: 684000 excw -4000729d: 400066 bnei a0, -1, 400072e1 <__umoddi3+0x47d> -400072a0: 6668 l32i.n a6, a6, 24 -400072a2: 684000 excw -400072a5: 400066 bnei a0, -1, 400072e9 <__umoddi3+0x485> -400072a8: 6668 l32i.n a6, a6, 24 -400072aa: 684000 excw -400072ad: 400066 bnei a0, -1, 400072f1 <__umoddi3+0x48d> -400072b0: 6668 l32i.n a6, a6, 24 -400072b2: 684000 excw -400072b5: 400066 bnei a0, -1, 400072f9 <__umoddi3+0x495> -400072b8: 6668 l32i.n a6, a6, 24 -400072ba: 684000 excw -400072bd: 400066 bnei a0, -1, 40007301 <__umoddi3+0x49d> -400072c0: 6668 l32i.n a6, a6, 24 -400072c2: 684000 excw -400072c5: 400066 bnei a0, -1, 40007309 <__umoddi3+0x4a5> -400072c8: 6668 l32i.n a6, a6, 24 -400072ca: 684000 excw -400072cd: 400066 bnei a0, -1, 40007311 <__umoddi3+0x4ad> -400072d0: 6668 l32i.n a6, a6, 24 -400072d2: 684000 excw -400072d5: 400066 bnei a0, -1, 40007319 <__umoddi3+0x4b5> -400072d8: 6668 l32i.n a6, a6, 24 -400072da: 684000 excw -400072dd: 400066 bnei a0, -1, 40007321 <__umoddi3+0x4bd> -400072e0: 6668 l32i.n a6, a6, 24 -400072e2: 684000 excw -400072e5: 400066 bnei a0, -1, 40007329 <__umoddi3+0x4c5> -400072e8: 6668 l32i.n a6, a6, 24 -400072ea: 684000 excw -400072ed: 400066 bnei a0, -1, 40007331 <__umoddi3+0x4cd> -400072f0: 6668 l32i.n a6, a6, 24 -400072f2: 684000 excw -400072f5: 400066 bnei a0, -1, 40007339 <__umoddi3+0x4d5> -400072f8: 6668 l32i.n a6, a6, 24 -400072fa: 684000 excw -400072fd: 400066 bnei a0, -1, 40007341 <__umoddi3+0x4dd> -40007300: 6668 l32i.n a6, a6, 24 -40007302: 684000 excw -40007305: 400066 bnei a0, -1, 40007349 <__umoddi3+0x4e5> -40007308: 6668 l32i.n a6, a6, 24 -4000730a: 004000 break 0, 0 - ... -40007315: 000000 ill -40007318: ff .byte 0xff -40007319: ff .byte 0xff -4000731a: ff .byte 0xff -4000731b: ff .byte 0xff -4000731c: 0066b0 rsil a11, 6 -4000731f: 001f40 movsp a4, a15 -40007322: b00000 addx8 a0, a0, a0 -40007325: 400066 bnei a0, -1, 40007369 <__umoddi3+0x505> -40007328: 1e .byte 0x1e -40007329: 000000 ill -4000732c: 0066b0 rsil a11, 6 -4000732f: 001d40 movsp a4, a13 -40007332: b00000 addx8 a0, a0, a0 -40007335: 400066 bnei a0, -1, 40007379 <__umoddi3+0x515> -40007338: 001c movi.n a0, 16 -4000733a: b00000 addx8 a0, a0, a0 -4000733d: 400066 bnei a0, -1, 40007381 <__umoddi3+0x51d> -40007340: 001b addi.n a0, a0, 1 -40007342: b00000 addx8 a0, a0, a0 -40007345: 400066 bnei a0, -1, 40007389 <__umoddi3+0x525> -40007348: 001a add.n a0, a0, a1 -4000734a: b00000 addx8 a0, a0, a0 -4000734d: 400066 bnei a0, -1, 40007391 <__umoddi3+0x52d> -40007350: 0019 s32i.n a1, a0, 0 -40007352: b00000 addx8 a0, a0, a0 -40007355: 400066 bnei a0, -1, 40007399 <__umoddi3+0x535> -40007358: 0018 l32i.n a1, a0, 0 -4000735a: b00000 addx8 a0, a0, a0 -4000735d: 400066 bnei a0, -1, 400073a1 <__umoddi3+0x53d> -40007360: 000017 bnone a0, a1, 40007364 <__umoddi3+0x500> -40007363: 66b000 excw -40007366: 164000 excw -40007369: 000000 ill -4000736c: 0066b0 rsil a11, 6 -4000736f: 001540 movsp a4, a5 -40007372: b00000 addx8 a0, a0, a0 -40007375: 400066 bnei a0, -1, 400073b9 <__umoddi3+0x555> -40007378: 000014 excw -4000737b: 66b000 excw -4000737e: 134000 excw -40007381: 000000 ill -40007384: 0066b0 rsil a11, 6 -40007387: 001240 movsp a4, a2 -4000738a: b00000 addx8 a0, a0, a0 -4000738d: 400066 bnei a0, -1, 400073d1 <__umoddi3+0x56d> -40007390: 000011 l32r a1, 3ffc7390 <_start-0x38c70> -40007393: 66b000 excw -40007396: 104000 and a4, a0, a0 -40007399: 000000 ill -4000739c: 0066b0 rsil a11, 6 -4000739f: 000f40 excw -400073a2: b00000 addx8 a0, a0, a0 -400073a5: 400066 bnei a0, -1, 400073e9 <__umoddi3+0x585> -400073a8: 0e .byte 0xe -400073a9: 000000 ill -400073ac: 0066b0 rsil a11, 6 -400073af: 000d40 excw -400073b2: b00000 addx8 a0, a0, a0 -400073b5: 400066 bnei a0, -1, 400073f9 <__umoddi3+0x595> -400073b8: 000c movi.n a0, 0 -400073ba: b00000 addx8 a0, a0, a0 -400073bd: 400066 bnei a0, -1, 40007401 <__umoddi3+0x59d> -400073c0: 000b addi.n a0, a0, -1 -400073c2: b00000 addx8 a0, a0, a0 -400073c5: 400066 bnei a0, -1, 40007409 <__umoddi3+0x5a5> -400073c8: 000a add.n a0, a0, a0 -400073ca: b00000 addx8 a0, a0, a0 -400073cd: 400066 bnei a0, -1, 40007411 <__umoddi3+0x5ad> -400073d0: 0009 s32i.n a0, a0, 0 -400073d2: b00000 addx8 a0, a0, a0 -400073d5: 400066 bnei a0, -1, 40007419 <__umoddi3+0x5b5> -400073d8: 0008 l32i.n a0, a0, 0 -400073da: b00000 addx8 a0, a0, a0 -400073dd: 400066 bnei a0, -1, 40007421 <__umoddi3+0x5bd> -400073e0: 000007 bnone a0, a0, 400073e4 <__umoddi3+0x580> -400073e3: 66b000 excw -400073e6: 064000 excw -400073e9: 000000 ill -400073ec: 0066b0 rsil a11, 6 -400073ef: 000540 excw -400073f2: b00000 addx8 a0, a0, a0 -400073f5: 400066 bnei a0, -1, 40007439 <__umoddi3+0x5d5> -400073f8: 000004 excw -400073fb: 66b000 excw -400073fe: 034000 excw -40007401: 000000 ill -40007404: 0066b0 rsil a11, 6 -40007407: 000240 excw -4000740a: b00000 addx8 a0, a0, a0 -4000740d: 400066 bnei a0, -1, 40007451 <__umoddi3+0x5ed> -40007410: 000001 l32r a0, 3ffc7410 <_start-0x38bf0> -40007413: 66b000 excw -40007416: 004000 break 0, 0 -40007419: 000000 ill -4000741c: 81c000 src a12, a0, a0 -4000741f: ff .byte 0xff -40007420: 000000 ill -40007423: c00080 sub a0, a0, a8 -40007426: 00ff81 l32r a8, 3ffc7824 <_start-0x387dc> -40007429: 400000 ssr a0 -4000742c: 81c000 src a12, a0, a0 -4000742f: ff .byte 0xff -40007430: 000000 ill -40007433: c00020 sub a0, a0, a2 -40007436: 00ff81 l32r a8, 3ffc7834 <_start-0x387cc> -40007439: 100000 and a0, a0, a0 -4000743c: 81c000 src a12, a0, a0 -4000743f: ff .byte 0xff -40007440: 000000 ill -40007443: 0008 l32i.n a0, a0, 0 -40007445: ff81c0 excw -40007448: 000000 ill -4000744b: c00004 excw -4000744e: 00ff81 l32r a8, 3ffc784c <_start-0x387b4> -40007451: 020000 andb b0, b0, b0 -40007454: 81c000 src a12, a0, a0 -40007457: ff .byte 0xff -40007458: 000000 ill -4000745b: c00001 l32r a0, 3fff745c <_start-0x8ba4> -4000745e: 00ff81 l32r a8, 3ffc785c <_start-0x387a4> -40007461: 008000 any4 b0, b0:b1:b2:b3 -40007464: 81c000 src a12, a0, a0 -40007467: ff .byte 0xff -40007468: 400000 ssr a0 -4000746b: c00000 sub a0, a0, a0 -4000746e: 00ffc1 l32r a12, 3ffc786c <_start-0x38794> -40007471: 002000 isync -40007474: e1c000 excw -40007477: ff .byte 0xff -40007478: 100000 and a0, a0, a0 -4000747b: c00000 sub a0, a0, a0 -4000747e: 00fff1 l32r a15, 3ffc787c <_start-0x38784> -40007481: 000800 excw -40007484: f9c000 excw -40007487: ff .byte 0xff -40007488: 040000 extui a0, a0, 0, 1 -4000748b: c00000 sub a0, a0, a0 -4000748e: fffd excw -40007490: 020000 andb b0, b0, b0 -40007493: c00000 sub a0, a0, a0 -40007496: ff .byte 0xff -40007497: ff .byte 0xff -40007498: 010000 slli a0, a0, 32 -4000749b: c00000 sub a0, a0, a0 -4000749e: ff .byte 0xff -4000749f: ff .byte 0xff -400074a0: 008000 any4 b0, b0:b1:b2:b3 -400074a3: c00000 sub a0, a0, a0 -400074a6: ff .byte 0xff -400074a7: ff .byte 0xff -400074a8: 004000 break 0, 0 -400074ab: c00000 sub a0, a0, a0 -400074ae: ff .byte 0xff -400074af: ff .byte 0xff -400074b0: 002000 isync -400074b3: e00000 subx4 a0, a0, a0 -400074b6: ff .byte 0xff -400074b7: ff .byte 0xff -400074b8: 001000 movsp a0, a0 -400074bb: f00000 subx8 a0, a0, a0 -400074be: ff .byte 0xff -400074bf: ff .byte 0xff -400074c0: 000800 excw -400074c3: f80000 excw -400074c6: ff .byte 0xff -400074c7: ff .byte 0xff -400074c8: 000400 excw -400074cb: fc0000 excw -400074ce: ff .byte 0xff -400074cf: ff .byte 0xff -400074d0: 000200 excw -400074d3: fe0000 excw -400074d6: ff .byte 0xff -400074d7: ff .byte 0xff -400074d8: 000100 excw -400074db: ff0000 excw -400074de: ff .byte 0xff -400074df: ff .byte 0xff -400074e0: 000080 ret -400074e3: ff8000 excw -400074e6: ff .byte 0xff -400074e7: ff .byte 0xff -400074e8: 000040 excw -400074eb: ffc000 excw -400074ee: ff .byte 0xff -400074ef: ff .byte 0xff -400074f0: 000020 excw -400074f3: ffe000 excw -400074f6: ff .byte 0xff -400074f7: ff .byte 0xff -400074f8: 000010 excw -400074fb: fff000 excw -400074fe: ff .byte 0xff -400074ff: ff .byte 0xff -40007500: 0008 l32i.n a0, a0, 0 -40007502: f80000 excw -40007505: ff .byte 0xff -40007506: ff .byte 0xff -40007507: ff .byte 0xff -40007508: 000004 excw -4000750b: fffc00 excw -4000750e: ff .byte 0xff -4000750f: ff .byte 0xff -40007510: 000002 l8ui a0, a0, 0 -40007513: fffe00 excw -40007516: ff .byte 0xff -40007517: ff .byte 0xff -40007518: 000001 l32r a0, 3ffc7518 <_start-0x38ae8> -4000751b: 000000 ill -4000751e: 040000 extui a0, a0, 0, 1 -40007521: 3fffc7 bbsi a15, 28, 40007564 <__umoddi3+0x700> -40007524: c76c movi.n a7, -20 -40007526: ff .byte 0xff -40007527: 3f .byte 0x3f -40007528: ffc7d4 excw -4000752b: 3f .byte 0x3f - ... -40007550: ffca50 excw -40007553: 3f .byte 0x3f - ... -400075c4: 000001 l32r a0, 3ffc75c4 <_start-0x38a3c> -400075c7: 000000 ill -400075ca: 0e0000 read_impwire a0 -400075cd: abcd33 excw -400075d0: 6d1234 excw -400075d3: deece6 bgei a12, 128, 400075b5 <__umoddi3+0x751> -400075d6: 0b0005 call0 400125d8 <__bss_start+0x25d8> - ... -40007941: 000000 ill -40007944: c418 l32i.n a1, a4, 48 -40007946: ff .byte 0xff -40007947: 3f .byte 0x3f -40007948: 000d mov.n a0, a0 -4000794a: 4c0000 excw -4000794d: ffc8 l32i.n a12, a15, 60 -4000794f: 3f .byte 0x3f -40007950: ef .byte 0xef -40007951: 001540 movsp a4, a5 -40007954: 200000 or a0, a0, a0 -40007957: 000000 ill -4000795a: 000001 l32r a0, 3ffc795c <_start-0x386a4> -4000795d: 000010 excw -40007960: 000100 excw -40007963: ffff00 excw - ... -40007972: 180000 excw -40007975: 3fffc4 excw - ... -40007984: 737465 call8 4007b0cc <__bss_start+0x6b0cc> -40007987: 5f .byte 0x5f -40007988: 706e75 call12 40078070 <__bss_start+0x68070> -4000798b: 6b6361 l32r a6, 3ffe2718 <_start-0x1d8e8> -4000798e: 5f .byte 0x5f -4000798f: 616c66 bnei a12, 6, 400079f4 <__umoddi3+0xb90> -40007992: 5f6873 excw -40007995: 646f63 excw -40007998: 000065 call8 400079a0 <__umoddi3+0xb3c> - ... -400079a3: 26b300 excw -400079a6: 684000 excw -400079a9: 400024 excw -400079ac: 2468 l32i.n a6, a4, 8 -400079ae: 684000 excw -400079b1: 400024 excw -400079b4: 2468 l32i.n a6, a4, 8 -400079b6: 684000 excw -400079b9: 400024 excw -400079bc: 2468 l32i.n a6, a4, 8 -400079be: 684000 excw -400079c1: 400024 excw -400079c4: 2468 l32i.n a6, a4, 8 -400079c6: 684000 excw -400079c9: 400024 excw -400079cc: 2468 l32i.n a6, a4, 8 -400079ce: 684000 excw -400079d1: 400024 excw -400079d4: 2468 l32i.n a6, a4, 8 -400079d6: 684000 excw -400079d9: 400024 excw -400079dc: 2468 l32i.n a6, a4, 8 -400079de: 684000 excw -400079e1: 400024 excw -400079e4: 2468 l32i.n a6, a4, 8 -400079e6: 684000 excw -400079e9: 400024 excw -400079ec: 2468 l32i.n a6, a4, 8 -400079ee: 684000 excw -400079f1: 400024 excw -400079f4: 2468 l32i.n a6, a4, 8 -400079f6: 684000 excw -400079f9: 400024 excw -400079fc: 2468 l32i.n a6, a4, 8 -400079fe: 684000 excw -40007a01: 400024 excw -40007a04: 2468 l32i.n a6, a4, 8 -40007a06: 684000 excw -40007a09: 400024 excw -40007a0c: 2468 l32i.n a6, a4, 8 -40007a0e: 684000 excw -40007a11: 400024 excw -40007a14: 2468 l32i.n a6, a4, 8 -40007a16: 684000 excw -40007a19: 400024 excw -40007a1c: 002681 l32r a8, 3ffc7ab4 <_start-0x3854c> -40007a1f: 253840 extui a3, a4, 24, 3 -40007a22: 684000 excw -40007a25: 400024 excw -40007a28: 2468 l32i.n a6, a4, 8 -40007a2a: 684000 excw -40007a2d: 400024 excw -40007a30: 2468 l32i.n a6, a4, 8 -40007a32: 684000 excw -40007a35: 400024 excw -40007a38: 2468 l32i.n a6, a4, 8 -40007a3a: 684000 excw -40007a3d: 400024 excw -40007a40: 2468 l32i.n a6, a4, 8 -40007a42: 684000 excw -40007a45: 400024 excw -40007a48: 2468 l32i.n a6, a4, 8 -40007a4a: 684000 excw -40007a4d: 400024 excw -40007a50: 2468 l32i.n a6, a4, 8 -40007a52: 684000 excw -40007a55: 400024 excw -40007a58: 2468 l32i.n a6, a4, 8 -40007a5a: 354000 extui a4, a0, 16, 4 -40007a5d: 400026 beqi a0, -1, 40007aa1 <__umoddi3+0xc3d> -40007a60: 2468 l32i.n a6, a4, 8 -40007a62: 384000 excw -40007a65: 400025 call8 40047a68 <__bss_start+0x37a68> -40007a68: 2468 l32i.n a6, a4, 8 -40007a6a: 684000 excw -40007a6d: 400024 excw -40007a70: 2538 l32i.n a3, a5, 8 -40007a72: 684000 excw -40007a75: 400024 excw -40007a78: 2468 l32i.n a6, a4, 8 -40007a7a: 684000 excw -40007a7d: 400024 excw -40007a80: 2468 l32i.n a6, a4, 8 -40007a82: 684000 excw -40007a85: 400024 excw -40007a88: 2468 l32i.n a6, a4, 8 -40007a8a: 684000 excw -40007a8d: 400024 excw -40007a90: 2468 l32i.n a6, a4, 8 -40007a92: 684000 excw -40007a95: 400024 excw -40007a98: 2468 l32i.n a6, a4, 8 -40007a9a: 814000 src a4, a0, a0 -40007a9d: 400026 beqi a0, -1, 40007ae1 <__umoddi3+0xc7d> -40007aa0: 2538 l32i.n a3, a5, 8 -40007aa2: 684000 excw -40007aa5: 400024 excw -40007aa8: 2468 l32i.n a6, a4, 8 -40007aaa: 684000 excw -40007aad: 400024 excw -40007ab0: 2468 l32i.n a6, a4, 8 -40007ab2: 684000 excw -40007ab5: 400024 excw -40007ab8: 2468 l32i.n a6, a4, 8 -40007aba: 684000 excw -40007abd: 400024 excw -40007ac0: 2468 l32i.n a6, a4, 8 -40007ac2: 684000 excw -40007ac5: 400024 excw -40007ac8: 2468 l32i.n a6, a4, 8 -40007aca: 684000 excw -40007acd: 400024 excw -40007ad0: 002527 blt a5, a2, 40007ad4 <__umoddi3+0xc70> -40007ad3: 246840 extui a6, a4, 8, 3 -40007ad6: 684000 excw -40007ad9: 400024 excw -40007adc: 002635 call12 40007d40 <__umoddi3+0xedc> -40007adf: 246840 extui a6, a4, 8, 3 -40007ae2: 384000 excw -40007ae5: 400025 call8 40047ae8 <__bss_start+0x37ae8> -40007ae8: 2468 l32i.n a6, a4, 8 -40007aea: 684000 excw -40007aed: 400024 excw -40007af0: 2538 l32i.n a3, a5, 8 -40007af2: 124000 andbc b4, b0, b0 -40007af5: 400030 excw -40007af8: 003012 excw -40007afb: 30c240 xor a12, a2, a4 -40007afe: b04000 addx8 a4, a0, a0 -40007b01: 400030 excw -40007b04: 3098 l32i.n a9, a0, 12 -40007b06: 1e4000 excw -40007b09: 400030 excw -40007b0c: 003084 excw -40007b0f: 307240 xor a7, a2, a4 -40007b12: 624000 excw -40007b15: 400030 excw -40007b18: 003052 excw -40007b1b: 304040 xor a4, a0, a4 -40007b1e: 304000 xor a4, a0, a0 -40007b21: 400030 excw -40007b24: 00dbc0 excw - ... -40007b33: 010100 slli a0, a1, 32 -40007b36: 010101 l32r a0, 3ffc7f3c <_start-0x380c4> -40007b39: 010101 l32r a0, 3ffc7f40 <_start-0x380c0> -40007b3c: 010101 l32r a0, 3ffc7f40 <_start-0x380c0> -40007b3f: 010101 l32r a0, 3ffc7f44 <_start-0x380bc> -40007b42: 050307 bnone a3, a0, 40007b4b <__umoddi3+0xce7> -40007b45: 010101 l32r a0, 3ffc7f4c <_start-0x380b4> -40007b48: 010101 l32r a0, 3ffc7f4c <_start-0x380b4> -40007b4b: 020202 l8ui a0, a2, 2 -40007b4e: 030302 l8ui a0, a3, 3 -40007b51: 050403 excw -40007b54: 000043 excw -40007b57: 200a00 or a0, a10, a0 -40007b5a: 737465 call8 4007b2a0 <__bss_start+0x6b2a0> -40007b5d: 732520 maxu a2, a5, a2 -40007b60: 722c movi.n a2, 39 -40007b62: 207473 excw -40007b65: 756163 excw -40007b68: 3a6573 excw -40007b6b: 2c6425 call8 400341ac <__bss_start+0x241ac> -40007b6e: 6f6220 excw -40007b71: 6f .byte 0x6f -40007b72: 6d2074 excw -40007b75: 6f .byte 0x6f -40007b76: 3a6564 excw -40007b79: 2528 l32i.n a2, a5, 8 -40007b7b: 2978 l32i.n a7, a9, 8 -40007b7d: 0a0a add.n a0, a10, a0 -40007b7f: 754a00 extui a4, a0, 26, 8 -40007b82: 206c movi.n a0, -30 -40007b84: 203520 or a3, a5, a2 -40007b87: 313032 excw -40007b8a: 250035 call12 4002cb8c <__bss_start+0x1cb8c> -40007b8d: 252073 excw -40007b90: 0a2075 call12 40011d98 <__bss_start+0x1d98> -40007b93: 746500 extui a6, a0, 5, 8 -40007b96: 6d5f73 excw -40007b99: 6e6961 l32r a6, 3ffe3540 <_start-0x1cac0> -40007b9c: 2e .byte 0x2e -40007b9d: 000063 excw -40007ba0: 657375 call12 4006d2d8 <__bss_start+0x5d2d8> -40007ba3: 632072 l32i a7, a0, 0x18c -40007ba6: 6f .byte 0x6f -40007ba7: 206564 excw -40007baa: 6e6f64 excw -40007bad: 000a65 call8 40007c54 <__umoddi3+0xdf0> -40007bb0: 696177 bbci a1, 7, 40007c1d <__umoddi3+0xdb9> -40007bb3: 6e6974 excw -40007bb6: 662067 blt a0, a6, 40007c20 <__umoddi3+0xdbc> -40007bb9: 6f .byte 0x6f -40007bba: 682072 l32i a7, a0, 0x1a0 -40007bbd: 6f .byte 0x6f -40007bbe: 0a7473 excw -40007bc1: 000000 ill -40007bc4: 697073 excw -40007bc7: 6d6320 excw -40007bca: 6c2064 excw -40007bcd: 206e65 call8 400282b4 <__bss_start+0x182b4> -40007bd0: 0a6425 call8 40012214 <__bss_start+0x2214> -40007bd3: 642500 extui a2, a0, 5, 7 -40007bd6: 696c20 excw -40007bd9: 6e .byte 0x6e -40007bda: 202c65 call8 40027ea0 <__bss_start+0x17ea0> -40007bdd: 206425 call8 40028220 <__bss_start+0x18220> -40007be0: 6f6d excw -40007be2: 0a6564 excw -40007be5: 000000 ill -40007be8: 6f6c movi.n a15, -26 -40007bea: 206461 l32r a6, 3ffcfd7c <_start-0x30284> -40007bed: 257830 extui a7, a3, 24, 3 -40007bf0: 783830 excw -40007bf3: 202c movi.n a0, 34 -40007bf5: 656c movi.n a5, -26 -40007bf7: 6e .byte 0x6e -40007bf8: 642520 extui a2, a2, 5, 7 -40007bfb: 202c movi.n a0, 34 -40007bfd: 6f6f72 s32i a7, a15, 0x1bc -40007c00: 206d excw -40007c02: 0a6425 call8 40012244 <__bss_start+0x2244> -40007c05: 000000 ill -40007c08: 616c66 bnei a12, 6, 40007c6d <__umoddi3+0xe09> -40007c0b: 206873 excw -40007c0e: 616572 s32i a7, a5, 0x184 -40007c11: 652064 excw -40007c14: 2c7272 excw -40007c17: 732520 maxu a2, a5, a2 -40007c1a: 000a add.n a0, a0, a0 -40007c1c: 696174 excw -40007c1f: 206c movi.n a0, -30 -40007c21: 0a6425 call8 40012264 <__bss_start+0x2264> -40007c24: 000000 ill -40007c27: 686300 excw -40007c2a: 736b addi.n a7, a3, 6 -40007c2c: 206d75 call12 40028304 <__bss_start+0x18304> -40007c2f: 257830 extui a7, a3, 24, 3 -40007c32: 783230 excw -40007c35: 000a add.n a0, a0, a0 -40007c37: 736300 maxu a6, a3, a0 -40007c3a: 206d75 call12 40028310 <__bss_start+0x18310> -40007c3d: 257830 extui a7, a3, 24, 3 -40007c40: 783230 excw -40007c43: 000a add.n a0, a0, a0 -40007c45: 000000 ill -40007c48: 757363 excw -40007c4b: 206d excw -40007c4d: 727265 call8 4007a374 <__bss_start+0x6a374> -40007c50: 000a add.n a0, a0, a0 -40007c52: 680000 excw -40007c55: 6f .byte 0x6f -40007c56: 642520 extui a2, a2, 5, 7 -40007c59: 617420 excw -40007c5c: 6c69 s32i.n a6, a12, 24 -40007c5e: 642520 extui a2, a2, 5, 7 -40007c61: 6f7220 excw -40007c64: 6f .byte 0x6f -40007c65: 206d excw -40007c67: 0a6425 call8 400122a8 <__bss_start+0x22a8> -40007c6a: 090000 l32e a0, a0, -64 -40007c6d: 735f20 maxu a5, a15, a2 -40007c70: 636174 excw -40007c73: 5f6b addi.n a5, a15, 6 -40007c75: 6e6573 excw -40007c78: 797274 excw -40007c7b: 203a add.n a2, a0, a3 -40007c7d: 2c7025 call8 40034380 <__bss_start+0x24380> -40007c80: 200920 or a0, a9, a2 -40007c83: 5f .byte 0x5f -40007c84: 5f .byte 0x5f -40007c85: 617473 excw -40007c88: 3a6b63 excw -40007c8b: 783020 excw -40007c8e: 383025 call8 4003ff90 <__bss_start+0x2ff90> -40007c91: 2c78 l32i.n a7, a12, 8 -40007c93: 200a20 or a0, a10, a2 -40007c96: 2009 s32i.n a0, a0, 8 -40007c98: 5f .byte 0x5f -40007c99: 737362 excw -40007c9c: 5f .byte 0x5f -40007c9d: 617473 excw -40007ca0: 3a7472 excw -40007ca3: 783020 excw -40007ca6: 383025 call8 4003ffa8 <__bss_start+0x2ffa8> -40007ca9: 2c78 l32i.n a7, a12, 8 -40007cab: 200920 or a0, a9, a2 -40007cae: 5f .byte 0x5f -40007caf: 737362 excw -40007cb2: 5f .byte 0x5f -40007cb3: 646e65 call8 4006c398 <__bss_start+0x5c398> -40007cb6: 203a add.n a2, a0, a3 -40007cb8: 257830 extui a7, a3, 24, 3 -40007cbb: 783830 excw -40007cbe: 202c movi.n a0, 34 -40007cc0: 200a add.n a2, a0, a0 -40007cc2: 2009 s32i.n a0, a0, 8 -40007cc4: 5f .byte 0x5f -40007cc5: 746164 excw -40007cc8: 735f61 l32r a6, 3ffe4a44 <_start-0x1b5bc> -40007ccb: 726174 excw -40007cce: 203a74 excw -40007cd1: 2c7025 call8 400343d4 <__bss_start+0x243d4> -40007cd4: 200920 or a0, a9, a2 -40007cd7: 5f .byte 0x5f -40007cd8: 746164 excw -40007cdb: 655f61 l32r a6, 3ffe1258 <_start-0x1eda8> -40007cde: 6e .byte 0x6e -40007cdf: 203a64 excw -40007ce2: 207025 call8 400283e4 <__bss_start+0x183e4> -40007ce5: 000a add.n a0, a0, a0 -40007ce7: 707300 excw -40007cea: 702520 excw -40007ced: 000a20 excw -40007cf0: 5f6673 excw -40007cf3: 6d7564 excw -40007cf6: 255b70 extui a5, a7, 27, 3 -40007cf9: 205d64 excw -40007cfc: 3a3061 l32r a6, 3ffd65bc <_start-0x29a44> -40007cff: 783020 excw -40007d02: 383025 call8 40040004 <__bss_start+0x30004> -40007d05: 2078 l32i.n a7, a0, 8 -40007d07: 316120 srai a6, a2, 17 -40007d0a: 203a add.n a2, a0, a3 -40007d0c: 257830 extui a7, a3, 24, 3 -40007d0f: 783830 excw -40007d12: 612020 xsr.m0 a2 -40007d15: 203a32 excw -40007d18: 257830 extui a7, a3, 24, 3 -40007d1b: 783830 excw -40007d1e: 612020 xsr.m0 a2 -40007d21: 203a33 excw -40007d24: 257830 extui a7, a3, 24, 3 -40007d27: 783830 excw -40007d2a: 000a20 excw -40007d2d: 000000 ill -40007d30: 637065 call8 4006b438 <__bss_start+0x5b438> -40007d33: 303d31 l32r a3, 3ffd3e28 <_start-0x2c1d8> -40007d36: 2578 l32i.n a7, a5, 8 -40007d38: 783830 excw -40007d3b: 202c movi.n a0, 34 -40007d3d: 637065 call8 4006b444 <__bss_start+0x5b444> -40007d40: 303d32 excw -40007d43: 2578 l32i.n a7, a5, 8 -40007d45: 783830 excw -40007d48: 202c movi.n a0, 34 -40007d4a: 637065 call8 4006b450 <__bss_start+0x5b450> -40007d4d: 303d33 excw -40007d50: 2578 l32i.n a7, a5, 8 -40007d52: 783830 excw -40007d55: 202c movi.n a0, 34 -40007d57: 637865 call8 4006b4dc <__bss_start+0x5b4dc> -40007d5a: 646176 excw -40007d5d: 3d7264 excw -40007d60: 257830 extui a7, a3, 24, 3 -40007d63: 783830 excw -40007d66: 202c movi.n a0, 34 -40007d68: 706564 excw -40007d6b: 303d63 excw -40007d6e: 2578 l32i.n a7, a5, 8 -40007d70: 783830 excw -40007d73: 000a add.n a0, a0, a0 -40007d75: 000000 ill -40007d78: 746146 j 40024f01 <__bss_start+0x14f01> -40007d7b: 206c61 l32r a6, 3ffcff2c <_start-0x300d4> -40007d7e: 637865 call8 4006b504 <__bss_start+0x5b504> -40007d81: 747065 call8 4007c488 <__bss_start+0x6c488> -40007d84: 6f69 s32i.n a6, a15, 24 -40007d86: 6e .byte 0x6e -40007d87: 252820 extui a2, a2, 24, 3 -40007d8a: 3a2964 excw -40007d8d: 000a20 excw -40007d90: 727065 call8 4007a498 <__bss_start+0x6a498> -40007d93: 6e69 s32i.n a6, a14, 24 -40007d95: 206674 excw -40007d98: 6e .byte 0x6e -40007d99: 6f .byte 0x6f -40007d9a: 756220 extui a6, a2, 18, 8 -40007d9d: 000a66 bnei a10, -1, 40007da1 <__umoddi3+0xf3d> -40007da0: 323130 orbc b3, b1, b3 -40007da3: 353433 excw -40007da6: 383736 entry a7, 0x1c18 -40007da9: 0039 s32i.n a3, a0, 0 -40007dab: 313000 srai a3, a0, 16 -40007dae: 343332 excw -40007db1: 373635 call12 4003f114 <__bss_start+0x2f114> -40007db4: 3938 l32i.n a3, a9, 12 -40007db6: 636261 l32r a6, 3ffe0b40 <_start-0x1f4c0> -40007db9: 666564 excw -40007dbc: 000000 ill -40007dbf: 313000 srai a3, a0, 16 -40007dc2: 343332 excw -40007dc5: 373635 call12 4003f128 <__bss_start+0x2f128> -40007dc8: 3938 l32i.n a3, a9, 12 -40007dca: 434241 l32r a4, 3ffd8ad4 <_start-0x2752c> -40007dcd: 464544 excw -40007dd0: 000000 ill -40007dd3: 6e3c00 excw -40007dd6: 6c6c75 call12 4007449c <__bss_start+0x6449c> -40007dd9: 3e .byte 0x3e -40007dda: 650000 extui a0, a0, 16, 7 -40007ddd: 5f7374 excw -40007de0: 6d6974 excw -40007de3: 2e7265 call8 40036508 <__bss_start+0x26508> -40007de6: 6e0063 excw -40007de9: 6f .byte 0x6f -40007dea: 647220 extui a7, a2, 2, 7 -40007ded: 000a73 excw -40007df0: 716573 excw -40007df3: 752520 extui a2, a2, 21, 8 -40007df6: 202c movi.n a0, 34 -40007df8: 207525 call8 4002854c <__bss_start+0x1854c> -40007dfb: 207364 excw -40007dfe: 0a7025 call8 40012500 <__bss_start+0x2500> -40007e01: 000000 ill -40007e04: 000c movi.n a0, 0 -40007e06: 000000 ill -40007e09: 000000 ill -40007e0c: 010001 l32r a0, 3ffc820c <_start-0x37df4> -40007e0f: 007c movi.n a0, -16 -40007e11: 010c movi.n a1, 0 -40007e13: 001000 movsp a0, a0 -40007e16: 140000 extui a0, a0, 0, 2 -40007e19: 000000 ill -40007e1c: 006c30 rsil a3, 12 -40007e1f: 023240 andb b3, b2, b4 -40007e22: 0e0000 read_impwire a0 -40007e25: 000040 excw -40007e28: 000010 excw -40007e2b: 002800 excw -40007e2e: 640000 extui a0, a0, 0, 7 -40007e31: 6e .byte 0x6e -40007e32: 704000 excw -40007e35: 000002 l8ui a0, a0, 0 -40007e38: 0e .byte 0xe -40007e39: 000050 excw -40007e3c: cd48 l32i.n a4, a13, 48 -40007e3e: ff .byte 0xff -40007e3f: 3f .byte 0x3f -40007e40: ffe1d0 excw -40007e43: 3f .byte 0x3f -40007e44: c008 l32i.n a0, a0, 48 -40007e46: ff .byte 0xff -40007e47: 3f .byte 0x3f - ... diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/disasm.sh b/src/mongoose-6.11/src/common/platforms/esp31/rom/disasm.sh deleted file mode 100755 index 5ab8fe5..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp31/rom/disasm.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -xtensa-esp108-elf-gcc -Wl,-N,-Ttext,0x40000000 -nostdlib rom.S -o rom.elf && \ - xtensa-esp108-elf-objdump -d rom.elf > ESP31B_ROM.txt diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/notes.c b/src/mongoose-6.11/src/common/platforms/esp31/rom/notes.c deleted file mode 100644 index 431d718..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp31/rom/notes.c +++ /dev/null @@ -1,163 +0,0 @@ -/* Just some notes scribbled while disassembling */ - -/* - * RTC = 0x60008000 - * RTC+0x18: ??c????? ???????? ???????? ???????? - * RTC+0x34: ???????? ??bbbbbb bbbb???? ??aaaaaa - */ -int _X_get_rst_cause(void) { - int ret; - int a = GET_PERI_REG_BITS(RTC_STATE1, 6, 0); - if (a == 5) { - int b = (RTC_STATE1 >> 12) && 0xfff; - if (b != 1) { - ret = (b == 8 ? a : 0); - } else { - ret = 20; - } - } else { - ret = a; - } - CLEAR_PERI_REG_MASK(RTC_STATE0, RTC_CNTL_SLP_WAKEUP); - return ret; -} - -/* - * RTC = 0x60008000 - * RTC+0x38: ???????? ???????? ???????? ??cccccc - * RTC+0x74: ???????? ???????? ???????? dddddddd - * RTC+0x80: ???????? ??????a? ???b???? ???????? - */ -void main(void) { - uint32_t rst_cause = _X_get_rst_cause(); - CLEAR_PERI_REG_MASK(RTC+0x80, BIT(17)); // a - SET_PERI_REG_MASK(RTC+0x80, BIT(12)); // b - uint32_t boot_mode = GET_PERI_REG_BITS(GPIO_STRAP, 6, 0); // c - if (boot_mode & (BIT(5) | BIT(4)) == (BIT(5) | BIT(4)) || boot_mode == 24 || boot_mode == 26) { - CLEAR_PERI_REG_MASK(RTC+0x74, 0xff); - } - if (boot_mode & (BIT(5) | BIT(4)) == BIT(5)) { - CLEAR_PERI_REG_MASK(RTC+0x94, BIT(31)); - CLEAR_PERI_REG_MASK(RTC+0x98, BIT(31)); - CLEAR_PERI_REG_MASK(RTC+0x9c, BIT(31)); - CLEAR_PERI_REG_MASK(RTC+0xa0, BIT(31)); - CLEAR_PERI_REG_MASK(RTC+0xa4, BIT(31)); - CLEAR_PERI_REG_MASK(RTC+0xa8, BIT(31)); - CLEAR_PERI_REG_MASK(RTC+0xac, BIT(31)); - } - if (boot_mode & (BIT(5) | BIT(3)) == 0) { - // ... 1405 - } - CLEAR_PERI_REG_MASK(RTC+0x74, 0xff); - _X_uart_attach(); - _X_uart_init(0); - // GPIO_STRAP ... - ets_printf(boot_banner, fw_build, rst_cause, boot_mode); - // rst_cause - if (rst_cause == 1 || rst_cause == 2) { - - } else { - // ... - } - ets_printf("%s %u", "ets_main.c", 305); - while(1) {} -} - -/* - * GPIO strap mapping: - * - * 0011 1111 1011 0011 - * || |||| |||| |||| - * || |||| |||| |||`- IO5 - * || |||| |||| ||`-- IO15 - * || |||| |||| |`--- IO4 - * || |||| |||| `---- IO2 - * || |||| |||`------ ? - * || |||| ||`------- IO0 - * || |||| |`-------- IO12 - * || |||| `--------- ? - * || |||`----------- CLK - * || ||`------------ ? - * || |`------------- SD0 - * || `-------------- SD1 - * |`---------------- ? SD2 - * `----------------- SD3 - */ - -struct uartdev { - uint32_t baud_rate; // 0 - uint32_t ud4; - uint32_t ud8; - uint32_t ud12; - uint32_t ud16; - uint32_t ud20; - uint8_t ud24; - uint8_t ud25; - uint32_t ud28; - uint32_t ud32; - uint32_t ud36; - uint8_t ud40; - uint32_t ud48; - uint32_t ud52; -}; - -void _X_uart_attach(void) { - // zero uartdev - uartdev.baud_rate = 115200; - _X_xtos_ints_off(1 << ETS_UART_INUM); - // INTR_MAP_REG_C - // 11111111 11111111 11111100 00011111 & - // 00000000 00000000 00000000 10100000 | - // PRODPORT_INTR_MAP_13 -> 5 = ETS_UART_INUM - // 11111111 11111111 10000011 11111111 & - // 00000000 00000000 00010100 11111111 | - // PRODPORT_INTR_MAP_14 -> 5 = ETS_UART_INUM - _xtos_set_interrupt_handler_arg(ETS_UART_INUM, uart_int_handler, _c_0x3fffdb2c_uart_int_handler_arg); -} - -void _X_uart_init(uint32_t a) { - // GPIO_FUNC_IN_SEL3 - // xx999999 88888877 77776666 66555555 - // 11111111 11111100 00001111 11111111 = 0xfffc0fff - // 00000000 00000000 10010000 00000000 = 0x00009000 - // GPIO17 func => 9 - // 00000000 00000010 00000000 00000000 - uart_div_modify(13000000 / uartdev.baud_rate); - // ... -} - -struct _st_0x3fffdc90 { -}; - -struct _st_0x3fffdf70 { - void *fp1; // 20 - void *fp2; // 24 - uint32_t d28; - uint32_t d32; - uint32_t d36; - struct _st_0x3fffdc90 *st; // 44 -} stdf70; - -void _X_slc_init_attach(void *fp1, void *fp2, struct _st_0x3fffdc90 *st, uint32_t gpio_mode) { - stdf70.fp1 = fp1; - stdf70.fp2 = fp2; - stdf70.st = st; - d28 = d32 = d36 = 0; - SET_PERI_REG_MASK(WIFI_RST_EN, PRODPORT_SDIO_RST); - CLEAR_PERI_REG_MASK(WIFI_RST_EN, PRODPORT_SDIO_RST); - if (gpio_mode == 4) { - SET_PERI_REG((READ_PERI_REG(PERIPHS_HINF_BASEADDR+4) & 0xf0000000) | 0x01110013); - } else { - SET_PERI_REG((READ_PERI_REG(PERIPHS_HINF_BASEADDR+4) & 0xf0000000) | 0x02320017); - } - SET_PERI_REG(PERIPHS_HINF_BASEADDR, 0x11116666); - _X_slc_set_host_io_max_window(); - ... -} - -#define SLC_TOKEN1 (PERIPHS_SLC_BASEADDR + 0x54) -#define SLC_BRIDGE_CONF (PERIPHS_SLC_BASEADDR + 0x74) - -void _X_slc_set_host_io_max_window(void) { - SET_PERI_REG(SLC_BRIDGE_CONF, (READ_PERI_REG(SLC_BRIDGE_CONF) & 0xfffff0c0) | 0x720); -} diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/rom.S b/src/mongoose-6.11/src/common/platforms/esp31/rom/rom.S deleted file mode 100644 index 55737a5..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp31/rom/rom.S +++ /dev/null @@ -1,16 +0,0 @@ -.text -.org 0 -.globl _start - -// xtensa-esp108-elf-gcc -Wl,-N,-Ttext,0x40000000 -nostdlib rom.S -o rom.elf - -here = . -#define PROVIDE(name, addr) name = here + addr - 0x40000000 - -#include "rom_functions.S" - -.text - -_start: -.incbin "rom.bin" -_end: diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/rom.bin b/src/mongoose-6.11/src/common/platforms/esp31/rom/rom.bin deleted file mode 100644 index 355329da2776060e91395da6959f82b384520a00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65536 zcmeFa3s_TE)-b%!IZ4Rns0m=a1ltfu1jHJGQ}I?4q$paaKou`-=Ot)R=w;Aav3>0{ z#1?~6C&a4Y*iMoT6{$=iwH2*SLs}86P9dGPXln;kQM98C)G8=s|7)LwwsZUc_j}*( zd!GOKpP%Q+Is3Zy+H0@9_S$Q&eLyF(q@QIed$P1YWTEK@B^c6j78)ZvDbyK-7BT^| zUU`{OrcKwUYBPoCU;LdENk0=QdqmnFMCca?={p&7s!XfWDlbP#mFV`y3jpHE=H>9r z1n~d-*PewsMbdLw%Bxx0!7RNwE8CWp@5m}?s@vL6-YKOrjlPmy_t$o4yt<1JC+`~MynrO)V~{~4?Qa*FcP|6%>V z{15AY_CKuudDIpQ%0U8*|I2;;c|qxnpU3_iVFStx{`Z>Z8jI}2iH4auDZlAg_A*+X zBaO*2I+*_k9{-u|RRVs*dzsf`Uzmy>TS0bcu6eGP^g7iW! zp66kao`v^F1s0)8QLzE6{g%;cH&&|7Fd{nk23B5)ig`r4I!b9ovESmHZ?W-jm}Op| zvkRr=5hHcUTfaIRDbd_OK#8(bJ@35}U9!jbAwTC!t~O-ozagnm z=+-*{trF=RjMB(xRZ68XD)uk<5S>OZj9j5odD#W;j9JHDysU^>oVpBJmA^tayhj$y~Lso@{R!K9J1rn8#j?y9~!Juu5O3TSB;OmSt(~Yxojk8ml zG3W?@5!3<2flMmi{blF}3SD&LYwV-|r~)j#uof~%-Q6qRMOt*f5w#q`r#X0%48FQ{>u(OLDru@lNU=-@A>U9n@b+Z3(1G*qhf3SpiGfNX z%?d^d!gFcEW4NRcB}A{_&5YJAlV)FvD&cqo@DiV@Vi8Akq{;A$*Tq&rNJt09VX6N+5z40!SbXQZBQqBSBkfZHxx!#)gu&+hdg$L0us4>v4|gWu?hyiY69TDBvlAX% zXj<{CR7ZFM^qo+j6#v|(cbJ^zt*R_!Ori4KlbV89ZH4% z#QqQz`KX)034S5c6-d!vjOhh3Q9p3KzL%M)%SzYYU~~n1P^;4m1ar)}1(5{>Q5y=P zEd^uk1#(xxICsH#Z@~m#!9;(7qQ4-<;u&yxl}fWduW=Zj+AK!DkeL~orDCI4o;E8= z&#ij0FM=$J?Fxoi0>=6()lD;3oiNl_o4P6HYD>42xmpH8eYL9dlVHoqAn;&U^13-4 zFaeysO#>}IO@OlAs?Y=&paM+Pob(XP1X)_FGAl|OYVQ5uobnAV-Tu~r{!_~IX|mjD zkp^`1Y&d|4L*qzxA>4HwOOc=-Q;*tsLb*2U64QF~3Wz>)TU zg50pbV|C@+c18E$a8hb34`j5;CoW-CP}wu3M7#<<2}K$dxGl(IrjuN zXr`oVlCn!J=9etl@s{iimm+t2#`77I-yM@%n52kLkq1?UQ?x4SqyWTYqk`#$QV*3IK8m~u% z&Y~G%T*!;yWWu;s2vU!Pd!)$uH|%lCj@5h5V;?(UVeh(62fk?jr9~pDRwxE+*2bB_ ztnvMeMSgbBbsk@NjmNTHh)E>=KU4n25})|4W|F9O6jC{m4H_u>3+_*vXHTGQSlPdh z?vap;Ndc1t)P_)-+J1d9tyOuH%8io(fWwyCDW@``Awo3*qQ|5Zb;&jO!#ToA+v0HM-oY8oshAx1xSYr2K z$@}g_5{u}rFL7Sp<~wb1B^R_EXQb_M<(F|YBYzaR;;3#3GBF4eK<1z4Xq*zn$^v9% z6xIr6gkC9wIDwI<5}8U+Tg>@{avU8~z7MCPk{{E~@=h=fv}q@d5?7{yuHF5!u$>EQ ze{3s}YXoSJau+8?kvbTIQuP4JM;7?~27Xe6wt()o6n<9t+2A*R64Kk?w;#e#x3-ij z>&O1LQEWes+1A;I=Lj6LluZHba!m9nQU4T&8B`VsJmF}8u~s7c6o;oqNdiwe;Av6q zT)S`_iK=$mY)eZgNFeN1?QCpZdf<1XA?%&{S~o1i5P0e}c=oGy&JE;aY2hbNR22<# z`L(uiel0}ZQ{S|`Pl~&rdJ~?OsRwLU616;a0G|C*>ue!(o~nbWck02Bx(6ZZo?1Ur zx1Lm)`qoI@x7gk6;pEEXZwU<5&QoLP@?yjld96DP{XiO{k9>9T|2X3T`qncOX3U}&yb~Yu@a?G4PAhJ*Vy`nry@Uc&MFal? z)00nG`FYK<2EOTDv>+-%^9GMSw|Cc)WCb+fQtdo)nr>Y|r~qB@c$Su&@yH(<`(xX_ zvzO(MN3#l@18LtNxC2UF2bF(8>O0%racHZ0Qwmzt8#Vj#-Nsx9pVx2dm75CKR@NR}pGPNo}S2tp&&acW=G5+mY4i;0}pE3<924(NNo*U z38c84=9hVSU1A9P;|Sw60^{Bw(&+^tb74?!0ll4vanoUZmR8#+4mObi zzo8uZjd>dfV4Pmyf*;*hH!zmdLH1sjXMT9A{H5W{^Vsb`Pz>6rJx@7~DI0i=jW=t* zq1gEH4*+h91Gkf%>Lns`KZd2*j=Nv*h5U}IS0)L` zg2~dYKVYR>>7r<_adgkuwAMUb=NNML5}sz2a!=%(7zPAH2$kB=Ox~ga-G@?n`Q!5lJSpjNgN8BfscsB@iDCC(GLt;O0I!#6MUWM^-@z&FtpJF&XupmX_AhLs$3vSWHX z+nnuXp6_;Dt&%6tX495i>GD_5<*~Ftg|cn&n`)x0eRd(Trz2Qp`xGjDmR7IdXzk11 zW!O}+I+UZY(b{Yq(}h%eI%Rt`6sFVD>5(v#p3aPfdFkoAkuX0!oj(#5q^Ao;!V&4| z5fI*C-J!DSH(HEi*0t0*vkN`>YYg&hzo_oCw(PT7+5%?tbc->ft_mo7u=7p%11$eX zx~PZgywmrxLZD2HXwyOI^VOC%$}U1?^G1TKexuSwC>X<eLGwnsS5Z!FCSU>d^=y@<5O-vv_I1P>HrWk!A>$TO zULo=c>0Tj>RO4~&)iI4Hv|eF^PssNRdFety!g$;7v>kuaCf>Wl3T)SSSi1w;_G=*# z#C=!Vi;(=}bq=@7ljzLDc(N(sU!iAj=l!3?PXNXzrT$sV z$$@PQEzqItjW^}vL@Yms*-=%~)WC4u*GcC+e+{^Ae?|kXw@I6`g{IF*?h4o(wj3UJ&$rh@N-5TBK(M>hru1(4LQBPK*Yi zCn&ZOvg+T=KjZOuHnw-FyLiopww(4Bffb{!mUHU`Nf^a2p=zc!I&Y#Is#Ru4ol5mQ zTB+aIz^kpHmDcIE(#B6ofSo}(50*dYfgqV7$6;BfY#`|(^&csnZv;Ucs^3R2-+G)h zC-sCN^(-LZIB{m-q#MXJh-RylAQM42hS=`s0ulI6;0;0>wxIs7QvDvXP-FB~k36Bv z`ASgzE@_f1CeD|FZr69}!y{wvGzZ;pB&**+nR$2(tOM&n?h~6X0y{347@b47X$ZG$ zz0>*for^Eu>AQo=Zs7__-AHx_deHkXLHWPIFZm3oyHBM|R~VM@H4GRAFuOwks&Z4^ zJ?FjdxsqQzIKiK6zMAMwE^D7z`KY~JI#sMlb$$m*Oiw*x={KXsIj=*cjn@_3 z8$U3;_8?0CNUBiHuwQZZ;>IS%<7PVEVY+WH0T0u2fE<8r47J(T6<4Jw$9@cAiIF-PoT{;vbDdbRqJR+jS?0c zTeZQSPNfSdy8xLpWVQ_DstjEQtxYrNGMMtOV7-Sv$1pK`c6^mnIgtfXCwz;1N02T@5k|48H=CCUBT^`rz0mo*~hEoG2B{yfxcyz|NyyC9@Q9O3E zd>TLf!{`MwGFK#;2MmsdhFPEWQNB?KMx&!td1{=fcmYtyLOK;P*sn+gNbXU{GK%8hmD~hO8m-EbO=i~u+P{F8 zzJP(|EDKQo0t$eBv76pdMEd~kC6sOnaz{}12xR_GGS;WYy;ZzdmqlB$M)tpQAHV51 z=E%){+*toiQ~Hp@ar40J1)Wdhf~m+pm3I86CqEHv1~*Gxx=rTsN#!~rFPD?-Ftu4YK#@}p7_!?8EU$jUj?xnC%~|)S z&*ypPQ>yuN?tIigpD9^k@GsCW^n6x)?oLfY>Koj_#3^;Ay9pnU`!|uJfYRKXdO3Aeb|wt zls}JPpU6sj+CdeDJW#DB_6%b(%eb;j=mw_e2E@-KkrXHk0;&hj4*#H3pC{V$ewbVR zA>J_z;}wyKyllD|l8#v(Ag9d4ieFUrT$S?<_O@u*U;0|4=VVu7+o_(hD1;*EkMpnC zorHW-DEnC!TJ3iPdL>iNcAF1(_fEGwg!~V|g5V#8yh|vsLi(2=*F>^!u}aM5!w##b zL1Jm^x2#0!yWBw#z)SfUH>XxE2AK{vndbSGvm0m#0FIESu}j#lZIQeNTKxdbCfG~Y z&IQ$3Y?=!(!21;eD`FL_So_vi-Sd2pLJ4yh3L2#CD zIsq1%uClZI`U}A94YY3oQk25tR?V9$nJT_6X2%ap8V7bcdUpkGDD!?bG;Ummbh1Zj zi{4Ztl9ak4kShZzGL&{uRpTXl-KT=yQ^TMIx+TcVq7A2pKqj~|C~pQ0%x=$M7H6;$ zcLv$2&|o$r0pJG*tQ&POHv8e%0l(v9oGzjQqx1srEI*e)iuv+)(^=-6v%QKHvd}%d zQC8Ghaq%fCdBwJ|iUxi+ja;!ZMZL=?%jJR0r{p~o+z$1T-QPe|k>Nf0sXTdt?_>u% z`f8v2g$eAK!ItEK<;|1l)GVK=Vk0)0$0ZMylsd9YZ5vK?a8;HcXXjKePm9PL0LDUW zg#DyXK0)g``KtVM>_dx>jLm%Pu~TCWkG=B6*g1a3nk!@PXGgPa|4Ezt6RpMZ?6$Fd zyJPd--D~8BQoOO!Ej#}65D3ng9Xn2YjnY$Z(E=l?u4*j+$i7%Z{ga@b;Agzl6* zw$OX>%WcOcRSlr2IjTht5kkqUfG#7%nL>y&v#GhItxQ|$@t?w;-|UiqJWl>M)WAZq z{#Y5yDsaD1HivKR^BRTn&GNRg%Hv$79cT+$rL3+g_cYG_h4Rgb<6%`%6hb{oY9Z6V z<$E`jIC=Vgw^7#BCvUiNJwmel1^(rG=^3&0z2%`LUyQx~v0q&oo6>)>PJXY_ z;#jk7tlI7<**jL^ay)xrER6Z)BfI08FV=OQrD8^1d;+UK!E?o6Mn$1Cw&CJh+;bMv zo}vWDK?AEikjzVd1!!OG?2ncDPwwA``~Qk9)8uAhB+Nt=;advC_MZxQUHP@kj;1diCVSzDPIL3bRE&0{a zF2H&FXg7@Al+o-xnH!;OTVFCiZ_CvS5t11%FrYQJy1>dcLai;g{Xnnp@7(YkW##*^ z9T>%WRz$RW>D~l>b(MfYC}#VqBwq4jH^ILkR@$k>`w9y_#|4^s`A=>23Xptg^lJi^ z*<=%qG15)T17 znF*LSpRUasLVQ}rs_^?o@JIlFFeXl)>C`aiE*AN|9+WvUM^tq97FtZygi*!zOdRdwaMjXe2v z1jm_fZ;aO)4{f_+dCnt0HXdqP{U&ZMm`j?rL(@}1YXP5%zXB}`1We6*k zOAB*#5Y@%MqMh@qCTa$RLH&2dzruFR@s(d+e95o?HvZ-c2006s@@ zLP0WKhNo|*Tql)Oab~@WnA23N&7@t(OLv%4F8`}M1g<6+e?ayDhOYVnefkL)U!s)zW7HY%AAoh2f)AW&jQJc(S7egshHK%VM*~c%xOJ)Sig91G?dR)Bc z8U?v(8tqv{%}!lp_q;4qWNhDV$wFW{C|vVJNZ;k~p0hjo*5vo-$?wk6Ir1mJ!%ue< zPChJtNI1E9lHHLxdFD7SbJ9cN$;YIRy)}8ZMCEM11L+82Z=5%@Oo8XDf!YCvI#i!(-!hnVSYll6y);qswz3dGwnTqg+15b^0asFqy}2 zxHLrGr{7X@Z-7Zo^I74RA6C*8krgV)>wx)3&W*Z=jsf=QAzUpvUId26P3BS731*Au z&GEtpQMIUYLhBt|!z4!SKR;7k&CIBgIs0)_KlVDB;Alhr5r#eCqyQ<nb*WTnBxtr5zcmvPf&sBl8vLP`V4ZK;;ZXk7tqT158j&Q{VHS5n2?fa49b`ZiWC z3X-l8T3Bgqs)NAz3^#oSFh1=$9)?lLo2fEoa`oQ}*N57s?=d98z(d{Ij9^PE4glz6 zB;QAYO?Q)7cpy@ELR=@V+$R{qm5cUIv5G4s>iI#t1G(PDt^!m8#Ay~EWd(OQA0{iU z5sq=bhnwDm6AN1XF4uSi%}Q9d_8Orvw4;6?7`q#%yhX5RX6&kp{SvDp{cUWs0ue`p zY*5r*!aZ|BeG(A2tdWB9{|weCZ1tYY6T2Rl9F6LleJ-v|(#~W_)U$%tU(`Ja$DHc4 zU^Tx^T_`#&e}1TWZ2em&_y@=KT*8?*IQ(Mn5L9~NIp7?g63lwsoUD0+f zYK6XLfPM;X^w=5LyIT^_T(L62(o@A~_jx8C6`CMW@0mOc4l6=qmvR&Tc#av{hZ8_5 zbK~-6R2GM_`{)mk`G*dGn*4Z@J)HLXI zP%yXht|4c`phb-AVk$2IX)5mFc7ZOX+d-KgMUKXs@{fZsiPoT*9|`(^Vc8hNRm|?t zEu}ae9-W{Te`L}l<12$XI|h9YN%g|rkZ9NJh7BSK6z(o7AAo>=TEKjR2zdXm1MF7V;JF>*+TSA0M2YT0 zocC#vB~uv53vNP@erPIm?8U+MYe<9s!abqmCmzrw&<^9~M9E?{A?FBwipNGgXJn(( zA4HaARL{6zwNaD!m{xm}-BwgqbraWAg#@SFjqLNLWdAqAx%`TZ|%V_gzlD@vsCRph<()kAk{jExs;xX zh&}Fw_z$6YTa|h$VjFx9Qki|>5|a`8x*y`#M@rPH(FTZL4aLt=rWnM&*bgu+jihvy z^(Ya$&H`zF9ZBnvAvUY640aZ*9n#N@q?-WGxum_%;EWN0STQA0u#4od?V%8RA8Gr~ zkf$_J;f}(D7|-#(B@%Y1d`&Z-jXVh2f?LI_8pKCt2Hb0CR@|oRQk~{Z{GOE%LEPEsc^N?M&w>csN4HW6=&AP7mQ@^{>tYRNH zGWW`notJWHUV(SKVrX!BgFxMe4~!MD8xk4L=K{In`T6fD+f+TPprI3jHkGM_Y7jK5 zYEMErIA&r)-VV(f)l2JCEL~qy|Nha8nx)}+Ts@slvPK_V!xrzYj%dqp$D>n%3@ij~9bj_-xyo1p!oj5%)gVQMp4UTg(2vc-~0)b}Lk-6W7JO8(<(0Nlc zbPDieUmOI;&~NCyS4H={6Rgg#suGrNui36jTw0S6nm2&UhDeGnFMW^Ty`qBP%}!y( z)jBUNWNFV^TMw?O;d3}7s1kXA5-Gurae5@RL#59!9J-VXOG>1m=Mc85h|IUBD7T8P z6J>v__0&t@gd6O2kX#{xb0JB_t_K6OM{3SqTRxc-!^u&$U;y z*O2-6#jy9#y>5r2?;5vyPJ5l8R8R16{rdH)%ed?^N%v2C-F8E2zCqIczkZ!v)#S%5 ze%#|5hTcNkDXySu{shwVApQ@swu9`=31W5vD+KAQtk{aIvPEPa$zAu-maFjAC2X~& z63M1pw>6~$pP(9;+Lh?g^I&cV+>arzY|$A~{K=QtN$_?jZ-rAL5_oc}YSDOTh#VD| z@{oKL&Lp-j3iehQk0H3Uycpbasrii0K{cM(>VA@SeuGqbtIG8u zVOdaqv5)IvK{;2T@>XwD-XlEM8@)YMB;Bs6oEv!W>bGDp=km8ZIBycWY6p0ZK#B_> zg%0iu%J(I(LS<~MS@p=h{o`P)L`Vp*>nRWi18v}73s|?(- zs-w(U*#w=Ns8+}YuI(+X{(({u-Vo?qW$TL zvfps+Km8JWFC_Tx+Zwn)>iM4D3Vqu0Le&SiA?8hVf!7rY6uFv?krY=VF{+b5kqe%d zMDAJSYW;+$jL>gGL=cp@bl!wA6Gj=b?HN!?7r(9anbDxvks1)*OoLtrVbvP*kMr0t zILy@>U$*>zsU8ei*&-Y}=caj?nKY%|HiMReg2FXsS8tmvqG1OCeFEHKyN7cfSVfOd zc>p6~95#oF=~UZhygY)F%FMa8aeevAa5`^%neOKZkvlB!aQ!e_(m56iNoi8v*fz|L zFA$7hufZr!_B_W&YD>iGrFI8LY|~4qQ1*Nw)OKmM9T3f35y>X9U7O6rKgXpp!(puJ zLA04(5$XPdQY_mxaooztBfFW$Bc6*4Re=JP&u4y*QB8XJK)6ru#c9}R_Ok}M=O!+k z7y&BPO{|%C+eIVIK6d#;kwT?_!@`XVc74T;@}*N#M*%4at}chCwiioMHO$;x$Cv32 z53$f7(-o{xf%dZJ85$rgm?&Gm?^~DS%bp%sC@<9Z;MUs^_)<-xUxB_X0BZ#RQeOgO z04C=>cPDJmXd`;45wYW6;2AB$uhBI!usaygiqG|%cRd$dqTaQ^;mK`{kxw60-CCG< zPx%;Bxu&n~J)@vr&wtv-q>oJp6*BA)^I z-;ReWU9s$(j{Uo8TCZKZb)UGY10Sn*iMO~6sP$`@f{riyDb#PE{05YF^KJtloCQF` z&`exe-J1Cwu9oeyyRwk=bD*}h;`7!|iT@X659TJ!Gqx1Qq4v%kFBs%B8(#QN4&m`z-H7{-Y|2rh@{8`_7%mZma^%$pG70o~WB z7Dldp71mZVwiW@h%!iYN6)MZ8z(p%n+jjH8Y)s0-kCXj|dxoutH3>Yf!U;PY+e?AeNsGX5J=i2h5|+!% z&2LB0AW^@@HiCu?qCSmn1Pv1PYHTBDkf>W@A3?(oQJ2O(f(D7&HTDrS>?y&1NKvV? z!j>0P79lNHr`jAwy_&02UBazc_CgZXX{z<9L>}w7s7g~~4pXoO68kkZCE?8kiTX7) z`q0{6NTNQ?K699Y`ylGo>?;Y$@In%GYxe0wYY-Q8X?BmG5#HpCph2QK&F&F2NK~cS zGlB-$zQCSl1PyLCquDcp28sGKdq>dN3sJ9T?+6+s>elQXL1VA;6rqt_ofTTM?I9YW z`#Amro!ztoITzop;+70o5s~tiL~|v`%?JVrf>SbK2?hzcY-C9X`yN?`sr$j6Wj&O z-1rWu?=C8Haw@_<;p7ES$swF-9~yxI_$c8=11#cIb#HaL?G1l@?SYwN0knD%aUUw5 zf{b3gz5};{EDqsZyO3mLs!_=s(l<}$b>6@o=Lui<3d`VdsbbSXS$?&nna81HTb!Y& zwwX8Pp6u3rs00I*9_2lM7qa^NxWACl&952yT2U(%RwsBLwqE;|Qa3Y1!#~L|n_ugi1%K|OW<-DQpLw#O+ z@qzx%S8%2Tg&@nXiGBjzQSE?M?TI#pwT1`2Sc@}q9ULLcq84h#gh+AmKw-q6MD5B8%k!f`FyS+V?$4 z?=RXG)`E*FxB|A8??}3r<6ANTzWj%|Kr$0;;L8Kv7VQkgTt$}fQ1PH`7@#;*l|W$V zie7?)1Fi(=1xzf!>YwITxGvz1tWY0JKTt}W9<5y(t<^@$-w?f*N(lD1+~RE(nzn8Jw{S;jLEt75xCEt0+MZ;u&;nqHE2Py(KJEh>9zwKVS{A*nP zHzEN!8yhRM7?GNXnoNCFZxQ#_pX43L0U|?*yn2Y6OQ?%tLoV#^~=y z=D3l_z^Z)&f+g`!BXn^?ki#7+oW+&1IGuu2;o1fzsFP?bAb+jY-gVq`XzChtR&Z{% zNtU^R#?XRYYyqbH0q4yc2K$i<)qaNawhxtqsvGJ9C~;?gg3}v<(>myci%^6Ne-EcN z5Qfvj>-mt_gD75Y2hI}@l@l`|GLx&?QZfhSc7Z1HX^rX`jc&6h{RNf}2W7Y7BnBCw zetJPUoCQi&>&s>4LZ!CMU}?h+D+xnpK|y)b4cz!H3A+m{1^iYgHYQnMVlTfD-mCeOEJt>R_eB7{BXm=v*uuU@fI``j z)=_L}G7OOhk}cuUeA2>_Nel3Qgw9Pvz(1M)%kM0 zONG>RVP2}E;dni77Otb+vB>mC;5j%Ph0?&gvAaTLO|?IkReu~R+f2orfqlH`Eu6O~ zR388S*FmI26jDkNQkoIU=f<(>4tACWbIy{vYEc8N!vkcp9P(-+AJnK8X><>3(wDM4 zP2|0m^U1h&a*U+5=tD*xQ&?;pdU2h&huhG*Av2`Fg)$A_;PUT)Zw!814x1ZfxQNTi zl4tnq_vKeZ{N&6hJOgu~<#X(;C)l___=fc8V#{%zCNfK-`jtlaj3%A1g^!cWAc#0x z^qu8+>1U%I5(rfv5mZ&cb)hrGaB)=i0Z9jT37btu(p!#^bgp4;){LZAz5B1>5MI|2 zyul<`^}_Hi)u^T^0Ro|@oI^n3)qh1?36-4&$pyy+5`Llw#o7KBbC*VXELbZo*UJ|Zjnv<08TTJ;lZxuJlr2J@7e%3#h zKNv1Q3(EJb_^~|PU#Ejl@#@&5SFIB_FWQ?HU6Sk1UhY|bcno+7F>uM{Kd}4`w`)5= zpJl)q|5rHgPB4)wl4WOlN;15MHY!bKu*aVeT!?UfH=OM?XwNvvKt77|o*#O?aMf~^ zmjYwO5b9(I>!p4W9F_lgN~_kakoWWg=CJ~HP(o$;rZu= z@V7)>kTwZ5@pnODS0n8n`>|cZVye|#yO0-iM`_MDGk$@XQyoFv>6aPy*QPr_N$EH& z)4uO8-2xGQf=On3u3<&S@>ehS+~L-As7uJ`JUD!UhPko)@75k--{8YRQSB*ga1Ph; zX4$P1n`iA!<2Snf^2H%{;GRFh{KX4-*69o5&AYtj5B+A9*PP+i=lUyS?Cn(cQcw2E z`Pnl)mEgp#?j|%>sB%x@ydQ!y4$Jf|Z_k&&OLz4uvp&@`WBT9HelhaPM2mxsqgTcoh=USX<+Z%7s z7_;2|&6c62FUgALePWl3Geo(b*u(L^@iUUFDkO4LSGX0P>4iki$SvF+%0C;*f0@fK z=km#hKDY1{EZmJ17RThtX%B$= zqV`C9z1RH^8j`RvTkg6}2X1*IyTm9`kN)Otggd_%r02wN65F3d*}dIpf*w^m8?9PU zxcZAJQ^AmpYqRWa^0|ub%K-KYe)2PuQ{^ca@hp{!M!G{UYJVZMaZaFs(A3a5Ky2+# zXPmJvoWR*k0C)9cLseCMHoF4=xevVvhE3ZW;2;~e{#%M%>PLsTsRCt9D~FnPVf)+I zG6mV}T3eme^#s~0WIM}9|6NbnS+-qb@PK#EjNFI@#?3=LgBbk$bRSZMAAVb#M^Gp9 z>RCF}R2G6mjliJ@oL`6F>;^ar!yh>~Lf3~%H&1E$VFb|DYwFxE4GblC=JdpPZcK9W zNnM?~3Z5$Ut1NH9BZ3AV_^bB8>?!;xU^EeNt26DY796UuZB<$d!M9>TuF&-n910@S zccj!yH%MjEo@)92ESpPtP!79iW~RWZX}%v0yNLm^flic%qv5evFC3~e{A>Eb`SpmA z+trgvs0e*$nGJO>8h+82Y}w?RpkGbMo~v;kFbPXuCIZ4z=u)JDt(EiD`2Z1*oiPZ* zx||<4Yy8w!N>qJW@{WN5_{F{SA`K zzQk#C$iy_076dS=?rd&h#Rt`Ou?Hn9McjCRF%?d2H|?SlR9sxTlq%an=(~>2=iOaG zOyWhf1iA`kWZZ#L8m&*2W`}W~6tKZO%Dw>dp$xSIvCoApEz$NCY3v=`?|^enedD*v zL6tif?Qk8`xgGf~Z+;C`Av0aaZ8=?rbE+L@+I1Y?R)^+e&flC+tK;n^ zT0Y1#O$B)az}SQFbGf+7}XLQoPl*C&cJ~1lnc!~?y zJWj}Wl*b+R6WLbPgKK`#dX>ojvg{jub8qMzgkF9X0vlUMYrIN#RuE2XqeJ>%JKePJ z7R2l5(D}kax@s%Qx6&b{sekg5;>x1-M60Z#7*3)c$R101i}^LhG3R^m-SFHq zlww_cv{JWAw6kBJ$%>apf8Rl6Sn_px$kL- z;27r@6f43BZZbGeK8*Bj@ME(%-_8p(ScjU1a0&&h`&p*q9rgX_;D(3Gf+6>+-kpg?jTg*KrgLNmEi*E!wS6v4VZDZi#Zb-3iXBOA6qae+fMW(VZ zi(-j&#+ya6M25j-klK*J6nHQ8Q}A>mJlGkuE|m)@)8)GfOIiNPH7l3)T)vyP0ajvn zJC@HDdfSKPUquuq&i9HLx0p6_MNh_0l@sTIQm5-kl zG%&!eC+Gy#$|=71sLH1rBwhyvN%9SM2y?<0k8HN-dF_KxAkHnOpiJQonS6No1i2;~ zDw+xBRt=2$JuCnpxxa5({V3(gzX>J5_dxxJpIVk*9}G7w=)r#r%>`pk(4vWuFQn96 zcM~?T%*^tcn}E`lI+mXq$w`VCMKLZ$Vy-7_;ATW40bwX`a8Djww`8bsPl(}c>&zn4 z7?kUv>3)ycqe!X@vO+AiLQxtYx;Lx;i0Y4u|21#ttfr17Tu zRp>n&_a~ho2B>QBln;G!+RwsO3991@3z-#XlpSzmP>DL@@6?Gq>ZOiDhST-7W2YO) z>T1!9t&u=G0yr54UZ|!kXKE5*O{|MFeNE&+U1ZO<uk$3kzj3aEHa10s~$fLXA+KK@1wgLs*Z@C<@6aSYKbGlSjv1$8bZzK4=--kma6P zU@zdqCB3E{x2vubq3)Zq8>T%8Jh9vR_FqXir|kBG>=RGye(yoP<>ZijKnCgVq_UQa zn0yW+v{^4MYx$Z$@D`hE=OcCwd`=KPoI(o5nZrd^o41g4)E;LJAB3znTizf`Mt__+ zytu43U*yjDeR1Z{>f&2%_BzRgbjQCQ2D9dMP`Il&cTG#Y2OJ_o8*%pI{uZgfCEBva z12^1!E~)pl)R&8{$_6RFOWF@Ucg48ueS*>7HFt!h{Jgn3Lvs!rqn?N^9v$35%s&1# z&7JTWH7K>_Fg(3$+~m3XCHEl|LpaAC*9r!mu0p5WxiHV&Wq*zt*?bsJMGO4P=7K?&vmxBy}MOE$K)^AHeb-zOoDSlOT*d$d@ z{UkAScvaP5>rz2=lj1fgqNHBp{>Bl#o}JiqiEOwmMf-bxf49@mb)#S_o7CeV0uyjZ zDZ+`L5}YA?%PljQFldIl_~C3@;pG<=RhiLxlUSCH#K z>mME5WF}*26|B>gSf%Dl!>SdP8sfkTiMd8(%)skSP=*LoyXI)AXv80(@C2mj3jYL0 zPo2|<3q~6Sed^>Ke_WcQFzr0`%CJvR;Zc|p{MKuro)UbUreWs;VVIUFM-K=`U_S9a zf%(L7($6)WQHqLbB5I=K3EL^zoC`31CS~e6A z{9|VfZqRr)tR}v@5Or&gPL`0iDke_~RM4DT7Qx3T2?IQ}abwj50??v48a5M!0e--_ zzps}KS~MQZ2t4R!sh9`w;IKzfl%P_fL;X2-hEk5gSrD0P&9wG?Y2%;9Y7~M5)&6vF zLWIONI{S8I-Xt+*H%Bna^ z^G3NpV&qcWWIkE!a#z1(U0Qb-gxc)_GL}^VY=@*Zu6QEoA~wWS|3#CSq?tJb@?3FS z!)L;)$;P^1^-DAQrycMoH@_tRa*(?p2SV8$9RqnS-(LsEDOie-kBeXY?z-=3ivWCw zn_sP*&5A?!B|LyR;m2bHKf@4K#l5CiNz)G~^&$J)>esk6x9pqiRZXM_JJC|K+l07f zh}4Vw!SGg4>n1Z&w`Mn2c8{OF?46Rc?%NKFY*lV9AFW^d?X71gnoB)pYrZX*{J>Xn zmbDM4*3L8U@vyW_vn%96pj-PuI;6b`X%3PW;x*#?ah9&V!(G?7KNcD0eG6Y5OyjMK zRz9L!$;jk&5?(C0gx|2847~O~66--Uk!MLNTN|!+iWp03!()-Q^M5885N}M`#uC4`& z`OZppA9jpDtC(N&$idY|p9z@t%5Mx>v%a+(xD2WRNh0JZgSdt2{+Xc6^+Ud|4qyyiiGPo@ZHM9%^-`g7L5vziV9PNz5*Rn4&OHjUA zI$NdWKIr)1bv%lfJ@Rcwps$5SpiHo{_EzU=-O2-!N3?G3fgLFdl{`VBNIgK$4;4_T z_Ex9J2jJG#0b2W0(mnDvp&~Ui0@`DRo2+Ag%Hb(ebM?MTi10WVg;_jZN4oKLP9Ke@JCn7ICvYiosZ zr9vgi*kqnSz$^xgeXej*tbebEaArq5W1x{kYXvAPSMnW(z}2fn;h*eO>}_g(38b zvXT!83&$)M%GL_UR)|Z5)Z%)`g9?)ZmQ~z5gcqz8i@1J}I_wC1MLiG{=433=d4~u- z+pZa{*VbFFMfe5d{G$Xg2o?s&e#km*wQNeNm02siuhbebJ5?D+I?IiZHHx$^UM^(U z3T3529L`jP%S;KsK^7eU1I)?n>}dH|xWrHT_!>`=aY(dn?>(E$@~3$F5AWYA$xx&! zRQvNn`K8r7pg)Mq$s$X4$`x#}AUrYaJNWNy+ zaZ0=W2rSx1Dn4GEx>)sIM%8Aziz%Jd-Ac~8M!F6yZ-nk!dE-{kg)~V9A@@>6{qc2S zDnn8w#J}nlhzJpZMh&z)19mw$=Yea+2d=Gu07k8G+&3*`)Rty~U%4paTEe(%x;HbT z^p;pS-?uU~aDA{F4(~ENm9N6L03;c%(^Ostr)qLh`E(2eTzn=JSrfD`q9hseOT$y@ zTgjOUq3?t@m(C|cd_)+%TsUd1P*Wj>ZZ57b74B^Hll~B}ihA%&hwv)6)*GBsPd-dA zGUn+Y4bQI6>;h$B6MQu*%vaF0HQd+>e4H*!i8i!kKTf~GDB(^UgZwmV{GIanjYA)U z$$gVqr8vV*>JDsVdA9D2fg7IgC0oI}6;yJ?!dG~ef41{b<%IGd@cu=_)@k7(d0KeR z9decRvt z3Dl|C07`m$Jf8!F2rK~y>v0a20AMNhC{C`(*?U~PPIgw5Ng zkA`9JsN_Cmlc&MnjZdNlekHckliP4dL;|h$C601h|aBg?$9C@!2|CiootVb~TW z;K8P=hD^AWR$W!OvaatyGi8PC1Dp<2tvYCn7Y)m&Hh*@1Ts)Qp9Tbj|XQML)0fMaA5_dYv#v%QBzC2(y ztM({r_AO&Yt8Nai7rcR2HE;irNDVg(+bIQ|c?>rQCDsEoFSj0nf!+Su_HTew&ii2a zue>&A0MNLd2AA5Q8%+m}j$YL~tL>-C3#vQ2suUN#m?<4-vG(;Eu2yYdwjB?)w7!Q| z!Ei&Ln?GvngsVbTo;Hu-%;{yT{&29R{Ox~g=ICp}fhvi0@4PGEJO=A!u7jD(zeig_ zF20#%k_-*+@d6r^%9knNJ4MmphBgQw-piO$_ImG@JL_NX)w>N^+eX_n+iioAO}~q7 zqit~Y{wwYCXJF&Dt&&HU4b@YyK!is`A!J zy8I4!H`q>Vw;zL*xOB% z)@DeR8A?5TH(og%G-s}i62246WoJ*%e#E29kZLoO+39aUA+7|c8wU(fmi(U@@-=t^ zY{&sqIM14$FgA@3sxtXnI20zInqRi!S&|EFDzCw=1m0(IwSg$&e|byA*Y-uhVscBz z*Y-!DaQkO+l>u)5a!bWi_Ce^Dj;HL0H`fOPdYqX{Pvrw$!F`)yAIg8yb5>xAK*h_7 zQ}Z)Cu+YJ{K2E=vQJ~C^&f7Dr-)PVR;XW2^+B+Jef0k6kjqE$D( zI&;HJ#7@VOcRGpR% zb7uPAN?V}X+N9sNw+Yc&9i7%0r@x)N{N8)}{`}tO{r2td+uOIIvLwcP=J#yXJ8ie_ zWDdbj^$u-OI`f$-dLE8FitY%fXzyrlwK_Ph1k^w#d{+vasH zt=ti-vmJ3ax5akn*KXAI#`a2u;}6D;rgWY^ooIh5(f$gpmahnAx8W+)$b4HxgS~B@ z$A&;kdzZ`5-W~HAb;>?Truu}xHqU5}%kd&)8FPusQitNe?Jtx=SO#2w`IA+zZH8Vs{T489#Q1Dg6h2@3ZUY}mkJ)4L>Fs6$p0{1od51FhHbt_AD?j-j%V1%wmD)A9xTn{(eP{H^16D_&7wEy1gM;gLB)Eo(ir*<R*yeB;a-eFI|JK}lK`?4qL{f*}@ z-oH5#B0M7Wn!L(7y)R>E%jU{~Wt9iZ;@b#H7az@QJ>>kL+8hZU z9^pOT)|E5MmD_sUWj*?`*5mSoAlEi>EZPOLJ>NIREtIRSyW59>PuHG zR@G-!EjHC>Q-Q;mp<3*!59YjomjS8uuvJZ&ARF5AXTz&(Dh#b0+B2?7Gl1pcf==2S zp0#U{yk<9N#{au4_T*?`^5?L=*6u8?&71FadQ*pXh71|wP`VdmMq$uu6lD)uP(F^q z81B!y(u%OQJEgw|qvCvlp94T8wyGB@K{5aHiKntr zo9jYMf26%@_Q|K_42(|3yR>eP{p91;fzgS5dwX8d{jVoo6N>*rqHp%tx%RH~k-;3L z@7VoFH1fFh#Pdj`dvta-um22}g}#*ZKb_EJEBS?3-|UX3a+RBo^>vqKcAT-PqZ2)k zYTEC0|JM`#KPCBug2jk$&)hb!RQa52>vh`(7BY99jCr#4y?FN02b<}F&CK35XgR-@X)u=s`Pv_JmLpu12iO9^ zVJ;OhfB$8F%3Q7{9?HFRx_zuBsn^6dWSktzRld2ic$+)(}sMf_tIG-?Nu1d@RPa9_nwt$hX-vZjIS39thfIb`~dYt|6_XJtZp|}#_#Pu zw9q$DkdY=wW=C)KLjSY+@Vd0@S+^_k<9gh!i*nsJVA-;iu_b~9dReBxsZ8(#l@5-k zYQsC~w~|%lAa#K5vJ6F1}yIY@*a7mg<=OG%&-9sO)rvQSB0;xc^Y)AhYT|Ad`p&fX;b)X$jSceA{?P2V< z;SQ+hl-6IWgMlo<5oeBT~vGrd({w>rA=Gl%F!?LQ`US2qRyiDGN-QZ%Qm5VC=^- zJ%*fX$~;rT?u__cQ|6d*wkh%5J!8LO$}Cf2sXZfJ&gn_L<@~;sxC}5PW*H2L2V{nn z7o>8(#guXwlwk-w?u_Bi7f;brQkasPa*io8O=&fy#gu6&shQ;n$RD9R4k>4d(L!Cm zu>nnWd4~;+bbMa}@7d${g@h(jBD*1x-H;seBH|?=JTsHo*_)8EL2pCKhD<=phM*5M z2n|v|qPR&8iR_bR_DM7Qq?vuv%swfz%gs76d2>D7&)`xq@BiPA^_<>6#eaYAl&(4M z=TEPj+wki@{>sLrUC>4Zh`|4zz=Cs=H_LJP^SIV{L&y=H!{MlugA4>lqjzjF{mh6^ zG_t*=vAHU6_x76lsz~kjKvOU@Bf;QuoR^n<{4R!iJ`Ej?g-oxxIJ)TxN5kqa01Q+r~hI`XTY2Gj?JEKONA}i($mwewv9-W z#nG$W87T@!qC8O55($+$gGIc$sVT}E(7mNAoozQ{WwBdZ>Nzjws%vdAb0R|v&tlRZ z8mj6VKNDaK8N*!K0}nPehO*eMs=8=h;|^Za6y~)}k*GZHN1E$+pdrZXLyg=S%wkS> z>l#Bv2pDzAH1$nYLGEgEx;$+~X2K#KZfa^Uaxi8X9uJ3*DijVQYb1+BtLo~_-7HXh zcci7kC_!yQfq{2~%rJ7wx@wzb&1UWymCWMXBT;GPkx*kayt|Y;n~OLiQ?aGf>Sf8u z>PSS!hojfx&Dc<5@LHUaHdqy{no4YzX+#+*oy|ZBMyL&*NX_Mh=4jX;Jy}U3$#`RgW>6jAkvxvqns^Jxt@pG0-@$; zT~i}p=?vabD*L`UT-O+_;f+nax}_$Ixz?)gb;avTJYGz6Py4H?1Hn*@)aP1US^36I zo43fgn;Ki{>pu%!VMo!rhEN!Wgz~~c^l2n?4~z``+ron|w`Qq~yq1((8cX{!%T62K!B@8n_^ZA5?w5CI}U1c(3;AOb{y2oM1x zKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&IhyW2F0z`la5CI}U1c(3;AOb{y2oM1x zKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&IhyW2F0z`la5CI}U1c(3;AOb{y2oM1x zKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&IhyW2F0z`la5CI}U1c(3;AOb{y2oM1x nKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&IhyW4zL=yNHtKkEZ diff --git a/src/mongoose-6.11/src/common/platforms/esp31/rom/rom_functions.S b/src/mongoose-6.11/src/common/platforms/esp31/rom/rom_functions.S deleted file mode 100644 index 5711a5c..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp31/rom/rom_functions.S +++ /dev/null @@ -1,294 +0,0 @@ -// These come from linker script - -PROVIDE ( Cache_Read_Disable , 0x4000444c ); -PROVIDE ( Cache_Read_Enable , 0x4000438c ); - -PROVIDE ( ets_delay_us , 0x40002db4 ); - -PROVIDE ( bzero , 0x40002a54 ); - -PROVIDE ( memcmp , 0x400068ec ); -PROVIDE ( memcpy , 0x40006974 ); -PROVIDE ( memmove , 0x40006a6c ); -PROVIDE ( memset , 0x40006be4 ); - -PROVIDE ( strcmp , 0x40005bb8 ); -PROVIDE ( strcpy , 0x40005cdc ); -PROVIDE ( strlen , 0x40005d6c ); -PROVIDE ( strncmp , 0x40005dd0 ); -PROVIDE ( strncpy , 0x40005e90 ); -PROVIDE ( strstr , 0x40005f6c ); - -PROVIDE ( ets_install_putc1 , 0x40002774 ); -PROVIDE ( ets_printf , 0x40002804 ); -PROVIDE ( ets_putc , 0x40002b14 ); - -PROVIDE ( ets_str2macaddr , 0x40002a64 ); - -PROVIDE ( gpio_output_set , 0x400049d8 ); -PROVIDE ( gpio_output_set_high , 0x400049f8 ); - -PROVIDE ( ets_get_cpu_frequency , 0x40002de8 ); -PROVIDE ( ets_update_cpu_frequency , 0x40002ddc ); - -PROVIDE ( lldesc_build_chain , 0x40004c8c ); - -PROVIDE ( multofup , 0x400068a0 ); -PROVIDE ( roundup2 , 0x40006890 ); - -PROVIDE (software_reset_cpu , 0x40002998 ); - -PROVIDE ( SPIEraseSector , 0x40004708 ); -PROVIDE ( SPIRead , 0x40004898 ); -PROVIDE ( SPIWrite , 0x40004738 ); - -PROVIDE ( uart_div_modify , 0x400034e8 ); -PROVIDE ( uart_tx_one_char , 0x4000362c ); - -PROVIDE ( __divsi3 , 0x40006888 ); -PROVIDE ( __udivdi3 , 0x40006c30 ); -PROVIDE ( __umoddi3 , 0x40006e64 ); - -PROVIDE ( _xtos_set_intlevel , 0x40006670 ); - -// These have been reverse-engineered. - -PROVIDE(_XX_Vec40, 0x40000040) -PROVIDE(_XX_ExcVec50, 0x40000050) -PROVIDE(_XX_ExcVec80, 0x40000080) - -PROVIDE(_XX_Vec400, 0x40000300) - -PROVIDE(_WindowOverflowHandler, 0x40000100) -PROVIDE(_WindowUnderflowHandler, 0x40000140) - -PROVIDE(_X_ResetVector, 0x40000500) - -PROVIDE(_c_stack, 0x40000700) -PROVIDE(_c_bss_start, 0x40000708) -PROVIDE(_c_bss_end, 0x4000070c) -PROVIDE(_c_0x3fffc210, 0x40000734) -PROVIDE(_c_0x80000000, 0x40000738) -PROVIDE(_c_0x40000000, 0x40000760) -PROVIDE(_c_0x7fffffff, 0x40000780) -PROVIDE(_c_0x00ff0000, 0x40000798) - -PROVIDE(_X_start, 0x400007ac) - -PROVIDE(_c_0x3fffd820, 0x40000f50) -PROVIDE(_X_ets_task, 0x40000f54) -PROVIDE(_XX_unk0f84, 0x40000f84) -PROVIDE(_XX_unk0f96, 0x40000f98) - -PROVIDE(_c_ets_critical_level, 0x400010a4) -PROVIDE(_X_ets_enter_critical, 0x400010a8) -PROVIDE(_X_ets_exit_critical, 0x400010bc) -PROVIDE(_X_ets_exit_critical_and_wait_int, 0x400010d4) -PROVIDE(_X_ets_isr_attach, 0x400010e8) // 3 args -PROVIDE(_X_ets_isr_mask, 0x400010f8) // 1 arg -PROVIDE(_X_ets_isr_unmask, 0x40001104) // 1 arg - -PROVIDE(_c_0x3fffda30, 0x40001110) -PROVIDE(_XX_set_0x3fffda30_0, 0x40001114) -PROVIDE(_XX_set_0x3fffda30_4, 0x40001120) -PROVIDE(_c_0xfffdffff, 0x4000112c) -PROVIDE(_c_0x60003e00, 0x40001130) -PROVIDE(_c_0x60008200, 0x40001134) -PROVIDE(_c_0x60007e00, 0x40001138) -PROVIDE(_c_0x1000, 0x4000113c) -PROVIDE(_s_fw_build, 0x40001140) -PROVIDE(_s_boot_banner, 0x40001144) -PROVIDE(_s_pct_s_pct_u, 0x40001148) -PROVIDE(_s_ets_main_c, 0x4000114c) -PROVIDE(_X_main, 0x4000115c) - -PROVIDE(_l_strap_0x0xxx, 0x4000125a) -PROVIDE(_l_strap_init_uart0, 0x40001269) -PROVIDE(_l_strap_0x0x00, 0x400012e2) -PROVIDE(_l_boot, 0x400012ea) -PROVIDE(_l_rst_cause_345, 0x40001336) -PROVIDE(_l_rst_cause_12, 0x40001342) -PROVIDE(_l_strap_NxNxxx, 0x40001405) -PROVIDE(_l_strap_0010xx, 0x4000144c) -PROVIDE(_l_strap_001000_0x110x, 0x400014b0) -PROVIDE(_l_strap_0x0x11_loader, 0x400014c9) // loader -PROVIDE(_l_strap_0x0x01, 0x400014d4) -PROVIDE(_l_strap_0x0x10, 0x400014e6) -PROVIDE(_c_0xffff8fff, 0x400014f0) -PROVIDE(_c_0x60008e00, 0x400014f4) - -PROVIDE(_s_waiting_for_host, 0x4000152c) -PROVIDE(_s_mem_banner, 0x40001cdc) -PROVIDE(_c_stack_sentry, 0x40001ce0) -PROVIDE(_XX_unk153c, 0x4000153c) - -PROVIDE(_c_data_end, 0x40001cd8) -PROVIDE(_c_data_start, 0x40001ce4) -PROVIDE(_X_print_mem_banner, 0x40001ce8) - -PROVIDE(_s_exc_sp_fmt, 0x40001d0c) -PROVIDE(_s_exc_sf_dump_fmt, 0x40001d10) -PROVIDE(_s_exc_regs_fmt, 0x40001d14) -PROVIDE(_X_exc_handler, 0x40001d18) - -PROVIDE(_XX_unk1d90, 0x40001d90) - -PROVIDE(_X_ets_memset, 0x40001db4) -PROVIDE(_X_ets_memcpy, 0x40001dc4) -PROVIDE(_X_ets_memmove, 0x40001dd4) -PROVIDE(_X_ets_memcmp, 0x40001de4) - -PROVIDE(_st_0x3fffda9c, 0x40002150) // struct - -PROVIDE(_X_ets_uart_putc, 0x4000223c) -PROVIDE(_X_ets_unk225c, 0x4000225c) - -PROVIDE(_c_0x4000223c_ets_uart_putc, 0x40002780) -PROVIDE(_X_ets_install_uart_printf, 0x40002784) -PROVIDE(_c_0x400027dc, 0x40002790) -PROVIDE(_X_ets_install_external_printf, 0x40002794) -PROVIDE(_X_ets_install_putc2, 0x400027b4) -PROVIDE(_X_ets_get_printf_buf_remain_len, 0x400027c0) -PROVIDE(_X_ets_reset_printf_buf_len, 0x400027cc) -PROVIDE(_X_ets_putc, 0x400027dc) - -PROVIDE(_c_0xdfffffff, 0x400028d4) -PROVIDE(_X_get_rst_cause, 0x400028d8) -PROVIDE(_XX_unk2948, 0x40002948) -PROVIDE(_l_2970, 0x40002970) -PROVIDE(_X_sw_sys_rst, 0x4000297c) -PROVIDE(_c_0x00400000, 0x400029b4) -PROVIDE(_c_0xffbfffff, 0x400029b8) -PROVIDE(_XX_apb_bridge_toggle, 0x400029bc) // turns RTC_CNTL_APB2RTC_BRIDGE_SEL on and off -PROVIDE(_X_ets_strcpy, 0x400029ec) -PROVIDE(_X_ets_strncpy, 0x40002a00) -PROVIDE(_X_ets_strcmp, 0x40002a10) -PROVIDE(_X_ets_strncmp, 0x40002a24) -PROVIDE(_X_ets_strlen, 0x40002a34) -PROVIDE(_X_ets_strstr, 0x40002a40) - -PROVIDE(_st_0x3fffdb10_uartdev, 0x40002e4c) // some struct - uartdev? - -PROVIDE(_c_0x3fffdb00, 0x40002e50) -PROVIDE(_c_0x3fffdb04, 0x40002f64) -PROVIDE(_XX_unk2e58, 0x40002e58) -PROVIDE(_X_UartDwnLdProc, 0x40002f6c) -PROVIDE(_c_0x00001800, 0x400030ec) -PROVIDE(_X_FlashDwnLdStartMsgProc, 0x400030f0) -PROVIDE(_XX_unk313c, 0x4000313c) -PROVIDE(_XX_unk31bc, 0x400031bc) -PROVIDE(_XX_unk31e4, 0x400031e4) -PROVIDE(_XX_unk3210, 0x40003210) -PROVIDE(_XX_unk3240, 0x40003240) -PROVIDE(_X_MemDwnLdStopReqMsgProc, 0x4000329c) -PROVIDE(_X_UartConnectProc, 0x400032c4) -PROVIDE(_X_UartRegWriteProc, 0x400032d4) -PROVIDE(_X_UartRegReadProc, 0x40003318) - -PROVIDE(_c_115200, 0x4000332c) -PROVIDE(_c_0x3feffe00, 0x40003330) -PROVIDE(_c_0xffff83ff, 0x40003334) -PROVIDE(_c_0x00001400, 0x40003338) -PROVIDE(_c_0x40003728_uart_int_handler, 0x4000333c) -PROVIDE(_c_0x3fffdb2c_uart_int_handler_arg, 0x40003340) -PROVIDE(_X_uart_attach, 0x40003344) -PROVIDE(_XX_uart_set_unk33c0, 0x400033c0) -PROVIDE(_c_0x5ffffe00, 0x400033cc) -PROVIDE(_c_0x0000ffff, 0x400033d0) -PROVIDE(_c_0x000fffff, 0x40003448) -PROVIDE(_c_0x00060000, 0x400034e0) -PROVIDE(_c_0xfff9ffff, 0x400034e4) - -PROVIDE(_c_0xfffc0fff, 0x40003520) -PROVIDE(_c_0x00009000, 0x40003524) -PROVIDE(_c_0x00020000, 0x40003528) -PROVIDE(_c_13000000, 0x4000352c) -PROVIDE(_c_0x08000000, 0x40003530) -PROVIDE(_X_uart_init, 0x40003534) -PROVIDE(_l_35f4, 0x400035f4) -PROVIDE(_X_uart_wait_tx_empty, 0x4000369c) - -PROVIDE(_X_uart_int_handler, 0x40003728) - -PROVIDE(_X_uart_tx_one_char2, 0x40003664) - -PROVIDE(_X_send_packet, 0x400037d4) -PROVIDE(_X_SendMsg, 0x40003828) -PROVIDE(_X_recv_packet, 0x4000383c) -PROVIDE(_X_RcvMsg, 0x40003988) -PROVIDE(_X_uart_rx_readbuff, 0x400039a0) - -PROVIDE(_c_0x60004e00, 0x40003a18) -PROVIDE(_X_SelectSpiFunction, 0x40003a1c) // 1 arg - SPI number -PROVIDE(_c_0x60002e00, 0x40003c78) -PROVIDE(_X_SPI_chip_erase, 0x40003c7c) -PROVIDE(_c_0x00ffffff, 0x40003cb4) -PROVIDE(_c_0x01000000, 0x40003cb8) -PROVIDE(_XX_unk3cbc, 0x40003cbc) -PROVIDE(_c_0x00800000, 0x40003d00) -PROVIDE(_c_0x02000000, 0x40003d4c) -PROVIDE(_XX_unk3e24, 0x40003e24) -PROVIDE(_X_SPI_read_status, 0x40003efc) -PROVIDE(_c_0x90000000, 0x40003f48) -PROVIDE(_c_0x70000035, 0x40003f4c) -PROVIDE(_c_0x00040000, 0x40003f50) -PROVIDE(_XX_unk3f54, 0x40003f54) -PROVIDE(_c_0x04000000, 0x40003fcc) -PROVIDE(_XX_unk4010, 0x40004010) -PROVIDE(_X_SPI_write_enable, 0x400041bc) -PROVIDE(_X_Wait_SPI_Idle, 0x40004208) -PROVIDE(_l_4228, 0x40004228) -PROVIDE(_l_4234, 0x40004234) -PROVIDE(_XX_unk4238, 0x40004238) -PROVIDE(_X_SPIFlashModeConfig, 0x400042d8) -PROVIDE(_X_spi_flash_attach, 0x40004370) // 2 args: SPI num, ??? -PROVIDE(_X_SPIReadModeConfig, 0x40004538) - -PROVIDE(_X_SPIEraseArea, 0x400048b4) -PROVIDE(_XX_unk4940, 0x40004940) - -PROVIDE(_st_0x3fffdc90, 0x40004d80) -PROVIDE(_XX_unk4d88, 0x40004d88) -PROVIDE(_XX_unk4f6c, 0x40004f6c) -PROVIDE(_XX_unk4fc8, 0x40004fc8) - -PROVIDE(_c_0xbfffffff, 0x40004c80) -PROVIDE(_c_0xff000fff, 0x40004c88) -PROVIDE(_XX_unk4f14, 0x40004f14) -PROVIDE(_s_no_rds, 0x40005008) -PROVIDE(_XX_unk500c, 0x4000500c) - -PROVIDE(_fp_0x40004f6c, 0x40005164) -PROVIDE(_fp_0x40004fc8, 0x40005168) -PROVIDE(_X_sip_init_attach, 0x40005170) // 1 arg, boot_mode? -PROVIDE(_XX_unk51ac, 0x400051ac) -PROVIDE(_c_0x60017e00, 0x40005478) -PROVIDE(_st_0x3fffdf70, 0x400054a4) -PROVIDE(_c_0x6000ae00, 0x400054b8) -PROVIDE(_c_0xf0000000, 0x400054bc) -PROVIDE(_c_0x02320017, 0x400054c0) -PROVIDE(_c_0x11116666, 0x400054c4) -PROVIDE(_c_0x01110013, 0x400054e4) -PROVIDE(_X_slc_init_attach, 0x400054e8) // 4 args - fp, fp, st, boot_mode; PRODPORT_SDIO_RST -PROVIDE(_l_slc_boot_mode_4, 0x40005654) -PROVIDE(_X_slc_enable, 0x40005678) -PROVIDE(_X_slc_select_tohost_gpio_mode, 0x400056fc) -PROVIDE(_X_slc_select_tohost_gpio, 0x40005708) -PROVIDE(_c_0xff300000, 0x40005730) -PROVIDE(_XX_unk5734, 0x40005734) -PROVIDE(_XX_unk57b8, 0x400057b8) -PROVIDE(_XX_unk57f4, 0x400057f4) -PROVIDE(_XX_unk5848, 0x40005848) - -PROVIDE(_c_0xfffff0c0, 0x40005988) -PROVIDE(_X_slc_set_host_io_max_window, 0x4000598c) -PROVIDE(_X_slc_init_credit, 0x400059ac) -PROVIDE(_X_slc_add_credits, 0x400059c4) - -PROVIDE(_X_xtos_set_interrupt_handler_arg, 0x400059d8) -PROVIDE(_X_xtos_set_interrupt_handler, 0x40005a24) -PROVIDE(_X_xtos_ints_on, 0x40005a34) -PROVIDE(_X_xtos_ints_off, 0x40005a58) - -PROVIDE(_XX_xtos_exc_unk5a80, 0x40005a80) -PROVIDE(_XX_xtos_exc_unk5b94, 0x40005b94) diff --git a/src/mongoose-6.11/src/common/platforms/esp32/rom/.gitattributes b/src/mongoose-6.11/src/common/platforms/esp32/rom/.gitattributes deleted file mode 100644 index b2dd225..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp32/rom/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -rom.bin -nodiff -rom.elf -nodiff diff --git a/src/mongoose-6.11/src/common/platforms/esp32/rom/disasm.sh b/src/mongoose-6.11/src/common/platforms/esp32/rom/disasm.sh deleted file mode 100755 index ae719ac..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp32/rom/disasm.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -xtensa-esp32-elf-gcc -Wl,-N,-Ttext,0x40000000 -nostdlib rom.S -o rom.elf && \ - xtensa-esp32-elf-objdump -d rom.elf > ESP32_ROM.txt diff --git a/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.S b/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.S deleted file mode 100644 index 11bd296..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.S +++ /dev/null @@ -1,28 +0,0 @@ -.text -.org 0 -.globl _start - -// xtensa-esp32-elf-gcc -Wl,-N,-Ttext,0x40000000 -nostdlib rom.S -o rom.elf - -here = . -#define PROVIDE(name, addr) name = here + addr - 0x40000000 - -#include "rom_functions.S" - -PROVIDE ( _x_unk_40061b88, 0x40061b88 ) -PROVIDE ( _x_unk_spi_400622c0, 0x400622c0 ) -PROVIDE ( _c_3ff000c8, 0x40062df0 ) -PROVIDE ( _c_3ff5b024, 0x40062e0c ) -PROVIDE ( _c_3ff5b000, 0x40062e10 ) -PROVIDE ( _c_3ff5b020, 0x40062e14 ) -PROVIDE ( _c_3ff5b028, 0x40062e18 ) -PROVIDE ( _l_40062e90, 0x40062e90 ) -PROVIDE ( _l_SPI_Prepare_Encrypt_Data_loop, 0x40062e34 ) -PROVIDE ( _l_SPI_Prepare_Encrypt_Data_wait, 0x40062e54 ) -PROVIDE ( _l_SPI_Prepare_Encrypt_Data_out, 0x40062e5e ) - -.text - -_start: -.incbin "rom.bin" -_end: diff --git a/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.bin b/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.bin deleted file mode 100644 index 8f0bb3fa676368b2cc2fb38b8d7fd7012220dab4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 458752 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⪼dDlCV)_Wy`wOxZk!o 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�`GS5Zb28@M4E(+pnSXovE=3L@|* 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 z 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 kz@HrWlLLQp;7<vP@FxfU%+wpX9**0P;R5aR2}S diff --git a/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.elf b/src/mongoose-6.11/src/common/platforms/esp32/rom/rom.elf deleted file mode 100755 index f65fbf09996b1f49996ff815c7e28fdabe12fd06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 521912 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%0Au=(YqQQcqP@3=`*i95EilaE~L_ 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%0DU(>2#J>;ph$jnn8-qLgkJC0L+XrH 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 diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.elf b/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.elf deleted file mode 100755 index 6a21116b321780589032452c7854e66d5b333804..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79984 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 $@" - @[ -d $(BUILD_DIR) ] || mkdir $(BUILD_DIR) - @docker run --rm -i -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \ - "cd /src/common/platforms/esp8266/stubs && \ - $(XT_CC) -std=c99 -Wall -Werror -Os -DESP8266 \ - -mtext-section-literals -mlongcalls -nostdlib -fno-builtin \ - -I. -I/src/common/platforms/esp \ - -I/opt/Espressif/ESP8266_SDK \ - -Wl,-static -ffunction-sections -Wl,--gc-sections \ - -Tstub.ld $(CFLAGS) -o $@ $^" - -wrap: $(STUB_JSON) - -$(STUB_JSON): $(STUB_ELF) $(COMMON_STUB_DIR)/esptool.py - @echo " WRAP $< -> $@" - @docker run --rm -i -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \ - "cd /src/common/platforms/esp8266/stubs && \ - $(COMMON_STUB_DIR)/esptool.py wrap_stub $<" > $@ - -run: $(STUB_JSON) - @echo " RUN $< $(PARAMS) -> $(PORT)" - @docker run --rm -i --privileged -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \ - "cd /src/common/platforms/esp8266/stubs && \ - $(COMMON_STUB_DIR)/esptool.py --port $(PORT) run_stub $< $(PARAMS)" - -clean: - @rm -rf $(BUILD_DIR) diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/README.md b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/README.md deleted file mode 100644 index 9265771..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/README.md +++ /dev/null @@ -1,13 +0,0 @@ -This is a ESP boot loader stub development environment. - -Code produced in this environment can be loaded and executed -in the bootloader environment. Usually it is used to implement -functionality not found in the bootloader. - -Stubs can be executed using the `run_stub` command of the modified esptool.py provided. -`wrap_stub` produces a JSON represenattion of the stub that can later be reused -or built into other tools. - -Example usage: - $ make run STUB=stub_flash_size.c PORT=/dev/ttyUSB0 - $ make run STUB=stub_md5.c PORT=/dev/ttyUSB0 PARAMS="0x11000 10000 1" diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/chop80.py b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/chop80.py deleted file mode 100755 index a488ba2..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/chop80.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/python - -import sys - -for line in open(sys.argv[1]): - i = 0 - while i < len(line): - n = len(line) - i - if n > 80: n = 80 - l = line[i:i+n] - if n == 80: - l += '\\' - print l - i += n diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/rom_functions.h b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/rom_functions.h deleted file mode 100644 index 82ea746..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/rom_functions.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef CS_COMMON_PLATFORMS_ESP8266_STUBS_ROM_FUNCTIONS_H_ -#define CS_COMMON_PLATFORMS_ESP8266_STUBS_ROM_FUNCTIONS_H_ - -#include - -#include "/opt/Espressif/ESP8266_NONOS_SDK/include/c_types.h" -#include "/opt/Espressif/ESP8266_NONOS_SDK/include/spi_flash.h" - -int uart_rx_one_char(uint8_t *ch); -uint8_t uart_rx_one_char_block(); -int uart_tx_one_char(char ch); -void uart_div_modify(uint32_t uart_no, uint32_t baud_div); - -int SendMsg(uint8_t *msg, uint8_t size); -int send_packet(uint8_t *packet, uint32_t size); -// recv_packet depends on global UartDev, better to avoid it. -// uint32_t recv_packet(void *packet, uint32_t len, uint8_t no_sync); - -void _putc1(char *ch); - -void ets_delay_us(uint32_t us); - -uint32_t SPILock(); -uint32_t SPIUnlock(); -uint32_t SPIRead(uint32_t addr, void *dst, uint32_t size); -uint32_t SPIWrite(uint32_t addr, const uint32_t *src, uint32_t size); -uint32_t SPIEraseChip(); -uint32_t SPIEraseBlock(uint32_t block_num); -uint32_t SPIEraseSector(uint32_t sector_num); - -extern SpiFlashChip *flashchip; -uint32_t Wait_SPI_Idle(SpiFlashChip *spi); -uint32_t SPI_chip_erase(SpiFlashChip *spi); -uint32_t SPI_read_status(SpiFlashChip *spi); -uint32_t SPI_write_enable(SpiFlashChip *spi); - -void spi_flash_attach(); - -/* ESP32 API compatibility */ -#define esp_rom_spiflash_unlock SPIUnlock -#define esp_rom_spiflash_erase_sector SPIEraseSector -#define esp_rom_spiflash_erase_block SPIEraseBlock -#define esp_rom_spiflash_erase_chip SPIEraseChip -#define esp_rom_spiflash_read SPIRead -#define esp_rom_spiflash_write SPIWrite -#define esp_rom_spiflash_config_param SPIParamCfg - -void SelectSpiFunction(); -void SPIFlashModeConfig(uint32_t a, uint32_t b); -void SPIReadModeCnfig(uint32_t a); -uint32_t SPIParamCfg(uint32_t deviceId, uint32_t chip_size, uint32_t block_size, - uint32_t sector_size, uint32_t page_size, - uint32_t status_mask); - -void Cache_Read_Disable(); - -void ets_delay_us(uint32_t delay_micros); - -void ets_isr_mask(uint32_t ints); -void ets_isr_unmask(uint32_t ints); -typedef void (*int_handler_t)(void *arg); - -void ets_intr_lock(); -void ets_intr_unlock(); -void ets_set_user_start(void (*user_start_fn)()); - -uint32_t rtc_get_reset_reason(); -void software_reset(); -void rom_phy_reset_req(); - -void uart_rx_intr_handler(void *arg); - -void _ResetVector(); - -/* Crypto functions are from wpa_supplicant. */ -int md5_vector(uint32_t num_msgs, const uint8_t *msgs[], - const uint32_t *msg_lens, uint8_t *digest); -int sha1_vector(uint32_t num_msgs, const uint8_t *msgs[], - const uint32_t *msg_lens, uint8_t *digest); - -struct MD5Context { - uint32_t buf[4]; - uint32_t bits[2]; - uint8_t in[64]; -}; - -void MD5Init(struct MD5Context *ctx); -void MD5Update(struct MD5Context *ctx, void *buf, uint32_t len); -void MD5Final(uint8_t digest[16], struct MD5Context *ctx); - -#endif /* CS_COMMON_PLATFORMS_ESP8266_STUBS_ROM_FUNCTIONS_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/stub.ld b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/stub.ld deleted file mode 100644 index f54a4f8..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/stub.ld +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2015 Cesanta Software Limited - * All rights reserved - */ - -MEMORY { - iram : org = 0x40100000, len = 0x8000 - dram : org = 0x3FFE8000, len = 0x14000 -} - -ENTRY(stub_main) - -SECTIONS { - .params 0x40100000 : { - _params_start = ABSOLUTE(.); - *(.params) - _params_end = ABSOLUTE(.); - } > iram - - .text : ALIGN(4) { - _code_start = ABSOLUTE(.); - *(.literal) - *(.text .text.*) - } > iram - - .bss : ALIGN(4) { - _bss_start = ABSOLUTE(.); - *(.bss) - _bss_end = ABSOLUTE(.); - } > dram - - .data : ALIGN(4) { - _data_start = ABSOLUTE(.); - *(.data) - *(.rodata .rodata.*) - } > dram -} - -INCLUDE "eagle.rom.addr.v6.ld" - -PROVIDE(SPIFlashModeConfig = 0x40004568); -PROVIDE(SPI_erase_sector = 0x400040c0); -PROVIDE(SPI_erase_block = 0x40004120); diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/stub_hello.c b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/stub_hello.c deleted file mode 100644 index 10a827d..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/stub_hello.c +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2015 Cesanta Software Limited - * All rights reserved - * - * - * Stub template. - */ - -#include -#include "rom_functions.h" - -/* Define the args vector and put it into the ".params" section. */ -uint32_t params[3] __attribute__((section(".params"))); - -/* Define a function called stub_main. Do not return or reboot. - * Use send_packet to communicate to the host. */ - -const char *hello = "Hello"; - -void stub_main(void) { - send_packet(hello, 5); - _ResetVector(); -} diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/uart.c b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/uart.c deleted file mode 100644 index 5651bd8..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/uart.c +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#include "uart.h" -#include "ets_sys.h" - -#define UART_CLKDIV_26MHZ(B) (52000000 + B / 2) / B - -void set_baud_rate(uint32_t uart_no, uint32_t baud_rate) { - uint32_t div = UART_CLKDIV_26MHZ(baud_rate); - WRITE_PERI_REG(UART_CLKDIV_REG(uart_no), div & 0xfffff); -} diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/uart.h b/src/mongoose-6.11/src/common/platforms/esp8266/stubs/uart.h deleted file mode 100644 index fc321c2..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/stubs/uart.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_ESP8266_STUBS_UART_H_ -#define CS_COMMON_PLATFORMS_ESP8266_STUBS_UART_H_ - -#include - -void set_baud_rate(uint32_t uart_no, uint32_t baud_rate); - -#define REG_UART_BASE(i) (0x60000000 + (i) *0xf00) -#define UART_FIFO_REG(i) (REG_UART_BASE(i) + 0x0) -#define UART_CONF1_REG(i) (REG_UART_BASE(i) + 0x24) -#define UART_RX_TOUT_EN (BIT(31)) -#define UART_RX_TOUT_THRHD_S 24 -#define UART_RXFIFO_FULL_THRHD_S 0 -#define UART_INT_ST_REG(i) (REG_UART_BASE(i) + 0x8) -#define UART_INT_ENA_REG(i) (REG_UART_BASE(i) + 0xC) -#define UART_RXFIFO_TOUT_INT_ENA (BIT(8)) -#define UART_RXFIFO_FULL_INT_ENA (BIT(0)) - -#define UART_INT_CLR_REG(i) (REG_UART_BASE(i) + 0x10) - -#define UART_CLKDIV_REG(i) (REG_UART_BASE(i) + 0x14) - -#define UART_STATUS_REG(i) (REG_UART_BASE(i) + 0x1C) - -#define UART_CONF0_REG(i) (REG_UART_BASE(i) + 0x20) -#define UART_RXFIFO_RST (BIT(17)) - -#define ETS_UART0_INUM ETS_UART_INUM - -#endif /* CS_COMMON_PLATFORMS_ESP8266_STUBS_UART_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/tools/malloc_analyzer.py b/src/mongoose-6.11/src/common/platforms/esp8266/tools/malloc_analyzer.py deleted file mode 100644 index 91d4d0c..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/tools/malloc_analyzer.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/python - -# Analyzers output of firmware compiler with -DESP_ENABLE_MALLOC_TRACES - -import re -import sys - -allocs = {} - -for l in sys.stdin: - m = re.match(r'(ca|ma|re|fr) (\S+)\s*(\S*)\s*(\S*)', l) - if not m: continue - op = m.group(1) - if op in ('ca', 'ma'): - allocs[m.group(2)] = long(m.group(3)) - else: - if m.group(2) in allocs: - del allocs[m.group(2)] - if op == 're': - allocs[m.group(3)] = long(m.group(4)) - -for k, v in sorted(allocs.iteritems()): - print k, v diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/uart_register.h b/src/mongoose-6.11/src/common/platforms/esp8266/uart_register.h deleted file mode 100644 index e8fcda7..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/uart_register.h +++ /dev/null @@ -1,136 +0,0 @@ -/* clang-format off */ -/* - * Copyright (c) 2010 - 2011 Espressif System - * - */ - -#ifndef CS_COMMON_PLATFORMS_ESP8266_UART_REGISTER_H_ -#define CS_COMMON_PLATFORMS_ESP8266_UART_REGISTER_H_ - -#define REG_UART_BASE(i) (0x60000000 + (i)*0xf00) -//version value:32'h062000 - -#define UART_FIFO(i) (REG_UART_BASE(i) + 0x0) -#define UART_RXFIFO_RD_BYTE 0x000000FF -#define UART_RXFIFO_RD_BYTE_S 0 - -#define UART_INT_RAW(i) (REG_UART_BASE(i) + 0x4) -#define UART_RXFIFO_TOUT_INT_RAW (BIT(8)) -#define UART_BRK_DET_INT_RAW (BIT(7)) -#define UART_CTS_CHG_INT_RAW (BIT(6)) -#define UART_DSR_CHG_INT_RAW (BIT(5)) -#define UART_RXFIFO_OVF_INT_RAW (BIT(4)) -#define UART_FRM_ERR_INT_RAW (BIT(3)) -#define UART_PARITY_ERR_INT_RAW (BIT(2)) -#define UART_TXFIFO_EMPTY_INT_RAW (BIT(1)) -#define UART_RXFIFO_FULL_INT_RAW (BIT(0)) - -#define UART_INT_ST(i) (REG_UART_BASE(i) + 0x8) -#define UART_RXFIFO_TOUT_INT_ST (BIT(8)) -#define UART_BRK_DET_INT_ST (BIT(7)) -#define UART_CTS_CHG_INT_ST (BIT(6)) -#define UART_DSR_CHG_INT_ST (BIT(5)) -#define UART_RXFIFO_OVF_INT_ST (BIT(4)) -#define UART_FRM_ERR_INT_ST (BIT(3)) -#define UART_PARITY_ERR_INT_ST (BIT(2)) -#define UART_TXFIFO_EMPTY_INT_ST (BIT(1)) -#define UART_RXFIFO_FULL_INT_ST (BIT(0)) - -#define UART_INT_ENA(i) (REG_UART_BASE(i) + 0xC) -#define UART_RXFIFO_TOUT_INT_ENA (BIT(8)) -#define UART_BRK_DET_INT_ENA (BIT(7)) -#define UART_CTS_CHG_INT_ENA (BIT(6)) -#define UART_DSR_CHG_INT_ENA (BIT(5)) -#define UART_RXFIFO_OVF_INT_ENA (BIT(4)) -#define UART_FRM_ERR_INT_ENA (BIT(3)) -#define UART_PARITY_ERR_INT_ENA (BIT(2)) -#define UART_TXFIFO_EMPTY_INT_ENA (BIT(1)) -#define UART_RXFIFO_FULL_INT_ENA (BIT(0)) - -#define UART_INT_CLR(i) (REG_UART_BASE(i) + 0x10) -#define UART_RXFIFO_TOUT_INT_CLR (BIT(8)) -#define UART_BRK_DET_INT_CLR (BIT(7)) -#define UART_CTS_CHG_INT_CLR (BIT(6)) -#define UART_DSR_CHG_INT_CLR (BIT(5)) -#define UART_RXFIFO_OVF_INT_CLR (BIT(4)) -#define UART_FRM_ERR_INT_CLR (BIT(3)) -#define UART_PARITY_ERR_INT_CLR (BIT(2)) -#define UART_TXFIFO_EMPTY_INT_CLR (BIT(1)) -#define UART_RXFIFO_FULL_INT_CLR (BIT(0)) - -#define UART_CLKDIV(i) (REG_UART_BASE(i) + 0x14) -#define UART_CLKDIV_CNT 0x000FFFFF -#define UART_CLKDIV_S 0 - -#define UART_AUTOBAUD(i) (REG_UART_BASE(i) + 0x18) -#define UART_GLITCH_FILT 0x000000FF -#define UART_GLITCH_FILT_S 8 -#define UART_AUTOBAUD_EN (BIT(0)) - -#define UART_STATUS(i) (REG_UART_BASE(i) + 0x1C) -#define UART_TXD (BIT(31)) -#define UART_RTSN (BIT(30)) -#define UART_DTRN (BIT(29)) -#define UART_TXFIFO_CNT 0x000000FF -#define UART_TXFIFO_CNT_S 16 -#define UART_RXD (BIT(15)) -#define UART_CTSN (BIT(14)) -#define UART_DSRN (BIT(13)) -#define UART_RXFIFO_CNT 0x000000FF -#define UART_RXFIFO_CNT_S 0 - -#define UART_CONF0(i) (REG_UART_BASE(i) + 0x20) -#define UART_DTR_INV (BIT(24)) -#define UART_RTS_INV (BIT(23)) -#define UART_TXD_INV (BIT(22)) -#define UART_DSR_INV (BIT(21)) -#define UART_CTS_INV (BIT(20)) -#define UART_RXD_INV (BIT(19)) -#define UART_TXFIFO_RST (BIT(18)) -#define UART_RXFIFO_RST (BIT(17)) -#define UART_IRDA_EN (BIT(16)) -#define UART_TX_FLOW_EN (BIT(15)) -#define UART_LOOPBACK (BIT(14)) -#define UART_IRDA_RX_INV (BIT(13)) -#define UART_IRDA_TX_INV (BIT(12)) -#define UART_IRDA_WCTL (BIT(11)) -#define UART_IRDA_TX_EN (BIT(10)) -#define UART_IRDA_DPLX (BIT(9)) -#define UART_TXD_BRK (BIT(8)) -#define UART_SW_DTR (BIT(7)) -#define UART_SW_RTS (BIT(6)) -#define UART_STOP_BIT_NUM 0x00000003 -#define UART_STOP_BIT_NUM_S 4 -#define UART_BIT_NUM 0x00000003 -#define UART_BIT_NUM_S 2 -#define UART_PARITY_EN (BIT(1)) -#define UART_PARITY (BIT(0)) - -#define UART_CONF1(i) (REG_UART_BASE(i) + 0x24) -#define UART_RX_TOUT_EN (BIT(31)) -#define UART_RX_TOUT_THRHD 0x0000007F -#define UART_RX_TOUT_THRHD_S 24 -#define UART_RX_FLOW_EN (BIT(23)) -#define UART_RX_FLOW_THRHD 0x0000007F -#define UART_RX_FLOW_THRHD_S 16 -#define UART_TXFIFO_EMPTY_THRHD 0x0000007F -#define UART_TXFIFO_EMPTY_THRHD_S 8 -#define UART_RXFIFO_FULL_THRHD 0x0000007F -#define UART_RXFIFO_FULL_THRHD_S 0 - -#define UART_LOWPULSE(i) (REG_UART_BASE(i) + 0x28) -#define UART_LOWPULSE_MIN_CNT 0x000FFFFF -#define UART_LOWPULSE_MIN_CNT_S 0 - -#define UART_HIGHPULSE(i) (REG_UART_BASE(i) + 0x2C) -#define UART_HIGHPULSE_MIN_CNT 0x000FFFFF -#define UART_HIGHPULSE_MIN_CNT_S 0 - -#define UART_PULSE_NUM(i) (REG_UART_BASE(i) + 0x30) -#define UART_PULSE_NUM_CNT 0x0003FF -#define UART_PULSE_NUM_CNT_S 0 - -#define UART_DATE(i) (REG_UART_BASE(i) + 0x78) -#define UART_ID(i) (REG_UART_BASE(i) + 0x7C) - -#endif /* CS_COMMON_PLATFORMS_ESP8266_UART_REGISTER_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip.h b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip.h deleted file mode 100644 index 1a772aa..0000000 --- a/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_ -#define CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_ - -#ifndef MG_LWIP -#define MG_LWIP 0 -#endif - -#if MG_LWIP - -/* - * When compiling for nRF5x chips with arm-none-eabi-gcc, it has BYTE_ORDER - * already defined, so in order to avoid warnings in lwip, we have to undefine - * it. - * - * TODO: Check if in the future versions of nRF5 SDK that changes. - * Current version of nRF51 SDK: 0.8.0 - * nRF5 SDK: 0.9.0 - */ -#if CS_PLATFORM == CS_P_NRF51 || CS_PLATFORM == CS_P_NRF52 -#undef BYTE_ORDER -#endif - -#include -#include -#include -#include -#include -#include - -#ifndef LWIP_PROVIDE_ERRNO -#include -#endif - -#if LWIP_SOCKET -#include -#else -/* We really need the definitions from sockets.h. */ -#undef LWIP_SOCKET -#define LWIP_SOCKET 1 -#include -#undef LWIP_SOCKET -#define LWIP_SOCKET 0 -#endif - -#define INVALID_SOCKET (-1) -#define SOMAXCONN 10 -typedef int sock_t; - -#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL -struct mg_mgr; -struct mg_connection; -uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr); -void mg_lwip_set_keepalive_params(struct mg_connection *nc, int idle, - int interval, int count); -#endif - -/* For older version of LWIP */ -#ifndef ipX_2_ip -#define ipX_2_ip(x) (x) -#endif - -#endif /* MG_LWIP */ - -#endif /* CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_ev_mgr.c b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_ev_mgr.c deleted file mode 100644 index 5eea26f..0000000 --- a/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_ev_mgr.c +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL - -#ifndef MG_SIG_QUEUE_LEN -#define MG_SIG_QUEUE_LEN 32 -#endif - -struct mg_ev_mgr_lwip_signal { - int sig; - struct mg_connection *nc; -}; - -struct mg_ev_mgr_lwip_data { - struct mg_ev_mgr_lwip_signal sig_queue[MG_SIG_QUEUE_LEN]; - int sig_queue_len; - int start_index; -}; - -void mg_lwip_post_signal(enum mg_sig_type sig, struct mg_connection *nc) { - struct mg_ev_mgr_lwip_data *md = - (struct mg_ev_mgr_lwip_data *) nc->iface->data; - mgos_lock(); - if (md->sig_queue_len >= MG_SIG_QUEUE_LEN) { - mgos_unlock(); - return; - } - int end_index = (md->start_index + md->sig_queue_len) % MG_SIG_QUEUE_LEN; - md->sig_queue[end_index].sig = sig; - md->sig_queue[end_index].nc = nc; - md->sig_queue_len++; - mg_lwip_mgr_schedule_poll(nc->mgr); - mgos_unlock(); -} - -void mg_ev_mgr_lwip_process_signals(struct mg_mgr *mgr) { - struct mg_ev_mgr_lwip_data *md = - (struct mg_ev_mgr_lwip_data *) mgr->ifaces[MG_MAIN_IFACE]->data; - while (md->sig_queue_len > 0) { - mgos_lock(); - int sig = md->sig_queue[md->start_index].sig; - struct mg_connection *nc = md->sig_queue[md->start_index].nc; - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - md->start_index = (md->start_index + 1) % MG_SIG_QUEUE_LEN; - md->sig_queue_len--; - mgos_unlock(); - if (nc->iface == NULL || nc->mgr == NULL) continue; - switch (sig) { - case MG_SIG_CONNECT_RESULT: { -#if MG_ENABLE_SSL - if (cs->err == 0 && (nc->flags & MG_F_SSL) && - !(nc->flags & MG_F_SSL_HANDSHAKE_DONE)) { - mg_lwip_ssl_do_hs(nc); - } else -#endif - { - mg_if_connect_cb(nc, cs->err); - } - break; - } - case MG_SIG_CLOSE_CONN: { - nc->flags |= MG_F_SEND_AND_CLOSE; - mg_close_conn(nc); - break; - } - case MG_SIG_RECV: { - cs->recv_pending = 0; - if (nc->flags & MG_F_UDP) { - mg_lwip_handle_recv_udp(nc); - } else { - mg_lwip_handle_recv_tcp(nc); - } - break; - } - case MG_SIG_TOMBSTONE: { - break; - } - case MG_SIG_ACCEPT: { - mg_lwip_handle_accept(nc); - break; - } - } - } -} - -void mg_lwip_if_init(struct mg_iface *iface) { - LOG(LL_INFO, ("%p Mongoose init", iface)); - iface->data = MG_CALLOC(1, sizeof(struct mg_ev_mgr_lwip_data)); -} - -void mg_lwip_if_free(struct mg_iface *iface) { - MG_FREE(iface->data); - iface->data = NULL; -} - -void mg_lwip_if_add_conn(struct mg_connection *nc) { - (void) nc; -} - -void mg_lwip_if_remove_conn(struct mg_connection *nc) { - struct mg_ev_mgr_lwip_data *md = - (struct mg_ev_mgr_lwip_data *) nc->iface->data; - /* Walk the queue and null-out further signals for this conn. */ - for (int i = 0; i < MG_SIG_QUEUE_LEN; i++) { - if (md->sig_queue[i].nc == nc) { - md->sig_queue[i].sig = MG_SIG_TOMBSTONE; - } - } -} - -time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms) { - struct mg_mgr *mgr = iface->mgr; - int n = 0; - double now = mg_time(); - struct mg_connection *nc, *tmp; - double min_timer = 0; - int num_timers = 0; -#if 0 - DBG(("begin poll @%u", (unsigned int) (now * 1000))); -#endif - mg_ev_mgr_lwip_process_signals(mgr); - for (nc = mgr->active_connections; nc != NULL; nc = tmp) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - tmp = nc->next; - n++; - if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || - ((nc->flags & MG_F_SEND_AND_CLOSE) && (nc->flags & MG_F_UDP) && - (nc->send_mbuf.len == 0))) { - mg_close_conn(nc); - continue; - } - mg_if_poll(nc, now); - mg_if_timer(nc, now); -#if MG_ENABLE_SSL - if ((nc->flags & MG_F_SSL) && cs != NULL && cs->pcb.tcp != NULL && - cs->pcb.tcp->state == ESTABLISHED) { - if (((nc->flags & MG_F_WANT_WRITE) || - ((nc->send_mbuf.len > 0) && - (nc->flags & MG_F_SSL_HANDSHAKE_DONE))) && - cs->pcb.tcp->snd_buf > 0) { - /* Can write more. */ - if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { - if (!(nc->flags & MG_F_CONNECTING)) mg_lwip_ssl_send(nc); - } else { - mg_lwip_ssl_do_hs(nc); - } - } - if (cs->rx_chain != NULL || (nc->flags & MG_F_WANT_READ)) { - if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { - if (!(nc->flags & MG_F_CONNECTING)) mg_lwip_ssl_recv(nc); - } else { - mg_lwip_ssl_do_hs(nc); - } - } - } else -#endif /* MG_ENABLE_SSL */ - { - if (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING)) { - mg_lwip_send_more(nc); - } - } - if (nc->sock != INVALID_SOCKET && - !(nc->flags & (MG_F_UDP | MG_F_LISTENING)) && cs->pcb.tcp != NULL && - cs->pcb.tcp->unsent != NULL) { - tcpip_callback(tcp_output_tcpip, cs->pcb.tcp); - } - if (nc->ev_timer_time > 0) { - if (num_timers == 0 || nc->ev_timer_time < min_timer) { - min_timer = nc->ev_timer_time; - } - num_timers++; - } - - if (nc->sock != INVALID_SOCKET) { - /* Try to consume data from cs->rx_chain */ - mg_lwip_consume_rx_chain_tcp(nc); - - /* - * If the connection is about to close, and rx_chain is finally empty, - * send the MG_SIG_CLOSE_CONN signal - */ - if (cs->draining_rx_chain && cs->rx_chain == NULL) { - mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); - } - } - } -#if 0 - DBG(("end poll @%u, %d conns, %d timers (min %u), next in %d ms", - (unsigned int) (now * 1000), n, num_timers, - (unsigned int) (min_timer * 1000), timeout_ms)); -#endif - (void) timeout_ms; - return now; -} - -uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr) { - struct mg_connection *nc; - double now; - double min_timer = 0; - int num_timers = 0; - mg_ev_mgr_lwip_process_signals(mgr); - for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - if (nc->ev_timer_time > 0) { - if (num_timers == 0 || nc->ev_timer_time < min_timer) { - min_timer = nc->ev_timer_time; - } - num_timers++; - } - if (nc->send_mbuf.len > 0 -#if MG_ENABLE_SSL - || (nc->flags & MG_F_WANT_WRITE) -#endif - ) { - int can_send = 0; - /* We have stuff to send, but can we? */ - if (nc->flags & MG_F_UDP) { - /* UDP is always ready for sending. */ - can_send = (cs->pcb.udp != NULL); - } else { - can_send = (cs->pcb.tcp != NULL && cs->pcb.tcp->snd_buf > 0); - } - /* We want and can send, request a poll immediately. */ - if (can_send) return 0; - } - } - uint32_t timeout_ms = ~0; - now = mg_time(); - if (num_timers > 0) { - /* If we have a timer that is past due, do a poll ASAP. */ - if (min_timer < now) return 0; - double timer_timeout_ms = (min_timer - now) * 1000 + 1 /* rounding */; - if (timer_timeout_ms < timeout_ms) { - timeout_ms = timer_timeout_ms; - } - } - return timeout_ms; -} - -#endif /* MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL */ diff --git a/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_net_if.c b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_net_if.c deleted file mode 100644 index 460f8f5..0000000 --- a/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_net_if.c +++ /dev/null @@ -1,779 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#if MG_ENABLE_NET_IF_LWIP_LOW_LEVEL - -#include "common/mg_mem.h" - -#include -#include -#include -#include -#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105 -#include /* For tcp_seg */ -#else -#include -#endif -#include - -#include "common/cs_dbg.h" - -/* - * Newest versions of LWIP have ip_2_ip4, older have ipX_2_ip, - * even older have nothing. - */ -#ifndef ip_2_ip4 -#ifdef ipX_2_ip -#define ip_2_ip4(addr) ipX_2_ip(addr) -#else -#define ip_2_ip4(addr) (addr) -#endif -#endif - -/* - * Depending on whether Mongoose is compiled with ipv6 support, use right - * lwip functions - */ -#if MG_ENABLE_IPV6 -#define TCP_NEW tcp_new_ip6 -#define TCP_BIND tcp_bind_ip6 -#define UDP_BIND udp_bind_ip6 -#define IPADDR_NTOA(x) ip6addr_ntoa((const ip6_addr_t *)(x)) -#define SET_ADDR(dst, src) \ - memcpy((dst)->sin6.sin6_addr.s6_addr, (src)->ip6.addr, \ - sizeof((dst)->sin6.sin6_addr.s6_addr)) -#else -#define TCP_NEW tcp_new -#define TCP_BIND tcp_bind -#define UDP_BIND udp_bind -#define IPADDR_NTOA ipaddr_ntoa -#define SET_ADDR(dst, src) (dst)->sin.sin_addr.s_addr = ip_2_ip4(src)->addr -#endif - -#if NO_SYS -#define tcpip_callback(fn, arg) (fn)(arg) -typedef void (*tcpip_callback_fn)(void *arg); -#endif - -void mg_lwip_ssl_do_hs(struct mg_connection *nc); -void mg_lwip_ssl_send(struct mg_connection *nc); -void mg_lwip_ssl_recv(struct mg_connection *nc); - -void mg_lwip_if_init(struct mg_iface *iface); -void mg_lwip_if_free(struct mg_iface *iface); -void mg_lwip_if_add_conn(struct mg_connection *nc); -void mg_lwip_if_remove_conn(struct mg_connection *nc); -time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms); - -#if defined(RTOS_SDK) || defined(ESP_PLATFORM) -extern void mgos_lock(); -extern void mgos_unlock(); -#else -#define mgos_lock() -#define mgos_unlock() -#endif - -static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p); - -#if LWIP_TCP_KEEPALIVE -void mg_lwip_set_keepalive_params(struct mg_connection *nc, int idle, - int interval, int count) { - if (nc->sock == INVALID_SOCKET || nc->flags & MG_F_UDP) { - return; - } - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - struct tcp_pcb *tpcb = cs->pcb.tcp; - if (idle > 0 && interval > 0 && count > 0) { - tpcb->keep_idle = idle * 1000; - tpcb->keep_intvl = interval * 1000; - tpcb->keep_cnt = count; - tpcb->so_options |= SOF_KEEPALIVE; - } else { - tpcb->so_options &= ~SOF_KEEPALIVE; - } -} -#elif !defined(MG_NO_LWIP_TCP_KEEPALIVE) -#warning LWIP TCP keepalive is disabled. Please consider enabling it. -#endif /* LWIP_TCP_KEEPALIVE */ - -static err_t mg_lwip_tcp_conn_cb(void *arg, struct tcp_pcb *tpcb, err_t err) { - struct mg_connection *nc = (struct mg_connection *) arg; - DBG(("%p connect to %s:%u = %d", nc, IPADDR_NTOA(ipX_2_ip(&tpcb->remote_ip)), - tpcb->remote_port, err)); - if (nc == NULL) { - tcp_abort(tpcb); - return ERR_ARG; - } - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - cs->err = err; -#if LWIP_TCP_KEEPALIVE - if (err == 0) mg_lwip_set_keepalive_params(nc, 60, 10, 6); -#endif - mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); - return ERR_OK; -} - -static void mg_lwip_tcp_error_cb(void *arg, err_t err) { - struct mg_connection *nc = (struct mg_connection *) arg; - DBG(("%p conn error %d", nc, err)); - if (nc == NULL) return; - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - cs->pcb.tcp = NULL; /* Has already been deallocated */ - if (nc->flags & MG_F_CONNECTING) { - cs->err = err; - mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); - } else { - mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); - } -} - -static err_t mg_lwip_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, - struct pbuf *p, err_t err) { - struct mg_connection *nc = (struct mg_connection *) arg; - DBG(("%p %p %u %d", nc, tpcb, (p != NULL ? p->tot_len : 0), err)); - if (p == NULL) { - if (nc != NULL && !(nc->flags & MG_F_CLOSE_IMMEDIATELY)) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - if (cs->rx_chain != NULL) { - /* - * rx_chain still contains non-consumed data, don't close the - * connection - */ - cs->draining_rx_chain = 1; - } else { - mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); - } - } else { - /* Tombstoned connection, do nothing. */ - } - return ERR_OK; - } else if (nc == NULL) { - tcp_abort(tpcb); - return ERR_ARG; - } - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - /* - * If we get a chain of more than one segment at once, we need to bump - * refcount on the subsequent bufs to make them independent. - */ - if (p->next != NULL) { - struct pbuf *q = p->next; - for (; q != NULL; q = q->next) pbuf_ref(q); - } - mgos_lock(); - if (cs->rx_chain == NULL) { - cs->rx_offset = 0; - } else if (pbuf_clen(cs->rx_chain) >= 4) { - /* ESP SDK has a limited pool of 5 pbufs. We must not hog them all or RX - * will be completely blocked. We already have at least 4 in the chain, - * this one is, so we have to make a copy and release this one. */ - struct pbuf *np = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); - if (np != NULL) { - pbuf_copy(np, p); - pbuf_free(p); - p = np; - } - } - mgos_unlock(); - mg_lwip_recv_common(nc, p); - return ERR_OK; -} - -static void mg_lwip_consume_rx_chain_tcp(struct mg_connection *nc) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - if (cs->rx_chain == NULL) return; -#if MG_ENABLE_SSL - if (nc->flags & MG_F_SSL) { - if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { - mg_lwip_ssl_recv(nc); - } else { - mg_lwip_ssl_do_hs(nc); - } - return; - } -#endif - mgos_lock(); - while (cs->rx_chain != NULL && nc->recv_mbuf.len < nc->recv_mbuf_limit) { - struct pbuf *seg = cs->rx_chain; - - size_t seg_len = (seg->len - cs->rx_offset); - size_t buf_avail = (nc->recv_mbuf_limit - nc->recv_mbuf.len); - size_t len = MIN(seg_len, buf_avail); - - char *data = (char *) MG_MALLOC(len); - if (data == NULL) { - mgos_unlock(); - DBG(("OOM")); - return; - } - pbuf_copy_partial(seg, data, len, cs->rx_offset); - cs->rx_offset += len; - if (cs->rx_offset == cs->rx_chain->len) { - cs->rx_chain = pbuf_dechain(cs->rx_chain); - pbuf_free(seg); - cs->rx_offset = 0; - } - mgos_unlock(); - mg_if_recv_tcp_cb(nc, data, len, 1 /* own */); - mgos_lock(); - } - mgos_unlock(); -} - -static void mg_lwip_handle_recv_tcp(struct mg_connection *nc) { - mg_lwip_consume_rx_chain_tcp(nc); - - if (nc->send_mbuf.len > 0) { - mg_lwip_mgr_schedule_poll(nc->mgr); - } -} - -static err_t mg_lwip_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb, - u16_t num_sent) { - struct mg_connection *nc = (struct mg_connection *) arg; - DBG(("%p %p %u %p %p", nc, tpcb, num_sent, tpcb->unsent, tpcb->unacked)); - if (nc == NULL) return ERR_OK; - if ((nc->flags & MG_F_SEND_AND_CLOSE) && !(nc->flags & MG_F_WANT_WRITE) && - nc->send_mbuf.len == 0 && tpcb->unsent == NULL && tpcb->unacked == NULL) { - mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); - } - return ERR_OK; -} - -struct mg_lwip_if_connect_tcp_ctx { - struct mg_connection *nc; - const union socket_address *sa; -}; - -static void mg_lwip_if_connect_tcp_tcpip(void *arg) { - struct mg_lwip_if_connect_tcp_ctx *ctx = - (struct mg_lwip_if_connect_tcp_ctx *) arg; - struct mg_connection *nc = ctx->nc; - const union socket_address *sa = ctx->sa; - - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - struct tcp_pcb *tpcb = TCP_NEW(); - cs->pcb.tcp = tpcb; - ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr; - u16_t port = ntohs(sa->sin.sin_port); - tcp_arg(tpcb, nc); - tcp_err(tpcb, mg_lwip_tcp_error_cb); - tcp_sent(tpcb, mg_lwip_tcp_sent_cb); - tcp_recv(tpcb, mg_lwip_tcp_recv_cb); - cs->err = TCP_BIND(tpcb, IP_ADDR_ANY, 0 /* any port */); - DBG(("%p tcp_bind = %d", nc, cs->err)); - if (cs->err != ERR_OK) { - mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); - return; - } - cs->err = tcp_connect(tpcb, ip, port, mg_lwip_tcp_conn_cb); - DBG(("%p tcp_connect %p = %d", nc, tpcb, cs->err)); - if (cs->err != ERR_OK) { - mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); - return; - } -} - -void mg_lwip_if_connect_tcp(struct mg_connection *nc, - const union socket_address *sa) { - struct mg_lwip_if_connect_tcp_ctx ctx = {.nc = nc, .sa = sa}; - tcpip_callback(mg_lwip_if_connect_tcp_tcpip, &ctx); -} - -/* - * Lwip included in the SDKs for nRF5x chips has different type for the - * callback of `udp_recv()` - */ -#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105 -static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, - const ip_addr_t *addr, u16_t port) -#else -static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, - ip_addr_t *addr, u16_t port) -#endif -{ - struct mg_connection *nc = (struct mg_connection *) arg; - DBG(("%p %s:%u %p %u %u", nc, IPADDR_NTOA(addr), port, p, p->ref, p->len)); - /* Put address in a separate pbuf and tack it onto the packet. */ - struct pbuf *sap = - pbuf_alloc(PBUF_RAW, sizeof(union socket_address), PBUF_RAM); - if (sap == NULL) { - pbuf_free(p); - return; - } - union socket_address *sa = (union socket_address *) sap->payload; -#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105 - sa->sin.sin_addr.s_addr = ip_2_ip4(addr)->addr; -#else - sa->sin.sin_addr.s_addr = addr->addr; -#endif - sa->sin.sin_port = htons(port); - /* Logic in the recv handler requires that there be exactly one data pbuf. */ - p = pbuf_coalesce(p, PBUF_RAW); - pbuf_chain(sap, p); - mg_lwip_recv_common(nc, sap); - (void) pcb; -} - -static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - mgos_lock(); - if (cs->rx_chain == NULL) { - cs->rx_chain = p; - } else { - pbuf_chain(cs->rx_chain, p); - } - if (!cs->recv_pending) { - cs->recv_pending = 1; - mg_lwip_post_signal(MG_SIG_RECV, nc); - } - mgos_unlock(); -} - -static void mg_lwip_handle_recv_udp(struct mg_connection *nc) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - /* - * For UDP, RX chain consists of interleaved address and packet bufs: - * Address pbuf followed by exactly one data pbuf (recv_cb took care of that). - */ - while (cs->rx_chain != NULL) { - struct pbuf *sap = cs->rx_chain; - struct pbuf *p = sap->next; - cs->rx_chain = pbuf_dechain(p); - size_t data_len = p->len; - char *data = (char *) MG_MALLOC(data_len); - if (data != NULL) { - pbuf_copy_partial(p, data, data_len, 0); - pbuf_free(p); - mg_if_recv_udp_cb(nc, data, data_len, - (union socket_address *) sap->payload, sap->len); - pbuf_free(sap); - } else { - pbuf_free(p); - pbuf_free(sap); - } - } -} - -static void mg_lwip_if_connect_udp_tcpip(void *arg) { - struct mg_connection *nc = (struct mg_connection *) arg; - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - struct udp_pcb *upcb = udp_new(); - cs->err = UDP_BIND(upcb, IP_ADDR_ANY, 0 /* any port */); - DBG(("%p udp_bind %p = %d", nc, upcb, cs->err)); - if (cs->err == ERR_OK) { - udp_recv(upcb, mg_lwip_udp_recv_cb, nc); - cs->pcb.udp = upcb; - } else { - udp_remove(upcb); - } - mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); -} - -void mg_lwip_if_connect_udp(struct mg_connection *nc) { - tcpip_callback(mg_lwip_if_connect_udp_tcpip, nc); -} - -void mg_lwip_accept_conn(struct mg_connection *nc, struct tcp_pcb *tpcb) { - union socket_address sa; - SET_ADDR(&sa, &tpcb->remote_ip); - sa.sin.sin_port = htons(tpcb->remote_port); - mg_if_accept_tcp_cb(nc, &sa, sizeof(sa.sin)); -} - -static void tcp_close_tcpip(void *arg) { - tcp_close((struct tcp_pcb *) arg); -} - -void mg_lwip_handle_accept(struct mg_connection *nc) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - if (cs->pcb.tcp == NULL) return; -#if MG_ENABLE_SSL - if (cs->lc->flags & MG_F_SSL) { - if (mg_ssl_if_conn_accept(nc, cs->lc) != MG_SSL_OK) { - LOG(LL_ERROR, ("SSL error")); - tcpip_callback(tcp_close_tcpip, cs->pcb.tcp); - } - } else -#endif - { - mg_lwip_accept_conn(nc, cs->pcb.tcp); - } -} - -static err_t mg_lwip_accept_cb(void *arg, struct tcp_pcb *newtpcb, err_t err) { - struct mg_connection *lc = (struct mg_connection *) arg, *nc; - struct mg_lwip_conn_state *lcs, *cs; - struct tcp_pcb_listen *lpcb; - LOG(LL_DEBUG, - ("%p conn %p from %s:%u", lc, newtpcb, - IPADDR_NTOA(ipX_2_ip(&newtpcb->remote_ip)), newtpcb->remote_port)); - if (lc == NULL) { - tcp_abort(newtpcb); - return ERR_ABRT; - } - lcs = (struct mg_lwip_conn_state *) lc->sock; - lpcb = (struct tcp_pcb_listen *) lcs->pcb.tcp; -#if TCP_LISTEN_BACKLOG - tcp_accepted(lpcb); -#endif - nc = mg_if_accept_new_conn(lc); - if (nc == NULL) { - tcp_abort(newtpcb); - return ERR_ABRT; - } - cs = (struct mg_lwip_conn_state *) nc->sock; - cs->lc = lc; - cs->pcb.tcp = newtpcb; - /* We need to set up callbacks before returning because data may start - * arriving immediately. */ - tcp_arg(newtpcb, nc); - tcp_err(newtpcb, mg_lwip_tcp_error_cb); - tcp_sent(newtpcb, mg_lwip_tcp_sent_cb); - tcp_recv(newtpcb, mg_lwip_tcp_recv_cb); -#if LWIP_TCP_KEEPALIVE - mg_lwip_set_keepalive_params(nc, 60, 10, 6); -#endif - mg_lwip_post_signal(MG_SIG_ACCEPT, nc); - (void) err; - (void) lpcb; - return ERR_OK; -} - -struct mg_lwip_if_listen_ctx { - struct mg_connection *nc; - union socket_address *sa; - int ret; -}; - -static void mg_lwip_if_listen_tcp_tcpip(void *arg) { - struct mg_lwip_if_listen_ctx *ctx = (struct mg_lwip_if_listen_ctx *) arg; - struct mg_connection *nc = ctx->nc; - union socket_address *sa = ctx->sa; - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - struct tcp_pcb *tpcb = TCP_NEW(); - ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr; - u16_t port = ntohs(sa->sin.sin_port); - cs->err = TCP_BIND(tpcb, ip, port); - DBG(("%p tcp_bind(%s:%u) = %d", nc, IPADDR_NTOA(ip), port, cs->err)); - if (cs->err != ERR_OK) { - tcp_close(tpcb); - ctx->ret = -1; - return; - } - tcp_arg(tpcb, nc); - tpcb = tcp_listen(tpcb); - cs->pcb.tcp = tpcb; - tcp_accept(tpcb, mg_lwip_accept_cb); - ctx->ret = 0; -} - -int mg_lwip_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) { - struct mg_lwip_if_listen_ctx ctx = {.nc = nc, .sa = sa}; - tcpip_callback(mg_lwip_if_listen_tcp_tcpip, &ctx); - return ctx.ret; -} - -static void mg_lwip_if_listen_udp_tcpip(void *arg) { - struct mg_lwip_if_listen_ctx *ctx = (struct mg_lwip_if_listen_ctx *) arg; - struct mg_connection *nc = ctx->nc; - union socket_address *sa = ctx->sa; - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - struct udp_pcb *upcb = udp_new(); - ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr; - u16_t port = ntohs(sa->sin.sin_port); - cs->err = UDP_BIND(upcb, ip, port); - DBG(("%p udb_bind(%s:%u) = %d", nc, IPADDR_NTOA(ip), port, cs->err)); - if (cs->err != ERR_OK) { - udp_remove(upcb); - ctx->ret = -1; - } else { - udp_recv(upcb, mg_lwip_udp_recv_cb, nc); - cs->pcb.udp = upcb; - ctx->ret = 0; - } -} - -int mg_lwip_if_listen_udp(struct mg_connection *nc, union socket_address *sa) { - struct mg_lwip_if_listen_ctx ctx = {.nc = nc, .sa = sa}; - tcpip_callback(mg_lwip_if_listen_udp_tcpip, &ctx); - return ctx.ret; -} - -struct mg_lwip_tcp_write_ctx { - struct mg_connection *nc; - const void *data; - uint16_t len; - int ret; -}; - -static void tcp_output_tcpip(void *arg) { - tcp_output((struct tcp_pcb *) arg); -} - -static void mg_lwip_tcp_write_tcpip(void *arg) { - struct mg_lwip_tcp_write_ctx *ctx = (struct mg_lwip_tcp_write_ctx *) arg; - struct mg_connection *nc = ctx->nc; - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - struct tcp_pcb *tpcb = cs->pcb.tcp; - size_t len = MIN(tpcb->mss, MIN(ctx->len, tpcb->snd_buf)); - size_t unsent, unacked; - if (len == 0) { - DBG(("%p no buf avail %u %u %p %p", tpcb, tpcb->snd_buf, tpcb->snd_queuelen, - tpcb->unsent, tpcb->unacked)); - tcpip_callback(tcp_output_tcpip, tpcb); - ctx->ret = 0; - return; - } - unsent = (tpcb->unsent != NULL ? tpcb->unsent->len : 0); - unacked = (tpcb->unacked != NULL ? tpcb->unacked->len : 0); -/* - * On ESP8266 we only allow one TCP segment in flight at any given time. - * This may increase latency and reduce efficiency of tcp windowing, - * but memory is scarce and precious on that platform so we do this to - * reduce footprint. - */ -#if CS_PLATFORM == CS_P_ESP8266 - if (unacked > 0) { - ctx->ret = 0; - return; - } - len = MIN(len, (TCP_MSS - unsent)); -#endif - cs->err = tcp_write(tpcb, ctx->data, len, TCP_WRITE_FLAG_COPY); - unsent = (tpcb->unsent != NULL ? tpcb->unsent->len : 0); - unacked = (tpcb->unacked != NULL ? tpcb->unacked->len : 0); - DBG(("%p tcp_write %u = %d, %u %u", tpcb, len, cs->err, unsent, unacked)); - if (cs->err != ERR_OK) { - /* - * We ignore ERR_MEM because memory will be freed up when the data is sent - * and we'll retry. - */ - ctx->ret = (cs->err == ERR_MEM ? 0 : -1); - return; - } - ctx->ret = len; -} - -static int mg_lwip_tcp_write(struct mg_connection *nc, const void *data, - uint16_t len) { - struct mg_lwip_tcp_write_ctx ctx = {.nc = nc, .data = data, .len = len}; - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - struct tcp_pcb *tpcb = cs->pcb.tcp; - if (tpcb == NULL) { - return -1; - } - tcpip_callback(mg_lwip_tcp_write_tcpip, &ctx); - return ctx.ret; -} - -struct udp_sendto_ctx { - struct udp_pcb *upcb; - struct pbuf *p; - ip_addr_t *ip; - uint16_t port; - int ret; -}; - -static void udp_sendto_tcpip(void *arg) { - struct udp_sendto_ctx *ctx = (struct udp_sendto_ctx *) arg; - ctx->ret = udp_sendto(ctx->upcb, ctx->p, ctx->ip, ctx->port); -} - -static int mg_lwip_udp_send(struct mg_connection *nc, const void *data, - uint16_t len) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - if (cs->pcb.udp == NULL) { - /* - * In case of UDP, this usually means, what - * async DNS resolve is still in progress and connection - * is not ready yet - */ - DBG(("%p socket is not connected", nc)); - return -1; - } - struct udp_pcb *upcb = cs->pcb.udp; - struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); -#if defined(LWIP_IPV4) && LWIP_IPV4 && defined(LWIP_IPV6) && LWIP_IPV6 - ip_addr_t ip = {.u_addr.ip4.addr = nc->sa.sin.sin_addr.s_addr, .type = 0}; -#else - ip_addr_t ip = {.addr = nc->sa.sin.sin_addr.s_addr}; -#endif - u16_t port = ntohs(nc->sa.sin.sin_port); - if (p == NULL) { - DBG(("OOM")); - return 0; - } - memcpy(p->payload, data, len); - struct udp_sendto_ctx ctx = {.upcb = upcb, .p = p, .ip = &ip, .port = port}; - tcpip_callback(udp_sendto_tcpip, &ctx); - cs->err = ctx.ret; - pbuf_free(p); - return (cs->err == ERR_OK ? len : -1); -} - -static void mg_lwip_send_more(struct mg_connection *nc) { - int num_sent = 0; - if (nc->sock == INVALID_SOCKET) return; - if (nc->flags & MG_F_UDP) { - num_sent = mg_lwip_udp_send(nc, nc->send_mbuf.buf, nc->send_mbuf.len); - DBG(("%p mg_lwip_udp_send %u = %d", nc, nc->send_mbuf.len, num_sent)); - } else { - num_sent = mg_lwip_tcp_write(nc, nc->send_mbuf.buf, nc->send_mbuf.len); - DBG(("%p mg_lwip_tcp_write %u = %d", nc, nc->send_mbuf.len, num_sent)); - } - if (num_sent == 0) return; - if (num_sent > 0) { - mg_if_sent_cb(nc, num_sent); - } else { - mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); - } -} - -void mg_lwip_if_tcp_send(struct mg_connection *nc, const void *buf, - size_t len) { - mbuf_append(&nc->send_mbuf, buf, len); - mg_lwip_mgr_schedule_poll(nc->mgr); -} - -void mg_lwip_if_udp_send(struct mg_connection *nc, const void *buf, - size_t len) { - mbuf_append(&nc->send_mbuf, buf, len); - mg_lwip_mgr_schedule_poll(nc->mgr); -} - -struct tcp_recved_ctx { - struct tcp_pcb *tpcb; - size_t len; -}; - -void tcp_recved_tcpip(void *arg) { - struct tcp_recved_ctx *ctx = (struct tcp_recved_ctx *) arg; - tcp_recved(ctx->tpcb, ctx->len); -} - -void mg_lwip_if_recved(struct mg_connection *nc, size_t len) { - if (nc->flags & MG_F_UDP) return; - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - if (nc->sock == INVALID_SOCKET || cs->pcb.tcp == NULL) { - DBG(("%p invalid socket", nc)); - return; - } - DBG(("%p %p %u %u", nc, cs->pcb.tcp, len, - (cs->rx_chain ? cs->rx_chain->tot_len : 0))); - struct tcp_recved_ctx ctx = {.tpcb = cs->pcb.tcp, .len = len}; -#if MG_ENABLE_SSL - if (!(nc->flags & MG_F_SSL)) { - tcpip_callback(tcp_recved_tcpip, &ctx); - } else { - /* Currently SSL acknowledges data immediately. - * TODO(rojer): Find a way to propagate mg_lwip_if_recved. */ - } -#else - tcpip_callback(tcp_recved_tcpip, &ctx); -#endif - mbuf_trim(&nc->recv_mbuf); -} - -int mg_lwip_if_create_conn(struct mg_connection *nc) { - struct mg_lwip_conn_state *cs = - (struct mg_lwip_conn_state *) MG_CALLOC(1, sizeof(*cs)); - if (cs == NULL) return 0; - cs->nc = nc; - nc->sock = (intptr_t) cs; - return 1; -} - -static void udp_remove_tcpip(void *arg) { - udp_remove((struct udp_pcb *) arg); -} - -void mg_lwip_if_destroy_conn(struct mg_connection *nc) { - if (nc->sock == INVALID_SOCKET) return; - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - if (!(nc->flags & MG_F_UDP)) { - struct tcp_pcb *tpcb = cs->pcb.tcp; - if (tpcb != NULL) { - tcp_arg(tpcb, NULL); - DBG(("%p tcp_close %p", nc, tpcb)); - tcp_arg(tpcb, NULL); - tcpip_callback(tcp_close_tcpip, tpcb); - } - while (cs->rx_chain != NULL) { - struct pbuf *seg = cs->rx_chain; - cs->rx_chain = pbuf_dechain(cs->rx_chain); - pbuf_free(seg); - } - memset(cs, 0, sizeof(*cs)); - MG_FREE(cs); - } else if (nc->listener == NULL) { - /* Only close outgoing UDP pcb or listeners. */ - struct udp_pcb *upcb = cs->pcb.udp; - if (upcb != NULL) { - DBG(("%p udp_remove %p", nc, upcb)); - tcpip_callback(udp_remove_tcpip, upcb); - } - memset(cs, 0, sizeof(*cs)); - MG_FREE(cs); - } - nc->sock = INVALID_SOCKET; -} - -void mg_lwip_if_get_conn_addr(struct mg_connection *nc, int remote, - union socket_address *sa) { - memset(sa, 0, sizeof(*sa)); - if (nc == NULL || nc->sock == INVALID_SOCKET) return; - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - if (nc->flags & MG_F_UDP) { - struct udp_pcb *upcb = cs->pcb.udp; - if (remote) { - memcpy(sa, &nc->sa, sizeof(*sa)); - } else if (upcb != NULL) { - sa->sin.sin_port = htons(upcb->local_port); - SET_ADDR(sa, &upcb->local_ip); - } - } else { - struct tcp_pcb *tpcb = cs->pcb.tcp; - if (remote) { - memcpy(sa, &nc->sa, sizeof(*sa)); - } else if (tpcb != NULL) { - sa->sin.sin_port = htons(tpcb->local_port); - SET_ADDR(sa, &tpcb->local_ip); - } - } -} - -void mg_lwip_if_sock_set(struct mg_connection *nc, sock_t sock) { - nc->sock = sock; -} - -/* clang-format off */ -#define MG_LWIP_IFACE_VTABLE \ - { \ - mg_lwip_if_init, \ - mg_lwip_if_free, \ - mg_lwip_if_add_conn, \ - mg_lwip_if_remove_conn, \ - mg_lwip_if_poll, \ - mg_lwip_if_listen_tcp, \ - mg_lwip_if_listen_udp, \ - mg_lwip_if_connect_tcp, \ - mg_lwip_if_connect_udp, \ - mg_lwip_if_tcp_send, \ - mg_lwip_if_udp_send, \ - mg_lwip_if_recved, \ - mg_lwip_if_create_conn, \ - mg_lwip_if_destroy_conn, \ - mg_lwip_if_sock_set, \ - mg_lwip_if_get_conn_addr, \ - } -/* clang-format on */ - -const struct mg_iface_vtable mg_lwip_iface_vtable = MG_LWIP_IFACE_VTABLE; -#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL -const struct mg_iface_vtable mg_default_iface_vtable = MG_LWIP_IFACE_VTABLE; -#endif - -#endif /* MG_ENABLE_NET_IF_LWIP_LOW_LEVEL */ diff --git a/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_net_if.h b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_net_if.h deleted file mode 100644 index d00fd2f..0000000 --- a/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_net_if.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_LWIP_MG_NET_IF_LWIP_H_ -#define CS_COMMON_PLATFORMS_LWIP_MG_NET_IF_LWIP_H_ - -#ifndef MG_ENABLE_NET_IF_LWIP_LOW_LEVEL -#define MG_ENABLE_NET_IF_LWIP_LOW_LEVEL MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL -#endif - -#if MG_ENABLE_NET_IF_LWIP_LOW_LEVEL - -#include - -extern const struct mg_iface_vtable mg_lwip_iface_vtable; - -struct mg_lwip_conn_state { - struct mg_connection *nc; - struct mg_connection *lc; - union { - struct tcp_pcb *tcp; - struct udp_pcb *udp; - } pcb; - err_t err; - size_t num_sent; /* Number of acknowledged bytes to be reported to the core */ - struct pbuf *rx_chain; /* Chain of incoming data segments. */ - size_t rx_offset; /* Offset within the first pbuf (if partially consumed) */ - /* Last SSL write size, for retries. */ - int last_ssl_write_size; - /* Whether MG_SIG_RECV is already pending for this connection */ - int recv_pending : 1; - /* Whether the connection is about to close, just `rx_chain` needs to drain */ - int draining_rx_chain : 1; -}; - -enum mg_sig_type { - MG_SIG_CONNECT_RESULT = 1, - MG_SIG_RECV = 2, - MG_SIG_CLOSE_CONN = 3, - MG_SIG_TOMBSTONE = 4, - MG_SIG_ACCEPT = 5, -}; - -void mg_lwip_post_signal(enum mg_sig_type sig, struct mg_connection *nc); - -/* To be implemented by the platform. */ -void mg_lwip_mgr_schedule_poll(struct mg_mgr *mgr); - -#endif /* MG_ENABLE_NET_IF_LWIP_LOW_LEVEL */ - -#endif /* CS_COMMON_PLATFORMS_LWIP_MG_NET_IF_LWIP_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_ssl_if.c b/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_ssl_if.c deleted file mode 100644 index 04ed778..0000000 --- a/src/mongoose-6.11/src/common/platforms/lwip/mg_lwip_ssl_if.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#if MG_ENABLE_SSL && MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL - -#include "common/mg_mem.h" -#include "common/cs_dbg.h" - -#include -#include - -#ifndef MG_LWIP_SSL_IO_SIZE -#define MG_LWIP_SSL_IO_SIZE 1024 -#endif - -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#endif - -void mg_lwip_ssl_do_hs(struct mg_connection *nc) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - int server_side = (nc->listener != NULL); - enum mg_ssl_if_result res; - if (nc->flags & MG_F_CLOSE_IMMEDIATELY) return; - res = mg_ssl_if_handshake(nc); - DBG(("%p %lu %d %d", nc, nc->flags, server_side, res)); - if (res != MG_SSL_OK) { - if (res == MG_SSL_WANT_WRITE) { - nc->flags |= MG_F_WANT_WRITE; - cs->err = 0; - } else if (res == MG_SSL_WANT_READ) { - /* - * Nothing to do in particular, we are callback-driven. - * What we definitely do not need anymore is SSL reading (nothing left). - */ - nc->flags &= ~MG_F_WANT_READ; - cs->err = 0; - } else { - cs->err = res; - if (server_side) { - mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); - } else { - mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); - } - } - } else { - cs->err = 0; - nc->flags &= ~MG_F_WANT_WRITE; - /* - * Handshake is done. Schedule a read immediately to consume app data - * which may already be waiting. - */ - nc->flags |= (MG_F_SSL_HANDSHAKE_DONE | MG_F_WANT_READ); - if (server_side) { - mg_lwip_accept_conn(nc, cs->pcb.tcp); - } else { - mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); - } - } -} - -void mg_lwip_ssl_send(struct mg_connection *nc) { - if (nc->sock == INVALID_SOCKET) { - DBG(("%p invalid socket", nc)); - return; - } - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - /* It's ok if the buffer is empty. Return value of 0 may also be valid. */ - int len = cs->last_ssl_write_size; - if (len == 0) { - len = MIN(MG_LWIP_SSL_IO_SIZE, nc->send_mbuf.len); - } - int ret = mg_ssl_if_write(nc, nc->send_mbuf.buf, len); - DBG(("%p SSL_write %u = %d", nc, len, ret)); - if (ret > 0) { - mg_if_sent_cb(nc, ret); - cs->last_ssl_write_size = 0; - } else if (ret < 0) { - /* This is tricky. We must remember the exact data we were sending to retry - * exactly the same send next time. */ - cs->last_ssl_write_size = len; - } - if (ret == len) { - nc->flags &= ~MG_F_WANT_WRITE; - } else if (ret == MG_SSL_WANT_WRITE) { - nc->flags |= MG_F_WANT_WRITE; - } else { - mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); - } -} - -void mg_lwip_ssl_recv(struct mg_connection *nc) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - /* Don't deliver data before connect callback */ - if (nc->flags & MG_F_CONNECTING) return; - while (nc->recv_mbuf.len < nc->recv_mbuf_limit) { - char *buf = (char *) MG_MALLOC(MG_LWIP_SSL_IO_SIZE); - if (buf == NULL) return; - int ret = mg_ssl_if_read(nc, buf, MG_LWIP_SSL_IO_SIZE); - DBG(("%p %p SSL_read %u = %d", nc, cs->rx_chain, MG_LWIP_SSL_IO_SIZE, ret)); - if (ret <= 0) { - MG_FREE(buf); - if (ret == MG_SSL_WANT_WRITE) { - nc->flags |= MG_F_WANT_WRITE; - return; - } else if (ret == MG_SSL_WANT_READ) { - /* - * Nothing to do in particular, we are callback-driven. - * What we definitely do not need anymore is SSL reading (nothing left). - */ - nc->flags &= ~MG_F_WANT_READ; - cs->err = 0; - return; - } else { - mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); - return; - } - } else { - mg_if_recv_tcp_cb(nc, buf, ret, 1 /* own */); - } - } -} - -#ifdef KR_VERSION - -ssize_t kr_send(int fd, const void *buf, size_t len) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) fd; - int ret = mg_lwip_tcp_write(cs->nc, buf, len); - DBG(("%p mg_lwip_tcp_write %u = %d", cs->nc, len, ret)); - if (ret == 0) ret = KR_IO_WOULDBLOCK; - return ret; -} - -ssize_t kr_recv(int fd, void *buf, size_t len) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) fd; - struct pbuf *seg = cs->rx_chain; - if (seg == NULL) { - DBG(("%u - nothing to read", len)); - return KR_IO_WOULDBLOCK; - } - size_t seg_len = (seg->len - cs->rx_offset); - DBG(("%u %u %u %u", len, cs->rx_chain->len, seg_len, cs->rx_chain->tot_len)); - len = MIN(len, seg_len); - pbuf_copy_partial(seg, buf, len, cs->rx_offset); - cs->rx_offset += len; - tcp_recved(cs->pcb.tcp, len); - if (cs->rx_offset == cs->rx_chain->len) { - cs->rx_chain = pbuf_dechain(cs->rx_chain); - pbuf_free(seg); - cs->rx_offset = 0; - } - return len; -} - -#elif MG_SSL_IF == MG_SSL_IF_MBEDTLS - -int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len) { - struct mg_connection *nc = (struct mg_connection *) ctx; - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - int ret = mg_lwip_tcp_write(cs->nc, buf, len); - if (ret == 0) ret = MBEDTLS_ERR_SSL_WANT_WRITE; - LOG(LL_DEBUG, ("%p %d -> %d", nc, len, ret)); - return ret; -} - -int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len) { - struct mg_connection *nc = (struct mg_connection *) ctx; - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - struct pbuf *seg = cs->rx_chain; - if (seg == NULL) { - DBG(("%u - nothing to read", len)); - return MBEDTLS_ERR_SSL_WANT_READ; - } - size_t seg_len = (seg->len - cs->rx_offset); - DBG(("%u %u %u %u", len, cs->rx_chain->len, seg_len, cs->rx_chain->tot_len)); - mgos_lock(); - len = MIN(len, seg_len); - pbuf_copy_partial(seg, buf, len, cs->rx_offset); - cs->rx_offset += len; - /* TCP PCB may be NULL if connection has already been closed - * but we still have data to deliver to SSL. */ - if (cs->pcb.tcp != NULL) tcp_recved(cs->pcb.tcp, len); - if (cs->rx_offset == cs->rx_chain->len) { - cs->rx_chain = pbuf_dechain(cs->rx_chain); - pbuf_free(seg); - cs->rx_offset = 0; - } - mgos_unlock(); - LOG(LL_DEBUG, ("%p <- %d", nc, (int) len)); - return len; -} - -#endif - -#endif /* MG_ENABLE_SSL && MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL */ diff --git a/src/mongoose-6.11/src/common/platforms/mbed/mbed_libc.c b/src/mongoose-6.11/src/common/platforms/mbed/mbed_libc.c deleted file mode 100644 index a5b35b1..0000000 --- a/src/mongoose-6.11/src/common/platforms/mbed/mbed_libc.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#include "common/platform.h" - -#if CS_PLATFORM == CS_P_MBED - -long timezone; - -/* - * The GCC ARM toolchain for implements a weak - * gettimeofday stub that should be implemented - * to hook the OS time source. But mbed OS doesn't do it; - * the mbed doc only talks about C date and time functions: - * - * https://docs.mbed.com/docs/mbed-os-api-reference/en/5.1/APIs/tasks/Time/ - * - * gettimeof day is a BSD API. - */ -int _gettimeofday(struct timeval *tv, void *tzvp) { - tv->tv_sec = time(NULL); - tv->tv_usec = 0; - return 0; -} - -int inet_aton(const char *cp, struct in_addr *inp) { - /* We don't have aton, but have pton in mbed */ - return inet_pton(AF_INET, cp, inp); -} - -in_addr_t inet_addr(const char *cp) { - in_addr_t ret; - if (inet_pton(AF_INET, cp, &ret) != 1) { - return 0; - } - - return ret; -} - -#endif /* CS_PLATFORM == CS_P_MBED */ diff --git a/src/mongoose-6.11/src/common/platforms/msp432/msp432_libc.c b/src/mongoose-6.11/src/common/platforms/msp432/msp432_libc.c deleted file mode 100644 index faedadc..0000000 --- a/src/mongoose-6.11/src/common/platforms/msp432/msp432_libc.c +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#if CS_PLATFORM == CS_P_MSP432 - -#include -#include - -int gettimeofday(struct timeval *tp, void *tzp) { - uint32_t ticks = Clock_getTicks(); - tp->tv_sec = ticks / 1000; - tp->tv_usec = (ticks % 1000) * 1000; - return 0; -} - -#endif /* CS_PLATFORM == CS_P_MSP432 */ diff --git a/src/mongoose-6.11/src/common/platforms/nrf5/nrf5_libc.c b/src/mongoose-6.11/src/common/platforms/nrf5/nrf5_libc.c deleted file mode 100644 index 9cf66dc..0000000 --- a/src/mongoose-6.11/src/common/platforms/nrf5/nrf5_libc.c +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#if (CS_PLATFORM == CS_P_NRF51 || CS_PLATFORM == CS_P_NRF52) && \ - defined(__ARMCC_VERSION) -int gettimeofday(struct timeval *tp, void *tzp) { - /* TODO */ - tp->tv_sec = 0; - tp->tv_usec = 0; - return 0; -} -#endif diff --git a/src/mongoose-6.11/src/common/platforms/pic32/pic32_net_if.c b/src/mongoose-6.11/src/common/platforms/pic32/pic32_net_if.c deleted file mode 100644 index d0e67e9..0000000 --- a/src/mongoose-6.11/src/common/platforms/pic32/pic32_net_if.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#if MG_ENABLE_NET_IF_PIC32 - -int mg_pic32_if_create_conn(struct mg_connection *nc) { - (void) nc; - return 1; -} - -void mg_pic32_if_recved(struct mg_connection *nc, size_t len) { - (void) nc; - (void) len; -} - -void mg_pic32_if_add_conn(struct mg_connection *nc) { - (void) nc; -} - -void mg_pic32_if_init(struct mg_iface *iface) { - (void) iface; - (void) mg_get_errno(); /* Shutup compiler */ -} - -void mg_pic32_if_free(struct mg_iface *iface) { - (void) iface; -} - -void mg_pic32_if_remove_conn(struct mg_connection *nc) { - (void) nc; -} - -void mg_pic32_if_destroy_conn(struct mg_connection *nc) { - if (nc->sock == INVALID_SOCKET) return; - /* For UDP, only close outgoing sockets or listeners. */ - if (!(nc->flags & MG_F_UDP)) { - /* Close TCP */ - TCPIP_TCP_Close((TCP_SOCKET) nc->sock); - } else if (nc->listener == NULL) { - /* Only close outgoing UDP or listeners. */ - TCPIP_UDP_Close((UDP_SOCKET) nc->sock); - } - - nc->sock = INVALID_SOCKET; -} - -int mg_pic32_if_listen_udp(struct mg_connection *nc, union socket_address *sa) { - nc->sock = TCPIP_UDP_ServerOpen( - sa->sin.sin_family == AF_INET ? IP_ADDRESS_TYPE_IPV4 - : IP_ADDRESS_TYPE_IPV6, - ntohs(sa->sin.sin_port), - sa->sin.sin_addr.s_addr == 0 ? 0 : (IP_MULTI_ADDRESS *) &sa->sin); - if (nc->sock == INVALID_SOCKET) { - return -1; - } - return 0; -} - -void mg_pic32_if_udp_send(struct mg_connection *nc, const void *buf, - size_t len) { - mbuf_append(&nc->send_mbuf, buf, len); -} - -void mg_pic32_if_tcp_send(struct mg_connection *nc, const void *buf, - size_t len) { - mbuf_append(&nc->send_mbuf, buf, len); -} - -int mg_pic32_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) { - nc->sock = TCPIP_TCP_ServerOpen( - sa->sin.sin_family == AF_INET ? IP_ADDRESS_TYPE_IPV4 - : IP_ADDRESS_TYPE_IPV6, - ntohs(sa->sin.sin_port), - sa->sin.sin_addr.s_addr == 0 ? 0 : (IP_MULTI_ADDRESS *) &sa->sin); - memcpy(&nc->sa, sa, sizeof(*sa)); - if (nc->sock == INVALID_SOCKET) { - return -1; - } - return 0; -} - -static int mg_accept_conn(struct mg_connection *lc) { - struct mg_connection *nc; - TCP_SOCKET_INFO si; - union socket_address sa; - - nc = mg_if_accept_new_conn(lc); - - if (nc == NULL) { - return 0; - } - - nc->sock = lc->sock; - nc->flags &= ~MG_F_LISTENING; - - if (!TCPIP_TCP_SocketInfoGet((TCP_SOCKET) nc->sock, &si)) { - return 0; - } - - if (si.addressType == IP_ADDRESS_TYPE_IPV4) { - sa.sin.sin_family = AF_INET; - sa.sin.sin_port = htons(si.remotePort); - sa.sin.sin_addr.s_addr = si.remoteIPaddress.v4Add.Val; - } else { - /* TODO(alashkin): do something with _potential_ IPv6 */ - memset(&sa, 0, sizeof(sa)); - } - - mg_if_accept_tcp_cb(nc, (union socket_address *) &sa, sizeof(sa)); - - return mg_pic32_if_listen_tcp(lc, &lc->sa) >= 0; -} - -char *inet_ntoa(struct in_addr in) { - static char addr[17]; - snprintf(addr, sizeof(addr), "%d.%d.%d.%d", (int) in.S_un.S_un_b.s_b1, - (int) in.S_un.S_un_b.s_b2, (int) in.S_un.S_un_b.s_b3, - (int) in.S_un.S_un_b.s_b4); - return addr; -} - -static void mg_handle_send(struct mg_connection *nc) { - uint16_t bytes_written = 0; - if (nc->flags & MG_F_UDP) { - if (!TCPIP_UDP_RemoteBind( - (UDP_SOCKET) nc->sock, - nc->sa.sin.sin_family == AF_INET ? IP_ADDRESS_TYPE_IPV4 - : IP_ADDRESS_TYPE_IPV6, - ntohs(nc->sa.sin.sin_port), (IP_MULTI_ADDRESS *) &nc->sa.sin)) { - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - return; - } - bytes_written = TCPIP_UDP_TxPutIsReady((UDP_SOCKET) nc->sock, 0); - if (bytes_written >= nc->send_mbuf.len) { - if (TCPIP_UDP_ArrayPut((UDP_SOCKET) nc->sock, - (uint8_t *) nc->send_mbuf.buf, - nc->send_mbuf.len) != nc->send_mbuf.len) { - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - bytes_written = 0; - } - } - } else { - bytes_written = TCPIP_TCP_FifoTxFreeGet((TCP_SOCKET) nc->sock); - if (bytes_written != 0) { - if (bytes_written > nc->send_mbuf.len) { - bytes_written = nc->send_mbuf.len; - } - if (TCPIP_TCP_ArrayPut((TCP_SOCKET) nc->sock, - (uint8_t *) nc->send_mbuf.buf, - bytes_written) != bytes_written) { - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - bytes_written = 0; - } - } - } - - mg_if_sent_cb(nc, bytes_written); -} - -static void mg_handle_recv(struct mg_connection *nc) { - uint16_t bytes_read = 0; - uint8_t *buf = NULL; - if (nc->flags & MG_F_UDP) { - bytes_read = TCPIP_UDP_GetIsReady((UDP_SOCKET) nc->sock); - if (bytes_read != 0 && - (nc->recv_mbuf_limit == -1 || - nc->recv_mbuf.len + bytes_read < nc->recv_mbuf_limit)) { - buf = (uint8_t *) MG_MALLOC(bytes_read); - if (TCPIP_UDP_ArrayGet((UDP_SOCKET) nc->sock, buf, bytes_read) != - bytes_read) { - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - bytes_read = 0; - MG_FREE(buf); - } - } - } else { - bytes_read = TCPIP_TCP_GetIsReady((TCP_SOCKET) nc->sock); - if (bytes_read != 0) { - if (nc->recv_mbuf_limit != -1 && - nc->recv_mbuf_limit - nc->recv_mbuf.len > bytes_read) { - bytes_read = nc->recv_mbuf_limit - nc->recv_mbuf.len; - } - buf = (uint8_t *) MG_MALLOC(bytes_read); - if (TCPIP_TCP_ArrayGet((TCP_SOCKET) nc->sock, buf, bytes_read) != - bytes_read) { - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - MG_FREE(buf); - bytes_read = 0; - } - } - } - - if (bytes_read != 0) { - mg_if_recv_tcp_cb(nc, buf, bytes_read, 1 /* own */); - } -} - -time_t mg_pic32_if_poll(struct mg_iface *iface, int timeout_ms) { - struct mg_mgr *mgr = iface->mgr; - double now = mg_time(); - struct mg_connection *nc, *tmp; - - for (nc = mgr->active_connections; nc != NULL; nc = tmp) { - tmp = nc->next; - - if (nc->flags & MG_F_CONNECTING) { - /* processing connections */ - if (nc->flags & MG_F_UDP || - TCPIP_TCP_IsConnected((TCP_SOCKET) nc->sock)) { - mg_if_connect_cb(nc, 0); - } - } else if (nc->flags & MG_F_LISTENING) { - if (TCPIP_TCP_IsConnected((TCP_SOCKET) nc->sock)) { - /* accept new connections */ - mg_accept_conn(nc); - } - } else { - if (nc->send_mbuf.len != 0) { - mg_handle_send(nc); - } - - if (nc->recv_mbuf_limit == -1 || - nc->recv_mbuf.len < nc->recv_mbuf_limit) { - mg_handle_recv(nc); - } - } - } - - for (nc = mgr->active_connections; nc != NULL; nc = tmp) { - tmp = nc->next; - if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || - (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) { - mg_close_conn(nc); - } - } - - return now; -} - -void mg_pic32_if_sock_set(struct mg_connection *nc, sock_t sock) { - nc->sock = sock; -} - -void mg_pic32_if_get_conn_addr(struct mg_connection *nc, int remote, - union socket_address *sa) { - /* TODO(alaskin): not implemented yet */ -} - -void mg_pic32_if_connect_tcp(struct mg_connection *nc, - const union socket_address *sa) { - nc->sock = TCPIP_TCP_ClientOpen( - sa->sin.sin_family == AF_INET ? IP_ADDRESS_TYPE_IPV4 - : IP_ADDRESS_TYPE_IPV6, - ntohs(sa->sin.sin_port), (IP_MULTI_ADDRESS *) &sa->sin); - nc->err = (nc->sock == INVALID_SOCKET) ? -1 : 0; -} - -void mg_pic32_if_connect_udp(struct mg_connection *nc) { - nc->sock = TCPIP_UDP_ClientOpen(IP_ADDRESS_TYPE_ANY, 0, NULL); - nc->err = (nc->sock == INVALID_SOCKET) ? -1 : 0; -} - -/* clang-format off */ -#define MG_PIC32_IFACE_VTABLE \ - { \ - mg_pic32_if_init, \ - mg_pic32_if_free, \ - mg_pic32_if_add_conn, \ - mg_pic32_if_remove_conn, \ - mg_pic32_if_poll, \ - mg_pic32_if_listen_tcp, \ - mg_pic32_if_listen_udp, \ - mg_pic32_if_connect_tcp, \ - mg_pic32_if_connect_udp, \ - mg_pic32_if_tcp_send, \ - mg_pic32_if_udp_send, \ - mg_pic32_if_recved, \ - mg_pic32_if_create_conn, \ - mg_pic32_if_destroy_conn, \ - mg_pic32_if_sock_set, \ - mg_pic32_if_get_conn_addr, \ - } -/* clang-format on */ - -const struct mg_iface_vtable mg_pic32_iface_vtable = MG_PIC32_IFACE_VTABLE; -#if MG_NET_IF == MG_NET_IF_PIC32 -const struct mg_iface_vtable mg_default_iface_vtable = MG_PIC32_IFACE_VTABLE; -#endif - -#endif /* MG_ENABLE_NET_IF_PIC32 */ diff --git a/src/mongoose-6.11/src/common/platforms/pic32/pic32_net_if.h b/src/mongoose-6.11/src/common/platforms/pic32/pic32_net_if.h deleted file mode 100644 index 2bf66ef..0000000 --- a/src/mongoose-6.11/src/common/platforms/pic32/pic32_net_if.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_PIC32_NET_IF_H_ -#define CS_COMMON_PLATFORMS_PIC32_NET_IF_H_ - -#include "mongoose/src/net_if.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifndef MG_ENABLE_NET_IF_PIC32 -#define MG_ENABLE_NET_IF_PIC32 MG_NET_IF == MG_NET_IF_PIC32 -#endif - -extern const struct mg_iface_vtable mg_pic32_iface_vtable; - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* CS_COMMON_PLATFORMS_PIC32_NET_IF_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/pic32/xc32.mk b/src/mongoose-6.11/src/common/platforms/pic32/xc32.mk deleted file mode 100644 index aff6b4e..0000000 --- a/src/mongoose-6.11/src/common/platforms/pic32/xc32.mk +++ /dev/null @@ -1,51 +0,0 @@ -APP_LDFLAGS ?= -CC_WRAPPER ?= -GENFILES_LIST ?= -CC = $(XC32_PATH)/bin/xc32-gcc -AR = $(XC32_PATH)/bin/xc32-ar -LD = $(XC32_PATH)/bin/xc32-gcc -BIN2HEX = $(XC32_PATH)/bin/xc32-bin2hex - -CFLAGS = -fframe-base-loclist -mprocessor=32MZ2048EFM064 \ - -ffunction-sections -Wall -g -no-legacy-libc \ - -O1 - -CFLAGS += -DCS_ENABLE_DEBUG - -LDFLAGS = -mprocessor=32MZ2048EFM064 - -# TODO(dfrank) enable that conditionally -CFLAGS += -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -LDFLAGS += -mdebugger -D__MPLAB_DEBUGGER_ICD3=1 - -OBJCOPY = $(XC32_PATH)/bin/xc32-objcopy - -# cc flags,file -define cc - $(vecho) "XC32 $2 -> $@" - $(Q) $(CC_WRAPPER) $(CC) -c $1 -o $@ $2 -endef - -# cc flags,file -define asm - $(vecho) "XC32 ASM $2 -> $@" - $(Q) $(CC) -c $1 -o $@ $2 -endef - -# ar files -define ar - $(vecho) "AR $@" - $(Q) $(AR) cru $@ $1 -endef - -# link script,flags,objs -define link - $(vecho) "LD $@ (ignoring linker script $1)" - $(Q) $(CC_WRAPPER) $(LD) -o $@ $2 $3 -no-legacy-libc \ - -Wl,--defsym=__MPLAB_BUILD=1,--defsym=__MPLAB_DEBUG=1,--defsym=__DEBUG=1,--defsym=__MPLAB_DEBUGGER_ICD3=1,--defsym=_min_heap_size=44960,--gc-sections -endef - -define bin2hex - $(vecho) "HEX $1 -> $@" - $(Q) $(BIN2HEX) $1 -endef diff --git a/src/mongoose-6.11/src/common/platforms/platform_cc3100.h b/src/mongoose-6.11/src/common/platforms/platform_cc3100.h deleted file mode 100644 index ba07f22..0000000 --- a/src/mongoose-6.11/src/common/platforms/platform_cc3100.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_ -#define CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_ -#if CS_PLATFORM == CS_P_CC3100 - -#include -#include -#include -#include -#include -#include -#include - -#define MG_NET_IF MG_NET_IF_SIMPLELINK -#define MG_SSL_IF MG_SSL_IF_SIMPLELINK - -/* - * CC3100 SDK and STM32 SDK include headers w/out path, just like - * #include "simplelink.h". As result, we have to add all required directories - * into Makefile IPATH and do the same thing (include w/out path) - */ - -#include -#include -#undef timeval - -typedef int sock_t; -#define INVALID_SOCKET (-1) - -#define to64(x) strtoll(x, NULL, 10) -#define INT64_FMT PRId64 -#define INT64_X_FMT PRIx64 -#define SIZE_T_FMT "u" - -#define SOMAXCONN 8 - -const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); -char *inet_ntoa(struct in_addr in); -int inet_pton(int af, const char *src, void *dst); - -#endif /* CS_PLATFORM == CS_P_CC3100 */ -#endif /* CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_cc3200.h b/src/mongoose-6.11/src/common/platforms/platform_cc3200.h deleted file mode 100644 index 1c2033a..0000000 --- a/src/mongoose-6.11/src/common/platforms/platform_cc3200.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ -#define CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ -#if CS_PLATFORM == CS_P_CC3200 - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef __TI_COMPILER_VERSION__ -#include -#include -#endif - -#define MG_NET_IF MG_NET_IF_SIMPLELINK -#define MG_SSL_IF MG_SSL_IF_SIMPLELINK - -/* Only SPIFFS supports directories, SLFS does not. */ -#if defined(CC3200_FS_SPIFFS) && !defined(MG_ENABLE_DIRECTORY_LISTING) -#define MG_ENABLE_DIRECTORY_LISTING 1 -#endif - -#include "common/platforms/simplelink/cs_simplelink.h" - -typedef int sock_t; -#define INVALID_SOCKET (-1) -#define SIZE_T_FMT "u" -typedef struct stat cs_stat_t; -#define DIRSEP '/' -#define to64(x) strtoll(x, NULL, 10) -#define INT64_FMT PRId64 -#define INT64_X_FMT PRIx64 -#define __cdecl - -#define fileno(x) -1 - -/* Some functions we implement for Mongoose. */ - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __TI_COMPILER_VERSION__ -struct SlTimeval_t; -#define timeval SlTimeval_t -int gettimeofday(struct timeval *t, void *tz); -int settimeofday(const struct timeval *tv, const void *tz); - -int asprintf(char **strp, const char *fmt, ...); - -#endif - -/* TI's libc does not have stat & friends, add them. */ -#ifdef __TI_COMPILER_VERSION__ - -#include - -typedef unsigned int mode_t; -typedef size_t _off_t; -typedef long ssize_t; - -struct stat { - int st_ino; - mode_t st_mode; - int st_nlink; - time_t st_mtime; - off_t st_size; -}; - -int _stat(const char *pathname, struct stat *st); -int stat(const char *pathname, struct stat *st); - -#define __S_IFMT 0170000 - -#define __S_IFDIR 0040000 -#define __S_IFCHR 0020000 -#define __S_IFREG 0100000 - -#define __S_ISTYPE(mode, mask) (((mode) &__S_IFMT) == (mask)) - -#define S_IFDIR __S_IFDIR -#define S_IFCHR __S_IFCHR -#define S_IFREG __S_IFREG -#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR) -#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG) - -/* 5.x series compilers don't have va_copy, 16.x do. */ -#if __TI_COMPILER_VERSION__ < 16000000 -#define va_copy(apc, ap) ((apc) = (ap)) -#endif - -#endif /* __TI_COMPILER_VERSION__ */ - -#ifdef CC3200_FS_SLFS -#define MG_FS_SLFS -#endif - -#if (defined(CC3200_FS_SPIFFS) || defined(CC3200_FS_SLFS)) && \ - !defined(MG_ENABLE_FILESYSTEM) -#define MG_ENABLE_FILESYSTEM 1 -#define CS_DEFINE_DIRENT -#endif - -#ifndef CS_ENABLE_STDIO -#define CS_ENABLE_STDIO 1 -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* CS_PLATFORM == CS_P_CC3200 */ -#endif /* CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_cc3220.h b/src/mongoose-6.11/src/common/platforms/platform_cc3220.h deleted file mode 100644 index a32264a..0000000 --- a/src/mongoose-6.11/src/common/platforms/platform_cc3220.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3220_H_ -#define CS_COMMON_PLATFORMS_PLATFORM_CC3220_H_ -#if CS_PLATFORM == CS_P_CC3220 - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef __TI_COMPILER_VERSION__ -#include -#include -#endif - -#define MG_NET_IF MG_NET_IF_SIMPLELINK -#define MG_SSL_IF MG_SSL_IF_SIMPLELINK - -/* Only SPIFFS supports directories, SLFS does not. */ -#if defined(CC3220_FS_SPIFFS) && !defined(MG_ENABLE_DIRECTORY_LISTING) -#define MG_ENABLE_DIRECTORY_LISTING 1 -#endif - -#include "common/platforms/simplelink/cs_simplelink.h" - -typedef int sock_t; -#define INVALID_SOCKET (-1) -#define SIZE_T_FMT "u" -typedef struct stat cs_stat_t; -#define DIRSEP '/' -#define to64(x) strtoll(x, NULL, 10) -#define INT64_FMT PRId64 -#define INT64_X_FMT PRIx64 -#define __cdecl - -#define fileno(x) -1 - -/* Some functions we implement for Mongoose. */ - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __TI_COMPILER_VERSION__ -struct SlTimeval_t; -#define timeval SlTimeval_t -int gettimeofday(struct timeval *t, void *tz); -int settimeofday(const struct timeval *tv, const void *tz); - -int asprintf(char **strp, const char *fmt, ...); - -#endif - -/* TI's libc does not have stat & friends, add them. */ -#ifdef __TI_COMPILER_VERSION__ - -#include - -typedef unsigned int mode_t; -typedef size_t _off_t; -typedef long ssize_t; - -struct stat { - int st_ino; - mode_t st_mode; - int st_nlink; - time_t st_mtime; - off_t st_size; -}; - -int _stat(const char *pathname, struct stat *st); -int stat(const char *pathname, struct stat *st); - -#define __S_IFMT 0170000 - -#define __S_IFDIR 0040000 -#define __S_IFCHR 0020000 -#define __S_IFREG 0100000 - -#define __S_ISTYPE(mode, mask) (((mode) &__S_IFMT) == (mask)) - -#define S_IFDIR __S_IFDIR -#define S_IFCHR __S_IFCHR -#define S_IFREG __S_IFREG -#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR) -#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG) - -#endif /* __TI_COMPILER_VERSION__ */ - -#ifndef CS_ENABLE_STDIO -#define CS_ENABLE_STDIO 1 -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* CS_PLATFORM == CS_P_CC3220 */ -#endif /* CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_esp32.h b/src/mongoose-6.11/src/common/platforms/platform_esp32.h deleted file mode 100644 index 4580f75..0000000 --- a/src/mongoose-6.11/src/common/platforms/platform_esp32.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_ -#define CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_ -#if CS_PLATFORM == CS_P_ESP32 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define SIZE_T_FMT "u" -typedef struct stat cs_stat_t; -#define DIRSEP '/' -#define to64(x) strtoll(x, NULL, 10) -#define INT64_FMT PRId64 -#define INT64_X_FMT PRIx64 -#define __cdecl -#define _FILE_OFFSET_BITS 32 - -#define MG_LWIP 1 - -#ifndef MG_NET_IF -#define MG_NET_IF MG_NET_IF_SOCKET -#endif - -#ifndef CS_ENABLE_STDIO -#define CS_ENABLE_STDIO 1 -#endif - -#endif /* CS_PLATFORM == CS_P_ESP32 */ -#endif /* CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_esp8266.h b/src/mongoose-6.11/src/common/platforms/platform_esp8266.h deleted file mode 100644 index b397101..0000000 --- a/src/mongoose-6.11/src/common/platforms/platform_esp8266.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_ -#define CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_ -#if CS_PLATFORM == CS_P_ESP8266 - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define SIZE_T_FMT "u" -typedef struct stat cs_stat_t; -#define DIRSEP '/' -#if !defined(MGOS_VFS_DEFINE_DIRENT) -#define CS_DEFINE_DIRENT -#endif - -#define to64(x) strtoll(x, NULL, 10) -#define INT64_FMT PRId64 -#define INT64_X_FMT PRIx64 -#define __cdecl -#define _FILE_OFFSET_BITS 32 - -#if !defined(RTOS_SDK) && !defined(__cplusplus) -#define fileno(x) -1 -#endif - -#define MG_LWIP 1 - -/* struct timeval is defined in sys/time.h. */ -#define LWIP_TIMEVAL_PRIVATE 0 - -#ifndef MG_NET_IF -#include -#if LWIP_SOCKET /* RTOS SDK has LWIP sockets */ -#define MG_NET_IF MG_NET_IF_SOCKET -#else -#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL -#endif -#endif - -#ifndef CS_ENABLE_STDIO -#define CS_ENABLE_STDIO 1 -#endif - -#define inet_ntop(af, src, dst, size) \ - (((af) == AF_INET) ? ipaddr_ntoa_r((const ip_addr_t *) (src), (dst), (size)) \ - : NULL) -#define inet_pton(af, src, dst) \ - (((af) == AF_INET) ? ipaddr_aton((src), (ip_addr_t *) (dst)) : 0) - -#endif /* CS_PLATFORM == CS_P_ESP8266 */ -#endif /* CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_mbed.h b/src/mongoose-6.11/src/common/platforms/platform_mbed.h deleted file mode 100644 index ab3f563..0000000 --- a/src/mongoose-6.11/src/common/platforms/platform_mbed.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_PLATFORM_MBED_H_ -#define CS_COMMON_PLATFORMS_PLATFORM_MBED_H_ -#if CS_PLATFORM == CS_P_MBED - -/* - * mbed.h contains C++ code (e.g. templates), thus, it should be processed - * only if included directly to startup file (ex: main.cpp) - */ -#ifdef __cplusplus -#include "mbed.h" -#endif /* __cplusplus */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -typedef struct stat cs_stat_t; -#define DIRSEP '/' - -#ifndef CS_ENABLE_STDIO -#define CS_ENABLE_STDIO 1 -#endif - -/* - * mbed can be compiled with the ARM compiler which - * just doesn't come with a gettimeofday shim - * because it's a BSD API and ARM targets embedded - * non-unix platforms. - */ -#if defined(__ARMCC_VERSION) || defined(__ICCARM__) -#define _TIMEVAL_DEFINED -#define gettimeofday _gettimeofday - -/* copied from GCC on ARM; for some reason useconds are signed */ -typedef long suseconds_t; /* microseconds (signed) */ -struct timeval { - time_t tv_sec; /* seconds */ - suseconds_t tv_usec; /* and microseconds */ -}; - -#endif - -#if MG_NET_IF == MG_NET_IF_SIMPLELINK - -#define MG_SIMPLELINK_NO_OSI 1 - -#include - -typedef int sock_t; -#define INVALID_SOCKET (-1) - -#define to64(x) strtoll(x, NULL, 10) -#define INT64_FMT PRId64 -#define INT64_X_FMT PRIx64 -#define SIZE_T_FMT "u" - -#define SOMAXCONN 8 - -const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); -char *inet_ntoa(struct in_addr in); -int inet_pton(int af, const char *src, void *dst); -int inet_aton(const char *cp, struct in_addr *inp); -in_addr_t inet_addr(const char *cp); - -#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */ - -#endif /* CS_PLATFORM == CS_P_MBED */ -#endif /* CS_COMMON_PLATFORMS_PLATFORM_MBED_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_msp432.h b/src/mongoose-6.11/src/common/platforms/platform_msp432.h deleted file mode 100644 index d8cf16a..0000000 --- a/src/mongoose-6.11/src/common/platforms/platform_msp432.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_PLATFORM_MSP432_H_ -#define CS_COMMON_PLATFORMS_PLATFORM_MSP432_H_ -#if CS_PLATFORM == CS_P_MSP432 - -#include -#include -#include -#include -#include -#include -#include - -#ifndef __TI_COMPILER_VERSION__ -#include -#include -#endif - -#define MG_NET_IF MG_NET_IF_SIMPLELINK -#define MG_SSL_IF MG_SSL_IF_SIMPLELINK - -#include "common/platforms/simplelink/cs_simplelink.h" - -typedef int sock_t; -#define INVALID_SOCKET (-1) -#define SIZE_T_FMT "u" -typedef struct stat cs_stat_t; -#define DIRSEP '/' -#define to64(x) strtoll(x, NULL, 10) -#define INT64_FMT PRId64 -#define INT64_X_FMT PRIx64 -#define __cdecl - -#define fileno(x) -1 - -/* Some functions we implement for Mongoose. */ - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __TI_COMPILER_VERSION__ -struct SlTimeval_t; -#define timeval SlTimeval_t -int gettimeofday(struct timeval *t, void *tz); -#endif - -/* TI's libc does not have stat & friends, add them. */ -#ifdef __TI_COMPILER_VERSION__ - -#include - -typedef unsigned int mode_t; -typedef size_t _off_t; -typedef long ssize_t; - -struct stat { - int st_ino; - mode_t st_mode; - int st_nlink; - time_t st_mtime; - off_t st_size; -}; - -int _stat(const char *pathname, struct stat *st); -#define stat(a, b) _stat(a, b) - -#define __S_IFMT 0170000 - -#define __S_IFDIR 0040000 -#define __S_IFCHR 0020000 -#define __S_IFREG 0100000 - -#define __S_ISTYPE(mode, mask) (((mode) &__S_IFMT) == (mask)) - -#define S_IFDIR __S_IFDIR -#define S_IFCHR __S_IFCHR -#define S_IFREG __S_IFREG -#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR) -#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG) - -/* As of 5.2.7, TI compiler does not support va_copy() yet. */ -#define va_copy(apc, ap) ((apc) = (ap)) - -#endif /* __TI_COMPILER_VERSION__ */ - -#ifndef CS_ENABLE_STDIO -#define CS_ENABLE_STDIO 1 -#endif - -#if (defined(CC3200_FS_SPIFFS) || defined(CC3200_FS_SLFS)) && \ - !defined(MG_ENABLE_FILESYSTEM) -#define MG_ENABLE_FILESYSTEM 1 -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* CS_PLATFORM == CS_P_MSP432 */ -#endif /* CS_COMMON_PLATFORMS_PLATFORM_MSP432_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_nrf51.h b/src/mongoose-6.11/src/common/platforms/platform_nrf51.h deleted file mode 100644 index 55f89dd..0000000 --- a/src/mongoose-6.11/src/common/platforms/platform_nrf51.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ -#ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_ -#define CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_ -#if CS_PLATFORM == CS_P_NRF51 - -#include -#include -#include -#include -#include -#include - -#define to64(x) strtoll(x, NULL, 10) - -#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL -#define MG_LWIP 1 -#define MG_ENABLE_IPV6 1 - -/* - * For ARM C Compiler, make lwip to export `struct timeval`; for other - * compilers, suppress it. - */ -#if !defined(__ARMCC_VERSION) -#define LWIP_TIMEVAL_PRIVATE 0 -#else -struct timeval; -int gettimeofday(struct timeval *tp, void *tzp); -#endif - -#define INT64_FMT PRId64 -#define SIZE_T_FMT "u" - -/* - * ARM C Compiler doesn't have strdup, so we provide it - */ -#define CS_ENABLE_STRDUP defined(__ARMCC_VERSION) - -#endif /* CS_PLATFORM == CS_P_NRF51 */ -#endif /* CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_nrf52.h b/src/mongoose-6.11/src/common/platforms/platform_nrf52.h deleted file mode 100644 index 3516cf9..0000000 --- a/src/mongoose-6.11/src/common/platforms/platform_nrf52.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ -#ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_ -#define CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_ -#if CS_PLATFORM == CS_P_NRF52 - -#include -#include -#include -#include -#include -#include -#include -#include - -#define to64(x) strtoll(x, NULL, 10) - -#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL -#define MG_LWIP 1 -#define MG_ENABLE_IPV6 1 - -#if !defined(ENOSPC) -#define ENOSPC 28 /* No space left on device */ -#endif - -/* - * For ARM C Compiler, make lwip to export `struct timeval`; for other - * compilers, suppress it. - */ -#if !defined(__ARMCC_VERSION) -#define LWIP_TIMEVAL_PRIVATE 0 -#endif - -#define INT64_FMT PRId64 -#define SIZE_T_FMT "u" - -/* - * ARM C Compiler doesn't have strdup, so we provide it - */ -#define CS_ENABLE_STRDUP defined(__ARMCC_VERSION) - -#endif /* CS_PLATFORM == CS_P_NRF52 */ -#endif /* CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_nxp_kinetis.h b/src/mongoose-6.11/src/common/platforms/platform_nxp_kinetis.h deleted file mode 100644 index b4ffd53..0000000 --- a/src/mongoose-6.11/src/common/platforms/platform_nxp_kinetis.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_ -#define CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_ - -#if CS_PLATFORM == CS_P_NXP_KINETIS - -#include -#include -#include -#include - -#define SIZE_T_FMT "u" -typedef struct stat cs_stat_t; -#define to64(x) strtoll(x, NULL, 10) -#define INT64_FMT "lld" -#define INT64_X_FMT "llx" -#define __cdecl - -#define MG_LWIP 1 - -#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL - -/* struct timeval is defined in sys/time.h. */ -#define LWIP_TIMEVAL_PRIVATE 0 - -#endif /* CS_PLATFORM == CS_P_NXP_KINETIS */ -#endif /* CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_nxp_lpc.h b/src/mongoose-6.11/src/common/platforms/platform_nxp_lpc.h deleted file mode 100644 index cf9b7e7..0000000 --- a/src/mongoose-6.11/src/common/platforms/platform_nxp_lpc.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_ -#define CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_ - -#if CS_PLATFORM == CS_P_NXP_LPC - -#include -#include -#include - -#define SIZE_T_FMT "u" -typedef struct stat cs_stat_t; -#define INT64_FMT "lld" -#define INT64_X_FMT "llx" -#define __cdecl - -#define MG_LWIP 1 - -#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL - -/* - * LPCXpress comes with 3 C library implementations: Newlib, NewlibNano and - *Redlib. - * See https://community.nxp.com/message/630860 for more details. - * - * Redlib is the default and lacks certain things, so we provide them. - */ -#ifdef __REDLIB_INTERFACE_VERSION__ - -/* Let LWIP define timeval for us. */ -#define LWIP_TIMEVAL_PRIVATE 1 - -#define va_copy(d, s) __builtin_va_copy(d, s) - -#define CS_ENABLE_TO64 1 -#define to64(x) cs_to64(x) - -#define CS_ENABLE_STRDUP 1 - -#else - -#include -#define LWIP_TIMEVAL_PRIVATE 0 -#define to64(x) strtoll(x, NULL, 10) - -#endif - -#endif /* CS_PLATFORM == CS_P_NXP_LPC */ -#endif /* CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_pic32.h b/src/mongoose-6.11/src/common/platforms/platform_pic32.h deleted file mode 100644 index b568025..0000000 --- a/src/mongoose-6.11/src/common/platforms/platform_pic32.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_ -#define CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_ - -#if CS_PLATFORM == CS_P_PIC32 - -#define MG_NET_IF MG_NET_IF_PIC32 - -#include -#include -#include -#include - -#include -#include - -#include - -typedef TCP_SOCKET sock_t; -#define to64(x) strtoll(x, NULL, 10) - -#define SIZE_T_FMT "lu" -#define INT64_FMT "lld" - -#ifndef CS_ENABLE_STDIO -#define CS_ENABLE_STDIO 1 -#endif - -char *inet_ntoa(struct in_addr in); - -#endif /* CS_PLATFORM == CS_P_PIC32 */ - -#endif /* CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_stm32.h b/src/mongoose-6.11/src/common/platforms/platform_stm32.h deleted file mode 100644 index ece78c8..0000000 --- a/src/mongoose-6.11/src/common/platforms/platform_stm32.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_PLATFORM_STM32_H_ -#define CS_COMMON_PLATFORMS_PLATFORM_STM32_H_ -#if CS_PLATFORM == CS_P_STM32 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define to64(x) strtoll(x, NULL, 10) -#define INT64_FMT PRId64 -#define SIZE_T_FMT "u" -typedef struct stat cs_stat_t; -#define DIRSEP '/' - -#ifndef CS_ENABLE_STDIO -#define CS_ENABLE_STDIO 1 -#endif - -#ifndef MG_ENABLE_FILESYSTEM -#define MG_ENABLE_FILESYSTEM 1 -#endif - -#endif /* CS_PLATFORM == CS_P_STM32 */ -#endif /* CS_COMMON_PLATFORMS_PLATFORM_STM32_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_tm4c129.h b/src/mongoose-6.11/src/common/platforms/platform_tm4c129.h deleted file mode 100644 index 5e87b94..0000000 --- a/src/mongoose-6.11/src/common/platforms/platform_tm4c129.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_PLATFORM_TM4C129_H_ -#define CS_COMMON_PLATFORMS_PLATFORM_TM4C129_H_ -#if CS_PLATFORM == CS_P_TM4C129 - -#include -#include -#include -#include -#include -#include -#include - -#ifndef __TI_COMPILER_VERSION__ -#include -#include -#endif - -#define SIZE_T_FMT "u" -typedef struct stat cs_stat_t; -#define DIRSEP '/' -#define to64(x) strtoll(x, NULL, 10) -#define INT64_FMT PRId64 -#define INT64_X_FMT PRIx64 -#define __cdecl - -#ifndef MG_NET_IF -#include -#if LWIP_SOCKET -#define MG_NET_IF MG_NET_IF_SOCKET -#else -#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL -#endif -#define MG_LWIP 1 -#elif MG_NET_IF == MG_NET_IF_SIMPLELINK -#include "common/platforms/simplelink/cs_simplelink.h" -#endif - -#ifndef CS_ENABLE_STDIO -#define CS_ENABLE_STDIO 1 -#endif - -#ifdef __TI_COMPILER_VERSION__ -/* As of 5.2.8, TI compiler does not support va_copy() yet. */ -#define va_copy(apc, ap) ((apc) = (ap)) -#endif /* __TI_COMPILER_VERSION__ */ - -#ifdef __cplusplus -} -#endif - -#endif /* CS_PLATFORM == CS_P_TM4C129 */ -#endif /* CS_COMMON_PLATFORMS_PLATFORM_TM4C129_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_unix.h b/src/mongoose-6.11/src/common/platforms/platform_unix.h deleted file mode 100644 index 59a04e3..0000000 --- a/src/mongoose-6.11/src/common/platforms/platform_unix.h +++ /dev/null @@ -1,145 +0,0 @@ -#ifndef CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_ -#define CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_ -#if CS_PLATFORM == CS_P_UNIX - -#ifndef _XOPEN_SOURCE -#define _XOPEN_SOURCE 600 -#endif - -/* wants this for C++ */ -#ifndef __STDC_FORMAT_MACROS -#define __STDC_FORMAT_MACROS -#endif - -/* C++ wants that for INT64_MAX */ -#ifndef __STDC_LIMIT_MACROS -#define __STDC_LIMIT_MACROS -#endif - -/* Enable fseeko() and ftello() functions */ -#ifndef _LARGEFILE_SOURCE -#define _LARGEFILE_SOURCE -#endif - -/* Enable 64-bit file offsets */ -#ifndef _FILE_OFFSET_BITS -#define _FILE_OFFSET_BITS 64 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __APPLE__ -#include -#ifndef BYTE_ORDER -#define LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN -#define BIG_ENDIAN __DARWIN_BIG_ENDIAN -#define PDP_ENDIAN __DARWIN_PDP_ENDIAN -#define BYTE_ORDER __DARWIN_BYTE_ORDER -#endif -#endif - -/* - * osx correctly avoids defining strtoll when compiling in strict ansi mode. - * c++ 11 standard defines strtoll as well. - * We require strtoll, and if your embedded pre-c99 compiler lacks one, please - * implement a shim. - */ -#if !(defined(__cplusplus) && __cplusplus >= 201103L) && \ - !(defined(__DARWIN_C_LEVEL) && __DARWIN_C_LEVEL >= 200809L) -long long strtoll(const char *, char **, int); -#endif - -typedef int sock_t; -#define INVALID_SOCKET (-1) -#define SIZE_T_FMT "zu" -typedef struct stat cs_stat_t; -#define DIRSEP '/' -#define to64(x) strtoll(x, NULL, 10) -#define INT64_FMT PRId64 -#define INT64_X_FMT PRIx64 - -#ifndef __cdecl -#define __cdecl -#endif - -#ifndef va_copy -#ifdef __va_copy -#define va_copy __va_copy -#else -#define va_copy(x, y) (x) = (y) -#endif -#endif - -#define closesocket(x) close(x) - -#ifndef MG_MAX_HTTP_REQUEST_SIZE -#define MG_MAX_HTTP_REQUEST_SIZE 8192 -#endif - -#ifndef MG_MAX_HTTP_SEND_MBUF -#define MG_MAX_HTTP_SEND_MBUF 4096 -#endif - -#ifndef MG_MAX_HTTP_HEADERS -#define MG_MAX_HTTP_HEADERS 40 -#endif - -#ifndef CS_ENABLE_STDIO -#define CS_ENABLE_STDIO 1 -#endif - -#ifndef MG_ENABLE_BROADCAST -#define MG_ENABLE_BROADCAST 1 -#endif - -#ifndef MG_ENABLE_DIRECTORY_LISTING -#define MG_ENABLE_DIRECTORY_LISTING 1 -#endif - -#ifndef MG_ENABLE_FILESYSTEM -#define MG_ENABLE_FILESYSTEM 1 -#endif - -#ifndef MG_ENABLE_HTTP_CGI -#define MG_ENABLE_HTTP_CGI MG_ENABLE_FILESYSTEM -#endif - -#ifndef MG_NET_IF -#define MG_NET_IF MG_NET_IF_SOCKET -#endif - -#ifndef MG_HOSTS_FILE_NAME -#define MG_HOSTS_FILE_NAME "/etc/hosts" -#endif - -#ifndef MG_RESOLV_CONF_FILE_NAME -#define MG_RESOLV_CONF_FILE_NAME "/etc/resolv.conf" -#endif - -#endif /* CS_PLATFORM == CS_P_UNIX */ -#endif /* CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_wince.h b/src/mongoose-6.11/src/common/platforms/platform_wince.h deleted file mode 100644 index 48c51f6..0000000 --- a/src/mongoose-6.11/src/common/platforms/platform_wince.h +++ /dev/null @@ -1,201 +0,0 @@ -#ifndef CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_ -#define CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_ - -#if CS_PLATFORM == CS_P_WINCE - -/* - * MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) - * MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) - * MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) - * MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) - * MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) - * MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) - * MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio 2003) - * MSVC++ 7.0 _MSC_VER == 1300 - * MSVC++ 6.0 _MSC_VER == 1200 - * MSVC++ 5.0 _MSC_VER == 1100 - */ -#pragma warning(disable : 4127) /* FD_SET() emits warning, disable it */ -#pragma warning(disable : 4204) /* missing c99 support */ - -#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS -#define _WINSOCK_DEPRECATED_NO_WARNINGS 1 -#endif - -#ifndef _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include -#include -#include -#include -#include -#include - -#pragma comment(lib, "ws2.lib") /* Linking with WinCE winsock library */ - -#include -#include -#include - -#define strdup _strdup - -#ifndef EINPROGRESS -#define EINPROGRESS WSAEINPROGRESS -#endif - -#ifndef EWOULDBLOCK -#define EWOULDBLOCK WSAEWOULDBLOCK -#endif - -#ifndef EAGAIN -#define EAGAIN EWOULDBLOCK -#endif - -#ifndef __func__ -#define STRX(x) #x -#define STR(x) STRX(x) -#define __func__ __FILE__ ":" STR(__LINE__) -#endif - -#define snprintf _snprintf -#define fileno _fileno -#define vsnprintf _vsnprintf -#define sleep(x) Sleep((x) *1000) -#define to64(x) _atoi64(x) -#define rmdir _rmdir - -#if defined(_MSC_VER) && _MSC_VER >= 1400 -#define fseeko(x, y, z) _fseeki64((x), (y), (z)) -#else -#define fseeko(x, y, z) fseek((x), (y), (z)) -#endif - -typedef int socklen_t; - -#if _MSC_VER >= 1700 -#include -#else -typedef signed char int8_t; -typedef unsigned char uint8_t; -typedef int int32_t; -typedef unsigned int uint32_t; -typedef short int16_t; -typedef unsigned short uint16_t; -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -#endif - -typedef SOCKET sock_t; -typedef uint32_t in_addr_t; - -#ifndef UINT16_MAX -#define UINT16_MAX 65535 -#endif - -#ifndef UINT32_MAX -#define UINT32_MAX 4294967295 -#endif - -#ifndef pid_t -#define pid_t HANDLE -#endif - -#define INT64_FMT "I64d" -#define INT64_X_FMT "I64x" -/* TODO(alashkin): check if this is correct */ -#define SIZE_T_FMT "u" - -#define DIRSEP '\\' -#define CS_DEFINE_DIRENT - -#ifndef va_copy -#ifdef __va_copy -#define va_copy __va_copy -#else -#define va_copy(x, y) (x) = (y) -#endif -#endif - -#ifndef MG_MAX_HTTP_REQUEST_SIZE -#define MG_MAX_HTTP_REQUEST_SIZE 8192 -#endif - -#ifndef MG_MAX_HTTP_SEND_MBUF -#define MG_MAX_HTTP_SEND_MBUF 4096 -#endif - -#ifndef MG_MAX_HTTP_HEADERS -#define MG_MAX_HTTP_HEADERS 40 -#endif - -#ifndef CS_ENABLE_STDIO -#define CS_ENABLE_STDIO 1 -#endif - -#define abort() DebugBreak(); - -#ifndef BUFSIZ -#define BUFSIZ 4096 -#endif -/* - * Explicitly disabling MG_ENABLE_THREADS for WinCE - * because they are enabled for _WIN32 by default - */ -#ifndef MG_ENABLE_THREADS -#define MG_ENABLE_THREADS 0 -#endif - -#ifndef MG_ENABLE_FILESYSTEM -#define MG_ENABLE_FILESYSTEM 1 -#endif - -#ifndef MG_NET_IF -#define MG_NET_IF MG_NET_IF_SOCKET -#endif - -typedef struct _stati64 { - uint32_t st_mtime; - uint32_t st_size; - uint32_t st_mode; -} cs_stat_t; - -/* - * WinCE 6.0 has a lot of useful definitions in ATL (not windows.h) headers - * use #ifdefs to avoid conflicts - */ - -#ifndef ENOENT -#define ENOENT ERROR_PATH_NOT_FOUND -#endif - -#ifndef EACCES -#define EACCES ERROR_ACCESS_DENIED -#endif - -#ifndef ENOMEM -#define ENOMEM ERROR_NOT_ENOUGH_MEMORY -#endif - -#ifndef _UINTPTR_T_DEFINED -typedef unsigned int *uintptr_t; -#endif - -#define _S_IFREG 2 -#define _S_IFDIR 4 - -#ifndef S_ISDIR -#define S_ISDIR(x) (((x) &_S_IFDIR) != 0) -#endif - -#ifndef S_ISREG -#define S_ISREG(x) (((x) &_S_IFREG) != 0) -#endif - -int open(const char *filename, int oflag, int pmode); -int _wstati64(const wchar_t *path, cs_stat_t *st); -const char *strerror(); - -#endif /* CS_PLATFORM == CS_P_WINCE */ -#endif /* CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/platform_windows.h b/src/mongoose-6.11/src/common/platforms/platform_windows.h deleted file mode 100644 index 3b300bd..0000000 --- a/src/mongoose-6.11/src/common/platforms/platform_windows.h +++ /dev/null @@ -1,183 +0,0 @@ -#ifndef CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_ -#define CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_ -#if CS_PLATFORM == CS_P_WINDOWS - -/* - * MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) - * MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) - * MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) - * MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) - * MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) - * MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) - * MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio 2003) - * MSVC++ 7.0 _MSC_VER == 1300 - * MSVC++ 6.0 _MSC_VER == 1200 - * MSVC++ 5.0 _MSC_VER == 1100 - */ -#ifdef _MSC_VER -#pragma warning(disable : 4127) /* FD_SET() emits warning, disable it */ -#pragma warning(disable : 4204) /* missing c99 support */ -#endif - -#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS -#define _WINSOCK_DEPRECATED_NO_WARNINGS 1 -#endif - -#ifndef _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */ -#endif - -#include -#include -#include -#include - -#if _MSC_VER < 1700 -typedef int bool; -#else -#include -#endif - -#if defined(_MSC_VER) && _MSC_VER >= 1800 -#define strdup _strdup -#endif - -#ifndef EINPROGRESS -#define EINPROGRESS WSAEINPROGRESS -#endif -#ifndef EWOULDBLOCK -#define EWOULDBLOCK WSAEWOULDBLOCK -#endif -#ifndef __func__ -#define STRX(x) #x -#define STR(x) STRX(x) -#define __func__ __FILE__ ":" STR(__LINE__) -#endif -#define snprintf _snprintf -#define vsnprintf _vsnprintf -#define to64(x) _atoi64(x) -#if !defined(__MINGW32__) && !defined(__MINGW64__) -#define popen(x, y) _popen((x), (y)) -#define pclose(x) _pclose(x) -#define fileno _fileno -#endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 -#define fseeko(x, y, z) _fseeki64((x), (y), (z)) -#else -#define fseeko(x, y, z) fseek((x), (y), (z)) -#endif -#if defined(_MSC_VER) && _MSC_VER <= 1200 -typedef unsigned long uintptr_t; -typedef long intptr_t; -#endif -typedef int socklen_t; -#if _MSC_VER >= 1700 -#include -#else -typedef signed char int8_t; -typedef unsigned char uint8_t; -typedef int int32_t; -typedef unsigned int uint32_t; -typedef short int16_t; -typedef unsigned short uint16_t; -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -#endif -typedef SOCKET sock_t; -typedef uint32_t in_addr_t; -#ifndef UINT16_MAX -#define UINT16_MAX 65535 -#endif -#ifndef UINT32_MAX -#define UINT32_MAX 4294967295 -#endif -#ifndef pid_t -#define pid_t HANDLE -#endif -#define INT64_FMT "I64d" -#define INT64_X_FMT "I64x" -#define SIZE_T_FMT "Iu" -typedef struct _stati64 cs_stat_t; -#ifndef S_ISDIR -#define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR) -#endif -#ifndef S_ISREG -#define S_ISREG(x) (((x) &_S_IFMT) == _S_IFREG) -#endif -#define DIRSEP '\\' -#define CS_DEFINE_DIRENT - -#ifndef va_copy -#ifdef __va_copy -#define va_copy __va_copy -#else -#define va_copy(x, y) (x) = (y) -#endif -#endif - -#ifndef MG_MAX_HTTP_REQUEST_SIZE -#define MG_MAX_HTTP_REQUEST_SIZE 8192 -#endif - -#ifndef MG_MAX_HTTP_SEND_MBUF -#define MG_MAX_HTTP_SEND_MBUF 4096 -#endif - -#ifndef MG_MAX_HTTP_HEADERS -#define MG_MAX_HTTP_HEADERS 40 -#endif - -#ifndef CS_ENABLE_STDIO -#define CS_ENABLE_STDIO 1 -#endif - -#ifndef MG_ENABLE_BROADCAST -#define MG_ENABLE_BROADCAST 1 -#endif - -#ifndef MG_ENABLE_DIRECTORY_LISTING -#define MG_ENABLE_DIRECTORY_LISTING 1 -#endif - -#ifndef MG_ENABLE_FILESYSTEM -#define MG_ENABLE_FILESYSTEM 1 -#endif - -#ifndef MG_ENABLE_HTTP_CGI -#define MG_ENABLE_HTTP_CGI MG_ENABLE_FILESYSTEM -#endif - -#ifndef MG_NET_IF -#define MG_NET_IF MG_NET_IF_SOCKET -#endif - -unsigned int sleep(unsigned int seconds); - -/* https://stackoverflow.com/questions/16647819/timegm-cross-platform */ -#define timegm _mkgmtime - -#define gmtime_r(a, b) \ - do { \ - *(b) = *gmtime(a); \ - } while (0) - -#endif /* CS_PLATFORM == CS_P_WINDOWS */ -#endif /* CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/cs_simplelink.h b/src/mongoose-6.11/src/common/platforms/simplelink/cs_simplelink.h deleted file mode 100644 index ed6afa2..0000000 --- a/src/mongoose-6.11/src/common/platforms/simplelink/cs_simplelink.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_ -#define CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_ - -#if defined(MG_NET_IF) && MG_NET_IF == MG_NET_IF_SIMPLELINK - -/* If simplelink.h is already included, all bets are off. */ -#if !defined(__SIMPLELINK_H__) - -#include - -#ifndef __TI_COMPILER_VERSION__ -#undef __CONCAT -#undef FD_CLR -#undef FD_ISSET -#undef FD_SET -#undef FD_SETSIZE -#undef FD_ZERO -#undef fd_set -#endif - -#if CS_PLATFORM == CS_P_CC3220 -#include -#include -#include -#include -#else -/* We want to disable SL_INC_STD_BSD_API_NAMING, so we include user.h ourselves - * and undef it. */ -#define PROVISIONING_API_H_ -#include -#undef PROVISIONING_API_H_ -#undef SL_INC_STD_BSD_API_NAMING - -#include -#include -#endif /* CS_PLATFORM == CS_P_CC3220 */ - -/* Now define only the subset of the BSD API that we use. - * Notably, close(), read() and write() are not defined. */ -#define AF_INET SL_AF_INET - -#define socklen_t SlSocklen_t -#define sockaddr SlSockAddr_t -#define sockaddr_in SlSockAddrIn_t -#define in_addr SlInAddr_t - -#define SOCK_STREAM SL_SOCK_STREAM -#define SOCK_DGRAM SL_SOCK_DGRAM - -#define htonl sl_Htonl -#define ntohl sl_Ntohl -#define htons sl_Htons -#define ntohs sl_Ntohs - -#ifndef EACCES -#define EACCES SL_EACCES -#endif -#ifndef EAFNOSUPPORT -#define EAFNOSUPPORT SL_EAFNOSUPPORT -#endif -#ifndef EAGAIN -#define EAGAIN SL_EAGAIN -#endif -#ifndef EBADF -#define EBADF SL_EBADF -#endif -#ifndef EINVAL -#define EINVAL SL_EINVAL -#endif -#ifndef ENOMEM -#define ENOMEM SL_ENOMEM -#endif -#ifndef EWOULDBLOCK -#define EWOULDBLOCK SL_EWOULDBLOCK -#endif - -#define SOMAXCONN 8 - -#ifdef __cplusplus -extern "C" { -#endif - -const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); -char *inet_ntoa(struct in_addr in); -int inet_pton(int af, const char *src, void *dst); - -struct mg_mgr; -struct mg_connection; - -typedef void (*mg_init_cb)(struct mg_mgr *mgr); -bool mg_start_task(int priority, int stack_size, mg_init_cb mg_init); - -void mg_run_in_task(void (*cb)(struct mg_mgr *mgr, void *arg), void *cb_arg); - -int sl_fs_init(void); - -void sl_restart_cb(struct mg_mgr *mgr); - -int sl_set_ssl_opts(int sock, struct mg_connection *nc); - -#ifdef __cplusplus -} -#endif - -#endif /* !defined(__SIMPLELINK_H__) */ - -/* Compatibility with older versions of SimpleLink */ -#if SL_MAJOR_VERSION_NUM < 2 - -#define SL_ERROR_BSD_EAGAIN SL_EAGAIN -#define SL_ERROR_BSD_EALREADY SL_EALREADY -#define SL_ERROR_BSD_ENOPROTOOPT SL_ENOPROTOOPT -#define SL_ERROR_BSD_ESECDATEERROR SL_ESECDATEERROR -#define SL_ERROR_BSD_ESECSNOVERIFY SL_ESECSNOVERIFY -#define SL_ERROR_FS_FAILED_TO_ALLOCATE_MEM SL_FS_ERR_FAILED_TO_ALLOCATE_MEM -#define SL_ERROR_FS_FILE_HAS_NOT_BEEN_CLOSE_CORRECTLY \ - SL_FS_FILE_HAS_NOT_BEEN_CLOSE_CORRECTLY -#define SL_ERROR_FS_FILE_NAME_EXIST SL_FS_FILE_NAME_EXIST -#define SL_ERROR_FS_FILE_NOT_EXISTS SL_FS_ERR_FILE_NOT_EXISTS -#define SL_ERROR_FS_NO_AVAILABLE_NV_INDEX SL_FS_ERR_NO_AVAILABLE_NV_INDEX -#define SL_ERROR_FS_NOT_ENOUGH_STORAGE_SPACE SL_FS_ERR_NO_AVAILABLE_BLOCKS -#define SL_ERROR_FS_NOT_SUPPORTED SL_FS_ERR_NOT_SUPPORTED -#define SL_ERROR_FS_WRONG_FILE_NAME SL_FS_WRONG_FILE_NAME -#define SL_ERROR_FS_INVALID_HANDLE SL_FS_ERR_INVALID_HANDLE -#define SL_NETCFG_MAC_ADDRESS_GET SL_MAC_ADDRESS_GET -#define SL_SOCKET_FD_ZERO SL_FD_ZERO -#define SL_SOCKET_FD_SET SL_FD_SET -#define SL_SOCKET_FD_ISSET SL_FD_ISSET -#define SL_SO_SECURE_DOMAIN_NAME_VERIFICATION SO_SECURE_DOMAIN_NAME_VERIFICATION - -#define SL_FS_READ FS_MODE_OPEN_READ -#define SL_FS_WRITE FS_MODE_OPEN_WRITE - -#define SL_FI_FILE_SIZE(fi) ((fi).FileLen) -#define SL_FI_FILE_MAX_SIZE(fi) ((fi).AllocatedLen) - -#define SlDeviceVersion_t SlVersionFull -#define sl_DeviceGet sl_DevGet -#define SL_DEVICE_GENERAL SL_DEVICE_GENERAL_CONFIGURATION -#define SL_LEN_TYPE _u8 -#define SL_OPT_TYPE _u8 - -#else /* SL_MAJOR_VERSION_NUM >= 2 */ - -#define FS_MODE_OPEN_CREATE(max_size, flag) \ - (SL_FS_CREATE | SL_FS_CREATE_MAX_SIZE(max_size)) -#define SL_FI_FILE_SIZE(fi) ((fi).Len) -#define SL_FI_FILE_MAX_SIZE(fi) ((fi).MaxSize) - -#define SL_LEN_TYPE _u16 -#define SL_OPT_TYPE _u16 - -#endif /* SL_MAJOR_VERSION_NUM < 2 */ - -int slfs_open(const unsigned char *fname, uint32_t flags); - -#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */ - -#endif /* CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs.c b/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs.c deleted file mode 100644 index fc09342..0000000 --- a/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs.c +++ /dev/null @@ -1,411 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#if MG_NET_IF == MG_NET_IF_SIMPLELINK && \ - (defined(MG_FS_SLFS) || defined(MG_FS_SPIFFS)) - -int set_errno(int e) { - errno = e; - return (e == 0 ? 0 : -1); -} - -const char *drop_dir(const char *fname, bool *is_slfs) { - if (is_slfs != NULL) { - *is_slfs = (strncmp(fname, "SL:", 3) == 0); - if (*is_slfs) fname += 3; - } - /* Drop "./", if any */ - if (fname[0] == '.' && fname[1] == '/') { - fname += 2; - } - /* - * Drop / if it is the only one in the path. - * This allows use of /pretend/directories but serves /file.txt as normal. - */ - if (fname[0] == '/' && strchr(fname + 1, '/') == NULL) { - fname++; - } - return fname; -} - -#if !defined(MG_FS_NO_VFS) - -#include -#include -#include -#include -#include -#ifdef __TI_COMPILER_VERSION__ -#include -#endif - -#include "common/cs_dbg.h" -#include "common/platform.h" - -#ifdef CC3200_FS_SPIFFS -#include "cc3200_fs_spiffs.h" -#endif - -#ifdef MG_FS_SLFS -#include "sl_fs_slfs.h" -#endif - -#define NUM_SYS_FDS 3 -#define SPIFFS_FD_BASE 10 -#define SLFS_FD_BASE 100 - -#if !defined(MG_UART_CHAR_PUT) && !defined(MG_UART_WRITE) -#if CS_PLATFORM == CS_P_CC3200 -#include -#include -#include -#include -#include -#define MG_UART_CHAR_PUT(fd, c) MAP_UARTCharPut(UARTA0_BASE, c); -#else -#define MG_UART_WRITE(fd, buf, len) -#endif /* CS_PLATFORM == CS_P_CC3200 */ -#endif /* !MG_UART_CHAR_PUT */ - -enum fd_type { - FD_INVALID, - FD_SYS, -#ifdef CC3200_FS_SPIFFS - FD_SPIFFS, -#endif -#ifdef MG_FS_SLFS - FD_SLFS -#endif -}; -static int fd_type(int fd) { - if (fd >= 0 && fd < NUM_SYS_FDS) return FD_SYS; -#ifdef CC3200_FS_SPIFFS - if (fd >= SPIFFS_FD_BASE && fd < SPIFFS_FD_BASE + MAX_OPEN_SPIFFS_FILES) { - return FD_SPIFFS; - } -#endif -#ifdef MG_FS_SLFS - if (fd >= SLFS_FD_BASE && fd < SLFS_FD_BASE + MAX_OPEN_SLFS_FILES) { - return FD_SLFS; - } -#endif - return FD_INVALID; -} - -#if MG_TI_NO_HOST_INTERFACE -int open(const char *pathname, unsigned flags, int mode) { -#else -int _open(const char *pathname, int flags, mode_t mode) { -#endif - int fd = -1; - bool is_sl; - const char *fname = drop_dir(pathname, &is_sl); - if (is_sl) { -#ifdef MG_FS_SLFS - fd = fs_slfs_open(fname, flags, mode); - if (fd >= 0) fd += SLFS_FD_BASE; -#endif - } else { -#ifdef CC3200_FS_SPIFFS - fd = fs_spiffs_open(fname, flags, mode); - if (fd >= 0) fd += SPIFFS_FD_BASE; -#endif - } - LOG(LL_DEBUG, - ("open(%s, 0x%x) = %d, fname = %s", pathname, flags, fd, fname)); - return fd; -} - -int _stat(const char *pathname, struct stat *st) { - int res = -1; - bool is_sl; - const char *fname = drop_dir(pathname, &is_sl); - memset(st, 0, sizeof(*st)); - /* Simulate statting the root directory. */ - if (fname[0] == '\0' || strcmp(fname, ".") == 0) { - st->st_ino = 0; - st->st_mode = S_IFDIR | 0777; - st->st_nlink = 1; - st->st_size = 0; - return 0; - } - if (is_sl) { -#ifdef MG_FS_SLFS - res = fs_slfs_stat(fname, st); -#endif - } else { -#ifdef CC3200_FS_SPIFFS - res = fs_spiffs_stat(fname, st); -#endif - } - LOG(LL_DEBUG, ("stat(%s) = %d; fname = %s", pathname, res, fname)); - return res; -} - -#if MG_TI_NO_HOST_INTERFACE -int close(int fd) { -#else -int _close(int fd) { -#endif - int r = -1; - switch (fd_type(fd)) { - case FD_INVALID: - r = set_errno(EBADF); - break; - case FD_SYS: - r = set_errno(EACCES); - break; -#ifdef CC3200_FS_SPIFFS - case FD_SPIFFS: - r = fs_spiffs_close(fd - SPIFFS_FD_BASE); - break; -#endif -#ifdef MG_FS_SLFS - case FD_SLFS: - r = fs_slfs_close(fd - SLFS_FD_BASE); - break; -#endif - } - DBG(("close(%d) = %d", fd, r)); - return r; -} - -#if MG_TI_NO_HOST_INTERFACE -off_t lseek(int fd, off_t offset, int whence) { -#else -off_t _lseek(int fd, off_t offset, int whence) { -#endif - int r = -1; - switch (fd_type(fd)) { - case FD_INVALID: - r = set_errno(EBADF); - break; - case FD_SYS: - r = set_errno(ESPIPE); - break; -#ifdef CC3200_FS_SPIFFS - case FD_SPIFFS: - r = fs_spiffs_lseek(fd - SPIFFS_FD_BASE, offset, whence); - break; -#endif -#ifdef MG_FS_SLFS - case FD_SLFS: - r = fs_slfs_lseek(fd - SLFS_FD_BASE, offset, whence); - break; -#endif - } - DBG(("lseek(%d, %d, %d) = %d", fd, (int) offset, whence, r)); - return r; -} - -int _fstat(int fd, struct stat *s) { - int r = -1; - memset(s, 0, sizeof(*s)); - switch (fd_type(fd)) { - case FD_INVALID: - r = set_errno(EBADF); - break; - case FD_SYS: { - /* Create barely passable stats for STD{IN,OUT,ERR}. */ - memset(s, 0, sizeof(*s)); - s->st_ino = fd; - s->st_mode = S_IFCHR | 0666; - r = 0; - break; - } -#ifdef CC3200_FS_SPIFFS - case FD_SPIFFS: - r = fs_spiffs_fstat(fd - SPIFFS_FD_BASE, s); - break; -#endif -#ifdef MG_FS_SLFS - case FD_SLFS: - r = fs_slfs_fstat(fd - SLFS_FD_BASE, s); - break; -#endif - } - DBG(("fstat(%d) = %d", fd, r)); - return r; -} - -#if MG_TI_NO_HOST_INTERFACE -int read(int fd, char *buf, unsigned count) { -#else -ssize_t _read(int fd, void *buf, size_t count) { -#endif - int r = -1; - switch (fd_type(fd)) { - case FD_INVALID: - r = set_errno(EBADF); - break; - case FD_SYS: { - if (fd != 0) { - r = set_errno(EACCES); - break; - } - /* Should we allow reading from stdin = uart? */ - r = set_errno(ENOTSUP); - break; - } -#ifdef CC3200_FS_SPIFFS - case FD_SPIFFS: - r = fs_spiffs_read(fd - SPIFFS_FD_BASE, buf, count); - break; -#endif -#ifdef MG_FS_SLFS - case FD_SLFS: - r = fs_slfs_read(fd - SLFS_FD_BASE, buf, count); - break; -#endif - } - DBG(("read(%d, %u) = %d", fd, count, r)); - return r; -} - -#if MG_TI_NO_HOST_INTERFACE -int write(int fd, const char *buf, unsigned count) { -#else -ssize_t _write(int fd, const void *buf, size_t count) { -#endif - int r = -1; - switch (fd_type(fd)) { - case FD_INVALID: - r = set_errno(EBADF); - break; - case FD_SYS: { - if (fd == 0) { - r = set_errno(EACCES); - break; - } -#ifdef MG_UART_WRITE - MG_UART_WRITE(fd, buf, count); -#elif defined(MG_UART_CHAR_PUT) - { - size_t i; - for (i = 0; i < count; i++) { - const char c = ((const char *) buf)[i]; - if (c == '\n') MG_UART_CHAR_PUT(fd, '\r'); - MG_UART_CHAR_PUT(fd, c); - } - } -#endif - r = count; - break; - } -#ifdef CC3200_FS_SPIFFS - case FD_SPIFFS: - r = fs_spiffs_write(fd - SPIFFS_FD_BASE, buf, count); - break; -#endif -#ifdef MG_FS_SLFS - case FD_SLFS: - r = fs_slfs_write(fd - SLFS_FD_BASE, buf, count); - break; -#endif - } - return r; -} - -/* - * On Newlib we override rename directly too, because the default - * implementation using _link and _unlink doesn't work for us. - */ -#if MG_TI_NO_HOST_INTERFACE || defined(_NEWLIB_VERSION) -int rename(const char *frompath, const char *topath) { - int r = -1; - bool is_sl_from, is_sl_to; - const char *from = drop_dir(frompath, &is_sl_from); - const char *to = drop_dir(topath, &is_sl_to); - if (is_sl_from || is_sl_to) { - set_errno(ENOTSUP); - } else { -#ifdef CC3200_FS_SPIFFS - r = fs_spiffs_rename(from, to); -#endif - } - DBG(("rename(%s, %s) = %d", from, to, r)); - return r; -} -#endif /* MG_TI_NO_HOST_INTERFACE || defined(_NEWLIB_VERSION) */ - -#if MG_TI_NO_HOST_INTERFACE -int unlink(const char *pathname) { -#else -int _unlink(const char *pathname) { -#endif - int r = -1; - bool is_sl; - const char *fname = drop_dir(pathname, &is_sl); - if (is_sl) { -#ifdef MG_FS_SLFS - r = fs_slfs_unlink(fname); -#endif - } else { -#ifdef CC3200_FS_SPIFFS - r = fs_spiffs_unlink(fname); -#endif - } - DBG(("unlink(%s) = %d, fname = %s", pathname, r, fname)); - return r; -} - -#ifdef CC3200_FS_SPIFFS /* FailFS does not support listing files. */ -DIR *opendir(const char *dir_name) { - DIR *r = NULL; - bool is_sl; - drop_dir(dir_name, &is_sl); - if (is_sl) { - r = NULL; - set_errno(ENOTSUP); - } else { - r = fs_spiffs_opendir(dir_name); - } - DBG(("opendir(%s) = %p", dir_name, r)); - return r; -} - -struct dirent *readdir(DIR *dir) { - struct dirent *res = fs_spiffs_readdir(dir); - DBG(("readdir(%p) = %p", dir, res)); - return res; -} - -int closedir(DIR *dir) { - int res = fs_spiffs_closedir(dir); - DBG(("closedir(%p) = %d", dir, res)); - return res; -} - -int rmdir(const char *path) { - return fs_spiffs_rmdir(path); -} - -int mkdir(const char *path, mode_t mode) { - (void) path; - (void) mode; - /* for spiffs supports only root dir, which comes from mongoose as '.' */ - return (strlen(path) == 1 && *path == '.') ? 0 : ENOTDIR; -} -#endif - -int sl_fs_init(void) { - int ret = 1; -#ifdef __TI_COMPILER_VERSION__ -#ifdef MG_FS_SLFS -#pragma diag_push -#pragma diag_suppress 169 /* Nothing we can do about the prototype mismatch. \ - */ - ret = (add_device("SL", _MSA, fs_slfs_open, fs_slfs_close, fs_slfs_read, - fs_slfs_write, fs_slfs_lseek, fs_slfs_unlink, - fs_slfs_rename) == 0); -#pragma diag_pop -#endif -#endif - return ret; -} - -#endif /* !defined(MG_FS_NO_VFS) */ -#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK && (defined(MG_FS_SLFS) || \ - defined(MG_FS_SPIFFS)) */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs_slfs.c b/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs_slfs.c deleted file mode 100644 index 2754b6b..0000000 --- a/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs_slfs.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -/* Standard libc interface to TI SimpleLink FS. */ - -#if defined(MG_FS_SLFS) || defined(CC3200_FS_SLFS) - -#include "common/platforms/simplelink/sl_fs_slfs.h" - -#include - -#if CS_PLATFORM == CS_P_CC3200 -#include -#endif - -#include "common/cs_dbg.h" -#include "common/mg_mem.h" - -#if SL_MAJOR_VERSION_NUM < 2 -int slfs_open(const unsigned char *fname, uint32_t flags) { - _i32 fh; - _i32 r = sl_FsOpen(fname, flags, NULL /* token */, &fh); - return (r < 0 ? r : fh); -} -#else /* SL_MAJOR_VERSION_NUM >= 2 */ -int slfs_open(const unsigned char *fname, uint32_t flags) { - return sl_FsOpen(fname, flags, NULL /* token */); -} -#endif - -/* From sl_fs.c */ -int set_errno(int e); -const char *drop_dir(const char *fname, bool *is_slfs); - -/* - * With SLFS, you have to pre-declare max file size. Yes. Really. - * 64K should be enough for everyone. Right? - */ -#ifndef FS_SLFS_MAX_FILE_SIZE -#define FS_SLFS_MAX_FILE_SIZE (64 * 1024) -#endif - -struct sl_file_size_hint { - char *name; - size_t size; -}; - -struct sl_fd_info { - _i32 fh; - _off_t pos; - size_t size; -}; - -static struct sl_fd_info s_sl_fds[MAX_OPEN_SLFS_FILES]; -static struct sl_file_size_hint s_sl_file_size_hints[MAX_OPEN_SLFS_FILES]; - -static int sl_fs_to_errno(_i32 r) { - DBG(("SL error: %d", (int) r)); - switch (r) { - case SL_FS_OK: - return 0; - case SL_ERROR_FS_FILE_NAME_EXIST: - return EEXIST; - case SL_ERROR_FS_WRONG_FILE_NAME: - return EINVAL; - case SL_ERROR_FS_NO_AVAILABLE_NV_INDEX: - case SL_ERROR_FS_NOT_ENOUGH_STORAGE_SPACE: - return ENOSPC; - case SL_ERROR_FS_FAILED_TO_ALLOCATE_MEM: - return ENOMEM; - case SL_ERROR_FS_FILE_NOT_EXISTS: - return ENOENT; - case SL_ERROR_FS_NOT_SUPPORTED: - return ENOTSUP; - } - return ENXIO; -} - -int fs_slfs_open(const char *pathname, int flags, mode_t mode) { - int fd; - for (fd = 0; fd < MAX_OPEN_SLFS_FILES; fd++) { - if (s_sl_fds[fd].fh <= 0) break; - } - if (fd >= MAX_OPEN_SLFS_FILES) return set_errno(ENOMEM); - struct sl_fd_info *fi = &s_sl_fds[fd]; - - /* - * Apply path manipulations again, in case we got here directly - * (via TI libc's "add_device"). - */ - pathname = drop_dir(pathname, NULL); - - _u32 am = 0; - fi->size = (size_t) -1; - int rw = (flags & 3); - size_t new_size = FS_SLFS_MAX_FILE_SIZE; - if (rw == O_RDONLY) { - SlFsFileInfo_t sl_fi; - _i32 r = sl_FsGetInfo((const _u8 *) pathname, 0, &sl_fi); - if (r == SL_FS_OK) { - fi->size = SL_FI_FILE_SIZE(sl_fi); - } - am = SL_FS_READ; - } else { - if (!(flags & O_TRUNC) || (flags & O_APPEND)) { - // FailFS files cannot be opened for append and will be truncated - // when opened for write. - return set_errno(ENOTSUP); - } - if (flags & O_CREAT) { - size_t i; - for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) { - if (s_sl_file_size_hints[i].name != NULL && - strcmp(s_sl_file_size_hints[i].name, pathname) == 0) { - new_size = s_sl_file_size_hints[i].size; - MG_FREE(s_sl_file_size_hints[i].name); - s_sl_file_size_hints[i].name = NULL; - break; - } - } - am = FS_MODE_OPEN_CREATE(new_size, 0); - } else { - am = SL_FS_WRITE; - } - } - fi->fh = slfs_open((_u8 *) pathname, am); - LOG(LL_DEBUG, ("sl_FsOpen(%s, 0x%x) sz %u = %d", pathname, (int) am, - (unsigned int) new_size, (int) fi->fh)); - int r; - if (fi->fh >= 0) { - fi->pos = 0; - r = fd; - } else { - r = set_errno(sl_fs_to_errno(fi->fh)); - } - return r; -} - -int fs_slfs_close(int fd) { - struct sl_fd_info *fi = &s_sl_fds[fd]; - if (fi->fh <= 0) return set_errno(EBADF); - _i32 r = sl_FsClose(fi->fh, NULL, NULL, 0); - LOG(LL_DEBUG, ("sl_FsClose(%d) = %d", (int) fi->fh, (int) r)); - s_sl_fds[fd].fh = -1; - return set_errno(sl_fs_to_errno(r)); -} - -ssize_t fs_slfs_read(int fd, void *buf, size_t count) { - struct sl_fd_info *fi = &s_sl_fds[fd]; - if (fi->fh <= 0) return set_errno(EBADF); - /* Simulate EOF. sl_FsRead @ file_size return SL_FS_ERR_OFFSET_OUT_OF_RANGE. - */ - if (fi->pos == fi->size) return 0; - _i32 r = sl_FsRead(fi->fh, fi->pos, buf, count); - DBG(("sl_FsRead(%d, %d, %d) = %d", (int) fi->fh, (int) fi->pos, (int) count, - (int) r)); - if (r >= 0) { - fi->pos += r; - return r; - } - return set_errno(sl_fs_to_errno(r)); -} - -ssize_t fs_slfs_write(int fd, const void *buf, size_t count) { - struct sl_fd_info *fi = &s_sl_fds[fd]; - if (fi->fh <= 0) return set_errno(EBADF); - _i32 r = sl_FsWrite(fi->fh, fi->pos, (_u8 *) buf, count); - DBG(("sl_FsWrite(%d, %d, %d) = %d", (int) fi->fh, (int) fi->pos, (int) count, - (int) r)); - if (r >= 0) { - fi->pos += r; - return r; - } - return set_errno(sl_fs_to_errno(r)); -} - -int fs_slfs_stat(const char *pathname, struct stat *s) { - SlFsFileInfo_t sl_fi; - /* - * Apply path manipulations again, in case we got here directly - * (via TI libc's "add_device"). - */ - pathname = drop_dir(pathname, NULL); - _i32 r = sl_FsGetInfo((const _u8 *) pathname, 0, &sl_fi); - if (r == SL_FS_OK) { - s->st_mode = S_IFREG | 0666; - s->st_nlink = 1; - s->st_size = SL_FI_FILE_SIZE(sl_fi); - return 0; - } - return set_errno(sl_fs_to_errno(r)); -} - -int fs_slfs_fstat(int fd, struct stat *s) { - struct sl_fd_info *fi = &s_sl_fds[fd]; - if (fi->fh <= 0) return set_errno(EBADF); - s->st_mode = 0666; - s->st_mode = S_IFREG | 0666; - s->st_nlink = 1; - s->st_size = fi->size; - return 0; -} - -off_t fs_slfs_lseek(int fd, off_t offset, int whence) { - if (s_sl_fds[fd].fh <= 0) return set_errno(EBADF); - switch (whence) { - case SEEK_SET: - s_sl_fds[fd].pos = offset; - break; - case SEEK_CUR: - s_sl_fds[fd].pos += offset; - break; - case SEEK_END: - return set_errno(ENOTSUP); - } - return 0; -} - -int fs_slfs_unlink(const char *pathname) { - /* - * Apply path manipulations again, in case we got here directly - * (via TI libc's "add_device"). - */ - pathname = drop_dir(pathname, NULL); - return set_errno(sl_fs_to_errno(sl_FsDel((const _u8 *) pathname, 0))); -} - -int fs_slfs_rename(const char *from, const char *to) { - return set_errno(ENOTSUP); -} - -void fs_slfs_set_new_file_size(const char *name, size_t size) { - int i; - for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) { - if (s_sl_file_size_hints[i].name == NULL) { - DBG(("File size hint: %s %d", name, (int) size)); - s_sl_file_size_hints[i].name = strdup(name); - s_sl_file_size_hints[i].size = size; - break; - } - } -} - -#endif /* defined(MG_FS_SLFS) || defined(CC3200_FS_SLFS) */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs_slfs.h b/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs_slfs.h deleted file mode 100644 index 0d8a4b1..0000000 --- a/src/mongoose-6.11/src/common/platforms/simplelink/sl_fs_slfs.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_SIMPLELINK_SL_FS_SLFS_H_ -#define CS_COMMON_PLATFORMS_SIMPLELINK_SL_FS_SLFS_H_ - -#if defined(MG_FS_SLFS) - -#include -#ifndef __TI_COMPILER_VERSION__ -#include -#include -#endif - -#define MAX_OPEN_SLFS_FILES 8 - -/* Indirect libc interface - same functions, different names. */ -int fs_slfs_open(const char *pathname, int flags, mode_t mode); -int fs_slfs_close(int fd); -ssize_t fs_slfs_read(int fd, void *buf, size_t count); -ssize_t fs_slfs_write(int fd, const void *buf, size_t count); -int fs_slfs_stat(const char *pathname, struct stat *s); -int fs_slfs_fstat(int fd, struct stat *s); -off_t fs_slfs_lseek(int fd, off_t offset, int whence); -int fs_slfs_unlink(const char *filename); -int fs_slfs_rename(const char *from, const char *to); - -void fs_slfs_set_new_file_size(const char *name, size_t size); - -#endif /* defined(MG_FS_SLFS) */ - -#endif /* CS_COMMON_PLATFORMS_SIMPLELINK_SL_FS_SLFS_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_mg_task.c b/src/mongoose-6.11/src/common/platforms/simplelink/sl_mg_task.c deleted file mode 100644 index f9c7671..0000000 --- a/src/mongoose-6.11/src/common/platforms/simplelink/sl_mg_task.c +++ /dev/null @@ -1,52 +0,0 @@ -#if MG_NET_IF == MG_NET_IF_SIMPLELINK && !defined(MG_SIMPLELINK_NO_OSI) - -#include "mg_task.h" - -#include - -enum mg_q_msg_type { - MG_Q_MSG_CB, -}; -struct mg_q_msg { - enum mg_q_msg_type type; - void (*cb)(struct mg_mgr *mgr, void *arg); - void *arg; -}; -static OsiMsgQ_t s_mg_q; -static void mg_task(void *arg); - -bool mg_start_task(int priority, int stack_size, mg_init_cb mg_init) { - if (osi_MsgQCreate(&s_mg_q, "MG", sizeof(struct mg_q_msg), 16) != OSI_OK) { - return false; - } - if (osi_TaskCreate(mg_task, (const signed char *) "MG", stack_size, - (void *) mg_init, priority, NULL) != OSI_OK) { - return false; - } - return true; -} - -static void mg_task(void *arg) { - struct mg_mgr mgr; - mg_init_cb mg_init = (mg_init_cb) arg; - mg_mgr_init(&mgr, NULL); - mg_init(&mgr); - while (1) { - struct mg_q_msg msg; - mg_mgr_poll(&mgr, 1); - if (osi_MsgQRead(&s_mg_q, &msg, 1) != OSI_OK) continue; - switch (msg.type) { - case MG_Q_MSG_CB: { - msg.cb(&mgr, msg.arg); - } - } - } -} - -void mg_run_in_task(void (*cb)(struct mg_mgr *mgr, void *arg), void *cb_arg) { - struct mg_q_msg msg = {MG_Q_MSG_CB, cb, cb_arg}; - osi_MsgQWrite(&s_mg_q, &msg, OSI_NO_WAIT); -} - -#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK && !defined(MG_SIMPLELINK_NO_OSI) \ - */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_net_if.c b/src/mongoose-6.11/src/common/platforms/simplelink/sl_net_if.c deleted file mode 100644 index 2a39d14..0000000 --- a/src/mongoose-6.11/src/common/platforms/simplelink/sl_net_if.c +++ /dev/null @@ -1,480 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#include "common/platforms/simplelink/sl_net_if.h" - -#if MG_ENABLE_NET_IF_SIMPLELINK - -#include "mongoose/src/internal.h" -#include "mongoose/src/util.h" - -#define MG_TCP_RECV_BUFFER_SIZE 1024 -#define MG_UDP_RECV_BUFFER_SIZE 1500 - -static sock_t mg_open_listening_socket(struct mg_connection *nc, - union socket_address *sa, int type, - int proto); - -void mg_set_non_blocking_mode(sock_t sock) { - SlSockNonblocking_t opt; -#if SL_MAJOR_VERSION_NUM < 2 - opt.NonblockingEnabled = 1; -#else - opt.NonBlockingEnabled = 1; -#endif - sl_SetSockOpt(sock, SL_SOL_SOCKET, SL_SO_NONBLOCKING, &opt, sizeof(opt)); -} - -static int mg_is_error(int n) { - return (n < 0 && n != SL_ERROR_BSD_EALREADY && n != SL_ERROR_BSD_EAGAIN); -} - -void mg_sl_if_connect_tcp(struct mg_connection *nc, - const union socket_address *sa) { - int proto = 0; - if (nc->flags & MG_F_SSL) proto = SL_SEC_SOCKET; - sock_t sock = sl_Socket(AF_INET, SOCK_STREAM, proto); - if (sock < 0) { - nc->err = sock; - goto out; - } - mg_sock_set(nc, sock); -#if MG_ENABLE_SSL - nc->err = sl_set_ssl_opts(sock, nc); - if (nc->err != 0) goto out; -#endif - nc->err = sl_Connect(sock, &sa->sa, sizeof(sa->sin)); -out: - DBG(("%p to %s:%d sock %d %d err %d", nc, inet_ntoa(sa->sin.sin_addr), - ntohs(sa->sin.sin_port), nc->sock, proto, nc->err)); -} - -void mg_sl_if_connect_udp(struct mg_connection *nc) { - sock_t sock = sl_Socket(AF_INET, SOCK_DGRAM, 0); - if (sock < 0) { - nc->err = sock; - return; - } - mg_sock_set(nc, sock); - nc->err = 0; -} - -int mg_sl_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) { - int proto = 0; - if (nc->flags & MG_F_SSL) proto = SL_SEC_SOCKET; - sock_t sock = mg_open_listening_socket(nc, sa, SOCK_STREAM, proto); - if (sock < 0) return sock; - mg_sock_set(nc, sock); - return 0; -} - -int mg_sl_if_listen_udp(struct mg_connection *nc, union socket_address *sa) { - sock_t sock = mg_open_listening_socket(nc, sa, SOCK_DGRAM, 0); - if (sock == INVALID_SOCKET) return (errno ? errno : 1); - mg_sock_set(nc, sock); - return 0; -} - -void mg_sl_if_tcp_send(struct mg_connection *nc, const void *buf, size_t len) { - mbuf_append(&nc->send_mbuf, buf, len); -} - -void mg_sl_if_udp_send(struct mg_connection *nc, const void *buf, size_t len) { - mbuf_append(&nc->send_mbuf, buf, len); -} - -void mg_sl_if_recved(struct mg_connection *nc, size_t len) { - (void) nc; - (void) len; -} - -int mg_sl_if_create_conn(struct mg_connection *nc) { - (void) nc; - return 1; -} - -void mg_sl_if_destroy_conn(struct mg_connection *nc) { - if (nc->sock == INVALID_SOCKET) return; - /* For UDP, only close outgoing sockets or listeners. */ - if (!(nc->flags & MG_F_UDP) || nc->listener == NULL) { - sl_Close(nc->sock); - } - nc->sock = INVALID_SOCKET; -} - -static int mg_accept_conn(struct mg_connection *lc) { - struct mg_connection *nc; - union socket_address sa; - socklen_t sa_len = sizeof(sa); - sock_t sock = sl_Accept(lc->sock, &sa.sa, &sa_len); - if (sock < 0) { - DBG(("%p: failed to accept: %d", lc, sock)); - return 0; - } - nc = mg_if_accept_new_conn(lc); - if (nc == NULL) { - sl_Close(sock); - return 0; - } - DBG(("%p conn from %s:%d", nc, inet_ntoa(sa.sin.sin_addr), - ntohs(sa.sin.sin_port))); - mg_sock_set(nc, sock); - if (nc->flags & MG_F_SSL) nc->flags |= MG_F_SSL_HANDSHAKE_DONE; - mg_if_accept_tcp_cb(nc, &sa, sa_len); - return 1; -} - -/* 'sa' must be an initialized address to bind to */ -static sock_t mg_open_listening_socket(struct mg_connection *nc, - union socket_address *sa, int type, - int proto) { - int r; - socklen_t sa_len = - (sa->sa.sa_family == AF_INET) ? sizeof(sa->sin) : sizeof(sa->sin6); - sock_t sock = sl_Socket(sa->sa.sa_family, type, proto); - if (sock < 0) return sock; - if ((r = sl_Bind(sock, &sa->sa, sa_len)) < 0) goto clean; - if (type != SOCK_DGRAM) { -#if MG_ENABLE_SSL - if ((r = sl_set_ssl_opts(sock, nc)) < 0) goto clean; -#endif - if ((r = sl_Listen(sock, SOMAXCONN)) < 0) goto clean; - } - mg_set_non_blocking_mode(sock); -clean: - if (r < 0) { - sl_Close(sock); - sock = r; - } - return sock; -} - -static void mg_write_to_socket(struct mg_connection *nc) { - struct mbuf *io = &nc->send_mbuf; - int n = 0; - - if (nc->flags & MG_F_UDP) { - n = sl_SendTo(nc->sock, io->buf, io->len, 0, &nc->sa.sa, - sizeof(nc->sa.sin)); - DBG(("%p %d %d %d %s:%hu", nc, nc->sock, n, errno, - inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port))); - } else { - n = (int) sl_Send(nc->sock, io->buf, io->len, 0); - DBG(("%p %d bytes -> %d", nc, n, nc->sock)); - } - - if (n > 0) { - mg_if_sent_cb(nc, n); - } else if (n < 0 && mg_is_error(n)) { - /* Something went wrong, drop the connection. */ - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } -} - -MG_INTERNAL size_t recv_avail_size(struct mg_connection *conn, size_t max) { - size_t avail; - if (conn->recv_mbuf_limit < conn->recv_mbuf.len) return 0; - avail = conn->recv_mbuf_limit - conn->recv_mbuf.len; - return avail > max ? max : avail; -} - -static void mg_handle_tcp_read(struct mg_connection *conn) { - int n = 0; - char *buf = (char *) MG_MALLOC(MG_TCP_RECV_BUFFER_SIZE); - - if (buf == NULL) { - DBG(("OOM")); - return; - } - - n = (int) sl_Recv(conn->sock, buf, - recv_avail_size(conn, MG_TCP_RECV_BUFFER_SIZE), 0); - DBG(("%p %d bytes <- %d", conn, n, conn->sock)); - if (n > 0) { - mg_if_recv_tcp_cb(conn, buf, n, 1 /* own */); - } else { - MG_FREE(buf); - } - if (n == 0) { - /* Orderly shutdown of the socket, try flushing output. */ - conn->flags |= MG_F_SEND_AND_CLOSE; - } else if (mg_is_error(n)) { - conn->flags |= MG_F_CLOSE_IMMEDIATELY; - } -} - -static void mg_handle_udp_read(struct mg_connection *nc) { - char *buf = (char *) MG_MALLOC(MG_UDP_RECV_BUFFER_SIZE); - if (buf == NULL) return; - union socket_address sa; - socklen_t sa_len = sizeof(sa); - int n = sl_RecvFrom(nc->sock, buf, MG_UDP_RECV_BUFFER_SIZE, 0, - (SlSockAddr_t *) &sa, &sa_len); - DBG(("%p %d bytes from %s:%d", nc, n, inet_ntoa(nc->sa.sin.sin_addr), - ntohs(nc->sa.sin.sin_port))); - if (n > 0) { - mg_if_recv_udp_cb(nc, buf, n, &sa, sa_len); - } else { - MG_FREE(buf); - } -} - -#define _MG_F_FD_CAN_READ 1 -#define _MG_F_FD_CAN_WRITE 1 << 1 -#define _MG_F_FD_ERROR 1 << 2 - -void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { - DBG(("%p fd=%d fd_flags=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, - fd_flags, nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); - - if (nc->flags & MG_F_CONNECTING) { - if (nc->flags & MG_F_UDP || nc->err != SL_ERROR_BSD_EALREADY) { - mg_if_connect_cb(nc, nc->err); - } else { - /* In SimpleLink, to get status of non-blocking connect() we need to wait - * until socket is writable and repeat the call to sl_Connect again, - * which will now return the real status. */ - if (fd_flags & _MG_F_FD_CAN_WRITE) { - nc->err = sl_Connect(nc->sock, &nc->sa.sa, sizeof(nc->sa.sin)); - DBG(("%p conn res=%d", nc, nc->err)); - if (nc->err == SL_ERROR_BSD_ESECSNOVERIFY || - /* TODO(rojer): Provide API to set the date for verification. */ - nc->err == SL_ERROR_BSD_ESECDATEERROR -#if SL_MAJOR_VERSION_NUM >= 2 - /* Per SWRU455, this error does not mean verification failed, - * it only means that the cert used is not present in the trusted - * root CA catalog. Which is perfectly fine. */ - || - nc->err == SL_ERROR_BSD_ESECUNKNOWNROOTCA -#endif - ) { - nc->err = 0; - } - if (nc->flags & MG_F_SSL && nc->err == 0) { - nc->flags |= MG_F_SSL_HANDSHAKE_DONE; - } - mg_if_connect_cb(nc, nc->err); - } - } - /* Ignore read/write in further processing, we've handled it. */ - fd_flags &= ~(_MG_F_FD_CAN_READ | _MG_F_FD_CAN_WRITE); - } - - if (fd_flags & _MG_F_FD_CAN_READ) { - if (nc->flags & MG_F_UDP) { - mg_handle_udp_read(nc); - } else { - if (nc->flags & MG_F_LISTENING) { - mg_accept_conn(nc); - } else { - mg_handle_tcp_read(nc); - } - } - } - - if (!(nc->flags & MG_F_CLOSE_IMMEDIATELY)) { - if ((fd_flags & _MG_F_FD_CAN_WRITE) && nc->send_mbuf.len > 0) { - mg_write_to_socket(nc); - } - - if (!(fd_flags & (_MG_F_FD_CAN_READ | _MG_F_FD_CAN_WRITE))) { - mg_if_poll(nc, now); - } - mg_if_timer(nc, now); - } - - DBG(("%p after fd=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, nc->flags, - (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); -} - -/* Associate a socket to a connection. */ -void mg_sl_if_sock_set(struct mg_connection *nc, sock_t sock) { - mg_set_non_blocking_mode(sock); - nc->sock = sock; - DBG(("%p %d", nc, sock)); -} - -void mg_sl_if_init(struct mg_iface *iface) { - (void) iface; - DBG(("%p using sl_Select()", iface->mgr)); -} - -void mg_sl_if_free(struct mg_iface *iface) { - (void) iface; -} - -void mg_sl_if_add_conn(struct mg_connection *nc) { - (void) nc; -} - -void mg_sl_if_remove_conn(struct mg_connection *nc) { - (void) nc; -} - -time_t mg_sl_if_poll(struct mg_iface *iface, int timeout_ms) { - struct mg_mgr *mgr = iface->mgr; - double now = mg_time(); - double min_timer; - struct mg_connection *nc, *tmp; - struct SlTimeval_t tv; - SlFdSet_t read_set, write_set, err_set; - sock_t max_fd = INVALID_SOCKET; - int num_fds, num_ev = 0, num_timers = 0; - - SL_SOCKET_FD_ZERO(&read_set); - SL_SOCKET_FD_ZERO(&write_set); - SL_SOCKET_FD_ZERO(&err_set); - - /* - * Note: it is ok to have connections with sock == INVALID_SOCKET in the list, - * e.g. timer-only "connections". - */ - min_timer = 0; - for (nc = mgr->active_connections, num_fds = 0; nc != NULL; nc = tmp) { - tmp = nc->next; - - if (nc->sock != INVALID_SOCKET) { - num_fds++; - - if (!(nc->flags & MG_F_WANT_WRITE) && - nc->recv_mbuf.len < nc->recv_mbuf_limit && - (!(nc->flags & MG_F_UDP) || nc->listener == NULL)) { - SL_SOCKET_FD_SET(nc->sock, &read_set); - if (max_fd == INVALID_SOCKET || nc->sock > max_fd) max_fd = nc->sock; - } - - if (((nc->flags & MG_F_CONNECTING) && !(nc->flags & MG_F_WANT_READ)) || - (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING))) { - SL_SOCKET_FD_SET(nc->sock, &write_set); - SL_SOCKET_FD_SET(nc->sock, &err_set); - if (max_fd == INVALID_SOCKET || nc->sock > max_fd) max_fd = nc->sock; - } - } - - if (nc->ev_timer_time > 0) { - if (num_timers == 0 || nc->ev_timer_time < min_timer) { - min_timer = nc->ev_timer_time; - } - num_timers++; - } - } - - /* - * If there is a timer to be fired earlier than the requested timeout, - * adjust the timeout. - */ - if (num_timers > 0) { - double timer_timeout_ms = (min_timer - mg_time()) * 1000 + 1 /* rounding */; - if (timer_timeout_ms < timeout_ms) { - timeout_ms = timer_timeout_ms; - } - } - if (timeout_ms < 0) timeout_ms = 0; - - tv.tv_sec = timeout_ms / 1000; - tv.tv_usec = (timeout_ms % 1000) * 1000; - - if (num_fds > 0) { - num_ev = sl_Select((int) max_fd + 1, &read_set, &write_set, &err_set, &tv); - } - - now = mg_time(); - DBG(("sl_Select @ %ld num_ev=%d of %d, timeout=%d", (long) now, num_ev, - num_fds, timeout_ms)); - - for (nc = mgr->active_connections; nc != NULL; nc = tmp) { - int fd_flags = 0; - if (nc->sock != INVALID_SOCKET) { - if (num_ev > 0) { - fd_flags = - (SL_SOCKET_FD_ISSET(nc->sock, &read_set) && - (!(nc->flags & MG_F_UDP) || nc->listener == NULL) - ? _MG_F_FD_CAN_READ - : 0) | - (SL_SOCKET_FD_ISSET(nc->sock, &write_set) ? _MG_F_FD_CAN_WRITE - : 0) | - (SL_SOCKET_FD_ISSET(nc->sock, &err_set) ? _MG_F_FD_ERROR : 0); - } - /* SimpleLink does not report UDP sockets as writable. */ - if (nc->flags & MG_F_UDP && nc->send_mbuf.len > 0) { - fd_flags |= _MG_F_FD_CAN_WRITE; - } - } - tmp = nc->next; - mg_mgr_handle_conn(nc, fd_flags, now); - } - - for (nc = mgr->active_connections; nc != NULL; nc = tmp) { - tmp = nc->next; - if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || - (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) { - mg_close_conn(nc); - } - } - - return now; -} - -void mg_sl_if_get_conn_addr(struct mg_connection *nc, int remote, - union socket_address *sa) { - /* SimpleLink does not provide a way to get socket's peer address after - * accept or connect. Address should have been preserved in the connection, - * so we do our best here by using it. */ - if (remote) memcpy(sa, &nc->sa, sizeof(*sa)); -} - -void sl_restart_cb(struct mg_mgr *mgr) { - /* - * SimpleLink has been restarted, meaning all sockets have been invalidated. - * We try our best - we'll restart the listeners, but for outgoing - * connections we have no option but to terminate. - */ - struct mg_connection *nc; - for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) { - if (nc->sock == INVALID_SOCKET) continue; /* Could be a timer */ - if (nc->flags & MG_F_LISTENING) { - DBG(("restarting %p %s:%d", nc, inet_ntoa(nc->sa.sin.sin_addr), - ntohs(nc->sa.sin.sin_port))); - int res = (nc->flags & MG_F_UDP ? mg_sl_if_listen_udp(nc, &nc->sa) - : mg_sl_if_listen_tcp(nc, &nc->sa)); - if (res == 0) continue; - /* Well, we tried and failed. Fall through to closing. */ - } - nc->sock = INVALID_SOCKET; - DBG(("terminating %p %s:%d", nc, inet_ntoa(nc->sa.sin.sin_addr), - ntohs(nc->sa.sin.sin_port))); - /* TODO(rojer): Outgoing UDP? */ - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } -} - -/* clang-format off */ -#define MG_SL_IFACE_VTABLE \ - { \ - mg_sl_if_init, \ - mg_sl_if_free, \ - mg_sl_if_add_conn, \ - mg_sl_if_remove_conn, \ - mg_sl_if_poll, \ - mg_sl_if_listen_tcp, \ - mg_sl_if_listen_udp, \ - mg_sl_if_connect_tcp, \ - mg_sl_if_connect_udp, \ - mg_sl_if_tcp_send, \ - mg_sl_if_udp_send, \ - mg_sl_if_recved, \ - mg_sl_if_create_conn, \ - mg_sl_if_destroy_conn, \ - mg_sl_if_sock_set, \ - mg_sl_if_get_conn_addr, \ - } -/* clang-format on */ - -const struct mg_iface_vtable mg_simplelink_iface_vtable = MG_SL_IFACE_VTABLE; -#if MG_NET_IF == MG_NET_IF_SIMPLELINK -const struct mg_iface_vtable mg_default_iface_vtable = MG_SL_IFACE_VTABLE; -#endif - -#endif /* MG_ENABLE_NET_IF_SIMPLELINK */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_net_if.h b/src/mongoose-6.11/src/common/platforms/simplelink/sl_net_if.h deleted file mode 100644 index aa49d5d..0000000 --- a/src/mongoose-6.11/src/common/platforms/simplelink/sl_net_if.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_SIMPLELINK_SL_NET_IF_H_ -#define CS_COMMON_PLATFORMS_SIMPLELINK_SL_NET_IF_H_ - -#include "mongoose/src/net_if.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifndef MG_ENABLE_NET_IF_SIMPLELINK -#define MG_ENABLE_NET_IF_SIMPLELINK MG_NET_IF == MG_NET_IF_SIMPLELINK -#endif - -extern const struct mg_iface_vtable mg_simplelink_iface_vtable; - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* CS_COMMON_PLATFORMS_SIMPLELINK_SL_NET_IF_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_socket.c b/src/mongoose-6.11/src/common/platforms/simplelink/sl_socket.c deleted file mode 100644 index e4a0c26..0000000 --- a/src/mongoose-6.11/src/common/platforms/simplelink/sl_socket.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#if MG_NET_IF == MG_NET_IF_SIMPLELINK - -#include -#include - -#include "common/platform.h" - -const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) { - int res; - struct in_addr *in = (struct in_addr *) src; - if (af != AF_INET) { - errno = ENOTSUP; - return NULL; - } - res = snprintf(dst, size, "%lu.%lu.%lu.%lu", SL_IPV4_BYTE(in->s_addr, 0), - SL_IPV4_BYTE(in->s_addr, 1), SL_IPV4_BYTE(in->s_addr, 2), - SL_IPV4_BYTE(in->s_addr, 3)); - return res > 0 ? dst : NULL; -} - -char *inet_ntoa(struct in_addr n) { - static char a[16]; - return (char *) inet_ntop(AF_INET, &n, a, sizeof(a)); -} - -int inet_pton(int af, const char *src, void *dst) { - uint32_t a0, a1, a2, a3; - uint8_t *db = (uint8_t *) dst; - if (af != AF_INET) { - errno = ENOTSUP; - return 0; - } - if (sscanf(src, "%lu.%lu.%lu.%lu", &a0, &a1, &a2, &a3) != 4) { - return 0; - } - *db = a3; - *(db + 1) = a2; - *(db + 2) = a1; - *(db + 3) = a0; - return 1; -} - -#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */ diff --git a/src/mongoose-6.11/src/common/platforms/simplelink/sl_ssl_if.c b/src/mongoose-6.11/src/common/platforms/simplelink/sl_ssl_if.c deleted file mode 100644 index 99642b4..0000000 --- a/src/mongoose-6.11/src/common/platforms/simplelink/sl_ssl_if.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_SIMPLELINK - -#include "common/mg_mem.h" - -#ifndef MG_SSL_IF_SIMPLELINK_SLFS_PREFIX -#define MG_SSL_IF_SIMPLELINK_SLFS_PREFIX "SL:" -#endif - -#define MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN \ - (sizeof(MG_SSL_IF_SIMPLELINK_SLFS_PREFIX) - 1) - -struct mg_ssl_if_ctx { - char *ssl_cert; - char *ssl_key; - char *ssl_ca_cert; - char *ssl_server_name; -}; - -void mg_ssl_if_init() { -} - -enum mg_ssl_if_result mg_ssl_if_conn_init( - struct mg_connection *nc, const struct mg_ssl_if_conn_params *params, - const char **err_msg) { - struct mg_ssl_if_ctx *ctx = - (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx)); - if (ctx == NULL) { - MG_SET_PTRPTR(err_msg, "Out of memory"); - return MG_SSL_ERROR; - } - nc->ssl_if_data = ctx; - - if (params->cert != NULL || params->key != NULL) { - if (params->cert != NULL && params->key != NULL) { - ctx->ssl_cert = strdup(params->cert); - ctx->ssl_key = strdup(params->key); - } else { - MG_SET_PTRPTR(err_msg, "Both cert and key are required."); - return MG_SSL_ERROR; - } - } - if (params->ca_cert != NULL && strcmp(params->ca_cert, "*") != 0) { - ctx->ssl_ca_cert = strdup(params->ca_cert); - } - /* TODO(rojer): cipher_suites. */ - if (params->server_name != NULL) { - ctx->ssl_server_name = strdup(params->server_name); - } - return MG_SSL_OK; -} - -void mg_ssl_if_conn_close_notify(struct mg_connection *nc) { - /* Nothing to do */ - (void) nc; -} - -void mg_ssl_if_conn_free(struct mg_connection *nc) { - struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; - if (ctx == NULL) return; - nc->ssl_if_data = NULL; - MG_FREE(ctx->ssl_cert); - MG_FREE(ctx->ssl_key); - MG_FREE(ctx->ssl_ca_cert); - MG_FREE(ctx->ssl_server_name); - memset(ctx, 0, sizeof(*ctx)); - MG_FREE(ctx); -} - -bool pem_to_der(const char *pem_file, const char *der_file) { - bool ret = false; - FILE *pf = NULL, *df = NULL; - bool writing = false; - pf = fopen(pem_file, "r"); - if (pf == NULL) goto clean; - remove(der_file); - fs_slfs_set_new_file_size(der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN, - 2048); - df = fopen(der_file, "w"); - if (df == NULL) goto clean; - while (1) { - char pem_buf[70]; - char der_buf[48]; - if (!fgets(pem_buf, sizeof(pem_buf), pf)) break; - if (writing) { - if (strstr(pem_buf, "-----END ") != NULL) { - ret = true; - break; - } - int l = 0; - while (!isspace((unsigned int) pem_buf[l])) l++; - int der_len = 0; - cs_base64_decode((const unsigned char *) pem_buf, sizeof(pem_buf), - der_buf, &der_len); - if (der_len <= 0) break; - if (fwrite(der_buf, 1, der_len, df) != der_len) break; - } else if (strstr(pem_buf, "-----BEGIN ") != NULL) { - writing = true; - } - } - -clean: - if (pf != NULL) fclose(pf); - if (df != NULL) { - fclose(df); - if (!ret) remove(der_file); - } - return ret; -} - -#if MG_ENABLE_FILESYSTEM && defined(MG_FS_SLFS) -/* If the file's extension is .pem, convert it to DER format and put on SLFS. */ -static char *sl_pem2der(const char *pem_file) { - const char *pem_ext = strstr(pem_file, ".pem"); - if (pem_ext == NULL || *(pem_ext + 4) != '\0') { - return strdup(pem_file); - } - char *der_file = NULL; - /* DER file must be located on SLFS, add prefix. */ - int l = mg_asprintf(&der_file, 0, MG_SSL_IF_SIMPLELINK_SLFS_PREFIX "%.*s.der", - (int) (pem_ext - pem_file), pem_file); - if (der_file == NULL) return NULL; - bool result = false; - cs_stat_t st; - if (mg_stat(der_file, &st) != 0) { - result = pem_to_der(pem_file, der_file); - LOG(LL_DEBUG, ("%s -> %s = %d", pem_file, der_file, result)); - } else { - /* File exists, assume it's already been converted. */ - result = true; - } - if (result) { - /* Strip the SL: prefix we added since NWP does not expect it. */ - memmove(der_file, der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN, - l - 2 /* including \0 */); - } else { - MG_FREE(der_file); - der_file = NULL; - } - return der_file; -} -#else -static char *sl_pem2der(const char *pem_file) { - return strdup(pem_file); -} -#endif - -int sl_set_ssl_opts(int sock, struct mg_connection *nc) { - int err; - const struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; - DBG(("%p ssl ctx: %p", nc, ctx)); - - if (ctx != NULL) { - DBG(("%p %s,%s,%s,%s", nc, (ctx->ssl_cert ? ctx->ssl_cert : "-"), - (ctx->ssl_key ? ctx->ssl_cert : "-"), - (ctx->ssl_ca_cert ? ctx->ssl_ca_cert : "-"), - (ctx->ssl_server_name ? ctx->ssl_server_name : "-"))); - if (ctx->ssl_cert != NULL && ctx->ssl_key != NULL) { - char *ssl_cert = sl_pem2der(ctx->ssl_cert); - char *ssl_key = sl_pem2der(ctx->ssl_key); - if (ssl_cert != NULL && ssl_key != NULL) { - err = sl_SetSockOpt(sock, SL_SOL_SOCKET, - SL_SO_SECURE_FILES_CERTIFICATE_FILE_NAME, ssl_cert, - strlen(ssl_cert)); - LOG(LL_INFO, ("CERTIFICATE_FILE_NAME %s -> %d", ssl_cert, err)); - err = sl_SetSockOpt(sock, SL_SOL_SOCKET, - SL_SO_SECURE_FILES_PRIVATE_KEY_FILE_NAME, ssl_key, - strlen(ssl_key)); - LOG(LL_INFO, ("PRIVATE_KEY_FILE_NAME %s -> %d", ssl_key, err)); - } else { - err = -1; - } - MG_FREE(ssl_cert); - MG_FREE(ssl_key); - if (err != 0) return err; - } - if (ctx->ssl_ca_cert != NULL) { - if (ctx->ssl_ca_cert[0] != '\0') { - char *ssl_ca_cert = sl_pem2der(ctx->ssl_ca_cert); - if (ssl_ca_cert != NULL) { - err = sl_SetSockOpt(sock, SL_SOL_SOCKET, - SL_SO_SECURE_FILES_CA_FILE_NAME, ssl_ca_cert, - strlen(ssl_ca_cert)); - LOG(LL_INFO, ("CA_FILE_NAME %s -> %d", ssl_ca_cert, err)); - } else { - err = -1; - } - MG_FREE(ssl_ca_cert); - if (err != 0) return err; - } - } - if (ctx->ssl_server_name != NULL) { - err = sl_SetSockOpt(sock, SL_SOL_SOCKET, - SL_SO_SECURE_DOMAIN_NAME_VERIFICATION, - ctx->ssl_server_name, strlen(ctx->ssl_server_name)); - DBG(("DOMAIN_NAME_VERIFICATION %s -> %d", ctx->ssl_server_name, err)); - /* Domain name verificationw as added in a NWP service pack, older - * versions return SL_ERROR_BSD_ENOPROTOOPT. There isn't much we can do - * about it, - * so we ignore the error. */ - if (err != 0 && err != SL_ERROR_BSD_ENOPROTOOPT) return err; - } - } - return 0; -} - -#endif /* MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_SIMPLELINK */ diff --git a/src/mongoose-6.11/src/common/platforms/stm32/LICENSE b/src/mongoose-6.11/src/common/platforms/stm32/LICENSE deleted file mode 100644 index 3d464ca..0000000 --- a/src/mongoose-6.11/src/common/platforms/stm32/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -Copyright (c) 2011 The stlink project (github.com/texane/stlink) contributors. - All rights reserved. -Copyright (c) 2011 The "Capt'ns Missing Link" Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Martin Capitanio nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/mongoose-6.11/src/common/platforms/stm32/common.c b/src/mongoose-6.11/src/common/platforms/stm32/common.c deleted file mode 100644 index 9a201ca..0000000 --- a/src/mongoose-6.11/src/common/platforms/stm32/common.c +++ /dev/null @@ -1,869 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "stlink.h" -#include "usb.h" - -#ifndef _WIN32 -#define O_BINARY 0 //! @todo get rid of this OH MY (@xor-gate) -#endif - -/* todo: stm32l15xxx flash memory, pm0062 manual */ - -/* stm32f FPEC flash controller interface, pm0063 manual */ -// TODO - all of this needs to be abstracted out.... -// STM32F05x is identical, based on RM0091 (DM00031936, Doc ID 018940 Rev 2, -// August 2012) -#define FLASH_REGS_ADDR 0x40022000 -#define FLASH_REGS_SIZE 0x28 - -#define FLASH_ACR (FLASH_REGS_ADDR + 0x00) -#define FLASH_KEYR (FLASH_REGS_ADDR + 0x04) -#define FLASH_SR (FLASH_REGS_ADDR + 0x0c) -#define FLASH_CR (FLASH_REGS_ADDR + 0x10) -#define FLASH_AR (FLASH_REGS_ADDR + 0x14) -#define FLASH_OBR (FLASH_REGS_ADDR + 0x1c) -#define FLASH_WRPR (FLASH_REGS_ADDR + 0x20) - -// For STM32F05x, the RDPTR_KEY may be wrong, but as it is not used anywhere... -#define FLASH_RDPTR_KEY 0x00a5 -#define FLASH_KEY1 0x45670123 -#define FLASH_KEY2 0xcdef89ab - -#define FLASH_SR_BSY 0 -#define FLASH_SR_EOP 5 - -#define FLASH_CR_PG 0 -#define FLASH_CR_PER 1 -#define FLASH_CR_MER 2 -#define FLASH_CR_STRT 6 -#define FLASH_CR_LOCK 7 - -// 32L = 32F1 same CoreID as 32F4! -#define STM32L_FLASH_REGS_ADDR ((uint32_t) 0x40023c00) -#define STM32L_FLASH_ACR (STM32L_FLASH_REGS_ADDR + 0x00) -#define STM32L_FLASH_PECR (STM32L_FLASH_REGS_ADDR + 0x04) -#define STM32L_FLASH_PDKEYR (STM32L_FLASH_REGS_ADDR + 0x08) -#define STM32L_FLASH_PEKEYR (STM32L_FLASH_REGS_ADDR + 0x0c) -#define STM32L_FLASH_PRGKEYR (STM32L_FLASH_REGS_ADDR + 0x10) -#define STM32L_FLASH_OPTKEYR (STM32L_FLASH_REGS_ADDR + 0x14) -#define STM32L_FLASH_SR (STM32L_FLASH_REGS_ADDR + 0x18) -#define STM32L_FLASH_OBR (STM32L_FLASH_REGS_ADDR + 0x1c) -#define STM32L_FLASH_WRPR (STM32L_FLASH_REGS_ADDR + 0x20) -#define FLASH_L1_FPRG 10 -#define FLASH_L1_PROG 3 - -// 32L4 register base is at FLASH_REGS_ADDR (0x40022000) -#define STM32L4_FLASH_KEYR (FLASH_REGS_ADDR + 0x08) -#define STM32L4_FLASH_SR (FLASH_REGS_ADDR + 0x10) -#define STM32L4_FLASH_CR (FLASH_REGS_ADDR + 0x14) -#define STM32L4_FLASH_OPTR (FLASH_REGS_ADDR + 0x20) - -#define STM32L4_FLASH_SR_BSY 16 -#define STM32L4_FLASH_SR_ERRMASK 0x3f8 /* SR [9:3] */ - -#define STM32L4_FLASH_CR_LOCK 31 /* Lock control register */ -#define STM32L4_FLASH_CR_PG 0 /* Program */ -#define STM32L4_FLASH_CR_PER 1 /* Page erase */ -#define STM32L4_FLASH_CR_MER1 2 /* Bank 1 erase */ -#define STM32L4_FLASH_CR_MER2 15 /* Bank 2 erase */ -#define STM32L4_FLASH_CR_STRT 16 /* Start command */ -#define STM32L4_FLASH_CR_BKER 11 /* Bank select for page erase */ -#define STM32L4_FLASH_CR_PNB 3 /* Page number (8 bits) */ -// Bits requesting flash operations (useful when we want to clear them) -#define STM32L4_FLASH_CR_OPBITS \ - ((1lu << STM32L4_FLASH_CR_PG) | (1lu << STM32L4_FLASH_CR_PER) | \ - (1lu << STM32L4_FLASH_CR_MER1) | (1lu << STM32L4_FLASH_CR_MER1)) -// Page is fully specified by BKER and PNB -#define STM32L4_FLASH_CR_PAGEMASK (0x1fflu << STM32L4_FLASH_CR_PNB) - -#define STM32L4_FLASH_OPTR_DUALBANK 21 - -// STM32L0x flash register base and offsets -// same as 32L1 above -// RM0090 - DM00031020.pdf -#define STM32L0_FLASH_REGS_ADDR ((uint32_t) 0x40022000) -#define FLASH_ACR_OFF ((uint32_t) 0x00) -#define FLASH_PECR_OFF ((uint32_t) 0x04) -#define FLASH_PDKEYR_OFF ((uint32_t) 0x08) -#define FLASH_PEKEYR_OFF ((uint32_t) 0x0c) -#define FLASH_PRGKEYR_OFF ((uint32_t) 0x10) -#define FLASH_OPTKEYR_OFF ((uint32_t) 0x14) -#define FLASH_SR_OFF ((uint32_t) 0x18) -#define FLASH_OBR_OFF ((uint32_t) 0x1c) -#define FLASH_WRPR_OFF ((uint32_t) 0x20) - -// STM32F4 -#define FLASH_F4_REGS_ADDR ((uint32_t) 0x40023c00) -#define FLASH_F4_KEYR (FLASH_F4_REGS_ADDR + 0x04) -#define FLASH_F4_OPT_KEYR (FLASH_F4_REGS_ADDR + 0x08) -#define FLASH_F4_SR (FLASH_F4_REGS_ADDR + 0x0c) -#define FLASH_F4_CR (FLASH_F4_REGS_ADDR + 0x10) -#define FLASH_F4_OPT_CR (FLASH_F4_REGS_ADDR + 0x14) -#define FLASH_F4_CR_STRT 16 -#define FLASH_F4_CR_LOCK 31 -#define FLASH_F4_CR_SER 1 -#define FLASH_F4_CR_SNB 3 -#define FLASH_F4_CR_SNB_MASK 0xf8 -#define FLASH_F4_SR_BSY 16 - -#define L1_WRITE_BLOCK_SIZE 0x80 -#define L0_WRITE_BLOCK_SIZE 0x40 - -#define STLINK_REG_CM3_CPUID 0xE000ED00 -#define STLINK_REG_CM3_FP_CTRL 0xE0002000 -#define STLINK_REG_CM3_FP_COMP0 0xE0002008 - -/* Cortexâ„¢-M3 Technical Reference Manual */ -/* Debug Halting Control and Status Register */ -#define STLINK_REG_DHCSR 0xe000edf0 -#define STLINK_REG_DHCSR_DBGKEY 0xa05f0000 -#define STLINK_REG_DCRSR 0xe000edf4 -#define STLINK_REG_DCRDR 0xe000edf8 - -struct stlink_chipid_params { - uint32_t chip_id; - char *description; - uint32_t flash_size_reg; - uint32_t flash_pagesize; - uint32_t sram_size; - uint32_t bootrom_base; - uint32_t bootrom_size; -}; - -void write_uint32(unsigned char *buf, uint32_t ui) { - if (!is_bigendian()) { // le -> le (don't swap) - buf[0] = ((unsigned char *) &ui)[0]; - buf[1] = ((unsigned char *) &ui)[1]; - buf[2] = ((unsigned char *) &ui)[2]; - buf[3] = ((unsigned char *) &ui)[3]; - } else { - buf[0] = ((unsigned char *) &ui)[3]; - buf[1] = ((unsigned char *) &ui)[2]; - buf[2] = ((unsigned char *) &ui)[1]; - buf[3] = ((unsigned char *) &ui)[0]; - } -} - -void write_uint16(unsigned char *buf, uint16_t ui) { - if (!is_bigendian()) { // le -> le (don't swap) - buf[0] = ((unsigned char *) &ui)[0]; - buf[1] = ((unsigned char *) &ui)[1]; - } else { - buf[0] = ((unsigned char *) &ui)[1]; - buf[1] = ((unsigned char *) &ui)[0]; - } -} - -uint32_t read_uint32(const unsigned char *c, const int pt) { - uint32_t ui; - char *p = (char *) &ui; - - if (!is_bigendian()) { // le -> le (don't swap) - p[0] = c[pt + 0]; - p[1] = c[pt + 1]; - p[2] = c[pt + 2]; - p[3] = c[pt + 3]; - } else { - p[0] = c[pt + 3]; - p[1] = c[pt + 2]; - p[2] = c[pt + 1]; - p[3] = c[pt + 0]; - } - return ui; -} - -static uint32_t __attribute__((unused)) read_flash_rdp(stlink_t *sl) { - uint32_t rdp; - stlink_read_debug32(sl, FLASH_WRPR, &rdp); - return rdp & 0xff; -} - -static inline uint32_t read_flash_cr(stlink_t *sl) { - uint32_t res; - stlink_read_debug32(sl, FLASH_F4_CR, &res); - return res; -} - -static inline unsigned int is_flash_locked(stlink_t *sl) { - /* return non zero for true */ - uint32_t cr_lock_shift = FLASH_F4_CR_LOCK, cr = read_flash_cr(sl); - return cr & (1 << cr_lock_shift); -} - -static void unlock_flash(stlink_t *sl) { - uint32_t key_reg = FLASH_F4_KEYR; - stlink_write_debug32(sl, key_reg, FLASH_KEY1); - stlink_write_debug32(sl, key_reg, FLASH_KEY2); -} - -static int unlock_flash_if(stlink_t *sl) { - /* unlock flash if already locked */ - - if (is_flash_locked(sl)) { - unlock_flash(sl); - if (is_flash_locked(sl)) { - printf("Failed to unlock flash!\n"); - return -1; - } - } - return 0; -} - -static void lock_flash(stlink_t *sl) { - uint32_t n; - uint32_t cr_reg = FLASH_F4_CR; - uint32_t cr_lock_shift = FLASH_F4_CR_LOCK; - - n = read_flash_cr(sl) | (1 << cr_lock_shift); - stlink_write_debug32(sl, cr_reg, n); -} - -static void set_flash_cr_pg(stlink_t *sl) { - uint32_t cr_reg, x; - - x = read_flash_cr(sl); - - cr_reg = FLASH_F4_CR; - x |= 1 << FLASH_CR_PG; - - stlink_write_debug32(sl, cr_reg, x); -} - -static void __attribute__((unused)) clear_flash_cr_pg(stlink_t *sl) { - uint32_t cr_reg, n; - - cr_reg = FLASH_F4_CR; - - n = read_flash_cr(sl) & ~(1 << FLASH_CR_PG); - stlink_write_debug32(sl, cr_reg, n); -} - -static void set_flash_cr_per(stlink_t *sl) { - const uint32_t n = 1 << FLASH_CR_PER; - stlink_write_debug32(sl, FLASH_CR, n); -} - -static void __attribute__((unused)) clear_flash_cr_per(stlink_t *sl) { - const uint32_t n = read_flash_cr(sl) & ~(1 << FLASH_CR_PER); - stlink_write_debug32(sl, FLASH_CR, n); -} - -static void set_flash_cr_mer(stlink_t *sl, bool v) { - uint32_t val, cr_reg, cr_mer, cr_pg; - - cr_reg = FLASH_F4_CR; - cr_mer = 1 << FLASH_CR_MER; - cr_pg = 1 << FLASH_CR_PG; - - stlink_read_debug32(sl, cr_reg, &val); - if (val & cr_pg) { - /* STM32F030 will drop MER bit if PG was set */ - val &= ~cr_pg; - stlink_write_debug32(sl, cr_reg, val); - } - - if (v) - val |= cr_mer; - else - val &= ~cr_mer; - stlink_write_debug32(sl, cr_reg, val); -} - -static void __attribute__((unused)) clear_flash_cr_mer(stlink_t *sl) { - uint32_t val, cr_reg, cr_mer; - - cr_reg = FLASH_F4_CR; - cr_mer = 1 << FLASH_CR_MER; - - stlink_read_debug32(sl, cr_reg, &val); - val &= ~cr_mer; - stlink_write_debug32(sl, cr_reg, val); -} - -static void set_flash_cr_strt(stlink_t *sl) { - uint32_t val, cr_reg, cr_strt; - - cr_reg = FLASH_F4_CR; - cr_strt = 1 << FLASH_F4_CR_STRT; - - stlink_read_debug32(sl, cr_reg, &val); - val |= cr_strt; - stlink_write_debug32(sl, cr_reg, val); -} - -static inline uint32_t read_flash_sr(stlink_t *sl) { - uint32_t res, sr_reg; - sr_reg = FLASH_F4_SR; - stlink_read_debug32(sl, sr_reg, &res); - return res; -} - -static inline unsigned int is_flash_busy(stlink_t *sl) { - uint32_t sr_busy_shift; - sr_busy_shift = FLASH_F4_SR_BSY; - return read_flash_sr(sl) & (1 << sr_busy_shift); -} - -static void wait_flash_busy(stlink_t *sl) { - /* todo: add some delays here */ - while (is_flash_busy(sl)) - ; -} - -static void wait_flash_busy_progress(stlink_t *sl) { - while (is_flash_busy(sl)) { - usleep(10000); - } -} - -static inline unsigned int is_flash_eop(stlink_t *sl) { - return read_flash_sr(sl) & (1 << FLASH_SR_EOP); -} - -static void __attribute__((unused)) clear_flash_sr_eop(stlink_t *sl) { - const uint32_t n = read_flash_sr(sl) & ~(1 << FLASH_SR_EOP); - stlink_write_debug32(sl, FLASH_SR, n); -} - -static void __attribute__((unused)) wait_flash_eop(stlink_t *sl) { - /* todo: add some delays here */ - while (is_flash_eop(sl) == 0) - ; -} - -static inline void write_flash_ar(stlink_t *sl, uint32_t n) { - stlink_write_debug32(sl, FLASH_AR, n); -} - -static inline void write_flash_cr_psiz(stlink_t *sl, uint32_t n) { - uint32_t x = read_flash_cr(sl); - x &= ~(0x03 << 8); - x |= (n << 8); - stlink_write_debug32(sl, FLASH_F4_CR, x); -} - -static inline void write_flash_cr_snb(stlink_t *sl, uint32_t n) { - uint32_t x = read_flash_cr(sl); - x &= ~FLASH_F4_CR_SNB_MASK; - x |= (n << FLASH_F4_CR_SNB); - x |= (1 << FLASH_F4_CR_SER); - stlink_write_debug32(sl, FLASH_F4_CR, x); -} - -static inline void write_flash_cr_bker_pnb(stlink_t *sl, uint32_t n) { - stlink_write_debug32(sl, STM32L4_FLASH_SR, - 0xFFFFFFFF & ~(1 << STM32L4_FLASH_SR_BSY)); - uint32_t x = read_flash_cr(sl); - x &= ~STM32L4_FLASH_CR_OPBITS; - x &= ~STM32L4_FLASH_CR_PAGEMASK; - x &= ~(1 << STM32L4_FLASH_CR_MER1); - x &= ~(1 << STM32L4_FLASH_CR_MER2); - x |= (n << STM32L4_FLASH_CR_PNB); - x |= (1lu << STM32L4_FLASH_CR_PER); - stlink_write_debug32(sl, STM32L4_FLASH_CR, x); -} - -void stlink_close(stlink_t *sl) { - if (!sl) return; - _stlink_usb_close(sl); - free(sl); -} - -int stlink_exit_debug_mode(stlink_t *sl) { - int ret; - - ret = stlink_write_debug32(sl, STLINK_REG_DHCSR, STLINK_REG_DHCSR_DBGKEY); - if (ret == -1) return ret; - - return _stlink_usb_exit_debug_mode(sl); -} - -int stlink_enter_swd_mode(stlink_t *sl) { - return _stlink_usb_enter_swd_mode(sl); -} - -// Force the core into the debug mode -> halted state. -int stlink_force_debug(stlink_t *sl) { - return _stlink_usb_force_debug(sl); -} - -int stlink_exit_dfu_mode(stlink_t *sl) { - return _stlink_usb_exit_dfu_mode(sl); -} - -int stlink_core_id(stlink_t *sl) { - int ret; - - ret = _stlink_usb_core_id(sl); - if (ret == -1) { - printf("Failed to read core_id\n"); - return ret; - } - return ret; -} - -int stlink_chip_id(stlink_t *sl, uint32_t *chip_id) { - int ret; - - ret = stlink_read_debug32(sl, 0xE0042000, chip_id); - if (ret == -1) return ret; - - if (*chip_id == 0) - ret = stlink_read_debug32( - sl, 0x40015800, - chip_id); // Try Corex M0 DBGMCU_IDCODE register address - - return ret; -} - -/** - * reads and decodes the flash parameters, as dynamically as possible - * @param sl - * @return 0 for success, or -1 for unsupported core type. - */ -static const struct stlink_chipid_params F7_params = { - // RM0385 and DS10916 document was used to find these paramaters - .chip_id = STLINK_CHIPID_STM32_F7, - .description = "F7 device", - .flash_size_reg = 0x1ff0f442, // section 41.2 - .flash_pagesize = 0x800, // No flash pages - .sram_size = 0x50000, // "SRAM" byte size in hex from DS Fig 18 - .bootrom_base = - 0x00100000, // "System memory" starting address from DS Fig 18 - .bootrom_size = 0xEDC0 // "System memory" byte size in hex from DS Fig 18 -}; - -int stlink_load_device_params(stlink_t *sl) { - const struct stlink_chipid_params *params = &F7_params; - stlink_core_id(sl); - uint32_t chip_id; - uint32_t flash_size; - - stlink_chip_id(sl, &chip_id); - sl->chip_id = chip_id & 0xfff; - /* Fix chip_id for F4 rev A errata , Read CPU ID, as CoreID is the same for - * F2/F4*/ - if (sl->chip_id == 0x411) { - uint32_t cpuid; - stlink_read_debug32(sl, 0xE000ED00, &cpuid); - if ((cpuid & 0xfff0) == 0xc240) sl->chip_id = 0x413; - } - - if (sl->chip_id != STLINK_CHIPID_STM32_F7) { - printf("unsupported chip id! %d\n", chip_id); - return -1; - } - - // These are fixed... - sl->flash_base = STM32_FLASH_BASE; - sl->sram_base = STM32_SRAM_BASE; - stlink_read_debug32(sl, (params->flash_size_reg) & ~3, &flash_size); - if (params->flash_size_reg & 2) flash_size = flash_size >> 16; - flash_size = flash_size & 0xffff; - - sl->flash_size = flash_size * 1024; - sl->flash_pgsz = params->flash_pagesize; - sl->sram_size = params->sram_size; - sl->sys_base = params->bootrom_base; - sl->sys_size = params->bootrom_size; - - return 0; -} - -int stlink_reset(stlink_t *sl) { - return _stlink_usb_reset(sl); -} - -int stlink_jtag_reset(stlink_t *sl, int value) { - return _stlink_usb_jtag_reset(sl, value); -} - -int stlink_run(stlink_t *sl) { - return _stlink_usb_run(sl); -} - -int stlink_set_swdclk(stlink_t *sl, uint16_t divisor) { - return _stlink_usb_set_swdclk(sl, divisor); -} - -int stlink_status(stlink_t *sl) { - int ret; - - ret = _stlink_usb_status(sl); - stlink_core_stat(sl); - - return ret; -} - -int stlink_target_voltage(stlink_t *sl) { - return _stlink_usb_target_voltage(sl); -} - -int stlink_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data) { - return _stlink_usb_read_debug32(sl, addr, data); -} - -int stlink_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) { - return _stlink_usb_write_debug32(sl, addr, data); -} - -int stlink_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { - if (len % 4 != 0) { - fprintf(stderr, - "Error: Data length doesn't have a 32 bit alignment: +%d byte.\n", - len % 4); - abort(); - } - return _stlink_usb_write_mem32(sl, addr, len); -} - -int stlink_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { - if (len % 4 != 0) { // !!! never ever: fw gives just wrong values - fprintf(stderr, - "Error: Data length doesn't have a 32 bit alignment: +%d byte.\n", - len % 4); - abort(); - } - return _stlink_usb_read_mem32(sl, addr, len); -} - -int stlink_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) { - if (len > 0x40) { // !!! never ever: Writing more then 0x40 bytes gives - // unexpected behaviour - fprintf(stderr, "Error: Data length > 64: +%d byte.\n", len); - abort(); - } - return _stlink_usb_write_mem8(sl, addr, len); -} - -int stlink_write_reg(stlink_t *sl, uint32_t reg, int idx) { - return _stlink_usb_write_reg(sl, reg, idx); -} - -int stlink_read_reg(stlink_t *sl, int r_idx, struct stlink_reg *regp) { - if (r_idx > 20 || r_idx < 0) { - fprintf(stderr, "Error: register index must be in [0..20]\n"); - return -1; - } - - return _stlink_usb_read_reg(sl, r_idx, regp); -} - -bool stlink_is_core_halted(stlink_t *sl) { - bool ret = false; - - stlink_status(sl); - if (sl->q_buf[0] == STLINK_CORE_HALTED) ret = true; - - return ret; -} - -int stlink_step(stlink_t *sl) { - return _stlink_usb_step(sl); -} - -int stlink_current_mode(stlink_t *sl) { - return _stlink_usb_current_mode(sl); -} - -// End of delegates.... Common code below here... - -// Endianness -// http://www.ibm.com/developerworks/aix/library/au-endianc/index.html -// const int i = 1; -// #define is_bigendian() ( (*(char*)&i) == 0 ) - -unsigned int is_bigendian(void) { - static volatile const unsigned int i = 1; - return *(volatile const char *) &i == 0; -} - -uint16_t read_uint16(const unsigned char *c, const int pt) { - uint32_t ui; - char *p = (char *) &ui; - - if (!is_bigendian()) { // le -> le (don't swap) - p[0] = c[pt + 0]; - p[1] = c[pt + 1]; - } else { - p[0] = c[pt + 1]; - p[1] = c[pt + 0]; - } - return ui; -} - -// same as above with entrypoint. - -void stlink_run_at(stlink_t *sl, stm32_addr_t addr) { - stlink_write_reg(sl, addr, 15); /* pc register */ - - stlink_run(sl); - - while (stlink_is_core_halted(sl)) usleep(3000000); -} - -void stlink_core_stat(stlink_t *sl) { - if (sl->q_len <= 0) return; - - switch (sl->q_buf[0]) { - case STLINK_CORE_RUNNING: - sl->core_stat = STLINK_CORE_RUNNING; - return; - case STLINK_CORE_HALTED: - sl->core_stat = STLINK_CORE_HALTED; - return; - default: - sl->core_stat = STLINK_CORE_STAT_UNKNOWN; - fprintf(stderr, " core status: unknown\n"); - } -} - -void stlink_fwrite_finalize(stlink_t *sl, stm32_addr_t addr) { - unsigned int val; - /* set stack*/ - stlink_read_debug32(sl, addr, &val); - stlink_write_reg(sl, val, 13); - /* Set PC to the reset routine*/ - stlink_read_debug32(sl, addr + 4, &val); - stlink_write_reg(sl, val, 15); - stlink_run(sl); -} - -typedef bool (*save_block_fn)(void *arg, uint8_t *block, ssize_t len); - -static int stlink_read(stlink_t *sl, stm32_addr_t addr, size_t size, - save_block_fn fn, void *fn_arg) { - int error = -1; - - if (size < 1) size = sl->flash_size; - - if (size > sl->flash_size) size = sl->flash_size; - - size_t cmp_size = (sl->flash_pgsz > 0x1800) ? 0x1800 : sl->flash_pgsz; - for (size_t off = 0; off < size; off += cmp_size) { - size_t aligned_size; - - /* adjust last page size */ - if ((off + cmp_size) > size) cmp_size = size - off; - - aligned_size = cmp_size; - if (aligned_size & (4 - 1)) aligned_size = (cmp_size + 4) & ~(4 - 1); - - stlink_read_mem32(sl, addr + (uint32_t) off, aligned_size); - - if (!fn(fn_arg, sl->q_buf, aligned_size)) { - goto on_error; - } - } - - /* success */ - error = 0; - -on_error: - return error; -} - -int write_buffer_to_sram(stlink_t *sl, flash_loader_t *fl, const uint8_t *buf, - size_t size) { - /* write the buffer right after the loader */ - size_t chunk = size & ~0x3; - size_t rem = size & 0x3; - if (chunk) { - memcpy(sl->q_buf, buf, chunk); - stlink_write_mem32(sl, fl->buf_addr, chunk); - } - if (rem) { - memcpy(sl->q_buf, buf + chunk, rem); - stlink_write_mem8(sl, (fl->buf_addr) + (uint32_t) chunk, rem); - } - return 0; -} - -uint32_t calculate_F7_sectornum(uint32_t flashaddr) { - flashaddr &= ~STM32_FLASH_BASE; // Page now holding the actual flash address - if (flashaddr < 0x20000) - return (flashaddr / 0x8000); - else if (flashaddr < 0x40000) - return (4); - else - return (flashaddr / 0x40000) + 4; -} - -uint32_t stlink_calculate_pagesize(stlink_t *sl, uint32_t flashaddr) { - if (sl->chip_id == STLINK_CHIPID_STM32_F7) { - uint32_t sector = calculate_F7_sectornum(flashaddr); - if (sector < 4) - sl->flash_pgsz = 0x8000; - else if (sector < 5) - sl->flash_pgsz = 0x20000; - else - sl->flash_pgsz = 0x40000; - } - return (uint32_t) sl->flash_pgsz; -} - -/** - * Erase a page of flash, assumes sl is fully populated with things like - * chip/core ids - * @param sl stlink context - * @param flashaddr an address in the flash page to erase - * @return 0 on success -ve on failure - */ -int stlink_erase_flash_page(stlink_t *sl, stm32_addr_t flashaddr) { - /* wait for ongoing op to finish */ - wait_flash_busy(sl); - - /* unlock if locked */ - unlock_flash_if(sl); - - /* select the page to erase */ - // calculate the actual page from the address - uint32_t sector = calculate_F7_sectornum(flashaddr); - - write_flash_cr_snb(sl, sector); - - /* start erase operation */ - set_flash_cr_strt(sl); - - /* wait for completion */ - wait_flash_busy(sl); - - /* relock the flash */ - // todo: fails to program if this is in - lock_flash(sl); - - return 0; -} - -/** - * Verify addr..addr+len is binary identical to base...base+len - * @param sl stlink context - * @param address stm device address - * @param data host side buffer to check against - * @param length how much - * @return 0 for success, -ve for failure - */ -int stlink_verify_write_flash(stlink_t *sl, stm32_addr_t address, uint8_t *data, - unsigned length) { - size_t off; - size_t cmp_size = (sl->flash_pgsz > 0x1800) ? 0x1800 : sl->flash_pgsz; - printf("Verifying flash...\n"); - for (off = 0; off < length; off += cmp_size) { - size_t aligned_size; - - /* adjust last page size */ - if ((off + cmp_size) > length) cmp_size = length - off; - - aligned_size = cmp_size; - if (aligned_size & (4 - 1)) aligned_size = (cmp_size + 4) & ~(4 - 1); - - stlink_read_mem32(sl, address + (uint32_t) off, aligned_size); - - if (memcmp(sl->q_buf, data + off, cmp_size)) { - printf("Verification of flash failed at offset: %u\n", - (unsigned int) off); - return -1; - } - } - return 0; -} - -int stlink_write_flash(stlink_t *sl, stm32_addr_t addr, uint8_t *base, - uint32_t len, uint8_t eraseonly) { - size_t off; - flash_loader_t fl; - printf("Writing %d (%#x) bytes to stm32 address: %u (%#x)\n", len, len, addr, - addr); - /* check addr range is inside the flash */ - stlink_calculate_pagesize(sl, addr); - if (addr < sl->flash_base) { - printf("addr too low %#x < %#x\n", addr, sl->flash_base); - return -1; - } else if ((addr + len) < addr) { - printf("addr overruns\n"); - return -1; - } else if ((addr + len) > (sl->flash_base + sl->flash_size)) { - printf("addr too high\n"); - return -1; - } else if (addr & 1) { - printf("unaligned addr 0x%x\n", addr); - return -1; - } else if (len & 1) { - printf("unaligned len 0x%x -- padding with zero\n", len); - len += 1; - } else if (addr & (sl->flash_pgsz - 1)) { - printf("addr not a multiple of pagesize, not supported\n"); - return -1; - } - - // Make sure we've loaded the context with the chip details - stlink_core_id(sl); - /* erase each page */ - int page_count = 0; - for (off = 0; off < len; - off += stlink_calculate_pagesize(sl, addr + (uint32_t) off)) { - fprintf(stdout, "Erasing flash page at addr 0x%08X\n", (int) (addr + off)); - /* addr must be an addr inside the page */ - if (stlink_erase_flash_page(sl, addr + (uint32_t) off) == -1) { - printf("Failed to erase_flash_page(%#zx) == -1\n", addr + off); - return -1; - } - page_count++; - } - - if (eraseonly) return 0; - - /* flash loader initialization */ - if (stlink_flash_loader_init(sl, &fl) == -1) { - printf("stlink_flash_loader_init() == -1\n"); - return -1; - } - - /* First unlock the cr */ - unlock_flash_if(sl); - - /* TODO: Check that Voltage range is 2.7 - 3.6 V */ - /* set parallelisim to 32 bit*/ - int voltage = stlink_target_voltage(sl); - if (voltage == -1) { - printf("Failed to read Target voltage\n"); - return voltage; - } else if (voltage > 2700) { - write_flash_cr_psiz(sl, 2); - } else { - printf( - "Target voltage (%d mV) too low for 32-bit flash, using 8-bit " - "flash writes\n", - voltage); - write_flash_cr_psiz(sl, 0); - } - - /* set programming mode */ - set_flash_cr_pg(sl); - - for (off = 0; off < len;) { - size_t size = len - off > 0x8000 ? 0x8000 : len - off; - printf("Writing %d bytes at 0x%08X\n", (int) size, - (int) (addr + (uint32_t) off)); - if (stlink_flash_loader_run(sl, &fl, addr + (uint32_t) off, base + off, - size) == -1) { - printf("stlink_flash_loader_run(%#zx) failed! == -1\n", addr + off); - return -1; - } - off += size; - } - - /* Relock flash */ - lock_flash(sl); - - return stlink_verify_write_flash(sl, addr, base, len); -} diff --git a/src/mongoose-6.11/src/common/platforms/stm32/flash.c b/src/mongoose-6.11/src/common/platforms/stm32/flash.c deleted file mode 100644 index 371c6eb..0000000 --- a/src/mongoose-6.11/src/common/platforms/stm32/flash.c +++ /dev/null @@ -1,93 +0,0 @@ -/* simple wrapper around the stlink_flash_write function */ - -#include -#include -#include -#include -#include - -#include "stlink.h" -#include "usb.h" - -#define FLASH_ADDRESS 0x8000000 - -int stm32_flash(const char *device_name, void *data, int len) { - stlink_t *sl = NULL; - int err = -1; - uint8_t serial[16]; - - int j = (int) strlen(device_name); - int length = j / 2; // the length of the destination-array - if (j % 2 != 0) return -1; - - for (size_t k = 0; j >= 0 && k < sizeof(serial); ++k, j -= 2) { - char buffer[3] = {0}; - memcpy(buffer, device_name + j, 2); - serial[length - k] = (uint8_t) strtol(buffer, NULL, 16); - } - - sl = stlink_open_usb(1, (char *) serial); - - if (sl == NULL) return -1; - - if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) { - if (stlink_exit_dfu_mode(sl)) { - printf("Failed to exit DFU mode\n"); - goto on_error; - } - } - - if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) { - if (stlink_enter_swd_mode(sl)) { - printf("Failed to enter SWD mode\n"); - goto on_error; - } - } - - if (stlink_jtag_reset(sl, 2)) { - printf("Failed to reset JTAG\n"); - goto on_error; - } - - if (stlink_reset(sl)) { - printf("Failed to reset device\n"); - goto on_error; - } - - // Core must be halted to use RAM based flashloaders - if (stlink_force_debug(sl)) { - printf("Failed to halt the core\n"); - goto on_error; - } - - if (stlink_status(sl)) { - printf("Failed to get Core's status\n"); - goto on_error; - } - - size_t size = 0; - - if ((FLASH_ADDRESS > sl->flash_base + sl->flash_size)) { - printf("Unknown memory region\n"); - goto on_error; - } - - err = stlink_write_flash(sl, FLASH_ADDRESS, (uint8_t *) data, len, 0); - stlink_fwrite_finalize(sl, FLASH_ADDRESS); - - if (err == -1) { - printf("stlink_fwrite_flash() == -1\n"); - goto on_error; - } - - stlink_jtag_reset(sl, 2); - stlink_reset(sl); - - err = 0; - -on_error: - stlink_exit_debug_mode(sl); - stlink_close(sl); - - return err; -} diff --git a/src/mongoose-6.11/src/common/platforms/stm32/flash_loader.c b/src/mongoose-6.11/src/common/platforms/stm32/flash_loader.c deleted file mode 100644 index b508e8e..0000000 --- a/src/mongoose-6.11/src/common/platforms/stm32/flash_loader.c +++ /dev/null @@ -1,93 +0,0 @@ -#include "stlink.h" - -#include -#include -#include - -static const uint8_t loader_code_stm32f7[] = { - 0x08, 0x4b, 0x72, 0xb1, 0x04, 0x68, 0x0c, 0x60, - 0xbf, 0xf3, 0x4f, 0x8f, // DSB Memory barrier for in order flash write - 0xdc, 0x89, 0x14, 0xf0, 0x01, 0x0f, 0xfb, 0xd1, - 0x00, 0xf1, 0x04, 0x00, 0x01, 0xf1, 0x04, 0x01, - 0xa2, 0xf1, 0x01, 0x02, 0xef, 0xe7, 0x00, 0xbe, // bkpt #0x00 - 0x00, 0x3c, 0x02, 0x40, -}; - -#define WAIT_ROUNDS 10000 - -int stlink_flash_loader_init(stlink_t *sl, flash_loader_t *fl) { - size_t size; - - /* allocate the loader in sram */ - if (stlink_flash_loader_write_to_sram(sl, &fl->loader_addr, &size) == -1) { - printf("Failed to write flash loader to sram!\n"); - return -1; - } - - /* allocate a one page buffer in sram right after loader */ - fl->buf_addr = fl->loader_addr + (uint32_t) size; - - return 0; -} - -int stlink_flash_loader_write_to_sram(stlink_t *sl, stm32_addr_t *addr, - size_t *size) { - memcpy(sl->q_buf, loader_code_stm32f7, sizeof(loader_code_stm32f7)); - stlink_write_mem32(sl, sl->sram_base, sizeof(loader_code_stm32f7)); - - *addr = sl->sram_base; - *size = sizeof(loader_code_stm32f7); - - /* success */ - return 0; -} - -int stlink_flash_loader_run(stlink_t *sl, flash_loader_t *fl, - stm32_addr_t target, const uint8_t *buf, - size_t size) { - struct stlink_reg rr; - int i = 0; - size_t count = 0; - - // FIXME This can never return -1 - if (write_buffer_to_sram(sl, fl, buf, size) == -1) { - // IMPOSSIBLE! - printf("write_buffer_to_sram() == -1\n"); - return -1; - } - - count = size / sizeof(uint32_t); - if (size % sizeof(uint32_t)) ++count; - - /* setup core */ - stlink_write_reg(sl, fl->buf_addr, 0); /* source */ - stlink_write_reg(sl, target, 1); /* target */ - stlink_write_reg(sl, (uint32_t) count, 2); /* count */ - stlink_write_reg( - sl, 0, - 3); /* flash bank 0 (input), only used on F0, but armless fopr others */ - stlink_write_reg(sl, fl->loader_addr, 15); /* pc register */ - - /* run loader */ - stlink_run(sl); - - /* wait until done (reaches breakpoint) */ - for (i = 0; i < WAIT_ROUNDS; i++) { - usleep(10); - if (stlink_is_core_halted(sl)) break; - } - - if (i >= WAIT_ROUNDS) { - printf("flash loader run error\n"); - return -1; - } - - /* check written byte count */ - stlink_read_reg(sl, 2, &rr); - if (rr.r[2] != 0) { - printf("write error, count == %u\n", rr.r[2]); - return -1; - } - - return 0; -} diff --git a/src/mongoose-6.11/src/common/platforms/stm32/flash_loader.h b/src/mongoose-6.11/src/common/platforms/stm32/flash_loader.h deleted file mode 100644 index a3f072d..0000000 --- a/src/mongoose-6.11/src/common/platforms/stm32/flash_loader.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * File: stlink.h - * - * This should contain all the common top level stlink interfaces, regardless - * of how the backend does the work.... - */ -#ifndef STLINK_FLASH_LOADER_H_ -#define STLINK_FLASH_LOADER_H_ - -#include -#include - -#include "stlink.h" - -#ifdef __cplusplus -extern "C" { -#endif - -int stlink_flash_loader_init(stlink_t *sl, flash_loader_t *fl); -int stlink_flash_loader_write_to_sram(stlink_t *sl, stm32_addr_t *addr, - size_t *size); -int stlink_flash_loader_run(stlink_t *sl, flash_loader_t *fl, - stm32_addr_t target, const uint8_t *buf, - size_t size); - -#ifdef __cplusplus -} -#endif - -#endif /* STLINK_FLASH_LOADER_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/stm32/flasher.go b/src/mongoose-6.11/src/common/platforms/stm32/flasher.go deleted file mode 100644 index 8ddde03..0000000 --- a/src/mongoose-6.11/src/common/platforms/stm32/flasher.go +++ /dev/null @@ -1,38 +0,0 @@ -// +build linux - -package stm32 - -// #cgo CFLAGS: -I/usr/include/libusb-1.0/ -// #cgo LDFLAGS: -lusb-1.0 -// #include "stm32_flash.h" -import "C" - -import ( - "time" - "unsafe" - - "cesanta.com/mos/flash/common" - "github.com/cesanta/errors" -) - -type FlashOpts struct { - ShareName string - Timeout time.Duration -} - -func Flash(fw *common.FirmwareBundle, opts *FlashOpts) error { - data, err := fw.GetPartData("boot") - if err != nil { - return errors.Annotatef(err, "invalid manifest") - } - - flash_res := C.stm32_flash(C.CString(opts.ShareName), - unsafe.Pointer(&data[0]), - C.int(len(data))) - - if flash_res != 0 { - return errors.Errorf("flash failed") - } - - return nil -} diff --git a/src/mongoose-6.11/src/common/platforms/stm32/stlink.h b/src/mongoose-6.11/src/common/platforms/stm32/stlink.h deleted file mode 100644 index ac34dd0..0000000 --- a/src/mongoose-6.11/src/common/platforms/stm32/stlink.h +++ /dev/null @@ -1,198 +0,0 @@ -/* - * File: stlink.h - * - * This should contain all the common top level stlink interfaces, regardless - * of how the backend does the work.... - */ -#ifndef STLINK_H -#define STLINK_H - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define STLINK_ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) - -// Max data transfer size. -// 6kB = max mem32_read block, 8kB sram -//#define Q_BUF_LEN 96 -#define Q_BUF_LEN (1024 * 100) - -// STLINK_DEBUG_RESETSYS, etc: -#define STLINK_CORE_RUNNING 0x80 -#define STLINK_CORE_HALTED 0x81 -#define STLINK_CORE_STAT_UNKNOWN -1 - -#define STLINK_GET_VERSION 0xf1 -#define STLINK_GET_CURRENT_MODE 0xf5 -#define STLINK_GET_TARGET_VOLTAGE 0xF7 - -#define STLINK_DEBUG_COMMAND 0xF2 -#define STLINK_DFU_COMMAND 0xF3 -#define STLINK_DFU_EXIT 0x07 - -// STLINK_GET_CURRENT_MODE -#define STLINK_DEV_DFU_MODE 0x00 -#define STLINK_DEV_MASS_MODE 0x01 -#define STLINK_DEV_DEBUG_MODE 0x02 -#define STLINK_DEV_UNKNOWN_MODE -1 - -// TODO - possible poor names... -#define STLINK_SWD_ENTER 0x30 -#define STLINK_SWD_READCOREID 0x32 // TBD -#define STLINK_JTAG_WRITEDEBUG_32BIT 0x35 -#define STLINK_JTAG_READDEBUG_32BIT 0x36 -#define STLINK_JTAG_DRIVE_NRST 0x3c - -#define STLINK_DEBUG_APIV2_SWD_SET_FREQ 0x43 - -/* cortex core ids */ -// TODO clean this up... -#define STM32VL_CORE_ID 0x1ba01477 -#define STM32F7_CORE_ID 0x5ba02477 - -// Constant STM32 memory map figures -#define STM32_FLASH_BASE 0x08000000 -#define STM32_SRAM_BASE 0x20000000 - -// Baud rate divisors for SWDCLK -#define STLINK_SWDCLK_4MHZ_DIVISOR 0 -#define STLINK_SWDCLK_1P8MHZ_DIVISOR 1 -#define STLINK_SWDCLK_1P2MHZ_DIVISOR 2 -#define STLINK_SWDCLK_950KHZ_DIVISOR 3 -#define STLINK_SWDCLK_480KHZ_DIVISOR 7 -#define STLINK_SWDCLK_240KHZ_DIVISOR 15 -#define STLINK_SWDCLK_125KHZ_DIVISOR 31 -#define STLINK_SWDCLK_100KHZ_DIVISOR 40 -#define STLINK_SWDCLK_50KHZ_DIVISOR 79 -#define STLINK_SWDCLK_25KHZ_DIVISOR 158 -#define STLINK_SWDCLK_15KHZ_DIVISOR 265 -#define STLINK_SWDCLK_5KHZ_DIVISOR 798 - -#define STLINK_CHIPID_STM32_F7 0x449 - -/* Enough space to hold both a V2 command or a V1 command packaged as generic - * scsi*/ -#define C_BUF_LEN 32 - -struct stlink_reg { - uint32_t r[16]; - uint32_t s[32]; - uint32_t xpsr; - uint32_t main_sp; - uint32_t process_sp; - uint32_t rw; - uint32_t rw2; - uint8_t control; - uint8_t faultmask; - uint8_t basepri; - uint8_t primask; - uint32_t fpscr; -}; - -typedef uint32_t stm32_addr_t; - -typedef struct flash_loader { - stm32_addr_t loader_addr; /* loader sram adddr */ - stm32_addr_t buf_addr; /* buffer sram address */ -} flash_loader_t; - -typedef struct stlink_version_ { - uint32_t stlink_v; - uint32_t jtag_v; - uint32_t swim_v; - uint32_t st_vid; - uint32_t stlink_pid; -} stlink_version_t; - -typedef struct _stlink stlink_t; - -struct _stlink { - void *backend_data; - - // Room for the command header - unsigned char c_buf[C_BUF_LEN]; - // Data transferred from or to device - unsigned char q_buf[Q_BUF_LEN]; - int q_len; - - // transport layer verboseness: 0 for no debug info, 10 for lots - uint32_t core_id; - uint32_t chip_id; - int core_stat; - - char serial[16]; - int serial_size; - - stm32_addr_t flash_base; - size_t flash_size; - size_t flash_pgsz; - - /* sram settings */ - stm32_addr_t sram_base; - size_t sram_size; - - // bootloader - stm32_addr_t sys_base; - size_t sys_size; - - struct stlink_version_ version; -}; - -int stlink_enter_swd_mode(stlink_t *sl); -int stlink_enter_jtag_mode(stlink_t *sl); -int stlink_exit_debug_mode(stlink_t *sl); -int stlink_exit_dfu_mode(stlink_t *sl); -void stlink_close(stlink_t *sl); -int stlink_core_id(stlink_t *sl); -int stlink_reset(stlink_t *sl); -int stlink_jtag_reset(stlink_t *sl, int value); -int stlink_run(stlink_t *sl); -int stlink_status(stlink_t *sl); -int stlink_version(stlink_t *sl); -int stlink_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data); -int stlink_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len); -int stlink_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data); -int stlink_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len); -int stlink_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len); -int stlink_read_all_regs(stlink_t *sl, struct stlink_reg *regp); -int stlink_read_reg(stlink_t *sl, int r_idx, struct stlink_reg *regp); -int stlink_write_reg(stlink_t *sl, uint32_t reg, int idx); -int stlink_step(stlink_t *sl); -int stlink_current_mode(stlink_t *sl); -int stlink_force_debug(stlink_t *sl); -int stlink_target_voltage(stlink_t *sl); -int stlink_set_swdclk(stlink_t *sl, uint16_t divisor); -int stlink_write_flash(stlink_t *sl, stm32_addr_t address, uint8_t *data, - uint32_t length, uint8_t eraseonly); -int stlink_fwrite_flash(stlink_t *sl, const char *path, stm32_addr_t addr); -int stlink_verify_write_flash(stlink_t *sl, stm32_addr_t address, uint8_t *data, - uint32_t length); -int stlink_chip_id(stlink_t *sl, uint32_t *chip_id); -int stlink_erase_flash_page(stlink_t *sl, stm32_addr_t flashaddr); -uint32_t stlink_calculate_pagesize(stlink_t *sl, uint32_t flashaddr); -uint16_t read_uint16(const unsigned char *c, const int pt); -void stlink_core_stat(stlink_t *sl); -unsigned int is_bigendian(void); -uint32_t read_uint32(const unsigned char *c, const int pt); -void write_uint32(unsigned char *buf, uint32_t ui); -void write_uint16(unsigned char *buf, uint16_t ui); -bool stlink_is_core_halted(stlink_t *sl); -int write_buffer_to_sram(stlink_t *sl, flash_loader_t *fl, const uint8_t *buf, - size_t size); -int write_loader_to_sram(stlink_t *sl, stm32_addr_t *addr, size_t *size); -int stlink_load_device_params(stlink_t *sl); -void stlink_fwrite_finalize(stlink_t *sl, stm32_addr_t addr); - -#include "usb.h" -#include "flash_loader.h" - -#ifdef __cplusplus -} -#endif - -#endif /* STLINK_H */ diff --git a/src/mongoose-6.11/src/common/platforms/stm32/stm32_flash.h b/src/mongoose-6.11/src/common/platforms/stm32/stm32_flash.h deleted file mode 100644 index 2846ab7..0000000 --- a/src/mongoose-6.11/src/common/platforms/stm32/stm32_flash.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef STM32_FLASH_H_ -#define STM32_FLASH_H_ - -int stm32_flash(const char *device_name, void *data, int len); - -#endif diff --git a/src/mongoose-6.11/src/common/platforms/stm32/usb.c b/src/mongoose-6.11/src/common/platforms/stm32/usb.c deleted file mode 100644 index a60e99f..0000000 --- a/src/mongoose-6.11/src/common/platforms/stm32/usb.c +++ /dev/null @@ -1,773 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "stlink.h" - -enum SCSI_Generic_Direction { SG_DXFER_TO_DEV = 0, SG_DXFER_FROM_DEV = 0x80 }; - -enum stlink_debug_commands { - STLINK_DEBUG_ENTER_JTAG = 0x00, - STLINK_DEBUG_GETSTATUS = 0x01, - STLINK_DEBUG_FORCEDEBUG = 0x02, - STLINK_DEBUG_RESETSYS = 0x03, - STLINK_DEBUG_READALLREGS = 0x04, - STLINK_DEBUG_READREG = 0x05, - STLINK_DEBUG_WRITEREG = 0x06, - STLINK_DEBUG_READMEM_32BIT = 0x07, - STLINK_DEBUG_WRITEMEM_32BIT = 0x08, - STLINK_DEBUG_RUNCORE = 0x09, - STLINK_DEBUG_STEPCORE = 0x0a, - STLINK_DEBUG_SETFP = 0x0b, - STLINK_DEBUG_WRITEMEM_8BIT = 0x0d, - STLINK_DEBUG_CLEARFP = 0x0e, - STLINK_DEBUG_WRITEDEBUGREG = 0x0f, - STLINK_DEBUG_ENTER = 0x20, - STLINK_DEBUG_EXIT = 0x21, - STLINK_DEBUG_READCOREID = 0x22, - STLINK_DEBUG_ENTER_SWD = 0xa3 -}; - -void _stlink_usb_close(stlink_t *sl) { - if (!sl) return; - - struct stlink_libusb *const handle = sl->backend_data; - // maybe we couldn't even get the usb device? - if (handle != NULL) { - if (handle->usb_handle != NULL) { - libusb_close(handle->usb_handle); - } - - libusb_exit(handle->libusb_ctx); - free(handle); - } -} - -ssize_t send_recv(struct stlink_libusb *handle, int terminate, - unsigned char *txbuf, size_t txsize, unsigned char *rxbuf, - size_t rxsize) { - /* note: txbuf and rxbuf can point to the same area */ - int res = 0; - int t; - - t = libusb_bulk_transfer(handle->usb_handle, handle->ep_req, txbuf, - (int) txsize, &res, 3000); - if (t) { - printf("[!] send_recv send request failed: %s\n", libusb_error_name(t)); - return -1; - } else if ((size_t) res != txsize) { - printf("[!] send_recv send request wrote %u bytes (instead of %u).\n", - (unsigned int) res, (unsigned int) txsize); - } - - if (rxsize != 0) { - t = libusb_bulk_transfer(handle->usb_handle, handle->ep_rep, rxbuf, - (int) rxsize, &res, 3000); - if (t) { - printf("[!] send_recv read reply failed: %s\n", libusb_error_name(t)); - return -1; - } - } - - if ((handle->protocoll == 1) && terminate) { - /* Read the SG reply */ - unsigned char sg_buf[13]; - t = libusb_bulk_transfer(handle->usb_handle, handle->ep_rep, sg_buf, 13, - &res, 3000); - if (t) { - printf("[!] send_recv read storage failed: %s\n", libusb_error_name(t)); - return -1; - } - /* The STLink doesn't seem to evaluate the sequence number */ - handle->sg_transfer_idx++; - } - - return res; -} - -static inline int send_only(struct stlink_libusb *handle, int terminate, - unsigned char *txbuf, size_t txsize) { - return (int) send_recv(handle, terminate, txbuf, txsize, NULL, 0); -} - -static int fill_command(stlink_t *sl, enum SCSI_Generic_Direction dir, - uint32_t len) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const cmd = sl->c_buf; - int i = 0; - memset(cmd, 0, sizeof(sl->c_buf)); - if (slu->protocoll == 1) { - cmd[i++] = 'U'; - cmd[i++] = 'S'; - cmd[i++] = 'B'; - cmd[i++] = 'C'; - write_uint32(&cmd[i], slu->sg_transfer_idx); - write_uint32(&cmd[i + 4], len); - i += 8; - cmd[i++] = (dir == SG_DXFER_FROM_DEV) ? 0x80 : 0; - cmd[i++] = 0; /* Logical unit */ - cmd[i++] = 0xa; /* Command length */ - } - return i; -} - -int _stlink_usb_version(stlink_t *sl) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const data = sl->q_buf; - unsigned char *const cmd = sl->c_buf; - ssize_t size; - uint32_t rep_len = 6; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_GET_VERSION; - - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv STLINK_GET_VERSION\n"); - return (int) size; - } - - return 0; -} - -int32_t _stlink_usb_target_voltage(stlink_t *sl) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const rdata = sl->q_buf; - unsigned char *const cmd = sl->c_buf; - ssize_t size; - uint32_t rep_len = 8; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - uint32_t factor, reading; - int voltage; - - cmd[i++] = STLINK_GET_TARGET_VOLTAGE; - - size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len); - if (size == -1) { - printf("[!] send_recv STLINK_GET_TARGET_VOLTAGE\n"); - return -1; - } else if (size != 8) { - printf("[!] wrong length STLINK_GET_TARGET_VOLTAGE\n"); - return -1; - } - - factor = - (rdata[3] << 24) | (rdata[2] << 16) | (rdata[1] << 8) | (rdata[0] << 0); - reading = - (rdata[7] << 24) | (rdata[6] << 16) | (rdata[5] << 8) | (rdata[4] << 0); - voltage = 2400 * reading / factor; - - return voltage; -} - -int _stlink_usb_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const rdata = sl->q_buf; - unsigned char *const cmd = sl->c_buf; - ssize_t size; - const int rep_len = 8; - - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_JTAG_READDEBUG_32BIT; - write_uint32(&cmd[i], addr); - size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len); - if (size == -1) { - printf("[!] send_recv STLINK_JTAG_READDEBUG_32BIT\n"); - return (int) size; - } - *data = read_uint32(rdata, 4); - return 0; -} - -int _stlink_usb_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const rdata = sl->q_buf; - unsigned char *const cmd = sl->c_buf; - ssize_t size; - const int rep_len = 2; - - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_JTAG_WRITEDEBUG_32BIT; - write_uint32(&cmd[i], addr); - write_uint32(&cmd[i + 4], data); - size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len); - if (size == -1) { - printf("[!] send_recv STLINK_JTAG_WRITEDEBUG_32BIT\n"); - return (int) size; - } - - return 0; -} - -int _stlink_usb_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const data = sl->q_buf; - unsigned char *const cmd = sl->c_buf; - int i, ret; - - i = fill_command(sl, SG_DXFER_TO_DEV, len); - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_WRITEMEM_32BIT; - write_uint32(&cmd[i], addr); - write_uint16(&cmd[i + 4], len); - ret = send_only(slu, 0, cmd, slu->cmd_len); - if (ret == -1) return ret; - - ret = send_only(slu, 1, data, len); - if (ret == -1) return ret; - - return 0; -} - -int _stlink_usb_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const data = sl->q_buf; - unsigned char *const cmd = sl->c_buf; - int i, ret; - - i = fill_command(sl, SG_DXFER_TO_DEV, 0); - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_WRITEMEM_8BIT; - write_uint32(&cmd[i], addr); - write_uint16(&cmd[i + 4], len); - ret = send_only(slu, 0, cmd, slu->cmd_len); - if (ret == -1) return ret; - - ret = send_only(slu, 1, data, len); - if (ret == -1) return ret; - - return 0; -} - -int _stlink_usb_current_mode(stlink_t *sl) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const cmd = sl->c_buf; - unsigned char *const data = sl->q_buf; - ssize_t size; - int rep_len = 2; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_GET_CURRENT_MODE; - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv STLINK_GET_CURRENT_MODE\n"); - return -1; - } - return sl->q_buf[0]; -} - -int _stlink_usb_core_id(stlink_t *sl) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const cmd = sl->c_buf; - unsigned char *const data = sl->q_buf; - ssize_t size; - int rep_len = 4; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_READCOREID; - - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv STLINK_DEBUG_READCOREID\n"); - return -1; - } - - sl->core_id = read_uint32(data, 0); - return 0; -} - -int _stlink_usb_status(stlink_t *sl) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const data = sl->q_buf; - unsigned char *const cmd = sl->c_buf; - ssize_t size; - int rep_len = 2; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_GETSTATUS; - - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv STLINK_DEBUG_GETSTATUS\n"); - return (int) size; - } - sl->q_len = (int) size; - - return 0; -} - -int _stlink_usb_force_debug(stlink_t *sl) { - struct stlink_libusb *slu = sl->backend_data; - unsigned char *const data = sl->q_buf; - unsigned char *const cmd = sl->c_buf; - ssize_t size; - int rep_len = 2; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_FORCEDEBUG; - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv STLINK_DEBUG_FORCEDEBUG\n"); - return (int) size; - } - - return 0; -} - -int _stlink_usb_enter_swd_mode(stlink_t *sl) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const cmd = sl->c_buf; - ssize_t size; - const int rep_len = 0; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_ENTER; - cmd[i++] = STLINK_DEBUG_ENTER_SWD; - - size = send_only(slu, 1, cmd, slu->cmd_len); - if (size == -1) { - printf("[!] send_recv STLINK_DEBUG_ENTER\n"); - return (int) size; - } - - return 0; -} - -int _stlink_usb_exit_dfu_mode(stlink_t *sl) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const cmd = sl->c_buf; - ssize_t size; - int i = fill_command(sl, SG_DXFER_FROM_DEV, 0); - - cmd[i++] = STLINK_DFU_COMMAND; - cmd[i++] = STLINK_DFU_EXIT; - - size = send_only(slu, 1, cmd, slu->cmd_len); - if (size == -1) { - printf("[!] send_recv STLINK_DFU_EXIT\n"); - return (int) size; - } - - return 0; -} - -/** - * TODO - not convinced this does anything... - * @param sl - */ -int _stlink_usb_reset(stlink_t *sl) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const data = sl->q_buf; - unsigned char *const cmd = sl->c_buf; - ssize_t size; - int rep_len = 2; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_RESETSYS; - - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv STLINK_DEBUG_RESETSYS\n"); - return (int) size; - } - - return 0; -} - -int _stlink_usb_jtag_reset(stlink_t *sl, int value) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const data = sl->q_buf; - unsigned char *const cmd = sl->c_buf; - ssize_t size; - int rep_len = 2; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_JTAG_DRIVE_NRST; - cmd[i++] = value; - - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv STLINK_JTAG_DRIVE_NRST\n"); - return (int) size; - } - - return 0; -} - -int _stlink_usb_step(stlink_t *sl) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const data = sl->q_buf; - unsigned char *const cmd = sl->c_buf; - ssize_t size; - int rep_len = 2; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_STEPCORE; - - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv STLINK_DEBUG_STEPCORE\n"); - return (int) size; - } - - return 0; -} - -/** - * This seems to do a good job of restarting things from the beginning? - * @param sl - */ -int _stlink_usb_run(stlink_t *sl) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const data = sl->q_buf; - unsigned char *const cmd = sl->c_buf; - ssize_t size; - int rep_len = 2; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_RUNCORE; - - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv STLINK_DEBUG_RUNCORE\n"); - return (int) size; - } - - return 0; -} - -int _stlink_usb_set_swdclk(stlink_t *sl, uint16_t clk_divisor) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const data = sl->q_buf; - unsigned char *const cmd = sl->c_buf; - ssize_t size; - int rep_len = 2; - int i; - - // clock speed only supported by stlink/v2 and for firmware >= 22 - if (sl->version.stlink_v >= 2 && sl->version.jtag_v >= 22) { - i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_APIV2_SWD_SET_FREQ; - cmd[i++] = clk_divisor & 0xFF; - cmd[i++] = (clk_divisor >> 8) & 0xFF; - - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv STLINK_DEBUG_APIV2_SWD_SET_FREQ\n"); - return (int) size; - } - - return 0; - } else { - return -1; - } -} - -int _stlink_usb_exit_debug_mode(stlink_t *sl) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const cmd = sl->c_buf; - ssize_t size; - int i = fill_command(sl, SG_DXFER_FROM_DEV, 0); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_EXIT; - - size = send_only(slu, 1, cmd, slu->cmd_len); - if (size == -1) { - printf("[!] send_only STLINK_DEBUG_EXIT\n"); - return (int) size; - } - - return 0; -} - -int _stlink_usb_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const data = sl->q_buf; - unsigned char *const cmd = sl->c_buf; - ssize_t size; - int i = fill_command(sl, SG_DXFER_FROM_DEV, len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_READMEM_32BIT; - write_uint32(&cmd[i], addr); - write_uint16(&cmd[i + 4], len); - - size = send_recv(slu, 1, cmd, slu->cmd_len, data, len); - if (size == -1) { - printf("[!] send_recv STLINK_DEBUG_READMEM_32BIT\n"); - return (int) size; - } - - sl->q_len = (int) size; - - return 0; -} - -int _stlink_usb_read_reg(stlink_t *sl, int r_idx, struct stlink_reg *regp) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const data = sl->q_buf; - unsigned char *const cmd = sl->c_buf; - ssize_t size; - uint32_t r; - uint32_t rep_len = 4; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_READREG; - cmd[i++] = (uint8_t) r_idx; - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv STLINK_DEBUG_READREG\n"); - return (int) size; - } - sl->q_len = (int) size; - r = read_uint32(sl->q_buf, 0); - - switch (r_idx) { - case 16: - regp->xpsr = r; - break; - case 17: - regp->main_sp = r; - break; - case 18: - regp->process_sp = r; - break; - case 19: - regp->rw = r; /* XXX ?(primask, basemask etc.) */ - break; - case 20: - regp->rw2 = r; /* XXX ?(primask, basemask etc.) */ - break; - default: - regp->r[r_idx] = r; - } - - return 0; -} - -int _stlink_usb_write_reg(stlink_t *sl, uint32_t reg, int idx) { - struct stlink_libusb *const slu = sl->backend_data; - unsigned char *const data = sl->q_buf; - unsigned char *const cmd = sl->c_buf; - ssize_t size; - uint32_t rep_len = 2; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_WRITEREG; - cmd[i++] = idx; - write_uint32(&cmd[i], reg); - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv STLINK_DEBUG_WRITEREG\n"); - return (int) size; - } - sl->q_len = (int) size; - - return 0; -} - -stlink_t *stlink_open_usb(bool reset, char serial[16]) { - stlink_t *sl = NULL; - struct stlink_libusb *slu = NULL; - int ret = -1; - int config; - - sl = calloc(1, sizeof(stlink_t)); - slu = calloc(1, sizeof(struct stlink_libusb)); - if (sl == NULL) goto on_malloc_error; - if (slu == NULL) goto on_malloc_error; - - sl->backend_data = slu; - - sl->core_stat = STLINK_CORE_STAT_UNKNOWN; - if (libusb_init(&(slu->libusb_ctx))) { - printf("failed to init libusb context, wrong version of libraries?\n"); - goto on_error; - } - - libusb_device **list; - /** @todo We should use ssize_t and use it as a counter if > 0. As per libusb - * API: ssize_t libusb_get_device_list (libusb_context *ctx, libusb_device - * ***list) */ - int cnt = (int) libusb_get_device_list(slu->libusb_ctx, &list); - struct libusb_device_descriptor desc; - int devBus = 0; - int devAddr = 0; - - /* @TODO: Reading a environment variable in a usb open function is not very - nice, this - should be refactored and moved into the CLI tools, and instead of giving - USB_BUS:USB_ADDR a real stlink - serial string should be passed to this function. Probably people are using - this but this is very odd because - as programmer can change to multiple busses and it is better to detect them - based on serial. */ - char *device = getenv("STLINK_DEVICE"); - if (device) { - char *c = strchr(device, ':'); - if (c == NULL) { - printf("STLINK_DEVICE must be : format\n"); - goto on_error; - } - devBus = atoi(device); - *c++ = 0; - devAddr = atoi(c); - printf("bus %03d dev %03d\n", devBus, devAddr); - } - - while (cnt--) { - libusb_get_device_descriptor(list[cnt], &desc); - if (desc.idVendor != STLINK_USB_VID_ST) continue; - - if (devBus && devAddr) { - if ((libusb_get_bus_number(list[cnt]) != devBus) || - (libusb_get_device_address(list[cnt]) != devAddr)) { - continue; - } - } - - if ((desc.idProduct == STLINK_USB_PID_STLINK_32L) || - (desc.idProduct == STLINK_USB_PID_STLINK_NUCLEO)) { - struct libusb_device_handle *handle; - - ret = libusb_open(list[cnt], &handle); - if (ret) continue; - - sl->serial_size = libusb_get_string_descriptor_ascii( - handle, desc.iSerialNumber, (unsigned char *) sl->serial, - sizeof(sl->serial)); - libusb_close(handle); - - if ((serial == NULL) || (*serial == 0)) break; - - if (sl->serial_size < 0) continue; - - if (memcmp(serial, &sl->serial, sl->serial_size) == 0) break; - - continue; - } - - if (desc.idProduct == STLINK_USB_PID_STLINK) { - slu->protocoll = 1; - break; - } - } - - if (cnt < 0) { - printf("Couldn't find %s ST-Link/V2 devices\n", - (devBus && devAddr) ? "matched" : "any"); - goto on_error; - } else { - ret = libusb_open(list[cnt], &slu->usb_handle); - if (ret != 0) { - printf("Error %d (%s) opening ST-Link/V2 device %03d:%03d\n", ret, - strerror(errno), libusb_get_bus_number(list[cnt]), - libusb_get_device_address(list[cnt])); - goto on_error; - } - } - - libusb_free_device_list(list, 1); - - if (libusb_kernel_driver_active(slu->usb_handle, 0) == 1) { - ret = libusb_detach_kernel_driver(slu->usb_handle, 0); - if (ret < 0) { - printf("libusb_detach_kernel_driver(() error %s\n", strerror(-ret)); - goto on_libusb_error; - } - } - - if (libusb_get_configuration(slu->usb_handle, &config)) { - /* this may fail for a previous configured device */ - printf("libusb_get_configuration()\n"); - goto on_libusb_error; - } - - if (config != 1) { - printf("setting new configuration (%d -> 1)\n", config); - if (libusb_set_configuration(slu->usb_handle, 1)) { - /* this may fail for a previous configured device */ - printf("libusb_set_configuration() failed\n"); - goto on_libusb_error; - } - } - - if (libusb_claim_interface(slu->usb_handle, 0)) { - printf( - "Stlink usb device found, but unable to claim (probably already in " - "use?)\n"); - goto on_libusb_error; - } - - // TODO - could use the scanning techniq from stm8 code here... - slu->ep_rep = 1 /* ep rep */ | LIBUSB_ENDPOINT_IN; - if (desc.idProduct == STLINK_USB_PID_STLINK_NUCLEO) { - slu->ep_req = 1 /* ep req */ | LIBUSB_ENDPOINT_OUT; - } else { - slu->ep_req = 2 /* ep req */ | LIBUSB_ENDPOINT_OUT; - } - - slu->sg_transfer_idx = 0; - // TODO - never used at the moment, always CMD_SIZE - slu->cmd_len = (slu->protocoll == 1) ? STLINK_SG_SIZE : STLINK_CMD_SIZE; - - if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) { - printf("-- exit_dfu_mode\n"); - stlink_exit_dfu_mode(sl); - } - - if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) { - stlink_enter_swd_mode(sl); - } - - sl->version.stlink_v = 2; - - if (reset) { - if (sl->version.stlink_v > 1) stlink_jtag_reset(sl, 2); - stlink_reset(sl); - usleep(10000); - } - - ret = stlink_load_device_params(sl); - - // Set the stlink clock speed (default is 1800kHz) - stlink_set_swdclk(sl, STLINK_SWDCLK_1P8MHZ_DIVISOR); - -on_libusb_error: - if (ret == -1) { - stlink_close(sl); - return NULL; - } - - return sl; - -on_error: - if (slu->libusb_ctx) libusb_exit(slu->libusb_ctx); - -on_malloc_error: - if (sl != NULL) free(sl); - if (slu != NULL) free(slu); - - return NULL; -} diff --git a/src/mongoose-6.11/src/common/platforms/stm32/usb.h b/src/mongoose-6.11/src/common/platforms/stm32/usb.h deleted file mode 100644 index 6ee4ff3..0000000 --- a/src/mongoose-6.11/src/common/platforms/stm32/usb.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * File: stlink/usb.h - * Author: karl - * - * Created on October 1, 2011, 11:29 PM - */ - -#ifndef STLINK_USB_H -#define STLINK_USB_H - -#include -#include - -#include "stlink.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define STLINK_USB_VID_ST 0x0483 -#define STLINK_USB_PID_STLINK 0x3744 -#define STLINK_USB_PID_STLINK_32L 0x3748 -#define STLINK_USB_PID_STLINK_NUCLEO 0x374b - -#define STLINK_SG_SIZE 31 -#define STLINK_CMD_SIZE 16 - -struct stlink_libusb { - libusb_context *libusb_ctx; - libusb_device_handle *usb_handle; - unsigned int ep_req; - unsigned int ep_rep; - int protocoll; - unsigned int sg_transfer_idx; - unsigned int cmd_len; -}; - -/** - * Open a stlink - * @param verbose Verbosity loglevel - * @param reset Reset stlink programmer - * @param serial Serial number to search for, when NULL the first stlink found - * is opened (binary format) - * @retval NULL Error while opening the stlink - * @retval !NULL Stlink found and ready to use - */ -stlink_t *stlink_open_usb(bool reset, char serial[16]); - -void _stlink_usb_close(stlink_t *sl); -int _stlink_usb_version(stlink_t *sl); -int32_t _stlink_usb_target_voltage(stlink_t *sl); -int _stlink_usb_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data); -int _stlink_usb_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data); -int _stlink_usb_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len); -int _stlink_usb_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len); -int _stlink_usb_current_mode(stlink_t *sl); -int _stlink_usb_core_id(stlink_t *sl); -int _stlink_usb_status(stlink_t *sl); -int _stlink_usb_force_debug(stlink_t *sl); -int _stlink_usb_enter_swd_mode(stlink_t *sl); -int _stlink_usb_exit_dfu_mode(stlink_t *sl); -int _stlink_usb_reset(stlink_t *sl); -int _stlink_usb_jtag_reset(stlink_t *sl, int value); -int _stlink_usb_step(stlink_t *sl); -int _stlink_usb_run(stlink_t *sl); -int _stlink_usb_set_swdclk(stlink_t *sl, uint16_t clk_divisor); -int _stlink_usb_exit_debug_mode(stlink_t *sl); -int _stlink_usb_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len); -int _stlink_usb_read_reg(stlink_t *sl, int r_idx, struct stlink_reg *regp); -int _stlink_usb_write_reg(stlink_t *sl, uint32_t reg, int idx); - -#ifdef __cplusplus -} -#endif - -#endif /* STLINK_USB_H */ diff --git a/src/mongoose-6.11/src/common/platforms/wince/wince_libc.c b/src/mongoose-6.11/src/common/platforms/wince/wince_libc.c deleted file mode 100644 index b385fde..0000000 --- a/src/mongoose-6.11/src/common/platforms/wince/wince_libc.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2016 Cesanta Software Limited - * All rights reserved - */ - -#ifdef WINCE - -const char *strerror(int err) { - /* - * TODO(alashkin): there is no strerror on WinCE; - * look for similar wce_xxxx function - */ - static char buf[10]; - snprintf(buf, sizeof(buf), "%d", err); - return buf; -} - -int open(const char *filename, int oflag, int pmode) { - /* - * TODO(alashkin): mg_open function is not used in mongoose - * but exists in documentation as utility function - * Shall we delete it at all or implement for WinCE as well? - */ - DebugBreak(); - return 0; /* for compiler */ -} - -int _wstati64(const wchar_t *path, cs_stat_t *st) { - DWORD fa = GetFileAttributesW(path); - if (fa == INVALID_FILE_ATTRIBUTES) { - return -1; - } - memset(st, 0, sizeof(*st)); - if ((fa & FILE_ATTRIBUTE_DIRECTORY) == 0) { - HANDLE h; - FILETIME ftime; - st->st_mode |= _S_IFREG; - h = CreateFileW(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL); - if (h == INVALID_HANDLE_VALUE) { - return -1; - } - st->st_size = GetFileSize(h, NULL); - GetFileTime(h, NULL, NULL, &ftime); - st->st_mtime = (uint32_t)((((uint64_t) ftime.dwLowDateTime + - ((uint64_t) ftime.dwHighDateTime << 32)) / - 10000000.0) - - 11644473600); - CloseHandle(h); - } else { - st->st_mode |= _S_IFDIR; - } - return 0; -} - -/* Windows CE doesn't have neither gmtime nor strftime */ -static void mg_gmt_time_string(char *buf, size_t buf_len, time_t *t) { - FILETIME ft; - SYSTEMTIME systime; - if (t != NULL) { - uint64_t filetime = (*t + 11644473600) * 10000000; - ft.dwLowDateTime = filetime & 0xFFFFFFFF; - ft.dwHighDateTime = (filetime & 0xFFFFFFFF00000000) >> 32; - FileTimeToSystemTime(&ft, &systime); - } else { - GetSystemTime(&systime); - } - /* There is no PRIu16 in WinCE SDK */ - snprintf(buf, buf_len, "%d.%d.%d %d:%d:%d GMT", (int) systime.wYear, - (int) systime.wMonth, (int) systime.wDay, (int) systime.wHour, - (int) systime.wMinute, (int) systime.wSecond); -} - -#endif diff --git a/src/mongoose-6.11/src/common/platforms/windows/windows_direct.c b/src/mongoose-6.11/src/common/platforms/windows/windows_direct.c deleted file mode 100644 index 8932899..0000000 --- a/src/mongoose-6.11/src/common/platforms/windows/windows_direct.c +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2017 Cesanta Software Limited - * All rights reserved - */ - -#ifdef _WIN32 - -int rmdir(const char *dirname) { - return _rmdir(dirname); -} - -unsigned int sleep(unsigned int seconds) { - Sleep(seconds * 1000); - return 0; -} - -#endif /* _WIN32 */ diff --git a/src/mongoose-6.11/src/common/test_util.c b/src/mongoose-6.11/src/common/test_util.c deleted file mode 100644 index 705a12f..0000000 --- a/src/mongoose-6.11/src/common/test_util.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#include "common/test_util.h" - -#include -#include -#include -#include -#ifndef _WIN32 -#include -#else -#include -#endif - -int num_tests = 0; - -static char *_escape(const char *s, size_t n) { - size_t i, j; - char *res = (char *) malloc(n * 4 + 1); - for (i = j = 0; s[i] != '\0' && i < n; i++) { - if (!iscntrl((int) s[i])) { - res[j++] = s[i]; - } else { - j += sprintf(res + j, "\\x%02x", s[i]); - } - } - res[j] = '\0'; - return res; -} - -void _strfail(const char *a, const char *e, int len) { - char *ae, *ee; - if (len < 0) { - len = strlen(a); - if (strlen(e) > (size_t) len) len = strlen(e); - } - ae = _escape(a, len); - ee = _escape(e, len); - printf("Expected: %s\nActual : %s\n", ee, ae); - free(ae); - free(ee); -} - -int _assert_streq(const char *actual, const char *expected) { - if (strcmp(actual, expected) != 0) { - _strfail(actual, expected, -1); - return 0; - } - return 1; -} - -int _assert_streq_nz(const char *actual, const char *expected) { - size_t n = strlen(expected); - if (strncmp(actual, expected, n) != 0) { - _strfail(actual, expected, n); - return 0; - } - return 1; -} - -#if MG_ENABLE_POLL_UNTIL -int c_str_ne(void *a, void *b) { - int r = strcmp((const char *) a, (const char *) b); - return r; -} - -int c_int_ne(void *a, void *b) { - return *((int *) a) != (intptr_t) b; -} - -int c_int_eq(void *a, void *b) { - return *((int *) a) == (intptr_t) b; -} - -void poll_until(struct mg_mgr *mgr, double timeout, int (*cond)(void *, void *), - void *cond_arg1, void *cond_arg2) { - int i; - double start = cs_time(); - while (cs_time() - start < timeout) { - mg_mgr_poll(mgr, 2); - if (cond != NULL && cond(cond_arg1, cond_arg2)) { - /* A few more cycles to test for overshoots. */ - for (i = 0; i < 5; i++) { - mg_mgr_poll(mgr, 2); - } - return; - } - } -} -#endif diff --git a/src/mongoose-6.11/src/common/test_util.h b/src/mongoose-6.11/src/common/test_util.h deleted file mode 100644 index 6110387..0000000 --- a/src/mongoose-6.11/src/common/test_util.h +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (c) 2015 Cesanta Software Limited - * All rights reserved - * This software is dual-licensed: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. For the terms of this - * license, see . - * - * You are free to use this software under the terms of the GNU General - * Public License, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * Alternatively, you can license this software under a commercial - * license, as set out in . - */ - -#ifndef CS_COMMON_TEST_UTIL_H_ -#define CS_COMMON_TEST_UTIL_H_ - -#include -#include - -#include "common/cs_time.h" - -#ifdef __cplusplus -extern "C" { -#endif - -extern int g_num_tests; - -#ifdef MG_TEST_ABORT_ON_FAIL -#define MG_TEST_ABORT abort() -#else -#define MG_TEST_ABORT -#endif - -int _assert_streq(const char *actual, const char *expected); -int _assert_streq_nz(const char *actual, const char *expected); -void _strfail(const char *a, const char *e, int len); - -#define FAIL(str, line) \ - do { \ - printf("%s:%d:1 [%s] (in %s)\n", __FILE__, line, str, __func__); \ - MG_TEST_ABORT; \ - return str; \ - } while (0) - -#define ASSERT(expr) \ - do { \ - g_num_tests++; \ - if (!(expr)) FAIL(#expr, __LINE__); \ - } while (0) -#define ASSERT_TRUE(expr) ASSERT(expr) -#define ASSERT_FALSE(expr) ASSERT(!(expr)) - -/* - * Run test function, use its name as the test name to print - */ -#define RUN_TEST(test) RUN_TEST_WNAME(test, #test) - -/* - * Run test function, use the provided name as the test name to print - */ -#define RUN_TEST_WNAME(test, test_name) \ - do { \ - const char *msg = NULL; \ - if (strstr(test_name, filter)) { \ - double elapsed = cs_time(); \ - msg = test(); \ - elapsed = cs_time() - elapsed; \ - printf(" [%.3f] %s\n", elapsed, test_name); \ - fflush(stdout); \ - } \ - if (msg) return msg; \ - } while (0) - -/* VC6 doesn't know how to cast an unsigned 64-bit int to double */ -#if (defined(_MSC_VER) && _MSC_VER <= 1200) -#define AS_DOUBLE(d) (double)(int64_t)(d) -#else -#define AS_DOUBLE(d) (double)(d) -#endif - -/* - * Numeric equality assertion. Comparison is made in native types but for - * printing both are convetrted to double. - */ -#define ASSERT_EQ(actual, expected) \ - do { \ - g_num_tests++; \ - if (!((actual) == (expected))) { \ - printf("%f != %f\n", AS_DOUBLE(actual), AS_DOUBLE(expected)); \ - FAIL(#actual " == " #expected, __LINE__); \ - } \ - } while (0) - -/* "Less than" assertion. */ -#define ASSERT_LT(a, b) \ - do { \ - g_num_tests++; \ - if (!((a) < (b))) { \ - printf("%f >= %f\n", AS_DOUBLE(a), AS_DOUBLE(b)); \ - FAIL(#a " < " #b, __LINE__); \ - } \ - } while (0) - -/* "Greater than" assertion. */ -#define ASSERT_GT(a, b) \ - do { \ - g_num_tests++; \ - if (!((a) > (b))) { \ - printf("%f <= %f\n", AS_DOUBLE(a), AS_DOUBLE(b)); \ - FAIL(#a " > " #b, __LINE__); \ - } \ - } while (0) - -/* Assert that actual == expected, where both are NUL-terminated. */ -#define ASSERT_STREQ(actual, expected) \ - do { \ - g_num_tests++; \ - if (!_assert_streq(actual, expected)) { \ - FAIL("ASSERT_STREQ(" #actual ", " #expected ")", __LINE__); \ - } \ - } while (0) - -/* Assert that actual == expected, where both are pointers */ -#define ASSERT_PTREQ(actual, expected) \ - do { \ - g_num_tests++; \ - if (actual != expected) { \ - printf("%p != %p\n", actual, expected); \ - FAIL("ASSERT_PTREQ(" #actual ", " #expected ")", __LINE__); \ - } \ - } while (0) - -/* Assert that actual != expected, where both are pointers */ -#define ASSERT_PTRNEQ(actual, expected) \ - do { \ - g_num_tests++; \ - if (actual == expected) { \ - printf("%p == %p\n", actual, expected); \ - FAIL("ASSERT_PTRNEQ(" #actual ", " #expected ")", __LINE__); \ - } \ - } while (0) - -/* Same as STREQ, but only expected is NUL-terminated. */ -#define ASSERT_STREQ_NZ(actual, expected) \ - do { \ - g_num_tests++; \ - if (!_assert_streq_nz(actual, expected)) { \ - FAIL("ASSERT_STREQ_NZ(" #actual ", " #expected ")", __LINE__); \ - } \ - } while (0) - -#define ASSERT_MG_STREQ(actual, expected) \ - do { \ - g_num_tests++; \ - if ((actual).len != strlen(expected) || \ - memcmp((actual).p, expected, (actual).len) != 0) { \ - printf("'%.*s' (%d) != '%s'\n", (int)(actual).len, (actual).p, \ - (int)(actual).len, expected); \ - FAIL("ASSERT_MG_STREQ(" #actual ", " #expected ")", __LINE__); \ - } \ - } while (0) - -#define CHECK_CALL(call) \ - do { \ - const char *r = (call); \ - if (r != NULL) { \ - fprintf(stderr, "... %s:%d in %s\n", __FILE__, __LINE__, __func__); \ - return r; \ - } \ - } while (0) - -#ifndef MG_ENABLE_POLL_UNTIL -#define MG_ENABLE_POLL_UNTIL 0 -#endif - -#if MG_ENABLE_POLL_UNTIL -#include "mongoose.h" -int c_str_ne(void *a, void *b); -int c_int_ne(void *a, void *b); -int c_int_eq(void *a, void *b); -void poll_until(struct mg_mgr *mgr, double timeout, int (*cond)(void *, void *), - void *cond_arg1, void *cond_arg2); -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* CS_COMMON_TEST_UTIL_H_ */ diff --git a/src/mongoose-6.11/src/mg_coap.c b/src/mongoose-6.11/src/mg_coap.c deleted file mode 100644 index 29667b7..0000000 --- a/src/mongoose-6.11/src/mg_coap.c +++ /dev/null @@ -1,597 +0,0 @@ -/* - * Copyright (c) 2015 Cesanta Software Limited - * All rights reserved - * This software is dual-licensed: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. For the terms of this - * license, see . - * - * You are free to use this software under the terms of the GNU General - * Public License, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * Alternatively, you can license this software under a commercial - * license, as set out in . - */ - -#include "mg_internal.h" -#include "mg_coap.h" - -#if MG_ENABLE_COAP - -void mg_coap_free_options(struct mg_coap_message *cm) { - while (cm->options != NULL) { - struct mg_coap_option *next = cm->options->next; - MG_FREE(cm->options); - cm->options = next; - } -} - -struct mg_coap_option *mg_coap_add_option(struct mg_coap_message *cm, - uint32_t number, char *value, - size_t len) { - struct mg_coap_option *new_option = - (struct mg_coap_option *) MG_CALLOC(1, sizeof(*new_option)); - - new_option->number = number; - new_option->value.p = value; - new_option->value.len = len; - - if (cm->options == NULL) { - cm->options = cm->optiomg_tail = new_option; - } else { - /* - * A very simple attention to help clients to compose options: - * CoAP wants to see options ASC ordered. - * Could be change by using sort in coap_compose - */ - if (cm->optiomg_tail->number <= new_option->number) { - /* if option is already ordered just add it */ - cm->optiomg_tail = cm->optiomg_tail->next = new_option; - } else { - /* looking for appropriate position */ - struct mg_coap_option *current_opt = cm->options; - struct mg_coap_option *prev_opt = 0; - - while (current_opt != NULL) { - if (current_opt->number > new_option->number) { - break; - } - prev_opt = current_opt; - current_opt = current_opt->next; - } - - if (prev_opt != NULL) { - prev_opt->next = new_option; - new_option->next = current_opt; - } else { - /* insert new_option to the beginning */ - new_option->next = cm->options; - cm->options = new_option; - } - } - } - - return new_option; -} - -/* - * Fills CoAP header in mg_coap_message. - * - * Helper function. - */ -static char *coap_parse_header(char *ptr, struct mbuf *io, - struct mg_coap_message *cm) { - if (io->len < sizeof(uint32_t)) { - cm->flags |= MG_COAP_NOT_ENOUGH_DATA; - return NULL; - } - - /* - * Version (Ver): 2-bit unsigned integer. Indicates the CoAP version - * number. Implementations of this specification MUST set this field - * to 1 (01 binary). Other values are reserved for future versions. - * Messages with unknown version numbers MUST be silently ignored. - */ - if (((uint8_t) *ptr >> 6) != 1) { - cm->flags |= MG_COAP_IGNORE; - return NULL; - } - - /* - * Type (T): 2-bit unsigned integer. Indicates if this message is of - * type Confirmable (0), Non-confirmable (1), Acknowledgement (2), or - * Reset (3). - */ - cm->msg_type = ((uint8_t) *ptr & 0x30) >> 4; - cm->flags |= MG_COAP_MSG_TYPE_FIELD; - - /* - * Token Length (TKL): 4-bit unsigned integer. Indicates the length of - * the variable-length Token field (0-8 bytes). Lengths 9-15 are - * reserved, MUST NOT be sent, and MUST be processed as a message - * format error. - */ - cm->token.len = *ptr & 0x0F; - if (cm->token.len > 8) { - cm->flags |= MG_COAP_FORMAT_ERROR; - return NULL; - } - - ptr++; - - /* - * Code: 8-bit unsigned integer, split into a 3-bit class (most - * significant bits) and a 5-bit detail (least significant bits) - */ - cm->code_class = (uint8_t) *ptr >> 5; - cm->code_detail = *ptr & 0x1F; - cm->flags |= (MG_COAP_CODE_CLASS_FIELD | MG_COAP_CODE_DETAIL_FIELD); - - ptr++; - - /* Message ID: 16-bit unsigned integer in network byte order. */ - cm->msg_id = (uint8_t) *ptr << 8 | (uint8_t) * (ptr + 1); - cm->flags |= MG_COAP_MSG_ID_FIELD; - - ptr += 2; - - return ptr; -} - -/* - * Fills token information in mg_coap_message. - * - * Helper function. - */ -static char *coap_get_token(char *ptr, struct mbuf *io, - struct mg_coap_message *cm) { - if (cm->token.len != 0) { - if (ptr + cm->token.len > io->buf + io->len) { - cm->flags |= MG_COAP_NOT_ENOUGH_DATA; - return NULL; - } else { - cm->token.p = ptr; - ptr += cm->token.len; - cm->flags |= MG_COAP_TOKEN_FIELD; - } - } - - return ptr; -} - -/* - * Returns Option Delta or Length. - * - * Helper function. - */ -static int coap_get_ext_opt(char *ptr, struct mbuf *io, uint16_t *opt_info) { - int ret = 0; - - if (*opt_info == 13) { - /* - * 13: An 8-bit unsigned integer follows the initial byte and - * indicates the Option Delta/Length minus 13. - */ - if (ptr < io->buf + io->len) { - *opt_info = (uint8_t) *ptr + 13; - ret = sizeof(uint8_t); - } else { - ret = -1; /* LCOV_EXCL_LINE */ - } - } else if (*opt_info == 14) { - /* - * 14: A 16-bit unsigned integer in network byte order follows the - * initial byte and indicates the Option Delta/Length minus 269. - */ - if (ptr + sizeof(uint8_t) < io->buf + io->len) { - *opt_info = ((uint8_t) *ptr << 8 | (uint8_t) * (ptr + 1)) + 269; - ret = sizeof(uint16_t); - } else { - ret = -1; /* LCOV_EXCL_LINE */ - } - } - - return ret; -} - -/* - * Fills options in mg_coap_message. - * - * Helper function. - * - * General options format: - * +---------------+---------------+ - * | Option Delta | Option Length | 1 byte - * +---------------+---------------+ - * \ Option Delta (extended) \ 0-2 bytes - * +-------------------------------+ - * / Option Length (extended) \ 0-2 bytes - * +-------------------------------+ - * \ Option Value \ 0 or more bytes - * +-------------------------------+ - */ -static char *coap_get_options(char *ptr, struct mbuf *io, - struct mg_coap_message *cm) { - uint16_t prev_opt = 0; - - if (ptr == io->buf + io->len) { - /* end of packet, ok */ - return NULL; - } - - /* 0xFF is payload marker */ - while (ptr < io->buf + io->len && (uint8_t) *ptr != 0xFF) { - uint16_t option_delta, option_lenght; - int optinfo_len; - - /* Option Delta: 4-bit unsigned integer */ - option_delta = ((uint8_t) *ptr & 0xF0) >> 4; - /* Option Length: 4-bit unsigned integer */ - option_lenght = *ptr & 0x0F; - - if (option_delta == 15 || option_lenght == 15) { - /* - * 15: Reserved for future use. If the field is set to this value, - * it MUST be processed as a message format error - */ - cm->flags |= MG_COAP_FORMAT_ERROR; - break; - } - - ptr++; - - /* check for extended option delta */ - optinfo_len = coap_get_ext_opt(ptr, io, &option_delta); - if (optinfo_len == -1) { - cm->flags |= MG_COAP_NOT_ENOUGH_DATA; /* LCOV_EXCL_LINE */ - break; /* LCOV_EXCL_LINE */ - } - - ptr += optinfo_len; - - /* check or extended option lenght */ - optinfo_len = coap_get_ext_opt(ptr, io, &option_lenght); - if (optinfo_len == -1) { - cm->flags |= MG_COAP_NOT_ENOUGH_DATA; /* LCOV_EXCL_LINE */ - break; /* LCOV_EXCL_LINE */ - } - - ptr += optinfo_len; - - /* - * Instead of specifying the Option Number directly, the instances MUST - * appear in order of their Option Numbers and a delta encoding is used - * between them. - */ - option_delta += prev_opt; - - mg_coap_add_option(cm, option_delta, ptr, option_lenght); - - prev_opt = option_delta; - - if (ptr + option_lenght > io->buf + io->len) { - cm->flags |= MG_COAP_NOT_ENOUGH_DATA; /* LCOV_EXCL_LINE */ - break; /* LCOV_EXCL_LINE */ - } - - ptr += option_lenght; - } - - if ((cm->flags & MG_COAP_ERROR) != 0) { - mg_coap_free_options(cm); - return NULL; - } - - cm->flags |= MG_COAP_OPTIOMG_FIELD; - - if (ptr == io->buf + io->len) { - /* end of packet, ok */ - return NULL; - } - - ptr++; - - return ptr; -} - -uint32_t mg_coap_parse(struct mbuf *io, struct mg_coap_message *cm) { - char *ptr; - - memset(cm, 0, sizeof(*cm)); - - if ((ptr = coap_parse_header(io->buf, io, cm)) == NULL) { - return cm->flags; - } - - if ((ptr = coap_get_token(ptr, io, cm)) == NULL) { - return cm->flags; - } - - if ((ptr = coap_get_options(ptr, io, cm)) == NULL) { - return cm->flags; - } - - /* the rest is payload */ - cm->payload.len = io->len - (ptr - io->buf); - if (cm->payload.len != 0) { - cm->payload.p = ptr; - cm->flags |= MG_COAP_PAYLOAD_FIELD; - } - - return cm->flags; -} - -/* - * Calculates extended size of given Opt Number/Length in coap message. - * - * Helper function. - */ -static size_t coap_get_ext_opt_size(uint32_t value) { - int ret = 0; - - if (value >= 13 && value <= 0xFF + 13) { - ret = sizeof(uint8_t); - } else if (value > 0xFF + 13 && value <= 0xFFFF + 269) { - ret = sizeof(uint16_t); - } - - return ret; -} - -/* - * Splits given Opt Number/Length into base and ext values. - * - * Helper function. - */ -static int coap_split_opt(uint32_t value, uint8_t *base, uint16_t *ext) { - int ret = 0; - - if (value < 13) { - *base = value; - } else if (value >= 13 && value <= 0xFF + 13) { - *base = 13; - *ext = value - 13; - ret = sizeof(uint8_t); - } else if (value > 0xFF + 13 && value <= 0xFFFF + 269) { - *base = 14; - *ext = value - 269; - ret = sizeof(uint16_t); - } - - return ret; -} - -/* - * Puts uint16_t (in network order) into given char stream. - * - * Helper function. - */ -static char *coap_add_uint16(char *ptr, uint16_t val) { - *ptr = val >> 8; - ptr++; - *ptr = val & 0x00FF; - ptr++; - return ptr; -} - -/* - * Puts extended value of Opt Number/Length into given char stream. - * - * Helper function. - */ -static char *coap_add_opt_info(char *ptr, uint16_t val, size_t len) { - if (len == sizeof(uint8_t)) { - *ptr = (char) val; - ptr++; - } else if (len == sizeof(uint16_t)) { - ptr = coap_add_uint16(ptr, val); - } - - return ptr; -} - -/* - * Verifies given mg_coap_message and calculates message size for it. - * - * Helper function. - */ -static uint32_t coap_calculate_packet_size(struct mg_coap_message *cm, - size_t *len) { - struct mg_coap_option *opt; - uint32_t prev_opt_number; - - *len = 4; /* header */ - if (cm->msg_type > MG_COAP_MSG_MAX) { - return MG_COAP_ERROR | MG_COAP_MSG_TYPE_FIELD; - } - if (cm->token.len > 8) { - return MG_COAP_ERROR | MG_COAP_TOKEN_FIELD; - } - if (cm->code_class > 7) { - return MG_COAP_ERROR | MG_COAP_CODE_CLASS_FIELD; - } - if (cm->code_detail > 31) { - return MG_COAP_ERROR | MG_COAP_CODE_DETAIL_FIELD; - } - - *len += cm->token.len; - if (cm->payload.len != 0) { - *len += cm->payload.len + 1; /* ... + 1; add payload marker */ - } - - opt = cm->options; - prev_opt_number = 0; - while (opt != NULL) { - *len += 1; /* basic delta/length */ - *len += coap_get_ext_opt_size(opt->number - prev_opt_number); - *len += coap_get_ext_opt_size((uint32_t) opt->value.len); - /* - * Current implementation performs check if - * option_number > previous option_number and produces an error - * TODO(alashkin): write design doc with limitations - * May be resorting is more suitable solution. - */ - if ((opt->next != NULL && opt->number > opt->next->number) || - opt->value.len > 0xFFFF + 269 || - opt->number - prev_opt_number > 0xFFFF + 269) { - return MG_COAP_ERROR | MG_COAP_OPTIOMG_FIELD; - } - *len += opt->value.len; - prev_opt_number = opt->number; - opt = opt->next; - } - - return 0; -} - -uint32_t mg_coap_compose(struct mg_coap_message *cm, struct mbuf *io) { - struct mg_coap_option *opt; - uint32_t res, prev_opt_number; - size_t prev_io_len, packet_size; - char *ptr; - - res = coap_calculate_packet_size(cm, &packet_size); - if (res != 0) { - return res; - } - - /* saving previous lenght to handle non-empty mbuf */ - prev_io_len = io->len; - if (mbuf_append(io, NULL, packet_size) == 0) return MG_COAP_ERROR; - ptr = io->buf + prev_io_len; - - /* - * since cm is verified, it is possible to use bits shift operator - * without additional zeroing of unused bits - */ - - /* ver: 2 bits, msg_type: 2 bits, toklen: 4 bits */ - *ptr = (1 << 6) | (cm->msg_type << 4) | (uint8_t)(cm->token.len); - ptr++; - - /* code class: 3 bits, code detail: 5 bits */ - *ptr = (cm->code_class << 5) | (cm->code_detail); - ptr++; - - ptr = coap_add_uint16(ptr, cm->msg_id); - - if (cm->token.len != 0) { - memcpy(ptr, cm->token.p, cm->token.len); - ptr += cm->token.len; - } - - opt = cm->options; - prev_opt_number = 0; - while (opt != NULL) { - uint8_t delta_base = 0, length_base = 0; - uint16_t delta_ext = 0, length_ext = 0; - - size_t opt_delta_len = - coap_split_opt(opt->number - prev_opt_number, &delta_base, &delta_ext); - size_t opt_lenght_len = - coap_split_opt((uint32_t) opt->value.len, &length_base, &length_ext); - - *ptr = (delta_base << 4) | length_base; - ptr++; - - ptr = coap_add_opt_info(ptr, delta_ext, opt_delta_len); - ptr = coap_add_opt_info(ptr, length_ext, opt_lenght_len); - - if (opt->value.len != 0) { - memcpy(ptr, opt->value.p, opt->value.len); - ptr += opt->value.len; - } - - prev_opt_number = opt->number; - opt = opt->next; - } - - if (cm->payload.len != 0) { - *ptr = (char) -1; - ptr++; - memcpy(ptr, cm->payload.p, cm->payload.len); - } - - return 0; -} - -uint32_t mg_coap_send_message(struct mg_connection *nc, - struct mg_coap_message *cm) { - struct mbuf packet_out; - uint32_t compose_res; - - mbuf_init(&packet_out, 0); - compose_res = mg_coap_compose(cm, &packet_out); - if (compose_res != 0) { - return compose_res; /* LCOV_EXCL_LINE */ - } - - mg_send(nc, packet_out.buf, (int) packet_out.len); - mbuf_free(&packet_out); - - return 0; -} - -uint32_t mg_coap_send_ack(struct mg_connection *nc, uint16_t msg_id) { - struct mg_coap_message cm; - memset(&cm, 0, sizeof(cm)); - cm.msg_type = MG_COAP_MSG_ACK; - cm.msg_id = msg_id; - - return mg_coap_send_message(nc, &cm); -} - -static void coap_handler(struct mg_connection *nc, int ev, - void *ev_data MG_UD_ARG(void *user_data)) { - struct mbuf *io = &nc->recv_mbuf; - struct mg_coap_message cm; - uint32_t parse_res; - - memset(&cm, 0, sizeof(cm)); - - nc->handler(nc, ev, ev_data MG_UD_ARG(user_data)); - - switch (ev) { - case MG_EV_RECV: - parse_res = mg_coap_parse(io, &cm); - if ((parse_res & MG_COAP_IGNORE) == 0) { - if ((cm.flags & MG_COAP_NOT_ENOUGH_DATA) != 0) { - /* - * Since we support UDP only - * MG_COAP_NOT_ENOUGH_DATA == MG_COAP_FORMAT_ERROR - */ - cm.flags |= MG_COAP_FORMAT_ERROR; /* LCOV_EXCL_LINE */ - } /* LCOV_EXCL_LINE */ - nc->handler(nc, MG_COAP_EVENT_BASE + cm.msg_type, - &cm MG_UD_ARG(user_data)); - } - - mg_coap_free_options(&cm); - mbuf_remove(io, io->len); - break; - } -} -/* - * Attach built-in CoAP event handler to the given connection. - * - * The user-defined event handler will receive following extra events: - * - * - MG_EV_COAP_CON - * - MG_EV_COAP_NOC - * - MG_EV_COAP_ACK - * - MG_EV_COAP_RST - */ -int mg_set_protocol_coap(struct mg_connection *nc) { - /* supports UDP only */ - if ((nc->flags & MG_F_UDP) == 0) { - return -1; - } - - nc->proto_handler = coap_handler; - - return 0; -} - -#endif /* MG_ENABLE_COAP */ diff --git a/src/mongoose-6.11/src/mg_coap.h b/src/mongoose-6.11/src/mg_coap.h deleted file mode 100644 index 296933f..0000000 --- a/src/mongoose-6.11/src/mg_coap.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2015 Cesanta Software Limited - * All rights reserved - * This software is dual-licensed: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. For the terms of this - * license, see . - * - * You are free to use this software under the terms of the GNU General - * Public License, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * Alternatively, you can license this software under a commercial - * license, as set out in . - */ - -/* - * === CoAP API reference - * - * CoAP message format: - * - * ``` - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - * |Ver| T | TKL | Code | Message ID | Token (if any, TKL bytes) ... - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - * | Options (if any) ... |1 1 1 1 1 1 1 1| Payload (if any) ... - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - * ``` - */ - -#ifndef CS_MONGOOSE_SRC_COAP_H_ -#define CS_MONGOOSE_SRC_COAP_H_ - -#if MG_ENABLE_COAP - -#define MG_COAP_MSG_TYPE_FIELD 0x2 -#define MG_COAP_CODE_CLASS_FIELD 0x4 -#define MG_COAP_CODE_DETAIL_FIELD 0x8 -#define MG_COAP_MSG_ID_FIELD 0x10 -#define MG_COAP_TOKEN_FIELD 0x20 -#define MG_COAP_OPTIOMG_FIELD 0x40 -#define MG_COAP_PAYLOAD_FIELD 0x80 - -#define MG_COAP_ERROR 0x10000 -#define MG_COAP_FORMAT_ERROR (MG_COAP_ERROR | 0x20000) -#define MG_COAP_IGNORE (MG_COAP_ERROR | 0x40000) -#define MG_COAP_NOT_ENOUGH_DATA (MG_COAP_ERROR | 0x80000) -#define MG_COAP_NETWORK_ERROR (MG_COAP_ERROR | 0x100000) - -#define MG_COAP_MSG_CON 0 -#define MG_COAP_MSG_NOC 1 -#define MG_COAP_MSG_ACK 2 -#define MG_COAP_MSG_RST 3 -#define MG_COAP_MSG_MAX 3 - -#define MG_COAP_CODECLASS_REQUEST 0 -#define MG_COAP_CODECLASS_RESP_OK 2 -#define MG_COAP_CODECLASS_CLIENT_ERR 4 -#define MG_COAP_CODECLASS_SRV_ERR 5 - -#define MG_COAP_EVENT_BASE 300 -#define MG_EV_COAP_CON (MG_COAP_EVENT_BASE + MG_COAP_MSG_CON) -#define MG_EV_COAP_NOC (MG_COAP_EVENT_BASE + MG_COAP_MSG_NOC) -#define MG_EV_COAP_ACK (MG_COAP_EVENT_BASE + MG_COAP_MSG_ACK) -#define MG_EV_COAP_RST (MG_COAP_EVENT_BASE + MG_COAP_MSG_RST) - -/* - * CoAP options. - * Use mg_coap_add_option and mg_coap_free_options - * for creation and destruction. - */ -struct mg_coap_option { - struct mg_coap_option *next; - uint32_t number; - struct mg_str value; -}; - -/* CoAP message. See RFC 7252 for details. */ -struct mg_coap_message { - uint32_t flags; - uint8_t msg_type; - uint8_t code_class; - uint8_t code_detail; - uint16_t msg_id; - struct mg_str token; - struct mg_coap_option *options; - struct mg_str payload; - struct mg_coap_option *optiomg_tail; -}; - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* Sets CoAP protocol handler - triggers CoAP specific events. */ -int mg_set_protocol_coap(struct mg_connection *nc); - -/* - * Adds a new option to mg_coap_message structure. - * Returns pointer to the newly created option. - * Note: options must be freed by using mg_coap_free_options - */ -struct mg_coap_option *mg_coap_add_option(struct mg_coap_message *cm, - uint32_t number, char *value, - size_t len); - -/* - * Frees the memory allocated for options. - * If the cm parameter doesn't contain any option it does nothing. - */ -void mg_coap_free_options(struct mg_coap_message *cm); - -/* - * Composes a CoAP message from `mg_coap_message` - * and sends it into `nc` connection. - * Returns 0 on success. On error, it is a bitmask: - * - * - `#define MG_COAP_ERROR 0x10000` - * - `#define MG_COAP_FORMAT_ERROR (MG_COAP_ERROR | 0x20000)` - * - `#define MG_COAP_IGNORE (MG_COAP_ERROR | 0x40000)` - * - `#define MG_COAP_NOT_ENOUGH_DATA (MG_COAP_ERROR | 0x80000)` - * - `#define MG_COAP_NETWORK_ERROR (MG_COAP_ERROR | 0x100000)` - */ -uint32_t mg_coap_send_message(struct mg_connection *nc, - struct mg_coap_message *cm); - -/* - * Composes CoAP acknowledgement from `mg_coap_message` - * and sends it into `nc` connection. - * Return value: see `mg_coap_send_message()` - */ -uint32_t mg_coap_send_ack(struct mg_connection *nc, uint16_t msg_id); - -/* - * Parses CoAP message and fills mg_coap_message and returns cm->flags. - * This is a helper function. - * - * NOTE: usually CoAP works over UDP, so lack of data means format error. - * But, in theory, it is possible to use CoAP over TCP (according to RFC) - * - * The caller has to check results and treat COAP_NOT_ENOUGH_DATA according to - * underlying protocol: - * - * - in case of UDP COAP_NOT_ENOUGH_DATA means COAP_FORMAT_ERROR, - * - in case of TCP client can try to receive more data - * - * Return value: see `mg_coap_send_message()` - */ -uint32_t mg_coap_parse(struct mbuf *io, struct mg_coap_message *cm); - -/* - * Composes CoAP message from mg_coap_message structure. - * This is a helper function. - * Return value: see `mg_coap_send_message()` - */ -uint32_t mg_coap_compose(struct mg_coap_message *cm, struct mbuf *io); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* MG_ENABLE_COAP */ - -#endif /* CS_MONGOOSE_SRC_COAP_H_ */ diff --git a/src/mongoose-6.11/src/mg_common.h b/src/mongoose-6.11/src/mg_common.h deleted file mode 100644 index 81ecdf9..0000000 --- a/src/mongoose-6.11/src/mg_common.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2004-2013 Sergey Lyubka - * Copyright (c) 2013-2015 Cesanta Software Limited - * All rights reserved - * - * This software is dual-licensed: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. For the terms of this - * license, see . - * - * You are free to use this software under the terms of the GNU General - * Public License, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * Alternatively, you can license this software under a commercial - * license, as set out in . - */ - -#ifndef CS_MONGOOSE_SRC_COMMON_H_ -#define CS_MONGOOSE_SRC_COMMON_H_ - -#define MG_VERSION "6.11" - -/* Local tweaks, applied before any of Mongoose's own headers. */ -#ifdef MG_LOCALS -#include -#endif - -#endif /* CS_MONGOOSE_SRC_COMMON_H_ */ diff --git a/src/mongoose-6.11/src/mg_dns.c b/src/mongoose-6.11/src/mg_dns.c deleted file mode 100644 index 21bc8c2..0000000 --- a/src/mongoose-6.11/src/mg_dns.c +++ /dev/null @@ -1,377 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -#if MG_ENABLE_DNS - -#include "mg_internal.h" -#include "mg_dns.h" - -static int mg_dns_tid = 0xa0; - -struct mg_dns_header { - uint16_t transaction_id; - uint16_t flags; - uint16_t num_questions; - uint16_t num_answers; - uint16_t num_authority_prs; - uint16_t num_other_prs; -}; - -struct mg_dns_resource_record *mg_dns_next_record( - struct mg_dns_message *msg, int query, - struct mg_dns_resource_record *prev) { - struct mg_dns_resource_record *rr; - - for (rr = (prev == NULL ? msg->answers : prev + 1); - rr - msg->answers < msg->num_answers; rr++) { - if (rr->rtype == query) { - return rr; - } - } - return NULL; -} - -int mg_dns_parse_record_data(struct mg_dns_message *msg, - struct mg_dns_resource_record *rr, void *data, - size_t data_len) { - switch (rr->rtype) { - case MG_DNS_A_RECORD: - if (data_len < sizeof(struct in_addr)) { - return -1; - } - if (rr->rdata.p + data_len > msg->pkt.p + msg->pkt.len) { - return -1; - } - memcpy(data, rr->rdata.p, data_len); - return 0; -#if MG_ENABLE_IPV6 - case MG_DNS_AAAA_RECORD: - if (data_len < sizeof(struct in6_addr)) { - return -1; /* LCOV_EXCL_LINE */ - } - memcpy(data, rr->rdata.p, data_len); - return 0; -#endif - case MG_DNS_CNAME_RECORD: - mg_dns_uncompress_name(msg, &rr->rdata, (char *) data, data_len); - return 0; - } - - return -1; -} - -int mg_dns_insert_header(struct mbuf *io, size_t pos, - struct mg_dns_message *msg) { - struct mg_dns_header header; - - memset(&header, 0, sizeof(header)); - header.transaction_id = msg->transaction_id; - header.flags = htons(msg->flags); - header.num_questions = htons(msg->num_questions); - header.num_answers = htons(msg->num_answers); - - return mbuf_insert(io, pos, &header, sizeof(header)); -} - -int mg_dns_copy_questions(struct mbuf *io, struct mg_dns_message *msg) { - unsigned char *begin, *end; - struct mg_dns_resource_record *last_q; - if (msg->num_questions <= 0) return 0; - begin = (unsigned char *) msg->pkt.p + sizeof(struct mg_dns_header); - last_q = &msg->questions[msg->num_questions - 1]; - end = (unsigned char *) last_q->name.p + last_q->name.len + 4; - return mbuf_append(io, begin, end - begin); -} - -int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len) { - const char *s; - unsigned char n; - size_t pos = io->len; - - do { - if ((s = strchr(name, '.')) == NULL) { - s = name + len; - } - - if (s - name > 127) { - return -1; /* TODO(mkm) cover */ - } - n = s - name; /* chunk length */ - mbuf_append(io, &n, 1); /* send length */ - mbuf_append(io, name, n); - - if (*s == '.') { - n++; - } - - name += n; - len -= n; - } while (*s != '\0'); - mbuf_append(io, "\0", 1); /* Mark end of host name */ - - return io->len - pos; -} - -int mg_dns_encode_record(struct mbuf *io, struct mg_dns_resource_record *rr, - const char *name, size_t nlen, const void *rdata, - size_t rlen) { - size_t pos = io->len; - uint16_t u16; - uint32_t u32; - - if (rr->kind == MG_DNS_INVALID_RECORD) { - return -1; /* LCOV_EXCL_LINE */ - } - - if (mg_dns_encode_name(io, name, nlen) == -1) { - return -1; - } - - u16 = htons(rr->rtype); - mbuf_append(io, &u16, 2); - u16 = htons(rr->rclass); - mbuf_append(io, &u16, 2); - - if (rr->kind == MG_DNS_ANSWER) { - u32 = htonl(rr->ttl); - mbuf_append(io, &u32, 4); - - if (rr->rtype == MG_DNS_CNAME_RECORD) { - int clen; - /* fill size after encoding */ - size_t off = io->len; - mbuf_append(io, &u16, 2); - if ((clen = mg_dns_encode_name(io, (const char *) rdata, rlen)) == -1) { - return -1; - } - u16 = clen; - io->buf[off] = u16 >> 8; - io->buf[off + 1] = u16 & 0xff; - } else { - u16 = htons((uint16_t) rlen); - mbuf_append(io, &u16, 2); - mbuf_append(io, rdata, rlen); - } - } - - return io->len - pos; -} - -void mg_send_dns_query(struct mg_connection *nc, const char *name, - int query_type) { - struct mg_dns_message *msg = - (struct mg_dns_message *) MG_CALLOC(1, sizeof(*msg)); - struct mbuf pkt; - struct mg_dns_resource_record *rr = &msg->questions[0]; - - DBG(("%s %d", name, query_type)); - - mbuf_init(&pkt, 64 /* Start small, it'll grow as needed. */); - - msg->transaction_id = ++mg_dns_tid; - msg->flags = 0x100; - msg->num_questions = 1; - - mg_dns_insert_header(&pkt, 0, msg); - - rr->rtype = query_type; - rr->rclass = 1; /* Class: inet */ - rr->kind = MG_DNS_QUESTION; - - if (mg_dns_encode_record(&pkt, rr, name, strlen(name), NULL, 0) == -1) { - /* TODO(mkm): return an error code */ - goto cleanup; /* LCOV_EXCL_LINE */ - } - - /* TCP DNS requires messages to be prefixed with len */ - if (!(nc->flags & MG_F_UDP)) { - uint16_t len = htons((uint16_t) pkt.len); - mbuf_insert(&pkt, 0, &len, 2); - } - - mg_send(nc, pkt.buf, pkt.len); - mbuf_free(&pkt); - -cleanup: - MG_FREE(msg); -} - -static unsigned char *mg_parse_dns_resource_record( - unsigned char *data, unsigned char *end, struct mg_dns_resource_record *rr, - int reply) { - unsigned char *name = data; - int chunk_len, data_len; - - while (data < end && (chunk_len = *data)) { - if (((unsigned char *) data)[0] & 0xc0) { - data += 1; - break; - } - data += chunk_len + 1; - } - - if (data > end - 5) { - return NULL; - } - - rr->name.p = (char *) name; - rr->name.len = data - name + 1; - data++; - - rr->rtype = data[0] << 8 | data[1]; - data += 2; - - rr->rclass = data[0] << 8 | data[1]; - data += 2; - - rr->kind = reply ? MG_DNS_ANSWER : MG_DNS_QUESTION; - if (reply) { - if (data >= end - 6) { - return NULL; - } - - rr->ttl = (uint32_t) data[0] << 24 | (uint32_t) data[1] << 16 | - data[2] << 8 | data[3]; - data += 4; - - data_len = *data << 8 | *(data + 1); - data += 2; - - rr->rdata.p = (char *) data; - rr->rdata.len = data_len; - data += data_len; - } - return data; -} - -int mg_parse_dns(const char *buf, int len, struct mg_dns_message *msg) { - struct mg_dns_header *header = (struct mg_dns_header *) buf; - unsigned char *data = (unsigned char *) buf + sizeof(*header); - unsigned char *end = (unsigned char *) buf + len; - int i; - - memset(msg, 0, sizeof(*msg)); - msg->pkt.p = buf; - msg->pkt.len = len; - - if (len < (int) sizeof(*header)) return -1; - - msg->transaction_id = header->transaction_id; - msg->flags = ntohs(header->flags); - msg->num_questions = ntohs(header->num_questions); - if (msg->num_questions > (int) ARRAY_SIZE(msg->questions)) { - msg->num_questions = (int) ARRAY_SIZE(msg->questions); - } - msg->num_answers = ntohs(header->num_answers); - if (msg->num_answers > (int) ARRAY_SIZE(msg->answers)) { - msg->num_answers = (int) ARRAY_SIZE(msg->answers); - } - - for (i = 0; i < msg->num_questions; i++) { - data = mg_parse_dns_resource_record(data, end, &msg->questions[i], 0); - if (data == NULL) return -1; - } - - for (i = 0; i < msg->num_answers; i++) { - data = mg_parse_dns_resource_record(data, end, &msg->answers[i], 1); - if (data == NULL) return -1; - } - - return 0; -} - -size_t mg_dns_uncompress_name(struct mg_dns_message *msg, struct mg_str *name, - char *dst, int dst_len) { - int chunk_len, num_ptrs = 0; - char *old_dst = dst; - const unsigned char *data = (unsigned char *) name->p; - const unsigned char *end = (unsigned char *) msg->pkt.p + msg->pkt.len; - - if (data >= end) { - return 0; - } - - while ((chunk_len = *data++)) { - int leeway = dst_len - (dst - old_dst); - if (data >= end) { - return 0; - } - - if ((chunk_len & 0xc0) == 0xc0) { - uint16_t off = (data[-1] & (~0xc0)) << 8 | data[0]; - if (off >= msg->pkt.len) { - return 0; - } - /* Basic circular loop avoidance: allow up to 16 pointer hops. */ - if (++num_ptrs > 15) { - return 0; - } - data = (unsigned char *) msg->pkt.p + off; - continue; - } - if (chunk_len > 63) { - return 0; - } - if (chunk_len > leeway) { - chunk_len = leeway; - } - - if (data + chunk_len >= end) { - return 0; - } - - memcpy(dst, data, chunk_len); - data += chunk_len; - dst += chunk_len; - leeway -= chunk_len; - if (leeway == 0) { - return dst - old_dst; - } - *dst++ = '.'; - } - - if (dst != old_dst) { - *--dst = 0; - } - return dst - old_dst; -} - -static void dns_handler(struct mg_connection *nc, int ev, - void *ev_data MG_UD_ARG(void *user_data)) { - struct mbuf *io = &nc->recv_mbuf; - struct mg_dns_message msg; - - /* Pass low-level events to the user handler */ - nc->handler(nc, ev, ev_data MG_UD_ARG(user_data)); - - switch (ev) { - case MG_EV_RECV: - if (!(nc->flags & MG_F_UDP)) { - mbuf_remove(&nc->recv_mbuf, 2); - } - if (mg_parse_dns(nc->recv_mbuf.buf, nc->recv_mbuf.len, &msg) == -1) { - /* reply + recursion allowed + format error */ - memset(&msg, 0, sizeof(msg)); - msg.flags = 0x8081; - mg_dns_insert_header(io, 0, &msg); - if (!(nc->flags & MG_F_UDP)) { - uint16_t len = htons((uint16_t) io->len); - mbuf_insert(io, 0, &len, 2); - } - mg_send(nc, io->buf, io->len); - } else { - /* Call user handler with parsed message */ - nc->handler(nc, MG_DNS_MESSAGE, &msg MG_UD_ARG(user_data)); - } - mbuf_remove(io, io->len); - break; - } -} - -void mg_set_protocol_dns(struct mg_connection *nc) { - nc->proto_handler = dns_handler; -} - -#endif /* MG_ENABLE_DNS */ diff --git a/src/mongoose-6.11/src/mg_dns.h b/src/mongoose-6.11/src/mg_dns.h deleted file mode 100644 index 8a76f58..0000000 --- a/src/mongoose-6.11/src/mg_dns.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -/* - * === DNS API reference - */ - -#ifndef CS_MONGOOSE_SRC_DNS_H_ -#define CS_MONGOOSE_SRC_DNS_H_ - -#include "mg_net.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#define MG_DNS_A_RECORD 0x01 /* Lookup IP address */ -#define MG_DNS_CNAME_RECORD 0x05 /* Lookup CNAME */ -#define MG_DNS_PTR_RECORD 0x0c /* Lookup PTR */ -#define MG_DNS_TXT_RECORD 0x10 /* Lookup TXT */ -#define MG_DNS_AAAA_RECORD 0x1c /* Lookup IPv6 address */ -#define MG_DNS_SRV_RECORD 0x21 /* Lookup SRV */ -#define MG_DNS_MX_RECORD 0x0f /* Lookup mail server for domain */ -#define MG_DNS_ANY_RECORD 0xff -#define MG_DNS_NSEC_RECORD 0x2f - -#define MG_MAX_DNS_QUESTIONS 32 -#define MG_MAX_DNS_ANSWERS 32 - -#define MG_DNS_MESSAGE 100 /* High-level DNS message event */ - -enum mg_dns_resource_record_kind { - MG_DNS_INVALID_RECORD = 0, - MG_DNS_QUESTION, - MG_DNS_ANSWER -}; - -/* DNS resource record. */ -struct mg_dns_resource_record { - struct mg_str name; /* buffer with compressed name */ - int rtype; - int rclass; - int ttl; - enum mg_dns_resource_record_kind kind; - struct mg_str rdata; /* protocol data (can be a compressed name) */ -}; - -/* DNS message (request and response). */ -struct mg_dns_message { - struct mg_str pkt; /* packet body */ - uint16_t flags; - uint16_t transaction_id; - int num_questions; - int num_answers; - struct mg_dns_resource_record questions[MG_MAX_DNS_QUESTIONS]; - struct mg_dns_resource_record answers[MG_MAX_DNS_ANSWERS]; -}; - -struct mg_dns_resource_record *mg_dns_next_record( - struct mg_dns_message *msg, int query, struct mg_dns_resource_record *prev); - -/* - * Parses the record data from a DNS resource record. - * - * - A: struct in_addr *ina - * - AAAA: struct in6_addr *ina - * - CNAME: char buffer - * - * Returns -1 on error. - * - * TODO(mkm): MX - */ -int mg_dns_parse_record_data(struct mg_dns_message *msg, - struct mg_dns_resource_record *rr, void *data, - size_t data_len); - -/* - * Sends a DNS query to the remote end. - */ -void mg_send_dns_query(struct mg_connection *nc, const char *name, - int query_type); - -/* - * Inserts a DNS header to an IO buffer. - * - * Returns the number of bytes inserted. - */ -int mg_dns_insert_header(struct mbuf *io, size_t pos, - struct mg_dns_message *msg); - -/* - * Appends already encoded questions from an existing message. - * - * This is useful when generating a DNS reply message which includes - * all question records. - * - * Returns the number of appended bytes. - */ -int mg_dns_copy_questions(struct mbuf *io, struct mg_dns_message *msg); - -/* - * Encodes and appends a DNS resource record to an IO buffer. - * - * The record metadata is taken from the `rr` parameter, while the name and data - * are taken from the parameters, encoded in the appropriate format depending on - * record type and stored in the IO buffer. The encoded values might contain - * offsets within the IO buffer. It's thus important that the IO buffer doesn't - * get trimmed while a sequence of records are encoded while preparing a DNS - * reply. - * - * This function doesn't update the `name` and `rdata` pointers in the `rr` - * struct because they might be invalidated as soon as the IO buffer grows - * again. - * - * Returns the number of bytes appended or -1 in case of error. - */ -int mg_dns_encode_record(struct mbuf *io, struct mg_dns_resource_record *rr, - const char *name, size_t nlen, const void *rdata, - size_t rlen); - -/* - * Encodes a DNS name. - */ -int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len); - -/* Low-level: parses a DNS response. */ -int mg_parse_dns(const char *buf, int len, struct mg_dns_message *msg); - -/* - * Uncompresses a DNS compressed name. - * - * The containing DNS message is required because of the compressed encoding - * and reference suffixes present elsewhere in the packet. - * - * If the name is less than `dst_len` characters long, the remainder - * of `dst` is terminated with `\0` characters. Otherwise, `dst` is not - * terminated. - * - * If `dst_len` is 0 `dst` can be NULL. - * Returns the uncompressed name length. - */ -size_t mg_dns_uncompress_name(struct mg_dns_message *msg, struct mg_str *name, - char *dst, int dst_len); - -/* - * Attaches a built-in DNS event handler to the given listening connection. - * - * The DNS event handler parses the incoming UDP packets, treating them as DNS - * requests. If an incoming packet gets successfully parsed by the DNS event - * handler, a user event handler will receive an `MG_DNS_REQUEST` event, with - * `ev_data` pointing to the parsed `struct mg_dns_message`. - * - * See - * [captive_dns_server](https://github.com/cesanta/mongoose/tree/master/examples/captive_dns_server) - * example on how to handle DNS request and send DNS reply. - */ -void mg_set_protocol_dns(struct mg_connection *nc); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* CS_MONGOOSE_SRC_DNS_H_ */ diff --git a/src/mongoose-6.11/src/mg_dns_server.c b/src/mongoose-6.11/src/mg_dns_server.c deleted file mode 100644 index 2dafb46..0000000 --- a/src/mongoose-6.11/src/mg_dns_server.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -#if MG_ENABLE_DNS_SERVER - -#include "mg_internal.h" -#include "dns-server.h" - -struct mg_dns_reply mg_dns_create_reply(struct mbuf *io, - struct mg_dns_message *msg) { - struct mg_dns_reply rep; - rep.msg = msg; - rep.io = io; - rep.start = io->len; - - /* reply + recursion allowed */ - msg->flags |= 0x8080; - mg_dns_copy_questions(io, msg); - - msg->num_answers = 0; - return rep; -} - -void mg_dns_send_reply(struct mg_connection *nc, struct mg_dns_reply *r) { - size_t sent = r->io->len - r->start; - mg_dns_insert_header(r->io, r->start, r->msg); - if (!(nc->flags & MG_F_UDP)) { - uint16_t len = htons((uint16_t) sent); - mbuf_insert(r->io, r->start, &len, 2); - } - - if (&nc->send_mbuf != r->io) { - mg_send(nc, r->io->buf + r->start, r->io->len - r->start); - r->io->len = r->start; - } -} - -int mg_dns_reply_record(struct mg_dns_reply *reply, - struct mg_dns_resource_record *question, - const char *name, int rtype, int ttl, const void *rdata, - size_t rdata_len) { - struct mg_dns_message *msg = (struct mg_dns_message *) reply->msg; - char rname[512]; - struct mg_dns_resource_record *ans = &msg->answers[msg->num_answers]; - if (msg->num_answers >= MG_MAX_DNS_ANSWERS) { - return -1; /* LCOV_EXCL_LINE */ - } - - if (name == NULL) { - name = rname; - rname[511] = 0; - mg_dns_uncompress_name(msg, &question->name, rname, sizeof(rname) - 1); - } - - *ans = *question; - ans->kind = MG_DNS_ANSWER; - ans->rtype = rtype; - ans->ttl = ttl; - - if (mg_dns_encode_record(reply->io, ans, name, strlen(name), rdata, - rdata_len) == -1) { - return -1; /* LCOV_EXCL_LINE */ - }; - - msg->num_answers++; - return 0; -} - -#endif /* MG_ENABLE_DNS_SERVER */ diff --git a/src/mongoose-6.11/src/mg_dns_server.h b/src/mongoose-6.11/src/mg_dns_server.h deleted file mode 100644 index 168a174..0000000 --- a/src/mongoose-6.11/src/mg_dns_server.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -/* - * === DNS server API reference - * - * Disabled by default; enable with `-DMG_ENABLE_DNS_SERVER`. - */ - -#ifndef CS_MONGOOSE_SRC_DNS_SERVER_H_ -#define CS_MONGOOSE_SRC_DNS_SERVER_H_ - -#if MG_ENABLE_DNS_SERVER - -#include "mg_dns.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#define MG_DNS_SERVER_DEFAULT_TTL 3600 - -struct mg_dns_reply { - struct mg_dns_message *msg; - struct mbuf *io; - size_t start; -}; - -/* - * Creates a DNS reply. - * - * The reply will be based on an existing query message `msg`. - * The query body will be appended to the output buffer. - * "reply + recursion allowed" will be added to the message flags and the - * message's num_answers will be set to 0. - * - * Answer records can be appended with `mg_dns_send_reply` or by lower - * level function defined in the DNS API. - * - * In order to send a reply use `mg_dns_send_reply`. - * It's possible to use a connection's send buffer as reply buffer, - * and it will work for both UDP and TCP connections. - * - * Example: - * - * ```c - * reply = mg_dns_create_reply(&nc->send_mbuf, msg); - * for (i = 0; i < msg->num_questions; i++) { - * rr = &msg->questions[i]; - * if (rr->rtype == MG_DNS_A_RECORD) { - * mg_dns_reply_record(&reply, rr, 3600, &dummy_ip_addr, 4); - * } - * } - * mg_dns_send_reply(nc, &reply); - * ``` - */ -struct mg_dns_reply mg_dns_create_reply(struct mbuf *io, - struct mg_dns_message *msg); - -/* - * Appends a DNS reply record to the IO buffer and to the DNS message. - * - * The message's num_answers field will be incremented. It's the caller's duty - * to ensure num_answers is properly initialised. - * - * Returns -1 on error. - */ -int mg_dns_reply_record(struct mg_dns_reply *reply, - struct mg_dns_resource_record *question, - const char *name, int rtype, int ttl, const void *rdata, - size_t rdata_len); - -/* - * Sends a DNS reply through a connection. - * - * The DNS data is stored in an IO buffer pointed by reply structure in `r`. - * This function mutates the content of that buffer in order to ensure that - * the DNS header reflects the size and flags of the message, that might have - * been updated either with `mg_dns_reply_record` or by direct manipulation of - * `r->message`. - * - * Once sent, the IO buffer will be trimmed unless the reply IO buffer - * is the connection's send buffer and the connection is not in UDP mode. - */ -void mg_dns_send_reply(struct mg_connection *nc, struct mg_dns_reply *r); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* MG_ENABLE_DNS_SERVER */ -#endif /* CS_MONGOOSE_SRC_DNS_SERVER_H_ */ diff --git a/src/mongoose-6.11/src/mg_features.h b/src/mongoose-6.11/src/mg_features.h deleted file mode 100644 index 515242d..0000000 --- a/src/mongoose-6.11/src/mg_features.h +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_MONGOOSE_SRC_FEATURES_H_ -#define CS_MONGOOSE_SRC_FEATURES_H_ - -#ifndef MG_DISABLE_HTTP_DIGEST_AUTH -#define MG_DISABLE_HTTP_DIGEST_AUTH 0 -#endif - -#ifndef MG_DISABLE_HTTP_KEEP_ALIVE -#define MG_DISABLE_HTTP_KEEP_ALIVE 0 -#endif - -#ifndef MG_DISABLE_PFS -#define MG_DISABLE_PFS 0 -#endif - -#ifndef MG_DISABLE_WS_RANDOM_MASK -#define MG_DISABLE_WS_RANDOM_MASK 0 -#endif - -#ifndef MG_ENABLE_ASYNC_RESOLVER -#define MG_ENABLE_ASYNC_RESOLVER 1 -#endif - -#ifndef MG_ENABLE_BROADCAST -#define MG_ENABLE_BROADCAST 0 -#endif - -#ifndef MG_ENABLE_COAP -#define MG_ENABLE_COAP 0 -#endif - -#ifndef MG_ENABLE_DEBUG -#define MG_ENABLE_DEBUG 0 -#endif - -#ifndef MG_ENABLE_DIRECTORY_LISTING -#define MG_ENABLE_DIRECTORY_LISTING 0 -#endif - -#ifndef MG_ENABLE_DNS -#define MG_ENABLE_DNS 1 -#endif - -#ifndef MG_ENABLE_DNS_SERVER -#define MG_ENABLE_DNS_SERVER 0 -#endif - -#ifndef MG_ENABLE_FAKE_DAVLOCK -#define MG_ENABLE_FAKE_DAVLOCK 0 -#endif - -#ifndef MG_ENABLE_FILESYSTEM -#define MG_ENABLE_FILESYSTEM 0 -#endif - -#ifndef MG_ENABLE_GETADDRINFO -#define MG_ENABLE_GETADDRINFO 0 -#endif - -#ifndef MG_ENABLE_HEXDUMP -#define MG_ENABLE_HEXDUMP CS_ENABLE_STDIO -#endif - -#ifndef MG_ENABLE_HTTP -#define MG_ENABLE_HTTP 1 -#endif - -#ifndef MG_ENABLE_HTTP_CGI -#define MG_ENABLE_HTTP_CGI 0 -#endif - -#ifndef MG_ENABLE_HTTP_SSI -#define MG_ENABLE_HTTP_SSI MG_ENABLE_FILESYSTEM -#endif - -#ifndef MG_ENABLE_HTTP_SSI_EXEC -#define MG_ENABLE_HTTP_SSI_EXEC 0 -#endif - -#ifndef MG_ENABLE_HTTP_STREAMING_MULTIPART -#define MG_ENABLE_HTTP_STREAMING_MULTIPART 0 -#endif - -#ifndef MG_ENABLE_HTTP_WEBDAV -#define MG_ENABLE_HTTP_WEBDAV 0 -#endif - -#ifndef MG_ENABLE_HTTP_WEBSOCKET -#define MG_ENABLE_HTTP_WEBSOCKET MG_ENABLE_HTTP -#endif - -#ifndef MG_ENABLE_IPV6 -#define MG_ENABLE_IPV6 0 -#endif - -#ifndef MG_ENABLE_MQTT -#define MG_ENABLE_MQTT 1 -#endif - -#ifndef MG_ENABLE_SOCKS -#define MG_ENABLE_SOCKS 0 -#endif - -#ifndef MG_ENABLE_MQTT_BROKER -#define MG_ENABLE_MQTT_BROKER 0 -#endif - -#ifndef MG_ENABLE_SSL -#define MG_ENABLE_SSL 0 -#endif - -#ifndef MG_ENABLE_SYNC_RESOLVER -#define MG_ENABLE_SYNC_RESOLVER 0 -#endif - -#ifndef MG_ENABLE_STDIO -#define MG_ENABLE_STDIO CS_ENABLE_STDIO -#endif - -#ifndef MG_NET_IF -#define MG_NET_IF MG_NET_IF_SOCKET -#endif - -#ifndef MG_SSL_IF -#define MG_SSL_IF MG_SSL_IF_OPENSSL -#endif - -#ifndef MG_ENABLE_THREADS /* ifdef-ok */ -#ifdef _WIN32 -#define MG_ENABLE_THREADS 1 -#else -#define MG_ENABLE_THREADS 0 -#endif -#endif - -#if MG_ENABLE_DEBUG && !defined(CS_ENABLE_DEBUG) -#define CS_ENABLE_DEBUG 1 -#endif - -/* MQTT broker requires MQTT */ -#if MG_ENABLE_MQTT_BROKER && !MG_ENABLE_MQTT -#undef MG_ENABLE_MQTT -#define MG_ENABLE_MQTT 1 -#endif - -#ifndef MG_ENABLE_HTTP_URL_REWRITES -#define MG_ENABLE_HTTP_URL_REWRITES \ - (CS_PLATFORM == CS_P_WINDOWS || CS_PLATFORM == CS_P_UNIX) -#endif - -#ifndef MG_ENABLE_SNTP -#define MG_ENABLE_SNTP 0 -#endif - -#ifndef MG_ENABLE_EXTRA_ERRORS_DESC -#define MG_ENABLE_EXTRA_ERRORS_DESC 0 -#endif - -#ifndef MG_ENABLE_CALLBACK_USERDATA -#define MG_ENABLE_CALLBACK_USERDATA 0 -#endif - -#if MG_ENABLE_CALLBACK_USERDATA -#define MG_UD_ARG(ud) , ud -#define MG_CB(cb, ud) cb, ud -#else -#define MG_UD_ARG(ud) -#define MG_CB(cb, ud) cb -#endif - -#endif /* CS_MONGOOSE_SRC_FEATURES_H_ */ diff --git a/src/mongoose-6.11/src/mg_http.c b/src/mongoose-6.11/src/mg_http.c deleted file mode 100644 index 068d208..0000000 --- a/src/mongoose-6.11/src/mg_http.c +++ /dev/null @@ -1,3077 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -#if MG_ENABLE_HTTP - -#include "common/cs_md5.h" -#include "mg_internal.h" -#include "mg_util.h" - -/* altbuf {{{ */ - -/* - * Alternate buffer: fills the client-provided buffer with data; and if it's - * not large enough, allocates another buffer (via mbuf), similar to asprintf. - */ -struct altbuf { - struct mbuf m; - char *user_buf; - size_t len; - size_t user_buf_size; -}; - -/* - * Initializes altbuf; `buf`, `buf_size` is the client-provided buffer. - */ -MG_INTERNAL void altbuf_init(struct altbuf *ab, char *buf, size_t buf_size) { - mbuf_init(&ab->m, 0); - ab->user_buf = buf; - ab->user_buf_size = buf_size; - ab->len = 0; -} - -/* - * Appends a single char to the altbuf. - */ -MG_INTERNAL void altbuf_append(struct altbuf *ab, char c) { - if (ab->len < ab->user_buf_size) { - /* The data fits into the original buffer */ - ab->user_buf[ab->len++] = c; - } else { - /* The data can't fit into the original buffer, so write it to mbuf. */ - - /* - * First of all, see if that's the first byte which overflows the original - * buffer: if so, copy the existing data from there to a newly allocated - * mbuf. - */ - if (ab->len > 0 && ab->m.len == 0) { - mbuf_append(&ab->m, ab->user_buf, ab->len); - } - - mbuf_append(&ab->m, &c, 1); - ab->len = ab->m.len; - } -} - -/* - * Resets any data previously appended to altbuf. - */ -MG_INTERNAL void altbuf_reset(struct altbuf *ab) { - mbuf_free(&ab->m); - ab->len = 0; -} - -/* - * Returns whether the additional buffer was allocated (and thus the data - * is in the mbuf, not the client-provided buffer) - */ -MG_INTERNAL int altbuf_reallocated(struct altbuf *ab) { - return ab->len > ab->user_buf_size; -} - -/* - * Returns the actual buffer with data, either the client-provided or a newly - * allocated one. If `trim` is non-zero, mbuf-backed buffer is trimmed first. - */ -MG_INTERNAL char *altbuf_get_buf(struct altbuf *ab, int trim) { - if (altbuf_reallocated(ab)) { - if (trim) { - mbuf_trim(&ab->m); - } - return ab->m.buf; - } else { - return ab->user_buf; - } -} - -/* }}} */ - -static const char *mg_version_header = "Mongoose/" MG_VERSION; - -enum mg_http_proto_data_type { DATA_NONE, DATA_FILE, DATA_PUT }; - -struct mg_http_proto_data_file { - FILE *fp; /* Opened file. */ - int64_t cl; /* Content-Length. How many bytes to send. */ - int64_t sent; /* How many bytes have been already sent. */ - int keepalive; /* Keep connection open after sending. */ - enum mg_http_proto_data_type type; -}; - -#if MG_ENABLE_HTTP_CGI -struct mg_http_proto_data_cgi { - struct mg_connection *cgi_nc; -}; -#endif - -struct mg_http_proto_data_chuncked { - int64_t body_len; /* How many bytes of chunked body was reassembled. */ -}; - -struct mg_http_endpoint { - struct mg_http_endpoint *next; - struct mg_str uri_pattern; /* owned */ - char *auth_domain; /* owned */ - char *auth_file; /* owned */ - - mg_event_handler_t handler; -#if MG_ENABLE_CALLBACK_USERDATA - void *user_data; -#endif -}; - -enum mg_http_multipart_stream_state { - MPS_BEGIN, - MPS_WAITING_FOR_BOUNDARY, - MPS_WAITING_FOR_CHUNK, - MPS_GOT_CHUNK, - MPS_GOT_BOUNDARY, - MPS_FINALIZE, - MPS_FINISHED -}; - -struct mg_http_multipart_stream { - const char *boundary; - int boundary_len; - const char *var_name; - const char *file_name; - void *user_data; - int prev_io_len; - enum mg_http_multipart_stream_state state; - int processing_part; -}; - -struct mg_reverse_proxy_data { - struct mg_connection *linked_conn; -}; - -struct mg_ws_proto_data { - /* - * Defragmented size of the frame so far. - * - * First byte of nc->recv_mbuf.buf is an op, the rest of the data is - * defragmented data. - */ - size_t reass_len; -}; - -struct mg_http_proto_data { -#if MG_ENABLE_FILESYSTEM - struct mg_http_proto_data_file file; -#endif -#if MG_ENABLE_HTTP_CGI - struct mg_http_proto_data_cgi cgi; -#endif -#if MG_ENABLE_HTTP_STREAMING_MULTIPART - struct mg_http_multipart_stream mp_stream; -#endif -#if MG_ENABLE_HTTP_WEBSOCKET - struct mg_ws_proto_data ws_data; -#endif - struct mg_http_proto_data_chuncked chunk; - struct mg_http_endpoint *endpoints; - mg_event_handler_t endpoint_handler; - struct mg_reverse_proxy_data reverse_proxy_data; - size_t rcvd; /* How many bytes we have received. */ -}; - -static void mg_http_conn_destructor(void *proto_data); -struct mg_connection *mg_connect_http_base( - struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), - struct mg_connect_opts opts, const char *scheme1, const char *scheme2, - const char *scheme_ssl1, const char *scheme_ssl2, const char *url, - struct mg_str *path, struct mg_str *user_info, struct mg_str *host); - -static struct mg_http_proto_data *mg_http_get_proto_data( - struct mg_connection *c) { - if (c->proto_data == NULL) { - c->proto_data = MG_CALLOC(1, sizeof(struct mg_http_proto_data)); - c->proto_data_destructor = mg_http_conn_destructor; - } - - return (struct mg_http_proto_data *) c->proto_data; -} - -#if MG_ENABLE_HTTP_STREAMING_MULTIPART -static void mg_http_free_proto_data_mp_stream( - struct mg_http_multipart_stream *mp) { - MG_FREE((void *) mp->boundary); - MG_FREE((void *) mp->var_name); - MG_FREE((void *) mp->file_name); - memset(mp, 0, sizeof(*mp)); -} -#endif - -#if MG_ENABLE_FILESYSTEM -static void mg_http_free_proto_data_file(struct mg_http_proto_data_file *d) { - if (d != NULL) { - if (d->fp != NULL) { - fclose(d->fp); - } - memset(d, 0, sizeof(struct mg_http_proto_data_file)); - } -} -#endif - -static void mg_http_free_proto_data_endpoints(struct mg_http_endpoint **ep) { - struct mg_http_endpoint *current = *ep; - - while (current != NULL) { - struct mg_http_endpoint *tmp = current->next; - MG_FREE((void *) current->uri_pattern.p); - MG_FREE((void *) current->auth_domain); - MG_FREE((void *) current->auth_file); - MG_FREE(current); - current = tmp; - } - - ep = NULL; -} - -static void mg_http_free_reverse_proxy_data(struct mg_reverse_proxy_data *rpd) { - if (rpd->linked_conn != NULL) { - /* - * Connection has linked one, we have to unlink & close it - * since _this_ connection is going to die and - * it doesn't make sense to keep another one - */ - struct mg_http_proto_data *pd = mg_http_get_proto_data(rpd->linked_conn); - if (pd->reverse_proxy_data.linked_conn != NULL) { - pd->reverse_proxy_data.linked_conn->flags |= MG_F_SEND_AND_CLOSE; - pd->reverse_proxy_data.linked_conn = NULL; - } - rpd->linked_conn = NULL; - } -} - -static void mg_http_conn_destructor(void *proto_data) { - struct mg_http_proto_data *pd = (struct mg_http_proto_data *) proto_data; -#if MG_ENABLE_FILESYSTEM - mg_http_free_proto_data_file(&pd->file); -#endif -#if MG_ENABLE_HTTP_CGI - mg_http_free_proto_data_cgi(&pd->cgi); -#endif -#if MG_ENABLE_HTTP_STREAMING_MULTIPART - mg_http_free_proto_data_mp_stream(&pd->mp_stream); -#endif - mg_http_free_proto_data_endpoints(&pd->endpoints); - mg_http_free_reverse_proxy_data(&pd->reverse_proxy_data); - MG_FREE(proto_data); -} - -#if MG_ENABLE_FILESYSTEM - -#define MIME_ENTRY(_ext, _type) \ - { _ext, sizeof(_ext) - 1, _type } -static const struct { - const char *extension; - size_t ext_len; - const char *mime_type; -} mg_static_builtin_mime_types[] = { - MIME_ENTRY("html", "text/html"), - MIME_ENTRY("html", "text/html"), - MIME_ENTRY("htm", "text/html"), - MIME_ENTRY("shtm", "text/html"), - MIME_ENTRY("shtml", "text/html"), - MIME_ENTRY("css", "text/css"), - MIME_ENTRY("js", "application/x-javascript"), - MIME_ENTRY("ico", "image/x-icon"), - MIME_ENTRY("gif", "image/gif"), - MIME_ENTRY("jpg", "image/jpeg"), - MIME_ENTRY("jpeg", "image/jpeg"), - MIME_ENTRY("png", "image/png"), - MIME_ENTRY("svg", "image/svg+xml"), - MIME_ENTRY("txt", "text/plain"), - MIME_ENTRY("torrent", "application/x-bittorrent"), - MIME_ENTRY("wav", "audio/x-wav"), - MIME_ENTRY("mp3", "audio/x-mp3"), - MIME_ENTRY("mid", "audio/mid"), - MIME_ENTRY("m3u", "audio/x-mpegurl"), - MIME_ENTRY("ogg", "application/ogg"), - MIME_ENTRY("ram", "audio/x-pn-realaudio"), - MIME_ENTRY("xml", "text/xml"), - MIME_ENTRY("ttf", "application/x-font-ttf"), - MIME_ENTRY("json", "application/json"), - MIME_ENTRY("xslt", "application/xml"), - MIME_ENTRY("xsl", "application/xml"), - MIME_ENTRY("ra", "audio/x-pn-realaudio"), - MIME_ENTRY("doc", "application/msword"), - MIME_ENTRY("exe", "application/octet-stream"), - MIME_ENTRY("zip", "application/x-zip-compressed"), - MIME_ENTRY("xls", "application/excel"), - MIME_ENTRY("tgz", "application/x-tar-gz"), - MIME_ENTRY("tar", "application/x-tar"), - MIME_ENTRY("gz", "application/x-gunzip"), - MIME_ENTRY("arj", "application/x-arj-compressed"), - MIME_ENTRY("rar", "application/x-rar-compressed"), - MIME_ENTRY("rtf", "application/rtf"), - MIME_ENTRY("pdf", "application/pdf"), - MIME_ENTRY("swf", "application/x-shockwave-flash"), - MIME_ENTRY("mpg", "video/mpeg"), - MIME_ENTRY("webm", "video/webm"), - MIME_ENTRY("mpeg", "video/mpeg"), - MIME_ENTRY("mov", "video/quicktime"), - MIME_ENTRY("mp4", "video/mp4"), - MIME_ENTRY("m4v", "video/x-m4v"), - MIME_ENTRY("asf", "video/x-ms-asf"), - MIME_ENTRY("avi", "video/x-msvideo"), - MIME_ENTRY("bmp", "image/bmp"), - {NULL, 0, NULL}}; - -static struct mg_str mg_get_mime_type(const char *path, const char *dflt, - const struct mg_serve_http_opts *opts) { - const char *ext, *overrides; - size_t i, path_len; - struct mg_str r, k, v; - - path_len = strlen(path); - - overrides = opts->custom_mime_types; - while ((overrides = mg_next_comma_list_entry(overrides, &k, &v)) != NULL) { - ext = path + (path_len - k.len); - if (path_len > k.len && mg_vcasecmp(&k, ext) == 0) { - return v; - } - } - - for (i = 0; mg_static_builtin_mime_types[i].extension != NULL; i++) { - ext = path + (path_len - mg_static_builtin_mime_types[i].ext_len); - if (path_len > mg_static_builtin_mime_types[i].ext_len && ext[-1] == '.' && - mg_casecmp(ext, mg_static_builtin_mime_types[i].extension) == 0) { - r.p = mg_static_builtin_mime_types[i].mime_type; - r.len = strlen(r.p); - return r; - } - } - - r.p = dflt; - r.len = strlen(r.p); - return r; -} -#endif - -/* - * Check whether full request is buffered. Return: - * -1 if request is malformed - * 0 if request is not yet fully buffered - * >0 actual request length, including last \r\n\r\n - */ -static int mg_http_get_request_len(const char *s, int buf_len) { - const unsigned char *buf = (unsigned char *) s; - int i; - - for (i = 0; i < buf_len; i++) { - if (!isprint(buf[i]) && buf[i] != '\r' && buf[i] != '\n' && buf[i] < 128) { - return -1; - } else if (buf[i] == '\n' && i + 1 < buf_len && buf[i + 1] == '\n') { - return i + 2; - } else if (buf[i] == '\n' && i + 2 < buf_len && buf[i + 1] == '\r' && - buf[i + 2] == '\n') { - return i + 3; - } - } - - return 0; -} - -static const char *mg_http_parse_headers(const char *s, const char *end, - int len, struct http_message *req) { - int i = 0; - while (i < (int) ARRAY_SIZE(req->header_names) - 1) { - struct mg_str *k = &req->header_names[i], *v = &req->header_values[i]; - - s = mg_skip(s, end, ": ", k); - s = mg_skip(s, end, "\r\n", v); - - while (v->len > 0 && v->p[v->len - 1] == ' ') { - v->len--; /* Trim trailing spaces in header value */ - } - - /* - * If header value is empty - skip it and go to next (if any). - * NOTE: Do not add it to headers_values because such addition changes API - * behaviour - */ - if (k->len != 0 && v->len == 0) { - continue; - } - - if (k->len == 0 || v->len == 0) { - k->p = v->p = NULL; - k->len = v->len = 0; - break; - } - - if (!mg_ncasecmp(k->p, "Content-Length", 14)) { - req->body.len = (size_t) to64(v->p); - req->message.len = len + req->body.len; - } - - i++; - } - - return s; -} - -int mg_parse_http(const char *s, int n, struct http_message *hm, int is_req) { - const char *end, *qs; - int len = mg_http_get_request_len(s, n); - - if (len <= 0) return len; - - memset(hm, 0, sizeof(*hm)); - hm->message.p = s; - hm->body.p = s + len; - hm->message.len = hm->body.len = (size_t) ~0; - end = s + len; - - /* Request is fully buffered. Skip leading whitespaces. */ - while (s < end && isspace(*(unsigned char *) s)) s++; - - if (is_req) { - /* Parse request line: method, URI, proto */ - s = mg_skip(s, end, " ", &hm->method); - s = mg_skip(s, end, " ", &hm->uri); - s = mg_skip(s, end, "\r\n", &hm->proto); - if (hm->uri.p <= hm->method.p || hm->proto.p <= hm->uri.p) return -1; - - /* If URI contains '?' character, initialize query_string */ - if ((qs = (char *) memchr(hm->uri.p, '?', hm->uri.len)) != NULL) { - hm->query_string.p = qs + 1; - hm->query_string.len = &hm->uri.p[hm->uri.len] - (qs + 1); - hm->uri.len = qs - hm->uri.p; - } - } else { - s = mg_skip(s, end, " ", &hm->proto); - if (end - s < 4 || s[3] != ' ') return -1; - hm->resp_code = atoi(s); - if (hm->resp_code < 100 || hm->resp_code >= 600) return -1; - s += 4; - s = mg_skip(s, end, "\r\n", &hm->resp_status_msg); - } - - s = mg_http_parse_headers(s, end, len, hm); - - /* - * mg_parse_http() is used to parse both HTTP requests and HTTP - * responses. If HTTP response does not have Content-Length set, then - * body is read until socket is closed, i.e. body.len is infinite (~0). - * - * For HTTP requests though, according to - * http://tools.ietf.org/html/rfc7231#section-8.1.3, - * only POST and PUT methods have defined body semantics. - * Therefore, if Content-Length is not specified and methods are - * not one of PUT or POST, set body length to 0. - * - * So, - * if it is HTTP request, and Content-Length is not set, - * and method is not (PUT or POST) then reset body length to zero. - */ - if (hm->body.len == (size_t) ~0 && is_req && - mg_vcasecmp(&hm->method, "PUT") != 0 && - mg_vcasecmp(&hm->method, "POST") != 0) { - hm->body.len = 0; - hm->message.len = len; - } - - return len; -} - -struct mg_str *mg_get_http_header(struct http_message *hm, const char *name) { - size_t i, len = strlen(name); - - for (i = 0; hm->header_names[i].len > 0; i++) { - struct mg_str *h = &hm->header_names[i], *v = &hm->header_values[i]; - if (h->p != NULL && h->len == len && !mg_ncasecmp(h->p, name, len)) - return v; - } - - return NULL; -} - -#if MG_ENABLE_FILESYSTEM -static void mg_http_transfer_file_data(struct mg_connection *nc) { - struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); - char buf[MG_MAX_HTTP_SEND_MBUF]; - size_t n = 0, to_read = 0, left = (size_t)(pd->file.cl - pd->file.sent); - - if (pd->file.type == DATA_FILE) { - struct mbuf *io = &nc->send_mbuf; - if (io->len >= MG_MAX_HTTP_SEND_MBUF) { - to_read = 0; - } else { - to_read = MG_MAX_HTTP_SEND_MBUF - io->len; - } - if (to_read > left) { - to_read = left; - } - if (to_read > 0) { - n = mg_fread(buf, 1, to_read, pd->file.fp); - if (n > 0) { - mg_send(nc, buf, n); - pd->file.sent += n; - DBG(("%p sent %d (total %d)", nc, (int) n, (int) pd->file.sent)); - } - } else { - /* Rate-limited */ - } - if (pd->file.sent >= pd->file.cl) { - LOG(LL_DEBUG, ("%p done, %d bytes", nc, (int) pd->file.sent)); - if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE; - mg_http_free_proto_data_file(&pd->file); - } - } else if (pd->file.type == DATA_PUT) { - struct mbuf *io = &nc->recv_mbuf; - size_t to_write = left <= 0 ? 0 : left < io->len ? (size_t) left : io->len; - size_t n = mg_fwrite(io->buf, 1, to_write, pd->file.fp); - if (n > 0) { - mbuf_remove(io, n); - pd->file.sent += n; - } - if (n == 0 || pd->file.sent >= pd->file.cl) { - if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE; - mg_http_free_proto_data_file(&pd->file); - } - } -#if MG_ENABLE_HTTP_CGI - else if (pd->cgi.cgi_nc != NULL) { - /* This is POST data that needs to be forwarded to the CGI process */ - if (pd->cgi.cgi_nc != NULL) { - mg_forward(nc, pd->cgi.cgi_nc); - } else { - nc->flags |= MG_F_SEND_AND_CLOSE; - } - } -#endif -} -#endif /* MG_ENABLE_FILESYSTEM */ - -/* - * Parse chunked-encoded buffer. Return 0 if the buffer is not encoded, or - * if it's incomplete. If the chunk is fully buffered, return total number of - * bytes in a chunk, and store data in `data`, `data_len`. - */ -static size_t mg_http_parse_chunk(char *buf, size_t len, char **chunk_data, - size_t *chunk_len) { - unsigned char *s = (unsigned char *) buf; - size_t n = 0; /* scanned chunk length */ - size_t i = 0; /* index in s */ - - /* Scan chunk length. That should be a hexadecimal number. */ - while (i < len && isxdigit(s[i])) { - n *= 16; - n += (s[i] >= '0' && s[i] <= '9') ? s[i] - '0' : tolower(s[i]) - 'a' + 10; - i++; - } - - /* Skip new line */ - if (i == 0 || i + 2 > len || s[i] != '\r' || s[i + 1] != '\n') { - return 0; - } - i += 2; - - /* Record where the data is */ - *chunk_data = (char *) s + i; - *chunk_len = n; - - /* Skip data */ - i += n; - - /* Skip new line */ - if (i == 0 || i + 2 > len || s[i] != '\r' || s[i + 1] != '\n') { - return 0; - } - return i + 2; -} - -MG_INTERNAL size_t mg_handle_chunked(struct mg_connection *nc, - struct http_message *hm, char *buf, - size_t blen) { - struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); - char *data; - size_t i, n, data_len, body_len, zero_chunk_received = 0; - /* Find out piece of received data that is not yet reassembled */ - body_len = (size_t) pd->chunk.body_len; - assert(blen >= body_len); - - /* Traverse all fully buffered chunks */ - for (i = body_len; - (n = mg_http_parse_chunk(buf + i, blen - i, &data, &data_len)) > 0; - i += n) { - /* Collapse chunk data to the rest of HTTP body */ - memmove(buf + body_len, data, data_len); - body_len += data_len; - hm->body.len = body_len; - - if (data_len == 0) { - zero_chunk_received = 1; - i += n; - break; - } - } - - if (i > body_len) { - /* Shift unparsed content to the parsed body */ - assert(i <= blen); - memmove(buf + body_len, buf + i, blen - i); - memset(buf + body_len + blen - i, 0, i - body_len); - nc->recv_mbuf.len -= i - body_len; - pd->chunk.body_len = body_len; - - /* Send MG_EV_HTTP_CHUNK event */ - nc->flags &= ~MG_F_DELETE_CHUNK; - mg_call(nc, nc->handler, nc->user_data, MG_EV_HTTP_CHUNK, hm); - - /* Delete processed data if user set MG_F_DELETE_CHUNK flag */ - if (nc->flags & MG_F_DELETE_CHUNK) { - memset(buf, 0, body_len); - memmove(buf, buf + body_len, blen - i); - nc->recv_mbuf.len -= body_len; - hm->body.len = 0; - pd->chunk.body_len = 0; - } - - if (zero_chunk_received) { - /* Total message size is len(body) + len(headers) */ - hm->message.len = - (size_t) pd->chunk.body_len + blen - i + (hm->body.p - hm->message.p); - } - } - - return body_len; -} - -struct mg_http_endpoint *mg_http_get_endpoint_handler(struct mg_connection *nc, - struct mg_str *uri_path) { - struct mg_http_proto_data *pd; - struct mg_http_endpoint *ret = NULL; - int matched, matched_max = 0; - struct mg_http_endpoint *ep; - - if (nc == NULL) { - return NULL; - } - - pd = mg_http_get_proto_data(nc); - - ep = pd->endpoints; - while (ep != NULL) { - if ((matched = mg_match_prefix_n(ep->uri_pattern, *uri_path)) > 0) { - if (matched > matched_max) { - /* Looking for the longest suitable handler */ - ret = ep; - matched_max = matched; - } - } - - ep = ep->next; - } - - return ret; -} - -#if MG_ENABLE_HTTP_STREAMING_MULTIPART -static void mg_http_multipart_continue(struct mg_connection *nc); - -static void mg_http_multipart_begin(struct mg_connection *nc, - struct http_message *hm, int req_len); - -#endif - -static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev, - struct http_message *hm); - -static void deliver_chunk(struct mg_connection *c, struct http_message *hm, - int req_len) { - /* Incomplete message received. Send MG_EV_HTTP_CHUNK event */ - hm->body.len = c->recv_mbuf.len - req_len; - c->flags &= ~MG_F_DELETE_CHUNK; - mg_call(c, c->handler, c->user_data, MG_EV_HTTP_CHUNK, hm); - /* Delete processed data if user set MG_F_DELETE_CHUNK flag */ - if (c->flags & MG_F_DELETE_CHUNK) c->recv_mbuf.len = req_len; -} - -/* - * lx106 compiler has a bug (TODO(mkm) report and insert tracking bug here) - * If a big structure is declared in a big function, lx106 gcc will make it - * even bigger (round up to 4k, from 700 bytes of actual size). - */ -#ifdef __xtensa__ -static void mg_http_handler2(struct mg_connection *nc, int ev, - void *ev_data MG_UD_ARG(void *user_data), - struct http_message *hm) __attribute__((noinline)); - -void mg_http_handler(struct mg_connection *nc, int ev, - void *ev_data MG_UD_ARG(void *user_data)) { - struct http_message hm; - mg_http_handler2(nc, ev, ev_data MG_UD_ARG(user_data), &hm); -} - -static void mg_http_handler2(struct mg_connection *nc, int ev, - void *ev_data MG_UD_ARG(void *user_data), - struct http_message *hm) { -#else /* !__XTENSA__ */ -void mg_http_handler(struct mg_connection *nc, int ev, - void *ev_data MG_UD_ARG(void *user_data)) { - struct http_message shm, *hm = &shm; -#endif /* __XTENSA__ */ - struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); - struct mbuf *io = &nc->recv_mbuf; - int req_len; - const int is_req = (nc->listener != NULL); -#if MG_ENABLE_HTTP_WEBSOCKET - struct mg_str *vec; -#endif - if (ev == MG_EV_CLOSE) { -#if MG_ENABLE_HTTP_CGI - /* Close associated CGI forwarder connection */ - if (pd->cgi.cgi_nc != NULL) { - pd->cgi.cgi_nc->user_data = NULL; - pd->cgi.cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } -#endif -#if MG_ENABLE_HTTP_STREAMING_MULTIPART - if (pd->mp_stream.boundary != NULL) { - /* - * Multipart message is in progress, but connection is closed. - * Finish part and request with an error flag. - */ - struct mg_http_multipart_part mp; - memset(&mp, 0, sizeof(mp)); - mp.status = -1; - mp.var_name = pd->mp_stream.var_name; - mp.file_name = pd->mp_stream.file_name; - mg_call(nc, (pd->endpoint_handler ? pd->endpoint_handler : nc->handler), - nc->user_data, MG_EV_HTTP_PART_END, &mp); - mp.var_name = NULL; - mp.file_name = NULL; - mg_call(nc, (pd->endpoint_handler ? pd->endpoint_handler : nc->handler), - nc->user_data, MG_EV_HTTP_MULTIPART_REQUEST_END, &mp); - } else -#endif - if (io->len > 0 && - (req_len = mg_parse_http(io->buf, io->len, hm, is_req)) > 0) { - /* - * For HTTP messages without Content-Length, always send HTTP message - * before MG_EV_CLOSE message. - */ - int ev2 = is_req ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY; - hm->message.len = io->len; - hm->body.len = io->buf + io->len - hm->body.p; - deliver_chunk(nc, hm, req_len); - mg_http_call_endpoint_handler(nc, ev2, hm); - } - pd->rcvd = 0; - } - -#if MG_ENABLE_FILESYSTEM - if (pd->file.fp != NULL) { - mg_http_transfer_file_data(nc); - } -#endif - - mg_call(nc, nc->handler, nc->user_data, ev, ev_data); - - if (ev == MG_EV_RECV) { - struct mg_str *s; - pd->rcvd += *(int *) ev_data; - -#if MG_ENABLE_HTTP_STREAMING_MULTIPART - if (pd->mp_stream.boundary != NULL) { - mg_http_multipart_continue(nc); - return; - } -#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ - - req_len = mg_parse_http(io->buf, io->len, hm, is_req); - - if (req_len > 0 && - (s = mg_get_http_header(hm, "Transfer-Encoding")) != NULL && - mg_vcasecmp(s, "chunked") == 0) { - mg_handle_chunked(nc, hm, io->buf + req_len, io->len - req_len); - } - -#if MG_ENABLE_HTTP_STREAMING_MULTIPART - if (req_len > 0 && (s = mg_get_http_header(hm, "Content-Type")) != NULL && - s->len >= 9 && strncmp(s->p, "multipart", 9) == 0) { - mg_http_multipart_begin(nc, hm, req_len); - mg_http_multipart_continue(nc); - return; - } -#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ - - /* TODO(alashkin): refactor this ifelseifelseifelseifelse */ - if ((req_len < 0 || - (req_len == 0 && io->len >= MG_MAX_HTTP_REQUEST_SIZE))) { - DBG(("invalid request")); - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } else if (req_len == 0) { - /* Do nothing, request is not yet fully buffered */ - } -#if MG_ENABLE_HTTP_WEBSOCKET - else if (nc->listener == NULL && - mg_get_http_header(hm, "Sec-WebSocket-Accept")) { - /* We're websocket client, got handshake response from server. */ - /* TODO(lsm): check the validity of accept Sec-WebSocket-Accept */ - mbuf_remove(io, req_len); - nc->proto_handler = mg_ws_handler; - nc->flags |= MG_F_IS_WEBSOCKET; - mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE, - NULL); - mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data)); - } else if (nc->listener != NULL && - (vec = mg_get_http_header(hm, "Sec-WebSocket-Key")) != NULL) { - struct mg_http_endpoint *ep; - - /* This is a websocket request. Switch protocol handlers. */ - mbuf_remove(io, req_len); - nc->proto_handler = mg_ws_handler; - nc->flags |= MG_F_IS_WEBSOCKET; - - /* - * If we have a handler set up with mg_register_http_endpoint(), - * deliver subsequent websocket events to this handler after the - * protocol switch. - */ - ep = mg_http_get_endpoint_handler(nc->listener, &hm->uri); - if (ep != NULL) { - nc->handler = ep->handler; -#if MG_ENABLE_CALLBACK_USERDATA - nc->user_data = ep->user_data; -#endif - } - - /* Send handshake */ - mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_REQUEST, - hm); - if (!(nc->flags & (MG_F_CLOSE_IMMEDIATELY | MG_F_SEND_AND_CLOSE))) { - if (nc->send_mbuf.len == 0) { - mg_ws_handshake(nc, vec, hm); - } - mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE, - NULL); - mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data)); - } - } -#endif /* MG_ENABLE_HTTP_WEBSOCKET */ - else if (hm->message.len > pd->rcvd) { - /* Not yet received all HTTP body, deliver MG_EV_HTTP_CHUNK */ - deliver_chunk(nc, hm, req_len); - if (nc->recv_mbuf_limit > 0 && nc->recv_mbuf.len >= nc->recv_mbuf_limit) { - LOG(LL_ERROR, ("%p recv buffer (%lu bytes) exceeds the limit " - "%lu bytes, and not drained, closing", - nc, (unsigned long) nc->recv_mbuf.len, - (unsigned long) nc->recv_mbuf_limit)); - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } - } else { - /* We did receive all HTTP body. */ - int trigger_ev = nc->listener ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY; - char addr[32]; - mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr), - MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT); - DBG(("%p %s %.*s %.*s", nc, addr, (int) hm->method.len, hm->method.p, - (int) hm->uri.len, hm->uri.p)); - deliver_chunk(nc, hm, req_len); - /* Whole HTTP message is fully buffered, call event handler */ - mg_http_call_endpoint_handler(nc, trigger_ev, hm); - mbuf_remove(io, hm->message.len); - pd->rcvd = 0; - } - } -} - -static size_t mg_get_line_len(const char *buf, size_t buf_len) { - size_t len = 0; - while (len < buf_len && buf[len] != '\n') len++; - return len == buf_len ? 0 : len + 1; -} - -#if MG_ENABLE_HTTP_STREAMING_MULTIPART -static void mg_http_multipart_begin(struct mg_connection *nc, - struct http_message *hm, int req_len) { - struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); - struct mg_str *ct; - struct mbuf *io = &nc->recv_mbuf; - - char boundary_buf[100]; - char *boundary = boundary_buf; - int boundary_len; - - ct = mg_get_http_header(hm, "Content-Type"); - if (ct == NULL) { - /* We need more data - or it isn't multipart mesage */ - goto exit_mp; - } - - /* Content-type should start with "multipart" */ - if (ct->len < 9 || strncmp(ct->p, "multipart", 9) != 0) { - goto exit_mp; - } - - boundary_len = - mg_http_parse_header2(ct, "boundary", &boundary, sizeof(boundary_buf)); - if (boundary_len == 0) { - /* - * Content type is multipart, but there is no boundary, - * probably malformed request - */ - nc->flags = MG_F_CLOSE_IMMEDIATELY; - DBG(("invalid request")); - goto exit_mp; - } - - /* If we reach this place - that is multipart request */ - - if (pd->mp_stream.boundary != NULL) { - /* - * Another streaming request was in progress, - * looks like protocol error - */ - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } else { - struct mg_http_endpoint *ep = NULL; - pd->mp_stream.state = MPS_BEGIN; - pd->mp_stream.boundary = strdup(boundary); - pd->mp_stream.boundary_len = strlen(boundary); - pd->mp_stream.var_name = pd->mp_stream.file_name = NULL; - pd->endpoint_handler = nc->handler; - - ep = mg_http_get_endpoint_handler(nc->listener, &hm->uri); - if (ep != NULL) { - pd->endpoint_handler = ep->handler; - } - - mg_http_call_endpoint_handler(nc, MG_EV_HTTP_MULTIPART_REQUEST, hm); - - mbuf_remove(io, req_len); - } -exit_mp: - if (boundary != boundary_buf) MG_FREE(boundary); -} - -#define CONTENT_DISPOSITION "Content-Disposition: " - -static void mg_http_multipart_call_handler(struct mg_connection *c, int ev, - const char *data, size_t data_len) { - struct mg_http_multipart_part mp; - struct mg_http_proto_data *pd = mg_http_get_proto_data(c); - memset(&mp, 0, sizeof(mp)); - - mp.var_name = pd->mp_stream.var_name; - mp.file_name = pd->mp_stream.file_name; - mp.user_data = pd->mp_stream.user_data; - mp.data.p = data; - mp.data.len = data_len; - mg_call(c, pd->endpoint_handler, c->user_data, ev, &mp); - pd->mp_stream.user_data = mp.user_data; -} - -static int mg_http_multipart_got_chunk(struct mg_connection *c) { - struct mg_http_proto_data *pd = mg_http_get_proto_data(c); - struct mbuf *io = &c->recv_mbuf; - - mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_DATA, io->buf, - pd->mp_stream.prev_io_len); - mbuf_remove(io, pd->mp_stream.prev_io_len); - pd->mp_stream.prev_io_len = 0; - pd->mp_stream.state = MPS_WAITING_FOR_CHUNK; - - return 0; -} - -static int mg_http_multipart_finalize(struct mg_connection *c) { - struct mg_http_proto_data *pd = mg_http_get_proto_data(c); - - mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_END, NULL, 0); - MG_FREE((void *) pd->mp_stream.file_name); - pd->mp_stream.file_name = NULL; - MG_FREE((void *) pd->mp_stream.var_name); - pd->mp_stream.var_name = NULL; - mg_http_multipart_call_handler(c, MG_EV_HTTP_MULTIPART_REQUEST_END, NULL, 0); - mg_http_free_proto_data_mp_stream(&pd->mp_stream); - pd->mp_stream.state = MPS_FINISHED; - - return 1; -} - -static int mg_http_multipart_wait_for_boundary(struct mg_connection *c) { - const char *boundary; - struct mbuf *io = &c->recv_mbuf; - struct mg_http_proto_data *pd = mg_http_get_proto_data(c); - - if (pd->mp_stream.boundary == NULL) { - pd->mp_stream.state = MPS_FINALIZE; - DBG(("Invalid request: boundary not initialized")); - return 0; - } - - if ((int) io->len < pd->mp_stream.boundary_len + 2) { - return 0; - } - - boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len); - if (boundary != NULL) { - const char *boundary_end = (boundary + pd->mp_stream.boundary_len); - if (io->len - (boundary_end - io->buf) < 4) { - return 0; - } - if (strncmp(boundary_end, "--\r\n", 4) == 0) { - pd->mp_stream.state = MPS_FINALIZE; - mbuf_remove(io, (boundary_end - io->buf) + 4); - } else { - pd->mp_stream.state = MPS_GOT_BOUNDARY; - } - } else { - return 0; - } - - return 1; -} - -static void mg_http_parse_header_internal(struct mg_str *hdr, - const char *var_name, - struct altbuf *ab); - -static int mg_http_multipart_process_boundary(struct mg_connection *c) { - int data_size; - const char *boundary, *block_begin; - struct mbuf *io = &c->recv_mbuf; - struct mg_http_proto_data *pd = mg_http_get_proto_data(c); - struct altbuf ab_file_name, ab_var_name; - int line_len; - boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len); - block_begin = boundary + pd->mp_stream.boundary_len + 2; - data_size = io->len - (block_begin - io->buf); - - altbuf_init(&ab_file_name, NULL, 0); - altbuf_init(&ab_var_name, NULL, 0); - - while (data_size > 0 && - (line_len = mg_get_line_len(block_begin, data_size)) != 0) { - if (line_len > (int) sizeof(CONTENT_DISPOSITION) && - mg_ncasecmp(block_begin, CONTENT_DISPOSITION, - sizeof(CONTENT_DISPOSITION) - 1) == 0) { - struct mg_str header; - - header.p = block_begin + sizeof(CONTENT_DISPOSITION) - 1; - header.len = line_len - sizeof(CONTENT_DISPOSITION) - 1; - - altbuf_reset(&ab_var_name); - mg_http_parse_header_internal(&header, "name", &ab_var_name); - - altbuf_reset(&ab_file_name); - mg_http_parse_header_internal(&header, "filename", &ab_file_name); - - block_begin += line_len; - data_size -= line_len; - - continue; - } - - if (line_len == 2 && mg_ncasecmp(block_begin, "\r\n", 2) == 0) { - mbuf_remove(io, block_begin - io->buf + 2); - - if (pd->mp_stream.processing_part != 0) { - mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_END, NULL, 0); - } - - /* Reserve 2 bytes for "\r\n" in file_name and var_name */ - altbuf_append(&ab_file_name, '\0'); - altbuf_append(&ab_file_name, '\0'); - altbuf_append(&ab_var_name, '\0'); - altbuf_append(&ab_var_name, '\0'); - - MG_FREE((void *) pd->mp_stream.file_name); - pd->mp_stream.file_name = altbuf_get_buf(&ab_file_name, 1 /* trim */); - MG_FREE((void *) pd->mp_stream.var_name); - pd->mp_stream.var_name = altbuf_get_buf(&ab_var_name, 1 /* trim */); - - mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_BEGIN, NULL, 0); - pd->mp_stream.state = MPS_WAITING_FOR_CHUNK; - pd->mp_stream.processing_part++; - return 1; - } - - block_begin += line_len; - } - - pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY; - - altbuf_reset(&ab_var_name); - altbuf_reset(&ab_file_name); - - return 0; -} - -static int mg_http_multipart_continue_wait_for_chunk(struct mg_connection *c) { - struct mg_http_proto_data *pd = mg_http_get_proto_data(c); - struct mbuf *io = &c->recv_mbuf; - - const char *boundary; - if ((int) io->len < pd->mp_stream.boundary_len + 6 /* \r\n, --, -- */) { - return 0; - } - - boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len); - if (boundary == NULL && pd->mp_stream.prev_io_len == 0) { - pd->mp_stream.prev_io_len = io->len; - return 0; - } else if (boundary == NULL && - (int) io->len > - pd->mp_stream.prev_io_len + pd->mp_stream.boundary_len + 4) { - pd->mp_stream.state = MPS_GOT_CHUNK; - return 1; - } else if (boundary != NULL) { - int data_size = (boundary - io->buf - 4); - mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_DATA, io->buf, data_size); - mbuf_remove(io, (boundary - io->buf)); - pd->mp_stream.prev_io_len = 0; - pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY; - return 1; - } else { - return 0; - } -} - -static void mg_http_multipart_continue(struct mg_connection *c) { - struct mg_http_proto_data *pd = mg_http_get_proto_data(c); - while (1) { - switch (pd->mp_stream.state) { - case MPS_BEGIN: { - pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY; - break; - } - case MPS_WAITING_FOR_BOUNDARY: { - if (mg_http_multipart_wait_for_boundary(c) == 0) { - return; - } - break; - } - case MPS_GOT_BOUNDARY: { - if (mg_http_multipart_process_boundary(c) == 0) { - return; - } - break; - } - case MPS_WAITING_FOR_CHUNK: { - if (mg_http_multipart_continue_wait_for_chunk(c) == 0) { - return; - } - break; - } - case MPS_GOT_CHUNK: { - if (mg_http_multipart_got_chunk(c) == 0) { - return; - } - break; - } - case MPS_FINALIZE: { - if (mg_http_multipart_finalize(c) == 0) { - return; - } - break; - } - case MPS_FINISHED: { - mbuf_remove(&c->recv_mbuf, c->recv_mbuf.len); - return; - } - } - } -} - -struct file_upload_state { - char *lfn; - size_t num_recd; - FILE *fp; -}; - -#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ - -void mg_set_protocol_http_websocket(struct mg_connection *nc) { - nc->proto_handler = mg_http_handler; -} - -const char *mg_status_message(int status_code) { - switch (status_code) { - case 206: - return "Partial Content"; - case 301: - return "Moved"; - case 302: - return "Found"; - case 400: - return "Bad Request"; - case 401: - return "Unauthorized"; - case 403: - return "Forbidden"; - case 404: - return "Not Found"; - case 416: - return "Requested Range Not Satisfiable"; - case 418: - return "I'm a teapot"; - case 500: - return "Internal Server Error"; - case 502: - return "Bad Gateway"; - case 503: - return "Service Unavailable"; - -#if MG_ENABLE_EXTRA_ERRORS_DESC - case 100: - return "Continue"; - case 101: - return "Switching Protocols"; - case 102: - return "Processing"; - case 200: - return "OK"; - case 201: - return "Created"; - case 202: - return "Accepted"; - case 203: - return "Non-Authoritative Information"; - case 204: - return "No Content"; - case 205: - return "Reset Content"; - case 207: - return "Multi-Status"; - case 208: - return "Already Reported"; - case 226: - return "IM Used"; - case 300: - return "Multiple Choices"; - case 303: - return "See Other"; - case 304: - return "Not Modified"; - case 305: - return "Use Proxy"; - case 306: - return "Switch Proxy"; - case 307: - return "Temporary Redirect"; - case 308: - return "Permanent Redirect"; - case 402: - return "Payment Required"; - case 405: - return "Method Not Allowed"; - case 406: - return "Not Acceptable"; - case 407: - return "Proxy Authentication Required"; - case 408: - return "Request Timeout"; - case 409: - return "Conflict"; - case 410: - return "Gone"; - case 411: - return "Length Required"; - case 412: - return "Precondition Failed"; - case 413: - return "Payload Too Large"; - case 414: - return "URI Too Long"; - case 415: - return "Unsupported Media Type"; - case 417: - return "Expectation Failed"; - case 422: - return "Unprocessable Entity"; - case 423: - return "Locked"; - case 424: - return "Failed Dependency"; - case 426: - return "Upgrade Required"; - case 428: - return "Precondition Required"; - case 429: - return "Too Many Requests"; - case 431: - return "Request Header Fields Too Large"; - case 451: - return "Unavailable For Legal Reasons"; - case 501: - return "Not Implemented"; - case 504: - return "Gateway Timeout"; - case 505: - return "HTTP Version Not Supported"; - case 506: - return "Variant Also Negotiates"; - case 507: - return "Insufficient Storage"; - case 508: - return "Loop Detected"; - case 510: - return "Not Extended"; - case 511: - return "Network Authentication Required"; -#endif /* MG_ENABLE_EXTRA_ERRORS_DESC */ - - default: - return "OK"; - } -} - -void mg_send_response_line_s(struct mg_connection *nc, int status_code, - const struct mg_str extra_headers) { - mg_printf(nc, "HTTP/1.1 %d %s\r\nServer: %s\r\n", status_code, - mg_status_message(status_code), mg_version_header); - if (extra_headers.len > 0) { - mg_printf(nc, "%.*s\r\n", (int) extra_headers.len, extra_headers.p); - } -} - -void mg_send_response_line(struct mg_connection *nc, int status_code, - const char *extra_headers) { - mg_send_response_line_s(nc, status_code, mg_mk_str(extra_headers)); -} - -void mg_http_send_redirect(struct mg_connection *nc, int status_code, - const struct mg_str location, - const struct mg_str extra_headers) { - char bbody[100], *pbody = bbody; - int bl = mg_asprintf(&pbody, sizeof(bbody), - "

Moved here.\r\n", - (int) location.len, location.p); - char bhead[150], *phead = bhead; - mg_asprintf(&phead, sizeof(bhead), - "Location: %.*s\r\n" - "Content-Type: text/html\r\n" - "Content-Length: %d\r\n" - "Cache-Control: no-cache\r\n" - "%.*s%s", - (int) location.len, location.p, bl, (int) extra_headers.len, - extra_headers.p, (extra_headers.len > 0 ? "\r\n" : "")); - mg_send_response_line(nc, status_code, phead); - if (phead != bhead) MG_FREE(phead); - mg_send(nc, pbody, bl); - if (pbody != bbody) MG_FREE(pbody); -} - -void mg_send_head(struct mg_connection *c, int status_code, - int64_t content_length, const char *extra_headers) { - mg_send_response_line(c, status_code, extra_headers); - if (content_length < 0) { - mg_printf(c, "%s", "Transfer-Encoding: chunked\r\n"); - } else { - mg_printf(c, "Content-Length: %" INT64_FMT "\r\n", content_length); - } - mg_send(c, "\r\n", 2); -} - -void mg_http_send_error(struct mg_connection *nc, int code, - const char *reason) { - if (!reason) reason = mg_status_message(code); - LOG(LL_DEBUG, ("%p %d %s", nc, code, reason)); - mg_send_head(nc, code, strlen(reason), - "Content-Type: text/plain\r\nConnection: close"); - mg_send(nc, reason, strlen(reason)); - nc->flags |= MG_F_SEND_AND_CLOSE; -} - -#if MG_ENABLE_FILESYSTEM -static void mg_http_construct_etag(char *buf, size_t buf_len, - const cs_stat_t *st) { - snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"", (unsigned long) st->st_mtime, - (int64_t) st->st_size); -} - -#ifndef WINCE -static void mg_gmt_time_string(char *buf, size_t buf_len, time_t *t) { - strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t)); -} -#else -/* Look wince_lib.c for WindowsCE implementation */ -static void mg_gmt_time_string(char *buf, size_t buf_len, time_t *t); -#endif - -static int mg_http_parse_range_header(const struct mg_str *header, int64_t *a, - int64_t *b) { - /* - * There is no snscanf. Headers are not guaranteed to be NUL-terminated, - * so we have this. Ugh. - */ - int result; - char *p = (char *) MG_MALLOC(header->len + 1); - if (p == NULL) return 0; - memcpy(p, header->p, header->len); - p[header->len] = '\0'; - result = sscanf(p, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b); - MG_FREE(p); - return result; -} - -void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm, - const char *path, const struct mg_str mime_type, - const struct mg_str extra_headers) { - struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); - cs_stat_t st; - LOG(LL_DEBUG, ("%p [%s] %.*s", nc, path, (int) mime_type.len, mime_type.p)); - if (mg_stat(path, &st) != 0 || (pd->file.fp = mg_fopen(path, "rb")) == NULL) { - int code, err = mg_get_errno(); - switch (err) { - case EACCES: - code = 403; - break; - case ENOENT: - code = 404; - break; - default: - code = 500; - }; - mg_http_send_error(nc, code, "Open failed"); - } else { - char etag[50], current_time[50], last_modified[50], range[70]; - time_t t = (time_t) mg_time(); - int64_t r1 = 0, r2 = 0, cl = st.st_size; - struct mg_str *range_hdr = mg_get_http_header(hm, "Range"); - int n, status_code = 200; - - /* Handle Range header */ - range[0] = '\0'; - if (range_hdr != NULL && - (n = mg_http_parse_range_header(range_hdr, &r1, &r2)) > 0 && r1 >= 0 && - r2 >= 0) { - /* If range is specified like "400-", set second limit to content len */ - if (n == 1) { - r2 = cl - 1; - } - if (r1 > r2 || r2 >= cl) { - status_code = 416; - cl = 0; - snprintf(range, sizeof(range), - "Content-Range: bytes */%" INT64_FMT "\r\n", - (int64_t) st.st_size); - } else { - status_code = 206; - cl = r2 - r1 + 1; - snprintf(range, sizeof(range), "Content-Range: bytes %" INT64_FMT - "-%" INT64_FMT "/%" INT64_FMT "\r\n", - r1, r1 + cl - 1, (int64_t) st.st_size); -#if _FILE_OFFSET_BITS == 64 || _POSIX_C_SOURCE >= 200112L || \ - _XOPEN_SOURCE >= 600 - fseeko(pd->file.fp, r1, SEEK_SET); -#else - fseek(pd->file.fp, (long) r1, SEEK_SET); -#endif - } - } - -#if !MG_DISABLE_HTTP_KEEP_ALIVE - { - struct mg_str *conn_hdr = mg_get_http_header(hm, "Connection"); - if (conn_hdr != NULL) { - pd->file.keepalive = (mg_vcasecmp(conn_hdr, "keep-alive") == 0); - } else { - pd->file.keepalive = (mg_vcmp(&hm->proto, "HTTP/1.1") == 0); - } - } -#endif - - mg_http_construct_etag(etag, sizeof(etag), &st); - mg_gmt_time_string(current_time, sizeof(current_time), &t); - mg_gmt_time_string(last_modified, sizeof(last_modified), &st.st_mtime); - /* - * Content length casted to size_t because: - * 1) that's the maximum buffer size anyway - * 2) ESP8266 RTOS SDK newlib vprintf cannot contain a 64bit arg at non-last - * position - * TODO(mkm): fix ESP8266 RTOS SDK - */ - mg_send_response_line_s(nc, status_code, extra_headers); - mg_printf(nc, - "Date: %s\r\n" - "Last-Modified: %s\r\n" - "Accept-Ranges: bytes\r\n" - "Content-Type: %.*s\r\n" - "Connection: %s\r\n" - "Content-Length: %" SIZE_T_FMT - "\r\n" - "%sEtag: %s\r\n\r\n", - current_time, last_modified, (int) mime_type.len, mime_type.p, - (pd->file.keepalive ? "keep-alive" : "close"), (size_t) cl, range, - etag); - - pd->file.cl = cl; - pd->file.type = DATA_FILE; - mg_http_transfer_file_data(nc); - } -} - -static void mg_http_serve_file2(struct mg_connection *nc, const char *path, - struct http_message *hm, - struct mg_serve_http_opts *opts) { -#if MG_ENABLE_HTTP_SSI - if (mg_match_prefix(opts->ssi_pattern, strlen(opts->ssi_pattern), path) > 0) { - mg_handle_ssi_request(nc, hm, path, opts); - return; - } -#endif - mg_http_serve_file(nc, hm, path, mg_get_mime_type(path, "text/plain", opts), - mg_mk_str(opts->extra_headers)); -} - -#endif - -int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, - int is_form_url_encoded) { - int i, j, a, b; -#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') - - for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) { - if (src[i] == '%') { - if (i < src_len - 2 && isxdigit(*(const unsigned char *) (src + i + 1)) && - isxdigit(*(const unsigned char *) (src + i + 2))) { - a = tolower(*(const unsigned char *) (src + i + 1)); - b = tolower(*(const unsigned char *) (src + i + 2)); - dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b)); - i += 2; - } else { - return -1; - } - } else if (is_form_url_encoded && src[i] == '+') { - dst[j] = ' '; - } else { - dst[j] = src[i]; - } - } - - dst[j] = '\0'; /* Null-terminate the destination */ - - return i >= src_len ? j : -1; -} - -int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst, - size_t dst_len) { - const char *p, *e, *s; - size_t name_len; - int len; - - /* - * According to the documentation function returns negative - * value in case of error. For debug purposes it returns: - * -1 - src is wrong (NUUL) - * -2 - dst is wrong (NULL) - * -3 - failed to decode url or dst is to small - * -4 - name does not exist - */ - if (dst == NULL || dst_len == 0) { - len = -2; - } else if (buf->p == NULL || name == NULL || buf->len == 0) { - len = -1; - dst[0] = '\0'; - } else { - name_len = strlen(name); - e = buf->p + buf->len; - len = -4; - dst[0] = '\0'; - - for (p = buf->p; p + name_len < e; p++) { - if ((p == buf->p || p[-1] == '&') && p[name_len] == '=' && - !mg_ncasecmp(name, p, name_len)) { - p += name_len + 1; - s = (const char *) memchr(p, '&', (size_t)(e - p)); - if (s == NULL) { - s = e; - } - len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1); - /* -1 means: failed to decode or dst is too small */ - if (len == -1) { - len = -3; - } - break; - } - } - } - - return len; -} - -void mg_send_http_chunk(struct mg_connection *nc, const char *buf, size_t len) { - char chunk_size[50]; - int n; - - n = snprintf(chunk_size, sizeof(chunk_size), "%lX\r\n", (unsigned long) len); - mg_send(nc, chunk_size, n); - mg_send(nc, buf, len); - mg_send(nc, "\r\n", 2); -} - -void mg_printf_http_chunk(struct mg_connection *nc, const char *fmt, ...) { - char mem[MG_VPRINTF_BUFFER_SIZE], *buf = mem; - int len; - va_list ap; - - va_start(ap, fmt); - len = mg_avprintf(&buf, sizeof(mem), fmt, ap); - va_end(ap); - - if (len >= 0) { - mg_send_http_chunk(nc, buf, len); - } - - /* LCOV_EXCL_START */ - if (buf != mem && buf != NULL) { - MG_FREE(buf); - } - /* LCOV_EXCL_STOP */ -} - -void mg_printf_html_escape(struct mg_connection *nc, const char *fmt, ...) { - char mem[MG_VPRINTF_BUFFER_SIZE], *buf = mem; - int i, j, len; - va_list ap; - - va_start(ap, fmt); - len = mg_avprintf(&buf, sizeof(mem), fmt, ap); - va_end(ap); - - if (len >= 0) { - for (i = j = 0; i < len; i++) { - if (buf[i] == '<' || buf[i] == '>') { - mg_send(nc, buf + j, i - j); - mg_send(nc, buf[i] == '<' ? "<" : ">", 4); - j = i + 1; - } - } - mg_send(nc, buf + j, i - j); - } - - /* LCOV_EXCL_START */ - if (buf != mem && buf != NULL) { - MG_FREE(buf); - } - /* LCOV_EXCL_STOP */ -} - -static void mg_http_parse_header_internal(struct mg_str *hdr, - const char *var_name, - struct altbuf *ab) { - int ch = ' ', ch1 = ',', n = strlen(var_name); - const char *p, *end = hdr ? hdr->p + hdr->len : NULL, *s = NULL; - - /* Find where variable starts */ - for (s = hdr->p; s != NULL && s + n < end; s++) { - if ((s == hdr->p || s[-1] == ch || s[-1] == ch1 || s[-1] == ';') && - s[n] == '=' && !strncmp(s, var_name, n)) - break; - } - - if (s != NULL && &s[n + 1] < end) { - s += n + 1; - if (*s == '"' || *s == '\'') { - ch = ch1 = *s++; - } - p = s; - while (p < end && p[0] != ch && p[0] != ch1) { - if (ch != ' ' && p[0] == '\\' && p[1] == ch) p++; - altbuf_append(ab, *p++); - } - - if (ch != ' ' && *p != ch) { - altbuf_reset(ab); - } - } - - /* If there is some data, append a NUL. */ - if (ab->len > 0) { - altbuf_append(ab, '\0'); - } -} - -int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf, - size_t buf_size) { - struct altbuf ab; - altbuf_init(&ab, *buf, buf_size); - if (hdr == NULL) return 0; - if (*buf != NULL && buf_size > 0) *buf[0] = '\0'; - - mg_http_parse_header_internal(hdr, var_name, &ab); - - /* - * Get a (trimmed) buffer, and return a len without a NUL byte which might - * have been added. - */ - *buf = altbuf_get_buf(&ab, 1 /* trim */); - return ab.len > 0 ? ab.len - 1 : 0; -} - -int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf, - size_t buf_size) { - char *buf2 = buf; - - int len = mg_http_parse_header2(hdr, var_name, &buf2, buf_size); - - if (buf2 != buf) { - /* Buffer was not enough and was reallocated: free it and just return 0 */ - MG_FREE(buf2); - return 0; - } - - return len; -} - -int mg_get_http_basic_auth(struct http_message *hm, char *user, size_t user_len, - char *pass, size_t pass_len) { - struct mg_str *hdr = mg_get_http_header(hm, "Authorization"); - if (hdr == NULL) return -1; - return mg_parse_http_basic_auth(hdr, user, user_len, pass, pass_len); -} - -int mg_parse_http_basic_auth(struct mg_str *hdr, char *user, size_t user_len, - char *pass, size_t pass_len) { - char *buf = NULL; - char fmt[64]; - int res = 0; - - if (mg_strncmp(*hdr, mg_mk_str("Basic "), 6) != 0) return -1; - - buf = (char *) MG_MALLOC(hdr->len); - cs_base64_decode((unsigned char *) hdr->p + 6, hdr->len, buf, NULL); - - /* e.g. "%123[^:]:%321[^\n]" */ - snprintf(fmt, sizeof(fmt), "%%%" SIZE_T_FMT "[^:]:%%%" SIZE_T_FMT "[^\n]", - user_len - 1, pass_len - 1); - if (sscanf(buf, fmt, user, pass) == 0) { - res = -1; - } - - MG_FREE(buf); - return res; -} - -#if MG_ENABLE_FILESYSTEM -static int mg_is_file_hidden(const char *path, - const struct mg_serve_http_opts *opts, - int exclude_specials) { - const char *p1 = opts->per_directory_auth_file; - const char *p2 = opts->hidden_file_pattern; - - /* Strip directory path from the file name */ - const char *pdir = strrchr(path, DIRSEP); - if (pdir != NULL) { - path = pdir + 1; - } - - return (exclude_specials && (!strcmp(path, ".") || !strcmp(path, ".."))) || - (p1 != NULL && mg_match_prefix(p1, strlen(p1), path) == strlen(p1)) || - (p2 != NULL && mg_match_prefix(p2, strlen(p2), path) > 0); -} - -#if !MG_DISABLE_HTTP_DIGEST_AUTH - -#ifndef MG_EXT_MD5 -void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[], - const size_t *msg_lens, uint8_t *digest) { - size_t i; - cs_md5_ctx md5_ctx; - cs_md5_init(&md5_ctx); - for (i = 0; i < num_msgs; i++) { - cs_md5_update(&md5_ctx, msgs[i], msg_lens[i]); - } - cs_md5_final(digest, &md5_ctx); -} -#else -extern void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[], - const size_t *msg_lens, uint8_t *digest); -#endif - -void cs_md5(char buf[33], ...) { - unsigned char hash[16]; - const uint8_t *msgs[20], *p; - size_t msg_lens[20]; - size_t num_msgs = 0; - va_list ap; - - va_start(ap, buf); - while ((p = va_arg(ap, const unsigned char *) ) != NULL) { - msgs[num_msgs] = p; - msg_lens[num_msgs] = va_arg(ap, size_t); - num_msgs++; - } - va_end(ap); - - mg_hash_md5_v(num_msgs, msgs, msg_lens, hash); - cs_to_hex(buf, hash, sizeof(hash)); -} - -static void mg_mkmd5resp(const char *method, size_t method_len, const char *uri, - size_t uri_len, const char *ha1, size_t ha1_len, - const char *nonce, size_t nonce_len, const char *nc, - size_t nc_len, const char *cnonce, size_t cnonce_len, - const char *qop, size_t qop_len, char *resp) { - static const char colon[] = ":"; - static const size_t one = 1; - char ha2[33]; - cs_md5(ha2, method, method_len, colon, one, uri, uri_len, NULL); - cs_md5(resp, ha1, ha1_len, colon, one, nonce, nonce_len, colon, one, nc, - nc_len, colon, one, cnonce, cnonce_len, colon, one, qop, qop_len, - colon, one, ha2, sizeof(ha2) - 1, NULL); -} - -int mg_http_create_digest_auth_header(char *buf, size_t buf_len, - const char *method, const char *uri, - const char *auth_domain, const char *user, - const char *passwd, const char *nonce) { - static const char colon[] = ":", qop[] = "auth"; - static const size_t one = 1; - char ha1[33], resp[33], cnonce[40]; - - snprintf(cnonce, sizeof(cnonce), "%lx", (unsigned long) mg_time()); - cs_md5(ha1, user, (size_t) strlen(user), colon, one, auth_domain, - (size_t) strlen(auth_domain), colon, one, passwd, - (size_t) strlen(passwd), NULL); - mg_mkmd5resp(method, strlen(method), uri, strlen(uri), ha1, sizeof(ha1) - 1, - nonce, strlen(nonce), "1", one, cnonce, strlen(cnonce), qop, - sizeof(qop) - 1, resp); - return snprintf(buf, buf_len, - "Authorization: Digest username=\"%s\"," - "realm=\"%s\",uri=\"%s\",qop=%s,nc=1,cnonce=%s," - "nonce=%s,response=%s\r\n", - user, auth_domain, uri, qop, cnonce, nonce, resp); -} - -/* - * Check for authentication timeout. - * Clients send time stamp encoded in nonce. Make sure it is not too old, - * to prevent replay attacks. - * Assumption: nonce is a hexadecimal number of seconds since 1970. - */ -static int mg_check_nonce(const char *nonce) { - unsigned long now = (unsigned long) mg_time(); - unsigned long val = (unsigned long) strtoul(nonce, NULL, 16); - return (now >= val) && (now - val < 60 * 60); -} - -int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain, - FILE *fp) { - int ret = 0; - struct mg_str *hdr; - char username_buf[50], cnonce_buf[64], response_buf[40], uri_buf[200], - qop_buf[20], nc_buf[20], nonce_buf[16]; - - char *username = username_buf, *cnonce = cnonce_buf, *response = response_buf, - *uri = uri_buf, *qop = qop_buf, *nc = nc_buf, *nonce = nonce_buf; - - /* Parse "Authorization:" header, fail fast on parse error */ - if (hm == NULL || fp == NULL || - (hdr = mg_get_http_header(hm, "Authorization")) == NULL || - mg_http_parse_header2(hdr, "username", &username, sizeof(username_buf)) == - 0 || - mg_http_parse_header2(hdr, "cnonce", &cnonce, sizeof(cnonce_buf)) == 0 || - mg_http_parse_header2(hdr, "response", &response, sizeof(response_buf)) == - 0 || - mg_http_parse_header2(hdr, "uri", &uri, sizeof(uri_buf)) == 0 || - mg_http_parse_header2(hdr, "qop", &qop, sizeof(qop_buf)) == 0 || - mg_http_parse_header2(hdr, "nc", &nc, sizeof(nc_buf)) == 0 || - mg_http_parse_header2(hdr, "nonce", &nonce, sizeof(nonce_buf)) == 0 || - mg_check_nonce(nonce) == 0) { - ret = 0; - goto clean; - } - - /* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */ - - ret = mg_check_digest_auth( - hm->method, - mg_mk_str_n( - hm->uri.p, - hm->uri.len + (hm->query_string.len ? hm->query_string.len + 1 : 0)), - mg_mk_str(username), mg_mk_str(cnonce), mg_mk_str(response), - mg_mk_str(qop), mg_mk_str(nc), mg_mk_str(nonce), mg_mk_str(auth_domain), - fp); - -clean: - if (username != username_buf) MG_FREE(username); - if (cnonce != cnonce_buf) MG_FREE(cnonce); - if (response != response_buf) MG_FREE(response); - if (uri != uri_buf) MG_FREE(uri); - if (qop != qop_buf) MG_FREE(qop); - if (nc != nc_buf) MG_FREE(nc); - if (nonce != nonce_buf) MG_FREE(nonce); - - return ret; -} - -int mg_check_digest_auth(struct mg_str method, struct mg_str uri, - struct mg_str username, struct mg_str cnonce, - struct mg_str response, struct mg_str qop, - struct mg_str nc, struct mg_str nonce, - struct mg_str auth_domain, FILE *fp) { - char buf[128], f_user[sizeof(buf)], f_ha1[sizeof(buf)], f_domain[sizeof(buf)]; - char expected_response[33]; - - /* - * Read passwords file line by line. If should have htdigest format, - * i.e. each line should be a colon-separated sequence: - * USER_NAME:DOMAIN_NAME:HA1_HASH_OF_USER_DOMAIN_AND_PASSWORD - */ - while (fgets(buf, sizeof(buf), fp) != NULL) { - if (sscanf(buf, "%[^:]:%[^:]:%s", f_user, f_domain, f_ha1) == 3 && - mg_vcmp(&username, f_user) == 0 && - mg_vcmp(&auth_domain, f_domain) == 0) { - /* Username and domain matched, check the password */ - mg_mkmd5resp(method.p, method.len, uri.p, uri.len, f_ha1, strlen(f_ha1), - nonce.p, nonce.len, nc.p, nc.len, cnonce.p, cnonce.len, - qop.p, qop.len, expected_response); - LOG(LL_DEBUG, - ("%.*s %s %.*s %s", (int) username.len, username.p, f_domain, - (int) response.len, response.p, expected_response)); - return mg_ncasecmp(response.p, expected_response, response.len) == 0; - } - } - - /* None of the entries in the passwords file matched - return failure */ - return 0; -} - -int mg_http_is_authorized(struct http_message *hm, struct mg_str path, - const char *domain, const char *passwords_file, - int flags) { - char buf[MG_MAX_PATH]; - const char *p; - FILE *fp; - int authorized = 1; - - if (domain != NULL && passwords_file != NULL) { - if (flags & MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE) { - fp = mg_fopen(passwords_file, "r"); - } else if (flags & MG_AUTH_FLAG_IS_DIRECTORY) { - snprintf(buf, sizeof(buf), "%.*s%c%s", (int) path.len, path.p, DIRSEP, - passwords_file); - fp = mg_fopen(buf, "r"); - } else { - p = strrchr(path.p, DIRSEP); - if (p == NULL) p = path.p; - snprintf(buf, sizeof(buf), "%.*s%c%s", (int) (p - path.p), path.p, DIRSEP, - passwords_file); - fp = mg_fopen(buf, "r"); - } - - if (fp != NULL) { - authorized = mg_http_check_digest_auth(hm, domain, fp); - fclose(fp); - } else if (!(flags & MG_AUTH_FLAG_ALLOW_MISSING_FILE)) { - authorized = 0; - } - } - - LOG(LL_DEBUG, ("%.*s %s %x %d", (int) path.len, path.p, - passwords_file ? passwords_file : "", flags, authorized)); - return authorized; -} -#else -int mg_http_is_authorized(struct http_message *hm, const struct mg_str path, - const char *domain, const char *passwords_file, - int flags) { - (void) hm; - (void) path; - (void) domain; - (void) passwords_file; - (void) flags; - return 1; -} -#endif - -#if MG_ENABLE_DIRECTORY_LISTING -static void mg_escape(const char *src, char *dst, size_t dst_len) { - size_t n = 0; - while (*src != '\0' && n + 5 < dst_len) { - unsigned char ch = *(unsigned char *) src++; - if (ch == '<') { - n += snprintf(dst + n, dst_len - n, "%s", "<"); - } else { - dst[n++] = ch; - } - } - dst[n] = '\0'; -} - -static void mg_print_dir_entry(struct mg_connection *nc, const char *file_name, - cs_stat_t *stp) { - char size[64], mod[64], path[MG_MAX_PATH]; - int64_t fsize = stp->st_size; - int is_dir = S_ISDIR(stp->st_mode); - const char *slash = is_dir ? "/" : ""; - struct mg_str href; - - if (is_dir) { - snprintf(size, sizeof(size), "%s", "[DIRECTORY]"); - } else { - /* - * We use (double) cast below because MSVC 6 compiler cannot - * convert unsigned __int64 to double. - */ - if (fsize < 1024) { - snprintf(size, sizeof(size), "%d", (int) fsize); - } else if (fsize < 0x100000) { - snprintf(size, sizeof(size), "%.1fk", (double) fsize / 1024.0); - } else if (fsize < 0x40000000) { - snprintf(size, sizeof(size), "%.1fM", (double) fsize / 1048576); - } else { - snprintf(size, sizeof(size), "%.1fG", (double) fsize / 1073741824); - } - } - strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&stp->st_mtime)); - mg_escape(file_name, path, sizeof(path)); - href = mg_url_encode(mg_mk_str(file_name)); - mg_printf_http_chunk(nc, - "%s%s" - "%s%s\n", - href.p, slash, path, slash, mod, is_dir ? -1 : fsize, - size); - free((void *) href.p); -} - -static void mg_scan_directory(struct mg_connection *nc, const char *dir, - const struct mg_serve_http_opts *opts, - void (*func)(struct mg_connection *, const char *, - cs_stat_t *)) { - char path[MG_MAX_PATH]; - cs_stat_t st; - struct dirent *dp; - DIR *dirp; - - LOG(LL_DEBUG, ("%p [%s]", nc, dir)); - if ((dirp = (opendir(dir))) != NULL) { - while ((dp = readdir(dirp)) != NULL) { - /* Do not show current dir and hidden files */ - if (mg_is_file_hidden((const char *) dp->d_name, opts, 1)) { - continue; - } - snprintf(path, sizeof(path), "%s/%s", dir, dp->d_name); - if (mg_stat(path, &st) == 0) { - func(nc, (const char *) dp->d_name, &st); - } - } - closedir(dirp); - } else { - LOG(LL_DEBUG, ("%p opendir(%s) -> %d", nc, dir, mg_get_errno())); - } -} - -static void mg_send_directory_listing(struct mg_connection *nc, const char *dir, - struct http_message *hm, - struct mg_serve_http_opts *opts) { - static const char *sort_js_code = - ""; - - mg_send_response_line(nc, 200, opts->extra_headers); - mg_printf(nc, "%s: %s\r\n%s: %s\r\n\r\n", "Transfer-Encoding", "chunked", - "Content-Type", "text/html; charset=utf-8"); - - mg_printf_http_chunk( - nc, - "Index of %.*s%s%s" - "\n" - "

Index of %.*s

\n" - "" - "\n" - "\n" - "", - (int) hm->uri.len, hm->uri.p, sort_js_code, sort_js_code2, - (int) hm->uri.len, hm->uri.p); - mg_scan_directory(nc, dir, opts, mg_print_dir_entry); - mg_printf_http_chunk(nc, - "\n" - "
Name" - "Modified" - "Size


\n" - "
%s
\n" - "", - mg_version_header); - mg_send_http_chunk(nc, "", 0); - /* TODO(rojer): Remove when cesanta/dev/issues/197 is fixed. */ - nc->flags |= MG_F_SEND_AND_CLOSE; -} -#endif /* MG_ENABLE_DIRECTORY_LISTING */ - -/* - * Given a directory path, find one of the files specified in the - * comma-separated list of index files `list`. - * First found index file wins. If an index file is found, then gets - * appended to the `path`, stat-ed, and result of `stat()` passed to `stp`. - * If index file is not found, then `path` and `stp` remain unchanged. - */ -MG_INTERNAL void mg_find_index_file(const char *path, const char *list, - char **index_file, cs_stat_t *stp) { - struct mg_str vec; - size_t path_len = strlen(path); - int found = 0; - *index_file = NULL; - - /* Traverse index files list. For each entry, append it to the given */ - /* path and see if the file exists. If it exists, break the loop */ - while ((list = mg_next_comma_list_entry(list, &vec, NULL)) != NULL) { - cs_stat_t st; - size_t len = path_len + 1 + vec.len + 1; - *index_file = (char *) MG_REALLOC(*index_file, len); - if (*index_file == NULL) break; - snprintf(*index_file, len, "%s%c%.*s", path, DIRSEP, (int) vec.len, vec.p); - - /* Does it exist? Is it a file? */ - if (mg_stat(*index_file, &st) == 0 && S_ISREG(st.st_mode)) { - /* Yes it does, break the loop */ - *stp = st; - found = 1; - break; - } - } - if (!found) { - MG_FREE(*index_file); - *index_file = NULL; - } - LOG(LL_DEBUG, ("[%s] [%s]", path, (*index_file ? *index_file : ""))); -} - -#if MG_ENABLE_HTTP_URL_REWRITES -static int mg_http_send_port_based_redirect( - struct mg_connection *c, struct http_message *hm, - const struct mg_serve_http_opts *opts) { - const char *rewrites = opts->url_rewrites; - struct mg_str a, b; - char local_port[20] = {'%'}; - - mg_conn_addr_to_str(c, local_port + 1, sizeof(local_port) - 1, - MG_SOCK_STRINGIFY_PORT); - - while ((rewrites = mg_next_comma_list_entry(rewrites, &a, &b)) != NULL) { - if (mg_vcmp(&a, local_port) == 0) { - mg_send_response_line(c, 301, NULL); - mg_printf(c, "Content-Length: 0\r\nLocation: %.*s%.*s\r\n\r\n", - (int) b.len, b.p, (int) (hm->proto.p - hm->uri.p - 1), - hm->uri.p); - return 1; - } - } - - return 0; -} - -static void mg_reverse_proxy_handler(struct mg_connection *nc, int ev, - void *ev_data MG_UD_ARG(void *user_data)) { - struct http_message *hm = (struct http_message *) ev_data; - struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); - - if (pd == NULL || pd->reverse_proxy_data.linked_conn == NULL) { - DBG(("%p: upstream closed", nc)); - return; - } - - switch (ev) { - case MG_EV_CONNECT: - if (*(int *) ev_data != 0) { - mg_http_send_error(pd->reverse_proxy_data.linked_conn, 502, NULL); - } - break; - /* TODO(mkm): handle streaming */ - case MG_EV_HTTP_REPLY: - mg_send(pd->reverse_proxy_data.linked_conn, hm->message.p, - hm->message.len); - pd->reverse_proxy_data.linked_conn->flags |= MG_F_SEND_AND_CLOSE; - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - break; - case MG_EV_CLOSE: - pd->reverse_proxy_data.linked_conn->flags |= MG_F_SEND_AND_CLOSE; - break; - } - -#if MG_ENABLE_CALLBACK_USERDATA - (void) user_data; -#endif -} - -void mg_http_reverse_proxy(struct mg_connection *nc, - const struct http_message *hm, struct mg_str mount, - struct mg_str upstream) { - struct mg_connection *be; - char burl[256], *purl = burl; - int i; - const char *error; - struct mg_connect_opts opts; - struct mg_str path = MG_NULL_STR, user_info = MG_NULL_STR, host = MG_NULL_STR; - memset(&opts, 0, sizeof(opts)); - opts.error_string = &error; - - mg_asprintf(&purl, sizeof(burl), "%.*s%.*s", (int) upstream.len, upstream.p, - (int) (hm->uri.len - mount.len), hm->uri.p + mount.len); - - be = mg_connect_http_base(nc->mgr, MG_CB(mg_reverse_proxy_handler, NULL), - opts, "http", NULL, "https", NULL, purl, &path, - &user_info, &host); - LOG(LL_DEBUG, ("Proxying %.*s to %s (rule: %.*s)", (int) hm->uri.len, - hm->uri.p, purl, (int) mount.len, mount.p)); - - if (be == NULL) { - LOG(LL_ERROR, ("Error connecting to %s: %s", purl, error)); - mg_http_send_error(nc, 502, NULL); - goto cleanup; - } - - /* link connections to each other, they must live and die together */ - mg_http_get_proto_data(be)->reverse_proxy_data.linked_conn = nc; - mg_http_get_proto_data(nc)->reverse_proxy_data.linked_conn = be; - - /* send request upstream */ - mg_printf(be, "%.*s %.*s HTTP/1.1\r\n", (int) hm->method.len, hm->method.p, - (int) path.len, path.p); - - mg_printf(be, "Host: %.*s\r\n", (int) host.len, host.p); - for (i = 0; i < MG_MAX_HTTP_HEADERS && hm->header_names[i].len > 0; i++) { - struct mg_str hn = hm->header_names[i]; - struct mg_str hv = hm->header_values[i]; - - /* we rewrite the host header */ - if (mg_vcasecmp(&hn, "Host") == 0) continue; - /* - * Don't pass chunked transfer encoding to the client because hm->body is - * already dechunked when we arrive here. - */ - if (mg_vcasecmp(&hn, "Transfer-encoding") == 0 && - mg_vcasecmp(&hv, "chunked") == 0) { - mg_printf(be, "Content-Length: %" SIZE_T_FMT "\r\n", hm->body.len); - continue; - } - /* We don't support proxying Expect: 100-continue. */ - if (mg_vcasecmp(&hn, "Expect") == 0 && - mg_vcasecmp(&hv, "100-continue") == 0) { - continue; - } - - mg_printf(be, "%.*s: %.*s\r\n", (int) hn.len, hn.p, (int) hv.len, hv.p); - } - - mg_send(be, "\r\n", 2); - mg_send(be, hm->body.p, hm->body.len); - -cleanup: - if (purl != burl) MG_FREE(purl); -} - -static int mg_http_handle_forwarding(struct mg_connection *nc, - struct http_message *hm, - const struct mg_serve_http_opts *opts) { - const char *rewrites = opts->url_rewrites; - struct mg_str a, b; - struct mg_str p1 = MG_MK_STR("http://"), p2 = MG_MK_STR("https://"); - - while ((rewrites = mg_next_comma_list_entry(rewrites, &a, &b)) != NULL) { - if (mg_strncmp(a, hm->uri, a.len) == 0) { - if (mg_strncmp(b, p1, p1.len) == 0 || mg_strncmp(b, p2, p2.len) == 0) { - mg_http_reverse_proxy(nc, hm, a, b); - return 1; - } - } - } - - return 0; -} -#endif /* MG_ENABLE_FILESYSTEM */ - -MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm, - const struct mg_serve_http_opts *opts, - char **local_path, - struct mg_str *remainder) { - int ok = 1; - const char *cp = hm->uri.p, *cp_end = hm->uri.p + hm->uri.len; - struct mg_str root = {NULL, 0}; - const char *file_uri_start = cp; - *local_path = NULL; - remainder->p = NULL; - remainder->len = 0; - - { /* 1. Determine which root to use. */ - -#if MG_ENABLE_HTTP_URL_REWRITES - const char *rewrites = opts->url_rewrites; -#else - const char *rewrites = ""; -#endif - struct mg_str *hh = mg_get_http_header(hm, "Host"); - struct mg_str a, b; - /* Check rewrites first. */ - while ((rewrites = mg_next_comma_list_entry(rewrites, &a, &b)) != NULL) { - if (a.len > 1 && a.p[0] == '@') { - /* Host rewrite. */ - if (hh != NULL && hh->len == a.len - 1 && - mg_ncasecmp(a.p + 1, hh->p, a.len - 1) == 0) { - root = b; - break; - } - } else { - /* Regular rewrite, URI=directory */ - size_t match_len = mg_match_prefix_n(a, hm->uri); - if (match_len > 0) { - file_uri_start = hm->uri.p + match_len; - if (*file_uri_start == '/' || file_uri_start == cp_end) { - /* Match ended at component boundary, ok. */ - } else if (*(file_uri_start - 1) == '/') { - /* Pattern ends with '/', backtrack. */ - file_uri_start--; - } else { - /* No match: must fall on the component boundary. */ - continue; - } - root = b; - break; - } - } - } - /* If no rewrite rules matched, use DAV or regular document root. */ - if (root.p == NULL) { -#if MG_ENABLE_HTTP_WEBDAV - if (opts->dav_document_root != NULL && mg_is_dav_request(&hm->method)) { - root.p = opts->dav_document_root; - root.len = strlen(opts->dav_document_root); - } else -#endif - { - root.p = opts->document_root; - root.len = strlen(opts->document_root); - } - } - assert(root.p != NULL && root.len > 0); - } - - { /* 2. Find where in the canonical URI path the local path ends. */ - const char *u = file_uri_start + 1; - char *lp = (char *) MG_MALLOC(root.len + hm->uri.len + 1); - char *lp_end = lp + root.len + hm->uri.len + 1; - char *p = lp, *ps; - int exists = 1; - if (lp == NULL) { - ok = 0; - goto out; - } - memcpy(p, root.p, root.len); - p += root.len; - if (*(p - 1) == DIRSEP) p--; - *p = '\0'; - ps = p; - - /* Chop off URI path components one by one and build local path. */ - while (u <= cp_end) { - const char *next = u; - struct mg_str component; - if (exists) { - cs_stat_t st; - exists = (mg_stat(lp, &st) == 0); - if (exists && S_ISREG(st.st_mode)) { - /* We found the terminal, the rest of the URI (if any) is path_info. - */ - if (*(u - 1) == '/') u--; - break; - } - } - if (u >= cp_end) break; - parse_uri_component((const char **) &next, cp_end, "/", &component); - if (component.len > 0) { - int len; - memmove(p + 1, component.p, component.len); - len = mg_url_decode(p + 1, component.len, p + 1, lp_end - p - 1, 0); - if (len <= 0) { - ok = 0; - break; - } - component.p = p + 1; - component.len = len; - if (mg_vcmp(&component, ".") == 0) { - /* Yum. */ - } else if (mg_vcmp(&component, "..") == 0) { - while (p > ps && *p != DIRSEP) p--; - *p = '\0'; - } else { - size_t i; -#ifdef _WIN32 - /* On Windows, make sure it's valid Unicode (no funny stuff). */ - wchar_t buf[MG_MAX_PATH * 2]; - if (to_wchar(component.p, buf, MG_MAX_PATH) == 0) { - DBG(("[%.*s] smells funny", (int) component.len, component.p)); - ok = 0; - break; - } -#endif - *p++ = DIRSEP; - /* No NULs and DIRSEPs in the component (percent-encoded). */ - for (i = 0; i < component.len; i++, p++) { - if (*p == '\0' || *p == DIRSEP -#ifdef _WIN32 - /* On Windows, "/" is also accepted, so check for that too. */ - || - *p == '/' -#endif - ) { - ok = 0; - break; - } - } - } - } - u = next; - } - if (ok) { - *local_path = lp; - if (u > cp_end) u = cp_end; - remainder->p = u; - remainder->len = cp_end - u; - } else { - MG_FREE(lp); - } - } - -out: - LOG(LL_DEBUG, - ("'%.*s' -> '%s' + '%.*s'", (int) hm->uri.len, hm->uri.p, - *local_path ? *local_path : "", (int) remainder->len, remainder->p)); - return ok; -} - -static int mg_get_month_index(const char *s) { - static const char *month_names[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; - size_t i; - - for (i = 0; i < ARRAY_SIZE(month_names); i++) - if (!strcmp(s, month_names[i])) return (int) i; - - return -1; -} - -static int mg_num_leap_years(int year) { - return year / 4 - year / 100 + year / 400; -} - -/* Parse UTC date-time string, and return the corresponding time_t value. */ -MG_INTERNAL time_t mg_parse_date_string(const char *datetime) { - static const unsigned short days_before_month[] = { - 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; - char month_str[32]; - int second, minute, hour, day, month, year, leap_days, days; - time_t result = (time_t) 0; - - if (((sscanf(datetime, "%d/%3s/%d %d:%d:%d", &day, month_str, &year, &hour, - &minute, &second) == 6) || - (sscanf(datetime, "%d %3s %d %d:%d:%d", &day, month_str, &year, &hour, - &minute, &second) == 6) || - (sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d", &day, month_str, &year, - &hour, &minute, &second) == 6) || - (sscanf(datetime, "%d-%3s-%d %d:%d:%d", &day, month_str, &year, &hour, - &minute, &second) == 6)) && - year > 1970 && (month = mg_get_month_index(month_str)) != -1) { - leap_days = mg_num_leap_years(year) - mg_num_leap_years(1970); - year -= 1970; - days = year * 365 + days_before_month[month] + (day - 1) + leap_days; - result = days * 24 * 3600 + hour * 3600 + minute * 60 + second; - } - - return result; -} - -MG_INTERNAL int mg_is_not_modified(struct http_message *hm, cs_stat_t *st) { - struct mg_str *hdr; - if ((hdr = mg_get_http_header(hm, "If-None-Match")) != NULL) { - char etag[64]; - mg_http_construct_etag(etag, sizeof(etag), st); - return mg_vcasecmp(hdr, etag) == 0; - } else if ((hdr = mg_get_http_header(hm, "If-Modified-Since")) != NULL) { - return st->st_mtime <= mg_parse_date_string(hdr->p); - } else { - return 0; - } -} - -void mg_http_send_digest_auth_request(struct mg_connection *c, - const char *domain) { - mg_printf(c, - "HTTP/1.1 401 Unauthorized\r\n" - "WWW-Authenticate: Digest qop=\"auth\", " - "realm=\"%s\", nonce=\"%lx\"\r\n" - "Content-Length: 0\r\n\r\n", - domain, (unsigned long) mg_time()); -} - -static void mg_http_send_options(struct mg_connection *nc) { - mg_printf(nc, "%s", - "HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, CONNECT, OPTIONS" -#if MG_ENABLE_HTTP_WEBDAV - ", MKCOL, PUT, DELETE, PROPFIND, MOVE\r\nDAV: 1,2" -#endif - "\r\n\r\n"); - nc->flags |= MG_F_SEND_AND_CLOSE; -} - -static int mg_is_creation_request(const struct http_message *hm) { - return mg_vcmp(&hm->method, "MKCOL") == 0 || mg_vcmp(&hm->method, "PUT") == 0; -} - -MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path, - const struct mg_str *path_info, - struct http_message *hm, - struct mg_serve_http_opts *opts) { - int exists, is_directory, is_cgi; -#if MG_ENABLE_HTTP_WEBDAV - int is_dav = mg_is_dav_request(&hm->method); -#else - int is_dav = 0; -#endif - char *index_file = NULL; - cs_stat_t st; - - exists = (mg_stat(path, &st) == 0); - is_directory = exists && S_ISDIR(st.st_mode); - - if (is_directory) - mg_find_index_file(path, opts->index_files, &index_file, &st); - - is_cgi = - (mg_match_prefix(opts->cgi_file_pattern, strlen(opts->cgi_file_pattern), - index_file ? index_file : path) > 0); - - LOG(LL_DEBUG, - ("%p %.*s [%s] exists=%d is_dir=%d is_dav=%d is_cgi=%d index=%s", nc, - (int) hm->method.len, hm->method.p, path, exists, is_directory, is_dav, - is_cgi, index_file ? index_file : "")); - - if (is_directory && hm->uri.p[hm->uri.len - 1] != '/' && !is_dav) { - mg_printf(nc, - "HTTP/1.1 301 Moved\r\nLocation: %.*s/\r\n" - "Content-Length: 0\r\n\r\n", - (int) hm->uri.len, hm->uri.p); - MG_FREE(index_file); - return; - } - - /* If we have path_info, the only way to handle it is CGI. */ - if (path_info->len > 0 && !is_cgi) { - mg_http_send_error(nc, 501, NULL); - MG_FREE(index_file); - return; - } - - if (is_dav && opts->dav_document_root == NULL) { - mg_http_send_error(nc, 501, NULL); - } else if (!mg_http_is_authorized( - hm, mg_mk_str(path), opts->auth_domain, opts->global_auth_file, - ((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) | - MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE | - MG_AUTH_FLAG_ALLOW_MISSING_FILE)) || - !mg_http_is_authorized( - hm, mg_mk_str(path), opts->auth_domain, - opts->per_directory_auth_file, - ((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) | - MG_AUTH_FLAG_ALLOW_MISSING_FILE))) { - mg_http_send_digest_auth_request(nc, opts->auth_domain); - } else if (is_cgi) { -#if MG_ENABLE_HTTP_CGI - mg_handle_cgi(nc, index_file ? index_file : path, path_info, hm, opts); -#else - mg_http_send_error(nc, 501, NULL); -#endif /* MG_ENABLE_HTTP_CGI */ - } else if ((!exists || - mg_is_file_hidden(path, opts, 0 /* specials are ok */)) && - !mg_is_creation_request(hm)) { - mg_http_send_error(nc, 404, NULL); -#if MG_ENABLE_HTTP_WEBDAV - } else if (!mg_vcmp(&hm->method, "PROPFIND")) { - mg_handle_propfind(nc, path, &st, hm, opts); -#if !MG_DISABLE_DAV_AUTH - } else if (is_dav && - (opts->dav_auth_file == NULL || - (strcmp(opts->dav_auth_file, "-") != 0 && - !mg_http_is_authorized( - hm, mg_mk_str(path), opts->auth_domain, opts->dav_auth_file, - ((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) | - MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE | - MG_AUTH_FLAG_ALLOW_MISSING_FILE))))) { - mg_http_send_digest_auth_request(nc, opts->auth_domain); -#endif - } else if (!mg_vcmp(&hm->method, "MKCOL")) { - mg_handle_mkcol(nc, path, hm); - } else if (!mg_vcmp(&hm->method, "DELETE")) { - mg_handle_delete(nc, opts, path); - } else if (!mg_vcmp(&hm->method, "PUT")) { - mg_handle_put(nc, path, hm); - } else if (!mg_vcmp(&hm->method, "MOVE")) { - mg_handle_move(nc, opts, path, hm); -#if MG_ENABLE_FAKE_DAVLOCK - } else if (!mg_vcmp(&hm->method, "LOCK")) { - mg_handle_lock(nc, path); -#endif -#endif /* MG_ENABLE_HTTP_WEBDAV */ - } else if (!mg_vcmp(&hm->method, "OPTIONS")) { - mg_http_send_options(nc); - } else if (is_directory && index_file == NULL) { -#if MG_ENABLE_DIRECTORY_LISTING - if (strcmp(opts->enable_directory_listing, "yes") == 0) { - mg_send_directory_listing(nc, path, hm, opts); - } else { - mg_http_send_error(nc, 403, NULL); - } -#else - mg_http_send_error(nc, 501, NULL); -#endif - } else if (mg_is_not_modified(hm, &st)) { - mg_http_send_error(nc, 304, "Not Modified"); - } else { - mg_http_serve_file2(nc, index_file ? index_file : path, hm, opts); - } - MG_FREE(index_file); -} - -void mg_serve_http(struct mg_connection *nc, struct http_message *hm, - struct mg_serve_http_opts opts) { - char *path = NULL; - struct mg_str *hdr, path_info; - uint32_t remote_ip = ntohl(*(uint32_t *) &nc->sa.sin.sin_addr); - - if (mg_check_ip_acl(opts.ip_acl, remote_ip) != 1) { - /* Not allowed to connect */ - mg_http_send_error(nc, 403, NULL); - nc->flags |= MG_F_SEND_AND_CLOSE; - return; - } - -#if MG_ENABLE_HTTP_URL_REWRITES - if (mg_http_handle_forwarding(nc, hm, &opts)) { - return; - } - - if (mg_http_send_port_based_redirect(nc, hm, &opts)) { - return; - } -#endif - - if (opts.document_root == NULL) { - opts.document_root = "."; - } - if (opts.per_directory_auth_file == NULL) { - opts.per_directory_auth_file = ".htpasswd"; - } - if (opts.enable_directory_listing == NULL) { - opts.enable_directory_listing = "yes"; - } - if (opts.cgi_file_pattern == NULL) { - opts.cgi_file_pattern = "**.cgi$|**.php$"; - } - if (opts.ssi_pattern == NULL) { - opts.ssi_pattern = "**.shtml$|**.shtm$"; - } - if (opts.index_files == NULL) { - opts.index_files = "index.html,index.htm,index.shtml,index.cgi,index.php"; - } - /* Normalize path - resolve "." and ".." (in-place). */ - if (!mg_normalize_uri_path(&hm->uri, &hm->uri)) { - mg_http_send_error(nc, 400, NULL); - return; - } - if (mg_uri_to_local_path(hm, &opts, &path, &path_info) == 0) { - mg_http_send_error(nc, 404, NULL); - return; - } - mg_send_http_file(nc, path, &path_info, hm, &opts); - - MG_FREE(path); - path = NULL; - - /* Close connection for non-keep-alive requests */ - if (mg_vcmp(&hm->proto, "HTTP/1.1") != 0 || - ((hdr = mg_get_http_header(hm, "Connection")) != NULL && - mg_vcmp(hdr, "keep-alive") != 0)) { -#if 0 - nc->flags |= MG_F_SEND_AND_CLOSE; -#endif - } -} - -#if MG_ENABLE_HTTP_STREAMING_MULTIPART -void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, - mg_fu_fname_fn local_name_fn - MG_UD_ARG(void *user_data)) { - switch (ev) { - case MG_EV_HTTP_PART_BEGIN: { - struct mg_http_multipart_part *mp = - (struct mg_http_multipart_part *) ev_data; - struct file_upload_state *fus = - (struct file_upload_state *) MG_CALLOC(1, sizeof(*fus)); - struct mg_str lfn = local_name_fn(nc, mg_mk_str(mp->file_name)); - mp->user_data = NULL; - if (lfn.p == NULL || lfn.len == 0) { - LOG(LL_ERROR, ("%p Not allowed to upload %s", nc, mp->file_name)); - mg_printf(nc, - "HTTP/1.1 403 Not Allowed\r\n" - "Content-Type: text/plain\r\n" - "Connection: close\r\n\r\n" - "Not allowed to upload %s\r\n", - mp->file_name); - nc->flags |= MG_F_SEND_AND_CLOSE; - return; - } - fus->lfn = (char *) MG_MALLOC(lfn.len + 1); - memcpy(fus->lfn, lfn.p, lfn.len); - fus->lfn[lfn.len] = '\0'; - if (lfn.p != mp->file_name) MG_FREE((char *) lfn.p); - LOG(LL_DEBUG, - ("%p Receiving file %s -> %s", nc, mp->file_name, fus->lfn)); - fus->fp = mg_fopen(fus->lfn, "w"); - if (fus->fp == NULL) { - mg_printf(nc, - "HTTP/1.1 500 Internal Server Error\r\n" - "Content-Type: text/plain\r\n" - "Connection: close\r\n\r\n"); - LOG(LL_ERROR, ("Failed to open %s: %d\n", fus->lfn, mg_get_errno())); - mg_printf(nc, "Failed to open %s: %d\n", fus->lfn, mg_get_errno()); - /* Do not close the connection just yet, discard remainder of the data. - * This is because at the time of writing some browsers (Chrome) fail to - * render response before all the data is sent. */ - } - mp->user_data = (void *) fus; - break; - } - case MG_EV_HTTP_PART_DATA: { - struct mg_http_multipart_part *mp = - (struct mg_http_multipart_part *) ev_data; - struct file_upload_state *fus = - (struct file_upload_state *) mp->user_data; - if (fus == NULL || fus->fp == NULL) break; - if (mg_fwrite(mp->data.p, 1, mp->data.len, fus->fp) != mp->data.len) { - LOG(LL_ERROR, ("Failed to write to %s: %d, wrote %d", fus->lfn, - mg_get_errno(), (int) fus->num_recd)); - if (mg_get_errno() == ENOSPC -#ifdef SPIFFS_ERR_FULL - || mg_get_errno() == SPIFFS_ERR_FULL -#endif - ) { - mg_printf(nc, - "HTTP/1.1 413 Payload Too Large\r\n" - "Content-Type: text/plain\r\n" - "Connection: close\r\n\r\n"); - mg_printf(nc, "Failed to write to %s: no space left; wrote %d\r\n", - fus->lfn, (int) fus->num_recd); - } else { - mg_printf(nc, - "HTTP/1.1 500 Internal Server Error\r\n" - "Content-Type: text/plain\r\n" - "Connection: close\r\n\r\n"); - mg_printf(nc, "Failed to write to %s: %d, wrote %d", mp->file_name, - mg_get_errno(), (int) fus->num_recd); - } - fclose(fus->fp); - remove(fus->lfn); - fus->fp = NULL; - /* Do not close the connection just yet, discard remainder of the data. - * This is because at the time of writing some browsers (Chrome) fail to - * render response before all the data is sent. */ - return; - } - fus->num_recd += mp->data.len; - LOG(LL_DEBUG, ("%p rec'd %d bytes, %d total", nc, (int) mp->data.len, - (int) fus->num_recd)); - break; - } - case MG_EV_HTTP_PART_END: { - struct mg_http_multipart_part *mp = - (struct mg_http_multipart_part *) ev_data; - struct file_upload_state *fus = - (struct file_upload_state *) mp->user_data; - if (fus == NULL) break; - if (mp->status >= 0 && fus->fp != NULL) { - LOG(LL_DEBUG, ("%p Uploaded %s (%s), %d bytes", nc, mp->file_name, - fus->lfn, (int) fus->num_recd)); - mg_printf(nc, - "HTTP/1.1 200 OK\r\n" - "Content-Type: text/plain\r\n" - "Connection: close\r\n\r\n" - "Ok, %s - %d bytes.\r\n", - mp->file_name, (int) fus->num_recd); - } else { - LOG(LL_ERROR, ("Failed to store %s (%s)", mp->file_name, fus->lfn)); - /* - * mp->status < 0 means connection was terminated, so no reason to send - * HTTP reply - */ - } - if (fus->fp != NULL) fclose(fus->fp); - MG_FREE(fus->lfn); - MG_FREE(fus); - mp->user_data = NULL; - nc->flags |= MG_F_SEND_AND_CLOSE; - break; - } - } - -#if MG_ENABLE_CALLBACK_USERDATA - (void) user_data; -#endif -} - -#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ -#endif /* MG_ENABLE_FILESYSTEM */ - -struct mg_connection *mg_connect_http_base( - struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), - struct mg_connect_opts opts, const char *scheme1, const char *scheme2, - const char *scheme_ssl1, const char *scheme_ssl2, const char *url, - struct mg_str *path, struct mg_str *user_info, struct mg_str *host) { - struct mg_connection *nc = NULL; - unsigned int port_i = 0; - int use_ssl = 0; - struct mg_str scheme, query, fragment; - char conn_addr_buf[2]; - char *conn_addr = conn_addr_buf; - - if (mg_parse_uri(mg_mk_str(url), &scheme, user_info, host, &port_i, path, - &query, &fragment) != 0) { - MG_SET_PTRPTR(opts.error_string, "cannot parse url"); - goto out; - } - - /* If query is present, do not strip it. Pass to the caller. */ - if (query.len > 0) path->len += query.len + 1; - - if (scheme.len == 0 || mg_vcmp(&scheme, scheme1) == 0 || - (scheme2 != NULL && mg_vcmp(&scheme, scheme2) == 0)) { - use_ssl = 0; - if (port_i == 0) port_i = 80; - } else if (mg_vcmp(&scheme, scheme_ssl1) == 0 || - (scheme2 != NULL && mg_vcmp(&scheme, scheme_ssl2) == 0)) { - use_ssl = 1; - if (port_i == 0) port_i = 443; - } else { - goto out; - } - - mg_asprintf(&conn_addr, sizeof(conn_addr_buf), "tcp://%.*s:%u", - (int) host->len, host->p, port_i); - if (conn_addr == NULL) goto out; - - LOG(LL_DEBUG, ("%s use_ssl? %d %s", url, use_ssl, conn_addr)); - if (use_ssl) { -#if MG_ENABLE_SSL - /* - * Schema requires SSL, but no SSL parameters were provided in opts. - * In order to maintain backward compatibility, use a faux-SSL with no - * verification. - */ - if (opts.ssl_ca_cert == NULL) { - opts.ssl_ca_cert = "*"; - } -#else - MG_SET_PTRPTR(opts.error_string, "ssl is disabled"); - goto out; -#endif - } - - if ((nc = mg_connect_opt(mgr, conn_addr, MG_CB(ev_handler, user_data), - opts)) != NULL) { - mg_set_protocol_http_websocket(nc); - } - -out: - if (conn_addr != NULL && conn_addr != conn_addr_buf) MG_FREE(conn_addr); - return nc; -} - -struct mg_connection *mg_connect_http_opt( - struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), - struct mg_connect_opts opts, const char *url, const char *extra_headers, - const char *post_data) { - struct mg_str user = MG_NULL_STR, null_str = MG_NULL_STR; - struct mg_str host = MG_NULL_STR, path = MG_NULL_STR; - struct mbuf auth; - struct mg_connection *nc = - mg_connect_http_base(mgr, MG_CB(ev_handler, user_data), opts, "http", - NULL, "https", NULL, url, &path, &user, &host); - - if (nc == NULL) { - return NULL; - } - - mbuf_init(&auth, 0); - if (user.len > 0) { - mg_basic_auth_header(user, null_str, &auth); - } - - if (post_data == NULL) post_data = ""; - if (extra_headers == NULL) extra_headers = ""; - if (path.len == 0) path = mg_mk_str("/"); - if (host.len == 0) host = mg_mk_str(""); - - mg_printf(nc, "%s %.*s HTTP/1.1\r\nHost: %.*s\r\nContent-Length: %" SIZE_T_FMT - "\r\n%.*s%s\r\n%s", - (post_data[0] == '\0' ? "GET" : "POST"), (int) path.len, path.p, - (int) (path.p - host.p), host.p, strlen(post_data), (int) auth.len, - (auth.buf == NULL ? "" : auth.buf), extra_headers, post_data); - - mbuf_free(&auth); - return nc; -} - -struct mg_connection *mg_connect_http( - struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), - const char *url, const char *extra_headers, const char *post_data) { - struct mg_connect_opts opts; - memset(&opts, 0, sizeof(opts)); - return mg_connect_http_opt(mgr, MG_CB(ev_handler, user_data), opts, url, - extra_headers, post_data); -} - -size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name, - size_t var_name_len, char *file_name, - size_t file_name_len, const char **data, - size_t *data_len) { - static const char cd[] = "Content-Disposition: "; - size_t hl, bl, n, ll, pos, cdl = sizeof(cd) - 1; - int shl; - - if (buf == NULL || buf_len <= 0) return 0; - if ((shl = mg_http_get_request_len(buf, buf_len)) <= 0) return 0; - hl = shl; - if (buf[0] != '-' || buf[1] != '-' || buf[2] == '\n') return 0; - - /* Get boundary length */ - bl = mg_get_line_len(buf, buf_len); - - /* Loop through headers, fetch variable name and file name */ - var_name[0] = file_name[0] = '\0'; - for (n = bl; (ll = mg_get_line_len(buf + n, hl - n)) > 0; n += ll) { - if (mg_ncasecmp(cd, buf + n, cdl) == 0) { - struct mg_str header; - header.p = buf + n + cdl; - header.len = ll - (cdl + 2); - { - char *var_name2 = var_name; - mg_http_parse_header2(&header, "name", &var_name2, var_name_len); - /* TODO: handle reallocated buffer correctly */ - if (var_name2 != var_name) { - MG_FREE(var_name2); - var_name[0] = '\0'; - } - } - { - char *file_name2 = file_name; - mg_http_parse_header2(&header, "filename", &file_name2, file_name_len); - /* TODO: handle reallocated buffer correctly */ - if (file_name2 != file_name) { - MG_FREE(file_name2); - file_name[0] = '\0'; - } - } - } - } - - /* Scan through the body, search for terminating boundary */ - for (pos = hl; pos + (bl - 2) < buf_len; pos++) { - if (buf[pos] == '-' && !strncmp(buf, &buf[pos], bl - 2)) { - if (data_len != NULL) *data_len = (pos - 2) - hl; - if (data != NULL) *data = buf + hl; - return pos; - } - } - - return 0; -} - -void mg_register_http_endpoint_opt(struct mg_connection *nc, - const char *uri_path, - mg_event_handler_t handler, - struct mg_http_endpoint_opts opts) { - struct mg_http_proto_data *pd = NULL; - struct mg_http_endpoint *new_ep = NULL; - - if (nc == NULL) return; - new_ep = (struct mg_http_endpoint *) MG_CALLOC(1, sizeof(*new_ep)); - if (new_ep == NULL) return; - - pd = mg_http_get_proto_data(nc); - new_ep->uri_pattern = mg_strdup(mg_mk_str(uri_path)); - if (opts.auth_domain != NULL && opts.auth_file != NULL) { - new_ep->auth_domain = strdup(opts.auth_domain); - new_ep->auth_file = strdup(opts.auth_file); - } - new_ep->handler = handler; -#if MG_ENABLE_CALLBACK_USERDATA - new_ep->user_data = opts.user_data; -#endif - new_ep->next = pd->endpoints; - pd->endpoints = new_ep; -} - -static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev, - struct http_message *hm) { - struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); - void *user_data = nc->user_data; - - if (ev == MG_EV_HTTP_REQUEST -#if MG_ENABLE_HTTP_STREAMING_MULTIPART - || ev == MG_EV_HTTP_MULTIPART_REQUEST -#endif - ) { - struct mg_http_endpoint *ep = - mg_http_get_endpoint_handler(nc->listener, &hm->uri); - if (ep != NULL) { -#if MG_ENABLE_FILESYSTEM && !MG_DISABLE_HTTP_DIGEST_AUTH - if (!mg_http_is_authorized(hm, hm->uri, ep->auth_domain, ep->auth_file, - MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE)) { - mg_http_send_digest_auth_request(nc, ep->auth_domain); - return; - } -#endif - pd->endpoint_handler = ep->handler; -#if MG_ENABLE_CALLBACK_USERDATA - user_data = ep->user_data; -#endif - } - } - mg_call(nc, pd->endpoint_handler ? pd->endpoint_handler : nc->handler, - user_data, ev, hm); -} - -void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path, - MG_CB(mg_event_handler_t handler, - void *user_data)) { - struct mg_http_endpoint_opts opts; - memset(&opts, 0, sizeof(opts)); -#if MG_ENABLE_CALLBACK_USERDATA - opts.user_data = user_data; -#endif - mg_register_http_endpoint_opt(nc, uri_path, handler, opts); -} - -#endif /* MG_ENABLE_HTTP */ diff --git a/src/mongoose-6.11/src/mg_http.h b/src/mongoose-6.11/src/mg_http.h deleted file mode 100644 index c89d38f..0000000 --- a/src/mongoose-6.11/src/mg_http.h +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -/* - * === Common API reference - */ - -#ifndef CS_MONGOOSE_SRC_HTTP_H_ -#define CS_MONGOOSE_SRC_HTTP_H_ - -#if MG_ENABLE_HTTP - -#include "mg_net.h" -#include "common/mg_str.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifndef MG_MAX_HTTP_HEADERS -#define MG_MAX_HTTP_HEADERS 20 -#endif - -#ifndef MG_MAX_HTTP_REQUEST_SIZE -#define MG_MAX_HTTP_REQUEST_SIZE 1024 -#endif - -#ifndef MG_MAX_HTTP_SEND_MBUF -#define MG_MAX_HTTP_SEND_MBUF 1024 -#endif - -#ifndef MG_CGI_ENVIRONMENT_SIZE -#define MG_CGI_ENVIRONMENT_SIZE 8192 -#endif - -/* HTTP message */ -struct http_message { - struct mg_str message; /* Whole message: request line + headers + body */ - struct mg_str body; /* Message body. 0-length for requests with no body */ - - /* HTTP Request line (or HTTP response line) */ - struct mg_str method; /* "GET" */ - struct mg_str uri; /* "/my_file.html" */ - struct mg_str proto; /* "HTTP/1.1" -- for both request and response */ - - /* For responses, code and response status message are set */ - int resp_code; - struct mg_str resp_status_msg; - - /* - * Query-string part of the URI. For example, for HTTP request - * GET /foo/bar?param1=val1¶m2=val2 - * | uri | query_string | - * - * Note that question mark character doesn't belong neither to the uri, - * nor to the query_string - */ - struct mg_str query_string; - - /* Headers */ - struct mg_str header_names[MG_MAX_HTTP_HEADERS]; - struct mg_str header_values[MG_MAX_HTTP_HEADERS]; -}; - -#if MG_ENABLE_HTTP_WEBSOCKET -/* WebSocket message */ -struct websocket_message { - unsigned char *data; - size_t size; - unsigned char flags; -}; -#endif - -/* HTTP multipart part */ -struct mg_http_multipart_part { - const char *file_name; - const char *var_name; - struct mg_str data; - int status; /* <0 on error */ - void *user_data; -}; - -/* SSI call context */ -struct mg_ssi_call_ctx { - struct http_message *req; /* The request being processed. */ - struct mg_str file; /* Filesystem path of the file being processed. */ - struct mg_str arg; /* The argument passed to the tag: . */ -}; - -/* HTTP and websocket events. void *ev_data is described in a comment. */ -#define MG_EV_HTTP_REQUEST 100 /* struct http_message * */ -#define MG_EV_HTTP_REPLY 101 /* struct http_message * */ -#define MG_EV_HTTP_CHUNK 102 /* struct http_message * */ -#define MG_EV_SSI_CALL 105 /* char * */ -#define MG_EV_SSI_CALL_CTX 106 /* struct mg_ssi_call_ctx * */ - -#if MG_ENABLE_HTTP_WEBSOCKET -#define MG_EV_WEBSOCKET_HANDSHAKE_REQUEST 111 /* struct http_message * */ -#define MG_EV_WEBSOCKET_HANDSHAKE_DONE 112 /* NULL */ -#define MG_EV_WEBSOCKET_FRAME 113 /* struct websocket_message * */ -#define MG_EV_WEBSOCKET_CONTROL_FRAME 114 /* struct websocket_message * */ -#endif - -#if MG_ENABLE_HTTP_STREAMING_MULTIPART -#define MG_EV_HTTP_MULTIPART_REQUEST 121 /* struct http_message */ -#define MG_EV_HTTP_PART_BEGIN 122 /* struct mg_http_multipart_part */ -#define MG_EV_HTTP_PART_DATA 123 /* struct mg_http_multipart_part */ -#define MG_EV_HTTP_PART_END 124 /* struct mg_http_multipart_part */ -/* struct mg_http_multipart_part */ -#define MG_EV_HTTP_MULTIPART_REQUEST_END 125 -#endif - -/* - * Attaches a built-in HTTP event handler to the given connection. - * The user-defined event handler will receive following extra events: - * - * - MG_EV_HTTP_REQUEST: HTTP request has arrived. Parsed HTTP request - * is passed as - * `struct http_message` through the handler's `void *ev_data` pointer. - * - MG_EV_HTTP_REPLY: The HTTP reply has arrived. The parsed HTTP reply is - * passed as `struct http_message` through the handler's `void *ev_data` - * pointer. - * - MG_EV_HTTP_CHUNK: The HTTP chunked-encoding chunk has arrived. - * The parsed HTTP reply is passed as `struct http_message` through the - * handler's `void *ev_data` pointer. `http_message::body` would contain - * incomplete, reassembled HTTP body. - * It will grow with every new chunk that arrives, and it can - * potentially consume a lot of memory. An event handler may process - * the body as chunks are coming, and signal Mongoose to delete processed - * body by setting `MG_F_DELETE_CHUNK` in `mg_connection::flags`. When - * the last zero chunk is received, - * Mongoose sends `MG_EV_HTTP_REPLY` event with - * full reassembled body (if handler did not signal to delete chunks) or - * with empty body (if handler did signal to delete chunks). - * - MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: server has received the WebSocket - * handshake request. `ev_data` contains parsed HTTP request. - * - MG_EV_WEBSOCKET_HANDSHAKE_DONE: server has completed the WebSocket - * handshake. `ev_data` is `NULL`. - * - MG_EV_WEBSOCKET_FRAME: new WebSocket frame has arrived. `ev_data` is - * `struct websocket_message *` - * - * When compiled with MG_ENABLE_HTTP_STREAMING_MULTIPART, Mongoose parses - * multipart requests and splits them into separate events: - * - MG_EV_HTTP_MULTIPART_REQUEST: Start of the request. - * This event is sent before body is parsed. After this, the user - * should expect a sequence of PART_BEGIN/DATA/END requests. - * This is also the last time when headers and other request fields are - * accessible. - * - MG_EV_HTTP_PART_BEGIN: Start of a part of a multipart message. - * Argument: mg_http_multipart_part with var_name and file_name set - * (if present). No data is passed in this message. - * - MG_EV_HTTP_PART_DATA: new portion of data from the multipart message. - * Argument: mg_http_multipart_part. var_name and file_name are preserved, - * data is available in mg_http_multipart_part.data. - * - MG_EV_HTTP_PART_END: End of the current part. var_name, file_name are - * the same, no data in the message. If status is 0, then the part is - * properly terminated with a boundary, status < 0 means that connection - * was terminated. - * - MG_EV_HTTP_MULTIPART_REQUEST_END: End of the multipart request. - * Argument: mg_http_multipart_part, var_name and file_name are NULL, - * status = 0 means request was properly closed, < 0 means connection - * was terminated (note: in this case both PART_END and REQUEST_END are - * delivered). - */ -void mg_set_protocol_http_websocket(struct mg_connection *nc); - -#if MG_ENABLE_HTTP_WEBSOCKET -/* - * Send websocket handshake to the server. - * - * `nc` must be a valid connection, connected to a server. `uri` is an URI - * to fetch, extra_headers` is extra HTTP headers to send or `NULL`. - * - * This function is intended to be used by websocket client. - * - * Note that the Host header is mandatory in HTTP/1.1 and must be - * included in `extra_headers`. `mg_send_websocket_handshake2` offers - * a better API for that. - * - * Deprecated in favour of `mg_send_websocket_handshake2` - */ -void mg_send_websocket_handshake(struct mg_connection *nc, const char *uri, - const char *extra_headers); - -/* - * Send websocket handshake to the server. - * - * `nc` must be a valid connection, connected to a server. `uri` is an URI - * to fetch, `host` goes into the `Host` header, `protocol` goes into the - * `Sec-WebSocket-Proto` header (NULL to omit), extra_headers` is extra HTTP - * headers to send or `NULL`. - * - * This function is intended to be used by websocket client. - */ -void mg_send_websocket_handshake2(struct mg_connection *nc, const char *path, - const char *host, const char *protocol, - const char *extra_headers); - -/* Like mg_send_websocket_handshake2 but also passes basic auth header */ -void mg_send_websocket_handshake3(struct mg_connection *nc, const char *path, - const char *host, const char *protocol, - const char *extra_headers, const char *user, - const char *pass); - -/* Same as mg_send_websocket_handshake3 but with strings not necessarily - * NUL-temrinated */ -void mg_send_websocket_handshake3v(struct mg_connection *nc, - const struct mg_str path, - const struct mg_str host, - const struct mg_str protocol, - const struct mg_str extra_headers, - const struct mg_str user, - const struct mg_str pass); - -/* - * Helper function that creates an outbound WebSocket connection. - * - * `url` is a URL to connect to. It must be properly URL-encoded, e.g. have - * no spaces, etc. By default, `mg_connect_ws()` sends Connection and - * Host headers. `extra_headers` is an extra HTTP header to send, e.g. - * `"User-Agent: my-app\r\n"`. - * If `protocol` is not NULL, then a `Sec-WebSocket-Protocol` header is sent. - * - * Examples: - * - * ```c - * nc1 = mg_connect_ws(mgr, ev_handler_1, "ws://echo.websocket.org", NULL, - * NULL); - * nc2 = mg_connect_ws(mgr, ev_handler_1, "wss://echo.websocket.org", NULL, - * NULL); - * nc3 = mg_connect_ws(mgr, ev_handler_1, "ws://api.cesanta.com", - * "clubby.cesanta.com", NULL); - * ``` - */ -struct mg_connection *mg_connect_ws(struct mg_mgr *mgr, - MG_CB(mg_event_handler_t event_handler, - void *user_data), - const char *url, const char *protocol, - const char *extra_headers); - -/* - * Helper function that creates an outbound WebSocket connection - * - * Mostly identical to `mg_connect_ws`, but allows to provide extra parameters - * (for example, SSL parameters) - */ -struct mg_connection *mg_connect_ws_opt( - struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), - struct mg_connect_opts opts, const char *url, const char *protocol, - const char *extra_headers); - -/* - * Send WebSocket frame to the remote end. - * - * `op_and_flags` specifies the frame's type. It's one of: - * - * - WEBSOCKET_OP_CONTINUE - * - WEBSOCKET_OP_TEXT - * - WEBSOCKET_OP_BINARY - * - WEBSOCKET_OP_CLOSE - * - WEBSOCKET_OP_PING - * - WEBSOCKET_OP_PONG - * - * Orred with one of the flags: - * - * - WEBSOCKET_DONT_FIN: Don't set the FIN flag on the frame to be sent. - * - * `data` and `data_len` contain frame data. - */ -void mg_send_websocket_frame(struct mg_connection *nc, int op_and_flags, - const void *data, size_t data_len); - -/* - * Like `mg_send_websocket_frame()`, but composes a single frame from multiple - * buffers. - */ -void mg_send_websocket_framev(struct mg_connection *nc, int op_and_flags, - const struct mg_str *strings, int num_strings); - -/* - * Sends WebSocket frame to the remote end. - * - * Like `mg_send_websocket_frame()`, but allows to create formatted messages - * with `printf()`-like semantics. - */ -void mg_printf_websocket_frame(struct mg_connection *nc, int op_and_flags, - const char *fmt, ...); - -/* Websocket opcodes, from http://tools.ietf.org/html/rfc6455 */ -#define WEBSOCKET_OP_CONTINUE 0 -#define WEBSOCKET_OP_TEXT 1 -#define WEBSOCKET_OP_BINARY 2 -#define WEBSOCKET_OP_CLOSE 8 -#define WEBSOCKET_OP_PING 9 -#define WEBSOCKET_OP_PONG 10 - -/* - * If set causes the FIN flag to not be set on outbound - * frames. This enables sending multiple fragments of a single - * logical message. - * - * The WebSocket protocol mandates that if the FIN flag of a data - * frame is not set, the next frame must be a WEBSOCKET_OP_CONTINUE. - * The last frame must have the FIN bit set. - * - * Note that mongoose will automatically defragment incoming messages, - * so this flag is used only on outbound messages. - */ -#define WEBSOCKET_DONT_FIN 0x100 - -#endif /* MG_ENABLE_HTTP_WEBSOCKET */ - -/* - * Decodes a URL-encoded string. - * - * Source string is specified by (`src`, `src_len`), and destination is - * (`dst`, `dst_len`). If `is_form_url_encoded` is non-zero, then - * `+` character is decoded as a blank space character. This function - * guarantees to NUL-terminate the destination. If destination is too small, - * then the source string is partially decoded and `-1` is returned. - *Otherwise, - * a length of the decoded string is returned, not counting final NUL. - */ -int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, - int is_form_url_encoded); - -extern void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[], - const size_t *msg_lens, uint8_t *digest); -extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[], - const size_t *msg_lens, uint8_t *digest); - -/* - * Flags for `mg_http_is_authorized()`. - */ -#define MG_AUTH_FLAG_IS_DIRECTORY (1 << 0) -#define MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE (1 << 1) -#define MG_AUTH_FLAG_ALLOW_MISSING_FILE (1 << 2) - -/* - * Checks whether an http request is authorized. `domain` is the authentication - * realm, `passwords_file` is a htdigest file (can be created e.g. with - * `htdigest` utility). If either `domain` or `passwords_file` is NULL, this - * function always returns 1; otherwise checks the authentication in the - * http request and returns 1 only if there is a match; 0 otherwise. - */ -int mg_http_is_authorized(struct http_message *hm, struct mg_str path, - const char *domain, const char *passwords_file, - int flags); - -/* - * Sends 401 Unauthorized response. - */ -void mg_http_send_digest_auth_request(struct mg_connection *c, - const char *domain); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* MG_ENABLE_HTTP */ - -#endif /* CS_MONGOOSE_SRC_HTTP_H_ */ diff --git a/src/mongoose-6.11/src/mg_http_cgi.c b/src/mongoose-6.11/src/mg_http_cgi.c deleted file mode 100644 index 437c93c..0000000 --- a/src/mongoose-6.11/src/mg_http_cgi.c +++ /dev/null @@ -1,514 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef _WIN32 -#include -#endif - -#if MG_ENABLE_HTTP && MG_ENABLE_HTTP_CGI - -#ifndef MG_MAX_CGI_ENVIR_VARS -#define MG_MAX_CGI_ENVIR_VARS 64 -#endif - -#ifndef MG_ENV_EXPORT_TO_CGI -#define MG_ENV_EXPORT_TO_CGI "MONGOOSE_CGI" -#endif - -#define MG_F_HTTP_CGI_PARSE_HEADERS MG_F_USER_1 - -/* - * This structure helps to create an environment for the spawned CGI program. - * Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings, - * last element must be NULL. - * However, on Windows there is a requirement that all these VARIABLE=VALUE\0 - * strings must reside in a contiguous buffer. The end of the buffer is - * marked by two '\0' characters. - * We satisfy both worlds: we create an envp array (which is vars), all - * entries are actually pointers inside buf. - */ -struct mg_cgi_env_block { - struct mg_connection *nc; - char buf[MG_CGI_ENVIRONMENT_SIZE]; /* Environment buffer */ - const char *vars[MG_MAX_CGI_ENVIR_VARS]; /* char *envp[] */ - int len; /* Space taken */ - int nvars; /* Number of variables in envp[] */ -}; - -#ifdef _WIN32 -struct mg_threadparam { - sock_t s; - HANDLE hPipe; -}; - -static int mg_wait_until_ready(sock_t sock, int for_read) { - fd_set set; - FD_ZERO(&set); - FD_SET(sock, &set); - return select(sock + 1, for_read ? &set : 0, for_read ? 0 : &set, 0, 0) == 1; -} - -static void *mg_push_to_stdin(void *arg) { - struct mg_threadparam *tp = (struct mg_threadparam *) arg; - int n, sent, stop = 0; - DWORD k; - char buf[BUFSIZ]; - - while (!stop && mg_wait_until_ready(tp->s, 1) && - (n = recv(tp->s, buf, sizeof(buf), 0)) > 0) { - if (n == -1 && GetLastError() == WSAEWOULDBLOCK) continue; - for (sent = 0; !stop && sent < n; sent += k) { - if (!WriteFile(tp->hPipe, buf + sent, n - sent, &k, 0)) stop = 1; - } - } - DBG(("%s", "FORWARED EVERYTHING TO CGI")); - CloseHandle(tp->hPipe); - MG_FREE(tp); - return NULL; -} - -static void *mg_pull_from_stdout(void *arg) { - struct mg_threadparam *tp = (struct mg_threadparam *) arg; - int k = 0, stop = 0; - DWORD n, sent; - char buf[BUFSIZ]; - - while (!stop && ReadFile(tp->hPipe, buf, sizeof(buf), &n, NULL)) { - for (sent = 0; !stop && sent < n; sent += k) { - if (mg_wait_until_ready(tp->s, 0) && - (k = send(tp->s, buf + sent, n - sent, 0)) <= 0) - stop = 1; - } - } - DBG(("%s", "EOF FROM CGI")); - CloseHandle(tp->hPipe); - shutdown(tp->s, 2); // Without this, IO thread may get truncated data - closesocket(tp->s); - MG_FREE(tp); - return NULL; -} - -static void mg_spawn_stdio_thread(sock_t sock, HANDLE hPipe, - void *(*func)(void *)) { - struct mg_threadparam *tp = (struct mg_threadparam *) MG_MALLOC(sizeof(*tp)); - if (tp != NULL) { - tp->s = sock; - tp->hPipe = hPipe; - mg_start_thread(func, tp); - } -} - -static void mg_abs_path(const char *utf8_path, char *abs_path, size_t len) { - wchar_t buf[MG_MAX_PATH], buf2[MG_MAX_PATH]; - to_wchar(utf8_path, buf, ARRAY_SIZE(buf)); - GetFullPathNameW(buf, ARRAY_SIZE(buf2), buf2, NULL); - WideCharToMultiByte(CP_UTF8, 0, buf2, wcslen(buf2) + 1, abs_path, len, 0, 0); -} - -static int mg_start_process(const char *interp, const char *cmd, - const char *env, const char *envp[], - const char *dir, sock_t sock) { - STARTUPINFOW si; - PROCESS_INFORMATION pi; - HANDLE a[2], b[2], me = GetCurrentProcess(); - wchar_t wcmd[MG_MAX_PATH], full_dir[MG_MAX_PATH]; - char buf[MG_MAX_PATH], buf2[MG_MAX_PATH], buf5[MG_MAX_PATH], - buf4[MG_MAX_PATH], cmdline[MG_MAX_PATH]; - DWORD flags = DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS; - FILE *fp; - - memset(&si, 0, sizeof(si)); - memset(&pi, 0, sizeof(pi)); - - si.cb = sizeof(si); - si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; - si.wShowWindow = SW_HIDE; - si.hStdError = GetStdHandle(STD_ERROR_HANDLE); - - CreatePipe(&a[0], &a[1], NULL, 0); - CreatePipe(&b[0], &b[1], NULL, 0); - DuplicateHandle(me, a[0], me, &si.hStdInput, 0, TRUE, flags); - DuplicateHandle(me, b[1], me, &si.hStdOutput, 0, TRUE, flags); - - if (interp == NULL && (fp = mg_fopen(cmd, "r")) != NULL) { - buf[0] = buf[1] = '\0'; - fgets(buf, sizeof(buf), fp); - buf[sizeof(buf) - 1] = '\0'; - if (buf[0] == '#' && buf[1] == '!') { - interp = buf + 2; - /* Trim leading spaces: https://github.com/cesanta/mongoose/issues/489 */ - while (*interp != '\0' && isspace(*(unsigned char *) interp)) { - interp++; - } - } - fclose(fp); - } - - snprintf(buf, sizeof(buf), "%s/%s", dir, cmd); - mg_abs_path(buf, buf2, ARRAY_SIZE(buf2)); - - mg_abs_path(dir, buf5, ARRAY_SIZE(buf5)); - to_wchar(dir, full_dir, ARRAY_SIZE(full_dir)); - - if (interp != NULL) { - mg_abs_path(interp, buf4, ARRAY_SIZE(buf4)); - snprintf(cmdline, sizeof(cmdline), "%s \"%s\"", buf4, buf2); - } else { - snprintf(cmdline, sizeof(cmdline), "\"%s\"", buf2); - } - to_wchar(cmdline, wcmd, ARRAY_SIZE(wcmd)); - - if (CreateProcessW(NULL, wcmd, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, - (void *) env, full_dir, &si, &pi) != 0) { - mg_spawn_stdio_thread(sock, a[1], mg_push_to_stdin); - mg_spawn_stdio_thread(sock, b[0], mg_pull_from_stdout); - - CloseHandle(si.hStdOutput); - CloseHandle(si.hStdInput); - - CloseHandle(pi.hThread); - CloseHandle(pi.hProcess); - } else { - CloseHandle(a[1]); - CloseHandle(b[0]); - closesocket(sock); - } - DBG(("CGI command: [%ls] -> %p", wcmd, pi.hProcess)); - - /* Not closing a[0] and b[1] because we've used DUPLICATE_CLOSE_SOURCE */ - (void) envp; - return (pi.hProcess != NULL); -} -#else -static int mg_start_process(const char *interp, const char *cmd, - const char *env, const char *envp[], - const char *dir, sock_t sock) { - char buf[500]; - pid_t pid = fork(); - (void) env; - - if (pid == 0) { - /* - * In Linux `chdir` declared with `warn_unused_result` attribute - * To shutup compiler we have yo use result in some way - */ - int tmp = chdir(dir); - (void) tmp; - (void) dup2(sock, 0); - (void) dup2(sock, 1); - closesocket(sock); - - /* - * After exec, all signal handlers are restored to their default values, - * with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's - * implementation, SIGCHLD's handler will leave unchanged after exec - * if it was set to be ignored. Restore it to default action. - */ - signal(SIGCHLD, SIG_DFL); - - if (interp == NULL) { - execle(cmd, cmd, (char *) 0, envp); /* (char *) 0 to squash warning */ - } else { - execle(interp, interp, cmd, (char *) 0, envp); - } - snprintf(buf, sizeof(buf), - "Status: 500\r\n\r\n" - "500 Server Error: %s%s%s: %s", - interp == NULL ? "" : interp, interp == NULL ? "" : " ", cmd, - strerror(errno)); - send(1, buf, strlen(buf), 0); - _exit(EXIT_FAILURE); /* exec call failed */ - } - - return (pid != 0); -} -#endif /* _WIN32 */ - -/* - * Append VARIABLE=VALUE\0 string to the buffer, and add a respective - * pointer into the vars array. - */ -static char *mg_addenv(struct mg_cgi_env_block *block, const char *fmt, ...) { - int n, space; - char *added = block->buf + block->len; - va_list ap; - - /* Calculate how much space is left in the buffer */ - space = sizeof(block->buf) - (block->len + 2); - if (space > 0) { - /* Copy VARIABLE=VALUE\0 string into the free space */ - va_start(ap, fmt); - n = vsnprintf(added, (size_t) space, fmt, ap); - va_end(ap); - - /* Make sure we do not overflow buffer and the envp array */ - if (n > 0 && n + 1 < space && - block->nvars < (int) ARRAY_SIZE(block->vars) - 2) { - /* Append a pointer to the added string into the envp array */ - block->vars[block->nvars++] = added; - /* Bump up used length counter. Include \0 terminator */ - block->len += n + 1; - } - } - - return added; -} - -static void mg_addenv2(struct mg_cgi_env_block *blk, const char *name) { - const char *s; - if ((s = getenv(name)) != NULL) mg_addenv(blk, "%s=%s", name, s); -} - -static void mg_prepare_cgi_environment(struct mg_connection *nc, - const char *prog, - const struct mg_str *path_info, - const struct http_message *hm, - const struct mg_serve_http_opts *opts, - struct mg_cgi_env_block *blk) { - const char *s; - struct mg_str *h; - char *p; - size_t i; - char buf[100]; - size_t path_info_len = path_info != NULL ? path_info->len : 0; - - blk->len = blk->nvars = 0; - blk->nc = nc; - - if ((s = getenv("SERVER_NAME")) != NULL) { - mg_addenv(blk, "SERVER_NAME=%s", s); - } else { - mg_sock_to_str(nc->sock, buf, sizeof(buf), 3); - mg_addenv(blk, "SERVER_NAME=%s", buf); - } - mg_addenv(blk, "SERVER_ROOT=%s", opts->document_root); - mg_addenv(blk, "DOCUMENT_ROOT=%s", opts->document_root); - mg_addenv(blk, "SERVER_SOFTWARE=%s/%s", "Mongoose", MG_VERSION); - - /* Prepare the environment block */ - mg_addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1"); - mg_addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1"); - mg_addenv(blk, "%s", "REDIRECT_STATUS=200"); /* For PHP */ - - mg_addenv(blk, "REQUEST_METHOD=%.*s", (int) hm->method.len, hm->method.p); - - mg_addenv(blk, "REQUEST_URI=%.*s%s%.*s", (int) hm->uri.len, hm->uri.p, - hm->query_string.len == 0 ? "" : "?", (int) hm->query_string.len, - hm->query_string.p); - - mg_conn_addr_to_str(nc, buf, sizeof(buf), - MG_SOCK_STRINGIFY_REMOTE | MG_SOCK_STRINGIFY_IP); - mg_addenv(blk, "REMOTE_ADDR=%s", buf); - mg_conn_addr_to_str(nc, buf, sizeof(buf), MG_SOCK_STRINGIFY_PORT); - mg_addenv(blk, "SERVER_PORT=%s", buf); - - s = hm->uri.p + hm->uri.len - path_info_len - 1; - if (*s == '/') { - const char *base_name = strrchr(prog, DIRSEP); - mg_addenv(blk, "SCRIPT_NAME=%.*s/%s", (int) (s - hm->uri.p), hm->uri.p, - (base_name != NULL ? base_name + 1 : prog)); - } else { - mg_addenv(blk, "SCRIPT_NAME=%.*s", (int) (s - hm->uri.p + 1), hm->uri.p); - } - mg_addenv(blk, "SCRIPT_FILENAME=%s", prog); - - if (path_info != NULL && path_info->len > 0) { - mg_addenv(blk, "PATH_INFO=%.*s", (int) path_info->len, path_info->p); - /* Not really translated... */ - mg_addenv(blk, "PATH_TRANSLATED=%.*s", (int) path_info->len, path_info->p); - } - -#if MG_ENABLE_SSL - mg_addenv(blk, "HTTPS=%s", (nc->flags & MG_F_SSL ? "on" : "off")); -#else - mg_addenv(blk, "HTTPS=off"); -#endif - - if ((h = mg_get_http_header((struct http_message *) hm, "Content-Type")) != - NULL) { - mg_addenv(blk, "CONTENT_TYPE=%.*s", (int) h->len, h->p); - } - - if (hm->query_string.len > 0) { - mg_addenv(blk, "QUERY_STRING=%.*s", (int) hm->query_string.len, - hm->query_string.p); - } - - if ((h = mg_get_http_header((struct http_message *) hm, "Content-Length")) != - NULL) { - mg_addenv(blk, "CONTENT_LENGTH=%.*s", (int) h->len, h->p); - } - - mg_addenv2(blk, "PATH"); - mg_addenv2(blk, "TMP"); - mg_addenv2(blk, "TEMP"); - mg_addenv2(blk, "TMPDIR"); - mg_addenv2(blk, "PERLLIB"); - mg_addenv2(blk, MG_ENV_EXPORT_TO_CGI); - -#ifdef _WIN32 - mg_addenv2(blk, "COMSPEC"); - mg_addenv2(blk, "SYSTEMROOT"); - mg_addenv2(blk, "SystemDrive"); - mg_addenv2(blk, "ProgramFiles"); - mg_addenv2(blk, "ProgramFiles(x86)"); - mg_addenv2(blk, "CommonProgramFiles(x86)"); -#else - mg_addenv2(blk, "LD_LIBRARY_PATH"); -#endif /* _WIN32 */ - - /* Add all headers as HTTP_* variables */ - for (i = 0; hm->header_names[i].len > 0; i++) { - p = mg_addenv(blk, "HTTP_%.*s=%.*s", (int) hm->header_names[i].len, - hm->header_names[i].p, (int) hm->header_values[i].len, - hm->header_values[i].p); - - /* Convert variable name into uppercase, and change - to _ */ - for (; *p != '=' && *p != '\0'; p++) { - if (*p == '-') *p = '_'; - *p = (char) toupper(*(unsigned char *) p); - } - } - - blk->vars[blk->nvars++] = NULL; - blk->buf[blk->len++] = '\0'; -} - -static void mg_cgi_ev_handler(struct mg_connection *cgi_nc, int ev, - void *ev_data MG_UD_ARG(void *user_data)) { -#if !MG_ENABLE_CALLBACK_USERDATA - void *user_data = cgi_nc->user_data; -#endif - struct mg_connection *nc = (struct mg_connection *) user_data; - (void) ev_data; - - if (nc == NULL) { - /* The corresponding network connection was closed. */ - cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY; - return; - } - - switch (ev) { - case MG_EV_RECV: - /* - * CGI script does not output reply line, like "HTTP/1.1 CODE XXXXX\n" - * It outputs headers, then body. Headers might include "Status" - * header, which changes CODE, and it might include "Location" header - * which changes CODE to 302. - * - * Therefore we do not send the output from the CGI script to the user - * until all CGI headers are received. - * - * Here we parse the output from the CGI script, and if all headers has - * been received, send appropriate reply line, and forward all - * received headers to the client. - */ - if (nc->flags & MG_F_HTTP_CGI_PARSE_HEADERS) { - struct mbuf *io = &cgi_nc->recv_mbuf; - int len = mg_http_get_request_len(io->buf, io->len); - - if (len == 0) break; - if (len < 0 || io->len > MG_MAX_HTTP_REQUEST_SIZE) { - cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY; - mg_http_send_error(nc, 500, "Bad headers"); - } else { - struct http_message hm; - struct mg_str *h; - mg_http_parse_headers(io->buf, io->buf + io->len, io->len, &hm); - if (mg_get_http_header(&hm, "Location") != NULL) { - mg_printf(nc, "%s", "HTTP/1.1 302 Moved\r\n"); - } else if ((h = mg_get_http_header(&hm, "Status")) != NULL) { - mg_printf(nc, "HTTP/1.1 %.*s\r\n", (int) h->len, h->p); - } else { - mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\n"); - } - } - nc->flags &= ~MG_F_HTTP_CGI_PARSE_HEADERS; - } - if (!(nc->flags & MG_F_HTTP_CGI_PARSE_HEADERS)) { - mg_forward(cgi_nc, nc); - } - break; - case MG_EV_CLOSE: - DBG(("%p CLOSE", cgi_nc)); - mg_http_free_proto_data_cgi(&mg_http_get_proto_data(nc)->cgi); - nc->flags |= MG_F_SEND_AND_CLOSE; - break; - } -} - -MG_INTERNAL void mg_handle_cgi(struct mg_connection *nc, const char *prog, - const struct mg_str *path_info, - const struct http_message *hm, - const struct mg_serve_http_opts *opts) { - struct mg_cgi_env_block blk; - char dir[MG_MAX_PATH]; - const char *p; - sock_t fds[2]; - - DBG(("%p [%s]", nc, prog)); - mg_prepare_cgi_environment(nc, prog, path_info, hm, opts, &blk); - /* - * CGI must be executed in its own directory. 'dir' must point to the - * directory containing executable program, 'p' must point to the - * executable program name relative to 'dir'. - */ - if ((p = strrchr(prog, DIRSEP)) == NULL) { - snprintf(dir, sizeof(dir), "%s", "."); - } else { - snprintf(dir, sizeof(dir), "%.*s", (int) (p - prog), prog); - prog = p + 1; - } - - if (!mg_socketpair(fds, SOCK_STREAM)) { - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - return; - } - -#ifndef _WIN32 - struct sigaction sa; - - sigemptyset(&sa.sa_mask); - sa.sa_handler = SIG_IGN; - sa.sa_flags = 0; - sigaction(SIGCHLD, &sa, NULL); -#endif - - if (mg_start_process(opts->cgi_interpreter, prog, blk.buf, blk.vars, dir, - fds[1]) != 0) { - size_t n = nc->recv_mbuf.len - (hm->message.len - hm->body.len); - struct mg_connection *cgi_nc = - mg_add_sock(nc->mgr, fds[0], mg_cgi_ev_handler MG_UD_ARG(nc)); - struct mg_http_proto_data *cgi_pd = mg_http_get_proto_data(nc); - cgi_pd->cgi.cgi_nc = cgi_nc; -#if !MG_ENABLE_CALLBACK_USERDATA - cgi_pd->cgi.cgi_nc->user_data = nc; -#endif - nc->flags |= MG_F_HTTP_CGI_PARSE_HEADERS; - /* Push POST data to the CGI */ - if (n > 0 && n < nc->recv_mbuf.len) { - mg_send(cgi_pd->cgi.cgi_nc, hm->body.p, n); - } - mbuf_remove(&nc->recv_mbuf, nc->recv_mbuf.len); - } else { - closesocket(fds[0]); - mg_http_send_error(nc, 500, "CGI failure"); - } - -#ifndef _WIN32 - closesocket(fds[1]); /* On Windows, CGI stdio thread closes that socket */ -#endif -} - -MG_INTERNAL void mg_http_free_proto_data_cgi(struct mg_http_proto_data_cgi *d) { - if (d == NULL) return; - if (d->cgi_nc != NULL) { - d->cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY; - d->cgi_nc->user_data = NULL; - } - memset(d, 0, sizeof(*d)); -} - -#endif /* MG_ENABLE_HTTP && MG_ENABLE_HTTP_CGI */ diff --git a/src/mongoose-6.11/src/mg_http_client.h b/src/mongoose-6.11/src/mg_http_client.h deleted file mode 100644 index 9597ec5..0000000 --- a/src/mongoose-6.11/src/mg_http_client.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * === Client API reference - */ - -#ifndef CS_MONGOOSE_SRC_HTTP_CLIENT_H_ -#define CS_MONGOOSE_SRC_HTTP_CLIENT_H_ - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* - * Helper function that creates an outbound HTTP connection. - * - * `url` is the URL to fetch. It must be properly URL-encoded, e.g. have - * no spaces, etc. By default, `mg_connect_http()` sends the Connection and - * Host headers. `extra_headers` is an extra HTTP header to send, e.g. - * `"User-Agent: my-app\r\n"`. - * If `post_data` is NULL, then a GET request is created. Otherwise, a POST - * request is created with the specified POST data. Note that if the data being - * posted is a form submission, the `Content-Type` header should be set - * accordingly (see example below). - * - * Examples: - * - * ```c - * nc1 = mg_connect_http(mgr, ev_handler_1, "http://www.google.com", NULL, - * NULL); - * nc2 = mg_connect_http(mgr, ev_handler_1, "https://github.com", NULL, NULL); - * nc3 = mg_connect_http( - * mgr, ev_handler_1, "my_server:8000/form_submit/", - * "Content-Type: application/x-www-form-urlencoded\r\n", - * "var_1=value_1&var_2=value_2"); - * ``` - */ -struct mg_connection *mg_connect_http( - struct mg_mgr *mgr, - MG_CB(mg_event_handler_t event_handler, void *user_data), const char *url, - const char *extra_headers, const char *post_data); - -/* - * Helper function that creates an outbound HTTP connection. - * - * Mostly identical to mg_connect_http, but allows you to provide extra - *parameters - * (for example, SSL parameters) - */ -struct mg_connection *mg_connect_http_opt( - struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), - struct mg_connect_opts opts, const char *url, const char *extra_headers, - const char *post_data); - -/* Creates digest authentication header for a client request. */ -int mg_http_create_digest_auth_header(char *buf, size_t buf_len, - const char *method, const char *uri, - const char *auth_domain, const char *user, - const char *passwd, const char *nonce); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* CS_MONGOOSE_SRC_HTTP_CLIENT_H_ */ diff --git a/src/mongoose-6.11/src/mg_http_server.h b/src/mongoose-6.11/src/mg_http_server.h deleted file mode 100644 index c4dfee4..0000000 --- a/src/mongoose-6.11/src/mg_http_server.h +++ /dev/null @@ -1,561 +0,0 @@ -/* - * === Server API reference - */ - -#ifndef CS_MONGOOSE_SRC_HTTP_SERVER_H_ -#define CS_MONGOOSE_SRC_HTTP_SERVER_H_ - -#if MG_ENABLE_HTTP - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* - * Parses a HTTP message. - * - * `is_req` should be set to 1 if parsing a request, 0 if reply. - * - * Returns the number of bytes parsed. If HTTP message is - * incomplete `0` is returned. On parse error, a negative number is returned. - */ -int mg_parse_http(const char *s, int n, struct http_message *hm, int is_req); - -/* - * Searches and returns the header `name` in parsed HTTP message `hm`. - * If header is not found, NULL is returned. Example: - * - * struct mg_str *host_hdr = mg_get_http_header(hm, "Host"); - */ -struct mg_str *mg_get_http_header(struct http_message *hm, const char *name); - -/* - * Parses the HTTP header `hdr`. Finds variable `var_name` and stores its value - * in the buffer `*buf`, `buf_size`. If the buffer size is not enough, - * allocates a buffer of required size and writes it to `*buf`, similar to - * asprintf(). The caller should always check whether the buffer was updated, - * and free it if so. - * - * This function is supposed to parse cookies, authentication headers, etc. - * Example (error handling omitted): - * - * char user_buf[20]; - * char *user = user_buf; - * struct mg_str *hdr = mg_get_http_header(hm, "Authorization"); - * mg_http_parse_header2(hdr, "username", &user, sizeof(user_buf)); - * // ... do something useful with user - * if (user != user_buf) { - * free(user); - * } - * - * Returns the length of the variable's value. If variable is not found, 0 is - * returned. - */ -int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf, - size_t buf_size); - -/* - * DEPRECATED: use mg_http_parse_header2() instead. - * - * Same as mg_http_parse_header2(), but takes buffer as a `char *` (instead of - * `char **`), and thus it cannot allocate a new buffer if the provided one - * is not enough, and just returns 0 in that case. - */ -int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf, - size_t buf_size) -#ifdef __GNUC__ - __attribute__((deprecated)); -#endif -; - -/* - * Gets and parses the Authorization: Basic header - * Returns -1 if no Authorization header is found, or if - * mg_parse_http_basic_auth - * fails parsing the resulting header. - */ -int mg_get_http_basic_auth(struct http_message *hm, char *user, size_t user_len, - char *pass, size_t pass_len); - -/* - * Parses the Authorization: Basic header - * Returns -1 iif the authorization type is not "Basic" or any other error such - * as incorrectly encoded base64 user password pair. - */ -int mg_parse_http_basic_auth(struct mg_str *hdr, char *user, size_t user_len, - char *pass, size_t pass_len); - -/* - * Parses the buffer `buf`, `buf_len` that contains multipart form data chunks. - * Stores the chunk name in a `var_name`, `var_name_len` buffer. - * If a chunk is an uploaded file, then `file_name`, `file_name_len` is - * filled with an uploaded file name. `chunk`, `chunk_len` - * points to the chunk data. - * - * Return: number of bytes to skip to the next chunk or 0 if there are - * no more chunks. - * - * Usage example: - * - * ```c - * static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { - * switch(ev) { - * case MG_EV_HTTP_REQUEST: { - * struct http_message *hm = (struct http_message *) ev_data; - * char var_name[100], file_name[100]; - * const char *chunk; - * size_t chunk_len, n1, n2; - * - * n1 = n2 = 0; - * while ((n2 = mg_parse_multipart(hm->body.p + n1, - * hm->body.len - n1, - * var_name, sizeof(var_name), - * file_name, sizeof(file_name), - * &chunk, &chunk_len)) > 0) { - * printf("var: %s, file_name: %s, size: %d, chunk: [%.*s]\n", - * var_name, file_name, (int) chunk_len, - * (int) chunk_len, chunk); - * n1 += n2; - * } - * } - * break; - * ``` - */ -size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name, - size_t var_name_len, char *file_name, - size_t file_name_len, const char **chunk, - size_t *chunk_len); - -/* - * Fetches a HTTP form variable. - * - * Fetches a variable `name` from a `buf` into a buffer specified by `dst`, - * `dst_len`. The destination is always zero-terminated. Returns the length of - * a fetched variable. If not found, 0 is returned. `buf` must be valid - * url-encoded buffer. If destination is too small or an error occured, - * negative number is returned. - */ -int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst, - size_t dst_len); - -#if MG_ENABLE_FILESYSTEM -/* - * This structure defines how `mg_serve_http()` works. - * Best practice is to set only required settings, and leave the rest as NULL. - */ -struct mg_serve_http_opts { - /* Path to web root directory */ - const char *document_root; - - /* List of index files. Default is "" */ - const char *index_files; - - /* - * Leave as NULL to disable authentication. - * To enable directory protection with authentication, set this to ".htpasswd" - * Then, creating ".htpasswd" file in any directory automatically protects - * it with digest authentication. - * Use `mongoose` web server binary, or `htdigest` Apache utility to - * create/manipulate passwords file. - * Make sure `auth_domain` is set to a valid domain name. - */ - const char *per_directory_auth_file; - - /* Authorization domain (domain name of this web server) */ - const char *auth_domain; - - /* - * Leave as NULL to disable authentication. - * Normally, only selected directories in the document root are protected. - * If absolutely every access to the web server needs to be authenticated, - * regardless of the URI, set this option to the path to the passwords file. - * Format of that file is the same as ".htpasswd" file. Make sure that file - * is located outside document root to prevent people fetching it. - */ - const char *global_auth_file; - - /* Set to "no" to disable directory listing. Enabled by default. */ - const char *enable_directory_listing; - - /* - * SSI files pattern. If not set, "**.shtml$|**.shtm$" is used. - * - * All files that match ssi_pattern are treated as SSI. - * - * Server Side Includes (SSI) is a simple interpreted server-side scripting - * language which is most commonly used to include the contents of a file - * into a web page. It can be useful when it is desirable to include a common - * piece of code throughout a website, for example, headers and footers. - * - * In order for a webpage to recognize an SSI-enabled HTML file, the - * filename should end with a special extension, by default the extension - * should be either .shtml or .shtm - * - * Unknown SSI directives are silently ignored by Mongoose. Currently, - * the following SSI directives are supported: - * <!--#include FILE_TO_INCLUDE --> - * <!--#exec "COMMAND_TO_EXECUTE" --> - * <!--#call COMMAND --> - * - * Note that <!--#include ...> directive supports three path - *specifications: - * - * <!--#include virtual="path" --> Path is relative to web server root - * <!--#include abspath="path" --> Path is absolute or relative to the - * web server working dir - * <!--#include file="path" -->, Path is relative to current document - * <!--#include "path" --> - * - * The include directive may be used to include the contents of a file or - * the result of running a CGI script. - * - * The exec directive is used to execute - * a command on a server, and show command's output. Example: - * - * <!--#exec "ls -l" --> - * - * The call directive is a way to invoke a C handler from the HTML page. - * On each occurence of <!--#call COMMAND OPTIONAL_PARAMS> directive, - * Mongoose calls a registered event handler with MG_EV_SSI_CALL event, - * and event parameter will point to the COMMAND OPTIONAL_PARAMS string. - * An event handler can output any text, for example by calling - * `mg_printf()`. This is a flexible way of generating a web page on - * server side by calling a C event handler. Example: - * - * <!--#call foo --> ... <!--#call bar --> - * - * In the event handler: - * case MG_EV_SSI_CALL: { - * const char *param = (const char *) ev_data; - * if (strcmp(param, "foo") == 0) { - * mg_printf(c, "hello from foo"); - * } else if (strcmp(param, "bar") == 0) { - * mg_printf(c, "hello from bar"); - * } - * break; - * } - */ - const char *ssi_pattern; - - /* IP ACL. By default, NULL, meaning all IPs are allowed to connect */ - const char *ip_acl; - -#if MG_ENABLE_HTTP_URL_REWRITES - /* URL rewrites. - * - * Comma-separated list of `uri_pattern=url_file_or_directory_path` rewrites. - * When HTTP request is received, Mongoose constructs a file name from the - * requested URI by combining `document_root` and the URI. However, if the - * rewrite option is used and `uri_pattern` matches requested URI, then - * `document_root` is ignored. Instead, `url_file_or_directory_path` is used, - * which should be a full path name or a path relative to the web server's - * current working directory. It can also be an URI (http:// or https://) - * in which case mongoose will behave as a reverse proxy for that destination. - * - * Note that `uri_pattern`, as all Mongoose patterns, is a prefix pattern. - * - * If uri_pattern starts with `@` symbol, then Mongoose compares it with the - * HOST header of the request. If they are equal, Mongoose sets document root - * to `file_or_directory_path`, implementing virtual hosts support. - * Example: `@foo.com=/document/root/for/foo.com` - * - * If `uri_pattern` starts with `%` symbol, then Mongoose compares it with - * the listening port. If they match, then Mongoose issues a 301 redirect. - * For example, to redirect all HTTP requests to the - * HTTPS port, do `%80=https://my.site.com`. Note that the request URI is - * automatically appended to the redirect location. - */ - const char *url_rewrites; -#endif - - /* DAV document root. If NULL, DAV requests are going to fail. */ - const char *dav_document_root; - - /* - * DAV passwords file. If NULL, DAV requests are going to fail. - * If passwords file is set to "-", then DAV auth is disabled. - */ - const char *dav_auth_file; - - /* Glob pattern for the files to hide. */ - const char *hidden_file_pattern; - - /* Set to non-NULL to enable CGI, e.g. **.cgi$|**.php$" */ - const char *cgi_file_pattern; - - /* If not NULL, ignore CGI script hashbang and use this interpreter */ - const char *cgi_interpreter; - - /* - * Comma-separated list of Content-Type overrides for path suffixes, e.g. - * ".txt=text/plain; charset=utf-8,.c=text/plain" - */ - const char *custom_mime_types; - - /* - * Extra HTTP headers to add to each server response. - * Example: to enable CORS, set this to "Access-Control-Allow-Origin: *". - */ - const char *extra_headers; -}; - -/* - * Serves given HTTP request according to the `options`. - * - * Example code snippet: - * - * ```c - * static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { - * struct http_message *hm = (struct http_message *) ev_data; - * struct mg_serve_http_opts opts = { .document_root = "/var/www" }; // C99 - * - * switch (ev) { - * case MG_EV_HTTP_REQUEST: - * mg_serve_http(nc, hm, opts); - * break; - * default: - * break; - * } - * } - * ``` - */ -void mg_serve_http(struct mg_connection *nc, struct http_message *hm, - struct mg_serve_http_opts opts); - -/* - * Serves a specific file with a given MIME type and optional extra headers. - * - * Example code snippet: - * - * ```c - * static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { - * switch (ev) { - * case MG_EV_HTTP_REQUEST: { - * struct http_message *hm = (struct http_message *) ev_data; - * mg_http_serve_file(nc, hm, "file.txt", - * mg_mk_str("text/plain"), mg_mk_str("")); - * break; - * } - * ... - * } - * } - * ``` - */ -void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm, - const char *path, const struct mg_str mime_type, - const struct mg_str extra_headers); - -#if MG_ENABLE_HTTP_STREAMING_MULTIPART - -/* Callback prototype for `mg_file_upload_handler()`. */ -typedef struct mg_str (*mg_fu_fname_fn)(struct mg_connection *nc, - struct mg_str fname); - -/* - * File upload handler. - * This handler can be used to implement file uploads with minimum code. - * This handler will process MG_EV_HTTP_PART_* events and store file data into - * a local file. - * `local_name_fn` will be invoked with whatever name was provided by the client - * and will expect the name of the local file to open. A return value of NULL - * will abort file upload (client will get a "403 Forbidden" response). If - * non-null, the returned string must be heap-allocated and will be freed by - * the caller. - * Exception: it is ok to return the same string verbatim. - * - * Example: - * - * ```c - * struct mg_str upload_fname(struct mg_connection *nc, struct mg_str fname) { - * // Just return the same filename. Do not actually do this except in test! - * // fname is user-controlled and needs to be sanitized. - * return fname; - * } - * void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { - * switch (ev) { - * ... - * case MG_EV_HTTP_PART_BEGIN: - * case MG_EV_HTTP_PART_DATA: - * case MG_EV_HTTP_PART_END: - * mg_file_upload_handler(nc, ev, ev_data, upload_fname); - * break; - * } - * } - * ``` - */ -void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, - mg_fu_fname_fn local_name_fn - MG_UD_ARG(void *user_data)); -#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ -#endif /* MG_ENABLE_FILESYSTEM */ - -/* - * Registers a callback for a specified http endpoint - * Note: if callback is registered it is called instead of the - * callback provided in mg_bind - * - * Example code snippet: - * - * ```c - * static void handle_hello1(struct mg_connection *nc, int ev, void *ev_data) { - * (void) ev; (void) ev_data; - * mg_printf(nc, "HTTP/1.0 200 OK\r\n\r\n[I am Hello1]"); - * nc->flags |= MG_F_SEND_AND_CLOSE; - * } - * - * static void handle_hello2(struct mg_connection *nc, int ev, void *ev_data) { - * (void) ev; (void) ev_data; - * mg_printf(nc, "HTTP/1.0 200 OK\r\n\r\n[I am Hello2]"); - * nc->flags |= MG_F_SEND_AND_CLOSE; - * } - * - * void init() { - * nc = mg_bind(&mgr, local_addr, cb1); - * mg_register_http_endpoint(nc, "/hello1", handle_hello1); - * mg_register_http_endpoint(nc, "/hello1/hello2", handle_hello2); - * } - * ``` - */ -void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path, - MG_CB(mg_event_handler_t handler, - void *user_data)); - -struct mg_http_endpoint_opts { - void *user_data; - /* Authorization domain (realm) */ - const char *auth_domain; - const char *auth_file; -}; - -void mg_register_http_endpoint_opt(struct mg_connection *nc, - const char *uri_path, - mg_event_handler_t handler, - struct mg_http_endpoint_opts opts); - -/* - * Authenticates a HTTP request against an opened password file. - * Returns 1 if authenticated, 0 otherwise. - */ -int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain, - FILE *fp); - -/* - * Authenticates given response params against an opened password file. - * Returns 1 if authenticated, 0 otherwise. - * - * It's used by mg_http_check_digest_auth(). - */ -int mg_check_digest_auth(struct mg_str method, struct mg_str uri, - struct mg_str username, struct mg_str cnonce, - struct mg_str response, struct mg_str qop, - struct mg_str nc, struct mg_str nonce, - struct mg_str auth_domain, FILE *fp); - -/* - * Sends buffer `buf` of size `len` to the client using chunked HTTP encoding. - * This function sends the buffer size as hex number + newline first, then - * the buffer itself, then the newline. For example, - * `mg_send_http_chunk(nc, "foo", 3)` will append the `3\r\nfoo\r\n` string - * to the `nc->send_mbuf` output IO buffer. - * - * NOTE: The HTTP header "Transfer-Encoding: chunked" should be sent prior to - * using this function. - * - * NOTE: do not forget to send an empty chunk at the end of the response, - * to tell the client that everything was sent. Example: - * - * ``` - * mg_printf_http_chunk(nc, "%s", "my response!"); - * mg_send_http_chunk(nc, "", 0); // Tell the client we're finished - * ``` - */ -void mg_send_http_chunk(struct mg_connection *nc, const char *buf, size_t len); - -/* - * Sends a printf-formatted HTTP chunk. - * Functionality is similar to `mg_send_http_chunk()`. - */ -void mg_printf_http_chunk(struct mg_connection *nc, const char *fmt, ...); - -/* - * Sends the response status line. - * If `extra_headers` is not NULL, then `extra_headers` are also sent - * after the response line. `extra_headers` must NOT end end with new line. - * Example: - * - * mg_send_response_line(nc, 200, "Access-Control-Allow-Origin: *"); - * - * Will result in: - * - * HTTP/1.1 200 OK\r\n - * Access-Control-Allow-Origin: *\r\n - */ -void mg_send_response_line(struct mg_connection *nc, int status_code, - const char *extra_headers); - -/* - * Sends an error response. If reason is NULL, the message will be inferred - * from the error code (if supported). - */ -void mg_http_send_error(struct mg_connection *nc, int code, const char *reason); - -/* - * Sends a redirect response. - * `status_code` should be either 301 or 302 and `location` point to the - * new location. - * If `extra_headers` is not empty, then `extra_headers` are also sent - * after the response line. `extra_headers` must NOT end end with new line. - * - * Example: - * - * mg_http_send_redirect(nc, 302, mg_mk_str("/login"), mg_mk_str(NULL)); - */ -void mg_http_send_redirect(struct mg_connection *nc, int status_code, - const struct mg_str location, - const struct mg_str extra_headers); - -/* - * Sends the response line and headers. - * This function sends the response line with the `status_code`, and - * automatically - * sends one header: either "Content-Length" or "Transfer-Encoding". - * If `content_length` is negative, then "Transfer-Encoding: chunked" header - * is sent, otherwise, "Content-Length" header is sent. - * - * NOTE: If `Transfer-Encoding` is `chunked`, then message body must be sent - * using `mg_send_http_chunk()` or `mg_printf_http_chunk()` functions. - * Otherwise, `mg_send()` or `mg_printf()` must be used. - * Extra headers could be set through `extra_headers`. Note `extra_headers` - * must NOT be terminated by a new line. - */ -void mg_send_head(struct mg_connection *n, int status_code, - int64_t content_length, const char *extra_headers); - -/* - * Sends a printf-formatted HTTP chunk, escaping HTML tags. - */ -void mg_printf_html_escape(struct mg_connection *nc, const char *fmt, ...); - -#if MG_ENABLE_HTTP_URL_REWRITES -/* - * Proxies a given request to a given upstream http server. The path prefix - * in `mount` will be stripped of the path requested to the upstream server, - * e.g. if mount is /api and upstream is http://localhost:8001/foo - * then an incoming request to /api/bar will cause a request to - * http://localhost:8001/foo/bar - * - * EXPERIMENTAL API. Please use http_serve_http + url_rewrites if a static - * mapping is good enough. - */ -void mg_http_reverse_proxy(struct mg_connection *nc, - const struct http_message *hm, struct mg_str mount, - struct mg_str upstream); -#endif - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* MG_ENABLE_HTTP */ - -#endif /* CS_MONGOOSE_SRC_HTTP_SERVER_H_ */ diff --git a/src/mongoose-6.11/src/mg_http_ssi.c b/src/mongoose-6.11/src/mg_http_ssi.c deleted file mode 100644 index 6d384db..0000000 --- a/src/mongoose-6.11/src/mg_http_ssi.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#if MG_ENABLE_HTTP && MG_ENABLE_HTTP_SSI && MG_ENABLE_FILESYSTEM - -static void mg_send_ssi_file(struct mg_connection *nc, struct http_message *hm, - const char *path, FILE *fp, int include_level, - const struct mg_serve_http_opts *opts); - -static void mg_send_file_data(struct mg_connection *nc, FILE *fp) { - char buf[BUFSIZ]; - size_t n; - while ((n = mg_fread(buf, 1, sizeof(buf), fp)) > 0) { - mg_send(nc, buf, n); - } -} - -static void mg_do_ssi_include(struct mg_connection *nc, struct http_message *hm, - const char *ssi, char *tag, int include_level, - const struct mg_serve_http_opts *opts) { - char file_name[MG_MAX_PATH], path[MG_MAX_PATH], *p; - FILE *fp; - - /* - * sscanf() is safe here, since send_ssi_file() also uses buffer - * of size MG_BUF_LEN to get the tag. So strlen(tag) is always < MG_BUF_LEN. - */ - if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) { - /* File name is relative to the webserver root */ - snprintf(path, sizeof(path), "%s/%s", opts->document_root, file_name); - } else if (sscanf(tag, " abspath=\"%[^\"]\"", file_name) == 1) { - /* - * File name is relative to the webserver working directory - * or it is absolute system path - */ - snprintf(path, sizeof(path), "%s", file_name); - } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1 || - sscanf(tag, " \"%[^\"]\"", file_name) == 1) { - /* File name is relative to the currect document */ - snprintf(path, sizeof(path), "%s", ssi); - if ((p = strrchr(path, DIRSEP)) != NULL) { - p[1] = '\0'; - } - snprintf(path + strlen(path), sizeof(path) - strlen(path), "%s", file_name); - } else { - mg_printf(nc, "Bad SSI #include: [%s]", tag); - return; - } - - if ((fp = mg_fopen(path, "rb")) == NULL) { - mg_printf(nc, "SSI include error: mg_fopen(%s): %s", path, - strerror(mg_get_errno())); - } else { - mg_set_close_on_exec((sock_t) fileno(fp)); - if (mg_match_prefix(opts->ssi_pattern, strlen(opts->ssi_pattern), path) > - 0) { - mg_send_ssi_file(nc, hm, path, fp, include_level + 1, opts); - } else { - mg_send_file_data(nc, fp); - } - fclose(fp); - } -} - -#if MG_ENABLE_HTTP_SSI_EXEC -static void do_ssi_exec(struct mg_connection *nc, char *tag) { - char cmd[BUFSIZ]; - FILE *fp; - - if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) { - mg_printf(nc, "Bad SSI #exec: [%s]", tag); - } else if ((fp = popen(cmd, "r")) == NULL) { - mg_printf(nc, "Cannot SSI #exec: [%s]: %s", cmd, strerror(mg_get_errno())); - } else { - mg_send_file_data(nc, fp); - pclose(fp); - } -} -#endif /* MG_ENABLE_HTTP_SSI_EXEC */ - -/* - * SSI directive has the following format: - * - */ -static void mg_send_ssi_file(struct mg_connection *nc, struct http_message *hm, - const char *path, FILE *fp, int include_level, - const struct mg_serve_http_opts *opts) { - static const struct mg_str btag = MG_MK_STR(" */ - buf[i--] = '\0'; - while (i > 0 && buf[i] == ' ') { - buf[i--] = '\0'; - } - - /* Handle known SSI directives */ - if (strncmp(p, d_include.p, d_include.len) == 0) { - mg_do_ssi_include(nc, hm, path, p + d_include.len + 1, include_level, - opts); - } else if (strncmp(p, d_call.p, d_call.len) == 0) { - struct mg_ssi_call_ctx cctx; - memset(&cctx, 0, sizeof(cctx)); - cctx.req = hm; - cctx.file = mg_mk_str(path); - cctx.arg = mg_mk_str(p + d_call.len + 1); - mg_call(nc, NULL, nc->user_data, MG_EV_SSI_CALL, - (void *) cctx.arg.p); /* NUL added above */ - mg_call(nc, NULL, nc->user_data, MG_EV_SSI_CALL_CTX, &cctx); -#if MG_ENABLE_HTTP_SSI_EXEC - } else if (strncmp(p, d_exec.p, d_exec.len) == 0) { - do_ssi_exec(nc, p + d_exec.len + 1); -#endif - } else { - /* Silently ignore unknown SSI directive. */ - } - len = 0; - } else if (ch == '<') { - in_ssi_tag = 1; - if (len > 0) { - mg_send(nc, buf, (size_t) len); - } - len = 0; - buf[len++] = ch & 0xff; - } else if (in_ssi_tag) { - if (len == (int) btag.len && strncmp(buf, btag.p, btag.len) != 0) { - /* Not an SSI tag */ - in_ssi_tag = 0; - } else if (len == (int) sizeof(buf) - 2) { - mg_printf(nc, "%s: SSI tag is too large", path); - len = 0; - } - buf[len++] = ch & 0xff; - } else { - buf[len++] = ch & 0xff; - if (len == (int) sizeof(buf)) { - mg_send(nc, buf, (size_t) len); - len = 0; - } - } - } - - /* Send the rest of buffered data */ - if (len > 0) { - mg_send(nc, buf, (size_t) len); - } -} - -MG_INTERNAL void mg_handle_ssi_request(struct mg_connection *nc, - struct http_message *hm, - const char *path, - const struct mg_serve_http_opts *opts) { - FILE *fp; - struct mg_str mime_type; - DBG(("%p %s", nc, path)); - - if ((fp = mg_fopen(path, "rb")) == NULL) { - mg_http_send_error(nc, 404, NULL); - } else { - mg_set_close_on_exec((sock_t) fileno(fp)); - - mime_type = mg_get_mime_type(path, "text/plain", opts); - mg_send_response_line(nc, 200, opts->extra_headers); - mg_printf(nc, - "Content-Type: %.*s\r\n" - "Connection: close\r\n\r\n", - (int) mime_type.len, mime_type.p); - mg_send_ssi_file(nc, hm, path, fp, 0, opts); - fclose(fp); - nc->flags |= MG_F_SEND_AND_CLOSE; - } -} - -#endif /* MG_ENABLE_HTTP_SSI && MG_ENABLE_HTTP && MG_ENABLE_FILESYSTEM */ diff --git a/src/mongoose-6.11/src/mg_http_webdav.c b/src/mongoose-6.11/src/mg_http_webdav.c deleted file mode 100644 index 6efb928..0000000 --- a/src/mongoose-6.11/src/mg_http_webdav.c +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#if MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBDAV - -MG_INTERNAL int mg_is_dav_request(const struct mg_str *s) { - static const char *methods[] = { - "PUT", - "DELETE", - "MKCOL", - "PROPFIND", - "MOVE" -#if MG_ENABLE_FAKE_DAVLOCK - , - "LOCK", - "UNLOCK" -#endif - }; - size_t i; - - for (i = 0; i < ARRAY_SIZE(methods); i++) { - if (mg_vcmp(s, methods[i]) == 0) { - return 1; - } - } - - return 0; -} - -static int mg_mkdir(const char *path, uint32_t mode) { -#ifndef _WIN32 - return mkdir(path, mode); -#else - (void) mode; - return _mkdir(path); -#endif -} - -static void mg_print_props(struct mg_connection *nc, const char *name, - cs_stat_t *stp) { - char mtime[64]; - time_t t = stp->st_mtime; /* store in local variable for NDK compile */ - struct mg_str name_esc = mg_url_encode(mg_mk_str(name)); - mg_gmt_time_string(mtime, sizeof(mtime), &t); - mg_printf(nc, - "" - "%s" - "" - "" - "%s" - "%" INT64_FMT - "" - "%s" - "" - "HTTP/1.1 200 OK" - "" - "\n", - name_esc.p, S_ISDIR(stp->st_mode) ? "" : "", - (int64_t) stp->st_size, mtime); - free((void *) name_esc.p); -} - -MG_INTERNAL void mg_handle_propfind(struct mg_connection *nc, const char *path, - cs_stat_t *stp, struct http_message *hm, - struct mg_serve_http_opts *opts) { - static const char header[] = - "HTTP/1.1 207 Multi-Status\r\n" - "Connection: close\r\n" - "Content-Type: text/xml; charset=utf-8\r\n\r\n" - "" - "\n"; - static const char footer[] = "\n"; - const struct mg_str *depth = mg_get_http_header(hm, "Depth"); - - /* Print properties for the requested resource itself */ - if (S_ISDIR(stp->st_mode) && - strcmp(opts->enable_directory_listing, "yes") != 0) { - mg_printf(nc, "%s", "HTTP/1.1 403 Directory Listing Denied\r\n\r\n"); - } else { - char uri[MG_MAX_PATH]; - mg_send(nc, header, sizeof(header) - 1); - snprintf(uri, sizeof(uri), "%.*s", (int) hm->uri.len, hm->uri.p); - mg_print_props(nc, uri, stp); - if (S_ISDIR(stp->st_mode) && (depth == NULL || mg_vcmp(depth, "0") != 0)) { - mg_scan_directory(nc, path, opts, mg_print_props); - } - mg_send(nc, footer, sizeof(footer) - 1); - nc->flags |= MG_F_SEND_AND_CLOSE; - } -} - -#if MG_ENABLE_FAKE_DAVLOCK -/* - * Windows explorer (probably there are another WebDav clients like it) - * requires LOCK support in webdav. W/out this, it still works, but fails - * to save file: shows error message and offers "Save As". - * "Save as" works, but this message is very annoying. - * This is fake lock, which doesn't lock something, just returns LOCK token, - * UNLOCK always answers "OK". - * With this fake LOCK Windows Explorer looks happy and saves file. - * NOTE: that is not DAV LOCK imlementation, it is just a way to shut up - * Windows native DAV client. This is why FAKE LOCK is not enabed by default - */ -MG_INTERNAL void mg_handle_lock(struct mg_connection *nc, const char *path) { - static const char *reply = - "HTTP/1.1 207 Multi-Status\r\n" - "Connection: close\r\n" - "Content-Type: text/xml; charset=utf-8\r\n\r\n" - "" - "\n" - "\n" - "\n" - "\n" - "\n" - "opaquelocktoken:%s%u" - "" - "" - "\n" - "" - "\n"; - mg_printf(nc, reply, path, (unsigned int) mg_time()); - nc->flags |= MG_F_SEND_AND_CLOSE; -} -#endif - -MG_INTERNAL void mg_handle_mkcol(struct mg_connection *nc, const char *path, - struct http_message *hm) { - int status_code = 500; - if (hm->body.len != (size_t) ~0 && hm->body.len > 0) { - status_code = 415; - } else if (!mg_mkdir(path, 0755)) { - status_code = 201; - } else if (errno == EEXIST) { - status_code = 405; - } else if (errno == EACCES) { - status_code = 403; - } else if (errno == ENOENT) { - status_code = 409; - } else { - status_code = 500; - } - mg_http_send_error(nc, status_code, NULL); -} - -static int mg_remove_directory(const struct mg_serve_http_opts *opts, - const char *dir) { - char path[MG_MAX_PATH]; - struct dirent *dp; - cs_stat_t st; - DIR *dirp; - - if ((dirp = opendir(dir)) == NULL) return 0; - - while ((dp = readdir(dirp)) != NULL) { - if (mg_is_file_hidden((const char *) dp->d_name, opts, 1)) { - continue; - } - snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name); - mg_stat(path, &st); - if (S_ISDIR(st.st_mode)) { - mg_remove_directory(opts, path); - } else { - remove(path); - } - } - closedir(dirp); - rmdir(dir); - - return 1; -} - -MG_INTERNAL void mg_handle_move(struct mg_connection *c, - const struct mg_serve_http_opts *opts, - const char *path, struct http_message *hm) { - const struct mg_str *dest = mg_get_http_header(hm, "Destination"); - if (dest == NULL) { - mg_http_send_error(c, 411, NULL); - } else { - const char *p = (char *) memchr(dest->p, '/', dest->len); - if (p != NULL && p[1] == '/' && - (p = (char *) memchr(p + 2, '/', dest->p + dest->len - p)) != NULL) { - char buf[MG_MAX_PATH]; - snprintf(buf, sizeof(buf), "%s%.*s", opts->dav_document_root, - (int) (dest->p + dest->len - p), p); - if (rename(path, buf) == 0) { - mg_http_send_error(c, 200, NULL); - } else { - mg_http_send_error(c, 418, NULL); - } - } else { - mg_http_send_error(c, 500, NULL); - } - } -} - -MG_INTERNAL void mg_handle_delete(struct mg_connection *nc, - const struct mg_serve_http_opts *opts, - const char *path) { - cs_stat_t st; - if (mg_stat(path, &st) != 0) { - mg_http_send_error(nc, 404, NULL); - } else if (S_ISDIR(st.st_mode)) { - mg_remove_directory(opts, path); - mg_http_send_error(nc, 204, NULL); - } else if (remove(path) == 0) { - mg_http_send_error(nc, 204, NULL); - } else { - mg_http_send_error(nc, 423, NULL); - } -} - -/* Return -1 on error, 1 on success. */ -static int mg_create_itermediate_directories(const char *path) { - const char *s; - - /* Create intermediate directories if they do not exist */ - for (s = path + 1; *s != '\0'; s++) { - if (*s == '/') { - char buf[MG_MAX_PATH]; - cs_stat_t st; - snprintf(buf, sizeof(buf), "%.*s", (int) (s - path), path); - buf[sizeof(buf) - 1] = '\0'; - if (mg_stat(buf, &st) != 0 && mg_mkdir(buf, 0755) != 0) { - return -1; - } - } - } - - return 1; -} - -MG_INTERNAL void mg_handle_put(struct mg_connection *nc, const char *path, - struct http_message *hm) { - struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); - cs_stat_t st; - const struct mg_str *cl_hdr = mg_get_http_header(hm, "Content-Length"); - int rc, status_code = mg_stat(path, &st) == 0 ? 200 : 201; - - mg_http_free_proto_data_file(&pd->file); - if ((rc = mg_create_itermediate_directories(path)) == 0) { - mg_printf(nc, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n", status_code); - } else if (rc == -1) { - mg_http_send_error(nc, 500, NULL); - } else if (cl_hdr == NULL) { - mg_http_send_error(nc, 411, NULL); - } else if ((pd->file.fp = mg_fopen(path, "w+b")) == NULL) { - mg_http_send_error(nc, 500, NULL); - } else { - const struct mg_str *range_hdr = mg_get_http_header(hm, "Content-Range"); - int64_t r1 = 0, r2 = 0; - pd->file.type = DATA_PUT; - mg_set_close_on_exec((sock_t) fileno(pd->file.fp)); - pd->file.cl = to64(cl_hdr->p); - if (range_hdr != NULL && - mg_http_parse_range_header(range_hdr, &r1, &r2) > 0) { - status_code = 206; - fseeko(pd->file.fp, r1, SEEK_SET); - pd->file.cl = r2 > r1 ? r2 - r1 + 1 : pd->file.cl - r1; - } - mg_printf(nc, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n", status_code); - /* Remove HTTP request from the mbuf, leave only payload */ - mbuf_remove(&nc->recv_mbuf, hm->message.len - hm->body.len); - mg_http_transfer_file_data(nc); - } -} - -#endif /* MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBDAV */ diff --git a/src/mongoose-6.11/src/mg_http_websocket.c b/src/mongoose-6.11/src/mg_http_websocket.c deleted file mode 100644 index e69e4b7..0000000 --- a/src/mongoose-6.11/src/mg_http_websocket.c +++ /dev/null @@ -1,517 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -#if MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBSOCKET - -#include "common/cs_sha1.h" - -#ifndef MG_WEBSOCKET_PING_INTERVAL_SECONDS -#define MG_WEBSOCKET_PING_INTERVAL_SECONDS 5 -#endif - -#define FLAGS_MASK_FIN (1 << 7) -#define FLAGS_MASK_OP 0x0f - -static int mg_is_ws_fragment(unsigned char flags) { - return (flags & FLAGS_MASK_FIN) == 0 || - (flags & FLAGS_MASK_OP) == WEBSOCKET_OP_CONTINUE; -} - -static int mg_is_ws_first_fragment(unsigned char flags) { - return (flags & FLAGS_MASK_FIN) == 0 && - (flags & FLAGS_MASK_OP) != WEBSOCKET_OP_CONTINUE; -} - -static int mg_is_ws_control_frame(unsigned char flags) { - unsigned char op = (flags & FLAGS_MASK_OP); - return op == WEBSOCKET_OP_CLOSE || op == WEBSOCKET_OP_PING || - op == WEBSOCKET_OP_PONG; -} - -static void mg_handle_incoming_websocket_frame(struct mg_connection *nc, - struct websocket_message *wsm) { - if (wsm->flags & 0x8) { - mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_CONTROL_FRAME, wsm); - } else { - mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_FRAME, wsm); - } -} - -static struct mg_ws_proto_data *mg_ws_get_proto_data(struct mg_connection *nc) { - struct mg_http_proto_data *htd = mg_http_get_proto_data(nc); - return (htd != NULL ? &htd->ws_data : NULL); -} - -/* - * Sends a Close websocket frame with the given data, and closes the underlying - * connection. If `len` is ~0, strlen(data) is used. - */ -static void mg_ws_close(struct mg_connection *nc, const void *data, - size_t len) { - if ((int) len == ~0) { - len = strlen((const char *) data); - } - mg_send_websocket_frame(nc, WEBSOCKET_OP_CLOSE, data, len); - nc->flags |= MG_F_SEND_AND_CLOSE; -} - -static int mg_deliver_websocket_data(struct mg_connection *nc) { - /* Using unsigned char *, cause of integer arithmetic below */ - uint64_t i, data_len = 0, frame_len = 0, new_data_len = nc->recv_mbuf.len, - len, mask_len = 0, header_len = 0; - struct mg_ws_proto_data *wsd = mg_ws_get_proto_data(nc); - unsigned char *new_data = (unsigned char *) nc->recv_mbuf.buf, - *e = (unsigned char *) nc->recv_mbuf.buf + nc->recv_mbuf.len; - uint8_t flags; - int ok, reass; - - if (wsd->reass_len > 0) { - /* - * We already have some previously received data which we need to - * reassemble and deliver to the client code when we get the final - * fragment. - * - * NOTE: it doesn't mean that the current message must be a continuation: - * it might be a control frame (Close, Ping or Pong), which should be - * handled without breaking the fragmented message. - */ - - size_t existing_len = wsd->reass_len; - assert(new_data_len >= existing_len); - - new_data += existing_len; - new_data_len -= existing_len; - } - - flags = new_data[0]; - - reass = new_data_len > 0 && mg_is_ws_fragment(flags) && - !(nc->flags & MG_F_WEBSOCKET_NO_DEFRAG); - - if (reass && mg_is_ws_control_frame(flags)) { - /* - * Control frames can't be fragmented, so if we encounter fragmented - * control frame, close connection immediately. - */ - mg_ws_close(nc, "fragmented control frames are illegal", ~0); - return 0; - } else if (new_data_len > 0 && !reass && !mg_is_ws_control_frame(flags) && - wsd->reass_len > 0) { - /* - * When in the middle of a fragmented message, only the continuations - * and control frames are allowed. - */ - mg_ws_close(nc, "non-continuation in the middle of a fragmented message", - ~0); - return 0; - } - - if (new_data_len >= 2) { - len = new_data[1] & 0x7f; - mask_len = new_data[1] & FLAGS_MASK_FIN ? 4 : 0; - if (len < 126 && new_data_len >= mask_len) { - data_len = len; - header_len = 2 + mask_len; - } else if (len == 126 && new_data_len >= 4 + mask_len) { - header_len = 4 + mask_len; - data_len = ntohs(*(uint16_t *) &new_data[2]); - } else if (new_data_len >= 10 + mask_len) { - header_len = 10 + mask_len; - data_len = (((uint64_t) ntohl(*(uint32_t *) &new_data[2])) << 32) + - ntohl(*(uint32_t *) &new_data[6]); - } - } - - frame_len = header_len + data_len; - ok = (frame_len > 0 && frame_len <= new_data_len); - - /* Check for overflow */ - if (frame_len < header_len || frame_len < data_len) { - ok = 0; - mg_ws_close(nc, "overflowed message", ~0); - } - - if (ok) { - size_t cleanup_len = 0; - struct websocket_message wsm; - - wsm.size = (size_t) data_len; - wsm.data = new_data + header_len; - wsm.flags = flags; - - /* Apply mask if necessary */ - if (mask_len > 0) { - for (i = 0; i < data_len; i++) { - new_data[i + header_len] ^= (new_data + header_len - mask_len)[i % 4]; - } - } - - if (reass) { - /* This is a message fragment */ - - if (mg_is_ws_first_fragment(flags)) { - /* - * On the first fragmented frame, skip the first byte (op) and also - * reset size to 1 (op), it'll be incremented with the data len below. - */ - new_data += 1; - wsd->reass_len = 1 /* op */; - } - - /* Append this frame to the reassembled buffer */ - memmove(new_data, wsm.data, e - wsm.data); - wsd->reass_len += wsm.size; - nc->recv_mbuf.len -= wsm.data - new_data; - - if (flags & FLAGS_MASK_FIN) { - /* On last fragmented frame - call user handler and remove data */ - wsm.flags = FLAGS_MASK_FIN | nc->recv_mbuf.buf[0]; - wsm.data = (unsigned char *) nc->recv_mbuf.buf + 1 /* op */; - wsm.size = wsd->reass_len - 1 /* op */; - cleanup_len = wsd->reass_len; - wsd->reass_len = 0; - - /* Pass reassembled message to the client code. */ - mg_handle_incoming_websocket_frame(nc, &wsm); - mbuf_remove(&nc->recv_mbuf, cleanup_len); /* Cleanup frame */ - } - } else { - /* - * This is a complete message, not a fragment. It might happen in between - * of a fragmented message (in this case, WebSocket protocol requires - * current message to be a control frame). - */ - cleanup_len = (size_t) frame_len; - - /* First of all, check if we need to react on a control frame. */ - switch (flags & FLAGS_MASK_OP) { - case WEBSOCKET_OP_PING: - mg_send_websocket_frame(nc, WEBSOCKET_OP_PONG, wsm.data, wsm.size); - break; - - case WEBSOCKET_OP_CLOSE: - mg_ws_close(nc, wsm.data, wsm.size); - break; - } - - /* Pass received message to the client code. */ - mg_handle_incoming_websocket_frame(nc, &wsm); - - /* Cleanup frame */ - memmove(nc->recv_mbuf.buf + wsd->reass_len, - nc->recv_mbuf.buf + wsd->reass_len + cleanup_len, - nc->recv_mbuf.len - wsd->reass_len - cleanup_len); - nc->recv_mbuf.len -= cleanup_len; - } - } - - return ok; -} - -struct ws_mask_ctx { - size_t pos; /* zero means unmasked */ - uint32_t mask; -}; - -static uint32_t mg_ws_random_mask(void) { - uint32_t mask; -/* - * The spec requires WS client to generate hard to - * guess mask keys. From RFC6455, Section 5.3: - * - * The unpredictability of the masking key is essential to prevent - * authors of malicious applications from selecting the bytes that appear on - * the wire. - * - * Hence this feature is essential when the actual end user of this API - * is untrusted code that wouldn't have access to a lower level net API - * anyway (e.g. web browsers). Hence this feature is low prio for most - * mongoose use cases and thus can be disabled, e.g. when porting to a platform - * that lacks rand(). - */ -#if MG_DISABLE_WS_RANDOM_MASK - mask = 0xefbeadde; /* generated with a random number generator, I swear */ -#else - if (sizeof(long) >= 4) { - mask = (uint32_t) rand(); - } else if (sizeof(long) == 2) { - mask = (uint32_t) rand() << 16 | (uint32_t) rand(); - } -#endif - return mask; -} - -static void mg_send_ws_header(struct mg_connection *nc, int op, size_t len, - struct ws_mask_ctx *ctx) { - int header_len; - unsigned char header[10]; - - header[0] = - (op & WEBSOCKET_DONT_FIN ? 0x0 : FLAGS_MASK_FIN) | (op & FLAGS_MASK_OP); - if (len < 126) { - header[1] = (unsigned char) len; - header_len = 2; - } else if (len < 65535) { - uint16_t tmp = htons((uint16_t) len); - header[1] = 126; - memcpy(&header[2], &tmp, sizeof(tmp)); - header_len = 4; - } else { - uint32_t tmp; - header[1] = 127; - tmp = htonl((uint32_t)((uint64_t) len >> 32)); - memcpy(&header[2], &tmp, sizeof(tmp)); - tmp = htonl((uint32_t)(len & 0xffffffff)); - memcpy(&header[6], &tmp, sizeof(tmp)); - header_len = 10; - } - - /* client connections enable masking */ - if (nc->listener == NULL) { - header[1] |= 1 << 7; /* set masking flag */ - mg_send(nc, header, header_len); - ctx->mask = mg_ws_random_mask(); - mg_send(nc, &ctx->mask, sizeof(ctx->mask)); - ctx->pos = nc->send_mbuf.len; - } else { - mg_send(nc, header, header_len); - ctx->pos = 0; - } -} - -static void mg_ws_mask_frame(struct mbuf *mbuf, struct ws_mask_ctx *ctx) { - size_t i; - if (ctx->pos == 0) return; - for (i = 0; i < (mbuf->len - ctx->pos); i++) { - mbuf->buf[ctx->pos + i] ^= ((char *) &ctx->mask)[i % 4]; - } -} - -void mg_send_websocket_frame(struct mg_connection *nc, int op, const void *data, - size_t len) { - struct ws_mask_ctx ctx; - DBG(("%p %d %d", nc, op, (int) len)); - mg_send_ws_header(nc, op, len, &ctx); - mg_send(nc, data, len); - - mg_ws_mask_frame(&nc->send_mbuf, &ctx); - - if (op == WEBSOCKET_OP_CLOSE) { - nc->flags |= MG_F_SEND_AND_CLOSE; - } -} - -void mg_send_websocket_framev(struct mg_connection *nc, int op, - const struct mg_str *strv, int strvcnt) { - struct ws_mask_ctx ctx; - int i; - int len = 0; - for (i = 0; i < strvcnt; i++) { - len += strv[i].len; - } - - mg_send_ws_header(nc, op, len, &ctx); - - for (i = 0; i < strvcnt; i++) { - mg_send(nc, strv[i].p, strv[i].len); - } - - mg_ws_mask_frame(&nc->send_mbuf, &ctx); - - if (op == WEBSOCKET_OP_CLOSE) { - nc->flags |= MG_F_SEND_AND_CLOSE; - } -} - -void mg_printf_websocket_frame(struct mg_connection *nc, int op, - const char *fmt, ...) { - char mem[MG_VPRINTF_BUFFER_SIZE], *buf = mem; - va_list ap; - int len; - - va_start(ap, fmt); - if ((len = mg_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) { - mg_send_websocket_frame(nc, op, buf, len); - } - va_end(ap); - - if (buf != mem && buf != NULL) { - MG_FREE(buf); - } -} - -MG_INTERNAL void mg_ws_handler(struct mg_connection *nc, int ev, - void *ev_data MG_UD_ARG(void *user_data)) { - mg_call(nc, nc->handler, nc->user_data, ev, ev_data); - - switch (ev) { - case MG_EV_RECV: - do { - } while (mg_deliver_websocket_data(nc)); - break; - case MG_EV_POLL: - /* Ping idle websocket connections */ - { - time_t now = *(time_t *) ev_data; - if (nc->flags & MG_F_IS_WEBSOCKET && - now > nc->last_io_time + MG_WEBSOCKET_PING_INTERVAL_SECONDS) { - mg_send_websocket_frame(nc, WEBSOCKET_OP_PING, "", 0); - } - } - break; - default: - break; - } -#if MG_ENABLE_CALLBACK_USERDATA - (void) user_data; -#endif -} - -#ifndef MG_EXT_SHA1 -void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[], - const size_t *msg_lens, uint8_t *digest) { - size_t i; - cs_sha1_ctx sha_ctx; - cs_sha1_init(&sha_ctx); - for (i = 0; i < num_msgs; i++) { - cs_sha1_update(&sha_ctx, msgs[i], msg_lens[i]); - } - cs_sha1_final(digest, &sha_ctx); -} -#else -extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[], - const size_t *msg_lens, uint8_t *digest); -#endif - -MG_INTERNAL void mg_ws_handshake(struct mg_connection *nc, - const struct mg_str *key, - struct http_message *hm) { - static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - const uint8_t *msgs[2] = {(const uint8_t *) key->p, (const uint8_t *) magic}; - const size_t msg_lens[2] = {key->len, 36}; - unsigned char sha[20]; - char b64_sha[30]; - struct mg_str *s; - - mg_hash_sha1_v(2, msgs, msg_lens, sha); - mg_base64_encode(sha, sizeof(sha), b64_sha); - mg_printf(nc, "%s", - "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n"); - - s = mg_get_http_header(hm, "Sec-WebSocket-Protocol"); - if (s != NULL) { - mg_printf(nc, "Sec-WebSocket-Protocol: %.*s\r\n", (int) s->len, s->p); - } - mg_printf(nc, "Sec-WebSocket-Accept: %s%s", b64_sha, "\r\n\r\n"); - - DBG(("%p %.*s %s", nc, (int) key->len, key->p, b64_sha)); -} - -void mg_send_websocket_handshake2(struct mg_connection *nc, const char *path, - const char *host, const char *protocol, - const char *extra_headers) { - mg_send_websocket_handshake3(nc, path, host, protocol, extra_headers, NULL, - NULL); -} - -void mg_send_websocket_handshake3(struct mg_connection *nc, const char *path, - const char *host, const char *protocol, - const char *extra_headers, const char *user, - const char *pass) { - mg_send_websocket_handshake3v(nc, mg_mk_str(path), mg_mk_str(host), - mg_mk_str(protocol), mg_mk_str(extra_headers), - mg_mk_str(user), mg_mk_str(pass)); -} - -void mg_send_websocket_handshake3v(struct mg_connection *nc, - const struct mg_str path, - const struct mg_str host, - const struct mg_str protocol, - const struct mg_str extra_headers, - const struct mg_str user, - const struct mg_str pass) { - struct mbuf auth; - char key[25]; - uint32_t nonce[4]; - nonce[0] = mg_ws_random_mask(); - nonce[1] = mg_ws_random_mask(); - nonce[2] = mg_ws_random_mask(); - nonce[3] = mg_ws_random_mask(); - mg_base64_encode((unsigned char *) &nonce, sizeof(nonce), key); - - mbuf_init(&auth, 0); - if (user.len > 0) { - mg_basic_auth_header(user, pass, &auth); - } - - /* - * NOTE: the (auth.buf == NULL ? "" : auth.buf) is because cc3200 libc is - * broken: it doesn't like zero length to be passed to %.*s - * i.e. sprintf("f%.*so", (int)0, NULL), yields `f\0o`. - * because it handles NULL specially (and incorrectly). - */ - mg_printf(nc, - "GET %.*s HTTP/1.1\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "%.*s" - "Sec-WebSocket-Version: 13\r\n" - "Sec-WebSocket-Key: %s\r\n", - (int) path.len, path.p, (int) auth.len, - (auth.buf == NULL ? "" : auth.buf), key); - - /* TODO(mkm): take default hostname from http proto data if host == NULL */ - if (host.len > 0) { - int host_len = (int) (path.p - host.p); /* Account for possible :PORT */ - mg_printf(nc, "Host: %.*s\r\n", host_len, host.p); - } - if (protocol.len > 0) { - mg_printf(nc, "Sec-WebSocket-Protocol: %.*s\r\n", (int) protocol.len, - protocol.p); - } - if (extra_headers.len > 0) { - mg_printf(nc, "%.*s", (int) extra_headers.len, extra_headers.p); - } - mg_printf(nc, "\r\n"); - - mbuf_free(&auth); -} - -void mg_send_websocket_handshake(struct mg_connection *nc, const char *path, - const char *extra_headers) { - struct mg_str null_str = MG_NULL_STR; - mg_send_websocket_handshake3v( - nc, mg_mk_str(path), null_str /* host */, null_str /* protocol */, - mg_mk_str(extra_headers), null_str /* user */, null_str /* pass */); -} - -struct mg_connection *mg_connect_ws_opt( - struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), - struct mg_connect_opts opts, const char *url, const char *protocol, - const char *extra_headers) { - struct mg_str null_str = MG_NULL_STR; - struct mg_str host = MG_NULL_STR, path = MG_NULL_STR, user_info = MG_NULL_STR; - struct mg_connection *nc = - mg_connect_http_base(mgr, MG_CB(ev_handler, user_data), opts, "http", - "ws", "https", "wss", url, &path, &user_info, &host); - if (nc != NULL) { - mg_send_websocket_handshake3v(nc, path, host, mg_mk_str(protocol), - mg_mk_str(extra_headers), user_info, - null_str); - } - return nc; -} - -struct mg_connection *mg_connect_ws( - struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), - const char *url, const char *protocol, const char *extra_headers) { - struct mg_connect_opts opts; - memset(&opts, 0, sizeof(opts)); - return mg_connect_ws_opt(mgr, MG_CB(ev_handler, user_data), opts, url, - protocol, extra_headers); -} -#endif /* MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBSOCKET */ diff --git a/src/mongoose-6.11/src/mg_internal.h b/src/mongoose-6.11/src/mg_internal.h deleted file mode 100644 index 2061423..0000000 --- a/src/mongoose-6.11/src/mg_internal.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_MONGOOSE_SRC_INTERNAL_H_ -#define CS_MONGOOSE_SRC_INTERNAL_H_ - -#include "common/mg_mem.h" - -#ifndef MBUF_REALLOC -#define MBUF_REALLOC MG_REALLOC -#endif - -#ifndef MBUF_FREE -#define MBUF_FREE MG_FREE -#endif - -#define MG_SET_PTRPTR(_ptr, _v) \ - do { \ - if (_ptr) *(_ptr) = _v; \ - } while (0) - -#ifndef MG_INTERNAL -#define MG_INTERNAL static -#endif - -#ifdef PICOTCP -#define NO_LIBC -#define MG_DISABLE_PFS -#endif - -#include "common/cs_dbg.h" -#include "mg_http.h" -#include "mg_net.h" - -#define MG_CTL_MSG_MESSAGE_SIZE 8192 - -/* internals that need to be accessible in unit tests */ -MG_INTERNAL struct mg_connection *mg_do_connect(struct mg_connection *nc, - int proto, - union socket_address *sa); - -MG_INTERNAL int mg_parse_address(const char *str, union socket_address *sa, - int *proto, char *host, size_t host_len); -MG_INTERNAL void mg_call(struct mg_connection *nc, - mg_event_handler_t ev_handler, void *user_data, int ev, - void *ev_data); -void mg_forward(struct mg_connection *from, struct mg_connection *to); -MG_INTERNAL void mg_add_conn(struct mg_mgr *mgr, struct mg_connection *c); -MG_INTERNAL void mg_remove_conn(struct mg_connection *c); -MG_INTERNAL struct mg_connection *mg_create_connection( - struct mg_mgr *mgr, mg_event_handler_t callback, - struct mg_add_sock_opts opts); -#ifdef _WIN32 -/* Retur value is the same as for MultiByteToWideChar. */ -int to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len); -#endif - -struct ctl_msg { - mg_event_handler_t callback; - char message[MG_CTL_MSG_MESSAGE_SIZE]; -}; - -#if MG_ENABLE_MQTT -struct mg_mqtt_message; - -#define MG_MQTT_ERROR_INCOMPLETE_MSG -1 -#define MG_MQTT_ERROR_MALFORMED_MSG -2 - -MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm); -#endif - -/* Forward declarations for testing. */ -extern void *(*test_malloc)(size_t size); -extern void *(*test_calloc)(size_t count, size_t size); - -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#endif - -#if MG_ENABLE_HTTP -struct mg_serve_http_opts; - -/* - * Reassemble the content of the buffer (buf, blen) which should be - * in the HTTP chunked encoding, by collapsing data chunks to the - * beginning of the buffer. - * - * If chunks get reassembled, modify hm->body to point to the reassembled - * body and fire MG_EV_HTTP_CHUNK event. If handler sets MG_F_DELETE_CHUNK - * in nc->flags, delete reassembled body from the mbuf. - * - * Return reassembled body size. - */ -MG_INTERNAL size_t mg_handle_chunked(struct mg_connection *nc, - struct http_message *hm, char *buf, - size_t blen); - -#if MG_ENABLE_FILESYSTEM -MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm, - const struct mg_serve_http_opts *opts, - char **local_path, - struct mg_str *remainder); -MG_INTERNAL time_t mg_parse_date_string(const char *datetime); -MG_INTERNAL int mg_is_not_modified(struct http_message *hm, cs_stat_t *st); -#endif -#if MG_ENABLE_HTTP_CGI -MG_INTERNAL void mg_handle_cgi(struct mg_connection *nc, const char *prog, - const struct mg_str *path_info, - const struct http_message *hm, - const struct mg_serve_http_opts *opts); -struct mg_http_proto_data_cgi; -MG_INTERNAL void mg_http_free_proto_data_cgi(struct mg_http_proto_data_cgi *d); -#endif -#if MG_ENABLE_HTTP_SSI -MG_INTERNAL void mg_handle_ssi_request(struct mg_connection *nc, - struct http_message *hm, - const char *path, - const struct mg_serve_http_opts *opts); -#endif -#if MG_ENABLE_HTTP_WEBDAV -MG_INTERNAL int mg_is_dav_request(const struct mg_str *s); -MG_INTERNAL void mg_handle_propfind(struct mg_connection *nc, const char *path, - cs_stat_t *stp, struct http_message *hm, - struct mg_serve_http_opts *opts); -MG_INTERNAL void mg_handle_lock(struct mg_connection *nc, const char *path); -MG_INTERNAL void mg_handle_mkcol(struct mg_connection *nc, const char *path, - struct http_message *hm); -MG_INTERNAL void mg_handle_move(struct mg_connection *c, - const struct mg_serve_http_opts *opts, - const char *path, struct http_message *hm); -MG_INTERNAL void mg_handle_delete(struct mg_connection *nc, - const struct mg_serve_http_opts *opts, - const char *path); -MG_INTERNAL void mg_handle_put(struct mg_connection *nc, const char *path, - struct http_message *hm); -#endif -#if MG_ENABLE_HTTP_WEBSOCKET -MG_INTERNAL void mg_ws_handler(struct mg_connection *nc, int ev, - void *ev_data MG_UD_ARG(void *user_data)); -MG_INTERNAL void mg_ws_handshake(struct mg_connection *nc, - const struct mg_str *key, - struct http_message *); -#endif -#endif /* MG_ENABLE_HTTP */ - -MG_INTERNAL int mg_get_errno(void); - -MG_INTERNAL void mg_close_conn(struct mg_connection *conn); - -#if MG_ENABLE_SNTP -MG_INTERNAL int mg_sntp_parse_reply(const char *buf, int len, - struct mg_sntp_message *msg); -#endif - -#endif /* CS_MONGOOSE_SRC_INTERNAL_H_ */ diff --git a/src/mongoose-6.11/src/mg_modules.mk b/src/mongoose-6.11/src/mg_modules.mk deleted file mode 100644 index 235e0d5..0000000 --- a/src/mongoose-6.11/src/mg_modules.mk +++ /dev/null @@ -1,103 +0,0 @@ -COMMON = ../../common - -HEADERS = mg_common.h \ - $(COMMON)/platform.h \ - $(COMMON)/platforms/platform_windows.h \ - $(COMMON)/platforms/platform_unix.h \ - $(COMMON)/platforms/platform_esp32.h \ - $(COMMON)/platforms/platform_esp8266.h \ - $(COMMON)/platforms/platform_cc3100.h \ - $(COMMON)/platforms/platform_cc3200.h \ - $(COMMON)/platforms/platform_msp432.h \ - $(COMMON)/platforms/platform_tm4c129.h \ - $(COMMON)/platforms/platform_mbed.h \ - $(COMMON)/platforms/platform_nrf51.h \ - $(COMMON)/platforms/platform_nrf52.h \ - $(COMMON)/platforms/simplelink/cs_simplelink.h \ - $(COMMON)/platforms/platform_wince.h \ - $(COMMON)/platforms/platform_nxp_lpc.h \ - $(COMMON)/platforms/platform_nxp_kinetis.h \ - $(COMMON)/platforms/platform_pic32.h \ - $(COMMON)/platforms/platform_stm32.h \ - $(COMMON)/platforms/lwip/mg_lwip.h \ - $(COMMON)/cs_md5.h \ - $(COMMON)/cs_sha1.h \ - $(COMMON)/cs_time.h \ - $(COMMON)/mg_str.h \ - $(COMMON)/mbuf.h \ - $(COMMON)/cs_base64.h \ - $(COMMON)/str_util.h \ - $(COMMON)/queue.h \ - mg_features.h \ - mg_net_if.h \ - mg_ssl_if.h \ - mg_net.h \ - mg_uri.h \ - mg_util.h \ - mg_http.h \ - mg_http_server.h \ - mg_http_client.h \ - mg_mqtt.h \ - mg_mqtt_server.h \ - mg_dns.h \ - mg_dns_server.h \ - mg_resolv.h \ - mg_coap.h \ - mg_sntp.h \ - mg_socks.h - -SOURCES = $(COMMON)/mg_mem.h \ - $(COMMON)/cs_base64.c \ - $(COMMON)/cs_dbg.h \ - $(COMMON)/cs_dbg.c \ - $(COMMON)/cs_dirent.h \ - $(COMMON)/cs_dirent.c \ - $(COMMON)/cs_time.c \ - $(COMMON)/cs_endian.h \ - $(COMMON)/cs_md5.c \ - $(COMMON)/cs_sha1.c \ - $(COMMON)/mbuf.c \ - $(COMMON)/mg_str.c \ - $(COMMON)/str_util.c \ - mg_net.c \ - mg_net_if_socket.h \ - mg_net_if_socks.h \ - mg_net_if.c \ - mg_net_if_socket.c \ - mg_net_if_socks.c \ - mg_ssl_if_openssl.c \ - mg_ssl_if_mbedtls.c \ - mg_uri.c \ - mg_http.c \ - mg_http_cgi.c \ - mg_http_ssi.c \ - mg_http_webdav.c \ - mg_http_websocket.c \ - mg_util.c \ - mg_mqtt.c \ - mg_mqtt_server.c \ - mg_dns.c \ - mg_dns_server.c \ - mg_resolv.c \ - mg_coap.c \ - mg_sntp.c \ - mg_socks.c \ - $(COMMON)/platforms/cc3200/cc3200_libc.c \ - $(COMMON)/platforms/msp432/msp432_libc.c \ - $(COMMON)/platforms/nrf5/nrf5_libc.c \ - $(COMMON)/platforms/simplelink/sl_fs_slfs.h \ - $(COMMON)/platforms/simplelink/sl_fs_slfs.c \ - $(COMMON)/platforms/simplelink/sl_fs.c \ - $(COMMON)/platforms/simplelink/sl_socket.c \ - $(COMMON)/platforms/simplelink/sl_mg_task.c \ - $(COMMON)/platforms/simplelink/sl_net_if.h \ - $(COMMON)/platforms/simplelink/sl_net_if.c \ - $(COMMON)/platforms/simplelink/sl_ssl_if.c \ - $(COMMON)/platforms/lwip/mg_lwip_net_if.h \ - $(COMMON)/platforms/lwip/mg_lwip_net_if.c \ - $(COMMON)/platforms/lwip/mg_lwip_ev_mgr.c \ - $(COMMON)/platforms/lwip/mg_lwip_ssl_if.c \ - $(COMMON)/platforms/wince/wince_libc.c \ - $(COMMON)/platforms/pic32/pic32_net_if.h \ - $(COMMON)/platforms/pic32/pic32_net_if.c \ - $(COMMON)/platforms/windows/windows_direct.c diff --git a/src/mongoose-6.11/src/mg_mqtt.c b/src/mongoose-6.11/src/mg_mqtt.c deleted file mode 100644 index 369b456..0000000 --- a/src/mongoose-6.11/src/mg_mqtt.c +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -#if MG_ENABLE_MQTT - -#include - -#include "mg_internal.h" -#include "mg_mqtt.h" - -static uint16_t getu16(const char *p) { - const uint8_t *up = (const uint8_t *) p; - return (up[0] << 8) + up[1]; -} - -static const char *scanto(const char *p, struct mg_str *s) { - s->len = getu16(p); - s->p = p + 2; - return s->p + s->len; -} - -MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) { - uint8_t header; - size_t len = 0, len_len = 0; - const char *p, *end; - unsigned char lc = 0; - int cmd; - - if (io->len < 2) return MG_MQTT_ERROR_INCOMPLETE_MSG; - header = io->buf[0]; - cmd = header >> 4; - - /* decode mqtt variable length */ - len = len_len = 0; - p = io->buf + 1; - while ((size_t)(p - io->buf) < io->len) { - lc = *((const unsigned char *) p++); - len += (lc & 0x7f) << 7 * len_len; - len_len++; - if (!(lc & 0x80)) break; - if (len_len > 4) return MG_MQTT_ERROR_MALFORMED_MSG; - } - - end = p + len; - if (lc & 0x80 || len > (io->len - (p - io->buf))) { - return MG_MQTT_ERROR_INCOMPLETE_MSG; - } - - mm->cmd = cmd; - mm->qos = MG_MQTT_GET_QOS(header); - - switch (cmd) { - case MG_MQTT_CMD_CONNECT: { - p = scanto(p, &mm->protocol_name); - if (p > end - 4) return MG_MQTT_ERROR_MALFORMED_MSG; - mm->protocol_version = *(uint8_t *) p++; - mm->connect_flags = *(uint8_t *) p++; - mm->keep_alive_timer = getu16(p); - p += 2; - if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG; - p = scanto(p, &mm->client_id); - if (p > end) return MG_MQTT_ERROR_MALFORMED_MSG; - if (mm->connect_flags & MG_MQTT_HAS_WILL) { - if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG; - p = scanto(p, &mm->will_topic); - } - if (mm->connect_flags & MG_MQTT_HAS_WILL) { - if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG; - p = scanto(p, &mm->will_message); - } - if (mm->connect_flags & MG_MQTT_HAS_USER_NAME) { - if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG; - p = scanto(p, &mm->user_name); - } - if (mm->connect_flags & MG_MQTT_HAS_PASSWORD) { - if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG; - p = scanto(p, &mm->password); - } - if (p != end) return MG_MQTT_ERROR_MALFORMED_MSG; - - LOG(LL_DEBUG, - ("%d %2x %d proto [%.*s] client_id [%.*s] will_topic [%.*s] " - "will_msg [%.*s] user_name [%.*s] password [%.*s]", - (int) len, (int) mm->connect_flags, (int) mm->keep_alive_timer, - (int) mm->protocol_name.len, mm->protocol_name.p, - (int) mm->client_id.len, mm->client_id.p, (int) mm->will_topic.len, - mm->will_topic.p, (int) mm->will_message.len, mm->will_message.p, - (int) mm->user_name.len, mm->user_name.p, (int) mm->password.len, - mm->password.p)); - break; - } - case MG_MQTT_CMD_CONNACK: - if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG; - mm->connack_ret_code = p[1]; - break; - case MG_MQTT_CMD_PUBACK: - case MG_MQTT_CMD_PUBREC: - case MG_MQTT_CMD_PUBREL: - case MG_MQTT_CMD_PUBCOMP: - case MG_MQTT_CMD_SUBACK: - mm->message_id = getu16(p); - break; - case MG_MQTT_CMD_PUBLISH: { - p = scanto(p, &mm->topic); - if (p > end) return MG_MQTT_ERROR_MALFORMED_MSG; - if (mm->qos > 0) { - if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG; - mm->message_id = getu16(p); - p += 2; - } - mm->payload.p = p; - mm->payload.len = end - p; - break; - } - case MG_MQTT_CMD_SUBSCRIBE: - if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG; - mm->message_id = getu16(p); - p += 2; - /* - * topic expressions are left in the payload and can be parsed with - * `mg_mqtt_next_subscribe_topic` - */ - mm->payload.p = p; - mm->payload.len = end - p; - break; - default: - /* Unhandled command */ - break; - } - - mm->len = end - io->buf; - return mm->len; -} - -static void mqtt_handler(struct mg_connection *nc, int ev, - void *ev_data MG_UD_ARG(void *user_data)) { - struct mbuf *io = &nc->recv_mbuf; - struct mg_mqtt_message mm; - memset(&mm, 0, sizeof(mm)); - - nc->handler(nc, ev, ev_data MG_UD_ARG(user_data)); - - switch (ev) { - case MG_EV_ACCEPT: - if (nc->proto_data == NULL) mg_set_protocol_mqtt(nc); - break; - case MG_EV_RECV: { - /* There can be multiple messages in the buffer, process them all. */ - while (1) { - int len = parse_mqtt(io, &mm); - if (len < 0) { - if (len == MG_MQTT_ERROR_MALFORMED_MSG) { - /* Protocol error. */ - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } else if (len == MG_MQTT_ERROR_INCOMPLETE_MSG) { - /* Not fully buffered, let's check if we have a chance to get more - * data later */ - if (nc->recv_mbuf_limit > 0 && - nc->recv_mbuf.len >= nc->recv_mbuf_limit) { - LOG(LL_ERROR, ("%p recv buffer (%lu bytes) exceeds the limit " - "%lu bytes, and not drained, closing", - nc, (unsigned long) nc->recv_mbuf.len, - (unsigned long) nc->recv_mbuf_limit)); - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } - } else { - /* Should never be here */ - LOG(LL_ERROR, ("%p invalid len: %d, closing", nc, len)); - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } - break; - } - - nc->handler(nc, MG_MQTT_EVENT_BASE + mm.cmd, &mm MG_UD_ARG(user_data)); - mbuf_remove(io, len); - } - break; - } - case MG_EV_POLL: { - struct mg_mqtt_proto_data *pd = - (struct mg_mqtt_proto_data *) nc->proto_data; - double now = mg_time(); - if (pd->keep_alive > 0 && pd->last_control_time > 0 && - (now - pd->last_control_time) > pd->keep_alive) { - LOG(LL_DEBUG, ("Send PINGREQ")); - mg_mqtt_ping(nc); - } - break; - } - } -} - -static void mg_mqtt_proto_data_destructor(void *proto_data) { - MG_FREE(proto_data); -} - -int mg_mqtt_match_topic_expression(struct mg_str exp, struct mg_str topic) { - /* TODO(mkm): implement real matching */ - if (memchr(exp.p, '#', exp.len)) { - /* exp `foo/#` will become `foo/` */ - exp.len -= 1; - /* - * topic should be longer than the expression: e.g. topic `foo/bar` does - * match `foo/#`, but neither `foo` nor `foo/` do. - */ - if (topic.len <= exp.len) { - return 0; - } - - /* Truncate topic so that it'll pass the next length check */ - topic.len = exp.len; - } - if (topic.len != exp.len) { - return 0; - } - return strncmp(topic.p, exp.p, exp.len) == 0; -} - -int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic) { - return mg_mqtt_match_topic_expression(mg_mk_str(exp), topic); -} - -void mg_set_protocol_mqtt(struct mg_connection *nc) { - nc->proto_handler = mqtt_handler; - nc->proto_data = MG_CALLOC(1, sizeof(struct mg_mqtt_proto_data)); - nc->proto_data_destructor = mg_mqtt_proto_data_destructor; -} - -static void mg_mqtt_prepend_header(struct mg_connection *nc, uint8_t cmd, - uint8_t flags, size_t len) { - struct mg_mqtt_proto_data *pd = (struct mg_mqtt_proto_data *) nc->proto_data; - size_t off = nc->send_mbuf.len - len; - uint8_t header = cmd << 4 | (uint8_t) flags; - - uint8_t buf[1 + sizeof(size_t)]; - uint8_t *vlen = &buf[1]; - - assert(nc->send_mbuf.len >= len); - - buf[0] = header; - - /* mqtt variable length encoding */ - do { - *vlen = len % 0x80; - len /= 0x80; - if (len > 0) *vlen |= 0x80; - vlen++; - } while (len > 0); - - mbuf_insert(&nc->send_mbuf, off, buf, vlen - buf); - pd->last_control_time = mg_time(); -} - -void mg_send_mqtt_handshake(struct mg_connection *nc, const char *client_id) { - static struct mg_send_mqtt_handshake_opts opts; - mg_send_mqtt_handshake_opt(nc, client_id, opts); -} - -void mg_send_mqtt_handshake_opt(struct mg_connection *nc, const char *client_id, - struct mg_send_mqtt_handshake_opts opts) { - uint16_t hlen, nlen, rem_len = 0; - struct mg_mqtt_proto_data *pd = (struct mg_mqtt_proto_data *) nc->proto_data; - - mg_send(nc, "\00\04MQTT\04", 7); - rem_len += 7; - - if (opts.user_name != NULL) { - opts.flags |= MG_MQTT_HAS_USER_NAME; - } - if (opts.password != NULL) { - opts.flags |= MG_MQTT_HAS_PASSWORD; - } - if (opts.will_topic != NULL && opts.will_message != NULL) { - opts.flags |= MG_MQTT_HAS_WILL; - } - if (opts.keep_alive == 0) { - opts.keep_alive = 60; - } - - mg_send(nc, &opts.flags, 1); - rem_len += 1; - - nlen = htons(opts.keep_alive); - mg_send(nc, &nlen, 2); - rem_len += 2; - - hlen = strlen(client_id); - nlen = htons((uint16_t) hlen); - mg_send(nc, &nlen, 2); - mg_send(nc, client_id, hlen); - rem_len += 2 + hlen; - - if (opts.flags & MG_MQTT_HAS_WILL) { - hlen = strlen(opts.will_topic); - nlen = htons((uint16_t) hlen); - mg_send(nc, &nlen, 2); - mg_send(nc, opts.will_topic, hlen); - rem_len += 2 + hlen; - - hlen = strlen(opts.will_message); - nlen = htons((uint16_t) hlen); - mg_send(nc, &nlen, 2); - mg_send(nc, opts.will_message, hlen); - rem_len += 2 + hlen; - } - - if (opts.flags & MG_MQTT_HAS_USER_NAME) { - hlen = strlen(opts.user_name); - nlen = htons((uint16_t) hlen); - mg_send(nc, &nlen, 2); - mg_send(nc, opts.user_name, hlen); - rem_len += 2 + hlen; - } - if (opts.flags & MG_MQTT_HAS_PASSWORD) { - hlen = strlen(opts.password); - nlen = htons((uint16_t) hlen); - mg_send(nc, &nlen, 2); - mg_send(nc, opts.password, hlen); - rem_len += 2 + hlen; - } - - mg_mqtt_prepend_header(nc, MG_MQTT_CMD_CONNECT, 0, rem_len); - - if (pd != NULL) { - pd->keep_alive = opts.keep_alive; - } -} - -void mg_mqtt_publish(struct mg_connection *nc, const char *topic, - uint16_t message_id, int flags, const void *data, - size_t len) { - size_t old_len = nc->send_mbuf.len; - - uint16_t topic_len = htons((uint16_t) strlen(topic)); - uint16_t message_id_net = htons(message_id); - - mg_send(nc, &topic_len, 2); - mg_send(nc, topic, strlen(topic)); - if (MG_MQTT_GET_QOS(flags) > 0) { - mg_send(nc, &message_id_net, 2); - } - mg_send(nc, data, len); - - mg_mqtt_prepend_header(nc, MG_MQTT_CMD_PUBLISH, flags, - nc->send_mbuf.len - old_len); -} - -void mg_mqtt_subscribe(struct mg_connection *nc, - const struct mg_mqtt_topic_expression *topics, - size_t topics_len, uint16_t message_id) { - size_t old_len = nc->send_mbuf.len; - - uint16_t message_id_n = htons(message_id); - size_t i; - - mg_send(nc, (char *) &message_id_n, 2); - for (i = 0; i < topics_len; i++) { - uint16_t topic_len_n = htons((uint16_t) strlen(topics[i].topic)); - mg_send(nc, &topic_len_n, 2); - mg_send(nc, topics[i].topic, strlen(topics[i].topic)); - mg_send(nc, &topics[i].qos, 1); - } - - mg_mqtt_prepend_header(nc, MG_MQTT_CMD_SUBSCRIBE, MG_MQTT_QOS(1), - nc->send_mbuf.len - old_len); -} - -int mg_mqtt_next_subscribe_topic(struct mg_mqtt_message *msg, - struct mg_str *topic, uint8_t *qos, int pos) { - unsigned char *buf = (unsigned char *) msg->payload.p + pos; - int new_pos; - - if ((size_t) pos >= msg->payload.len) return -1; - - topic->len = buf[0] << 8 | buf[1]; - topic->p = (char *) buf + 2; - new_pos = pos + 2 + topic->len + 1; - if ((size_t) new_pos > msg->payload.len) return -1; - *qos = buf[2 + topic->len]; - return new_pos; -} - -void mg_mqtt_unsubscribe(struct mg_connection *nc, char **topics, - size_t topics_len, uint16_t message_id) { - size_t old_len = nc->send_mbuf.len; - - uint16_t message_id_n = htons(message_id); - size_t i; - - mg_send(nc, (char *) &message_id_n, 2); - for (i = 0; i < topics_len; i++) { - uint16_t topic_len_n = htons((uint16_t) strlen(topics[i])); - mg_send(nc, &topic_len_n, 2); - mg_send(nc, topics[i], strlen(topics[i])); - } - - mg_mqtt_prepend_header(nc, MG_MQTT_CMD_UNSUBSCRIBE, MG_MQTT_QOS(1), - nc->send_mbuf.len - old_len); -} - -void mg_mqtt_connack(struct mg_connection *nc, uint8_t return_code) { - uint8_t unused = 0; - mg_send(nc, &unused, 1); - mg_send(nc, &return_code, 1); - mg_mqtt_prepend_header(nc, MG_MQTT_CMD_CONNACK, 0, 2); -} - -/* - * Sends a command which contains only a `message_id` and a QoS level of 1. - * - * Helper function. - */ -static void mg_send_mqtt_short_command(struct mg_connection *nc, uint8_t cmd, - uint16_t message_id) { - uint16_t message_id_net = htons(message_id); - uint8_t flags = (cmd == MG_MQTT_CMD_PUBREL ? 2 : 0); - mg_send(nc, &message_id_net, 2); - mg_mqtt_prepend_header(nc, cmd, flags, 2 /* len */); -} - -void mg_mqtt_puback(struct mg_connection *nc, uint16_t message_id) { - mg_send_mqtt_short_command(nc, MG_MQTT_CMD_PUBACK, message_id); -} - -void mg_mqtt_pubrec(struct mg_connection *nc, uint16_t message_id) { - mg_send_mqtt_short_command(nc, MG_MQTT_CMD_PUBREC, message_id); -} - -void mg_mqtt_pubrel(struct mg_connection *nc, uint16_t message_id) { - mg_send_mqtt_short_command(nc, MG_MQTT_CMD_PUBREL, message_id); -} - -void mg_mqtt_pubcomp(struct mg_connection *nc, uint16_t message_id) { - mg_send_mqtt_short_command(nc, MG_MQTT_CMD_PUBCOMP, message_id); -} - -void mg_mqtt_suback(struct mg_connection *nc, uint8_t *qoss, size_t qoss_len, - uint16_t message_id) { - size_t i; - uint16_t message_id_net = htons(message_id); - mg_send(nc, &message_id_net, 2); - for (i = 0; i < qoss_len; i++) { - mg_send(nc, &qoss[i], 1); - } - mg_mqtt_prepend_header(nc, MG_MQTT_CMD_SUBACK, MG_MQTT_QOS(1), 2 + qoss_len); -} - -void mg_mqtt_unsuback(struct mg_connection *nc, uint16_t message_id) { - mg_send_mqtt_short_command(nc, MG_MQTT_CMD_UNSUBACK, message_id); -} - -void mg_mqtt_ping(struct mg_connection *nc) { - mg_mqtt_prepend_header(nc, MG_MQTT_CMD_PINGREQ, 0, 0); -} - -void mg_mqtt_pong(struct mg_connection *nc) { - mg_mqtt_prepend_header(nc, MG_MQTT_CMD_PINGRESP, 0, 0); -} - -void mg_mqtt_disconnect(struct mg_connection *nc) { - mg_mqtt_prepend_header(nc, MG_MQTT_CMD_DISCONNECT, 0, 0); -} - -#endif /* MG_ENABLE_MQTT */ diff --git a/src/mongoose-6.11/src/mg_mqtt.h b/src/mongoose-6.11/src/mg_mqtt.h deleted file mode 100644 index 6067ccc..0000000 --- a/src/mongoose-6.11/src/mg_mqtt.h +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - * This software is dual-licensed: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. For the terms of this - * license, see . - * - * You are free to use this software under the terms of the GNU General - * Public License, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * Alternatively, you can license this software under a commercial - * license, as set out in . - */ - -/* - * === MQTT API reference - */ - -#ifndef CS_MONGOOSE_SRC_MQTT_H_ -#define CS_MONGOOSE_SRC_MQTT_H_ - -#include "mg_net.h" - -struct mg_mqtt_message { - int cmd; - int qos; - int len; /* message length in the IO buffer */ - struct mg_str topic; - struct mg_str payload; - - uint8_t connack_ret_code; /* connack */ - uint16_t message_id; /* puback */ - - /* connect */ - uint8_t protocol_version; - uint8_t connect_flags; - uint16_t keep_alive_timer; - struct mg_str protocol_name; - struct mg_str client_id; - struct mg_str will_topic; - struct mg_str will_message; - struct mg_str user_name; - struct mg_str password; -}; - -struct mg_mqtt_topic_expression { - const char *topic; - uint8_t qos; -}; - -struct mg_send_mqtt_handshake_opts { - unsigned char flags; /* connection flags */ - uint16_t keep_alive; - const char *will_topic; - const char *will_message; - const char *user_name; - const char *password; -}; - -/* mg_mqtt_proto_data should be in header to allow external access to it */ -struct mg_mqtt_proto_data { - uint16_t keep_alive; - double last_control_time; -}; - -/* Message types */ -#define MG_MQTT_CMD_CONNECT 1 -#define MG_MQTT_CMD_CONNACK 2 -#define MG_MQTT_CMD_PUBLISH 3 -#define MG_MQTT_CMD_PUBACK 4 -#define MG_MQTT_CMD_PUBREC 5 -#define MG_MQTT_CMD_PUBREL 6 -#define MG_MQTT_CMD_PUBCOMP 7 -#define MG_MQTT_CMD_SUBSCRIBE 8 -#define MG_MQTT_CMD_SUBACK 9 -#define MG_MQTT_CMD_UNSUBSCRIBE 10 -#define MG_MQTT_CMD_UNSUBACK 11 -#define MG_MQTT_CMD_PINGREQ 12 -#define MG_MQTT_CMD_PINGRESP 13 -#define MG_MQTT_CMD_DISCONNECT 14 - -/* MQTT event types */ -#define MG_MQTT_EVENT_BASE 200 -#define MG_EV_MQTT_CONNECT (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_CONNECT) -#define MG_EV_MQTT_CONNACK (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_CONNACK) -#define MG_EV_MQTT_PUBLISH (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBLISH) -#define MG_EV_MQTT_PUBACK (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBACK) -#define MG_EV_MQTT_PUBREC (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBREC) -#define MG_EV_MQTT_PUBREL (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBREL) -#define MG_EV_MQTT_PUBCOMP (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PUBCOMP) -#define MG_EV_MQTT_SUBSCRIBE (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_SUBSCRIBE) -#define MG_EV_MQTT_SUBACK (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_SUBACK) -#define MG_EV_MQTT_UNSUBSCRIBE (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_UNSUBSCRIBE) -#define MG_EV_MQTT_UNSUBACK (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_UNSUBACK) -#define MG_EV_MQTT_PINGREQ (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PINGREQ) -#define MG_EV_MQTT_PINGRESP (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_PINGRESP) -#define MG_EV_MQTT_DISCONNECT (MG_MQTT_EVENT_BASE + MG_MQTT_CMD_DISCONNECT) - -/* Message flags */ -#define MG_MQTT_RETAIN 0x1 -#define MG_MQTT_DUP 0x4 -#define MG_MQTT_QOS(qos) ((qos) << 1) -#define MG_MQTT_GET_QOS(flags) (((flags) &0x6) >> 1) -#define MG_MQTT_SET_QOS(flags, qos) (flags) = ((flags) & ~0x6) | ((qos) << 1) - -/* Connection flags */ -#define MG_MQTT_CLEAN_SESSION 0x02 -#define MG_MQTT_HAS_WILL 0x04 -#define MG_MQTT_WILL_RETAIN 0x20 -#define MG_MQTT_HAS_PASSWORD 0x40 -#define MG_MQTT_HAS_USER_NAME 0x80 -#define MG_MQTT_GET_WILL_QOS(flags) (((flags) &0x18) >> 3) -#define MG_MQTT_SET_WILL_QOS(flags, qos) \ - (flags) = ((flags) & ~0x18) | ((qos) << 3) - -/* CONNACK return codes */ -#define MG_EV_MQTT_CONNACK_ACCEPTED 0 -#define MG_EV_MQTT_CONNACK_UNACCEPTABLE_VERSION 1 -#define MG_EV_MQTT_CONNACK_IDENTIFIER_REJECTED 2 -#define MG_EV_MQTT_CONNACK_SERVER_UNAVAILABLE 3 -#define MG_EV_MQTT_CONNACK_BAD_AUTH 4 -#define MG_EV_MQTT_CONNACK_NOT_AUTHORIZED 5 - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* - * Attaches a built-in MQTT event handler to the given connection. - * - * The user-defined event handler will receive following extra events: - * - * - MG_EV_MQTT_CONNACK - * - MG_EV_MQTT_PUBLISH - * - MG_EV_MQTT_PUBACK - * - MG_EV_MQTT_PUBREC - * - MG_EV_MQTT_PUBREL - * - MG_EV_MQTT_PUBCOMP - * - MG_EV_MQTT_SUBACK - */ -void mg_set_protocol_mqtt(struct mg_connection *nc); - -/* Sends an MQTT handshake. */ -void mg_send_mqtt_handshake(struct mg_connection *nc, const char *client_id); - -/* Sends an MQTT handshake with optional parameters. */ -void mg_send_mqtt_handshake_opt(struct mg_connection *nc, const char *client_id, - struct mg_send_mqtt_handshake_opts); - -/* Publishes a message to a given topic. */ -void mg_mqtt_publish(struct mg_connection *nc, const char *topic, - uint16_t message_id, int flags, const void *data, - size_t len); - -/* Subscribes to a bunch of topics. */ -void mg_mqtt_subscribe(struct mg_connection *nc, - const struct mg_mqtt_topic_expression *topics, - size_t topics_len, uint16_t message_id); - -/* Unsubscribes from a bunch of topics. */ -void mg_mqtt_unsubscribe(struct mg_connection *nc, char **topics, - size_t topics_len, uint16_t message_id); - -/* Sends a DISCONNECT command. */ -void mg_mqtt_disconnect(struct mg_connection *nc); - -/* Sends a CONNACK command with a given `return_code`. */ -void mg_mqtt_connack(struct mg_connection *nc, uint8_t return_code); - -/* Sends a PUBACK command with a given `message_id`. */ -void mg_mqtt_puback(struct mg_connection *nc, uint16_t message_id); - -/* Sends a PUBREC command with a given `message_id`. */ -void mg_mqtt_pubrec(struct mg_connection *nc, uint16_t message_id); - -/* Sends a PUBREL command with a given `message_id`. */ -void mg_mqtt_pubrel(struct mg_connection *nc, uint16_t message_id); - -/* Sends a PUBCOMP command with a given `message_id`. */ -void mg_mqtt_pubcomp(struct mg_connection *nc, uint16_t message_id); - -/* - * Sends a SUBACK command with a given `message_id` - * and a sequence of granted QoSs. - */ -void mg_mqtt_suback(struct mg_connection *nc, uint8_t *qoss, size_t qoss_len, - uint16_t message_id); - -/* Sends a UNSUBACK command with a given `message_id`. */ -void mg_mqtt_unsuback(struct mg_connection *nc, uint16_t message_id); - -/* Sends a PINGREQ command. */ -void mg_mqtt_ping(struct mg_connection *nc); - -/* Sends a PINGRESP command. */ -void mg_mqtt_pong(struct mg_connection *nc); - -/* - * Extracts the next topic expression from a SUBSCRIBE command payload. - * - * The topic expression name will point to a string in the payload buffer. - * Returns the pos of the next topic expression or -1 when the list - * of topics is exhausted. - */ -int mg_mqtt_next_subscribe_topic(struct mg_mqtt_message *msg, - struct mg_str *topic, uint8_t *qos, int pos); - -/* - * Matches a topic against a topic expression - * - * Returns 1 if it matches; 0 otherwise. - */ -int mg_mqtt_match_topic_expression(struct mg_str exp, struct mg_str topic); - -/* - * Same as `mg_mqtt_match_topic_expression()`, but takes `exp` as a - * NULL-terminated string. - */ -int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* CS_MONGOOSE_SRC_MQTT_H_ */ diff --git a/src/mongoose-6.11/src/mg_mqtt_client.h b/src/mongoose-6.11/src/mg_mqtt_client.h deleted file mode 100644 index 998414d..0000000 --- a/src/mongoose-6.11/src/mg_mqtt_client.h +++ /dev/null @@ -1,8 +0,0 @@ -/* - * === API reference - */ - -#ifndef CS_MONGOOSE_SRC_MQTT_CLIENT_H_ -#define CS_MONGOOSE_SRC_MQTT_CLIENT_H_ - -#endif /* CS_MONGOOSE_SRC_MQTT_CLIENT_H_ */ diff --git a/src/mongoose-6.11/src/mg_mqtt_server.c b/src/mongoose-6.11/src/mg_mqtt_server.c deleted file mode 100644 index cb56648..0000000 --- a/src/mongoose-6.11/src/mg_mqtt_server.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -#include "mg_internal.h" -#include "mg_mqtt_server.h" - -#if MG_ENABLE_MQTT_BROKER - -static void mg_mqtt_session_init(struct mg_mqtt_broker *brk, - struct mg_mqtt_session *s, - struct mg_connection *nc) { - s->brk = brk; - s->subscriptions = NULL; - s->num_subscriptions = 0; - s->nc = nc; -} - -static void mg_mqtt_add_session(struct mg_mqtt_session *s) { - LIST_INSERT_HEAD(&s->brk->sessions, s, link); -} - -static void mg_mqtt_remove_session(struct mg_mqtt_session *s) { - LIST_REMOVE(s, link); -} - -static void mg_mqtt_destroy_session(struct mg_mqtt_session *s) { - size_t i; - for (i = 0; i < s->num_subscriptions; i++) { - MG_FREE((void *) s->subscriptions[i].topic); - } - MG_FREE(s->subscriptions); - MG_FREE(s); -} - -static void mg_mqtt_close_session(struct mg_mqtt_session *s) { - mg_mqtt_remove_session(s); - mg_mqtt_destroy_session(s); -} - -void mg_mqtt_broker_init(struct mg_mqtt_broker *brk, void *user_data) { - LIST_INIT(&brk->sessions); - brk->user_data = user_data; -} - -static void mg_mqtt_broker_handle_connect(struct mg_mqtt_broker *brk, - struct mg_connection *nc) { - struct mg_mqtt_session *s = - (struct mg_mqtt_session *) MG_CALLOC(1, sizeof *s); - if (s == NULL) { - /* LCOV_EXCL_START */ - mg_mqtt_connack(nc, MG_EV_MQTT_CONNACK_SERVER_UNAVAILABLE); - return; - /* LCOV_EXCL_STOP */ - } - - /* TODO(mkm): check header (magic and version) */ - - mg_mqtt_session_init(brk, s, nc); - nc->priv_2 = s; - mg_mqtt_add_session(s); - - mg_mqtt_connack(nc, MG_EV_MQTT_CONNACK_ACCEPTED); -} - -static void mg_mqtt_broker_handle_subscribe(struct mg_connection *nc, - struct mg_mqtt_message *msg) { - struct mg_mqtt_session *ss = (struct mg_mqtt_session *) nc->priv_2; - uint8_t qoss[MG_MQTT_MAX_SESSION_SUBSCRIPTIONS]; - size_t num_subs = 0; - struct mg_str topic; - uint8_t qos; - int pos; - struct mg_mqtt_topic_expression *te; - - for (pos = 0; - (pos = mg_mqtt_next_subscribe_topic(msg, &topic, &qos, pos)) != -1;) { - if (num_subs >= sizeof(MG_MQTT_MAX_SESSION_SUBSCRIPTIONS) || - (ss->num_subscriptions + num_subs >= - MG_MQTT_MAX_SESSION_SUBSCRIPTIONS)) { - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - return; - } - qoss[num_subs++] = qos; - } - - if (num_subs > 0) { - te = (struct mg_mqtt_topic_expression *) MG_REALLOC( - ss->subscriptions, - sizeof(*ss->subscriptions) * (ss->num_subscriptions + num_subs)); - if (te == NULL) { - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - return; - } - ss->subscriptions = te; - for (pos = 0; - pos < (int) msg->payload.len && - (pos = mg_mqtt_next_subscribe_topic(msg, &topic, &qos, pos)) != -1; - ss->num_subscriptions++) { - te = &ss->subscriptions[ss->num_subscriptions]; - te->topic = (char *) MG_MALLOC(topic.len + 1); - te->qos = qos; - memcpy((char *) te->topic, topic.p, topic.len); - ((char *) te->topic)[topic.len] = '\0'; - } - } - - if (pos == (int) msg->payload.len) { - mg_mqtt_suback(nc, qoss, num_subs, msg->message_id); - } else { - /* We did not fully parse the payload, something must be wrong. */ - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } -} - -static void mg_mqtt_broker_handle_publish(struct mg_mqtt_broker *brk, - struct mg_mqtt_message *msg) { - struct mg_mqtt_session *s; - size_t i; - - for (s = mg_mqtt_next(brk, NULL); s != NULL; s = mg_mqtt_next(brk, s)) { - for (i = 0; i < s->num_subscriptions; i++) { - if (mg_mqtt_vmatch_topic_expression(s->subscriptions[i].topic, - msg->topic)) { - char buf[100], *p = buf; - mg_asprintf(&p, sizeof(buf), "%.*s", (int) msg->topic.len, - msg->topic.p); - if (p == NULL) { - return; - } - mg_mqtt_publish(s->nc, p, 0, 0, msg->payload.p, msg->payload.len); - if (p != buf) { - MG_FREE(p); - } - break; - } - } - } -} - -void mg_mqtt_broker(struct mg_connection *nc, int ev, void *data) { - struct mg_mqtt_message *msg = (struct mg_mqtt_message *) data; - struct mg_mqtt_broker *brk; - - if (nc->listener) { - brk = (struct mg_mqtt_broker *) nc->listener->priv_2; - } else { - brk = (struct mg_mqtt_broker *) nc->priv_2; - } - - switch (ev) { - case MG_EV_ACCEPT: - if (nc->proto_data == NULL) mg_set_protocol_mqtt(nc); - nc->priv_2 = NULL; /* Clear up the inherited pointer to broker */ - break; - case MG_EV_MQTT_CONNECT: - if (nc->priv_2 == NULL) { - mg_mqtt_broker_handle_connect(brk, nc); - } else { - /* Repeated CONNECT */ - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } - break; - case MG_EV_MQTT_SUBSCRIBE: - if (nc->priv_2 != NULL) { - mg_mqtt_broker_handle_subscribe(nc, msg); - } else { - /* Subscribe before CONNECT */ - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } - break; - case MG_EV_MQTT_PUBLISH: - if (nc->priv_2 != NULL) { - mg_mqtt_broker_handle_publish(brk, msg); - } else { - /* Publish before CONNECT */ - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } - break; - case MG_EV_CLOSE: - if (nc->listener && nc->priv_2 != NULL) { - mg_mqtt_close_session((struct mg_mqtt_session *) nc->priv_2); - } - break; - } -} - -struct mg_mqtt_session *mg_mqtt_next(struct mg_mqtt_broker *brk, - struct mg_mqtt_session *s) { - return s == NULL ? LIST_FIRST(&brk->sessions) : LIST_NEXT(s, link); -} - -#endif /* MG_ENABLE_MQTT_BROKER */ diff --git a/src/mongoose-6.11/src/mg_mqtt_server.h b/src/mongoose-6.11/src/mg_mqtt_server.h deleted file mode 100644 index 8c34b5e..0000000 --- a/src/mongoose-6.11/src/mg_mqtt_server.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - * This software is dual-licensed: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. For the terms of this - * license, see . - * - * You are free to use this software under the terms of the GNU General - * Public License, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * Alternatively, you can license this software under a commercial - * license, as set out in . - */ - -/* - * === MQTT Server API reference - */ - -#ifndef CS_MONGOOSE_SRC_MQTT_BROKER_H_ -#define CS_MONGOOSE_SRC_MQTT_BROKER_H_ - -#if MG_ENABLE_MQTT_BROKER - -#include "common/queue.h" -#include "mg_mqtt.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifndef MG_MQTT_MAX_SESSION_SUBSCRIPTIONS -#define MG_MQTT_MAX_SESSION_SUBSCRIPTIONS 512 -#endif - -struct mg_mqtt_broker; - -/* MQTT session (Broker side). */ -struct mg_mqtt_session { - struct mg_mqtt_broker *brk; /* Broker */ - LIST_ENTRY(mg_mqtt_session) link; /* mg_mqtt_broker::sessions linkage */ - struct mg_connection *nc; /* Connection with the client */ - size_t num_subscriptions; /* Size of `subscriptions` array */ - void *user_data; /* User data */ - struct mg_mqtt_topic_expression *subscriptions; -}; - -/* MQTT broker. */ -struct mg_mqtt_broker { - LIST_HEAD(_mg_sesshead, mg_mqtt_session) sessions; /* Session list */ - void *user_data; /* User data */ -}; - -/* Initialises a MQTT broker. */ -void mg_mqtt_broker_init(struct mg_mqtt_broker *brk, void *user_data); - -/* - * Processes a MQTT broker message. - * - * The listening connection expects a pointer to an initialised - * `mg_mqtt_broker` structure in the `user_data` field. - * - * Basic usage: - * - * ```c - * mg_mqtt_broker_init(&brk, NULL); - * - * if ((nc = mg_bind(&mgr, address, mg_mqtt_broker)) == NULL) { - * // fail; - * } - * nc->user_data = &brk; - * ``` - * - * New incoming connections will receive a `mg_mqtt_session` structure - * in the connection `user_data`. The original `user_data` will be stored - * in the `user_data` field of the session structure. This allows the user - * handler to store user data before `mg_mqtt_broker` creates the session. - * - * Since only the MG_EV_ACCEPT message is processed by the listening socket, - * for most events the `user_data` will thus point to a `mg_mqtt_session`. - */ -void mg_mqtt_broker(struct mg_connection *brk, int ev, void *data); - -/* - * Iterates over all MQTT session connections. Example: - * - * ```c - * struct mg_mqtt_session *s; - * for (s = mg_mqtt_next(brk, NULL); s != NULL; s = mg_mqtt_next(brk, s)) { - * // Do something - * } - * ``` - */ -struct mg_mqtt_session *mg_mqtt_next(struct mg_mqtt_broker *brk, - struct mg_mqtt_session *s); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* MG_ENABLE_MQTT_BROKER */ -#endif /* CS_MONGOOSE_SRC_MQTT_BROKER_H_ */ diff --git a/src/mongoose-6.11/src/mg_net.c b/src/mongoose-6.11/src/mg_net.c deleted file mode 100644 index 70e2cc8..0000000 --- a/src/mongoose-6.11/src/mg_net.c +++ /dev/null @@ -1,1008 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - * - * This software is dual-licensed: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. For the terms of this - * license, see . - * - * You are free to use this software under the terms of the GNU General - * Public License, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * Alternatively, you can license this software under a commercial - * license, as set out in . - */ - -#include "common/cs_time.h" -#include "mg_dns.h" -#include "mg_internal.h" -#include "mg_resolv.h" -#include "mg_util.h" - -#define MG_MAX_HOST_LEN 200 - -#define MG_COPY_COMMON_CONNECTION_OPTIONS(dst, src) \ - memcpy(dst, src, sizeof(*dst)); - -/* Which flags can be pre-set by the user at connection creation time. */ -#define _MG_ALLOWED_CONNECT_FLAGS_MASK \ - (MG_F_USER_1 | MG_F_USER_2 | MG_F_USER_3 | MG_F_USER_4 | MG_F_USER_5 | \ - MG_F_USER_6 | MG_F_WEBSOCKET_NO_DEFRAG | MG_F_ENABLE_BROADCAST) -/* Which flags should be modifiable by user's callbacks. */ -#define _MG_CALLBACK_MODIFIABLE_FLAGS_MASK \ - (MG_F_USER_1 | MG_F_USER_2 | MG_F_USER_3 | MG_F_USER_4 | MG_F_USER_5 | \ - MG_F_USER_6 | MG_F_WEBSOCKET_NO_DEFRAG | MG_F_SEND_AND_CLOSE | \ - MG_F_CLOSE_IMMEDIATELY | MG_F_IS_WEBSOCKET | MG_F_DELETE_CHUNK) - -#ifndef intptr_t -#define intptr_t long -#endif - -MG_INTERNAL void mg_add_conn(struct mg_mgr *mgr, struct mg_connection *c) { - DBG(("%p %p", mgr, c)); - c->mgr = mgr; - c->next = mgr->active_connections; - mgr->active_connections = c; - c->prev = NULL; - if (c->next != NULL) c->next->prev = c; - if (c->sock != INVALID_SOCKET) { - c->iface->vtable->add_conn(c); - } -} - -MG_INTERNAL void mg_remove_conn(struct mg_connection *conn) { - if (conn->prev == NULL) conn->mgr->active_connections = conn->next; - if (conn->prev) conn->prev->next = conn->next; - if (conn->next) conn->next->prev = conn->prev; - conn->prev = conn->next = NULL; - conn->iface->vtable->remove_conn(conn); -} - -MG_INTERNAL void mg_call(struct mg_connection *nc, - mg_event_handler_t ev_handler, void *user_data, int ev, - void *ev_data) { - static int nesting_level = 0; - nesting_level++; - if (ev_handler == NULL) { - /* - * If protocol handler is specified, call it. Otherwise, call user-specified - * event handler. - */ - ev_handler = nc->proto_handler ? nc->proto_handler : nc->handler; - } - if (ev != MG_EV_POLL) { - DBG(("%p %s ev=%d ev_data=%p flags=%lu rmbl=%d smbl=%d", nc, - ev_handler == nc->handler ? "user" : "proto", ev, ev_data, nc->flags, - (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); - } - -#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP - if (nc->mgr->hexdump_file != NULL && ev != MG_EV_POLL && ev != MG_EV_RECV && - ev != MG_EV_SEND /* handled separately */) { - mg_hexdump_connection(nc, nc->mgr->hexdump_file, NULL, 0, ev); - } -#endif - if (ev_handler != NULL) { - unsigned long flags_before = nc->flags; - size_t recv_mbuf_before = nc->recv_mbuf.len, recved; - ev_handler(nc, ev, ev_data MG_UD_ARG(user_data)); - recved = (recv_mbuf_before - nc->recv_mbuf.len); - /* Prevent user handler from fiddling with system flags. */ - if (ev_handler == nc->handler && nc->flags != flags_before) { - nc->flags = (flags_before & ~_MG_CALLBACK_MODIFIABLE_FLAGS_MASK) | - (nc->flags & _MG_CALLBACK_MODIFIABLE_FLAGS_MASK); - } - /* It's important to not double-count recved bytes, and since mg_call can be - * called recursively (e.g. proto_handler invokes user handler), we keep - * track of recursion and only report received bytes at the top level. */ - if (nesting_level == 1 && recved > 0 && !(nc->flags & MG_F_UDP)) { - nc->iface->vtable->recved(nc, recved); - } - } - if (ev != MG_EV_POLL) { - DBG(("%p after %s flags=%lu rmbl=%d smbl=%d", nc, - ev_handler == nc->handler ? "user" : "proto", nc->flags, - (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); - } - nesting_level--; -#if !MG_ENABLE_CALLBACK_USERDATA - (void) user_data; -#endif -} - -void mg_if_timer(struct mg_connection *c, double now) { - if (c->ev_timer_time > 0 && now >= c->ev_timer_time) { - double old_value = c->ev_timer_time; - c->ev_timer_time = 0; - mg_call(c, NULL, c->user_data, MG_EV_TIMER, &old_value); - } -} - -void mg_if_poll(struct mg_connection *nc, time_t now) { - if (!(nc->flags & MG_F_SSL) || (nc->flags & MG_F_SSL_HANDSHAKE_DONE)) { - mg_call(nc, NULL, nc->user_data, MG_EV_POLL, &now); - } -} - -void mg_destroy_conn(struct mg_connection *conn, int destroy_if) { - if (destroy_if) conn->iface->vtable->destroy_conn(conn); - if (conn->proto_data != NULL && conn->proto_data_destructor != NULL) { - conn->proto_data_destructor(conn->proto_data); - } -#if MG_ENABLE_SSL - mg_ssl_if_conn_free(conn); -#endif - mbuf_free(&conn->recv_mbuf); - mbuf_free(&conn->send_mbuf); - - memset(conn, 0, sizeof(*conn)); - MG_FREE(conn); -} - -void mg_close_conn(struct mg_connection *conn) { - DBG(("%p %lu %d", conn, conn->flags, conn->sock)); -#if MG_ENABLE_SSL - if (conn->flags & MG_F_SSL_HANDSHAKE_DONE) { - mg_ssl_if_conn_close_notify(conn); - } -#endif - mg_remove_conn(conn); - conn->iface->vtable->destroy_conn(conn); - mg_call(conn, NULL, conn->user_data, MG_EV_CLOSE, NULL); - mg_destroy_conn(conn, 0 /* destroy_if */); -} - -void mg_mgr_init(struct mg_mgr *m, void *user_data) { - struct mg_mgr_init_opts opts; - memset(&opts, 0, sizeof(opts)); - mg_mgr_init_opt(m, user_data, opts); -} - -void mg_mgr_init_opt(struct mg_mgr *m, void *user_data, - struct mg_mgr_init_opts opts) { - memset(m, 0, sizeof(*m)); -#if MG_ENABLE_BROADCAST - m->ctl[0] = m->ctl[1] = INVALID_SOCKET; -#endif - m->user_data = user_data; - -#ifdef _WIN32 - { - WSADATA data; - WSAStartup(MAKEWORD(2, 2), &data); - } -#elif defined(__unix__) - /* Ignore SIGPIPE signal, so if client cancels the request, it - * won't kill the whole process. */ - signal(SIGPIPE, SIG_IGN); -#endif - -#if MG_ENABLE_SSL - { - static int init_done; - if (!init_done) { - mg_ssl_if_init(); - init_done++; - } - } -#endif - { - int i; - if (opts.num_ifaces == 0) { - opts.num_ifaces = mg_num_ifaces; - opts.ifaces = mg_ifaces; - } - if (opts.main_iface != NULL) { - opts.ifaces[MG_MAIN_IFACE] = opts.main_iface; - } - m->num_ifaces = opts.num_ifaces; - m->ifaces = - (struct mg_iface **) MG_MALLOC(sizeof(*m->ifaces) * opts.num_ifaces); - for (i = 0; i < mg_num_ifaces; i++) { - m->ifaces[i] = mg_if_create_iface(opts.ifaces[i], m); - m->ifaces[i]->vtable->init(m->ifaces[i]); - } - } - if (opts.nameserver != NULL) { - m->nameserver = strdup(opts.nameserver); - } - DBG(("==================================")); - DBG(("init mgr=%p", m)); -} - -void mg_mgr_free(struct mg_mgr *m) { - struct mg_connection *conn, *tmp_conn; - - DBG(("%p", m)); - if (m == NULL) return; - /* Do one last poll, see https://github.com/cesanta/mongoose/issues/286 */ - mg_mgr_poll(m, 0); - -#if MG_ENABLE_BROADCAST - if (m->ctl[0] != INVALID_SOCKET) closesocket(m->ctl[0]); - if (m->ctl[1] != INVALID_SOCKET) closesocket(m->ctl[1]); - m->ctl[0] = m->ctl[1] = INVALID_SOCKET; -#endif - - for (conn = m->active_connections; conn != NULL; conn = tmp_conn) { - tmp_conn = conn->next; - mg_close_conn(conn); - } - - { - int i; - for (i = 0; i < m->num_ifaces; i++) { - m->ifaces[i]->vtable->free(m->ifaces[i]); - MG_FREE(m->ifaces[i]); - } - MG_FREE(m->ifaces); - } - - MG_FREE((char *) m->nameserver); -} - -time_t mg_mgr_poll(struct mg_mgr *m, int timeout_ms) { - int i; - time_t now = 0; /* oh GCC, seriously ? */ - - if (m->num_ifaces == 0) { - LOG(LL_ERROR, ("cannot poll: no interfaces")); - return 0; - } - - for (i = 0; i < m->num_ifaces; i++) { - now = m->ifaces[i]->vtable->poll(m->ifaces[i], timeout_ms); - } - return now; -} - -int mg_vprintf(struct mg_connection *nc, const char *fmt, va_list ap) { - char mem[MG_VPRINTF_BUFFER_SIZE], *buf = mem; - int len; - - if ((len = mg_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) { - mg_send(nc, buf, len); - } - if (buf != mem && buf != NULL) { - MG_FREE(buf); /* LCOV_EXCL_LINE */ - } /* LCOV_EXCL_LINE */ - - return len; -} - -int mg_printf(struct mg_connection *conn, const char *fmt, ...) { - int len; - va_list ap; - va_start(ap, fmt); - len = mg_vprintf(conn, fmt, ap); - va_end(ap); - return len; -} - -#if MG_ENABLE_SYNC_RESOLVER -/* TODO(lsm): use non-blocking resolver */ -static int mg_resolve2(const char *host, struct in_addr *ina) { -#if MG_ENABLE_GETADDRINFO - int rv = 0; - struct addrinfo hints, *servinfo, *p; - struct sockaddr_in *h = NULL; - memset(&hints, 0, sizeof hints); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - if ((rv = getaddrinfo(host, NULL, NULL, &servinfo)) != 0) { - DBG(("getaddrinfo(%s) failed: %s", host, strerror(mg_get_errno()))); - return 0; - } - for (p = servinfo; p != NULL; p = p->ai_next) { - memcpy(&h, &p->ai_addr, sizeof(struct sockaddr_in *)); - memcpy(ina, &h->sin_addr, sizeof(ina)); - } - freeaddrinfo(servinfo); - return 1; -#else - struct hostent *he; - if ((he = gethostbyname(host)) == NULL) { - DBG(("gethostbyname(%s) failed: %s", host, strerror(mg_get_errno()))); - } else { - memcpy(ina, he->h_addr_list[0], sizeof(*ina)); - return 1; - } - return 0; -#endif /* MG_ENABLE_GETADDRINFO */ -} - -int mg_resolve(const char *host, char *buf, size_t n) { - struct in_addr ad; - return mg_resolve2(host, &ad) ? snprintf(buf, n, "%s", inet_ntoa(ad)) : 0; -} -#endif /* MG_ENABLE_SYNC_RESOLVER */ - -MG_INTERNAL struct mg_connection *mg_create_connection_base( - struct mg_mgr *mgr, mg_event_handler_t callback, - struct mg_add_sock_opts opts) { - struct mg_connection *conn; - - if ((conn = (struct mg_connection *) MG_CALLOC(1, sizeof(*conn))) != NULL) { - conn->sock = INVALID_SOCKET; - conn->handler = callback; - conn->mgr = mgr; - conn->last_io_time = (time_t) mg_time(); - conn->iface = - (opts.iface != NULL ? opts.iface : mgr->ifaces[MG_MAIN_IFACE]); - conn->flags = opts.flags & _MG_ALLOWED_CONNECT_FLAGS_MASK; - conn->user_data = opts.user_data; - /* - * SIZE_MAX is defined as a long long constant in - * system headers on some platforms and so it - * doesn't compile with pedantic ansi flags. - */ - conn->recv_mbuf_limit = ~0; - } else { - MG_SET_PTRPTR(opts.error_string, "failed to create connection"); - } - - return conn; -} - -MG_INTERNAL struct mg_connection *mg_create_connection( - struct mg_mgr *mgr, mg_event_handler_t callback, - struct mg_add_sock_opts opts) { - struct mg_connection *conn = mg_create_connection_base(mgr, callback, opts); - - if (conn != NULL && !conn->iface->vtable->create_conn(conn)) { - MG_FREE(conn); - conn = NULL; - } - if (conn == NULL) { - MG_SET_PTRPTR(opts.error_string, "failed to init connection"); - } - - return conn; -} - -/* - * Address format: [PROTO://][HOST]:PORT - * - * HOST could be IPv4/IPv6 address or a host name. - * `host` is a destination buffer to hold parsed HOST part. Should be at least - * MG_MAX_HOST_LEN bytes long. - * `proto` is a returned socket type, either SOCK_STREAM or SOCK_DGRAM - * - * Return: - * -1 on parse error - * 0 if HOST needs DNS lookup - * >0 length of the address string - */ -MG_INTERNAL int mg_parse_address(const char *str, union socket_address *sa, - int *proto, char *host, size_t host_len) { - unsigned int a, b, c, d, port = 0; - int ch, len = 0; -#if MG_ENABLE_IPV6 - char buf[100]; -#endif - - /* - * MacOS needs that. If we do not zero it, subsequent bind() will fail. - * Also, all-zeroes in the socket address means binding to all addresses - * for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT). - */ - memset(sa, 0, sizeof(*sa)); - sa->sin.sin_family = AF_INET; - - *proto = SOCK_STREAM; - - if (strncmp(str, "udp://", 6) == 0) { - str += 6; - *proto = SOCK_DGRAM; - } else if (strncmp(str, "tcp://", 6) == 0) { - str += 6; - } - - if (sscanf(str, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) { - /* Bind to a specific IPv4 address, e.g. 192.168.1.5:8080 */ - sa->sin.sin_addr.s_addr = - htonl(((uint32_t) a << 24) | ((uint32_t) b << 16) | c << 8 | d); - sa->sin.sin_port = htons((uint16_t) port); -#if MG_ENABLE_IPV6 - } else if (sscanf(str, "[%99[^]]]:%u%n", buf, &port, &len) == 2 && - inet_pton(AF_INET6, buf, &sa->sin6.sin6_addr)) { - /* IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080 */ - sa->sin6.sin6_family = AF_INET6; - sa->sin.sin_port = htons((uint16_t) port); -#endif -#if MG_ENABLE_ASYNC_RESOLVER - } else if (strlen(str) < host_len && - sscanf(str, "%[^ :]:%u%n", host, &port, &len) == 2) { - sa->sin.sin_port = htons((uint16_t) port); - if (mg_resolve_from_hosts_file(host, sa) != 0) { - /* - * if resolving from hosts file failed and the host - * we are trying to resolve is `localhost` - we should - * try to resolve it using `gethostbyname` and do not try - * to resolve it via DNS server if gethostbyname has failed too - */ - if (mg_ncasecmp(host, "localhost", 9) != 0) { - return 0; - } - -#if MG_ENABLE_SYNC_RESOLVER - if (!mg_resolve2(host, &sa->sin.sin_addr)) { - return -1; - } -#else - return -1; -#endif - } -#endif - } else if (sscanf(str, ":%u%n", &port, &len) == 1 || - sscanf(str, "%u%n", &port, &len) == 1) { - /* If only port is specified, bind to IPv4, INADDR_ANY */ - sa->sin.sin_port = htons((uint16_t) port); - } else { - return -1; - } - - /* Required for MG_ENABLE_ASYNC_RESOLVER=0 */ - (void) host; - (void) host_len; - - ch = str[len]; /* Character that follows the address */ - return port < 0xffffUL && (ch == '\0' || ch == ',' || isspace(ch)) ? len : -1; -} - -struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc) { - struct mg_add_sock_opts opts; - struct mg_connection *nc; - memset(&opts, 0, sizeof(opts)); - nc = mg_create_connection(lc->mgr, lc->handler, opts); - if (nc == NULL) return NULL; - nc->listener = lc; - nc->proto_handler = lc->proto_handler; - nc->user_data = lc->user_data; - nc->recv_mbuf_limit = lc->recv_mbuf_limit; - nc->iface = lc->iface; - if (lc->flags & MG_F_SSL) nc->flags |= MG_F_SSL; - mg_add_conn(nc->mgr, nc); - DBG(("%p %p %d %d", lc, nc, nc->sock, (int) nc->flags)); - return nc; -} - -void mg_if_accept_tcp_cb(struct mg_connection *nc, union socket_address *sa, - size_t sa_len) { - (void) sa_len; - nc->sa = *sa; - mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa); -} - -void mg_send(struct mg_connection *nc, const void *buf, int len) { - nc->last_io_time = (time_t) mg_time(); - if (nc->flags & MG_F_UDP) { - nc->iface->vtable->udp_send(nc, buf, len); - } else { - nc->iface->vtable->tcp_send(nc, buf, len); - } -} - -void mg_if_sent_cb(struct mg_connection *nc, int num_sent) { - DBG(("%p %d", nc, num_sent)); -#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP - if (nc->mgr && nc->mgr->hexdump_file != NULL) { - char *buf = nc->send_mbuf.buf; - mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, num_sent, MG_EV_SEND); - } -#endif - if (num_sent < 0) { - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } else { - mbuf_remove(&nc->send_mbuf, num_sent); - mbuf_trim(&nc->send_mbuf); - } - mg_call(nc, NULL, nc->user_data, MG_EV_SEND, &num_sent); -} - -MG_INTERNAL void mg_recv_common(struct mg_connection *nc, void *buf, int len, - int own) { - DBG(("%p %d %u", nc, len, (unsigned int) nc->recv_mbuf.len)); - -#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP - if (nc->mgr && nc->mgr->hexdump_file != NULL) { - mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, len, MG_EV_RECV); - } -#endif - - if (nc->flags & MG_F_CLOSE_IMMEDIATELY) { - DBG(("%p discarded %d bytes", nc, len)); - /* - * This connection will not survive next poll. Do not deliver events, - * send data to /dev/null without acking. - */ - if (own) { - MG_FREE(buf); - } - return; - } - nc->last_io_time = (time_t) mg_time(); - if (!own) { - mbuf_append(&nc->recv_mbuf, buf, len); - } else if (nc->recv_mbuf.len == 0) { - /* Adopt buf as recv_mbuf's backing store. */ - mbuf_free(&nc->recv_mbuf); - nc->recv_mbuf.buf = (char *) buf; - nc->recv_mbuf.size = nc->recv_mbuf.len = len; - } else { - mbuf_append(&nc->recv_mbuf, buf, len); - MG_FREE(buf); - } - mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &len); -} - -void mg_if_recv_tcp_cb(struct mg_connection *nc, void *buf, int len, int own) { - mg_recv_common(nc, buf, len, own); -} - -void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, - union socket_address *sa, size_t sa_len) { - assert(nc->flags & MG_F_UDP); - DBG(("%p %u", nc, (unsigned int) len)); - if (nc->flags & MG_F_LISTENING) { - struct mg_connection *lc = nc; - /* - * Do we have an existing connection for this source? - * This is very inefficient for long connection lists. - */ - for (nc = mg_next(lc->mgr, NULL); nc != NULL; nc = mg_next(lc->mgr, nc)) { - if (memcmp(&nc->sa.sa, &sa->sa, sa_len) == 0 && nc->listener == lc) { - break; - } - } - if (nc == NULL) { - struct mg_add_sock_opts opts; - memset(&opts, 0, sizeof(opts)); - /* Create fake connection w/out sock initialization */ - nc = mg_create_connection_base(lc->mgr, lc->handler, opts); - if (nc != NULL) { - nc->sock = lc->sock; - nc->listener = lc; - nc->sa = *sa; - nc->proto_handler = lc->proto_handler; - nc->user_data = lc->user_data; - nc->recv_mbuf_limit = lc->recv_mbuf_limit; - nc->flags = MG_F_UDP; - /* - * Long-lived UDP "connections" i.e. interactions that involve more - * than one request and response are rare, most are transactional: - * response is sent and the "connection" is closed. Or - should be. - * But users (including ourselves) tend to forget about that part, - * because UDP is connectionless and one does not think about - * processing a UDP request as handling a connection that needs to be - * closed. Thus, we begin with SEND_AND_CLOSE flag set, which should - * be a reasonable default for most use cases, but it is possible to - * turn it off the connection should be kept alive after processing. - */ - nc->flags |= MG_F_SEND_AND_CLOSE; - mg_add_conn(lc->mgr, nc); - mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa); - } else { - DBG(("OOM")); - /* No return here, we still need to drop on the floor */ - } - } - } - if (nc != NULL) { - mg_recv_common(nc, buf, len, 1); - } else { - /* Drop on the floor. */ - MG_FREE(buf); - } -} - -/* - * Schedules an async connect for a resolved address and proto. - * Called from two places: `mg_connect_opt()` and from async resolver. - * When called from the async resolver, it must trigger `MG_EV_CONNECT` event - * with a failure flag to indicate connection failure. - */ -MG_INTERNAL struct mg_connection *mg_do_connect(struct mg_connection *nc, - int proto, - union socket_address *sa) { - DBG(("%p %s://%s:%hu", nc, proto == SOCK_DGRAM ? "udp" : "tcp", - inet_ntoa(sa->sin.sin_addr), ntohs(sa->sin.sin_port))); - - nc->flags |= MG_F_CONNECTING; - if (proto == SOCK_DGRAM) { - nc->iface->vtable->connect_udp(nc); - } else { - nc->iface->vtable->connect_tcp(nc, sa); - } - mg_add_conn(nc->mgr, nc); - return nc; -} - -void mg_if_connect_cb(struct mg_connection *nc, int err) { - DBG(("%p connect, err=%d", nc, err)); - nc->flags &= ~MG_F_CONNECTING; - if (err != 0) { - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } - mg_call(nc, NULL, nc->user_data, MG_EV_CONNECT, &err); -} - -#if MG_ENABLE_ASYNC_RESOLVER -/* - * Callback for the async resolver on mg_connect_opt() call. - * Main task of this function is to trigger MG_EV_CONNECT event with - * either failure (and dealloc the connection) - * or success (and proceed with connect() - */ -static void resolve_cb(struct mg_dns_message *msg, void *data, - enum mg_resolve_err e) { - struct mg_connection *nc = (struct mg_connection *) data; - int i; - int failure = -1; - - nc->flags &= ~MG_F_RESOLVING; - if (msg != NULL) { - /* - * Take the first DNS A answer and run... - */ - for (i = 0; i < msg->num_answers; i++) { - if (msg->answers[i].rtype == MG_DNS_A_RECORD) { - /* - * Async resolver guarantees that there is at least one answer. - * TODO(lsm): handle IPv6 answers too - */ - mg_dns_parse_record_data(msg, &msg->answers[i], &nc->sa.sin.sin_addr, - 4); - mg_do_connect(nc, nc->flags & MG_F_UDP ? SOCK_DGRAM : SOCK_STREAM, - &nc->sa); - return; - } - } - } - - if (e == MG_RESOLVE_TIMEOUT) { - double now = mg_time(); - mg_call(nc, NULL, nc->user_data, MG_EV_TIMER, &now); - } - - /* - * If we get there was no MG_DNS_A_RECORD in the answer - */ - mg_call(nc, NULL, nc->user_data, MG_EV_CONNECT, &failure); - mg_call(nc, NULL, nc->user_data, MG_EV_CLOSE, NULL); - mg_destroy_conn(nc, 1 /* destroy_if */); -} -#endif - -struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *address, - MG_CB(mg_event_handler_t callback, - void *user_data)) { - struct mg_connect_opts opts; - memset(&opts, 0, sizeof(opts)); - return mg_connect_opt(mgr, address, MG_CB(callback, user_data), opts); -} - -struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address, - MG_CB(mg_event_handler_t callback, - void *user_data), - struct mg_connect_opts opts) { - struct mg_connection *nc = NULL; - int proto, rc; - struct mg_add_sock_opts add_sock_opts; - char host[MG_MAX_HOST_LEN]; - - MG_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts); - - if ((nc = mg_create_connection(mgr, callback, add_sock_opts)) == NULL) { - return NULL; - } - - if ((rc = mg_parse_address(address, &nc->sa, &proto, host, sizeof(host))) < - 0) { - /* Address is malformed */ - MG_SET_PTRPTR(opts.error_string, "cannot parse address"); - mg_destroy_conn(nc, 1 /* destroy_if */); - return NULL; - } - - nc->flags |= opts.flags & _MG_ALLOWED_CONNECT_FLAGS_MASK; - nc->flags |= (proto == SOCK_DGRAM) ? MG_F_UDP : 0; -#if MG_ENABLE_CALLBACK_USERDATA - nc->user_data = user_data; -#else - nc->user_data = opts.user_data; -#endif - -#if MG_ENABLE_SSL - DBG(("%p %s %s,%s,%s", nc, address, (opts.ssl_cert ? opts.ssl_cert : "-"), - (opts.ssl_key ? opts.ssl_key : "-"), - (opts.ssl_ca_cert ? opts.ssl_ca_cert : "-"))); - - if (opts.ssl_cert != NULL || opts.ssl_ca_cert != NULL || - opts.ssl_psk_identity != NULL) { - const char *err_msg = NULL; - struct mg_ssl_if_conn_params params; - if (nc->flags & MG_F_UDP) { - MG_SET_PTRPTR(opts.error_string, "SSL for UDP is not supported"); - mg_destroy_conn(nc, 1 /* destroy_if */); - return NULL; - } - memset(¶ms, 0, sizeof(params)); - params.cert = opts.ssl_cert; - params.key = opts.ssl_key; - params.ca_cert = opts.ssl_ca_cert; - params.cipher_suites = opts.ssl_cipher_suites; - params.psk_identity = opts.ssl_psk_identity; - params.psk_key = opts.ssl_psk_key; - if (opts.ssl_ca_cert != NULL) { - if (opts.ssl_server_name != NULL) { - if (strcmp(opts.ssl_server_name, "*") != 0) { - params.server_name = opts.ssl_server_name; - } - } else if (rc == 0) { /* If it's a DNS name, use host. */ - params.server_name = host; - } - } - if (mg_ssl_if_conn_init(nc, ¶ms, &err_msg) != MG_SSL_OK) { - MG_SET_PTRPTR(opts.error_string, err_msg); - mg_destroy_conn(nc, 1 /* destroy_if */); - return NULL; - } - nc->flags |= MG_F_SSL; - } -#endif /* MG_ENABLE_SSL */ - - if (rc == 0) { -#if MG_ENABLE_ASYNC_RESOLVER - /* - * DNS resolution is required for host. - * mg_parse_address() fills port in nc->sa, which we pass to resolve_cb() - */ - struct mg_connection *dns_conn = NULL; - struct mg_resolve_async_opts o; - memset(&o, 0, sizeof(o)); - o.dns_conn = &dns_conn; - o.nameserver = opts.nameserver; - if (mg_resolve_async_opt(nc->mgr, host, MG_DNS_A_RECORD, resolve_cb, nc, - o) != 0) { - MG_SET_PTRPTR(opts.error_string, "cannot schedule DNS lookup"); - mg_destroy_conn(nc, 1 /* destroy_if */); - return NULL; - } - nc->priv_2 = dns_conn; - nc->flags |= MG_F_RESOLVING; - return nc; -#else - MG_SET_PTRPTR(opts.error_string, "Resolver is disabled"); - mg_destroy_conn(nc, 1 /* destroy_if */); - return NULL; -#endif - } else { - /* Address is parsed and resolved to IP. proceed with connect() */ - return mg_do_connect(nc, proto, &nc->sa); - } -} - -struct mg_connection *mg_bind(struct mg_mgr *srv, const char *address, - MG_CB(mg_event_handler_t event_handler, - void *user_data)) { - struct mg_bind_opts opts; - memset(&opts, 0, sizeof(opts)); - return mg_bind_opt(srv, address, MG_CB(event_handler, user_data), opts); -} - -struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address, - MG_CB(mg_event_handler_t callback, - void *user_data), - struct mg_bind_opts opts) { - union socket_address sa; - struct mg_connection *nc = NULL; - int proto, rc; - struct mg_add_sock_opts add_sock_opts; - char host[MG_MAX_HOST_LEN]; - -#if MG_ENABLE_CALLBACK_USERDATA - opts.user_data = user_data; -#endif - - if (callback == NULL) { - MG_SET_PTRPTR(opts.error_string, "handler is required"); - return NULL; - } - - MG_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts); - - if (mg_parse_address(address, &sa, &proto, host, sizeof(host)) <= 0) { - MG_SET_PTRPTR(opts.error_string, "cannot parse address"); - return NULL; - } - - nc = mg_create_connection(mgr, callback, add_sock_opts); - if (nc == NULL) { - return NULL; - } - - nc->sa = sa; - nc->flags |= MG_F_LISTENING; - if (proto == SOCK_DGRAM) nc->flags |= MG_F_UDP; - -#if MG_ENABLE_SSL - DBG(("%p %s %s,%s,%s", nc, address, (opts.ssl_cert ? opts.ssl_cert : "-"), - (opts.ssl_key ? opts.ssl_key : "-"), - (opts.ssl_ca_cert ? opts.ssl_ca_cert : "-"))); - - if (opts.ssl_cert != NULL || opts.ssl_ca_cert != NULL) { - const char *err_msg = NULL; - struct mg_ssl_if_conn_params params; - if (nc->flags & MG_F_UDP) { - MG_SET_PTRPTR(opts.error_string, "SSL for UDP is not supported"); - mg_destroy_conn(nc, 1 /* destroy_if */); - return NULL; - } - memset(¶ms, 0, sizeof(params)); - params.cert = opts.ssl_cert; - params.key = opts.ssl_key; - params.ca_cert = opts.ssl_ca_cert; - params.cipher_suites = opts.ssl_cipher_suites; - if (mg_ssl_if_conn_init(nc, ¶ms, &err_msg) != MG_SSL_OK) { - MG_SET_PTRPTR(opts.error_string, err_msg); - mg_destroy_conn(nc, 1 /* destroy_if */); - return NULL; - } - nc->flags |= MG_F_SSL; - } -#endif /* MG_ENABLE_SSL */ - - if (nc->flags & MG_F_UDP) { - rc = nc->iface->vtable->listen_udp(nc, &nc->sa); - } else { - rc = nc->iface->vtable->listen_tcp(nc, &nc->sa); - } - if (rc != 0) { - DBG(("Failed to open listener: %d", rc)); - MG_SET_PTRPTR(opts.error_string, "failed to open listener"); - mg_destroy_conn(nc, 1 /* destroy_if */); - return NULL; - } - mg_add_conn(nc->mgr, nc); - - return nc; -} - -struct mg_connection *mg_next(struct mg_mgr *s, struct mg_connection *conn) { - return conn == NULL ? s->active_connections : conn->next; -} - -#if MG_ENABLE_BROADCAST -void mg_broadcast(struct mg_mgr *mgr, mg_event_handler_t cb, void *data, - size_t len) { - struct ctl_msg ctl_msg; - - /* - * Mongoose manager has a socketpair, `struct mg_mgr::ctl`, - * where `mg_broadcast()` pushes the message. - * `mg_mgr_poll()` wakes up, reads a message from the socket pair, and calls - * specified callback for each connection. Thus the callback function executes - * in event manager thread. - */ - if (mgr->ctl[0] != INVALID_SOCKET && data != NULL && - len < sizeof(ctl_msg.message)) { - size_t dummy; - - ctl_msg.callback = cb; - memcpy(ctl_msg.message, data, len); - dummy = MG_SEND_FUNC(mgr->ctl[0], (char *) &ctl_msg, - offsetof(struct ctl_msg, message) + len, 0); - dummy = MG_RECV_FUNC(mgr->ctl[0], (char *) &len, 1, 0); - (void) dummy; /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509 */ - } -} -#endif /* MG_ENABLE_BROADCAST */ - -static int isbyte(int n) { - return n >= 0 && n <= 255; -} - -static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) { - int n, a, b, c, d, slash = 32, len = 0; - - if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 || - sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) && - isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) && slash >= 0 && - slash < 33) { - len = n; - *net = - ((uint32_t) a << 24) | ((uint32_t) b << 16) | ((uint32_t) c << 8) | d; - *mask = slash ? 0xffffffffU << (32 - slash) : 0; - } - - return len; -} - -int mg_check_ip_acl(const char *acl, uint32_t remote_ip) { - int allowed, flag; - uint32_t net, mask; - struct mg_str vec; - - /* If any ACL is set, deny by default */ - allowed = (acl == NULL || *acl == '\0') ? '+' : '-'; - - while ((acl = mg_next_comma_list_entry(acl, &vec, NULL)) != NULL) { - flag = vec.p[0]; - if ((flag != '+' && flag != '-') || - parse_net(&vec.p[1], &net, &mask) == 0) { - return -1; - } - - if (net == (remote_ip & mask)) { - allowed = flag; - } - } - - DBG(("%08x %c", (unsigned int) remote_ip, allowed)); - return allowed == '+'; -} - -/* Move data from one connection to another */ -void mg_forward(struct mg_connection *from, struct mg_connection *to) { - mg_send(to, from->recv_mbuf.buf, from->recv_mbuf.len); - mbuf_remove(&from->recv_mbuf, from->recv_mbuf.len); -} - -double mg_set_timer(struct mg_connection *c, double timestamp) { - double result = c->ev_timer_time; - c->ev_timer_time = timestamp; - /* - * If this connection is resolving, it's not in the list of active - * connections, so not processed yet. It has a DNS resolver connection - * linked to it. Set up a timer for the DNS connection. - */ - DBG(("%p %p %d -> %lu", c, c->priv_2, (c->flags & MG_F_RESOLVING ? 1 : 0), - (unsigned long) timestamp)); - if ((c->flags & MG_F_RESOLVING) && c->priv_2 != NULL) { - ((struct mg_connection *) c->priv_2)->ev_timer_time = timestamp; - } - return result; -} - -void mg_sock_set(struct mg_connection *nc, sock_t sock) { - if (sock != INVALID_SOCKET) { - nc->iface->vtable->sock_set(nc, sock); - } -} - -void mg_if_get_conn_addr(struct mg_connection *nc, int remote, - union socket_address *sa) { - nc->iface->vtable->get_conn_addr(nc, remote, sa); -} - -struct mg_connection *mg_add_sock_opt(struct mg_mgr *s, sock_t sock, - MG_CB(mg_event_handler_t callback, - void *user_data), - struct mg_add_sock_opts opts) { -#if MG_ENABLE_CALLBACK_USERDATA - opts.user_data = user_data; -#endif - - struct mg_connection *nc = mg_create_connection_base(s, callback, opts); - if (nc != NULL) { - mg_sock_set(nc, sock); - mg_add_conn(nc->mgr, nc); - } - return nc; -} - -struct mg_connection *mg_add_sock(struct mg_mgr *s, sock_t sock, - MG_CB(mg_event_handler_t callback, - void *user_data)) { - struct mg_add_sock_opts opts; - memset(&opts, 0, sizeof(opts)); - return mg_add_sock_opt(s, sock, MG_CB(callback, user_data), opts); -} - -double mg_time(void) { - return cs_time(); -} diff --git a/src/mongoose-6.11/src/mg_net.h b/src/mongoose-6.11/src/mg_net.h deleted file mode 100644 index 90b42b4..0000000 --- a/src/mongoose-6.11/src/mg_net.h +++ /dev/null @@ -1,591 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - * This software is dual-licensed: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. For the terms of this - * license, see . - * - * You are free to use this software under the terms of the GNU General - * Public License, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * Alternatively, you can license this software under a commercial - * license, as set out in . - */ - -/* - * === Core API: TCP/UDP/SSL - * - * NOTE: Mongoose manager is single threaded. It does not protect - * its data structures by mutexes, therefore all functions that are dealing - * with a particular event manager should be called from the same thread, - * with exception of the `mg_broadcast()` function. It is fine to have different - * event managers handled by different threads. - */ - -#ifndef CS_MONGOOSE_SRC_NET_H_ -#define CS_MONGOOSE_SRC_NET_H_ - -#include "mg_common.h" -#include "mg_net_if.h" -#include "common/mbuf.h" - -#ifndef MG_VPRINTF_BUFFER_SIZE -#define MG_VPRINTF_BUFFER_SIZE 100 -#endif - -#ifdef MG_USE_READ_WRITE -#define MG_RECV_FUNC(s, b, l, f) read(s, b, l) -#define MG_SEND_FUNC(s, b, l, f) write(s, b, l) -#else -#define MG_RECV_FUNC(s, b, l, f) recv(s, b, l, f) -#define MG_SEND_FUNC(s, b, l, f) send(s, b, l, f) -#endif - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -union socket_address { - struct sockaddr sa; - struct sockaddr_in sin; -#if MG_ENABLE_IPV6 - struct sockaddr_in6 sin6; -#else - struct sockaddr sin6; -#endif -}; - -struct mg_connection; - -/* - * Callback function (event handler) prototype. Must be defined by the user. - * Mongoose calls the event handler, passing the events defined below. - */ -typedef void (*mg_event_handler_t)(struct mg_connection *nc, int ev, - void *ev_data MG_UD_ARG(void *user_data)); - -/* Events. Meaning of event parameter (evp) is given in the comment. */ -#define MG_EV_POLL 0 /* Sent to each connection on each mg_mgr_poll() call */ -#define MG_EV_ACCEPT 1 /* New connection accepted. union socket_address * */ -#define MG_EV_CONNECT 2 /* connect() succeeded or failed. int * */ -#define MG_EV_RECV 3 /* Data has been received. int *num_bytes */ -#define MG_EV_SEND 4 /* Data has been written to a socket. int *num_bytes */ -#define MG_EV_CLOSE 5 /* Connection is closed. NULL */ -#define MG_EV_TIMER 6 /* now >= conn->ev_timer_time. double * */ - -/* - * Mongoose event manager. - */ -struct mg_mgr { - struct mg_connection *active_connections; -#if MG_ENABLE_HEXDUMP - const char *hexdump_file; /* Debug hexdump file path */ -#endif -#if MG_ENABLE_BROADCAST - sock_t ctl[2]; /* Socketpair for mg_broadcast() */ -#endif - void *user_data; /* User data */ - int num_ifaces; - struct mg_iface **ifaces; /* network interfaces */ - const char *nameserver; /* DNS server to use */ -}; - -/* - * Mongoose connection. - */ -struct mg_connection { - struct mg_connection *next, *prev; /* mg_mgr::active_connections linkage */ - struct mg_connection *listener; /* Set only for accept()-ed connections */ - struct mg_mgr *mgr; /* Pointer to containing manager */ - - sock_t sock; /* Socket to the remote peer */ - int err; - union socket_address sa; /* Remote peer address */ - size_t recv_mbuf_limit; /* Max size of recv buffer */ - struct mbuf recv_mbuf; /* Received data */ - struct mbuf send_mbuf; /* Data scheduled for sending */ - time_t last_io_time; /* Timestamp of the last socket IO */ - double ev_timer_time; /* Timestamp of the future MG_EV_TIMER */ -#if MG_ENABLE_SSL - void *ssl_if_data; /* SSL library data. */ -#endif - mg_event_handler_t proto_handler; /* Protocol-specific event handler */ - void *proto_data; /* Protocol-specific data */ - void (*proto_data_destructor)(void *proto_data); - mg_event_handler_t handler; /* Event handler function */ - void *user_data; /* User-specific data */ - union { - void *v; - /* - * the C standard is fussy about fitting function pointers into - * void pointers, since some archs might have fat pointers for functions. - */ - mg_event_handler_t f; - } priv_1; - void *priv_2; - void *mgr_data; /* Implementation-specific event manager's data. */ - struct mg_iface *iface; - unsigned long flags; -/* Flags set by Mongoose */ -#define MG_F_LISTENING (1 << 0) /* This connection is listening */ -#define MG_F_UDP (1 << 1) /* This connection is UDP */ -#define MG_F_RESOLVING (1 << 2) /* Waiting for async resolver */ -#define MG_F_CONNECTING (1 << 3) /* connect() call in progress */ -#define MG_F_SSL (1 << 4) /* SSL is enabled on the connection */ -#define MG_F_SSL_HANDSHAKE_DONE (1 << 5) /* SSL hanshake has completed */ -#define MG_F_WANT_READ (1 << 6) /* SSL specific */ -#define MG_F_WANT_WRITE (1 << 7) /* SSL specific */ -#define MG_F_IS_WEBSOCKET (1 << 8) /* Websocket specific */ - -/* Flags that are settable by user */ -#define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */ -#define MG_F_CLOSE_IMMEDIATELY (1 << 11) /* Disconnect */ -#define MG_F_WEBSOCKET_NO_DEFRAG (1 << 12) /* Websocket specific */ -#define MG_F_DELETE_CHUNK (1 << 13) /* HTTP specific */ -#define MG_F_ENABLE_BROADCAST (1 << 14) /* Allow broadcast address usage */ - -#define MG_F_USER_1 (1 << 20) /* Flags left for application */ -#define MG_F_USER_2 (1 << 21) -#define MG_F_USER_3 (1 << 22) -#define MG_F_USER_4 (1 << 23) -#define MG_F_USER_5 (1 << 24) -#define MG_F_USER_6 (1 << 25) -}; - -/* - * Initialise Mongoose manager. Side effect: ignores SIGPIPE signal. - * `mgr->user_data` field will be initialised with a `user_data` parameter. - * That is an arbitrary pointer, where the user code can associate some data - * with the particular Mongoose manager. For example, a C++ wrapper class - * could be written in which case `user_data` can hold a pointer to the - * class instance. - */ -void mg_mgr_init(struct mg_mgr *mgr, void *user_data); - -/* - * Optional parameters to `mg_mgr_init_opt()`. - * - * If `main_iface` is not NULL, it will be used as the main interface in the - * default interface set. The pointer will be free'd by `mg_mgr_free`. - * Otherwise, the main interface will be autodetected based on the current - * platform. - * - * If `num_ifaces` is 0 and `ifaces` is NULL, the default interface set will be - * used. - * This is an advanced option, as it requires you to construct a full interface - * set, including special networking interfaces required by some optional - * features such as TCP tunneling. Memory backing `ifaces` and each of the - * `num_ifaces` pointers it contains will be reclaimed by `mg_mgr_free`. - */ -struct mg_mgr_init_opts { - const struct mg_iface_vtable *main_iface; - int num_ifaces; - const struct mg_iface_vtable **ifaces; - const char *nameserver; -}; - -/* - * Like `mg_mgr_init` but with more options. - * - * Notably, this allows you to create a manger and choose - * dynamically which networking interface implementation to use. - */ -void mg_mgr_init_opt(struct mg_mgr *mgr, void *user_data, - struct mg_mgr_init_opts opts); - -/* - * De-initialises Mongoose manager. - * - * Closes and deallocates all active connections. - */ -void mg_mgr_free(struct mg_mgr *); - -/* - * This function performs the actual IO and must be called in a loop - * (an event loop). It returns the current timestamp. - * `milli` is the maximum number of milliseconds to sleep. - * `mg_mgr_poll()` checks all connections for IO readiness. If at least one - * of the connections is IO-ready, `mg_mgr_poll()` triggers the respective - * event handlers and returns. - */ -time_t mg_mgr_poll(struct mg_mgr *, int milli); - -#if MG_ENABLE_BROADCAST -/* - * Passes a message of a given length to all connections. - * - * Must be called from a thread that does NOT call `mg_mgr_poll()`. - * Note that `mg_broadcast()` is the only function - * that can be, and must be, called from a different (non-IO) thread. - * - * `func` callback function will be called by the IO thread for each - * connection. When called, the event will be `MG_EV_POLL`, and a message will - * be passed as the `ev_data` pointer. Maximum message size is capped - * by `MG_CTL_MSG_MESSAGE_SIZE` which is set to 8192 bytes. - */ -void mg_broadcast(struct mg_mgr *mgr, mg_event_handler_t cb, void *data, - size_t len); -#endif - -/* - * Iterates over all active connections. - * - * Returns the next connection from the list - * of active connections or `NULL` if there are no more connections. Below - * is the iteration idiom: - * - * ```c - * for (c = mg_next(srv, NULL); c != NULL; c = mg_next(srv, c)) { - * // Do something with connection `c` - * } - * ``` - */ -struct mg_connection *mg_next(struct mg_mgr *mgr, struct mg_connection *c); - -/* - * Optional parameters to `mg_add_sock_opt()`. - * - * `flags` is an initial `struct mg_connection::flags` bitmask to set, - * see `MG_F_*` flags definitions. - */ -struct mg_add_sock_opts { - void *user_data; /* Initial value for connection's user_data */ - unsigned int flags; /* Initial connection flags */ - const char **error_string; /* Placeholder for the error string */ - struct mg_iface *iface; /* Interface instance */ -}; - -/* - * Creates a connection, associates it with the given socket and event handler - * and adds it to the manager. - * - * For more options see the `mg_add_sock_opt` variant. - */ -struct mg_connection *mg_add_sock(struct mg_mgr *mgr, sock_t sock, - MG_CB(mg_event_handler_t handler, - void *user_data)); - -/* - * Creates a connection, associates it with the given socket and event handler - * and adds to the manager. - * - * See the `mg_add_sock_opts` structure for a description of the options. - */ -struct mg_connection *mg_add_sock_opt(struct mg_mgr *mgr, sock_t sock, - MG_CB(mg_event_handler_t handler, - void *user_data), - struct mg_add_sock_opts opts); - -/* - * Optional parameters to `mg_bind_opt()`. - * - * `flags` is an initial `struct mg_connection::flags` bitmask to set, - * see `MG_F_*` flags definitions. - */ -struct mg_bind_opts { - void *user_data; /* Initial value for connection's user_data */ - unsigned int flags; /* Extra connection flags */ - const char **error_string; /* Placeholder for the error string */ - struct mg_iface *iface; /* Interface instance */ -#if MG_ENABLE_SSL - /* - * SSL settings. - * - * Server certificate to present to clients or client certificate to - * present to tunnel dispatcher (for tunneled connections). - */ - const char *ssl_cert; - /* Private key corresponding to the certificate. If ssl_cert is set but - * ssl_key is not, ssl_cert is used. */ - const char *ssl_key; - /* CA bundle used to verify client certificates or tunnel dispatchers. */ - const char *ssl_ca_cert; - /* Colon-delimited list of acceptable cipher suites. - * Names depend on the library used, for example: - * - * ECDH-ECDSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256 (OpenSSL) - * TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256 - * (mbedTLS) - * - * For OpenSSL the list can be obtained by running "openssl ciphers". - * For mbedTLS, names can be found in library/ssl_ciphersuites.c - * If NULL, a reasonable default is used. - */ - const char *ssl_cipher_suites; -#endif -}; - -/* - * Creates a listening connection. - * - * See `mg_bind_opt` for full documentation. - */ -struct mg_connection *mg_bind(struct mg_mgr *mgr, const char *address, - MG_CB(mg_event_handler_t handler, - void *user_data)); -/* - * Creates a listening connection. - * - * The `address` parameter specifies which address to bind to. It's format is - * the same as for the `mg_connect()` call, where `HOST` part is optional. - * `address` can be just a port number, e.g. `:8000`. To bind to a specific - * interface, an IP address can be specified, e.g. `1.2.3.4:8000`. By default, - * a TCP connection is created. To create UDP connection, prepend `udp://` - * prefix, e.g. `udp://:8000`. To summarize, `address` parameter has following - * format: `[PROTO://][IP_ADDRESS]:PORT`, where `PROTO` could be `tcp` or - * `udp`. - * - * See the `mg_bind_opts` structure for a description of the optional - * parameters. - * - * Returns a new listening connection or `NULL` on error. - * NOTE: The connection remains owned by the manager, do not free(). - */ -struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address, - MG_CB(mg_event_handler_t handler, - void *user_data), - struct mg_bind_opts opts); - -/* Optional parameters to `mg_connect_opt()` */ -struct mg_connect_opts { - void *user_data; /* Initial value for connection's user_data */ - unsigned int flags; /* Extra connection flags */ - const char **error_string; /* Placeholder for the error string */ - struct mg_iface *iface; /* Interface instance */ - const char *nameserver; /* DNS server to use, NULL for default */ -#if MG_ENABLE_SSL - /* - * SSL settings. - * Client certificate to present to the server. - */ - const char *ssl_cert; - /* - * Private key corresponding to the certificate. - * If ssl_cert is set but ssl_key is not, ssl_cert is used. - */ - const char *ssl_key; - /* - * Verify server certificate using this CA bundle. If set to "*", then SSL - * is enabled but no cert verification is performed. - */ - const char *ssl_ca_cert; - /* Colon-delimited list of acceptable cipher suites. - * Names depend on the library used, for example: - * - * ECDH-ECDSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256 (OpenSSL) - * TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256 - * (mbedTLS) - * - * For OpenSSL the list can be obtained by running "openssl ciphers". - * For mbedTLS, names can be found in library/ssl_ciphersuites.c - * If NULL, a reasonable default is used. - */ - const char *ssl_cipher_suites; - /* - * Server name verification. If ssl_ca_cert is set and the certificate has - * passed verification, its subject will be verified against this string. - * By default (if ssl_server_name is NULL) hostname part of the address will - * be used. Wildcard matching is supported. A special value of "*" disables - * name verification. - */ - const char *ssl_server_name; - /* - * PSK identity and key. Identity is a NUL-terminated string and key is a hex - * string. Key must be either 16 or 32 bytes (32 or 64 hex digits) for AES-128 - * or AES-256 respectively. - * Note: Default list of cipher suites does not include PSK suites, if you - * want to use PSK you will need to set ssl_cipher_suites as well. - */ - const char *ssl_psk_identity; - const char *ssl_psk_key; -#endif -}; - -/* - * Connects to a remote host. - * - * See `mg_connect_opt()` for full documentation. - */ -struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *address, - MG_CB(mg_event_handler_t handler, - void *user_data)); - -/* - * Connects to a remote host. - * - * The `address` format is `[PROTO://]HOST:PORT`. `PROTO` could be `tcp` or - * `udp`. `HOST` could be an IP address, - * IPv6 address (if Mongoose is compiled with `-DMG_ENABLE_IPV6`) or a host - * name. If `HOST` is a name, Mongoose will resolve it asynchronously. Examples - * of valid addresses: `google.com:80`, `udp://1.2.3.4:53`, `10.0.0.1:443`, - * `[::1]:80` - * - * See the `mg_connect_opts` structure for a description of the optional - * parameters. - * - * Returns a new outbound connection or `NULL` on error. - * - * NOTE: The connection remains owned by the manager, do not free(). - * - * NOTE: To enable IPv6 addresses `-DMG_ENABLE_IPV6` should be specified - * in the compilation flags. - * - * NOTE: The new connection will receive `MG_EV_CONNECT` as its first event - * which will report the connect success status. - * If the asynchronous resolution fails or the `connect()` syscall fails for - * whatever reason (e.g. with `ECONNREFUSED` or `ENETUNREACH`), then - * `MG_EV_CONNECT` event will report failure. Code example below: - * - * ```c - * static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { - * int connect_status; - * - * switch (ev) { - * case MG_EV_CONNECT: - * connect_status = * (int *) ev_data; - * if (connect_status == 0) { - * // Success - * } else { - * // Error - * printf("connect() error: %s\n", strerror(connect_status)); - * } - * break; - * ... - * } - * } - * - * ... - * mg_connect(mgr, "my_site.com:80", ev_handler); - * ``` - */ -struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address, - MG_CB(mg_event_handler_t handler, - void *user_data), - struct mg_connect_opts opts); - -#if MG_ENABLE_SSL && MG_NET_IF != MG_NET_IF_SIMPLELINK -/* - * Note: This function is deprecated. Please, use SSL options in - * mg_connect_opt. - * - * Enables SSL for a given connection. - * `cert` is a server certificate file name for a listening connection - * or a client certificate file name for an outgoing connection. - * The certificate files must be in PEM format. The server certificate file - * must contain a certificate, concatenated with a private key, optionally - * concatenated with DH parameters. - * `ca_cert` is a CA certificate or NULL if peer verification is not - * required. - * Return: NULL on success or error message on error. - */ -const char *mg_set_ssl(struct mg_connection *nc, const char *cert, - const char *ca_cert); -#endif - -/* - * Sends data to the connection. - * - * Note that sending functions do not actually push data to the socket. - * They just append data to the output buffer. MG_EV_SEND will be delivered when - * the data has actually been pushed out. - */ -void mg_send(struct mg_connection *, const void *buf, int len); - -/* Enables format string warnings for mg_printf */ -#if defined(__GNUC__) -__attribute__((format(printf, 2, 3))) -#endif -/* don't separate from mg_printf declaration */ - -/* - * Sends `printf`-style formatted data to the connection. - * - * See `mg_send` for more details on send semantics. - */ -int mg_printf(struct mg_connection *, const char *fmt, ...); - -/* Same as `mg_printf()`, but takes `va_list ap` as an argument. */ -int mg_vprintf(struct mg_connection *, const char *fmt, va_list ap); - -/* - * Creates a socket pair. - * `sock_type` can be either `SOCK_STREAM` or `SOCK_DGRAM`. - * Returns 0 on failure and 1 on success. - */ -int mg_socketpair(sock_t[2], int sock_type); - -#if MG_ENABLE_SYNC_RESOLVER -/* - * Convert domain name into IP address. - * - * This is a utility function. If compilation flags have - * `-DMG_ENABLE_GETADDRINFO`, then `getaddrinfo()` call is used for name - * resolution. Otherwise, `gethostbyname()` is used. - * - * CAUTION: this function can block. - * Return 1 on success, 0 on failure. - */ -int mg_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len); -#endif - -/* - * Verify given IP address against the ACL. - * - * `remote_ip` - an IPv4 address to check, in host byte order - * `acl` - a comma separated list of IP subnets: `x.x.x.x/x` or `x.x.x.x`. - * Each subnet is - * prepended by either a - or a + sign. A plus sign means allow, where a - * minus sign means deny. If a subnet mask is omitted, such as `-1.2.3.4`, - * it means that only that single IP address is denied. - * Subnet masks may vary from 0 to 32, inclusive. The default setting - * is to allow all access. On each request the full list is traversed, - * and the last match wins. Example: - * - * `-0.0.0.0/0,+192.168/16` - deny all accesses, only allow 192.168/16 subnet - * - * To learn more about subnet masks, see this - * link:https://en.wikipedia.org/wiki/Subnetwork[Wikipedia page on Subnetwork]. - * - * Returns -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed. - */ -int mg_check_ip_acl(const char *acl, uint32_t remote_ip); - -/* - * Schedules an MG_EV_TIMER event to be delivered at `timestamp` time. - * `timestamp` is UNIX time (the number of seconds since Epoch). It is - * `double` instead of `time_t` to allow for sub-second precision. - * Returns the old timer value. - * - * Example: set the connect timeout to 1.5 seconds: - * - * ``` - * c = mg_connect(&mgr, "cesanta.com", ev_handler); - * mg_set_timer(c, mg_time() + 1.5); - * ... - * - * void ev_handler(struct mg_connection *c, int ev, void *ev_data) { - * switch (ev) { - * case MG_EV_CONNECT: - * mg_set_timer(c, 0); // Clear connect timer - * break; - * case MG_EV_TIMER: - * log("Connect timeout"); - * c->flags |= MG_F_CLOSE_IMMEDIATELY; - * break; - * ``` - */ -double mg_set_timer(struct mg_connection *c, double timestamp); - -/* - * A sub-second precision version of time(). - */ -double mg_time(void); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* CS_MONGOOSE_SRC_NET_H_ */ diff --git a/src/mongoose-6.11/src/mg_net_if.c b/src/mongoose-6.11/src/mg_net_if.c deleted file mode 100644 index 2a93dcf..0000000 --- a/src/mongoose-6.11/src/mg_net_if.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "mg_net_if.h" -#include "mg_internal.h" -#include "mg_net_if_socket.h" - -extern const struct mg_iface_vtable mg_default_iface_vtable; - -const struct mg_iface_vtable *mg_ifaces[] = { - &mg_default_iface_vtable, -}; - -int mg_num_ifaces = (int) (sizeof(mg_ifaces) / sizeof(mg_ifaces[0])); - -struct mg_iface *mg_if_create_iface(const struct mg_iface_vtable *vtable, - struct mg_mgr *mgr) { - struct mg_iface *iface = (struct mg_iface *) MG_CALLOC(1, sizeof(*iface)); - iface->mgr = mgr; - iface->data = NULL; - iface->vtable = vtable; - return iface; -} - -struct mg_iface *mg_find_iface(struct mg_mgr *mgr, - const struct mg_iface_vtable *vtable, - struct mg_iface *from) { - int i = 0; - if (from != NULL) { - for (i = 0; i < mgr->num_ifaces; i++) { - if (mgr->ifaces[i] == from) { - i++; - break; - } - } - } - - for (; i < mgr->num_ifaces; i++) { - if (mgr->ifaces[i]->vtable == vtable) { - return mgr->ifaces[i]; - } - } - return NULL; -} diff --git a/src/mongoose-6.11/src/mg_net_if.h b/src/mongoose-6.11/src/mg_net_if.h deleted file mode 100644 index 43eb2d9..0000000 --- a/src/mongoose-6.11/src/mg_net_if.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_MONGOOSE_SRC_NET_IF_H_ -#define CS_MONGOOSE_SRC_NET_IF_H_ - -#include "common/platform.h" - -/* - * Internal async networking core interface. - * Consists of calls made by the core, which should not block, - * and callbacks back into the core ("..._cb"). - * Callbacks may (will) cause methods to be invoked from within, - * but methods are not allowed to invoke callbacks inline. - * - * Implementation must ensure that only one callback is invoked at any time. - */ - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#define MG_MAIN_IFACE 0 - -struct mg_mgr; -struct mg_connection; -union socket_address; - -struct mg_iface_vtable; - -struct mg_iface { - struct mg_mgr *mgr; - void *data; /* Implementation-specific data */ - const struct mg_iface_vtable *vtable; -}; - -struct mg_iface_vtable { - void (*init)(struct mg_iface *iface); - void (*free)(struct mg_iface *iface); - void (*add_conn)(struct mg_connection *nc); - void (*remove_conn)(struct mg_connection *nc); - time_t (*poll)(struct mg_iface *iface, int timeout_ms); - - /* Set up a listening TCP socket on a given address. rv = 0 -> ok. */ - int (*listen_tcp)(struct mg_connection *nc, union socket_address *sa); - /* Request that a "listening" UDP socket be created. */ - int (*listen_udp)(struct mg_connection *nc, union socket_address *sa); - - /* Request that a TCP connection is made to the specified address. */ - void (*connect_tcp)(struct mg_connection *nc, const union socket_address *sa); - /* Open a UDP socket. Doesn't actually connect anything. */ - void (*connect_udp)(struct mg_connection *nc); - - /* Send functions for TCP and UDP. Sent data is copied before return. */ - void (*tcp_send)(struct mg_connection *nc, const void *buf, size_t len); - void (*udp_send)(struct mg_connection *nc, const void *buf, size_t len); - - void (*recved)(struct mg_connection *nc, size_t len); - - /* Perform interface-related connection initialization. Return 1 on ok. */ - int (*create_conn)(struct mg_connection *nc); - /* Perform interface-related cleanup on connection before destruction. */ - void (*destroy_conn)(struct mg_connection *nc); - - /* Associate a socket to a connection. */ - void (*sock_set)(struct mg_connection *nc, sock_t sock); - - /* Put connection's address into *sa, local (remote = 0) or remote. */ - void (*get_conn_addr)(struct mg_connection *nc, int remote, - union socket_address *sa); -}; - -extern const struct mg_iface_vtable *mg_ifaces[]; -extern int mg_num_ifaces; - -/* Creates a new interface instance. */ -struct mg_iface *mg_if_create_iface(const struct mg_iface_vtable *vtable, - struct mg_mgr *mgr); - -/* - * Find an interface with a given implementation. The search is started from - * interface `from`, exclusive. Returns NULL if none is found. - */ -struct mg_iface *mg_find_iface(struct mg_mgr *mgr, - const struct mg_iface_vtable *vtable, - struct mg_iface *from); -/* - * Deliver a new TCP connection. Returns NULL in case on error (unable to - * create connection, in which case interface state should be discarded. - * This is phase 1 of the two-phase process - MG_EV_ACCEPT will be delivered - * when mg_if_accept_tcp_cb is invoked. - */ -struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc); -void mg_if_accept_tcp_cb(struct mg_connection *nc, union socket_address *sa, - size_t sa_len); - -/* Callback invoked by connect methods. err = 0 -> ok, != 0 -> error. */ -void mg_if_connect_cb(struct mg_connection *nc, int err); -/* Callback that reports that data has been put on the wire. */ -void mg_if_sent_cb(struct mg_connection *nc, int num_sent); -/* - * Receive callback. - * if `own` is true, buf must be heap-allocated and ownership is transferred - * to the core. - * Core will acknowledge consumption by calling iface::recved. - */ -void mg_if_recv_tcp_cb(struct mg_connection *nc, void *buf, int len, int own); -/* - * Receive callback. - * buf must be heap-allocated and ownership is transferred to the core. - * Core will acknowledge consumption by calling iface::recved. - */ -void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, - union socket_address *sa, size_t sa_len); - -/* void mg_if_close_conn(struct mg_connection *nc); */ - -/* Deliver a POLL event to the connection. */ -void mg_if_poll(struct mg_connection *nc, time_t now); - -/* Deliver a TIMER event to the connection. */ -void mg_if_timer(struct mg_connection *c, double now); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* CS_MONGOOSE_SRC_NET_IF_H_ */ diff --git a/src/mongoose-6.11/src/mg_net_if_socket.c b/src/mongoose-6.11/src/mg_net_if_socket.c deleted file mode 100644 index 0d15764..0000000 --- a/src/mongoose-6.11/src/mg_net_if_socket.c +++ /dev/null @@ -1,749 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#if MG_ENABLE_NET_IF_SOCKET - -#include "mg_net_if_socket.h" -#include "mg_internal.h" -#include "mg_util.h" - -#define MG_TCP_RECV_BUFFER_SIZE 1024 -#define MG_UDP_RECV_BUFFER_SIZE 1500 - -static sock_t mg_open_listening_socket(union socket_address *sa, int type, - int proto); -#if MG_ENABLE_SSL -static void mg_ssl_begin(struct mg_connection *nc); -#endif - -void mg_set_non_blocking_mode(sock_t sock) { -#ifdef _WIN32 - unsigned long on = 1; - ioctlsocket(sock, FIONBIO, &on); -#else - int flags = fcntl(sock, F_GETFL, 0); - fcntl(sock, F_SETFL, flags | O_NONBLOCK); -#endif -} - -static int mg_is_error(void) { - int err = mg_get_errno(); - return err != EINPROGRESS && err != EWOULDBLOCK -#ifndef WINCE - && err != EAGAIN && err != EINTR -#endif -#ifdef _WIN32 - && WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK -#endif - ; -} - -void mg_socket_if_connect_tcp(struct mg_connection *nc, - const union socket_address *sa) { - int rc, proto = 0; - nc->sock = socket(AF_INET, SOCK_STREAM, proto); - if (nc->sock == INVALID_SOCKET) { - nc->err = mg_get_errno() ? mg_get_errno() : 1; - return; - } -#if !defined(MG_ESP8266) - mg_set_non_blocking_mode(nc->sock); -#endif - rc = connect(nc->sock, &sa->sa, sizeof(sa->sin)); - nc->err = rc < 0 && mg_is_error() ? mg_get_errno() : 0; - DBG(("%p sock %d rc %d errno %d err %d", nc, nc->sock, rc, mg_get_errno(), - nc->err)); -} - -void mg_socket_if_connect_udp(struct mg_connection *nc) { - nc->sock = socket(AF_INET, SOCK_DGRAM, 0); - if (nc->sock == INVALID_SOCKET) { - nc->err = mg_get_errno() ? mg_get_errno() : 1; - return; - } - if (nc->flags & MG_F_ENABLE_BROADCAST) { - int optval = 1; - if (setsockopt(nc->sock, SOL_SOCKET, SO_BROADCAST, (const char *) &optval, - sizeof(optval)) < 0) { - nc->err = mg_get_errno() ? mg_get_errno() : 1; - return; - } - } - nc->err = 0; -} - -int mg_socket_if_listen_tcp(struct mg_connection *nc, - union socket_address *sa) { - int proto = 0; - sock_t sock = mg_open_listening_socket(sa, SOCK_STREAM, proto); - if (sock == INVALID_SOCKET) { - return (mg_get_errno() ? mg_get_errno() : 1); - } - mg_sock_set(nc, sock); - return 0; -} - -int mg_socket_if_listen_udp(struct mg_connection *nc, - union socket_address *sa) { - sock_t sock = mg_open_listening_socket(sa, SOCK_DGRAM, 0); - if (sock == INVALID_SOCKET) return (mg_get_errno() ? mg_get_errno() : 1); - mg_sock_set(nc, sock); - return 0; -} - -void mg_socket_if_tcp_send(struct mg_connection *nc, const void *buf, - size_t len) { - mbuf_append(&nc->send_mbuf, buf, len); -} - -void mg_socket_if_udp_send(struct mg_connection *nc, const void *buf, - size_t len) { - mbuf_append(&nc->send_mbuf, buf, len); -} - -void mg_socket_if_recved(struct mg_connection *nc, size_t len) { - (void) nc; - (void) len; -} - -int mg_socket_if_create_conn(struct mg_connection *nc) { - (void) nc; - return 1; -} - -void mg_socket_if_destroy_conn(struct mg_connection *nc) { - if (nc->sock == INVALID_SOCKET) return; - if (!(nc->flags & MG_F_UDP)) { - closesocket(nc->sock); - } else { - /* Only close outgoing UDP sockets or listeners. */ - if (nc->listener == NULL) closesocket(nc->sock); - } - nc->sock = INVALID_SOCKET; -} - -static int mg_accept_conn(struct mg_connection *lc) { - struct mg_connection *nc; - union socket_address sa; - socklen_t sa_len = sizeof(sa); - /* NOTE(lsm): on Windows, sock is always > FD_SETSIZE */ - sock_t sock = accept(lc->sock, &sa.sa, &sa_len); - if (sock == INVALID_SOCKET) { - if (mg_is_error()) DBG(("%p: failed to accept: %d", lc, mg_get_errno())); - return 0; - } - nc = mg_if_accept_new_conn(lc); - if (nc == NULL) { - closesocket(sock); - return 0; - } - DBG(("%p conn from %s:%d", nc, inet_ntoa(sa.sin.sin_addr), - ntohs(sa.sin.sin_port))); - mg_sock_set(nc, sock); -#if MG_ENABLE_SSL - if (lc->flags & MG_F_SSL) { - if (mg_ssl_if_conn_accept(nc, lc) != MG_SSL_OK) mg_close_conn(nc); - } else -#endif - { - mg_if_accept_tcp_cb(nc, &sa, sa_len); - } - return 1; -} - -/* 'sa' must be an initialized address to bind to */ -static sock_t mg_open_listening_socket(union socket_address *sa, int type, - int proto) { - socklen_t sa_len = - (sa->sa.sa_family == AF_INET) ? sizeof(sa->sin) : sizeof(sa->sin6); - sock_t sock = INVALID_SOCKET; -#if !MG_LWIP - int on = 1; -#endif - - if ((sock = socket(sa->sa.sa_family, type, proto)) != INVALID_SOCKET && -#if !MG_LWIP /* LWIP doesn't support either */ -#if defined(_WIN32) && defined(SO_EXCLUSIVEADDRUSE) && !defined(WINCE) - /* "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE" http://goo.gl/RmrFTm */ - !setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void *) &on, - sizeof(on)) && -#endif - -#if !defined(_WIN32) || !defined(SO_EXCLUSIVEADDRUSE) - /* - * SO_RESUSEADDR is not enabled on Windows because the semantics of - * SO_REUSEADDR on UNIX and Windows is different. On Windows, - * SO_REUSEADDR allows to bind a socket to a port without error even if - * the port is already open by another program. This is not the behavior - * SO_REUSEADDR was designed for, and leads to hard-to-track failure - * scenarios. Therefore, SO_REUSEADDR was disabled on Windows unless - * SO_EXCLUSIVEADDRUSE is supported and set on a socket. - */ - !setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) && -#endif -#endif /* !MG_LWIP */ - - !bind(sock, &sa->sa, sa_len) && - (type == SOCK_DGRAM || listen(sock, SOMAXCONN) == 0)) { -#if !MG_LWIP - mg_set_non_blocking_mode(sock); - /* In case port was set to 0, get the real port number */ - (void) getsockname(sock, &sa->sa, &sa_len); -#endif - } else if (sock != INVALID_SOCKET) { - closesocket(sock); - sock = INVALID_SOCKET; - } - - return sock; -} - -static void mg_write_to_socket(struct mg_connection *nc) { - struct mbuf *io = &nc->send_mbuf; - int n = 0; - -#if MG_LWIP - /* With LWIP we don't know if the socket is ready */ - if (io->len == 0) return; -#endif - - assert(io->len > 0); - - if (nc->flags & MG_F_UDP) { - int n = - sendto(nc->sock, io->buf, io->len, 0, &nc->sa.sa, sizeof(nc->sa.sin)); - DBG(("%p %d %d %d %s:%hu", nc, nc->sock, n, mg_get_errno(), - inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port))); - mg_if_sent_cb(nc, n); - return; - } - -#if MG_ENABLE_SSL - if (nc->flags & MG_F_SSL) { - if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { - n = mg_ssl_if_write(nc, io->buf, io->len); - DBG(("%p %d bytes -> %d (SSL)", nc, n, nc->sock)); - if (n < 0) { - if (n != MG_SSL_WANT_READ && n != MG_SSL_WANT_WRITE) { - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } - return; - } else { - /* Successful SSL operation, clear off SSL wait flags */ - nc->flags &= ~(MG_F_WANT_READ | MG_F_WANT_WRITE); - } - } else { - mg_ssl_begin(nc); - return; - } - } else -#endif - { - n = (int) MG_SEND_FUNC(nc->sock, io->buf, io->len, 0); - DBG(("%p %d bytes -> %d", nc, n, nc->sock)); - } - - mg_if_sent_cb(nc, n); -} - -MG_INTERNAL size_t recv_avail_size(struct mg_connection *conn, size_t max) { - size_t avail; - if (conn->recv_mbuf_limit < conn->recv_mbuf.len) return 0; - avail = conn->recv_mbuf_limit - conn->recv_mbuf.len; - return avail > max ? max : avail; -} - -static void mg_handle_tcp_read(struct mg_connection *conn) { - int n = 0; - char *buf = (char *) MG_MALLOC(MG_TCP_RECV_BUFFER_SIZE); - - if (buf == NULL) { - DBG(("OOM")); - return; - } - -#if MG_ENABLE_SSL - if (conn->flags & MG_F_SSL) { - if (conn->flags & MG_F_SSL_HANDSHAKE_DONE) { - /* SSL library may have more bytes ready to read than we ask to read. - * Therefore, read in a loop until we read everything. Without the loop, - * we skip to the next select() cycle which can just timeout. */ - while ((n = mg_ssl_if_read(conn, buf, MG_TCP_RECV_BUFFER_SIZE)) > 0) { - DBG(("%p %d bytes <- %d (SSL)", conn, n, conn->sock)); - mg_if_recv_tcp_cb(conn, buf, n, 1 /* own */); - buf = NULL; - if (conn->flags & MG_F_CLOSE_IMMEDIATELY) break; - /* buf has been freed, we need a new one. */ - buf = (char *) MG_MALLOC(MG_TCP_RECV_BUFFER_SIZE); - if (buf == NULL) break; - } - MG_FREE(buf); - if (n < 0 && n != MG_SSL_WANT_READ) conn->flags |= MG_F_CLOSE_IMMEDIATELY; - } else { - MG_FREE(buf); - mg_ssl_begin(conn); - return; - } - } else -#endif - { - n = (int) MG_RECV_FUNC(conn->sock, buf, - recv_avail_size(conn, MG_TCP_RECV_BUFFER_SIZE), 0); - DBG(("%p %d bytes (PLAIN) <- %d", conn, n, conn->sock)); - if (n > 0) { - mg_if_recv_tcp_cb(conn, buf, n, 1 /* own */); - } else { - MG_FREE(buf); - } - if (n == 0) { - /* Orderly shutdown of the socket, try flushing output. */ - conn->flags |= MG_F_SEND_AND_CLOSE; - } else if (n < 0 && mg_is_error()) { - conn->flags |= MG_F_CLOSE_IMMEDIATELY; - } - } -} - -static int mg_recvfrom(struct mg_connection *nc, union socket_address *sa, - socklen_t *sa_len, char **buf) { - int n; - *buf = (char *) MG_MALLOC(MG_UDP_RECV_BUFFER_SIZE); - if (*buf == NULL) { - DBG(("Out of memory")); - return -ENOMEM; - } - n = recvfrom(nc->sock, *buf, MG_UDP_RECV_BUFFER_SIZE, 0, &sa->sa, sa_len); - if (n <= 0) { - DBG(("%p recvfrom: %s", nc, strerror(mg_get_errno()))); - MG_FREE(*buf); - } - return n; -} - -static void mg_handle_udp_read(struct mg_connection *nc) { - char *buf = NULL; - union socket_address sa; - socklen_t sa_len = sizeof(sa); - int n = mg_recvfrom(nc, &sa, &sa_len, &buf); - DBG(("%p %d bytes from %s:%d", nc, n, inet_ntoa(nc->sa.sin.sin_addr), - ntohs(nc->sa.sin.sin_port))); - mg_if_recv_udp_cb(nc, buf, n, &sa, sa_len); -} - -#if MG_ENABLE_SSL -static void mg_ssl_begin(struct mg_connection *nc) { - int server_side = (nc->listener != NULL); - enum mg_ssl_if_result res = mg_ssl_if_handshake(nc); - DBG(("%p %d res %d", nc, server_side, res)); - - if (res == MG_SSL_OK) { - nc->flags |= MG_F_SSL_HANDSHAKE_DONE; - nc->flags &= ~(MG_F_WANT_READ | MG_F_WANT_WRITE); - - if (server_side) { - union socket_address sa; - socklen_t sa_len = sizeof(sa); - (void) getpeername(nc->sock, &sa.sa, &sa_len); - mg_if_accept_tcp_cb(nc, &sa, sa_len); - } else { - mg_if_connect_cb(nc, 0); - } - } else if (res != MG_SSL_WANT_READ && res != MG_SSL_WANT_WRITE) { - if (!server_side) { - mg_if_connect_cb(nc, res); - } - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } -} -#endif /* MG_ENABLE_SSL */ - -#define _MG_F_FD_CAN_READ 1 -#define _MG_F_FD_CAN_WRITE 1 << 1 -#define _MG_F_FD_ERROR 1 << 2 - -void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { - int worth_logging = - fd_flags != 0 || (nc->flags & (MG_F_WANT_READ | MG_F_WANT_WRITE)); - if (worth_logging) { - DBG(("%p fd=%d fd_flags=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, - fd_flags, nc->flags, (int) nc->recv_mbuf.len, - (int) nc->send_mbuf.len)); - } - - if (nc->flags & MG_F_CONNECTING) { - if (fd_flags != 0) { - int err = 0; -#if !defined(MG_ESP8266) - if (!(nc->flags & MG_F_UDP)) { - socklen_t len = sizeof(err); - int ret = - getsockopt(nc->sock, SOL_SOCKET, SO_ERROR, (char *) &err, &len); - if (ret != 0) { - err = 1; - } else if (err == EAGAIN || err == EWOULDBLOCK) { - err = 0; - } - } -#else - /* - * On ESP8266 we use blocking connect. - */ - err = nc->err; -#endif -#if MG_ENABLE_SSL - if ((nc->flags & MG_F_SSL) && err == 0) { - mg_ssl_begin(nc); - } else { - mg_if_connect_cb(nc, err); - } -#else - mg_if_connect_cb(nc, err); -#endif - } else if (nc->err != 0) { - mg_if_connect_cb(nc, nc->err); - } - } - - if (fd_flags & _MG_F_FD_CAN_READ) { - if (nc->flags & MG_F_UDP) { - mg_handle_udp_read(nc); - } else { - if (nc->flags & MG_F_LISTENING) { - /* - * We're not looping here, and accepting just one connection at - * a time. The reason is that eCos does not respect non-blocking - * flag on a listening socket and hangs in a loop. - */ - mg_accept_conn(nc); - } else { - mg_handle_tcp_read(nc); - } - } - } - - if (!(nc->flags & MG_F_CLOSE_IMMEDIATELY)) { - if ((fd_flags & _MG_F_FD_CAN_WRITE) && nc->send_mbuf.len > 0) { - mg_write_to_socket(nc); - } - mg_if_poll(nc, (time_t) now); - mg_if_timer(nc, now); - } - - if (worth_logging) { - DBG(("%p after fd=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, nc->flags, - (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); - } -} - -#if MG_ENABLE_BROADCAST -static void mg_mgr_handle_ctl_sock(struct mg_mgr *mgr) { - struct ctl_msg ctl_msg; - int len = - (int) MG_RECV_FUNC(mgr->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0); - size_t dummy = MG_SEND_FUNC(mgr->ctl[1], ctl_msg.message, 1, 0); - DBG(("read %d from ctl socket", len)); - (void) dummy; /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509 */ - if (len >= (int) sizeof(ctl_msg.callback) && ctl_msg.callback != NULL) { - struct mg_connection *nc; - for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) { - ctl_msg.callback(nc, MG_EV_POLL, - ctl_msg.message MG_UD_ARG(nc->user_data)); - } - } -} -#endif - -/* Associate a socket to a connection. */ -void mg_socket_if_sock_set(struct mg_connection *nc, sock_t sock) { - mg_set_non_blocking_mode(sock); - mg_set_close_on_exec(sock); - nc->sock = sock; - DBG(("%p %d", nc, sock)); -} - -void mg_socket_if_init(struct mg_iface *iface) { - (void) iface; - DBG(("%p using select()", iface->mgr)); -#if MG_ENABLE_BROADCAST - mg_socketpair(iface->mgr->ctl, SOCK_DGRAM); -#endif -} - -void mg_socket_if_free(struct mg_iface *iface) { - (void) iface; -} - -void mg_socket_if_add_conn(struct mg_connection *nc) { - (void) nc; -} - -void mg_socket_if_remove_conn(struct mg_connection *nc) { - (void) nc; -} - -void mg_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) { - if (sock != INVALID_SOCKET -#ifdef __unix__ - && sock < (sock_t) FD_SETSIZE -#endif - ) { - FD_SET(sock, set); - if (*max_fd == INVALID_SOCKET || sock > *max_fd) { - *max_fd = sock; - } - } -} - -time_t mg_socket_if_poll(struct mg_iface *iface, int timeout_ms) { - struct mg_mgr *mgr = iface->mgr; - double now = mg_time(); - double min_timer; - struct mg_connection *nc, *tmp; - struct timeval tv; - fd_set read_set, write_set, err_set; - sock_t max_fd = INVALID_SOCKET; - int num_fds, num_ev, num_timers = 0; -#ifdef __unix__ - int try_dup = 1; -#endif - - FD_ZERO(&read_set); - FD_ZERO(&write_set); - FD_ZERO(&err_set); -#if MG_ENABLE_BROADCAST - mg_add_to_set(mgr->ctl[1], &read_set, &max_fd); -#endif - - /* - * Note: it is ok to have connections with sock == INVALID_SOCKET in the list, - * e.g. timer-only "connections". - */ - min_timer = 0; - for (nc = mgr->active_connections, num_fds = 0; nc != NULL; nc = tmp) { - tmp = nc->next; - - if (nc->sock != INVALID_SOCKET) { - num_fds++; - -#ifdef __unix__ - /* A hack to make sure all our file descriptos fit into FD_SETSIZE. */ - if (nc->sock >= (sock_t) FD_SETSIZE && try_dup) { - int new_sock = dup(nc->sock); - if (new_sock >= 0) { - if (new_sock < (sock_t) FD_SETSIZE) { - closesocket(nc->sock); - DBG(("new sock %d -> %d", nc->sock, new_sock)); - nc->sock = new_sock; - } else { - closesocket(new_sock); - DBG(("new sock is still larger than FD_SETSIZE, disregard")); - try_dup = 0; - } - } else { - try_dup = 0; - } - } -#endif - - if (!(nc->flags & MG_F_WANT_WRITE) && - nc->recv_mbuf.len < nc->recv_mbuf_limit && - (!(nc->flags & MG_F_UDP) || nc->listener == NULL)) { - mg_add_to_set(nc->sock, &read_set, &max_fd); - } - - if (((nc->flags & MG_F_CONNECTING) && !(nc->flags & MG_F_WANT_READ)) || - (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING))) { - mg_add_to_set(nc->sock, &write_set, &max_fd); - mg_add_to_set(nc->sock, &err_set, &max_fd); - } - } - - if (nc->ev_timer_time > 0) { - if (num_timers == 0 || nc->ev_timer_time < min_timer) { - min_timer = nc->ev_timer_time; - } - num_timers++; - } - } - - /* - * If there is a timer to be fired earlier than the requested timeout, - * adjust the timeout. - */ - if (num_timers > 0) { - double timer_timeout_ms = (min_timer - mg_time()) * 1000 + 1 /* rounding */; - if (timer_timeout_ms < timeout_ms) { - timeout_ms = (int) timer_timeout_ms; - } - } - if (timeout_ms < 0) timeout_ms = 0; - - tv.tv_sec = timeout_ms / 1000; - tv.tv_usec = (timeout_ms % 1000) * 1000; - - num_ev = select((int) max_fd + 1, &read_set, &write_set, &err_set, &tv); - now = mg_time(); -#if 0 - DBG(("select @ %ld num_ev=%d of %d, timeout=%d", (long) now, num_ev, num_fds, - timeout_ms)); -#endif - -#if MG_ENABLE_BROADCAST - if (num_ev > 0 && mgr->ctl[1] != INVALID_SOCKET && - FD_ISSET(mgr->ctl[1], &read_set)) { - mg_mgr_handle_ctl_sock(mgr); - } -#endif - - for (nc = mgr->active_connections; nc != NULL; nc = tmp) { - int fd_flags = 0; - if (nc->sock != INVALID_SOCKET) { - if (num_ev > 0) { - fd_flags = (FD_ISSET(nc->sock, &read_set) && - (!(nc->flags & MG_F_UDP) || nc->listener == NULL) - ? _MG_F_FD_CAN_READ - : 0) | - (FD_ISSET(nc->sock, &write_set) ? _MG_F_FD_CAN_WRITE : 0) | - (FD_ISSET(nc->sock, &err_set) ? _MG_F_FD_ERROR : 0); - } -#if MG_LWIP - /* With LWIP socket emulation layer, we don't get write events for UDP */ - if ((nc->flags & MG_F_UDP) && nc->listener == NULL) { - fd_flags |= _MG_F_FD_CAN_WRITE; - } -#endif - } - tmp = nc->next; - mg_mgr_handle_conn(nc, fd_flags, now); - } - - for (nc = mgr->active_connections; nc != NULL; nc = tmp) { - tmp = nc->next; - if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || - (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) { - mg_close_conn(nc); - } - } - - return (time_t) now; -} - -#if MG_ENABLE_BROADCAST -MG_INTERNAL void mg_socketpair_close(sock_t *sock) { - while (1) { - if (closesocket(*sock) == -1 && errno == EINTR) continue; - break; - } - *sock = INVALID_SOCKET; -} - -MG_INTERNAL sock_t -mg_socketpair_accept(sock_t sock, union socket_address *sa, socklen_t sa_len) { - sock_t rc; - while (1) { - if ((rc = accept(sock, &sa->sa, &sa_len)) == INVALID_SOCKET && - errno == EINTR) - continue; - break; - } - return rc; -} - -int mg_socketpair(sock_t sp[2], int sock_type) { - union socket_address sa; - sock_t sock; - socklen_t len = sizeof(sa.sin); - int ret = 0; - - sock = sp[0] = sp[1] = INVALID_SOCKET; - - (void) memset(&sa, 0, sizeof(sa)); - sa.sin.sin_family = AF_INET; - sa.sin.sin_port = htons(0); - sa.sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */ - - if ((sock = socket(AF_INET, sock_type, 0)) == INVALID_SOCKET) { - } else if (bind(sock, &sa.sa, len) != 0) { - } else if (sock_type == SOCK_STREAM && listen(sock, 1) != 0) { - } else if (getsockname(sock, &sa.sa, &len) != 0) { - } else if ((sp[0] = socket(AF_INET, sock_type, 0)) == INVALID_SOCKET) { - } else if (connect(sp[0], &sa.sa, len) != 0) { - } else if (sock_type == SOCK_DGRAM && - (getsockname(sp[0], &sa.sa, &len) != 0 || - connect(sock, &sa.sa, len) != 0)) { - } else if ((sp[1] = (sock_type == SOCK_DGRAM ? sock : mg_socketpair_accept( - sock, &sa, len))) == - INVALID_SOCKET) { - } else { - mg_set_close_on_exec(sp[0]); - mg_set_close_on_exec(sp[1]); - if (sock_type == SOCK_STREAM) mg_socketpair_close(&sock); - ret = 1; - } - - if (!ret) { - if (sp[0] != INVALID_SOCKET) mg_socketpair_close(&sp[0]); - if (sp[1] != INVALID_SOCKET) mg_socketpair_close(&sp[1]); - if (sock != INVALID_SOCKET) mg_socketpair_close(&sock); - } - - return ret; -} -#endif /* MG_ENABLE_BROADCAST */ - -static void mg_sock_get_addr(sock_t sock, int remote, - union socket_address *sa) { - socklen_t slen = sizeof(*sa); - memset(sa, 0, slen); - if (remote) { - getpeername(sock, &sa->sa, &slen); - } else { - getsockname(sock, &sa->sa, &slen); - } -} - -void mg_sock_to_str(sock_t sock, char *buf, size_t len, int flags) { - union socket_address sa; - mg_sock_get_addr(sock, flags & MG_SOCK_STRINGIFY_REMOTE, &sa); - mg_sock_addr_to_str(&sa, buf, len, flags); -} - -void mg_socket_if_get_conn_addr(struct mg_connection *nc, int remote, - union socket_address *sa) { - if ((nc->flags & MG_F_UDP) && remote) { - memcpy(sa, &nc->sa, sizeof(*sa)); - return; - } - mg_sock_get_addr(nc->sock, remote, sa); -} - -/* clang-format off */ -#define MG_SOCKET_IFACE_VTABLE \ - { \ - mg_socket_if_init, \ - mg_socket_if_free, \ - mg_socket_if_add_conn, \ - mg_socket_if_remove_conn, \ - mg_socket_if_poll, \ - mg_socket_if_listen_tcp, \ - mg_socket_if_listen_udp, \ - mg_socket_if_connect_tcp, \ - mg_socket_if_connect_udp, \ - mg_socket_if_tcp_send, \ - mg_socket_if_udp_send, \ - mg_socket_if_recved, \ - mg_socket_if_create_conn, \ - mg_socket_if_destroy_conn, \ - mg_socket_if_sock_set, \ - mg_socket_if_get_conn_addr, \ - } -/* clang-format on */ - -const struct mg_iface_vtable mg_socket_iface_vtable = MG_SOCKET_IFACE_VTABLE; -#if MG_NET_IF == MG_NET_IF_SOCKET -const struct mg_iface_vtable mg_default_iface_vtable = MG_SOCKET_IFACE_VTABLE; -#endif - -#endif /* MG_ENABLE_NET_IF_SOCKET */ diff --git a/src/mongoose-6.11/src/mg_net_if_socket.h b/src/mongoose-6.11/src/mg_net_if_socket.h deleted file mode 100644 index 744fc0f..0000000 --- a/src/mongoose-6.11/src/mg_net_if_socket.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_MONGOOSE_SRC_NET_IF_SOCKET_H_ -#define CS_MONGOOSE_SRC_NET_IF_SOCKET_H_ - -#include "mg_net_if.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifndef MG_ENABLE_NET_IF_SOCKET -#define MG_ENABLE_NET_IF_SOCKET MG_NET_IF == MG_NET_IF_SOCKET -#endif - -extern const struct mg_iface_vtable mg_socket_iface_vtable; - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* CS_MONGOOSE_SRC_NET_IF_SOCKET_H_ */ diff --git a/src/mongoose-6.11/src/mg_net_if_socks.c b/src/mongoose-6.11/src/mg_net_if_socks.c deleted file mode 100644 index c5661e9..0000000 --- a/src/mongoose-6.11/src/mg_net_if_socks.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#if MG_ENABLE_SOCKS - -struct socksdata { - char *proxy_addr; /* HOST:PORT of the socks5 proxy server */ - struct mg_connection *s; /* Respective connection to the server */ - struct mg_connection *c; /* Connection to the client */ - struct mbuf tmp; /* Temporary buffer for sent data */ -}; - -static void socks_if_disband(struct socksdata *d) { - LOG(LL_DEBUG, ("disbanding proxy %p %p", d->c, d->s)); - if (d->c) d->c->flags |= MG_F_SEND_AND_CLOSE; - if (d->s) d->s->flags |= MG_F_SEND_AND_CLOSE; - d->c = d->s = NULL; -} - -static void socks_if_handler(struct mg_connection *c, int ev, void *ev_data) { - struct socksdata *d = (struct socksdata *) c->user_data; - if (ev == MG_EV_CONNECT) { - int res = *(int *) ev_data; - if (res == 0) { - /* Send handshake to the proxy server */ - unsigned char buf[] = {MG_SOCKS_VERSION, 1, MG_SOCKS_HANDSHAKE_NOAUTH}; - mg_send(d->s, buf, sizeof(buf)); - LOG(LL_DEBUG, ("Sent handshake to %s", d->proxy_addr)); - } else { - LOG(LL_ERROR, ("Cannot connect to %s: %d", d->proxy_addr, res)); - d->c->flags |= MG_F_CLOSE_IMMEDIATELY; - } - } else if (ev == MG_EV_CLOSE) { - socks_if_disband(d); - } else if (ev == MG_EV_RECV) { - /* Handle handshake reply */ - if (!(c->flags & MG_SOCKS_HANDSHAKE_DONE)) { - /* TODO(lsm): process IPv6 too */ - unsigned char buf[10] = {MG_SOCKS_VERSION, MG_SOCKS_CMD_CONNECT, 0, - MG_SOCKS_ADDR_IPV4}; - if (c->recv_mbuf.len < 2) return; - if ((unsigned char) c->recv_mbuf.buf[1] == MG_SOCKS_HANDSHAKE_FAILURE) { - LOG(LL_ERROR, ("Server kicked us out")); - socks_if_disband(d); - return; - } - mbuf_remove(&c->recv_mbuf, 2); - c->flags |= MG_SOCKS_HANDSHAKE_DONE; - - /* Send connect request */ - memcpy(buf + 4, &d->c->sa.sin.sin_addr, 4); - memcpy(buf + 8, &d->c->sa.sin.sin_port, 2); - mg_send(c, buf, sizeof(buf)); - } - /* Process connect request */ - if ((c->flags & MG_SOCKS_HANDSHAKE_DONE) && - !(c->flags & MG_SOCKS_CONNECT_DONE)) { - if (c->recv_mbuf.len < 10) return; - if (c->recv_mbuf.buf[1] != MG_SOCKS_SUCCESS) { - LOG(LL_ERROR, ("Socks connection error: %d", c->recv_mbuf.buf[1])); - socks_if_disband(d); - return; - } - mbuf_remove(&c->recv_mbuf, 10); - c->flags |= MG_SOCKS_CONNECT_DONE; - /* Connected. Move sent data from client, if any, to server */ - if (d->s && d->c) { - mbuf_append(&d->s->send_mbuf, d->tmp.buf, d->tmp.len); - mbuf_free(&d->tmp); - } - } - /* All flags are set, we're in relay mode */ - if ((c->flags & MG_SOCKS_CONNECT_DONE) && d->c && d->s) { - mbuf_append(&d->c->recv_mbuf, d->s->recv_mbuf.buf, d->s->recv_mbuf.len); - mbuf_remove(&d->s->recv_mbuf, d->s->recv_mbuf.len); - } - } -} - -static void mg_socks_if_connect_tcp(struct mg_connection *c, - const union socket_address *sa) { - struct socksdata *d = (struct socksdata *) c->iface->data; - d->c = c; - d->s = mg_connect(c->mgr, d->proxy_addr, socks_if_handler); - d->s->user_data = d; - LOG(LL_DEBUG, ("%p %s", c, d->proxy_addr)); - (void) sa; -} - -static void mg_socks_if_connect_udp(struct mg_connection *c) { - (void) c; -} - -static int mg_socks_if_listen_tcp(struct mg_connection *c, - union socket_address *sa) { - (void) c; - (void) sa; - return 0; -} - -static int mg_socks_if_listen_udp(struct mg_connection *c, - union socket_address *sa) { - (void) c; - (void) sa; - return -1; -} - -static void mg_socks_if_tcp_send(struct mg_connection *c, const void *buf, - size_t len) { - struct socksdata *d = (struct socksdata *) c->iface->data; - LOG(LL_DEBUG, ("%p -> %p %d %d", c, buf, (int) len, (int) c->send_mbuf.len)); - if (d && d->s && d->s->flags & MG_SOCKS_CONNECT_DONE) { - mbuf_append(&d->s->send_mbuf, d->tmp.buf, d->tmp.len); - mbuf_append(&d->s->send_mbuf, buf, len); - mbuf_free(&d->tmp); - } else { - mbuf_append(&d->tmp, buf, len); - } -} - -static void mg_socks_if_udp_send(struct mg_connection *c, const void *buf, - size_t len) { - (void) c; - (void) buf; - (void) len; -} - -static void mg_socks_if_recved(struct mg_connection *c, size_t len) { - (void) c; - (void) len; -} - -static int mg_socks_if_create_conn(struct mg_connection *c) { - (void) c; - return 1; -} - -static void mg_socks_if_destroy_conn(struct mg_connection *c) { - c->iface->vtable->free(c->iface); - MG_FREE(c->iface); - c->iface = NULL; - LOG(LL_DEBUG, ("%p", c)); -} - -static void mg_socks_if_sock_set(struct mg_connection *c, sock_t sock) { - (void) c; - (void) sock; -} - -static void mg_socks_if_init(struct mg_iface *iface) { - (void) iface; -} - -static void mg_socks_if_free(struct mg_iface *iface) { - struct socksdata *d = (struct socksdata *) iface->data; - LOG(LL_DEBUG, ("%p", iface)); - if (d != NULL) { - socks_if_disband(d); - mbuf_free(&d->tmp); - MG_FREE(d->proxy_addr); - MG_FREE(d); - iface->data = NULL; - } -} - -static void mg_socks_if_add_conn(struct mg_connection *c) { - c->sock = INVALID_SOCKET; -} - -static void mg_socks_if_remove_conn(struct mg_connection *c) { - (void) c; -} - -static time_t mg_socks_if_poll(struct mg_iface *iface, int timeout_ms) { - LOG(LL_DEBUG, ("%p", iface)); - (void) iface; - (void) timeout_ms; - return (time_t) cs_time(); -} - -static void mg_socks_if_get_conn_addr(struct mg_connection *c, int remote, - union socket_address *sa) { - LOG(LL_DEBUG, ("%p", c)); - (void) c; - (void) remote; - (void) sa; -} - -const struct mg_iface_vtable mg_socks_iface_vtable = { - mg_socks_if_init, mg_socks_if_free, - mg_socks_if_add_conn, mg_socks_if_remove_conn, - mg_socks_if_poll, mg_socks_if_listen_tcp, - mg_socks_if_listen_udp, mg_socks_if_connect_tcp, - mg_socks_if_connect_udp, mg_socks_if_tcp_send, - mg_socks_if_udp_send, mg_socks_if_recved, - mg_socks_if_create_conn, mg_socks_if_destroy_conn, - mg_socks_if_sock_set, mg_socks_if_get_conn_addr, -}; - -struct mg_iface *mg_socks_mk_iface(struct mg_mgr *mgr, const char *proxy_addr) { - struct mg_iface *iface = mg_if_create_iface(&mg_socks_iface_vtable, mgr); - iface->data = MG_CALLOC(1, sizeof(struct socksdata)); - ((struct socksdata *) iface->data)->proxy_addr = strdup(proxy_addr); - return iface; -} - -#endif diff --git a/src/mongoose-6.11/src/mg_net_if_socks.h b/src/mongoose-6.11/src/mg_net_if_socks.h deleted file mode 100644 index 80e3107..0000000 --- a/src/mongoose-6.11/src/mg_net_if_socks.h +++ /dev/null @@ -1,22 +0,0 @@ -/* -* Copyright (c) 2014-2017 Cesanta Software Limited -* All rights reserved -*/ - -#ifndef CS_MONGOOSE_SRC_NET_IF_SOCKS_H_ -#define CS_MONGOOSE_SRC_NET_IF_SOCKS_H_ - -#if MG_ENABLE_SOCKS -#include "mg_net_if.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -extern const struct mg_iface_vtable mg_socks_iface_vtable; - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* MG_ENABLE_SOCKS */ -#endif /* CS_MONGOOSE_SRC_NET_IF_SOCKS_H_ */ diff --git a/src/mongoose-6.11/src/mg_resolv.c b/src/mongoose-6.11/src/mg_resolv.c deleted file mode 100644 index e445ac5..0000000 --- a/src/mongoose-6.11/src/mg_resolv.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -#if MG_ENABLE_ASYNC_RESOLVER - -#include "mg_internal.h" -#include "mg_resolv.h" - -#ifndef MG_DEFAULT_NAMESERVER -#define MG_DEFAULT_NAMESERVER "8.8.8.8" -#endif - -struct mg_resolve_async_request { - char name[1024]; - int query; - mg_resolve_callback_t callback; - void *data; - time_t timeout; - int max_retries; - enum mg_resolve_err err; - - /* state */ - time_t last_time; - int retries; -}; - -/* - * Find what nameserver to use. - * - * Return 0 if OK, -1 if error - */ -static int mg_get_ip_address_of_nameserver(char *name, size_t name_len) { - int ret = -1; - -#ifdef _WIN32 - int i; - LONG err; - HKEY hKey, hSub; - wchar_t subkey[512], value[128], - *key = L"SYSTEM\\ControlSet001\\Services\\Tcpip\\Parameters\\Interfaces"; - - if ((err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hKey)) != - ERROR_SUCCESS) { - fprintf(stderr, "cannot open reg key %S: %ld\n", key, err); - ret = -1; - } else { - for (ret = -1, i = 0; 1; i++) { - DWORD subkey_size = sizeof(subkey), type, len = sizeof(value); - if (RegEnumKeyExW(hKey, i, subkey, &subkey_size, NULL, NULL, NULL, - NULL) != ERROR_SUCCESS) { - break; - } - if (RegOpenKeyExW(hKey, subkey, 0, KEY_READ, &hSub) == ERROR_SUCCESS && - ((RegQueryValueExW(hSub, L"NameServer", 0, &type, (void *) value, - &len) == ERROR_SUCCESS && - value[0] != '\0') || - (RegQueryValueExW(hSub, L"DhcpNameServer", 0, &type, (void *) value, - &len) == ERROR_SUCCESS && - value[0] != '\0'))) { - /* - * See https://github.com/cesanta/mongoose/issues/176 - * The value taken from the registry can be empty, a single - * IP address, or multiple IP addresses separated by comma. - * If it's empty, check the next interface. - * If it's multiple IP addresses, take the first one. - */ - wchar_t *comma = wcschr(value, ','); - if (comma != NULL) { - *comma = '\0'; - } - /* %S will convert wchar_t -> char */ - snprintf(name, name_len, "%S", value); - ret = 0; - RegCloseKey(hSub); - break; - } - } - RegCloseKey(hKey); - } -#elif MG_ENABLE_FILESYSTEM && defined(MG_RESOLV_CONF_FILE_NAME) - FILE *fp; - char line[512]; - - if ((fp = mg_fopen(MG_RESOLV_CONF_FILE_NAME, "r")) == NULL) { - ret = -1; - } else { - /* Try to figure out what nameserver to use */ - for (ret = -1; fgets(line, sizeof(line), fp) != NULL;) { - unsigned int a, b, c, d; - if (sscanf(line, "nameserver %u.%u.%u.%u", &a, &b, &c, &d) == 4) { - snprintf(name, name_len, "%u.%u.%u.%u", a, b, c, d); - ret = 0; - break; - } - } - (void) fclose(fp); - } -#else - snprintf(name, name_len, "%s", MG_DEFAULT_NAMESERVER); -#endif /* _WIN32 */ - - return ret; -} - -int mg_resolve_from_hosts_file(const char *name, union socket_address *usa) { -#if MG_ENABLE_FILESYSTEM && defined(MG_HOSTS_FILE_NAME) - /* TODO(mkm) cache /etc/hosts */ - FILE *fp; - char line[1024]; - char *p; - char alias[256]; - unsigned int a, b, c, d; - int len = 0; - - if ((fp = mg_fopen(MG_HOSTS_FILE_NAME, "r")) == NULL) { - return -1; - } - - for (; fgets(line, sizeof(line), fp) != NULL;) { - if (line[0] == '#') continue; - - if (sscanf(line, "%u.%u.%u.%u%n", &a, &b, &c, &d, &len) == 0) { - /* TODO(mkm): handle ipv6 */ - continue; - } - for (p = line + len; sscanf(p, "%s%n", alias, &len) == 1; p += len) { - if (strcmp(alias, name) == 0) { - usa->sin.sin_addr.s_addr = htonl(a << 24 | b << 16 | c << 8 | d); - fclose(fp); - return 0; - } - } - } - - fclose(fp); -#else - (void) name; - (void) usa; -#endif - - return -1; -} - -static void mg_resolve_async_eh(struct mg_connection *nc, int ev, - void *data MG_UD_ARG(void *user_data)) { - time_t now = (time_t) mg_time(); - struct mg_resolve_async_request *req; - struct mg_dns_message *msg; - int first = 0; -#if !MG_ENABLE_CALLBACK_USERDATA - void *user_data = nc->user_data; -#endif - - if (ev != MG_EV_POLL) DBG(("ev=%d user_data=%p", ev, user_data)); - - req = (struct mg_resolve_async_request *) user_data; - - if (req == NULL) { - return; - } - - switch (ev) { - case MG_EV_CONNECT: - /* don't depend on timer not being at epoch for sending out first req */ - first = 1; - /* fallthrough */ - case MG_EV_POLL: - if (req->retries > req->max_retries) { - req->err = MG_RESOLVE_EXCEEDED_RETRY_COUNT; - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - break; - } - if (first || now - req->last_time >= req->timeout) { - mg_send_dns_query(nc, req->name, req->query); - req->last_time = now; - req->retries++; - } - break; - case MG_EV_RECV: - msg = (struct mg_dns_message *) MG_MALLOC(sizeof(*msg)); - if (mg_parse_dns(nc->recv_mbuf.buf, *(int *) data, msg) == 0 && - msg->num_answers > 0) { - req->callback(msg, req->data, MG_RESOLVE_OK); - nc->user_data = NULL; - MG_FREE(req); - } else { - req->err = MG_RESOLVE_NO_ANSWERS; - } - MG_FREE(msg); - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - break; - case MG_EV_SEND: - /* - * If a send error occurs, prevent closing of the connection by the core. - * We will retry after timeout. - */ - nc->flags &= ~MG_F_CLOSE_IMMEDIATELY; - mbuf_remove(&nc->send_mbuf, nc->send_mbuf.len); - break; - case MG_EV_TIMER: - req->err = MG_RESOLVE_TIMEOUT; - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - break; - case MG_EV_CLOSE: - /* If we got here with request still not done, fire an error callback. */ - if (req != NULL) { - char addr[32]; - mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr), MG_SOCK_STRINGIFY_IP); -#ifdef MG_LOG_DNS_FAILURES - LOG(LL_ERROR, ("Failed to resolve '%s', server %s", req->name, addr)); -#endif - req->callback(NULL, req->data, req->err); - nc->user_data = NULL; - MG_FREE(req); - } - break; - } -} - -int mg_resolve_async(struct mg_mgr *mgr, const char *name, int query, - mg_resolve_callback_t cb, void *data) { - struct mg_resolve_async_opts opts; - memset(&opts, 0, sizeof(opts)); - return mg_resolve_async_opt(mgr, name, query, cb, data, opts); -} - -int mg_resolve_async_opt(struct mg_mgr *mgr, const char *name, int query, - mg_resolve_callback_t cb, void *data, - struct mg_resolve_async_opts opts) { - struct mg_resolve_async_request *req; - struct mg_connection *dns_nc; - const char *nameserver = opts.nameserver; - char dns_server_buff[17], nameserver_url[26]; - - if (nameserver == NULL) { - nameserver = mgr->nameserver; - } - - DBG(("%s %d %p", name, query, opts.dns_conn)); - - /* resolve with DNS */ - req = (struct mg_resolve_async_request *) MG_CALLOC(1, sizeof(*req)); - if (req == NULL) { - return -1; - } - - strncpy(req->name, name, sizeof(req->name)); - req->name[sizeof(req->name) - 1] = '\0'; - - req->query = query; - req->callback = cb; - req->data = data; - /* TODO(mkm): parse defaults out of resolve.conf */ - req->max_retries = opts.max_retries ? opts.max_retries : 2; - req->timeout = opts.timeout ? opts.timeout : 5; - - /* Lazily initialize dns server */ - if (nameserver == NULL) { - if (mg_get_ip_address_of_nameserver(dns_server_buff, - sizeof(dns_server_buff)) != -1) { - nameserver = dns_server_buff; - } else { - nameserver = MG_DEFAULT_NAMESERVER; - } - } - - snprintf(nameserver_url, sizeof(nameserver_url), "udp://%s:53", nameserver); - - dns_nc = mg_connect(mgr, nameserver_url, MG_CB(mg_resolve_async_eh, NULL)); - if (dns_nc == NULL) { - MG_FREE(req); - return -1; - } - dns_nc->user_data = req; - if (opts.dns_conn != NULL) { - *opts.dns_conn = dns_nc; - } - - return 0; -} - -void mg_set_nameserver(struct mg_mgr *mgr, const char *nameserver) { - MG_FREE((char *) mgr->nameserver); - mgr->nameserver = NULL; - if (nameserver != NULL) { - mgr->nameserver = strdup(nameserver); - } -} - -#endif /* MG_ENABLE_ASYNC_RESOLVER */ diff --git a/src/mongoose-6.11/src/mg_resolv.h b/src/mongoose-6.11/src/mg_resolv.h deleted file mode 100644 index a604c17..0000000 --- a/src/mongoose-6.11/src/mg_resolv.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -/* - * === API reference - */ - -#ifndef CS_MONGOOSE_SRC_RESOLV_H_ -#define CS_MONGOOSE_SRC_RESOLV_H_ - -#include "mg_dns.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -enum mg_resolve_err { - MG_RESOLVE_OK = 0, - MG_RESOLVE_NO_ANSWERS = 1, - MG_RESOLVE_EXCEEDED_RETRY_COUNT = 2, - MG_RESOLVE_TIMEOUT = 3 -}; - -typedef void (*mg_resolve_callback_t)(struct mg_dns_message *dns_message, - void *user_data, enum mg_resolve_err); - -/* Options for `mg_resolve_async_opt`. */ -struct mg_resolve_async_opts { - const char *nameserver; - int max_retries; /* defaults to 2 if zero */ - int timeout; /* in seconds; defaults to 5 if zero */ - int accept_literal; /* pseudo-resolve literal ipv4 and ipv6 addrs */ - int only_literal; /* only resolves literal addrs; sync cb invocation */ - struct mg_connection **dns_conn; /* return DNS connection */ -}; - -/* See `mg_resolve_async_opt()` */ -int mg_resolve_async(struct mg_mgr *mgr, const char *name, int query, - mg_resolve_callback_t cb, void *data); - -/* Set default DNS server */ -void mg_set_nameserver(struct mg_mgr *mgr, const char *nameserver); - -/* - * Resolved a DNS name asynchronously. - * - * Upon successful resolution, the user callback will be invoked - * with the full DNS response message and a pointer to the user's - * context `data`. - * - * In case of timeout while performing the resolution the callback - * will receive a NULL `msg`. - * - * The DNS answers can be extracted with `mg_next_record` and - * `mg_dns_parse_record_data`: - * - * [source,c] - * ---- - * struct in_addr ina; - * struct mg_dns_resource_record *rr = mg_next_record(msg, MG_DNS_A_RECORD, - * NULL); - * mg_dns_parse_record_data(msg, rr, &ina, sizeof(ina)); - * ---- - */ -int mg_resolve_async_opt(struct mg_mgr *mgr, const char *name, int query, - mg_resolve_callback_t cb, void *data, - struct mg_resolve_async_opts opts); - -/* - * Resolve a name from `/etc/hosts`. - * - * Returns 0 on success, -1 on failure. - */ -int mg_resolve_from_hosts_file(const char *host, union socket_address *usa); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* CS_MONGOOSE_SRC_RESOLV_H_ */ diff --git a/src/mongoose-6.11/src/mg_sntp.c b/src/mongoose-6.11/src/mg_sntp.c deleted file mode 100644 index 5d40e1a..0000000 --- a/src/mongoose-6.11/src/mg_sntp.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (c) 2016 Cesanta Software Limited - * All rights reserved - */ - -#include "mg_internal.h" -#include "mg_sntp.h" -#include "mg_util.h" - -#if MG_ENABLE_SNTP - -#define SNTP_TIME_OFFSET 2208988800 - -#ifndef SNTP_TIMEOUT -#define SNTP_TIMEOUT 10 -#endif - -#ifndef SNTP_ATTEMPTS -#define SNTP_ATTEMPTS 3 -#endif - -static uint64_t mg_get_sec(uint64_t val) { - return (val & 0xFFFFFFFF00000000) >> 32; -} - -static uint64_t mg_get_usec(uint64_t val) { - uint64_t tmp = (val & 0x00000000FFFFFFFF); - tmp *= 1000000; - tmp >>= 32; - return tmp; -} - -static void mg_ntp_to_tv(uint64_t val, struct timeval *tv) { - uint64_t tmp; - tmp = mg_get_sec(val); - tmp -= SNTP_TIME_OFFSET; - tv->tv_sec = tmp; - tv->tv_usec = mg_get_usec(val); -} - -static void mg_get_ntp_ts(const char *ntp, uint64_t *val) { - uint32_t tmp; - memcpy(&tmp, ntp, sizeof(tmp)); - tmp = ntohl(tmp); - *val = (uint64_t) tmp << 32; - memcpy(&tmp, ntp + 4, sizeof(tmp)); - tmp = ntohl(tmp); - *val |= tmp; -} - -void mg_sntp_send_request(struct mg_connection *c) { - uint8_t buf[48] = {0}; - /* - * header - 8 bit: - * LI (2 bit) - 3 (not in sync), VN (3 bit) - 4 (version), - * mode (3 bit) - 3 (client) - */ - buf[0] = (3 << 6) | (4 << 3) | 3; - -/* - * Next fields should be empty in client request - * stratum, 8 bit - * poll interval, 8 bit - * rrecision, 8 bit - * root delay, 32 bit - * root dispersion, 32 bit - * ref id, 32 bit - * ref timestamp, 64 bit - * originate Timestamp, 64 bit - * receive Timestamp, 64 bit -*/ - -/* - * convert time to sntp format (sntp starts from 00:00:00 01.01.1900) - * according to rfc868 it is 2208988800L sec - * this information is used to correct roundtrip delay - * but if local clock is absolutely broken (and doesn't work even - * as simple timer), it is better to disable it -*/ -#ifndef MG_SNTP_NO_DELAY_CORRECTION - uint32_t sec; - sec = htonl((uint32_t)(mg_time() + SNTP_TIME_OFFSET)); - memcpy(&buf[40], &sec, sizeof(sec)); -#endif - - mg_send(c, buf, sizeof(buf)); -} - -#ifndef MG_SNTP_NO_DELAY_CORRECTION -static uint64_t mg_calculate_delay(uint64_t t1, uint64_t t2, uint64_t t3) { - /* roundloop delay = (T4 - T1) - (T3 - T2) */ - uint64_t d1 = ((mg_time() + SNTP_TIME_OFFSET) * 1000000) - - (mg_get_sec(t1) * 1000000 + mg_get_usec(t1)); - uint64_t d2 = (mg_get_sec(t3) * 1000000 + mg_get_usec(t3)) - - (mg_get_sec(t2) * 1000000 + mg_get_usec(t2)); - - return (d1 > d2) ? d1 - d2 : 0; -} -#endif - -MG_INTERNAL int mg_sntp_parse_reply(const char *buf, int len, - struct mg_sntp_message *msg) { - uint8_t hdr; - uint64_t trsm_ts_T3, delay = 0; - int mode; - struct timeval tv; - - if (len < 48) { - return -1; - } - - hdr = buf[0]; - - if ((hdr & 0x38) >> 3 != 4) { - /* Wrong version */ - return -1; - } - - mode = hdr & 0x7; - if (mode != 4 && mode != 5) { - /* Not a server reply */ - return -1; - } - - memset(msg, 0, sizeof(*msg)); - - msg->kiss_of_death = (buf[1] == 0); /* Server asks to not send requests */ - - mg_get_ntp_ts(&buf[40], &trsm_ts_T3); - -#ifndef MG_SNTP_NO_DELAY_CORRECTION - { - uint64_t orig_ts_T1, recv_ts_T2; - mg_get_ntp_ts(&buf[24], &orig_ts_T1); - mg_get_ntp_ts(&buf[32], &recv_ts_T2); - delay = mg_calculate_delay(orig_ts_T1, recv_ts_T2, trsm_ts_T3); - } -#endif - - mg_ntp_to_tv(trsm_ts_T3, &tv); - - msg->time = (double) tv.tv_sec + (((double) tv.tv_usec + delay) / 1000000.0); - - return 0; -} - -static void mg_sntp_handler(struct mg_connection *c, int ev, - void *ev_data MG_UD_ARG(void *user_data)) { - struct mbuf *io = &c->recv_mbuf; - struct mg_sntp_message msg; - - c->handler(c, ev, ev_data MG_UD_ARG(user_data)); - - switch (ev) { - case MG_EV_RECV: { - if (mg_sntp_parse_reply(io->buf, io->len, &msg) < 0) { - DBG(("Invalid SNTP packet received (%d)", (int) io->len)); - c->handler(c, MG_SNTP_MALFORMED_REPLY, NULL MG_UD_ARG(user_data)); - } else { - c->handler(c, MG_SNTP_REPLY, (void *) &msg MG_UD_ARG(user_data)); - } - - mbuf_remove(io, io->len); - break; - } - } -} - -int mg_set_protocol_sntp(struct mg_connection *c) { - if ((c->flags & MG_F_UDP) == 0) { - return -1; - } - - c->proto_handler = mg_sntp_handler; - - return 0; -} - -struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, - MG_CB(mg_event_handler_t event_handler, - void *user_data), - const char *sntp_server_name) { - struct mg_connection *c = NULL; - char url[100], *p_url = url; - const char *proto = "", *port = "", *tmp; - - /* If port is not specified, use default (123) */ - tmp = strchr(sntp_server_name, ':'); - if (tmp != NULL && *(tmp + 1) == '/') { - tmp = strchr(tmp + 1, ':'); - } - - if (tmp == NULL) { - port = ":123"; - } - - /* Add udp:// if needed */ - if (strncmp(sntp_server_name, "udp://", 6) != 0) { - proto = "udp://"; - } - - mg_asprintf(&p_url, sizeof(url), "%s%s%s", proto, sntp_server_name, port); - - c = mg_connect(mgr, p_url, event_handler MG_UD_ARG(user_data)); - - if (c == NULL) { - goto cleanup; - } - - mg_set_protocol_sntp(c); - -cleanup: - if (p_url != url) { - MG_FREE(p_url); - } - - return c; -} - -struct sntp_data { - mg_event_handler_t hander; - int count; -}; - -static void mg_sntp_util_ev_handler(struct mg_connection *c, int ev, - void *ev_data MG_UD_ARG(void *user_data)) { -#if !MG_ENABLE_CALLBACK_USERDATA - void *user_data = c->user_data; -#endif - struct sntp_data *sd = (struct sntp_data *) user_data; - - switch (ev) { - case MG_EV_CONNECT: - if (*(int *) ev_data != 0) { - mg_call(c, sd->hander, c->user_data, MG_SNTP_FAILED, NULL); - break; - } - /* fallthrough */ - case MG_EV_TIMER: - if (sd->count <= SNTP_ATTEMPTS) { - mg_sntp_send_request(c); - mg_set_timer(c, mg_time() + 10); - sd->count++; - } else { - mg_call(c, sd->hander, c->user_data, MG_SNTP_FAILED, NULL); - c->flags |= MG_F_CLOSE_IMMEDIATELY; - } - break; - case MG_SNTP_MALFORMED_REPLY: - mg_call(c, sd->hander, c->user_data, MG_SNTP_FAILED, NULL); - c->flags |= MG_F_CLOSE_IMMEDIATELY; - break; - case MG_SNTP_REPLY: - mg_call(c, sd->hander, c->user_data, MG_SNTP_REPLY, ev_data); - c->flags |= MG_F_CLOSE_IMMEDIATELY; - break; - case MG_EV_CLOSE: - MG_FREE(user_data); - c->user_data = NULL; - break; - } -} - -struct mg_connection *mg_sntp_get_time(struct mg_mgr *mgr, - mg_event_handler_t event_handler, - const char *sntp_server_name) { - struct mg_connection *c; - struct sntp_data *sd = (struct sntp_data *) MG_CALLOC(1, sizeof(*sd)); - if (sd == NULL) { - return NULL; - } - - c = mg_sntp_connect(mgr, MG_CB(mg_sntp_util_ev_handler, sd), - sntp_server_name); - if (c == NULL) { - MG_FREE(sd); - return NULL; - } - - sd->hander = event_handler; -#if !MG_ENABLE_CALLBACK_USERDATA - c->user_data = sd; -#endif - - return c; -} - -#endif /* MG_ENABLE_SNTP */ diff --git a/src/mongoose-6.11/src/mg_sntp.h b/src/mongoose-6.11/src/mg_sntp.h deleted file mode 100644 index 5cdff2e..0000000 --- a/src/mongoose-6.11/src/mg_sntp.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_MONGOOSE_SRC_SNTP_H_ -#define CS_MONGOOSE_SRC_SNTP_H_ - -#if MG_ENABLE_SNTP - -#define MG_SNTP_EVENT_BASE 500 - -/* - * Received reply from time server. Event handler parameter contains - * pointer to mg_sntp_message structure - */ -#define MG_SNTP_REPLY (MG_SNTP_EVENT_BASE + 1) - -/* Received malformed SNTP packet */ -#define MG_SNTP_MALFORMED_REPLY (MG_SNTP_EVENT_BASE + 2) - -/* Failed to get time from server (timeout etc) */ -#define MG_SNTP_FAILED (MG_SNTP_EVENT_BASE + 3) - -struct mg_sntp_message { - /* if server sends this flags, user should not send requests to it */ - int kiss_of_death; - /* usual mg_time */ - double time; -}; - -/* Establishes connection to given sntp server */ -struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, - MG_CB(mg_event_handler_t event_handler, - void *user_data), - const char *sntp_server_name); - -/* Sends time request to given connection */ -void mg_sntp_send_request(struct mg_connection *c); - -/* - * Helper function - * Establishes connection to time server, tries to send request - * repeats sending SNTP_ATTEMPTS times every SNTP_TIMEOUT sec - * (if needed) - * See sntp_client example - */ -struct mg_connection *mg_sntp_get_time(struct mg_mgr *mgr, - mg_event_handler_t event_handler, - const char *sntp_server_name); - -#endif - -#endif /* CS_MONGOOSE_SRC_SNTP_H_ */ diff --git a/src/mongoose-6.11/src/mg_socks.c b/src/mongoose-6.11/src/mg_socks.c deleted file mode 100644 index 56b2d5c..0000000 --- a/src/mongoose-6.11/src/mg_socks.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2017 Cesanta Software Limited - * All rights reserved - */ - -#if MG_ENABLE_SOCKS - -#include "mg_socks.h" -#include "mg_internal.h" - -/* - * https://www.ietf.org/rfc/rfc1928.txt paragraph 3, handle client handshake - * - * +----+----------+----------+ - * |VER | NMETHODS | METHODS | - * +----+----------+----------+ - * | 1 | 1 | 1 to 255 | - * +----+----------+----------+ - */ -static void mg_socks5_handshake(struct mg_connection *c) { - struct mbuf *r = &c->recv_mbuf; - if (r->buf[0] != MG_SOCKS_VERSION) { - c->flags |= MG_F_CLOSE_IMMEDIATELY; - } else if (r->len > 2 && (size_t) r->buf[1] + 2 <= r->len) { - /* https://www.ietf.org/rfc/rfc1928.txt paragraph 3 */ - unsigned char reply[2] = {MG_SOCKS_VERSION, MG_SOCKS_HANDSHAKE_FAILURE}; - int i; - for (i = 2; i < r->buf[1] + 2; i++) { - /* TODO(lsm): support other auth methods */ - if (r->buf[i] == MG_SOCKS_HANDSHAKE_NOAUTH) reply[1] = r->buf[i]; - } - mbuf_remove(r, 2 + r->buf[1]); - mg_send(c, reply, sizeof(reply)); - c->flags |= MG_SOCKS_HANDSHAKE_DONE; /* Mark handshake done */ - } -} - -static void disband(struct mg_connection *c) { - struct mg_connection *c2 = (struct mg_connection *) c->user_data; - if (c2 != NULL) { - c2->flags |= MG_F_SEND_AND_CLOSE; - c2->user_data = NULL; - } - c->flags |= MG_F_SEND_AND_CLOSE; - c->user_data = NULL; -} - -static void relay_data(struct mg_connection *c) { - struct mg_connection *c2 = (struct mg_connection *) c->user_data; - if (c2 != NULL) { - mg_send(c2, c->recv_mbuf.buf, c->recv_mbuf.len); - mbuf_remove(&c->recv_mbuf, c->recv_mbuf.len); - } else { - c->flags |= MG_F_SEND_AND_CLOSE; - } -} - -static void serv_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_CLOSE) { - disband(c); - } else if (ev == MG_EV_RECV) { - relay_data(c); - } else if (ev == MG_EV_CONNECT) { - int res = *(int *) ev_data; - if (res != 0) LOG(LL_ERROR, ("connect error: %d", res)); - } -} - -static void mg_socks5_connect(struct mg_connection *c, const char *addr) { - struct mg_connection *serv = mg_connect(c->mgr, addr, serv_ev_handler); - serv->user_data = c; - c->user_data = serv; -} - -/* - * Request, https://www.ietf.org/rfc/rfc1928.txt paragraph 4 - * - * +----+-----+-------+------+----------+----------+ - * |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | - * +----+-----+-------+------+----------+----------+ - * | 1 | 1 | X'00' | 1 | Variable | 2 | - * +----+-----+-------+------+----------+----------+ - */ -static void mg_socks5_handle_request(struct mg_connection *c) { - struct mbuf *r = &c->recv_mbuf; - unsigned char *p = (unsigned char *) r->buf; - unsigned char addr_len = 4, reply = MG_SOCKS_SUCCESS; - int ver, cmd, atyp; - char addr[300]; - - if (r->len < 8) return; /* return if not fully buffered. min DST.ADDR is 2 */ - ver = p[0]; - cmd = p[1]; - atyp = p[3]; - - /* TODO(lsm): support other commands */ - if (ver != MG_SOCKS_VERSION || cmd != MG_SOCKS_CMD_CONNECT) { - reply = MG_SOCKS_CMD_NOT_SUPPORTED; - } else if (atyp == MG_SOCKS_ADDR_IPV4) { - addr_len = 4; - if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */ - snprintf(addr, sizeof(addr), "%d.%d.%d.%d:%d", p[4], p[5], p[6], p[7], - p[8] << 8 | p[9]); - mg_socks5_connect(c, addr); - } else if (atyp == MG_SOCKS_ADDR_IPV6) { - addr_len = 16; - if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */ - snprintf(addr, sizeof(addr), "[%x:%x:%x:%x:%x:%x:%x:%x]:%d", - p[4] << 8 | p[5], p[6] << 8 | p[7], p[8] << 8 | p[9], - p[10] << 8 | p[11], p[12] << 8 | p[13], p[14] << 8 | p[15], - p[16] << 8 | p[17], p[18] << 8 | p[19], p[20] << 8 | p[21]); - mg_socks5_connect(c, addr); - } else if (atyp == MG_SOCKS_ADDR_DOMAIN) { - addr_len = p[4] + 1; - if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */ - snprintf(addr, sizeof(addr), "%.*s:%d", p[4], p + 5, - p[4 + addr_len] << 8 | p[4 + addr_len + 1]); - mg_socks5_connect(c, addr); - } else { - reply = MG_SOCKS_ADDR_NOT_SUPPORTED; - } - - /* - * Reply, https://www.ietf.org/rfc/rfc1928.txt paragraph 5 - * - * +----+-----+-------+------+----------+----------+ - * |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | - * +----+-----+-------+------+----------+----------+ - * | 1 | 1 | X'00' | 1 | Variable | 2 | - * +----+-----+-------+------+----------+----------+ - */ - { - unsigned char buf[] = {MG_SOCKS_VERSION, reply, 0}; - mg_send(c, buf, sizeof(buf)); - } - mg_send(c, r->buf + 3, addr_len + 1 + 2); - - mbuf_remove(r, 6 + addr_len); /* Remove request from the input stream */ - c->flags |= MG_SOCKS_CONNECT_DONE; /* Mark ourselves as connected */ -} - -static void socks_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_RECV) { - if (!(c->flags & MG_SOCKS_HANDSHAKE_DONE)) mg_socks5_handshake(c); - if (c->flags & MG_SOCKS_HANDSHAKE_DONE && - !(c->flags & MG_SOCKS_CONNECT_DONE)) { - mg_socks5_handle_request(c); - } - if (c->flags & MG_SOCKS_CONNECT_DONE) relay_data(c); - } else if (ev == MG_EV_CLOSE) { - disband(c); - } - (void) ev_data; -} - -void mg_set_protocol_socks(struct mg_connection *c) { - c->proto_handler = socks_handler; -} -#endif diff --git a/src/mongoose-6.11/src/mg_socks.h b/src/mongoose-6.11/src/mg_socks.h deleted file mode 100644 index e7b231a..0000000 --- a/src/mongoose-6.11/src/mg_socks.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2017 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_MONGOOSE_SRC_SOCKS_H_ -#define CS_MONGOOSE_SRC_SOCKS_H_ - -#if MG_ENABLE_SOCKS - -#define MG_SOCKS_VERSION 5 - -#define MG_SOCKS_HANDSHAKE_DONE MG_F_USER_1 -#define MG_SOCKS_CONNECT_DONE MG_F_USER_2 - -/* SOCKS5 handshake methods */ -enum mg_socks_handshake_method { - MG_SOCKS_HANDSHAKE_NOAUTH = 0, /* Handshake method - no authentication */ - MG_SOCKS_HANDSHAKE_GSSAPI = 1, /* Handshake method - GSSAPI auth */ - MG_SOCKS_HANDSHAKE_USERPASS = 2, /* Handshake method - user/password auth */ - MG_SOCKS_HANDSHAKE_FAILURE = 0xff, /* Handshake method - failure */ -}; - -/* SOCKS5 commands */ -enum mg_socks_command { - MG_SOCKS_CMD_CONNECT = 1, /* Command: CONNECT */ - MG_SOCKS_CMD_BIND = 2, /* Command: BIND */ - MG_SOCKS_CMD_UDP_ASSOCIATE = 3, /* Command: UDP ASSOCIATE */ -}; - -/* SOCKS5 address types */ -enum mg_socks_address_type { - MG_SOCKS_ADDR_IPV4 = 1, /* Address type: IPv4 */ - MG_SOCKS_ADDR_DOMAIN = 3, /* Address type: Domain name */ - MG_SOCKS_ADDR_IPV6 = 4, /* Address type: IPv6 */ -}; - -/* SOCKS5 response codes */ -enum mg_socks_response { - MG_SOCKS_SUCCESS = 0, /* Response: success */ - MG_SOCKS_FAILURE = 1, /* Response: failure */ - MG_SOCKS_NOT_ALLOWED = 2, /* Response: connection not allowed */ - MG_SOCKS_NET_UNREACHABLE = 3, /* Response: network unreachable */ - MG_SOCKS_HOST_UNREACHABLE = 4, /* Response: network unreachable */ - MG_SOCKS_CONN_REFUSED = 5, /* Response: network unreachable */ - MG_SOCKS_TTL_EXPIRED = 6, /* Response: network unreachable */ - MG_SOCKS_CMD_NOT_SUPPORTED = 7, /* Response: network unreachable */ - MG_SOCKS_ADDR_NOT_SUPPORTED = 8, /* Response: network unreachable */ -}; - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* Turn the connection into the SOCKS server */ -void mg_set_protocol_socks(struct mg_connection *c); - -/* Create socks tunnel for the client connection */ -struct mg_iface *mg_socks_mk_iface(struct mg_mgr *, const char *proxy_addr); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif -#endif diff --git a/src/mongoose-6.11/src/mg_ssl_if.h b/src/mongoose-6.11/src/mg_ssl_if.h deleted file mode 100644 index 62f24b5..0000000 --- a/src/mongoose-6.11/src/mg_ssl_if.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_MONGOOSE_SRC_SSL_IF_H_ -#define CS_MONGOOSE_SRC_SSL_IF_H_ - -#if MG_ENABLE_SSL - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -struct mg_ssl_if_ctx; -struct mg_connection; - -void mg_ssl_if_init(); - -enum mg_ssl_if_result { - MG_SSL_OK = 0, - MG_SSL_WANT_READ = -1, - MG_SSL_WANT_WRITE = -2, - MG_SSL_ERROR = -3, -}; - -struct mg_ssl_if_conn_params { - const char *cert; - const char *key; - const char *ca_cert; - const char *server_name; - const char *cipher_suites; - const char *psk_identity; - const char *psk_key; -}; - -enum mg_ssl_if_result mg_ssl_if_conn_init( - struct mg_connection *nc, const struct mg_ssl_if_conn_params *params, - const char **err_msg); -enum mg_ssl_if_result mg_ssl_if_conn_accept(struct mg_connection *nc, - struct mg_connection *lc); -void mg_ssl_if_conn_close_notify(struct mg_connection *nc); -void mg_ssl_if_conn_free(struct mg_connection *nc); - -enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc); -int mg_ssl_if_read(struct mg_connection *nc, void *buf, size_t buf_size); -int mg_ssl_if_write(struct mg_connection *nc, const void *data, size_t len); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* MG_ENABLE_SSL */ - -#endif /* CS_MONGOOSE_SRC_SSL_IF_H_ */ diff --git a/src/mongoose-6.11/src/mg_ssl_if_mbedtls.c b/src/mongoose-6.11/src/mg_ssl_if_mbedtls.c deleted file mode 100644 index 4c1c1db..0000000 --- a/src/mongoose-6.11/src/mg_ssl_if_mbedtls.c +++ /dev/null @@ -1,478 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_MBEDTLS - -#include -#include -#include -#include -#include - -static void mg_ssl_mbed_log(void *ctx, int level, const char *file, int line, - const char *str) { - enum cs_log_level cs_level; - switch (level) { - case 1: - cs_level = LL_ERROR; - break; - case 2: - case 3: - cs_level = LL_DEBUG; - break; - default: - cs_level = LL_VERBOSE_DEBUG; - } - /* mbedTLS passes strings with \n at the end, strip it. */ - LOG(cs_level, ("%p %.*s", ctx, (int) (strlen(str) - 1), str)); - (void) file; - (void) line; -} - -struct mg_ssl_if_ctx { - mbedtls_ssl_config *conf; - mbedtls_ssl_context *ssl; - mbedtls_x509_crt *cert; - mbedtls_pk_context *key; - mbedtls_x509_crt *ca_cert; - struct mbuf cipher_suites; -}; - -/* Must be provided by the platform. ctx is struct mg_connection. */ -extern int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len); - -void mg_ssl_if_init() { -} - -enum mg_ssl_if_result mg_ssl_if_conn_accept(struct mg_connection *nc, - struct mg_connection *lc) { - struct mg_ssl_if_ctx *ctx = - (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx)); - struct mg_ssl_if_ctx *lc_ctx = (struct mg_ssl_if_ctx *) lc->ssl_if_data; - nc->ssl_if_data = ctx; - if (ctx == NULL || lc_ctx == NULL) return MG_SSL_ERROR; - ctx->ssl = (mbedtls_ssl_context *) MG_CALLOC(1, sizeof(*ctx->ssl)); - if (mbedtls_ssl_setup(ctx->ssl, lc_ctx->conf) != 0) { - return MG_SSL_ERROR; - } - return MG_SSL_OK; -} - -static enum mg_ssl_if_result mg_use_cert(struct mg_ssl_if_ctx *ctx, - const char *cert, const char *key, - const char **err_msg); -static enum mg_ssl_if_result mg_use_ca_cert(struct mg_ssl_if_ctx *ctx, - const char *cert); -static enum mg_ssl_if_result mg_set_cipher_list(struct mg_ssl_if_ctx *ctx, - const char *ciphers); -static enum mg_ssl_if_result mg_ssl_if_mbed_set_psk(struct mg_ssl_if_ctx *ctx, - const char *identity, - const char *key); - -enum mg_ssl_if_result mg_ssl_if_conn_init( - struct mg_connection *nc, const struct mg_ssl_if_conn_params *params, - const char **err_msg) { - struct mg_ssl_if_ctx *ctx = - (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx)); - DBG(("%p %s,%s,%s", nc, (params->cert ? params->cert : ""), - (params->key ? params->key : ""), - (params->ca_cert ? params->ca_cert : ""))); - - if (ctx == NULL) { - MG_SET_PTRPTR(err_msg, "Out of memory"); - return MG_SSL_ERROR; - } - nc->ssl_if_data = ctx; - ctx->conf = (mbedtls_ssl_config *) MG_CALLOC(1, sizeof(*ctx->conf)); - mbuf_init(&ctx->cipher_suites, 0); - mbedtls_ssl_config_init(ctx->conf); - mbedtls_ssl_conf_dbg(ctx->conf, mg_ssl_mbed_log, nc); - if (mbedtls_ssl_config_defaults( - ctx->conf, (nc->flags & MG_F_LISTENING ? MBEDTLS_SSL_IS_SERVER - : MBEDTLS_SSL_IS_CLIENT), - MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT) != 0) { - MG_SET_PTRPTR(err_msg, "Failed to init SSL config"); - return MG_SSL_ERROR; - } - - /* TLS 1.2 and up */ - mbedtls_ssl_conf_min_version(ctx->conf, MBEDTLS_SSL_MAJOR_VERSION_3, - MBEDTLS_SSL_MINOR_VERSION_3); - mbedtls_ssl_conf_rng(ctx->conf, mg_ssl_if_mbed_random, nc); - - if (params->cert != NULL && - mg_use_cert(ctx, params->cert, params->key, err_msg) != MG_SSL_OK) { - return MG_SSL_ERROR; - } - - if (params->ca_cert != NULL && - mg_use_ca_cert(ctx, params->ca_cert) != MG_SSL_OK) { - MG_SET_PTRPTR(err_msg, "Invalid SSL CA cert"); - return MG_SSL_ERROR; - } - - if (mg_set_cipher_list(ctx, params->cipher_suites) != MG_SSL_OK) { - MG_SET_PTRPTR(err_msg, "Invalid cipher suite list"); - return MG_SSL_ERROR; - } - - if (mg_ssl_if_mbed_set_psk(ctx, params->psk_identity, params->psk_key) != - MG_SSL_OK) { - MG_SET_PTRPTR(err_msg, "Invalid PSK settings"); - return MG_SSL_ERROR; - } - - if (!(nc->flags & MG_F_LISTENING)) { - ctx->ssl = (mbedtls_ssl_context *) MG_CALLOC(1, sizeof(*ctx->ssl)); - mbedtls_ssl_init(ctx->ssl); - if (mbedtls_ssl_setup(ctx->ssl, ctx->conf) != 0) { - MG_SET_PTRPTR(err_msg, "Failed to create SSL session"); - return MG_SSL_ERROR; - } - if (params->server_name != NULL && - mbedtls_ssl_set_hostname(ctx->ssl, params->server_name) != 0) { - return MG_SSL_ERROR; - } - } - -#ifdef MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN - if (mbedtls_ssl_conf_max_frag_len(ctx->conf, -#if MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN == 512 - MBEDTLS_SSL_MAX_FRAG_LEN_512 -#elif MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN == 1024 - MBEDTLS_SSL_MAX_FRAG_LEN_1024 -#elif MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN == 2048 - MBEDTLS_SSL_MAX_FRAG_LEN_2048 -#elif MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN == 4096 - MBEDTLS_SSL_MAX_FRAG_LEN_4096 -#else -#error Invalid MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN -#endif - ) != 0) { - return MG_SSL_ERROR; - } -#endif - - nc->flags |= MG_F_SSL; - - return MG_SSL_OK; -} - -#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL -int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len); -int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len); -#else -static int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len) { - struct mg_connection *nc = (struct mg_connection *) ctx; - int n = (int) MG_SEND_FUNC(nc->sock, buf, len, 0); - LOG(LL_DEBUG, ("%p %d -> %d", nc, (int) len, n)); - if (n >= 0) return n; - n = mg_get_errno(); - return ((n == EAGAIN || n == EINPROGRESS) ? MBEDTLS_ERR_SSL_WANT_WRITE : -1); -} - -static int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len) { - struct mg_connection *nc = (struct mg_connection *) ctx; - int n = (int) MG_RECV_FUNC(nc->sock, buf, len, 0); - LOG(LL_DEBUG, ("%p %d <- %d", nc, (int) len, n)); - if (n >= 0) return n; - n = mg_get_errno(); - return ((n == EAGAIN || n == EINPROGRESS) ? MBEDTLS_ERR_SSL_WANT_READ : -1); -} -#endif - -static enum mg_ssl_if_result mg_ssl_if_mbed_err(struct mg_connection *nc, - int ret) { - if (ret == MBEDTLS_ERR_SSL_WANT_READ) return MG_SSL_WANT_READ; - if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) return MG_SSL_WANT_WRITE; - if (ret != - MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { /* CLOSE_NOTIFY = Normal shutdown */ - LOG(LL_ERROR, ("%p SSL error: %d", nc, ret)); - } - nc->err = ret; - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - return MG_SSL_ERROR; -} - -static void mg_ssl_if_mbed_free_certs_and_keys(struct mg_ssl_if_ctx *ctx) { - if (ctx->cert != NULL) { - mbedtls_x509_crt_free(ctx->cert); - MG_FREE(ctx->cert); - ctx->cert = NULL; - mbedtls_pk_free(ctx->key); - MG_FREE(ctx->key); - ctx->key = NULL; - } - if (ctx->ca_cert != NULL) { - mbedtls_ssl_conf_ca_chain(ctx->conf, NULL, NULL); -#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK - if (ctx->ca_cert->ca_chain_file != NULL) { - MG_FREE((void *) ctx->ca_cert->ca_chain_file); - ctx->ca_cert->ca_chain_file = NULL; - } -#endif - mbedtls_x509_crt_free(ctx->ca_cert); - MG_FREE(ctx->ca_cert); - ctx->ca_cert = NULL; - } -} - -enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc) { - struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; - int err; - /* If bio is not yet set, do it now. */ - if (ctx->ssl->p_bio == NULL) { - mbedtls_ssl_set_bio(ctx->ssl, nc, ssl_socket_send, ssl_socket_recv, NULL); - } - err = mbedtls_ssl_handshake(ctx->ssl); - if (err != 0) return mg_ssl_if_mbed_err(nc, err); -#ifdef MG_SSL_IF_MBEDTLS_FREE_CERTS - /* - * Free the peer certificate, we don't need it after handshake. - * Note that this effectively disables renegotiation. - */ - mbedtls_x509_crt_free(ctx->ssl->session->peer_cert); - mbedtls_free(ctx->ssl->session->peer_cert); - ctx->ssl->session->peer_cert = NULL; - /* On a client connection we can also free our own and CA certs. */ - if (nc->listener == NULL) { - if (ctx->conf->key_cert != NULL) { - /* Note that this assumes one key_cert entry, which matches our init. */ - MG_FREE(ctx->conf->key_cert); - ctx->conf->key_cert = NULL; - } - mbedtls_ssl_conf_ca_chain(ctx->conf, NULL, NULL); - mg_ssl_if_mbed_free_certs_and_keys(ctx); - } -#endif - return MG_SSL_OK; -} - -int mg_ssl_if_read(struct mg_connection *nc, void *buf, size_t buf_size) { - struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; - int n = mbedtls_ssl_read(ctx->ssl, (unsigned char *) buf, buf_size); - DBG(("%p %d -> %d", nc, (int) buf_size, n)); - if (n < 0) return mg_ssl_if_mbed_err(nc, n); - if (n == 0) nc->flags |= MG_F_CLOSE_IMMEDIATELY; - return n; -} - -int mg_ssl_if_write(struct mg_connection *nc, const void *data, size_t len) { - struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; - int n = mbedtls_ssl_write(ctx->ssl, (const unsigned char *) data, len); - DBG(("%p %d -> %d", nc, (int) len, n)); - if (n < 0) return mg_ssl_if_mbed_err(nc, n); - return n; -} - -void mg_ssl_if_conn_close_notify(struct mg_connection *nc) { - struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; - if (ctx == NULL) return; - mbedtls_ssl_close_notify(ctx->ssl); -} - -void mg_ssl_if_conn_free(struct mg_connection *nc) { - struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; - if (ctx == NULL) return; - nc->ssl_if_data = NULL; - if (ctx->ssl != NULL) { - mbedtls_ssl_free(ctx->ssl); - MG_FREE(ctx->ssl); - } - mg_ssl_if_mbed_free_certs_and_keys(ctx); - if (ctx->conf != NULL) { - mbedtls_ssl_config_free(ctx->conf); - MG_FREE(ctx->conf); - } - mbuf_free(&ctx->cipher_suites); - memset(ctx, 0, sizeof(*ctx)); - MG_FREE(ctx); -} - -static enum mg_ssl_if_result mg_use_ca_cert(struct mg_ssl_if_ctx *ctx, - const char *ca_cert) { - if (ca_cert == NULL || strcmp(ca_cert, "*") == 0) { - mbedtls_ssl_conf_authmode(ctx->conf, MBEDTLS_SSL_VERIFY_NONE); - return MG_SSL_OK; - } - ctx->ca_cert = (mbedtls_x509_crt *) MG_CALLOC(1, sizeof(*ctx->ca_cert)); - mbedtls_x509_crt_init(ctx->ca_cert); -#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK - ca_cert = strdup(ca_cert); - if (mbedtls_x509_crt_set_ca_chain_file(ctx->ca_cert, ca_cert) != 0) { - return MG_SSL_ERROR; - } -#else - if (mbedtls_x509_crt_parse_file(ctx->ca_cert, ca_cert) != 0) { - return MG_SSL_ERROR; - } -#endif - mbedtls_ssl_conf_ca_chain(ctx->conf, ctx->ca_cert, NULL); - mbedtls_ssl_conf_authmode(ctx->conf, MBEDTLS_SSL_VERIFY_REQUIRED); - return MG_SSL_OK; -} - -static enum mg_ssl_if_result mg_use_cert(struct mg_ssl_if_ctx *ctx, - const char *cert, const char *key, - const char **err_msg) { - if (key == NULL) key = cert; - if (cert == NULL || cert[0] == '\0' || key == NULL || key[0] == '\0') { - return MG_SSL_OK; - } - ctx->cert = (mbedtls_x509_crt *) MG_CALLOC(1, sizeof(*ctx->cert)); - mbedtls_x509_crt_init(ctx->cert); - ctx->key = (mbedtls_pk_context *) MG_CALLOC(1, sizeof(*ctx->key)); - mbedtls_pk_init(ctx->key); - if (mbedtls_x509_crt_parse_file(ctx->cert, cert) != 0) { - MG_SET_PTRPTR(err_msg, "Invalid SSL cert"); - return MG_SSL_ERROR; - } - if (mbedtls_pk_parse_keyfile(ctx->key, key, NULL) != 0) { - MG_SET_PTRPTR(err_msg, "Invalid SSL key"); - return MG_SSL_ERROR; - } - if (mbedtls_ssl_conf_own_cert(ctx->conf, ctx->cert, ctx->key) != 0) { - MG_SET_PTRPTR(err_msg, "Invalid SSL key or cert"); - return MG_SSL_ERROR; - } - return MG_SSL_OK; -} - -static const int mg_s_cipher_list[] = { -#if CS_PLATFORM != CS_P_ESP8266 - MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, - MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, - MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, - MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, - MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, - MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, - MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, - MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, - MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256, - MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256, - MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA, -#else - /* - * ECDHE is way too slow on ESP8266 w/o cryptochip, this sometimes results - * in WiFi STA deauths. Use weaker but faster cipher suites. Sad but true. - * Disable DHE completely because it's just hopelessly slow. - */ - MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256, - MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256, - MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, - MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, - MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, - MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, - MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, - MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, - MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA, -#endif /* CS_PLATFORM != CS_P_ESP8266 */ - 0, -}; - -/* - * Ciphers can be specified as a colon-separated list of cipher suite names. - * These can be found in - * https://github.com/ARMmbed/mbedtls/blob/development/library/ssl_ciphersuites.c#L267 - * E.g.: TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-CCM - */ -static enum mg_ssl_if_result mg_set_cipher_list(struct mg_ssl_if_ctx *ctx, - const char *ciphers) { - if (ciphers != NULL) { - int l, id; - const char *s = ciphers, *e; - char tmp[50]; - while (s != NULL) { - e = strchr(s, ':'); - l = (e != NULL ? (e - s) : (int) strlen(s)); - strncpy(tmp, s, l); - tmp[l] = '\0'; - id = mbedtls_ssl_get_ciphersuite_id(tmp); - DBG(("%s -> %04x", tmp, id)); - if (id != 0) { - mbuf_append(&ctx->cipher_suites, &id, sizeof(id)); - } - s = (e != NULL ? e + 1 : NULL); - } - if (ctx->cipher_suites.len == 0) return MG_SSL_ERROR; - id = 0; - mbuf_append(&ctx->cipher_suites, &id, sizeof(id)); - mbuf_trim(&ctx->cipher_suites); - mbedtls_ssl_conf_ciphersuites(ctx->conf, - (const int *) ctx->cipher_suites.buf); - } else { - mbedtls_ssl_conf_ciphersuites(ctx->conf, mg_s_cipher_list); - } - return MG_SSL_OK; -} - -static enum mg_ssl_if_result mg_ssl_if_mbed_set_psk(struct mg_ssl_if_ctx *ctx, - const char *identity, - const char *key_str) { - unsigned char key[32]; - size_t key_len; - if (identity == NULL && key_str == NULL) return MG_SSL_OK; - if (identity == NULL || key_str == NULL) return MG_SSL_ERROR; - key_len = strlen(key_str); - if (key_len != 32 && key_len != 64) return MG_SSL_ERROR; - size_t i = 0; - memset(key, 0, sizeof(key)); - key_len = 0; - for (i = 0; key_str[i] != '\0'; i++) { - unsigned char c; - char hc = tolower((int) key_str[i]); - if (hc >= '0' && hc <= '9') { - c = hc - '0'; - } else if (hc >= 'a' && hc <= 'f') { - c = hc - 'a' + 0xa; - } else { - return MG_SSL_ERROR; - } - key_len = i / 2; - key[key_len] <<= 4; - key[key_len] |= c; - } - key_len++; - DBG(("identity = '%s', key = (%u)", identity, (unsigned int) key_len)); - /* mbedTLS makes copies of psk and identity. */ - if (mbedtls_ssl_conf_psk(ctx->conf, (const unsigned char *) key, key_len, - (const unsigned char *) identity, - strlen(identity)) != 0) { - return MG_SSL_ERROR; - } - return MG_SSL_OK; -} - -const char *mg_set_ssl(struct mg_connection *nc, const char *cert, - const char *ca_cert) { - const char *err_msg = NULL; - struct mg_ssl_if_conn_params params; - memset(¶ms, 0, sizeof(params)); - params.cert = cert; - params.ca_cert = ca_cert; - if (mg_ssl_if_conn_init(nc, ¶ms, &err_msg) != MG_SSL_OK) { - return err_msg; - } - return NULL; -} - -/* Lazy RNG. Warning: it would be a bad idea to do this in production! */ -#ifdef MG_SSL_MBED_DUMMY_RANDOM -int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len) { - (void) ctx; - while (len--) *buf++ = rand(); - return 0; -} -#endif - -#endif /* MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_MBEDTLS */ diff --git a/src/mongoose-6.11/src/mg_ssl_if_openssl.c b/src/mongoose-6.11/src/mg_ssl_if_openssl.c deleted file mode 100644 index 2f3da5e..0000000 --- a/src/mongoose-6.11/src/mg_ssl_if_openssl.c +++ /dev/null @@ -1,394 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_OPENSSL - -#ifdef __APPLE__ -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif - -#include - -struct mg_ssl_if_ctx { - SSL *ssl; - SSL_CTX *ssl_ctx; - struct mbuf psk; - size_t identity_len; -}; - -void mg_ssl_if_init() { - SSL_library_init(); -} - -enum mg_ssl_if_result mg_ssl_if_conn_accept(struct mg_connection *nc, - struct mg_connection *lc) { - struct mg_ssl_if_ctx *ctx = - (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx)); - struct mg_ssl_if_ctx *lc_ctx = (struct mg_ssl_if_ctx *) lc->ssl_if_data; - nc->ssl_if_data = ctx; - if (ctx == NULL || lc_ctx == NULL) return MG_SSL_ERROR; - ctx->ssl_ctx = lc_ctx->ssl_ctx; - if ((ctx->ssl = SSL_new(ctx->ssl_ctx)) == NULL) { - return MG_SSL_ERROR; - } - return MG_SSL_OK; -} - -static enum mg_ssl_if_result mg_use_cert(SSL_CTX *ctx, const char *cert, - const char *key, const char **err_msg); -static enum mg_ssl_if_result mg_use_ca_cert(SSL_CTX *ctx, const char *cert); -static enum mg_ssl_if_result mg_set_cipher_list(SSL_CTX *ctx, const char *cl); -static enum mg_ssl_if_result mg_ssl_if_ossl_set_psk(struct mg_ssl_if_ctx *ctx, - const char *identity, - const char *key_str); - -enum mg_ssl_if_result mg_ssl_if_conn_init( - struct mg_connection *nc, const struct mg_ssl_if_conn_params *params, - const char **err_msg) { - struct mg_ssl_if_ctx *ctx = - (struct mg_ssl_if_ctx *) MG_CALLOC(1, sizeof(*ctx)); - DBG(("%p %s,%s,%s", nc, (params->cert ? params->cert : ""), - (params->key ? params->key : ""), - (params->ca_cert ? params->ca_cert : ""))); - if (ctx == NULL) { - MG_SET_PTRPTR(err_msg, "Out of memory"); - return MG_SSL_ERROR; - } - nc->ssl_if_data = ctx; - if (nc->flags & MG_F_LISTENING) { - ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method()); - } else { - ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method()); - } - if (ctx->ssl_ctx == NULL) { - MG_SET_PTRPTR(err_msg, "Failed to create SSL context"); - return MG_SSL_ERROR; - } - -#ifndef KR_VERSION - /* Disable deprecated protocols. */ - SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2); - SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv3); - SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1); -#ifdef MG_SSL_OPENSSL_NO_COMPRESSION - SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_COMPRESSION); -#endif -#ifdef MG_SSL_OPENSSL_CIPHER_SERVER_PREFERENCE - SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); -#endif -#else -/* Krypton only supports TLSv1.2 anyway. */ -#endif - - if (params->cert != NULL && - mg_use_cert(ctx->ssl_ctx, params->cert, params->key, err_msg) != - MG_SSL_OK) { - return MG_SSL_ERROR; - } - - if (params->ca_cert != NULL && - mg_use_ca_cert(ctx->ssl_ctx, params->ca_cert) != MG_SSL_OK) { - MG_SET_PTRPTR(err_msg, "Invalid SSL CA cert"); - return MG_SSL_ERROR; - } - - if (params->server_name != NULL) { -#ifdef KR_VERSION - SSL_CTX_kr_set_verify_name(ctx->ssl_ctx, params->server_name); -#else -/* TODO(rojer): Implement server name verification on OpenSSL. */ -#endif - } - - if (mg_set_cipher_list(ctx->ssl_ctx, params->cipher_suites) != MG_SSL_OK) { - MG_SET_PTRPTR(err_msg, "Invalid cipher suite list"); - return MG_SSL_ERROR; - } - - mbuf_init(&ctx->psk, 0); - if (mg_ssl_if_ossl_set_psk(ctx, params->psk_identity, params->psk_key) != - MG_SSL_OK) { - MG_SET_PTRPTR(err_msg, "Invalid PSK settings"); - return MG_SSL_ERROR; - } - - if (!(nc->flags & MG_F_LISTENING) && - (ctx->ssl = SSL_new(ctx->ssl_ctx)) == NULL) { - MG_SET_PTRPTR(err_msg, "Failed to create SSL session"); - return MG_SSL_ERROR; - } - - nc->flags |= MG_F_SSL; - - return MG_SSL_OK; -} - -static enum mg_ssl_if_result mg_ssl_if_ssl_err(struct mg_connection *nc, - int res) { - struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; - int err = SSL_get_error(ctx->ssl, res); - if (err == SSL_ERROR_WANT_READ) return MG_SSL_WANT_READ; - if (err == SSL_ERROR_WANT_WRITE) return MG_SSL_WANT_WRITE; - DBG(("%p %p SSL error: %d %d", nc, ctx->ssl_ctx, res, err)); - nc->err = err; - return MG_SSL_ERROR; -} - -enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc) { - struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; - int server_side = (nc->listener != NULL); - int res; - /* If descriptor is not yet set, do it now. */ - if (SSL_get_fd(ctx->ssl) < 0) { - if (SSL_set_fd(ctx->ssl, nc->sock) != 1) return MG_SSL_ERROR; - } - res = server_side ? SSL_accept(ctx->ssl) : SSL_connect(ctx->ssl); - if (res != 1) return mg_ssl_if_ssl_err(nc, res); - return MG_SSL_OK; -} - -int mg_ssl_if_read(struct mg_connection *nc, void *buf, size_t buf_size) { - struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; - int n = SSL_read(ctx->ssl, buf, buf_size); - DBG(("%p %d -> %d", nc, (int) buf_size, n)); - if (n < 0) return mg_ssl_if_ssl_err(nc, n); - if (n == 0) nc->flags |= MG_F_CLOSE_IMMEDIATELY; - return n; -} - -int mg_ssl_if_write(struct mg_connection *nc, const void *data, size_t len) { - struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; - int n = SSL_write(ctx->ssl, data, len); - DBG(("%p %d -> %d", nc, (int) len, n)); - if (n <= 0) return mg_ssl_if_ssl_err(nc, n); - return n; -} - -void mg_ssl_if_conn_close_notify(struct mg_connection *nc) { - struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; - if (ctx == NULL) return; - SSL_shutdown(ctx->ssl); -} - -void mg_ssl_if_conn_free(struct mg_connection *nc) { - struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; - if (ctx == NULL) return; - nc->ssl_if_data = NULL; - if (ctx->ssl != NULL) SSL_free(ctx->ssl); - if (ctx->ssl_ctx != NULL && nc->listener == NULL) SSL_CTX_free(ctx->ssl_ctx); - mbuf_free(&ctx->psk); - memset(ctx, 0, sizeof(*ctx)); - MG_FREE(ctx); -} - -/* - * Cipher suite options used for TLS negotiation. - * https://wiki.mozilla.org/Security/Server_Side_TLS#Recommended_configurations - */ -static const char mg_s_cipher_list[] = -#if defined(MG_SSL_CRYPTO_MODERN) - "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:" - "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:" - "DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:" - "ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:" - "ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:" - "ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:" - "DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:" - "DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:" - "!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK" -#elif defined(MG_SSL_CRYPTO_OLD) - "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:" - "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:" - "DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:" - "ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:" - "ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:" - "ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:" - "DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:" - "DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:" - "ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:" - "AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:" - "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:" - "!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA" -#else /* Default - intermediate. */ - "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:" - "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:" - "DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:" - "ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:" - "ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:" - "ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:" - "DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:" - "DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:" - "AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:" - "DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:" - "!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA" -#endif - ; - -/* - * Default DH params for PFS cipher negotiation. This is a 2048-bit group. - * Will be used if none are provided by the user in the certificate file. - */ -#if !MG_DISABLE_PFS && !defined(KR_VERSION) -static const char mg_s_default_dh_params[] = - "\ ------BEGIN DH PARAMETERS-----\n\ -MIIBCAKCAQEAlvbgD/qh9znWIlGFcV0zdltD7rq8FeShIqIhkQ0C7hYFThrBvF2E\n\ -Z9bmgaP+sfQwGpVlv9mtaWjvERbu6mEG7JTkgmVUJrUt/wiRzwTaCXBqZkdUO8Tq\n\ -+E6VOEQAilstG90ikN1Tfo+K6+X68XkRUIlgawBTKuvKVwBhuvlqTGerOtnXWnrt\n\ -ym//hd3cd5PBYGBix0i7oR4xdghvfR2WLVu0LgdThTBb6XP7gLd19cQ1JuBtAajZ\n\ -wMuPn7qlUkEFDIkAZy59/Hue/H2Q2vU/JsvVhHWCQBL4F1ofEAt50il6ZxR1QfFK\n\ -9VGKDC4oOgm9DlxwwBoC2FjqmvQlqVV3kwIBAg==\n\ ------END DH PARAMETERS-----\n"; -#endif - -static enum mg_ssl_if_result mg_use_ca_cert(SSL_CTX *ctx, const char *cert) { - if (cert == NULL || strcmp(cert, "*") == 0) { - return MG_SSL_OK; - } - SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0); - return SSL_CTX_load_verify_locations(ctx, cert, NULL) == 1 ? MG_SSL_OK - : MG_SSL_ERROR; -} - -static enum mg_ssl_if_result mg_use_cert(SSL_CTX *ctx, const char *cert, - const char *key, - const char **err_msg) { - if (key == NULL) key = cert; - if (cert == NULL || cert[0] == '\0' || key == NULL || key[0] == '\0') { - return MG_SSL_OK; - } else if (SSL_CTX_use_certificate_file(ctx, cert, 1) == 0) { - MG_SET_PTRPTR(err_msg, "Invalid SSL cert"); - return MG_SSL_ERROR; - } else if (SSL_CTX_use_PrivateKey_file(ctx, key, 1) == 0) { - MG_SET_PTRPTR(err_msg, "Invalid SSL key"); - return MG_SSL_ERROR; - } else if (SSL_CTX_use_certificate_chain_file(ctx, cert) == 0) { - MG_SET_PTRPTR(err_msg, "Invalid CA bundle"); - return MG_SSL_ERROR; - } else { - SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); -#if !MG_DISABLE_PFS && !defined(KR_VERSION) - BIO *bio = NULL; - DH *dh = NULL; - - /* Try to read DH parameters from the cert/key file. */ - bio = BIO_new_file(cert, "r"); - if (bio != NULL) { - dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); - BIO_free(bio); - } - /* - * If there are no DH params in the file, fall back to hard-coded ones. - * Not ideal, but better than nothing. - */ - if (dh == NULL) { - bio = BIO_new_mem_buf((void *) mg_s_default_dh_params, -1); - dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); - BIO_free(bio); - } - if (dh != NULL) { - SSL_CTX_set_tmp_dh(ctx, dh); - SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE); - DH_free(dh); - } -#if OPENSSL_VERSION_NUMBER > 0x10002000L - SSL_CTX_set_ecdh_auto(ctx, 1); -#endif -#endif - } - return MG_SSL_OK; -} - -static enum mg_ssl_if_result mg_set_cipher_list(SSL_CTX *ctx, const char *cl) { - return (SSL_CTX_set_cipher_list(ctx, cl ? cl : mg_s_cipher_list) == 1 - ? MG_SSL_OK - : MG_SSL_ERROR); -} - -#ifndef KR_VERSION -static unsigned int mg_ssl_if_ossl_psk_cb(SSL *ssl, const char *hint, - char *identity, - unsigned int max_identity_len, - unsigned char *psk, - unsigned int max_psk_len) { - struct mg_ssl_if_ctx *ctx = - (struct mg_ssl_if_ctx *) SSL_CTX_get_app_data(SSL_get_SSL_CTX(ssl)); - size_t key_len = ctx->psk.len - ctx->identity_len - 1; - DBG(("hint: '%s'", (hint ? hint : ""))); - if (ctx->identity_len + 1 > max_identity_len) { - DBG(("identity too long")); - return 0; - } - if (key_len > max_psk_len) { - DBG(("key too long")); - return 0; - } - memcpy(identity, ctx->psk.buf, ctx->identity_len + 1); - memcpy(psk, ctx->psk.buf + ctx->identity_len + 1, key_len); - (void) ssl; - return key_len; -} - -static enum mg_ssl_if_result mg_ssl_if_ossl_set_psk(struct mg_ssl_if_ctx *ctx, - const char *identity, - const char *key_str) { - unsigned char key[32]; - size_t key_len; - size_t i = 0; - if (identity == NULL && key_str == NULL) return MG_SSL_OK; - if (identity == NULL || key_str == NULL) return MG_SSL_ERROR; - key_len = strlen(key_str); - if (key_len != 32 && key_len != 64) return MG_SSL_ERROR; - memset(key, 0, sizeof(key)); - key_len = 0; - for (i = 0; key_str[i] != '\0'; i++) { - unsigned char c; - char hc = tolower((int) key_str[i]); - if (hc >= '0' && hc <= '9') { - c = hc - '0'; - } else if (hc >= 'a' && hc <= 'f') { - c = hc - 'a' + 0xa; - } else { - return MG_SSL_ERROR; - } - key_len = i / 2; - key[key_len] <<= 4; - key[key_len] |= c; - } - key_len++; - DBG(("identity = '%s', key = (%u)", identity, (unsigned int) key_len)); - ctx->identity_len = strlen(identity); - mbuf_append(&ctx->psk, identity, ctx->identity_len + 1); - mbuf_append(&ctx->psk, key, key_len); - SSL_CTX_set_psk_client_callback(ctx->ssl_ctx, mg_ssl_if_ossl_psk_cb); - SSL_CTX_set_app_data(ctx->ssl_ctx, ctx); - return MG_SSL_OK; -} -#else -static enum mg_ssl_if_result mg_ssl_if_ossl_set_psk(struct mg_ssl_if_ctx *ctx, - const char *identity, - const char *key_str) { - (void) ctx; - (void) identity; - (void) key_str; - /* Krypton does not support PSK. */ - return MG_SSL_ERROR; -} -#endif /* defined(KR_VERSION) */ - -const char *mg_set_ssl(struct mg_connection *nc, const char *cert, - const char *ca_cert) { - const char *err_msg = NULL; - struct mg_ssl_if_conn_params params; - memset(¶ms, 0, sizeof(params)); - params.cert = cert; - params.ca_cert = ca_cert; - if (mg_ssl_if_conn_init(nc, ¶ms, &err_msg) != MG_SSL_OK) { - return err_msg; - } - return NULL; -} - -#endif /* MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_OPENSSL */ diff --git a/src/mongoose-6.11/src/mg_uri.c b/src/mongoose-6.11/src/mg_uri.c deleted file mode 100644 index 9b6228b..0000000 --- a/src/mongoose-6.11/src/mg_uri.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -#include "mg_internal.h" -#include "mg_uri.h" - -/* - * scan string until encountering one of `seps`, keeping track of component - * boundaries in `res`. - * - * `p` will point to the char after the separator or it will be `end`. - */ -static void parse_uri_component(const char **p, const char *end, - const char *seps, struct mg_str *res) { - const char *q; - res->p = *p; - for (; *p < end; (*p)++) { - for (q = seps; *q != '\0'; q++) { - if (**p == *q) break; - } - if (*q != '\0') break; - } - res->len = (*p) - res->p; - if (*p < end) (*p)++; -} - -int mg_parse_uri(const struct mg_str uri, struct mg_str *scheme, - struct mg_str *user_info, struct mg_str *host, - unsigned int *port, struct mg_str *path, struct mg_str *query, - struct mg_str *fragment) { - struct mg_str rscheme = {0, 0}, ruser_info = {0, 0}, rhost = {0, 0}, - rpath = {0, 0}, rquery = {0, 0}, rfragment = {0, 0}; - unsigned int rport = 0; - enum { - P_START, - P_SCHEME_OR_PORT, - P_USER_INFO, - P_HOST, - P_PORT, - P_REST - } state = P_START; - - const char *p = uri.p, *end = p + uri.len; - while (p < end) { - switch (state) { - case P_START: - /* - * expecting on of: - * - `scheme://xxxx` - * - `xxxx:port` - * - `[a:b:c]:port` - * - `xxxx/path` - */ - if (*p == '[') { - state = P_HOST; - break; - } - for (; p < end; p++) { - if (*p == ':') { - state = P_SCHEME_OR_PORT; - break; - } else if (*p == '/') { - state = P_REST; - break; - } - } - if (state == P_START || state == P_REST) { - rhost.p = uri.p; - rhost.len = p - uri.p; - } - break; - case P_SCHEME_OR_PORT: - if (end - p >= 3 && strncmp(p, "://", 3) == 0) { - rscheme.p = uri.p; - rscheme.len = p - uri.p; - state = P_USER_INFO; - p += 3; - } else { - rhost.p = uri.p; - rhost.len = p - uri.p; - state = P_PORT; - } - break; - case P_USER_INFO: - ruser_info.p = p; - for (; p < end; p++) { - if (*p == '@' || *p == '[' || *p == '/') { - break; - } - } - if (p == end || *p == '/' || *p == '[') { - /* backtrack and parse as host */ - p = ruser_info.p; - } - ruser_info.len = p - ruser_info.p; - state = P_HOST; - break; - case P_HOST: - if (*p == '@') p++; - rhost.p = p; - if (*p == '[') { - int found = 0; - for (; !found && p < end; p++) { - found = (*p == ']'); - } - if (!found) return -1; - } else { - for (; p < end; p++) { - if (*p == ':' || *p == '/') break; - } - } - rhost.len = p - rhost.p; - if (p < end) { - if (*p == ':') { - state = P_PORT; - break; - } else if (*p == '/') { - state = P_REST; - break; - } - } - break; - case P_PORT: - p++; - for (; p < end; p++) { - if (*p == '/') { - state = P_REST; - break; - } - rport *= 10; - rport += *p - '0'; - } - break; - case P_REST: - /* `p` points to separator. `path` includes the separator */ - parse_uri_component(&p, end, "?#", &rpath); - if (p < end && *(p - 1) == '?') { - parse_uri_component(&p, end, "#", &rquery); - } - parse_uri_component(&p, end, "", &rfragment); - break; - } - } - - if (scheme != 0) *scheme = rscheme; - if (user_info != 0) *user_info = ruser_info; - if (host != 0) *host = rhost; - if (port != 0) *port = rport; - if (path != 0) *path = rpath; - if (query != 0) *query = rquery; - if (fragment != 0) *fragment = rfragment; - - return 0; -} - -/* Normalize the URI path. Remove/resolve "." and "..". */ -int mg_normalize_uri_path(const struct mg_str *in, struct mg_str *out) { - const char *s = in->p, *se = s + in->len; - char *cp = (char *) out->p, *d; - - if (in->len == 0 || *s != '/') { - out->len = 0; - return 0; - } - - d = cp; - - while (s < se) { - const char *next = s; - struct mg_str component; - parse_uri_component(&next, se, "/", &component); - if (mg_vcmp(&component, ".") == 0) { - /* Yum. */ - } else if (mg_vcmp(&component, "..") == 0) { - /* Backtrack to previous slash. */ - if (d > cp + 1 && *(d - 1) == '/') d--; - while (d > cp && *(d - 1) != '/') d--; - } else { - memmove(d, s, next - s); - d += next - s; - } - s = next; - } - if (d == cp) *d++ = '/'; - - out->p = cp; - out->len = d - cp; - return 1; -} - -int mg_assemble_uri(const struct mg_str *scheme, const struct mg_str *user_info, - const struct mg_str *host, unsigned int port, - const struct mg_str *path, const struct mg_str *query, - const struct mg_str *fragment, int normalize_path, - struct mg_str *uri) { - int result = -1; - struct mbuf out; - mbuf_init(&out, 0); - - if (scheme != NULL && scheme->len > 0) { - mbuf_append(&out, scheme->p, scheme->len); - mbuf_append(&out, "://", 3); - } - - if (user_info != NULL && user_info->len > 0) { - mbuf_append(&out, user_info->p, user_info->len); - mbuf_append(&out, "@", 1); - } - - if (host != NULL && host->len > 0) { - mbuf_append(&out, host->p, host->len); - } - - if (port != 0) { - char port_str[20]; - int port_str_len = sprintf(port_str, ":%u", port); - mbuf_append(&out, port_str, port_str_len); - } - - if (path != NULL && path->len > 0) { - if (normalize_path) { - struct mg_str npath = mg_strdup(*path); - if (npath.len != path->len) goto out; - if (!mg_normalize_uri_path(path, &npath)) { - free((void *) npath.p); - goto out; - } - mbuf_append(&out, npath.p, npath.len); - free((void *) npath.p); - } else { - mbuf_append(&out, path->p, path->len); - } - } else if (normalize_path) { - mbuf_append(&out, "/", 1); - } - - if (query != NULL && query->len > 0) { - mbuf_append(&out, "?", 1); - mbuf_append(&out, query->p, query->len); - } - - if (fragment != NULL && fragment->len > 0) { - mbuf_append(&out, "#", 1); - mbuf_append(&out, fragment->p, fragment->len); - } - - result = 0; - -out: - if (result == 0) { - uri->p = out.buf; - uri->len = out.len; - } else { - mbuf_free(&out); - uri->p = NULL; - uri->len = 0; - } - return result; -} diff --git a/src/mongoose-6.11/src/mg_uri.h b/src/mongoose-6.11/src/mg_uri.h deleted file mode 100644 index f79e547..0000000 --- a/src/mongoose-6.11/src/mg_uri.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -/* - * === URI - */ - -#ifndef CS_MONGOOSE_SRC_URI_H_ -#define CS_MONGOOSE_SRC_URI_H_ - -#include "mg_net.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* - * Parses an URI and fills string chunks with locations of the respective - * uri components within the input uri string. NULL pointers will be - * ignored. - * - * General syntax: - * - * [scheme://[user_info@]]host[:port][/path][?query][#fragment] - * - * Example: - * - * foo.com:80 - * tcp://foo.com:1234 - * http://foo.com:80/bar?baz=1 - * https://user:pw@foo.com:443/blah - * - * `path` will include the leading slash. `query` won't include the leading `?`. - * `host` can contain embedded colons if surrounded by square brackets in order - * to support IPv6 literal addresses. - * - * - * Returns 0 on success, -1 on error. - */ -int mg_parse_uri(const struct mg_str uri, struct mg_str *scheme, - struct mg_str *user_info, struct mg_str *host, - unsigned int *port, struct mg_str *path, struct mg_str *query, - struct mg_str *fragment); - -/* - * Assemble URI from parts. Any of the inputs can be NULL or zero-length mg_str. - * - * If normalize_path is true, path is normalized by resolving relative refs. - * - * Result is a heap-allocated string (uri->p must be free()d after use). - * - * Returns 0 on success, -1 on error. - */ -int mg_assemble_uri(const struct mg_str *scheme, const struct mg_str *user_info, - const struct mg_str *host, unsigned int port, - const struct mg_str *path, const struct mg_str *query, - const struct mg_str *fragment, int normalize_path, - struct mg_str *uri); - -int mg_normalize_uri_path(const struct mg_str *in, struct mg_str *out); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* CS_MONGOOSE_SRC_URI_H_ */ diff --git a/src/mongoose-6.11/src/mg_util.c b/src/mongoose-6.11/src/mg_util.c deleted file mode 100644 index 485ba27..0000000 --- a/src/mongoose-6.11/src/mg_util.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -#include "common/cs_base64.h" -#include "mg_internal.h" -#include "mg_util.h" - -/* For platforms with limited libc */ -#ifndef MAX -#define MAX(a, b) ((a) > (b) ? (a) : (b)) -#endif - -const char *mg_skip(const char *s, const char *end, const char *delims, - struct mg_str *v) { - v->p = s; - while (s < end && strchr(delims, *(unsigned char *) s) == NULL) s++; - v->len = s - v->p; - while (s < end && strchr(delims, *(unsigned char *) s) != NULL) s++; - return s; -} - -#if MG_ENABLE_FILESYSTEM && !defined(MG_USER_FILE_FUNCTIONS) -int mg_stat(const char *path, cs_stat_t *st) { -#ifdef _WIN32 - wchar_t wpath[MG_MAX_PATH]; - to_wchar(path, wpath, ARRAY_SIZE(wpath)); - DBG(("[%ls] -> %d", wpath, _wstati64(wpath, st))); - return _wstati64(wpath, st); -#else - return stat(path, st); -#endif -} - -FILE *mg_fopen(const char *path, const char *mode) { -#ifdef _WIN32 - wchar_t wpath[MG_MAX_PATH], wmode[10]; - to_wchar(path, wpath, ARRAY_SIZE(wpath)); - to_wchar(mode, wmode, ARRAY_SIZE(wmode)); - return _wfopen(wpath, wmode); -#else - return fopen(path, mode); -#endif -} - -int mg_open(const char *path, int flag, int mode) { /* LCOV_EXCL_LINE */ -#if defined(_WIN32) && !defined(WINCE) - wchar_t wpath[MG_MAX_PATH]; - to_wchar(path, wpath, ARRAY_SIZE(wpath)); - return _wopen(wpath, flag, mode); -#else - return open(path, flag, mode); /* LCOV_EXCL_LINE */ -#endif -} - -size_t mg_fread(void *ptr, size_t size, size_t count, FILE *f) { - return fread(ptr, size, count, f); -} - -size_t mg_fwrite(const void *ptr, size_t size, size_t count, FILE *f) { - return fwrite(ptr, size, count, f); -} -#endif - -void mg_base64_encode(const unsigned char *src, int src_len, char *dst) { - cs_base64_encode(src, src_len, dst); -} - -int mg_base64_decode(const unsigned char *s, int len, char *dst) { - return cs_base64_decode(s, len, dst, NULL); -} - -#if MG_ENABLE_THREADS -void *mg_start_thread(void *(*f)(void *), void *p) { -#ifdef WINCE - return (void *) CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) f, p, 0, NULL); -#elif defined(_WIN32) - return (void *) _beginthread((void(__cdecl *) (void *) ) f, 0, p); -#else - pthread_t thread_id = (pthread_t) 0; - pthread_attr_t attr; - - (void) pthread_attr_init(&attr); - (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - -#if defined(MG_STACK_SIZE) && MG_STACK_SIZE > 1 - (void) pthread_attr_setstacksize(&attr, MG_STACK_SIZE); -#endif - - pthread_create(&thread_id, &attr, f, p); - pthread_attr_destroy(&attr); - - return (void *) thread_id; -#endif -} -#endif /* MG_ENABLE_THREADS */ - -/* Set close-on-exec bit for a given socket. */ -void mg_set_close_on_exec(sock_t sock) { -#if defined(_WIN32) && !defined(WINCE) - (void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0); -#elif defined(__unix__) - fcntl(sock, F_SETFD, FD_CLOEXEC); -#else - (void) sock; -#endif -} - -int mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len, - int flags) { - int is_v6; - if (buf == NULL || len <= 0) return 0; - memset(buf, 0, len); -#if MG_ENABLE_IPV6 - is_v6 = sa->sa.sa_family == AF_INET6; -#else - is_v6 = 0; -#endif - if (flags & MG_SOCK_STRINGIFY_IP) { -#if MG_ENABLE_IPV6 - const void *addr = NULL; - char *start = buf; - socklen_t capacity = len; - if (!is_v6) { - addr = &sa->sin.sin_addr; - } else { - addr = (void *) &sa->sin6.sin6_addr; - if (flags & MG_SOCK_STRINGIFY_PORT) { - *buf = '['; - start++; - capacity--; - } - } - if (inet_ntop(sa->sa.sa_family, addr, start, capacity) == NULL) { - goto cleanup; - } -#elif defined(_WIN32) || MG_LWIP || (MG_NET_IF == MG_NET_IF_PIC32) - /* Only Windoze Vista (and newer) have inet_ntop() */ - char *addr_str = inet_ntoa(sa->sin.sin_addr); - if (addr_str != NULL) { - strncpy(buf, inet_ntoa(sa->sin.sin_addr), len - 1); - } else { - goto cleanup; - } -#else - if (inet_ntop(AF_INET, (void *) &sa->sin.sin_addr, buf, len - 1) == NULL) { - goto cleanup; - } -#endif - } - if (flags & MG_SOCK_STRINGIFY_PORT) { - int port = ntohs(sa->sin.sin_port); - if (flags & MG_SOCK_STRINGIFY_IP) { - int buf_len = strlen(buf); - snprintf(buf + buf_len, len - (buf_len + 1), "%s:%d", (is_v6 ? "]" : ""), - port); - } else { - snprintf(buf, len, "%d", port); - } - } - - return strlen(buf); - -cleanup: - *buf = '\0'; - return 0; -} - -int mg_conn_addr_to_str(struct mg_connection *nc, char *buf, size_t len, - int flags) { - union socket_address sa; - memset(&sa, 0, sizeof(sa)); - mg_if_get_conn_addr(nc, flags & MG_SOCK_STRINGIFY_REMOTE, &sa); - return mg_sock_addr_to_str(&sa, buf, len, flags); -} - -#if MG_ENABLE_HEXDUMP -static int mg_hexdump_n(const void *buf, int len, char *dst, int dst_len, - int offset) { - const unsigned char *p = (const unsigned char *) buf; - char ascii[17] = ""; - int i, idx, n = 0; - - for (i = 0; i < len; i++) { - idx = i % 16; - if (idx == 0) { - if (i > 0) n += snprintf(dst + n, MAX(dst_len - n, 0), " %s\n", ascii); - n += snprintf(dst + n, MAX(dst_len - n, 0), "%04x ", i + offset); - } - if (dst_len - n < 0) { - return n; - } - n += snprintf(dst + n, MAX(dst_len - n, 0), " %02x", p[i]); - ascii[idx] = p[i] < 0x20 || p[i] > 0x7e ? '.' : p[i]; - ascii[idx + 1] = '\0'; - } - - while (i++ % 16) n += snprintf(dst + n, MAX(dst_len - n, 0), "%s", " "); - n += snprintf(dst + n, MAX(dst_len - n, 0), " %s\n", ascii); - - return n; -} - -int mg_hexdump(const void *buf, int len, char *dst, int dst_len) { - return mg_hexdump_n(buf, len, dst, dst_len, 0); -} - -void mg_hexdumpf(FILE *fp, const void *buf, int len) { - char tmp[80]; - int offset = 0, n; - while (len > 0) { - n = (len < 16 ? len : 16); - mg_hexdump_n(((const char *) buf) + offset, n, tmp, sizeof(tmp), offset); - fputs(tmp, fp); - offset += n; - len -= n; - } -} - -void mg_hexdump_connection(struct mg_connection *nc, const char *path, - const void *buf, int num_bytes, int ev) { - FILE *fp = NULL; - char src[60], dst[60]; - const char *tag = NULL; - switch (ev) { - case MG_EV_RECV: - tag = "<-"; - break; - case MG_EV_SEND: - tag = "->"; - break; - case MG_EV_ACCEPT: - tag = " 0) { - mg_hexdumpf(fp, buf, num_bytes); - } - if (fp != stdout && fp != stderr) fclose(fp); -} -#endif - -int mg_is_big_endian(void) { - static const int n = 1; - /* TODO(mkm) use compiletime check with 4-byte char literal */ - return ((char *) &n)[0] == 0; -} - -DO_NOT_WARN_UNUSED MG_INTERNAL int mg_get_errno(void) { -#ifndef WINCE - return errno; -#else - /* TODO(alashkin): translate error codes? */ - return GetLastError(); -#endif -} - -void mg_mbuf_append_base64_putc(char ch, void *user_data) { - struct mbuf *mbuf = (struct mbuf *) user_data; - mbuf_append(mbuf, &ch, sizeof(ch)); -} - -void mg_mbuf_append_base64(struct mbuf *mbuf, const void *data, size_t len) { - struct cs_base64_ctx ctx; - cs_base64_init(&ctx, mg_mbuf_append_base64_putc, mbuf); - cs_base64_update(&ctx, (const char *) data, len); - cs_base64_finish(&ctx); -} - -void mg_basic_auth_header(const struct mg_str user, const struct mg_str pass, - struct mbuf *buf) { - const char *header_prefix = "Authorization: Basic "; - const char *header_suffix = "\r\n"; - - struct cs_base64_ctx ctx; - cs_base64_init(&ctx, mg_mbuf_append_base64_putc, buf); - - mbuf_append(buf, header_prefix, strlen(header_prefix)); - - cs_base64_update(&ctx, user.p, user.len); - if (pass.len > 0) { - cs_base64_update(&ctx, ":", 1); - cs_base64_update(&ctx, pass.p, pass.len); - } - cs_base64_finish(&ctx); - mbuf_append(buf, header_suffix, strlen(header_suffix)); -} - -struct mg_str mg_url_encode(const struct mg_str src) { - static const char *dont_escape = "._-$,;~()/"; - static const char *hex = "0123456789abcdef"; - size_t i = 0; - struct mbuf mb; - mbuf_init(&mb, src.len); - - for (i = 0; i < src.len; i++) { - const unsigned char c = *((const unsigned char *) src.p + i); - if (isalnum(c) || strchr(dont_escape, c) != NULL) { - mbuf_append(&mb, &c, 1); - } else { - mbuf_append(&mb, "%", 1); - mbuf_append(&mb, &hex[c >> 4], 1); - mbuf_append(&mb, &hex[c & 15], 1); - } - } - mbuf_append(&mb, "", 1); - mbuf_trim(&mb); - return mg_mk_str_n(mb.buf, mb.len - 1); -} diff --git a/src/mongoose-6.11/src/mg_util.h b/src/mongoose-6.11/src/mg_util.h deleted file mode 100644 index e6f2e48..0000000 --- a/src/mongoose-6.11/src/mg_util.h +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -/* - * === Utility API - */ - -#ifndef CS_MONGOOSE_SRC_UTIL_H_ -#define CS_MONGOOSE_SRC_UTIL_H_ - -#include - -#include "mg_common.h" -#include "mg_net_if.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifndef MG_MAX_PATH -#ifdef PATH_MAX -#define MG_MAX_PATH PATH_MAX -#else -#define MG_MAX_PATH 256 -#endif -#endif - -/* - * Fetches substring from input string `s`, `end` into `v`. - * Skips initial delimiter characters. Records first non-delimiter character - * at the beginning of substring `v`. Then scans the rest of the string - * until a delimiter character or end-of-string is found. - * `delimiters` is a 0-terminated string containing delimiter characters. - * Either one of `delimiters` or `end_string` terminates the search. - * Returns an `s` pointer, advanced forward where parsing has stopped. - */ -const char *mg_skip(const char *s, const char *end_string, - const char *delimiters, struct mg_str *v); - -/* - * Decodes base64-encoded string `s`, `len` into the destination `dst`. - * The destination has to have enough space to hold the decoded buffer. - * Decoding stops either when all strings have been decoded or invalid an - * character appeared. - * Destination is '\0'-terminated. - * Returns the number of decoded characters. On success, that should be equal - * to `len`. On error (invalid character) the return value is smaller then - * `len`. - */ -int mg_base64_decode(const unsigned char *s, int len, char *dst); - -/* - * Base64-encode chunk of memory `src`, `src_len` into the destination `dst`. - * Destination has to have enough space to hold encoded buffer. - * Destination is '\0'-terminated. - */ -void mg_base64_encode(const unsigned char *src, int src_len, char *dst); - -#if MG_ENABLE_FILESYSTEM -/* - * Performs a 64-bit `stat()` call against a given file. - * - * `path` should be UTF8 encoded. - * - * Return value is the same as for `stat()` syscall. - */ -int mg_stat(const char *path, cs_stat_t *st); - -/* - * Opens the given file and returns a file stream. - * - * `path` and `mode` should be UTF8 encoded. - * - * Return value is the same as for the `fopen()` call. - */ -FILE *mg_fopen(const char *path, const char *mode); - -/* - * Opens the given file and returns a file stream. - * - * `path` should be UTF8 encoded. - * - * Return value is the same as for the `open()` syscall. - */ -int mg_open(const char *path, int flag, int mode); - -/* - * Reads data from the given file stream. - * - * Return value is a number of bytes readen. - */ -size_t mg_fread(void *ptr, size_t size, size_t count, FILE *f); - -/* - * Writes data to the given file stream. - * - * Return value is a number of bytes wtitten. - */ -size_t mg_fwrite(const void *ptr, size_t size, size_t count, FILE *f); - -#endif /* MG_ENABLE_FILESYSTEM */ - -#if MG_ENABLE_THREADS -/* - * Starts a new detached thread. - * Arguments and semantics are the same as pthead's `pthread_create()`. - * `thread_func` is a thread function, `thread_func_param` is a parameter - * that is passed to the thread function. - */ -void *mg_start_thread(void *(*thread_func)(void *), void *thread_func_param); -#endif - -void mg_set_close_on_exec(sock_t); - -#define MG_SOCK_STRINGIFY_IP 1 -#define MG_SOCK_STRINGIFY_PORT 2 -#define MG_SOCK_STRINGIFY_REMOTE 4 -/* - * Converts a connection's local or remote address into string. - * - * The `flags` parameter is a bit mask that controls the behaviour, - * see `MG_SOCK_STRINGIFY_*` definitions. - * - * - MG_SOCK_STRINGIFY_IP - print IP address - * - MG_SOCK_STRINGIFY_PORT - print port number - * - MG_SOCK_STRINGIFY_REMOTE - print remote peer's IP/port, not local address - * - * If both port number and IP address are printed, they are separated by `:`. - * If compiled with `-DMG_ENABLE_IPV6`, IPv6 addresses are supported. - * Return length of the stringified address. - */ -int mg_conn_addr_to_str(struct mg_connection *c, char *buf, size_t len, - int flags); -#if MG_NET_IF == MG_NET_IF_SOCKET -/* Legacy interface. */ -void mg_sock_to_str(sock_t sock, char *buf, size_t len, int flags); -#endif - -/* - * Convert the socket's address into string. - * - * `flags` is MG_SOCK_STRINGIFY_IP and/or MG_SOCK_STRINGIFY_PORT. - */ -int mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len, - int flags); - -#if MG_ENABLE_HEXDUMP -/* - * Generates a human-readable hexdump of memory chunk. - * - * Takes a memory buffer `buf` of length `len` and creates a hex dump of that - * buffer in `dst`. The generated output is a-la hexdump(1). - * Returns the length of generated string, excluding terminating `\0`. If - * returned length is bigger than `dst_len`, the overflow bytes are discarded. - */ -int mg_hexdump(const void *buf, int len, char *dst, int dst_len); - -/* Same as mg_hexdump, but with output going to file instead of a buffer. */ -void mg_hexdumpf(FILE *fp, const void *buf, int len); - -/* - * Generates human-readable hexdump of the data sent or received by the - * connection. `path` is a file name where hexdump should be written. - * `num_bytes` is a number of bytes sent/received. `ev` is one of the `MG_*` - * events sent to an event handler. This function is supposed to be called from - * the event handler. - */ -void mg_hexdump_connection(struct mg_connection *nc, const char *path, - const void *buf, int num_bytes, int ev); -#endif - -/* - * Returns true if target platform is big endian. - */ -int mg_is_big_endian(void); - -/* - * Use with cs_base64_init/update/finish in order to write out base64 in chunks. - */ -void mg_mbuf_append_base64_putc(char ch, void *user_data); - -/* - * Encode `len` bytes starting at `data` as base64 and append them to an mbuf. - */ -void mg_mbuf_append_base64(struct mbuf *mbuf, const void *data, size_t len); - -/* - * Generate a Basic Auth header and appends it to buf. - * If pass is NULL, then user is expected to contain the credentials pair - * already encoded as `user:pass`. - */ -void mg_basic_auth_header(const struct mg_str user, const struct mg_str pass, - struct mbuf *buf); - -/* - * URL-escape the specified string. - * All non-printable characters are escaped, plus `._-$,;~()/`. - * Input need not be NUL-terminated, but the returned string is. - * Returned string is heap-allocated and must be free()'d. - */ -struct mg_str mg_url_encode(const struct mg_str src); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* CS_MONGOOSE_SRC_UTIL_H_ */ diff --git a/src/mongoose-6.11/LICENSE b/src/mongoose/LICENSE similarity index 100% rename from src/mongoose-6.11/LICENSE rename to src/mongoose/LICENSE diff --git a/src/mongoose-6.11/mongoose.c b/src/mongoose/mongoose.c similarity index 99% rename from src/mongoose-6.11/mongoose.c rename to src/mongoose/mongoose.c index 141e501..0bb94ae 100644 --- a/src/mongoose-6.11/mongoose.c +++ b/src/mongoose/mongoose.c @@ -9479,6 +9479,7 @@ static struct mg_ws_proto_data *mg_ws_get_proto_data(struct mg_connection *nc) { */ static void mg_ws_close(struct mg_connection *nc, const void *data, size_t len) { + printf("SEND: %.*s\n",len,data); if ((int) len == ~0) { len = strlen((const char *) data); } diff --git a/src/mongoose-6.11/mongoose.h b/src/mongoose/mongoose.h similarity index 100% rename from src/mongoose-6.11/mongoose.h rename to src/mongoose/mongoose.h diff --git a/src/mpd_client.c b/src/mpd_client.c index 0d0d8ca..4996a03 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -69,9 +69,9 @@ void callback_mpd(struct mg_connection *nc, const struct mg_str msg) char *p_charbuf = NULL, *token; char *p_charbuf2 = NULL; char *searchstr = NULL; - + #ifdef DEBUG fprintf(stdout,"Got request: %s\n",msg.p); - + #endif if(cmd_id == -1) return; @@ -466,7 +466,9 @@ out_set_replaygain: if(mpd.conn_state == MPD_CONNECTED && mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS) { + #ifdef DEBUG fprintf(stdout,"Error: %s\n",mpd_connection_get_error_message(mpd.conn)); + #endif n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\", \"data\": \"%s\"}", mpd_connection_get_error_message(mpd.conn)); @@ -476,7 +478,9 @@ out_set_replaygain: } if(n > 0) { + #ifdef DEBUG fprintf(stdout,"Send response:\n %s\n",mpd.buf); + #endif mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, mpd.buf, n); } } @@ -498,7 +502,9 @@ static int mpd_notify_callback(struct mg_connection *c, const char *param) { { /* error message? */ n=snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\",\"data\":\"%s\"}",param); + #ifdef DEBUG fprintf(stdout,"Error in mpd_notify_callback: %s\n",param); + #endif mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, n); return 0; } @@ -510,18 +516,24 @@ static int mpd_notify_callback(struct mg_connection *c, const char *param) { if(mpd.conn_state != MPD_CONNECTED) { n=snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"disconnected\"}"); + #ifdef DEBUG fprintf(stdout,"Notify: disconnected\n"); + #endif mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, n); } else { + #ifdef DEBUG fprintf(stdout,"Notify: %s\n",mpd.buf); + #endif mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, mpd.buf_size); if(s->song_id != mpd.song_id) { n=mpd_put_current_song(mpd.buf); + #ifdef DEBUG fprintf(stdout,"Notify: %s\n",mpd.buf); + #endif mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, n); s->song_id = mpd.song_id; } @@ -529,7 +541,9 @@ static int mpd_notify_callback(struct mg_connection *c, const char *param) { if(s->queue_version != mpd.queue_version) { n=snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"update_queue\"}"); + #ifdef DEBUG fprintf(stdout,"Notify: update_queue\n"); + #endif mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, n); s->queue_version = mpd.queue_version; } diff --git a/src/mympd.c b/src/mympd.c index 7f2ea81..7449f93 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -46,7 +46,9 @@ static void signal_handler(int sig_num) { static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { switch(ev) { case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { + #ifdef DEBUG fprintf(stdout,"New Websocket connection\n"); + #endif struct mg_str d = {(char *) "MPD_API_GET_SETTINGS", 20 }; callback_mpd(nc, d); d.p="MPD_API_GET_OUTPUTS"; @@ -58,23 +60,31 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { struct websocket_message *wm = (struct websocket_message *) ev_data; wm->data[wm->size]='\0'; struct mg_str d = {(char *) wm->data, wm->size}; + #ifdef DEBUG fprintf(stdout,"Websocket request: %s\n",wm->data); + #endif callback_mpd(nc, d); break; } case MG_EV_HTTP_REQUEST: { struct http_message *hm = (struct http_message *) ev_data; + #ifdef DEBUG printf("HTTP request: %.*s\n",hm->uri.len,hm->uri.p); + #endif mg_serve_http(nc, hm, s_http_server_opts); break; } case MG_EV_CLOSE: { if (is_websocket(nc)) { + #ifdef DEBUG fprintf(stdout,"Websocket connection closed\n"); + #endif mpd_close_handler(nc); } else { + #ifdef DEBUG fprintf(stdout,"HTTP Close\n"); + #endif } break; } @@ -185,7 +195,7 @@ int main(int argc, char **argv) mg_set_protocol_http_websocket(nc); s_http_server_opts.document_root = SRC_PATH; - printf("Started on port %s\n", webport); + printf("myMPD started on port %s\n", webport); while (s_signal_received == 0) { mg_mgr_poll(&mgr, 200); current_timer = time(NULL); From 5bc3816d103e9ff01e26a587b8d0f17fb3aa2593 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 14 Jun 2018 20:56:12 +0100 Subject: [PATCH 06/32] Replaced websocket through ajax requests for requests. Now the websocket pushes only the states of mpd. Merged outputstate in state notify --- CMakeLists.txt | 8 +- README.md | 3 - htdocs/js/mpd.js | 827 ++++++++++++++++++++++++++--------------------- mympd.1 | 6 - src/mpd_client.c | 149 +++++---- src/mpd_client.h | 6 +- src/mympd.c | 68 ++-- 7 files changed, 588 insertions(+), 479 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e09ef62..2103898 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 2.6) project (mympd C) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") -set(CPACK_PACKAGE_VERSION_MAJOR "2") -set(CPACK_PACKAGE_VERSION_MINOR "3") +set(CPACK_PACKAGE_VERSION_MAJOR "3") +set(CPACK_PACKAGE_VERSION_MINOR "0") set(CPACK_PACKAGE_VERSION_PATCH "0") if(CMAKE_BUILD_TYPE MATCHES RELEASE) set(ASSETS_PATH "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}/htdocs") @@ -23,8 +23,8 @@ include_directories(${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR} ${LIBMPDCLIENT_I include(CheckCSourceCompiles) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -pedantic") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -pedantic -D_FORTIFY_SOURCE=2 -fstack-protector -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=shift -fsanitize=integer-divide-by-zero -fsanitize=unreachable -fsanitize=vla-bound -fsanitize=null -fsanitize=return -fsanitize=signed-integer-overflow -fsanitize=bounds -fsanitize=bounds-strict -fsanitize=alignment -fsanitize=object-size -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fsanitize=nonnull-attribute -fsanitize=returns-nonnull-attribute -fsanitize=bool -fsanitize=enum -fsanitize=vptr -static-libasan") +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -pedantic") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -pedantic -D_FORTIFY_SOURCE=2 -fstack-protector -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=shift -fsanitize=integer-divide-by-zero -fsanitize=unreachable -fsanitize=vla-bound -fsanitize=null -fsanitize=return -fsanitize=signed-integer-overflow -fsanitize=bounds -fsanitize=bounds-strict -fsanitize=alignment -fsanitize=object-size -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fsanitize=nonnull-attribute -fsanitize=returns-nonnull-attribute -fsanitize=bool -fsanitize=enum -fsanitize=vptr -static-libasan") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb") if(WITH_IPV6) set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS NS_ENABLE_IPV6) diff --git a/README.md b/README.md index 0e82e7e..fe6af0c 100644 --- a/README.md +++ b/README.md @@ -45,11 +45,8 @@ Run flags ``` Usage: ./mympd [OPTION]... - -D, --digest path to htdigest file for authorization - (realm mympd) [no authorization] -h, --host connect to mpd at host [localhost] -p, --port connect to mpd at port [6600] - -l, --localport skip authorization for local port -w, --webport [ip:] listen interface/port for webserver [8080] -s, --streamport connect to mpd http stream at port [8000] -u, --user drop priviliges to user after socket bind diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index 1c54a58..2828e90 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -120,7 +120,11 @@ app.route=function() { app.prepare(); if (app.current.app == 'Playback') { - socket.send('MPD_API_GET_CURRENT_SONG'); + $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", + data: "MPD_API_GET_CURRENT_SONG"}).done(function( data ) { + var obj = JSON.parse(data); + songChange(obj.data.title, obj.data.artist, obj.data.album, obj.data.uri, obj.data.currentsongid); + }); } else if (app.current.app == 'Queue' ) { if (app.last.app != app.current.app) { @@ -128,41 +132,53 @@ app.route=function() { setPagination(app.current.page); } $('#searchqueuetag > button').each(function() { - if ($(this).text == app.current.filter) { - $(this).removeClass('btn-secondary').addClass('btn-success'); + $(this).addClass('btn-secondary').removeClass('btn-success'); + if ($(this).text() == app.current.filter) { + $(this).removeClass('btn-secondary').addClass('btn-success'); $('#searchqueuetagdesc').text($(this).text()); } }); } - if (app.current.search.length >= 2) { - socket.send('MPD_API_SEARCH_QUEUE,' + app.current.filter + ','+app.current.page+',' + app.current.search); - } - else { - socket.send('MPD_API_GET_QUEUE,'+app.current.page); - } + getQueue(); } else if (app.current.app == 'Browse' && app.current.tab == 'Playlists') { - socket.send('MPD_API_GET_PLAYLISTS,'+app.current.page+','+app.current.filter); - doSetFilterLetter('#browsePlaylistsFilter'); + $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", + data: "MPD_API_GET_PLAYLISTS," + app.current.page + ","+app.current.filter}).done(function( data ) { + var obj = JSON.parse(data); + parsePlaylists(obj); + doSetFilterLetter('#browsePlaylistsFilter'); + }); } else if (app.current.app == 'Browse' && app.current.tab == 'Database' && app.current.view == 'Artist') { - socket.send('MPD_API_GET_ARTISTS,' + app.current.page + ',' + app.current.filter); - doSetFilterLetter('#BrowseDatabaseFilter'); + $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", + data: "MPD_API_GET_ARTISTS," + app.current.page + ","+app.current.filter}).done(function( data ) { + var obj = JSON.parse(data); + parseListDBtags(obj); + doSetFilterLetter('#BrowseDatabaseFilter'); + }); } else if (app.current.app == 'Browse' && app.current.tab == 'Database' && app.current.view == 'Album') { - socket.send('MPD_API_GET_ARTISTALBUMS,' + app.current.page+',' + app.current.filter + ',' + app.current.search); - doSetFilterLetter('#BrowseDatabaseFilter'); + $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", + data: "MPD_API_GET_ARTISTALBUMS," + app.current.page + ","+app.current.filter + "," + app.current.search}).done(function( data ) { + var obj = JSON.parse(data); + parseListDBtags(obj); + doSetFilterLetter('#BrowseDatabaseFilter'); + }); } else if (app.current.app == 'Browse' && app.current.tab == 'Filesystem') { $('#BrowseBreadcrumb').empty().append(''); - socket.send('MPD_API_GET_BROWSE,'+app.current.page+','+(app.current.search ? app.current.search : '/')+','+app.current.filter); - doSetFilterLetter('#BrowseFilesystemFilter'); + $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", + data: "MPD_API_GET_FILESYSTEM," + app.current.page + ","+(app.current.search ? app.current.search : "/")+","+app.current.filter}).done(function( data ) { + var obj = JSON.parse(data); + parseFilesystem(obj); + doSetFilterLetter('#BrowseFilesystemFilter'); + }); // Don't add all songs from root var add_all_songs = $('#browseFilesystemAddAllSongs'); if (app.current.search) { add_all_songs.off(); // remove previous binds add_all_songs.on('click', function() { - socket.send('MPD_API_ADD_TRACK,'+app.current.search); + sendAPI('MPD_API_ADD_TRACK,'+app.current.search); }); add_all_songs.removeAttr('disabled').removeClass('disabled'); } else { @@ -194,15 +210,20 @@ app.route=function() { setPagination(app.current.page); } $('#searchstr2').val(app.current.search); - $('#searchtags2 > button').each(function() { - if ($(this).text == app.current.filter) { - $(this).removeClass('btn-secondary').addClass('btn-success'); - $('#searchtags2desc').text($(this).text); - } - }); } + $('#searchtags2 > button').each(function() { + $(this).addClass('btn-secondary').removeClass('btn-success'); + if ($(this).text() == app.current.filter) { + $(this).removeClass('btn-secondary').addClass('btn-success'); + $('#searchtags2desc').text($(this).text()); + } + }); if (app.current.search.length >= 2) { - socket.send('MPD_API_SEARCH,' + app.current.filter + ','+app.current.page+',' + app.current.search); + $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", + data: "MPD_API_SEARCH," + app.current.filter + ","+app.current.page+","+app.current.search}).done(function( data ) { + var obj = JSON.parse(data); + parseSearch(obj); + }); } else { $('#SearchList > tbody').empty(); $('#searchAddAllSongs').attr('disabled','disabled').addClass('disabled'); @@ -218,12 +239,24 @@ app.route=function() { }; $(document).ready(function(){ + $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", + data: "MPD_API_GET_SETTINGS"}).done(function( data ) { + var obj = JSON.parse(data); + parseSettings(obj); + }); + $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", + data: "MPD_API_GET_OUTPUTNAMES"}).done(function( data ) { + var obj = JSON.parse(data); + parseOutputnames(obj); + }); + + webSocketConnect(); volumeBar=$('#volumebar').slider(); volumeBar.slider('setValue',0); volumeBar.slider('on','slideStop', function(value){ - socket.send("MPD_API_SET_VOLUME,"+value); + sendAPI("MPD_API_SET_VOLUME,"+value); }); progressBar=$('#progressbar').slider(); @@ -232,16 +265,24 @@ $(document).ready(function(){ progressBar.slider('on','slideStop', function(value){ if(current_song && current_song.currentSongId >= 0) { var seekVal = Math.ceil(current_song.totalTime*(value/100)); - socket.send("MPD_API_SET_SEEK,"+current_song.currentSongId+","+seekVal); + sendAPI("MPD_API_SET_SEEK,"+current_song.currentSongId+","+seekVal); } }); $('#about').on('shown.bs.modal', function () { - socket.send("MPD_API_GET_STATS"); + $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", + data: "MPD_API_GET_STATS"}).done(function( data ) { + var obj = JSON.parse(data); + parseStats(obj); + }); }) $('#settings').on('shown.bs.modal', function () { - socket.send("MPD_API_GET_SETTINGS"); + $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", + data: "MPD_API_GET_SETTINGS"}).done(function( data ) { + var obj = JSON.parse(data); + parseSettings(obj); + }); }) $('#addstream').on('shown.bs.modal', function () { @@ -283,8 +324,6 @@ function webSocketConnect() { try { socket.onopen = function() { console.log("connected"); - //socket.send('MPD_API_GET_OUTPUTS'); - //socket.send('MPD_API_GET_SETTINGS'); showNotification('Connected to myMPD','','','success'); $('#modalConnectionError').modal('hide'); app.route(); @@ -300,11 +339,216 @@ function webSocketConnect() { } switch (obj.type) { - case 'queuesearch': - //Do the same as queue - case 'queue': + case 'state': + parseState(obj); + break; + case 'disconnected': + showNotification('myMPD lost connection to MPD','','','danger'); + break; + case 'update_queue': + if(app.current.app === 'Queue') + getQueue(); + break; + case "song_change": + songChange(obj.data.title, obj.data.artist, obj.data.album, obj.data.uri, obj.data.currentsongid); + break; + case 'settings': + parseSettings(obj); + break; + case 'error': + showNotification(obj.data,'','','danger'); + default: + break; + } + } + + socket.onclose = function(){ + console.log('disconnected'); + $('#modalConnectionError').modal('show'); + setTimeout(function() { + console.log('reconnect'); + webSocketConnect(); + },3000); + } + + } catch(exception) { + alert('

Error' + exception); + } + +} + +function get_appropriate_ws_url() +{ + var pcol; + var u = document.URL; + var separator; + + if (u.substring(0, 5) == "https") { + pcol = "wss://"; + u = u.substr(8); + } else { + pcol = "ws://"; + if (u.substring(0, 4) == "http") + u = u.substr(7); + } + + u = u.split('#'); + if (/\/$/.test(u[0])) { + separator = ""; + } else { + separator = "/"; + } + return pcol + u[0] + separator + "ws"; +} + +function parseStats(obj) { + $('#mpdstats_artists').text(obj.data.artists); + $('#mpdstats_albums').text(obj.data.albums); + $('#mpdstats_songs').text(obj.data.songs); + $('#mpdstats_dbplaytime').text(beautifyDuration(obj.data.dbplaytime)); + $('#mpdstats_playtime').text(beautifyDuration(obj.data.playtime)); + $('#mpdstats_uptime').text(beautifyDuration(obj.data.uptime)); + var d = new Date(obj.data.dbupdated * 1000); + $('#mpdstats_dbupdated').text(d.toUTCString()); + $('#mympdVersion').text(obj.data.mympd_version); + $('#mpdVersion').text(obj.data.mpd_version); +} + +function parseSettings(obj) { + if (!isNaN(obj.data.max_elements_per_page)) + MAX_ELEMENTS_PER_PAGE=obj.data.max_elements_per_page; + + if(obj.data.random) + $('#btnrandom').removeClass('btn-secondary').addClass("btn-success") + else + $('#btnrandom').removeClass("btn-success").addClass("btn-secondary"); + + if(obj.data.consume) + $('#btnconsume').removeClass('btn-secondary').addClass("btn-success") + else + $('#btnconsume').removeClass("btn-success").addClass("btn-secondary"); + + if(obj.data.single) + $('#btnsingle').removeClass('btn-secondary').addClass("btn-success") + else + $('#btnsingle').removeClass("btn-success").addClass("btn-secondary"); + + if(obj.data.crossfade != undefined) + $('#inputCrossfade').removeAttr('disabled').val(obj.data.crossfade); + else + $('#inputCrossfade').attr('disabled', 'disabled'); + + if(obj.data.mixrampdb != undefined) + $('#inputMixrampdb').removeAttr('disabled').val(obj.data.mixrampdb); + else + $('#inputMixrampdb').attr('disabled', 'disabled'); + + if(obj.data.mixrampdelay != undefined) + $('#inputMixrampdelay').removeAttr('disabled').val(obj.data.mixrampdelay); + else + $('#inputMixrampdb').attr('disabled', 'disabled'); + + if(obj.data.repeat) + $('#btnrepeat').removeClass('btn-secondary').addClass("btn-success") + else + $('#btnrepeat').removeClass("btn-success").addClass("btn-secondary"); + + $("#selectReplaygain").val(obj.data.replaygain); + + setLocalStream(obj.data.mpdhost,obj.data.streamport); + coverImageFile=obj.data.coverimage; +} + +function parseOutputnames(obj) { + $('#btn-outputs-block button').remove(); + if ( Object.keys(obj.data).length ) { + $.each(obj.data, function(id, name){ + var btn = $(''); + btn.appendTo($('#btn-outputs-block')); + }); + } else { + $('#btn-outputs-block').addClass('hide'); + } + /* remove cache, since the buttons have been recreated */ + last_outputs = ''; +} + +function parseState(obj) { + updatePlayIcon(obj); + updateVolumeIcon(obj.data.volume); + + if(JSON.stringify(obj) === JSON.stringify(last_state)) + return; + + current_song.totalTime = obj.data.totalTime; + current_song.currentSongId = obj.data.currentsongid; + var total_minutes = Math.floor(obj.data.totalTime / 60); + var total_seconds = obj.data.totalTime - total_minutes * 60; + + var elapsed_minutes = Math.floor(obj.data.elapsedTime / 60); + var elapsed_seconds = obj.data.elapsedTime - elapsed_minutes * 60; + + volumeBar.slider('setValue',obj.data.volume); + var progress = Math.floor(100*obj.data.elapsedTime/obj.data.totalTime); + progressBar.slider('setValue',progress); + + var counterText=elapsed_minutes + ":" + + (elapsed_seconds < 10 ? '0' : '') + elapsed_seconds + " / " + + total_minutes + ":" + (total_seconds < 10 ? '0' : '') + total_seconds; + + $('#counter').text(counterText); + + if (last_state) { + $('#QueueList > tbody > tr[trackid='+last_state.data.currentsongid+'] > td').eq(4).text(last_state.data.totalTime); + $('#QueueList > tbody > tr[trackid='+last_state.data.currentsongid+'] > td').eq(0).removeClass('material-icons').text(last_state.data.songpos); + } + $('#QueueList > tbody > tr').removeClass('active').removeClass("font-weight-bold"); + + $('#QueueList > tbody > tr[trackid='+obj.data.currentsongid+'] > td').eq(4).text(counterText); + $('#QueueList > tbody > tr[trackid='+obj.data.currentsongid+'] > td').eq(0).addClass('material-icons').text('play_arrow'); + $('#QueueList > tbody > tr[trackid='+obj.data.currentsongid+']').addClass('active').addClass("font-weight-bold"); + + //Get current song on queue change for http streams + if (last_state == undefined || obj.data.queue_version != last_state.data.queue_version) { + $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", + data: "MPD_API_GET_CURRENT_SONG"}).done(function( data ) { + var obj = JSON.parse(data); + songChange(obj.data.title, obj.data.artist, obj.data.album, obj.data.uri, obj.data.currentsongid); + }); + } + + last_state = obj; + + $.each(obj.data.outputs, function(id, enabled){ + if (enabled) + $('#btnoutput'+id).removeClass('btn-secondary').addClass("btn-success") + else + $('#btnoutput'+id).removeClass("btn-success").addClass("btn-secondary"); + }); + last_outputs = obj.data.outputs; +} + +function getQueue() { + if (app.current.search.length >= 2) { + $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", + data: "MPD_API_SEARCH_QUEUE," + app.current.filter + ","+app.current.page+"," + app.current.search}).done(function( data ) { + var obj = JSON.parse(data); + parseQueue(obj); + }); + } + else { + $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", + data: "MPD_API_GET_QUEUE," + app.current.page}).done(function( data ) { + var obj = JSON.parse(data); + parseQueue(obj); + }); + } +} + +function parseQueue(obj) { if(app.current.app !== 'Queue') - break; + return; $('#panel-heading-queue').empty(); if (obj.totalEntities > 0) { $('#panel-heading-queue').text(obj.totalEntities+' Songs'); @@ -378,156 +622,27 @@ function webSocketConnect() { $('#QueueList > tbody > tr').on({ click: function() { $('#queueList > tbody > tr').removeClass('active'); - socket.send('MPD_API_PLAY_TRACK,'+$(this).attr('trackid')); + sendAPI('MPD_API_PLAY_TRACK,'+$(this).attr('trackid')); $(this).addClass('active'); }, }); - break; - case 'playlists': - if(app.current.app !== 'Browse' && app.current.tab !== 'Playlists') - break; - var nrItems=0; - var tr=document.getElementById(app.current.app+app.current.tab+'List').getElementsByTagName('tbody')[0].getElementsByTagName('tr'); - for (var item in obj.data) { - nrItems++; - var d = new Date(obj.data[item].last_modified * 1000); - var row='' + - 'list' + - '' + basename(obj.data[item].plist) + '' + - ''+d.toUTCString()+''; - if (nrItems <= tr.length) { $(tr[nrItems-1]).replaceWith(row); } - else { $('#'+app.current.app+app.current.tab+'List > tbody').append(row); } - } - for (var i=tr.length;i>nrItems;i--) { - $(tr[tr.length-1]).remove(); - } - setPagination(obj.totalEntities); - if ( isTouch ) { - $('#'+app.current.app+app.current.tab+'List > tbody > tr > td:last-child').append( - '' + - 'delete'); - } else { - $('#'+app.current.app+app.current.tab+'List > tbody > tr').on({ - mouseover: function(){ - if($(this).children().last().has('a').length == 0) - $(this).children().last().append( - '' + - 'delete'); - }, - mouseleave: function(){ - var doomed = $(this); - $(this).children().last().find("a").stop().remove(); - } - }); - }; - $('#'+app.current.app+app.current.tab+'List > tbody > tr').on({ - click: function() { - socket.send('MPD_API_ADD_PLAYLIST,' + decodeURI($(this).attr('uri'))); - showNotification('"' + $('td:nth-last-child(3)', this).text() + '" added','','','success'); - } - }); - if (nrItems == 0) { - $('#'+app.current.app+app.current.tab+'List > tbody').append( - 'error_outline' + - 'No playlists found.' + - '' - ); - } - break; - - case 'listDBtags': - if(app.current.app !== 'Browse' && app.current.tab !== 'Database') - break; - if (obj.tagtype == 'AlbumArtist') { - $('#BrowseDatabaseAlbumCards').addClass('hide'); - $('#BrowseDatabaseArtistList').removeClass('hide'); - $('#btnBrowseDatabaseArtist').addClass('hide'); - var nrItems=0; - var tr=document.getElementById(app.current.app+app.current.tab+app.current.view+'List').getElementsByTagName('tbody')[0].getElementsByTagName('tr'); - for (var item in obj.data) { - nrItems++; - var row='' + - 'album' + - '' + obj.data[item].value + ''; - if (nrItems <= tr.length) { $(tr[nrItems-1]).replaceWith(row); } - else { $('#'+app.current.app+app.current.tab+app.current.view+'List > tbody').append(row); } - - } - for (var i=tr.length;i>nrItems;i--) { - $(tr[tr.length-1]).remove(); - } - setPagination(obj.totalEntities); - $('#'+app.current.app+app.current.tab+app.current.view+'List > tbody > tr').on({ - click: function() { - app.goto('Browse','Database','Album','0/-/'+$(this).attr('uri')); - } - }); - if (nrItems == 0) { - $('#'+app.current.app+app.current.tab+app.current.view+'List > tbody').append( - 'error_outline' + - 'No entries found.' + - '' - ); - } - } else if (obj.tagtype == 'Album') { - $('#BrowseDatabaseArtistList').addClass('hide'); - $('#BrowseDatabaseAlbumCards').empty(); - $('#BrowseDatabaseAlbumCards').removeClass('hide'); - $('#btnBrowseDatabaseArtist').removeClass('hide'); - var nrItems=0; - for (var item in obj.data) { - var card='

'+ - ' '+ - '
'+ - '
'+obj.searchstr+'
'+ - '

'+obj.data[item].value+'

'+ - ' '+ - ''; - $('#BrowseDatabaseAlbumCards').append(card); - socket.send('MPD_API_GET_ARTISTALBUMTITLES,' + obj.searchstr + ','+obj.data[item].value); - } - setPagination(obj.totalEntities); - } - break; - case 'listTitles': - var album=$('#'+genId(obj.album)+' > div > table > tbody'); - $('#'+genId(obj.album)+' > img').attr('src','/library/'+obj.data[0].uri.replace(/\/[^\/]+$/,'\/')+coverImageFile); - $('#'+genId(obj.album)+' > img').attr('uri',obj.data[0].uri.replace(/\/[^\/]+$/,'')); - $('#'+genId(obj.album)+' > img').attr('data-album',encodeURI(obj.album)); - var titleList=''; - for (var item in obj.data) { - titleList+=''+ - ''; - } - album.append(titleList); +} - $('#'+genId(obj.album)+' > img').on({ - click: function() { - socket.send('MPD_API_ADD_TRACK,' + decodeURI($(this).attr('uri'))); - showNotification('"'+decodeURI($(this).attr('data-album'))+'" added','','','success'); - } - }); - - $('#tbl'+genId(obj.album)+' > tbody > tr').on({ - click: function() { - socket.send('MPD_API_ADD_TRACK,' + decodeURI($(this).attr('uri'))); - showNotification('"' + $('td:nth-last-child(1)', this).text() + '" added','','','success'); - } - }); - break; - case 'search': +function parseSearch(obj) { + if(app.current.app !== 'Search') + return; $('#panel-heading-search').text(obj.totalEntities + ' Songs found'); if (obj.totalEntities > 0) { $('#searchAddAllSongs').removeAttr('disabled').removeClass('disabled'); } else { $('#searchAddAllSongs').attr('disabled','disabled').addClass('disabled'); } - case 'browse': + parseFilesystem(obj); +} + +function parseFilesystem(obj) { if(app.current.app !== 'Browse' && app.current.tab !== 'Filesystem' && app.current.app !== 'Search') - break; + return; /* The use of encodeURI() below might seem useless, but it's not. It prevents * some browsers, such as Safari, from changing the normalization form of the @@ -584,7 +699,7 @@ function webSocketConnect() { '' + glyphicon + '') .find('a').click(function(e) { e.stopPropagation(); - socket.send(onClickAction + "," + decodeURI($(this).parents("tr").attr("uri"))); + sendAPI(onClickAction + "," + decodeURI($(this).parents("tr").attr("uri"))); showNotification('"' + $('td:nth-last-child(3)', $(this).parents('tr')).text() + '" added','','','success'); }); } @@ -615,11 +730,11 @@ function webSocketConnect() { app.goto('Browse','Filesystem',undefined,app.current.page+'/'+app.current.filter+'/'+app.current.search); break; case 'song': - socket.send("MPD_API_ADD_TRACK," + decodeURI($(this).attr("uri"))); + sendAPI("MPD_API_ADD_TRACK," + decodeURI($(this).attr("uri"))); showNotification('"' + $('td:nth-last-child(5)', this).text() + '" added','','','success'); break; case 'plist': - socket.send("MPD_API_ADD_PLAYLIST," + decodeURI($(this).attr("uri"))); + sendAPI("MPD_API_ADD_PLAYLIST," + decodeURI($(this).attr("uri"))); showNotification('"' + $('td:nth-last-child(3)', this).text() + '" added','','','success'); break; } @@ -634,192 +749,148 @@ function webSocketConnect() { app.goto('Browse','Filesystem',undefined,app.current.page+'/'+app.current.filter+'/'+app.current.search); } }); +} - break; - case 'state': - updatePlayIcon(obj); - updateVolumeIcon(obj.data.volume); - - if(JSON.stringify(obj) === JSON.stringify(last_state)) - break; - - current_song.totalTime = obj.data.totalTime; - current_song.currentSongId = obj.data.currentsongid; - var total_minutes = Math.floor(obj.data.totalTime / 60); - var total_seconds = obj.data.totalTime - total_minutes * 60; - - var elapsed_minutes = Math.floor(obj.data.elapsedTime / 60); - var elapsed_seconds = obj.data.elapsedTime - elapsed_minutes * 60; - - volumeBar.slider('setValue',obj.data.volume); - var progress = Math.floor(100*obj.data.elapsedTime/obj.data.totalTime); - progressBar.slider('setValue',progress); - - var counterText=elapsed_minutes + ":" + - (elapsed_seconds < 10 ? '0' : '') + elapsed_seconds + " / " + - total_minutes + ":" + (total_seconds < 10 ? '0' : '') + total_seconds; - - $('#counter').text(counterText); - - if (last_state) { - $('#QueueList > tbody > tr[trackid='+last_state.data.currentsongid+'] > td').eq(4).text(last_state.data.totalTime); - $('#QueueList > tbody > tr[trackid='+last_state.data.currentsongid+'] > td').eq(0).removeClass('material-icons').text(last_state.data.songpos); +function parsePlaylists(obj) { + if(app.current.app !== 'Browse' && app.current.tab !== 'Playlists') + return; + var nrItems=0; + var tr=document.getElementById(app.current.app+app.current.tab+'List').getElementsByTagName('tbody')[0].getElementsByTagName('tr'); + for (var item in obj.data) { + nrItems++; + var d = new Date(obj.data[item].last_modified * 1000); + var row='' + + '' + + '' + + ''; + if (nrItems <= tr.length) { $(tr[nrItems-1]).replaceWith(row); } + else { $('#'+app.current.app+app.current.tab+'List > tbody').append(row); } } - $('#QueueList > tbody > tr').removeClass('active').removeClass("font-weight-bold"); - - $('#QueueList > tbody > tr[trackid='+obj.data.currentsongid+'] > td').eq(4).text(counterText); - $('#QueueList > tbody > tr[trackid='+obj.data.currentsongid+'] > td').eq(0).addClass('material-icons').text('play_arrow'); - $('#QueueList > tbody > tr[trackid='+obj.data.currentsongid+']').addClass('active').addClass("font-weight-bold"); - - //Get current song on queue change for http streams - if (last_state == undefined || obj.data.queue_version != last_state.data.queue_version) { - socket.send('MPD_API_GET_CURRENT_SONG'); + for (var i=tr.length;i>nrItems;i--) { + $(tr[tr.length-1]).remove(); } - - last_state = obj; - break; - case 'outputnames': - $('#btn-outputs-block button').remove(); - if ( Object.keys(obj.data).length ) { - $.each(obj.data, function(id, name){ - var btn = $(''); - btn.appendTo($('#btn-outputs-block')); + setPagination(obj.totalEntities); + if ( isTouch ) { + $('#'+app.current.app+app.current.tab+'List > tbody > tr > td:last-child').append( + '' + + 'delete'); + } else { + $('#'+app.current.app+app.current.tab+'List > tbody > tr').on({ + mouseover: function(){ + if($(this).children().last().has('a').length == 0) + $(this).children().last().append( + '' + + 'delete'); + }, + mouseleave: function(){ + var doomed = $(this); + $(this).children().last().find("a").stop().remove(); + } }); - } else { - $('#btn-outputs-block').addClass('hide'); - } - /* remove cache, since the buttons have been recreated */ - last_outputs = ''; - break; - case 'outputs': - if(JSON.stringify(obj) === JSON.stringify(last_outputs)) - break; - $.each(obj.data, function(id, enabled){ - if (enabled) - $('#btnoutput'+id).removeClass('btn-secondary').addClass("btn-success") - else - $('#btnoutput'+id).removeClass("btn-success").addClass("btn-secondary"); + }; + $('#'+app.current.app+app.current.tab+'List > tbody > tr').on({ + click: function() { + sendAPI('MPD_API_ADD_PLAYLIST,' + decodeURI($(this).attr('uri'))); + showNotification('"' + $('td:nth-last-child(3)', this).text() + '" added','','','success'); + } }); - last_outputs = obj; - break; - case 'disconnected': - showNotification('myMPD lost connection to MPD','','','danger'); - break; - case 'update_queue': - if(app.current.app === 'Queue') - socket.send('MPD_API_GET_QUEUE,'+app.current.page); - break; - case "song_change": - songChange(obj.data.title, obj.data.artist, obj.data.album, obj.data.uri, obj.data.currentsongid); - break; - case 'settings': - if (!isNaN(obj.data.max_elements_per_page)) - MAX_ELEMENTS_PER_PAGE=obj.data.max_elements_per_page; - - if(obj.data.random) - $('#btnrandom').removeClass('btn-secondary').addClass("btn-success") - else - $('#btnrandom').removeClass("btn-success").addClass("btn-secondary"); - - if(obj.data.consume) - $('#btnconsume').removeClass('btn-secondary').addClass("btn-success") - else - $('#btnconsume').removeClass("btn-success").addClass("btn-secondary"); - - if(obj.data.single) - $('#btnsingle').removeClass('btn-secondary').addClass("btn-success") - else - $('#btnsingle').removeClass("btn-success").addClass("btn-secondary"); - - if(obj.data.crossfade != undefined) - $('#inputCrossfade').removeAttr('disabled').val(obj.data.crossfade); - else - $('#inputCrossfade').attr('disabled', 'disabled'); - - if(obj.data.mixrampdb != undefined) - $('#inputMixrampdb').removeAttr('disabled').val(obj.data.mixrampdb); - else - $('#inputMixrampdb').attr('disabled', 'disabled'); - - if(obj.data.mixrampdelay != undefined) - $('#inputMixrampdelay').removeAttr('disabled').val(obj.data.mixrampdelay); - else - $('#inputMixrampdb').attr('disabled', 'disabled'); - - if(obj.data.repeat) - $('#btnrepeat').removeClass('btn-secondary').addClass("btn-success") - else - $('#btnrepeat').removeClass("btn-success").addClass("btn-secondary"); - - $("#selectReplaygain").val(obj.data.replaygain); - - setLocalStream(obj.data.mpdhost,obj.data.streamport); - coverImageFile=obj.data.coverimage; - break; - case 'mpdstats': - $('#mpdstats_artists').text(obj.data.artists); - $('#mpdstats_albums').text(obj.data.albums); - $('#mpdstats_songs').text(obj.data.songs); - $('#mpdstats_dbplaytime').text(beautifyDuration(obj.data.dbplaytime)); - $('#mpdstats_playtime').text(beautifyDuration(obj.data.playtime)); - $('#mpdstats_uptime').text(beautifyDuration(obj.data.uptime)); - var d = new Date(obj.data.dbupdated * 1000); - $('#mpdstats_dbupdated').text(d.toUTCString()); - $('#mympdVersion').text(obj.data.mympd_version); - $('#mpdVersion').text(obj.data.mpd_version); - break; - case 'error': - showNotification(obj.data,'','','danger'); - default: - break; - } - } - - socket.onclose = function(){ - console.log('disconnected'); - $('#modalConnectionError').modal('show'); - setTimeout(function() { - console.log('reconnect'); - webSocketConnect(); - },3000); - } - - } catch(exception) { - alert('

Error' + exception); - } + if (nrItems == 0) { + $('#'+app.current.app+app.current.tab+'List > tbody').append( + '

' + + '' + + '' + ); + } } -function get_appropriate_ws_url() -{ - var pcol; - var u = document.URL; - var separator; +function parseListDBtags(obj) { + if(app.current.app !== 'Browse' && app.current.tab !== 'Database' && app.current.view !== 'Artist') return; + + if (obj.tagtype == 'AlbumArtist') { + $('#BrowseDatabaseAlbumCards').addClass('hide'); + $('#BrowseDatabaseArtistList').removeClass('hide'); + $('#btnBrowseDatabaseArtist').addClass('hide'); + var nrItems=0; + var tr=document.getElementById(app.current.app+app.current.tab+app.current.view+'List').getElementsByTagName('tbody')[0].getElementsByTagName('tr'); + for (var item in obj.data) { + nrItems++; + var row='' + + '' + + ''; + if (nrItems <= tr.length) { $(tr[nrItems-1]).replaceWith(row); } + else { $('#'+app.current.app+app.current.tab+app.current.view+'List > tbody').append(row); } + + } + for (var i=tr.length;i>nrItems;i--) { + $(tr[tr.length-1]).remove(); + } + setPagination(obj.totalEntities); + $('#'+app.current.app+app.current.tab+app.current.view+'List > tbody > tr').on({ + click: function() { + app.goto('Browse','Database','Album','0/-/'+$(this).attr('uri')); + } + }); + if (nrItems == 0) { + $('#'+app.current.app+app.current.tab+app.current.view+'List > tbody').append( + '' + + '' + + '' + ); + } + } else if (obj.tagtype == 'Album') { + $('#BrowseDatabaseArtistList').addClass('hide'); + $('#BrowseDatabaseAlbumCards').empty(); + $('#BrowseDatabaseAlbumCards').removeClass('hide'); + $('#btnBrowseDatabaseArtist').removeClass('hide'); + var nrItems=0; + for (var item in obj.data) { + var card='
'+ + ' '+ + '
'+ + '
'+obj.searchstr+'
'+ + '

'+obj.data[item].value+'

'+ + '
'+obj.data[item].track+''+obj.data[item].title+'
list' + basename(obj.data[item].plist) + ''+d.toUTCString()+'
error_outlineNo playlists found.
album' + obj.data[item].value + '
error_outlineNo entries found.
'+ + ''; + $('#BrowseDatabaseAlbumCards').append(card); + $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", data: "MPD_API_GET_ARTISTALBUMTITLES," + obj.searchstr + ","+obj.data[item].value}).done(function( data ) { + var obj = JSON.parse(data); + parseListTitles(obj); + }); + } + setPagination(obj.totalEntities); + } +} - /* - /* We open the websocket encrypted if this page came on an - /* https:// url itself, otherwise unencrypted - /*/ +function parseListTitles(obj) { + if(app.current.app !== 'Browse' && app.current.tab !== 'Database' && app.current.view !== 'Album') return; + var album=$('#'+genId(obj.album)+' > div > table > tbody'); + $('#'+genId(obj.album)+' > img').attr('src','/library/'+obj.data[0].uri.replace(/\/[^\/]+$/,'\/')+coverImageFile); + $('#'+genId(obj.album)+' > img').attr('uri',obj.data[0].uri.replace(/\/[^\/]+$/,'')); + $('#'+genId(obj.album)+' > img').attr('data-album',encodeURI(obj.album)); + var titleList=''; + for (var item in obj.data) { + titleList+=''+ + ''; + } + album.append(titleList); - if (u.substring(0, 5) == "https") { - pcol = "wss://"; - u = u.substr(8); - } else { - pcol = "ws://"; - if (u.substring(0, 4) == "http") - u = u.substr(7); - } - - u = u.split('#'); - - if (/\/$/.test(u[0])) { - separator = ""; - } else { - separator = "/"; - } - - return pcol + u[0] + separator + "ws"; + $('#'+genId(obj.album)+' > img').on({ + click: function() { + sendAPI('MPD_API_ADD_TRACK,' + decodeURI($(this).attr('uri'))); + showNotification('"'+decodeURI($(this).attr('data-album'))+'" added','','','success'); + } + }); + + $('#tbl'+genId(obj.album)+' > tbody > tr').on({ + click: function() { + sendAPI('MPD_API_ADD_TRACK,' + decodeURI($(this).attr('uri'))); + showNotification('"' + $('td:nth-last-child(1)', this).text() + '" added','','','success'); + } + }); } function setPagination(number) { @@ -909,21 +980,25 @@ function updatePlayIcon(obj) { } } +function sendAPI(cmd) { + $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", data: cmd}); +} + function updateDB(event) { - socket.send('MPD_API_UPDATE_DB'); + sendAPI('MPD_API_UPDATE_DB'); showNotification('Updating MPD Database...','','','success'); event.preventDefault(); } function clickPlay() { if( playstate != 'play') - socket.send('MPD_API_SET_PLAY'); + sendAPI('MPD_API_SET_PLAY'); else - socket.send('MPD_API_SET_PAUSE'); + sendAPI('MPD_API_SET_PAUSE'); } function clickStop() { - socket.send('MPD_API_SET_STOP'); + sendAPI('MPD_API_SET_STOP'); } function setLocalStream(mpdhost,streamport) { @@ -939,16 +1014,16 @@ function setLocalStream(mpdhost,streamport) { function delQueueSong(tr,event) { event.stopPropagation(); if ( $('#btntrashmodeup').hasClass('btn-success') ) { - socket.send('MPD_API_RM_RANGE,0,' + (tr.index() + 1)); + sendAPI('MPD_API_RM_RANGE,0,' + (tr.index() + 1)); } else if ( $('#btntrashmodesingle').hasClass('btn-success') ) { - socket.send('MPD_API_RM_TRACK,' + tr.attr('trackid')); + sendAPI('MPD_API_RM_TRACK,' + tr.attr('trackid')); } else if ( $('#btntrashmodedown').hasClass('btn-success') ) { - socket.send('MPD_API_RM_RANGE,' + tr.index() + ',-1'); + sendAPI('MPD_API_RM_RANGE,' + tr.index() + ',-1'); }; } function delPlaylist(tr) { - socket.send("MPD_API_RM_PLAYLIST," + decodeURI(tr.attr("uri"))); + sendAPI("MPD_API_RM_PLAYLIST," + decodeURI(tr.attr("uri"))); tr.remove(); } @@ -1061,23 +1136,23 @@ function confirmSettings() { } } if (formOK == true) { - socket.send("MPD_API_TOGGLE_CONSUME," + ($('#btnconsume').hasClass('btn-success') ? 1 : 0)); - socket.send("MPD_API_TOGGLE_RANDOM," + ($('#btnrandom').hasClass('btn-success') ? 1 : 0)); - socket.send("MPD_API_TOGGLE_SINGLE," + ($('#btnsingle').hasClass('btn-success') ? 1 : 0)); - socket.send("MPD_API_TOGGLE_REPEAT," + ($('#btnrepeat').hasClass('btn-success') ? 1 : 0)); - socket.send("MPD_API_SET_REPLAYGAIN," + $('#selectReplaygain').val()); + sendAPI("MPD_API_TOGGLE_CONSUME," + ($('#btnconsume').hasClass('btn-success') ? 1 : 0)); + sendAPI("MPD_API_TOGGLE_RANDOM," + ($('#btnrandom').hasClass('btn-success') ? 1 : 0)); + sendAPI("MPD_API_TOGGLE_SINGLE," + ($('#btnsingle').hasClass('btn-success') ? 1 : 0)); + sendAPI("MPD_API_TOGGLE_REPEAT," + ($('#btnrepeat').hasClass('btn-success') ? 1 : 0)); + sendAPI("MPD_API_SET_REPLAYGAIN," + $('#selectReplaygain').val()); if (!$('#inputCrossfade').is(':disabled')) - socket.send("MPD_API_SET_CROSSFADE," + $('#inputCrossfade').val()); + sendAPI("MPD_API_SET_CROSSFADE," + $('#inputCrossfade').val()); if (!$('#inputMixrampdb').is(':disabled')) - socket.send("MPD_API_SET_MIXRAMPDB," + $('#inputMixrampdb').val()); + sendAPI("MPD_API_SET_MIXRAMPDB," + $('#inputMixrampdb').val()); if (!$('#inputMixrampdelay').is(':disabled')) - socket.send("MPD_API_SET_MIXRAMPDELAY," + $('#inputMixrampdelay').val()); + sendAPI("MPD_API_SET_MIXRAMPDELAY," + $('#inputMixrampdelay').val()); $('#settings').modal('hide'); } } function toggleoutput(button, id) { - socket.send("MPD_API_TOGGLE_OUTPUT,"+id+"," + ($(button).hasClass('btn-success') ? 0 : 1)); + sendAPI("MPD_API_TOGGLE_OUTPUT,"+id+"," + ($(button).hasClass('btn-success') ? 0 : 1)); } $('#trashmodebtns > button').on('click', function(e) { @@ -1130,7 +1205,7 @@ $('#search2').submit(function () { function addAllFromSearch() { if (app.current.search.length >= 2) { - socket.send('MPD_API_SEARCH_ADD,' + app.current.filter + ',' + app.current.search); + sendAPI('MPD_API_SEARCH_ADD,' + app.current.filter + ',' + app.current.search); var rowCount = $('#SearchList >tbody >tr').length; showNotification('Added '+rowCount+' songs from search','','','success'); } @@ -1195,7 +1270,7 @@ function gotoPage(x,element,event) { function addStream() { if($('#streamurl').val().length > 0) { - socket.send('MPD_API_ADD_TRACK,'+$('#streamurl').val()); + sendAPI('MPD_API_ADD_TRACK,'+$('#streamurl').val()); } $('#streamurl').val(""); $('#addstream').modal('hide'); @@ -1203,7 +1278,7 @@ function addStream() { function saveQueue() { if($('#playlistname').val().length > 0) { - socket.send('MPD_API_SAVE_QUEUE,'+$('#playlistname').val()); + sendAPI('MPD_API_SAVE_QUEUE,'+$('#playlistname').val()); } $('#savequeue').modal('hide'); } @@ -1285,10 +1360,10 @@ $(document).keydown(function(e){ } switch (e.which) { case 37: //left - socket.send('MPD_API_SET_PREV'); + sendAPI('MPD_API_SET_PREV'); break; case 39: //right - socket.send('MPD_API_SET_NEXT'); + sendAPI('MPD_API_SET_NEXT'); break; case 32: //space clickPlay(); @@ -1340,7 +1415,7 @@ function chVolume (increment) { if (newValue<0) { newValue=0; } else if (newValue > 100) { newValue=100; } volumeBar.slider('setValue',newValue); - socket.send("MPD_API_SET_VOLUME,"+newValue); + sendAPI("MPD_API_SET_VOLUME,"+newValue); } function beautifyDuration(x) { diff --git a/mympd.1 b/mympd.1 index 8851257..350d1c4 100644 --- a/mympd.1 +++ b/mympd.1 @@ -18,12 +18,6 @@ connect to mpd at host, defaults to localhost \fB\-p\fR, \fB\-\-port PORT\fR connect to mpd at port, defaults to 6600 .TP -\fB\-D\fR, \fB\-\-digest HTDIGEST\fR -path to htdigest file for authorization -.TP -\fB\-l\fR, \fB\-\-localport PORT\fR -skip authorization for local port -.TP \fB\-w\fR, \fB\-\-webport PORT\fR specifies the port for the webserver to listen to, defaults to 8080 .TP diff --git a/src/mpd_client.c b/src/mpd_client.c index 4996a03..4e6ccf8 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -146,9 +146,8 @@ void callback_mpd(struct mg_connection *nc, const struct mg_str msg) if(sscanf(msg.p, "MPD_API_SET_MIXRAMPDELAY,%f", &float_buf)) mpd_run_mixrampdelay(mpd.conn, float_buf); break; - case MPD_API_GET_OUTPUTS: - mpd.buf_size = mpd_put_outputs(mpd.buf, 1); - mpd_notify_callback(nc, NULL); + case MPD_API_GET_OUTPUTNAMES: + n = mpd_put_outputnames(mpd.buf); break; case MPD_API_TOGGLE_OUTPUT: if (sscanf(msg.p, "MPD_API_TOGGLE_OUTPUT,%u,%u", &uint_buf, &uint_buf_2)) { @@ -245,9 +244,9 @@ out_artistalbumtitle: out_playlists: free(p_charbuf); break; - case MPD_API_GET_BROWSE: + case MPD_API_GET_FILESYSTEM: p_charbuf = strdup(msg.p); - if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_BROWSE")) + if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_FILESYSTEM")) goto out_browse; uint_buf = strtoul(strtok(NULL, ","), NULL, 10); @@ -478,10 +477,19 @@ out_set_replaygain: } if(n > 0) { - #ifdef DEBUG - fprintf(stdout,"Send response:\n %s\n",mpd.buf); - #endif - mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, mpd.buf, n); + + if(is_websocket(nc)) { + #ifdef DEBUG + fprintf(stdout,"Send response over websocket:\n %s\n",mpd.buf); + #endif + mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, mpd.buf, n); + } + else { + #ifdef DEBUG + fprintf(stdout,"Send http response:\n %s\n",mpd.buf); + #endif + mg_send_http_chunk(nc, mpd.buf, n); + } } } @@ -590,12 +598,6 @@ void mpd_poll(struct mg_mgr *s) fprintf(stderr, "MPD connected.\n"); mpd_connection_set_timeout(mpd.conn, 10000); mpd.conn_state = MPD_CONNECTED; - /* write outputs */ - mpd.buf_size = mpd_put_outputs(mpd.buf, 1); - for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) - { - mpd_notify_callback(c, NULL); - } break; case MPD_FAILURE: @@ -615,11 +617,6 @@ void mpd_poll(struct mg_mgr *s) { mpd_notify_callback(c, NULL); } - mpd.buf_size = mpd_put_outputs(mpd.buf, 0); - for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) - { - mpd_notify_callback(c, NULL); - } break; } } @@ -701,7 +698,9 @@ int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsign { struct mpd_status *status; const struct mpd_audio_format *audioformat; - int len; + struct mpd_output *out; + char *cur = buffer; + const char *end = buffer + MAX_SIZE; status = mpd_run_status(mpd.conn); if (!status) { @@ -713,35 +712,59 @@ int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsign audioformat = mpd_status_get_audio_format(status); } - len = snprintf(buffer, MAX_SIZE, - "{\"type\":\"state\", \"data\":{" - "\"state\":%d, \"volume\":%d, \"songpos\": %d, \"elapsedTime\": %d, " - "\"totalTime\":%d, \"currentsongid\": %d, \"kbitrate\": %d, " - "\"audioformat\": { \"sample_rate\": %d, \"bits\": %d, \"channels\": %d}, " - "\"queue_length\": %d, \"nextsongpos\": %d, \"nextsongid\": %d, " - "\"queue_version\": %d" - "}}", - mpd_status_get_state(status), - mpd_status_get_volume(status), - mpd_status_get_song_pos(status), - mpd_status_get_elapsed_time(status), - mpd_status_get_total_time(status), - mpd_status_get_song_id(status), - mpd_status_get_kbit_rate(status), - audioformat ? audioformat->sample_rate : 0, - audioformat ? audioformat->bits : 0, - audioformat ? audioformat->channels : 0, - mpd_status_get_queue_length(status), - mpd_status_get_next_song_pos(status), - mpd_status_get_next_song_id(status), - mpd_status_get_queue_version(status) - ); + cur += json_emit_raw_str(cur, end - cur, "{\"type\": \"state\", \"data\":{\"state\":"); + cur += json_emit_int(cur, end - cur, mpd_status_get_state(status)); + cur += json_emit_raw_str(cur, end - cur, ",\"volume\":"); + cur += json_emit_int(cur, end - cur, mpd_status_get_volume(status)); + cur += json_emit_raw_str(cur, end - cur, ",\"songpos\":"); + cur += json_emit_int(cur, end - cur, mpd_status_get_song_pos(status)); + cur += json_emit_raw_str(cur, end - cur, ",\"elapsedTime\":"); + cur += json_emit_int(cur, end - cur, mpd_status_get_elapsed_time(status)); + cur += json_emit_raw_str(cur, end - cur, ",\"totalTime\":"); + cur += json_emit_int(cur, end - cur, mpd_status_get_total_time(status)); + cur += json_emit_raw_str(cur, end - cur, ",\"currentsongid\":"); + cur += json_emit_int(cur, end - cur, mpd_status_get_song_id(status)); + cur += json_emit_raw_str(cur, end - cur, ",\"kbitrate\":"); + cur += json_emit_int(cur, end - cur, mpd_status_get_kbit_rate(status)); + cur += json_emit_raw_str(cur, end - cur, ",\"audioformat\": {"); + cur += json_emit_raw_str(cur, end - cur, "\"sample_rate\":"); + cur += json_emit_int(cur, end - cur, audioformat ? audioformat->sample_rate : 0); + cur += json_emit_raw_str(cur, end - cur, ",\"bits\":"); + cur += json_emit_int(cur, end - cur, audioformat ? audioformat->bits : 0); + cur += json_emit_raw_str(cur, end - cur, ",\"channels\":"); + cur += json_emit_int(cur, end - cur, audioformat ? audioformat->channels : 0); + cur += json_emit_raw_str(cur, end - cur, "},\"queue_length\":"); + cur += json_emit_int(cur, end - cur, mpd_status_get_queue_length(status)); + cur += json_emit_raw_str(cur, end - cur, ",\"nextsongpos\":"); + cur += json_emit_int(cur, end - cur, mpd_status_get_next_song_pos(status)); + cur += json_emit_raw_str(cur, end - cur, ",\"nextsongid\":"); + cur += json_emit_int(cur, end - cur, mpd_status_get_next_song_id(status)); + cur += json_emit_raw_str(cur, end - cur, ",\"queue_version\":"); + cur += json_emit_int(cur, end - cur, mpd_status_get_queue_version(status)); + cur += json_emit_raw_str(cur, end - cur, ",\"outputs\": {"); + + mpd_send_outputs(mpd.conn); + while ((out = mpd_recv_output(mpd.conn)) != NULL) { + cur += json_emit_raw_str(cur, end - cur, "\""); + cur += json_emit_int(cur, end - cur, mpd_output_get_id(out)); + cur += json_emit_raw_str(cur, end - cur, "\":"); + cur += json_emit_int(cur, end - cur, mpd_output_get_enabled(out)); + cur += json_emit_raw_str(cur, end - cur, ","); + mpd_output_free(out); + } + if (!mpd_response_finish(mpd.conn)) { + fprintf(stderr, "MPD outputs: %s\n", mpd_connection_get_error_message(mpd.conn)); + mpd_connection_clear_error(mpd.conn); + } + + cur --; + cur += json_emit_raw_str(cur, end - cur, "}}}"); *current_song_id = mpd_status_get_song_id(status); *next_song_id = mpd_status_get_next_song_id(status); *queue_version = mpd_status_get_queue_version(status); mpd_status_free(status); - return len; + return cur - buffer; } int mympd_put_settings(char *buffer) @@ -788,37 +811,33 @@ int mympd_put_settings(char *buffer) } -int mpd_put_outputs(char *buffer, int names) +int mpd_put_outputnames(char *buffer) { + char *cur = buffer; + const char *end = buffer + MAX_SIZE; struct mpd_output *out; - int nout; - char *str, *strend; - - str = buffer; - strend = buffer+MAX_SIZE; - str += snprintf(str, strend-str, "{\"type\":\"%s\", \"data\":{", - names ? "outputnames" : "outputs"); + + cur += json_emit_raw_str(cur, end - cur, "{\"type\": \"outputnames\", \"data\":{"); mpd_send_outputs(mpd.conn); - nout = 0; + while ((out = mpd_recv_output(mpd.conn)) != NULL) { - if (nout++) - *str++ = ','; - if (names) - str += snprintf(str, strend - str, " \"%d\":\"%s\"", - mpd_output_get_id(out), mpd_output_get_name(out)); - else - str += snprintf(str, strend-str, " \"%d\":%d", - mpd_output_get_id(out), mpd_output_get_enabled(out)); + cur += json_emit_raw_str(cur, end - cur, "\""); + cur += json_emit_int(cur, end - cur, mpd_output_get_id(out)); + cur += json_emit_raw_str(cur, end - cur, "\":"); + cur += json_emit_quoted_str(cur, end - cur, mpd_output_get_name(out)); + cur += json_emit_raw_str(cur, end - cur, ","); mpd_output_free(out); } if (!mpd_response_finish(mpd.conn)) { fprintf(stderr, "MPD outputs: %s\n", mpd_connection_get_error_message(mpd.conn)); mpd_connection_clear_error(mpd.conn); - return 0; } - str += snprintf(str, strend-str, " }}"); - return str-buffer; + + cur --; + cur += json_emit_raw_str(cur, end - cur, "}}"); + + return cur - buffer; } int mpd_put_current_song(char *buffer) diff --git a/src/mpd_client.h b/src/mpd_client.h index 0f515e1..ba578ae 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -44,7 +44,7 @@ #define GEN_STR(X) #X, #define MPD_CMDS(X) \ X(MPD_API_GET_QUEUE) \ - X(MPD_API_GET_BROWSE) \ + X(MPD_API_GET_FILESYSTEM) \ X(MPD_API_ADD_TRACK) \ X(MPD_API_ADD_PLAY_TRACK) \ X(MPD_API_ADD_PLAYLIST) \ @@ -66,7 +66,7 @@ X(MPD_API_SET_NEXT) \ X(MPD_API_SET_PREV) \ X(MPD_API_UPDATE_DB) \ - X(MPD_API_GET_OUTPUTS) \ + X(MPD_API_GET_OUTPUTNAMES) \ X(MPD_API_TOGGLE_OUTPUT) \ X(MPD_API_TOGGLE_RANDOM) \ X(MPD_API_TOGGLE_CONSUME) \ @@ -134,7 +134,7 @@ void mpd_poll(struct mg_mgr *s); void callback_mpd(struct mg_connection *nc, const struct mg_str msg); int mpd_close_handler(struct mg_connection *c); int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsigned *queue_version); -int mpd_put_outputs(char *buffer, int putnames); +int mpd_put_outputnames(char *buffer); int mpd_put_current_song(char *buffer); int mpd_put_queue(char *buffer, unsigned int offset); int mpd_put_browse(char *buffer, char *path, unsigned int offset, char *filter); diff --git a/src/mympd.c b/src/mympd.c index 7449f93..b508eb2 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -28,7 +28,8 @@ #include #include #include -#include +//#include +#include #include "mongoose/mongoose.h" #include "mpd_client.h" @@ -43,6 +44,15 @@ static void signal_handler(int sig_num) { s_signal_received = sig_num; } +static void handle_api(struct mg_connection *nc, struct http_message *hm) { + mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"); + char buf[1000] = {0}; + memcpy(buf, hm->body.p,sizeof(buf) - 1 < hm->body.len ? sizeof(buf) - 1 : hm->body.len); + struct mg_str d = {buf, strlen(buf)}; + callback_mpd(nc, d); + mg_send_http_chunk(nc, "", 0); /* Send empty chunk, the end of response */ +} + static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { switch(ev) { case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { @@ -71,7 +81,13 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { #ifdef DEBUG printf("HTTP request: %.*s\n",hm->uri.len,hm->uri.p); #endif - mg_serve_http(nc, hm, s_http_server_opts); + if (mg_vcmp(&hm->uri, "/api") == 0) { + handle_api(nc, hm); + + } + else { + mg_serve_http(nc, hm, s_http_server_opts); + } break; } case MG_EV_CLOSE: { @@ -94,23 +110,11 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { int main(int argc, char **argv) { int n, option_index = 0; - struct mg_mgr mgr; struct mg_connection *nc; - unsigned int current_timer = 0, last_timer = 0; - - signal(SIGTERM, signal_handler); - signal(SIGINT, signal_handler); - setvbuf(stdout, NULL, _IOLBF, 0); - setvbuf(stderr, NULL, _IOLBF, 0); - - mg_mgr_init(&mgr, NULL); - char *run_as_user = NULL; - char const *error_msg = NULL; char *webport = "8080"; - mpd.port = 6600; mpd.local_port = 0; mpd.gpass = NULL; @@ -119,10 +123,8 @@ int main(int argc, char **argv) strcpy(coverimage, "folder.jpg"); static struct option long_options[] = { - {"digest", required_argument, 0, 'D'}, {"host", required_argument, 0, 'h'}, {"port", required_argument, 0, 'p'}, - {"localport", required_argument, 0, 'l'}, {"webport", required_argument, 0, 'w'}, {"user", required_argument, 0, 'u'}, {"version", no_argument, 0, 'v'}, @@ -145,9 +147,6 @@ int main(int argc, char **argv) case 'p': mpd.port = atoi(optarg); break; - case 'l': - mpd.local_port = atoi(optarg); - break; case 'w': webport = strdup(optarg); break; @@ -173,11 +172,8 @@ int main(int argc, char **argv) break; default: fprintf(stderr, "Usage: %s [OPTION]...\n\n" - " -D, --digest \tpath to htdigest file for authorization\n" - " \t(realm ympd) [no authorization]\n" " -h, --host \t\tconnect to mpd at host [localhost]\n" " -p, --port \t\tconnect to mpd at port [6600]\n" - " -l, --localport \t\tskip authorization for local port\n" " -w, --webport [ip:]\tlisten interface/port for webserver [8080]\n" " -u, --user \t\tdrop priviliges to user after socket bind\n" " -v, --version\t\t\tget version\n" @@ -191,7 +187,35 @@ int main(int argc, char **argv) } + signal(SIGTERM, signal_handler); + signal(SIGINT, signal_handler); + setvbuf(stdout, NULL, _IOLBF, 0); + setvbuf(stderr, NULL, _IOLBF, 0); + + mg_mgr_init(&mgr, NULL); + nc = mg_bind(&mgr, webport, ev_handler); + if (nc == NULL) { + printf("myMPD can't bind to port %s\n", webport); + return EXIT_FAILURE; + } + + if(run_as_user != NULL) { + printf("Droping privileges\n"); + struct passwd *pw; + if ((pw = getpwnam(run_as_user)) == NULL) { + printf("Unknown user\n"); + return EXIT_FAILURE; + } else if (setgid(pw->pw_gid) != 0) { + printf("setgid() failed"); + return EXIT_FAILURE; + } else if (setuid(pw->pw_uid) != 0) { + printf("setuid() failed\n"); + return EXIT_FAILURE; + } + free(run_as_user); + } + mg_set_protocol_http_websocket(nc); s_http_server_opts.document_root = SRC_PATH; From f119a69b968749ce2695cfbae7c50c9c737300a4 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 14 Jun 2018 21:03:20 +0100 Subject: [PATCH 07/32] Updated --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fe6af0c..53df1db 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ myMPD is a lightweight MPD web client that runs without a dedicated webserver or It's tuned for minimal resource usage and requires only very litte dependencies. myMPD is a fork of ympd. -This fork provides a reworked ui based on Bootstrap 4. +This fork provides a reworked ui based on Bootstrap 4 and a modernized backend. ![image](https://jcgames.de/stuff/myMPD/screenshots.gif) From 290d591c39f9dac29ec2b6e542bf2431b65c355a Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 14 Jun 2018 21:19:36 +0100 Subject: [PATCH 08/32] Initial websocket connection with mympd version --- src/mpd_client.c | 14 ++++++++++++++ src/mpd_client.h | 3 ++- src/mympd.c | 6 +----- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/mpd_client.c b/src/mpd_client.c index 4e6ccf8..2f0ef73 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -77,6 +77,8 @@ void callback_mpd(struct mg_connection *nc, const struct mg_str msg) switch(cmd_id) { + case MPD_API_WELCOME: + n = mympd_put_welcome(mpd.buf); case MPD_API_UPDATE_DB: mpd_run_update(mpd.conn, NULL); break; @@ -767,6 +769,18 @@ int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsign return cur - buffer; } +int mympd_put_welcome(char *buffer) +{ + int len; + len = snprintf(buffer, MAX_SIZE, + "{\"type\":\"welcome\", \"data\":{" + "\"version\":\"%s\"}}", + MYMPD_VERSION + ); + + return len; +} + int mympd_put_settings(char *buffer) { struct mpd_status *status; diff --git a/src/mpd_client.h b/src/mpd_client.h index ba578ae..722901f 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -84,7 +84,8 @@ X(MPD_API_GET_ARTISTALBUMS) \ X(MPD_API_GET_ARTISTALBUMTITLES) \ X(MPD_API_GET_ARTISTS) \ - X(MPD_API_GET_CURRENT_SONG) + X(MPD_API_GET_CURRENT_SONG) \ + X(MPD_API_WELCOME) enum mpd_cmd_ids { MPD_CMDS(GEN_ENUM) diff --git a/src/mympd.c b/src/mympd.c index b508eb2..36f2db9 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -28,7 +28,6 @@ #include #include #include -//#include #include #include "mongoose/mongoose.h" @@ -59,10 +58,7 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { #ifdef DEBUG fprintf(stdout,"New Websocket connection\n"); #endif - struct mg_str d = {(char *) "MPD_API_GET_SETTINGS", 20 }; - callback_mpd(nc, d); - d.p="MPD_API_GET_OUTPUTS"; - d.len=19; + struct mg_str d = {(char *) "MPD_API_WELCOME" }; callback_mpd(nc, d); break; } From 325094f6e7abbe40d63c32deebd6377533e24a73 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 14 Jun 2018 21:24:26 +0100 Subject: [PATCH 09/32] Removed obsolet options --- contrib/mympd.default | 2 -- contrib/mympd.service | 4 +--- src/mympd.c | 3 +-- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/contrib/mympd.default b/contrib/mympd.default index 6d973fa..8eb21f7 100644 --- a/contrib/mympd.default +++ b/contrib/mympd.default @@ -5,5 +5,3 @@ MPD_PASSWORD= WEB_PORT=80 MYMPD_USER=nobody COVERIMAGE=--coverimage folder.jpg -#DIGEST=--digest /path/to/htdigest -#LOCALPORT=--localport 80 diff --git a/contrib/mympd.service b/contrib/mympd.service index 6a26208..628192f 100644 --- a/contrib/mympd.service +++ b/contrib/mympd.service @@ -8,11 +8,9 @@ Environment=MPD_PORT=6600 Environment=MPD_PASSWORD= Environment=WEB_PORT=80 Environment=MYMPD_USER=nobody -Environment=DIGEST= -Environment=LOCALPORT= Environment=COVERIMAGE=--coverimage folder.jpg EnvironmentFile=/etc/default/mympd -ExecStart=/usr/bin/mympd --user $MYMPD_USER --webport $WEB_PORT --host $MPD_HOST --port $MPD_PORT $COVERIMAGE $DIGEST $LOCALPORT +ExecStart=/usr/bin/mympd --user $MYMPD_USER --webport $WEB_PORT --host $MPD_HOST --port $MPD_PORT $COVERIMAGE Type=simple [Install] diff --git a/src/mympd.c b/src/mympd.c index 36f2db9..375d3d9 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -79,7 +79,6 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { #endif if (mg_vcmp(&hm->uri, "/api") == 0) { handle_api(nc, hm); - } else { mg_serve_http(nc, hm, s_http_server_opts); @@ -131,7 +130,7 @@ int main(int argc, char **argv) {0, 0, 0, 0 } }; - while((n = getopt_long(argc, argv, "D:h:p:l:w:u:d:vm:s:i:", + while((n = getopt_long(argc, argv, "D:h:p:w:u:vm:s:i:", long_options, &option_index)) != -1) { switch (n) { case 'D': From fba2a0540adc3573e193e312d893994a20b5c152 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 14 Jun 2018 23:00:54 +0100 Subject: [PATCH 10/32] Renamed functions with mympd prefix --- CMakeLists.txt | 7 ++-- src/mpd_client.c | 101 ++++++++++++++++++++++++----------------------- src/mpd_client.h | 25 ++++++------ src/mympd.c | 14 +++---- 4 files changed, 74 insertions(+), 73 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2103898..4672deb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,9 +30,8 @@ if(WITH_IPV6) set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS NS_ENABLE_IPV6) endif() if(WITH_SSL) - find_package(OpenSSL REQUIRED) - include_directories(${OPENSSL_INCLUDE_DIR}) -# list(APPEND LIB_LIST ${OPENSSL_LIBRARIES}) + find_package(OpenSSL REQUIRED) + include_directories(${OPENSSL_INCLUDE_DIR}) set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS NS_ENABLE_SSL) endif() @@ -50,7 +49,7 @@ set(SOURCES src/mympd.c src/mpd_client.c src/mongoose/mongoose.c - src/json_encode.c + src/frozen/frozen.c ) add_executable(mympd ${SOURCES}) diff --git a/src/mpd_client.c b/src/mpd_client.c index 2f0ef73..e5510aa 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -33,10 +33,11 @@ #include "mpd_client.h" #include "config.h" +#include "frozen/frozen.h" #include "json_encode.h" /* forward declaration */ -static int mpd_notify_callback(struct mg_connection *c, const char *param); +static int mympd_notify_callback(struct mg_connection *c, const char *param); const char * mpd_cmd_strs[] = { MPD_CMDS(GEN_STR) @@ -59,7 +60,7 @@ static inline enum mpd_cmd_ids get_cmd_id(const char *cmd) return -1; } -void callback_mpd(struct mg_connection *nc, const struct mg_str msg) +void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { enum mpd_cmd_ids cmd_id = get_cmd_id(msg.p); size_t n = 0; @@ -149,7 +150,7 @@ void callback_mpd(struct mg_connection *nc, const struct mg_str msg) mpd_run_mixrampdelay(mpd.conn, float_buf); break; case MPD_API_GET_OUTPUTNAMES: - n = mpd_put_outputnames(mpd.buf); + n = mympd_put_outputnames(mpd.buf); break; case MPD_API_TOGGLE_OUTPUT: if (sscanf(msg.p, "MPD_API_TOGGLE_OUTPUT,%u,%u", &uint_buf, &uint_buf_2)) { @@ -169,10 +170,10 @@ void callback_mpd(struct mg_connection *nc, const struct mg_str msg) break; case MPD_API_GET_QUEUE: if(sscanf(msg.p, "MPD_API_GET_QUEUE,%u", &uint_buf)) - n = mpd_put_queue(mpd.buf, uint_buf); + n = mympd_put_queue(mpd.buf, uint_buf); break; case MPD_API_GET_CURRENT_SONG: - n = mpd_put_current_song(mpd.buf); + n = mympd_put_current_song(mpd.buf); break; case MPD_API_GET_ARTISTS: @@ -265,7 +266,7 @@ out_playlists: } else { searchstr = strdup(token); } - n = mpd_put_browse(mpd.buf, p_charbuf2, uint_buf, searchstr); + n = mympd_put_browse(mpd.buf, p_charbuf2, uint_buf, searchstr); free(searchstr); free(p_charbuf2); out_browse: @@ -349,7 +350,7 @@ out_save_queue: } else { searchstr = strdup(token); } - n = mpd_search_queue(mpd.buf, p_charbuf2, uint_buf, searchstr); + n = mympd_search_queue(mpd.buf, p_charbuf2, uint_buf, searchstr); free(searchstr); free(p_charbuf2); out_search_queue: @@ -371,7 +372,7 @@ out_search_queue: } else { searchstr = strdup(token); } - n = mpd_search_add(mpd.buf, p_charbuf2, searchstr); + n = mympd_search_add(mpd.buf, p_charbuf2, searchstr); free(searchstr); free(p_charbuf2); out_search_add: @@ -395,7 +396,7 @@ out_search_add: } else { searchstr = strdup(token); } - n = mpd_search(mpd.buf, p_charbuf2, uint_buf, searchstr); + n = mympd_search(mpd.buf, p_charbuf2, uint_buf, searchstr); free(searchstr); free(p_charbuf2); out_search: @@ -495,7 +496,7 @@ out_set_replaygain: } } -int mpd_close_handler(struct mg_connection *c) +int mympd_close_handler(struct mg_connection *c) { /* Cleanup session data */ if(c->user_data) @@ -503,7 +504,7 @@ int mpd_close_handler(struct mg_connection *c) return 0; } -static int mpd_notify_callback(struct mg_connection *c, const char *param) { +static int mympd_notify_callback(struct mg_connection *c, const char *param) { size_t n; if(!is_websocket(c)) return 0; @@ -540,7 +541,7 @@ static int mpd_notify_callback(struct mg_connection *c, const char *param) { if(s->song_id != mpd.song_id) { - n=mpd_put_current_song(mpd.buf); + n=mympd_put_current_song(mpd.buf); #ifdef DEBUG fprintf(stdout,"Notify: %s\n",mpd.buf); #endif @@ -563,7 +564,7 @@ static int mpd_notify_callback(struct mg_connection *c, const char *param) { return 0; } -void mpd_poll(struct mg_mgr *s) +void mympd_poll(struct mg_mgr *s) { switch (mpd.conn_state) { case MPD_DISCONNECTED: @@ -580,7 +581,7 @@ void mpd_poll(struct mg_mgr *s) fprintf(stderr, "MPD connection: %s\n", mpd_connection_get_error_message(mpd.conn)); for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) { - mpd_notify_callback(c, mpd_connection_get_error_message(mpd.conn)); + mympd_notify_callback(c, mpd_connection_get_error_message(mpd.conn)); } mpd.conn_state = MPD_FAILURE; return; @@ -591,7 +592,7 @@ void mpd_poll(struct mg_mgr *s) fprintf(stderr, "MPD connection: %s\n", mpd_connection_get_error_message(mpd.conn)); for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) { - mpd_notify_callback(c, mpd_connection_get_error_message(mpd.conn)); + mympd_notify_callback(c, mpd_connection_get_error_message(mpd.conn)); } mpd.conn_state = MPD_FAILURE; return; @@ -614,16 +615,16 @@ void mpd_poll(struct mg_mgr *s) break; case MPD_CONNECTED: - mpd.buf_size = mpd_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.queue_version); + mpd.buf_size = mympd_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.queue_version); for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) { - mpd_notify_callback(c, NULL); + mympd_notify_callback(c, NULL); } break; } } -char* mpd_get_title(struct mpd_song const *song) +char* mympd_get_title(struct mpd_song const *song) { char *str; @@ -635,7 +636,7 @@ char* mpd_get_title(struct mpd_song const *song) return str; } -char* mpd_get_track(struct mpd_song const *song) +char* mympd_get_track(struct mpd_song const *song) { char *str; @@ -648,7 +649,7 @@ char* mpd_get_track(struct mpd_song const *song) } -char* mpd_get_album(struct mpd_song const *song) +char* mympd_get_album(struct mpd_song const *song) { char *str; @@ -660,7 +661,7 @@ char* mpd_get_album(struct mpd_song const *song) return str; } -char* mpd_get_artist(struct mpd_song const *song) +char* mympd_get_artist(struct mpd_song const *song) { char *str; @@ -672,7 +673,7 @@ char* mpd_get_artist(struct mpd_song const *song) return str; } -char* mpd_get_album_artist(struct mpd_song const *song) +char* mympd_get_album_artist(struct mpd_song const *song) { char *str; @@ -684,7 +685,7 @@ char* mpd_get_album_artist(struct mpd_song const *song) return str; } -char* mpd_get_year(struct mpd_song const *song) +char* mympd_get_year(struct mpd_song const *song) { char *str; @@ -696,7 +697,7 @@ char* mpd_get_year(struct mpd_song const *song) return str; } -int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsigned *queue_version) +int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsigned *queue_version) { struct mpd_status *status; const struct mpd_audio_format *audioformat; @@ -825,7 +826,7 @@ int mympd_put_settings(char *buffer) } -int mpd_put_outputnames(char *buffer) +int mympd_put_outputnames(char *buffer) { char *cur = buffer; const char *end = buffer + MAX_SIZE; @@ -854,7 +855,7 @@ int mpd_put_outputnames(char *buffer) return cur - buffer; } -int mpd_put_current_song(char *buffer) +int mympd_put_current_song(char *buffer) { char *cur = buffer; const char *end = buffer + MAX_SIZE; @@ -867,11 +868,11 @@ int mpd_put_current_song(char *buffer) cur += json_emit_raw_str(cur, end - cur, "{\"type\": \"song_change\", \"data\":{\"pos\":"); cur += json_emit_int(cur, end - cur, mpd_song_get_pos(song)); cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_get_title(song)); + cur += json_emit_quoted_str(cur, end - cur, mympd_get_title(song)); cur += json_emit_raw_str(cur, end - cur, ",\"artist\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_get_artist(song)); + cur += json_emit_quoted_str(cur, end - cur, mympd_get_artist(song)); cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_get_album(song)); + cur += json_emit_quoted_str(cur, end - cur, mympd_get_album(song)); cur += json_emit_raw_str(cur, end - cur, ",\"uri\":"); cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(song)); cur += json_emit_raw_str(cur, end - cur, ",\"currentsongid\":"); @@ -884,7 +885,7 @@ int mpd_put_current_song(char *buffer) return cur - buffer; } -int mpd_put_queue(char *buffer, unsigned int offset) +int mympd_put_queue(char *buffer, unsigned int offset) { char *cur = buffer; const char *end = buffer + MAX_SIZE; @@ -916,11 +917,11 @@ int mpd_put_queue(char *buffer, unsigned int offset) cur += json_emit_raw_str(cur, end - cur, ",\"duration\":"); cur += json_emit_int(cur, end - cur, mpd_song_get_duration(song)); cur += json_emit_raw_str(cur, end - cur, ",\"artist\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_get_artist(song)); + cur += json_emit_quoted_str(cur, end - cur, mympd_get_artist(song)); cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_get_album(song)); + cur += json_emit_quoted_str(cur, end - cur, mympd_get_album(song)); cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_get_title(song)); + cur += json_emit_quoted_str(cur, end - cur, mympd_get_title(song)); cur += json_emit_raw_str(cur, end - cur, "},"); } @@ -943,7 +944,7 @@ int mpd_put_queue(char *buffer, unsigned int offset) return cur - buffer; } -int mpd_put_browse(char *buffer, char *path, unsigned int offset, char *filter) +int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter) { char *cur = buffer; const char *end = buffer + MAX_SIZE; @@ -969,7 +970,7 @@ int mpd_put_browse(char *buffer, char *path, unsigned int offset, char *filter) break; case MPD_ENTITY_TYPE_SONG: song = mpd_entity_get_song(entity); - entityName = mpd_get_title(song); + entityName = mympd_get_title(song); if (strncmp(filter,"-",1) == 0 || strncasecmp(filter,entityName,1) == 0 || ( strncmp(filter,"0",1) == 0 && isalpha(*entityName) == 0 ) ) { @@ -977,9 +978,9 @@ int mpd_put_browse(char *buffer, char *path, unsigned int offset, char *filter) cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"song\",\"uri\":"); cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(song)); cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_get_album(song)); + cur += json_emit_quoted_str(cur, end - cur, mympd_get_album(song)); cur += json_emit_raw_str(cur, end - cur, ",\"artist\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_get_artist(song)); + cur += json_emit_quoted_str(cur, end - cur, mympd_get_artist(song)); cur += json_emit_raw_str(cur, end - cur, ",\"duration\":"); cur += json_emit_int(cur, end - cur, mpd_song_get_duration(song)); cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); @@ -1151,9 +1152,9 @@ int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album) cur += json_emit_raw_str(cur, end - cur, ",\"duration\":"); cur += json_emit_int(cur, end - cur, mpd_song_get_duration(song)); cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_get_title(song)); + cur += json_emit_quoted_str(cur, end - cur, mympd_get_title(song)); cur += json_emit_raw_str(cur, end - cur, ",\"track\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_get_track(song)); + cur += json_emit_quoted_str(cur, end - cur, mympd_get_track(song)); cur += json_emit_raw_str(cur, end - cur, "},"); } mpd_song_free(song); @@ -1226,7 +1227,7 @@ int mympd_put_playlists(char *buffer, unsigned int offset, char *filter) return cur - buffer; } -int mpd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr) +int mympd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr) { char *cur = buffer; const char *end = buffer + MAX_SIZE; @@ -1260,13 +1261,13 @@ int mpd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *search cur += json_emit_raw_str(cur, end - cur, ",\"uri\":"); cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(song)); cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_get_album(song)); + cur += json_emit_quoted_str(cur, end - cur, mympd_get_album(song)); cur += json_emit_raw_str(cur, end - cur, ",\"artist\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_get_artist(song)); + cur += json_emit_quoted_str(cur, end - cur, mympd_get_artist(song)); cur += json_emit_raw_str(cur, end - cur, ",\"duration\":"); cur += json_emit_int(cur, end - cur, mpd_song_get_duration(song)); cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_get_title(song)); + cur += json_emit_quoted_str(cur, end - cur, mympd_get_title(song)); cur += json_emit_raw_str(cur, end - cur, "},"); } mpd_song_free(song); @@ -1289,7 +1290,7 @@ int mpd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *search return cur - buffer; } -int mpd_search_add(char *buffer,char *mpdtagtype, char *searchstr) +int mympd_search_add(char *buffer,char *mpdtagtype, char *searchstr) { char *cur = buffer; const char *end = buffer + MAX_SIZE; @@ -1318,7 +1319,7 @@ int mpd_search_add(char *buffer,char *mpdtagtype, char *searchstr) return cur - buffer; } -int mpd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr) +int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr) { char *cur = buffer; const char *end = buffer + MAX_SIZE; @@ -1354,13 +1355,13 @@ int mpd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char * cur += json_emit_raw_str(cur, end - cur, ",\"pos\":"); cur += json_emit_int(cur, end - cur, mpd_song_get_pos(song)); cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_get_album(song)); + cur += json_emit_quoted_str(cur, end - cur, mympd_get_album(song)); cur += json_emit_raw_str(cur, end - cur, ",\"artist\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_get_artist(song)); + cur += json_emit_quoted_str(cur, end - cur, mympd_get_artist(song)); cur += json_emit_raw_str(cur, end - cur, ",\"duration\":"); cur += json_emit_int(cur, end - cur, mpd_song_get_duration(song)); cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_get_title(song)); + cur += json_emit_quoted_str(cur, end - cur, mympd_get_title(song)); cur += json_emit_raw_str(cur, end - cur, "},"); mpd_song_free(song); } @@ -1419,8 +1420,8 @@ int mympd_get_stats(char *buffer) return cur - buffer; } -void mpd_disconnect() +void mympd_disconnect() { mpd.conn_state = MPD_DISCONNECT; - mpd_poll(NULL); + mympd_poll(NULL); } diff --git a/src/mpd_client.h b/src/mpd_client.h index 722901f..de1db40 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -131,22 +131,23 @@ struct t_mpd_client_session { unsigned queue_version; }; -void mpd_poll(struct mg_mgr *s); -void callback_mpd(struct mg_connection *nc, const struct mg_str msg); -int mpd_close_handler(struct mg_connection *c); -int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsigned *queue_version); -int mpd_put_outputnames(char *buffer); -int mpd_put_current_song(char *buffer); -int mpd_put_queue(char *buffer, unsigned int offset); -int mpd_put_browse(char *buffer, char *path, unsigned int offset, char *filter); -int mpd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr); -int mpd_search_add(char *buffer, char *mpdtagtype, char *searchstr); -int mpd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr); +void mympd_poll(struct mg_mgr *s); +void callback_mympd(struct mg_connection *nc, const struct mg_str msg); +int mympd_close_handler(struct mg_connection *c); +int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsigned *queue_version); +int mympd_put_outputnames(char *buffer); +int mympd_put_current_song(char *buffer); +int mympd_put_queue(char *buffer, unsigned int offset); +int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter); +int mympd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr); +int mympd_search_add(char *buffer, char *mpdtagtype, char *searchstr); +int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr); +int mympd_put_welcome(char *buffer); int mympd_get_stats(char *buffer); int mympd_put_settings(char *buffer); int mympd_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char *mpdsearchtagtype, char *searchstr, char *filter); int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album); int mympd_put_playlists(char *buffer, unsigned int offset, char *filter); -void mpd_disconnect(); +void mympd_disconnect(); #endif diff --git a/src/mympd.c b/src/mympd.c index 375d3d9..f585907 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -48,7 +48,7 @@ static void handle_api(struct mg_connection *nc, struct http_message *hm) { char buf[1000] = {0}; memcpy(buf, hm->body.p,sizeof(buf) - 1 < hm->body.len ? sizeof(buf) - 1 : hm->body.len); struct mg_str d = {buf, strlen(buf)}; - callback_mpd(nc, d); + callback_mympd(nc, d); mg_send_http_chunk(nc, "", 0); /* Send empty chunk, the end of response */ } @@ -58,8 +58,8 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { #ifdef DEBUG fprintf(stdout,"New Websocket connection\n"); #endif - struct mg_str d = {(char *) "MPD_API_WELCOME" }; - callback_mpd(nc, d); + struct mg_str d = {(char *) "MPD_API_WELCOME", 15 }; + callback_mympd(nc, d); break; } case MG_EV_WEBSOCKET_FRAME: { @@ -69,7 +69,7 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { #ifdef DEBUG fprintf(stdout,"Websocket request: %s\n",wm->data); #endif - callback_mpd(nc, d); + callback_mympd(nc, d); break; } case MG_EV_HTTP_REQUEST: { @@ -90,7 +90,7 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { #ifdef DEBUG fprintf(stdout,"Websocket connection closed\n"); #endif - mpd_close_handler(nc); + mympd_close_handler(nc); } else { #ifdef DEBUG @@ -221,10 +221,10 @@ int main(int argc, char **argv) if(current_timer - last_timer) { last_timer = current_timer; - mpd_poll(&mgr); + mympd_poll(&mgr); } } mg_mgr_free(&mgr); - mpd_disconnect(); + mympd_disconnect(); return EXIT_SUCCESS; } From 96a7b8a5c98dc157a7a3855907084815e443d982 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 14 Jun 2018 23:04:01 +0100 Subject: [PATCH 11/32] Add frozen --- src/frozen/LICENSE | 16 + src/frozen/frozen.c | 994 ++++++++++++++++++++++++++++++++++++++++++++ src/frozen/frozen.h | 214 ++++++++++ 3 files changed, 1224 insertions(+) create mode 100644 src/frozen/LICENSE create mode 100644 src/frozen/frozen.c create mode 100644 src/frozen/frozen.h diff --git a/src/frozen/LICENSE b/src/frozen/LICENSE new file mode 100644 index 0000000..7b1a2d9 --- /dev/null +++ b/src/frozen/LICENSE @@ -0,0 +1,16 @@ +Copyright (c) 2004-2013 Sergey Lyubka +Copyright (c) 2013 Cesanta Software Limited +All rights reserved + +This code is dual-licensed: you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. For the terms of this +license, see . + +You are free to use this code under the terms of the GNU General +Public License, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU General Public License for more details. + +Alternatively, you can license this code under a commercial +license, as set out in . diff --git a/src/frozen/frozen.c b/src/frozen/frozen.c new file mode 100644 index 0000000..4b5e279 --- /dev/null +++ b/src/frozen/frozen.c @@ -0,0 +1,994 @@ +/* + * Copyright (c) 2004-2013 Sergey Lyubka + * Copyright (c) 2013 Cesanta Software Limited + * All rights reserved + * + * This library is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this library under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this library under a commercial + * license, as set out in . + */ + +#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005+ */ + +#include "frozen.h" +#include +#include +#include +#include +#include + +#if !defined(WEAK) +#if (defined(__GNUC__) || defined(__TI_COMPILER_VERSION__)) && !defined(_WIN32) +#define WEAK __attribute__((weak)) +#else +#define WEAK +#endif +#endif + +#ifdef _WIN32 +#define snprintf cs_win_snprintf +#define vsnprintf cs_win_vsnprintf +int cs_win_snprintf(char *str, size_t size, const char *format, ...); +int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap); +#if _MSC_VER >= 1700 +#include +#else +typedef _int64 int64_t; +typedef unsigned _int64 uint64_t; +#endif +#define PRId64 "I64d" +#define PRIu64 "I64u" +#if !defined(SIZE_T_FMT) +#if _MSC_VER >= 1310 +#define SIZE_T_FMT "Iu" +#else +#define SIZE_T_FMT "u" +#endif +#endif +#else /* _WIN32 */ +/* wants this for C++ */ +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif +#include +#if !defined(SIZE_T_FMT) +#define SIZE_T_FMT "zu" +#endif +#endif /* _WIN32 */ + +#define INT64_FMT PRId64 +#define UINT64_FMT PRIu64 + +#ifndef va_copy +#define va_copy(x, y) x = y +#endif + +#ifndef JSON_MAX_PATH_LEN +#define JSON_MAX_PATH_LEN 60 +#endif + +struct frozen { + const char *end; + const char *cur; + + const char *cur_name; + size_t cur_name_len; + + /* For callback API */ + char path[JSON_MAX_PATH_LEN]; + int path_len; + void *callback_data; + json_walk_callback_t callback; +}; + +struct fstate { + const char *ptr; + int path_len; +}; + +#define SET_STATE(fr, ptr, str, len) \ + struct fstate fstate = {(ptr), (fr)->path_len}; \ + append_to_path((fr), (str), (len)); + +#define CALL_BACK(fr, tok, value, len) \ + do { \ + if ((fr)->callback && \ + ((fr)->path_len == 0 || (fr)->path[(fr)->path_len - 1] != '.')) { \ + struct json_token t = {(value), (len), (tok)}; \ + \ + /* Call the callback with the given value and current name */ \ + (fr)->callback((fr)->callback_data, (fr)->cur_name, (fr)->cur_name_len, \ + (fr)->path, &t); \ + \ + /* Reset the name */ \ + (fr)->cur_name = NULL; \ + (fr)->cur_name_len = 0; \ + } \ + } while (0) + +static int append_to_path(struct frozen *f, const char *str, int size) { + int n = f->path_len; + f->path_len += + snprintf(f->path + f->path_len, sizeof(f->path) - (f->path_len + 1), + "%.*s", size, str); + + return n; +} + +static void truncate_path(struct frozen *f, int len) { + f->path_len = len; + f->path[len] = '\0'; +} + +static int parse_object(struct frozen *f); +static int parse_value(struct frozen *f); + +#define EXPECT(cond, err_code) \ + do { \ + if (!(cond)) return (err_code); \ + } while (0) + +#define TRY(expr) \ + do { \ + int _n = expr; \ + if (_n < 0) return _n; \ + } while (0) + +#define END_OF_STRING (-1) + +static int left(const struct frozen *f) { + return f->end - f->cur; +} + +static int is_space(int ch) { + return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'; +} + +static void skip_whitespaces(struct frozen *f) { + while (f->cur < f->end && is_space(*f->cur)) f->cur++; +} + +static int cur(struct frozen *f) { + skip_whitespaces(f); + return f->cur >= f->end ? END_OF_STRING : *(unsigned char *) f->cur; +} + +static int test_and_skip(struct frozen *f, int expected) { + int ch = cur(f); + if (ch == expected) { + f->cur++; + return 0; + } + return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; +} + +static int is_alpha(int ch) { + return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); +} + +static int is_digit(int ch) { + return ch >= '0' && ch <= '9'; +} + +static int is_hex_digit(int ch) { + return is_digit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'); +} + +static int get_escape_len(const char *s, int len) { + switch (*s) { + case 'u': + return len < 6 ? JSON_STRING_INCOMPLETE + : is_hex_digit(s[1]) && is_hex_digit(s[2]) && + is_hex_digit(s[3]) && is_hex_digit(s[4]) + ? 5 + : JSON_STRING_INVALID; + case '"': + case '\\': + case '/': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + return len < 2 ? JSON_STRING_INCOMPLETE : 1; + default: + return JSON_STRING_INVALID; + } +} + +/* identifier = letter { letter | digit | '_' } */ +static int parse_identifier(struct frozen *f) { + EXPECT(is_alpha(cur(f)), JSON_STRING_INVALID); + { + SET_STATE(f, f->cur, "", 0); + while (f->cur < f->end && + (*f->cur == '_' || is_alpha(*f->cur) || is_digit(*f->cur))) { + f->cur++; + } + truncate_path(f, fstate.path_len); + CALL_BACK(f, JSON_TYPE_STRING, fstate.ptr, f->cur - fstate.ptr); + } + return 0; +} + +static int get_utf8_char_len(unsigned char ch) { + if ((ch & 0x80) == 0) return 1; + switch (ch & 0xf0) { + case 0xf0: + return 4; + case 0xe0: + return 3; + default: + return 2; + } +} + +/* string = '"' { quoted_printable_chars } '"' */ +static int parse_string(struct frozen *f) { + int n, ch = 0, len = 0; + TRY(test_and_skip(f, '"')); + { + SET_STATE(f, f->cur, "", 0); + for (; f->cur < f->end; f->cur += len) { + ch = *(unsigned char *) f->cur; + len = get_utf8_char_len((unsigned char) ch); + EXPECT(ch >= 32 && len > 0, JSON_STRING_INVALID); /* No control chars */ + EXPECT(len < left(f), JSON_STRING_INCOMPLETE); + if (ch == '\\') { + EXPECT((n = get_escape_len(f->cur + 1, left(f))) > 0, n); + len += n; + } else if (ch == '"') { + truncate_path(f, fstate.path_len); + CALL_BACK(f, JSON_TYPE_STRING, fstate.ptr, f->cur - fstate.ptr); + f->cur++; + break; + }; + } + } + return ch == '"' ? 0 : JSON_STRING_INCOMPLETE; +} + +/* number = [ '-' ] digit+ [ '.' digit+ ] [ ['e'|'E'] ['+'|'-'] digit+ ] */ +static int parse_number(struct frozen *f) { + int ch = cur(f); + SET_STATE(f, f->cur, "", 0); + if (ch == '-') f->cur++; + EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); + EXPECT(is_digit(f->cur[0]), JSON_STRING_INVALID); + while (f->cur < f->end && is_digit(f->cur[0])) f->cur++; + if (f->cur < f->end && f->cur[0] == '.') { + f->cur++; + EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); + EXPECT(is_digit(f->cur[0]), JSON_STRING_INVALID); + while (f->cur < f->end && is_digit(f->cur[0])) f->cur++; + } + if (f->cur < f->end && (f->cur[0] == 'e' || f->cur[0] == 'E')) { + f->cur++; + EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); + if ((f->cur[0] == '+' || f->cur[0] == '-')) f->cur++; + EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); + EXPECT(is_digit(f->cur[0]), JSON_STRING_INVALID); + while (f->cur < f->end && is_digit(f->cur[0])) f->cur++; + } + truncate_path(f, fstate.path_len); + CALL_BACK(f, JSON_TYPE_NUMBER, fstate.ptr, f->cur - fstate.ptr); + return 0; +} + +/* array = '[' [ value { ',' value } ] ']' */ +static int parse_array(struct frozen *f) { + int i = 0, current_path_len; + char buf[20]; + TRY(test_and_skip(f, '[')); + { + CALL_BACK(f, JSON_TYPE_ARRAY_START, NULL, 0); + { + SET_STATE(f, f->cur - 1, "", 0); + while (cur(f) != ']') { + snprintf(buf, sizeof(buf), "[%d]", i); + i++; + current_path_len = append_to_path(f, buf, strlen(buf)); + f->cur_name = + f->path + strlen(f->path) - strlen(buf) + 1 /*opening brace*/; + f->cur_name_len = strlen(buf) - 2 /*braces*/; + TRY(parse_value(f)); + truncate_path(f, current_path_len); + if (cur(f) == ',') f->cur++; + } + TRY(test_and_skip(f, ']')); + truncate_path(f, fstate.path_len); + CALL_BACK(f, JSON_TYPE_ARRAY_END, fstate.ptr, f->cur - fstate.ptr); + } + } + return 0; +} + +static int expect(struct frozen *f, const char *s, int len, + enum json_token_type tok_type) { + int i, n = left(f); + SET_STATE(f, f->cur, "", 0); + for (i = 0; i < len; i++) { + if (i >= n) return JSON_STRING_INCOMPLETE; + if (f->cur[i] != s[i]) return JSON_STRING_INVALID; + } + f->cur += len; + truncate_path(f, fstate.path_len); + + CALL_BACK(f, tok_type, fstate.ptr, f->cur - fstate.ptr); + + return 0; +} + +/* value = 'null' | 'true' | 'false' | number | string | array | object */ +static int parse_value(struct frozen *f) { + int ch = cur(f); + + switch (ch) { + case '"': + TRY(parse_string(f)); + break; + case '{': + TRY(parse_object(f)); + break; + case '[': + TRY(parse_array(f)); + break; + case 'n': + TRY(expect(f, "null", 4, JSON_TYPE_NULL)); + break; + case 't': + TRY(expect(f, "true", 4, JSON_TYPE_TRUE)); + break; + case 'f': + TRY(expect(f, "false", 5, JSON_TYPE_FALSE)); + break; + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + TRY(parse_number(f)); + break; + default: + return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; + } + + return 0; +} + +/* key = identifier | string */ +static int parse_key(struct frozen *f) { + int ch = cur(f); +#if 0 + printf("%s [%.*s]\n", __func__, (int) (f->end - f->cur), f->cur); +#endif + if (is_alpha(ch)) { + TRY(parse_identifier(f)); + } else if (ch == '"') { + TRY(parse_string(f)); + } else { + return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; + } + return 0; +} + +/* pair = key ':' value */ +static int parse_pair(struct frozen *f) { + int current_path_len; + const char *tok; + skip_whitespaces(f); + tok = f->cur; + TRY(parse_key(f)); + { + f->cur_name = *tok == '"' ? tok + 1 : tok; + f->cur_name_len = *tok == '"' ? f->cur - tok - 2 : f->cur - tok; + current_path_len = append_to_path(f, f->cur_name, f->cur_name_len); + } + TRY(test_and_skip(f, ':')); + TRY(parse_value(f)); + truncate_path(f, current_path_len); + return 0; +} + +/* object = '{' pair { ',' pair } '}' */ +static int parse_object(struct frozen *f) { + TRY(test_and_skip(f, '{')); + { + CALL_BACK(f, JSON_TYPE_OBJECT_START, NULL, 0); + { + SET_STATE(f, f->cur - 1, ".", 1); + while (cur(f) != '}') { + TRY(parse_pair(f)); + if (cur(f) == ',') f->cur++; + } + TRY(test_and_skip(f, '}')); + truncate_path(f, fstate.path_len); + CALL_BACK(f, JSON_TYPE_OBJECT_END, fstate.ptr, f->cur - fstate.ptr); + } + } + return 0; +} + +static int doit(struct frozen *f) { + if (f->cur == 0 || f->end < f->cur) return JSON_STRING_INVALID; + if (f->end == f->cur) return JSON_STRING_INCOMPLETE; + return parse_value(f); +} + +static int json_encode_string(struct json_out *out, const char *p, size_t len) { + size_t i, cl, n = 0; + const char *hex_digits = "0123456789abcdef"; + const char *specials = "btnvfr"; + + for (i = 0; i < len; i++) { + unsigned char ch = ((unsigned char *) p)[i]; + if (ch == '"' || ch == '\\') { + n += out->printer(out, "\\", 1); + n += out->printer(out, p + i, 1); + } else if (ch >= '\b' && ch <= '\r') { + n += out->printer(out, "\\", 1); + n += out->printer(out, &specials[ch - '\b'], 1); + } else if (isprint(ch)) { + n += out->printer(out, p + i, 1); + } else if ((cl = get_utf8_char_len(ch)) == 1) { + n += out->printer(out, "\\u00", 4); + n += out->printer(out, &hex_digits[(ch >> 4) % 0xf], 1); + n += out->printer(out, &hex_digits[ch % 0xf], 1); + } else { + n += out->printer(out, p + i, cl); + i += cl - 1; + } + } + + return n; +} + +int json_printer_buf(struct json_out *out, const char *buf, size_t len) WEAK; +int json_printer_buf(struct json_out *out, const char *buf, size_t len) { + size_t avail = out->u.buf.size - out->u.buf.len; + size_t n = len < avail ? len : avail; + memcpy(out->u.buf.buf + out->u.buf.len, buf, n); + out->u.buf.len += n; + if (out->u.buf.size > 0) { + size_t idx = out->u.buf.len; + if (idx >= out->u.buf.size) idx = out->u.buf.size - 1; + out->u.buf.buf[idx] = '\0'; + } + return len; +} + +int json_printer_file(struct json_out *out, const char *buf, size_t len) WEAK; +int json_printer_file(struct json_out *out, const char *buf, size_t len) { + return fwrite(buf, 1, len, out->u.fp); +} + +static int b64idx(int c) { + if (c < 26) { + return c + 'A'; + } else if (c < 52) { + return c - 26 + 'a'; + } else if (c < 62) { + return c - 52 + '0'; + } else { + return c == 62 ? '+' : '/'; + } +} + +static int b64rev(int c) { + if (c >= 'A' && c <= 'Z') { + return c - 'A'; + } else if (c >= 'a' && c <= 'z') { + return c + 26 - 'a'; + } else if (c >= '0' && c <= '9') { + return c + 52 - '0'; + } else if (c == '+') { + return 62; + } else if (c == '/') { + return 63; + } else { + return 64; + } +} + +static uint8_t hexdec(const char *s) { +#define HEXTOI(x) (x >= '0' && x <= '9' ? x - '0' : x - 'W') + int a = tolower(*(const unsigned char *) s); + int b = tolower(*(const unsigned char *) (s + 1)); + return (HEXTOI(a) << 4) | HEXTOI(b); +} + +static int b64enc(struct json_out *out, const unsigned char *p, int n) { + char buf[4]; + int i, len = 0; + for (i = 0; i < n; i += 3) { + int a = p[i], b = i + 1 < n ? p[i + 1] : 0, c = i + 2 < n ? p[i + 2] : 0; + buf[0] = b64idx(a >> 2); + buf[1] = b64idx((a & 3) << 4 | (b >> 4)); + buf[2] = b64idx((b & 15) << 2 | (c >> 6)); + buf[3] = b64idx(c & 63); + if (i + 1 >= n) buf[2] = '='; + if (i + 2 >= n) buf[3] = '='; + len += out->printer(out, buf, sizeof(buf)); + } + return len; +} + +static int b64dec(const char *src, int n, char *dst) { + const char *end = src + n; + int len = 0; + while (src + 3 < end) { + int a = b64rev(src[0]), b = b64rev(src[1]), c = b64rev(src[2]), + d = b64rev(src[3]); + dst[len++] = (a << 2) | (b >> 4); + if (src[2] != '=') { + dst[len++] = (b << 4) | (c >> 2); + if (src[3] != '=') { + dst[len++] = (c << 6) | d; + } + } + src += 4; + } + return len; +} + +int json_vprintf(struct json_out *out, const char *fmt, va_list xap) WEAK; +int json_vprintf(struct json_out *out, const char *fmt, va_list xap) { + int len = 0; + const char *quote = "\"", *null = "null"; + va_list ap; + va_copy(ap, xap); + + while (*fmt != '\0') { + if (strchr(":, \r\n\t[]{}\"", *fmt) != NULL) { + len += out->printer(out, fmt, 1); + fmt++; + } else if (fmt[0] == '%') { + char buf[21]; + size_t skip = 2; + + if (fmt[1] == 'l' && fmt[2] == 'l' && (fmt[3] == 'd' || fmt[3] == 'u')) { + int64_t val = va_arg(ap, int64_t); + const char *fmt2 = fmt[3] == 'u' ? "%" UINT64_FMT : "%" INT64_FMT; + snprintf(buf, sizeof(buf), fmt2, val); + len += out->printer(out, buf, strlen(buf)); + skip += 2; + } else if (fmt[1] == 'z' && fmt[2] == 'u') { + size_t val = va_arg(ap, size_t); + snprintf(buf, sizeof(buf), "%" SIZE_T_FMT, val); + len += out->printer(out, buf, strlen(buf)); + skip += 1; + } else if (fmt[1] == 'M') { + json_printf_callback_t f = va_arg(ap, json_printf_callback_t); + len += f(out, &ap); + } else if (fmt[1] == 'B') { + int val = va_arg(ap, int); + const char *str = val ? "true" : "false"; + len += out->printer(out, str, strlen(str)); + } else if (fmt[1] == 'H') { + const char *hex = "0123456789abcdef"; + int i, n = va_arg(ap, int); + const unsigned char *p = va_arg(ap, const unsigned char *); + len += out->printer(out, quote, 1); + for (i = 0; i < n; i++) { + len += out->printer(out, &hex[(p[i] >> 4) & 0xf], 1); + len += out->printer(out, &hex[p[i] & 0xf], 1); + } + len += out->printer(out, quote, 1); + } else if (fmt[1] == 'V') { + const unsigned char *p = va_arg(ap, const unsigned char *); + int n = va_arg(ap, int); + len += out->printer(out, quote, 1); + len += b64enc(out, p, n); + len += out->printer(out, quote, 1); + } else if (fmt[1] == 'Q' || + (fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 'Q')) { + size_t l = 0; + const char *p; + + if (fmt[1] == '.') { + l = (size_t) va_arg(ap, int); + skip += 2; + } + p = va_arg(ap, char *); + + if (p == NULL) { + len += out->printer(out, null, 4); + } else { + if (fmt[1] == 'Q') { + l = strlen(p); + } + len += out->printer(out, quote, 1); + len += json_encode_string(out, p, l); + len += out->printer(out, quote, 1); + } + } else { + /* + * we delegate printing to the system printf. + * The goal here is to delegate all modifiers parsing to the system + * printf, as you can see below we still have to parse the format + * types. + * + * Currently, %s with strings longer than 20 chars will require + * double-buffering (an auxiliary buffer will be allocated from heap). + * TODO(dfrank): reimplement %s and %.*s in order to avoid that. + */ + + const char *end_of_format_specifier = "sdfFgGlhuI.*-0123456789"; + size_t n = strspn(fmt + 1, end_of_format_specifier); + char *pbuf = buf; + size_t need_len; + char fmt2[20]; + va_list sub_ap; + strncpy(fmt2, fmt, n + 1 > sizeof(fmt2) ? sizeof(fmt2) : n + 1); + fmt2[n + 1] = '\0'; + + va_copy(sub_ap, ap); + need_len = + vsnprintf(buf, sizeof(buf), fmt2, sub_ap) + 1 /* null-term */; + /* + * TODO(lsm): Fix windows & eCos code path here. Their vsnprintf + * implementation returns -1 on overflow rather needed size. + */ + if (need_len > sizeof(buf)) { + /* + * resulting string doesn't fit into a stack-allocated buffer `buf`, + * so we need to allocate a new buffer from heap and use it + */ + pbuf = (char *) malloc(need_len); + va_copy(sub_ap, ap); + vsnprintf(pbuf, need_len, fmt2, sub_ap); + } + + /* + * however we need to parse the type ourselves in order to advance + * the va_list by the correct amount; there is no portable way to + * inherit the advancement made by vprintf. + * 32-bit (linux or windows) passes va_list by value. + */ + if ((n + 1 == strlen("%" PRId64) && strcmp(fmt2, "%" PRId64) == 0) || + (n + 1 == strlen("%" PRIu64) && strcmp(fmt2, "%" PRIu64) == 0)) { + (void) va_arg(ap, int64_t); + skip += strlen(PRIu64) - 1; + } else if (strcmp(fmt2, "%.*s") == 0) { + (void) va_arg(ap, int); + (void) va_arg(ap, char *); + } else { + switch (fmt2[n]) { + case 'u': + case 'd': + (void) va_arg(ap, int); + break; + case 'g': + case 'f': + (void) va_arg(ap, double); + break; + case 'p': + (void) va_arg(ap, void *); + break; + default: + /* many types are promoted to int */ + (void) va_arg(ap, int); + } + } + + len += out->printer(out, pbuf, strlen(pbuf)); + skip = n + 1; + + /* If buffer was allocated from heap, free it */ + if (pbuf != buf) { + free(pbuf); + pbuf = NULL; + } + } + fmt += skip; + } else if (*fmt == '_' || is_alpha(*fmt)) { + len += out->printer(out, quote, 1); + while (*fmt == '_' || is_alpha(*fmt) || is_digit(*fmt)) { + len += out->printer(out, fmt, 1); + fmt++; + } + len += out->printer(out, quote, 1); + } else { + fmt++; + } + } + va_end(ap); + + return len; +} + +int json_printf(struct json_out *out, const char *fmt, ...) WEAK; +int json_printf(struct json_out *out, const char *fmt, ...) { + int n; + va_list ap; + va_start(ap, fmt); + n = json_vprintf(out, fmt, ap); + va_end(ap); + return n; +} + +int json_printf_array(struct json_out *out, va_list *ap) WEAK; +int json_printf_array(struct json_out *out, va_list *ap) { + int len = 0; + char *arr = va_arg(*ap, char *); + size_t i, arr_size = va_arg(*ap, size_t); + size_t elem_size = va_arg(*ap, size_t); + const char *fmt = va_arg(*ap, char *); + len += json_printf(out, "[", 1); + for (i = 0; arr != NULL && i < arr_size / elem_size; i++) { + union { + int64_t i; + double d; + } val; + memcpy(&val, arr + i * elem_size, + elem_size > sizeof(val) ? sizeof(val) : elem_size); + if (i > 0) len += json_printf(out, ", "); + if (strchr(fmt, 'f') != NULL) { + len += json_printf(out, fmt, val.d); + } else { + len += json_printf(out, fmt, val.i); + } + } + len += json_printf(out, "]", 1); + return len; +} + +#ifdef _WIN32 +int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap) WEAK; +int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap) { + int res = _vsnprintf(str, size, format, ap); + va_end(ap); + if (res >= size) { + str[size - 1] = '\0'; + } + return res; +} + +int cs_win_snprintf(char *str, size_t size, const char *format, ...) WEAK; +int cs_win_snprintf(char *str, size_t size, const char *format, ...) { + int res; + va_list ap; + va_start(ap, format); + res = vsnprintf(str, size, format, ap); + va_end(ap); + return res; +} +#endif /* _WIN32 */ + +int json_walk(const char *json_string, int json_string_length, + json_walk_callback_t callback, void *callback_data) WEAK; +int json_walk(const char *json_string, int json_string_length, + json_walk_callback_t callback, void *callback_data) { + struct frozen frozen; + + memset(&frozen, 0, sizeof(frozen)); + frozen.end = json_string + json_string_length; + frozen.cur = json_string; + frozen.callback_data = callback_data; + frozen.callback = callback; + + TRY(doit(&frozen)); + + return frozen.cur - json_string; +} + +struct scan_array_info { + char path[JSON_MAX_PATH_LEN]; + struct json_token *token; +}; + +static void json_scanf_array_elem_cb(void *callback_data, const char *name, + size_t name_len, const char *path, + const struct json_token *token) { + struct scan_array_info *info = (struct scan_array_info *) callback_data; + + (void) name; + (void) name_len; + + if (strcmp(path, info->path) == 0) { + *info->token = *token; + } +} + +int json_scanf_array_elem(const char *s, int len, const char *path, int idx, + struct json_token *token) WEAK; +int json_scanf_array_elem(const char *s, int len, const char *path, int idx, + struct json_token *token) { + struct scan_array_info info; + info.token = token; + memset(token, 0, sizeof(*token)); + snprintf(info.path, sizeof(info.path), "%s[%d]", path, idx); + json_walk(s, len, json_scanf_array_elem_cb, &info); + return token->len; +} + +struct json_scanf_info { + int num_conversions; + char *path; + const char *fmt; + void *target; + void *user_data; + int type; +}; + +int json_unescape(const char *src, int slen, char *dst, int dlen) WEAK; +int json_unescape(const char *src, int slen, char *dst, int dlen) { + char *send = (char *) src + slen, *dend = dst + dlen, *orig_dst = dst, *p; + const char *esc1 = "\"\\/bfnrt", *esc2 = "\"\\/\b\f\n\r\t"; + + while (src < send) { + if (*src == '\\') { + if (++src >= send) return JSON_STRING_INCOMPLETE; + if (*src == 'u') { + /* TODO(lsm): \uXXXX escapes drag utf8 lib... Do it at some stage */ + return JSON_STRING_INVALID; + } else if ((p = (char *) strchr(esc1, *src)) != NULL) { + if (dst < dend) *dst = esc2[p - esc1]; + } else { + return JSON_STRING_INVALID; + } + } else { + if (dst < dend) *dst = *src; + } + dst++; + src++; + } + + return dst - orig_dst; +} + +static void json_scanf_cb(void *callback_data, const char *name, + size_t name_len, const char *path, + const struct json_token *token) { + struct json_scanf_info *info = (struct json_scanf_info *) callback_data; + + (void) name; + (void) name_len; + + if (strcmp(path, info->path) != 0) { + /* It's not the path we're looking for, so, just ignore this callback */ + return; + } + + if (token->ptr == NULL) { + /* + * We're not interested here in the events for which we have no value; + * namely, JSON_TYPE_OBJECT_START and JSON_TYPE_ARRAY_START + */ + return; + } + + switch (info->type) { + case 'B': + info->num_conversions++; + *(int *) info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0); + break; + case 'M': { + union { + void *p; + json_scanner_t f; + } u = {info->target}; + info->num_conversions++; + u.f(token->ptr, token->len, info->user_data); + break; + } + case 'Q': { + char **dst = (char **) info->target; + int unescaped_len = json_unescape(token->ptr, token->len, NULL, 0); + if (unescaped_len >= 0 && + (*dst = (char *) malloc(unescaped_len + 1)) != NULL) { + info->num_conversions++; + json_unescape(token->ptr, token->len, *dst, unescaped_len); + (*dst)[unescaped_len] = '\0'; + } + break; + } + case 'H': { + char **dst = (char **) info->user_data; + int i, len = token->len / 2; + *(int *) info->target = len; + if ((*dst = (char *) malloc(len + 1)) != NULL) { + for (i = 0; i < len; i++) { + (*dst)[i] = hexdec(token->ptr + 2 * i); + } + (*dst)[len] = '\0'; + info->num_conversions++; + } + break; + } + case 'V': { + char **dst = (char **) info->target; + int len = token->len * 4 / 3 + 2; + if ((*dst = (char *) malloc(len + 1)) != NULL) { + int n = b64dec(token->ptr, token->len, *dst); + (*dst)[n] = '\0'; + *(int *) info->user_data = n; + info->num_conversions++; + } + break; + } + case 'T': + info->num_conversions++; + *(struct json_token *) info->target = *token; + break; + default: + info->num_conversions += sscanf(token->ptr, info->fmt, info->target); + break; + } +} + +int json_vscanf(const char *s, int len, const char *fmt, va_list ap) WEAK; +int json_vscanf(const char *s, int len, const char *fmt, va_list ap) { + char path[JSON_MAX_PATH_LEN] = "", fmtbuf[20]; + int i = 0; + char *p = NULL; + struct json_scanf_info info = {0, path, fmtbuf, NULL, NULL, 0}; + + while (fmt[i] != '\0') { + if (fmt[i] == '{') { + strcat(path, "."); + i++; + } else if (fmt[i] == '}') { + if ((p = strrchr(path, '.')) != NULL) *p = '\0'; + i++; + } else if (fmt[i] == '%') { + info.target = va_arg(ap, void *); + info.type = fmt[i + 1]; + switch (fmt[i + 1]) { + case 'M': + case 'V': + case 'H': + info.user_data = va_arg(ap, void *); + /* FALLTHROUGH */ + case 'B': + case 'Q': + case 'T': + i += 2; + break; + default: { + const char *delims = ", \t\r\n]}"; + int conv_len = strcspn(fmt + i + 1, delims) + 1; + snprintf(fmtbuf, sizeof(fmtbuf), "%.*s", conv_len, fmt + i); + i += conv_len; + i += strspn(fmt + i, delims); + break; + } + } + json_walk(s, len, json_scanf_cb, &info); + } else if (is_alpha(fmt[i]) || get_utf8_char_len(fmt[i]) > 1) { + const char *delims = ": \r\n\t"; + int key_len = strcspn(&fmt[i], delims); + if ((p = strrchr(path, '.')) != NULL) p[1] = '\0'; + sprintf(path + strlen(path), "%.*s", key_len, &fmt[i]); + i += key_len + strspn(fmt + i + key_len, delims); + } else { + i++; + } + } + return info.num_conversions; +} + +int json_scanf(const char *str, int len, const char *fmt, ...) WEAK; +int json_scanf(const char *str, int len, const char *fmt, ...) { + int result; + va_list ap; + va_start(ap, fmt); + result = json_vscanf(str, len, fmt, ap); + va_end(ap); + return result; +} diff --git a/src/frozen/frozen.h b/src/frozen/frozen.h new file mode 100644 index 0000000..6822d33 --- /dev/null +++ b/src/frozen/frozen.h @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2004-2013 Sergey Lyubka + * Copyright (c) 2013 Cesanta Software Limited + * All rights reserved + * + * This library is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this library under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this library under a commercial + * license, as set out in . + */ + +#ifndef CS_FROZEN_FROZEN_H_ +#define CS_FROZEN_FROZEN_H_ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include +#include +#include + +/* JSON token type */ +enum json_token_type { + JSON_TYPE_INVALID = 0, /* memsetting to 0 should create INVALID value */ + JSON_TYPE_STRING, + JSON_TYPE_NUMBER, + JSON_TYPE_TRUE, + JSON_TYPE_FALSE, + JSON_TYPE_NULL, + JSON_TYPE_OBJECT_START, + JSON_TYPE_OBJECT_END, + JSON_TYPE_ARRAY_START, + JSON_TYPE_ARRAY_END, + + JSON_TYPES_CNT +}; + +/* + * Structure containing token type and value. Used in `json_walk()` and + * `json_scanf()` with the format specifier `%T`. + */ +struct json_token { + const char *ptr; /* Points to the beginning of the value */ + int len; /* Value length */ + enum json_token_type type; /* Type of the token, possible values are above */ +}; + +#define JSON_INVALID_TOKEN \ + { 0, 0, JSON_TYPE_INVALID } + +/* Error codes */ +#define JSON_STRING_INVALID -1 +#define JSON_STRING_INCOMPLETE -2 + +/* + * Callback-based SAX-like API. + * + * Property name and length is given only if it's available: i.e. if current + * event is an object's property. In other cases, `name` is `NULL`. For + * example, name is never given: + * - For the first value in the JSON string; + * - For events JSON_TYPE_OBJECT_END and JSON_TYPE_ARRAY_END + * + * E.g. for the input `{ "foo": 123, "bar": [ 1, 2, { "baz": true } ] }`, + * the sequence of callback invocations will be as follows: + * + * - type: JSON_TYPE_OBJECT_START, name: NULL, path: "", value: NULL + * - type: JSON_TYPE_NUMBER, name: "foo", path: ".foo", value: "123" + * - type: JSON_TYPE_ARRAY_START, name: "bar", path: ".bar", value: NULL + * - type: JSON_TYPE_NUMBER, name: "0", path: ".bar[0]", value: "1" + * - type: JSON_TYPE_NUMBER, name: "1", path: ".bar[1]", value: "2" + * - type: JSON_TYPE_OBJECT_START, name: "2", path: ".bar[2]", value: NULL + * - type: JSON_TYPE_TRUE, name: "baz", path: ".bar[2].baz", value: "true" + * - type: JSON_TYPE_OBJECT_END, name: NULL, path: ".bar[2]", value: "{ \"baz\": + *true }" + * - type: JSON_TYPE_ARRAY_END, name: NULL, path: ".bar", value: "[ 1, 2, { + *\"baz\": true } ]" + * - type: JSON_TYPE_OBJECT_END, name: NULL, path: "", value: "{ \"foo\": 123, + *\"bar\": [ 1, 2, { \"baz\": true } ] }" + */ +typedef void (*json_walk_callback_t)(void *callback_data, const char *name, + size_t name_len, const char *path, + const struct json_token *token); + +/* + * Parse `json_string`, invoking `callback` in a way similar to SAX parsers; + * see `json_walk_callback_t`. + */ +int json_walk(const char *json_string, int json_string_length, + json_walk_callback_t callback, void *callback_data); + +/* + * JSON generation API. + * struct json_out abstracts output, allowing alternative printing plugins. + */ +struct json_out { + int (*printer)(struct json_out *, const char *str, size_t len); + union { + struct { + char *buf; + size_t size; + size_t len; + } buf; + void *data; + FILE *fp; + } u; +}; + +extern int json_printer_buf(struct json_out *, const char *, size_t); +extern int json_printer_file(struct json_out *, const char *, size_t); + +#define JSON_OUT_BUF(buf, len) \ + { \ + json_printer_buf, { \ + { buf, len, 0 } \ + } \ + } +#define JSON_OUT_FILE(fp) \ + { \ + json_printer_file, { \ + { (void *) fp, 0, 0 } \ + } \ + } + +typedef int (*json_printf_callback_t)(struct json_out *, va_list *ap); + +/* + * Generate formatted output into a given sting buffer. + * This is a superset of printf() function, with extra format specifiers: + * - `%B` print json boolean, `true` or `false`. Accepts an `int`. + * - `%Q` print quoted escaped string or `null`. Accepts a `const char *`. + * - `%.*Q` same as `%Q`, but with length. Accepts `int`, `const char *` + * - `%V` print quoted base64-encoded string. Accepts a `const char *`, `int`. + * - `%H` print quoted hex-encoded string. Accepts a `int`, `const char *`. + * - `%M` invokes a json_printf_callback_t function. That callback function + * can consume more parameters. + * + * Return number of bytes printed. If the return value is bigger then the + * supplied buffer, that is an indicator of overflow. In the overflow case, + * overflown bytes are not printed. + */ +int json_printf(struct json_out *, const char *fmt, ...); +int json_vprintf(struct json_out *, const char *fmt, va_list ap); + +/* + * Helper %M callback that prints contiguous C arrays. + * Consumes void *array_ptr, size_t array_size, size_t elem_size, char *fmt + * Return number of bytes printed. + */ +int json_printf_array(struct json_out *, va_list *ap); + +/* + * Scan JSON string `str`, performing scanf-like conversions according to `fmt`. + * This is a `scanf()` - like function, with following differences: + * + * 1. Object keys in the format string may be not quoted, e.g. "{key: %d}" + * 2. Order of keys in an object is irrelevant. + * 3. Several extra format specifiers are supported: + * - %B: consumes `int *`, expects boolean `true` or `false`. + * - %Q: consumes `char **`, expects quoted, JSON-encoded string. Scanned + * string is malloc-ed, caller must free() the string. + * - %V: consumes `char **`, `int *`. Expects base64-encoded string. + * Result string is base64-decoded, malloced and NUL-terminated. + * The length of result string is stored in `int *` placeholder. + * Caller must free() the result. + * - %H: consumes `int *`, `char **`. + * Expects a hex-encoded string, e.g. "fa014f". + * Result string is hex-decoded, malloced and NUL-terminated. + * The length of the result string is stored in `int *` placeholder. + * Caller must free() the result. + * - %M: consumes custom scanning function pointer and + * `void *user_data` parameter - see json_scanner_t definition. + * - %T: consumes `struct json_token *`, fills it out with matched token. + * + * Return number of elements successfully scanned & converted. + * Negative number means scan error. + */ +int json_scanf(const char *str, int str_len, const char *fmt, ...); +int json_vscanf(const char *str, int str_len, const char *fmt, va_list ap); + +/* json_scanf's %M handler */ +typedef void (*json_scanner_t)(const char *str, int len, void *user_data); + +/* + * Helper function to scan array item with given path and index. + * Fills `token` with the matched JSON token. + * Return 0 if no array element found, otherwise non-0. + */ +int json_scanf_array_elem(const char *s, int len, const char *path, int index, + struct json_token *token); + +/* + * Unescape JSON-encoded string src,slen into dst, dlen. + * src and dst may overlap. + * If destination buffer is too small (or zero-length), result string is not + * written but the length is counted nevertheless (similar to snprintf). + * Return the length of unescaped string in bytes. + */ +int json_unescape(const char *src, int slen, char *dst, int dlen); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CS_FROZEN_FROZEN_H_ */ From 3df104e72d63f2ec53de983aa5ce5824fdb03af8 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 14 Jun 2018 23:57:57 +0100 Subject: [PATCH 12/32] First code for new jsonrpc api --- CMakeLists.txt | 1 + htdocs/js/mpd.js | 3 ++- src/mpd_client.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ src/mympd.c | 12 +++++++++++ 4 files changed, 68 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4672deb..b4a0985 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ set(SOURCES src/mpd_client.c src/mongoose/mongoose.c src/frozen/frozen.c + src/json_encode.c ) add_executable(mympd ${SOURCES}) diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index 2828e90..8f66ea2 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -856,7 +856,8 @@ function parseListDBtags(obj) { ' '+ ''; $('#BrowseDatabaseAlbumCards').append(card); - $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", data: "MPD_API_GET_ARTISTALBUMTITLES," + obj.searchstr + ","+obj.data[item].value}).done(function( data ) { + $.ajax({url: "/jsonrpc", contentType:"application/json", method: "POST", + data: JSON.stringify({"cmd":"MPD_API_GET_ARTISTALBUMTITLES", "data": { "albumartist": obj.searchstr, "album": obj.data[item].value}})}).done(function( data ) { var obj = JSON.parse(data); parseListTitles(obj); }); diff --git a/src/mpd_client.c b/src/mpd_client.c index e5510aa..d015922 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -60,6 +60,59 @@ static inline enum mpd_cmd_ids get_cmd_id(const char *cmd) return -1; } +void callback_mympd_jsonrpc(struct mg_connection *nc, const struct mg_str msg) +{ + size_t n = 0; + char *cmd; + int int_buf; + int je; + float float_buf; + char *p_charbuf1; + char *p_charbuf2; + char *p_charbuf3; + + #ifdef DEBUG + fprintf(stdout,"Got request: %s\n",msg.p); + #endif + + json_scanf(msg.p, msg.len, "{cmd:%Q}", &cmd); + enum mpd_cmd_ids cmd_id = get_cmd_id(cmd); + + if(cmd_id == -1) + return; + + switch(cmd_id) { + case MPD_API_GET_ARTISTALBUMTITLES: + je = json_scanf(msg.p, msg.len, "{ data: { albumartist:%Q, album:%Q } }", &p_charbuf1, &p_charbuf2); + if (je == 2) + n = mympd_put_songs_in_album(mpd.buf, p_charbuf1, p_charbuf2); + free(p_charbuf1); + free(p_charbuf2); + break; + } + free(cmd); + + if(mpd.conn_state == MPD_CONNECTED && mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS) + { + #ifdef DEBUG + fprintf(stdout,"Error: %s\n",mpd_connection_get_error_message(mpd.conn)); + #endif + n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\", \"data\": \"%s\"}", + mpd_connection_get_error_message(mpd.conn)); + + /* Try to recover error */ + if (!mpd_connection_clear_error(mpd.conn)) + mpd.conn_state = MPD_FAILURE; + } + + if(n > 0) { + #ifdef DEBUG + fprintf(stdout,"Send http response:\n %s\n",mpd.buf); + #endif + mg_send_http_chunk(nc, mpd.buf, n); + } +} + void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { enum mpd_cmd_ids cmd_id = get_cmd_id(msg.p); diff --git a/src/mympd.c b/src/mympd.c index f585907..72deb5b 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -52,6 +52,15 @@ static void handle_api(struct mg_connection *nc, struct http_message *hm) { mg_send_http_chunk(nc, "", 0); /* Send empty chunk, the end of response */ } +static void handle_jsonrpc(struct mg_connection *nc, struct http_message *hm) { + mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"); + char buf[1000] = {0}; + memcpy(buf, hm->body.p,sizeof(buf) - 1 < hm->body.len ? sizeof(buf) - 1 : hm->body.len); + struct mg_str d = {buf, strlen(buf)}; + callback_mympd_jsonrpc(nc, d); + mg_send_http_chunk(nc, "", 0); /* Send empty chunk, the end of response */ +} + static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { switch(ev) { case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { @@ -80,6 +89,9 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { if (mg_vcmp(&hm->uri, "/api") == 0) { handle_api(nc, hm); } + else if (mg_vcmp(&hm->uri, "/jsonrpc") == 0) { + handle_jsonrpc(nc, hm); + } else { mg_serve_http(nc, hm, s_http_server_opts); } From 4dcf6137081625d87ffdaedd0a3e9695cd15761f Mon Sep 17 00:00:00 2001 From: jcorporation Date: Sun, 17 Jun 2018 23:42:22 +0100 Subject: [PATCH 13/32] Output json with frozen --- CMakeLists.txt | 16 ++---- README.md | 17 +----- src/mpd_client.c | 141 +++++++++++++++++++++++------------------------ src/mpd_client.h | 1 + src/mympd.c | 4 +- 5 files changed, 79 insertions(+), 100 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b4a0985..1b38331 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,13 +7,13 @@ set(CPACK_PACKAGE_VERSION_MINOR "0") set(CPACK_PACKAGE_VERSION_PATCH "0") if(CMAKE_BUILD_TYPE MATCHES RELEASE) set(ASSETS_PATH "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}/htdocs") + set(DEBUG "OFF") else() set(ASSETS_PATH "${PROJECT_SOURCE_DIR}/htdocs") - set(DEBUG ON) + set(DEBUG "ON") endif() option(WITH_IPV6 "enable IPv6 support" ON) -option(WITH_SSL "enable SSL support" ON) find_package(LibMPDClient REQUIRED) find_package(Threads REQUIRED) @@ -23,17 +23,11 @@ include_directories(${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR} ${LIBMPDCLIENT_I include(CheckCSourceCompiles) -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -pedantic") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -pedantic -D_FORTIFY_SOURCE=2 -fstack-protector -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=shift -fsanitize=integer-divide-by-zero -fsanitize=unreachable -fsanitize=vla-bound -fsanitize=null -fsanitize=return -fsanitize=signed-integer-overflow -fsanitize=bounds -fsanitize=bounds-strict -fsanitize=alignment -fsanitize=object-size -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fsanitize=nonnull-attribute -fsanitize=returns-nonnull-attribute -fsanitize=bool -fsanitize=enum -fsanitize=vptr -static-libasan") -set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -pedantic ") +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -D_FORTIFY_SOURCE=2 -fstack-protector -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=shift -fsanitize=integer-divide-by-zero -fsanitize=unreachable -fsanitize=vla-bound -fsanitize=null -fsanitize=return -fsanitize=signed-integer-overflow -fsanitize=bounds -fsanitize=bounds-strict -fsanitize=alignment -fsanitize=object-size -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fsanitize=nonnull-attribute -fsanitize=returns-nonnull-attribute -fsanitize=bool -fsanitize=enum -fsanitize=vptr -static-libasan") if(WITH_IPV6) set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS NS_ENABLE_IPV6) endif() -if(WITH_SSL) - find_package(OpenSSL REQUIRED) - include_directories(${OPENSSL_INCLUDE_DIR}) - set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS NS_ENABLE_SSL) -endif() file(GLOB RESOURCES RELATIVE ${PROJECT_SOURCE_DIR} @@ -54,7 +48,7 @@ set(SOURCES ) add_executable(mympd ${SOURCES}) -target_link_libraries(mympd ${LIBMPDCLIENT_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${OPENSSL_LIBRARIES}) +target_link_libraries(mympd ${LIBMPDCLIENT_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) install(TARGETS mympd DESTINATION bin) install(FILES mympd.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1) diff --git a/README.md b/README.md index 53df1db..1e43c5a 100644 --- a/README.md +++ b/README.md @@ -27,12 +27,11 @@ Dependencies ------------ - libmpdclient 2: http://www.musicpd.org/libs/libmpdclient/ - cmake 2.6: http://cmake.org/ - - OpenSSL: https://www.openssl.org/ Unix Build Instructions ----------------------- -1. install dependencies. cmake, libmpdclient (dev), and OpenSSL (dev) are available from all major distributions. +1. install dependencies. cmake and libmpdclient (dev) are available from all major distributions. 2. create build directory ```cd /path/to/src; mkdir build; cd build``` 3. create makefile ```cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_BUILD_TYPE=RELEASE``` 4. build ```make``` @@ -56,20 +55,6 @@ Usage: ./mympd [OPTION]... --help this help ``` -SSL Support ------------ -To run myMPD with SSL support: - -- create a certificate (key and cert in the same file), example: -``` -# openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 1000 -nodes -sha256 -# cat key.pem cert.pem > ssl.pem -``` -- tell myMPD to use a webport using SSL and where to find the certificate: -``` -# ./mympd -w "ssl://8081:/path/to/ssl.pem" -``` - Copyright --------- ympd: 2013-2014 diff --git a/src/mpd_client.c b/src/mpd_client.c index d015922..5c1dd49 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -754,9 +754,10 @@ int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsi { struct mpd_status *status; const struct mpd_audio_format *audioformat; - struct mpd_output *out; - char *cur = buffer; - const char *end = buffer + MAX_SIZE; + struct mpd_output *output; + int len; + int nr; + struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); status = mpd_run_status(mpd.conn); if (!status) { @@ -767,79 +768,74 @@ int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsi if (status) { audioformat = mpd_status_get_audio_format(status); } - - cur += json_emit_raw_str(cur, end - cur, "{\"type\": \"state\", \"data\":{\"state\":"); - cur += json_emit_int(cur, end - cur, mpd_status_get_state(status)); - cur += json_emit_raw_str(cur, end - cur, ",\"volume\":"); - cur += json_emit_int(cur, end - cur, mpd_status_get_volume(status)); - cur += json_emit_raw_str(cur, end - cur, ",\"songpos\":"); - cur += json_emit_int(cur, end - cur, mpd_status_get_song_pos(status)); - cur += json_emit_raw_str(cur, end - cur, ",\"elapsedTime\":"); - cur += json_emit_int(cur, end - cur, mpd_status_get_elapsed_time(status)); - cur += json_emit_raw_str(cur, end - cur, ",\"totalTime\":"); - cur += json_emit_int(cur, end - cur, mpd_status_get_total_time(status)); - cur += json_emit_raw_str(cur, end - cur, ",\"currentsongid\":"); - cur += json_emit_int(cur, end - cur, mpd_status_get_song_id(status)); - cur += json_emit_raw_str(cur, end - cur, ",\"kbitrate\":"); - cur += json_emit_int(cur, end - cur, mpd_status_get_kbit_rate(status)); - cur += json_emit_raw_str(cur, end - cur, ",\"audioformat\": {"); - cur += json_emit_raw_str(cur, end - cur, "\"sample_rate\":"); - cur += json_emit_int(cur, end - cur, audioformat ? audioformat->sample_rate : 0); - cur += json_emit_raw_str(cur, end - cur, ",\"bits\":"); - cur += json_emit_int(cur, end - cur, audioformat ? audioformat->bits : 0); - cur += json_emit_raw_str(cur, end - cur, ",\"channels\":"); - cur += json_emit_int(cur, end - cur, audioformat ? audioformat->channels : 0); - cur += json_emit_raw_str(cur, end - cur, "},\"queue_length\":"); - cur += json_emit_int(cur, end - cur, mpd_status_get_queue_length(status)); - cur += json_emit_raw_str(cur, end - cur, ",\"nextsongpos\":"); - cur += json_emit_int(cur, end - cur, mpd_status_get_next_song_pos(status)); - cur += json_emit_raw_str(cur, end - cur, ",\"nextsongid\":"); - cur += json_emit_int(cur, end - cur, mpd_status_get_next_song_id(status)); - cur += json_emit_raw_str(cur, end - cur, ",\"queue_version\":"); - cur += json_emit_int(cur, end - cur, mpd_status_get_queue_version(status)); - cur += json_emit_raw_str(cur, end - cur, ",\"outputs\": {"); + + len = json_printf(&out,"{type:state, data:{" + "state:%d, volume:%d, songpos: %d, elapsedTime: %d, " + "totalTime:%d, currentsongid: %d, kbitrate: %d, " + "audioformat: { sample_rate: %d, bits: %d, channels: %d}, " + "queue_length: %d, nextsongpos: %d, nextsongid: %d, " + "queue_version: %d", + mpd_status_get_state(status), + mpd_status_get_volume(status), + mpd_status_get_song_pos(status), + mpd_status_get_elapsed_time(status), + mpd_status_get_total_time(status), + mpd_status_get_song_id(status), + mpd_status_get_kbit_rate(status), + audioformat ? audioformat->sample_rate : 0, + audioformat ? audioformat->bits : 0, + audioformat ? audioformat->channels : 0, + mpd_status_get_queue_length(status), + mpd_status_get_next_song_pos(status), + mpd_status_get_next_song_id(status), + mpd_status_get_queue_version(status) + ); + + len += json_printf(&out, ",outputs: {"); mpd_send_outputs(mpd.conn); - while ((out = mpd_recv_output(mpd.conn)) != NULL) { - cur += json_emit_raw_str(cur, end - cur, "\""); - cur += json_emit_int(cur, end - cur, mpd_output_get_id(out)); - cur += json_emit_raw_str(cur, end - cur, "\":"); - cur += json_emit_int(cur, end - cur, mpd_output_get_enabled(out)); - cur += json_emit_raw_str(cur, end - cur, ","); - mpd_output_free(out); + nr=0; + while ((output = mpd_recv_output(mpd.conn)) != NULL) { + if (nr++) len += json_printf(&out, ","); + len += json_printf(&out, "\"%d\":%d", + mpd_output_get_id(output), + mpd_output_get_enabled(output) + ); + mpd_output_free(output); } if (!mpd_response_finish(mpd.conn)) { fprintf(stderr, "MPD outputs: %s\n", mpd_connection_get_error_message(mpd.conn)); mpd_connection_clear_error(mpd.conn); } - cur --; - cur += json_emit_raw_str(cur, end - cur, "}}}"); + len += json_printf(&out, "}}}"); *current_song_id = mpd_status_get_song_id(status); *next_song_id = mpd_status_get_next_song_id(status); *queue_version = mpd_status_get_queue_version(status); mpd_status_free(status); - return cur - buffer; + + if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); + return len; } int mympd_put_welcome(char *buffer) { int len; - len = snprintf(buffer, MAX_SIZE, - "{\"type\":\"welcome\", \"data\":{" - "\"version\":\"%s\"}}", - MYMPD_VERSION - ); + struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); + len = json_printf(&out, "{type: %Q, data: { version: %Q}}", "welcome", MYMPD_VERSION); + + if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); return len; } int mympd_put_settings(char *buffer) { struct mpd_status *status; - int len; char *replaygain; + int len; + struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); status = mpd_run_status(mpd.conn); if (!status) { @@ -855,11 +851,11 @@ int mympd_put_settings(char *buffer) mpd_return_pair(mpd.conn, pair); } - len = snprintf(buffer, MAX_SIZE, - "{\"type\":\"settings\", \"data\":{" - "\"repeat\":%d, \"single\":%d, \"crossfade\":%d, \"consume\":%d, \"random\":%d, " - "\"mixrampdb\": %f, \"mixrampdelay\": %f, \"mpdhost\" : \"%s\", \"mpdport\": \"%d\", \"passwort_set\": %s, " - "\"streamport\": \"%d\",\"coverimage\": \"%s\", \"max_elements_per_page\": %d, \"replaygain\": \"%s\"" + len = json_printf(&out, + "{type:settings, data:{" + "repeat:%d, single:%d, crossfade:%d, consume:%d, random:%d, " + "mixrampdb: %f, mixrampdelay: %f, mpdhost : %Q, mpdport: %d, passwort_set: %B, " + "streamport: %d, coverimage: %Q, max_elements_per_page: %d, replaygain: %Q" "}}", mpd_status_get_repeat(status), mpd_status_get_single(status), @@ -875,37 +871,40 @@ int mympd_put_settings(char *buffer) replaygain ); mpd_status_free(status); + + if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); return len; } int mympd_put_outputnames(char *buffer) { - char *cur = buffer; - const char *end = buffer + MAX_SIZE; - struct mpd_output *out; + struct mpd_output *output; + int len; + int nr; + struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); + + len = json_printf(&out,"{type: outputnames, data:{"); - cur += json_emit_raw_str(cur, end - cur, "{\"type\": \"outputnames\", \"data\":{"); - mpd_send_outputs(mpd.conn); - - while ((out = mpd_recv_output(mpd.conn)) != NULL) { - cur += json_emit_raw_str(cur, end - cur, "\""); - cur += json_emit_int(cur, end - cur, mpd_output_get_id(out)); - cur += json_emit_raw_str(cur, end - cur, "\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_output_get_name(out)); - cur += json_emit_raw_str(cur, end - cur, ","); - mpd_output_free(out); + nr=0; + while ((output = mpd_recv_output(mpd.conn)) != NULL) { + if (nr++) len += json_printf(&out, ","); + len += json_printf(&out,"\"%d\":%Q", + mpd_output_get_id(output), + mpd_output_get_name(output) + ); + mpd_output_free(output); } if (!mpd_response_finish(mpd.conn)) { fprintf(stderr, "MPD outputs: %s\n", mpd_connection_get_error_message(mpd.conn)); mpd_connection_clear_error(mpd.conn); } - cur --; - cur += json_emit_raw_str(cur, end - cur, "}}"); + len += json_printf(&out,"}}"); - return cur - buffer; + if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); + return len; } int mympd_put_current_song(char *buffer) diff --git a/src/mpd_client.h b/src/mpd_client.h index de1db40..bd2694d 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -132,6 +132,7 @@ struct t_mpd_client_session { }; void mympd_poll(struct mg_mgr *s); +void callback_mympd_jsonrpc(struct mg_connection *nc, const struct mg_str msg); void callback_mympd(struct mg_connection *nc, const struct mg_str msg); int mympd_close_handler(struct mg_connection *c); int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsigned *queue_version); diff --git a/src/mympd.c b/src/mympd.c index 72deb5b..27fb3ee 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -142,7 +142,7 @@ int main(int argc, char **argv) {0, 0, 0, 0 } }; - while((n = getopt_long(argc, argv, "D:h:p:w:u:vm:s:i:", + while((n = getopt_long(argc, argv, "D:h:p:w:u:vm:s:i:c:", long_options, &option_index)) != -1) { switch (n) { case 'D': @@ -203,7 +203,7 @@ int main(int argc, char **argv) nc = mg_bind(&mgr, webport, ev_handler); if (nc == NULL) { - printf("myMPD can't bind to port %s\n", webport); + fprintf(stderr, "Error starting server on port %s\n", webport); return EXIT_FAILURE; } From e6fa1206caaa2ebb811cc69b768537d64044a03d Mon Sep 17 00:00:00 2001 From: jcorporation Date: Mon, 18 Jun 2018 17:53:30 +0100 Subject: [PATCH 14/32] Replaced json_encode with frozen --- CMakeLists.txt | 1 - src/json_encode.c | 58 ------ src/json_encode.h | 27 --- src/mpd_client.c | 438 ++++++++++++++++++++-------------------------- src/mpd_client.h | 7 +- 5 files changed, 190 insertions(+), 341 deletions(-) delete mode 100644 src/json_encode.c delete mode 100644 src/json_encode.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b38331..b3e434a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,6 @@ set(SOURCES src/mpd_client.c src/mongoose/mongoose.c src/frozen/frozen.c - src/json_encode.c ) add_executable(mympd ${SOURCES}) diff --git a/src/json_encode.c b/src/json_encode.c deleted file mode 100644 index 4e1b550..0000000 --- a/src/json_encode.c +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2004-2013 Sergey Lyubka -// Copyright (c) 2013 Cesanta Software Limited -// All rights reserved -// -// This library is dual-licensed: you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. For the terms of this -// license, see . -// -// You are free to use this library under the terms of the GNU General -// Public License, but WITHOUT ANY WARRANTY; without even the implied -// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU General Public License for more details. -// -// Alternatively, you can license this library under a commercial -// license, as set out in . - -// json encoder 'frozen' from https://github.com/cesanta/frozen - -#include - -#include "json_encode.h" - -int json_emit_int(char *buf, int buf_len, long int value) { - return buf_len <= 0 ? 0 : snprintf(buf, buf_len, "%ld", value); -} - -int json_emit_double(char *buf, int buf_len, double value) { - return buf_len <= 0 ? 0 : snprintf(buf, buf_len, "%g", value); -} - -int json_emit_quoted_str(char *buf, int buf_len, const char *str) { - int i = 0, j = 0, ch; - -#define EMIT(x) do { if (j < buf_len) buf[j++] = x; } while (0) - - EMIT('"'); - while ((ch = str[i++]) != '\0' && j < buf_len) { - switch (ch) { - case '"': EMIT('\\'); EMIT('"'); break; - case '\\': EMIT('\\'); EMIT('\\'); break; - case '\b': EMIT('\\'); EMIT('b'); break; - case '\f': EMIT('\\'); EMIT('f'); break; - case '\n': EMIT('\\'); EMIT('n'); break; - case '\r': EMIT('\\'); EMIT('r'); break; - case '\t': EMIT('\\'); EMIT('t'); break; - default: EMIT(ch); - } - } - EMIT('"'); - EMIT(0); - - return j == 0 ? 0 : j - 1; -} - -int json_emit_raw_str(char *buf, int buf_len, const char *str) { - return buf_len <= 0 ? 0 : snprintf(buf, buf_len, "%s", str); -} \ No newline at end of file diff --git a/src/json_encode.h b/src/json_encode.h deleted file mode 100644 index a0bc7c8..0000000 --- a/src/json_encode.h +++ /dev/null @@ -1,27 +0,0 @@ -/* ympd - (c) 2013-2014 Andrew Karpow - This project's homepage is: http://www.ympd.org - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#ifndef __JSON_ENCODE_H__ -#define __JSON_ENCODE_H__ - -int json_emit_int(char *buf, int buf_len, long int value); -int json_emit_double(char *buf, int buf_len, double value); -int json_emit_quoted_str(char *buf, int buf_len, const char *str); -int json_emit_raw_str(char *buf, int buf_len, const char *str); - -#endif \ No newline at end of file diff --git a/src/mpd_client.c b/src/mpd_client.c index 5c1dd49..2b329ed 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -34,7 +34,6 @@ #include "mpd_client.h" #include "config.h" #include "frozen/frozen.h" -#include "json_encode.h" /* forward declaration */ static int mympd_notify_callback(struct mg_connection *c, const char *param); @@ -909,107 +908,96 @@ int mympd_put_outputnames(char *buffer) int mympd_put_current_song(char *buffer) { - char *cur = buffer; - const char *end = buffer + MAX_SIZE; struct mpd_song *song; + int len; + struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); song = mpd_run_current_song(mpd.conn); if(song == NULL) return 0; - cur += json_emit_raw_str(cur, end - cur, "{\"type\": \"song_change\", \"data\":{\"pos\":"); - cur += json_emit_int(cur, end - cur, mpd_song_get_pos(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); - cur += json_emit_quoted_str(cur, end - cur, mympd_get_title(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"artist\":"); - cur += json_emit_quoted_str(cur, end - cur, mympd_get_artist(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); - cur += json_emit_quoted_str(cur, end - cur, mympd_get_album(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"uri\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"currentsongid\":"); - cur += json_emit_int(cur, end - cur, mpd.song_id); - cur += json_emit_raw_str(cur, end - cur, "}}"); + len = json_printf(&out,"{type: song_change, data: { pos: %d, title: %Q, " + "artist: %Q, album: %Q, uri: %Q, currentsongid: %d, albumartist: %Q, " + "duration: %d }}", + mpd_song_get_pos(song), + mympd_get_title(song), + mympd_get_artist(song), + mympd_get_album(song), + mpd_song_get_uri(song), + mpd.song_id, + mympd_get_album_artist(song), + mpd_song_get_duration(song) + ); mpd_song_free(song); mpd_response_finish(mpd.conn); - return cur - buffer; + if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); + return len; } int mympd_put_queue(char *buffer, unsigned int offset) { - char *cur = buffer; - const char *end = buffer + MAX_SIZE; struct mpd_entity *entity; unsigned long totalTime = 0; unsigned long entity_count = 0; unsigned long entities_returned = 0; + int len; + struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); if (!mpd_send_list_queue_range_meta(mpd.conn, 0, -1)) RETURN_ERROR_AND_RECOVER("mpd_send_list_queue_meta"); - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"queue\",\"data\":[ "); + len = json_printf(&out, "{type:queue, data :[ "); - while((entity = mpd_recv_entity(mpd.conn)) != NULL) { + while ((entity = mpd_recv_entity(mpd.conn)) != NULL) { const struct mpd_song *song; unsigned int drtn; - - if(mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) { + if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) { song = mpd_entity_get_song(entity); drtn = mpd_song_get_duration(song); totalTime += drtn; entity_count ++; - if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { - entities_returned ++; - cur += json_emit_raw_str(cur, end - cur, "{\"id\":"); - cur += json_emit_int(cur, end - cur, mpd_song_get_id(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"pos\":"); - cur += json_emit_int(cur, end - cur, mpd_song_get_pos(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"duration\":"); - cur += json_emit_int(cur, end - cur, mpd_song_get_duration(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"artist\":"); - cur += json_emit_quoted_str(cur, end - cur, mympd_get_artist(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); - cur += json_emit_quoted_str(cur, end - cur, mympd_get_album(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); - cur += json_emit_quoted_str(cur, end - cur, mympd_get_title(song)); - cur += json_emit_raw_str(cur, end - cur, "},"); + if (entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { + if (entities_returned ++) len += json_printf(&out,","); + len += json_printf(&out, "{ id: %d, pos: %d, duration: %d, artist: %Q, album: %Q, title: %Q }", + mpd_song_get_id(song), + mpd_song_get_pos(song), + mpd_song_get_duration(song), + mympd_get_artist(song), + mympd_get_album(song), + mympd_get_title(song) + ); } - } mpd_entity_free(entity); } - /* remove last ',' */ - cur--; - - cur += json_emit_raw_str(cur, end - cur, "],\"totalTime\":"); - cur += json_emit_int(cur, end - cur, totalTime); - cur += json_emit_raw_str(cur, end - cur, ",\"totalEntities\":"); - cur += json_emit_int(cur, end - cur, entity_count); - cur += json_emit_raw_str(cur, end - cur, ",\"offset\":"); - cur += json_emit_int(cur, end - cur, offset); - cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); - cur += json_emit_int(cur, end - cur, entities_returned); - cur += json_emit_raw_str(cur, end - cur, "}"); - return cur - buffer; + len += json_printf(&out, "],totalTime: %d, totalEntities: %d, offset: %d, returnedEntities: %d }", + totalTime, + entity_count, + offset, + entities_returned + ); + + if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); + return len; } int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter) { - char *cur = buffer; - const char *end = buffer + MAX_SIZE; struct mpd_entity *entity; const struct mpd_playlist *pl; unsigned int entity_count = 0; unsigned int entities_returned = 0; const char *entityName; + int len; + struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); if (!mpd_send_list_meta(mpd.conn, path)) RETURN_ERROR_AND_RECOVER("mpd_send_list_meta"); - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"browse\",\"data\":[ "); + len = json_printf(&out, "{ type: browse, data: [ "); while((entity = mpd_recv_entity(mpd.conn)) != NULL) { const struct mpd_song *song; @@ -1026,18 +1014,14 @@ int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter if (strncmp(filter,"-",1) == 0 || strncasecmp(filter,entityName,1) == 0 || ( strncmp(filter,"0",1) == 0 && isalpha(*entityName) == 0 ) ) { - entities_returned ++; - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"song\",\"uri\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); - cur += json_emit_quoted_str(cur, end - cur, mympd_get_album(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"artist\":"); - cur += json_emit_quoted_str(cur, end - cur, mympd_get_artist(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"duration\":"); - cur += json_emit_int(cur, end - cur, mpd_song_get_duration(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); - cur += json_emit_quoted_str(cur, end - cur, entityName); - cur += json_emit_raw_str(cur, end - cur, "},"); + if (entities_returned ++) len += json_printf(&out,","); + len += json_printf(&out, "{type:song, uri: %Q, album: %Q, artist: %Q, duration: %d, title: %Q }", + mpd_song_get_uri(song), + mympd_get_album(song), + mympd_get_artist(song), + mpd_song_get_duration(song), + entityName + ); } else { entity_count --; } @@ -1050,15 +1034,15 @@ int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter if (dirName != NULL) { dirName ++; } else { - dirName = strdup(entityName); + dirName = strdup(entityName); } if (strncmp(filter,"-",1) == 0 || strncasecmp(filter,dirName,1) == 0 || ( strncmp(filter,"0",1) == 0 && isalpha(*dirName) == 0 ) ) { - entities_returned ++; - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"directory\",\"dir\":"); - cur += json_emit_quoted_str(cur, end - cur, entityName); - cur += json_emit_raw_str(cur, end - cur, "},"); + if (entities_returned ++) len += json_printf(&out,","); + len += json_printf(&out, "{type: directory, dir: %Q }", + entityName + ); } else { entity_count --; } @@ -1076,10 +1060,10 @@ int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter if (strncmp(filter,"-",1) == 0 || strncasecmp(filter,plName,1) == 0 || ( strncmp(filter,"0",1) == 0 && isalpha(*plName) == 0 ) ) { - entities_returned ++; - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"playlist\",\"plist\":"); - cur += json_emit_quoted_str(cur, end - cur, entityName ); - cur += json_emit_raw_str(cur, end - cur, "},"); + if (entities_returned ++) len += json_printf(&out,","); + len += json_printf(&out, "{ type: playlist, plist: %Q }", + entityName + ); } else { entity_count --; } @@ -1095,27 +1079,24 @@ int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter return 0; } - /* remove last ',' */ - cur--; - cur += json_emit_raw_str(cur, end - cur, "],\"totalEntities\":"); - cur += json_emit_int(cur, end - cur, entity_count); - cur += json_emit_raw_str(cur, end - cur, ",\"offset\":"); - cur += json_emit_int(cur, end - cur, offset); - cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); - cur += json_emit_int(cur, end - cur, entities_returned); - cur += json_emit_raw_str(cur, end - cur, ",\"filter\":"); - cur += json_emit_quoted_str(cur, end - cur, filter); - cur += json_emit_raw_str(cur, end - cur, "}"); - return cur - buffer; + len += json_printf(&out, "], totalEntities: %d, offset: %d, returnedEntities: %d, filter: %Q }", + entity_count, + offset, + entities_returned, + filter + ); + + if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); + return len; } int mympd_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char *mpdsearchtagtype, char *searchstr, char *filter) { - char *cur = buffer; - const char *end = buffer + MAX_SIZE; struct mpd_pair *pair; unsigned long entity_count = 0; unsigned long entities_returned = 0; + int len; + struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); if(mpd_search_db_tags(mpd.conn, mpd_tag_name_parse(mpdtagtype)) == false) RETURN_ERROR_AND_RECOVER("mpd_search_db_tags"); @@ -1128,19 +1109,18 @@ int mympd_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char * if(mpd_search_commit(mpd.conn) == false) RETURN_ERROR_AND_RECOVER("mpd_search_commit"); - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"listDBtags\",\"data\":[ "); + len = json_printf(&out, "{type: listDBtags, data: [ "); while((pair = mpd_recv_pair_tag(mpd.conn, mpd_tag_name_parse(mpdtagtype))) != NULL) { entity_count ++; if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { if (strncmp(filter,"-",1) == 0 || strncasecmp(filter,pair->value,1) == 0 || ( strncmp(filter,"0",1) == 0 && isalpha(*pair->value) == 0 ) ) { - entities_returned ++; - cur += json_emit_raw_str(cur, end - cur, "{\"type\":"); - cur += json_emit_quoted_str(cur, end - cur, mpdtagtype); - cur += json_emit_raw_str(cur, end - cur, ",\"value\":"); - cur += json_emit_quoted_str(cur, end - cur, pair->value); - cur += json_emit_raw_str(cur, end - cur, "},"); + if (entities_returned ++) len += json_printf(&out,", "); + len += json_printf(&out, "{type: %Q, value: %Q }", + mpdtagtype, + pair->value + ); } else { entity_count --; } @@ -1148,36 +1128,28 @@ int mympd_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char * mpd_return_pair(mpd.conn, pair); } - /* remove last ',' */ - cur--; + len += json_printf(&out, "], totalEntities: %d, offset: %d, returnedEntities: %d, " + "tagtype: %Q, searchtagtype: %Q, searchstr: %Q, filter: %Q }", + entity_count, + offset, + entities_returned, + mpdtagtype, + mpdsearchtagtype, + searchstr, + filter + ); - cur += json_emit_raw_str(cur, end - cur, "]"); - cur += json_emit_raw_str(cur, end - cur, ",\"totalEntities\":"); - cur += json_emit_int(cur, end - cur, entity_count); - cur += json_emit_raw_str(cur, end - cur, ",\"offset\":"); - cur += json_emit_int(cur, end - cur, offset); - cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); - cur += json_emit_int(cur, end - cur, entities_returned); - cur += json_emit_raw_str(cur, end - cur, ",\"tagtype\":"); - cur += json_emit_quoted_str(cur, end - cur, mpdtagtype); - cur += json_emit_raw_str(cur, end - cur, ",\"searchtagtype\":"); - cur += json_emit_quoted_str(cur, end - cur, mpdsearchtagtype); - cur += json_emit_raw_str(cur, end - cur, ",\"searchstr\":"); - cur += json_emit_quoted_str(cur, end - cur, searchstr); - cur += json_emit_raw_str(cur, end - cur, ",\"filter\":"); - cur += json_emit_quoted_str(cur, end - cur, filter); - cur += json_emit_raw_str(cur, end - cur, "}"); - - return cur - buffer; + if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); + return len; } int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album) { - char *cur = buffer; - const char *end = buffer + MAX_SIZE; struct mpd_song *song; unsigned long entity_count = 0; unsigned long entities_returned = 0; + int len; + struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); if(mpd_search_db_songs(mpd.conn, true) == false) { RETURN_ERROR_AND_RECOVER("mpd_search_db_songs"); @@ -1192,56 +1164,47 @@ int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album) if(mpd_search_commit(mpd.conn) == false) RETURN_ERROR_AND_RECOVER("mpd_search_commit"); else { - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"listTitles\",\"data\":[ "); + len = json_printf(&out, "{ type: listTitles, data: [ "); while((song = mpd_recv_song(mpd.conn)) != NULL) { entity_count ++; if(entity_count <= MAX_ELEMENTS_PER_PAGE) { - entities_returned ++; - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"song\""); - cur += json_emit_raw_str(cur, end - cur, ",\"uri\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"duration\":"); - cur += json_emit_int(cur, end - cur, mpd_song_get_duration(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); - cur += json_emit_quoted_str(cur, end - cur, mympd_get_title(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"track\":"); - cur += json_emit_quoted_str(cur, end - cur, mympd_get_track(song)); - cur += json_emit_raw_str(cur, end - cur, "},"); + if (entities_returned ++) len += json_printf(&out, ", "); + len += json_printf(&out, "{ type: song, uri: %Q, duration: %d, title: %Q, track: %Q }", + mpd_song_get_uri(song), + mpd_song_get_duration(song), + mympd_get_title(song), + mympd_get_track(song) + ); } mpd_song_free(song); } - /* remove last ',' */ - cur--; - - cur += json_emit_raw_str(cur, end - cur, "]"); - cur += json_emit_raw_str(cur, end - cur, ",\"totalEntities\":"); - cur += json_emit_int(cur, end - cur, entity_count); - cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); - cur += json_emit_int(cur, end - cur, entities_returned); - cur += json_emit_raw_str(cur, end - cur, ",\"albumartist\":"); - cur += json_emit_quoted_str(cur, end - cur, albumartist); - cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); - cur += json_emit_quoted_str(cur, end - cur, album); - cur += json_emit_raw_str(cur, end - cur, "}"); + len += json_printf(&out, "], totalEntities: %d, returnedEntities: %d, albumartist: %Q, album: %Q }", + entity_count, + entities_returned, + albumartist, + album + ); } - return cur - buffer; + + if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); + return len; } int mympd_put_playlists(char *buffer, unsigned int offset, char *filter) { - char *cur = buffer; - const char *end = buffer + MAX_SIZE; struct mpd_playlist *pl; unsigned int entity_count = 0; unsigned int entities_returned = 0; const char *plpath; + int len; + struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); if (!mpd_send_list_playlists(mpd.conn)) RETURN_ERROR_AND_RECOVER("mpd_send_lists_playlists"); - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"playlists\",\"data\":[ "); + len = json_printf(&out, "{ type: playlists, data: [ "); while((pl = mpd_recv_playlist(mpd.conn)) != NULL) { entity_count ++; @@ -1250,12 +1213,11 @@ int mympd_put_playlists(char *buffer, unsigned int offset, char *filter) if (strncmp(filter,"-",1) == 0 || strncasecmp(filter,plpath,1) == 0 || ( strncmp(filter,"0",1) == 0 && isalpha(*plpath) == 0 ) ) { - entities_returned ++; - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"playlist\",\"plist\":"); - cur += json_emit_quoted_str(cur, end - cur, plpath); - cur += json_emit_raw_str(cur, end - cur, ",\"last_modified\":"); - cur += json_emit_int(cur, end - cur, mpd_playlist_get_last_modified(pl)); - cur += json_emit_raw_str(cur, end - cur, "},"); + if (entities_returned ++) len += json_printf(&out, ", "); + len += json_printf(&out, "{ type: playlist, plist: %Q, last_modified: %d }", + plpath, + mpd_playlist_get_last_modified(pl) + ); } else { entity_count --; } @@ -1266,26 +1228,23 @@ int mympd_put_playlists(char *buffer, unsigned int offset, char *filter) if (mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS || !mpd_response_finish(mpd.conn)) RETURN_ERROR_AND_RECOVER("mpd_send_list_playlists"); - /* remove last ',' */ - cur--; + len += json_printf(&out, "], totalEntities: %d, offset: %d, returnedEntities: %d }", + entity_count, + offset, + entities_returned + ); - cur += json_emit_raw_str(cur, end - cur, "],\"totalEntities\":"); - cur += json_emit_int(cur, end - cur, entity_count); - cur += json_emit_raw_str(cur, end - cur, ",\"offset\":"); - cur += json_emit_int(cur, end - cur, offset); - cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); - cur += json_emit_int(cur, end - cur, entities_returned); - cur += json_emit_raw_str(cur, end - cur, "}"); - return cur - buffer; + if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); + return len; } int mympd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr) { - char *cur = buffer; - const char *end = buffer + MAX_SIZE; struct mpd_song *song; unsigned long entity_count = 0; unsigned long entities_returned = 0; + int len; + struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); if(mpd_search_db_songs(mpd.conn, false) == false) { RETURN_ERROR_AND_RECOVER("mpd_search_db_songs"); @@ -1303,50 +1262,41 @@ int mympd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *sear if(mpd_search_commit(mpd.conn) == false) RETURN_ERROR_AND_RECOVER("mpd_search_commit"); else { - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"search\",\"data\":[ "); + len = json_printf(&out, "{ type: search, data: [ "); while((song = mpd_recv_song(mpd.conn)) != NULL) { entity_count ++; if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { - entities_returned ++; - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"song\""); - cur += json_emit_raw_str(cur, end - cur, ",\"uri\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); - cur += json_emit_quoted_str(cur, end - cur, mympd_get_album(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"artist\":"); - cur += json_emit_quoted_str(cur, end - cur, mympd_get_artist(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"duration\":"); - cur += json_emit_int(cur, end - cur, mpd_song_get_duration(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); - cur += json_emit_quoted_str(cur, end - cur, mympd_get_title(song)); - cur += json_emit_raw_str(cur, end - cur, "},"); + if (entities_returned ++) len += json_printf(&out, ", "); + len += json_printf(&out, "{ type: song, uri: %Q, album: %Q, artist: %Q, duration: %d, title: %Q }", + mpd_song_get_uri(song), + mympd_get_album(song), + mympd_get_artist(song), + mpd_song_get_duration(song), + mympd_get_title(song) + ); } mpd_song_free(song); } - - /* remove last ',' */ - cur--; - cur += json_emit_raw_str(cur, end - cur, "]"); - cur += json_emit_raw_str(cur, end - cur, ",\"totalEntities\":"); - cur += json_emit_int(cur, end - cur, entity_count); - cur += json_emit_raw_str(cur, end - cur, ",\"offset\":"); - cur += json_emit_int(cur, end - cur, offset); - cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); - cur += json_emit_int(cur, end - cur, entities_returned); - cur += json_emit_raw_str(cur, end - cur, ",\"mpdtagtype\":"); - cur += json_emit_quoted_str(cur, end - cur, mpdtagtype); - cur += json_emit_raw_str(cur, end - cur, "}"); + len += json_printf(&out, "], totalEntities: %d, offset: %d, returnedEntities: %d, mpdtagtype: %Q }", + entity_count, + offset, + entities_returned, + mpdtagtype + ); } - return cur - buffer; + + if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); + return len; } int mympd_search_add(char *buffer,char *mpdtagtype, char *searchstr) { - char *cur = buffer; - const char *end = buffer + MAX_SIZE; struct mpd_song *song; + int len; + struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); + len = 0; if(mpd_search_add_db_songs(mpd.conn, false) == false) { RETURN_ERROR_AND_RECOVER("mpd_search_add_db_songs"); @@ -1368,16 +1318,17 @@ int mympd_search_add(char *buffer,char *mpdtagtype, char *searchstr) mpd_song_free(song); } - return cur - buffer; + if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); + return len; } int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr) { - char *cur = buffer; - const char *end = buffer + MAX_SIZE; struct mpd_song *song; unsigned long entity_count = 0; unsigned long entities_returned = 0; + int len; + struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); if(mpd_search_queue_songs(mpd.conn, false) == false) { RETURN_ERROR_AND_RECOVER("mpd_search_queue_songs"); @@ -1395,81 +1346,64 @@ int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char if(mpd_search_commit(mpd.conn) == false) RETURN_ERROR_AND_RECOVER("mpd_search_commit"); else { - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"queuesearch\",\"data\":[ "); + len = json_printf(&out, "{ type: queuesearch, data: [ "); while((song = mpd_recv_song(mpd.conn)) != NULL) { entity_count ++; if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { - entities_returned ++; - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"song\""); - cur += json_emit_raw_str(cur, end - cur, ",\"id\":"); - cur += json_emit_int(cur, end - cur, mpd_song_get_id(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"pos\":"); - cur += json_emit_int(cur, end - cur, mpd_song_get_pos(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); - cur += json_emit_quoted_str(cur, end - cur, mympd_get_album(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"artist\":"); - cur += json_emit_quoted_str(cur, end - cur, mympd_get_artist(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"duration\":"); - cur += json_emit_int(cur, end - cur, mpd_song_get_duration(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); - cur += json_emit_quoted_str(cur, end - cur, mympd_get_title(song)); - cur += json_emit_raw_str(cur, end - cur, "},"); + if (entities_returned ++) len += json_printf(&out, ", "); + len += json_printf(&out, "{ type: song, id: %d, pos: %d, album: %Q, artist: %Q, duration: %d, title: %Q }", + mpd_song_get_id(song), + mpd_song_get_pos(song), + mympd_get_album(song), + mympd_get_artist(song), + mpd_song_get_duration(song), + mympd_get_title(song) + ); mpd_song_free(song); } } - cur--; - - cur += json_emit_raw_str(cur, end - cur, "]"); - cur += json_emit_raw_str(cur, end - cur, ",\"totalEntities\":"); - cur += json_emit_int(cur, end - cur, entity_count); - cur += json_emit_raw_str(cur, end - cur, ",\"offset\":"); - cur += json_emit_int(cur, end - cur, offset); - cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); - cur += json_emit_int(cur, end - cur, entities_returned); - cur += json_emit_raw_str(cur, end - cur, ",\"mpdtagtype\":"); - cur += json_emit_quoted_str(cur, end - cur, mpdtagtype); - cur += json_emit_raw_str(cur, end - cur, "}"); + len += json_printf(&out, "], totalEntities: %d, offset: %d, returnedEntities: %d, mpdtagtype: %Q }", + entity_count, + offset, + entities_returned, + mpdtagtype + ); } - return cur - buffer; + + if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); + return len; } int mympd_get_stats(char *buffer) { - char *cur = buffer; - const char *end = buffer + MAX_SIZE; struct mpd_stats *stats = mpd_run_stats(mpd.conn); const unsigned *version = mpd_connection_get_server_version(mpd.conn); char mpd_version[20]; + int len; + struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); snprintf(mpd_version,20,"%i.%i.%i", version[0], version[1], version[2]); if (stats == NULL) RETURN_ERROR_AND_RECOVER("mympd_get_stats"); - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"mpdstats\",\"data\": {"); - cur += json_emit_raw_str(cur, end - cur, "\"artists\":"); - cur += json_emit_int(cur, end - cur, mpd_stats_get_number_of_artists(stats)); - cur += json_emit_raw_str(cur, end - cur, ",\"albums\":"); - cur += json_emit_int(cur, end - cur, mpd_stats_get_number_of_albums(stats)); - cur += json_emit_raw_str(cur, end - cur, ",\"songs\":"); - cur += json_emit_int(cur, end - cur, mpd_stats_get_number_of_songs(stats)); - cur += json_emit_raw_str(cur, end - cur, ",\"playtime\":"); - cur += json_emit_int(cur, end - cur, mpd_stats_get_play_time(stats)); - cur += json_emit_raw_str(cur, end - cur, ",\"uptime\":"); - cur += json_emit_int(cur, end - cur, mpd_stats_get_uptime(stats)); - cur += json_emit_raw_str(cur, end - cur, ",\"dbupdated\":"); - cur += json_emit_int(cur, end - cur, mpd_stats_get_db_update_time(stats)); - cur += json_emit_raw_str(cur, end - cur, ",\"dbplaytime\":"); - cur += json_emit_int(cur, end - cur, mpd_stats_get_db_play_time(stats)); - cur += json_emit_raw_str(cur, end - cur, ",\"mympd_version\":"); - cur += json_emit_quoted_str(cur, end - cur, MYMPD_VERSION); - cur += json_emit_raw_str(cur, end - cur, ",\"mpd_version\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_version); - cur += json_emit_raw_str(cur, end - cur, "}}"); - + len = json_printf(&out, "{ type: mpdstats, data: { artists: %d, albums: %d, songs: %d, " + "playtime: %d, dbupdated: %d, dbplaytime: %d, mympd_version: %Q, mpd_version: %Q }}", + mpd_stats_get_number_of_artists(stats), + mpd_stats_get_number_of_albums(stats), + mpd_stats_get_number_of_songs(stats), + mpd_stats_get_play_time(stats), + mpd_stats_get_uptime(stats), + mpd_stats_get_db_update_time(stats), + mpd_stats_get_db_play_time(stats), + MYMPD_VERSION, + mpd_version + ); mpd_stats_free(stats); - return cur - buffer; + + if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); + return len; } void mympd_disconnect() diff --git a/src/mpd_client.h b/src/mpd_client.h index bd2694d..e212bf1 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -29,11 +29,12 @@ #define RETURN_ERROR_AND_RECOVER(X) do { \ fprintf(stderr, "MPD X: %s\n", mpd_connection_get_error_message(mpd.conn)); \ - cur += snprintf(cur, end - cur, "{\"type\":\"error\",\"data\":\"%s\"}", \ - mpd_connection_get_error_message(mpd.conn)); \ + len = json_printf(&out, "{ type:error, data : %Q }", \ + mpd_connection_get_error_message(mpd.conn) \ + ); \ if (!mpd_connection_clear_error(mpd.conn)) \ mpd.conn_state = MPD_FAILURE; \ - return cur - buffer; \ + return len; \ } while(0) From df91c3370537639701b6f128bb5a9c5354f266d3 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Mon, 18 Jun 2018 20:55:54 +0100 Subject: [PATCH 15/32] Implement jsonrpc api --- htdocs/js/mpd.js | 138 +++++++------- src/mpd_client.c | 465 +++++++++++++---------------------------------- src/mpd_client.h | 1 - src/mympd.c | 40 ++-- 4 files changed, 197 insertions(+), 447 deletions(-) diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index 8f66ea2..4349b0a 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -120,9 +120,8 @@ app.route=function() { app.prepare(); if (app.current.app == 'Playback') { - $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", - data: "MPD_API_GET_CURRENT_SONG"}).done(function( data ) { - var obj = JSON.parse(data); + $.ajax({url: "/api", contentType:"application/json", method: "POST", + data: JSON.stringify({"cmd":"MPD_API_GET_CURRENT_SONG"})}).done(function( obj ) { songChange(obj.data.title, obj.data.artist, obj.data.album, obj.data.uri, obj.data.currentsongid); }); } @@ -142,34 +141,30 @@ app.route=function() { getQueue(); } else if (app.current.app == 'Browse' && app.current.tab == 'Playlists') { - $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", - data: "MPD_API_GET_PLAYLISTS," + app.current.page + ","+app.current.filter}).done(function( data ) { - var obj = JSON.parse(data); + $.ajax({url: "/api", contentType:"application/json", method: "POST", + data: JSON.stringify({"cmd":"MPD_API_GET_PLAYLISTS","data": { "offset": app.current.page, "filter": app.current.filter}})}).done(function( obj ) { parsePlaylists(obj); doSetFilterLetter('#browsePlaylistsFilter'); }); } else if (app.current.app == 'Browse' && app.current.tab == 'Database' && app.current.view == 'Artist') { - $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", - data: "MPD_API_GET_ARTISTS," + app.current.page + ","+app.current.filter}).done(function( data ) { - var obj = JSON.parse(data); + $.ajax({url: "/api", contentType:"application/json", method: "POST", + data: JSON.stringify({"cmd":"MPD_API_GET_ARTISTS","data": { "offset": app.current.page, "filter": app.current.filter}})}).done(function( obj ) { parseListDBtags(obj); doSetFilterLetter('#BrowseDatabaseFilter'); }); } else if (app.current.app == 'Browse' && app.current.tab == 'Database' && app.current.view == 'Album') { - $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", - data: "MPD_API_GET_ARTISTALBUMS," + app.current.page + ","+app.current.filter + "," + app.current.search}).done(function( data ) { - var obj = JSON.parse(data); + $.ajax({url: "/api", contentType:"application/json", method: "POST", + data: JSON.stringify({"cmd":"MPD_API_GET_ARTISTALBUMS","data": { "offset": app.current.page, "filter": app.current.filter, "albumartist": app.current.search}})}).done(function( obj ) { parseListDBtags(obj); doSetFilterLetter('#BrowseDatabaseFilter'); }); } else if (app.current.app == 'Browse' && app.current.tab == 'Filesystem') { $('#BrowseBreadcrumb').empty().append(''); - $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", - data: "MPD_API_GET_FILESYSTEM," + app.current.page + ","+(app.current.search ? app.current.search : "/")+","+app.current.filter}).done(function( data ) { - var obj = JSON.parse(data); + $.ajax({url: "/api", contentType:"application/json", method: "POST", + data: JSON.stringify({"cmd":"MPD_API_GET_FILESYSTEM","data": { "offset": app.current.page,"path":(app.current.search ? app.current.search : "/"),"filter": app.current.filter}})}).done(function( obj ) { parseFilesystem(obj); doSetFilterLetter('#BrowseFilesystemFilter'); }); @@ -178,7 +173,7 @@ app.route=function() { if (app.current.search) { add_all_songs.off(); // remove previous binds add_all_songs.on('click', function() { - sendAPI('MPD_API_ADD_TRACK,'+app.current.search); + sendAPI(JSON.stringify({"cmd":"MPD_API_ADD_TRACK", "data": { "uri": app.current.search}})); }); add_all_songs.removeAttr('disabled').removeClass('disabled'); } else { @@ -219,9 +214,8 @@ app.route=function() { } }); if (app.current.search.length >= 2) { - $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", - data: "MPD_API_SEARCH," + app.current.filter + ","+app.current.page+","+app.current.search}).done(function( data ) { - var obj = JSON.parse(data); + $.ajax({url: "/api", contentType:"application/json", method: "POST", + data: JSON.stringify({"cmd":"MPD_API_SEARCH", "data": { "mpdtag": app.current.filter,"offset":app.current.page,"searchstr":app.current.search}})}).done(function( obj ) { parseSearch(obj); }); } else { @@ -239,14 +233,12 @@ app.route=function() { }; $(document).ready(function(){ - $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", - data: "MPD_API_GET_SETTINGS"}).done(function( data ) { - var obj = JSON.parse(data); + $.ajax({url: "/api", contentType:"application/json", method: "POST", + data: JSON.stringify({"cmd":"MPD_API_GET_SETTINGS"})}).done(function( obj ) { parseSettings(obj); }); - $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", - data: "MPD_API_GET_OUTPUTNAMES"}).done(function( data ) { - var obj = JSON.parse(data); + $.ajax({url: "/api", contentType:"application/json", method: "POST", + data: JSON.stringify({"cmd":"MPD_API_GET_OUTPUTNAMES"})}).done(function( obj ) { parseOutputnames(obj); }); @@ -256,7 +248,7 @@ $(document).ready(function(){ volumeBar=$('#volumebar').slider(); volumeBar.slider('setValue',0); volumeBar.slider('on','slideStop', function(value){ - sendAPI("MPD_API_SET_VOLUME,"+value); + sendAPI(JSON.stringify({"cmd":"MPD_API_SET_VOLUME","data": {"volume":value}})); }); progressBar=$('#progressbar').slider(); @@ -265,22 +257,20 @@ $(document).ready(function(){ progressBar.slider('on','slideStop', function(value){ if(current_song && current_song.currentSongId >= 0) { var seekVal = Math.ceil(current_song.totalTime*(value/100)); - sendAPI("MPD_API_SET_SEEK,"+current_song.currentSongId+","+seekVal); + sendAPI(JSON.stringify({"cmd":"MPD_API_SET_SEEK", "data": {"songid":current_song.currentSongId,"seek":seekVal}})); } }); $('#about').on('shown.bs.modal', function () { - $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", - data: "MPD_API_GET_STATS"}).done(function( data ) { - var obj = JSON.parse(data); + $.ajax({url: "/api", contentType:"application/json", method: "POST", + data: JSON.stringify({"cmd":"MPD_API_GET_STATS"})}).done(function( obj ) { parseStats(obj); }); }) $('#settings').on('shown.bs.modal', function () { - $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", - data: "MPD_API_GET_SETTINGS"}).done(function( data ) { - var obj = JSON.parse(data); + $.ajax({url: "/api", contentType:"application/json", method: "POST", + data: JSON.stringify({"cmd":"MPD_API_GET_SETTINGS"})}).done(function( obj ) { parseSettings(obj); }); }) @@ -511,9 +501,8 @@ function parseState(obj) { //Get current song on queue change for http streams if (last_state == undefined || obj.data.queue_version != last_state.data.queue_version) { - $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", - data: "MPD_API_GET_CURRENT_SONG"}).done(function( data ) { - var obj = JSON.parse(data); + $.ajax({url: "/api", contentType:"application/json", method: "POST", + data: JSON.stringify({"cmd":"MPD_API_GET_CURRENT_SONG"})}).done(function( obj ) { songChange(obj.data.title, obj.data.artist, obj.data.album, obj.data.uri, obj.data.currentsongid); }); } @@ -531,16 +520,14 @@ function parseState(obj) { function getQueue() { if (app.current.search.length >= 2) { - $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", - data: "MPD_API_SEARCH_QUEUE," + app.current.filter + ","+app.current.page+"," + app.current.search}).done(function( data ) { - var obj = JSON.parse(data); + $.ajax({url: "/api", contentType:"application/json", method: "POST", + data: JSON.stringify({"cmd":"MPD_API_SEARCH_QUEUE", "data": {"mpdtag":app.current.filter, "offset":app.current.page,"searchstr": app.current.search}})}).done(function( obj ) { parseQueue(obj); }); } else { - $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", - data: "MPD_API_GET_QUEUE," + app.current.page}).done(function( data ) { - var obj = JSON.parse(data); + $.ajax({url: "/api", contentType:"application/json", method: "POST", + data: JSON.stringify({"cmd":"MPD_API_GET_QUEUE", "data": {"offset": app.current.page}})}).done(function( obj ) { parseQueue(obj); }); } @@ -622,7 +609,7 @@ function parseQueue(obj) { $('#QueueList > tbody > tr').on({ click: function() { $('#queueList > tbody > tr').removeClass('active'); - sendAPI('MPD_API_PLAY_TRACK,'+$(this).attr('trackid')); + sendAPI(JSON.stringify({"cmd":"MPD_API_PLAY_TRACK","data": {"track":$(this).attr('trackid')}})); $(this).addClass('active'); }, }); @@ -699,7 +686,7 @@ function parseFilesystem(obj) { '' + glyphicon + '') .find('a').click(function(e) { e.stopPropagation(); - sendAPI(onClickAction + "," + decodeURI($(this).parents("tr").attr("uri"))); + sendAPI(JSON.stringify({"cmd":onClickAction,"data":{ "uri":decodeURI($(this).parents("tr").attr("uri"))}})); showNotification('"' + $('td:nth-last-child(3)', $(this).parents('tr')).text() + '" added','','','success'); }); } @@ -730,11 +717,11 @@ function parseFilesystem(obj) { app.goto('Browse','Filesystem',undefined,app.current.page+'/'+app.current.filter+'/'+app.current.search); break; case 'song': - sendAPI("MPD_API_ADD_TRACK," + decodeURI($(this).attr("uri"))); + sendAPI(JSON.stringify({"cmd":"MPD_API_ADD_TRACK", "data": {"uri": decodeURI($(this).attr("uri"))}})); showNotification('"' + $('td:nth-last-child(5)', this).text() + '" added','','','success'); break; case 'plist': - sendAPI("MPD_API_ADD_PLAYLIST," + decodeURI($(this).attr("uri"))); + sendAPI(JSON.stringify({"cmd":"MPD_API_ADD_PLAYLIST", "data": {"plist": decodeURI($(this).attr("uri"))}})); showNotification('"' + $('td:nth-last-child(3)', this).text() + '" added','','','success'); break; } @@ -792,7 +779,7 @@ function parsePlaylists(obj) { }; $('#'+app.current.app+app.current.tab+'List > tbody > tr').on({ click: function() { - sendAPI('MPD_API_ADD_PLAYLIST,' + decodeURI($(this).attr('uri'))); + sendAPI(JSON.stringify({"cmd":"MPD_API_ADD_PLAYLIST", "data": { "plist": decodeURI($(this).attr('uri'))}})); showNotification('"' + $('td:nth-last-child(3)', this).text() + '" added','','','success'); } }); @@ -856,9 +843,8 @@ function parseListDBtags(obj) { ' '+ ''; $('#BrowseDatabaseAlbumCards').append(card); - $.ajax({url: "/jsonrpc", contentType:"application/json", method: "POST", - data: JSON.stringify({"cmd":"MPD_API_GET_ARTISTALBUMTITLES", "data": { "albumartist": obj.searchstr, "album": obj.data[item].value}})}).done(function( data ) { - var obj = JSON.parse(data); + $.ajax({url: "/api", contentType:"application/json", method: "POST", + data: JSON.stringify({"cmd":"MPD_API_GET_ARTISTALBUMTITLES", "data": { "albumartist": obj.searchstr, "album": obj.data[item].value}})}).done(function( obj ) { parseListTitles(obj); }); } @@ -881,14 +867,14 @@ function parseListTitles(obj) { $('#'+genId(obj.album)+' > img').on({ click: function() { - sendAPI('MPD_API_ADD_TRACK,' + decodeURI($(this).attr('uri'))); + sendAPI(JSON.stringify({"cmd":"MPD_API_ADD_TRACK", "data": { "track": decodeURI($(this).attr('uri'))}})); showNotification('"'+decodeURI($(this).attr('data-album'))+'" added','','','success'); } }); $('#tbl'+genId(obj.album)+' > tbody > tr').on({ click: function() { - sendAPI('MPD_API_ADD_TRACK,' + decodeURI($(this).attr('uri'))); + sendAPI(JSON.stringify({"cmd":"MPD_API_ADD_TRACK", "data": { "track": decodeURI($(this).attr('uri'))}})); showNotification('"' + $('td:nth-last-child(1)', this).text() + '" added','','','success'); } }); @@ -982,24 +968,24 @@ function updatePlayIcon(obj) { } function sendAPI(cmd) { - $.ajax({url: "/api", mimeType: "text/plain", contentType:"text/plain", method: "POST", data: cmd}); + $.ajax({url: "/api", contentType:"application/json", method: "POST", data: cmd}); } function updateDB(event) { - sendAPI('MPD_API_UPDATE_DB'); + sendAPI(JSON.stringify({"cmd":"MPD_API_UPDATE_DB"})); showNotification('Updating MPD Database...','','','success'); event.preventDefault(); } function clickPlay() { if( playstate != 'play') - sendAPI('MPD_API_SET_PLAY'); + sendAPI(JSON.stringify({"cmd":"MPD_API_SET_PLAY"})); else - sendAPI('MPD_API_SET_PAUSE'); + sendAPI(JSON.stringify({"cmd":"MPD_API_SET_PAUSE"})); } function clickStop() { - sendAPI('MPD_API_SET_STOP'); + sendAPI(JSON.stringify({"cmd":"MPD_API_SET_STOP"})); } function setLocalStream(mpdhost,streamport) { @@ -1015,16 +1001,16 @@ function setLocalStream(mpdhost,streamport) { function delQueueSong(tr,event) { event.stopPropagation(); if ( $('#btntrashmodeup').hasClass('btn-success') ) { - sendAPI('MPD_API_RM_RANGE,0,' + (tr.index() + 1)); + sendAPI(JSON.stringify({"cmd":"MPD_API_RM_RANGE", "data": {"start":0, "end": (tr.index() + 1)}})); } else if ( $('#btntrashmodesingle').hasClass('btn-success') ) { - sendAPI('MPD_API_RM_TRACK,' + tr.attr('trackid')); + sendAPI(JSON.stringify({"cmd":"MPD_API_RM_TRACK", "data": { "track": tr.attr('trackid')}})); } else if ( $('#btntrashmodedown').hasClass('btn-success') ) { - sendAPI('MPD_API_RM_RANGE,' + tr.index() + ',-1'); + sendAPI(JSON.stringify({"cmd":"MPD_API_RM_RANGE", "data": {"start": tr.index(), "end":-1}})); }; } function delPlaylist(tr) { - sendAPI("MPD_API_RM_PLAYLIST," + decodeURI(tr.attr("uri"))); + sendAPI(JSON.stringify({"cmd":"MPD_API_RM_PLAYLIST","data": {"plist": decodeURI(tr.attr("uri"))}})); tr.remove(); } @@ -1137,23 +1123,23 @@ function confirmSettings() { } } if (formOK == true) { - sendAPI("MPD_API_TOGGLE_CONSUME," + ($('#btnconsume').hasClass('btn-success') ? 1 : 0)); - sendAPI("MPD_API_TOGGLE_RANDOM," + ($('#btnrandom').hasClass('btn-success') ? 1 : 0)); - sendAPI("MPD_API_TOGGLE_SINGLE," + ($('#btnsingle').hasClass('btn-success') ? 1 : 0)); - sendAPI("MPD_API_TOGGLE_REPEAT," + ($('#btnrepeat').hasClass('btn-success') ? 1 : 0)); - sendAPI("MPD_API_SET_REPLAYGAIN," + $('#selectReplaygain').val()); + sendAPI(JSON.stringify({"cmd":"MPD_API_TOGGLE_CONSUME","data": {"state": ($('#btnconsume').hasClass('btn-success') ? 1 : 0)}})); + sendAPI(JSON.stringify({"cmd":"MPD_API_TOGGLE_RANDOM","data":{"state": ($('#btnrandom').hasClass('btn-success') ? 1 : 0)}})); + sendAPI(JSON.stringify({"cmd":"MPD_API_TOGGLE_SINGLE","data":{"state": ($('#btnsingle').hasClass('btn-success') ? 1 : 0)}})); + sendAPI(JSON.stringify({"cmd":"MPD_API_TOGGLE_REPEAT","data":{"state": ($('#btnrepeat').hasClass('btn-success') ? 1 : 0)}})); + sendAPI(JSON.stringify({"cmd":"MPD_API_SET_REPLAYGAIN","data":{"mode": $('#selectReplaygain').val()}})); if (!$('#inputCrossfade').is(':disabled')) - sendAPI("MPD_API_SET_CROSSFADE," + $('#inputCrossfade').val()); + sendAPI(JSON.stringify({"cmd":"MPD_API_SET_CROSSFADE","data":{"state": $('#inputCrossfade').val()}})); if (!$('#inputMixrampdb').is(':disabled')) - sendAPI("MPD_API_SET_MIXRAMPDB," + $('#inputMixrampdb').val()); + sendAPI(JSON.stringify({"cmd":"MPD_API_SET_MIXRAMPDB","data":{"state": $('#inputMixrampdb').val()}})); if (!$('#inputMixrampdelay').is(':disabled')) - sendAPI("MPD_API_SET_MIXRAMPDELAY," + $('#inputMixrampdelay').val()); + sendAPI(JSON.stringify({"cmd":"MPD_API_SET_MIXRAMPDELAY","data":{"state": $('#inputMixrampdelay').val()}})); $('#settings').modal('hide'); } } function toggleoutput(button, id) { - sendAPI("MPD_API_TOGGLE_OUTPUT,"+id+"," + ($(button).hasClass('btn-success') ? 0 : 1)); + sendAPI(JSON.stringify({"cmd":"MPD_API_TOGGLE_OUTPUT", "data": {"output": id, "state": ($(button).hasClass('btn-success') ? 0 : 1)}})); } $('#trashmodebtns > button').on('click', function(e) { @@ -1206,7 +1192,7 @@ $('#search2').submit(function () { function addAllFromSearch() { if (app.current.search.length >= 2) { - sendAPI('MPD_API_SEARCH_ADD,' + app.current.filter + ',' + app.current.search); + sendAPI(JSON.stringify({"cmd":"MPD_API_SEARCH_ADD","data":{"filter": app.current.filter,"searchstr": + app.current.search}})); var rowCount = $('#SearchList >tbody >tr').length; showNotification('Added '+rowCount+' songs from search','','','success'); } @@ -1271,7 +1257,7 @@ function gotoPage(x,element,event) { function addStream() { if($('#streamurl').val().length > 0) { - sendAPI('MPD_API_ADD_TRACK,'+$('#streamurl').val()); + sendAPI(JSON.stringify({"cmd":"MPD_API_ADD_TRACK","data":{"uri":$('#streamurl').val()}})); } $('#streamurl').val(""); $('#addstream').modal('hide'); @@ -1279,7 +1265,7 @@ function addStream() { function saveQueue() { if($('#playlistname').val().length > 0) { - sendAPI('MPD_API_SAVE_QUEUE,'+$('#playlistname').val()); + sendAPI(JSON.stringify({"cmd":"MPD_API_SAVE_QUEUE","data":{"plist":$('#playlistname').val()}})); } $('#savequeue').modal('hide'); } @@ -1361,10 +1347,10 @@ $(document).keydown(function(e){ } switch (e.which) { case 37: //left - sendAPI('MPD_API_SET_PREV'); + sendAPI(JSON.stringify({"cmd":"MPD_API_SET_PREV"})); break; case 39: //right - sendAPI('MPD_API_SET_NEXT'); + sendAPI(JSON.stringify({"cmd":"MPD_API_SET_NEXT"})); break; case 32: //space clickPlay(); @@ -1416,7 +1402,7 @@ function chVolume (increment) { if (newValue<0) { newValue=0; } else if (newValue > 100) { newValue=100; } volumeBar.slider('setValue',newValue); - sendAPI("MPD_API_SET_VOLUME,"+newValue); + sendAPI(JSON.stringify({"cmd":"MPD_API_SET_VOLUME", "data": {"volume":newValue}})); } function beautifyDuration(x) { diff --git a/src/mpd_client.c b/src/mpd_client.c index 2b329ed..1a25e97 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -42,14 +42,6 @@ const char * mpd_cmd_strs[] = { MPD_CMDS(GEN_STR) }; -char * get_arg1 (const char *p) { - return strchr(p, ',') + 1; -} - -char * get_arg2 (const char *p) { - return get_arg1(get_arg1(p)); -} - static inline enum mpd_cmd_ids get_cmd_id(const char *cmd) { for(int i = 0; i < sizeof(mpd_cmd_strs)/sizeof(mpd_cmd_strs[0]); i++) @@ -59,16 +51,14 @@ static inline enum mpd_cmd_ids get_cmd_id(const char *cmd) return -1; } -void callback_mympd_jsonrpc(struct mg_connection *nc, const struct mg_str msg) +void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { size_t n = 0; char *cmd; - int int_buf; - int je; + unsigned int uint_buf1, uint_buf2; + int je, int_buf; float float_buf; - char *p_charbuf1; - char *p_charbuf2; - char *p_charbuf3; + char *p_charbuf1, *p_charbuf2; #ifdef DEBUG fprintf(stdout,"Got request: %s\n",msg.p); @@ -88,48 +78,6 @@ void callback_mympd_jsonrpc(struct mg_connection *nc, const struct mg_str msg) free(p_charbuf1); free(p_charbuf2); break; - } - free(cmd); - - if(mpd.conn_state == MPD_CONNECTED && mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS) - { - #ifdef DEBUG - fprintf(stdout,"Error: %s\n",mpd_connection_get_error_message(mpd.conn)); - #endif - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\", \"data\": \"%s\"}", - mpd_connection_get_error_message(mpd.conn)); - - /* Try to recover error */ - if (!mpd_connection_clear_error(mpd.conn)) - mpd.conn_state = MPD_FAILURE; - } - - if(n > 0) { - #ifdef DEBUG - fprintf(stdout,"Send http response:\n %s\n",mpd.buf); - #endif - mg_send_http_chunk(nc, mpd.buf, n); - } -} - -void callback_mympd(struct mg_connection *nc, const struct mg_str msg) -{ - enum mpd_cmd_ids cmd_id = get_cmd_id(msg.p); - size_t n = 0; - unsigned int uint_buf, uint_buf_2; - int int_buf; - float float_buf; - char *p_charbuf = NULL, *token; - char *p_charbuf2 = NULL; - char *searchstr = NULL; - #ifdef DEBUG - fprintf(stdout,"Got request: %s\n",msg.p); - #endif - if(cmd_id == -1) - return; - - switch(cmd_id) - { case MPD_API_WELCOME: n = mympd_put_welcome(mpd.buf); case MPD_API_UPDATE_DB: @@ -154,343 +102,184 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) mpd_run_clear(mpd.conn); break; case MPD_API_RM_TRACK: - if(sscanf(msg.p, "MPD_API_RM_TRACK,%u", &uint_buf)) - mpd_run_delete_id(mpd.conn, uint_buf); + je = json_scanf(msg.p, msg.len, "{ data: { track:%u } }", &uint_buf1); + if (je == 1) + mpd_run_delete_id(mpd.conn, uint_buf1); break; case MPD_API_RM_RANGE: - if(sscanf(msg.p, "MPD_API_RM_RANGE,%u,%u", &uint_buf, &uint_buf_2)) - mpd_run_delete_range(mpd.conn, uint_buf, uint_buf_2); + je = json_scanf(msg.p, msg.len, "{ data: { start:%u, end:%u } }", &uint_buf1, &uint_buf2); + if (je == 2) + mpd_run_delete_range(mpd.conn, uint_buf1, uint_buf2); break; case MPD_API_MOVE_TRACK: - if (sscanf(msg.p, "MPD_API_MOVE_TRACK,%u,%u", &uint_buf, &uint_buf_2) == 2) + je = json_scanf(msg.p, msg.len, "{ data: { track:%u, pos:%u } }", &uint_buf1, &uint_buf2); + if (je == 2) { - uint_buf -= 1; - uint_buf_2 -= 1; - mpd_run_move(mpd.conn, uint_buf, uint_buf_2); + uint_buf1 -= 1; + uint_buf2 -= 1; + mpd_run_move(mpd.conn, uint_buf1, uint_buf2); } break; case MPD_API_PLAY_TRACK: - if(sscanf(msg.p, "MPD_API_PLAY_TRACK,%u", &uint_buf)) - mpd_run_play_id(mpd.conn, uint_buf); + je = json_scanf(msg.p, msg.len, "{ data: { track:%u } }", &uint_buf1); + if (je == 1) + mpd_run_play_id(mpd.conn, uint_buf1); break; case MPD_API_TOGGLE_RANDOM: - if(sscanf(msg.p, "MPD_API_TOGGLE_RANDOM,%u", &uint_buf)) - mpd_run_random(mpd.conn, uint_buf); + je = json_scanf(msg.p, msg.len, "{ data: { state:%u } }", &uint_buf1); + if (je == 1) + mpd_run_random(mpd.conn, uint_buf1); break; case MPD_API_TOGGLE_REPEAT: - if(sscanf(msg.p, "MPD_API_TOGGLE_REPEAT,%u", &uint_buf)) - mpd_run_repeat(mpd.conn, uint_buf); + je = json_scanf(msg.p, msg.len, "{ data: { state:%u } }", &uint_buf1); + if (je == 1) + mpd_run_repeat(mpd.conn, uint_buf1); break; case MPD_API_TOGGLE_CONSUME: - if(sscanf(msg.p, "MPD_API_TOGGLE_CONSUME,%u", &uint_buf)) - mpd_run_consume(mpd.conn, uint_buf); + je = json_scanf(msg.p, msg.len, "{ data: { state:%u } }", &uint_buf1); + if (je == 1) + mpd_run_consume(mpd.conn, uint_buf1); break; case MPD_API_TOGGLE_SINGLE: - if(sscanf(msg.p, "MPD_API_TOGGLE_SINGLE,%u", &uint_buf)) - mpd_run_single(mpd.conn, uint_buf); + je = json_scanf(msg.p, msg.len, "{ data: { state:%u } }", &uint_buf1); + if (je == 1) + mpd_run_single(mpd.conn, uint_buf1); break; case MPD_API_SET_CROSSFADE: - if(sscanf(msg.p, "MPD_API_SET_CROSSFADE,%u", &uint_buf)) - mpd_run_crossfade(mpd.conn, uint_buf); + je = json_scanf(msg.p, msg.len, "{ data: { state:%u } }", &uint_buf1); + if (je == 1) + mpd_run_crossfade(mpd.conn, uint_buf1); break; case MPD_API_SET_MIXRAMPDB: - if(sscanf(msg.p, "MPD_API_SET_MIXRAMPDB,%f", &float_buf)) + je = json_scanf(msg.p, msg.len, "{ data: { state:%f } }", &float_buf); + if (je == 1) mpd_run_mixrampdb(mpd.conn, float_buf); break; case MPD_API_SET_MIXRAMPDELAY: - if(sscanf(msg.p, "MPD_API_SET_MIXRAMPDELAY,%f", &float_buf)) + je = json_scanf(msg.p, msg.len, "{ data: { state:%f } }", &float_buf); + if (je == 1) mpd_run_mixrampdelay(mpd.conn, float_buf); break; case MPD_API_GET_OUTPUTNAMES: n = mympd_put_outputnames(mpd.buf); break; case MPD_API_TOGGLE_OUTPUT: - if (sscanf(msg.p, "MPD_API_TOGGLE_OUTPUT,%u,%u", &uint_buf, &uint_buf_2)) { - if (uint_buf_2) - mpd_run_enable_output(mpd.conn, uint_buf); + je = json_scanf(msg.p, msg.len, "{ data: { output:%u, state:%u } }", &uint_buf1, &uint_buf2); + if (je == 2) + { + if (uint_buf2) + mpd_run_enable_output(mpd.conn, uint_buf1); else - mpd_run_disable_output(mpd.conn, uint_buf); + mpd_run_disable_output(mpd.conn, uint_buf1); } break; case MPD_API_SET_VOLUME: - if(sscanf(msg.p, "MPD_API_SET_VOLUME,%ud", &uint_buf) && uint_buf <= 100) - mpd_run_set_volume(mpd.conn, uint_buf); + je = json_scanf(msg.p, msg.len, "{ data: { volume:%u } }", &uint_buf1); + if (je == 1) + mpd_run_set_volume(mpd.conn, uint_buf1); break; case MPD_API_SET_SEEK: - if(sscanf(msg.p, "MPD_API_SET_SEEK,%u,%u", &uint_buf, &uint_buf_2)) - mpd_run_seek_id(mpd.conn, uint_buf, uint_buf_2); + je = json_scanf(msg.p, msg.len, "{ data: { songid:%u, seek:%u } }", &uint_buf1, &uint_buf2); + if (je == 2) + mpd_run_seek_id(mpd.conn, uint_buf1, uint_buf2); break; case MPD_API_GET_QUEUE: - if(sscanf(msg.p, "MPD_API_GET_QUEUE,%u", &uint_buf)) - n = mympd_put_queue(mpd.buf, uint_buf); + je = json_scanf(msg.p, msg.len, "{ data: { offset:%u } }", &uint_buf1); + if (je == 1) + n = mympd_put_queue(mpd.buf, uint_buf1); break; case MPD_API_GET_CURRENT_SONG: n = mympd_put_current_song(mpd.buf); break; - case MPD_API_GET_ARTISTS: - p_charbuf = strdup(msg.p); - if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_ARTISTS")) - goto out_artist; - uint_buf = strtoul(strtok(NULL, ","), NULL, 10); - - if((token = strtok(NULL, ",")) == NULL) { - goto out_artist; - } else { - p_charbuf2 = strdup(token); - } - n = mympd_put_db_tag(mpd.buf, uint_buf, "AlbumArtist","","",p_charbuf2); - free(p_charbuf2); -out_artist: - free(p_charbuf); + je = json_scanf(msg.p, msg.len, "{ data: { offset:%u, filter:%Q } }", &uint_buf1, &p_charbuf1); + if (je == 2) + n = mympd_put_db_tag(mpd.buf, uint_buf1, "AlbumArtist","","",p_charbuf1); + free(p_charbuf1); break; case MPD_API_GET_ARTISTALBUMS: - p_charbuf = strdup(msg.p); - if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_ARTISTALBUMS")) - goto out_artistalbum; - uint_buf = strtoul(strtok(NULL, ","), NULL, 10); - - if((token = strtok(NULL, ",")) == NULL) { - goto out_artistalbum; - } else { - p_charbuf2 = strdup(token); - } - - if((token = strtok(NULL, ",")) == NULL) { - free(p_charbuf2); - goto out_artistalbum; - } else { - searchstr = strdup(token); - } - n = mympd_put_db_tag(mpd.buf, uint_buf, "Album", "AlbumArtist", searchstr, p_charbuf2); - free(searchstr); + je = json_scanf(msg.p, msg.len, "{ data: { offset:%u, filter:%Q, albumartist:%Q } }", &uint_buf1, &p_charbuf1, &p_charbuf2); + if (je == 3) + n = mympd_put_db_tag(mpd.buf, uint_buf1, "Album", "AlbumArtist", p_charbuf2, p_charbuf1); + free(p_charbuf1); free(p_charbuf2); -out_artistalbum: - free(p_charbuf); - break; - case MPD_API_GET_ARTISTALBUMTITLES: - p_charbuf = strdup(msg.p); - if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_ARTISTALBUMTITLES")) - goto out_artistalbumtitle; - - if((token = strtok(NULL, ",")) == NULL) { - goto out_artistalbumtitle; - } else { - searchstr = strdup(token); - } - n = mympd_put_songs_in_album(mpd.buf, searchstr, token+strlen(token)+1); - free(searchstr); -out_artistalbumtitle: - free(p_charbuf); break; case MPD_API_GET_PLAYLISTS: - p_charbuf = strdup(msg.p); - if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_PLAYLISTS")) - goto out_artist; - uint_buf = strtoul(strtok(NULL, ","), NULL, 10); - - if((token = strtok(NULL, ",")) == NULL) { - goto out_playlists; - } else { - p_charbuf2 = strdup(token); - } - n = mympd_put_playlists(mpd.buf, uint_buf, p_charbuf2); - free(p_charbuf2); -out_playlists: - free(p_charbuf); + je = json_scanf(msg.p, msg.len, "{ data: { offset:%u, filter:%Q } }", &uint_buf1, &p_charbuf1); + if (je == 2) + n = mympd_put_playlists(mpd.buf, uint_buf1, p_charbuf1); + free(p_charbuf1); break; case MPD_API_GET_FILESYSTEM: - p_charbuf = strdup(msg.p); - if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_FILESYSTEM")) - goto out_browse; - - uint_buf = strtoul(strtok(NULL, ","), NULL, 10); - - if((token = strtok(NULL, ",")) == NULL) { - goto out_browse; - } else { - p_charbuf2 = strdup(token); - } - - if((token = strtok(NULL, ",")) == NULL) { - free(p_charbuf2); - goto out_browse; - } else { - searchstr = strdup(token); - } - n = mympd_put_browse(mpd.buf, p_charbuf2, uint_buf, searchstr); - free(searchstr); + je = json_scanf(msg.p, msg.len, "{ data: { offset:%u, filter:%Q, path:%Q } }", &uint_buf1, &p_charbuf1, &p_charbuf2); + if (je == 3) + n = mympd_put_browse(mpd.buf, p_charbuf2, uint_buf1, p_charbuf1); + free(p_charbuf1); free(p_charbuf2); -out_browse: - free(p_charbuf); - break; - break; case MPD_API_ADD_TRACK: - p_charbuf = strdup(msg.p); - if(strcmp(strtok(p_charbuf, ","), "MPD_API_ADD_TRACK")) - goto out_add_track; - - if((token = strtok(NULL, ",")) == NULL) - goto out_add_track; - - free(p_charbuf); - p_charbuf = strdup(msg.p); - mpd_run_add(mpd.conn, get_arg1(p_charbuf)); -out_add_track: - free(p_charbuf); + je = json_scanf(msg.p, msg.len, "{ data: { uri:%Q } }", &p_charbuf1); + if (je == 1) + mpd_run_add(mpd.conn, p_charbuf1); + free(p_charbuf1); break; case MPD_API_ADD_PLAY_TRACK: - p_charbuf = strdup(msg.p); - if(strcmp(strtok(p_charbuf, ","), "MPD_API_ADD_PLAY_TRACK")) - goto out_play_track; - - if((token = strtok(NULL, ",")) == NULL) - goto out_play_track; - - free(p_charbuf); - p_charbuf = strdup(msg.p); - int_buf = mpd_run_add_id(mpd.conn, get_arg1(p_charbuf)); - if(int_buf != -1) - mpd_run_play_id(mpd.conn, int_buf); -out_play_track: - free(p_charbuf); + je = json_scanf(msg.p, msg.len, "{ data: { uri:%Q } }", &p_charbuf1); + if (je == 1) { + int_buf = mpd_run_add_id(mpd.conn, p_charbuf1); + if(int_buf != -1) + mpd_run_play_id(mpd.conn, int_buf); + } + free(p_charbuf1); break; case MPD_API_ADD_PLAYLIST: - p_charbuf = strdup(msg.p); - if(strcmp(strtok(p_charbuf, ","), "MPD_API_ADD_PLAYLIST")) - goto out_playlist; - - if((token = strtok(NULL, ",")) == NULL) - goto out_playlist; - - free(p_charbuf); - p_charbuf = strdup(msg.p); - mpd_run_load(mpd.conn, get_arg1(p_charbuf)); -out_playlist: - free(p_charbuf); + je = json_scanf(msg.p, msg.len, "{ data: { plist:%Q } }", &p_charbuf1); + if (je == 1) + mpd_run_load(mpd.conn, p_charbuf1); + free(p_charbuf1); break; case MPD_API_SAVE_QUEUE: - p_charbuf = strdup(msg.p); - if(strcmp(strtok(p_charbuf, ","), "MPD_API_SAVE_QUEUE")) - goto out_save_queue; - - if((token = strtok(NULL, ",")) == NULL) - goto out_save_queue; - - free(p_charbuf); - p_charbuf = strdup(msg.p); - mpd_run_save(mpd.conn, get_arg1(p_charbuf)); -out_save_queue: - free(p_charbuf); + je = json_scanf(msg.p, msg.len, "{ data: { plist:%Q } }", &p_charbuf1); + if (je == 1) + mpd_run_save(mpd.conn, p_charbuf1); + free(p_charbuf1); break; case MPD_API_SEARCH_QUEUE: - p_charbuf = strdup(msg.p); - if(strcmp(strtok(p_charbuf, ","), "MPD_API_SEARCH_QUEUE")) - goto out_search_queue; - if((token = strtok(NULL, ",")) == NULL) { - goto out_search_queue; - } else { - p_charbuf2 = strdup(token); - } - - uint_buf = strtoul(strtok(NULL, ","), NULL, 10); - - if((token = strtok(NULL, ",")) == NULL) { - free(p_charbuf2); - goto out_search_queue; - } else { - searchstr = strdup(token); - } - n = mympd_search_queue(mpd.buf, p_charbuf2, uint_buf, searchstr); - free(searchstr); + je = json_scanf(msg.p, msg.len, "{ data: { offset:%u, mpdtag:%Q, searchstr:%Q } }", &uint_buf1, &p_charbuf1, &p_charbuf2); + if (je == 3) + n = mympd_search_queue(mpd.buf, p_charbuf1, uint_buf1, p_charbuf2); + free(p_charbuf1); free(p_charbuf2); -out_search_queue: - free(p_charbuf); break; case MPD_API_SEARCH_ADD: - p_charbuf = strdup(msg.p); - if(strcmp(strtok(p_charbuf, ","), "MPD_API_SEARCH_ADD")) - goto out_search_add; - if((token = strtok(NULL, ",")) == NULL) { - goto out_search_add; - } else { - p_charbuf2 = strdup(token); - } - - if((token = strtok(NULL, ",")) == NULL) { - free(p_charbuf2); - goto out_search_add; - } else { - searchstr = strdup(token); - } - n = mympd_search_add(mpd.buf, p_charbuf2, searchstr); - free(searchstr); + je = json_scanf(msg.p, msg.len, "{ data: { mpdtag:%Q, searchstr:%Q } }", &p_charbuf1, &p_charbuf2); + if (je == 2) + n = mympd_search_add(mpd.buf, p_charbuf1, p_charbuf2); + free(p_charbuf1); free(p_charbuf2); -out_search_add: - free(p_charbuf); break; case MPD_API_SEARCH: - p_charbuf = strdup(msg.p); - if(strcmp(strtok(p_charbuf, ","), "MPD_API_SEARCH")) - goto out_search; - if((token = strtok(NULL, ",")) == NULL) { - goto out_search; - } else { - p_charbuf2 = strdup(token); - } - - uint_buf = strtoul(strtok(NULL, ","), NULL, 10); - - if((token = strtok(NULL, ",")) == NULL) { - free(p_charbuf2); - goto out_search; - } else { - searchstr = strdup(token); - } - n = mympd_search(mpd.buf, p_charbuf2, uint_buf, searchstr); - free(searchstr); + je = json_scanf(msg.p, msg.len, "{ data: { offset:%u, mpdtag:%Q, searchstr:%Q } }", &uint_buf1, &p_charbuf1, &p_charbuf2); + if (je == 3) + n = mympd_search(mpd.buf, p_charbuf1, uint_buf1, p_charbuf2); + free(p_charbuf1); free(p_charbuf2); -out_search: - free(p_charbuf); break; case MPD_API_SEND_SHUFFLE: mpd_run_shuffle(mpd.conn); break; case MPD_API_SEND_MESSAGE: - p_charbuf = strdup(msg.p); - if(strcmp(strtok(p_charbuf, ","), "MPD_API_SEND_MESSAGE")) - goto out_send_message; - - if((token = strtok(NULL, ",")) == NULL) - goto out_send_message; - - free(p_charbuf); - p_charbuf = strdup(get_arg1(msg.p)); - - if ( strtok(p_charbuf, ",") == NULL ) - goto out_send_message; - - if ( (token = strtok(NULL, ",")) == NULL ) - goto out_send_message; - - mpd_run_send_message(mpd.conn, p_charbuf, token); -out_send_message: - free(p_charbuf); + je = json_scanf(msg.p, msg.len, "{ data: { channel:%Q, text:%Q } }", &p_charbuf1, &p_charbuf2); + if (je == 2) + mpd_run_send_message(mpd.conn, p_charbuf1, p_charbuf2); + free(p_charbuf1); + free(p_charbuf2); break; case MPD_API_RM_PLAYLIST: - p_charbuf = strdup(msg.p); - if(strcmp(strtok(p_charbuf, ","), "MPD_API_RM_PLAYLIST")) - goto out_rm_playlist; - - if((token = strtok(NULL, ",")) == NULL) - goto out_rm_playlist; - - free(p_charbuf); - p_charbuf = strdup(msg.p); - mpd_run_rm(mpd.conn, get_arg1(p_charbuf)); -out_rm_playlist: - free(p_charbuf); + je = json_scanf(msg.p, msg.len, "{ data: { plist:%Q } }", &p_charbuf1); + if (je == 1) + mpd_run_rm(mpd.conn, p_charbuf1); + free(p_charbuf1); break; case MPD_API_GET_SETTINGS: n = mympd_put_settings(mpd.buf); @@ -499,25 +288,20 @@ out_rm_playlist: n = mympd_get_stats(mpd.buf); break; case MPD_API_SET_REPLAYGAIN: - p_charbuf = strdup(msg.p); - if(strcmp(strtok(p_charbuf, ","), "MPD_API_SET_REPLAYGAIN")) - goto out_set_replaygain; - - if((token = strtok(NULL, ",")) == NULL) - goto out_set_replaygain; - - free(p_charbuf); - p_charbuf = strdup(msg.p); - mpd_send_command(mpd.conn, "replay_gain_mode", get_arg1(p_charbuf), NULL); - struct mpd_pair *pair; - while ((pair = mpd_recv_pair(mpd.conn)) != NULL) { - mpd_return_pair(mpd.conn, pair); - } -out_set_replaygain: - free(p_charbuf); - break; + je = json_scanf(msg.p, msg.len, "{ data: { mode:%Q } }", &p_charbuf1); + if (je == 1) + { + mpd_send_command(mpd.conn, "replay_gain_mode", p_charbuf1, NULL); + struct mpd_pair *pair; + while ((pair = mpd_recv_pair(mpd.conn)) != NULL) { + mpd_return_pair(mpd.conn, pair); + } + } + free(p_charbuf1); + break; } - + free(cmd); + if(mpd.conn_state == MPD_CONNECTED && mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS) { #ifdef DEBUG @@ -532,10 +316,9 @@ out_set_replaygain: } if(n > 0) { - if(is_websocket(nc)) { #ifdef DEBUG - fprintf(stdout,"Send response over websocket:\n %s\n",mpd.buf); + fprintf(stdout,"Send websocket response:\n %s\n",mpd.buf); #endif mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, mpd.buf, n); } @@ -543,9 +326,9 @@ out_set_replaygain: #ifdef DEBUG fprintf(stdout,"Send http response:\n %s\n",mpd.buf); #endif - mg_send_http_chunk(nc, mpd.buf, n); + mg_send_http_chunk(nc, mpd.buf, n); } - } + } } int mympd_close_handler(struct mg_connection *c) diff --git a/src/mpd_client.h b/src/mpd_client.h index e212bf1..6086f01 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -133,7 +133,6 @@ struct t_mpd_client_session { }; void mympd_poll(struct mg_mgr *s); -void callback_mympd_jsonrpc(struct mg_connection *nc, const struct mg_str msg); void callback_mympd(struct mg_connection *nc, const struct mg_str msg); int mympd_close_handler(struct mg_connection *c); int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsigned *queue_version); diff --git a/src/mympd.c b/src/mympd.c index 27fb3ee..2fb81c2 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -44,21 +44,16 @@ static void signal_handler(int sig_num) { } static void handle_api(struct mg_connection *nc, struct http_message *hm) { - mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"); - char buf[1000] = {0}; - memcpy(buf, hm->body.p,sizeof(buf) - 1 < hm->body.len ? sizeof(buf) - 1 : hm->body.len); - struct mg_str d = {buf, strlen(buf)}; - callback_mympd(nc, d); - mg_send_http_chunk(nc, "", 0); /* Send empty chunk, the end of response */ -} - -static void handle_jsonrpc(struct mg_connection *nc, struct http_message *hm) { - mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"); - char buf[1000] = {0}; - memcpy(buf, hm->body.p,sizeof(buf) - 1 < hm->body.len ? sizeof(buf) - 1 : hm->body.len); - struct mg_str d = {buf, strlen(buf)}; - callback_mympd_jsonrpc(nc, d); - mg_send_http_chunk(nc, "", 0); /* Send empty chunk, the end of response */ + if(!is_websocket(nc)) { + mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: application/json\r\n\r\n"); + } + char buf[1000] = {0}; + memcpy(buf, hm->body.p,sizeof(buf) - 1 < hm->body.len ? sizeof(buf) - 1 : hm->body.len); + struct mg_str d = {buf, strlen(buf)}; + callback_mympd(nc, d); + if(!is_websocket(nc)) { + mg_send_http_chunk(nc, "", 0); /* Send empty chunk, the end of response */ + } } static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { @@ -67,20 +62,10 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { #ifdef DEBUG fprintf(stdout,"New Websocket connection\n"); #endif - struct mg_str d = {(char *) "MPD_API_WELCOME", 15 }; + struct mg_str d = {(char *) "{\"cmd\":\"MPD_API_WELCOME\"}", 25 }; callback_mympd(nc, d); break; } - case MG_EV_WEBSOCKET_FRAME: { - struct websocket_message *wm = (struct websocket_message *) ev_data; - wm->data[wm->size]='\0'; - struct mg_str d = {(char *) wm->data, wm->size}; - #ifdef DEBUG - fprintf(stdout,"Websocket request: %s\n",wm->data); - #endif - callback_mympd(nc, d); - break; - } case MG_EV_HTTP_REQUEST: { struct http_message *hm = (struct http_message *) ev_data; #ifdef DEBUG @@ -89,9 +74,6 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { if (mg_vcmp(&hm->uri, "/api") == 0) { handle_api(nc, hm); } - else if (mg_vcmp(&hm->uri, "/jsonrpc") == 0) { - handle_jsonrpc(nc, hm); - } else { mg_serve_http(nc, hm, s_http_server_opts); } From bd00810072076d400afd82d35f6f3378c579924d Mon Sep 17 00:00:00 2001 From: jcorporation Date: Mon, 18 Jun 2018 22:24:35 +0100 Subject: [PATCH 16/32] Fixed buffer overflow in notify callback. Occurs only if multiple clients are connected. --- src/mpd_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mpd_client.c b/src/mpd_client.c index 1a25e97..0b6b868 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -372,7 +372,7 @@ static int mympd_notify_callback(struct mg_connection *c, const char *param) { #ifdef DEBUG fprintf(stdout,"Notify: %s\n",mpd.buf); #endif - mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, mpd.buf_size); + mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); if(s->song_id != mpd.song_id) { From ef7b7b9017161986dcb57d0678a1c44e25204c10 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Mon, 18 Jun 2018 22:26:00 +0100 Subject: [PATCH 17/32] Fixed prev and next buttons --- htdocs/index.html | 4 ++-- htdocs/js/mpd.js | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/htdocs/index.html b/htdocs/index.html index 04e2d34..fdfd9e1 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -38,7 +38,7 @@ ' ); } - + doSetFilterLetter('#browsePlaylistsFilter'); } function parseListDBtags(obj) { @@ -843,13 +805,11 @@ function parseListDBtags(obj) { ' '+ ''; $('#BrowseDatabaseAlbumCards').append(card); - $.ajax({url: "/api", contentType:"application/json", method: "POST", - data: JSON.stringify({"cmd":"MPD_API_GET_ARTISTALBUMTITLES", "data": { "albumartist": obj.searchstr, "album": obj.data[item].value}})}).done(function( obj ) { - parseListTitles(obj); - }); + sendAPI({"cmd":"MPD_API_GET_ARTISTALBUMTITLES", "data": { "albumartist": obj.searchstr, "album": obj.data[item].value}},parseListTitles); } setPagination(obj.totalEntities); } + doSetFilterLetter('#BrowseDatabaseFilter'); } function parseListTitles(obj) { @@ -867,14 +827,14 @@ function parseListTitles(obj) { $('#'+genId(obj.album)+' > img').on({ click: function() { - sendAPI(JSON.stringify({"cmd":"MPD_API_ADD_TRACK", "data": { "track": decodeURI($(this).attr('uri'))}})); + sendAPI({"cmd":"MPD_API_ADD_TRACK", "data": { "track": decodeURI($(this).attr('uri'))}}); showNotification('"'+decodeURI($(this).attr('data-album'))+'" added','','','success'); } }); $('#tbl'+genId(obj.album)+' > tbody > tr').on({ click: function() { - sendAPI(JSON.stringify({"cmd":"MPD_API_ADD_TRACK", "data": { "track": decodeURI($(this).attr('uri'))}})); + sendAPI({"cmd":"MPD_API_ADD_TRACK", "data": { "track": decodeURI($(this).attr('uri'))}}); showNotification('"' + $('td:nth-last-child(1)', this).text() + '" added','','','success'); } }); @@ -967,33 +927,37 @@ function updatePlayIcon(obj) { } } -function sendAPI(cmd) { - $.ajax({url: "/api", contentType:"application/json", method: "POST", data: cmd}); +function sendAPI(request, callback) { + $.ajax({url: "/api", contentType:"application/json", method: "POST", data: JSON.stringify(request)}).done(function(obj,callback) { + if (callback != undefined && typeof callback == 'function') { + callback(obj); + } + }); } function updateDB(event) { - sendAPI(JSON.stringify({"cmd":"MPD_API_UPDATE_DB"})); + sendAPI({"cmd":"MPD_API_UPDATE_DB"}); showNotification('Updating MPD Database...','','','success'); event.preventDefault(); } function clickPlay() { if( playstate != 'play') - sendAPI(JSON.stringify({"cmd":"MPD_API_SET_PLAY"})); + sendAPI({"cmd":"MPD_API_SET_PLAY"}); else - sendAPI(JSON.stringify({"cmd":"MPD_API_SET_PAUSE"})); + sendAPI({"cmd":"MPD_API_SET_PAUSE"}); } function clickStop() { - sendAPI(JSON.stringify({"cmd":"MPD_API_SET_STOP"})); + sendAPI({"cmd":"MPD_API_SET_STOP"}); } function clickPrev() { - sendAPI(JSON.stringify({"cmd":"MPD_API_SET_PREV"})); + sendAPI({"cmd":"MPD_API_SET_PREV"}); } function clickNext() { - sendAPI(JSON.stringify({"cmd":"MPD_API_SET_NEXT"})); + sendAPI({"cmd":"MPD_API_SET_NEXT"}); } function setLocalStream(mpdhost,streamport) { @@ -1009,16 +973,16 @@ function setLocalStream(mpdhost,streamport) { function delQueueSong(tr,event) { event.stopPropagation(); if ( $('#btntrashmodeup').hasClass('btn-success') ) { - sendAPI(JSON.stringify({"cmd":"MPD_API_RM_RANGE", "data": {"start":0, "end": (tr.index() + 1)}})); + sendAPI({"cmd":"MPD_API_RM_RANGE", "data": {"start":0, "end": (tr.index() + 1)}}); } else if ( $('#btntrashmodesingle').hasClass('btn-success') ) { - sendAPI(JSON.stringify({"cmd":"MPD_API_RM_TRACK", "data": { "track": tr.attr('trackid')}})); + sendAPI({"cmd":"MPD_API_RM_TRACK", "data": { "track": tr.attr('trackid')}}); } else if ( $('#btntrashmodedown').hasClass('btn-success') ) { - sendAPI(JSON.stringify({"cmd":"MPD_API_RM_RANGE", "data": {"start": tr.index(), "end":-1}})); + sendAPI({"cmd":"MPD_API_RM_RANGE", "data": {"start": tr.index(), "end":-1}}); }; } function delPlaylist(tr) { - sendAPI(JSON.stringify({"cmd":"MPD_API_RM_PLAYLIST","data": {"plist": decodeURI(tr.attr("uri"))}})); + sendAPI({"cmd":"MPD_API_RM_PLAYLIST","data": {"plist": decodeURI(tr.attr("uri"))}}); tr.remove(); } @@ -1131,23 +1095,26 @@ function confirmSettings() { } } if (formOK == true) { - sendAPI(JSON.stringify({"cmd":"MPD_API_TOGGLE_CONSUME","data": {"state": ($('#btnconsume').hasClass('btn-success') ? 1 : 0)}})); - sendAPI(JSON.stringify({"cmd":"MPD_API_TOGGLE_RANDOM","data":{"state": ($('#btnrandom').hasClass('btn-success') ? 1 : 0)}})); - sendAPI(JSON.stringify({"cmd":"MPD_API_TOGGLE_SINGLE","data":{"state": ($('#btnsingle').hasClass('btn-success') ? 1 : 0)}})); - sendAPI(JSON.stringify({"cmd":"MPD_API_TOGGLE_REPEAT","data":{"state": ($('#btnrepeat').hasClass('btn-success') ? 1 : 0)}})); - sendAPI(JSON.stringify({"cmd":"MPD_API_SET_REPLAYGAIN","data":{"mode": $('#selectReplaygain').val()}})); - if (!$('#inputCrossfade').is(':disabled')) - sendAPI(JSON.stringify({"cmd":"MPD_API_SET_CROSSFADE","data":{"state": $('#inputCrossfade').val()}})); - if (!$('#inputMixrampdb').is(':disabled')) - sendAPI(JSON.stringify({"cmd":"MPD_API_SET_MIXRAMPDB","data":{"state": $('#inputMixrampdb').val()}})); - if (!$('#inputMixrampdelay').is(':disabled')) - sendAPI(JSON.stringify({"cmd":"MPD_API_SET_MIXRAMPDELAY","data":{"state": $('#inputMixrampdelay').val()}})); + sendAPI({"cmd":"MPD_API_SET_MPD_SETTINGS", "data": { + "consume": ($('#btnconsume').hasClass('btn-success') ? 1 : 0), + "random": ($('#btnrandom').hasClass('btn-success') ? 1 : 0), + "single": ($('#btnsingle').hasClass('btn-success') ? 1 : 0), + "repeat": ($('#btnrepeat').hasClass('btn-success') ? 1 : 0), + "replaygain": $('#selectReplaygain').val(), + "crossfade": $('#inputCrossfade').val(), + "mixrampdb": $('#inputMixrampdb').val(), + "mixrampdelay": $('#inputMixrampdelay').val() + }}); + sendAPI({"cmd":"MPD_API_SET_MYMPD_SETTINGS","data": { + "notificationsWeb": ($('#btnnotifyWeb').hasClass('btn-success') ? 1 : 0), + "notificationsPage": ($('#btnnotifyPage').hasClass('btn-success') ? 1 : 0) + }}); $('#settings').modal('hide'); } } function toggleoutput(button, id) { - sendAPI(JSON.stringify({"cmd":"MPD_API_TOGGLE_OUTPUT", "data": {"output": id, "state": ($(button).hasClass('btn-success') ? 0 : 1)}})); + sendAPI({"cmd":"MPD_API_TOGGLE_OUTPUT", "data": {"output": id, "state": ($(button).hasClass('btn-success') ? 0 : 1)}}); } $('#trashmodebtns > button').on('click', function(e) { @@ -1200,7 +1167,7 @@ $('#search2').submit(function () { function addAllFromSearch() { if (app.current.search.length >= 2) { - sendAPI(JSON.stringify({"cmd":"MPD_API_SEARCH_ADD","data":{"filter": app.current.filter,"searchstr": + app.current.search}})); + sendAPI({"cmd":"MPD_API_SEARCH_ADD","data":{"filter": app.current.filter,"searchstr": + app.current.search}}); var rowCount = $('#SearchList >tbody >tr').length; showNotification('Added '+rowCount+' songs from search','','','success'); } @@ -1265,7 +1232,7 @@ function gotoPage(x,element,event) { function addStream() { if($('#streamurl').val().length > 0) { - sendAPI(JSON.stringify({"cmd":"MPD_API_ADD_TRACK","data":{"uri":$('#streamurl').val()}})); + sendAPI({"cmd":"MPD_API_ADD_TRACK","data":{"uri":$('#streamurl').val()}}); } $('#streamurl').val(""); $('#addstream').modal('hide'); @@ -1273,7 +1240,7 @@ function addStream() { function saveQueue() { if($('#playlistname').val().length > 0) { - sendAPI(JSON.stringify({"cmd":"MPD_API_SAVE_QUEUE","data":{"plist":$('#playlistname').val()}})); + sendAPI({"cmd":"MPD_API_SAVE_QUEUE","data":{"plist":$('#playlistname').val()}}); } $('#savequeue').modal('hide'); } @@ -1303,49 +1270,49 @@ function notificationsSupported() { } function songChange(title, artist, album, uri, songId) { - if (last_song == title+artist+album+uri+songId) + if (last_song == obj.data.title+obj.data.artist+obj.data.album+obj.data.uri+obj.data.currentsongid) return; var textNotification = ''; var htmlNotification = ''; var pageTitle = 'myMPD: '; - if (typeof uri != 'undefined' && uri.length > 0) { + if (typeof obj.data.uri != 'undefined' && obj.data.uri.length > 0) { var coverImg=''; - if (uri.indexOf('http://') == 0 || uri.indexOf('https://') == 0 ) { + if (obj.data.uri.indexOf('http://') == 0 || obj.data.uri.indexOf('https://') == 0 ) { coverImg='/assets/coverimage-httpstream.png'; } else if (coverImageFile != '') { - coverImg='/library/'+uri.replace(/\/[^\/]+$/,'\/'+coverImageFile); + coverImg='/library/'+obj.data.uri.replace(/\/[^\/]+$/,'\/'+coverImageFile); } else { coverImg='/assets/coverimage-notavailable.png'; } $('#album-cover').css('backgroundImage','url("'+coverImg+'"),url("/assets/coverimage-notavailable.png")'); } - if(typeof artist != 'undefined' && artist.length > 0 && artist != '-') { - textNotification += artist; - htmlNotification += '
' + artist; - pageTitle += artist + ' - '; - $('#artist').text(artist); + if(typeof obj.data.artist != 'undefined' && obj.data.artist.length > 0 && obj.data.artist != '-') { + textNotification += obj.data.artist; + htmlNotification += '
' + obj.data.artist; + pageTitle += obj.data.artist + ' - '; + $('#artist').text(obj.data.artist); } else { $('#artist').text(''); } - if(typeof album != 'undefined' && album.length > 0 && album != '-') { - textNotification += ' - ' + album; - htmlNotification += '
' + album; - $('#album').text(album); + if(typeof obj.data.album != 'undefined' && obj.data.album.length > 0 && obj.data.album != '-') { + textNotification += ' - ' + obj.data.album; + htmlNotification += '
' + obj.data.album; + $('#album').text(obj.data.album); } else { $('#album').text(''); } - if(typeof title != 'undefined' && title.length > 0) { - pageTitle += title; - $('#currenttrack').text(title); + if(typeof obj.data.title != 'undefined' && obj.data.title.length > 0) { + pageTitle += obj.data.title; + $('#currenttrack').text(obj.data.title); } else { $('#currenttrack').text(''); } document.title = pageTitle; - showNotification(title,textNotification,htmlNotification,'success'); - last_song = title+artist+album+uri+songId; + showNotification(obj.data.title,textNotification,htmlNotification,'success'); + last_song = obj.data.title+obj.data.artist+obj.data.album+obj.data.uri+obj.data.currentsongid; } @@ -1355,10 +1322,10 @@ $(document).keydown(function(e){ } switch (e.which) { case 37: //left - sendAPI(JSON.stringify({"cmd":"MPD_API_SET_PREV"})); + sendAPI({"cmd":"MPD_API_SET_PREV"}); break; case 39: //right - sendAPI(JSON.stringify({"cmd":"MPD_API_SET_NEXT"})); + sendAPI({"cmd":"MPD_API_SET_NEXT"}); break; case 32: //space clickPlay(); @@ -1410,7 +1377,7 @@ function chVolume (increment) { if (newValue<0) { newValue=0; } else if (newValue > 100) { newValue=100; } volumeBar.slider('setValue',newValue); - sendAPI(JSON.stringify({"cmd":"MPD_API_SET_VOLUME", "data": {"volume":newValue}})); + sendAPI({"cmd":"MPD_API_SET_VOLUME", "data": {"volume":newValue}}); } function beautifyDuration(x) { diff --git a/src/mpd_client.c b/src/mpd_client.c index 0b6b868..ae137ef 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -59,6 +59,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) int je, int_buf; float float_buf; char *p_charbuf1, *p_charbuf2; + FILE *fp; #ifdef DEBUG fprintf(stdout,"Got request: %s\n",msg.p); @@ -71,15 +72,73 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) return; switch(cmd_id) { + case MPD_API_SET_MYMPD_SETTINGS: + fp = fopen(mpd.statefile,"w"); + if(fp != NULL) { + fprintf(fp,"%.*s",msg.len,msg.p); + fclose(fp); + } else { + fprintf(stderr,"Cant write state file\n"); + } + break; + case MPD_API_GET_MYMPD_SETTINGS: + fp = fopen(mpd.statefile,"r"); + if(fp != NULL) { + fgets(mpd.buf, MAX_SIZE, fp); + fclose(fp); + n=strlen(mpd.buf); + } + break; + case MPD_API_SET_MPD_SETTINGS: + je = json_scanf(msg.p, msg.len, "{ data: { random:%u } }", &uint_buf1); + if (je == 1) + mpd_run_random(mpd.conn, uint_buf1); + + je = json_scanf(msg.p, msg.len, "{ data: { repeat:%u } }", &uint_buf1); + if (je == 1) + mpd_run_repeat(mpd.conn, uint_buf1); + + je = json_scanf(msg.p, msg.len, "{ data: { consume:%u } }", &uint_buf1); + if (je == 1) + mpd_run_consume(mpd.conn, uint_buf1); + + je = json_scanf(msg.p, msg.len, "{ data: { single:%u } }", &uint_buf1); + if (je == 1) + mpd_run_single(mpd.conn, uint_buf1); + + je = json_scanf(msg.p, msg.len, "{ data: { crossfade:%u } }", &uint_buf1); + if (je == 1) + mpd_run_crossfade(mpd.conn, uint_buf1); + + je = json_scanf(msg.p, msg.len, "{ data: { mixrampdb:%f } }", &float_buf); + if (je == 1) + mpd_run_mixrampdb(mpd.conn, float_buf); + + je = json_scanf(msg.p, msg.len, "{ data: { mixrampdelay:%f } }", &float_buf); + if (je == 1) + mpd_run_mixrampdelay(mpd.conn, float_buf); + + je = json_scanf(msg.p, msg.len, "{ data: { replaygain:%Q } }", &p_charbuf1); + if (je == 1) + { + mpd_send_command(mpd.conn, "replay_gain_mode", p_charbuf1, NULL); + struct mpd_pair *pair; + while ((pair = mpd_recv_pair(mpd.conn)) != NULL) { + mpd_return_pair(mpd.conn, pair); + } + } + free(p_charbuf1); + break; case MPD_API_GET_ARTISTALBUMTITLES: je = json_scanf(msg.p, msg.len, "{ data: { albumartist:%Q, album:%Q } }", &p_charbuf1, &p_charbuf2); if (je == 2) n = mympd_put_songs_in_album(mpd.buf, p_charbuf1, p_charbuf2); free(p_charbuf1); free(p_charbuf2); - break; + break; case MPD_API_WELCOME: - n = mympd_put_welcome(mpd.buf); + n = mympd_put_welcome(mpd.buf); + break; case MPD_API_UPDATE_DB: mpd_run_update(mpd.conn, NULL); break; @@ -125,41 +184,6 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) if (je == 1) mpd_run_play_id(mpd.conn, uint_buf1); break; - case MPD_API_TOGGLE_RANDOM: - je = json_scanf(msg.p, msg.len, "{ data: { state:%u } }", &uint_buf1); - if (je == 1) - mpd_run_random(mpd.conn, uint_buf1); - break; - case MPD_API_TOGGLE_REPEAT: - je = json_scanf(msg.p, msg.len, "{ data: { state:%u } }", &uint_buf1); - if (je == 1) - mpd_run_repeat(mpd.conn, uint_buf1); - break; - case MPD_API_TOGGLE_CONSUME: - je = json_scanf(msg.p, msg.len, "{ data: { state:%u } }", &uint_buf1); - if (je == 1) - mpd_run_consume(mpd.conn, uint_buf1); - break; - case MPD_API_TOGGLE_SINGLE: - je = json_scanf(msg.p, msg.len, "{ data: { state:%u } }", &uint_buf1); - if (je == 1) - mpd_run_single(mpd.conn, uint_buf1); - break; - case MPD_API_SET_CROSSFADE: - je = json_scanf(msg.p, msg.len, "{ data: { state:%u } }", &uint_buf1); - if (je == 1) - mpd_run_crossfade(mpd.conn, uint_buf1); - break; - case MPD_API_SET_MIXRAMPDB: - je = json_scanf(msg.p, msg.len, "{ data: { state:%f } }", &float_buf); - if (je == 1) - mpd_run_mixrampdb(mpd.conn, float_buf); - break; - case MPD_API_SET_MIXRAMPDELAY: - je = json_scanf(msg.p, msg.len, "{ data: { state:%f } }", &float_buf); - if (je == 1) - mpd_run_mixrampdelay(mpd.conn, float_buf); - break; case MPD_API_GET_OUTPUTNAMES: n = mympd_put_outputnames(mpd.buf); break; @@ -281,24 +305,12 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) mpd_run_rm(mpd.conn, p_charbuf1); free(p_charbuf1); break; - case MPD_API_GET_SETTINGS: + case MPD_API_GET_MPD_SETTINGS: n = mympd_put_settings(mpd.buf); break; case MPD_API_GET_STATS: n = mympd_get_stats(mpd.buf); break; - case MPD_API_SET_REPLAYGAIN: - je = json_scanf(msg.p, msg.len, "{ data: { mode:%Q } }", &p_charbuf1); - if (je == 1) - { - mpd_send_command(mpd.conn, "replay_gain_mode", p_charbuf1, NULL); - struct mpd_pair *pair; - while ((pair = mpd_recv_pair(mpd.conn)) != NULL) { - mpd_return_pair(mpd.conn, pair); - } - } - free(p_charbuf1); - break; } free(cmd); diff --git a/src/mpd_client.h b/src/mpd_client.h index 6086f01..b95795f 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -69,24 +69,19 @@ X(MPD_API_UPDATE_DB) \ X(MPD_API_GET_OUTPUTNAMES) \ X(MPD_API_TOGGLE_OUTPUT) \ - X(MPD_API_TOGGLE_RANDOM) \ - X(MPD_API_TOGGLE_CONSUME) \ - X(MPD_API_TOGGLE_SINGLE) \ - X(MPD_API_SET_CROSSFADE) \ - X(MPD_API_TOGGLE_REPEAT) \ - X(MPD_API_GET_SETTINGS) \ X(MPD_API_SEND_SHUFFLE) \ X(MPD_API_GET_STATS) \ - X(MPD_API_SET_MIXRAMPDB) \ - X(MPD_API_SET_MIXRAMPDELAY) \ X(MPD_API_GET_PLAYLISTS) \ X(MPD_API_RM_PLAYLIST) \ - X(MPD_API_SET_REPLAYGAIN) \ X(MPD_API_GET_ARTISTALBUMS) \ X(MPD_API_GET_ARTISTALBUMTITLES) \ X(MPD_API_GET_ARTISTS) \ X(MPD_API_GET_CURRENT_SONG) \ - X(MPD_API_WELCOME) + X(MPD_API_WELCOME) \ + X(MPD_API_GET_MPD_SETTINGS) \ + X(MPD_API_SET_MPD_SETTINGS) \ + X(MPD_API_SET_MYMPD_SETTINGS) \ + X(MPD_API_GET_MYMPD_SETTINGS) enum mpd_cmd_ids { MPD_CMDS(GEN_ENUM) @@ -102,10 +97,9 @@ enum mpd_conn_states { struct t_mpd { int port; - int local_port; char host[128]; char *password; - char *gpass; + char *statefile; struct mpd_connection *conn; enum mpd_conn_states conn_state; diff --git a/src/mympd.c b/src/mympd.c index 2fb81c2..382e554 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -105,11 +105,10 @@ int main(int argc, char **argv) char *run_as_user = NULL; char *webport = "8080"; mpd.port = 6600; - mpd.local_port = 0; - mpd.gpass = NULL; strcpy(mpd.host, "127.0.0.1"); streamport = 8000; strcpy(coverimage, "folder.jpg"); + mpd.statefile="/var/lib/mympd/mympd.state"; static struct option long_options[] = { {"host", required_argument, 0, 'h'}, @@ -121,14 +120,15 @@ int main(int argc, char **argv) {"mpdpass", required_argument, 0, 'm'}, {"streamport", required_argument, 0, 's'}, {"coverimage", required_argument, 0, 'i'}, + {"statefile", required_argument, 0, 't'}, {0, 0, 0, 0 } }; - while((n = getopt_long(argc, argv, "D:h:p:w:u:vm:s:i:c:", + while((n = getopt_long(argc, argv, "D:h:p:w:u:vm:s:i:c:t:", long_options, &option_index)) != -1) { switch (n) { - case 'D': - mpd.gpass = strdup(optarg); + case 't': + mpd.statefile = strdup(optarg); break; case 'h': strncpy(mpd.host, optarg, sizeof(mpd.host)); @@ -169,6 +169,7 @@ int main(int argc, char **argv) " -m, --mpdpass \tspecifies the password to use when connecting to mpd\n" " -s, --streamport \tconnect to mpd http stream at port [8000]\n" " -i, --coverimage \tfilename for coverimage [folder.jpg]\n" + " -t, --statefile \tstatefile [/var/lib/mympd/mympd.state]\n" " --help\t\t\t\tthis help\n" , argv[0]); return EXIT_FAILURE; From 39e963804e95e2e26a1a0efdd4edb640a640dafe Mon Sep 17 00:00:00 2001 From: jcorporation Date: Wed, 20 Jun 2018 00:23:08 +0100 Subject: [PATCH 19/32] Fix ajax callback --- htdocs/js/mpd.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index 12bdbe9..2745531 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -928,11 +928,7 @@ function updatePlayIcon(obj) { } function sendAPI(request, callback) { - $.ajax({url: "/api", contentType:"application/json", method: "POST", data: JSON.stringify(request)}).done(function(obj,callback) { - if (callback != undefined && typeof callback == 'function') { - callback(obj); - } - }); + $.ajax({url: "/api", contentType:"application/json", method: "POST", data: JSON.stringify(request), success: callback }); } function updateDB(event) { @@ -1269,10 +1265,9 @@ function notificationsSupported() { return "Notification" in window; } -function songChange(title, artist, album, uri, songId) { +function songChange(obj) { if (last_song == obj.data.title+obj.data.artist+obj.data.album+obj.data.uri+obj.data.currentsongid) return; - var textNotification = ''; var htmlNotification = ''; var pageTitle = 'myMPD: '; From 4fdacde42acb7a546cfbff7d33c84faec8909a64 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Wed, 20 Jun 2018 23:00:46 +0100 Subject: [PATCH 20/32] Fixed local player - separated js from html - no cookies --- htdocs/index.html | 2 +- htdocs/js/mpd.js | 16 ++++++++++------ htdocs/js/player.js | 5 +++++ htdocs/player.html | 11 ++--------- 4 files changed, 18 insertions(+), 16 deletions(-) create mode 100644 htdocs/js/player.js diff --git a/htdocs/index.html b/htdocs/index.html index fdfd9e1..4949f4c 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -31,7 +31,7 @@ Add Stream Update Database - Local Player + Local Player Settings About diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index 2745531..86c7405 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -33,7 +33,7 @@ var playstate = ''; var progressBar; var volumeBar; var coverImageFile = ''; -var settings = { "notifyPage":1, "notifyWeb": 0 }; +var settings = { "notifyPage":1, "notifyWeb": 0, "mpdstream":"" }; var app = {}; @@ -80,22 +80,25 @@ app.prepare=function() { } app.goto=function(a,t,v,s) { + var hash=''; if (app.apps[a].tabs) { if (t == undefined) t = app.apps[a].active; if (app.apps[a].tabs[t].views) { if (v == undefined) v = app.apps[a].tabs[t].active; - location.hash = '/'+a+'/'+t+'/'+v+'!'+ (s == undefined ? app.apps[a].tabs[t].views[v].state : s); + hash = '/'+a+'/'+t+'/'+v+'!'+ (s == undefined ? app.apps[a].tabs[t].views[v].state : s); } else { - location.hash = '/'+a+'/'+t+'!'+ (s == undefined ? app.apps[a].tabs[t].state : s); + hash = '/'+a+'/'+t+'!'+ (s == undefined ? app.apps[a].tabs[t].state : s); } } else { - location.hash = '/'+a+'!'+ (s == undefined ? app.apps[a].state : s); + hash = '/'+a+'!'+ (s == undefined ? app.apps[a].state : s); } + location.hash=hash; } app.route=function() { var hash=decodeURI(location.hash); - if (params=hash.match(/^\#\/(\w+)\/?(\w+)?\/?(\w+)?\!((\d+)\/([^\/]+)\/(.*))$/)) { + if (params=hash.match(/^\#\/(\w+)\/?(\w+)?\/?(\w+)?\!((\d+)\/([^\/]+)\/(.*))$/)) + { app.current.app = params[1]; app.current.tab = params[2]; app.current.view = params[3]; @@ -963,7 +966,8 @@ function setLocalStream(mpdhost,streamport) { else mpdstream += mpdhost; mpdstream += ':'+streamport+'/'; - Cookies.set('mpdstream', mpdstream, { expires: 424242 }); + settings.mpdstream=mpdstream; +// Cookies.set('mpdstream', mpdstream, { expires: 424242 }); } function delQueueSong(tr,event) { diff --git a/htdocs/js/player.js b/htdocs/js/player.js new file mode 100644 index 0000000..b02e298 --- /dev/null +++ b/htdocs/js/player.js @@ -0,0 +1,5 @@ +var mpdstream = decodeURI(location.hash).replace(/^#/,''); +player.src = mpdstream; +console.log('playing mpd stream: ' + player.src); +player.load(); +player.play(); diff --git a/htdocs/player.html b/htdocs/player.html index 65af38f..bdfb6f4 100644 --- a/htdocs/player.html +++ b/htdocs/player.html @@ -30,14 +30,7 @@ - - - + + From aac7605acb63123302312a1c334b37268f83164a Mon Sep 17 00:00:00 2001 From: jcorporation Date: Wed, 20 Jun 2018 23:16:22 +0100 Subject: [PATCH 21/32] Optimized table refresh --- htdocs/js/mpd.js | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index 86c7405..809c640 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -521,7 +521,7 @@ function parseQueue(obj) { "" + ""; - if (nrItems <= tr.length) { $(tr[nrItems-1]).replaceWith(row); } + if (nrItems <= tr.length) { if ($(tr[nrItems-1]).attr('trackid')!=obj.data[song].id) $(tr[nrItems-1]).replaceWith(row); } else { $('#'+app.current.app+'List > tbody').append(row); } } for (var i=tr.length;i>nrItems;i--) { @@ -604,9 +604,11 @@ function parseFilesystem(obj) { for (var item in obj.data) { nrItems++; var row=''; + var uri=''; switch(obj.data[item].type) { case 'directory': - row ='' + + uri=encodeURI(obj.data[item].dir); + row ='' + '' + '' + ''; @@ -614,7 +616,8 @@ function parseFilesystem(obj) { case 'song': var minutes = Math.floor(obj.data[item].duration / 60); var seconds = obj.data[item].duration - minutes * 60; - row ='' + + uri=encodeURI(obj.data[item].uri); + row ='' + '' + '' + '' + @@ -623,13 +626,14 @@ function parseFilesystem(obj) { ''; break; case 'playlist': - row ='' + + uri=encodeURI(obj.data[item].plist); + row ='' + '' + '' + ''; break; } - if (nrItems <= tr.length) { $(tr[nrItems-1]).replaceWith(row); } + if (nrItems <= tr.length) { if ($(tr[nrItems-1]).attr('uri') != uri) $(tr[nrItems-1]).replaceWith(row); } else { $('#'+app.current.app+(app.current.tab==undefined ? '' : app.current.tab)+'List > tbody').append(row); } } for (var i=tr.length;i>nrItems;i--) { @@ -711,11 +715,12 @@ function parsePlaylists(obj) { for (var item in obj.data) { nrItems++; var d = new Date(obj.data[item].last_modified * 1000); - var row='' + + var uri=encodeURI(obj.data[item].plist); + var row='' + '' + '' + ''; - if (nrItems <= tr.length) { $(tr[nrItems-1]).replaceWith(row); } + if (nrItems <= tr.length) { if ($(tr[nrItems-1]).attr('uri') != uri) $(tr[nrItems-1]).replaceWith(row); } else { $('#'+app.current.app+app.current.tab+'List > tbody').append(row); } } for (var i=tr.length;i>nrItems;i--) { @@ -769,10 +774,11 @@ function parseListDBtags(obj) { var tr=document.getElementById(app.current.app+app.current.tab+app.current.view+'List').getElementsByTagName('tbody')[0].getElementsByTagName('tr'); for (var item in obj.data) { nrItems++; - var row='' + + var uri=encodeURI(obj.data[item].value); + var row='' + '' + ''; - if (nrItems <= tr.length) { $(tr[nrItems-1]).replaceWith(row); } + if (nrItems <= tr.length) { if ($(tr[nrItems-1]).attr('uri')!=uri) $(tr[nrItems-1]).replaceWith(row); } else { $('#'+app.current.app+app.current.tab+app.current.view+'List > tbody').append(row); } } @@ -967,7 +973,6 @@ function setLocalStream(mpdhost,streamport) { mpdstream += mpdhost; mpdstream += ':'+streamport+'/'; settings.mpdstream=mpdstream; -// Cookies.set('mpdstream', mpdstream, { expires: 424242 }); } function delQueueSong(tr,event) { From b790a687a38db8a30ce2e720914ecba66d91bde2 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Wed, 20 Jun 2018 23:46:52 +0100 Subject: [PATCH 22/32] Fixed invalid free for run_as_user --- src/mympd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mympd.c b/src/mympd.c index 382e554..cfab0b6 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -203,7 +203,6 @@ int main(int argc, char **argv) printf("setuid() failed\n"); return EXIT_FAILURE; } - free(run_as_user); } mg_set_protocol_http_websocket(nc); From c11bfa0d1adeaf25bced0c87d21619b49b5d3fc2 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 21 Jun 2018 17:57:40 +0100 Subject: [PATCH 23/32] Save myMPD settings in state file, not in cookies --- htdocs/index.html | 1 - htdocs/js/js.cookie-2.2.0.js | 165 --------- htdocs/js/js.cookie-2.2.0.min.js | 3 - htdocs/js/mpd.js | 188 +++++----- htdocs/player.html | 4 +- src/frozen/frozen.c | 566 +++++++++++++++++++++++++++---- src/frozen/frozen.h | 129 ++++++- src/mpd_client.c | 45 ++- src/mpd_client.h | 6 +- 9 files changed, 716 insertions(+), 391 deletions(-) delete mode 100644 htdocs/js/js.cookie-2.2.0.js delete mode 100644 htdocs/js/js.cookie-2.2.0.min.js diff --git a/htdocs/index.html b/htdocs/index.html index 4949f4c..e03973a 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -687,7 +687,6 @@ - diff --git a/htdocs/js/js.cookie-2.2.0.js b/htdocs/js/js.cookie-2.2.0.js deleted file mode 100644 index 9a0945e..0000000 --- a/htdocs/js/js.cookie-2.2.0.js +++ /dev/null @@ -1,165 +0,0 @@ -/*! - * JavaScript Cookie v2.2.0 - * https://github.com/js-cookie/js-cookie - * - * Copyright 2006, 2015 Klaus Hartl & Fagner Brack - * Released under the MIT license - */ -;(function (factory) { - var registeredInModuleLoader = false; - if (typeof define === 'function' && define.amd) { - define(factory); - registeredInModuleLoader = true; - } - if (typeof exports === 'object') { - module.exports = factory(); - registeredInModuleLoader = true; - } - if (!registeredInModuleLoader) { - var OldCookies = window.Cookies; - var api = window.Cookies = factory(); - api.noConflict = function () { - window.Cookies = OldCookies; - return api; - }; - } -}(function () { - function extend () { - var i = 0; - var result = {}; - for (; i < arguments.length; i++) { - var attributes = arguments[ i ]; - for (var key in attributes) { - result[key] = attributes[key]; - } - } - return result; - } - - function init (converter) { - function api (key, value, attributes) { - var result; - if (typeof document === 'undefined') { - return; - } - - // Write - - if (arguments.length > 1) { - attributes = extend({ - path: '/' - }, api.defaults, attributes); - - if (typeof attributes.expires === 'number') { - var expires = new Date(); - expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5); - attributes.expires = expires; - } - - // We're using "expires" because "max-age" is not supported by IE - attributes.expires = attributes.expires ? attributes.expires.toUTCString() : ''; - - try { - result = JSON.stringify(value); - if (/^[\{\[]/.test(result)) { - value = result; - } - } catch (e) {} - - if (!converter.write) { - value = encodeURIComponent(String(value)) - .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent); - } else { - value = converter.write(value, key); - } - - key = encodeURIComponent(String(key)); - key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent); - key = key.replace(/[\(\)]/g, escape); - - var stringifiedAttributes = ''; - - for (var attributeName in attributes) { - if (!attributes[attributeName]) { - continue; - } - stringifiedAttributes += '; ' + attributeName; - if (attributes[attributeName] === true) { - continue; - } - stringifiedAttributes += '=' + attributes[attributeName]; - } - return (document.cookie = key + '=' + value + stringifiedAttributes); - } - - // Read - - if (!key) { - result = {}; - } - - // To prevent the for loop in the first place assign an empty array - // in case there are no cookies at all. Also prevents odd result when - // calling "get()" - var cookies = document.cookie ? document.cookie.split('; ') : []; - var rdecode = /(%[0-9A-Z]{2})+/g; - var i = 0; - - for (; i < cookies.length; i++) { - var parts = cookies[i].split('='); - var cookie = parts.slice(1).join('='); - - if (!this.json && cookie.charAt(0) === '"') { - cookie = cookie.slice(1, -1); - } - - try { - var name = parts[0].replace(rdecode, decodeURIComponent); - cookie = converter.read ? - converter.read(cookie, name) : converter(cookie, name) || - cookie.replace(rdecode, decodeURIComponent); - - if (this.json) { - try { - cookie = JSON.parse(cookie); - } catch (e) {} - } - - if (key === name) { - result = cookie; - break; - } - - if (!key) { - result[name] = cookie; - } - } catch (e) {} - } - - return result; - } - - api.set = api; - api.get = function (key) { - return api.call(api, key); - }; - api.getJSON = function () { - return api.apply({ - json: true - }, [].slice.call(arguments)); - }; - api.defaults = {}; - - api.remove = function (key, attributes) { - api(key, '', extend(attributes, { - expires: -1 - })); - }; - - api.withConverter = init; - - return api; - } - - return init(function () {}); -})); diff --git a/htdocs/js/js.cookie-2.2.0.min.js b/htdocs/js/js.cookie-2.2.0.min.js deleted file mode 100644 index 69fecbb..0000000 --- a/htdocs/js/js.cookie-2.2.0.min.js +++ /dev/null @@ -1,3 +0,0 @@ -/*! js-cookie v2.2.0 | MIT */ - -!function(e){var n=!1;if("function"==typeof define&&define.amd&&(define(e),n=!0),"object"==typeof exports&&(module.exports=e(),n=!0),!n){var o=window.Cookies,t=window.Cookies=e();t.noConflict=function(){return window.Cookies=o,t}}}(function(){function e(){for(var e=0,n={};e1){if("number"==typeof(i=e({path:"/"},t.defaults,i)).expires){var a=new Date;a.setMilliseconds(a.getMilliseconds()+864e5*i.expires),i.expires=a}i.expires=i.expires?i.expires.toUTCString():"";try{c=JSON.stringify(r),/^[\{\[]/.test(c)&&(r=c)}catch(e){}r=o.write?o.write(r,n):encodeURIComponent(r+"").replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),n=(n=(n=encodeURIComponent(n+"")).replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent)).replace(/[\(\)]/g,escape);var s="";for(var f in i)i[f]&&(s+="; "+f,!0!==i[f]&&(s+="="+i[f]));return document.cookie=n+"="+r+s}n||(c={});for(var p=document.cookie?document.cookie.split("; "):[],d=/(%[0-9A-Z]{2})+/g,u=0;u input').focus(); }); - if(!notificationsSupported()) - $('#btnnotifyWeb').addClass("disabled"); - else - if (Cookies.get('notificationWeb') === 'true') - $('#btnnotifyWeb').removeClass('btn-secondary').addClass("btn-success") - - if (Cookies.get('notificationPage') === 'true') - $('#btnnotifyPage').removeClass('btn-secondary').addClass("btn-success") + + + add_filter('#BrowseFilesystemFilterLetters'); add_filter('#BrowseDatabaseFilterLetters'); @@ -311,9 +304,6 @@ function webSocketConnect() { case "song_change": songChange(obj); break; - case 'settings': - parseSettings(obj); - break; case 'error': showNotification(obj.data,'','','danger'); default: @@ -373,55 +363,73 @@ function parseStats(obj) { $('#mpdVersion').text(obj.data.mpd_version); } -function parseMympdSettings(obj) { - settings.notifyPage=obj.data.notifyPage; - settings.notifyWeb=obj.data.notifyWeb; - //try to set web notification -} - function parseSettings(obj) { - if (!isNaN(obj.data.max_elements_per_page)) - MAX_ELEMENTS_PER_PAGE=obj.data.max_elements_per_page; - - if(obj.data.random) - $('#btnrandom').removeClass('btn-secondary').addClass("btn-success") - else - $('#btnrandom').removeClass("btn-success").addClass("btn-secondary"); + if (obj.data.random) + $('#btnrandom').removeClass('btn-secondary').addClass("btn-success") + else + $('#btnrandom').removeClass("btn-success").addClass("btn-secondary"); - if(obj.data.consume) - $('#btnconsume').removeClass('btn-secondary').addClass("btn-success") - else - $('#btnconsume').removeClass("btn-success").addClass("btn-secondary"); + if (obj.data.consume) + $('#btnconsume').removeClass('btn-secondary').addClass("btn-success") + else + $('#btnconsume').removeClass("btn-success").addClass("btn-secondary"); - if(obj.data.single) - $('#btnsingle').removeClass('btn-secondary').addClass("btn-success") - else - $('#btnsingle').removeClass("btn-success").addClass("btn-secondary"); + if (obj.data.single) + $('#btnsingle').removeClass('btn-secondary').addClass("btn-success") + else + $('#btnsingle').removeClass("btn-success").addClass("btn-secondary"); - if(obj.data.crossfade != undefined) - $('#inputCrossfade').removeAttr('disabled').val(obj.data.crossfade); - else - $('#inputCrossfade').attr('disabled', 'disabled'); + if (obj.data.crossfade != undefined) + $('#inputCrossfade').removeAttr('disabled').val(obj.data.crossfade); + else + $('#inputCrossfade').attr('disabled', 'disabled'); - if(obj.data.mixrampdb != undefined) - $('#inputMixrampdb').removeAttr('disabled').val(obj.data.mixrampdb); - else - $('#inputMixrampdb').attr('disabled', 'disabled'); + if (obj.data.mixrampdb != undefined) + $('#inputMixrampdb').removeAttr('disabled').val(obj.data.mixrampdb); + else + $('#inputMixrampdb').attr('disabled', 'disabled'); - if(obj.data.mixrampdelay != undefined) - $('#inputMixrampdelay').removeAttr('disabled').val(obj.data.mixrampdelay); - else - $('#inputMixrampdb').attr('disabled', 'disabled'); + if (obj.data.mixrampdelay != undefined) + $('#inputMixrampdelay').removeAttr('disabled').val(obj.data.mixrampdelay); + else + $('#inputMixrampdb').attr('disabled', 'disabled'); - if(obj.data.repeat) - $('#btnrepeat').removeClass('btn-secondary').addClass("btn-success") - else - $('#btnrepeat').removeClass("btn-success").addClass("btn-secondary"); + if (obj.data.repeat) + $('#btnrepeat').removeClass('btn-secondary').addClass("btn-success") + else + $('#btnrepeat').removeClass("btn-success").addClass("btn-secondary"); - $("#selectReplaygain").val(obj.data.replaygain); + $("#selectReplaygain").val(obj.data.replaygain); + if (notificationsSupported()) { + if (obj.data.notificationWeb) { + $('#btnnotifyWeb').removeClass('btn-secondary').addClass("btn-success") + + Notification.requestPermission(function (permission) { + if(!('permission' in Notification)) + Notification.permission = permission; + if (permission === 'granted') { + $('#btnnotifyWeb').removeClass('btn-secondary').addClass('btn-success'); + } else { + $('#btnnotifyWeb').addClass('btn-secondary').removeClass('btn-success'); + obj.data.notificationWeb=0; + } + }); + } + else + $('#btnnotifyWeb').addClass('btn-secondary').removeClass("btn-success"); + } else { + $('#btnnotifyWeb').addClass("disabled"); + $('#btnnotifyWeb').addClass('btn-secondary').removeClass("btn-success"); + } + + if (obj.data.notificationPage) + $('#btnnotifyPage').removeClass('btn-secondary').addClass("btn-success") + else + $('#btnnotifyPage').addClass('btn-secondary').removeClass("btn-success"); + + settings=obj.data; setLocalStream(obj.data.mpdhost,obj.data.streamport); - coverImageFile=obj.data.coverimage; } function parseOutputnames(obj) { @@ -824,7 +832,7 @@ function parseListDBtags(obj) { function parseListTitles(obj) { if(app.current.app !== 'Browse' && app.current.tab !== 'Database' && app.current.view !== 'Album') return; var album=$('#'+genId(obj.album)+' > div > table > tbody'); - $('#'+genId(obj.album)+' > img').attr('src','/library/'+obj.data[0].uri.replace(/\/[^\/]+$/,'\/')+coverImageFile); + $('#'+genId(obj.album)+' > img').attr('src','/library/'+obj.data[0].uri.replace(/\/[^\/]+$/,'\/')+settings.coverimage); $('#'+genId(obj.album)+' > img').attr('uri',obj.data[0].uri.replace(/\/[^\/]+$/,'')); $('#'+genId(obj.album)+' > img').attr('data-album',encodeURI(obj.album)); var titleList=''; @@ -850,26 +858,26 @@ function parseListTitles(obj) { } function setPagination(number) { - var totalPages=Math.ceil(number / MAX_ELEMENTS_PER_PAGE); + var totalPages=Math.ceil(number / settings.max_elements_per_page); var cat=app.current.app+(app.current.tab==undefined ? '': app.current.tab); if (totalPages==0) { totalPages=1; } - $('#'+cat+'PaginationTopPage').text('Page '+(app.current.page / MAX_ELEMENTS_PER_PAGE + 1)+' / '+totalPages); - $('#'+cat+'PaginationBottomPage').text('Page '+(app.current.page / MAX_ELEMENTS_PER_PAGE + 1)+' / '+totalPages); + $('#'+cat+'PaginationTopPage').text('Page '+(app.current.page / settings.max_elements_per_page + 1)+' / '+totalPages); + $('#'+cat+'PaginationBottomPage').text('Page '+(app.current.page / settings.max_elements_per_page + 1)+' / '+totalPages); if (totalPages > 1) { $('#'+cat+'PaginationTopPage').removeClass('disabled').removeAttr('disabled'); $('#'+cat+'PaginationBottomPage').removeClass('disabled').removeAttr('disabled'); $('#'+cat+'PaginationTopPages').empty(); $('#'+cat+'PaginationBottomPages').empty(); for (var i=0;i'+(i+1)+''); - $('#'+cat+'PaginationBottomPages').append(''); + $('#'+cat+'PaginationTopPages').append(''); + $('#'+cat+'PaginationBottomPages').append(''); } } else { $('#'+cat+'PaginationTopPage').addClass('disabled').attr('disabled','disabled'); $('#'+cat+'PaginationBottomPage').addClass('disabled').attr('disabled','disabled'); } - if(number > app.current.page + MAX_ELEMENTS_PER_PAGE) { + if(number > app.current.page + settings.max_elements_per_page) { $('#'+cat+'PaginationTopNext').removeClass('disabled').removeAttr('disabled'); $('#'+cat+'PaginationBottomNext').removeClass('disabled').removeAttr('disabled'); $('#'+cat+'ButtonsBottom').removeClass('hide'); @@ -1013,10 +1021,16 @@ $('#btnconsume').on('click', function (e) { $('#btnsingle').on('click', function (e) { toggleBtn(this); }); - $('#btnrepeat').on('click', function (e) { toggleBtn(this); }); +$('#btnnotifyPage').on('click', function (e) { + toggleBtn(this); +}); +$('#btnnotifyWeb').on('click', function (e) { + toggleBtn(this); +}); + $('#cardBrowseNavFilesystem').on('click', function (e) { app.goto('Browse','Filesystem'); @@ -1100,7 +1114,7 @@ function confirmSettings() { } } if (formOK == true) { - sendAPI({"cmd":"MPD_API_SET_MPD_SETTINGS", "data": { + sendAPI({"cmd":"MPD_API_SET_SETTINGS", "data": { "consume": ($('#btnconsume').hasClass('btn-success') ? 1 : 0), "random": ($('#btnrandom').hasClass('btn-success') ? 1 : 0), "single": ($('#btnsingle').hasClass('btn-success') ? 1 : 0), @@ -1108,11 +1122,9 @@ function confirmSettings() { "replaygain": $('#selectReplaygain').val(), "crossfade": $('#inputCrossfade').val(), "mixrampdb": $('#inputMixrampdb').val(), - "mixrampdelay": $('#inputMixrampdelay').val() - }}); - sendAPI({"cmd":"MPD_API_SET_MYMPD_SETTINGS","data": { - "notificationsWeb": ($('#btnnotifyWeb').hasClass('btn-success') ? 1 : 0), - "notificationsPage": ($('#btnnotifyPage').hasClass('btn-success') ? 1 : 0) + "mixrampdelay": $('#inputMixrampdelay').val(), + "notificationWeb": ($('#btnnotifyWeb').hasClass('btn-success') ? 1 : 0), + "notificationPage": ($('#btnnotifyPage').hasClass('btn-success') ? 1 : 0) }}); $('#settings').modal('hide'); } @@ -1127,33 +1139,7 @@ $('#trashmodebtns > button').on('click', function(e) { $(this).removeClass("btn-secondary").addClass("btn-success"); }); -$('#btnnotifyWeb').on('click', function (e) { - if(Cookies.get('notificationWeb') === 'true') { - Cookies.set('notificationWeb', false, { expires: 424242 }); - $('#btnnotifyWeb').removeClass('btn-success').addClass('btn-secondary'); - } else { - Notification.requestPermission(function (permission) { - if(!('permission' in Notification)) { - Notification.permission = permission; - } - - if (permission === 'granted') { - Cookies.set('notificationWeb', true, { expires: 424242 }); - $('#btnnotifyWeb').removeClass('btn-secondary').addClass('btn-success'); - } - }); - } -}); - -$('#btnnotifyPage').on('click', function (e) { - if(Cookies.get("notificationPage") === 'true') { - Cookies.set("notificationPage", false, { expires: 424242 }); - $('#btnnotifyPage').removeClass('btn-success').addClass('btn-secondary'); - } else { - Cookies.set('notificationPage', true, { expires: 424242 }); - $('#btnnotifyPage').removeClass('btn-secondary').addClass('btn-success'); - } -}); + $('#search > input').keypress(function (event) { if ( event.which == 13 ) { @@ -1221,10 +1207,10 @@ function scrollToTop() { function gotoPage(x,element,event) { switch (x) { case "next": - app.current.page += MAX_ELEMENTS_PER_PAGE; + app.current.page += settings.max_elements_per_page; break; case "prev": - app.current.page -= MAX_ELEMENTS_PER_PAGE; + app.current.page -= settings.max_elements_per_page; if(app.current.page <= 0) app.current.page = 0; break; @@ -1251,13 +1237,13 @@ function saveQueue() { } function showNotification(notificationTitle,notificationText,notificationHtml,notificationType) { - if (Cookies.get('notificationWeb') === 'true') { + if (settings.notificationWeb == 1) { var notification = new Notification(notificationTitle, {icon: 'assets/favicon.ico', body: notificationText}); setTimeout(function(notification) { notification.close(); }, 3000, notification); } - if (Cookies.get('notificationPage') === 'true') { + if (settings.notificationPage == 1) { $.notify({ title: notificationTitle, message: notificationHtml},{ type: notificationType, offset: { y: 60, x:20 }, template: ' - - - + diff --git a/src/frozen/frozen.c b/src/frozen/frozen.c index 4b5e279..209a6bb 100644 --- a/src/frozen/frozen.c +++ b/src/frozen/frozen.c @@ -1,20 +1,19 @@ /* * Copyright (c) 2004-2013 Sergey Lyubka - * Copyright (c) 2013 Cesanta Software Limited + * Copyright (c) 2018 Cesanta Software Limited * All rights reserved * - * This library is dual-licensed: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. For the terms of this - * license, see . + * Licensed under the Apache License, Version 2.0 (the ""License""); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * You are free to use this library under the terms of the GNU General - * Public License, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * Alternatively, you can license this library under a commercial - * license, as set out in . + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an ""AS IS"" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ #define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005+ */ @@ -35,6 +34,8 @@ #endif #ifdef _WIN32 +#undef snprintf +#undef vsnprintf #define snprintf cs_win_snprintf #define vsnprintf cs_win_vsnprintf int cs_win_snprintf(char *str, size_t size, const char *format, ...); @@ -47,33 +48,27 @@ typedef unsigned _int64 uint64_t; #endif #define PRId64 "I64d" #define PRIu64 "I64u" -#if !defined(SIZE_T_FMT) -#if _MSC_VER >= 1310 -#define SIZE_T_FMT "Iu" -#else -#define SIZE_T_FMT "u" -#endif -#endif #else /* _WIN32 */ /* wants this for C++ */ #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif #include -#if !defined(SIZE_T_FMT) -#define SIZE_T_FMT "zu" -#endif #endif /* _WIN32 */ +#ifndef INT64_FMT #define INT64_FMT PRId64 +#endif +#ifndef UINT64_FMT #define UINT64_FMT PRIu64 +#endif #ifndef va_copy #define va_copy(x, y) x = y #endif #ifndef JSON_MAX_PATH_LEN -#define JSON_MAX_PATH_LEN 60 +#define JSON_MAX_PATH_LEN 256 #endif struct frozen { @@ -85,14 +80,14 @@ struct frozen { /* For callback API */ char path[JSON_MAX_PATH_LEN]; - int path_len; + size_t path_len; void *callback_data; json_walk_callback_t callback; }; struct fstate { const char *ptr; - int path_len; + size_t path_len; }; #define SET_STATE(fr, ptr, str, len) \ @@ -117,14 +112,15 @@ struct fstate { static int append_to_path(struct frozen *f, const char *str, int size) { int n = f->path_len; - f->path_len += - snprintf(f->path + f->path_len, sizeof(f->path) - (f->path_len + 1), - "%.*s", size, str); - + int left = sizeof(f->path) - n - 1; + if (size > left) size = left; + memcpy(f->path + n, str, size); + f->path[n + size] = '\0'; + f->path_len += size; return n; } -static void truncate_path(struct frozen *f, int len) { +static void truncate_path(struct frozen *f, size_t len) { f->path_len = len; f->path[len] = '\0'; } @@ -242,7 +238,7 @@ static int parse_string(struct frozen *f) { ch = *(unsigned char *) f->cur; len = get_utf8_char_len((unsigned char) ch); EXPECT(ch >= 32 && len > 0, JSON_STRING_INVALID); /* No control chars */ - EXPECT(len < left(f), JSON_STRING_INCOMPLETE); + EXPECT(len <= left(f), JSON_STRING_INCOMPLETE); if (ch == '\\') { EXPECT((n = get_escape_len(f->cur + 1, left(f))) > 0, n); len += n; @@ -288,9 +284,9 @@ static int parse_number(struct frozen *f) { static int parse_array(struct frozen *f) { int i = 0, current_path_len; char buf[20]; + CALL_BACK(f, JSON_TYPE_ARRAY_START, NULL, 0); TRY(test_and_skip(f, '[')); { - CALL_BACK(f, JSON_TYPE_ARRAY_START, NULL, 0); { SET_STATE(f, f->cur - 1, "", 0); while (cur(f) != ']') { @@ -374,9 +370,6 @@ static int parse_value(struct frozen *f) { /* key = identifier | string */ static int parse_key(struct frozen *f) { int ch = cur(f); -#if 0 - printf("%s [%.*s]\n", __func__, (int) (f->end - f->cur), f->cur); -#endif if (is_alpha(ch)) { TRY(parse_identifier(f)); } else if (ch == '"') { @@ -407,19 +400,17 @@ static int parse_pair(struct frozen *f) { /* object = '{' pair { ',' pair } '}' */ static int parse_object(struct frozen *f) { + CALL_BACK(f, JSON_TYPE_OBJECT_START, NULL, 0); TRY(test_and_skip(f, '{')); { - CALL_BACK(f, JSON_TYPE_OBJECT_START, NULL, 0); - { - SET_STATE(f, f->cur - 1, ".", 1); - while (cur(f) != '}') { - TRY(parse_pair(f)); - if (cur(f) == ',') f->cur++; - } - TRY(test_and_skip(f, '}')); - truncate_path(f, fstate.path_len); - CALL_BACK(f, JSON_TYPE_OBJECT_END, fstate.ptr, f->cur - fstate.ptr); + SET_STATE(f, f->cur - 1, ".", 1); + while (cur(f) != '}') { + TRY(parse_pair(f)); + if (cur(f) == ',') f->cur++; } + TRY(test_and_skip(f, '}')); + truncate_path(f, fstate.path_len); + CALL_BACK(f, JSON_TYPE_OBJECT_END, fstate.ptr, f->cur - fstate.ptr); } return 0; } @@ -430,7 +421,8 @@ static int doit(struct frozen *f) { return parse_value(f); } -static int json_encode_string(struct json_out *out, const char *p, size_t len) { +int json_escape(struct json_out *out, const char *p, size_t len) WEAK; +int json_escape(struct json_out *out, const char *p, size_t len) { size_t i, cl, n = 0; const char *hex_digits = "0123456789abcdef"; const char *specials = "btnvfr"; @@ -505,7 +497,7 @@ static int b64rev(int c) { } } -static uint8_t hexdec(const char *s) { +static unsigned char hexdec(const char *s) { #define HEXTOI(x) (x >= '0' && x <= '9' ? x - '0' : x - 'W') int a = tolower(*(const unsigned char *) s); int b = tolower(*(const unsigned char *) (s + 1)); @@ -569,7 +561,7 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) { skip += 2; } else if (fmt[1] == 'z' && fmt[2] == 'u') { size_t val = va_arg(ap, size_t); - snprintf(buf, sizeof(buf), "%" SIZE_T_FMT, val); + snprintf(buf, sizeof(buf), "%lu", (unsigned long) val); len += out->printer(out, buf, strlen(buf)); skip += 1; } else if (fmt[1] == 'M') { @@ -613,7 +605,7 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) { l = strlen(p); } len += out->printer(out, quote, 1); - len += json_encode_string(out, p, l); + len += json_escape(out, p, l); len += out->printer(out, quote, 1); } } else { @@ -628,30 +620,48 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) { * TODO(dfrank): reimplement %s and %.*s in order to avoid that. */ - const char *end_of_format_specifier = "sdfFgGlhuI.*-0123456789"; - size_t n = strspn(fmt + 1, end_of_format_specifier); + const char *end_of_format_specifier = "sdfFgGlhuIcx.*-0123456789"; + int n = strspn(fmt + 1, end_of_format_specifier); char *pbuf = buf; - size_t need_len; + int need_len, size = sizeof(buf); char fmt2[20]; - va_list sub_ap; - strncpy(fmt2, fmt, n + 1 > sizeof(fmt2) ? sizeof(fmt2) : n + 1); + va_list ap_copy; + strncpy(fmt2, fmt, + n + 1 > (int) sizeof(fmt2) ? sizeof(fmt2) : (size_t) n + 1); fmt2[n + 1] = '\0'; - va_copy(sub_ap, ap); - need_len = - vsnprintf(buf, sizeof(buf), fmt2, sub_ap) + 1 /* null-term */; - /* - * TODO(lsm): Fix windows & eCos code path here. Their vsnprintf - * implementation returns -1 on overflow rather needed size. - */ - if (need_len > sizeof(buf)) { + va_copy(ap_copy, ap); + need_len = vsnprintf(pbuf, size, fmt2, ap_copy); + va_end(ap_copy); + + if (need_len < 0) { + /* + * Windows & eCos vsnprintf implementation return -1 on overflow + * instead of needed size. + */ + pbuf = NULL; + while (need_len < 0) { + free(pbuf); + size *= 2; + if ((pbuf = (char *) malloc(size)) == NULL) break; + va_copy(ap_copy, ap); + need_len = vsnprintf(pbuf, size, fmt2, ap_copy); + va_end(ap_copy); + } + } else if (need_len >= (int) sizeof(buf)) { /* * resulting string doesn't fit into a stack-allocated buffer `buf`, * so we need to allocate a new buffer from heap and use it */ - pbuf = (char *) malloc(need_len); - va_copy(sub_ap, ap); - vsnprintf(pbuf, need_len, fmt2, sub_ap); + if ((pbuf = (char *) malloc(need_len + 1)) != NULL) { + va_copy(ap_copy, ap); + vsnprintf(pbuf, need_len + 1, fmt2, ap_copy); + va_end(ap_copy); + } + } + if (pbuf == NULL) { + buf[0] = '\0'; + pbuf = buf; } /* @@ -663,7 +673,6 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) { if ((n + 1 == strlen("%" PRId64) && strcmp(fmt2, "%" PRId64) == 0) || (n + 1 == strlen("%" PRIu64) && strcmp(fmt2, "%" PRIu64) == 0)) { (void) va_arg(ap, int64_t); - skip += strlen(PRIu64) - 1; } else if (strcmp(fmt2, "%.*s") == 0) { (void) va_arg(ap, int); (void) va_arg(ap, char *); @@ -704,6 +713,7 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) { } len += out->printer(out, quote, 1); } else { + len += out->printer(out, fmt, 1); fmt++; } } @@ -749,7 +759,8 @@ int json_printf_array(struct json_out *out, va_list *ap) { } #ifdef _WIN32 -int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap) WEAK; +int cs_win_vsnprintf(char *str, size_t size, const char *format, + va_list ap) WEAK; int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap) { int res = _vsnprintf(str, size, format, ap); va_end(ap); @@ -788,6 +799,7 @@ int json_walk(const char *json_string, int json_string_length, } struct scan_array_info { + int found; char path[JSON_MAX_PATH_LEN]; struct json_token *token; }; @@ -802,6 +814,7 @@ static void json_scanf_array_elem_cb(void *callback_data, const char *name, if (strcmp(path, info->path) == 0) { *info->token = *token; + info->found = 1; } } @@ -811,10 +824,11 @@ int json_scanf_array_elem(const char *s, int len, const char *path, int idx, struct json_token *token) { struct scan_array_info info; info.token = token; + info.found = 0; memset(token, 0, sizeof(*token)); snprintf(info.path, sizeof(info.path), "%s[%d]", path, idx); json_walk(s, len, json_scanf_array_elem_cb, &info); - return token->len; + return info.found ? token->len : -1; } struct json_scanf_info { @@ -856,6 +870,7 @@ static void json_scanf_cb(void *callback_data, const char *name, size_t name_len, const char *path, const struct json_token *token) { struct json_scanf_info *info = (struct json_scanf_info *) callback_data; + char buf[32]; /* Must be enough to hold numbers */ (void) name; (void) name_len; @@ -876,7 +891,17 @@ static void json_scanf_cb(void *callback_data, const char *name, switch (info->type) { case 'B': info->num_conversions++; - *(int *) info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0); + switch (sizeof(bool)) { + case sizeof(char): + *(char *) info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0); + break; + case sizeof(int): + *(int *) info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0); + break; + default: + /* should never be here */ + abort(); + } break; case 'M': { union { @@ -889,12 +914,21 @@ static void json_scanf_cb(void *callback_data, const char *name, } case 'Q': { char **dst = (char **) info->target; - int unescaped_len = json_unescape(token->ptr, token->len, NULL, 0); - if (unescaped_len >= 0 && - (*dst = (char *) malloc(unescaped_len + 1)) != NULL) { - info->num_conversions++; - json_unescape(token->ptr, token->len, *dst, unescaped_len); - (*dst)[unescaped_len] = '\0'; + if (token->type == JSON_TYPE_NULL) { + *dst = NULL; + } else { + int unescaped_len = json_unescape(token->ptr, token->len, NULL, 0); + if (unescaped_len >= 0 && + (*dst = (char *) malloc(unescaped_len + 1)) != NULL) { + info->num_conversions++; + if (json_unescape(token->ptr, token->len, *dst, unescaped_len) == + unescaped_len) { + (*dst)[unescaped_len] = '\0'; + } else { + free(*dst); + *dst = NULL; + } + } } break; } @@ -927,7 +961,12 @@ static void json_scanf_cb(void *callback_data, const char *name, *(struct json_token *) info->target = *token; break; default: - info->num_conversions += sscanf(token->ptr, info->fmt, info->target); + /* Before scanf, copy into tmp buffer in order to 0-terminate it */ + if (token->len < (int) sizeof(buf)) { + memcpy(buf, token->ptr, token->len); + buf[token->len] = '\0'; + info->num_conversions += sscanf(buf, info->fmt, info->target); + } break; } } @@ -992,3 +1031,380 @@ int json_scanf(const char *str, int len, const char *fmt, ...) { va_end(ap); return result; } + +int json_vfprintf(const char *file_name, const char *fmt, va_list ap) WEAK; +int json_vfprintf(const char *file_name, const char *fmt, va_list ap) { + int res = -1; + FILE *fp = fopen(file_name, "wb"); + if (fp != NULL) { + struct json_out out = JSON_OUT_FILE(fp); + res = json_vprintf(&out, fmt, ap); + fputc('\n', fp); + fclose(fp); + } + return res; +} + +int json_fprintf(const char *file_name, const char *fmt, ...) WEAK; +int json_fprintf(const char *file_name, const char *fmt, ...) { + int result; + va_list ap; + va_start(ap, fmt); + result = json_vfprintf(file_name, fmt, ap); + va_end(ap); + return result; +} + +char *json_fread(const char *path) WEAK; +char *json_fread(const char *path) { + FILE *fp; + char *data = NULL; + if ((fp = fopen(path, "rb")) == NULL) { + } else if (fseek(fp, 0, SEEK_END) != 0) { + fclose(fp); + } else { + long size = ftell(fp); + if (size > 0 && (data = (char *) malloc(size + 1)) != NULL) { + fseek(fp, 0, SEEK_SET); /* Some platforms might not have rewind(), Oo */ + if (fread(data, 1, size, fp) != (size_t) size) { + free(data); + data = NULL; + } else { + data[size] = '\0'; + } + } + fclose(fp); + } + return data; +} + +struct json_setf_data { + const char *json_path; + const char *base; /* Pointer to the source JSON string */ + int matched; /* Matched part of json_path */ + int pos; /* Offset of the mutated value begin */ + int end; /* Offset of the mutated value end */ + int prev; /* Offset of the previous token end */ +}; + +static int get_matched_prefix_len(const char *s1, const char *s2) { + int i = 0; + while (s1[i] && s2[i] && s1[i] == s2[i]) i++; + return i; +} + +static void json_vsetf_cb(void *userdata, const char *name, size_t name_len, + const char *path, const struct json_token *t) { + struct json_setf_data *data = (struct json_setf_data *) userdata; + int off, len = get_matched_prefix_len(path, data->json_path); + if (t->ptr == NULL) return; + off = t->ptr - data->base; + // printf("--%d %s %d\n", t->type, path, off); + if (len > data->matched) data->matched = len; + + /* + * If there is no exact path match, set the mutation position to tbe end + * of the object or array + */ + if (len < data->matched && data->pos == 0 && + (t->type == JSON_TYPE_OBJECT_END || t->type == JSON_TYPE_ARRAY_END)) { + data->pos = data->end = data->prev; + } + + /* Exact path match. Set mutation position to the value of this token */ + if (strcmp(path, data->json_path) == 0 && t->type != JSON_TYPE_OBJECT_START && + t->type != JSON_TYPE_ARRAY_START) { + data->pos = off; + data->end = off + t->len; + } + + /* + * For deletion, we need to know where the previous value ends, because + * we don't know where matched value key starts. + * When the mutation position is not yet set, remember each value end. + * When the mutation position is already set, but it is at the beginning + * of the object/array, we catch the end of the object/array and see + * whether the object/array start is closer then previously stored prev. + */ + if (data->pos == 0) { + data->prev = off + t->len; /* pos is not yet set */ + } else if ((t->ptr[0] == '[' || t->ptr[0] == '{') && off + 1 < data->pos && + off + 1 > data->prev) { + data->prev = off + 1; + } + (void) name; + (void) name_len; +} + +int json_vsetf(const char *s, int len, struct json_out *out, + const char *json_path, const char *json_fmt, va_list ap) WEAK; +int json_vsetf(const char *s, int len, struct json_out *out, + const char *json_path, const char *json_fmt, va_list ap) { + struct json_setf_data data; + memset(&data, 0, sizeof(data)); + data.json_path = json_path; + data.base = s; + data.end = len; + // printf("S:[%.*s] %s %p\n", len, s, json_path, json_fmt); + json_walk(s, len, json_vsetf_cb, &data); + // printf("-> %d %d %d\n", data.prev, data.pos, data.end); + if (json_fmt == NULL) { + /* Deletion codepath */ + json_printf(out, "%.*s", data.prev, s); + /* Trim comma after the value that begins at object/array start */ + if (s[data.prev - 1] == '{' || s[data.prev - 1] == '[') { + int i = data.end; + while (i < len && is_space(s[i])) i++; + if (s[i] == ',') data.end = i + 1; /* Point after comma */ + } + json_printf(out, "%.*s", len - data.end, s + data.end); + } else { + /* Modification codepath */ + int n, off = data.matched, depth = 0; + + /* Print the unchanged beginning */ + json_printf(out, "%.*s", data.pos, s); + + /* Add missing keys */ + while ((n = strcspn(&json_path[off], ".[")) > 0) { + if (s[data.prev - 1] != '{' && s[data.prev - 1] != '[' && depth == 0) { + json_printf(out, ","); + } + if (off > 0 && json_path[off - 1] != '.') break; + json_printf(out, "%.*Q:", 1, json_path + off); + off += n; + if (json_path[off] != '\0') { + json_printf(out, "%c", json_path[off] == '.' ? '{' : '['); + depth++; + off++; + } + } + /* Print the new value */ + json_vprintf(out, json_fmt, ap); + + /* Close brackets/braces of the added missing keys */ + for (; off > data.matched; off--) { + int ch = json_path[off]; + const char *p = ch == '.' ? "}" : ch == '[' ? "]" : ""; + json_printf(out, "%s", p); + } + + /* Print the rest of the unchanged string */ + json_printf(out, "%.*s", len - data.end, s + data.end); + } + return data.end > data.pos ? 1 : 0; +} + +int json_setf(const char *s, int len, struct json_out *out, + const char *json_path, const char *json_fmt, ...) WEAK; +int json_setf(const char *s, int len, struct json_out *out, + const char *json_path, const char *json_fmt, ...) { + int result; + va_list ap; + va_start(ap, json_fmt); + result = json_vsetf(s, len, out, json_path, json_fmt, ap); + va_end(ap); + return result; +} + +struct prettify_data { + struct json_out *out; + int level; + int last_token; +}; + +static void indent(struct json_out *out, int level) { + while (level-- > 0) out->printer(out, " ", 2); +} + +static void print_key(struct prettify_data *pd, const char *path, + const char *name, int name_len) { + if (pd->last_token != JSON_TYPE_INVALID && + pd->last_token != JSON_TYPE_ARRAY_START && + pd->last_token != JSON_TYPE_OBJECT_START) { + pd->out->printer(pd->out, ",", 1); + } + if (path[0] != '\0') pd->out->printer(pd->out, "\n", 1); + indent(pd->out, pd->level); + if (path[0] != '\0' && path[strlen(path) - 1] != ']') { + pd->out->printer(pd->out, "\"", 1); + pd->out->printer(pd->out, name, (int) name_len); + pd->out->printer(pd->out, "\"", 1); + pd->out->printer(pd->out, ": ", 2); + } +} + +static void prettify_cb(void *userdata, const char *name, size_t name_len, + const char *path, const struct json_token *t) { + struct prettify_data *pd = (struct prettify_data *) userdata; + switch (t->type) { + case JSON_TYPE_OBJECT_START: + case JSON_TYPE_ARRAY_START: + print_key(pd, path, name, name_len); + pd->out->printer(pd->out, t->type == JSON_TYPE_ARRAY_START ? "[" : "{", + 1); + pd->level++; + break; + case JSON_TYPE_OBJECT_END: + case JSON_TYPE_ARRAY_END: + pd->level--; + if (pd->last_token != JSON_TYPE_INVALID && + pd->last_token != JSON_TYPE_ARRAY_START && + pd->last_token != JSON_TYPE_OBJECT_START) { + pd->out->printer(pd->out, "\n", 1); + indent(pd->out, pd->level); + } + pd->out->printer(pd->out, t->type == JSON_TYPE_ARRAY_END ? "]" : "}", 1); + break; + case JSON_TYPE_NUMBER: + case JSON_TYPE_NULL: + case JSON_TYPE_TRUE: + case JSON_TYPE_FALSE: + case JSON_TYPE_STRING: + print_key(pd, path, name, name_len); + if (t->type == JSON_TYPE_STRING) pd->out->printer(pd->out, "\"", 1); + pd->out->printer(pd->out, t->ptr, t->len); + if (t->type == JSON_TYPE_STRING) pd->out->printer(pd->out, "\"", 1); + break; + default: + break; + } + pd->last_token = t->type; +} + +int json_prettify(const char *s, int len, struct json_out *out) WEAK; +int json_prettify(const char *s, int len, struct json_out *out) { + struct prettify_data pd = {out, 0, JSON_TYPE_INVALID}; + return json_walk(s, len, prettify_cb, &pd); +} + +int json_prettify_file(const char *file_name) WEAK; +int json_prettify_file(const char *file_name) { + int res = -1; + char *s = json_fread(file_name); + FILE *fp; + if (s != NULL && (fp = fopen(file_name, "wb")) != NULL) { + struct json_out out = JSON_OUT_FILE(fp); + res = json_prettify(s, strlen(s), &out); + if (res < 0) { + /* On error, restore the old content */ + fclose(fp); + fp = fopen(file_name, "wb"); + fseek(fp, 0, SEEK_SET); + fwrite(s, 1, strlen(s), fp); + } else { + fputc('\n', fp); + } + fclose(fp); + } + free(s); + return res; +} + +struct next_data { + void *handle; // Passed handle. Changed if a next entry is found + const char *path; // Path to the iterated object/array + int path_len; // Path length - optimisation + int found; // Non-0 if found the next entry + struct json_token *key; // Object's key + struct json_token *val; // Object's value + int *idx; // Array index +}; + +static void next_set_key(struct next_data *d, const char *name, int name_len, + int is_array) { + if (is_array) { + /* Array. Set index and reset key */ + if (d->key != NULL) { + d->key->len = 0; + d->key->ptr = NULL; + } + if (d->idx != NULL) *d->idx = atoi(name); + } else { + /* Object. Set key and make index -1 */ + if (d->key != NULL) { + d->key->ptr = name; + d->key->len = name_len; + } + if (d->idx != NULL) *d->idx = -1; + } +} + +static void next_cb(void *userdata, const char *name, size_t name_len, + const char *path, const struct json_token *t) { + struct next_data *d = (struct next_data *) userdata; + const char *p = path + d->path_len; + if (d->found) return; + if (d->path_len >= (int) strlen(path)) return; + if (strncmp(d->path, path, d->path_len) != 0) return; + if (strchr(p + 1, '.') != NULL) return; /* More nested objects - skip */ + if (strchr(p + 1, '[') != NULL) return; /* Ditto for arrays */ + // {OBJECT,ARRAY}_END types do not pass name, _START does. Save key. + if (t->type == JSON_TYPE_OBJECT_START || t->type == JSON_TYPE_ARRAY_START) { + // printf("SAV %s %d %p\n", path, t->type, t->ptr); + next_set_key(d, name, name_len, p[0] == '['); + } else if (d->handle == NULL || d->handle < (void *) t->ptr) { + // printf("END %s %d %p\n", path, t->type, t->ptr); + if (t->type != JSON_TYPE_OBJECT_END && t->type != JSON_TYPE_ARRAY_END) { + next_set_key(d, name, name_len, p[0] == '['); + } + if (d->val != NULL) *d->val = *t; + d->handle = (void *) t->ptr; + d->found = 1; + } +} + +static void *json_next(const char *s, int len, void *handle, const char *path, + struct json_token *key, struct json_token *val, int *i) { + struct json_token tmpval, *v = val == NULL ? &tmpval : val; + struct json_token tmpkey, *k = key == NULL ? &tmpkey : key; + int tmpidx, *pidx = i == NULL ? &tmpidx : i; + struct next_data data = {handle, path, strlen(path), 0, k, v, pidx}; + json_walk(s, len, next_cb, &data); + return data.found ? data.handle : NULL; +} + +void *json_next_key(const char *s, int len, void *handle, const char *path, + struct json_token *key, struct json_token *val) WEAK; +void *json_next_key(const char *s, int len, void *handle, const char *path, + struct json_token *key, struct json_token *val) { + return json_next(s, len, handle, path, key, val, NULL); +} + +void *json_next_elem(const char *s, int len, void *handle, const char *path, + int *idx, struct json_token *val) WEAK; +void *json_next_elem(const char *s, int len, void *handle, const char *path, + int *idx, struct json_token *val) { + return json_next(s, len, handle, path, NULL, val, idx); +} + +static int json_sprinter(struct json_out *out, const char *str, size_t len) { + size_t old_len = out->u.buf.buf == NULL ? 0 : strlen(out->u.buf.buf); + size_t new_len = len + old_len; + char *p = (char *) realloc(out->u.buf.buf, new_len + 1); + if (p != NULL) { + memcpy(p + old_len, str, len); + p[new_len] = '\0'; + out->u.buf.buf = p; + } + return len; +} + +char *json_vasprintf(const char *fmt, va_list ap) WEAK; +char *json_vasprintf(const char *fmt, va_list ap) { + struct json_out out; + memset(&out, 0, sizeof(out)); + out.printer = json_sprinter; + json_vprintf(&out, fmt, ap); + return out.u.buf.buf; +} + +char *json_asprintf(const char *fmt, ...) WEAK; +char *json_asprintf(const char *fmt, ...) { + char *result = NULL; + va_list ap; + va_start(ap, fmt); + result = json_vasprintf(fmt, ap); + va_end(ap); + return result; +} diff --git a/src/frozen/frozen.h b/src/frozen/frozen.h index 6822d33..a46b576 100644 --- a/src/frozen/frozen.h +++ b/src/frozen/frozen.h @@ -1,20 +1,19 @@ /* * Copyright (c) 2004-2013 Sergey Lyubka - * Copyright (c) 2013 Cesanta Software Limited + * Copyright (c) 2018 Cesanta Software Limited * All rights reserved * - * This library is dual-licensed: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. For the terms of this - * license, see . + * Licensed under the Apache License, Version 2.0 (the ""License""); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * You are free to use this library under the terms of the GNU General - * Public License, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * Alternatively, you can license this library under a commercial - * license, as set out in . + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an ""AS IS"" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ #ifndef CS_FROZEN_FROZEN_H_ @@ -28,6 +27,13 @@ extern "C" { #include #include +#if defined(_WIN32) && _MSC_VER < 1700 +typedef int bool; +enum { false = 0, true = 1 }; +#else +#include +#endif + /* JSON token type */ enum json_token_type { JSON_TYPE_INVALID = 0, /* memsetting to 0 should create INVALID value */ @@ -94,6 +100,7 @@ typedef void (*json_walk_callback_t)(void *callback_data, const char *name, /* * Parse `json_string`, invoking `callback` in a way similar to SAX parsers; * see `json_walk_callback_t`. + * Return number of processed bytes, or a negative error code. */ int json_walk(const char *json_string, int json_string_length, json_walk_callback_t callback, void *callback_data); @@ -127,7 +134,7 @@ extern int json_printer_file(struct json_out *, const char *, size_t); #define JSON_OUT_FILE(fp) \ { \ json_printer_file, { \ - { (void *) fp, 0, 0 } \ + { (char *) fp, 0, 0 } \ } \ } @@ -144,13 +151,34 @@ typedef int (*json_printf_callback_t)(struct json_out *, va_list *ap); * - `%M` invokes a json_printf_callback_t function. That callback function * can consume more parameters. * - * Return number of bytes printed. If the return value is bigger then the + * Return number of bytes printed. If the return value is bigger than the * supplied buffer, that is an indicator of overflow. In the overflow case, * overflown bytes are not printed. */ int json_printf(struct json_out *, const char *fmt, ...); int json_vprintf(struct json_out *, const char *fmt, va_list ap); +/* + * Same as json_printf, but prints to a file. + * File is created if does not exist. File is truncated if already exists. + */ +int json_fprintf(const char *file_name, const char *fmt, ...); +int json_vfprintf(const char *file_name, const char *fmt, va_list ap); + +/* + * Print JSON into an allocated 0-terminated string. + * Return allocated string, or NULL on error. + * Example: + * + * ```c + * char *str = json_asprintf("{a:%H}", 3, "abc"); + * printf("%s\n", str); // Prints "616263" + * free(str); + * ``` + */ +char *json_asprintf(const char *fmt, ...); +char *json_vasprintf(const char *fmt, va_list ap); + /* * Helper %M callback that prints contiguous C arrays. * Consumes void *array_ptr, size_t array_size, size_t elem_size, char *fmt @@ -165,7 +193,8 @@ int json_printf_array(struct json_out *, va_list *ap); * 1. Object keys in the format string may be not quoted, e.g. "{key: %d}" * 2. Order of keys in an object is irrelevant. * 3. Several extra format specifiers are supported: - * - %B: consumes `int *`, expects boolean `true` or `false`. + * - %B: consumes `int *` (or `char *`, if `sizeof(bool) == sizeof(char)`), + * expects boolean `true` or `false`. * - %Q: consumes `char **`, expects quoted, JSON-encoded string. Scanned * string is malloc-ed, caller must free() the string. * - %V: consumes `char **`, `int *`. Expects base64-encoded string. @@ -193,7 +222,7 @@ typedef void (*json_scanner_t)(const char *str, int len, void *user_data); /* * Helper function to scan array item with given path and index. * Fills `token` with the matched JSON token. - * Return 0 if no array element found, otherwise non-0. + * Return -1 if no array element found, otherwise non-negative token length. */ int json_scanf_array_elem(const char *s, int len, const char *path, int index, struct json_token *token); @@ -207,6 +236,76 @@ int json_scanf_array_elem(const char *s, int len, const char *path, int index, */ int json_unescape(const char *src, int slen, char *dst, int dlen); +/* + * Escape a string `str`, `str_len` into the printer `out`. + * Return the number of bytes printed. + */ +int json_escape(struct json_out *out, const char *str, size_t str_len); + +/* + * Read the whole file in memory. + * Return malloc-ed file content, or NULL on error. The caller must free(). + */ +char *json_fread(const char *file_name); + +/* + * Update given JSON string `s,len` by changing the value at given `json_path`. + * The result is saved to `out`. If `json_fmt` == NULL, that deletes the key. + * If path is not present, missing keys are added. Array path without an + * index pushes a value to the end of an array. + * Return 1 if the string was changed, 0 otherwise. + * + * Example: s is a JSON string { "a": 1, "b": [ 2 ] } + * json_setf(s, len, out, ".a", "7"); // { "a": 7, "b": [ 2 ] } + * json_setf(s, len, out, ".b", "7"); // { "a": 1, "b": 7 } + * json_setf(s, len, out, ".b[]", "7"); // { "a": 1, "b": [ 2,7 ] } + * json_setf(s, len, out, ".b", NULL); // { "a": 1 } + */ +int json_setf(const char *s, int len, struct json_out *out, + const char *json_path, const char *json_fmt, ...); + +int json_vsetf(const char *s, int len, struct json_out *out, + const char *json_path, const char *json_fmt, va_list ap); + +/* + * Pretty-print JSON string `s,len` into `out`. + * Return number of processed bytes in `s`. + */ +int json_prettify(const char *s, int len, struct json_out *out); + +/* + * Prettify JSON file `file_name`. + * Return number of processed bytes, or negative number of error. + * On error, file content is not modified. + */ +int json_prettify_file(const char *file_name); + +/* + * Iterate over an object at given JSON `path`. + * On each iteration, fill the `key` and `val` tokens. It is OK to pass NULL + * for `key`, or `val`, in which case they won't be populated. + * Return an opaque value suitable for the next iteration, or NULL when done. + * + * Example: + * + * ```c + * void *h = NULL; + * struct json_token key, val; + * while ((h = json_next_key(s, len, h, ".foo", &key, &val)) != NULL) { + * printf("[%.*s] -> [%.*s]\n", key.len, key.ptr, val.len, val.ptr); + * } + * ``` + */ +void *json_next_key(const char *s, int len, void *handle, const char *path, + struct json_token *key, struct json_token *val); + +/* + * Iterate over an array at given JSON `path`. + * Similar to `json_next_key`, but fills array index `idx` instead of `key`. + */ +void *json_next_elem(const char *s, int len, void *handle, const char *path, + int *idx, struct json_token *val); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/mpd_client.c b/src/mpd_client.c index ae137ef..978c9b7 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -59,8 +59,8 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) int je, int_buf; float float_buf; char *p_charbuf1, *p_charbuf2; - FILE *fp; - + struct mympd_state { int a; int b; } state = { .a = 0, .b = 0 }; + #ifdef DEBUG fprintf(stdout,"Got request: %s\n",msg.p); #endif @@ -72,24 +72,10 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) return; switch(cmd_id) { - case MPD_API_SET_MYMPD_SETTINGS: - fp = fopen(mpd.statefile,"w"); - if(fp != NULL) { - fprintf(fp,"%.*s",msg.len,msg.p); - fclose(fp); - } else { - fprintf(stderr,"Cant write state file\n"); - } - break; - case MPD_API_GET_MYMPD_SETTINGS: - fp = fopen(mpd.statefile,"r"); - if(fp != NULL) { - fgets(mpd.buf, MAX_SIZE, fp); - fclose(fp); - n=strlen(mpd.buf); - } - break; - case MPD_API_SET_MPD_SETTINGS: + case MPD_API_SET_SETTINGS: + json_scanf(msg.p, msg.len, "{ data: { notificationWeb: %d, notificationPage: %d} }", &state.a, &state.b); + json_fprintf(mpd.statefile, "{ notificationWeb: %d, notificationPage: %d}", state.a, state.b); + je = json_scanf(msg.p, msg.len, "{ data: { random:%u } }", &uint_buf1); if (je == 1) mpd_run_random(mpd.conn, uint_buf1); @@ -305,7 +291,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) mpd_run_rm(mpd.conn, p_charbuf1); free(p_charbuf1); break; - case MPD_API_GET_MPD_SETTINGS: + case MPD_API_GET_SETTINGS: n = mympd_put_settings(mpd.buf); break; case MPD_API_GET_STATS: @@ -630,6 +616,14 @@ int mympd_put_settings(char *buffer) char *replaygain; int len; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); + struct mympd_state { int a; int b; } state = { .a = 0, .b = 0 }; + if( access( mpd.statefile, F_OK ) != -1 ) { + char *content = json_fread(mpd.statefile); + json_scanf(content, strlen(content), "{notificationWeb: %d, notificationPage: %d}", &state.a, &state.b); + } else { + state.a=0; + state.b=0; + } status = mpd_run_status(mpd.conn); if (!status) { @@ -644,12 +638,13 @@ int mympd_put_settings(char *buffer) replaygain=strdup(pair->value); mpd_return_pair(mpd.conn, pair); } - + len = json_printf(&out, "{type:settings, data:{" "repeat:%d, single:%d, crossfade:%d, consume:%d, random:%d, " "mixrampdb: %f, mixrampdelay: %f, mpdhost : %Q, mpdport: %d, passwort_set: %B, " - "streamport: %d, coverimage: %Q, max_elements_per_page: %d, replaygain: %Q" + "streamport: %d, coverimage: %Q, max_elements_per_page: %d, replaygain: %Q," + "notificationWeb: %d, notificationPage: %d" "}}", mpd_status_get_repeat(status), mpd_status_get_single(status), @@ -662,7 +657,9 @@ int mympd_put_settings(char *buffer) mpd.password ? "true" : "false", streamport, coverimage, MAX_ELEMENTS_PER_PAGE, - replaygain + replaygain, + state.a, + state.b ); mpd_status_free(status); diff --git a/src/mpd_client.h b/src/mpd_client.h index b95795f..4025822 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -78,10 +78,8 @@ X(MPD_API_GET_ARTISTS) \ X(MPD_API_GET_CURRENT_SONG) \ X(MPD_API_WELCOME) \ - X(MPD_API_GET_MPD_SETTINGS) \ - X(MPD_API_SET_MPD_SETTINGS) \ - X(MPD_API_SET_MYMPD_SETTINGS) \ - X(MPD_API_GET_MYMPD_SETTINGS) + X(MPD_API_GET_SETTINGS) \ + X(MPD_API_SET_SETTINGS) enum mpd_cmd_ids { MPD_CMDS(GEN_ENUM) From 20980607653b98636c7b0f64c663800a117d3382 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 21 Jun 2018 18:28:07 +0100 Subject: [PATCH 24/32] Document state file option --- CMakeLists.txt | 1 + README.md | 7 ++++--- contrib/mympd.default | 1 + contrib/mympd.service | 3 ++- htdocs/js/mpd.js | 9 ++++++--- mympd.1 | 3 +++ src/mpd_client.c | 3 ++- 7 files changed, 19 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b3e434a..eaf8daf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,3 +52,4 @@ target_link_libraries(mympd ${LIBMPDCLIENT_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) install(TARGETS mympd DESTINATION bin) install(FILES mympd.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1) install(DIRECTORY htdocs DESTINATION share/${PROJECT_NAME}) +install(DIRECTORY DESTINATION /var/lib/mympd) diff --git a/README.md b/README.md index 1e43c5a..c7b9e3f 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,6 @@ UI Components - Bootstrap Slider: https://github.com/seiyria/bootstrap-slider - Material Design Icons: https://material.io/tools/icons/?style=baseline - jQuery: https://jquery.com/ - - js-cookie: https://github.com/js-cookie/js-cookie Backend ------- @@ -36,8 +35,9 @@ Unix Build Instructions 3. create makefile ```cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_BUILD_TYPE=RELEASE``` 4. build ```make``` 5. install ```sudo make install``` -6. Link your mpd music directory to ```/usr/share/mympd/htdocs/library``` and put ```folder.jpg``` files in your album directories -7. Configure your mpd with http stream output to use the local player +6. change owner of /var/lib/mympd to user you will run mympd, default nobody (chown nobody /var/lib/mympd) +7. Link your mpd music directory to ```/usr/share/mympd/htdocs/library``` and put ```folder.jpg``` files in your album directories +8. Configure your mpd with http stream output to use the local player Run flags --------- @@ -51,6 +51,7 @@ Usage: ./mympd [OPTION]... -u, --user drop priviliges to user after socket bind -m, --mpdpass specifies the password to use when connecting to mpd -i, --coverimage filename for coverimage [folder.jpg] + -t, --statefile filename for mympd state [/var/lib/mympd/mympd.state] -v, --version get version --help this help ``` diff --git a/contrib/mympd.default b/contrib/mympd.default index 8eb21f7..09da42d 100644 --- a/contrib/mympd.default +++ b/contrib/mympd.default @@ -5,3 +5,4 @@ MPD_PASSWORD= WEB_PORT=80 MYMPD_USER=nobody COVERIMAGE=--coverimage folder.jpg +STATEFILE=--statefile /var/lib/mympd/mympd.state \ No newline at end of file diff --git a/contrib/mympd.service b/contrib/mympd.service index 628192f..180dbad 100644 --- a/contrib/mympd.service +++ b/contrib/mympd.service @@ -9,8 +9,9 @@ Environment=MPD_PASSWORD= Environment=WEB_PORT=80 Environment=MYMPD_USER=nobody Environment=COVERIMAGE=--coverimage folder.jpg +Envirinment=STATEFILE=--statefile /var/lib/mympd/mympd.state EnvironmentFile=/etc/default/mympd -ExecStart=/usr/bin/mympd --user $MYMPD_USER --webport $WEB_PORT --host $MPD_HOST --port $MPD_PORT $COVERIMAGE +ExecStart=/usr/bin/mympd --user $MYMPD_USER --webport $WEB_PORT --host $MPD_HOST --port $MPD_PORT $COVERIMAGE $STATEFILE Type=simple [Install] diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index d76a081..025ec73 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -31,7 +31,6 @@ var isTouch = Modernizr.touch ? 1 : 0; var playstate = ''; var progressBar; var volumeBar; -var coverImageFile = ''; var settings = {}; var app = {}; @@ -213,7 +212,7 @@ app.route=function() { }; $(document).ready(function(){ - sendAPI({"cmd":"MPD_API_GET_SETTINGS"},parseSettings); + getSettings(); sendAPI({"cmd":"MPD_API_GET_OUTPUTNAMES"},parseOutputnames); webSocketConnect(); @@ -432,6 +431,10 @@ function parseSettings(obj) { setLocalStream(obj.data.mpdhost,obj.data.streamport); } +function getSettings() { + sendAPI({"cmd":"MPD_API_GET_SETTINGS"},parseSettings); +} + function parseOutputnames(obj) { $('#btn-outputs-block button').remove(); if ( Object.keys(obj.data).length ) { @@ -1125,7 +1128,7 @@ function confirmSettings() { "mixrampdelay": $('#inputMixrampdelay').val(), "notificationWeb": ($('#btnnotifyWeb').hasClass('btn-success') ? 1 : 0), "notificationPage": ($('#btnnotifyPage').hasClass('btn-success') ? 1 : 0) - }}); + }},getSettings); $('#settings').modal('hide'); } } diff --git a/mympd.1 b/mympd.1 index 350d1c4..92bf375 100644 --- a/mympd.1 +++ b/mympd.1 @@ -33,6 +33,9 @@ specifies the password to use when connecting to mpd \fB-i\fR, \fB\-\-coverimage FILENAME\fR filename for coverimage [folder.jpg] .TP +\fB-t\fR, \fB\-\-statefile FILENAME\fR +filename for mympd state [/var/lib/mympd/mympd.state] +.TP \fB\-v\fR, \fB\-\-version\fR print version and exit .TP diff --git a/src/mpd_client.c b/src/mpd_client.c index 978c9b7..1fbe18f 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -113,7 +113,8 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) mpd_return_pair(mpd.conn, pair); } } - free(p_charbuf1); + free(p_charbuf1); + n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"result\", \"data\": \"ok\"}"); break; case MPD_API_GET_ARTISTALBUMTITLES: je = json_scanf(msg.p, msg.len, "{ data: { albumartist:%Q, album:%Q } }", &p_charbuf1, &p_charbuf2); From 2dff568abe30020f61b98842f77e527f69cf7815 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 21 Jun 2018 22:29:49 +0100 Subject: [PATCH 25/32] Added google closure compiler for css and js files --- CMakeLists.txt | 30 +- htdocs/css/bootstrap-slider.css | 321 - htdocs/css/bootstrap.css | 8981 ----------------- htdocs/css/bootstrap.css.map | 1 - htdocs/css/bootstrap.min.css | 1 - htdocs/css/bootstrap.min.css.map | 1 - htdocs/css/mpd.css | 14 +- htdocs/css/mpd.min.css | 1 + htdocs/index.html | 35 +- htdocs/js/bootstrap-notify.js | 349 - htdocs/js/bootstrap-slider.js | 1878 ---- htdocs/js/bootstrap.bundle.js | 6444 ------------ htdocs/js/bootstrap.bundle.js.map | 1 - htdocs/js/bootstrap.bundle.min.js | 1 - htdocs/js/bootstrap.bundle.min.js.map | 1 - htdocs/js/jquery-3.3.1.min.map | 1 - ...nizr-custom.js => modernizr-custom.min.js} | 0 htdocs/js/mpd.js | 42 +- htdocs/js/mpd.min.js | 88 + htdocs/js/player.min.js | 1 + htdocs/player.html | 6 +- mkdebug.sh | 15 + mkrelease.sh | 22 + src/mpd_client.c | 9 +- src/mpd_client.h | 2 +- src/mympd.c | 4 +- 26 files changed, 200 insertions(+), 18049 deletions(-) delete mode 100644 htdocs/css/bootstrap-slider.css delete mode 100644 htdocs/css/bootstrap.css delete mode 100644 htdocs/css/bootstrap.css.map delete mode 100644 htdocs/css/bootstrap.min.css.map create mode 100644 htdocs/css/mpd.min.css delete mode 100644 htdocs/js/bootstrap-notify.js delete mode 100644 htdocs/js/bootstrap-slider.js delete mode 100644 htdocs/js/bootstrap.bundle.js delete mode 100644 htdocs/js/bootstrap.bundle.js.map delete mode 100644 htdocs/js/bootstrap.bundle.min.js.map delete mode 100644 htdocs/js/jquery-3.3.1.min.map rename htdocs/js/{modernizr-custom.js => modernizr-custom.min.js} (100%) create mode 100644 htdocs/js/mpd.min.js create mode 100644 htdocs/js/player.min.js create mode 100755 mkdebug.sh create mode 100755 mkrelease.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index eaf8daf..8918351 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,8 +13,6 @@ else() set(DEBUG "ON") endif() -option(WITH_IPV6 "enable IPv6 support" ON) - find_package(LibMPDClient REQUIRED) find_package(Threads REQUIRED) @@ -26,19 +24,9 @@ include(CheckCSourceCompiles) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -pedantic ") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -D_FORTIFY_SOURCE=2 -fstack-protector -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=shift -fsanitize=integer-divide-by-zero -fsanitize=unreachable -fsanitize=vla-bound -fsanitize=null -fsanitize=return -fsanitize=signed-integer-overflow -fsanitize=bounds -fsanitize=bounds-strict -fsanitize=alignment -fsanitize=object-size -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fsanitize=nonnull-attribute -fsanitize=returns-nonnull-attribute -fsanitize=bool -fsanitize=enum -fsanitize=vptr -static-libasan") if(WITH_IPV6) - set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS NS_ENABLE_IPV6) + set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS) endif() -file(GLOB RESOURCES - RELATIVE ${PROJECT_SOURCE_DIR} - htdocs/js/* - htdocs/assets/* - htdocs/css/*.css - htdocs/fonts/* - htdocs/index.html - htdocs/player.html -) - set(SOURCES src/mympd.c src/mpd_client.c @@ -51,5 +39,17 @@ target_link_libraries(mympd ${LIBMPDCLIENT_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) install(TARGETS mympd DESTINATION bin) install(FILES mympd.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1) -install(DIRECTORY htdocs DESTINATION share/${PROJECT_NAME}) -install(DIRECTORY DESTINATION /var/lib/mympd) +install(FILES htdocs/index.html DESTINATION share/${PROJECT_NAME}/htdocs/) +install(FILES htdocs/player.html DESTINATION share/${PROJECT_NAME}/htdocs/) +install(FILES htdocs/js/modernizr-custom.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/) +install(FILES htdocs/js/player.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/) +install(FILES htdocs/js/bootstrap.bundle.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/) +install(FILES htdocs/js/bootstrap-notify.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/) +install(FILES htdocs/js/bootstrap-slider.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/) +install(FILES htdocs/js/jquery-3.3.1.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/) +install(FILES htdocs/js/mpd.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/) +install(FILES htdocs/css/bootstrap.min.css DESTINATION share/${PROJECT_NAME}/htdocs/css/) +install(FILES htdocs/css/bootstrap-slider.min.css DESTINATION share/${PROJECT_NAME}/htdocs/css/) +install(FILES htdocs/css/mpd.min.css DESTINATION share/${PROJECT_NAME}/htdocs/css/) +install(DIRECTORY htdocs/assets DESTINATION share/${PROJECT_NAME}/htdocs) +install(DIRECTORY DESTINATION /var/lib/${PROJECT_NAME}/) diff --git a/htdocs/css/bootstrap-slider.css b/htdocs/css/bootstrap-slider.css deleted file mode 100644 index 76201bf..0000000 --- a/htdocs/css/bootstrap-slider.css +++ /dev/null @@ -1,321 +0,0 @@ -/*! ======================================================= - VERSION 10.0.2 -========================================================= */ -/*! ========================================================= - * bootstrap-slider.js - * - * Maintainers: - * Kyle Kemp - * - Twitter: @seiyria - * - Github: seiyria - * Rohit Kalkur - * - Twitter: @Rovolutionary - * - Github: rovolution - * - * ========================================================= - * - * bootstrap-slider is released under the MIT License - * Copyright (c) 2017 Kyle Kemp, Rohit Kalkur, and contributors - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * ========================================================= */ -.slider { - display: inline-block; - vertical-align: middle; - position: relative; -} -.slider.slider-horizontal { - width: 210px; - height: 20px; -} -.slider.slider-horizontal .slider-track { - height: 10px; - width: 100%; - margin-top: -5px; - top: 50%; - left: 0; -} -.slider.slider-horizontal .slider-selection, -.slider.slider-horizontal .slider-track-low, -.slider.slider-horizontal .slider-track-high { - height: 100%; - top: 0; - bottom: 0; -} -.slider.slider-horizontal .slider-tick, -.slider.slider-horizontal .slider-handle { - margin-left: -10px; -} -.slider.slider-horizontal .slider-tick.triangle, -.slider.slider-horizontal .slider-handle.triangle { - position: relative; - top: 50%; - -ms-transform: translateY(-50%); - transform: translateY(-50%); - border-width: 0 10px 10px 10px; - width: 0; - height: 0; - border-bottom-color: #2e6da4; - margin-top: 0; -} -.slider.slider-horizontal .slider-tick-container { - white-space: nowrap; - position: absolute; - top: 0; - left: 0; - width: 100%; -} -.slider.slider-horizontal .slider-tick-label-container { - white-space: nowrap; - margin-top: 20px; -} -.slider.slider-horizontal .slider-tick-label-container .slider-tick-label { - padding-top: 4px; - display: inline-block; - text-align: center; -} -.slider.slider-horizontal .tooltip { - -ms-transform: translateX(-50%); - transform: translateX(-50%); -} -.slider.slider-horizontal.slider-rtl .slider-track { - left: initial; - right: 0; -} -.slider.slider-horizontal.slider-rtl .slider-tick, -.slider.slider-horizontal.slider-rtl .slider-handle { - margin-left: initial; - margin-right: -10px; -} -.slider.slider-horizontal.slider-rtl .slider-tick-container { - left: initial; - right: 0; -} -.slider.slider-horizontal.slider-rtl .tooltip { - -ms-transform: translateX(50%); - transform: translateX(50%); -} -.slider.slider-vertical { - height: 210px; - width: 20px; -} -.slider.slider-vertical .slider-track { - width: 10px; - height: 100%; - left: 25%; - top: 0; -} -.slider.slider-vertical .slider-selection { - width: 100%; - left: 0; - top: 0; - bottom: 0; -} -.slider.slider-vertical .slider-track-low, -.slider.slider-vertical .slider-track-high { - width: 100%; - left: 0; - right: 0; -} -.slider.slider-vertical .slider-tick, -.slider.slider-vertical .slider-handle { - margin-top: -10px; -} -.slider.slider-vertical .slider-tick.triangle, -.slider.slider-vertical .slider-handle.triangle { - border-width: 10px 0 10px 10px; - width: 1px; - height: 1px; - border-left-color: #2e6da4; - border-right-color: #2e6da4; - margin-left: 0; - margin-right: 0; -} -.slider.slider-vertical .slider-tick-label-container { - white-space: nowrap; -} -.slider.slider-vertical .slider-tick-label-container .slider-tick-label { - padding-left: 4px; -} -.slider.slider-vertical .tooltip { - -ms-transform: translateY(-50%); - transform: translateY(-50%); -} -.slider.slider-vertical.slider-rtl .slider-track { - left: initial; - right: 25%; -} -.slider.slider-vertical.slider-rtl .slider-selection { - left: initial; - right: 0; -} -.slider.slider-vertical.slider-rtl .slider-tick.triangle, -.slider.slider-vertical.slider-rtl .slider-handle.triangle { - border-width: 10px 10px 10px 0; -} -.slider.slider-vertical.slider-rtl .slider-tick-label-container .slider-tick-label { - padding-left: initial; - padding-right: 4px; -} -.slider.slider-disabled .slider-handle { - background-image: -webkit-linear-gradient(top, #dfdfdf 0%, #bebebe 100%); - background-image: -o-linear-gradient(top, #dfdfdf 0%, #bebebe 100%); - background-image: linear-gradient(to bottom, #dfdfdf 0%, #bebebe 100%); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdfdfdf', endColorstr='#ffbebebe', GradientType=0); -} -.slider.slider-disabled .slider-track { - background-image: -webkit-linear-gradient(top, #e5e5e5 0%, #e9e9e9 100%); - background-image: -o-linear-gradient(top, #e5e5e5 0%, #e9e9e9 100%); - background-image: linear-gradient(to bottom, #e5e5e5 0%, #e9e9e9 100%); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe5e5e5', endColorstr='#ffe9e9e9', GradientType=0); - cursor: not-allowed; -} -.slider input { - display: none; -} -.slider .tooltip.top { - margin-top: -36px; -} -.slider .tooltip-inner { - white-space: nowrap; - max-width: none; -} -.slider .hide { - display: none; -} -.slider-track { - position: absolute; - cursor: pointer; - background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #f9f9f9 100%); - background-image: -o-linear-gradient(top, #f5f5f5 0%, #f9f9f9 100%); - background-image: linear-gradient(to bottom, #f5f5f5 0%, #f9f9f9 100%); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - border-radius: 4px; -} -.slider-selection { - position: absolute; - background-image: -webkit-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%); - background-image: -o-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%); - background-image: linear-gradient(to bottom, #f9f9f9 0%, #f5f5f5 100%); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9', endColorstr='#fff5f5f5', GradientType=0); - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - border-radius: 4px; -} -.slider-selection.tick-slider-selection { - background-image: -webkit-linear-gradient(top, #8ac1ef 0%, #82b3de 100%); - background-image: -o-linear-gradient(top, #8ac1ef 0%, #82b3de 100%); - background-image: linear-gradient(to bottom, #8ac1ef 0%, #82b3de 100%); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff8ac1ef', endColorstr='#ff82b3de', GradientType=0); -} -.slider-track-low, -.slider-track-high { - position: absolute; - background: transparent; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - border-radius: 4px; -} -.slider-handle { - position: absolute; - top: 0; - width: 20px; - height: 20px; - background-color: #337ab7; - background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); - background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); - background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); - filter: none; - -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); - box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); - border: 0px solid transparent; -} -.slider-handle.round { - border-radius: 50%; -} -.slider-handle.triangle { - background: transparent none; -} -.slider-handle.custom { - background: transparent none; -} -.slider-handle.custom::before { - line-height: 20px; - font-size: 20px; - content: '\2605'; - color: #726204; -} -.slider-tick { - position: absolute; - width: 20px; - height: 20px; - background-image: -webkit-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%); - background-image: -o-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%); - background-image: linear-gradient(to bottom, #f9f9f9 0%, #f5f5f5 100%); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9', endColorstr='#fff5f5f5', GradientType=0); - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - filter: none; - opacity: 0.8; - border: 0px solid transparent; -} -.slider-tick.round { - border-radius: 50%; -} -.slider-tick.triangle { - background: transparent none; -} -.slider-tick.custom { - background: transparent none; -} -.slider-tick.custom::before { - line-height: 20px; - font-size: 20px; - content: '\2605'; - color: #726204; -} -.slider-tick.in-selection { - background-image: -webkit-linear-gradient(top, #8ac1ef 0%, #82b3de 100%); - background-image: -o-linear-gradient(top, #8ac1ef 0%, #82b3de 100%); - background-image: linear-gradient(to bottom, #8ac1ef 0%, #82b3de 100%); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff8ac1ef', endColorstr='#ff82b3de', GradientType=0); - opacity: 1; -} diff --git a/htdocs/css/bootstrap.css b/htdocs/css/bootstrap.css deleted file mode 100644 index 7d43e1f..0000000 --- a/htdocs/css/bootstrap.css +++ /dev/null @@ -1,8981 +0,0 @@ -/*! - * Bootstrap v4.1.1 (https://getbootstrap.com/) - * Copyright 2011-2018 The Bootstrap Authors - * Copyright 2011-2018 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -:root { - --blue: #007bff; - --indigo: #6610f2; - --purple: #6f42c1; - --pink: #e83e8c; - --red: #dc3545; - --orange: #fd7e14; - --yellow: #ffc107; - --green: #28a745; - --teal: #20c997; - --cyan: #17a2b8; - --white: #fff; - --gray: #6c757d; - --gray-dark: #343a40; - --primary: #007bff; - --secondary: #6c757d; - --success: #28a745; - --info: #17a2b8; - --warning: #ffc107; - --danger: #dc3545; - --light: #f8f9fa; - --dark: #343a40; - --breakpoint-xs: 0; - --breakpoint-sm: 576px; - --breakpoint-md: 768px; - --breakpoint-lg: 992px; - --breakpoint-xl: 1200px; - --font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; - --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; -} - -*, -*::before, -*::after { - box-sizing: border-box; -} - -html { - font-family: sans-serif; - line-height: 1.15; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; - -ms-overflow-style: scrollbar; - -webkit-tap-highlight-color: transparent; -} - -@-ms-viewport { - width: device-width; -} - -article, aside, figcaption, figure, footer, header, hgroup, main, nav, section { - display: block; -} - -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: #212529; - text-align: left; - background-color: #fff; -} - -[tabindex="-1"]:focus { - outline: 0 !important; -} - -hr { - box-sizing: content-box; - height: 0; - overflow: visible; -} - -h1, h2, h3, h4, h5, h6 { - margin-top: 0; - margin-bottom: 0.5rem; -} - -p { - margin-top: 0; - margin-bottom: 1rem; -} - -abbr[title], -abbr[data-original-title] { - text-decoration: underline; - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; - cursor: help; - border-bottom: 0; -} - -address { - margin-bottom: 1rem; - font-style: normal; - line-height: inherit; -} - -ol, -ul, -dl { - margin-top: 0; - margin-bottom: 1rem; -} - -ol ol, -ul ul, -ol ul, -ul ol { - margin-bottom: 0; -} - -dt { - font-weight: 700; -} - -dd { - margin-bottom: .5rem; - margin-left: 0; -} - -blockquote { - margin: 0 0 1rem; -} - -dfn { - font-style: italic; -} - -b, -strong { - font-weight: bolder; -} - -small { - font-size: 80%; -} - -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} - -sub { - bottom: -.25em; -} - -sup { - top: -.5em; -} - -a { - color: #007bff; - text-decoration: none; - background-color: transparent; - -webkit-text-decoration-skip: objects; -} - -a:hover { - color: #0056b3; - text-decoration: underline; -} - -a:not([href]):not([tabindex]) { - color: inherit; - text-decoration: none; -} - -a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus { - color: inherit; - text-decoration: none; -} - -a:not([href]):not([tabindex]):focus { - outline: 0; -} - -pre, -code, -kbd, -samp { - font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - font-size: 1em; -} - -pre { - margin-top: 0; - margin-bottom: 1rem; - overflow: auto; - -ms-overflow-style: scrollbar; -} - -figure { - margin: 0 0 1rem; -} - -img { - vertical-align: middle; - border-style: none; -} - -svg:not(:root) { - overflow: hidden; -} - -table { - border-collapse: collapse; -} - -caption { - padding-top: 0.75rem; - padding-bottom: 0.75rem; - color: #6c757d; - text-align: left; - caption-side: bottom; -} - -th { - text-align: inherit; -} - -label { - display: inline-block; - margin-bottom: 0.5rem; -} - -button { - border-radius: 0; -} - -button:focus { - outline: 1px dotted; - outline: 5px auto -webkit-focus-ring-color; -} - -input, -button, -select, -optgroup, -textarea { - margin: 0; - font-family: inherit; - font-size: inherit; - line-height: inherit; -} - -button, -input { - overflow: visible; -} - -button, -select { - text-transform: none; -} - -button, -html [type="button"], -[type="reset"], -[type="submit"] { - -webkit-appearance: button; -} - -button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { - padding: 0; - border-style: none; -} - -input[type="radio"], -input[type="checkbox"] { - box-sizing: border-box; - padding: 0; -} - -input[type="date"], -input[type="time"], -input[type="datetime-local"], -input[type="month"] { - -webkit-appearance: listbox; -} - -textarea { - overflow: auto; - resize: vertical; -} - -fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; -} - -legend { - display: block; - width: 100%; - max-width: 100%; - padding: 0; - margin-bottom: .5rem; - font-size: 1.5rem; - line-height: inherit; - color: inherit; - white-space: normal; -} - -progress { - vertical-align: baseline; -} - -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; -} - -[type="search"] { - outline-offset: -2px; - -webkit-appearance: none; -} - -[type="search"]::-webkit-search-cancel-button, -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -::-webkit-file-upload-button { - font: inherit; - -webkit-appearance: button; -} - -output { - display: inline-block; -} - -summary { - display: list-item; - cursor: pointer; -} - -template { - display: none; -} - -[hidden] { - display: none !important; -} - -h1, h2, h3, h4, h5, h6, -.h1, .h2, .h3, .h4, .h5, .h6 { - margin-bottom: 0.5rem; - font-family: inherit; - font-weight: 500; - line-height: 1.2; - color: inherit; -} - -h1, .h1 { - font-size: 2.5rem; -} - -h2, .h2 { - font-size: 2rem; -} - -h3, .h3 { - font-size: 1.75rem; -} - -h4, .h4 { - font-size: 1.5rem; -} - -h5, .h5 { - font-size: 1.25rem; -} - -h6, .h6 { - font-size: 1rem; -} - -.lead { - font-size: 1.25rem; - font-weight: 300; -} - -.display-1 { - font-size: 6rem; - font-weight: 300; - line-height: 1.2; -} - -.display-2 { - font-size: 5.5rem; - font-weight: 300; - line-height: 1.2; -} - -.display-3 { - font-size: 4.5rem; - font-weight: 300; - line-height: 1.2; -} - -.display-4 { - font-size: 3.5rem; - font-weight: 300; - line-height: 1.2; -} - -hr { - margin-top: 1rem; - margin-bottom: 1rem; - border: 0; - border-top: 1px solid rgba(0, 0, 0, 0.1); -} - -small, -.small { - font-size: 80%; - font-weight: 400; -} - -mark, -.mark { - padding: 0.2em; - background-color: #fcf8e3; -} - -.list-unstyled { - padding-left: 0; - list-style: none; -} - -.list-inline { - padding-left: 0; - list-style: none; -} - -.list-inline-item { - display: inline-block; -} - -.list-inline-item:not(:last-child) { - margin-right: 0.5rem; -} - -.initialism { - font-size: 90%; - text-transform: uppercase; -} - -.blockquote { - margin-bottom: 1rem; - font-size: 1.25rem; -} - -.blockquote-footer { - display: block; - font-size: 80%; - color: #6c757d; -} - -.blockquote-footer::before { - content: "\2014 \00A0"; -} - -.img-fluid { - max-width: 100%; - height: auto; -} - -.img-thumbnail { - padding: 0.25rem; - background-color: #fff; - border: 1px solid #dee2e6; - border-radius: 0.25rem; - max-width: 100%; - height: auto; -} - -.figure { - display: inline-block; -} - -.figure-img { - margin-bottom: 0.5rem; - line-height: 1; -} - -.figure-caption { - font-size: 90%; - color: #6c757d; -} - -code { - font-size: 87.5%; - color: #e83e8c; - word-break: break-word; -} - -a > code { - color: inherit; -} - -kbd { - padding: 0.2rem 0.4rem; - font-size: 87.5%; - color: #fff; - background-color: #212529; - border-radius: 0.2rem; -} - -kbd kbd { - padding: 0; - font-size: 100%; - font-weight: 700; -} - -pre { - display: block; - font-size: 87.5%; - color: #212529; -} - -pre code { - font-size: inherit; - color: inherit; - word-break: normal; -} - -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; -} - -.container { - width: 100%; - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} - -@media (min-width: 576px) { - .container { - max-width: 540px; - } -} - -@media (min-width: 768px) { - .container { - max-width: 720px; - } -} - -@media (min-width: 992px) { - .container { - max-width: 960px; - } -} - -@media (min-width: 1200px) { - .container { - max-width: 1140px; - } -} - -.container-fluid { - width: 100%; - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} - -.row { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - margin-right: -15px; - margin-left: -15px; -} - -.no-gutters { - margin-right: 0; - margin-left: 0; -} - -.no-gutters > .col, -.no-gutters > [class*="col-"] { - padding-right: 0; - padding-left: 0; -} - -.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col, -.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm, -.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md, -.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg, -.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl, -.col-xl-auto { - position: relative; - width: 100%; - min-height: 1px; - padding-right: 15px; - padding-left: 15px; -} - -.col { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; -} - -.col-auto { - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: none; -} - -.col-1 { - -ms-flex: 0 0 8.333333%; - flex: 0 0 8.333333%; - max-width: 8.333333%; -} - -.col-2 { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; -} - -.col-3 { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; -} - -.col-4 { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; -} - -.col-5 { - -ms-flex: 0 0 41.666667%; - flex: 0 0 41.666667%; - max-width: 41.666667%; -} - -.col-6 { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; -} - -.col-7 { - -ms-flex: 0 0 58.333333%; - flex: 0 0 58.333333%; - max-width: 58.333333%; -} - -.col-8 { - -ms-flex: 0 0 66.666667%; - flex: 0 0 66.666667%; - max-width: 66.666667%; -} - -.col-9 { - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; -} - -.col-10 { - -ms-flex: 0 0 83.333333%; - flex: 0 0 83.333333%; - max-width: 83.333333%; -} - -.col-11 { - -ms-flex: 0 0 91.666667%; - flex: 0 0 91.666667%; - max-width: 91.666667%; -} - -.col-12 { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; -} - -.order-first { - -ms-flex-order: -1; - order: -1; -} - -.order-last { - -ms-flex-order: 13; - order: 13; -} - -.order-0 { - -ms-flex-order: 0; - order: 0; -} - -.order-1 { - -ms-flex-order: 1; - order: 1; -} - -.order-2 { - -ms-flex-order: 2; - order: 2; -} - -.order-3 { - -ms-flex-order: 3; - order: 3; -} - -.order-4 { - -ms-flex-order: 4; - order: 4; -} - -.order-5 { - -ms-flex-order: 5; - order: 5; -} - -.order-6 { - -ms-flex-order: 6; - order: 6; -} - -.order-7 { - -ms-flex-order: 7; - order: 7; -} - -.order-8 { - -ms-flex-order: 8; - order: 8; -} - -.order-9 { - -ms-flex-order: 9; - order: 9; -} - -.order-10 { - -ms-flex-order: 10; - order: 10; -} - -.order-11 { - -ms-flex-order: 11; - order: 11; -} - -.order-12 { - -ms-flex-order: 12; - order: 12; -} - -.offset-1 { - margin-left: 8.333333%; -} - -.offset-2 { - margin-left: 16.666667%; -} - -.offset-3 { - margin-left: 25%; -} - -.offset-4 { - margin-left: 33.333333%; -} - -.offset-5 { - margin-left: 41.666667%; -} - -.offset-6 { - margin-left: 50%; -} - -.offset-7 { - margin-left: 58.333333%; -} - -.offset-8 { - margin-left: 66.666667%; -} - -.offset-9 { - margin-left: 75%; -} - -.offset-10 { - margin-left: 83.333333%; -} - -.offset-11 { - margin-left: 91.666667%; -} - -@media (min-width: 576px) { - .col-sm { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; - } - .col-sm-auto { - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: none; - } - .col-sm-1 { - -ms-flex: 0 0 8.333333%; - flex: 0 0 8.333333%; - max-width: 8.333333%; - } - .col-sm-2 { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; - } - .col-sm-3 { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .col-sm-4 { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; - } - .col-sm-5 { - -ms-flex: 0 0 41.666667%; - flex: 0 0 41.666667%; - max-width: 41.666667%; - } - .col-sm-6 { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .col-sm-7 { - -ms-flex: 0 0 58.333333%; - flex: 0 0 58.333333%; - max-width: 58.333333%; - } - .col-sm-8 { - -ms-flex: 0 0 66.666667%; - flex: 0 0 66.666667%; - max-width: 66.666667%; - } - .col-sm-9 { - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; - } - .col-sm-10 { - -ms-flex: 0 0 83.333333%; - flex: 0 0 83.333333%; - max-width: 83.333333%; - } - .col-sm-11 { - -ms-flex: 0 0 91.666667%; - flex: 0 0 91.666667%; - max-width: 91.666667%; - } - .col-sm-12 { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .order-sm-first { - -ms-flex-order: -1; - order: -1; - } - .order-sm-last { - -ms-flex-order: 13; - order: 13; - } - .order-sm-0 { - -ms-flex-order: 0; - order: 0; - } - .order-sm-1 { - -ms-flex-order: 1; - order: 1; - } - .order-sm-2 { - -ms-flex-order: 2; - order: 2; - } - .order-sm-3 { - -ms-flex-order: 3; - order: 3; - } - .order-sm-4 { - -ms-flex-order: 4; - order: 4; - } - .order-sm-5 { - -ms-flex-order: 5; - order: 5; - } - .order-sm-6 { - -ms-flex-order: 6; - order: 6; - } - .order-sm-7 { - -ms-flex-order: 7; - order: 7; - } - .order-sm-8 { - -ms-flex-order: 8; - order: 8; - } - .order-sm-9 { - -ms-flex-order: 9; - order: 9; - } - .order-sm-10 { - -ms-flex-order: 10; - order: 10; - } - .order-sm-11 { - -ms-flex-order: 11; - order: 11; - } - .order-sm-12 { - -ms-flex-order: 12; - order: 12; - } - .offset-sm-0 { - margin-left: 0; - } - .offset-sm-1 { - margin-left: 8.333333%; - } - .offset-sm-2 { - margin-left: 16.666667%; - } - .offset-sm-3 { - margin-left: 25%; - } - .offset-sm-4 { - margin-left: 33.333333%; - } - .offset-sm-5 { - margin-left: 41.666667%; - } - .offset-sm-6 { - margin-left: 50%; - } - .offset-sm-7 { - margin-left: 58.333333%; - } - .offset-sm-8 { - margin-left: 66.666667%; - } - .offset-sm-9 { - margin-left: 75%; - } - .offset-sm-10 { - margin-left: 83.333333%; - } - .offset-sm-11 { - margin-left: 91.666667%; - } -} - -@media (min-width: 768px) { - .col-md { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; - } - .col-md-auto { - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: none; - } - .col-md-1 { - -ms-flex: 0 0 8.333333%; - flex: 0 0 8.333333%; - max-width: 8.333333%; - } - .col-md-2 { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; - } - .col-md-3 { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .col-md-4 { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; - } - .col-md-5 { - -ms-flex: 0 0 41.666667%; - flex: 0 0 41.666667%; - max-width: 41.666667%; - } - .col-md-6 { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .col-md-7 { - -ms-flex: 0 0 58.333333%; - flex: 0 0 58.333333%; - max-width: 58.333333%; - } - .col-md-8 { - -ms-flex: 0 0 66.666667%; - flex: 0 0 66.666667%; - max-width: 66.666667%; - } - .col-md-9 { - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; - } - .col-md-10 { - -ms-flex: 0 0 83.333333%; - flex: 0 0 83.333333%; - max-width: 83.333333%; - } - .col-md-11 { - -ms-flex: 0 0 91.666667%; - flex: 0 0 91.666667%; - max-width: 91.666667%; - } - .col-md-12 { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .order-md-first { - -ms-flex-order: -1; - order: -1; - } - .order-md-last { - -ms-flex-order: 13; - order: 13; - } - .order-md-0 { - -ms-flex-order: 0; - order: 0; - } - .order-md-1 { - -ms-flex-order: 1; - order: 1; - } - .order-md-2 { - -ms-flex-order: 2; - order: 2; - } - .order-md-3 { - -ms-flex-order: 3; - order: 3; - } - .order-md-4 { - -ms-flex-order: 4; - order: 4; - } - .order-md-5 { - -ms-flex-order: 5; - order: 5; - } - .order-md-6 { - -ms-flex-order: 6; - order: 6; - } - .order-md-7 { - -ms-flex-order: 7; - order: 7; - } - .order-md-8 { - -ms-flex-order: 8; - order: 8; - } - .order-md-9 { - -ms-flex-order: 9; - order: 9; - } - .order-md-10 { - -ms-flex-order: 10; - order: 10; - } - .order-md-11 { - -ms-flex-order: 11; - order: 11; - } - .order-md-12 { - -ms-flex-order: 12; - order: 12; - } - .offset-md-0 { - margin-left: 0; - } - .offset-md-1 { - margin-left: 8.333333%; - } - .offset-md-2 { - margin-left: 16.666667%; - } - .offset-md-3 { - margin-left: 25%; - } - .offset-md-4 { - margin-left: 33.333333%; - } - .offset-md-5 { - margin-left: 41.666667%; - } - .offset-md-6 { - margin-left: 50%; - } - .offset-md-7 { - margin-left: 58.333333%; - } - .offset-md-8 { - margin-left: 66.666667%; - } - .offset-md-9 { - margin-left: 75%; - } - .offset-md-10 { - margin-left: 83.333333%; - } - .offset-md-11 { - margin-left: 91.666667%; - } -} - -@media (min-width: 992px) { - .col-lg { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; - } - .col-lg-auto { - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: none; - } - .col-lg-1 { - -ms-flex: 0 0 8.333333%; - flex: 0 0 8.333333%; - max-width: 8.333333%; - } - .col-lg-2 { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; - } - .col-lg-3 { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .col-lg-4 { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; - } - .col-lg-5 { - -ms-flex: 0 0 41.666667%; - flex: 0 0 41.666667%; - max-width: 41.666667%; - } - .col-lg-6 { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .col-lg-7 { - -ms-flex: 0 0 58.333333%; - flex: 0 0 58.333333%; - max-width: 58.333333%; - } - .col-lg-8 { - -ms-flex: 0 0 66.666667%; - flex: 0 0 66.666667%; - max-width: 66.666667%; - } - .col-lg-9 { - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; - } - .col-lg-10 { - -ms-flex: 0 0 83.333333%; - flex: 0 0 83.333333%; - max-width: 83.333333%; - } - .col-lg-11 { - -ms-flex: 0 0 91.666667%; - flex: 0 0 91.666667%; - max-width: 91.666667%; - } - .col-lg-12 { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .order-lg-first { - -ms-flex-order: -1; - order: -1; - } - .order-lg-last { - -ms-flex-order: 13; - order: 13; - } - .order-lg-0 { - -ms-flex-order: 0; - order: 0; - } - .order-lg-1 { - -ms-flex-order: 1; - order: 1; - } - .order-lg-2 { - -ms-flex-order: 2; - order: 2; - } - .order-lg-3 { - -ms-flex-order: 3; - order: 3; - } - .order-lg-4 { - -ms-flex-order: 4; - order: 4; - } - .order-lg-5 { - -ms-flex-order: 5; - order: 5; - } - .order-lg-6 { - -ms-flex-order: 6; - order: 6; - } - .order-lg-7 { - -ms-flex-order: 7; - order: 7; - } - .order-lg-8 { - -ms-flex-order: 8; - order: 8; - } - .order-lg-9 { - -ms-flex-order: 9; - order: 9; - } - .order-lg-10 { - -ms-flex-order: 10; - order: 10; - } - .order-lg-11 { - -ms-flex-order: 11; - order: 11; - } - .order-lg-12 { - -ms-flex-order: 12; - order: 12; - } - .offset-lg-0 { - margin-left: 0; - } - .offset-lg-1 { - margin-left: 8.333333%; - } - .offset-lg-2 { - margin-left: 16.666667%; - } - .offset-lg-3 { - margin-left: 25%; - } - .offset-lg-4 { - margin-left: 33.333333%; - } - .offset-lg-5 { - margin-left: 41.666667%; - } - .offset-lg-6 { - margin-left: 50%; - } - .offset-lg-7 { - margin-left: 58.333333%; - } - .offset-lg-8 { - margin-left: 66.666667%; - } - .offset-lg-9 { - margin-left: 75%; - } - .offset-lg-10 { - margin-left: 83.333333%; - } - .offset-lg-11 { - margin-left: 91.666667%; - } -} - -@media (min-width: 1200px) { - .col-xl { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; - } - .col-xl-auto { - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: none; - } - .col-xl-1 { - -ms-flex: 0 0 8.333333%; - flex: 0 0 8.333333%; - max-width: 8.333333%; - } - .col-xl-2 { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; - } - .col-xl-3 { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .col-xl-4 { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; - } - .col-xl-5 { - -ms-flex: 0 0 41.666667%; - flex: 0 0 41.666667%; - max-width: 41.666667%; - } - .col-xl-6 { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .col-xl-7 { - -ms-flex: 0 0 58.333333%; - flex: 0 0 58.333333%; - max-width: 58.333333%; - } - .col-xl-8 { - -ms-flex: 0 0 66.666667%; - flex: 0 0 66.666667%; - max-width: 66.666667%; - } - .col-xl-9 { - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; - } - .col-xl-10 { - -ms-flex: 0 0 83.333333%; - flex: 0 0 83.333333%; - max-width: 83.333333%; - } - .col-xl-11 { - -ms-flex: 0 0 91.666667%; - flex: 0 0 91.666667%; - max-width: 91.666667%; - } - .col-xl-12 { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .order-xl-first { - -ms-flex-order: -1; - order: -1; - } - .order-xl-last { - -ms-flex-order: 13; - order: 13; - } - .order-xl-0 { - -ms-flex-order: 0; - order: 0; - } - .order-xl-1 { - -ms-flex-order: 1; - order: 1; - } - .order-xl-2 { - -ms-flex-order: 2; - order: 2; - } - .order-xl-3 { - -ms-flex-order: 3; - order: 3; - } - .order-xl-4 { - -ms-flex-order: 4; - order: 4; - } - .order-xl-5 { - -ms-flex-order: 5; - order: 5; - } - .order-xl-6 { - -ms-flex-order: 6; - order: 6; - } - .order-xl-7 { - -ms-flex-order: 7; - order: 7; - } - .order-xl-8 { - -ms-flex-order: 8; - order: 8; - } - .order-xl-9 { - -ms-flex-order: 9; - order: 9; - } - .order-xl-10 { - -ms-flex-order: 10; - order: 10; - } - .order-xl-11 { - -ms-flex-order: 11; - order: 11; - } - .order-xl-12 { - -ms-flex-order: 12; - order: 12; - } - .offset-xl-0 { - margin-left: 0; - } - .offset-xl-1 { - margin-left: 8.333333%; - } - .offset-xl-2 { - margin-left: 16.666667%; - } - .offset-xl-3 { - margin-left: 25%; - } - .offset-xl-4 { - margin-left: 33.333333%; - } - .offset-xl-5 { - margin-left: 41.666667%; - } - .offset-xl-6 { - margin-left: 50%; - } - .offset-xl-7 { - margin-left: 58.333333%; - } - .offset-xl-8 { - margin-left: 66.666667%; - } - .offset-xl-9 { - margin-left: 75%; - } - .offset-xl-10 { - margin-left: 83.333333%; - } - .offset-xl-11 { - margin-left: 91.666667%; - } -} - -.table { - width: 100%; - max-width: 100%; - margin-bottom: 1rem; - background-color: transparent; -} - -.table th, -.table td { - padding: 0.75rem; - vertical-align: top; - border-top: 1px solid #dee2e6; -} - -.table thead th { - vertical-align: bottom; - border-bottom: 2px solid #dee2e6; -} - -.table tbody + tbody { - border-top: 2px solid #dee2e6; -} - -.table .table { - background-color: #fff; -} - -.table-sm th, -.table-sm td { - padding: 0.3rem; -} - -.table-bordered { - border: 1px solid #dee2e6; -} - -.table-bordered th, -.table-bordered td { - border: 1px solid #dee2e6; -} - -.table-bordered thead th, -.table-bordered thead td { - border-bottom-width: 2px; -} - -.table-borderless th, -.table-borderless td, -.table-borderless thead th, -.table-borderless tbody + tbody { - border: 0; -} - -.table-striped tbody tr:nth-of-type(odd) { - background-color: rgba(0, 0, 0, 0.05); -} - -.table-hover tbody tr:hover { - background-color: rgba(0, 0, 0, 0.075); -} - -.table-primary, -.table-primary > th, -.table-primary > td { - background-color: #b8daff; -} - -.table-hover .table-primary:hover { - background-color: #9fcdff; -} - -.table-hover .table-primary:hover > td, -.table-hover .table-primary:hover > th { - background-color: #9fcdff; -} - -.table-secondary, -.table-secondary > th, -.table-secondary > td { - background-color: #d6d8db; -} - -.table-hover .table-secondary:hover { - background-color: #c8cbcf; -} - -.table-hover .table-secondary:hover > td, -.table-hover .table-secondary:hover > th { - background-color: #c8cbcf; -} - -.table-success, -.table-success > th, -.table-success > td { - background-color: #c3e6cb; -} - -.table-hover .table-success:hover { - background-color: #b1dfbb; -} - -.table-hover .table-success:hover > td, -.table-hover .table-success:hover > th { - background-color: #b1dfbb; -} - -.table-info, -.table-info > th, -.table-info > td { - background-color: #bee5eb; -} - -.table-hover .table-info:hover { - background-color: #abdde5; -} - -.table-hover .table-info:hover > td, -.table-hover .table-info:hover > th { - background-color: #abdde5; -} - -.table-warning, -.table-warning > th, -.table-warning > td { - background-color: #ffeeba; -} - -.table-hover .table-warning:hover { - background-color: #ffe8a1; -} - -.table-hover .table-warning:hover > td, -.table-hover .table-warning:hover > th { - background-color: #ffe8a1; -} - -.table-danger, -.table-danger > th, -.table-danger > td { - background-color: #f5c6cb; -} - -.table-hover .table-danger:hover { - background-color: #f1b0b7; -} - -.table-hover .table-danger:hover > td, -.table-hover .table-danger:hover > th { - background-color: #f1b0b7; -} - -.table-light, -.table-light > th, -.table-light > td { - background-color: #fdfdfe; -} - -.table-hover .table-light:hover { - background-color: #ececf6; -} - -.table-hover .table-light:hover > td, -.table-hover .table-light:hover > th { - background-color: #ececf6; -} - -.table-dark, -.table-dark > th, -.table-dark > td { - background-color: #c6c8ca; -} - -.table-hover .table-dark:hover { - background-color: #b9bbbe; -} - -.table-hover .table-dark:hover > td, -.table-hover .table-dark:hover > th { - background-color: #b9bbbe; -} - -.table-active, -.table-active > th, -.table-active > td { - background-color: rgba(0, 0, 0, 0.075); -} - -.table-hover .table-active:hover { - background-color: rgba(0, 0, 0, 0.075); -} - -.table-hover .table-active:hover > td, -.table-hover .table-active:hover > th { - background-color: rgba(0, 0, 0, 0.075); -} - -.table .thead-dark th { - color: #fff; - background-color: #212529; - border-color: #32383e; -} - -.table .thead-light th { - color: #495057; - background-color: #e9ecef; - border-color: #dee2e6; -} - -.table-dark { - color: #fff; - background-color: #212529; -} - -.table-dark th, -.table-dark td, -.table-dark thead th { - border-color: #32383e; -} - -.table-dark.table-bordered { - border: 0; -} - -.table-dark.table-striped tbody tr:nth-of-type(odd) { - background-color: rgba(255, 255, 255, 0.05); -} - -.table-dark.table-hover tbody tr:hover { - background-color: rgba(255, 255, 255, 0.075); -} - -@media (max-width: 575.98px) { - .table-responsive-sm { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - -ms-overflow-style: -ms-autohiding-scrollbar; - } - .table-responsive-sm > .table-bordered { - border: 0; - } -} - -@media (max-width: 767.98px) { - .table-responsive-md { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - -ms-overflow-style: -ms-autohiding-scrollbar; - } - .table-responsive-md > .table-bordered { - border: 0; - } -} - -@media (max-width: 991.98px) { - .table-responsive-lg { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - -ms-overflow-style: -ms-autohiding-scrollbar; - } - .table-responsive-lg > .table-bordered { - border: 0; - } -} - -@media (max-width: 1199.98px) { - .table-responsive-xl { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - -ms-overflow-style: -ms-autohiding-scrollbar; - } - .table-responsive-xl > .table-bordered { - border: 0; - } -} - -.table-responsive { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - -ms-overflow-style: -ms-autohiding-scrollbar; -} - -.table-responsive > .table-bordered { - border: 0; -} - -.form-control { - display: block; - width: 100%; - padding: 0.375rem 0.75rem; - font-size: 1rem; - line-height: 1.5; - color: #495057; - background-color: #fff; - background-clip: padding-box; - border: 1px solid #ced4da; - border-radius: 0.25rem; - transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; -} - -@media screen and (prefers-reduced-motion: reduce) { - .form-control { - transition: none; - } -} - -.form-control::-ms-expand { - background-color: transparent; - border: 0; -} - -.form-control:focus { - color: #495057; - background-color: #fff; - border-color: #80bdff; - outline: 0; - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -.form-control::-webkit-input-placeholder { - color: #6c757d; - opacity: 1; -} - -.form-control::-moz-placeholder { - color: #6c757d; - opacity: 1; -} - -.form-control:-ms-input-placeholder { - color: #6c757d; - opacity: 1; -} - -.form-control::-ms-input-placeholder { - color: #6c757d; - opacity: 1; -} - -.form-control::placeholder { - color: #6c757d; - opacity: 1; -} - -.form-control:disabled, .form-control[readonly] { - background-color: #e9ecef; - opacity: 1; -} - -select.form-control:not([size]):not([multiple]) { - height: calc(2.25rem + 2px); -} - -select.form-control:focus::-ms-value { - color: #495057; - background-color: #fff; -} - -.form-control-file, -.form-control-range { - display: block; - width: 100%; -} - -.col-form-label { - padding-top: calc(0.375rem + 1px); - padding-bottom: calc(0.375rem + 1px); - margin-bottom: 0; - font-size: inherit; - line-height: 1.5; -} - -.col-form-label-lg { - padding-top: calc(0.5rem + 1px); - padding-bottom: calc(0.5rem + 1px); - font-size: 1.25rem; - line-height: 1.5; -} - -.col-form-label-sm { - padding-top: calc(0.25rem + 1px); - padding-bottom: calc(0.25rem + 1px); - font-size: 0.875rem; - line-height: 1.5; -} - -.form-control-plaintext { - display: block; - width: 100%; - padding-top: 0.375rem; - padding-bottom: 0.375rem; - margin-bottom: 0; - line-height: 1.5; - color: #212529; - background-color: transparent; - border: solid transparent; - border-width: 1px 0; -} - -.form-control-plaintext.form-control-sm, .input-group-sm > .form-control-plaintext.form-control, -.input-group-sm > .input-group-prepend > .form-control-plaintext.input-group-text, -.input-group-sm > .input-group-append > .form-control-plaintext.input-group-text, -.input-group-sm > .input-group-prepend > .form-control-plaintext.btn, -.input-group-sm > .input-group-append > .form-control-plaintext.btn, .form-control-plaintext.form-control-lg, .input-group-lg > .form-control-plaintext.form-control, -.input-group-lg > .input-group-prepend > .form-control-plaintext.input-group-text, -.input-group-lg > .input-group-append > .form-control-plaintext.input-group-text, -.input-group-lg > .input-group-prepend > .form-control-plaintext.btn, -.input-group-lg > .input-group-append > .form-control-plaintext.btn { - padding-right: 0; - padding-left: 0; -} - -.form-control-sm, .input-group-sm > .form-control, -.input-group-sm > .input-group-prepend > .input-group-text, -.input-group-sm > .input-group-append > .input-group-text, -.input-group-sm > .input-group-prepend > .btn, -.input-group-sm > .input-group-append > .btn { - padding: 0.25rem 0.5rem; - font-size: 0.875rem; - line-height: 1.5; - border-radius: 0.2rem; -} - -select.form-control-sm:not([size]):not([multiple]), .input-group-sm > select.form-control:not([size]):not([multiple]), -.input-group-sm > .input-group-prepend > select.input-group-text:not([size]):not([multiple]), -.input-group-sm > .input-group-append > select.input-group-text:not([size]):not([multiple]), -.input-group-sm > .input-group-prepend > select.btn:not([size]):not([multiple]), -.input-group-sm > .input-group-append > select.btn:not([size]):not([multiple]) { - height: calc(1.8125rem + 2px); -} - -.form-control-lg, .input-group-lg > .form-control, -.input-group-lg > .input-group-prepend > .input-group-text, -.input-group-lg > .input-group-append > .input-group-text, -.input-group-lg > .input-group-prepend > .btn, -.input-group-lg > .input-group-append > .btn { - padding: 0.5rem 1rem; - font-size: 1.25rem; - line-height: 1.5; - border-radius: 0.3rem; -} - -select.form-control-lg:not([size]):not([multiple]), .input-group-lg > select.form-control:not([size]):not([multiple]), -.input-group-lg > .input-group-prepend > select.input-group-text:not([size]):not([multiple]), -.input-group-lg > .input-group-append > select.input-group-text:not([size]):not([multiple]), -.input-group-lg > .input-group-prepend > select.btn:not([size]):not([multiple]), -.input-group-lg > .input-group-append > select.btn:not([size]):not([multiple]) { - height: calc(2.875rem + 2px); -} - -.form-group { - margin-bottom: 1rem; -} - -.form-text { - display: block; - margin-top: 0.25rem; -} - -.form-row { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - margin-right: -5px; - margin-left: -5px; -} - -.form-row > .col, -.form-row > [class*="col-"] { - padding-right: 5px; - padding-left: 5px; -} - -.form-check { - position: relative; - display: block; - padding-left: 1.25rem; -} - -.form-check-input { - position: absolute; - margin-top: 0.3rem; - margin-left: -1.25rem; -} - -.form-check-input:disabled ~ .form-check-label { - color: #6c757d; -} - -.form-check-label { - margin-bottom: 0; -} - -.form-check-inline { - display: -ms-inline-flexbox; - display: inline-flex; - -ms-flex-align: center; - align-items: center; - padding-left: 0; - margin-right: 0.75rem; -} - -.form-check-inline .form-check-input { - position: static; - margin-top: 0; - margin-right: 0.3125rem; - margin-left: 0; -} - -.valid-feedback { - display: none; - width: 100%; - margin-top: 0.25rem; - font-size: 80%; - color: #28a745; -} - -.valid-tooltip { - position: absolute; - top: 100%; - z-index: 5; - display: none; - max-width: 100%; - padding: .5rem; - margin-top: .1rem; - font-size: .875rem; - line-height: 1; - color: #fff; - background-color: rgba(40, 167, 69, 0.8); - border-radius: .2rem; -} - -.was-validated .form-control:valid, .form-control.is-valid, .was-validated -.custom-select:valid, -.custom-select.is-valid { - border-color: #28a745; -} - -.was-validated .form-control:valid:focus, .form-control.is-valid:focus, .was-validated -.custom-select:valid:focus, -.custom-select.is-valid:focus { - border-color: #28a745; - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); -} - -.was-validated .form-control:valid ~ .valid-feedback, -.was-validated .form-control:valid ~ .valid-tooltip, .form-control.is-valid ~ .valid-feedback, -.form-control.is-valid ~ .valid-tooltip, .was-validated -.custom-select:valid ~ .valid-feedback, -.was-validated -.custom-select:valid ~ .valid-tooltip, -.custom-select.is-valid ~ .valid-feedback, -.custom-select.is-valid ~ .valid-tooltip { - display: block; -} - -.was-validated .form-control-file:valid ~ .valid-feedback, -.was-validated .form-control-file:valid ~ .valid-tooltip, .form-control-file.is-valid ~ .valid-feedback, -.form-control-file.is-valid ~ .valid-tooltip { - display: block; -} - -.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { - color: #28a745; -} - -.was-validated .form-check-input:valid ~ .valid-feedback, -.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback, -.form-check-input.is-valid ~ .valid-tooltip { - display: block; -} - -.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label { - color: #28a745; -} - -.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before { - background-color: #71dd8a; -} - -.was-validated .custom-control-input:valid ~ .valid-feedback, -.was-validated .custom-control-input:valid ~ .valid-tooltip, .custom-control-input.is-valid ~ .valid-feedback, -.custom-control-input.is-valid ~ .valid-tooltip { - display: block; -} - -.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before { - background-color: #34ce57; -} - -.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before { - box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(40, 167, 69, 0.25); -} - -.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label { - border-color: #28a745; -} - -.was-validated .custom-file-input:valid ~ .custom-file-label::before, .custom-file-input.is-valid ~ .custom-file-label::before { - border-color: inherit; -} - -.was-validated .custom-file-input:valid ~ .valid-feedback, -.was-validated .custom-file-input:valid ~ .valid-tooltip, .custom-file-input.is-valid ~ .valid-feedback, -.custom-file-input.is-valid ~ .valid-tooltip { - display: block; -} - -.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label { - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); -} - -.invalid-feedback { - display: none; - width: 100%; - margin-top: 0.25rem; - font-size: 80%; - color: #dc3545; -} - -.invalid-tooltip { - position: absolute; - top: 100%; - z-index: 5; - display: none; - max-width: 100%; - padding: .5rem; - margin-top: .1rem; - font-size: .875rem; - line-height: 1; - color: #fff; - background-color: rgba(220, 53, 69, 0.8); - border-radius: .2rem; -} - -.was-validated .form-control:invalid, .form-control.is-invalid, .was-validated -.custom-select:invalid, -.custom-select.is-invalid { - border-color: #dc3545; -} - -.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus, .was-validated -.custom-select:invalid:focus, -.custom-select.is-invalid:focus { - border-color: #dc3545; - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); -} - -.was-validated .form-control:invalid ~ .invalid-feedback, -.was-validated .form-control:invalid ~ .invalid-tooltip, .form-control.is-invalid ~ .invalid-feedback, -.form-control.is-invalid ~ .invalid-tooltip, .was-validated -.custom-select:invalid ~ .invalid-feedback, -.was-validated -.custom-select:invalid ~ .invalid-tooltip, -.custom-select.is-invalid ~ .invalid-feedback, -.custom-select.is-invalid ~ .invalid-tooltip { - display: block; -} - -.was-validated .form-control-file:invalid ~ .invalid-feedback, -.was-validated .form-control-file:invalid ~ .invalid-tooltip, .form-control-file.is-invalid ~ .invalid-feedback, -.form-control-file.is-invalid ~ .invalid-tooltip { - display: block; -} - -.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { - color: #dc3545; -} - -.was-validated .form-check-input:invalid ~ .invalid-feedback, -.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback, -.form-check-input.is-invalid ~ .invalid-tooltip { - display: block; -} - -.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label { - color: #dc3545; -} - -.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before { - background-color: #efa2a9; -} - -.was-validated .custom-control-input:invalid ~ .invalid-feedback, -.was-validated .custom-control-input:invalid ~ .invalid-tooltip, .custom-control-input.is-invalid ~ .invalid-feedback, -.custom-control-input.is-invalid ~ .invalid-tooltip { - display: block; -} - -.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before { - background-color: #e4606d; -} - -.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before { - box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(220, 53, 69, 0.25); -} - -.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label { - border-color: #dc3545; -} - -.was-validated .custom-file-input:invalid ~ .custom-file-label::before, .custom-file-input.is-invalid ~ .custom-file-label::before { - border-color: inherit; -} - -.was-validated .custom-file-input:invalid ~ .invalid-feedback, -.was-validated .custom-file-input:invalid ~ .invalid-tooltip, .custom-file-input.is-invalid ~ .invalid-feedback, -.custom-file-input.is-invalid ~ .invalid-tooltip { - display: block; -} - -.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label { - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); -} - -.form-inline { - display: -ms-flexbox; - display: flex; - -ms-flex-flow: row wrap; - flex-flow: row wrap; - -ms-flex-align: center; - align-items: center; -} - -.form-inline .form-check { - width: 100%; -} - -@media (min-width: 576px) { - .form-inline label { - display: -ms-flexbox; - display: flex; - -ms-flex-align: center; - align-items: center; - -ms-flex-pack: center; - justify-content: center; - margin-bottom: 0; - } - .form-inline .form-group { - display: -ms-flexbox; - display: flex; - -ms-flex: 0 0 auto; - flex: 0 0 auto; - -ms-flex-flow: row wrap; - flex-flow: row wrap; - -ms-flex-align: center; - align-items: center; - margin-bottom: 0; - } - .form-inline .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .form-inline .form-control-plaintext { - display: inline-block; - } - .form-inline .input-group, - .form-inline .custom-select { - width: auto; - } - .form-inline .form-check { - display: -ms-flexbox; - display: flex; - -ms-flex-align: center; - align-items: center; - -ms-flex-pack: center; - justify-content: center; - width: auto; - padding-left: 0; - } - .form-inline .form-check-input { - position: relative; - margin-top: 0; - margin-right: 0.25rem; - margin-left: 0; - } - .form-inline .custom-control { - -ms-flex-align: center; - align-items: center; - -ms-flex-pack: center; - justify-content: center; - } - .form-inline .custom-control-label { - margin-bottom: 0; - } -} - -.btn { - display: inline-block; - font-weight: 400; - text-align: center; - white-space: nowrap; - vertical-align: middle; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - border: 1px solid transparent; - padding: 0.375rem 0.75rem; - font-size: 1rem; - line-height: 1.5; - border-radius: 0.25rem; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; -} - -@media screen and (prefers-reduced-motion: reduce) { - .btn { - transition: none; - } -} - -.btn:hover, .btn:focus { - text-decoration: none; -} - -.btn:focus, .btn.focus { - outline: 0; - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -.btn.disabled, .btn:disabled { - opacity: 0.65; -} - -.btn:not(:disabled):not(.disabled) { - cursor: pointer; -} - -.btn:not(:disabled):not(.disabled):active, .btn:not(:disabled):not(.disabled).active { - background-image: none; -} - -a.btn.disabled, -fieldset:disabled a.btn { - pointer-events: none; -} - -.btn-primary { - color: #fff; - background-color: #007bff; - border-color: #007bff; -} - -.btn-primary:hover { - color: #fff; - background-color: #0069d9; - border-color: #0062cc; -} - -.btn-primary:focus, .btn-primary.focus { - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); -} - -.btn-primary.disabled, .btn-primary:disabled { - color: #fff; - background-color: #007bff; - border-color: #007bff; -} - -.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active, -.show > .btn-primary.dropdown-toggle { - color: #fff; - background-color: #0062cc; - border-color: #005cbf; -} - -.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus, -.show > .btn-primary.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); -} - -.btn-secondary { - color: #fff; - background-color: #6c757d; - border-color: #6c757d; -} - -.btn-secondary:hover { - color: #fff; - background-color: #5a6268; - border-color: #545b62; -} - -.btn-secondary:focus, .btn-secondary.focus { - box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); -} - -.btn-secondary.disabled, .btn-secondary:disabled { - color: #fff; - background-color: #6c757d; - border-color: #6c757d; -} - -.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active, -.show > .btn-secondary.dropdown-toggle { - color: #fff; - background-color: #545b62; - border-color: #4e555b; -} - -.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus, -.show > .btn-secondary.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); -} - -.btn-success { - color: #fff; - background-color: #28a745; - border-color: #28a745; -} - -.btn-success:hover { - color: #fff; - background-color: #218838; - border-color: #1e7e34; -} - -.btn-success:focus, .btn-success.focus { - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); -} - -.btn-success.disabled, .btn-success:disabled { - color: #fff; - background-color: #28a745; - border-color: #28a745; -} - -.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active, -.show > .btn-success.dropdown-toggle { - color: #fff; - background-color: #1e7e34; - border-color: #1c7430; -} - -.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus, -.show > .btn-success.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); -} - -.btn-info { - color: #fff; - background-color: #17a2b8; - border-color: #17a2b8; -} - -.btn-info:hover { - color: #fff; - background-color: #138496; - border-color: #117a8b; -} - -.btn-info:focus, .btn-info.focus { - box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); -} - -.btn-info.disabled, .btn-info:disabled { - color: #fff; - background-color: #17a2b8; - border-color: #17a2b8; -} - -.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active, -.show > .btn-info.dropdown-toggle { - color: #fff; - background-color: #117a8b; - border-color: #10707f; -} - -.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus, -.show > .btn-info.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); -} - -.btn-warning { - color: #212529; - background-color: #ffc107; - border-color: #ffc107; -} - -.btn-warning:hover { - color: #212529; - background-color: #e0a800; - border-color: #d39e00; -} - -.btn-warning:focus, .btn-warning.focus { - box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); -} - -.btn-warning.disabled, .btn-warning:disabled { - color: #212529; - background-color: #ffc107; - border-color: #ffc107; -} - -.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active, -.show > .btn-warning.dropdown-toggle { - color: #212529; - background-color: #d39e00; - border-color: #c69500; -} - -.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus, -.show > .btn-warning.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); -} - -.btn-danger { - color: #fff; - background-color: #dc3545; - border-color: #dc3545; -} - -.btn-danger:hover { - color: #fff; - background-color: #c82333; - border-color: #bd2130; -} - -.btn-danger:focus, .btn-danger.focus { - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); -} - -.btn-danger.disabled, .btn-danger:disabled { - color: #fff; - background-color: #dc3545; - border-color: #dc3545; -} - -.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active, -.show > .btn-danger.dropdown-toggle { - color: #fff; - background-color: #bd2130; - border-color: #b21f2d; -} - -.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus, -.show > .btn-danger.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); -} - -.btn-light { - color: #212529; - background-color: #f8f9fa; - border-color: #f8f9fa; -} - -.btn-light:hover { - color: #212529; - background-color: #e2e6ea; - border-color: #dae0e5; -} - -.btn-light:focus, .btn-light.focus { - box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); -} - -.btn-light.disabled, .btn-light:disabled { - color: #212529; - background-color: #f8f9fa; - border-color: #f8f9fa; -} - -.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active, -.show > .btn-light.dropdown-toggle { - color: #212529; - background-color: #dae0e5; - border-color: #d3d9df; -} - -.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus, -.show > .btn-light.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); -} - -.btn-dark { - color: #fff; - background-color: #343a40; - border-color: #343a40; -} - -.btn-dark:hover { - color: #fff; - background-color: #23272b; - border-color: #1d2124; -} - -.btn-dark:focus, .btn-dark.focus { - box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); -} - -.btn-dark.disabled, .btn-dark:disabled { - color: #fff; - background-color: #343a40; - border-color: #343a40; -} - -.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active, -.show > .btn-dark.dropdown-toggle { - color: #fff; - background-color: #1d2124; - border-color: #171a1d; -} - -.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus, -.show > .btn-dark.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); -} - -.btn-outline-primary { - color: #007bff; - background-color: transparent; - background-image: none; - border-color: #007bff; -} - -.btn-outline-primary:hover { - color: #fff; - background-color: #007bff; - border-color: #007bff; -} - -.btn-outline-primary:focus, .btn-outline-primary.focus { - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); -} - -.btn-outline-primary.disabled, .btn-outline-primary:disabled { - color: #007bff; - background-color: transparent; -} - -.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active, -.show > .btn-outline-primary.dropdown-toggle { - color: #fff; - background-color: #007bff; - border-color: #007bff; -} - -.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-primary.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); -} - -.btn-outline-secondary { - color: #6c757d; - background-color: transparent; - background-image: none; - border-color: #6c757d; -} - -.btn-outline-secondary:hover { - color: #fff; - background-color: #6c757d; - border-color: #6c757d; -} - -.btn-outline-secondary:focus, .btn-outline-secondary.focus { - box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); -} - -.btn-outline-secondary.disabled, .btn-outline-secondary:disabled { - color: #6c757d; - background-color: transparent; -} - -.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active, -.show > .btn-outline-secondary.dropdown-toggle { - color: #fff; - background-color: #6c757d; - border-color: #6c757d; -} - -.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-secondary.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); -} - -.btn-outline-success { - color: #28a745; - background-color: transparent; - background-image: none; - border-color: #28a745; -} - -.btn-outline-success:hover { - color: #fff; - background-color: #28a745; - border-color: #28a745; -} - -.btn-outline-success:focus, .btn-outline-success.focus { - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); -} - -.btn-outline-success.disabled, .btn-outline-success:disabled { - color: #28a745; - background-color: transparent; -} - -.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active, -.show > .btn-outline-success.dropdown-toggle { - color: #fff; - background-color: #28a745; - border-color: #28a745; -} - -.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-success.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); -} - -.btn-outline-info { - color: #17a2b8; - background-color: transparent; - background-image: none; - border-color: #17a2b8; -} - -.btn-outline-info:hover { - color: #fff; - background-color: #17a2b8; - border-color: #17a2b8; -} - -.btn-outline-info:focus, .btn-outline-info.focus { - box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); -} - -.btn-outline-info.disabled, .btn-outline-info:disabled { - color: #17a2b8; - background-color: transparent; -} - -.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active, -.show > .btn-outline-info.dropdown-toggle { - color: #fff; - background-color: #17a2b8; - border-color: #17a2b8; -} - -.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-info.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); -} - -.btn-outline-warning { - color: #ffc107; - background-color: transparent; - background-image: none; - border-color: #ffc107; -} - -.btn-outline-warning:hover { - color: #212529; - background-color: #ffc107; - border-color: #ffc107; -} - -.btn-outline-warning:focus, .btn-outline-warning.focus { - box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); -} - -.btn-outline-warning.disabled, .btn-outline-warning:disabled { - color: #ffc107; - background-color: transparent; -} - -.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active, -.show > .btn-outline-warning.dropdown-toggle { - color: #212529; - background-color: #ffc107; - border-color: #ffc107; -} - -.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-warning.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); -} - -.btn-outline-danger { - color: #dc3545; - background-color: transparent; - background-image: none; - border-color: #dc3545; -} - -.btn-outline-danger:hover { - color: #fff; - background-color: #dc3545; - border-color: #dc3545; -} - -.btn-outline-danger:focus, .btn-outline-danger.focus { - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); -} - -.btn-outline-danger.disabled, .btn-outline-danger:disabled { - color: #dc3545; - background-color: transparent; -} - -.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active, -.show > .btn-outline-danger.dropdown-toggle { - color: #fff; - background-color: #dc3545; - border-color: #dc3545; -} - -.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-danger.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); -} - -.btn-outline-light { - color: #f8f9fa; - background-color: transparent; - background-image: none; - border-color: #f8f9fa; -} - -.btn-outline-light:hover { - color: #212529; - background-color: #f8f9fa; - border-color: #f8f9fa; -} - -.btn-outline-light:focus, .btn-outline-light.focus { - box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); -} - -.btn-outline-light.disabled, .btn-outline-light:disabled { - color: #f8f9fa; - background-color: transparent; -} - -.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active, -.show > .btn-outline-light.dropdown-toggle { - color: #212529; - background-color: #f8f9fa; - border-color: #f8f9fa; -} - -.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-light.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); -} - -.btn-outline-dark { - color: #343a40; - background-color: transparent; - background-image: none; - border-color: #343a40; -} - -.btn-outline-dark:hover { - color: #fff; - background-color: #343a40; - border-color: #343a40; -} - -.btn-outline-dark:focus, .btn-outline-dark.focus { - box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); -} - -.btn-outline-dark.disabled, .btn-outline-dark:disabled { - color: #343a40; - background-color: transparent; -} - -.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active, -.show > .btn-outline-dark.dropdown-toggle { - color: #fff; - background-color: #343a40; - border-color: #343a40; -} - -.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-dark.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); -} - -.btn-link { - font-weight: 400; - color: #007bff; - background-color: transparent; -} - -.btn-link:hover { - color: #0056b3; - text-decoration: underline; - background-color: transparent; - border-color: transparent; -} - -.btn-link:focus, .btn-link.focus { - text-decoration: underline; - border-color: transparent; - box-shadow: none; -} - -.btn-link:disabled, .btn-link.disabled { - color: #6c757d; - pointer-events: none; -} - -.btn-lg, .btn-group-lg > .btn { - padding: 0.5rem 1rem; - font-size: 1.25rem; - line-height: 1.5; - border-radius: 0.3rem; -} - -.btn-sm, .btn-group-sm > .btn { - padding: 0.25rem 0.5rem; - font-size: 0.875rem; - line-height: 1.5; - border-radius: 0.2rem; -} - -.btn-block { - display: block; - width: 100%; -} - -.btn-block + .btn-block { - margin-top: 0.5rem; -} - -input[type="submit"].btn-block, -input[type="reset"].btn-block, -input[type="button"].btn-block { - width: 100%; -} - -.fade { - transition: opacity 0.15s linear; -} - -@media screen and (prefers-reduced-motion: reduce) { - .fade { - transition: none; - } -} - -.fade:not(.show) { - opacity: 0; -} - -.collapse:not(.show) { - display: none; -} - -.collapsing { - position: relative; - height: 0; - overflow: hidden; - transition: height 0.35s ease; -} - -@media screen and (prefers-reduced-motion: reduce) { - .collapsing { - transition: none; - } -} - -.dropup, -.dropright, -.dropdown, -.dropleft { - position: relative; -} - -.dropdown-toggle::after { - display: inline-block; - width: 0; - height: 0; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid; - border-right: 0.3em solid transparent; - border-bottom: 0; - border-left: 0.3em solid transparent; -} - -.dropdown-toggle:empty::after { - margin-left: 0; -} - -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - display: none; - float: left; - min-width: 10rem; - padding: 0.5rem 0; - margin: 0.125rem 0 0; - font-size: 1rem; - color: #212529; - text-align: left; - list-style: none; - background-color: #fff; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0.15); - border-radius: 0.25rem; -} - -.dropdown-menu-right { - right: 0; - left: auto; -} - -.dropup .dropdown-menu { - top: auto; - bottom: 100%; - margin-top: 0; - margin-bottom: 0.125rem; -} - -.dropup .dropdown-toggle::after { - display: inline-block; - width: 0; - height: 0; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0; - border-right: 0.3em solid transparent; - border-bottom: 0.3em solid; - border-left: 0.3em solid transparent; -} - -.dropup .dropdown-toggle:empty::after { - margin-left: 0; -} - -.dropright .dropdown-menu { - top: 0; - right: auto; - left: 100%; - margin-top: 0; - margin-left: 0.125rem; -} - -.dropright .dropdown-toggle::after { - display: inline-block; - width: 0; - height: 0; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid transparent; - border-right: 0; - border-bottom: 0.3em solid transparent; - border-left: 0.3em solid; -} - -.dropright .dropdown-toggle:empty::after { - margin-left: 0; -} - -.dropright .dropdown-toggle::after { - vertical-align: 0; -} - -.dropleft .dropdown-menu { - top: 0; - right: 100%; - left: auto; - margin-top: 0; - margin-right: 0.125rem; -} - -.dropleft .dropdown-toggle::after { - display: inline-block; - width: 0; - height: 0; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; -} - -.dropleft .dropdown-toggle::after { - display: none; -} - -.dropleft .dropdown-toggle::before { - display: inline-block; - width: 0; - height: 0; - margin-right: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid transparent; - border-right: 0.3em solid; - border-bottom: 0.3em solid transparent; -} - -.dropleft .dropdown-toggle:empty::after { - margin-left: 0; -} - -.dropleft .dropdown-toggle::before { - vertical-align: 0; -} - -.dropdown-menu[x-placement^="top"], .dropdown-menu[x-placement^="right"], .dropdown-menu[x-placement^="bottom"], .dropdown-menu[x-placement^="left"] { - right: auto; - bottom: auto; -} - -.dropdown-divider { - height: 0; - margin: 0.5rem 0; - overflow: hidden; - border-top: 1px solid #e9ecef; -} - -.dropdown-item { - display: block; - width: 100%; - padding: 0.25rem 1.5rem; - clear: both; - font-weight: 400; - color: #212529; - text-align: inherit; - white-space: nowrap; - background-color: transparent; - border: 0; -} - -.dropdown-item:hover, .dropdown-item:focus { - color: #16181b; - text-decoration: none; - background-color: #f8f9fa; -} - -.dropdown-item.active, .dropdown-item:active { - color: #fff; - text-decoration: none; - background-color: #007bff; -} - -.dropdown-item.disabled, .dropdown-item:disabled { - color: #6c757d; - background-color: transparent; -} - -.dropdown-menu.show { - display: block; -} - -.dropdown-header { - display: block; - padding: 0.5rem 1.5rem; - margin-bottom: 0; - font-size: 0.875rem; - color: #6c757d; - white-space: nowrap; -} - -.dropdown-item-text { - display: block; - padding: 0.25rem 1.5rem; - color: #212529; -} - -.btn-group, -.btn-group-vertical { - position: relative; - display: -ms-inline-flexbox; - display: inline-flex; - vertical-align: middle; -} - -.btn-group > .btn, -.btn-group-vertical > .btn { - position: relative; - -ms-flex: 0 1 auto; - flex: 0 1 auto; -} - -.btn-group > .btn:hover, -.btn-group-vertical > .btn:hover { - z-index: 1; -} - -.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active, -.btn-group-vertical > .btn:focus, -.btn-group-vertical > .btn:active, -.btn-group-vertical > .btn.active { - z-index: 1; -} - -.btn-group .btn + .btn, -.btn-group .btn + .btn-group, -.btn-group .btn-group + .btn, -.btn-group .btn-group + .btn-group, -.btn-group-vertical .btn + .btn, -.btn-group-vertical .btn + .btn-group, -.btn-group-vertical .btn-group + .btn, -.btn-group-vertical .btn-group + .btn-group { - margin-left: -1px; -} - -.btn-toolbar { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -ms-flex-pack: start; - justify-content: flex-start; -} - -.btn-toolbar .input-group { - width: auto; -} - -.btn-group > .btn:first-child { - margin-left: 0; -} - -.btn-group > .btn:not(:last-child):not(.dropdown-toggle), -.btn-group > .btn-group:not(:last-child) > .btn { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.btn-group > .btn:not(:first-child), -.btn-group > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - -.dropdown-toggle-split { - padding-right: 0.5625rem; - padding-left: 0.5625rem; -} - -.dropdown-toggle-split::after, -.dropup .dropdown-toggle-split::after, -.dropright .dropdown-toggle-split::after { - margin-left: 0; -} - -.dropleft .dropdown-toggle-split::before { - margin-right: 0; -} - -.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split { - padding-right: 0.375rem; - padding-left: 0.375rem; -} - -.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split { - padding-right: 0.75rem; - padding-left: 0.75rem; -} - -.btn-group-vertical { - -ms-flex-direction: column; - flex-direction: column; - -ms-flex-align: start; - align-items: flex-start; - -ms-flex-pack: center; - justify-content: center; -} - -.btn-group-vertical .btn, -.btn-group-vertical .btn-group { - width: 100%; -} - -.btn-group-vertical > .btn + .btn, -.btn-group-vertical > .btn + .btn-group, -.btn-group-vertical > .btn-group + .btn, -.btn-group-vertical > .btn-group + .btn-group { - margin-top: -1px; - margin-left: 0; -} - -.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle), -.btn-group-vertical > .btn-group:not(:last-child) > .btn { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} - -.btn-group-vertical > .btn:not(:first-child), -.btn-group-vertical > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-top-right-radius: 0; -} - -.btn-group-toggle > .btn, -.btn-group-toggle > .btn-group > .btn { - margin-bottom: 0; -} - -.btn-group-toggle > .btn input[type="radio"], -.btn-group-toggle > .btn input[type="checkbox"], -.btn-group-toggle > .btn-group > .btn input[type="radio"], -.btn-group-toggle > .btn-group > .btn input[type="checkbox"] { - position: absolute; - clip: rect(0, 0, 0, 0); - pointer-events: none; -} - -.input-group { - position: relative; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -ms-flex-align: stretch; - align-items: stretch; - width: 100%; -} - -.input-group > .form-control, -.input-group > .custom-select, -.input-group > .custom-file { - position: relative; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - width: 1%; - margin-bottom: 0; -} - -.input-group > .form-control:focus, -.input-group > .custom-select:focus, -.input-group > .custom-file:focus { - z-index: 3; -} - -.input-group > .form-control + .form-control, -.input-group > .form-control + .custom-select, -.input-group > .form-control + .custom-file, -.input-group > .custom-select + .form-control, -.input-group > .custom-select + .custom-select, -.input-group > .custom-select + .custom-file, -.input-group > .custom-file + .form-control, -.input-group > .custom-file + .custom-select, -.input-group > .custom-file + .custom-file { - margin-left: -1px; -} - -.input-group > .form-control:not(:last-child), -.input-group > .custom-select:not(:last-child) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.input-group > .form-control:not(:first-child), -.input-group > .custom-select:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - -.input-group > .custom-file { - display: -ms-flexbox; - display: flex; - -ms-flex-align: center; - align-items: center; -} - -.input-group > .custom-file:not(:last-child) .custom-file-label, -.input-group > .custom-file:not(:last-child) .custom-file-label::after { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.input-group > .custom-file:not(:first-child) .custom-file-label { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - -.input-group-prepend, -.input-group-append { - display: -ms-flexbox; - display: flex; -} - -.input-group-prepend .btn, -.input-group-append .btn { - position: relative; - z-index: 2; -} - -.input-group-prepend .btn + .btn, -.input-group-prepend .btn + .input-group-text, -.input-group-prepend .input-group-text + .input-group-text, -.input-group-prepend .input-group-text + .btn, -.input-group-append .btn + .btn, -.input-group-append .btn + .input-group-text, -.input-group-append .input-group-text + .input-group-text, -.input-group-append .input-group-text + .btn { - margin-left: -1px; -} - -.input-group-prepend { - margin-right: -1px; -} - -.input-group-append { - margin-left: -1px; -} - -.input-group-text { - display: -ms-flexbox; - display: flex; - -ms-flex-align: center; - align-items: center; - padding: 0.375rem 0.75rem; - margin-bottom: 0; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: #495057; - text-align: center; - white-space: nowrap; - background-color: #e9ecef; - border: 1px solid #ced4da; - border-radius: 0.25rem; -} - -.input-group-text input[type="radio"], -.input-group-text input[type="checkbox"] { - margin-top: 0; -} - -.input-group > .input-group-prepend > .btn, -.input-group > .input-group-prepend > .input-group-text, -.input-group > .input-group-append:not(:last-child) > .btn, -.input-group > .input-group-append:not(:last-child) > .input-group-text, -.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle), -.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.input-group > .input-group-append > .btn, -.input-group > .input-group-append > .input-group-text, -.input-group > .input-group-prepend:not(:first-child) > .btn, -.input-group > .input-group-prepend:not(:first-child) > .input-group-text, -.input-group > .input-group-prepend:first-child > .btn:not(:first-child), -.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - -.custom-control { - position: relative; - display: block; - min-height: 1.5rem; - padding-left: 1.5rem; -} - -.custom-control-inline { - display: -ms-inline-flexbox; - display: inline-flex; - margin-right: 1rem; -} - -.custom-control-input { - position: absolute; - z-index: -1; - opacity: 0; -} - -.custom-control-input:checked ~ .custom-control-label::before { - color: #fff; - background-color: #007bff; -} - -.custom-control-input:focus ~ .custom-control-label::before { - box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -.custom-control-input:active ~ .custom-control-label::before { - color: #fff; - background-color: #b3d7ff; -} - -.custom-control-input:disabled ~ .custom-control-label { - color: #6c757d; -} - -.custom-control-input:disabled ~ .custom-control-label::before { - background-color: #e9ecef; -} - -.custom-control-label { - position: relative; - margin-bottom: 0; -} - -.custom-control-label::before { - position: absolute; - top: 0.25rem; - left: -1.5rem; - display: block; - width: 1rem; - height: 1rem; - pointer-events: none; - content: ""; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - background-color: #dee2e6; -} - -.custom-control-label::after { - position: absolute; - top: 0.25rem; - left: -1.5rem; - display: block; - width: 1rem; - height: 1rem; - content: ""; - background-repeat: no-repeat; - background-position: center center; - background-size: 50% 50%; -} - -.custom-checkbox .custom-control-label::before { - border-radius: 0.25rem; -} - -.custom-checkbox .custom-control-input:checked ~ .custom-control-label::before { - background-color: #007bff; -} - -.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after { - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E"); -} - -.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before { - background-color: #007bff; -} - -.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after { - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E"); -} - -.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before { - background-color: rgba(0, 123, 255, 0.5); -} - -.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before { - background-color: rgba(0, 123, 255, 0.5); -} - -.custom-radio .custom-control-label::before { - border-radius: 50%; -} - -.custom-radio .custom-control-input:checked ~ .custom-control-label::before { - background-color: #007bff; -} - -.custom-radio .custom-control-input:checked ~ .custom-control-label::after { - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E"); -} - -.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before { - background-color: rgba(0, 123, 255, 0.5); -} - -.custom-select { - display: inline-block; - width: 100%; - height: calc(2.25rem + 2px); - padding: 0.375rem 1.75rem 0.375rem 0.75rem; - line-height: 1.5; - color: #495057; - vertical-align: middle; - background: #fff url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right 0.75rem center; - background-size: 8px 10px; - border: 1px solid #ced4da; - border-radius: 0.25rem; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; -} - -.custom-select:focus { - border-color: #80bdff; - outline: 0; - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075), 0 0 5px rgba(128, 189, 255, 0.5); -} - -.custom-select:focus::-ms-value { - color: #495057; - background-color: #fff; -} - -.custom-select[multiple], .custom-select[size]:not([size="1"]) { - height: auto; - padding-right: 0.75rem; - background-image: none; -} - -.custom-select:disabled { - color: #6c757d; - background-color: #e9ecef; -} - -.custom-select::-ms-expand { - opacity: 0; -} - -.custom-select-sm { - height: calc(1.8125rem + 2px); - padding-top: 0.375rem; - padding-bottom: 0.375rem; - font-size: 75%; -} - -.custom-select-lg { - height: calc(2.875rem + 2px); - padding-top: 0.375rem; - padding-bottom: 0.375rem; - font-size: 125%; -} - -.custom-file { - position: relative; - display: inline-block; - width: 100%; - height: calc(2.25rem + 2px); - margin-bottom: 0; -} - -.custom-file-input { - position: relative; - z-index: 2; - width: 100%; - height: calc(2.25rem + 2px); - margin: 0; - opacity: 0; -} - -.custom-file-input:focus ~ .custom-file-label { - border-color: #80bdff; - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -.custom-file-input:focus ~ .custom-file-label::after { - border-color: #80bdff; -} - -.custom-file-input:lang(en) ~ .custom-file-label::after { - content: "Browse"; -} - -.custom-file-label { - position: absolute; - top: 0; - right: 0; - left: 0; - z-index: 1; - height: calc(2.25rem + 2px); - padding: 0.375rem 0.75rem; - line-height: 1.5; - color: #495057; - background-color: #fff; - border: 1px solid #ced4da; - border-radius: 0.25rem; -} - -.custom-file-label::after { - position: absolute; - top: 0; - right: 0; - bottom: 0; - z-index: 3; - display: block; - height: 2.25rem; - padding: 0.375rem 0.75rem; - line-height: 1.5; - color: #495057; - content: "Browse"; - background-color: #e9ecef; - border-left: 1px solid #ced4da; - border-radius: 0 0.25rem 0.25rem 0; -} - -.custom-range { - width: 100%; - padding-left: 0; - background-color: transparent; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; -} - -.custom-range:focus { - outline: none; -} - -.custom-range::-moz-focus-outer { - border: 0; -} - -.custom-range::-webkit-slider-thumb { - width: 1rem; - height: 1rem; - margin-top: -0.25rem; - background-color: #007bff; - border: 0; - border-radius: 1rem; - -webkit-appearance: none; - appearance: none; -} - -.custom-range::-webkit-slider-thumb:focus { - outline: none; - box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -.custom-range::-webkit-slider-thumb:active { - background-color: #b3d7ff; -} - -.custom-range::-webkit-slider-runnable-track { - width: 100%; - height: 0.5rem; - color: transparent; - cursor: pointer; - background-color: #dee2e6; - border-color: transparent; - border-radius: 1rem; -} - -.custom-range::-moz-range-thumb { - width: 1rem; - height: 1rem; - background-color: #007bff; - border: 0; - border-radius: 1rem; - -moz-appearance: none; - appearance: none; -} - -.custom-range::-moz-range-thumb:focus { - outline: none; - box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -.custom-range::-moz-range-thumb:active { - background-color: #b3d7ff; -} - -.custom-range::-moz-range-track { - width: 100%; - height: 0.5rem; - color: transparent; - cursor: pointer; - background-color: #dee2e6; - border-color: transparent; - border-radius: 1rem; -} - -.custom-range::-ms-thumb { - width: 1rem; - height: 1rem; - background-color: #007bff; - border: 0; - border-radius: 1rem; - appearance: none; -} - -.custom-range::-ms-thumb:focus { - outline: none; - box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -.custom-range::-ms-thumb:active { - background-color: #b3d7ff; -} - -.custom-range::-ms-track { - width: 100%; - height: 0.5rem; - color: transparent; - cursor: pointer; - background-color: transparent; - border-color: transparent; - border-width: 0.5rem; -} - -.custom-range::-ms-fill-lower { - background-color: #dee2e6; - border-radius: 1rem; -} - -.custom-range::-ms-fill-upper { - margin-right: 15px; - background-color: #dee2e6; - border-radius: 1rem; -} - -.nav { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - padding-left: 0; - margin-bottom: 0; - list-style: none; -} - -.nav-link { - display: block; - padding: 0.5rem 1rem; -} - -.nav-link:hover, .nav-link:focus { - text-decoration: none; -} - -.nav-link.disabled { - color: #6c757d; -} - -.nav-tabs { - border-bottom: 1px solid #dee2e6; -} - -.nav-tabs .nav-item { - margin-bottom: -1px; -} - -.nav-tabs .nav-link { - border: 1px solid transparent; - border-top-left-radius: 0.25rem; - border-top-right-radius: 0.25rem; -} - -.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { - border-color: #e9ecef #e9ecef #dee2e6; -} - -.nav-tabs .nav-link.disabled { - color: #6c757d; - background-color: transparent; - border-color: transparent; -} - -.nav-tabs .nav-link.active, -.nav-tabs .nav-item.show .nav-link { - color: #495057; - background-color: #fff; - border-color: #dee2e6 #dee2e6 #fff; -} - -.nav-tabs .dropdown-menu { - margin-top: -1px; - border-top-left-radius: 0; - border-top-right-radius: 0; -} - -.nav-pills .nav-link { - border-radius: 0.25rem; -} - -.nav-pills .nav-link.active, -.nav-pills .show > .nav-link { - color: #fff; - background-color: #007bff; -} - -.nav-fill .nav-item { - -ms-flex: 1 1 auto; - flex: 1 1 auto; - text-align: center; -} - -.nav-justified .nav-item { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -ms-flex-positive: 1; - flex-grow: 1; - text-align: center; -} - -.tab-content > .tab-pane { - display: none; -} - -.tab-content > .active { - display: block; -} - -.navbar { - position: relative; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -ms-flex-align: center; - align-items: center; - -ms-flex-pack: justify; - justify-content: space-between; - padding: 0.5rem 1rem; -} - -.navbar > .container, -.navbar > .container-fluid { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -ms-flex-align: center; - align-items: center; - -ms-flex-pack: justify; - justify-content: space-between; -} - -.navbar-brand { - display: inline-block; - padding-top: 0.3125rem; - padding-bottom: 0.3125rem; - margin-right: 1rem; - font-size: 1.25rem; - line-height: inherit; - white-space: nowrap; -} - -.navbar-brand:hover, .navbar-brand:focus { - text-decoration: none; -} - -.navbar-nav { - display: -ms-flexbox; - display: flex; - -ms-flex-direction: column; - flex-direction: column; - padding-left: 0; - margin-bottom: 0; - list-style: none; -} - -.navbar-nav .nav-link { - padding-right: 0; - padding-left: 0; -} - -.navbar-nav .dropdown-menu { - position: static; - float: none; -} - -.navbar-text { - display: inline-block; - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} - -.navbar-collapse { - -ms-flex-preferred-size: 100%; - flex-basis: 100%; - -ms-flex-positive: 1; - flex-grow: 1; - -ms-flex-align: center; - align-items: center; -} - -.navbar-toggler { - padding: 0.25rem 0.75rem; - font-size: 1.25rem; - line-height: 1; - background-color: transparent; - border: 1px solid transparent; - border-radius: 0.25rem; -} - -.navbar-toggler:hover, .navbar-toggler:focus { - text-decoration: none; -} - -.navbar-toggler:not(:disabled):not(.disabled) { - cursor: pointer; -} - -.navbar-toggler-icon { - display: inline-block; - width: 1.5em; - height: 1.5em; - vertical-align: middle; - content: ""; - background: no-repeat center center; - background-size: 100% 100%; -} - -@media (max-width: 575.98px) { - .navbar-expand-sm > .container, - .navbar-expand-sm > .container-fluid { - padding-right: 0; - padding-left: 0; - } -} - -@media (min-width: 576px) { - .navbar-expand-sm { - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -ms-flex-pack: start; - justify-content: flex-start; - } - .navbar-expand-sm .navbar-nav { - -ms-flex-direction: row; - flex-direction: row; - } - .navbar-expand-sm .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-sm .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-sm > .container, - .navbar-expand-sm > .container-fluid { - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - } - .navbar-expand-sm .navbar-collapse { - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; - } - .navbar-expand-sm .navbar-toggler { - display: none; - } -} - -@media (max-width: 767.98px) { - .navbar-expand-md > .container, - .navbar-expand-md > .container-fluid { - padding-right: 0; - padding-left: 0; - } -} - -@media (min-width: 768px) { - .navbar-expand-md { - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -ms-flex-pack: start; - justify-content: flex-start; - } - .navbar-expand-md .navbar-nav { - -ms-flex-direction: row; - flex-direction: row; - } - .navbar-expand-md .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-md .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-md > .container, - .navbar-expand-md > .container-fluid { - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - } - .navbar-expand-md .navbar-collapse { - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; - } - .navbar-expand-md .navbar-toggler { - display: none; - } -} - -@media (max-width: 991.98px) { - .navbar-expand-lg > .container, - .navbar-expand-lg > .container-fluid { - padding-right: 0; - padding-left: 0; - } -} - -@media (min-width: 992px) { - .navbar-expand-lg { - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -ms-flex-pack: start; - justify-content: flex-start; - } - .navbar-expand-lg .navbar-nav { - -ms-flex-direction: row; - flex-direction: row; - } - .navbar-expand-lg .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-lg .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-lg > .container, - .navbar-expand-lg > .container-fluid { - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - } - .navbar-expand-lg .navbar-collapse { - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; - } - .navbar-expand-lg .navbar-toggler { - display: none; - } -} - -@media (max-width: 1199.98px) { - .navbar-expand-xl > .container, - .navbar-expand-xl > .container-fluid { - padding-right: 0; - padding-left: 0; - } -} - -@media (min-width: 1200px) { - .navbar-expand-xl { - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -ms-flex-pack: start; - justify-content: flex-start; - } - .navbar-expand-xl .navbar-nav { - -ms-flex-direction: row; - flex-direction: row; - } - .navbar-expand-xl .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-xl .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-xl > .container, - .navbar-expand-xl > .container-fluid { - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - } - .navbar-expand-xl .navbar-collapse { - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; - } - .navbar-expand-xl .navbar-toggler { - display: none; - } -} - -.navbar-expand { - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -ms-flex-pack: start; - justify-content: flex-start; -} - -.navbar-expand > .container, -.navbar-expand > .container-fluid { - padding-right: 0; - padding-left: 0; -} - -.navbar-expand .navbar-nav { - -ms-flex-direction: row; - flex-direction: row; -} - -.navbar-expand .navbar-nav .dropdown-menu { - position: absolute; -} - -.navbar-expand .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; -} - -.navbar-expand > .container, -.navbar-expand > .container-fluid { - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; -} - -.navbar-expand .navbar-collapse { - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; -} - -.navbar-expand .navbar-toggler { - display: none; -} - -.navbar-light .navbar-brand { - color: rgba(0, 0, 0, 0.9); -} - -.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus { - color: rgba(0, 0, 0, 0.9); -} - -.navbar-light .navbar-nav .nav-link { - color: rgba(0, 0, 0, 0.5); -} - -.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus { - color: rgba(0, 0, 0, 0.7); -} - -.navbar-light .navbar-nav .nav-link.disabled { - color: rgba(0, 0, 0, 0.3); -} - -.navbar-light .navbar-nav .show > .nav-link, -.navbar-light .navbar-nav .active > .nav-link, -.navbar-light .navbar-nav .nav-link.show, -.navbar-light .navbar-nav .nav-link.active { - color: rgba(0, 0, 0, 0.9); -} - -.navbar-light .navbar-toggler { - color: rgba(0, 0, 0, 0.5); - border-color: rgba(0, 0, 0, 0.1); -} - -.navbar-light .navbar-toggler-icon { - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"); -} - -.navbar-light .navbar-text { - color: rgba(0, 0, 0, 0.5); -} - -.navbar-light .navbar-text a { - color: rgba(0, 0, 0, 0.9); -} - -.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus { - color: rgba(0, 0, 0, 0.9); -} - -.navbar-dark .navbar-brand { - color: #fff; -} - -.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus { - color: #fff; -} - -.navbar-dark .navbar-nav .nav-link { - color: rgba(255, 255, 255, 0.5); -} - -.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus { - color: rgba(255, 255, 255, 0.75); -} - -.navbar-dark .navbar-nav .nav-link.disabled { - color: rgba(255, 255, 255, 0.25); -} - -.navbar-dark .navbar-nav .show > .nav-link, -.navbar-dark .navbar-nav .active > .nav-link, -.navbar-dark .navbar-nav .nav-link.show, -.navbar-dark .navbar-nav .nav-link.active { - color: #fff; -} - -.navbar-dark .navbar-toggler { - color: rgba(255, 255, 255, 0.5); - border-color: rgba(255, 255, 255, 0.1); -} - -.navbar-dark .navbar-toggler-icon { - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"); -} - -.navbar-dark .navbar-text { - color: rgba(255, 255, 255, 0.5); -} - -.navbar-dark .navbar-text a { - color: #fff; -} - -.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus { - color: #fff; -} - -.card { - position: relative; - display: -ms-flexbox; - display: flex; - -ms-flex-direction: column; - flex-direction: column; - min-width: 0; - word-wrap: break-word; - background-color: #fff; - background-clip: border-box; - border: 1px solid rgba(0, 0, 0, 0.125); - border-radius: 0.25rem; -} - -.card > hr { - margin-right: 0; - margin-left: 0; -} - -.card > .list-group:first-child .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-top-right-radius: 0.25rem; -} - -.card > .list-group:last-child .list-group-item:last-child { - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; -} - -.card-body { - -ms-flex: 1 1 auto; - flex: 1 1 auto; - padding: 1.25rem; -} - -.card-title { - margin-bottom: 0.75rem; -} - -.card-subtitle { - margin-top: -0.375rem; - margin-bottom: 0; -} - -.card-text:last-child { - margin-bottom: 0; -} - -.card-link:hover { - text-decoration: none; -} - -.card-link + .card-link { - margin-left: 1.25rem; -} - -.card-header { - padding: 0.75rem 1.25rem; - margin-bottom: 0; - background-color: rgba(0, 0, 0, 0.03); - border-bottom: 1px solid rgba(0, 0, 0, 0.125); -} - -.card-header:first-child { - border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0; -} - -.card-header + .list-group .list-group-item:first-child { - border-top: 0; -} - -.card-footer { - padding: 0.75rem 1.25rem; - background-color: rgba(0, 0, 0, 0.03); - border-top: 1px solid rgba(0, 0, 0, 0.125); -} - -.card-footer:last-child { - border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px); -} - -.card-header-tabs { - margin-right: -0.625rem; - margin-bottom: -0.75rem; - margin-left: -0.625rem; - border-bottom: 0; -} - -.card-header-pills { - margin-right: -0.625rem; - margin-left: -0.625rem; -} - -.card-img-overlay { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - padding: 1.25rem; -} - -.card-img { - width: 100%; - border-radius: calc(0.25rem - 1px); -} - -.card-img-top { - width: 100%; - border-top-left-radius: calc(0.25rem - 1px); - border-top-right-radius: calc(0.25rem - 1px); -} - -.card-img-bottom { - width: 100%; - border-bottom-right-radius: calc(0.25rem - 1px); - border-bottom-left-radius: calc(0.25rem - 1px); -} - -.card-deck { - display: -ms-flexbox; - display: flex; - -ms-flex-direction: column; - flex-direction: column; -} - -.card-deck .card { - margin-bottom: 15px; -} - -@media (min-width: 576px) { - .card-deck { - -ms-flex-flow: row wrap; - flex-flow: row wrap; - margin-right: -15px; - margin-left: -15px; - } - .card-deck .card { - display: -ms-flexbox; - display: flex; - -ms-flex: 1 0 0%; - flex: 1 0 0%; - -ms-flex-direction: column; - flex-direction: column; - margin-right: 15px; - margin-bottom: 0; - margin-left: 15px; - } -} - -.card-group { - display: -ms-flexbox; - display: flex; - -ms-flex-direction: column; - flex-direction: column; -} - -.card-group > .card { - margin-bottom: 15px; -} - -@media (min-width: 576px) { - .card-group { - -ms-flex-flow: row wrap; - flex-flow: row wrap; - } - .card-group > .card { - -ms-flex: 1 0 0%; - flex: 1 0 0%; - margin-bottom: 0; - } - .card-group > .card + .card { - margin-left: 0; - border-left: 0; - } - .card-group > .card:first-child { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - .card-group > .card:first-child .card-img-top, - .card-group > .card:first-child .card-header { - border-top-right-radius: 0; - } - .card-group > .card:first-child .card-img-bottom, - .card-group > .card:first-child .card-footer { - border-bottom-right-radius: 0; - } - .card-group > .card:last-child { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - .card-group > .card:last-child .card-img-top, - .card-group > .card:last-child .card-header { - border-top-left-radius: 0; - } - .card-group > .card:last-child .card-img-bottom, - .card-group > .card:last-child .card-footer { - border-bottom-left-radius: 0; - } - .card-group > .card:only-child { - border-radius: 0.25rem; - } - .card-group > .card:only-child .card-img-top, - .card-group > .card:only-child .card-header { - border-top-left-radius: 0.25rem; - border-top-right-radius: 0.25rem; - } - .card-group > .card:only-child .card-img-bottom, - .card-group > .card:only-child .card-footer { - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - } - .card-group > .card:not(:first-child):not(:last-child):not(:only-child) { - border-radius: 0; - } - .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-top, - .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom, - .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-header, - .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-footer { - border-radius: 0; - } -} - -.card-columns .card { - margin-bottom: 0.75rem; -} - -@media (min-width: 576px) { - .card-columns { - -webkit-column-count: 3; - -moz-column-count: 3; - column-count: 3; - -webkit-column-gap: 1.25rem; - -moz-column-gap: 1.25rem; - column-gap: 1.25rem; - orphans: 1; - widows: 1; - } - .card-columns .card { - display: inline-block; - width: 100%; - } -} - -.accordion .card:not(:first-of-type):not(:last-of-type) { - border-bottom: 0; - border-radius: 0; -} - -.accordion .card:not(:first-of-type) .card-header:first-child { - border-radius: 0; -} - -.accordion .card:first-of-type { - border-bottom: 0; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} - -.accordion .card:last-of-type { - border-top-left-radius: 0; - border-top-right-radius: 0; -} - -.breadcrumb { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - padding: 0.75rem 1rem; - margin-bottom: 1rem; - list-style: none; - background-color: #e9ecef; - border-radius: 0.25rem; -} - -.breadcrumb-item + .breadcrumb-item { - padding-left: 0.5rem; -} - -.breadcrumb-item + .breadcrumb-item::before { - display: inline-block; - padding-right: 0.5rem; - color: #6c757d; - content: "/"; -} - -.breadcrumb-item + .breadcrumb-item:hover::before { - text-decoration: underline; -} - -.breadcrumb-item + .breadcrumb-item:hover::before { - text-decoration: none; -} - -.breadcrumb-item.active { - color: #6c757d; -} - -.pagination { - display: -ms-flexbox; - display: flex; - padding-left: 0; - list-style: none; - border-radius: 0.25rem; -} - -.page-link { - position: relative; - display: block; - padding: 0.5rem 0.75rem; - margin-left: -1px; - line-height: 1.25; - color: #007bff; - background-color: #fff; - border: 1px solid #dee2e6; -} - -.page-link:hover { - z-index: 2; - color: #0056b3; - text-decoration: none; - background-color: #e9ecef; - border-color: #dee2e6; -} - -.page-link:focus { - z-index: 2; - outline: 0; - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -.page-link:not(:disabled):not(.disabled) { - cursor: pointer; -} - -.page-item:first-child .page-link { - margin-left: 0; - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; -} - -.page-item:last-child .page-link { - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; -} - -.page-item.active .page-link { - z-index: 1; - color: #fff; - background-color: #007bff; - border-color: #007bff; -} - -.page-item.disabled .page-link { - color: #6c757d; - pointer-events: none; - cursor: auto; - background-color: #fff; - border-color: #dee2e6; -} - -.pagination-lg .page-link { - padding: 0.75rem 1.5rem; - font-size: 1.25rem; - line-height: 1.5; -} - -.pagination-lg .page-item:first-child .page-link { - border-top-left-radius: 0.3rem; - border-bottom-left-radius: 0.3rem; -} - -.pagination-lg .page-item:last-child .page-link { - border-top-right-radius: 0.3rem; - border-bottom-right-radius: 0.3rem; -} - -.pagination-sm .page-link { - padding: 0.25rem 0.5rem; - font-size: 0.875rem; - line-height: 1.5; -} - -.pagination-sm .page-item:first-child .page-link { - border-top-left-radius: 0.2rem; - border-bottom-left-radius: 0.2rem; -} - -.pagination-sm .page-item:last-child .page-link { - border-top-right-radius: 0.2rem; - border-bottom-right-radius: 0.2rem; -} - -.badge { - display: inline-block; - padding: 0.25em 0.4em; - font-size: 75%; - font-weight: 700; - line-height: 1; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: 0.25rem; -} - -.badge:empty { - display: none; -} - -.btn .badge { - position: relative; - top: -1px; -} - -.badge-pill { - padding-right: 0.6em; - padding-left: 0.6em; - border-radius: 10rem; -} - -.badge-primary { - color: #fff; - background-color: #007bff; -} - -.badge-primary[href]:hover, .badge-primary[href]:focus { - color: #fff; - text-decoration: none; - background-color: #0062cc; -} - -.badge-secondary { - color: #fff; - background-color: #6c757d; -} - -.badge-secondary[href]:hover, .badge-secondary[href]:focus { - color: #fff; - text-decoration: none; - background-color: #545b62; -} - -.badge-success { - color: #fff; - background-color: #28a745; -} - -.badge-success[href]:hover, .badge-success[href]:focus { - color: #fff; - text-decoration: none; - background-color: #1e7e34; -} - -.badge-info { - color: #fff; - background-color: #17a2b8; -} - -.badge-info[href]:hover, .badge-info[href]:focus { - color: #fff; - text-decoration: none; - background-color: #117a8b; -} - -.badge-warning { - color: #212529; - background-color: #ffc107; -} - -.badge-warning[href]:hover, .badge-warning[href]:focus { - color: #212529; - text-decoration: none; - background-color: #d39e00; -} - -.badge-danger { - color: #fff; - background-color: #dc3545; -} - -.badge-danger[href]:hover, .badge-danger[href]:focus { - color: #fff; - text-decoration: none; - background-color: #bd2130; -} - -.badge-light { - color: #212529; - background-color: #f8f9fa; -} - -.badge-light[href]:hover, .badge-light[href]:focus { - color: #212529; - text-decoration: none; - background-color: #dae0e5; -} - -.badge-dark { - color: #fff; - background-color: #343a40; -} - -.badge-dark[href]:hover, .badge-dark[href]:focus { - color: #fff; - text-decoration: none; - background-color: #1d2124; -} - -.jumbotron { - padding: 2rem 1rem; - margin-bottom: 2rem; - background-color: #e9ecef; - border-radius: 0.3rem; -} - -@media (min-width: 576px) { - .jumbotron { - padding: 4rem 2rem; - } -} - -.jumbotron-fluid { - padding-right: 0; - padding-left: 0; - border-radius: 0; -} - -.alert { - position: relative; - padding: 0.75rem 1.25rem; - margin-bottom: 1rem; - border: 1px solid transparent; - border-radius: 0.25rem; -} - -.alert-heading { - color: inherit; -} - -.alert-link { - font-weight: 700; -} - -.alert-dismissible { - padding-right: 4rem; -} - -.alert-dismissible .close { - position: absolute; - top: 0; - right: 0; - padding: 0.75rem 1.25rem; - color: inherit; -} - -.alert-primary { - color: #004085; - background-color: #cce5ff; - border-color: #b8daff; -} - -.alert-primary hr { - border-top-color: #9fcdff; -} - -.alert-primary .alert-link { - color: #002752; -} - -.alert-secondary { - color: #383d41; - background-color: #e2e3e5; - border-color: #d6d8db; -} - -.alert-secondary hr { - border-top-color: #c8cbcf; -} - -.alert-secondary .alert-link { - color: #202326; -} - -.alert-success { - color: #155724; - background-color: #d4edda; - border-color: #c3e6cb; -} - -.alert-success hr { - border-top-color: #b1dfbb; -} - -.alert-success .alert-link { - color: #0b2e13; -} - -.alert-info { - color: #0c5460; - background-color: #d1ecf1; - border-color: #bee5eb; -} - -.alert-info hr { - border-top-color: #abdde5; -} - -.alert-info .alert-link { - color: #062c33; -} - -.alert-warning { - color: #856404; - background-color: #fff3cd; - border-color: #ffeeba; -} - -.alert-warning hr { - border-top-color: #ffe8a1; -} - -.alert-warning .alert-link { - color: #533f03; -} - -.alert-danger { - color: #721c24; - background-color: #f8d7da; - border-color: #f5c6cb; -} - -.alert-danger hr { - border-top-color: #f1b0b7; -} - -.alert-danger .alert-link { - color: #491217; -} - -.alert-light { - color: #818182; - background-color: #fefefe; - border-color: #fdfdfe; -} - -.alert-light hr { - border-top-color: #ececf6; -} - -.alert-light .alert-link { - color: #686868; -} - -.alert-dark { - color: #1b1e21; - background-color: #d6d8d9; - border-color: #c6c8ca; -} - -.alert-dark hr { - border-top-color: #b9bbbe; -} - -.alert-dark .alert-link { - color: #040505; -} - -@-webkit-keyframes progress-bar-stripes { - from { - background-position: 1rem 0; - } - to { - background-position: 0 0; - } -} - -@keyframes progress-bar-stripes { - from { - background-position: 1rem 0; - } - to { - background-position: 0 0; - } -} - -.progress { - display: -ms-flexbox; - display: flex; - height: 1rem; - overflow: hidden; - font-size: 0.75rem; - background-color: #e9ecef; - border-radius: 0.25rem; -} - -.progress-bar { - display: -ms-flexbox; - display: flex; - -ms-flex-direction: column; - flex-direction: column; - -ms-flex-pack: center; - justify-content: center; - color: #fff; - text-align: center; - white-space: nowrap; - background-color: #007bff; - transition: width 0.6s ease; -} - -@media screen and (prefers-reduced-motion: reduce) { - .progress-bar { - transition: none; - } -} - -.progress-bar-striped { - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-size: 1rem 1rem; -} - -.progress-bar-animated { - -webkit-animation: progress-bar-stripes 1s linear infinite; - animation: progress-bar-stripes 1s linear infinite; -} - -.media { - display: -ms-flexbox; - display: flex; - -ms-flex-align: start; - align-items: flex-start; -} - -.media-body { - -ms-flex: 1; - flex: 1; -} - -.list-group { - display: -ms-flexbox; - display: flex; - -ms-flex-direction: column; - flex-direction: column; - padding-left: 0; - margin-bottom: 0; -} - -.list-group-item-action { - width: 100%; - color: #495057; - text-align: inherit; -} - -.list-group-item-action:hover, .list-group-item-action:focus { - color: #495057; - text-decoration: none; - background-color: #f8f9fa; -} - -.list-group-item-action:active { - color: #212529; - background-color: #e9ecef; -} - -.list-group-item { - position: relative; - display: block; - padding: 0.75rem 1.25rem; - margin-bottom: -1px; - background-color: #fff; - border: 1px solid rgba(0, 0, 0, 0.125); -} - -.list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-top-right-radius: 0.25rem; -} - -.list-group-item:last-child { - margin-bottom: 0; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; -} - -.list-group-item:hover, .list-group-item:focus { - z-index: 1; - text-decoration: none; -} - -.list-group-item.disabled, .list-group-item:disabled { - color: #6c757d; - background-color: #fff; -} - -.list-group-item.active { - z-index: 2; - color: #fff; - background-color: #007bff; - border-color: #007bff; -} - -.list-group-flush .list-group-item { - border-right: 0; - border-left: 0; - border-radius: 0; -} - -.list-group-flush:first-child .list-group-item:first-child { - border-top: 0; -} - -.list-group-flush:last-child .list-group-item:last-child { - border-bottom: 0; -} - -.list-group-item-primary { - color: #004085; - background-color: #b8daff; -} - -.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus { - color: #004085; - background-color: #9fcdff; -} - -.list-group-item-primary.list-group-item-action.active { - color: #fff; - background-color: #004085; - border-color: #004085; -} - -.list-group-item-secondary { - color: #383d41; - background-color: #d6d8db; -} - -.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus { - color: #383d41; - background-color: #c8cbcf; -} - -.list-group-item-secondary.list-group-item-action.active { - color: #fff; - background-color: #383d41; - border-color: #383d41; -} - -.list-group-item-success { - color: #155724; - background-color: #c3e6cb; -} - -.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus { - color: #155724; - background-color: #b1dfbb; -} - -.list-group-item-success.list-group-item-action.active { - color: #fff; - background-color: #155724; - border-color: #155724; -} - -.list-group-item-info { - color: #0c5460; - background-color: #bee5eb; -} - -.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus { - color: #0c5460; - background-color: #abdde5; -} - -.list-group-item-info.list-group-item-action.active { - color: #fff; - background-color: #0c5460; - border-color: #0c5460; -} - -.list-group-item-warning { - color: #856404; - background-color: #ffeeba; -} - -.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus { - color: #856404; - background-color: #ffe8a1; -} - -.list-group-item-warning.list-group-item-action.active { - color: #fff; - background-color: #856404; - border-color: #856404; -} - -.list-group-item-danger { - color: #721c24; - background-color: #f5c6cb; -} - -.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus { - color: #721c24; - background-color: #f1b0b7; -} - -.list-group-item-danger.list-group-item-action.active { - color: #fff; - background-color: #721c24; - border-color: #721c24; -} - -.list-group-item-light { - color: #818182; - background-color: #fdfdfe; -} - -.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus { - color: #818182; - background-color: #ececf6; -} - -.list-group-item-light.list-group-item-action.active { - color: #fff; - background-color: #818182; - border-color: #818182; -} - -.list-group-item-dark { - color: #1b1e21; - background-color: #c6c8ca; -} - -.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus { - color: #1b1e21; - background-color: #b9bbbe; -} - -.list-group-item-dark.list-group-item-action.active { - color: #fff; - background-color: #1b1e21; - border-color: #1b1e21; -} - -.close { - float: right; - font-size: 1.5rem; - font-weight: 700; - line-height: 1; - color: #000; - text-shadow: 0 1px 0 #fff; - opacity: .5; -} - -.close:hover, .close:focus { - color: #000; - text-decoration: none; - opacity: .75; -} - -.close:not(:disabled):not(.disabled) { - cursor: pointer; -} - -button.close { - padding: 0; - background-color: transparent; - border: 0; - -webkit-appearance: none; -} - -.modal-open { - overflow: hidden; -} - -.modal { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1050; - display: none; - overflow: hidden; - outline: 0; -} - -.modal-open .modal { - overflow-x: hidden; - overflow-y: auto; -} - -.modal-dialog { - position: relative; - width: auto; - margin: 0.5rem; - pointer-events: none; -} - -.modal.fade .modal-dialog { - transition: -webkit-transform 0.3s ease-out; - transition: transform 0.3s ease-out; - transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out; - -webkit-transform: translate(0, -25%); - transform: translate(0, -25%); -} - -@media screen and (prefers-reduced-motion: reduce) { - .modal.fade .modal-dialog { - transition: none; - } -} - -.modal.show .modal-dialog { - -webkit-transform: translate(0, 0); - transform: translate(0, 0); -} - -.modal-dialog-centered { - display: -ms-flexbox; - display: flex; - -ms-flex-align: center; - align-items: center; - min-height: calc(100% - (0.5rem * 2)); -} - -.modal-content { - position: relative; - display: -ms-flexbox; - display: flex; - -ms-flex-direction: column; - flex-direction: column; - width: 100%; - pointer-events: auto; - background-color: #fff; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0.2); - border-radius: 0.3rem; - outline: 0; -} - -.modal-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1040; - background-color: #000; -} - -.modal-backdrop.fade { - opacity: 0; -} - -.modal-backdrop.show { - opacity: 0.5; -} - -.modal-header { - display: -ms-flexbox; - display: flex; - -ms-flex-align: start; - align-items: flex-start; - -ms-flex-pack: justify; - justify-content: space-between; - padding: 1rem; - border-bottom: 1px solid #e9ecef; - border-top-left-radius: 0.3rem; - border-top-right-radius: 0.3rem; -} - -.modal-header .close { - padding: 1rem; - margin: -1rem -1rem -1rem auto; -} - -.modal-title { - margin-bottom: 0; - line-height: 1.5; -} - -.modal-body { - position: relative; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - padding: 1rem; -} - -.modal-footer { - display: -ms-flexbox; - display: flex; - -ms-flex-align: center; - align-items: center; - -ms-flex-pack: end; - justify-content: flex-end; - padding: 1rem; - border-top: 1px solid #e9ecef; -} - -.modal-footer > :not(:first-child) { - margin-left: .25rem; -} - -.modal-footer > :not(:last-child) { - margin-right: .25rem; -} - -.modal-scrollbar-measure { - position: absolute; - top: -9999px; - width: 50px; - height: 50px; - overflow: scroll; -} - -@media (min-width: 576px) { - .modal-dialog { - max-width: 500px; - margin: 1.75rem auto; - } - .modal-dialog-centered { - min-height: calc(100% - (1.75rem * 2)); - } - .modal-sm { - max-width: 300px; - } -} - -@media (min-width: 992px) { - .modal-lg { - max-width: 800px; - } -} - -.tooltip { - position: absolute; - z-index: 1070; - display: block; - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; - font-style: normal; - font-weight: 400; - line-height: 1.5; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - white-space: normal; - line-break: auto; - font-size: 0.875rem; - word-wrap: break-word; - opacity: 0; -} - -.tooltip.show { - opacity: 0.9; -} - -.tooltip .arrow { - position: absolute; - display: block; - width: 0.8rem; - height: 0.4rem; -} - -.tooltip .arrow::before { - position: absolute; - content: ""; - border-color: transparent; - border-style: solid; -} - -.bs-tooltip-top, .bs-tooltip-auto[x-placement^="top"] { - padding: 0.4rem 0; -} - -.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^="top"] .arrow { - bottom: 0; -} - -.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^="top"] .arrow::before { - top: 0; - border-width: 0.4rem 0.4rem 0; - border-top-color: #000; -} - -.bs-tooltip-right, .bs-tooltip-auto[x-placement^="right"] { - padding: 0 0.4rem; -} - -.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^="right"] .arrow { - left: 0; - width: 0.4rem; - height: 0.8rem; -} - -.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^="right"] .arrow::before { - right: 0; - border-width: 0.4rem 0.4rem 0.4rem 0; - border-right-color: #000; -} - -.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^="bottom"] { - padding: 0.4rem 0; -} - -.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^="bottom"] .arrow { - top: 0; -} - -.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^="bottom"] .arrow::before { - bottom: 0; - border-width: 0 0.4rem 0.4rem; - border-bottom-color: #000; -} - -.bs-tooltip-left, .bs-tooltip-auto[x-placement^="left"] { - padding: 0 0.4rem; -} - -.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^="left"] .arrow { - right: 0; - width: 0.4rem; - height: 0.8rem; -} - -.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^="left"] .arrow::before { - left: 0; - border-width: 0.4rem 0 0.4rem 0.4rem; - border-left-color: #000; -} - -.tooltip-inner { - max-width: 200px; - padding: 0.25rem 0.5rem; - color: #fff; - text-align: center; - background-color: #000; - border-radius: 0.25rem; -} - -.popover { - position: absolute; - top: 0; - left: 0; - z-index: 1060; - display: block; - max-width: 276px; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; - font-style: normal; - font-weight: 400; - line-height: 1.5; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - white-space: normal; - line-break: auto; - font-size: 0.875rem; - word-wrap: break-word; - background-color: #fff; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0.2); - border-radius: 0.3rem; -} - -.popover .arrow { - position: absolute; - display: block; - width: 1rem; - height: 0.5rem; - margin: 0 0.3rem; -} - -.popover .arrow::before, .popover .arrow::after { - position: absolute; - display: block; - content: ""; - border-color: transparent; - border-style: solid; -} - -.bs-popover-top, .bs-popover-auto[x-placement^="top"] { - margin-bottom: 0.5rem; -} - -.bs-popover-top .arrow, .bs-popover-auto[x-placement^="top"] .arrow { - bottom: calc((0.5rem + 1px) * -1); -} - -.bs-popover-top .arrow::before, .bs-popover-auto[x-placement^="top"] .arrow::before, -.bs-popover-top .arrow::after, -.bs-popover-auto[x-placement^="top"] .arrow::after { - border-width: 0.5rem 0.5rem 0; -} - -.bs-popover-top .arrow::before, .bs-popover-auto[x-placement^="top"] .arrow::before { - bottom: 0; - border-top-color: rgba(0, 0, 0, 0.25); -} - - -.bs-popover-top .arrow::after, -.bs-popover-auto[x-placement^="top"] .arrow::after { - bottom: 1px; - border-top-color: #fff; -} - -.bs-popover-right, .bs-popover-auto[x-placement^="right"] { - margin-left: 0.5rem; -} - -.bs-popover-right .arrow, .bs-popover-auto[x-placement^="right"] .arrow { - left: calc((0.5rem + 1px) * -1); - width: 0.5rem; - height: 1rem; - margin: 0.3rem 0; -} - -.bs-popover-right .arrow::before, .bs-popover-auto[x-placement^="right"] .arrow::before, -.bs-popover-right .arrow::after, -.bs-popover-auto[x-placement^="right"] .arrow::after { - border-width: 0.5rem 0.5rem 0.5rem 0; -} - -.bs-popover-right .arrow::before, .bs-popover-auto[x-placement^="right"] .arrow::before { - left: 0; - border-right-color: rgba(0, 0, 0, 0.25); -} - - -.bs-popover-right .arrow::after, -.bs-popover-auto[x-placement^="right"] .arrow::after { - left: 1px; - border-right-color: #fff; -} - -.bs-popover-bottom, .bs-popover-auto[x-placement^="bottom"] { - margin-top: 0.5rem; -} - -.bs-popover-bottom .arrow, .bs-popover-auto[x-placement^="bottom"] .arrow { - top: calc((0.5rem + 1px) * -1); -} - -.bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^="bottom"] .arrow::before, -.bs-popover-bottom .arrow::after, -.bs-popover-auto[x-placement^="bottom"] .arrow::after { - border-width: 0 0.5rem 0.5rem 0.5rem; -} - -.bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^="bottom"] .arrow::before { - top: 0; - border-bottom-color: rgba(0, 0, 0, 0.25); -} - - -.bs-popover-bottom .arrow::after, -.bs-popover-auto[x-placement^="bottom"] .arrow::after { - top: 1px; - border-bottom-color: #fff; -} - -.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^="bottom"] .popover-header::before { - position: absolute; - top: 0; - left: 50%; - display: block; - width: 1rem; - margin-left: -0.5rem; - content: ""; - border-bottom: 1px solid #f7f7f7; -} - -.bs-popover-left, .bs-popover-auto[x-placement^="left"] { - margin-right: 0.5rem; -} - -.bs-popover-left .arrow, .bs-popover-auto[x-placement^="left"] .arrow { - right: calc((0.5rem + 1px) * -1); - width: 0.5rem; - height: 1rem; - margin: 0.3rem 0; -} - -.bs-popover-left .arrow::before, .bs-popover-auto[x-placement^="left"] .arrow::before, -.bs-popover-left .arrow::after, -.bs-popover-auto[x-placement^="left"] .arrow::after { - border-width: 0.5rem 0 0.5rem 0.5rem; -} - -.bs-popover-left .arrow::before, .bs-popover-auto[x-placement^="left"] .arrow::before { - right: 0; - border-left-color: rgba(0, 0, 0, 0.25); -} - - -.bs-popover-left .arrow::after, -.bs-popover-auto[x-placement^="left"] .arrow::after { - right: 1px; - border-left-color: #fff; -} - -.popover-header { - padding: 0.5rem 0.75rem; - margin-bottom: 0; - font-size: 1rem; - color: inherit; - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - border-top-left-radius: calc(0.3rem - 1px); - border-top-right-radius: calc(0.3rem - 1px); -} - -.popover-header:empty { - display: none; -} - -.popover-body { - padding: 0.5rem 0.75rem; - color: #212529; -} - -.carousel { - position: relative; -} - -.carousel-inner { - position: relative; - width: 100%; - overflow: hidden; -} - -.carousel-item { - position: relative; - display: none; - -ms-flex-align: center; - align-items: center; - width: 100%; - transition: -webkit-transform 0.6s ease; - transition: transform 0.6s ease; - transition: transform 0.6s ease, -webkit-transform 0.6s ease; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -webkit-perspective: 1000px; - perspective: 1000px; -} - -@media screen and (prefers-reduced-motion: reduce) { - .carousel-item { - transition: none; - } -} - -.carousel-item.active, -.carousel-item-next, -.carousel-item-prev { - display: block; -} - -.carousel-item-next, -.carousel-item-prev { - position: absolute; - top: 0; -} - -.carousel-item-next.carousel-item-left, -.carousel-item-prev.carousel-item-right { - -webkit-transform: translateX(0); - transform: translateX(0); -} - -@supports ((-webkit-transform-style: preserve-3d) or (transform-style: preserve-3d)) { - .carousel-item-next.carousel-item-left, - .carousel-item-prev.carousel-item-right { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -.carousel-item-next, -.active.carousel-item-right { - -webkit-transform: translateX(100%); - transform: translateX(100%); -} - -@supports ((-webkit-transform-style: preserve-3d) or (transform-style: preserve-3d)) { - .carousel-item-next, - .active.carousel-item-right { - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } -} - -.carousel-item-prev, -.active.carousel-item-left { - -webkit-transform: translateX(-100%); - transform: translateX(-100%); -} - -@supports ((-webkit-transform-style: preserve-3d) or (transform-style: preserve-3d)) { - .carousel-item-prev, - .active.carousel-item-left { - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } -} - -.carousel-fade .carousel-item { - opacity: 0; - transition-duration: .6s; - transition-property: opacity; -} - -.carousel-fade .carousel-item.active, -.carousel-fade .carousel-item-next.carousel-item-left, -.carousel-fade .carousel-item-prev.carousel-item-right { - opacity: 1; -} - -.carousel-fade .active.carousel-item-left, -.carousel-fade .active.carousel-item-right { - opacity: 0; -} - -.carousel-fade .carousel-item-next, -.carousel-fade .carousel-item-prev, -.carousel-fade .carousel-item.active, -.carousel-fade .active.carousel-item-left, -.carousel-fade .active.carousel-item-prev { - -webkit-transform: translateX(0); - transform: translateX(0); -} - -@supports ((-webkit-transform-style: preserve-3d) or (transform-style: preserve-3d)) { - .carousel-fade .carousel-item-next, - .carousel-fade .carousel-item-prev, - .carousel-fade .carousel-item.active, - .carousel-fade .active.carousel-item-left, - .carousel-fade .active.carousel-item-prev { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -.carousel-control-prev, -.carousel-control-next { - position: absolute; - top: 0; - bottom: 0; - display: -ms-flexbox; - display: flex; - -ms-flex-align: center; - align-items: center; - -ms-flex-pack: center; - justify-content: center; - width: 15%; - color: #fff; - text-align: center; - opacity: 0.5; -} - -.carousel-control-prev:hover, .carousel-control-prev:focus, -.carousel-control-next:hover, -.carousel-control-next:focus { - color: #fff; - text-decoration: none; - outline: 0; - opacity: .9; -} - -.carousel-control-prev { - left: 0; -} - -.carousel-control-next { - right: 0; -} - -.carousel-control-prev-icon, -.carousel-control-next-icon { - display: inline-block; - width: 20px; - height: 20px; - background: transparent no-repeat center center; - background-size: 100% 100%; -} - -.carousel-control-prev-icon { - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E"); -} - -.carousel-control-next-icon { - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E"); -} - -.carousel-indicators { - position: absolute; - right: 0; - bottom: 10px; - left: 0; - z-index: 15; - display: -ms-flexbox; - display: flex; - -ms-flex-pack: center; - justify-content: center; - padding-left: 0; - margin-right: 15%; - margin-left: 15%; - list-style: none; -} - -.carousel-indicators li { - position: relative; - -ms-flex: 0 1 auto; - flex: 0 1 auto; - width: 30px; - height: 3px; - margin-right: 3px; - margin-left: 3px; - text-indent: -999px; - cursor: pointer; - background-color: rgba(255, 255, 255, 0.5); -} - -.carousel-indicators li::before { - position: absolute; - top: -10px; - left: 0; - display: inline-block; - width: 100%; - height: 10px; - content: ""; -} - -.carousel-indicators li::after { - position: absolute; - bottom: -10px; - left: 0; - display: inline-block; - width: 100%; - height: 10px; - content: ""; -} - -.carousel-indicators .active { - background-color: #fff; -} - -.carousel-caption { - position: absolute; - right: 15%; - bottom: 20px; - left: 15%; - z-index: 10; - padding-top: 20px; - padding-bottom: 20px; - color: #fff; - text-align: center; -} - -.align-baseline { - vertical-align: baseline !important; -} - -.align-top { - vertical-align: top !important; -} - -.align-middle { - vertical-align: middle !important; -} - -.align-bottom { - vertical-align: bottom !important; -} - -.align-text-bottom { - vertical-align: text-bottom !important; -} - -.align-text-top { - vertical-align: text-top !important; -} - -.bg-primary { - background-color: #007bff !important; -} - -a.bg-primary:hover, a.bg-primary:focus, -button.bg-primary:hover, -button.bg-primary:focus { - background-color: #0062cc !important; -} - -.bg-secondary { - background-color: #6c757d !important; -} - -a.bg-secondary:hover, a.bg-secondary:focus, -button.bg-secondary:hover, -button.bg-secondary:focus { - background-color: #545b62 !important; -} - -.bg-success { - background-color: #28a745 !important; -} - -a.bg-success:hover, a.bg-success:focus, -button.bg-success:hover, -button.bg-success:focus { - background-color: #1e7e34 !important; -} - -.bg-info { - background-color: #17a2b8 !important; -} - -a.bg-info:hover, a.bg-info:focus, -button.bg-info:hover, -button.bg-info:focus { - background-color: #117a8b !important; -} - -.bg-warning { - background-color: #ffc107 !important; -} - -a.bg-warning:hover, a.bg-warning:focus, -button.bg-warning:hover, -button.bg-warning:focus { - background-color: #d39e00 !important; -} - -.bg-danger { - background-color: #dc3545 !important; -} - -a.bg-danger:hover, a.bg-danger:focus, -button.bg-danger:hover, -button.bg-danger:focus { - background-color: #bd2130 !important; -} - -.bg-light { - background-color: #f8f9fa !important; -} - -a.bg-light:hover, a.bg-light:focus, -button.bg-light:hover, -button.bg-light:focus { - background-color: #dae0e5 !important; -} - -.bg-dark { - background-color: #343a40 !important; -} - -a.bg-dark:hover, a.bg-dark:focus, -button.bg-dark:hover, -button.bg-dark:focus { - background-color: #1d2124 !important; -} - -.bg-white { - background-color: #fff !important; -} - -.bg-transparent { - background-color: transparent !important; -} - -.border { - border: 1px solid #dee2e6 !important; -} - -.border-top { - border-top: 1px solid #dee2e6 !important; -} - -.border-right { - border-right: 1px solid #dee2e6 !important; -} - -.border-bottom { - border-bottom: 1px solid #dee2e6 !important; -} - -.border-left { - border-left: 1px solid #dee2e6 !important; -} - -.border-0 { - border: 0 !important; -} - -.border-top-0 { - border-top: 0 !important; -} - -.border-right-0 { - border-right: 0 !important; -} - -.border-bottom-0 { - border-bottom: 0 !important; -} - -.border-left-0 { - border-left: 0 !important; -} - -.border-primary { - border-color: #007bff !important; -} - -.border-secondary { - border-color: #6c757d !important; -} - -.border-success { - border-color: #28a745 !important; -} - -.border-info { - border-color: #17a2b8 !important; -} - -.border-warning { - border-color: #ffc107 !important; -} - -.border-danger { - border-color: #dc3545 !important; -} - -.border-light { - border-color: #f8f9fa !important; -} - -.border-dark { - border-color: #343a40 !important; -} - -.border-white { - border-color: #fff !important; -} - -.rounded { - border-radius: 0.25rem !important; -} - -.rounded-top { - border-top-left-radius: 0.25rem !important; - border-top-right-radius: 0.25rem !important; -} - -.rounded-right { - border-top-right-radius: 0.25rem !important; - border-bottom-right-radius: 0.25rem !important; -} - -.rounded-bottom { - border-bottom-right-radius: 0.25rem !important; - border-bottom-left-radius: 0.25rem !important; -} - -.rounded-left { - border-top-left-radius: 0.25rem !important; - border-bottom-left-radius: 0.25rem !important; -} - -.rounded-circle { - border-radius: 50% !important; -} - -.rounded-0 { - border-radius: 0 !important; -} - -.clearfix::after { - display: block; - clear: both; - content: ""; -} - -.d-none { - display: none !important; -} - -.d-inline { - display: inline !important; -} - -.d-inline-block { - display: inline-block !important; -} - -.d-block { - display: block !important; -} - -.d-table { - display: table !important; -} - -.d-table-row { - display: table-row !important; -} - -.d-table-cell { - display: table-cell !important; -} - -.d-flex { - display: -ms-flexbox !important; - display: flex !important; -} - -.d-inline-flex { - display: -ms-inline-flexbox !important; - display: inline-flex !important; -} - -@media (min-width: 576px) { - .d-sm-none { - display: none !important; - } - .d-sm-inline { - display: inline !important; - } - .d-sm-inline-block { - display: inline-block !important; - } - .d-sm-block { - display: block !important; - } - .d-sm-table { - display: table !important; - } - .d-sm-table-row { - display: table-row !important; - } - .d-sm-table-cell { - display: table-cell !important; - } - .d-sm-flex { - display: -ms-flexbox !important; - display: flex !important; - } - .d-sm-inline-flex { - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} - -@media (min-width: 768px) { - .d-md-none { - display: none !important; - } - .d-md-inline { - display: inline !important; - } - .d-md-inline-block { - display: inline-block !important; - } - .d-md-block { - display: block !important; - } - .d-md-table { - display: table !important; - } - .d-md-table-row { - display: table-row !important; - } - .d-md-table-cell { - display: table-cell !important; - } - .d-md-flex { - display: -ms-flexbox !important; - display: flex !important; - } - .d-md-inline-flex { - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} - -@media (min-width: 992px) { - .d-lg-none { - display: none !important; - } - .d-lg-inline { - display: inline !important; - } - .d-lg-inline-block { - display: inline-block !important; - } - .d-lg-block { - display: block !important; - } - .d-lg-table { - display: table !important; - } - .d-lg-table-row { - display: table-row !important; - } - .d-lg-table-cell { - display: table-cell !important; - } - .d-lg-flex { - display: -ms-flexbox !important; - display: flex !important; - } - .d-lg-inline-flex { - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} - -@media (min-width: 1200px) { - .d-xl-none { - display: none !important; - } - .d-xl-inline { - display: inline !important; - } - .d-xl-inline-block { - display: inline-block !important; - } - .d-xl-block { - display: block !important; - } - .d-xl-table { - display: table !important; - } - .d-xl-table-row { - display: table-row !important; - } - .d-xl-table-cell { - display: table-cell !important; - } - .d-xl-flex { - display: -ms-flexbox !important; - display: flex !important; - } - .d-xl-inline-flex { - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} - -@media print { - .d-print-none { - display: none !important; - } - .d-print-inline { - display: inline !important; - } - .d-print-inline-block { - display: inline-block !important; - } - .d-print-block { - display: block !important; - } - .d-print-table { - display: table !important; - } - .d-print-table-row { - display: table-row !important; - } - .d-print-table-cell { - display: table-cell !important; - } - .d-print-flex { - display: -ms-flexbox !important; - display: flex !important; - } - .d-print-inline-flex { - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} - -.embed-responsive { - position: relative; - display: block; - width: 100%; - padding: 0; - overflow: hidden; -} - -.embed-responsive::before { - display: block; - content: ""; -} - -.embed-responsive .embed-responsive-item, -.embed-responsive iframe, -.embed-responsive embed, -.embed-responsive object, -.embed-responsive video { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - border: 0; -} - -.embed-responsive-21by9::before { - padding-top: 42.857143%; -} - -.embed-responsive-16by9::before { - padding-top: 56.25%; -} - -.embed-responsive-4by3::before { - padding-top: 75%; -} - -.embed-responsive-1by1::before { - padding-top: 100%; -} - -.flex-row { - -ms-flex-direction: row !important; - flex-direction: row !important; -} - -.flex-column { - -ms-flex-direction: column !important; - flex-direction: column !important; -} - -.flex-row-reverse { - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; -} - -.flex-column-reverse { - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; -} - -.flex-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; -} - -.flex-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; -} - -.flex-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; -} - -.flex-fill { - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; -} - -.flex-grow-0 { - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; -} - -.flex-grow-1 { - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; -} - -.flex-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; -} - -.flex-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; -} - -.justify-content-start { - -ms-flex-pack: start !important; - justify-content: flex-start !important; -} - -.justify-content-end { - -ms-flex-pack: end !important; - justify-content: flex-end !important; -} - -.justify-content-center { - -ms-flex-pack: center !important; - justify-content: center !important; -} - -.justify-content-between { - -ms-flex-pack: justify !important; - justify-content: space-between !important; -} - -.justify-content-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; -} - -.align-items-start { - -ms-flex-align: start !important; - align-items: flex-start !important; -} - -.align-items-end { - -ms-flex-align: end !important; - align-items: flex-end !important; -} - -.align-items-center { - -ms-flex-align: center !important; - align-items: center !important; -} - -.align-items-baseline { - -ms-flex-align: baseline !important; - align-items: baseline !important; -} - -.align-items-stretch { - -ms-flex-align: stretch !important; - align-items: stretch !important; -} - -.align-content-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; -} - -.align-content-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; -} - -.align-content-center { - -ms-flex-line-pack: center !important; - align-content: center !important; -} - -.align-content-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; -} - -.align-content-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; -} - -.align-content-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; -} - -.align-self-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; -} - -.align-self-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; -} - -.align-self-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; -} - -.align-self-center { - -ms-flex-item-align: center !important; - align-self: center !important; -} - -.align-self-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; -} - -.align-self-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; -} - -@media (min-width: 576px) { - .flex-sm-row { - -ms-flex-direction: row !important; - flex-direction: row !important; - } - .flex-sm-column { - -ms-flex-direction: column !important; - flex-direction: column !important; - } - .flex-sm-row-reverse { - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; - } - .flex-sm-column-reverse { - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; - } - .flex-sm-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; - } - .flex-sm-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; - } - .flex-sm-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; - } - .flex-sm-fill { - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; - } - .flex-sm-grow-0 { - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; - } - .flex-sm-grow-1 { - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; - } - .flex-sm-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; - } - .flex-sm-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; - } - .justify-content-sm-start { - -ms-flex-pack: start !important; - justify-content: flex-start !important; - } - .justify-content-sm-end { - -ms-flex-pack: end !important; - justify-content: flex-end !important; - } - .justify-content-sm-center { - -ms-flex-pack: center !important; - justify-content: center !important; - } - .justify-content-sm-between { - -ms-flex-pack: justify !important; - justify-content: space-between !important; - } - .justify-content-sm-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; - } - .align-items-sm-start { - -ms-flex-align: start !important; - align-items: flex-start !important; - } - .align-items-sm-end { - -ms-flex-align: end !important; - align-items: flex-end !important; - } - .align-items-sm-center { - -ms-flex-align: center !important; - align-items: center !important; - } - .align-items-sm-baseline { - -ms-flex-align: baseline !important; - align-items: baseline !important; - } - .align-items-sm-stretch { - -ms-flex-align: stretch !important; - align-items: stretch !important; - } - .align-content-sm-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; - } - .align-content-sm-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; - } - .align-content-sm-center { - -ms-flex-line-pack: center !important; - align-content: center !important; - } - .align-content-sm-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; - } - .align-content-sm-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; - } - .align-content-sm-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; - } - .align-self-sm-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; - } - .align-self-sm-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; - } - .align-self-sm-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; - } - .align-self-sm-center { - -ms-flex-item-align: center !important; - align-self: center !important; - } - .align-self-sm-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; - } - .align-self-sm-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; - } -} - -@media (min-width: 768px) { - .flex-md-row { - -ms-flex-direction: row !important; - flex-direction: row !important; - } - .flex-md-column { - -ms-flex-direction: column !important; - flex-direction: column !important; - } - .flex-md-row-reverse { - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; - } - .flex-md-column-reverse { - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; - } - .flex-md-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; - } - .flex-md-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; - } - .flex-md-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; - } - .flex-md-fill { - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; - } - .flex-md-grow-0 { - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; - } - .flex-md-grow-1 { - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; - } - .flex-md-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; - } - .flex-md-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; - } - .justify-content-md-start { - -ms-flex-pack: start !important; - justify-content: flex-start !important; - } - .justify-content-md-end { - -ms-flex-pack: end !important; - justify-content: flex-end !important; - } - .justify-content-md-center { - -ms-flex-pack: center !important; - justify-content: center !important; - } - .justify-content-md-between { - -ms-flex-pack: justify !important; - justify-content: space-between !important; - } - .justify-content-md-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; - } - .align-items-md-start { - -ms-flex-align: start !important; - align-items: flex-start !important; - } - .align-items-md-end { - -ms-flex-align: end !important; - align-items: flex-end !important; - } - .align-items-md-center { - -ms-flex-align: center !important; - align-items: center !important; - } - .align-items-md-baseline { - -ms-flex-align: baseline !important; - align-items: baseline !important; - } - .align-items-md-stretch { - -ms-flex-align: stretch !important; - align-items: stretch !important; - } - .align-content-md-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; - } - .align-content-md-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; - } - .align-content-md-center { - -ms-flex-line-pack: center !important; - align-content: center !important; - } - .align-content-md-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; - } - .align-content-md-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; - } - .align-content-md-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; - } - .align-self-md-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; - } - .align-self-md-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; - } - .align-self-md-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; - } - .align-self-md-center { - -ms-flex-item-align: center !important; - align-self: center !important; - } - .align-self-md-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; - } - .align-self-md-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; - } -} - -@media (min-width: 992px) { - .flex-lg-row { - -ms-flex-direction: row !important; - flex-direction: row !important; - } - .flex-lg-column { - -ms-flex-direction: column !important; - flex-direction: column !important; - } - .flex-lg-row-reverse { - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; - } - .flex-lg-column-reverse { - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; - } - .flex-lg-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; - } - .flex-lg-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; - } - .flex-lg-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; - } - .flex-lg-fill { - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; - } - .flex-lg-grow-0 { - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; - } - .flex-lg-grow-1 { - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; - } - .flex-lg-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; - } - .flex-lg-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; - } - .justify-content-lg-start { - -ms-flex-pack: start !important; - justify-content: flex-start !important; - } - .justify-content-lg-end { - -ms-flex-pack: end !important; - justify-content: flex-end !important; - } - .justify-content-lg-center { - -ms-flex-pack: center !important; - justify-content: center !important; - } - .justify-content-lg-between { - -ms-flex-pack: justify !important; - justify-content: space-between !important; - } - .justify-content-lg-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; - } - .align-items-lg-start { - -ms-flex-align: start !important; - align-items: flex-start !important; - } - .align-items-lg-end { - -ms-flex-align: end !important; - align-items: flex-end !important; - } - .align-items-lg-center { - -ms-flex-align: center !important; - align-items: center !important; - } - .align-items-lg-baseline { - -ms-flex-align: baseline !important; - align-items: baseline !important; - } - .align-items-lg-stretch { - -ms-flex-align: stretch !important; - align-items: stretch !important; - } - .align-content-lg-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; - } - .align-content-lg-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; - } - .align-content-lg-center { - -ms-flex-line-pack: center !important; - align-content: center !important; - } - .align-content-lg-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; - } - .align-content-lg-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; - } - .align-content-lg-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; - } - .align-self-lg-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; - } - .align-self-lg-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; - } - .align-self-lg-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; - } - .align-self-lg-center { - -ms-flex-item-align: center !important; - align-self: center !important; - } - .align-self-lg-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; - } - .align-self-lg-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; - } -} - -@media (min-width: 1200px) { - .flex-xl-row { - -ms-flex-direction: row !important; - flex-direction: row !important; - } - .flex-xl-column { - -ms-flex-direction: column !important; - flex-direction: column !important; - } - .flex-xl-row-reverse { - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; - } - .flex-xl-column-reverse { - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; - } - .flex-xl-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; - } - .flex-xl-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; - } - .flex-xl-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; - } - .flex-xl-fill { - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; - } - .flex-xl-grow-0 { - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; - } - .flex-xl-grow-1 { - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; - } - .flex-xl-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; - } - .flex-xl-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; - } - .justify-content-xl-start { - -ms-flex-pack: start !important; - justify-content: flex-start !important; - } - .justify-content-xl-end { - -ms-flex-pack: end !important; - justify-content: flex-end !important; - } - .justify-content-xl-center { - -ms-flex-pack: center !important; - justify-content: center !important; - } - .justify-content-xl-between { - -ms-flex-pack: justify !important; - justify-content: space-between !important; - } - .justify-content-xl-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; - } - .align-items-xl-start { - -ms-flex-align: start !important; - align-items: flex-start !important; - } - .align-items-xl-end { - -ms-flex-align: end !important; - align-items: flex-end !important; - } - .align-items-xl-center { - -ms-flex-align: center !important; - align-items: center !important; - } - .align-items-xl-baseline { - -ms-flex-align: baseline !important; - align-items: baseline !important; - } - .align-items-xl-stretch { - -ms-flex-align: stretch !important; - align-items: stretch !important; - } - .align-content-xl-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; - } - .align-content-xl-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; - } - .align-content-xl-center { - -ms-flex-line-pack: center !important; - align-content: center !important; - } - .align-content-xl-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; - } - .align-content-xl-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; - } - .align-content-xl-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; - } - .align-self-xl-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; - } - .align-self-xl-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; - } - .align-self-xl-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; - } - .align-self-xl-center { - -ms-flex-item-align: center !important; - align-self: center !important; - } - .align-self-xl-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; - } - .align-self-xl-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; - } -} - -.float-left { - float: left !important; -} - -.float-right { - float: right !important; -} - -.float-none { - float: none !important; -} - -@media (min-width: 576px) { - .float-sm-left { - float: left !important; - } - .float-sm-right { - float: right !important; - } - .float-sm-none { - float: none !important; - } -} - -@media (min-width: 768px) { - .float-md-left { - float: left !important; - } - .float-md-right { - float: right !important; - } - .float-md-none { - float: none !important; - } -} - -@media (min-width: 992px) { - .float-lg-left { - float: left !important; - } - .float-lg-right { - float: right !important; - } - .float-lg-none { - float: none !important; - } -} - -@media (min-width: 1200px) { - .float-xl-left { - float: left !important; - } - .float-xl-right { - float: right !important; - } - .float-xl-none { - float: none !important; - } -} - -.position-static { - position: static !important; -} - -.position-relative { - position: relative !important; -} - -.position-absolute { - position: absolute !important; -} - -.position-fixed { - position: fixed !important; -} - -.position-sticky { - position: -webkit-sticky !important; - position: sticky !important; -} - -.fixed-top { - position: fixed; - top: 0; - right: 0; - left: 0; - z-index: 1030; -} - -.fixed-bottom { - position: fixed; - right: 0; - bottom: 0; - left: 0; - z-index: 1030; -} - -@supports ((position: -webkit-sticky) or (position: sticky)) { - .sticky-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; - } -} - -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border: 0; -} - -.sr-only-focusable:active, .sr-only-focusable:focus { - position: static; - width: auto; - height: auto; - overflow: visible; - clip: auto; - white-space: normal; -} - -.shadow-sm { - box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; -} - -.shadow { - box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; -} - -.shadow-lg { - box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; -} - -.shadow-none { - box-shadow: none !important; -} - -.w-25 { - width: 25% !important; -} - -.w-50 { - width: 50% !important; -} - -.w-75 { - width: 75% !important; -} - -.w-100 { - width: 100% !important; -} - -.w-auto { - width: auto !important; -} - -.h-25 { - height: 25% !important; -} - -.h-50 { - height: 50% !important; -} - -.h-75 { - height: 75% !important; -} - -.h-100 { - height: 100% !important; -} - -.h-auto { - height: auto !important; -} - -.mw-100 { - max-width: 100% !important; -} - -.mh-100 { - max-height: 100% !important; -} - -.m-0 { - margin: 0 !important; -} - -.mt-0, -.my-0 { - margin-top: 0 !important; -} - -.mr-0, -.mx-0 { - margin-right: 0 !important; -} - -.mb-0, -.my-0 { - margin-bottom: 0 !important; -} - -.ml-0, -.mx-0 { - margin-left: 0 !important; -} - -.m-1 { - margin: 0.25rem !important; -} - -.mt-1, -.my-1 { - margin-top: 0.25rem !important; -} - -.mr-1, -.mx-1 { - margin-right: 0.25rem !important; -} - -.mb-1, -.my-1 { - margin-bottom: 0.25rem !important; -} - -.ml-1, -.mx-1 { - margin-left: 0.25rem !important; -} - -.m-2 { - margin: 0.5rem !important; -} - -.mt-2, -.my-2 { - margin-top: 0.5rem !important; -} - -.mr-2, -.mx-2 { - margin-right: 0.5rem !important; -} - -.mb-2, -.my-2 { - margin-bottom: 0.5rem !important; -} - -.ml-2, -.mx-2 { - margin-left: 0.5rem !important; -} - -.m-3 { - margin: 1rem !important; -} - -.mt-3, -.my-3 { - margin-top: 1rem !important; -} - -.mr-3, -.mx-3 { - margin-right: 1rem !important; -} - -.mb-3, -.my-3 { - margin-bottom: 1rem !important; -} - -.ml-3, -.mx-3 { - margin-left: 1rem !important; -} - -.m-4 { - margin: 1.5rem !important; -} - -.mt-4, -.my-4 { - margin-top: 1.5rem !important; -} - -.mr-4, -.mx-4 { - margin-right: 1.5rem !important; -} - -.mb-4, -.my-4 { - margin-bottom: 1.5rem !important; -} - -.ml-4, -.mx-4 { - margin-left: 1.5rem !important; -} - -.m-5 { - margin: 3rem !important; -} - -.mt-5, -.my-5 { - margin-top: 3rem !important; -} - -.mr-5, -.mx-5 { - margin-right: 3rem !important; -} - -.mb-5, -.my-5 { - margin-bottom: 3rem !important; -} - -.ml-5, -.mx-5 { - margin-left: 3rem !important; -} - -.p-0 { - padding: 0 !important; -} - -.pt-0, -.py-0 { - padding-top: 0 !important; -} - -.pr-0, -.px-0 { - padding-right: 0 !important; -} - -.pb-0, -.py-0 { - padding-bottom: 0 !important; -} - -.pl-0, -.px-0 { - padding-left: 0 !important; -} - -.p-1 { - padding: 0.25rem !important; -} - -.pt-1, -.py-1 { - padding-top: 0.25rem !important; -} - -.pr-1, -.px-1 { - padding-right: 0.25rem !important; -} - -.pb-1, -.py-1 { - padding-bottom: 0.25rem !important; -} - -.pl-1, -.px-1 { - padding-left: 0.25rem !important; -} - -.p-2 { - padding: 0.5rem !important; -} - -.pt-2, -.py-2 { - padding-top: 0.5rem !important; -} - -.pr-2, -.px-2 { - padding-right: 0.5rem !important; -} - -.pb-2, -.py-2 { - padding-bottom: 0.5rem !important; -} - -.pl-2, -.px-2 { - padding-left: 0.5rem !important; -} - -.p-3 { - padding: 1rem !important; -} - -.pt-3, -.py-3 { - padding-top: 1rem !important; -} - -.pr-3, -.px-3 { - padding-right: 1rem !important; -} - -.pb-3, -.py-3 { - padding-bottom: 1rem !important; -} - -.pl-3, -.px-3 { - padding-left: 1rem !important; -} - -.p-4 { - padding: 1.5rem !important; -} - -.pt-4, -.py-4 { - padding-top: 1.5rem !important; -} - -.pr-4, -.px-4 { - padding-right: 1.5rem !important; -} - -.pb-4, -.py-4 { - padding-bottom: 1.5rem !important; -} - -.pl-4, -.px-4 { - padding-left: 1.5rem !important; -} - -.p-5 { - padding: 3rem !important; -} - -.pt-5, -.py-5 { - padding-top: 3rem !important; -} - -.pr-5, -.px-5 { - padding-right: 3rem !important; -} - -.pb-5, -.py-5 { - padding-bottom: 3rem !important; -} - -.pl-5, -.px-5 { - padding-left: 3rem !important; -} - -.m-auto { - margin: auto !important; -} - -.mt-auto, -.my-auto { - margin-top: auto !important; -} - -.mr-auto, -.mx-auto { - margin-right: auto !important; -} - -.mb-auto, -.my-auto { - margin-bottom: auto !important; -} - -.ml-auto, -.mx-auto { - margin-left: auto !important; -} - -@media (min-width: 576px) { - .m-sm-0 { - margin: 0 !important; - } - .mt-sm-0, - .my-sm-0 { - margin-top: 0 !important; - } - .mr-sm-0, - .mx-sm-0 { - margin-right: 0 !important; - } - .mb-sm-0, - .my-sm-0 { - margin-bottom: 0 !important; - } - .ml-sm-0, - .mx-sm-0 { - margin-left: 0 !important; - } - .m-sm-1 { - margin: 0.25rem !important; - } - .mt-sm-1, - .my-sm-1 { - margin-top: 0.25rem !important; - } - .mr-sm-1, - .mx-sm-1 { - margin-right: 0.25rem !important; - } - .mb-sm-1, - .my-sm-1 { - margin-bottom: 0.25rem !important; - } - .ml-sm-1, - .mx-sm-1 { - margin-left: 0.25rem !important; - } - .m-sm-2 { - margin: 0.5rem !important; - } - .mt-sm-2, - .my-sm-2 { - margin-top: 0.5rem !important; - } - .mr-sm-2, - .mx-sm-2 { - margin-right: 0.5rem !important; - } - .mb-sm-2, - .my-sm-2 { - margin-bottom: 0.5rem !important; - } - .ml-sm-2, - .mx-sm-2 { - margin-left: 0.5rem !important; - } - .m-sm-3 { - margin: 1rem !important; - } - .mt-sm-3, - .my-sm-3 { - margin-top: 1rem !important; - } - .mr-sm-3, - .mx-sm-3 { - margin-right: 1rem !important; - } - .mb-sm-3, - .my-sm-3 { - margin-bottom: 1rem !important; - } - .ml-sm-3, - .mx-sm-3 { - margin-left: 1rem !important; - } - .m-sm-4 { - margin: 1.5rem !important; - } - .mt-sm-4, - .my-sm-4 { - margin-top: 1.5rem !important; - } - .mr-sm-4, - .mx-sm-4 { - margin-right: 1.5rem !important; - } - .mb-sm-4, - .my-sm-4 { - margin-bottom: 1.5rem !important; - } - .ml-sm-4, - .mx-sm-4 { - margin-left: 1.5rem !important; - } - .m-sm-5 { - margin: 3rem !important; - } - .mt-sm-5, - .my-sm-5 { - margin-top: 3rem !important; - } - .mr-sm-5, - .mx-sm-5 { - margin-right: 3rem !important; - } - .mb-sm-5, - .my-sm-5 { - margin-bottom: 3rem !important; - } - .ml-sm-5, - .mx-sm-5 { - margin-left: 3rem !important; - } - .p-sm-0 { - padding: 0 !important; - } - .pt-sm-0, - .py-sm-0 { - padding-top: 0 !important; - } - .pr-sm-0, - .px-sm-0 { - padding-right: 0 !important; - } - .pb-sm-0, - .py-sm-0 { - padding-bottom: 0 !important; - } - .pl-sm-0, - .px-sm-0 { - padding-left: 0 !important; - } - .p-sm-1 { - padding: 0.25rem !important; - } - .pt-sm-1, - .py-sm-1 { - padding-top: 0.25rem !important; - } - .pr-sm-1, - .px-sm-1 { - padding-right: 0.25rem !important; - } - .pb-sm-1, - .py-sm-1 { - padding-bottom: 0.25rem !important; - } - .pl-sm-1, - .px-sm-1 { - padding-left: 0.25rem !important; - } - .p-sm-2 { - padding: 0.5rem !important; - } - .pt-sm-2, - .py-sm-2 { - padding-top: 0.5rem !important; - } - .pr-sm-2, - .px-sm-2 { - padding-right: 0.5rem !important; - } - .pb-sm-2, - .py-sm-2 { - padding-bottom: 0.5rem !important; - } - .pl-sm-2, - .px-sm-2 { - padding-left: 0.5rem !important; - } - .p-sm-3 { - padding: 1rem !important; - } - .pt-sm-3, - .py-sm-3 { - padding-top: 1rem !important; - } - .pr-sm-3, - .px-sm-3 { - padding-right: 1rem !important; - } - .pb-sm-3, - .py-sm-3 { - padding-bottom: 1rem !important; - } - .pl-sm-3, - .px-sm-3 { - padding-left: 1rem !important; - } - .p-sm-4 { - padding: 1.5rem !important; - } - .pt-sm-4, - .py-sm-4 { - padding-top: 1.5rem !important; - } - .pr-sm-4, - .px-sm-4 { - padding-right: 1.5rem !important; - } - .pb-sm-4, - .py-sm-4 { - padding-bottom: 1.5rem !important; - } - .pl-sm-4, - .px-sm-4 { - padding-left: 1.5rem !important; - } - .p-sm-5 { - padding: 3rem !important; - } - .pt-sm-5, - .py-sm-5 { - padding-top: 3rem !important; - } - .pr-sm-5, - .px-sm-5 { - padding-right: 3rem !important; - } - .pb-sm-5, - .py-sm-5 { - padding-bottom: 3rem !important; - } - .pl-sm-5, - .px-sm-5 { - padding-left: 3rem !important; - } - .m-sm-auto { - margin: auto !important; - } - .mt-sm-auto, - .my-sm-auto { - margin-top: auto !important; - } - .mr-sm-auto, - .mx-sm-auto { - margin-right: auto !important; - } - .mb-sm-auto, - .my-sm-auto { - margin-bottom: auto !important; - } - .ml-sm-auto, - .mx-sm-auto { - margin-left: auto !important; - } -} - -@media (min-width: 768px) { - .m-md-0 { - margin: 0 !important; - } - .mt-md-0, - .my-md-0 { - margin-top: 0 !important; - } - .mr-md-0, - .mx-md-0 { - margin-right: 0 !important; - } - .mb-md-0, - .my-md-0 { - margin-bottom: 0 !important; - } - .ml-md-0, - .mx-md-0 { - margin-left: 0 !important; - } - .m-md-1 { - margin: 0.25rem !important; - } - .mt-md-1, - .my-md-1 { - margin-top: 0.25rem !important; - } - .mr-md-1, - .mx-md-1 { - margin-right: 0.25rem !important; - } - .mb-md-1, - .my-md-1 { - margin-bottom: 0.25rem !important; - } - .ml-md-1, - .mx-md-1 { - margin-left: 0.25rem !important; - } - .m-md-2 { - margin: 0.5rem !important; - } - .mt-md-2, - .my-md-2 { - margin-top: 0.5rem !important; - } - .mr-md-2, - .mx-md-2 { - margin-right: 0.5rem !important; - } - .mb-md-2, - .my-md-2 { - margin-bottom: 0.5rem !important; - } - .ml-md-2, - .mx-md-2 { - margin-left: 0.5rem !important; - } - .m-md-3 { - margin: 1rem !important; - } - .mt-md-3, - .my-md-3 { - margin-top: 1rem !important; - } - .mr-md-3, - .mx-md-3 { - margin-right: 1rem !important; - } - .mb-md-3, - .my-md-3 { - margin-bottom: 1rem !important; - } - .ml-md-3, - .mx-md-3 { - margin-left: 1rem !important; - } - .m-md-4 { - margin: 1.5rem !important; - } - .mt-md-4, - .my-md-4 { - margin-top: 1.5rem !important; - } - .mr-md-4, - .mx-md-4 { - margin-right: 1.5rem !important; - } - .mb-md-4, - .my-md-4 { - margin-bottom: 1.5rem !important; - } - .ml-md-4, - .mx-md-4 { - margin-left: 1.5rem !important; - } - .m-md-5 { - margin: 3rem !important; - } - .mt-md-5, - .my-md-5 { - margin-top: 3rem !important; - } - .mr-md-5, - .mx-md-5 { - margin-right: 3rem !important; - } - .mb-md-5, - .my-md-5 { - margin-bottom: 3rem !important; - } - .ml-md-5, - .mx-md-5 { - margin-left: 3rem !important; - } - .p-md-0 { - padding: 0 !important; - } - .pt-md-0, - .py-md-0 { - padding-top: 0 !important; - } - .pr-md-0, - .px-md-0 { - padding-right: 0 !important; - } - .pb-md-0, - .py-md-0 { - padding-bottom: 0 !important; - } - .pl-md-0, - .px-md-0 { - padding-left: 0 !important; - } - .p-md-1 { - padding: 0.25rem !important; - } - .pt-md-1, - .py-md-1 { - padding-top: 0.25rem !important; - } - .pr-md-1, - .px-md-1 { - padding-right: 0.25rem !important; - } - .pb-md-1, - .py-md-1 { - padding-bottom: 0.25rem !important; - } - .pl-md-1, - .px-md-1 { - padding-left: 0.25rem !important; - } - .p-md-2 { - padding: 0.5rem !important; - } - .pt-md-2, - .py-md-2 { - padding-top: 0.5rem !important; - } - .pr-md-2, - .px-md-2 { - padding-right: 0.5rem !important; - } - .pb-md-2, - .py-md-2 { - padding-bottom: 0.5rem !important; - } - .pl-md-2, - .px-md-2 { - padding-left: 0.5rem !important; - } - .p-md-3 { - padding: 1rem !important; - } - .pt-md-3, - .py-md-3 { - padding-top: 1rem !important; - } - .pr-md-3, - .px-md-3 { - padding-right: 1rem !important; - } - .pb-md-3, - .py-md-3 { - padding-bottom: 1rem !important; - } - .pl-md-3, - .px-md-3 { - padding-left: 1rem !important; - } - .p-md-4 { - padding: 1.5rem !important; - } - .pt-md-4, - .py-md-4 { - padding-top: 1.5rem !important; - } - .pr-md-4, - .px-md-4 { - padding-right: 1.5rem !important; - } - .pb-md-4, - .py-md-4 { - padding-bottom: 1.5rem !important; - } - .pl-md-4, - .px-md-4 { - padding-left: 1.5rem !important; - } - .p-md-5 { - padding: 3rem !important; - } - .pt-md-5, - .py-md-5 { - padding-top: 3rem !important; - } - .pr-md-5, - .px-md-5 { - padding-right: 3rem !important; - } - .pb-md-5, - .py-md-5 { - padding-bottom: 3rem !important; - } - .pl-md-5, - .px-md-5 { - padding-left: 3rem !important; - } - .m-md-auto { - margin: auto !important; - } - .mt-md-auto, - .my-md-auto { - margin-top: auto !important; - } - .mr-md-auto, - .mx-md-auto { - margin-right: auto !important; - } - .mb-md-auto, - .my-md-auto { - margin-bottom: auto !important; - } - .ml-md-auto, - .mx-md-auto { - margin-left: auto !important; - } -} - -@media (min-width: 992px) { - .m-lg-0 { - margin: 0 !important; - } - .mt-lg-0, - .my-lg-0 { - margin-top: 0 !important; - } - .mr-lg-0, - .mx-lg-0 { - margin-right: 0 !important; - } - .mb-lg-0, - .my-lg-0 { - margin-bottom: 0 !important; - } - .ml-lg-0, - .mx-lg-0 { - margin-left: 0 !important; - } - .m-lg-1 { - margin: 0.25rem !important; - } - .mt-lg-1, - .my-lg-1 { - margin-top: 0.25rem !important; - } - .mr-lg-1, - .mx-lg-1 { - margin-right: 0.25rem !important; - } - .mb-lg-1, - .my-lg-1 { - margin-bottom: 0.25rem !important; - } - .ml-lg-1, - .mx-lg-1 { - margin-left: 0.25rem !important; - } - .m-lg-2 { - margin: 0.5rem !important; - } - .mt-lg-2, - .my-lg-2 { - margin-top: 0.5rem !important; - } - .mr-lg-2, - .mx-lg-2 { - margin-right: 0.5rem !important; - } - .mb-lg-2, - .my-lg-2 { - margin-bottom: 0.5rem !important; - } - .ml-lg-2, - .mx-lg-2 { - margin-left: 0.5rem !important; - } - .m-lg-3 { - margin: 1rem !important; - } - .mt-lg-3, - .my-lg-3 { - margin-top: 1rem !important; - } - .mr-lg-3, - .mx-lg-3 { - margin-right: 1rem !important; - } - .mb-lg-3, - .my-lg-3 { - margin-bottom: 1rem !important; - } - .ml-lg-3, - .mx-lg-3 { - margin-left: 1rem !important; - } - .m-lg-4 { - margin: 1.5rem !important; - } - .mt-lg-4, - .my-lg-4 { - margin-top: 1.5rem !important; - } - .mr-lg-4, - .mx-lg-4 { - margin-right: 1.5rem !important; - } - .mb-lg-4, - .my-lg-4 { - margin-bottom: 1.5rem !important; - } - .ml-lg-4, - .mx-lg-4 { - margin-left: 1.5rem !important; - } - .m-lg-5 { - margin: 3rem !important; - } - .mt-lg-5, - .my-lg-5 { - margin-top: 3rem !important; - } - .mr-lg-5, - .mx-lg-5 { - margin-right: 3rem !important; - } - .mb-lg-5, - .my-lg-5 { - margin-bottom: 3rem !important; - } - .ml-lg-5, - .mx-lg-5 { - margin-left: 3rem !important; - } - .p-lg-0 { - padding: 0 !important; - } - .pt-lg-0, - .py-lg-0 { - padding-top: 0 !important; - } - .pr-lg-0, - .px-lg-0 { - padding-right: 0 !important; - } - .pb-lg-0, - .py-lg-0 { - padding-bottom: 0 !important; - } - .pl-lg-0, - .px-lg-0 { - padding-left: 0 !important; - } - .p-lg-1 { - padding: 0.25rem !important; - } - .pt-lg-1, - .py-lg-1 { - padding-top: 0.25rem !important; - } - .pr-lg-1, - .px-lg-1 { - padding-right: 0.25rem !important; - } - .pb-lg-1, - .py-lg-1 { - padding-bottom: 0.25rem !important; - } - .pl-lg-1, - .px-lg-1 { - padding-left: 0.25rem !important; - } - .p-lg-2 { - padding: 0.5rem !important; - } - .pt-lg-2, - .py-lg-2 { - padding-top: 0.5rem !important; - } - .pr-lg-2, - .px-lg-2 { - padding-right: 0.5rem !important; - } - .pb-lg-2, - .py-lg-2 { - padding-bottom: 0.5rem !important; - } - .pl-lg-2, - .px-lg-2 { - padding-left: 0.5rem !important; - } - .p-lg-3 { - padding: 1rem !important; - } - .pt-lg-3, - .py-lg-3 { - padding-top: 1rem !important; - } - .pr-lg-3, - .px-lg-3 { - padding-right: 1rem !important; - } - .pb-lg-3, - .py-lg-3 { - padding-bottom: 1rem !important; - } - .pl-lg-3, - .px-lg-3 { - padding-left: 1rem !important; - } - .p-lg-4 { - padding: 1.5rem !important; - } - .pt-lg-4, - .py-lg-4 { - padding-top: 1.5rem !important; - } - .pr-lg-4, - .px-lg-4 { - padding-right: 1.5rem !important; - } - .pb-lg-4, - .py-lg-4 { - padding-bottom: 1.5rem !important; - } - .pl-lg-4, - .px-lg-4 { - padding-left: 1.5rem !important; - } - .p-lg-5 { - padding: 3rem !important; - } - .pt-lg-5, - .py-lg-5 { - padding-top: 3rem !important; - } - .pr-lg-5, - .px-lg-5 { - padding-right: 3rem !important; - } - .pb-lg-5, - .py-lg-5 { - padding-bottom: 3rem !important; - } - .pl-lg-5, - .px-lg-5 { - padding-left: 3rem !important; - } - .m-lg-auto { - margin: auto !important; - } - .mt-lg-auto, - .my-lg-auto { - margin-top: auto !important; - } - .mr-lg-auto, - .mx-lg-auto { - margin-right: auto !important; - } - .mb-lg-auto, - .my-lg-auto { - margin-bottom: auto !important; - } - .ml-lg-auto, - .mx-lg-auto { - margin-left: auto !important; - } -} - -@media (min-width: 1200px) { - .m-xl-0 { - margin: 0 !important; - } - .mt-xl-0, - .my-xl-0 { - margin-top: 0 !important; - } - .mr-xl-0, - .mx-xl-0 { - margin-right: 0 !important; - } - .mb-xl-0, - .my-xl-0 { - margin-bottom: 0 !important; - } - .ml-xl-0, - .mx-xl-0 { - margin-left: 0 !important; - } - .m-xl-1 { - margin: 0.25rem !important; - } - .mt-xl-1, - .my-xl-1 { - margin-top: 0.25rem !important; - } - .mr-xl-1, - .mx-xl-1 { - margin-right: 0.25rem !important; - } - .mb-xl-1, - .my-xl-1 { - margin-bottom: 0.25rem !important; - } - .ml-xl-1, - .mx-xl-1 { - margin-left: 0.25rem !important; - } - .m-xl-2 { - margin: 0.5rem !important; - } - .mt-xl-2, - .my-xl-2 { - margin-top: 0.5rem !important; - } - .mr-xl-2, - .mx-xl-2 { - margin-right: 0.5rem !important; - } - .mb-xl-2, - .my-xl-2 { - margin-bottom: 0.5rem !important; - } - .ml-xl-2, - .mx-xl-2 { - margin-left: 0.5rem !important; - } - .m-xl-3 { - margin: 1rem !important; - } - .mt-xl-3, - .my-xl-3 { - margin-top: 1rem !important; - } - .mr-xl-3, - .mx-xl-3 { - margin-right: 1rem !important; - } - .mb-xl-3, - .my-xl-3 { - margin-bottom: 1rem !important; - } - .ml-xl-3, - .mx-xl-3 { - margin-left: 1rem !important; - } - .m-xl-4 { - margin: 1.5rem !important; - } - .mt-xl-4, - .my-xl-4 { - margin-top: 1.5rem !important; - } - .mr-xl-4, - .mx-xl-4 { - margin-right: 1.5rem !important; - } - .mb-xl-4, - .my-xl-4 { - margin-bottom: 1.5rem !important; - } - .ml-xl-4, - .mx-xl-4 { - margin-left: 1.5rem !important; - } - .m-xl-5 { - margin: 3rem !important; - } - .mt-xl-5, - .my-xl-5 { - margin-top: 3rem !important; - } - .mr-xl-5, - .mx-xl-5 { - margin-right: 3rem !important; - } - .mb-xl-5, - .my-xl-5 { - margin-bottom: 3rem !important; - } - .ml-xl-5, - .mx-xl-5 { - margin-left: 3rem !important; - } - .p-xl-0 { - padding: 0 !important; - } - .pt-xl-0, - .py-xl-0 { - padding-top: 0 !important; - } - .pr-xl-0, - .px-xl-0 { - padding-right: 0 !important; - } - .pb-xl-0, - .py-xl-0 { - padding-bottom: 0 !important; - } - .pl-xl-0, - .px-xl-0 { - padding-left: 0 !important; - } - .p-xl-1 { - padding: 0.25rem !important; - } - .pt-xl-1, - .py-xl-1 { - padding-top: 0.25rem !important; - } - .pr-xl-1, - .px-xl-1 { - padding-right: 0.25rem !important; - } - .pb-xl-1, - .py-xl-1 { - padding-bottom: 0.25rem !important; - } - .pl-xl-1, - .px-xl-1 { - padding-left: 0.25rem !important; - } - .p-xl-2 { - padding: 0.5rem !important; - } - .pt-xl-2, - .py-xl-2 { - padding-top: 0.5rem !important; - } - .pr-xl-2, - .px-xl-2 { - padding-right: 0.5rem !important; - } - .pb-xl-2, - .py-xl-2 { - padding-bottom: 0.5rem !important; - } - .pl-xl-2, - .px-xl-2 { - padding-left: 0.5rem !important; - } - .p-xl-3 { - padding: 1rem !important; - } - .pt-xl-3, - .py-xl-3 { - padding-top: 1rem !important; - } - .pr-xl-3, - .px-xl-3 { - padding-right: 1rem !important; - } - .pb-xl-3, - .py-xl-3 { - padding-bottom: 1rem !important; - } - .pl-xl-3, - .px-xl-3 { - padding-left: 1rem !important; - } - .p-xl-4 { - padding: 1.5rem !important; - } - .pt-xl-4, - .py-xl-4 { - padding-top: 1.5rem !important; - } - .pr-xl-4, - .px-xl-4 { - padding-right: 1.5rem !important; - } - .pb-xl-4, - .py-xl-4 { - padding-bottom: 1.5rem !important; - } - .pl-xl-4, - .px-xl-4 { - padding-left: 1.5rem !important; - } - .p-xl-5 { - padding: 3rem !important; - } - .pt-xl-5, - .py-xl-5 { - padding-top: 3rem !important; - } - .pr-xl-5, - .px-xl-5 { - padding-right: 3rem !important; - } - .pb-xl-5, - .py-xl-5 { - padding-bottom: 3rem !important; - } - .pl-xl-5, - .px-xl-5 { - padding-left: 3rem !important; - } - .m-xl-auto { - margin: auto !important; - } - .mt-xl-auto, - .my-xl-auto { - margin-top: auto !important; - } - .mr-xl-auto, - .mx-xl-auto { - margin-right: auto !important; - } - .mb-xl-auto, - .my-xl-auto { - margin-bottom: auto !important; - } - .ml-xl-auto, - .mx-xl-auto { - margin-left: auto !important; - } -} - -.text-monospace { - font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; -} - -.text-justify { - text-align: justify !important; -} - -.text-nowrap { - white-space: nowrap !important; -} - -.text-truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.text-left { - text-align: left !important; -} - -.text-right { - text-align: right !important; -} - -.text-center { - text-align: center !important; -} - -@media (min-width: 576px) { - .text-sm-left { - text-align: left !important; - } - .text-sm-right { - text-align: right !important; - } - .text-sm-center { - text-align: center !important; - } -} - -@media (min-width: 768px) { - .text-md-left { - text-align: left !important; - } - .text-md-right { - text-align: right !important; - } - .text-md-center { - text-align: center !important; - } -} - -@media (min-width: 992px) { - .text-lg-left { - text-align: left !important; - } - .text-lg-right { - text-align: right !important; - } - .text-lg-center { - text-align: center !important; - } -} - -@media (min-width: 1200px) { - .text-xl-left { - text-align: left !important; - } - .text-xl-right { - text-align: right !important; - } - .text-xl-center { - text-align: center !important; - } -} - -.text-lowercase { - text-transform: lowercase !important; -} - -.text-uppercase { - text-transform: uppercase !important; -} - -.text-capitalize { - text-transform: capitalize !important; -} - -.font-weight-light { - font-weight: 300 !important; -} - -.font-weight-normal { - font-weight: 400 !important; -} - -.font-weight-bold { - font-weight: 700 !important; -} - -.font-italic { - font-style: italic !important; -} - -.text-white { - color: #fff !important; -} - -.text-primary { - color: #007bff !important; -} - -a.text-primary:hover, a.text-primary:focus { - color: #0062cc !important; -} - -.text-secondary { - color: #6c757d !important; -} - -a.text-secondary:hover, a.text-secondary:focus { - color: #545b62 !important; -} - -.text-success { - color: #28a745 !important; -} - -a.text-success:hover, a.text-success:focus { - color: #1e7e34 !important; -} - -.text-info { - color: #17a2b8 !important; -} - -a.text-info:hover, a.text-info:focus { - color: #117a8b !important; -} - -.text-warning { - color: #ffc107 !important; -} - -a.text-warning:hover, a.text-warning:focus { - color: #d39e00 !important; -} - -.text-danger { - color: #dc3545 !important; -} - -a.text-danger:hover, a.text-danger:focus { - color: #bd2130 !important; -} - -.text-light { - color: #f8f9fa !important; -} - -a.text-light:hover, a.text-light:focus { - color: #dae0e5 !important; -} - -.text-dark { - color: #343a40 !important; -} - -a.text-dark:hover, a.text-dark:focus { - color: #1d2124 !important; -} - -.text-body { - color: #212529 !important; -} - -.text-muted { - color: #6c757d !important; -} - -.text-black-50 { - color: rgba(0, 0, 0, 0.5) !important; -} - -.text-white-50 { - color: rgba(255, 255, 255, 0.5) !important; -} - -.text-hide { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} - -.visible { - visibility: visible !important; -} - -.invisible { - visibility: hidden !important; -} - -@media print { - *, - *::before, - *::after { - text-shadow: none !important; - box-shadow: none !important; - } - a:not(.btn) { - text-decoration: underline; - } - abbr[title]::after { - content: " (" attr(title) ")"; - } - pre { - white-space: pre-wrap !important; - } - pre, - blockquote { - border: 1px solid #adb5bd; - page-break-inside: avoid; - } - thead { - display: table-header-group; - } - tr, - img { - page-break-inside: avoid; - } - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - h2, - h3 { - page-break-after: avoid; - } - @page { - size: a3; - } - body { - min-width: 992px !important; - } - .container { - min-width: 992px !important; - } - .navbar { - display: none; - } - .badge { - border: 1px solid #000; - } - .table { - border-collapse: collapse !important; - } - .table td, - .table th { - background-color: #fff !important; - } - .table-bordered th, - .table-bordered td { - border: 1px solid #dee2e6 !important; - } - .table-dark { - color: inherit; - } - .table-dark th, - .table-dark td, - .table-dark thead th, - .table-dark tbody + tbody { - border-color: #dee2e6; - } - .table .thead-dark th { - color: inherit; - border-color: #dee2e6; - } -} -/*# sourceMappingURL=bootstrap.css.map */ \ No newline at end of file diff --git a/htdocs/css/bootstrap.css.map b/htdocs/css/bootstrap.css.map deleted file mode 100644 index 28d6241..0000000 --- a/htdocs/css/bootstrap.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../scss/bootstrap.scss","../../scss/_root.scss","../../scss/_reboot.scss","../../scss/_variables.scss","bootstrap.css","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/mixins/_border-radius.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_breakpoints.scss","../../scss/mixins/_grid-framework.scss","../../scss/_tables.scss","../../scss/mixins/_table-row.scss","../../scss/_functions.scss","../../scss/_forms.scss","../../scss/mixins/_transition.scss","../../scss/mixins/_forms.scss","../../scss/mixins/_gradients.scss","../../scss/_buttons.scss","../../scss/mixins/_buttons.scss","../../scss/_transitions.scss","../../scss/_dropdown.scss","../../scss/mixins/_caret.scss","../../scss/mixins/_nav-divider.scss","../../scss/_button-group.scss","../../scss/_input-group.scss","../../scss/_custom-forms.scss","../../scss/_nav.scss","../../scss/_navbar.scss","../../scss/_card.scss","../../scss/_breadcrumb.scss","../../scss/_pagination.scss","../../scss/mixins/_pagination.scss","../../scss/_badge.scss","../../scss/mixins/_badge.scss","../../scss/_jumbotron.scss","../../scss/_alert.scss","../../scss/mixins/_alert.scss","../../scss/_progress.scss","../../scss/_media.scss","../../scss/_list-group.scss","../../scss/mixins/_list-group.scss","../../scss/_close.scss","../../scss/_modal.scss","../../scss/_tooltip.scss","../../scss/mixins/_reset-text.scss","../../scss/_popover.scss","../../scss/_carousel.scss","../../scss/utilities/_align.scss","../../scss/mixins/_background-variant.scss","../../scss/utilities/_background.scss","../../scss/utilities/_borders.scss","../../scss/mixins/_clearfix.scss","../../scss/utilities/_display.scss","../../scss/utilities/_embed.scss","../../scss/utilities/_flex.scss","../../scss/utilities/_float.scss","../../scss/mixins/_float.scss","../../scss/utilities/_position.scss","../../scss/utilities/_screenreaders.scss","../../scss/mixins/_screen-reader.scss","../../scss/utilities/_shadows.scss","../../scss/utilities/_sizing.scss","../../scss/utilities/_spacing.scss","../../scss/utilities/_text.scss","../../scss/mixins/_text-truncate.scss","../../scss/mixins/_text-emphasis.scss","../../scss/mixins/_text-hide.scss","../../scss/utilities/_visibility.scss","../../scss/mixins/_visibility.scss","../../scss/_print.scss"],"names":[],"mappings":"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B;;AAMD;EACE,sBAAqB;CACtB;;AAED;EACE,mBAAkB;EAClB,gBAAe;CAChB;;AAED;EACE,cAAa;CACd;;AEtHD;EF2HE,yBAAwB;CACzB;;AIzdD;;EAEE,sBHwPyC;EGvPzC,qBHwPmC;EGvPnC,iBHwP+B;EGvP/B,iBHwP+B;EGvP/B,eHwPmC;CGvPpC;;AAED;EAAU,kBH0OyC;CG1Ob;;AACtC;EAAU,gBH0OuC;CG1OX;;AACtC;EAAU,mBH0O0C;CG1Od;;AACtC;EAAU,kBH0OyC;CG1Ob;;AACtC;EAAU,mBH0O0C;CG1Od;;AACtC;EAAU,gBH0NwB;CG1NI;;AAEtC;EACE,mBH0PoD;EGzPpD,iBH0P+B;CGzPhC;;AAGD;EACE,gBHyOgC;EGxOhC,iBH6O+B;EG5O/B,iBHoO+B;CGnOhC;;AACD;EACE,kBHqOkC;EGpOlC,iBHyO+B;EGxO/B,iBH+N+B;CG9NhC;;AACD;EACE,kBHiOkC;EGhOlC,iBHqO+B;EGpO/B,iBH0N+B;CGzNhC;;AACD;EACE,kBH6NkC;EG5NlC,iBHiO+B;EGhO/B,iBHqN+B;CGpNhC;;AJmCD;EI3BE,iBH8DW;EG7DX,oBH6DW;EG5DX,UAAS;EACT,yCHrCa;CGsCd;;AAOD;;EAEE,eHgN+B;EG/M/B,iBH8K+B;CG7KhC;;AAED;;EAEE,eHoNgC;EGnNhC,0BH4NmC;CG3NpC;;AAOD;EC/EE,gBAAe;EACf,iBAAgB;CDgFjB;;AAGD;ECpFE,gBAAe;EACf,iBAAgB;CDqFjB;;AACD;EACE,sBAAqB;CAKtB;;AAND;EAII,qBHsM+B;CGrMhC;;AASH;EACE,eAAc;EACd,0BAAyB;CAC1B;;AAGD;EACE,oBHKW;EGJX,mBHwKoD;CGvKrD;;AAED;EACE,eAAc;EACd,eAAc;EACd,eHtGgB;CG2GjB;;AARD;EAMI,uBAAsB;CACvB;;AEpHH;ECIE,gBAAe;EAGf,aAAY;CDLb;;AAID;EACE,iBLs0BwC;EKr0BxC,uBLJa;EKKb,0BLFgB;EOVd,uBP8MgC;EMvMlC,gBAAe;EAGf,aAAY;CDQb;;AAMD;EAEE,sBAAqB;CACtB;;AAED;EACE,sBAA4B;EAC5B,eAAc;CA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FD;IFTR,wBAA8C;GEWrC;CXmxCV;;AY50CD;EACE,YAAW;EACX,gBAAe;EACf,oBb8GW;Ea7GX,8Bb2SuC;CatRxC;;AAzBD;;EAQI,iBboSgC;EanShC,oBAAmB;EACnB,8BbAc;CaCf;;AAXH;EAcI,uBAAsB;EACtB,iCbLc;CaMf;;AAhBH;EAmBI,8BbTc;CaUf;;AApBH;EAuBI,uBbhBW;CaiBZ;;AAQH;;EAGI,gBb0Q+B;CazQhC;;AAQH;EACE,0BbnCgB;CagDjB;;AAdD;;EAKI,0BbvCc;CawCf;;AANH;;EAWM,yBAA8C;CAC/C;;AAIL;;;;EAKI,UAAS;CACV;;AAOH;EAEI,sCb1DW;Ca2DZ;;AXpED;EW+EI,uCbtES;CETS;;AYPtB;;;EAII,0BC2E4D;CD1E7D;;AZEH;EYQM,0BAJsC;CZJtB;;AYGtB;;EASQ,0BARoC;CASrC;;AApBP;;;EAII,0BC2E4D;CD1E7D;;AZEH;EYQM,0BAJsC;CZJtB;;AYGtB;;EASQ,0BARoC;CASrC;;AApBP;;;EAII,0BC2E4D;CD1E7D;;AZEH;EYQM,0BAJsC;CZJtB;;AYGtB;;EASQ,0BARoC;CASrC;;AApBP;;;EAII,0BC2E4D;CD1E7D;;AZEH;EYQM,0BAJsC;CZJtB;;AYGtB;;EASQ,0BARoC;CASrC;;AApBP;;;EAII,0BC2E4D;CD1E7D;;AZEH;EYQM,0BAJsC;CZJtB;;AYGtB;;EASQ,0BARoC;CASrC;;AApBP;;;EAII,0BC2E4D;CD1E7D;;AZEH;EYQM,0BAJsC;CZJtB;;AYGtB;;EASQ,0BARoC;CASrC;;AApBP;;;EAII,0BC2E4D;CD1E7D;;AZEH;EYQM,0BAJsC;CZJtB;;AYGtB;;EASQ,0BARoC;CASrC;;AApBP;;;EAII,0BC2E4D;CD1E7D;;AZEH;EYQM,0BAJsC;CZJtB;;AYGtB;;EASQ,0BARoC;CASrC;;AApBP;;;EAII,uCdYS;CcXV;;AZEH;EYQM,uCAJsC;CZJtB;;AYGtB;;EASQ,uCARoC;CASrC;;ADyFT;EAGM,Yb1GS;Ea2GT,0BblGY;EamGZ,sBb0MgD;CazMjD;;AANL;EAWM,eb3GY;Ea4GZ,0BbjHY;EakHZ,sB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B;;AAIH;;EAEE,qBAAoB;CACrB;;AAQC;ECzDA,YrBKa;EmBLX,0BnB8Ba;EqB5Bf,sBrB4Be;CoB6Bd;;AlBrDD;EmBAE,YrBDW;EmBLX,0BEDoF;EASpF,sBATyH;CnBOrG;;AmBKtB;EAMI,gDrBaW;CqBXd;;AAGD;EAEE,YrBnBW;EqBoBX,0BrBKa;EqBJb,sBrBIa;CqBHd;;AAED;;EAGE,YrB3BW;EqB4BX,0BAlCuK;EAsCvK,sBAtC+M;CAgDhN;;AARC;;EAKI,gDrBdS;CqBgBZ;;ADWH;ECzDA,YrBKa;EmBLX,0BnBWc;EqBThB,sBrBSgB;CoBgDf;;AlBrDD;EmBAE,YrBDW;EmBLX,0BEDoF;EASpF,sBATyH;CnBOrG;;AmBKtB;EAMI,kDrBNY;CqBQf;;AAGD;EAEE,YrBnBW;EqBoBX,0BrBdc;EqBed,sBrBfc;CqBgBf;;AAED;;EAGE,YrB3BW;EqB4BX,0BAlCuK;EAsCvK,sBAtC+M;CAgDhN;;AARC;;EAKI,kDrBjCU;CqBmCb;;ADWH;ECzDA,YrBKa;EmBLX,0BnBqCa;EqBnCf,sBrBmCe;CoBsBd;;AlBrDD;EmBAE,YrBDW;EmBLX,0BEDoF;EASpF,sBATyH;CnBOrG;;AmBKtB;EAMI,gDrBoBW;CqBlBd;;AAGD;EAEE,YrBnBW;EqBoBX,0BrBYa;EqBXb,sBrBWa;CqBVd;;AAED;;EAGE,YrB3BW;EqB4BX,0BAlCuK;EAsCvK,sBAtC+M;CAgDhN;;AARC;;EAKI,gDrBPS;CqBSZ;;ADWH;ECzDA,YrBKa;EmBLX,0BnBuCa;EqBrCf,sBrBqCe;CoBoBd;;AlBrDD;EmBAE,YrBDW;EmBLX,0BEDoF;EASpF,sBATyH;CnBOrG;;AmBKtB;EAMI,iDrBsBW;CqBpBd;;AAGD;EAEE,YrBnBW;EqBoBX,0BrBca;EqBbb,sBrBaa;CqBZd;;AAED;;EAGE,YrB3BW;EqB4BX,0BAlCuK;EAsCvK,sBAtC+M;CAgDhN;;AARC;;EAKI,iDrBLS;CqBOZ;;ADWH;ECzDA,erBcgB;EmBdd,0BnBoCa;EqBlCf,sBrBkCe;CoBuBd;;AlBrDD;EmBAE,erBQc;EmBdd,0BEDoF;EASpF,sBATyH;CnBOrG;;AmBKtB;EAMI,gDrBmBW;CqBjBd;;AAGD;EAEE,erBVc;EqBWd,0BrBWa;EqBVb,sBrBUa;CqBTd;;AAED;;EAGE,erBlBc;EqBmBd,0BAlCuK;EAsCvK,sBAtC+M;CAgDhN;;AARC;;EAKI,gDrBRS;CqBUZ;;ADWH;ECzDA,YrBKa;EmBLX,0BnBkCa;EqBhCf,sBrBgCe;CoByBd;;AlBrDD;EmBAE,YrBDW;EmBLX,0BEDoF;EASpF,sBATyH;CnBOrG;;AmBKtB;EAMI,gDrBiBW;CqBfd;;AAGD;EAEE,YrBnBW;EqBoBX,0BrBSa;EqBRb,sBrBQa;CqBPd;;AAED;;EAGE,YrB3BW;EqB4BX,0BAlCuK;EAsCvK,sBAtC+M;CAgDhN;;AARC;;EAKI,gDrBVS;CqBYZ;;ADWH;ECzDA,erBcgB;EmBdd,0BnBMc;EqBJhB,sBrBIgB;CoBqDf;;AlBrDD;EmBAE,erBQc;EmBdd,0BEDoF;EASpF,sBATyH;CnBOrG;;AmBKtB;EAMI,kDrBXY;CqBaf;;AAGD;EAEE,erBVc;EqBWd,0BrBnBc;EqBoBd,sBrBpBc;CqBqBf;;AAED;;EAGE,erBlBc;EqBmBd,0BAlCuK;EAsCvK,sBAtC+M;CAgDhN;;AARC;;EAKI,kDrBtCU;CqBwCb;;ADWH;ECzDA,YrBKa;EmBLX,0BnBac;EqBXhB,sBrBWgB;CoB8Cf;;AlBrDD;EmBAE,YrBDW;EmBLX,0BEDoF;EASpF,sBATyH;CnBOrG;;AmBKtB;EAMI,+CrBJY;CqBMf;;AAGD;EAEE,YrBnBW;EqBoBX,0BrBZc;EqBad,sBrBbc;CqBcf;;AAED;;EAGE,YrB3BW;EqB4BX,0BAlCuK;EAsCvK,sBAtC+M;CAgDhN;;AARC;;EAKI,+CrB/BU;CqBiCb;;ADiBH;ECZA,erBrBe;EqBsBf,8BAA6B;EAC7B,uBAAsB;EACtB,sBrBxBe;CoBmCd;;ACTD;EACE,YrBpDW;EqBqDX,0BrB5Ba;EqB6Bb,sBrB7Ba;CqB8Bd;;AAED;EAEE,gDrBlCa;CqBmCd;;AAED;EAEE,erBvCa;EqBwCb,8BAA6B;CAC9B;;AAED;;EAGE,YrBvEW;EqBwEX,0BrB/Ca;EqBgDb,sBrBhDa;CqB0Dd;;AARC;;EAKI,gDrBvDS;CqByDZ;;ADxBH;ECZA,erBxCgB;EqByChB,8BAA6B;EAC7B,uBAAsB;EACtB,sBrB3CgB;CoBsDf;;ACTD;EACE,YrBpDW;EqBqDX,0BrB/Cc;EqBgDd,sBrBhDc;CqBiDf;;AAED;EAEE,kDrBrDc;CqBsDf;;AAED;EAEE,erB1Dc;EqB2Dd,8BAA6B;CAC9B;;AAED;;EAGE,YrBvEW;EqBwEX,0BrBlEc;EqBmEd,sBrBnEc;CqB6Ef;;AARC;;EAKI,kDrB1EU;CqB4Eb;;ADxBH;ECZA,erBde;EqBef,8BAA6B;EAC7B,uBAAsB;EACtB,sBrBjBe;CoB4Bd;;ACTD;EACE,YrBpDW;EqBqDX,0BrBrBa;EqBsBb,sBrBtBa;CqBuBd;;AAED;EAEE,gDrB3Ba;CqB4Bd;;AAED;EAEE,erBhCa;EqBiCb,8BAA6B;CAC9B;;AAED;;EAGE,YrBvEW;EqBwEX,0BrBxCa;EqByCb,sBrBzCa;CqBmDd;;AARC;;EAKI,gDrBhDS;CqBkDZ;;ADxBH;ECZA,erBZe;EqBaf,8BAA6B;EAC7B,uBAAsB;EACtB,sBrBfe;CoB0Bd;;ACTD;EACE,YrBpDW;EqBqDX,0BrBnBa;EqBoBb,sBrBpBa;CqBqBd;;AAED;EAEE,iDrBzBa;CqB0Bd;;AAED;EAEE,erB9Ba;EqB+Bb,8BAA6B;CAC9B;;AAED;;EAGE,YrBvEW;EqBwEX,0BrBtCa;EqBuCb,sBrBvCa;CqBiDd;;AARC;;EAKI,iDrB9CS;CqBgDZ;;ADxBH;ECZA,erBfe;EqBgBf,8BAA6B;EAC7B,uBAAsB;EACtB,sBrBlBe;CoB6Bd;;ACTD;EACE,erB3Cc;EqB4Cd,0BrBtBa;EqBuBb,sBrBvBa;CqBwBd;;AAED;EAEE,gDrB5Ba;CqB6Bd;;AAED;EAEE,erBjCa;EqBkCb,8BAA6B;CAC9B;;AAED;;EAGE,erB9Dc;EqB+Dd,0BrBzCa;EqB0Cb,sBrB1Ca;CqBoDd;;AARC;;EAKI,gDrBjDS;CqBmDZ;;ADxBH;ECZA,erBjBe;EqBkBf,8BAA6B;EAC7B,uBAAsB;EACtB,sBrBpBe;CoB+Bd;;ACTD;EACE,YrBpDW;EqBqDX,0BrBxBa;EqByBb,sBrBzBa;CqB0Bd;;AAED;EAEE,gDrB9Ba;CqB+Bd;;AAED;EAEE,erBnCa;EqBoCb,8BAA6B;CAC9B;;AAED;;EAGE,YrBvEW;EqBwEX,0BrB3Ca;EqB4Cb,sBrB5Ca;CqBsDd;;AARC;;EAKI,gDrBnDS;CqBqDZ;;ADxBH;ECZA,erB7CgB;EqB8ChB,8BAA6B;EAC7B,uBAAsB;EACtB,sBrBhDgB;CoB2Df;;ACTD;EACE,erB3Cc;EqB4Cd,0BrBpDc;EqBqDd,sBrBrDc;CqBsDf;;AAED;EAEE,kDrB1Dc;CqB2Df;;AAED;EAEE,erB/Dc;EqBgEd,8BAA6B;CAC9B;;AAED;;EAGE,erB9Dc;EqB+Dd,0BrBvEc;EqBwEd,sBrBxEc;CqBkFf;;AARC;;EAKI,kDrB/EU;CqBiFb;;ADxBH;ECZA,erBtCgB;EqBuChB,8BAA6B;EAC7B,uBAAsB;EACtB,sBrBzCgB;CoBoDf;;ACTD;EACE,YrBpDW;EqBqDX,0BrB7Cc;EqB8Cd,sBrB9Cc;CqB+Cf;;AAED;EAEE,+CrBnDc;CqBoDf;;AAED;EAEE,erBxDc;EqByDd,8BAA6B;CAC9B;;AAED;;EAGE,YrBvEW;EqBwEX,0BrBhEc;EqBiEd,sBrBjEc;CqB2Ef;;AARC;;EAKI,+CrBxEU;CqB0Eb;;ADbL;EACE,iBpBkK+B;EoBjK/B,epB9Ce;EoB+Cf,8BAA6B;CAuB9B;;AlB9FC;EkB0EE,epBkEgD;EoBjEhD,2BpBkEiC;EoBjEjC,8BAA6B;EAC7B,0BAAyB;ClB7EL;;AkBoExB;EAcI,2BpB2DiC;EoB1DjC,0BAAyB;EACzB,iBAAgB;CACjB;;AAjBH;EAqBI,epBpFc;EoBqFd,qBAAoB;CACrB;;AAUH;ECdE,qBrBsPgC;EqBrPhC,mBrB2IoD;EqB1IpD,iBrBuG+B;EqBpG7B,sBrB2G+B;CoBhGlC;;AAED;EClBE,wBrBkPiC;EqBjPjC,oBrB4IoD;EqB3IpD,iBrBwG+B;EqBrG7B,sBrB4G+B;CoB7FlC;;AAOD;EACE,eAAc;EACd,YAAW;CAMZ;;AARD;EAMI,mBpBsP+B;CoBrPhC;;AAIH;;;EAII,YAAW;CACZ;;AE5IH;ELGM,iCj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U;EACV,eAAc;EACd,gB5B2I2G;E4B1I3G,0B5B4CgC;E4B3ChC,iB5B3C6B;E4B4C7B,e5BjRc;E4BkRd,kBAAiB;ET9RjB,0BnBOc;E4ByRd,+B5BvRc;EOXd,mCqBmSgF;CACjF;;AASH;EACE,YAAW;EACX,gBAAe;EACf,8BAA6B;EAC7B,yBAAgB;EAAhB,sBAAgB;EAAhB,iBAAgB;CA+GjB;;AAnHD;EAOI,cAAa;CACd;;AARH;EAWI,UAAS;CACV;;AAZH;EAeI,Y5BsMsC;E4BrMtC,a5BqMsC;E4BpMtC,qBAA6C;ET5T7C,0BnB8Ba;E4BgSb,U5BqMmC;EOrgBnC,oBPsgBsC;E4BnMtC,yBAAgB;EAAhB,iBAAgB;CAUjB;;AAhCH;EAyBM,cAAa;EACb,iE5BvSW;C4BwSZ;;AA3BL;ET3SI,0BnBugBoE;C4B7LnE;;AA/BL;EAmCI,Y5B2KoC;E4B1KpC,e5B2KqC;E4B1KrC,mBAAkB;EAClB,gB5B0KuC;E4BzKvC,0B5B1Uc;E4B2Ud,0BAAyB;ErBrVzB,oBP+foC;C4BvKrC;;AA3CH;EA8CI,Y5BuKsC;E4BtKtC,a5BsKsC;EmBhgBtC,0BnB8Ba;E4B8Tb,U5BuKmC;EOrgBnC,oBPsgBsC;E4BrKtC,sBAAgB;EAAhB,iBAAgB;CAUjB;;AA9DH;EAuDM,cAAa;EACb,iE5BrUW;C4BsUZ;;AAzDL;ET3SI,0BnBugBoE;C4B/JnE;;AA7DL;EAiEI,Y5B6IoC;E4B5IpC,e5B6IqC;E4B5IrC,mBAAkB;EAClB,gB5B4IuC;E4B3IvC,0B5BxWc;E4ByWd,0BAAyB;ErBnXzB,oBP+foC;C4BzIrC;;AAzEH;EA4EI,Y5ByIsC;E4BxItC,a5BwIsC;EmBhgBtC,0BnB8Ba;E4B4Vb,U5ByImC;EOrgBnC,oBPsgBsC;E4BvItC,iBAAgB;CAUjB;;AA5FH;EAqFM,cAAa;EACb,iE5BnWW;C4BoWZ;;AAvFL;ET3SI,0BnBugBoE;C4BjInE;;AA3FL;EA+FI,Y5B+GoC;E4B9GpC,e5B+GqC;E4B9GrC,mBAAkB;EAClB,gB5B8GuC;E4B7GvC,8BAA6B;EAC7B,0BAAyB;EACzB,qBAA+C;CAEhD;;AAvGH;EA0GI,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Z;;A5B5LD;E4B0LI,0B9BrLS;CEFZ;;A4BkLH;EAWM,0B9B3LS;C8BoMV;;A5BzMH;E4BmMM,0B9B9LO;CEFZ;;A4BkLH;EAkBQ,0B9BlMO;C8BmMR;;AAnBP;;;;EA0BM,0B9B1MS;C8B2MV;;AA3BL;EA+BI,0B9B/MW;E8BgNX,iC9BhNW;C8BiNZ;;AAjCH;EAoCI,sQ9B8ZmS;C8B7ZpS;;AArCH;EAwCI,0B9BxNW;C8BgOZ;;AAhDH;EA0CM,0B9B1NS;C8B+NV;;A5BpOH;E4BkOM,0B9B7NO;CEFZ;;A4BsOH;EAEI,Y9BhPW;C8BqPZ;;A5BhPD;E4B8OI,Y9BnPS;CEQZ;;A4BsOH;EAWM,gC9BzPS;C8BkQV;;A5B7PH;E4BuPM,iC9B5PO;CEQZ;;A4BsOH;EAkBQ,iC9BhQO;C8BiQR;;AAnBP;;;;EA0BM,Y9BxQS;C8ByQV;;AA3BL;EA+BI,gC9B7QW;E8B8QX,uC9B9QW;C8B+QZ;;AAjCH;EAoCI,4Q9BmWkS;C8BlWnS;;AArCH;EAwCI,gC9BtRW;C8B8RZ;;AAhDH;EA0CM,Y9BxRS;C8B6RV;;A5BxRH;E4BsRM,Y9B3RO;CEQZ;;A6BfH;EACE,mBAAkB;EAClB,qBAAa;EAAb,cAAa;EACb,2BAAsB;EAAtB,uBAAsB;EACtB,aAAY;EACZ,sBAAqB;EACrB,uB/BCa;E+BAb,4BAA2B;EAC3B,uC/BSa;EOjBX,uBP8MgC;C+BnLnC;;AA3BD;EAYI,gBAAe;EACf,eAAc;CACf;;AAdH;ExBMI,gCPwMgC;EOvMhC,iCPuMgC;C+B3L/B;;AAnBL;ExBoBI,oCP0LgC;EOzLhC,mCPyLgC;C+BrL/B;;AAIL;EAGE,mBAAc;EAAd,eAAc;EACd,iB/B6oByC;C+B5oB1C;;AAED;EACE,uB/BwoBwC;C+BvoBzC;;AAED;EACE,sBAAgC;EAChC,iBAAgB;CACjB;;AAED;EACE,iBAAgB;CACjB;;A7BvCC;E6B2CE,sBAAqB;C7B3CD;;A6ByCxB;EAMI,qB/BunBuC;C+BtnBxC;;AAOH;EACE,yB/B8mByC;E+B7mBzC,iBAAgB;EAChB,sC/BjDa;E+BkDb,8C/BlDa;C+B6Dd;;AAfD;ExB/DI,2DwBsE8E;CAC/E;;AARH;EAYM,cAAa;CACd;;AAIL;EACE,yB/B6lByC;E+B5lBzC,sC/BjEa;E+BkEb,2C/BlEa;C+BuEd;;AARD;ExBhFI,2DPkrBoF;C+B3lBrF;;AAQH;EACE,wBAAkC;EAClC,wB/B4kBwC;E+B3kBxC,uBAAiC;EACjC,iBAAgB;CACjB;;AAED;EACE,wBAAkC;EAClC,uBAAiC;CAClC;;AAGD;EACE,mBAAkB;EAClB,OAAM;EACN,SAAQ;EACR,UAAS;EACT,QAAO;EACP,iB/BokByC;C+BnkB1C;;AAED;EACE,YAAW;ExBtHT,mCPkrBoF;C+B1jBvF;;AAGD;EACE,YAAW;ExBtHT,4CP4qBoF;EO3qBpF,6CP2qBoF;C+BpjBvF;;AAED;EACE,YAAW;ExB7GT,gDP8pBoF;EO7pBpF,+CP6pBoF;C+B/iBvF;;AAKD;EACE,qBAAa;EAAb,cAAa;EACb,2BAAsB;EAAtB,uBAAsB;CAqBvB;;AAvBD;EAKI,oB/B2iBwD;C+B1iBzD;;ApBtFC;EoBgFJ;IASI,wBAAmB;IAAnB,oBAAmB;IACnB,oB/BsiBwD;I+BriBxD,mB/BqiBwD;G+BzhB3D;EAvBD;IAcM,qBAAa;IAAb,cAAa;IAEb,iBAAY;IAAZ,aAAY;IACZ,2BAAsB;IAAtB,uBAAsB;IACtB,mB/B8hBsD;I+B7hBtD,iBAAgB;IAChB,kB/B4hBsD;G+B3hBvD;C9Bw8HJ;;A8B/7HD;EACE,qBAAa;EAAb,cAAa;EACb,2BAAsB;EAAtB,uBAAsB;CA4EvB;;AA9ED;EAOI,oB/B2gBwD;C+B1gBzD;;ApBtHC;EoB8GJ;IAWI,wBAAmB;IAAnB,oBAAmB;GAmEtB;EA9ED;IAgBM,iBAAY;IAAZ,aAAY;IACZ,iBAAgB;GA2DjB;EA5EL;IAoBQ,eAAc;IACd,eAAc;GACf;EAtBP;IxBzJI,2BwBoLoC;IxBnLpC,8BwBmLoC;GAU/B;EArCT;;IA+BY,2BAA0B;GAC3B;EAhCX;;IAmCY,8BAA6B;GAC9B;EApCX;IxB3II,0BwBmLmC;IxBlLnC,6BwBkLmC;GAU9B;EAlDT;;IA4CY,0BAAyB;GAC1B;EA7CX;;IAgDY,6BAA4B;GAC7B;EAjDX;IxBtKI,uBP8MgC;G+BuB3B;EA/DT;;IxBhKI,gCPwMgC;IOvMhC,iCPuMgC;G+BkBzB;EA1DX;;IxBlJI,oCP0LgC;IOzLhC,mCPyLgC;G+BsBzB;EA9DX;IxBtKI,iBwBwO8B;GAQzB;EA1ET;;;;IxBtKI,iBwB8OgC;GACzB;C9B27HV;;A8B/6HD;EAEI,uB/BgbsC;C+B/avC;;ApBtMC;EoBmMJ;IAMI,wB/B0biC;I+B1bjC,qB/B0biC;I+B1bjC,gB/B0biC;I+BzbjC,4B/B0buC;I+B1bvC,yB/B0buC;I+B1bvC,oB/B0buC;I+BzbvC,WAAU;IACV,UAAS;GAOZ;EAhBD;IAYM,sBAAqB;IACrB,YAAW;GACZ;C9Bk7HJ;;A8Bz6HD;EAEI,iBAAgB;EAChB,iBAAgB;CACjB;;AAJH;EAQM,iBAAgB;CACjB;;AATL;EAaI,iBAAgB;EAChB,8BAA6B;EAC7B,6BAA4B;CAC7B;;AAhBH;EAmBI,0BAAyB;EACzB,2BAA0B;CAC3B;;AC3SH;EACE,qBAAa;EAAb,cAAa;EACb,oBAAe;EAAf,gBAAe;EACf,sBhCk2BsC;EgCj2BtC,oBhCo2BsC;EgCn2BtC,iBAAgB;EAChB,0BhCOgB;EOTd,uBP8MgC;CgC1MnC;;AAED;EAGI,qBhCy1BqC;CgCj1BtC;;AAXH;EAMM,sBAAqB;EACrB,sBhCq1BmC;EgCp1BnC,ehCDY;EgCEZ,ahC01BuC;CgCz1BxC;;AAVL;EAoBI,2BAA0B;CAC3B;;AArBH;EAwBI,sBAAqB;CACtB;;AAzBH;EA4BI,ehCrBc;CgCsBf;;ACvCH;EACE,qBAAa;EAAb,cAAa;E7BGb,gBAAe;EACf,iBAAgB;EGDd,uBP8MgC;CiC9MnC;;AAED;EACE,mBAAkB;EAClB,eAAc;EACd,wBjCooBwC;EiCnoBxC,kBjCqM+B;EiCpM/B,kBjCuoBsC;EiCtoBtC,ejCwBe;EiCvBf,uBjCFa;EiCGb,0BjCAgB;CiCoBjB;;AA5BD;EAWI,WAAU;EACV,ejCsIgD;EiCrIhD,sBAAqB;EACrB,0BjCPc;EiCQd,sBjCPc;CiCQf;;AAhBH;EAmBI,WAAU;EACV,WjCgoBiC;EiC/nBjC,iDjCSa;CiCRd;;AAtBH;EA0BI,gBAAe;CAChB;;AAGH;EAGM,eAAc;E1BRhB,gCPmLgC;EOlLhC,mCPkLgC;CiCzK/B;;AALL;E1BnBI,iCPiMgC;EOhMhC,oCPgMgC;CiCpK/B;;AAVL;EAcI,WAAU;EACV,YjCxCW;EiCyCX,0BjChBa;EiCiBb,sBjCjBa;CiCkBd;;AAlBH;EAqBI,ejCxCc;EiCyCd,qBAAoB;EAEpB,aAAY;EACZ,uBjClDW;EiCmDX,sBjChDc;CiCiDf;;AC5DD;EACE,wBlC6oBsC;EkC5oBtC,mBlC0OkD;EkCzOlD,iBlCsM6B;CkCrM9B;;AAIG;E3BoBF,+BPoL+B;EOnL/B,kCPmL+B;CkCtM5B;;AAGD;E3BCF,gCPkM+B;EOjM/B,mCPiM+B;CkCjM5B;;AAfL;EACE,wBlC2oBqC;EkC1oBrC,oBlC2OkD;EkC1OlD,iBlCuM6B;CkCtM9B;;AAIG;E3BoBF,+BPqL+B;EOpL/B,kCPoL+B;CkCvM5B;;AAGD;E3BCF,gCPmM+B;EOlM/B,mCPkM+B;CkClM5B;;ACbP;EACE,sBAAqB;EACrB,sBnC6uBsC;EmC5uBtC,enCyuBqC;EmCxuBrC,iBnC2O+B;EmC1O/B,eAAc;EACd,mBAAkB;EAClB,oBAAmB;EACnB,yBAAwB;E5BTtB,uBP8MgC;CmC9LnC;;AAfD;EAaI,cAAa;CACd;;AAIH;EACE,mBAAkB;EAClB,UAAS;CACV;;AAMD;EACE,qBnCstBsC;EmCrtBtC,oBnCqtBsC;EOnvBpC,qBPsvBqC;CmCttBxC;;AAOC;EC1CA,YpCUa;EoCTb,0BpCkCe;CmCSd;;AjC7BD;EkCVI,YpCKS;EoCJT,sBAAqB;EACrB,0BAAkC;ClCWrC;;AiCwBD;EC1CA,YpCUa;EoCTb,0BpCegB;CmC4Bf;;AjC7BD;EkCVI,YpCKS;EoCJT,sBAAqB;EACrB,0BAAkC;ClCWrC;;AiCwBD;EC1CA,YpCUa;EoCTb,0BpCyCe;CmCEd;;AjC7BD;EkCVI,YpCKS;EoCJT,sBAAqB;EACrB,0BAAkC;ClCWrC;;AiCwBD;EC1CA,YpCUa;EoCTb,0BpC2Ce;CmCAd;;AjC7BD;EkCVI,YpCKS;EoCJT,sBAAqB;EACrB,0BAAkC;ClCWrC;;AiCwBD;EC1CA,epCmBgB;EoClBhB,0BpCwCe;CmCGd;;AjC7BD;EkCVI,epCcY;EoCbZ,sBAAqB;EACrB,0BAAkC;ClCWrC;;AiCwBD;EC1CA,YpCUa;EoCTb,0BpCsCe;CmCKd;;AjC7BD;EkCVI,YpCKS;EoCJT,sBAAqB;EACrB,0BAAkC;ClCWrC;;AiCwBD;EC1CA,epCmBgB;EoClBhB,0BpCUgB;CmCiCf;;AjC7BD;EkCVI,epCcY;EoCbZ,sBAAqB;EACrB,0BAAkC;ClCWrC;;AiCwBD;EC1CA,YpCUa;EoCTb,0BpCiBgB;CmC0Bf;;AjC7BD;EkCVI,YpCKS;EoCJT,sBAAqB;EACrB,0BAAkC;ClCWrC;;AmCnBH;EACE,mBAAoD;EACpD,oBrCyqBsC;EqCxqBtC,0BrCUgB;EOTd,sBP+M+B;CqC1MlC;;A1BmDG;E0B5DJ;IAOI,mBrCoqBoC;GqClqBvC;CpCy8IA;;AoCv8ID;EACE,iBAAgB;EAChB,gBAAe;E9BTb,iB8BUsB;CACzB;;ACXD;EACE,mBAAkB;EAClB,yBtC4xByC;EsC3xBzC,oBtC4xBsC;EsC3xBtC,8BAA6C;E/BJ3C,uBP8MgC;CsCxMnC;;AAGD;EAEE,eAAc;CACf;;AAGD;EACE,iBtCgO+B;CsC/NhC;;AAOD;EACE,oBAAwD;CAUzD;;AAXD;EAKI,mBAAkB;EAClB,OAAM;EACN,SAAQ;EACR,yBtC8vBuC;EsC7vBvC,eAAc;CACf;;AASD;EC9CA,exBmFgE;EI9E9D,0BJ8E8D;EwBjFhE,sBxBiFgE;CuBnC/D;;AC5CD;EACE,0BAAqC;CACtC;;AAED;EACE,eAA0B;CAC3B;;ADoCD;EC9CA,exBmFgE;EI9E9D,0BJ8E8D;EwBjFhE,sBxBiFgE;CuBnC/D;;AC5CD;EACE,0BAAqC;CACtC;;AAED;EACE,eAA0B;CAC3B;;ADoCD;EC9CA,exBmFgE;EI9E9D,0BJ8E8D;EwBjFhE,sBxBiFgE;CuBnC/D;;AC5CD;EACE,0BAAqC;CACtC;;AAED;EACE,eAA0B;CAC3B;;ADoCD;EC9CA,exBmFgE;EI9E9D,0BJ8E8D;EwBjFhE,sBxBiFgE;CuBnC/D;;AC5CD;EACE,0BAAqC;CACtC;;AAED;EACE,eAA0B;CAC3B;;ADoCD;EC9CA,exBmFgE;EI9E9D,0BJ8E8D;EwBjFhE,sBxBiFgE;CuBnC/D;;AC5CD;EACE,0BAAqC;CACtC;;AAED;EACE,eAA0B;CAC3B;;ADoCD;EC9CA,exBmFgE;EI9E9D,0BJ8E8D;EwBjFhE,sBxBiFgE;CuBnC/D;;AC5CD;EACE,0BAAqC;CACtC;;AAED;EACE,eAA0B;CAC3B;;ADoCD;EC9CA,exBmFgE;EI9E9D,0BJ8E8D;EwBjFhE,sBxBiFgE;CuBnC/D;;AC5CD;EACE,0BAAqC;CACtC;;AAED;EACE,eAA0B;CAC3B;;ADoCD;EC9CA,exBmFgE;EI9E9D,0BJ8E8D;EwBjFhE,sBxBiFgE;CuBnC/D;;AC5CD;EACE,0BAAqC;CACtC;;AAED;EACE,eAA0B;CAC3B;;ACXH;EACE;IAAO,4BAAuC;GvCwmJ7C;EuCvmJD;IAAK,yBAAwB;GvC0mJ5B;CACF;;AuC7mJD;EACE;IAAO,4BAAuC;GvCwmJ7C;EuCvmJD;IAAK,yBAAwB;GvC0mJ5B;CACF;;AuCxmJD;EACE,qBAAa;EAAb,cAAa;EACb,axCwyBsC;EwCvyBtC,iBAAgB;EAChB,mBxCuyByD;EwCtyBzD,0BxCGgB;EOTd,uBP8MgC;CwCrMnC;;AAED;EACE,qBAAa;EAAb,cAAa;EACb,2BAAsB;EAAtB,uBAAsB;EACtB,sBAAuB;EAAvB,wBAAuB;EACvB,YxCRa;EwCSb,mBAAkB;EAClB,oBAAmB;EACnB,0BxCce;EiB/BX,4BjBkzB4C;CwC/xBjD;;AvBfC;EuBMF;IvBLI,iBAAgB;GuBcnB;CvC+mJA;;AuC7mJD;ErBiBE,sMAA6I;EqBf7I,2BxCmxBsC;CwClxBvC;;AAED;EACE,2DxCsxBoD;EwCtxBpD,mDxCsxBoD;CwCrxBrD;;ACjCD;EACE,qBAAa;EAAb,cAAa;EACb,sBAAuB;EAAvB,wBAAuB;CACxB;;AAED;EACE,YAAO;EAAP,QAAO;CACR;;ACHD;EACE,qBAAa;EAAb,cAAa;EACb,2BAAsB;EAAtB,uBAAsB;EAGtB,gBAAe;EACf,iBAAgB;CACjB;;AAQD;EACE,YAAW;EACX,e1CHgB;E0CIhB,oBAAmB;CAapB;;AxCnBC;EwCUE,e1CRc;E0CSd,sBAAqB;EACrB,0B1ChBc;CEOf;;AwCAH;EAaI,e1CZc;E0Cad,0B1CpBc;C0CqBf;;AAQH;EACE,mBAAkB;EAClB,eAAc;EACd,yB1CoxByC;E0ClxBzC,oB1CgK+B;E0C/J/B,uB1CrCa;E0CsCb,uC1C5Ba;C0CyDd;;AApCD;EnChCI,gCPwMgC;EOvMhC,iCPuMgC;C0C7JjC;;AAXH;EAcI,iBAAgB;EnChChB,oCP0LgC;EOzLhC,mCPyLgC;C0CxJjC;;AxC1CD;EwC6CE,WAAU;EACV,sBAAqB;CxC3CtB;;AwCuBH;EAyBI,e1ClDc;E0CmDd,uB1CzDW;C0C0DZ;;AA3BH;EA+BI,WAAU;EACV,Y1C/DW;E0CgEX,0B1CvCa;E0CwCb,sB1CxCa;C0CyCd;;AASH;EAEI,gBAAe;EACf,eAAc;EnCrFd,iBmCsFwB;CACzB;;AALH;EASM,cAAa;CACd;;AAVL;EAeM,iBAAgB;CACjB;;ACnGH;EACE,e5BgF8D;E4B/E9D,0B5B+E8D;C4BjE/D;;AzCHD;EyCPM,e5B2E0D;E4B1E1D,0BAAyC;CzCS9C;;AyChBD;EAWM,Y3CHO;E2CIP,0B5BqE0D;E4BpE1D,sB5BoE0D;C4BnE3D;;AAdL;EACE,e5BgF8D;E4B/E9D,0B5B+E8D;C4BjE/D;;AzCHD;EyCPM,e5B2E0D;E4B1E1D,0BAAyC;CzCS9C;;AyChBD;EAWM,Y3CHO;E2CIP,0B5BqE0D;E4BpE1D,sB5BoE0D;C4BnE3D;;AAdL;EACE,e5BgF8D;E4B/E9D,0B5B+E8D;C4BjE/D;;AzCHD;EyCPM,e5B2E0D;E4B1E1D,0BAAyC;CzCS9C;;AyChBD;EAWM,Y3CHO;E2CIP,0B5BqE0D;E4BpE1D,sB5BoE0D;C4BnE3D;;AAdL;EACE,e5BgF8D;E4B/E9D,0B5B+E8D;C4BjE/D;;AzCHD;EyCPM,e5B2E0D;E4B1E1D,0BAAyC;CzCS9C;;AyChBD;EAWM,Y3CHO;E2CIP,0B5BqE0D;E4BpE1D,sB5BoE0D;C4BnE3D;;AAdL;EACE,e5BgF8D;E4B/E9D,0B5B+E8D;C4BjE/D;;AzCHD;EyCPM,e5B2E0D;E4B1E1D,0BAAyC;CzCS9C;;AyChBD;EAWM,Y3CHO;E2CIP,0B5BqE0D;E4BpE1D,sB5BoE0D;C4BnE3D;;AAdL;EACE,e5BgF8D;E4B/E9D,0B5B+E8D;C4BjE/D;;AzCHD;EyCPM,e5B2E0D;E4B1E1D,0BAAyC;CzCS9C;;AyChBD;EAWM,Y3CHO;E2CIP,0B5BqE0D;E4BpE1D,sB5BoE0D;C4BnE3D;;AAdL;EACE,e5BgF8D;E4B/E9D,0B5B+E8D;C4BjE/D;;AzCHD;EyCPM,e5B2E0D;E4B1E1D,0BAAyC;CzCS9C;;AyChBD;EAWM,Y3CHO;E2CIP,0B5BqE0D;E4BpE1D,sB5BoE0D;C4BnE3D;;AAdL;EACE,e5BgF8D;E4B/E9D,0B5B+E8D;C4BjE/D;;AzCHD;EyCPM,e5B2E0D;E4B1E1D,0BAAyC;CzCS9C;;AyChBD;EAWM,Y3CHO;E2CIP,0B5BqE0D;E4BpE1D,sB5BoE0D;C4BnE3D;;ACjBP;EACE,aAAY;EACZ,kB5Cw4BuD;E4Cv4BvD,iB5CiP+B;E4ChP/B,eAAc;EACd,Y5CgBa;E4Cfb,0B5CKa;E4CJb,YAAW;CAYZ;;A1CHC;E0CNE,Y5CWW;E4CVX,sBAAqB;EACrB,aAAY;C1COb;;A0CnBH;EAiBI,gBAAe;CAChB;;AASH;EACE,WAAU;EACV,8BAA6B;EAC7B,UAAS;EACT,yBAAwB;CACzB;;ACzBD;EACE,iBAAgB;CACjB;;AAGD;EACE,gBAAe;EACf,OAAM;EACN,SAAQ;EACR,UAAS;EACT,QAAO;EACP,c7C+jBsC;E6C9jBtC,cAAa;EACb,iBAAgB;EAGhB,WAAU;CASX;;AAJC;EACE,mBAAkB;EAClB,iBAAgB;CACjB;;AAIH;EACE,mBAAkB;EAClB,YAAW;EACX,e7C4tBuC;E6C1tBvC,qBAAoB;CAUrB;;AAPC;E5BtCI,4CjBqxBoD;EiBrxBpD,oCjBqxBoD;EiBrxBpD,qEjBqxBoD;E6C7uBtD,sCAA6B;EAA7B,8BAA6B;CAC9B;;A5BrCD;E4BkCA;I5BjCE,iBAAgB;G4BoCjB;C5Cw3JF;;A4Cv3JC;EACE,mCAA0B;EAA1B,2BAA0B;CAC3B;;AAGH;EAC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","file":"bootstrap.css","sourcesContent":["/*!\n * Bootstrap v4.1.1 (https://getbootstrap.com/)\n * Copyright 2011-2018 The Bootstrap Authors\n * Copyright 2011-2018 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n@import \"functions\";\n@import \"variables\";\n@import \"mixins\";\n@import \"root\";\n@import \"reboot\";\n@import \"type\";\n@import \"images\";\n@import \"code\";\n@import \"grid\";\n@import \"tables\";\n@import \"forms\";\n@import \"buttons\";\n@import \"transitions\";\n@import \"dropdown\";\n@import \"button-group\";\n@import \"input-group\";\n@import \"custom-forms\";\n@import \"nav\";\n@import \"navbar\";\n@import \"card\";\n@import \"breadcrumb\";\n@import \"pagination\";\n@import \"badge\";\n@import \"jumbotron\";\n@import \"alert\";\n@import \"progress\";\n@import \"media\";\n@import \"list-group\";\n@import \"close\";\n@import \"modal\";\n@import \"tooltip\";\n@import \"popover\";\n@import \"carousel\";\n@import \"utilities\";\n@import \"print\";\n",":root {\n // Custom variable values only support SassScript inside `#{}`.\n @each $color, $value in $colors {\n --#{$color}: #{$value};\n }\n\n @each $color, $value in $theme-colors {\n --#{$color}: #{$value};\n }\n\n @each $bp, $value in $grid-breakpoints {\n --breakpoint-#{$bp}: #{$value};\n }\n\n // Use `inspect` for lists so that quoted items keep the quotes.\n // See https://github.com/sass/sass/issues/2383#issuecomment-336349172\n --font-family-sans-serif: #{inspect($font-family-sans-serif)};\n --font-family-monospace: #{inspect($font-family-monospace)};\n}\n","// stylelint-disable at-rule-no-vendor-prefix, declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix\n\n// Reboot\n//\n// Normalization of HTML elements, manually forked from Normalize.css to remove\n// styles targeting irrelevant browsers while applying new styles.\n//\n// Normalize is licensed MIT. https://github.com/necolas/normalize.css\n\n\n// Document\n//\n// 1. Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.\n// 2. Change the default font family in all browsers.\n// 3. Correct the line height in all browsers.\n// 4. Prevent adjustments of font size after orientation changes in IE on Windows Phone and in iOS.\n// 5. Setting @viewport causes scrollbars to overlap content in IE11 and Edge, so\n// we force a non-overlapping, non-auto-hiding scrollbar to counteract.\n// 6. Change the default tap highlight to be completely transparent in iOS.\n\n*,\n*::before,\n*::after {\n box-sizing: border-box; // 1\n}\n\nhtml {\n font-family: sans-serif; // 2\n line-height: 1.15; // 3\n -webkit-text-size-adjust: 100%; // 4\n -ms-text-size-adjust: 100%; // 4\n -ms-overflow-style: scrollbar; // 5\n -webkit-tap-highlight-color: rgba($black, 0); // 6\n}\n\n// IE10+ doesn't honor `` in some cases.\n@at-root {\n @-ms-viewport {\n width: device-width;\n }\n}\n\n// stylelint-disable selector-list-comma-newline-after\n// Shim for \"new\" HTML5 structural elements to display correctly (IE10, older browsers)\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n// stylelint-enable selector-list-comma-newline-after\n\n// Body\n//\n// 1. Remove the margin in all browsers.\n// 2. As a best practice, apply a default `background-color`.\n// 3. Set an explicit initial text-align value so that we can later use the\n// the `inherit` value on things like `";c.append(d);$("#card"+b+" > img").on({click:function(){sendAPI({cmd:"MPD_API_ADD_TRACK",data:{track:decodeURI($(this).attr("uri"))}});showNotification('"'+decodeURI($(this).attr("data-album"))+'" added',"","","success")}});$("#tbl"+b+" > tbody > tr").on({click:function(){sendAPI({cmd:"MPD_API_ADD_TRACK",data:{track:decodeURI($(this).attr("uri"))}});showNotification('"'+$("td:nth-last-child(1)",this).text()+'" added',"","","success")}})}} +function setPagination(a){var b=Math.ceil(a/settings.max_elements_per_page),c=app.current.app+(void 0==app.current.tab?"":app.current.tab);0==b&&(b=1);$("#"+c+"PaginationTopPage").text("Page "+(app.current.page/settings.max_elements_per_page+1)+" / "+b);$("#"+c+"PaginationBottomPage").text("Page "+(app.current.page/settings.max_elements_per_page+1)+" / "+b);if(1'+(d+1)+""),$("#"+c+"PaginationBottomPages").append('")}else $("#"+c+"PaginationTopPage").addClass("disabled").attr("disabled", +"disabled"),$("#"+c+"PaginationBottomPage").addClass("disabled").attr("disabled","disabled");a>app.current.page+settings.max_elements_per_page?($("#"+c+"PaginationTopNext").removeClass("disabled").removeAttr("disabled"),$("#"+c+"PaginationBottomNext").removeClass("disabled").removeAttr("disabled"),$("#"+c+"ButtonsBottom").removeClass("hide")):($("#"+c+"PaginationTopNext").addClass("disabled").attr("disabled","disabled"),$("#"+c+"PaginationBottomNext").addClass("disabled").attr("disabled","disabled"), +$("#"+c+"ButtonsBottom").addClass("hide"));0a?$("#volume-icon").text("volume_down"):$("#volume-icon").text("volume_up"))} +function updatePlayIcon(a){1==a.data.state?($("#btnPlay > span").text("play_arrow"),playstate="stop"):2==a.data.state?($("#btnPlay > span").text("pause"),playstate="play"):($("#btnPlay > span").text("play_arrow"),playstate="pause");-1==a.data.nextsongpos?$("#btnNext").addClass("disabled").attr("disabled","disabled"):$("#btnNext").removeClass("disabled").removeAttr("disabled");0>=a.data.songpos?$("#btnPrev").addClass("disabled").attr("disabled","disabled"):$("#btnPrev").removeClass("disabled").removeAttr("disabled"); +0==a.data.queue_length?$("#btnPlay").addClass("disabled").attr("disabled","disabled"):$("#btnPlay").removeClass("disabled").removeAttr("disabled")}function sendAPI(a,b){$.ajax({url:"/api",contentType:"application/json",method:"POST",data:JSON.stringify(a),success:b})}function updateDB(a){sendAPI({cmd:"MPD_API_UPDATE_DB"});showNotification("Updating MPD Database...","","","success");a.preventDefault()} +function clickPlay(){"play"!=playstate?sendAPI({cmd:"MPD_API_SET_PLAY"}):sendAPI({cmd:"MPD_API_SET_PAUSE"})}function clickStop(){sendAPI({cmd:"MPD_API_SET_STOP"})}function clickPrev(){sendAPI({cmd:"MPD_API_SET_PREV"})}function clickNext(){sendAPI({cmd:"MPD_API_SET_NEXT"})}function setLocalStream(a,b){var c="http://";c="127.0.0.1"==a||"localhost"==a?c+window.location.hostname:c+a;settings.mpdstream=c+(":"+b+"/")} +function delQueueSong(a,b){b.stopPropagation();$("#btntrashmodeup").hasClass("btn-success")?sendAPI({cmd:"MPD_API_RM_RANGE",data:{start:0,end:a.index()+1}}):$("#btntrashmodesingle").hasClass("btn-success")?sendAPI({cmd:"MPD_API_RM_TRACK",data:{track:a.attr("trackid")}}):$("#btntrashmodedown").hasClass("btn-success")&&sendAPI({cmd:"MPD_API_RM_RANGE",data:{start:a.index(),end:-1}})}function delPlaylist(a){sendAPI({cmd:"MPD_API_RM_PLAYLIST",data:{plist:decodeURI(a.attr("uri"))}});a.remove()} +function basename(a){return a.split("/").reverse()[0]}function toggleBtn(a){$(a).hasClass("btn-success")?$(a).removeClass("btn-success").addClass("btn-secondary"):$(a).removeClass("btn-secondary").addClass("btn-success")}$("#btnrandom").on("click",function(a){toggleBtn(this)});$("#btnconsume").on("click",function(a){toggleBtn(this)});$("#btnsingle").on("click",function(a){toggleBtn(this)});$("#btnrepeat").on("click",function(a){toggleBtn(this)});$("#btnnotifyPage").on("click",function(a){toggleBtn(this)}); +$("#btnnotifyWeb").on("click",function(a){toggleBtn(this)});$("#cardBrowseNavFilesystem").on("click",function(a){app.goto("Browse","Filesystem");a.preventDefault()});$("#cardBrowseNavDatabase").on("click",function(a){app.goto("Browse","Database");a.preventDefault()});$("#btnBrowseDatabaseArtist").on("click",function(a){app.goto("Browse","Database","Artist");a.preventDefault()});$("#cardBrowseNavPlaylists").on("click",function(a){app.goto("Browse","Playlists");a.preventDefault()}); +$("#cardBrowseNavFilesystem").on("click",function(a){app.goto("Browse","Filesystem");a.preventDefault()});$("#navPlayback").on("click",function(a){app.goto("Playback");a.preventDefault()});$("#navQueue").on("click",function(a){app.goto("Queue");a.preventDefault()});$("#navBrowse").on("click",function(a){app.goto("Browse");a.preventDefault()});$("#navSearch").on("click",function(a){app.goto("Search");a.preventDefault()}); +function confirmSettings(){var a=!0;if(!$("#inputCrossfade").is(":disabled")){var b=parseInt($("#inputCrossfade").val());isNaN(b)?($("#inputCrossfade").popover({content:"Must be a number",trigger:"manual"}),$("#inputCrossfade").popover("show"),$("#inputCrossfade").focus(),a=!1):$("#inputCrossfade").val(b)}$("#inputMixrampdb").is(":disabled")||(b=parseFloat($("#inputMixrampdb").val()),isNaN(b)?($("#inputMixrampdb").popover({content:"Must be a number",trigger:"manual"}),$("#inputMixrampdb").popover("show"), +$("#inputMixrampdb").focus(),a=!1):$("#inputMixrampdb").val(b));$("#inputMixrampdelay").is(":disabled")||("nan"==$("#inputMixrampdelay").val()&&$("#inputMixrampdelay").val("-1"),b=parseFloat($("#inputMixrampdelay").val()),isNaN(b)?($("#inputMixrampdelay").popover({content:"Must be a number, -1 to disable",trigger:"manual"}),$("#inputMixrampdelay").popover("show"),$("#inputMixrampdelay").focus(),a=!1):$("#inputMixrampdelay").val(b));1==a&&(sendAPI({cmd:"MPD_API_SET_SETTINGS",data:{consume:$("#btnconsume").hasClass("btn-success")? +1:0,random:$("#btnrandom").hasClass("btn-success")?1:0,single:$("#btnsingle").hasClass("btn-success")?1:0,repeat:$("#btnrepeat").hasClass("btn-success")?1:0,replaygain:$("#selectReplaygain").val(),crossfade:$("#inputCrossfade").val(),mixrampdb:$("#inputMixrampdb").val(),mixrampdelay:$("#inputMixrampdelay").val(),notificationWeb:$("#btnnotifyWeb").hasClass("btn-success")?1:0,notificationPage:$("#btnnotifyPage").hasClass("btn-success")?1:0}},getSettings),$("#settings").modal("hide"))} +function toggleoutput(a,b){sendAPI({cmd:"MPD_API_TOGGLE_OUTPUT",data:{output:b,state:$(a).hasClass("btn-success")?0:1}})}$("#trashmodebtns > button").on("click",function(a){$("#trashmodebtns").children("button").removeClass("btn-success").addClass("btn-secondary");$(this).removeClass("btn-secondary").addClass("btn-success")});$("#search > input").keypress(function(a){13==a.which&&$("#mainMenu > a").dropdown("toggle")}); +$("#search").submit(function(){app.goto("Search",void 0,void 0,app.current.page+"/Any Tag/"+$("#search > input").val());return!1});$("#search2").submit(function(){return!1});function addAllFromSearch(){if(2<=app.current.search.length){sendAPI({cmd:"MPD_API_SEARCH_ADD",data:{filter:app.current.filter,searchstr:+app.current.search}});var a=$("#SearchList >tbody >tr").length;showNotification("Added "+a+" songs from search","","","success")}} +$("#searchstr2").keyup(function(a){app.current.page=0;app.current.search=$(this).val();app.goto("Search",void 0,void 0,app.current.page+"/"+app.current.filter+"/"+app.current.search)}); +$("#searchtags2 > button").on("click",function(a){$("#searchtags2 > button").removeClass("btn-success").addClass("btn-secondary");$(this).removeClass("btn-secondary").addClass("btn-success");app.current.filter=$(this).text();app.goto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.search)}); +$("#searchqueuestr").keyup(function(a){app.current.page=0;app.current.search=$(this).val();app.goto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.search)}); +$("#searchqueuetag > button").on("click",function(a){$("#searchqueuetag > button").removeClass("btn-success").addClass("btn-secondary");$(this).removeClass("btn-secondary").addClass("btn-success");app.current.filter=$(this).text();$("#searchqueuetagdesc").text(app.current.filter);app.goto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.search)});$("#searchqueue").submit(function(){return!1});$("#searchqueue").submit(function(){return!1}); +function scrollToTop(){document.body.scrollTop=0;document.documentElement.scrollTop=0}function gotoPage(a,b,c){switch(a){case "next":app.current.page+=settings.max_elements_per_page;break;case "prev":app.current.page-=settings.max_elements_per_page;0>=app.current.page&&(app.current.page=0);break;default:app.current.page=a}app.goto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.search);c.preventDefault()} +function addStream(){0<$("#streamurl").val().length&&sendAPI({cmd:"MPD_API_ADD_TRACK",data:{uri:$("#streamurl").val()}});$("#streamurl").val("");$("#addstream").modal("hide")}function saveQueue(){0<$("#playlistname").val().length&&sendAPI({cmd:"MPD_API_SAVE_QUEUE",data:{plist:$("#playlistname").val()}});$("#savequeue").modal("hide")} +function showNotification(a,b,c,d){1==settings.notificationWeb&&(b=new Notification(a,{icon:"assets/favicon.ico",body:b}),setTimeout(function(a){a.close()},3E3,b));1==settings.notificationPage&&$.notify({title:a,message:c},{type:d,offset:{y:60,x:20},template:''})}function notificationsSupported(){return"Notification"in window} +function songChange(a){if(last_song!=a.data.title+a.data.artist+a.data.album+a.data.uri+a.data.currentsongid){var b="",c="",d="myMPD: ";if("undefined"!=typeof a.data.uri&&0"+a.data.artist,d+=a.data.artist+" - ",$("#artist").text(a.data.artist)):$("#artist").text("");"undefined"!=typeof a.data.album&&0 button").removeClass("btn-success").addClass("btn-secondary");"0"==app.current.filter?($(a).text("Filter: #"),$(a+"Letters > button").each(function(){"#"==$(this).text()&&$(this).addClass("btn-success")})):"-"!=app.current.filter?($(a).text("Filter: "+app.current.filter),$(a+"Letters > button").each(function(){$(this).text()==app.current.filter&&$(this).addClass("btn-success")})):$(a).text("Filter")} +function add_filter(a){$(a).append('');$(a).append('');for(i=65;90>=i;i++){var b=String.fromCharCode(i);$(a).append('")}} +function chVolume(a){a=volumeBar.slider("getValue")+a;0>a?a=0:100d?"0":""):"")+d+"\u2009m "+(10>a?"0":"")+a+"\u2009s"}function genId(a){return"id"+a.replace(/[^\w]/g,"")}; diff --git a/htdocs/js/player.min.js b/htdocs/js/player.min.js new file mode 100644 index 0000000..53757ae --- /dev/null +++ b/htdocs/js/player.min.js @@ -0,0 +1 @@ +var mpdstream=decodeURI(location.hash).replace(/^#/,"");player.src=mpdstream;console.log("playing mpd stream: "+player.src);player.load();player.play(); diff --git a/htdocs/player.html b/htdocs/player.html index 209f388..abf47e0 100644 --- a/htdocs/player.html +++ b/htdocs/player.html @@ -6,8 +6,8 @@ myMPD: Local Player - - + + @@ -29,6 +29,6 @@ - + diff --git a/mkdebug.sh b/mkdebug.sh new file mode 100755 index 0000000..4323c84 --- /dev/null +++ b/mkdebug.sh @@ -0,0 +1,15 @@ +#/bin/bash + +rm htdocs/js/player.min.js +cp -l htdocs/js/player.js htdocs/js/player.min.js + +rm htdocs/js/mpd.min.js +cp -l htdocs/js/mpd.js htdocs/js/mpd.min.js + +rm htdocs/css/mpd.min.css +cp -l htdocs/css/mpd.css htdocs/css/mpd.min.css + +[ -d debug ] || mkdir debug +cd debug +cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_BUILD_TYPE=DEBUG .. +make diff --git a/mkrelease.sh b/mkrelease.sh new file mode 100755 index 0000000..daea0c3 --- /dev/null +++ b/mkrelease.sh @@ -0,0 +1,22 @@ +#/bin/bash + +if [ -f buildtools/closure-compiler.jar ] +then + rm -f htdocs/js/player.min.js + java -jar buildtools/closure-compiler.jar htdocs/js/player.js > htdocs/js/player.min.js + + rm -f htdocs/js/mpd.min.js + java -jar buildtools/closure-compiler.jar htdocs/js/mpd.js > htdocs/js/mpd.min.js +fi + +if [ -f buildtools/closure-stylesheets.jar ] +then + rm -f htdocs/css/mpd.min.css + java -jar buildtools/closure-stylesheets.jar htdocs/css/mpd.css > htdocs/css/mpd.min.css +fi + +[ -d release ] || mkdir release +cd release +cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_BUILD_TYPE=RELEASE .. +make +make install diff --git a/src/mpd_client.c b/src/mpd_client.c index 1fbe18f..805ca06 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -1,6 +1,6 @@ /* myMPD (c) 2018 Juergen Mang - This project's homepage is: https://github.com/jcorporation/ympd + This project's homepage is: https://github.com/jcorporation/mympd myMPD ist fork of: @@ -616,11 +616,16 @@ int mympd_put_settings(char *buffer) struct mpd_status *status; char *replaygain; int len; + int je; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); struct mympd_state { int a; int b; } state = { .a = 0, .b = 0 }; if( access( mpd.statefile, F_OK ) != -1 ) { char *content = json_fread(mpd.statefile); - json_scanf(content, strlen(content), "{notificationWeb: %d, notificationPage: %d}", &state.a, &state.b); + je = json_scanf(content, strlen(content), "{notificationWeb: %d, notificationPage: %d}", &state.a, &state.b); + if (je != 2) { + state.a=0; + state.b=0; + } } else { state.a=0; state.b=0; diff --git a/src/mpd_client.h b/src/mpd_client.h index 4025822..327ff21 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -1,6 +1,6 @@ /* myMPD (c) 2018 Juergen Mang - This project's homepage is: https://github.com/jcorporation/ympd + This project's homepage is: https://github.com/jcorporation/mympd myMPD ist fork of: diff --git a/src/mympd.c b/src/mympd.c index cfab0b6..15b4412 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -1,6 +1,6 @@ /* myMPD (c) 2018 Juergen Mang - This project's homepage is: https://github.com/jcorporation/ympd + This project's homepage is: https://github.com/jcorporation/mympd myMPD ist fork of: @@ -169,7 +169,7 @@ int main(int argc, char **argv) " -m, --mpdpass \tspecifies the password to use when connecting to mpd\n" " -s, --streamport \tconnect to mpd http stream at port [8000]\n" " -i, --coverimage \tfilename for coverimage [folder.jpg]\n" - " -t, --statefile \tstatefile [/var/lib/mympd/mympd.state]\n" + " -t, --statefile \tfilename for mympd state [/var/lib/mympd/mympd.state]\n" " --help\t\t\t\tthis help\n" , argv[0]); return EXIT_FAILURE; From 1f815889736530fb57a1e2513a652511e922aed0 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 21 Jun 2018 22:56:39 +0100 Subject: [PATCH 26/32] Add minified files --- htdocs/index.html | 4 ++-- htdocs/player.html | 4 ++-- mkdebug.sh | 9 --------- mkrelease.sh | 20 +++++++++++++------- 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/htdocs/index.html b/htdocs/index.html index 58c461b..c9f1ffe 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -9,7 +9,7 @@ - + @@ -689,6 +689,6 @@ - + diff --git a/htdocs/player.html b/htdocs/player.html index abf47e0..c3749f9 100644 --- a/htdocs/player.html +++ b/htdocs/player.html @@ -7,7 +7,7 @@ myMPD: Local Player - + @@ -29,6 +29,6 @@ - + diff --git a/mkdebug.sh b/mkdebug.sh index 4323c84..994bb13 100755 --- a/mkdebug.sh +++ b/mkdebug.sh @@ -1,14 +1,5 @@ #/bin/bash -rm htdocs/js/player.min.js -cp -l htdocs/js/player.js htdocs/js/player.min.js - -rm htdocs/js/mpd.min.js -cp -l htdocs/js/mpd.js htdocs/js/mpd.min.js - -rm htdocs/css/mpd.min.css -cp -l htdocs/css/mpd.css htdocs/css/mpd.min.css - [ -d debug ] || mkdir debug cd debug cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_BUILD_TYPE=DEBUG .. diff --git a/mkrelease.sh b/mkrelease.sh index daea0c3..d95600d 100755 --- a/mkrelease.sh +++ b/mkrelease.sh @@ -2,17 +2,21 @@ if [ -f buildtools/closure-compiler.jar ] then - rm -f htdocs/js/player.min.js - java -jar buildtools/closure-compiler.jar htdocs/js/player.js > htdocs/js/player.min.js - - rm -f htdocs/js/mpd.min.js - java -jar buildtools/closure-compiler.jar htdocs/js/mpd.js > htdocs/js/mpd.min.js + [ htdocs/js/player.js -nt htdocs/js/player.min.js ] && \ + java -jar buildtools/closure-compiler.jar htdocs/js/player.js > htdocs/js/player.min.js + [ htdocs/js/mpd.js -nt htdocs/js/mpd.min.js ] && \ + java -jar buildtools/closure-compiler.jar htdocs/js/mpd.js > htdocs/js/mpd.min.js +else + cp htdocs/js/player.js htdocs/js/player.min.js + cp htdocs/js/mpd.js htdocs/js/mpd.min.js fi if [ -f buildtools/closure-stylesheets.jar ] then - rm -f htdocs/css/mpd.min.css - java -jar buildtools/closure-stylesheets.jar htdocs/css/mpd.css > htdocs/css/mpd.min.css + [ htdocs/css/mpd.css -nt htdocs/css/mpd.min.css ] && \ + java -jar buildtools/closure-stylesheets.jar htdocs/css/mpd.css > htdocs/css/mpd.min.css +else + cp htdocs/css/mpd.css htdocs/css/mpd.min.css fi [ -d release ] || mkdir release @@ -20,3 +24,5 @@ cd release cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_BUILD_TYPE=RELEASE .. make make install +sed -i -e 's/mpd\.css/mpd\.min\.css/' -e 's/mpd\.js/mpd\.min\.js/' /usr/share/mympd/htdocs/index.html +sed -i -e 's/mpd\.css/mpd\.min\.css/' -e 's/player\.js/player\.min\.js/' /usr/share/mympd/htdocs/player.html From 7cff777d05b1636f230dbfe148b18b527558aa2d Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 21 Jun 2018 23:15:54 +0100 Subject: [PATCH 27/32] Dont run under uid 0 Optimize mkdebug.sh and mkrelease.sh --- README.md | 12 ++++-------- mkdebug.sh | 2 +- mkrelease.sh | 29 ++++++++++++++++++++++------- src/mympd.c | 7 ++++++- 4 files changed, 33 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index c7b9e3f..1d32812 100644 --- a/README.md +++ b/README.md @@ -31,13 +31,9 @@ Unix Build Instructions ----------------------- 1. install dependencies. cmake and libmpdclient (dev) are available from all major distributions. -2. create build directory ```cd /path/to/src; mkdir build; cd build``` -3. create makefile ```cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_BUILD_TYPE=RELEASE``` -4. build ```make``` -5. install ```sudo make install``` -6. change owner of /var/lib/mympd to user you will run mympd, default nobody (chown nobody /var/lib/mympd) -7. Link your mpd music directory to ```/usr/share/mympd/htdocs/library``` and put ```folder.jpg``` files in your album directories -8. Configure your mpd with http stream output to use the local player +2. build and install it ```cd /path/to/src; ./mkrelease.sh``` +3. Link your mpd music directory to ```/usr/share/mympd/htdocs/library``` and put ```folder.jpg``` files in your album directories +4. Configure your mpd with http stream output to use the local player Run flags --------- @@ -46,7 +42,7 @@ Usage: ./mympd [OPTION]... -h, --host connect to mpd at host [localhost] -p, --port connect to mpd at port [6600] - -w, --webport [ip:] listen interface/port for webserver [8080] + -w, --webport listen port for webserver [80] -s, --streamport connect to mpd http stream at port [8000] -u, --user drop priviliges to user after socket bind -m, --mpdpass specifies the password to use when connecting to mpd diff --git a/mkdebug.sh b/mkdebug.sh index 994bb13..7802f6e 100755 --- a/mkdebug.sh +++ b/mkdebug.sh @@ -1,4 +1,4 @@ -#/bin/bash +#/bin/sh [ -d debug ] || mkdir debug cd debug diff --git a/mkrelease.sh b/mkrelease.sh index d95600d..2e7324f 100755 --- a/mkrelease.sh +++ b/mkrelease.sh @@ -1,4 +1,4 @@ -#/bin/bash +#/bin/sh if [ -f buildtools/closure-compiler.jar ] then @@ -7,8 +7,10 @@ then [ htdocs/js/mpd.js -nt htdocs/js/mpd.min.js ] && \ java -jar buildtools/closure-compiler.jar htdocs/js/mpd.js > htdocs/js/mpd.min.js else - cp htdocs/js/player.js htdocs/js/player.min.js - cp htdocs/js/mpd.js htdocs/js/mpd.min.js + [ htdocs/js/player.js -nt htdocs/js/player.min.js ] && \ + cp htdocs/js/player.js htdocs/js/player.min.js + [ htdocs/js/mpd.js -nt htdocs/js/mpd.min.js ] && \ + cp htdocs/js/mpd.js htdocs/js/mpd.min.js fi if [ -f buildtools/closure-stylesheets.jar ] @@ -16,13 +18,26 @@ then [ htdocs/css/mpd.css -nt htdocs/css/mpd.min.css ] && \ java -jar buildtools/closure-stylesheets.jar htdocs/css/mpd.css > htdocs/css/mpd.min.css else - cp htdocs/css/mpd.css htdocs/css/mpd.min.css + [ htdocs/css/mpd.css -nt htdocs/css/mpd.min.css ] && \ + cp htdocs/css/mpd.css htdocs/css/mpd.min.css fi [ -d release ] || mkdir release cd release cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_BUILD_TYPE=RELEASE .. make -make install -sed -i -e 's/mpd\.css/mpd\.min\.css/' -e 's/mpd\.js/mpd\.min\.js/' /usr/share/mympd/htdocs/index.html -sed -i -e 's/mpd\.css/mpd\.min\.css/' -e 's/player\.js/player\.min\.js/' /usr/share/mympd/htdocs/player.html +sudo make install +cd .. + +sudo sed -i -e 's/mpd\.css/mpd\.min\.css/' -e 's/mpd\.js/mpd\.min\.js/' /usr/share/mympd/htdocs/index.html +sudo sed -i -e 's/mpd\.css/mpd\.min\.css/' -e 's/player\.js/player\.min\.js/' /usr/share/mympd/htdocs/player.html + +sudo chown nobody /var/lib/mympd + +[ -d /etc/systemd/system ] && \ + sudo cp -v contrib/mympd.service /etc/systemd/system/ + +[ -d /etc/default ] && \ + sudo cp -v contrib/mympd.default /etc/default/mympd + +echo "myMPD installed" diff --git a/src/mympd.c b/src/mympd.c index 15b4412..42e2bd1 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -103,7 +103,7 @@ int main(int argc, char **argv) struct mg_connection *nc; unsigned int current_timer = 0, last_timer = 0; char *run_as_user = NULL; - char *webport = "8080"; + char *webport = "80"; mpd.port = 6600; strcpy(mpd.host, "127.0.0.1"); streamport = 8000; @@ -204,6 +204,11 @@ int main(int argc, char **argv) return EXIT_FAILURE; } } + + if (getuid() == 0) { + printf("myMPD should not be run with root privileges\n"); + return EXIT_FAILURE; + } mg_set_protocol_http_websocket(nc); s_http_server_opts.document_root = SRC_PATH; From 9996c33adf412f42a08dc019870dc43184ab7328 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 21 Jun 2018 23:26:20 +0100 Subject: [PATCH 28/32] Free mgr --- src/mympd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mympd.c b/src/mympd.c index 42e2bd1..dbf47c0 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -207,6 +207,7 @@ int main(int argc, char **argv) if (getuid() == 0) { printf("myMPD should not be run with root privileges\n"); + mg_mgr_free(&mgr); return EXIT_FAILURE; } From f5f5b233123d892b2f2a69abd9d60b43753da31e Mon Sep 17 00:00:00 2001 From: jcorporation Date: Fri, 22 Jun 2018 01:01:15 +0100 Subject: [PATCH 29/32] Return coverimage in backend to client, dont guess it. --- htdocs/css/mpd.css | 3 +-- htdocs/js/mpd.js | 15 +++----------- src/mpd_client.c | 49 ++++++++++++++++++++++++++++++---------------- 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/htdocs/css/mpd.css b/htdocs/css/mpd.css index 2629f90..92a131f 100644 --- a/htdocs/css/mpd.css +++ b/htdocs/css/mpd.css @@ -194,8 +194,7 @@ main { .card-img-top { min-height:250px; - background-image:url('/assets/coverimage-notavailable.png'); background-repeat:no-repeat; - background-color:#d45500; + background-color:#eee; cursor:pointer; } diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index 85f040f..e392197 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -839,7 +839,7 @@ function parseListTitles(obj) { if(app.current.app !== 'Browse' && app.current.tab !== 'Database' && app.current.view !== 'Album') return; var id=genId(obj.album); var album=$('#card'+id+' > div > table > tbody'); - $('#card'+id+' > img').attr('src','/library/'+obj.data[0].uri.replace(/\/[^\/]+$/,'\/')+settings.coverimage) + $('#card'+id+' > img').attr('src',obj.cover) .attr('uri',obj.data[0].uri.replace(/\/[^\/]+$/,'')) .attr('data-album',encodeURI(obj.album)); var titleList=''; @@ -1270,17 +1270,8 @@ function songChange(obj) { var htmlNotification = ''; var pageTitle = 'myMPD: '; - if (typeof obj.data.uri != 'undefined' && obj.data.uri.length > 0) { - var coverImg=''; - if (obj.data.uri.indexOf('http://') == 0 || obj.data.uri.indexOf('https://') == 0 ) { - coverImg='/assets/coverimage-httpstream.png'; - } else if (settings.coverimage != '') { - coverImg='/library/'+obj.data.uri.replace(/\/[^\/]+$/,'\/'+settings.coverimage); - } else { - coverImg='/assets/coverimage-notavailable.png'; - } - $('#album-cover').css('backgroundImage','url("'+coverImg+'"),url("/assets/coverimage-notavailable.png")'); - } + $('#album-cover').css('backgroundImage','url("'+obj.data.cover+'")'); + if(typeof obj.data.artist != 'undefined' && obj.data.artist.length > 0 && obj.data.artist != '-') { textNotification += obj.data.artist; htmlNotification += '
' + obj.data.artist; diff --git a/src/mpd_client.c b/src/mpd_client.c index 805ca06..d11ba70 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -461,24 +462,20 @@ void mympd_poll(struct mg_mgr *s) char* mympd_get_title(struct mpd_song const *song) { char *str; - str = (char *)mpd_song_get_tag(song, MPD_TAG_TITLE, 0); if(str == NULL){ str = basename((char *)mpd_song_get_uri(song)); } - return str; } char* mympd_get_track(struct mpd_song const *song) { char *str; - str = (char *)mpd_song_get_tag(song, MPD_TAG_TRACK, 0); if(str == NULL){ str = "-"; } - return str; } @@ -486,48 +483,40 @@ char* mympd_get_track(struct mpd_song const *song) char* mympd_get_album(struct mpd_song const *song) { char *str; - str = (char *)mpd_song_get_tag(song, MPD_TAG_ALBUM, 0); if(str == NULL){ str = "-"; } - return str; } char* mympd_get_artist(struct mpd_song const *song) { char *str; - str = (char *)mpd_song_get_tag(song, MPD_TAG_ARTIST, 0); if(str == NULL){ str = "-"; } - return str; } char* mympd_get_album_artist(struct mpd_song const *song) { char *str; - str = (char *)mpd_song_get_tag(song, MPD_TAG_ALBUM_ARTIST, 0); if(str == NULL){ str = "-"; } - return str; } char* mympd_get_year(struct mpd_song const *song) { char *str; - str = (char *)mpd_song_get_tag(song, MPD_TAG_DATE, 0); if(str == NULL){ str = "-"; } - return str; } @@ -704,19 +693,41 @@ int mympd_put_outputnames(char *buffer) return len; } +int mympd_get_cover(const char *uri, char *cover, int cover_len) +{ + char *path=strdup(uri); + int len; + if (strncasecmp("http:",path,5) == 0 ) { + len=snprintf(cover,cover_len,"/assets/coverimage-httpstream.png"); + } + else { + dirname(path); + snprintf(cover,cover_len,"%s/library/%s/%s",SRC_PATH,path,coverimage); + if ( access(cover, F_OK ) == -1 ) { + len = snprintf(cover,cover_len,"/assets/coverimage-notavailable.png"); + } else { + len = snprintf(cover,cover_len,"/library/%s/%s",path,coverimage); + } + } + return len; +} + int mympd_put_current_song(char *buffer) { struct mpd_song *song; int len; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); - + char cover[500]; + song = mpd_run_current_song(mpd.conn); if(song == NULL) return 0; + + mympd_get_cover(mpd_song_get_uri(song),cover,500); len = json_printf(&out,"{type: song_change, data: { pos: %d, title: %Q, " "artist: %Q, album: %Q, uri: %Q, currentsongid: %d, albumartist: %Q, " - "duration: %d }}", + "duration: %d, cover: %Q }}", mpd_song_get_pos(song), mympd_get_title(song), mympd_get_artist(song), @@ -724,7 +735,8 @@ int mympd_put_current_song(char *buffer) mpd_song_get_uri(song), mpd.song_id, mympd_get_album_artist(song), - mpd_song_get_duration(song) + mpd_song_get_duration(song), + cover ); mpd_song_free(song); @@ -947,6 +959,7 @@ int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album) unsigned long entity_count = 0; unsigned long entities_returned = 0; int len; + char cover[500]; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); if(mpd_search_db_songs(mpd.conn, true) == false) { @@ -968,6 +981,7 @@ int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album) entity_count ++; if(entity_count <= MAX_ELEMENTS_PER_PAGE) { if (entities_returned ++) len += json_printf(&out, ", "); + else mympd_get_cover(mpd_song_get_uri(song),cover,500); len += json_printf(&out, "{ type: song, uri: %Q, duration: %d, title: %Q, track: %Q }", mpd_song_get_uri(song), mpd_song_get_duration(song), @@ -978,11 +992,12 @@ int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album) mpd_song_free(song); } - len += json_printf(&out, "], totalEntities: %d, returnedEntities: %d, albumartist: %Q, album: %Q }", + len += json_printf(&out, "], totalEntities: %d, returnedEntities: %d, albumartist: %Q, album: %Q, cover: %Q }", entity_count, entities_returned, albumartist, - album + album, + cover ); } From b6d1985d38928974edc8e1673e9e143463795f80 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Sun, 24 Jun 2018 18:57:47 +0100 Subject: [PATCH 30/32] Improved button states --- htdocs/css/mpd.css | 6 ++ htdocs/index.html | 15 +++-- htdocs/js/mpd.js | 165 +++++++++++++++++++-------------------------- 3 files changed, 84 insertions(+), 102 deletions(-) diff --git a/htdocs/css/mpd.css b/htdocs/css/mpd.css index 92a131f..6d039ae 100644 --- a/htdocs/css/mpd.css +++ b/htdocs/css/mpd.css @@ -198,3 +198,9 @@ main { background-color:#eee; cursor:pointer; } + +button.active { + color: #fff; + background-color: #28a745 !important; + border-color: #28a745 !important; +} \ No newline at end of file diff --git a/htdocs/index.html b/htdocs/index.html index c9f1ffe..5b92042 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -111,7 +111,7 @@ vertical_align_top Delete upward - @@ -142,7 +142,7 @@ +
'):setPagination(app.current.page), -$("#searchstr2").val(app.current.search)),$("#searchtags2 > button").each(function(){$(this).addClass("btn-secondary").removeClass("btn-success");$(this).text()==app.current.filter&&($(this).removeClass("btn-secondary").addClass("btn-success"),$("#searchtags2desc").text($(this).text()))}),2<=app.current.search.length?sendAPI({cmd:"MPD_API_SEARCH",data:{mpdtag:app.current.filter,offset:app.current.page,searchstr:app.current.search}},parseSearch):($("#SearchList > tbody").empty(),$("#searchAddAllSongs").attr("disabled", -"disabled").addClass("disabled"))):app.goto("Playback");app.last.app=app.current.app;app.last.tab=app.current.tab;app.last.view=app.current.view}else app.goto("Playback")}}; +app.current.app&&(2>app.current.search.length&&setPagination(app.current.page),$("#searchqueuetag > button").each(function(){$(this).removeClass("active");$(this).text()==app.current.filter&&($(this).addClass("active"),$("#searchqueuetagdesc").text($(this).text()))})),getQueue();else if("Browse"==app.current.app&&"Playlists"==app.current.tab)sendAPI({cmd:"MPD_API_GET_PLAYLISTS",data:{offset:app.current.page,filter:app.current.filter}},parsePlaylists);else if("Browse"==app.current.app&&"Database"== +app.current.tab&&"Artist"==app.current.view)sendAPI({cmd:"MPD_API_GET_ARTISTS",data:{offset:app.current.page,filter:app.current.filter}},parseListDBtags);else if("Browse"==app.current.app&&"Database"==app.current.tab&&"Album"==app.current.view)sendAPI({cmd:"MPD_API_GET_ARTISTALBUMS",data:{offset:app.current.page,filter:app.current.filter,albumartist:app.current.search}},parseListDBtags);else if("Browse"==app.current.app&&"Filesystem"==app.current.tab){$("#BrowseBreadcrumb").empty().append(''); +sendAPI({cmd:"MPD_API_GET_FILESYSTEM",data:{offset:app.current.page,path:app.current.search?app.current.search:"/",filter:app.current.filter}},parseFilesystem);var a=$("#browseFilesystemAddAllSongs");app.current.search?(a.off(),a.on("click",function(){sendAPI({cmd:"MPD_API_ADD_TRACK",data:{uri:app.current.search}})}),a.removeAttr("disabled").removeClass("disabled")):a.attr("disabled","disabled").addClass("disabled");var b=app.current.search.split("/"),c="";$.each(b,function(a,e){b.length-1==a?$("#BrowseBreadcrumb").append('"):(c+=e,$("#BrowseBreadcrumb").append('"),c+="/")})}else"Search"==app.current.app?(app.last.app!=app.current.app&&(""!=app.current.search?$("#SearchList > tbody").append(''):setPagination(app.current.page),$("#searchstr2").val(app.current.search)),$("#searchtags2 > button").each(function(){$(this).removeClass("active");$(this).text()== +app.current.filter&&($(this).addClass("active"),$("#searchtags2desc").text($(this).text()))}),2<=app.current.search.length?sendAPI({cmd:"MPD_API_SEARCH",data:{mpdtag:app.current.filter,offset:app.current.page,searchstr:app.current.search}},parseSearch):($("#SearchList > tbody").empty(),$("#searchAddAllSongs").attr("disabled","disabled").addClass("disabled"))):app.goto("Playback");app.last.app=app.current.app;app.last.tab=app.current.tab;app.last.view=app.current.view}else app.goto("Playback")}}; $(document).ready(function(){getSettings();sendAPI({cmd:"MPD_API_GET_OUTPUTNAMES"},parseOutputnames);webSocketConnect();volumeBar=$("#volumebar").slider();volumeBar.slider("setValue",0);volumeBar.slider("on","slideStop",function(a){sendAPI({cmd:"MPD_API_SET_VOLUME",data:{volume:a}})});progressBar=$("#progressbar").slider();progressBar.slider("setValue",0);progressBar.slider("on","slideStop",function(a){current_song&&0<=current_song.currentSongId&&sendAPI({cmd:"MPD_API_SET_SEEK",data:{songid:current_song.currentSongId, -seek:Math.ceil(a/100*current_song.totalTime)}})});$("#about").on("shown.bs.modal",function(){sendAPI({cmd:"MPD_API_GET_STATS"},parseStats)});$("#settings").on("shown.bs.modal",function(){sendAPI({cmd:"MPD_API_GET_SETTINGS"},parseSettings)});$("#addstream").on("shown.bs.modal",function(){$("#streamurl").focus()});$("#addstream form").on("submit",function(a){addStream()});$("#mainMenu").on("shown.bs.dropdown",function(){$("#search > input").val("");$("#search > input").focus()});add_filter("#BrowseFilesystemFilterLetters"); -add_filter("#BrowseDatabaseFilterLetters");add_filter("#BrowsePlaylistsFilterLetters");window.addEventListener("hashchange",app.route,!1)}); +seek:Math.ceil(a/100*current_song.totalTime)}})});$("#about").on("shown.bs.modal",function(){sendAPI({cmd:"MPD_API_GET_STATS"},parseStats)});$("#settings").on("shown.bs.modal",function(){sendAPI({cmd:"MPD_API_GET_SETTINGS"},parseSettings);document.getElementById("settingsFrm").classList.remove("was-validated");document.getElementById("inputCrossfade").classList.remove("is-invalid");document.getElementById("inputMixrampdb").classList.remove("is-invalid");document.getElementById("inputMixrampdelay").classList.remove("is-invalid")}); +$("#addstream").on("shown.bs.modal",function(){$("#streamurl").focus()});$("#addstream form").on("submit",function(a){addStream()});$("#mainMenu").on("shown.bs.dropdown",function(){$("#search > input").val("");$("#search > input").focus()});add_filter("#BrowseFilesystemFilterLetters");add_filter("#BrowseDatabaseFilterLetters");add_filter("#BrowsePlaylistsFilterLetters");window.addEventListener("hashchange",app.route,!1)}); function webSocketConnect(){socket="undefined"!=typeof MozWebSocket?new MozWebSocket(get_appropriate_ws_url()):new WebSocket(get_appropriate_ws_url());try{socket.onopen=function(){console.log("connected");showNotification("Connected to myMPD","","","success");$("#modalConnectionError").modal("hide");app.route()},socket.onmessage=function(a){if(a.data!==last_state&&0!=a.data.length){try{var b=JSON.parse(a.data)}catch(c){console.log("Invalid JSON data received: "+a.data)}switch(b.type){case "state":parseState(b); break;case "disconnected":showNotification("myMPD lost connection to MPD","","","danger");break;case "update_queue":"Queue"===app.current.app&&getQueue();break;case "song_change":songChange(b);break;case "error":showNotification(b.data,"","","danger")}}},socket.onclose=function(){console.log("disconnected");$("#modalConnectionError").modal("show");setTimeout(function(){console.log("reconnect");webSocketConnect()},3E3)}}catch(a){alert("

Error"+a)}} function get_appropriate_ws_url(){var a=document.URL;if("https"==a.substring(0,5)){var b="wss://";a=a.substr(8)}else b="ws://","http"==a.substring(0,4)&&(a=a.substr(7));a=a.split("#");var c=/\/$/.test(a[0])?"":"/";return b+a[0]+c+"ws"} function parseStats(a){$("#mpdstats_artists").text(a.data.artists);$("#mpdstats_albums").text(a.data.albums);$("#mpdstats_songs").text(a.data.songs);$("#mpdstats_dbplaytime").text(beautifyDuration(a.data.dbplaytime));$("#mpdstats_playtime").text(beautifyDuration(a.data.playtime));$("#mpdstats_uptime").text(beautifyDuration(a.data.uptime));var b=new Date(1E3*a.data.dbupdated);$("#mpdstats_dbupdated").text(b.toUTCString());$("#mympdVersion").text(a.data.mympd_version);$("#mpdVersion").text(a.data.mpd_version)} -function parseSettings(a){a.data.random?$("#btnrandom").removeClass("btn-secondary").addClass("btn-success"):$("#btnrandom").removeClass("btn-success").addClass("btn-secondary");a.data.consume?$("#btnconsume").removeClass("btn-secondary").addClass("btn-success"):$("#btnconsume").removeClass("btn-success").addClass("btn-secondary");a.data.single?$("#btnsingle").removeClass("btn-secondary").addClass("btn-success"):$("#btnsingle").removeClass("btn-success").addClass("btn-secondary");void 0!=a.data.crossfade? -$("#inputCrossfade").removeAttr("disabled").val(a.data.crossfade):$("#inputCrossfade").attr("disabled","disabled");void 0!=a.data.mixrampdb?$("#inputMixrampdb").removeAttr("disabled").val(a.data.mixrampdb):$("#inputMixrampdb").attr("disabled","disabled");void 0!=a.data.mixrampdelay?$("#inputMixrampdelay").removeAttr("disabled").val(a.data.mixrampdelay):$("#inputMixrampdb").attr("disabled","disabled");a.data.repeat?$("#btnrepeat").removeClass("btn-secondary").addClass("btn-success"):$("#btnrepeat").removeClass("btn-success").addClass("btn-secondary"); -$("#selectReplaygain").val(a.data.replaygain);notificationsSupported()?a.data.notificationWeb?($("#btnnotifyWeb").removeClass("btn-secondary").addClass("btn-success"),Notification.requestPermission(function(b){"permission"in Notification||(Notification.permission=b);"granted"===b?$("#btnnotifyWeb").removeClass("btn-secondary").addClass("btn-success"):($("#btnnotifyWeb").addClass("btn-secondary").removeClass("btn-success"),a.data.notificationWeb=0)})):$("#btnnotifyWeb").addClass("btn-secondary").removeClass("btn-success"): -($("#btnnotifyWeb").addClass("disabled"),$("#btnnotifyWeb").addClass("btn-secondary").removeClass("btn-success"));a.data.notificationPage?$("#btnnotifyPage").removeClass("btn-secondary").addClass("btn-success"):$("#btnnotifyPage").addClass("btn-secondary").removeClass("btn-success");settings=a.data;setLocalStream(a.data.mpdhost,a.data.streamport)}function getSettings(){sendAPI({cmd:"MPD_API_GET_SETTINGS"},parseSettings)} +function parseSettings(a){a.data.random?$("#btnrandom").addClass("active").attr("aria-pressed","true"):$("#btnrandom").removeClass("active").attr("aria-pressed","false");a.data.consume?$("#btnconsume").addClass("active").attr("aria-pressed","true"):$("#btnconsume").removeClass("active").attr("aria-pressed","false");a.data.single?$("#btnsingle").addClass("active").attr("aria-pressed","true"):$("#btnsingle").removeClass("active").attr("aria-pressed","false");a.data.repeat?$("#btnrepeat").addClass("active").attr("aria-pressed", +"true"):$("#btnrepeat").removeClass("active").attr("aria-pressed","false");void 0!=a.data.crossfade?$("#inputCrossfade").removeAttr("disabled").val(a.data.crossfade):$("#inputCrossfade").attr("disabled","disabled");void 0!=a.data.mixrampdb?$("#inputMixrampdb").removeAttr("disabled").val(a.data.mixrampdb):$("#inputMixrampdb").attr("disabled","disabled");void 0!=a.data.mixrampdelay?$("#inputMixrampdelay").removeAttr("disabled").val(a.data.mixrampdelay):$("#inputMixrampdb").attr("disabled","disabled"); +$("#selectReplaygain").val(a.data.replaygain);notificationsSupported()?a.data.notificationWeb?($("#btnnotifyWeb").addClass("active").attr("aria-pressed","true"),Notification.requestPermission(function(b){"permission"in Notification||(Notification.permission=b);"granted"===b?$("#btnnotifyWeb").addClass("active").attr("aria-pressed","true"):($("#btnnotifyWeb").removeClass("active").attr("aria-pressed","false"),a.data.notificationWeb=0)})):$("#btnnotifyWeb").removeClass("active").attr("aria-pressed", +"false"):($("#btnnotifyWeb").addClass("disabled"),$("#btnnotifyWeb").removeClass("active").attr("aria-pressed","false"));a.data.notificationPage?$("#btnnotifyPage").addClass("active").attr("aria-pressed","true"):$("#btnnotifyPage").removeClass("active").attr("aria-pressed","false");settings=a.data;setLocalStream(a.data.mpdhost,a.data.streamport)}function getSettings(){sendAPI({cmd:"MPD_API_GET_SETTINGS"},parseSettings)} function parseOutputnames(a){$("#btn-outputs-block button").remove();Object.keys(a.data).length?$.each(a.data,function(a,c){$('").appendTo($("#btn-outputs-block"))}):$("#btn-outputs-block").addClass("hide");last_outputs=""} function parseState(a){updatePlayIcon(a);updateVolumeIcon(a.data.volume);if(JSON.stringify(a)!==JSON.stringify(last_state)){current_song.totalTime=a.data.totalTime;current_song.currentSongId=a.data.currentsongid;var b=Math.floor(a.data.totalTime/60),c=a.data.totalTime-60*b,d=Math.floor(a.data.elapsedTime/60),e=a.data.elapsedTime-60*d;volumeBar.slider("setValue",a.data.volume);progressBar.slider("setValue",Math.floor(100*a.data.elapsedTime/a.data.totalTime));b=d+":"+(10>e?"0":"")+e+" / "+b+":"+(10> c?"0":"")+c;$("#counter").text(b);last_state&&($("#QueueList > tbody > tr[trackid="+last_state.data.currentsongid+"] > td").eq(4).text(last_state.data.totalTime),$("#QueueList > tbody > tr[trackid="+last_state.data.currentsongid+"] > td").eq(0).removeClass("material-icons").text(last_state.data.songpos));$("#QueueList > tbody > tr").removeClass("active").removeClass("font-weight-bold");$("#QueueList > tbody > tr[trackid="+a.data.currentsongid+"] > td").eq(4).text(b);$("#QueueList > tbody > tr[trackid="+ -a.data.currentsongid+"] > td").eq(0).addClass("material-icons").text("play_arrow");$("#QueueList > tbody > tr[trackid="+a.data.currentsongid+"]").addClass("active").addClass("font-weight-bold");void 0!=last_state&&a.data.queue_version==last_state.data.queue_version||sendAPI({cmd:"MPD_API_GET_CURRENT_SONG"},songChange);last_state=a;$.each(a.data.outputs,function(a,b){b?$("#btnoutput"+a).removeClass("btn-secondary").addClass("btn-success"):$("#btnoutput"+a).removeClass("btn-success").addClass("btn-secondary")}); -last_outputs=a.data.outputs}}function getQueue(){2<=app.current.search.length?sendAPI({cmd:"MPD_API_SEARCH_QUEUE",data:{mpdtag:app.current.filter,offset:app.current.page,searchstr:app.current.search}},parseQueue):sendAPI({cmd:"MPD_API_GET_QUEUE",data:{offset:app.current.page}},parseQueue)} +a.data.currentsongid+"] > td").eq(0).addClass("material-icons").text("play_arrow");$("#QueueList > tbody > tr[trackid="+a.data.currentsongid+"]").addClass("active").addClass("font-weight-bold");void 0!=last_state&&a.data.queue_version==last_state.data.queue_version||sendAPI({cmd:"MPD_API_GET_CURRENT_SONG"},songChange);last_state=a;$.each(a.data.outputs,function(a,b){b?$("#btnoutput"+a).addClass("active"):$("#btnoutput"+a).removeClass("active")});last_outputs=a.data.outputs}} +function getQueue(){2<=app.current.search.length?sendAPI({cmd:"MPD_API_SEARCH_QUEUE",data:{mpdtag:app.current.filter,offset:app.current.page,searchstr:app.current.search}},parseQueue):sendAPI({cmd:"MPD_API_GET_QUEUE",data:{offset:app.current.page}},parseQueue)} function parseQueue(a){if("Queue"===app.current.app){$("#panel-heading-queue").empty();0

";b<=c.length?$(c[b-1]).attr("trackid")!=a.data[d].id&&$(c[b-1]).replaceWith(e):$("#"+app.current.app+"List > tbody").append(e)}for(d=c.length;d>b;d--)$(c[c.length-1]).remove();"queuesearch"==a.type&&0==b&&$("#QueueList > tbody").append(''); -setPagination(a.totalEntities);if(isTouch)$("#QueueList > tbody > tr > td:last-child").append('delete');else $("#QueueList > tbody > tr").on({mouseover:function(){var a=$(this);$("#btntrashmodeup").hasClass("btn-success")&&(a=$("#QueueList > tbody > tr:lt("+($(this).index()+ -1)+")"));$("#btntrashmodedown").hasClass("btn-success")&&(a=$("#QueueList > tbody > tr:gt("+($(this).index()-1)+")"));$.each(a,function(){0==$(this).children().last().has("a").length&&$(this).children().last().append('delete').find("a").fadeTo("fast",1)})},mouseleave:function(){var a= -$(this);$("#btntrashmodeup").hasClass("btn-success")&&(a=$("#QueueList > tbody > tr:lt("+($(this).index()+1)+")"));$("#btntrashmodedown").hasClass("btn-success")&&(a=$("#QueueList > tbody > tr:gt("+($(this).index()-1)+")"));$.each(a,function(){$(this).children().last().find("a").stop().remove()})}});$("#QueueList > tbody > tr").on({click:function(){$("#queueList > tbody > tr").removeClass("active");sendAPI({cmd:"MPD_API_PLAY_TRACK",data:{track:$(this).attr("trackid")}});$(this).addClass("active")}})}} +setPagination(a.totalEntities);if(isTouch)$("#QueueList > tbody > tr > td:last-child").append('delete');else $("#QueueList > tbody > tr").on({mouseover:function(){var a=$(this);$("#btntrashmodeup").hasClass("active")&&(a=$("#QueueList > tbody > tr:lt("+($(this).index()+1)+ +")"));$("#btntrashmodedown").hasClass("active")&&(a=$("#QueueList > tbody > tr:gt("+($(this).index()-1)+")"));$.each(a,function(){0==$(this).children().last().has("a").length&&$(this).children().last().append('delete').find("a").fadeTo("fast",1)})},mouseleave:function(){var a= +$(this);$("#btntrashmodeup").hasClass("active")&&(a=$("#QueueList > tbody > tr:lt("+($(this).index()+1)+")"));$("#btntrashmodedown").hasClass("active")&&(a=$("#QueueList > tbody > tr:gt("+($(this).index()-1)+")"));$.each(a,function(){$(this).children().last().find("a").stop().remove()})}});$("#QueueList > tbody > tr").on({click:function(){$("#queueList > tbody > tr").removeClass("active");sendAPI({cmd:"MPD_API_PLAY_TRACK",data:{track:$(this).attr("trackid")}});$(this).addClass("active")}})}} function parseSearch(a){"Search"===app.current.app&&($("#panel-heading-search").text(a.totalEntities+" Songs found"),0'+c+"").find("a").click(function(a){a.stopPropagation();sendAPI({cmd:b,data:{uri:decodeURI($(this).parents("tr").attr("uri"))}});showNotification('"'+$("td:nth-last-child(3)",$(this).parents("tr")).text()+'" added',"","","success")})}if("Browse"===app.current.app||"Filesystem"===app.current.tab||"Search"===app.current.app){var c=0,d=document.getElementById(app.current.app+ (void 0==app.current.tab?"":app.current.tab)+"List").getElementsByTagName("tbody")[0].getElementsByTagName("tr"),e;for(e in a.data){c++;var f="",g="";switch(a.data[e].type){case "directory":g=encodeURI(a.data[e].dir);f='";break;case "song":f=Math.floor(a.data[e].duration/60);var h=a.data[e].duration-60*f;g=encodeURI(a.data[e].uri);f=' tbody").append('')}else if("Album"==a.tagtype){$("#BrowseDatabaseArtistList").addClass("hide");$("#BrowseDatabaseAlbumCards").removeClass("hide");$("#btnBrowseDatabaseArtist").removeClass("hide");b=0;c=document.getElementById("BrowseDatabaseAlbumCards").querySelectorAll(".col-md");for(d in a.data)b++,e=genId(a.data[d].value), f='
'+a.searchstr+'

'+a.data[d].value+'

'+obj.data[item].track+''+obj.data[item].title+'
"+ obj.data[song].album +""+ minutes + ":" + (seconds < 10 ? '0' : '') + seconds + "
folder_open' + basename(obj.data[item].dir) + '
music_note' + obj.data[item].title + '' + obj.data[item].artist + '
list' + basename(obj.data[item].plist) + '
list' + basename(obj.data[item].plist) + ''+d.toUTCString()+'
album' + obj.data[item].value + '
` elements.\n\nbody {\n margin: 0; // 1\n font-family: $font-family-base;\n font-size: $font-size-base;\n font-weight: $font-weight-base;\n line-height: $line-height-base;\n color: $body-color;\n text-align: left; // 3\n background-color: $body-bg; // 2\n}\n\n// Suppress the focus outline on elements that cannot be accessed via keyboard.\n// This prevents an unwanted focus outline from appearing around elements that\n// might still respond to pointer events.\n//\n// Credit: https://github.com/suitcss/base\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\n\n// Content grouping\n//\n// 1. Add the correct box sizing in Firefox.\n// 2. Show the overflow in Edge and IE.\n\nhr {\n box-sizing: content-box; // 1\n height: 0; // 1\n overflow: visible; // 2\n}\n\n\n//\n// Typography\n//\n\n// Remove top margins from headings\n//\n// By default, `

`-`

` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n// stylelint-disable selector-list-comma-newline-after\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: $headings-margin-bottom;\n}\n// stylelint-enable selector-list-comma-newline-after\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `

`s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n// Abbreviations\n//\n// 1. Remove the bottom border in Firefox 39-.\n// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Duplicate behavior to the data-* attribute for our tooltip plugin\n\nabbr[title],\nabbr[data-original-title] { // 4\n text-decoration: underline; // 2\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n border-bottom: 0; // 1\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // Undo browser default\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\ndfn {\n font-style: italic; // Add the correct font style in Android 4.3-\n}\n\n// stylelint-disable font-weight-notation\nb,\nstrong {\n font-weight: bolder; // Add the correct font weight in Chrome, Edge, and Safari\n}\n// stylelint-enable font-weight-notation\n\nsmall {\n font-size: 80%; // Add the correct font size in all browsers\n}\n\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n//\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n//\n// Links\n//\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n background-color: transparent; // Remove the gray background on active links in IE 10.\n -webkit-text-decoration-skip: objects; // Remove gaps in links underline in iOS 8+ and Safari 8+.\n\n @include hover {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href)\n// which have not been made explicitly keyboard-focusable (without tabindex).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n\n @include hover-focus {\n color: inherit;\n text-decoration: none;\n }\n\n &:focus {\n outline: 0;\n }\n}\n\n\n//\n// Code\n//\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-monospace;\n font-size: 1em; // Correct the odd `em` font sizing in all browsers.\n}\n\npre {\n // Remove browser default top margin\n margin-top: 0;\n // Reset browser default of `1em` to use `rem`s\n margin-bottom: 1rem;\n // Don't allow content to break outside\n overflow: auto;\n // We have @viewport set which causes scrollbars to overlap content in IE11 and Edge, so\n // we force a non-overlapping, non-auto-hiding scrollbar to counteract.\n -ms-overflow-style: scrollbar;\n}\n\n\n//\n// Figures\n//\n\nfigure {\n // Apply a consistent margin strategy (matches our type styles).\n margin: 0 0 1rem;\n}\n\n\n//\n// Images and content\n//\n\nimg {\n vertical-align: middle;\n border-style: none; // Remove the border on images inside links in IE 10-.\n}\n\nsvg:not(:root) {\n overflow: hidden; // Hide the overflow in IE\n}\n\n\n//\n// Tables\n//\n\ntable {\n border-collapse: collapse; // Prevent double borders\n}\n\ncaption {\n padding-top: $table-cell-padding;\n padding-bottom: $table-cell-padding;\n color: $table-caption-color;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n // Matches default `

` alignment by inheriting from the ``, or the\n // closest parent with a set `text-align`.\n text-align: inherit;\n}\n\n\n//\n// Forms\n//\n\nlabel {\n // Allow labels to use `margin` for spacing.\n display: inline-block;\n margin-bottom: $label-margin-bottom;\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24093\nbutton {\n border-radius: 0;\n}\n\n// Work around a Firefox/IE bug where the transparent `button` background\n// results in a loss of the default `button` focus styles.\n//\n// Credit: https://github.com/suitcss/base/\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // Remove the margin in Firefox and Safari\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible; // Show the overflow in Edge\n}\n\nbutton,\nselect {\n text-transform: none; // Remove the inheritance of text transform in Firefox\n}\n\n// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`\n// controls in Android 4.\n// 2. Correct the inability to style clickable types in iOS and Safari.\nbutton,\nhtml [type=\"button\"], // 1\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button; // 2\n}\n\n// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box; // 1. Add the correct box sizing in IE 10-\n padding: 0; // 2. Remove the padding in IE 10-\n}\n\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n // Remove the default appearance of temporal inputs to avoid a Mobile Safari\n // bug where setting a custom line-height prevents text from being vertically\n // centered within the input.\n // See https://bugs.webkit.org/show_bug.cgi?id=139848\n // and https://github.com/twbs/bootstrap/issues/11266\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto; // Remove the default vertical scrollbar in IE.\n // Textareas should really only resize vertically so they don't break their (horizontal) containers.\n resize: vertical;\n}\n\nfieldset {\n // Browsers set a default `min-width: min-content;` on fieldsets,\n // unlike e.g. `
`s, which have `min-width: 0;` by default.\n // So we reset that to ensure fieldsets behave more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359\n // and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements\n min-width: 0;\n // Reset the default outline behavior of fieldsets so they don't affect page layout.\n padding: 0;\n margin: 0;\n border: 0;\n}\n\n// 1. Correct the text wrapping in Edge and IE.\n// 2. Correct the color inheritance from `fieldset` elements in IE.\nlegend {\n display: block;\n width: 100%;\n max-width: 100%; // 1\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit; // 2\n white-space: normal; // 1\n}\n\nprogress {\n vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.\n}\n\n// Correct the cursor style of increment and decrement buttons in Chrome.\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n // This overrides the extra rounded corners on search inputs in iOS so that our\n // `.form-control` class can properly style them. Note that this cannot simply\n // be added to `.form-control` as it's not specific enough. For details, see\n // https://github.com/twbs/bootstrap/issues/11586.\n outline-offset: -2px; // 2. Correct the outline style in Safari.\n -webkit-appearance: none;\n}\n\n//\n// Remove the inner padding and cancel buttons in Chrome and Safari on macOS.\n//\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// 1. Correct the inability to style clickable types in iOS and Safari.\n// 2. Change font properties to `inherit` in Safari.\n//\n\n::-webkit-file-upload-button {\n font: inherit; // 2\n -webkit-appearance: button; // 1\n}\n\n//\n// Correct element displays\n//\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item; // Add the correct display in all browsers\n cursor: pointer;\n}\n\ntemplate {\n display: none; // Add the correct display in IE\n}\n\n// Always hide an element with the `hidden` HTML attribute (from PureCSS).\n// Needed for proper display in IE 10-.\n[hidden] {\n display: none !important;\n}\n","// Variables\n//\n// Variables should follow the `$component-state-property-size` formula for\n// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs.\n\n\n//\n// Color system\n//\n\n// stylelint-disable\n$white: #fff !default;\n$gray-100: #f8f9fa !default;\n$gray-200: #e9ecef !default;\n$gray-300: #dee2e6 !default;\n$gray-400: #ced4da !default;\n$gray-500: #adb5bd !default;\n$gray-600: #6c757d !default;\n$gray-700: #495057 !default;\n$gray-800: #343a40 !default;\n$gray-900: #212529 !default;\n$black: #000 !default;\n\n$grays: () !default;\n$grays: map-merge((\n \"100\": $gray-100,\n \"200\": $gray-200,\n \"300\": $gray-300,\n \"400\": $gray-400,\n \"500\": $gray-500,\n \"600\": $gray-600,\n \"700\": $gray-700,\n \"800\": $gray-800,\n \"900\": $gray-900\n), $grays);\n\n$blue: #007bff !default;\n$indigo: #6610f2 !default;\n$purple: #6f42c1 !default;\n$pink: #e83e8c !default;\n$red: #dc3545 !default;\n$orange: #fd7e14 !default;\n$yellow: #ffc107 !default;\n$green: #28a745 !default;\n$teal: #20c997 !default;\n$cyan: #17a2b8 !default;\n\n$colors: () !default;\n$colors: map-merge((\n \"blue\": $blue,\n \"indigo\": $indigo,\n \"purple\": $purple,\n \"pink\": $pink,\n \"red\": $red,\n \"orange\": $orange,\n \"yellow\": $yellow,\n \"green\": $green,\n \"teal\": $teal,\n \"cyan\": $cyan,\n \"white\": $white,\n \"gray\": $gray-600,\n \"gray-dark\": $gray-800\n), $colors);\n\n$primary: $blue !default;\n$secondary: $gray-600 !default;\n$success: $green !default;\n$info: $cyan !default;\n$warning: $yellow !default;\n$danger: $red !default;\n$light: $gray-100 !default;\n$dark: $gray-800 !default;\n\n$theme-colors: () !default;\n$theme-colors: map-merge((\n \"primary\": $primary,\n \"secondary\": $secondary,\n \"success\": $success,\n \"info\": $info,\n \"warning\": $warning,\n \"danger\": $danger,\n \"light\": $light,\n \"dark\": $dark\n), $theme-colors);\n// stylelint-enable\n\n// Set a specific jump point for requesting color jumps\n$theme-color-interval: 8% !default;\n\n// The yiq lightness value that determines when the lightness of color changes from \"dark\" to \"light\". Acceptable values are between 0 and 255.\n$yiq-contrasted-threshold: 150 !default;\n\n// Customize the light and dark text colors for use in our YIQ color contrast function.\n$yiq-text-dark: $gray-900 !default;\n$yiq-text-light: $white !default;\n\n// Options\n//\n// Quickly modify global styling by enabling or disabling optional features.\n\n$enable-caret: true !default;\n$enable-rounded: true !default;\n$enable-shadows: false !default;\n$enable-gradients: false !default;\n$enable-transitions: true !default;\n$enable-hover-media-query: false !default; // Deprecated, no longer affects any compiled CSS\n$enable-grid-classes: true !default;\n$enable-print-styles: true !default;\n\n\n// Spacing\n//\n// Control the default styling of most Bootstrap elements by modifying these\n// variables. Mostly focused on spacing.\n// You can add more entries to the $spacers map, should you need more variation.\n\n// stylelint-disable\n$spacer: 1rem !default;\n$spacers: () !default;\n$spacers: map-merge((\n 0: 0,\n 1: ($spacer * .25),\n 2: ($spacer * .5),\n 3: $spacer,\n 4: ($spacer * 1.5),\n 5: ($spacer * 3)\n), $spacers);\n\n// This variable affects the `.h-*` and `.w-*` classes.\n$sizes: () !default;\n$sizes: map-merge((\n 25: 25%,\n 50: 50%,\n 75: 75%,\n 100: 100%,\n auto: auto\n), $sizes);\n// stylelint-enable\n\n// Body\n//\n// Settings for the `` element.\n\n$body-bg: $white !default;\n$body-color: $gray-900 !default;\n\n// Links\n//\n// Style anchor elements.\n\n$link-color: theme-color(\"primary\") !default;\n$link-decoration: none !default;\n$link-hover-color: darken($link-color, 15%) !default;\n$link-hover-decoration: underline !default;\n\n// Paragraphs\n//\n// Style p element.\n\n$paragraph-margin-bottom: 1rem !default;\n\n\n// Grid breakpoints\n//\n// Define the minimum dimensions at which your layout will change,\n// adapting to different screen sizes, for use in media queries.\n\n$grid-breakpoints: (\n xs: 0,\n sm: 576px,\n md: 768px,\n lg: 992px,\n xl: 1200px\n) !default;\n\n@include _assert-ascending($grid-breakpoints, \"$grid-breakpoints\");\n@include _assert-starts-at-zero($grid-breakpoints);\n\n\n// Grid containers\n//\n// Define the maximum width of `.container` for different screen sizes.\n\n$container-max-widths: (\n sm: 540px,\n md: 720px,\n lg: 960px,\n xl: 1140px\n) !default;\n\n@include _assert-ascending($container-max-widths, \"$container-max-widths\");\n\n\n// Grid columns\n//\n// Set the number of columns and specify the width of the gutters.\n\n$grid-columns: 12 !default;\n$grid-gutter-width: 30px !default;\n\n// Components\n//\n// Define common padding and border radius sizes and more.\n\n$line-height-lg: 1.5 !default;\n$line-height-sm: 1.5 !default;\n\n$border-width: 1px !default;\n$border-color: $gray-300 !default;\n\n$border-radius: .25rem !default;\n$border-radius-lg: .3rem !default;\n$border-radius-sm: .2rem !default;\n\n$box-shadow-sm: 0 .125rem .25rem rgba($black, .075) !default;\n$box-shadow: 0 .5rem 1rem rgba($black, .15) !default;\n$box-shadow-lg: 0 1rem 3rem rgba($black, .175) !default;\n\n$component-active-color: $white !default;\n$component-active-bg: theme-color(\"primary\") !default;\n\n$caret-width: .3em !default;\n\n$transition-base: all .2s ease-in-out !default;\n$transition-fade: opacity .15s linear !default;\n$transition-collapse: height .35s ease !default;\n\n\n// Fonts\n//\n// Font, line-height, and color for body text, headings, and more.\n\n// stylelint-disable value-keyword-case\n$font-family-sans-serif: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\" !default;\n$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace !default;\n$font-family-base: $font-family-sans-serif !default;\n// stylelint-enable value-keyword-case\n\n$font-size-base: 1rem !default; // Assumes the browser default, typically `16px`\n$font-size-lg: ($font-size-base * 1.25) !default;\n$font-size-sm: ($font-size-base * .875) !default;\n\n$font-weight-light: 300 !default;\n$font-weight-normal: 400 !default;\n$font-weight-bold: 700 !default;\n\n$font-weight-base: $font-weight-normal !default;\n$line-height-base: 1.5 !default;\n\n$h1-font-size: $font-size-base * 2.5 !default;\n$h2-font-size: $font-size-base * 2 !default;\n$h3-font-size: $font-size-base * 1.75 !default;\n$h4-font-size: $font-size-base * 1.5 !default;\n$h5-font-size: $font-size-base * 1.25 !default;\n$h6-font-size: $font-size-base !default;\n\n$headings-margin-bottom: ($spacer / 2) !default;\n$headings-font-family: inherit !default;\n$headings-font-weight: 500 !default;\n$headings-line-height: 1.2 !default;\n$headings-color: inherit !default;\n\n$display1-size: 6rem !default;\n$display2-size: 5.5rem !default;\n$display3-size: 4.5rem !default;\n$display4-size: 3.5rem !default;\n\n$display1-weight: 300 !default;\n$display2-weight: 300 !default;\n$display3-weight: 300 !default;\n$display4-weight: 300 !default;\n$display-line-height: $headings-line-height !default;\n\n$lead-font-size: ($font-size-base * 1.25) !default;\n$lead-font-weight: 300 !default;\n\n$small-font-size: 80% !default;\n\n$text-muted: $gray-600 !default;\n\n$blockquote-small-color: $gray-600 !default;\n$blockquote-font-size: ($font-size-base * 1.25) !default;\n\n$hr-border-color: rgba($black, .1) !default;\n$hr-border-width: $border-width !default;\n\n$mark-padding: .2em !default;\n\n$dt-font-weight: $font-weight-bold !default;\n\n$kbd-box-shadow: inset 0 -.1rem 0 rgba($black, .25) !default;\n$nested-kbd-font-weight: $font-weight-bold !default;\n\n$list-inline-padding: .5rem !default;\n\n$mark-bg: #fcf8e3 !default;\n\n$hr-margin-y: $spacer !default;\n\n\n// Tables\n//\n// Customizes the `.table` component with basic values, each used across all table variations.\n\n$table-cell-padding: .75rem !default;\n$table-cell-padding-sm: .3rem !default;\n\n$table-bg: transparent !default;\n$table-accent-bg: rgba($black, .05) !default;\n$table-hover-bg: rgba($black, .075) !default;\n$table-active-bg: $table-hover-bg !default;\n\n$table-border-width: $border-width !default;\n$table-border-color: $gray-300 !default;\n\n$table-head-bg: $gray-200 !default;\n$table-head-color: $gray-700 !default;\n\n$table-dark-bg: $gray-900 !default;\n$table-dark-accent-bg: rgba($white, .05) !default;\n$table-dark-hover-bg: rgba($white, .075) !default;\n$table-dark-border-color: lighten($gray-900, 7.5%) !default;\n$table-dark-color: $body-bg !default;\n\n$table-striped-order: odd !default;\n\n$table-caption-color: $text-muted !default;\n\n// Buttons + Forms\n//\n// Shared variables that are reassigned to `$input-` and `$btn-` specific variables.\n\n$input-btn-padding-y: .375rem !default;\n$input-btn-padding-x: .75rem !default;\n$input-btn-line-height: $line-height-base !default;\n\n$input-btn-focus-width: .2rem !default;\n$input-btn-focus-color: rgba($component-active-bg, .25) !default;\n$input-btn-focus-box-shadow: 0 0 0 $input-btn-focus-width $input-btn-focus-color !default;\n\n$input-btn-padding-y-sm: .25rem !default;\n$input-btn-padding-x-sm: .5rem !default;\n$input-btn-line-height-sm: $line-height-sm !default;\n\n$input-btn-padding-y-lg: .5rem !default;\n$input-btn-padding-x-lg: 1rem !default;\n$input-btn-line-height-lg: $line-height-lg !default;\n\n$input-btn-border-width: $border-width !default;\n\n\n// Buttons\n//\n// For each of Bootstrap's buttons, define text, background, and border color.\n\n$btn-padding-y: $input-btn-padding-y !default;\n$btn-padding-x: $input-btn-padding-x !default;\n$btn-line-height: $input-btn-line-height !default;\n\n$btn-padding-y-sm: $input-btn-padding-y-sm !default;\n$btn-padding-x-sm: $input-btn-padding-x-sm !default;\n$btn-line-height-sm: $input-btn-line-height-sm !default;\n\n$btn-padding-y-lg: $input-btn-padding-y-lg !default;\n$btn-padding-x-lg: $input-btn-padding-x-lg !default;\n$btn-line-height-lg: $input-btn-line-height-lg !default;\n\n$btn-border-width: $input-btn-border-width !default;\n\n$btn-font-weight: $font-weight-normal !default;\n$btn-box-shadow: inset 0 1px 0 rgba($white, .15), 0 1px 1px rgba($black, .075) !default;\n$btn-focus-width: $input-btn-focus-width !default;\n$btn-focus-box-shadow: $input-btn-focus-box-shadow !default;\n$btn-disabled-opacity: .65 !default;\n$btn-active-box-shadow: inset 0 3px 5px rgba($black, .125) !default;\n\n$btn-link-disabled-color: $gray-600 !default;\n\n$btn-block-spacing-y: .5rem !default;\n\n// Allows for customizing button radius independently from global border radius\n$btn-border-radius: $border-radius !default;\n$btn-border-radius-lg: $border-radius-lg !default;\n$btn-border-radius-sm: $border-radius-sm !default;\n\n$btn-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n\n// Forms\n\n$label-margin-bottom: .5rem !default;\n\n$input-padding-y: $input-btn-padding-y !default;\n$input-padding-x: $input-btn-padding-x !default;\n$input-line-height: $input-btn-line-height !default;\n\n$input-padding-y-sm: $input-btn-padding-y-sm !default;\n$input-padding-x-sm: $input-btn-padding-x-sm !default;\n$input-line-height-sm: $input-btn-line-height-sm !default;\n\n$input-padding-y-lg: $input-btn-padding-y-lg !default;\n$input-padding-x-lg: $input-btn-padding-x-lg !default;\n$input-line-height-lg: $input-btn-line-height-lg !default;\n\n$input-bg: $white !default;\n$input-disabled-bg: $gray-200 !default;\n\n$input-color: $gray-700 !default;\n$input-border-color: $gray-400 !default;\n$input-border-width: $input-btn-border-width !default;\n$input-box-shadow: inset 0 1px 1px rgba($black, .075) !default;\n\n$input-border-radius: $border-radius !default;\n$input-border-radius-lg: $border-radius-lg !default;\n$input-border-radius-sm: $border-radius-sm !default;\n\n$input-focus-bg: $input-bg !default;\n$input-focus-border-color: lighten($component-active-bg, 25%) !default;\n$input-focus-color: $input-color !default;\n$input-focus-width: $input-btn-focus-width !default;\n$input-focus-box-shadow: $input-btn-focus-box-shadow !default;\n\n$input-placeholder-color: $gray-600 !default;\n$input-plaintext-color: $body-color !default;\n\n$input-height-border: $input-border-width * 2 !default;\n\n$input-height-inner: ($font-size-base * $input-btn-line-height) + ($input-btn-padding-y * 2) !default;\n$input-height: calc(#{$input-height-inner} + #{$input-height-border}) !default;\n\n$input-height-inner-sm: ($font-size-sm * $input-btn-line-height-sm) + ($input-btn-padding-y-sm * 2) !default;\n$input-height-sm: calc(#{$input-height-inner-sm} + #{$input-height-border}) !default;\n\n$input-height-inner-lg: ($font-size-lg * $input-btn-line-height-lg) + ($input-btn-padding-y-lg * 2) !default;\n$input-height-lg: calc(#{$input-height-inner-lg} + #{$input-height-border}) !default;\n\n$input-transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n$form-text-margin-top: .25rem !default;\n\n$form-check-input-gutter: 1.25rem !default;\n$form-check-input-margin-y: .3rem !default;\n$form-check-input-margin-x: .25rem !default;\n\n$form-check-inline-margin-x: .75rem !default;\n$form-check-inline-input-margin-x: .3125rem !default;\n\n$form-group-margin-bottom: 1rem !default;\n\n$input-group-addon-color: $input-color !default;\n$input-group-addon-bg: $gray-200 !default;\n$input-group-addon-border-color: $input-border-color !default;\n\n$custom-control-gutter: 1.5rem !default;\n$custom-control-spacer-x: 1rem !default;\n\n$custom-control-indicator-size: 1rem !default;\n$custom-control-indicator-bg: $gray-300 !default;\n$custom-control-indicator-bg-size: 50% 50% !default;\n$custom-control-indicator-box-shadow: inset 0 .25rem .25rem rgba($black, .1) !default;\n\n$custom-control-indicator-disabled-bg: $gray-200 !default;\n$custom-control-label-disabled-color: $gray-600 !default;\n\n$custom-control-indicator-checked-color: $component-active-color !default;\n$custom-control-indicator-checked-bg: $component-active-bg !default;\n$custom-control-indicator-checked-disabled-bg: rgba(theme-color(\"primary\"), .5) !default;\n$custom-control-indicator-checked-box-shadow: none !default;\n\n$custom-control-indicator-focus-box-shadow: 0 0 0 1px $body-bg, $input-btn-focus-box-shadow !default;\n\n$custom-control-indicator-active-color: $component-active-color !default;\n$custom-control-indicator-active-bg: lighten($component-active-bg, 35%) !default;\n$custom-control-indicator-active-box-shadow: none !default;\n\n$custom-checkbox-indicator-border-radius: $border-radius !default;\n$custom-checkbox-indicator-icon-checked: str-replace(url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='#{$custom-control-indicator-checked-color}' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\"), \"#\", \"%23\") !default;\n\n$custom-checkbox-indicator-indeterminate-bg: $component-active-bg !default;\n$custom-checkbox-indicator-indeterminate-color: $custom-control-indicator-checked-color !default;\n$custom-checkbox-indicator-icon-indeterminate: str-replace(url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='#{$custom-checkbox-indicator-indeterminate-color}' d='M0 2h4'/%3E%3C/svg%3E\"), \"#\", \"%23\") !default;\n$custom-checkbox-indicator-indeterminate-box-shadow: none !default;\n\n$custom-radio-indicator-border-radius: 50% !default;\n$custom-radio-indicator-icon-checked: str-replace(url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='#{$custom-control-indicator-checked-color}'/%3E%3C/svg%3E\"), \"#\", \"%23\") !default;\n\n$custom-select-padding-y: .375rem !default;\n$custom-select-padding-x: .75rem !default;\n$custom-select-height: $input-height !default;\n$custom-select-indicator-padding: 1rem !default; // Extra padding to account for the presence of the background-image based indicator\n$custom-select-line-height: $input-btn-line-height !default;\n$custom-select-color: $input-color !default;\n$custom-select-disabled-color: $gray-600 !default;\n$custom-select-bg: $input-bg !default;\n$custom-select-disabled-bg: $gray-200 !default;\n$custom-select-bg-size: 8px 10px !default; // In pixels because image dimensions\n$custom-select-indicator-color: $gray-800 !default;\n$custom-select-indicator: str-replace(url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='#{$custom-select-indicator-color}' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E\"), \"#\", \"%23\") !default;\n$custom-select-border-width: $input-btn-border-width !default;\n$custom-select-border-color: $input-border-color !default;\n$custom-select-border-radius: $border-radius !default;\n\n$custom-select-focus-border-color: $input-focus-border-color !default;\n$custom-select-focus-box-shadow: inset 0 1px 2px rgba($black, .075), 0 0 5px rgba($custom-select-focus-border-color, .5) !default;\n\n$custom-select-font-size-sm: 75% !default;\n$custom-select-height-sm: $input-height-sm !default;\n\n$custom-select-font-size-lg: 125% !default;\n$custom-select-height-lg: $input-height-lg !default;\n\n$custom-range-track-width: 100% !default;\n$custom-range-track-height: .5rem !default;\n$custom-range-track-cursor: pointer !default;\n$custom-range-track-bg: $gray-300 !default;\n$custom-range-track-border-radius: 1rem !default;\n$custom-range-track-box-shadow: inset 0 .25rem .25rem rgba($black, .1) !default;\n\n$custom-range-thumb-width: 1rem !default;\n$custom-range-thumb-height: $custom-range-thumb-width !default;\n$custom-range-thumb-bg: $component-active-bg !default;\n$custom-range-thumb-border: 0 !default;\n$custom-range-thumb-border-radius: 1rem !default;\n$custom-range-thumb-box-shadow: 0 .1rem .25rem rgba($black, .1) !default;\n$custom-range-thumb-focus-box-shadow: 0 0 0 1px $body-bg, $input-btn-focus-box-shadow !default;\n$custom-range-thumb-active-bg: lighten($component-active-bg, 35%) !default;\n\n$custom-file-height: $input-height !default;\n$custom-file-height-inner: $input-height-inner !default;\n$custom-file-focus-border-color: $input-focus-border-color !default;\n$custom-file-focus-box-shadow: $input-btn-focus-box-shadow !default;\n\n$custom-file-padding-y: $input-btn-padding-y !default;\n$custom-file-padding-x: $input-btn-padding-x !default;\n$custom-file-line-height: $input-btn-line-height !default;\n$custom-file-color: $input-color !default;\n$custom-file-bg: $input-bg !default;\n$custom-file-border-width: $input-btn-border-width !default;\n$custom-file-border-color: $input-border-color !default;\n$custom-file-border-radius: $input-border-radius !default;\n$custom-file-box-shadow: $input-box-shadow !default;\n$custom-file-button-color: $custom-file-color !default;\n$custom-file-button-bg: $input-group-addon-bg !default;\n$custom-file-text: (\n en: \"Browse\"\n) !default;\n\n\n// Form validation\n$form-feedback-margin-top: $form-text-margin-top !default;\n$form-feedback-font-size: $small-font-size !default;\n$form-feedback-valid-color: theme-color(\"success\") !default;\n$form-feedback-invalid-color: theme-color(\"danger\") !default;\n\n\n// Dropdowns\n//\n// Dropdown menu container and contents.\n\n$dropdown-min-width: 10rem !default;\n$dropdown-padding-y: .5rem !default;\n$dropdown-spacer: .125rem !default;\n$dropdown-bg: $white !default;\n$dropdown-border-color: rgba($black, .15) !default;\n$dropdown-border-radius: $border-radius !default;\n$dropdown-border-width: $border-width !default;\n$dropdown-divider-bg: $gray-200 !default;\n$dropdown-box-shadow: 0 .5rem 1rem rgba($black, .175) !default;\n\n$dropdown-link-color: $gray-900 !default;\n$dropdown-link-hover-color: darken($gray-900, 5%) !default;\n$dropdown-link-hover-bg: $gray-100 !default;\n\n$dropdown-link-active-color: $component-active-color !default;\n$dropdown-link-active-bg: $component-active-bg !default;\n\n$dropdown-link-disabled-color: $gray-600 !default;\n\n$dropdown-item-padding-y: .25rem !default;\n$dropdown-item-padding-x: 1.5rem !default;\n\n$dropdown-header-color: $gray-600 !default;\n\n\n// Z-index master list\n//\n// Warning: Avoid customizing these values. They're used for a bird's eye view\n// of components dependent on the z-axis and are designed to all work together.\n\n$zindex-dropdown: 1000 !default;\n$zindex-sticky: 1020 !default;\n$zindex-fixed: 1030 !default;\n$zindex-modal-backdrop: 1040 !default;\n$zindex-modal: 1050 !default;\n$zindex-popover: 1060 !default;\n$zindex-tooltip: 1070 !default;\n\n// Navs\n\n$nav-link-padding-y: .5rem !default;\n$nav-link-padding-x: 1rem !default;\n$nav-link-disabled-color: $gray-600 !default;\n\n$nav-tabs-border-color: $gray-300 !default;\n$nav-tabs-border-width: $border-width !default;\n$nav-tabs-border-radius: $border-radius !default;\n$nav-tabs-link-hover-border-color: $gray-200 $gray-200 $nav-tabs-border-color !default;\n$nav-tabs-link-active-color: $gray-700 !default;\n$nav-tabs-link-active-bg: $body-bg !default;\n$nav-tabs-link-active-border-color: $gray-300 $gray-300 $nav-tabs-link-active-bg !default;\n\n$nav-pills-border-radius: $border-radius !default;\n$nav-pills-link-active-color: $component-active-color !default;\n$nav-pills-link-active-bg: $component-active-bg !default;\n\n$nav-divider-color: $gray-200 !default;\n$nav-divider-margin-y: ($spacer / 2) !default;\n\n// Navbar\n\n$navbar-padding-y: ($spacer / 2) !default;\n$navbar-padding-x: $spacer !default;\n\n$navbar-nav-link-padding-x: .5rem !default;\n\n$navbar-brand-font-size: $font-size-lg !default;\n// Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link\n$nav-link-height: ($font-size-base * $line-height-base + $nav-link-padding-y * 2) !default;\n$navbar-brand-height: $navbar-brand-font-size * $line-height-base !default;\n$navbar-brand-padding-y: ($nav-link-height - $navbar-brand-height) / 2 !default;\n\n$navbar-toggler-padding-y: .25rem !default;\n$navbar-toggler-padding-x: .75rem !default;\n$navbar-toggler-font-size: $font-size-lg !default;\n$navbar-toggler-border-radius: $btn-border-radius !default;\n\n$navbar-dark-color: rgba($white, .5) !default;\n$navbar-dark-hover-color: rgba($white, .75) !default;\n$navbar-dark-active-color: $white !default;\n$navbar-dark-disabled-color: rgba($white, .25) !default;\n$navbar-dark-toggler-icon-bg: str-replace(url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$navbar-dark-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\"), \"#\", \"%23\") !default;\n$navbar-dark-toggler-border-color: rgba($white, .1) !default;\n\n$navbar-light-color: rgba($black, .5) !default;\n$navbar-light-hover-color: rgba($black, .7) !default;\n$navbar-light-active-color: rgba($black, .9) !default;\n$navbar-light-disabled-color: rgba($black, .3) !default;\n$navbar-light-toggler-icon-bg: str-replace(url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$navbar-light-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\"), \"#\", \"%23\") !default;\n$navbar-light-toggler-border-color: rgba($black, .1) !default;\n\n// Pagination\n\n$pagination-padding-y: .5rem !default;\n$pagination-padding-x: .75rem !default;\n$pagination-padding-y-sm: .25rem !default;\n$pagination-padding-x-sm: .5rem !default;\n$pagination-padding-y-lg: .75rem !default;\n$pagination-padding-x-lg: 1.5rem !default;\n$pagination-line-height: 1.25 !default;\n\n$pagination-color: $link-color !default;\n$pagination-bg: $white !default;\n$pagination-border-width: $border-width !default;\n$pagination-border-color: $gray-300 !default;\n\n$pagination-focus-box-shadow: $input-btn-focus-box-shadow !default;\n$pagination-focus-outline: 0 !default;\n\n$pagination-hover-color: $link-hover-color !default;\n$pagination-hover-bg: $gray-200 !default;\n$pagination-hover-border-color: $gray-300 !default;\n\n$pagination-active-color: $component-active-color !default;\n$pagination-active-bg: $component-active-bg !default;\n$pagination-active-border-color: $pagination-active-bg !default;\n\n$pagination-disabled-color: $gray-600 !default;\n$pagination-disabled-bg: $white !default;\n$pagination-disabled-border-color: $gray-300 !default;\n\n\n// Jumbotron\n\n$jumbotron-padding: 2rem !default;\n$jumbotron-bg: $gray-200 !default;\n\n\n// Cards\n\n$card-spacer-y: .75rem !default;\n$card-spacer-x: 1.25rem !default;\n$card-border-width: $border-width !default;\n$card-border-radius: $border-radius !default;\n$card-border-color: rgba($black, .125) !default;\n$card-inner-border-radius: calc(#{$card-border-radius} - #{$card-border-width}) !default;\n$card-cap-bg: rgba($black, .03) !default;\n$card-bg: $white !default;\n\n$card-img-overlay-padding: 1.25rem !default;\n\n$card-group-margin: ($grid-gutter-width / 2) !default;\n$card-deck-margin: $card-group-margin !default;\n\n$card-columns-count: 3 !default;\n$card-columns-gap: 1.25rem !default;\n$card-columns-margin: $card-spacer-y !default;\n\n\n// Tooltips\n\n$tooltip-font-size: $font-size-sm !default;\n$tooltip-max-width: 200px !default;\n$tooltip-color: $white !default;\n$tooltip-bg: $black !default;\n$tooltip-border-radius: $border-radius !default;\n$tooltip-opacity: .9 !default;\n$tooltip-padding-y: .25rem !default;\n$tooltip-padding-x: .5rem !default;\n$tooltip-margin: 0 !default;\n\n$tooltip-arrow-width: .8rem !default;\n$tooltip-arrow-height: .4rem !default;\n$tooltip-arrow-color: $tooltip-bg !default;\n\n\n// Popovers\n\n$popover-font-size: $font-size-sm !default;\n$popover-bg: $white !default;\n$popover-max-width: 276px !default;\n$popover-border-width: $border-width !default;\n$popover-border-color: rgba($black, .2) !default;\n$popover-border-radius: $border-radius-lg !default;\n$popover-box-shadow: 0 .25rem .5rem rgba($black, .2) !default;\n\n$popover-header-bg: darken($popover-bg, 3%) !default;\n$popover-header-color: $headings-color !default;\n$popover-header-padding-y: .5rem !default;\n$popover-header-padding-x: .75rem !default;\n\n$popover-body-color: $body-color !default;\n$popover-body-padding-y: $popover-header-padding-y !default;\n$popover-body-padding-x: $popover-header-padding-x !default;\n\n$popover-arrow-width: 1rem !default;\n$popover-arrow-height: .5rem !default;\n$popover-arrow-color: $popover-bg !default;\n\n$popover-arrow-outer-color: fade-in($popover-border-color, .05) !default;\n\n\n// Badges\n\n$badge-font-size: 75% !default;\n$badge-font-weight: $font-weight-bold !default;\n$badge-padding-y: .25em !default;\n$badge-padding-x: .4em !default;\n$badge-border-radius: $border-radius !default;\n\n$badge-pill-padding-x: .6em !default;\n// Use a higher than normal value to ensure completely rounded edges when\n// customizing padding or font-size on labels.\n$badge-pill-border-radius: 10rem !default;\n\n\n// Modals\n\n// Padding applied to the modal body\n$modal-inner-padding: 1rem !default;\n\n$modal-dialog-margin: .5rem !default;\n$modal-dialog-margin-y-sm-up: 1.75rem !default;\n\n$modal-title-line-height: $line-height-base !default;\n\n$modal-content-bg: $white !default;\n$modal-content-border-color: rgba($black, .2) !default;\n$modal-content-border-width: $border-width !default;\n$modal-content-border-radius: $border-radius-lg !default;\n$modal-content-box-shadow-xs: 0 .25rem .5rem rgba($black, .5) !default;\n$modal-content-box-shadow-sm-up: 0 .5rem 1rem rgba($black, .5) !default;\n\n$modal-backdrop-bg: $black !default;\n$modal-backdrop-opacity: .5 !default;\n$modal-header-border-color: $gray-200 !default;\n$modal-footer-border-color: $modal-header-border-color !default;\n$modal-header-border-width: $modal-content-border-width !default;\n$modal-footer-border-width: $modal-header-border-width !default;\n$modal-header-padding: 1rem !default;\n\n$modal-lg: 800px !default;\n$modal-md: 500px !default;\n$modal-sm: 300px !default;\n\n$modal-transition: transform .3s ease-out !default;\n\n\n// Alerts\n//\n// Define alert colors, border radius, and padding.\n\n$alert-padding-y: .75rem !default;\n$alert-padding-x: 1.25rem !default;\n$alert-margin-bottom: 1rem !default;\n$alert-border-radius: $border-radius !default;\n$alert-link-font-weight: $font-weight-bold !default;\n$alert-border-width: $border-width !default;\n\n$alert-bg-level: -10 !default;\n$alert-border-level: -9 !default;\n$alert-color-level: 6 !default;\n\n\n// Progress bars\n\n$progress-height: 1rem !default;\n$progress-font-size: ($font-size-base * .75) !default;\n$progress-bg: $gray-200 !default;\n$progress-border-radius: $border-radius !default;\n$progress-box-shadow: inset 0 .1rem .1rem rgba($black, .1) !default;\n$progress-bar-color: $white !default;\n$progress-bar-bg: theme-color(\"primary\") !default;\n$progress-bar-animation-timing: 1s linear infinite !default;\n$progress-bar-transition: width .6s ease !default;\n\n// List group\n\n$list-group-bg: $white !default;\n$list-group-border-color: rgba($black, .125) !default;\n$list-group-border-width: $border-width !default;\n$list-group-border-radius: $border-radius !default;\n\n$list-group-item-padding-y: .75rem !default;\n$list-group-item-padding-x: 1.25rem !default;\n\n$list-group-hover-bg: $gray-100 !default;\n$list-group-active-color: $component-active-color !default;\n$list-group-active-bg: $component-active-bg !default;\n$list-group-active-border-color: $list-group-active-bg !default;\n\n$list-group-disabled-color: $gray-600 !default;\n$list-group-disabled-bg: $list-group-bg !default;\n\n$list-group-action-color: $gray-700 !default;\n$list-group-action-hover-color: $list-group-action-color !default;\n\n$list-group-action-active-color: $body-color !default;\n$list-group-action-active-bg: $gray-200 !default;\n\n\n// Image thumbnails\n\n$thumbnail-padding: .25rem !default;\n$thumbnail-bg: $body-bg !default;\n$thumbnail-border-width: $border-width !default;\n$thumbnail-border-color: $gray-300 !default;\n$thumbnail-border-radius: $border-radius !default;\n$thumbnail-box-shadow: 0 1px 2px rgba($black, .075) !default;\n\n\n// Figures\n\n$figure-caption-font-size: 90% !default;\n$figure-caption-color: $gray-600 !default;\n\n\n// Breadcrumbs\n\n$breadcrumb-padding-y: .75rem !default;\n$breadcrumb-padding-x: 1rem !default;\n$breadcrumb-item-padding: .5rem !default;\n\n$breadcrumb-margin-bottom: 1rem !default;\n\n$breadcrumb-bg: $gray-200 !default;\n$breadcrumb-divider-color: $gray-600 !default;\n$breadcrumb-active-color: $gray-600 !default;\n$breadcrumb-divider: quote(\"/\") !default;\n\n$breadcrumb-border-radius: $border-radius !default;\n\n\n// Carousel\n\n$carousel-control-color: $white !default;\n$carousel-control-width: 15% !default;\n$carousel-control-opacity: .5 !default;\n\n$carousel-indicator-width: 30px !default;\n$carousel-indicator-height: 3px !default;\n$carousel-indicator-spacer: 3px !default;\n$carousel-indicator-active-bg: $white !default;\n\n$carousel-caption-width: 70% !default;\n$carousel-caption-color: $white !default;\n\n$carousel-control-icon-width: 20px !default;\n\n$carousel-control-prev-icon-bg: str-replace(url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E\"), \"#\", \"%23\") !default;\n$carousel-control-next-icon-bg: str-replace(url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E\"), \"#\", \"%23\") !default;\n\n$carousel-transition: transform .6s ease !default; // Define transform transition first if using multiple transitions (e.g., `transform 2s ease, opacity .5s ease-out`)\n\n\n// Close\n\n$close-font-size: $font-size-base * 1.5 !default;\n$close-font-weight: $font-weight-bold !default;\n$close-color: $black !default;\n$close-text-shadow: 0 1px 0 $white !default;\n\n// Code\n\n$code-font-size: 87.5% !default;\n$code-color: $pink !default;\n\n$kbd-padding-y: .2rem !default;\n$kbd-padding-x: .4rem !default;\n$kbd-font-size: $code-font-size !default;\n$kbd-color: $white !default;\n$kbd-bg: $gray-900 !default;\n\n$pre-color: $gray-900 !default;\n$pre-scrollable-max-height: 340px !default;\n\n\n// Printing\n$print-page-size: a3 !default;\n$print-body-min-width: map-get($grid-breakpoints, \"lg\") !default;\n","/*!\n * Bootstrap v4.1.1 (https://getbootstrap.com/)\n * Copyright 2011-2018 The Bootstrap Authors\n * Copyright 2011-2018 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n:root {\n --blue: #007bff;\n --indigo: #6610f2;\n --purple: #6f42c1;\n --pink: #e83e8c;\n --red: #dc3545;\n --orange: #fd7e14;\n --yellow: #ffc107;\n --green: #28a745;\n --teal: #20c997;\n --cyan: #17a2b8;\n --white: #fff;\n --gray: #6c757d;\n --gray-dark: #343a40;\n --primary: #007bff;\n --secondary: #6c757d;\n --success: #28a745;\n --info: #17a2b8;\n --warning: #ffc107;\n --danger: #dc3545;\n --light: #f8f9fa;\n --dark: #343a40;\n --breakpoint-xs: 0;\n --breakpoint-sm: 576px;\n --breakpoint-md: 768px;\n --breakpoint-lg: 992px;\n --breakpoint-xl: 1200px;\n --font-family-sans-serif: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -ms-text-size-adjust: 100%;\n -ms-overflow-style: scrollbar;\n -webkit-tap-highlight-color: transparent;\n}\n\n@-ms-viewport {\n width: device-width;\n}\n\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\ndfn {\n font-style: italic;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n -webkit-text-decoration-skip: objects;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):focus {\n outline: 0;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n -ms-overflow-style: scrollbar;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: 0.5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nbutton,\nhtml [type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n margin-bottom: 0.5rem;\n font-family: inherit;\n font-weight: 500;\n line-height: 1.2;\n color: inherit;\n}\n\nh1, .h1 {\n font-size: 2.5rem;\n}\n\nh2, .h2 {\n font-size: 2rem;\n}\n\nh3, .h3 {\n font-size: 1.75rem;\n}\n\nh4, .h4 {\n font-size: 1.5rem;\n}\n\nh5, .h5 {\n font-size: 1.25rem;\n}\n\nh6, .h6 {\n font-size: 1rem;\n}\n\n.lead {\n font-size: 1.25rem;\n font-weight: 300;\n}\n\n.display-1 {\n font-size: 6rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-2 {\n font-size: 5.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-3 {\n font-size: 4.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-4 {\n font-size: 3.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\nhr {\n margin-top: 1rem;\n margin-bottom: 1rem;\n border: 0;\n border-top: 1px solid rgba(0, 0, 0, 0.1);\n}\n\nsmall,\n.small {\n font-size: 80%;\n font-weight: 400;\n}\n\nmark,\n.mark {\n padding: 0.2em;\n background-color: #fcf8e3;\n}\n\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline-item {\n display: inline-block;\n}\n\n.list-inline-item:not(:last-child) {\n margin-right: 0.5rem;\n}\n\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\n\n.blockquote {\n margin-bottom: 1rem;\n font-size: 1.25rem;\n}\n\n.blockquote-footer {\n display: block;\n font-size: 80%;\n color: #6c757d;\n}\n\n.blockquote-footer::before {\n content: \"\\2014 \\00A0\";\n}\n\n.img-fluid {\n max-width: 100%;\n height: auto;\n}\n\n.img-thumbnail {\n padding: 0.25rem;\n background-color: #fff;\n border: 1px solid #dee2e6;\n border-radius: 0.25rem;\n max-width: 100%;\n height: auto;\n}\n\n.figure {\n display: inline-block;\n}\n\n.figure-img {\n margin-bottom: 0.5rem;\n line-height: 1;\n}\n\n.figure-caption {\n font-size: 90%;\n color: #6c757d;\n}\n\ncode {\n font-size: 87.5%;\n color: #e83e8c;\n word-break: break-word;\n}\n\na > code {\n color: inherit;\n}\n\nkbd {\n padding: 0.2rem 0.4rem;\n font-size: 87.5%;\n color: #fff;\n background-color: #212529;\n border-radius: 0.2rem;\n}\n\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: 700;\n}\n\npre {\n display: block;\n font-size: 87.5%;\n color: #212529;\n}\n\npre code {\n font-size: inherit;\n color: inherit;\n word-break: normal;\n}\n\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n\n.container {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n@media (min-width: 576px) {\n .container {\n max-width: 540px;\n }\n}\n\n@media (min-width: 768px) {\n .container {\n max-width: 720px;\n }\n}\n\n@media (min-width: 992px) {\n .container {\n max-width: 960px;\n }\n}\n\n@media (min-width: 1200px) {\n .container {\n max-width: 1140px;\n }\n}\n\n.container-fluid {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n.row {\n display: flex;\n flex-wrap: wrap;\n margin-right: -15px;\n margin-left: -15px;\n}\n\n.no-gutters {\n margin-right: 0;\n margin-left: 0;\n}\n\n.no-gutters > .col,\n.no-gutters > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n}\n\n.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col,\n.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm,\n.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md,\n.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg,\n.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl,\n.col-xl-auto {\n position: relative;\n width: 100%;\n min-height: 1px;\n padding-right: 15px;\n padding-left: 15px;\n}\n\n.col {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n}\n\n.col-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n}\n\n.col-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n}\n\n.col-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n}\n\n.col-3 {\n flex: 0 0 25%;\n max-width: 25%;\n}\n\n.col-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n}\n\n.col-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n}\n\n.col-6 {\n flex: 0 0 50%;\n max-width: 50%;\n}\n\n.col-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n}\n\n.col-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n}\n\n.col-9 {\n flex: 0 0 75%;\n max-width: 75%;\n}\n\n.col-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n}\n\n.col-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n}\n\n.col-12 {\n flex: 0 0 100%;\n max-width: 100%;\n}\n\n.order-first {\n order: -1;\n}\n\n.order-last {\n order: 13;\n}\n\n.order-0 {\n order: 0;\n}\n\n.order-1 {\n order: 1;\n}\n\n.order-2 {\n order: 2;\n}\n\n.order-3 {\n order: 3;\n}\n\n.order-4 {\n order: 4;\n}\n\n.order-5 {\n order: 5;\n}\n\n.order-6 {\n order: 6;\n}\n\n.order-7 {\n order: 7;\n}\n\n.order-8 {\n order: 8;\n}\n\n.order-9 {\n order: 9;\n}\n\n.order-10 {\n order: 10;\n}\n\n.order-11 {\n order: 11;\n}\n\n.order-12 {\n order: 12;\n}\n\n.offset-1 {\n margin-left: 8.333333%;\n}\n\n.offset-2 {\n margin-left: 16.666667%;\n}\n\n.offset-3 {\n margin-left: 25%;\n}\n\n.offset-4 {\n margin-left: 33.333333%;\n}\n\n.offset-5 {\n margin-left: 41.666667%;\n}\n\n.offset-6 {\n margin-left: 50%;\n}\n\n.offset-7 {\n margin-left: 58.333333%;\n}\n\n.offset-8 {\n margin-left: 66.666667%;\n}\n\n.offset-9 {\n margin-left: 75%;\n}\n\n.offset-10 {\n margin-left: 83.333333%;\n}\n\n.offset-11 {\n margin-left: 91.666667%;\n}\n\n@media (min-width: 576px) {\n .col-sm {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-sm-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-sm-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-sm-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-sm-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-sm-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-sm-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-sm-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-sm-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-sm-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-sm-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-sm-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-sm-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-sm-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-sm-first {\n order: -1;\n }\n .order-sm-last {\n order: 13;\n }\n .order-sm-0 {\n order: 0;\n }\n .order-sm-1 {\n order: 1;\n }\n .order-sm-2 {\n order: 2;\n }\n .order-sm-3 {\n order: 3;\n }\n .order-sm-4 {\n order: 4;\n }\n .order-sm-5 {\n order: 5;\n }\n .order-sm-6 {\n order: 6;\n }\n .order-sm-7 {\n order: 7;\n }\n .order-sm-8 {\n order: 8;\n }\n .order-sm-9 {\n order: 9;\n }\n .order-sm-10 {\n order: 10;\n }\n .order-sm-11 {\n order: 11;\n }\n .order-sm-12 {\n order: 12;\n }\n .offset-sm-0 {\n margin-left: 0;\n }\n .offset-sm-1 {\n margin-left: 8.333333%;\n }\n .offset-sm-2 {\n margin-left: 16.666667%;\n }\n .offset-sm-3 {\n margin-left: 25%;\n }\n .offset-sm-4 {\n margin-left: 33.333333%;\n }\n .offset-sm-5 {\n margin-left: 41.666667%;\n }\n .offset-sm-6 {\n margin-left: 50%;\n }\n .offset-sm-7 {\n margin-left: 58.333333%;\n }\n .offset-sm-8 {\n margin-left: 66.666667%;\n }\n .offset-sm-9 {\n margin-left: 75%;\n }\n .offset-sm-10 {\n margin-left: 83.333333%;\n }\n .offset-sm-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 768px) {\n .col-md {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-md-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-md-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-md-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-md-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-md-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-md-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-md-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-md-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-md-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-md-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-md-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-md-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-md-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-md-first {\n order: -1;\n }\n .order-md-last {\n order: 13;\n }\n .order-md-0 {\n order: 0;\n }\n .order-md-1 {\n order: 1;\n }\n .order-md-2 {\n order: 2;\n }\n .order-md-3 {\n order: 3;\n }\n .order-md-4 {\n order: 4;\n }\n .order-md-5 {\n order: 5;\n }\n .order-md-6 {\n order: 6;\n }\n .order-md-7 {\n order: 7;\n }\n .order-md-8 {\n order: 8;\n }\n .order-md-9 {\n order: 9;\n }\n .order-md-10 {\n order: 10;\n }\n .order-md-11 {\n order: 11;\n }\n .order-md-12 {\n order: 12;\n }\n .offset-md-0 {\n margin-left: 0;\n }\n .offset-md-1 {\n margin-left: 8.333333%;\n }\n .offset-md-2 {\n margin-left: 16.666667%;\n }\n .offset-md-3 {\n margin-left: 25%;\n }\n .offset-md-4 {\n margin-left: 33.333333%;\n }\n .offset-md-5 {\n margin-left: 41.666667%;\n }\n .offset-md-6 {\n margin-left: 50%;\n }\n .offset-md-7 {\n margin-left: 58.333333%;\n }\n .offset-md-8 {\n margin-left: 66.666667%;\n }\n .offset-md-9 {\n margin-left: 75%;\n }\n .offset-md-10 {\n margin-left: 83.333333%;\n }\n .offset-md-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 992px) {\n .col-lg {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-lg-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-lg-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-lg-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-lg-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-lg-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-lg-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-lg-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-lg-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-lg-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-lg-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-lg-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-lg-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-lg-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-lg-first {\n order: -1;\n }\n .order-lg-last {\n order: 13;\n }\n .order-lg-0 {\n order: 0;\n }\n .order-lg-1 {\n order: 1;\n }\n .order-lg-2 {\n order: 2;\n }\n .order-lg-3 {\n order: 3;\n }\n .order-lg-4 {\n order: 4;\n }\n .order-lg-5 {\n order: 5;\n }\n .order-lg-6 {\n order: 6;\n }\n .order-lg-7 {\n order: 7;\n }\n .order-lg-8 {\n order: 8;\n }\n .order-lg-9 {\n order: 9;\n }\n .order-lg-10 {\n order: 10;\n }\n .order-lg-11 {\n order: 11;\n }\n .order-lg-12 {\n order: 12;\n }\n .offset-lg-0 {\n margin-left: 0;\n }\n .offset-lg-1 {\n margin-left: 8.333333%;\n }\n .offset-lg-2 {\n margin-left: 16.666667%;\n }\n .offset-lg-3 {\n margin-left: 25%;\n }\n .offset-lg-4 {\n margin-left: 33.333333%;\n }\n .offset-lg-5 {\n margin-left: 41.666667%;\n }\n .offset-lg-6 {\n margin-left: 50%;\n }\n .offset-lg-7 {\n margin-left: 58.333333%;\n }\n .offset-lg-8 {\n margin-left: 66.666667%;\n }\n .offset-lg-9 {\n margin-left: 75%;\n }\n .offset-lg-10 {\n margin-left: 83.333333%;\n }\n .offset-lg-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 1200px) {\n .col-xl {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-xl-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-xl-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-xl-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-xl-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-xl-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-xl-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-xl-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-xl-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-xl-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-xl-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-xl-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-xl-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-xl-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-xl-first {\n order: -1;\n }\n .order-xl-last {\n order: 13;\n }\n .order-xl-0 {\n order: 0;\n }\n .order-xl-1 {\n order: 1;\n }\n .order-xl-2 {\n order: 2;\n }\n .order-xl-3 {\n order: 3;\n }\n .order-xl-4 {\n order: 4;\n }\n .order-xl-5 {\n order: 5;\n }\n .order-xl-6 {\n order: 6;\n }\n .order-xl-7 {\n order: 7;\n }\n .order-xl-8 {\n order: 8;\n }\n .order-xl-9 {\n order: 9;\n }\n .order-xl-10 {\n order: 10;\n }\n .order-xl-11 {\n order: 11;\n }\n .order-xl-12 {\n order: 12;\n }\n .offset-xl-0 {\n margin-left: 0;\n }\n .offset-xl-1 {\n margin-left: 8.333333%;\n }\n .offset-xl-2 {\n margin-left: 16.666667%;\n }\n .offset-xl-3 {\n margin-left: 25%;\n }\n .offset-xl-4 {\n margin-left: 33.333333%;\n }\n .offset-xl-5 {\n margin-left: 41.666667%;\n }\n .offset-xl-6 {\n margin-left: 50%;\n }\n .offset-xl-7 {\n margin-left: 58.333333%;\n }\n .offset-xl-8 {\n margin-left: 66.666667%;\n }\n .offset-xl-9 {\n margin-left: 75%;\n }\n .offset-xl-10 {\n margin-left: 83.333333%;\n }\n .offset-xl-11 {\n margin-left: 91.666667%;\n }\n}\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: 1rem;\n background-color: transparent;\n}\n\n.table th,\n.table td {\n padding: 0.75rem;\n vertical-align: top;\n border-top: 1px solid #dee2e6;\n}\n\n.table thead th {\n vertical-align: bottom;\n border-bottom: 2px solid #dee2e6;\n}\n\n.table tbody + tbody {\n border-top: 2px solid #dee2e6;\n}\n\n.table .table {\n background-color: #fff;\n}\n\n.table-sm th,\n.table-sm td {\n padding: 0.3rem;\n}\n\n.table-bordered {\n border: 1px solid #dee2e6;\n}\n\n.table-bordered th,\n.table-bordered td {\n border: 1px solid #dee2e6;\n}\n\n.table-bordered thead th,\n.table-bordered thead td {\n border-bottom-width: 2px;\n}\n\n.table-borderless th,\n.table-borderless td,\n.table-borderless thead th,\n.table-borderless tbody + tbody {\n border: 0;\n}\n\n.table-striped tbody tr:nth-of-type(odd) {\n background-color: rgba(0, 0, 0, 0.05);\n}\n\n.table-hover tbody tr:hover {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-primary,\n.table-primary > th,\n.table-primary > td {\n background-color: #b8daff;\n}\n\n.table-hover .table-primary:hover {\n background-color: #9fcdff;\n}\n\n.table-hover .table-primary:hover > td,\n.table-hover .table-primary:hover > th {\n background-color: #9fcdff;\n}\n\n.table-secondary,\n.table-secondary > th,\n.table-secondary > td {\n background-color: #d6d8db;\n}\n\n.table-hover .table-secondary:hover {\n background-color: #c8cbcf;\n}\n\n.table-hover .table-secondary:hover > td,\n.table-hover .table-secondary:hover > th {\n background-color: #c8cbcf;\n}\n\n.table-success,\n.table-success > th,\n.table-success > td {\n background-color: #c3e6cb;\n}\n\n.table-hover .table-success:hover {\n background-color: #b1dfbb;\n}\n\n.table-hover .table-success:hover > td,\n.table-hover .table-success:hover > th {\n background-color: #b1dfbb;\n}\n\n.table-info,\n.table-info > th,\n.table-info > td {\n background-color: #bee5eb;\n}\n\n.table-hover .table-info:hover {\n background-color: #abdde5;\n}\n\n.table-hover .table-info:hover > td,\n.table-hover .table-info:hover > th {\n background-color: #abdde5;\n}\n\n.table-warning,\n.table-warning > th,\n.table-warning > td {\n background-color: #ffeeba;\n}\n\n.table-hover .table-warning:hover {\n background-color: #ffe8a1;\n}\n\n.table-hover .table-warning:hover > td,\n.table-hover .table-warning:hover > th {\n background-color: #ffe8a1;\n}\n\n.table-danger,\n.table-danger > th,\n.table-danger > td {\n background-color: #f5c6cb;\n}\n\n.table-hover .table-danger:hover {\n background-color: #f1b0b7;\n}\n\n.table-hover .table-danger:hover > td,\n.table-hover .table-danger:hover > th {\n background-color: #f1b0b7;\n}\n\n.table-light,\n.table-light > th,\n.table-light > td {\n background-color: #fdfdfe;\n}\n\n.table-hover .table-light:hover {\n background-color: #ececf6;\n}\n\n.table-hover .table-light:hover > td,\n.table-hover .table-light:hover > th {\n background-color: #ececf6;\n}\n\n.table-dark,\n.table-dark > th,\n.table-dark > td {\n background-color: #c6c8ca;\n}\n\n.table-hover .table-dark:hover {\n background-color: #b9bbbe;\n}\n\n.table-hover .table-dark:hover > td,\n.table-hover .table-dark:hover > th {\n background-color: #b9bbbe;\n}\n\n.table-active,\n.table-active > th,\n.table-active > td {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover > td,\n.table-hover .table-active:hover > th {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table .thead-dark th {\n color: #fff;\n background-color: #212529;\n border-color: #32383e;\n}\n\n.table .thead-light th {\n color: #495057;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n\n.table-dark {\n color: #fff;\n background-color: #212529;\n}\n\n.table-dark th,\n.table-dark td,\n.table-dark thead th {\n border-color: #32383e;\n}\n\n.table-dark.table-bordered {\n border: 0;\n}\n\n.table-dark.table-striped tbody tr:nth-of-type(odd) {\n background-color: rgba(255, 255, 255, 0.05);\n}\n\n.table-dark.table-hover tbody tr:hover {\n background-color: rgba(255, 255, 255, 0.075);\n}\n\n@media (max-width: 575.98px) {\n .table-responsive-sm {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-sm > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 767.98px) {\n .table-responsive-md {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-md > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 991.98px) {\n .table-responsive-lg {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-lg > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 1199.98px) {\n .table-responsive-xl {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-xl > .table-bordered {\n border: 0;\n }\n}\n\n.table-responsive {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n}\n\n.table-responsive > .table-bordered {\n border: 0;\n}\n\n.form-control {\n display: block;\n width: 100%;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n line-height: 1.5;\n color: #495057;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .form-control {\n transition: none;\n }\n}\n\n.form-control::-ms-expand {\n background-color: transparent;\n border: 0;\n}\n\n.form-control:focus {\n color: #495057;\n background-color: #fff;\n border-color: #80bdff;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.form-control::placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control:disabled, .form-control[readonly] {\n background-color: #e9ecef;\n opacity: 1;\n}\n\nselect.form-control:not([size]):not([multiple]) {\n height: calc(2.25rem + 2px);\n}\n\nselect.form-control:focus::-ms-value {\n color: #495057;\n background-color: #fff;\n}\n\n.form-control-file,\n.form-control-range {\n display: block;\n width: 100%;\n}\n\n.col-form-label {\n padding-top: calc(0.375rem + 1px);\n padding-bottom: calc(0.375rem + 1px);\n margin-bottom: 0;\n font-size: inherit;\n line-height: 1.5;\n}\n\n.col-form-label-lg {\n padding-top: calc(0.5rem + 1px);\n padding-bottom: calc(0.5rem + 1px);\n font-size: 1.25rem;\n line-height: 1.5;\n}\n\n.col-form-label-sm {\n padding-top: calc(0.25rem + 1px);\n padding-bottom: calc(0.25rem + 1px);\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.form-control-plaintext {\n display: block;\n width: 100%;\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n margin-bottom: 0;\n line-height: 1.5;\n color: #212529;\n background-color: transparent;\n border: solid transparent;\n border-width: 1px 0;\n}\n\n.form-control-plaintext.form-control-sm, .input-group-sm > .form-control-plaintext.form-control,\n.input-group-sm > .input-group-prepend > .form-control-plaintext.input-group-text,\n.input-group-sm > .input-group-append > .form-control-plaintext.input-group-text,\n.input-group-sm > .input-group-prepend > .form-control-plaintext.btn,\n.input-group-sm > .input-group-append > .form-control-plaintext.btn, .form-control-plaintext.form-control-lg, .input-group-lg > .form-control-plaintext.form-control,\n.input-group-lg > .input-group-prepend > .form-control-plaintext.input-group-text,\n.input-group-lg > .input-group-append > .form-control-plaintext.input-group-text,\n.input-group-lg > .input-group-prepend > .form-control-plaintext.btn,\n.input-group-lg > .input-group-append > .form-control-plaintext.btn {\n padding-right: 0;\n padding-left: 0;\n}\n\n.form-control-sm, .input-group-sm > .form-control,\n.input-group-sm > .input-group-prepend > .input-group-text,\n.input-group-sm > .input-group-append > .input-group-text,\n.input-group-sm > .input-group-prepend > .btn,\n.input-group-sm > .input-group-append > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\nselect.form-control-sm:not([size]):not([multiple]), .input-group-sm > select.form-control:not([size]):not([multiple]),\n.input-group-sm > .input-group-prepend > select.input-group-text:not([size]):not([multiple]),\n.input-group-sm > .input-group-append > select.input-group-text:not([size]):not([multiple]),\n.input-group-sm > .input-group-prepend > select.btn:not([size]):not([multiple]),\n.input-group-sm > .input-group-append > select.btn:not([size]):not([multiple]) {\n height: calc(1.8125rem + 2px);\n}\n\n.form-control-lg, .input-group-lg > .form-control,\n.input-group-lg > .input-group-prepend > .input-group-text,\n.input-group-lg > .input-group-append > .input-group-text,\n.input-group-lg > .input-group-prepend > .btn,\n.input-group-lg > .input-group-append > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\nselect.form-control-lg:not([size]):not([multiple]), .input-group-lg > select.form-control:not([size]):not([multiple]),\n.input-group-lg > .input-group-prepend > select.input-group-text:not([size]):not([multiple]),\n.input-group-lg > .input-group-append > select.input-group-text:not([size]):not([multiple]),\n.input-group-lg > .input-group-prepend > select.btn:not([size]):not([multiple]),\n.input-group-lg > .input-group-append > select.btn:not([size]):not([multiple]) {\n height: calc(2.875rem + 2px);\n}\n\n.form-group {\n margin-bottom: 1rem;\n}\n\n.form-text {\n display: block;\n margin-top: 0.25rem;\n}\n\n.form-row {\n display: flex;\n flex-wrap: wrap;\n margin-right: -5px;\n margin-left: -5px;\n}\n\n.form-row > .col,\n.form-row > [class*=\"col-\"] {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.form-check {\n position: relative;\n display: block;\n padding-left: 1.25rem;\n}\n\n.form-check-input {\n position: absolute;\n margin-top: 0.3rem;\n margin-left: -1.25rem;\n}\n\n.form-check-input:disabled ~ .form-check-label {\n color: #6c757d;\n}\n\n.form-check-label {\n margin-bottom: 0;\n}\n\n.form-check-inline {\n display: inline-flex;\n align-items: center;\n padding-left: 0;\n margin-right: 0.75rem;\n}\n\n.form-check-inline .form-check-input {\n position: static;\n margin-top: 0;\n margin-right: 0.3125rem;\n margin-left: 0;\n}\n\n.valid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 80%;\n color: #28a745;\n}\n\n.valid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: .5rem;\n margin-top: .1rem;\n font-size: .875rem;\n line-height: 1;\n color: #fff;\n background-color: rgba(40, 167, 69, 0.8);\n border-radius: .2rem;\n}\n\n.was-validated .form-control:valid, .form-control.is-valid, .was-validated\n.custom-select:valid,\n.custom-select.is-valid {\n border-color: #28a745;\n}\n\n.was-validated .form-control:valid:focus, .form-control.is-valid:focus, .was-validated\n.custom-select:valid:focus,\n.custom-select.is-valid:focus {\n border-color: #28a745;\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .form-control:valid ~ .valid-feedback,\n.was-validated .form-control:valid ~ .valid-tooltip, .form-control.is-valid ~ .valid-feedback,\n.form-control.is-valid ~ .valid-tooltip, .was-validated\n.custom-select:valid ~ .valid-feedback,\n.was-validated\n.custom-select:valid ~ .valid-tooltip,\n.custom-select.is-valid ~ .valid-feedback,\n.custom-select.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .form-control-file:valid ~ .valid-feedback,\n.was-validated .form-control-file:valid ~ .valid-tooltip, .form-control-file.is-valid ~ .valid-feedback,\n.form-control-file.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {\n color: #28a745;\n}\n\n.was-validated .form-check-input:valid ~ .valid-feedback,\n.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback,\n.form-check-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label {\n color: #28a745;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before {\n background-color: #71dd8a;\n}\n\n.was-validated .custom-control-input:valid ~ .valid-feedback,\n.was-validated .custom-control-input:valid ~ .valid-tooltip, .custom-control-input.is-valid ~ .valid-feedback,\n.custom-control-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before {\n background-color: #34ce57;\n}\n\n.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label {\n border-color: #28a745;\n}\n\n.was-validated .custom-file-input:valid ~ .custom-file-label::before, .custom-file-input.is-valid ~ .custom-file-label::before {\n border-color: inherit;\n}\n\n.was-validated .custom-file-input:valid ~ .valid-feedback,\n.was-validated .custom-file-input:valid ~ .valid-tooltip, .custom-file-input.is-valid ~ .valid-feedback,\n.custom-file-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.invalid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 80%;\n color: #dc3545;\n}\n\n.invalid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: .5rem;\n margin-top: .1rem;\n font-size: .875rem;\n line-height: 1;\n color: #fff;\n background-color: rgba(220, 53, 69, 0.8);\n border-radius: .2rem;\n}\n\n.was-validated .form-control:invalid, .form-control.is-invalid, .was-validated\n.custom-select:invalid,\n.custom-select.is-invalid {\n border-color: #dc3545;\n}\n\n.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus, .was-validated\n.custom-select:invalid:focus,\n.custom-select.is-invalid:focus {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .form-control:invalid ~ .invalid-feedback,\n.was-validated .form-control:invalid ~ .invalid-tooltip, .form-control.is-invalid ~ .invalid-feedback,\n.form-control.is-invalid ~ .invalid-tooltip, .was-validated\n.custom-select:invalid ~ .invalid-feedback,\n.was-validated\n.custom-select:invalid ~ .invalid-tooltip,\n.custom-select.is-invalid ~ .invalid-feedback,\n.custom-select.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .form-control-file:invalid ~ .invalid-feedback,\n.was-validated .form-control-file:invalid ~ .invalid-tooltip, .form-control-file.is-invalid ~ .invalid-feedback,\n.form-control-file.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {\n color: #dc3545;\n}\n\n.was-validated .form-check-input:invalid ~ .invalid-feedback,\n.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback,\n.form-check-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label {\n color: #dc3545;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before {\n background-color: #efa2a9;\n}\n\n.was-validated .custom-control-input:invalid ~ .invalid-feedback,\n.was-validated .custom-control-input:invalid ~ .invalid-tooltip, .custom-control-input.is-invalid ~ .invalid-feedback,\n.custom-control-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before {\n background-color: #e4606d;\n}\n\n.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label {\n border-color: #dc3545;\n}\n\n.was-validated .custom-file-input:invalid ~ .custom-file-label::before, .custom-file-input.is-invalid ~ .custom-file-label::before {\n border-color: inherit;\n}\n\n.was-validated .custom-file-input:invalid ~ .invalid-feedback,\n.was-validated .custom-file-input:invalid ~ .invalid-tooltip, .custom-file-input.is-invalid ~ .invalid-feedback,\n.custom-file-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.form-inline {\n display: flex;\n flex-flow: row wrap;\n align-items: center;\n}\n\n.form-inline .form-check {\n width: 100%;\n}\n\n@media (min-width: 576px) {\n .form-inline label {\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 0;\n }\n .form-inline .form-group {\n display: flex;\n flex: 0 0 auto;\n flex-flow: row wrap;\n align-items: center;\n margin-bottom: 0;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-plaintext {\n display: inline-block;\n }\n .form-inline .input-group,\n .form-inline .custom-select {\n width: auto;\n }\n .form-inline .form-check {\n display: flex;\n align-items: center;\n justify-content: center;\n width: auto;\n padding-left: 0;\n }\n .form-inline .form-check-input {\n position: relative;\n margin-top: 0;\n margin-right: 0.25rem;\n margin-left: 0;\n }\n .form-inline .custom-control {\n align-items: center;\n justify-content: center;\n }\n .form-inline .custom-control-label {\n margin-bottom: 0;\n }\n}\n\n.btn {\n display: inline-block;\n font-weight: 400;\n text-align: center;\n white-space: nowrap;\n vertical-align: middle;\n user-select: none;\n border: 1px solid transparent;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n line-height: 1.5;\n border-radius: 0.25rem;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .btn {\n transition: none;\n }\n}\n\n.btn:hover, .btn:focus {\n text-decoration: none;\n}\n\n.btn:focus, .btn.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.btn.disabled, .btn:disabled {\n opacity: 0.65;\n}\n\n.btn:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.btn:not(:disabled):not(.disabled):active, .btn:not(:disabled):not(.disabled).active {\n background-image: none;\n}\n\na.btn.disabled,\nfieldset:disabled a.btn {\n pointer-events: none;\n}\n\n.btn-primary {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-primary:hover {\n color: #fff;\n background-color: #0069d9;\n border-color: #0062cc;\n}\n\n.btn-primary:focus, .btn-primary.focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-primary.disabled, .btn-primary:disabled {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active,\n.show > .btn-primary.dropdown-toggle {\n color: #fff;\n background-color: #0062cc;\n border-color: #005cbf;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-secondary {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-secondary:hover {\n color: #fff;\n background-color: #5a6268;\n border-color: #545b62;\n}\n\n.btn-secondary:focus, .btn-secondary.focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-secondary.disabled, .btn-secondary:disabled {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-secondary.dropdown-toggle {\n color: #fff;\n background-color: #545b62;\n border-color: #4e555b;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-success {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-success:hover {\n color: #fff;\n background-color: #218838;\n border-color: #1e7e34;\n}\n\n.btn-success:focus, .btn-success.focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-success.disabled, .btn-success:disabled {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active,\n.show > .btn-success.dropdown-toggle {\n color: #fff;\n background-color: #1e7e34;\n border-color: #1c7430;\n}\n\n.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-info {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-info:hover {\n color: #fff;\n background-color: #138496;\n border-color: #117a8b;\n}\n\n.btn-info:focus, .btn-info.focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-info.disabled, .btn-info:disabled {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active,\n.show > .btn-info.dropdown-toggle {\n color: #fff;\n background-color: #117a8b;\n border-color: #10707f;\n}\n\n.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-warning {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-warning:hover {\n color: #212529;\n background-color: #e0a800;\n border-color: #d39e00;\n}\n\n.btn-warning:focus, .btn-warning.focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-warning.disabled, .btn-warning:disabled {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active,\n.show > .btn-warning.dropdown-toggle {\n color: #212529;\n background-color: #d39e00;\n border-color: #c69500;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-danger {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-danger:hover {\n color: #fff;\n background-color: #c82333;\n border-color: #bd2130;\n}\n\n.btn-danger:focus, .btn-danger.focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-danger.disabled, .btn-danger:disabled {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active,\n.show > .btn-danger.dropdown-toggle {\n color: #fff;\n background-color: #bd2130;\n border-color: #b21f2d;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-light {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-light:hover {\n color: #212529;\n background-color: #e2e6ea;\n border-color: #dae0e5;\n}\n\n.btn-light:focus, .btn-light.focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-light.disabled, .btn-light:disabled {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active,\n.show > .btn-light.dropdown-toggle {\n color: #212529;\n background-color: #dae0e5;\n border-color: #d3d9df;\n}\n\n.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-dark {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-dark:hover {\n color: #fff;\n background-color: #23272b;\n border-color: #1d2124;\n}\n\n.btn-dark:focus, .btn-dark.focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-dark.disabled, .btn-dark:disabled {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active,\n.show > .btn-dark.dropdown-toggle {\n color: #fff;\n background-color: #1d2124;\n border-color: #171a1d;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-primary {\n color: #007bff;\n background-color: transparent;\n background-image: none;\n border-color: #007bff;\n}\n\n.btn-outline-primary:hover {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:focus, .btn-outline-primary.focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-primary.disabled, .btn-outline-primary:disabled {\n color: #007bff;\n background-color: transparent;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-primary.dropdown-toggle {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-secondary {\n color: #6c757d;\n background-color: transparent;\n background-image: none;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:hover {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:focus, .btn-outline-secondary.focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-secondary.disabled, .btn-outline-secondary:disabled {\n color: #6c757d;\n background-color: transparent;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-secondary.dropdown-toggle {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-success {\n color: #28a745;\n background-color: transparent;\n background-image: none;\n border-color: #28a745;\n}\n\n.btn-outline-success:hover {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:focus, .btn-outline-success.focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-success.disabled, .btn-outline-success:disabled {\n color: #28a745;\n background-color: transparent;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active,\n.show > .btn-outline-success.dropdown-toggle {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-info {\n color: #17a2b8;\n background-color: transparent;\n background-image: none;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:hover {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:focus, .btn-outline-info.focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-info.disabled, .btn-outline-info:disabled {\n color: #17a2b8;\n background-color: transparent;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active,\n.show > .btn-outline-info.dropdown-toggle {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-warning {\n color: #ffc107;\n background-color: transparent;\n background-image: none;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:hover {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:focus, .btn-outline-warning.focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-warning.disabled, .btn-outline-warning:disabled {\n color: #ffc107;\n background-color: transparent;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active,\n.show > .btn-outline-warning.dropdown-toggle {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-danger {\n color: #dc3545;\n background-color: transparent;\n background-image: none;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:hover {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:focus, .btn-outline-danger.focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-danger.disabled, .btn-outline-danger:disabled {\n color: #dc3545;\n background-color: transparent;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active,\n.show > .btn-outline-danger.dropdown-toggle {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-light {\n color: #f8f9fa;\n background-color: transparent;\n background-image: none;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:hover {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:focus, .btn-outline-light.focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-light.disabled, .btn-outline-light:disabled {\n color: #f8f9fa;\n background-color: transparent;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active,\n.show > .btn-outline-light.dropdown-toggle {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-dark {\n color: #343a40;\n background-color: transparent;\n background-image: none;\n border-color: #343a40;\n}\n\n.btn-outline-dark:hover {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:focus, .btn-outline-dark.focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-dark.disabled, .btn-outline-dark:disabled {\n color: #343a40;\n background-color: transparent;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active,\n.show > .btn-outline-dark.dropdown-toggle {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-link {\n font-weight: 400;\n color: #007bff;\n background-color: transparent;\n}\n\n.btn-link:hover {\n color: #0056b3;\n text-decoration: underline;\n background-color: transparent;\n border-color: transparent;\n}\n\n.btn-link:focus, .btn-link.focus {\n text-decoration: underline;\n border-color: transparent;\n box-shadow: none;\n}\n\n.btn-link:disabled, .btn-link.disabled {\n color: #6c757d;\n pointer-events: none;\n}\n\n.btn-lg, .btn-group-lg > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\n.btn-sm, .btn-group-sm > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\n.btn-block {\n display: block;\n width: 100%;\n}\n\n.btn-block + .btn-block {\n margin-top: 0.5rem;\n}\n\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n\n.fade {\n transition: opacity 0.15s linear;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .fade {\n transition: none;\n }\n}\n\n.fade:not(.show) {\n opacity: 0;\n}\n\n.collapse:not(.show) {\n display: none;\n}\n\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n transition: height 0.35s ease;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .collapsing {\n transition: none;\n }\n}\n\n.dropup,\n.dropright,\n.dropdown,\n.dropleft {\n position: relative;\n}\n\n.dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid;\n border-right: 0.3em solid transparent;\n border-bottom: 0;\n border-left: 0.3em solid transparent;\n}\n\n.dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 10rem;\n padding: 0.5rem 0;\n margin: 0.125rem 0 0;\n font-size: 1rem;\n color: #212529;\n text-align: left;\n list-style: none;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 0.25rem;\n}\n\n.dropdown-menu-right {\n right: 0;\n left: auto;\n}\n\n.dropup .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-top: 0;\n margin-bottom: 0.125rem;\n}\n\n.dropup .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0;\n border-right: 0.3em solid transparent;\n border-bottom: 0.3em solid;\n border-left: 0.3em solid transparent;\n}\n\n.dropup .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropright .dropdown-menu {\n top: 0;\n right: auto;\n left: 100%;\n margin-top: 0;\n margin-left: 0.125rem;\n}\n\n.dropright .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0;\n border-bottom: 0.3em solid transparent;\n border-left: 0.3em solid;\n}\n\n.dropright .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropright .dropdown-toggle::after {\n vertical-align: 0;\n}\n\n.dropleft .dropdown-menu {\n top: 0;\n right: 100%;\n left: auto;\n margin-top: 0;\n margin-right: 0.125rem;\n}\n\n.dropleft .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n}\n\n.dropleft .dropdown-toggle::after {\n display: none;\n}\n\n.dropleft .dropdown-toggle::before {\n display: inline-block;\n width: 0;\n height: 0;\n margin-right: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0.3em solid;\n border-bottom: 0.3em solid transparent;\n}\n\n.dropleft .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropleft .dropdown-toggle::before {\n vertical-align: 0;\n}\n\n.dropdown-menu[x-placement^=\"top\"], .dropdown-menu[x-placement^=\"right\"], .dropdown-menu[x-placement^=\"bottom\"], .dropdown-menu[x-placement^=\"left\"] {\n right: auto;\n bottom: auto;\n}\n\n.dropdown-divider {\n height: 0;\n margin: 0.5rem 0;\n overflow: hidden;\n border-top: 1px solid #e9ecef;\n}\n\n.dropdown-item {\n display: block;\n width: 100%;\n padding: 0.25rem 1.5rem;\n clear: both;\n font-weight: 400;\n color: #212529;\n text-align: inherit;\n white-space: nowrap;\n background-color: transparent;\n border: 0;\n}\n\n.dropdown-item:hover, .dropdown-item:focus {\n color: #16181b;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n\n.dropdown-item.active, .dropdown-item:active {\n color: #fff;\n text-decoration: none;\n background-color: #007bff;\n}\n\n.dropdown-item.disabled, .dropdown-item:disabled {\n color: #6c757d;\n background-color: transparent;\n}\n\n.dropdown-menu.show {\n display: block;\n}\n\n.dropdown-header {\n display: block;\n padding: 0.5rem 1.5rem;\n margin-bottom: 0;\n font-size: 0.875rem;\n color: #6c757d;\n white-space: nowrap;\n}\n\n.dropdown-item-text {\n display: block;\n padding: 0.25rem 1.5rem;\n color: #212529;\n}\n\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-flex;\n vertical-align: middle;\n}\n\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n flex: 0 1 auto;\n}\n\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover {\n z-index: 1;\n}\n\n.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active,\n.btn-group-vertical > .btn:focus,\n.btn-group-vertical > .btn:active,\n.btn-group-vertical > .btn.active {\n z-index: 1;\n}\n\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group,\n.btn-group-vertical .btn + .btn,\n.btn-group-vertical .btn + .btn-group,\n.btn-group-vertical .btn-group + .btn,\n.btn-group-vertical .btn-group + .btn-group {\n margin-left: -1px;\n}\n\n.btn-toolbar {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-start;\n}\n\n.btn-toolbar .input-group {\n width: auto;\n}\n\n.btn-group > .btn:first-child {\n margin-left: 0;\n}\n\n.btn-group > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group > .btn-group:not(:last-child) > .btn {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.btn-group > .btn:not(:first-child),\n.btn-group > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.dropdown-toggle-split {\n padding-right: 0.5625rem;\n padding-left: 0.5625rem;\n}\n\n.dropdown-toggle-split::after,\n.dropup .dropdown-toggle-split::after,\n.dropright .dropdown-toggle-split::after {\n margin-left: 0;\n}\n\n.dropleft .dropdown-toggle-split::before {\n margin-right: 0;\n}\n\n.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {\n padding-right: 0.375rem;\n padding-left: 0.375rem;\n}\n\n.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {\n padding-right: 0.75rem;\n padding-left: 0.75rem;\n}\n\n.btn-group-vertical {\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n\n.btn-group-vertical .btn,\n.btn-group-vertical .btn-group {\n width: 100%;\n}\n\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n}\n\n.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group-vertical > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.btn-group-toggle > .btn,\n.btn-group-toggle > .btn-group > .btn {\n margin-bottom: 0;\n}\n\n.btn-group-toggle > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn input[type=\"checkbox\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n\n.input-group {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-items: stretch;\n width: 100%;\n}\n\n.input-group > .form-control,\n.input-group > .custom-select,\n.input-group > .custom-file {\n position: relative;\n flex: 1 1 auto;\n width: 1%;\n margin-bottom: 0;\n}\n\n.input-group > .form-control:focus,\n.input-group > .custom-select:focus,\n.input-group > .custom-file:focus {\n z-index: 3;\n}\n\n.input-group > .form-control + .form-control,\n.input-group > .form-control + .custom-select,\n.input-group > .form-control + .custom-file,\n.input-group > .custom-select + .form-control,\n.input-group > .custom-select + .custom-select,\n.input-group > .custom-select + .custom-file,\n.input-group > .custom-file + .form-control,\n.input-group > .custom-file + .custom-select,\n.input-group > .custom-file + .custom-file {\n margin-left: -1px;\n}\n\n.input-group > .form-control:not(:last-child),\n.input-group > .custom-select:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .form-control:not(:first-child),\n.input-group > .custom-select:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.input-group > .custom-file {\n display: flex;\n align-items: center;\n}\n\n.input-group > .custom-file:not(:last-child) .custom-file-label,\n.input-group > .custom-file:not(:last-child) .custom-file-label::after {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .custom-file:not(:first-child) .custom-file-label {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.input-group-prepend,\n.input-group-append {\n display: flex;\n}\n\n.input-group-prepend .btn,\n.input-group-append .btn {\n position: relative;\n z-index: 2;\n}\n\n.input-group-prepend .btn + .btn,\n.input-group-prepend .btn + .input-group-text,\n.input-group-prepend .input-group-text + .input-group-text,\n.input-group-prepend .input-group-text + .btn,\n.input-group-append .btn + .btn,\n.input-group-append .btn + .input-group-text,\n.input-group-append .input-group-text + .input-group-text,\n.input-group-append .input-group-text + .btn {\n margin-left: -1px;\n}\n\n.input-group-prepend {\n margin-right: -1px;\n}\n\n.input-group-append {\n margin-left: -1px;\n}\n\n.input-group-text {\n display: flex;\n align-items: center;\n padding: 0.375rem 0.75rem;\n margin-bottom: 0;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #495057;\n text-align: center;\n white-space: nowrap;\n background-color: #e9ecef;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.input-group-text input[type=\"radio\"],\n.input-group-text input[type=\"checkbox\"] {\n margin-top: 0;\n}\n\n.input-group > .input-group-prepend > .btn,\n.input-group > .input-group-prepend > .input-group-text,\n.input-group > .input-group-append:not(:last-child) > .btn,\n.input-group > .input-group-append:not(:last-child) > .input-group-text,\n.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .input-group-append > .btn,\n.input-group > .input-group-append > .input-group-text,\n.input-group > .input-group-prepend:not(:first-child) > .btn,\n.input-group > .input-group-prepend:not(:first-child) > .input-group-text,\n.input-group > .input-group-prepend:first-child > .btn:not(:first-child),\n.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.custom-control {\n position: relative;\n display: block;\n min-height: 1.5rem;\n padding-left: 1.5rem;\n}\n\n.custom-control-inline {\n display: inline-flex;\n margin-right: 1rem;\n}\n\n.custom-control-input {\n position: absolute;\n z-index: -1;\n opacity: 0;\n}\n\n.custom-control-input:checked ~ .custom-control-label::before {\n color: #fff;\n background-color: #007bff;\n}\n\n.custom-control-input:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-control-input:active ~ .custom-control-label::before {\n color: #fff;\n background-color: #b3d7ff;\n}\n\n.custom-control-input:disabled ~ .custom-control-label {\n color: #6c757d;\n}\n\n.custom-control-input:disabled ~ .custom-control-label::before {\n background-color: #e9ecef;\n}\n\n.custom-control-label {\n position: relative;\n margin-bottom: 0;\n}\n\n.custom-control-label::before {\n position: absolute;\n top: 0.25rem;\n left: -1.5rem;\n display: block;\n width: 1rem;\n height: 1rem;\n pointer-events: none;\n content: \"\";\n user-select: none;\n background-color: #dee2e6;\n}\n\n.custom-control-label::after {\n position: absolute;\n top: 0.25rem;\n left: -1.5rem;\n display: block;\n width: 1rem;\n height: 1rem;\n content: \"\";\n background-repeat: no-repeat;\n background-position: center center;\n background-size: 50% 50%;\n}\n\n.custom-checkbox .custom-control-label::before {\n border-radius: 0.25rem;\n}\n\n.custom-checkbox .custom-control-input:checked ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E\");\n}\n\n.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-radio .custom-control-label::before {\n border-radius: 50%;\n}\n\n.custom-radio .custom-control-input:checked ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-radio .custom-control-input:checked ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E\");\n}\n\n.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-select {\n display: inline-block;\n width: 100%;\n height: calc(2.25rem + 2px);\n padding: 0.375rem 1.75rem 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n vertical-align: middle;\n background: #fff url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E\") no-repeat right 0.75rem center;\n background-size: 8px 10px;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n appearance: none;\n}\n\n.custom-select:focus {\n border-color: #80bdff;\n outline: 0;\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075), 0 0 5px rgba(128, 189, 255, 0.5);\n}\n\n.custom-select:focus::-ms-value {\n color: #495057;\n background-color: #fff;\n}\n\n.custom-select[multiple], .custom-select[size]:not([size=\"1\"]) {\n height: auto;\n padding-right: 0.75rem;\n background-image: none;\n}\n\n.custom-select:disabled {\n color: #6c757d;\n background-color: #e9ecef;\n}\n\n.custom-select::-ms-expand {\n opacity: 0;\n}\n\n.custom-select-sm {\n height: calc(1.8125rem + 2px);\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n font-size: 75%;\n}\n\n.custom-select-lg {\n height: calc(2.875rem + 2px);\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n font-size: 125%;\n}\n\n.custom-file {\n position: relative;\n display: inline-block;\n width: 100%;\n height: calc(2.25rem + 2px);\n margin-bottom: 0;\n}\n\n.custom-file-input {\n position: relative;\n z-index: 2;\n width: 100%;\n height: calc(2.25rem + 2px);\n margin: 0;\n opacity: 0;\n}\n\n.custom-file-input:focus ~ .custom-file-label {\n border-color: #80bdff;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-file-input:focus ~ .custom-file-label::after {\n border-color: #80bdff;\n}\n\n.custom-file-input:lang(en) ~ .custom-file-label::after {\n content: \"Browse\";\n}\n\n.custom-file-label {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1;\n height: calc(2.25rem + 2px);\n padding: 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n background-color: #fff;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.custom-file-label::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n z-index: 3;\n display: block;\n height: 2.25rem;\n padding: 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n content: \"Browse\";\n background-color: #e9ecef;\n border-left: 1px solid #ced4da;\n border-radius: 0 0.25rem 0.25rem 0;\n}\n\n.custom-range {\n width: 100%;\n padding-left: 0;\n background-color: transparent;\n appearance: none;\n}\n\n.custom-range:focus {\n outline: none;\n}\n\n.custom-range::-moz-focus-outer {\n border: 0;\n}\n\n.custom-range::-webkit-slider-thumb {\n width: 1rem;\n height: 1rem;\n margin-top: -0.25rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n appearance: none;\n}\n\n.custom-range::-webkit-slider-thumb:focus {\n outline: none;\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-webkit-slider-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-webkit-slider-runnable-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: #dee2e6;\n border-color: transparent;\n border-radius: 1rem;\n}\n\n.custom-range::-moz-range-thumb {\n width: 1rem;\n height: 1rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n appearance: none;\n}\n\n.custom-range::-moz-range-thumb:focus {\n outline: none;\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-moz-range-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-moz-range-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: #dee2e6;\n border-color: transparent;\n border-radius: 1rem;\n}\n\n.custom-range::-ms-thumb {\n width: 1rem;\n height: 1rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n appearance: none;\n}\n\n.custom-range::-ms-thumb:focus {\n outline: none;\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-ms-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-ms-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: transparent;\n border-color: transparent;\n border-width: 0.5rem;\n}\n\n.custom-range::-ms-fill-lower {\n background-color: #dee2e6;\n border-radius: 1rem;\n}\n\n.custom-range::-ms-fill-upper {\n margin-right: 15px;\n background-color: #dee2e6;\n border-radius: 1rem;\n}\n\n.nav {\n display: flex;\n flex-wrap: wrap;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.nav-link {\n display: block;\n padding: 0.5rem 1rem;\n}\n\n.nav-link:hover, .nav-link:focus {\n text-decoration: none;\n}\n\n.nav-link.disabled {\n color: #6c757d;\n}\n\n.nav-tabs {\n border-bottom: 1px solid #dee2e6;\n}\n\n.nav-tabs .nav-item {\n margin-bottom: -1px;\n}\n\n.nav-tabs .nav-link {\n border: 1px solid transparent;\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {\n border-color: #e9ecef #e9ecef #dee2e6;\n}\n\n.nav-tabs .nav-link.disabled {\n color: #6c757d;\n background-color: transparent;\n border-color: transparent;\n}\n\n.nav-tabs .nav-link.active,\n.nav-tabs .nav-item.show .nav-link {\n color: #495057;\n background-color: #fff;\n border-color: #dee2e6 #dee2e6 #fff;\n}\n\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.nav-pills .nav-link {\n border-radius: 0.25rem;\n}\n\n.nav-pills .nav-link.active,\n.nav-pills .show > .nav-link {\n color: #fff;\n background-color: #007bff;\n}\n\n.nav-fill .nav-item {\n flex: 1 1 auto;\n text-align: center;\n}\n\n.nav-justified .nav-item {\n flex-basis: 0;\n flex-grow: 1;\n text-align: center;\n}\n\n.tab-content > .tab-pane {\n display: none;\n}\n\n.tab-content > .active {\n display: block;\n}\n\n.navbar {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n padding: 0.5rem 1rem;\n}\n\n.navbar > .container,\n.navbar > .container-fluid {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n}\n\n.navbar-brand {\n display: inline-block;\n padding-top: 0.3125rem;\n padding-bottom: 0.3125rem;\n margin-right: 1rem;\n font-size: 1.25rem;\n line-height: inherit;\n white-space: nowrap;\n}\n\n.navbar-brand:hover, .navbar-brand:focus {\n text-decoration: none;\n}\n\n.navbar-nav {\n display: flex;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.navbar-nav .nav-link {\n padding-right: 0;\n padding-left: 0;\n}\n\n.navbar-nav .dropdown-menu {\n position: static;\n float: none;\n}\n\n.navbar-text {\n display: inline-block;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n\n.navbar-collapse {\n flex-basis: 100%;\n flex-grow: 1;\n align-items: center;\n}\n\n.navbar-toggler {\n padding: 0.25rem 0.75rem;\n font-size: 1.25rem;\n line-height: 1;\n background-color: transparent;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.navbar-toggler:hover, .navbar-toggler:focus {\n text-decoration: none;\n}\n\n.navbar-toggler:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.navbar-toggler-icon {\n display: inline-block;\n width: 1.5em;\n height: 1.5em;\n vertical-align: middle;\n content: \"\";\n background: no-repeat center center;\n background-size: 100% 100%;\n}\n\n@media (max-width: 575.98px) {\n .navbar-expand-sm > .container,\n .navbar-expand-sm > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 576px) {\n .navbar-expand-sm {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-sm .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-sm .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-sm .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-sm > .container,\n .navbar-expand-sm > .container-fluid {\n flex-wrap: nowrap;\n }\n .navbar-expand-sm .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-sm .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 767.98px) {\n .navbar-expand-md > .container,\n .navbar-expand-md > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 768px) {\n .navbar-expand-md {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-md .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-md .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-md .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-md > .container,\n .navbar-expand-md > .container-fluid {\n flex-wrap: nowrap;\n }\n .navbar-expand-md .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-md .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 991.98px) {\n .navbar-expand-lg > .container,\n .navbar-expand-lg > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 992px) {\n .navbar-expand-lg {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-lg .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-lg .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-lg .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-lg > .container,\n .navbar-expand-lg > .container-fluid {\n flex-wrap: nowrap;\n }\n .navbar-expand-lg .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-lg .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 1199.98px) {\n .navbar-expand-xl > .container,\n .navbar-expand-xl > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 1200px) {\n .navbar-expand-xl {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-xl .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-xl .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-xl .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-xl > .container,\n .navbar-expand-xl > .container-fluid {\n flex-wrap: nowrap;\n }\n .navbar-expand-xl .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-xl .navbar-toggler {\n display: none;\n }\n}\n\n.navbar-expand {\n flex-flow: row nowrap;\n justify-content: flex-start;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n}\n\n.navbar-expand .navbar-nav {\n flex-direction: row;\n}\n\n.navbar-expand .navbar-nav .dropdown-menu {\n position: absolute;\n}\n\n.navbar-expand .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid {\n flex-wrap: nowrap;\n}\n\n.navbar-expand .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n}\n\n.navbar-expand .navbar-toggler {\n display: none;\n}\n\n.navbar-light .navbar-brand {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-nav .nav-link {\n color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus {\n color: rgba(0, 0, 0, 0.7);\n}\n\n.navbar-light .navbar-nav .nav-link.disabled {\n color: rgba(0, 0, 0, 0.3);\n}\n\n.navbar-light .navbar-nav .show > .nav-link,\n.navbar-light .navbar-nav .active > .nav-link,\n.navbar-light .navbar-nav .nav-link.show,\n.navbar-light .navbar-nav .nav-link.active {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-toggler {\n color: rgba(0, 0, 0, 0.5);\n border-color: rgba(0, 0, 0, 0.1);\n}\n\n.navbar-light .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\");\n}\n\n.navbar-light .navbar-text {\n color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-text a {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-dark .navbar-brand {\n color: #fff;\n}\n\n.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus {\n color: #fff;\n}\n\n.navbar-dark .navbar-nav .nav-link {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus {\n color: rgba(255, 255, 255, 0.75);\n}\n\n.navbar-dark .navbar-nav .nav-link.disabled {\n color: rgba(255, 255, 255, 0.25);\n}\n\n.navbar-dark .navbar-nav .show > .nav-link,\n.navbar-dark .navbar-nav .active > .nav-link,\n.navbar-dark .navbar-nav .nav-link.show,\n.navbar-dark .navbar-nav .nav-link.active {\n color: #fff;\n}\n\n.navbar-dark .navbar-toggler {\n color: rgba(255, 255, 255, 0.5);\n border-color: rgba(255, 255, 255, 0.1);\n}\n\n.navbar-dark .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\");\n}\n\n.navbar-dark .navbar-text {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.navbar-dark .navbar-text a {\n color: #fff;\n}\n\n.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus {\n color: #fff;\n}\n\n.card {\n position: relative;\n display: flex;\n flex-direction: column;\n min-width: 0;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: border-box;\n border: 1px solid rgba(0, 0, 0, 0.125);\n border-radius: 0.25rem;\n}\n\n.card > hr {\n margin-right: 0;\n margin-left: 0;\n}\n\n.card > .list-group:first-child .list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.card > .list-group:last-child .list-group-item:last-child {\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.card-body {\n flex: 1 1 auto;\n padding: 1.25rem;\n}\n\n.card-title {\n margin-bottom: 0.75rem;\n}\n\n.card-subtitle {\n margin-top: -0.375rem;\n margin-bottom: 0;\n}\n\n.card-text:last-child {\n margin-bottom: 0;\n}\n\n.card-link:hover {\n text-decoration: none;\n}\n\n.card-link + .card-link {\n margin-left: 1.25rem;\n}\n\n.card-header {\n padding: 0.75rem 1.25rem;\n margin-bottom: 0;\n background-color: rgba(0, 0, 0, 0.03);\n border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card-header:first-child {\n border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0;\n}\n\n.card-header + .list-group .list-group-item:first-child {\n border-top: 0;\n}\n\n.card-footer {\n padding: 0.75rem 1.25rem;\n background-color: rgba(0, 0, 0, 0.03);\n border-top: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card-footer:last-child {\n border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px);\n}\n\n.card-header-tabs {\n margin-right: -0.625rem;\n margin-bottom: -0.75rem;\n margin-left: -0.625rem;\n border-bottom: 0;\n}\n\n.card-header-pills {\n margin-right: -0.625rem;\n margin-left: -0.625rem;\n}\n\n.card-img-overlay {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n padding: 1.25rem;\n}\n\n.card-img {\n width: 100%;\n border-radius: calc(0.25rem - 1px);\n}\n\n.card-img-top {\n width: 100%;\n border-top-left-radius: calc(0.25rem - 1px);\n border-top-right-radius: calc(0.25rem - 1px);\n}\n\n.card-img-bottom {\n width: 100%;\n border-bottom-right-radius: calc(0.25rem - 1px);\n border-bottom-left-radius: calc(0.25rem - 1px);\n}\n\n.card-deck {\n display: flex;\n flex-direction: column;\n}\n\n.card-deck .card {\n margin-bottom: 15px;\n}\n\n@media (min-width: 576px) {\n .card-deck {\n flex-flow: row wrap;\n margin-right: -15px;\n margin-left: -15px;\n }\n .card-deck .card {\n display: flex;\n flex: 1 0 0%;\n flex-direction: column;\n margin-right: 15px;\n margin-bottom: 0;\n margin-left: 15px;\n }\n}\n\n.card-group {\n display: flex;\n flex-direction: column;\n}\n\n.card-group > .card {\n margin-bottom: 15px;\n}\n\n@media (min-width: 576px) {\n .card-group {\n flex-flow: row wrap;\n }\n .card-group > .card {\n flex: 1 0 0%;\n margin-bottom: 0;\n }\n .card-group > .card + .card {\n margin-left: 0;\n border-left: 0;\n }\n .card-group > .card:first-child {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n }\n .card-group > .card:first-child .card-img-top,\n .card-group > .card:first-child .card-header {\n border-top-right-radius: 0;\n }\n .card-group > .card:first-child .card-img-bottom,\n .card-group > .card:first-child .card-footer {\n border-bottom-right-radius: 0;\n }\n .card-group > .card:last-child {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n .card-group > .card:last-child .card-img-top,\n .card-group > .card:last-child .card-header {\n border-top-left-radius: 0;\n }\n .card-group > .card:last-child .card-img-bottom,\n .card-group > .card:last-child .card-footer {\n border-bottom-left-radius: 0;\n }\n .card-group > .card:only-child {\n border-radius: 0.25rem;\n }\n .card-group > .card:only-child .card-img-top,\n .card-group > .card:only-child .card-header {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n }\n .card-group > .card:only-child .card-img-bottom,\n .card-group > .card:only-child .card-footer {\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n }\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) {\n border-radius: 0;\n }\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-top,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-header,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-footer {\n border-radius: 0;\n }\n}\n\n.card-columns .card {\n margin-bottom: 0.75rem;\n}\n\n@media (min-width: 576px) {\n .card-columns {\n column-count: 3;\n column-gap: 1.25rem;\n orphans: 1;\n widows: 1;\n }\n .card-columns .card {\n display: inline-block;\n width: 100%;\n }\n}\n\n.accordion .card:not(:first-of-type):not(:last-of-type) {\n border-bottom: 0;\n border-radius: 0;\n}\n\n.accordion .card:not(:first-of-type) .card-header:first-child {\n border-radius: 0;\n}\n\n.accordion .card:first-of-type {\n border-bottom: 0;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.accordion .card:last-of-type {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.breadcrumb {\n display: flex;\n flex-wrap: wrap;\n padding: 0.75rem 1rem;\n margin-bottom: 1rem;\n list-style: none;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.breadcrumb-item + .breadcrumb-item {\n padding-left: 0.5rem;\n}\n\n.breadcrumb-item + .breadcrumb-item::before {\n display: inline-block;\n padding-right: 0.5rem;\n color: #6c757d;\n content: \"/\";\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n text-decoration: underline;\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n text-decoration: none;\n}\n\n.breadcrumb-item.active {\n color: #6c757d;\n}\n\n.pagination {\n display: flex;\n padding-left: 0;\n list-style: none;\n border-radius: 0.25rem;\n}\n\n.page-link {\n position: relative;\n display: block;\n padding: 0.5rem 0.75rem;\n margin-left: -1px;\n line-height: 1.25;\n color: #007bff;\n background-color: #fff;\n border: 1px solid #dee2e6;\n}\n\n.page-link:hover {\n z-index: 2;\n color: #0056b3;\n text-decoration: none;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n\n.page-link:focus {\n z-index: 2;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.page-link:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.page-item:first-child .page-link {\n margin-left: 0;\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.page-item:last-child .page-link {\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n}\n\n.page-item.active .page-link {\n z-index: 1;\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.page-item.disabled .page-link {\n color: #6c757d;\n pointer-events: none;\n cursor: auto;\n background-color: #fff;\n border-color: #dee2e6;\n}\n\n.pagination-lg .page-link {\n padding: 0.75rem 1.5rem;\n font-size: 1.25rem;\n line-height: 1.5;\n}\n\n.pagination-lg .page-item:first-child .page-link {\n border-top-left-radius: 0.3rem;\n border-bottom-left-radius: 0.3rem;\n}\n\n.pagination-lg .page-item:last-child .page-link {\n border-top-right-radius: 0.3rem;\n border-bottom-right-radius: 0.3rem;\n}\n\n.pagination-sm .page-link {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.pagination-sm .page-item:first-child .page-link {\n border-top-left-radius: 0.2rem;\n border-bottom-left-radius: 0.2rem;\n}\n\n.pagination-sm .page-item:last-child .page-link {\n border-top-right-radius: 0.2rem;\n border-bottom-right-radius: 0.2rem;\n}\n\n.badge {\n display: inline-block;\n padding: 0.25em 0.4em;\n font-size: 75%;\n font-weight: 700;\n line-height: 1;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: 0.25rem;\n}\n\n.badge:empty {\n display: none;\n}\n\n.btn .badge {\n position: relative;\n top: -1px;\n}\n\n.badge-pill {\n padding-right: 0.6em;\n padding-left: 0.6em;\n border-radius: 10rem;\n}\n\n.badge-primary {\n color: #fff;\n background-color: #007bff;\n}\n\n.badge-primary[href]:hover, .badge-primary[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #0062cc;\n}\n\n.badge-secondary {\n color: #fff;\n background-color: #6c757d;\n}\n\n.badge-secondary[href]:hover, .badge-secondary[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #545b62;\n}\n\n.badge-success {\n color: #fff;\n background-color: #28a745;\n}\n\n.badge-success[href]:hover, .badge-success[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #1e7e34;\n}\n\n.badge-info {\n color: #fff;\n background-color: #17a2b8;\n}\n\n.badge-info[href]:hover, .badge-info[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #117a8b;\n}\n\n.badge-warning {\n color: #212529;\n background-color: #ffc107;\n}\n\n.badge-warning[href]:hover, .badge-warning[href]:focus {\n color: #212529;\n text-decoration: none;\n background-color: #d39e00;\n}\n\n.badge-danger {\n color: #fff;\n background-color: #dc3545;\n}\n\n.badge-danger[href]:hover, .badge-danger[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #bd2130;\n}\n\n.badge-light {\n color: #212529;\n background-color: #f8f9fa;\n}\n\n.badge-light[href]:hover, .badge-light[href]:focus {\n color: #212529;\n text-decoration: none;\n background-color: #dae0e5;\n}\n\n.badge-dark {\n color: #fff;\n background-color: #343a40;\n}\n\n.badge-dark[href]:hover, .badge-dark[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #1d2124;\n}\n\n.jumbotron {\n padding: 2rem 1rem;\n margin-bottom: 2rem;\n background-color: #e9ecef;\n border-radius: 0.3rem;\n}\n\n@media (min-width: 576px) {\n .jumbotron {\n padding: 4rem 2rem;\n }\n}\n\n.jumbotron-fluid {\n padding-right: 0;\n padding-left: 0;\n border-radius: 0;\n}\n\n.alert {\n position: relative;\n padding: 0.75rem 1.25rem;\n margin-bottom: 1rem;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.alert-heading {\n color: inherit;\n}\n\n.alert-link {\n font-weight: 700;\n}\n\n.alert-dismissible {\n padding-right: 4rem;\n}\n\n.alert-dismissible .close {\n position: absolute;\n top: 0;\n right: 0;\n padding: 0.75rem 1.25rem;\n color: inherit;\n}\n\n.alert-primary {\n color: #004085;\n background-color: #cce5ff;\n border-color: #b8daff;\n}\n\n.alert-primary hr {\n border-top-color: #9fcdff;\n}\n\n.alert-primary .alert-link {\n color: #002752;\n}\n\n.alert-secondary {\n color: #383d41;\n background-color: #e2e3e5;\n border-color: #d6d8db;\n}\n\n.alert-secondary hr {\n border-top-color: #c8cbcf;\n}\n\n.alert-secondary .alert-link {\n color: #202326;\n}\n\n.alert-success {\n color: #155724;\n background-color: #d4edda;\n border-color: #c3e6cb;\n}\n\n.alert-success hr {\n border-top-color: #b1dfbb;\n}\n\n.alert-success .alert-link {\n color: #0b2e13;\n}\n\n.alert-info {\n color: #0c5460;\n background-color: #d1ecf1;\n border-color: #bee5eb;\n}\n\n.alert-info hr {\n border-top-color: #abdde5;\n}\n\n.alert-info .alert-link {\n color: #062c33;\n}\n\n.alert-warning {\n color: #856404;\n background-color: #fff3cd;\n border-color: #ffeeba;\n}\n\n.alert-warning hr {\n border-top-color: #ffe8a1;\n}\n\n.alert-warning .alert-link {\n color: #533f03;\n}\n\n.alert-danger {\n color: #721c24;\n background-color: #f8d7da;\n border-color: #f5c6cb;\n}\n\n.alert-danger hr {\n border-top-color: #f1b0b7;\n}\n\n.alert-danger .alert-link {\n color: #491217;\n}\n\n.alert-light {\n color: #818182;\n background-color: #fefefe;\n border-color: #fdfdfe;\n}\n\n.alert-light hr {\n border-top-color: #ececf6;\n}\n\n.alert-light .alert-link {\n color: #686868;\n}\n\n.alert-dark {\n color: #1b1e21;\n background-color: #d6d8d9;\n border-color: #c6c8ca;\n}\n\n.alert-dark hr {\n border-top-color: #b9bbbe;\n}\n\n.alert-dark .alert-link {\n color: #040505;\n}\n\n@keyframes progress-bar-stripes {\n from {\n background-position: 1rem 0;\n }\n to {\n background-position: 0 0;\n }\n}\n\n.progress {\n display: flex;\n height: 1rem;\n overflow: hidden;\n font-size: 0.75rem;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.progress-bar {\n display: flex;\n flex-direction: column;\n justify-content: center;\n color: #fff;\n text-align: center;\n white-space: nowrap;\n background-color: #007bff;\n transition: width 0.6s ease;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .progress-bar {\n transition: none;\n }\n}\n\n.progress-bar-striped {\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-size: 1rem 1rem;\n}\n\n.progress-bar-animated {\n animation: progress-bar-stripes 1s linear infinite;\n}\n\n.media {\n display: flex;\n align-items: flex-start;\n}\n\n.media-body {\n flex: 1;\n}\n\n.list-group {\n display: flex;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n}\n\n.list-group-item-action {\n width: 100%;\n color: #495057;\n text-align: inherit;\n}\n\n.list-group-item-action:hover, .list-group-item-action:focus {\n color: #495057;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n\n.list-group-item-action:active {\n color: #212529;\n background-color: #e9ecef;\n}\n\n.list-group-item {\n position: relative;\n display: block;\n padding: 0.75rem 1.25rem;\n margin-bottom: -1px;\n background-color: #fff;\n border: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.list-group-item:last-child {\n margin-bottom: 0;\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.list-group-item:hover, .list-group-item:focus {\n z-index: 1;\n text-decoration: none;\n}\n\n.list-group-item.disabled, .list-group-item:disabled {\n color: #6c757d;\n background-color: #fff;\n}\n\n.list-group-item.active {\n z-index: 2;\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.list-group-flush .list-group-item {\n border-right: 0;\n border-left: 0;\n border-radius: 0;\n}\n\n.list-group-flush:first-child .list-group-item:first-child {\n border-top: 0;\n}\n\n.list-group-flush:last-child .list-group-item:last-child {\n border-bottom: 0;\n}\n\n.list-group-item-primary {\n color: #004085;\n background-color: #b8daff;\n}\n\n.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus {\n color: #004085;\n background-color: #9fcdff;\n}\n\n.list-group-item-primary.list-group-item-action.active {\n color: #fff;\n background-color: #004085;\n border-color: #004085;\n}\n\n.list-group-item-secondary {\n color: #383d41;\n background-color: #d6d8db;\n}\n\n.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus {\n color: #383d41;\n background-color: #c8cbcf;\n}\n\n.list-group-item-secondary.list-group-item-action.active {\n color: #fff;\n background-color: #383d41;\n border-color: #383d41;\n}\n\n.list-group-item-success {\n color: #155724;\n background-color: #c3e6cb;\n}\n\n.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus {\n color: #155724;\n background-color: #b1dfbb;\n}\n\n.list-group-item-success.list-group-item-action.active {\n color: #fff;\n background-color: #155724;\n border-color: #155724;\n}\n\n.list-group-item-info {\n color: #0c5460;\n background-color: #bee5eb;\n}\n\n.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus {\n color: #0c5460;\n background-color: #abdde5;\n}\n\n.list-group-item-info.list-group-item-action.active {\n color: #fff;\n background-color: #0c5460;\n border-color: #0c5460;\n}\n\n.list-group-item-warning {\n color: #856404;\n background-color: #ffeeba;\n}\n\n.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus {\n color: #856404;\n background-color: #ffe8a1;\n}\n\n.list-group-item-warning.list-group-item-action.active {\n color: #fff;\n background-color: #856404;\n border-color: #856404;\n}\n\n.list-group-item-danger {\n color: #721c24;\n background-color: #f5c6cb;\n}\n\n.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus {\n color: #721c24;\n background-color: #f1b0b7;\n}\n\n.list-group-item-danger.list-group-item-action.active {\n color: #fff;\n background-color: #721c24;\n border-color: #721c24;\n}\n\n.list-group-item-light {\n color: #818182;\n background-color: #fdfdfe;\n}\n\n.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus {\n color: #818182;\n background-color: #ececf6;\n}\n\n.list-group-item-light.list-group-item-action.active {\n color: #fff;\n background-color: #818182;\n border-color: #818182;\n}\n\n.list-group-item-dark {\n color: #1b1e21;\n background-color: #c6c8ca;\n}\n\n.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus {\n color: #1b1e21;\n background-color: #b9bbbe;\n}\n\n.list-group-item-dark.list-group-item-action.active {\n color: #fff;\n background-color: #1b1e21;\n border-color: #1b1e21;\n}\n\n.close {\n float: right;\n font-size: 1.5rem;\n font-weight: 700;\n line-height: 1;\n color: #000;\n text-shadow: 0 1px 0 #fff;\n opacity: .5;\n}\n\n.close:hover, .close:focus {\n color: #000;\n text-decoration: none;\n opacity: .75;\n}\n\n.close:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\nbutton.close {\n padding: 0;\n background-color: transparent;\n border: 0;\n -webkit-appearance: none;\n}\n\n.modal-open {\n overflow: hidden;\n}\n\n.modal {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1050;\n display: none;\n overflow: hidden;\n outline: 0;\n}\n\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 0.5rem;\n pointer-events: none;\n}\n\n.modal.fade .modal-dialog {\n transition: transform 0.3s ease-out;\n transform: translate(0, -25%);\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .modal.fade .modal-dialog {\n transition: none;\n }\n}\n\n.modal.show .modal-dialog {\n transform: translate(0, 0);\n}\n\n.modal-dialog-centered {\n display: flex;\n align-items: center;\n min-height: calc(100% - (0.5rem * 2));\n}\n\n.modal-content {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n pointer-events: auto;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n outline: 0;\n}\n\n.modal-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1040;\n background-color: #000;\n}\n\n.modal-backdrop.fade {\n opacity: 0;\n}\n\n.modal-backdrop.show {\n opacity: 0.5;\n}\n\n.modal-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n padding: 1rem;\n border-bottom: 1px solid #e9ecef;\n border-top-left-radius: 0.3rem;\n border-top-right-radius: 0.3rem;\n}\n\n.modal-header .close {\n padding: 1rem;\n margin: -1rem -1rem -1rem auto;\n}\n\n.modal-title {\n margin-bottom: 0;\n line-height: 1.5;\n}\n\n.modal-body {\n position: relative;\n flex: 1 1 auto;\n padding: 1rem;\n}\n\n.modal-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n padding: 1rem;\n border-top: 1px solid #e9ecef;\n}\n\n.modal-footer > :not(:first-child) {\n margin-left: .25rem;\n}\n\n.modal-footer > :not(:last-child) {\n margin-right: .25rem;\n}\n\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n\n@media (min-width: 576px) {\n .modal-dialog {\n max-width: 500px;\n margin: 1.75rem auto;\n }\n .modal-dialog-centered {\n min-height: calc(100% - (1.75rem * 2));\n }\n .modal-sm {\n max-width: 300px;\n }\n}\n\n@media (min-width: 992px) {\n .modal-lg {\n max-width: 800px;\n }\n}\n\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n opacity: 0;\n}\n\n.tooltip.show {\n opacity: 0.9;\n}\n\n.tooltip .arrow {\n position: absolute;\n display: block;\n width: 0.8rem;\n height: 0.4rem;\n}\n\n.tooltip .arrow::before {\n position: absolute;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-tooltip-top, .bs-tooltip-auto[x-placement^=\"top\"] {\n padding: 0.4rem 0;\n}\n\n.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^=\"top\"] .arrow {\n bottom: 0;\n}\n\n.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^=\"top\"] .arrow::before {\n top: 0;\n border-width: 0.4rem 0.4rem 0;\n border-top-color: #000;\n}\n\n.bs-tooltip-right, .bs-tooltip-auto[x-placement^=\"right\"] {\n padding: 0 0.4rem;\n}\n\n.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^=\"right\"] .arrow {\n left: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n\n.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^=\"right\"] .arrow::before {\n right: 0;\n border-width: 0.4rem 0.4rem 0.4rem 0;\n border-right-color: #000;\n}\n\n.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^=\"bottom\"] {\n padding: 0.4rem 0;\n}\n\n.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow {\n top: 0;\n}\n\n.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow::before {\n bottom: 0;\n border-width: 0 0.4rem 0.4rem;\n border-bottom-color: #000;\n}\n\n.bs-tooltip-left, .bs-tooltip-auto[x-placement^=\"left\"] {\n padding: 0 0.4rem;\n}\n\n.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^=\"left\"] .arrow {\n right: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n\n.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^=\"left\"] .arrow::before {\n left: 0;\n border-width: 0.4rem 0 0.4rem 0.4rem;\n border-left-color: #000;\n}\n\n.tooltip-inner {\n max-width: 200px;\n padding: 0.25rem 0.5rem;\n color: #fff;\n text-align: center;\n background-color: #000;\n border-radius: 0.25rem;\n}\n\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: block;\n max-width: 276px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n}\n\n.popover .arrow {\n position: absolute;\n display: block;\n width: 1rem;\n height: 0.5rem;\n margin: 0 0.3rem;\n}\n\n.popover .arrow::before, .popover .arrow::after {\n position: absolute;\n display: block;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-popover-top, .bs-popover-auto[x-placement^=\"top\"] {\n margin-bottom: 0.5rem;\n}\n\n.bs-popover-top .arrow, .bs-popover-auto[x-placement^=\"top\"] .arrow {\n bottom: calc((0.5rem + 1px) * -1);\n}\n\n.bs-popover-top .arrow::before, .bs-popover-auto[x-placement^=\"top\"] .arrow::before,\n.bs-popover-top .arrow::after,\n.bs-popover-auto[x-placement^=\"top\"] .arrow::after {\n border-width: 0.5rem 0.5rem 0;\n}\n\n.bs-popover-top .arrow::before, .bs-popover-auto[x-placement^=\"top\"] .arrow::before {\n bottom: 0;\n border-top-color: rgba(0, 0, 0, 0.25);\n}\n\n\n.bs-popover-top .arrow::after,\n.bs-popover-auto[x-placement^=\"top\"] .arrow::after {\n bottom: 1px;\n border-top-color: #fff;\n}\n\n.bs-popover-right, .bs-popover-auto[x-placement^=\"right\"] {\n margin-left: 0.5rem;\n}\n\n.bs-popover-right .arrow, .bs-popover-auto[x-placement^=\"right\"] .arrow {\n left: calc((0.5rem + 1px) * -1);\n width: 0.5rem;\n height: 1rem;\n margin: 0.3rem 0;\n}\n\n.bs-popover-right .arrow::before, .bs-popover-auto[x-placement^=\"right\"] .arrow::before,\n.bs-popover-right .arrow::after,\n.bs-popover-auto[x-placement^=\"right\"] .arrow::after {\n border-width: 0.5rem 0.5rem 0.5rem 0;\n}\n\n.bs-popover-right .arrow::before, .bs-popover-auto[x-placement^=\"right\"] .arrow::before {\n left: 0;\n border-right-color: rgba(0, 0, 0, 0.25);\n}\n\n\n.bs-popover-right .arrow::after,\n.bs-popover-auto[x-placement^=\"right\"] .arrow::after {\n left: 1px;\n border-right-color: #fff;\n}\n\n.bs-popover-bottom, .bs-popover-auto[x-placement^=\"bottom\"] {\n margin-top: 0.5rem;\n}\n\n.bs-popover-bottom .arrow, .bs-popover-auto[x-placement^=\"bottom\"] .arrow {\n top: calc((0.5rem + 1px) * -1);\n}\n\n.bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::before,\n.bs-popover-bottom .arrow::after,\n.bs-popover-auto[x-placement^=\"bottom\"] .arrow::after {\n border-width: 0 0.5rem 0.5rem 0.5rem;\n}\n\n.bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::before {\n top: 0;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n}\n\n\n.bs-popover-bottom .arrow::after,\n.bs-popover-auto[x-placement^=\"bottom\"] .arrow::after {\n top: 1px;\n border-bottom-color: #fff;\n}\n\n.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^=\"bottom\"] .popover-header::before {\n position: absolute;\n top: 0;\n left: 50%;\n display: block;\n width: 1rem;\n margin-left: -0.5rem;\n content: \"\";\n border-bottom: 1px solid #f7f7f7;\n}\n\n.bs-popover-left, .bs-popover-auto[x-placement^=\"left\"] {\n margin-right: 0.5rem;\n}\n\n.bs-popover-left .arrow, .bs-popover-auto[x-placement^=\"left\"] .arrow {\n right: calc((0.5rem + 1px) * -1);\n width: 0.5rem;\n height: 1rem;\n margin: 0.3rem 0;\n}\n\n.bs-popover-left .arrow::before, .bs-popover-auto[x-placement^=\"left\"] .arrow::before,\n.bs-popover-left .arrow::after,\n.bs-popover-auto[x-placement^=\"left\"] .arrow::after {\n border-width: 0.5rem 0 0.5rem 0.5rem;\n}\n\n.bs-popover-left .arrow::before, .bs-popover-auto[x-placement^=\"left\"] .arrow::before {\n right: 0;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n\n\n.bs-popover-left .arrow::after,\n.bs-popover-auto[x-placement^=\"left\"] .arrow::after {\n right: 1px;\n border-left-color: #fff;\n}\n\n.popover-header {\n padding: 0.5rem 0.75rem;\n margin-bottom: 0;\n font-size: 1rem;\n color: inherit;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-top-left-radius: calc(0.3rem - 1px);\n border-top-right-radius: calc(0.3rem - 1px);\n}\n\n.popover-header:empty {\n display: none;\n}\n\n.popover-body {\n padding: 0.5rem 0.75rem;\n color: #212529;\n}\n\n.carousel {\n position: relative;\n}\n\n.carousel-inner {\n position: relative;\n width: 100%;\n overflow: hidden;\n}\n\n.carousel-item {\n position: relative;\n display: none;\n align-items: center;\n width: 100%;\n transition: transform 0.6s ease;\n backface-visibility: hidden;\n perspective: 1000px;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .carousel-item {\n transition: none;\n }\n}\n\n.carousel-item.active,\n.carousel-item-next,\n.carousel-item-prev {\n display: block;\n}\n\n.carousel-item-next,\n.carousel-item-prev {\n position: absolute;\n top: 0;\n}\n\n.carousel-item-next.carousel-item-left,\n.carousel-item-prev.carousel-item-right {\n transform: translateX(0);\n}\n\n@supports (transform-style: preserve-3d) {\n .carousel-item-next.carousel-item-left,\n .carousel-item-prev.carousel-item-right {\n transform: translate3d(0, 0, 0);\n }\n}\n\n.carousel-item-next,\n.active.carousel-item-right {\n transform: translateX(100%);\n}\n\n@supports (transform-style: preserve-3d) {\n .carousel-item-next,\n .active.carousel-item-right {\n transform: translate3d(100%, 0, 0);\n }\n}\n\n.carousel-item-prev,\n.active.carousel-item-left {\n transform: translateX(-100%);\n}\n\n@supports (transform-style: preserve-3d) {\n .carousel-item-prev,\n .active.carousel-item-left {\n transform: translate3d(-100%, 0, 0);\n }\n}\n\n.carousel-fade .carousel-item {\n opacity: 0;\n transition-duration: .6s;\n transition-property: opacity;\n}\n\n.carousel-fade .carousel-item.active,\n.carousel-fade .carousel-item-next.carousel-item-left,\n.carousel-fade .carousel-item-prev.carousel-item-right {\n opacity: 1;\n}\n\n.carousel-fade .active.carousel-item-left,\n.carousel-fade .active.carousel-item-right {\n opacity: 0;\n}\n\n.carousel-fade .carousel-item-next,\n.carousel-fade .carousel-item-prev,\n.carousel-fade .carousel-item.active,\n.carousel-fade .active.carousel-item-left,\n.carousel-fade .active.carousel-item-prev {\n transform: translateX(0);\n}\n\n@supports (transform-style: preserve-3d) {\n .carousel-fade .carousel-item-next,\n .carousel-fade .carousel-item-prev,\n .carousel-fade .carousel-item.active,\n .carousel-fade .active.carousel-item-left,\n .carousel-fade .active.carousel-item-prev {\n transform: translate3d(0, 0, 0);\n }\n}\n\n.carousel-control-prev,\n.carousel-control-next {\n position: absolute;\n top: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 15%;\n color: #fff;\n text-align: center;\n opacity: 0.5;\n}\n\n.carousel-control-prev:hover, .carousel-control-prev:focus,\n.carousel-control-next:hover,\n.carousel-control-next:focus {\n color: #fff;\n text-decoration: none;\n outline: 0;\n opacity: .9;\n}\n\n.carousel-control-prev {\n left: 0;\n}\n\n.carousel-control-next {\n right: 0;\n}\n\n.carousel-control-prev-icon,\n.carousel-control-next-icon {\n display: inline-block;\n width: 20px;\n height: 20px;\n background: transparent no-repeat center center;\n background-size: 100% 100%;\n}\n\n.carousel-control-prev-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E\");\n}\n\n.carousel-control-next-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E\");\n}\n\n.carousel-indicators {\n position: absolute;\n right: 0;\n bottom: 10px;\n left: 0;\n z-index: 15;\n display: flex;\n justify-content: center;\n padding-left: 0;\n margin-right: 15%;\n margin-left: 15%;\n list-style: none;\n}\n\n.carousel-indicators li {\n position: relative;\n flex: 0 1 auto;\n width: 30px;\n height: 3px;\n margin-right: 3px;\n margin-left: 3px;\n text-indent: -999px;\n cursor: pointer;\n background-color: rgba(255, 255, 255, 0.5);\n}\n\n.carousel-indicators li::before {\n position: absolute;\n top: -10px;\n left: 0;\n display: inline-block;\n width: 100%;\n height: 10px;\n content: \"\";\n}\n\n.carousel-indicators li::after {\n position: absolute;\n bottom: -10px;\n left: 0;\n display: inline-block;\n width: 100%;\n height: 10px;\n content: \"\";\n}\n\n.carousel-indicators .active {\n background-color: #fff;\n}\n\n.carousel-caption {\n position: absolute;\n right: 15%;\n bottom: 20px;\n left: 15%;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #fff;\n text-align: center;\n}\n\n.align-baseline {\n vertical-align: baseline !important;\n}\n\n.align-top {\n vertical-align: top !important;\n}\n\n.align-middle {\n vertical-align: middle !important;\n}\n\n.align-bottom {\n vertical-align: bottom !important;\n}\n\n.align-text-bottom {\n vertical-align: text-bottom !important;\n}\n\n.align-text-top {\n vertical-align: text-top !important;\n}\n\n.bg-primary {\n background-color: #007bff !important;\n}\n\na.bg-primary:hover, a.bg-primary:focus,\nbutton.bg-primary:hover,\nbutton.bg-primary:focus {\n background-color: #0062cc !important;\n}\n\n.bg-secondary {\n background-color: #6c757d !important;\n}\n\na.bg-secondary:hover, a.bg-secondary:focus,\nbutton.bg-secondary:hover,\nbutton.bg-secondary:focus {\n background-color: #545b62 !important;\n}\n\n.bg-success {\n background-color: #28a745 !important;\n}\n\na.bg-success:hover, a.bg-success:focus,\nbutton.bg-success:hover,\nbutton.bg-success:focus {\n background-color: #1e7e34 !important;\n}\n\n.bg-info {\n background-color: #17a2b8 !important;\n}\n\na.bg-info:hover, a.bg-info:focus,\nbutton.bg-info:hover,\nbutton.bg-info:focus {\n background-color: #117a8b !important;\n}\n\n.bg-warning {\n background-color: #ffc107 !important;\n}\n\na.bg-warning:hover, a.bg-warning:focus,\nbutton.bg-warning:hover,\nbutton.bg-warning:focus {\n background-color: #d39e00 !important;\n}\n\n.bg-danger {\n background-color: #dc3545 !important;\n}\n\na.bg-danger:hover, a.bg-danger:focus,\nbutton.bg-danger:hover,\nbutton.bg-danger:focus {\n background-color: #bd2130 !important;\n}\n\n.bg-light {\n background-color: #f8f9fa !important;\n}\n\na.bg-light:hover, a.bg-light:focus,\nbutton.bg-light:hover,\nbutton.bg-light:focus {\n background-color: #dae0e5 !important;\n}\n\n.bg-dark {\n background-color: #343a40 !important;\n}\n\na.bg-dark:hover, a.bg-dark:focus,\nbutton.bg-dark:hover,\nbutton.bg-dark:focus {\n background-color: #1d2124 !important;\n}\n\n.bg-white {\n background-color: #fff !important;\n}\n\n.bg-transparent {\n background-color: transparent !important;\n}\n\n.border {\n border: 1px solid #dee2e6 !important;\n}\n\n.border-top {\n border-top: 1px solid #dee2e6 !important;\n}\n\n.border-right {\n border-right: 1px solid #dee2e6 !important;\n}\n\n.border-bottom {\n border-bottom: 1px solid #dee2e6 !important;\n}\n\n.border-left {\n border-left: 1px solid #dee2e6 !important;\n}\n\n.border-0 {\n border: 0 !important;\n}\n\n.border-top-0 {\n border-top: 0 !important;\n}\n\n.border-right-0 {\n border-right: 0 !important;\n}\n\n.border-bottom-0 {\n border-bottom: 0 !important;\n}\n\n.border-left-0 {\n border-left: 0 !important;\n}\n\n.border-primary {\n border-color: #007bff !important;\n}\n\n.border-secondary {\n border-color: #6c757d !important;\n}\n\n.border-success {\n border-color: #28a745 !important;\n}\n\n.border-info {\n border-color: #17a2b8 !important;\n}\n\n.border-warning {\n border-color: #ffc107 !important;\n}\n\n.border-danger {\n border-color: #dc3545 !important;\n}\n\n.border-light {\n border-color: #f8f9fa !important;\n}\n\n.border-dark {\n border-color: #343a40 !important;\n}\n\n.border-white {\n border-color: #fff !important;\n}\n\n.rounded {\n border-radius: 0.25rem !important;\n}\n\n.rounded-top {\n border-top-left-radius: 0.25rem !important;\n border-top-right-radius: 0.25rem !important;\n}\n\n.rounded-right {\n border-top-right-radius: 0.25rem !important;\n border-bottom-right-radius: 0.25rem !important;\n}\n\n.rounded-bottom {\n border-bottom-right-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-left {\n border-top-left-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-circle {\n border-radius: 50% !important;\n}\n\n.rounded-0 {\n border-radius: 0 !important;\n}\n\n.clearfix::after {\n display: block;\n clear: both;\n content: \"\";\n}\n\n.d-none {\n display: none !important;\n}\n\n.d-inline {\n display: inline !important;\n}\n\n.d-inline-block {\n display: inline-block !important;\n}\n\n.d-block {\n display: block !important;\n}\n\n.d-table {\n display: table !important;\n}\n\n.d-table-row {\n display: table-row !important;\n}\n\n.d-table-cell {\n display: table-cell !important;\n}\n\n.d-flex {\n display: flex !important;\n}\n\n.d-inline-flex {\n display: inline-flex !important;\n}\n\n@media (min-width: 576px) {\n .d-sm-none {\n display: none !important;\n }\n .d-sm-inline {\n display: inline !important;\n }\n .d-sm-inline-block {\n display: inline-block !important;\n }\n .d-sm-block {\n display: block !important;\n }\n .d-sm-table {\n display: table !important;\n }\n .d-sm-table-row {\n display: table-row !important;\n }\n .d-sm-table-cell {\n display: table-cell !important;\n }\n .d-sm-flex {\n display: flex !important;\n }\n .d-sm-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 768px) {\n .d-md-none {\n display: none !important;\n }\n .d-md-inline {\n display: inline !important;\n }\n .d-md-inline-block {\n display: inline-block !important;\n }\n .d-md-block {\n display: block !important;\n }\n .d-md-table {\n display: table !important;\n }\n .d-md-table-row {\n display: table-row !important;\n }\n .d-md-table-cell {\n display: table-cell !important;\n }\n .d-md-flex {\n display: flex !important;\n }\n .d-md-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 992px) {\n .d-lg-none {\n display: none !important;\n }\n .d-lg-inline {\n display: inline !important;\n }\n .d-lg-inline-block {\n display: inline-block !important;\n }\n .d-lg-block {\n display: block !important;\n }\n .d-lg-table {\n display: table !important;\n }\n .d-lg-table-row {\n display: table-row !important;\n }\n .d-lg-table-cell {\n display: table-cell !important;\n }\n .d-lg-flex {\n display: flex !important;\n }\n .d-lg-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 1200px) {\n .d-xl-none {\n display: none !important;\n }\n .d-xl-inline {\n display: inline !important;\n }\n .d-xl-inline-block {\n display: inline-block !important;\n }\n .d-xl-block {\n display: block !important;\n }\n .d-xl-table {\n display: table !important;\n }\n .d-xl-table-row {\n display: table-row !important;\n }\n .d-xl-table-cell {\n display: table-cell !important;\n }\n .d-xl-flex {\n display: flex !important;\n }\n .d-xl-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media print {\n .d-print-none {\n display: none !important;\n }\n .d-print-inline {\n display: inline !important;\n }\n .d-print-inline-block {\n display: inline-block !important;\n }\n .d-print-block {\n display: block !important;\n }\n .d-print-table {\n display: table !important;\n }\n .d-print-table-row {\n display: table-row !important;\n }\n .d-print-table-cell {\n display: table-cell !important;\n }\n .d-print-flex {\n display: flex !important;\n }\n .d-print-inline-flex {\n display: inline-flex !important;\n }\n}\n\n.embed-responsive {\n position: relative;\n display: block;\n width: 100%;\n padding: 0;\n overflow: hidden;\n}\n\n.embed-responsive::before {\n display: block;\n content: \"\";\n}\n\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n height: 100%;\n border: 0;\n}\n\n.embed-responsive-21by9::before {\n padding-top: 42.857143%;\n}\n\n.embed-responsive-16by9::before {\n padding-top: 56.25%;\n}\n\n.embed-responsive-4by3::before {\n padding-top: 75%;\n}\n\n.embed-responsive-1by1::before {\n padding-top: 100%;\n}\n\n.flex-row {\n flex-direction: row !important;\n}\n\n.flex-column {\n flex-direction: column !important;\n}\n\n.flex-row-reverse {\n flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n flex-direction: column-reverse !important;\n}\n\n.flex-wrap {\n flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n}\n\n.flex-fill {\n flex: 1 1 auto !important;\n}\n\n.flex-grow-0 {\n flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n flex-shrink: 1 !important;\n}\n\n.justify-content-start {\n justify-content: flex-start !important;\n}\n\n.justify-content-end {\n justify-content: flex-end !important;\n}\n\n.justify-content-center {\n justify-content: center !important;\n}\n\n.justify-content-between {\n justify-content: space-between !important;\n}\n\n.justify-content-around {\n justify-content: space-around !important;\n}\n\n.align-items-start {\n align-items: flex-start !important;\n}\n\n.align-items-end {\n align-items: flex-end !important;\n}\n\n.align-items-center {\n align-items: center !important;\n}\n\n.align-items-baseline {\n align-items: baseline !important;\n}\n\n.align-items-stretch {\n align-items: stretch !important;\n}\n\n.align-content-start {\n align-content: flex-start !important;\n}\n\n.align-content-end {\n align-content: flex-end !important;\n}\n\n.align-content-center {\n align-content: center !important;\n}\n\n.align-content-between {\n align-content: space-between !important;\n}\n\n.align-content-around {\n align-content: space-around !important;\n}\n\n.align-content-stretch {\n align-content: stretch !important;\n}\n\n.align-self-auto {\n align-self: auto !important;\n}\n\n.align-self-start {\n align-self: flex-start !important;\n}\n\n.align-self-end {\n align-self: flex-end !important;\n}\n\n.align-self-center {\n align-self: center !important;\n}\n\n.align-self-baseline {\n align-self: baseline !important;\n}\n\n.align-self-stretch {\n align-self: stretch !important;\n}\n\n@media (min-width: 576px) {\n .flex-sm-row {\n flex-direction: row !important;\n }\n .flex-sm-column {\n flex-direction: column !important;\n }\n .flex-sm-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-sm-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-sm-wrap {\n flex-wrap: wrap !important;\n }\n .flex-sm-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-sm-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .flex-sm-fill {\n flex: 1 1 auto !important;\n }\n .flex-sm-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-sm-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-sm-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-sm-shrink-1 {\n flex-shrink: 1 !important;\n }\n .justify-content-sm-start {\n justify-content: flex-start !important;\n }\n .justify-content-sm-end {\n justify-content: flex-end !important;\n }\n .justify-content-sm-center {\n justify-content: center !important;\n }\n .justify-content-sm-between {\n justify-content: space-between !important;\n }\n .justify-content-sm-around {\n justify-content: space-around !important;\n }\n .align-items-sm-start {\n align-items: flex-start !important;\n }\n .align-items-sm-end {\n align-items: flex-end !important;\n }\n .align-items-sm-center {\n align-items: center !important;\n }\n .align-items-sm-baseline {\n align-items: baseline !important;\n }\n .align-items-sm-stretch {\n align-items: stretch !important;\n }\n .align-content-sm-start {\n align-content: flex-start !important;\n }\n .align-content-sm-end {\n align-content: flex-end !important;\n }\n .align-content-sm-center {\n align-content: center !important;\n }\n .align-content-sm-between {\n align-content: space-between !important;\n }\n .align-content-sm-around {\n align-content: space-around !important;\n }\n .align-content-sm-stretch {\n align-content: stretch !important;\n }\n .align-self-sm-auto {\n align-self: auto !important;\n }\n .align-self-sm-start {\n align-self: flex-start !important;\n }\n .align-self-sm-end {\n align-self: flex-end !important;\n }\n .align-self-sm-center {\n align-self: center !important;\n }\n .align-self-sm-baseline {\n align-self: baseline !important;\n }\n .align-self-sm-stretch {\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 768px) {\n .flex-md-row {\n flex-direction: row !important;\n }\n .flex-md-column {\n flex-direction: column !important;\n }\n .flex-md-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-md-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-md-wrap {\n flex-wrap: wrap !important;\n }\n .flex-md-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-md-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .flex-md-fill {\n flex: 1 1 auto !important;\n }\n .flex-md-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-md-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-md-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-md-shrink-1 {\n flex-shrink: 1 !important;\n }\n .justify-content-md-start {\n justify-content: flex-start !important;\n }\n .justify-content-md-end {\n justify-content: flex-end !important;\n }\n .justify-content-md-center {\n justify-content: center !important;\n }\n .justify-content-md-between {\n justify-content: space-between !important;\n }\n .justify-content-md-around {\n justify-content: space-around !important;\n }\n .align-items-md-start {\n align-items: flex-start !important;\n }\n .align-items-md-end {\n align-items: flex-end !important;\n }\n .align-items-md-center {\n align-items: center !important;\n }\n .align-items-md-baseline {\n align-items: baseline !important;\n }\n .align-items-md-stretch {\n align-items: stretch !important;\n }\n .align-content-md-start {\n align-content: flex-start !important;\n }\n .align-content-md-end {\n align-content: flex-end !important;\n }\n .align-content-md-center {\n align-content: center !important;\n }\n .align-content-md-between {\n align-content: space-between !important;\n }\n .align-content-md-around {\n align-content: space-around !important;\n }\n .align-content-md-stretch {\n align-content: stretch !important;\n }\n .align-self-md-auto {\n align-self: auto !important;\n }\n .align-self-md-start {\n align-self: flex-start !important;\n }\n .align-self-md-end {\n align-self: flex-end !important;\n }\n .align-self-md-center {\n align-self: center !important;\n }\n .align-self-md-baseline {\n align-self: baseline !important;\n }\n .align-self-md-stretch {\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 992px) {\n .flex-lg-row {\n flex-direction: row !important;\n }\n .flex-lg-column {\n flex-direction: column !important;\n }\n .flex-lg-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-lg-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-lg-wrap {\n flex-wrap: wrap !important;\n }\n .flex-lg-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-lg-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .flex-lg-fill {\n flex: 1 1 auto !important;\n }\n .flex-lg-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-lg-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-lg-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-lg-shrink-1 {\n flex-shrink: 1 !important;\n }\n .justify-content-lg-start {\n justify-content: flex-start !important;\n }\n .justify-content-lg-end {\n justify-content: flex-end !important;\n }\n .justify-content-lg-center {\n justify-content: center !important;\n }\n .justify-content-lg-between {\n justify-content: space-between !important;\n }\n .justify-content-lg-around {\n justify-content: space-around !important;\n }\n .align-items-lg-start {\n align-items: flex-start !important;\n }\n .align-items-lg-end {\n align-items: flex-end !important;\n }\n .align-items-lg-center {\n align-items: center !important;\n }\n .align-items-lg-baseline {\n align-items: baseline !important;\n }\n .align-items-lg-stretch {\n align-items: stretch !important;\n }\n .align-content-lg-start {\n align-content: flex-start !important;\n }\n .align-content-lg-end {\n align-content: flex-end !important;\n }\n .align-content-lg-center {\n align-content: center !important;\n }\n .align-content-lg-between {\n align-content: space-between !important;\n }\n .align-content-lg-around {\n align-content: space-around !important;\n }\n .align-content-lg-stretch {\n align-content: stretch !important;\n }\n .align-self-lg-auto {\n align-self: auto !important;\n }\n .align-self-lg-start {\n align-self: flex-start !important;\n }\n .align-self-lg-end {\n align-self: flex-end !important;\n }\n .align-self-lg-center {\n align-self: center !important;\n }\n .align-self-lg-baseline {\n align-self: baseline !important;\n }\n .align-self-lg-stretch {\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 1200px) {\n .flex-xl-row {\n flex-direction: row !important;\n }\n .flex-xl-column {\n flex-direction: column !important;\n }\n .flex-xl-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-xl-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-xl-wrap {\n flex-wrap: wrap !important;\n }\n .flex-xl-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-xl-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .flex-xl-fill {\n flex: 1 1 auto !important;\n }\n .flex-xl-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-xl-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-xl-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-xl-shrink-1 {\n flex-shrink: 1 !important;\n }\n .justify-content-xl-start {\n justify-content: flex-start !important;\n }\n .justify-content-xl-end {\n justify-content: flex-end !important;\n }\n .justify-content-xl-center {\n justify-content: center !important;\n }\n .justify-content-xl-between {\n justify-content: space-between !important;\n }\n .justify-content-xl-around {\n justify-content: space-around !important;\n }\n .align-items-xl-start {\n align-items: flex-start !important;\n }\n .align-items-xl-end {\n align-items: flex-end !important;\n }\n .align-items-xl-center {\n align-items: center !important;\n }\n .align-items-xl-baseline {\n align-items: baseline !important;\n }\n .align-items-xl-stretch {\n align-items: stretch !important;\n }\n .align-content-xl-start {\n align-content: flex-start !important;\n }\n .align-content-xl-end {\n align-content: flex-end !important;\n }\n .align-content-xl-center {\n align-content: center !important;\n }\n .align-content-xl-between {\n align-content: space-between !important;\n }\n .align-content-xl-around {\n align-content: space-around !important;\n }\n .align-content-xl-stretch {\n align-content: stretch !important;\n }\n .align-self-xl-auto {\n align-self: auto !important;\n }\n .align-self-xl-start {\n align-self: flex-start !important;\n }\n .align-self-xl-end {\n align-self: flex-end !important;\n }\n .align-self-xl-center {\n align-self: center !important;\n }\n .align-self-xl-baseline {\n align-self: baseline !important;\n }\n .align-self-xl-stretch {\n align-self: stretch !important;\n }\n}\n\n.float-left {\n float: left !important;\n}\n\n.float-right {\n float: right !important;\n}\n\n.float-none {\n float: none !important;\n}\n\n@media (min-width: 576px) {\n .float-sm-left {\n float: left !important;\n }\n .float-sm-right {\n float: right !important;\n }\n .float-sm-none {\n float: none !important;\n }\n}\n\n@media (min-width: 768px) {\n .float-md-left {\n float: left !important;\n }\n .float-md-right {\n float: right !important;\n }\n .float-md-none {\n float: none !important;\n }\n}\n\n@media (min-width: 992px) {\n .float-lg-left {\n float: left !important;\n }\n .float-lg-right {\n float: right !important;\n }\n .float-lg-none {\n float: none !important;\n }\n}\n\n@media (min-width: 1200px) {\n .float-xl-left {\n float: left !important;\n }\n .float-xl-right {\n float: right !important;\n }\n .float-xl-none {\n float: none !important;\n }\n}\n\n.position-static {\n position: static !important;\n}\n\n.position-relative {\n position: relative !important;\n}\n\n.position-absolute {\n position: absolute !important;\n}\n\n.position-fixed {\n position: fixed !important;\n}\n\n.position-sticky {\n position: sticky !important;\n}\n\n.fixed-top {\n position: fixed;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n\n.fixed-bottom {\n position: fixed;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1030;\n}\n\n@supports (position: sticky) {\n .sticky-top {\n position: sticky;\n top: 0;\n z-index: 1020;\n }\n}\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n\n.sr-only-focusable:active, .sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n overflow: visible;\n clip: auto;\n white-space: normal;\n}\n\n.shadow-sm {\n box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;\n}\n\n.shadow {\n box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;\n}\n\n.shadow-lg {\n box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;\n}\n\n.shadow-none {\n box-shadow: none !important;\n}\n\n.w-25 {\n width: 25% !important;\n}\n\n.w-50 {\n width: 50% !important;\n}\n\n.w-75 {\n width: 75% !important;\n}\n\n.w-100 {\n width: 100% !important;\n}\n\n.w-auto {\n width: auto !important;\n}\n\n.h-25 {\n height: 25% !important;\n}\n\n.h-50 {\n height: 50% !important;\n}\n\n.h-75 {\n height: 75% !important;\n}\n\n.h-100 {\n height: 100% !important;\n}\n\n.h-auto {\n height: auto !important;\n}\n\n.mw-100 {\n max-width: 100% !important;\n}\n\n.mh-100 {\n max-height: 100% !important;\n}\n\n.m-0 {\n margin: 0 !important;\n}\n\n.mt-0,\n.my-0 {\n margin-top: 0 !important;\n}\n\n.mr-0,\n.mx-0 {\n margin-right: 0 !important;\n}\n\n.mb-0,\n.my-0 {\n margin-bottom: 0 !important;\n}\n\n.ml-0,\n.mx-0 {\n margin-left: 0 !important;\n}\n\n.m-1 {\n margin: 0.25rem !important;\n}\n\n.mt-1,\n.my-1 {\n margin-top: 0.25rem !important;\n}\n\n.mr-1,\n.mx-1 {\n margin-right: 0.25rem !important;\n}\n\n.mb-1,\n.my-1 {\n margin-bottom: 0.25rem !important;\n}\n\n.ml-1,\n.mx-1 {\n margin-left: 0.25rem !important;\n}\n\n.m-2 {\n margin: 0.5rem !important;\n}\n\n.mt-2,\n.my-2 {\n margin-top: 0.5rem !important;\n}\n\n.mr-2,\n.mx-2 {\n margin-right: 0.5rem !important;\n}\n\n.mb-2,\n.my-2 {\n margin-bottom: 0.5rem !important;\n}\n\n.ml-2,\n.mx-2 {\n margin-left: 0.5rem !important;\n}\n\n.m-3 {\n margin: 1rem !important;\n}\n\n.mt-3,\n.my-3 {\n margin-top: 1rem !important;\n}\n\n.mr-3,\n.mx-3 {\n margin-right: 1rem !important;\n}\n\n.mb-3,\n.my-3 {\n margin-bottom: 1rem !important;\n}\n\n.ml-3,\n.mx-3 {\n margin-left: 1rem !important;\n}\n\n.m-4 {\n margin: 1.5rem !important;\n}\n\n.mt-4,\n.my-4 {\n margin-top: 1.5rem !important;\n}\n\n.mr-4,\n.mx-4 {\n margin-right: 1.5rem !important;\n}\n\n.mb-4,\n.my-4 {\n margin-bottom: 1.5rem !important;\n}\n\n.ml-4,\n.mx-4 {\n margin-left: 1.5rem !important;\n}\n\n.m-5 {\n margin: 3rem !important;\n}\n\n.mt-5,\n.my-5 {\n margin-top: 3rem !important;\n}\n\n.mr-5,\n.mx-5 {\n margin-right: 3rem !important;\n}\n\n.mb-5,\n.my-5 {\n margin-bottom: 3rem !important;\n}\n\n.ml-5,\n.mx-5 {\n margin-left: 3rem !important;\n}\n\n.p-0 {\n padding: 0 !important;\n}\n\n.pt-0,\n.py-0 {\n padding-top: 0 !important;\n}\n\n.pr-0,\n.px-0 {\n padding-right: 0 !important;\n}\n\n.pb-0,\n.py-0 {\n padding-bottom: 0 !important;\n}\n\n.pl-0,\n.px-0 {\n padding-left: 0 !important;\n}\n\n.p-1 {\n padding: 0.25rem !important;\n}\n\n.pt-1,\n.py-1 {\n padding-top: 0.25rem !important;\n}\n\n.pr-1,\n.px-1 {\n padding-right: 0.25rem !important;\n}\n\n.pb-1,\n.py-1 {\n padding-bottom: 0.25rem !important;\n}\n\n.pl-1,\n.px-1 {\n padding-left: 0.25rem !important;\n}\n\n.p-2 {\n padding: 0.5rem !important;\n}\n\n.pt-2,\n.py-2 {\n padding-top: 0.5rem !important;\n}\n\n.pr-2,\n.px-2 {\n padding-right: 0.5rem !important;\n}\n\n.pb-2,\n.py-2 {\n padding-bottom: 0.5rem !important;\n}\n\n.pl-2,\n.px-2 {\n padding-left: 0.5rem !important;\n}\n\n.p-3 {\n padding: 1rem !important;\n}\n\n.pt-3,\n.py-3 {\n padding-top: 1rem !important;\n}\n\n.pr-3,\n.px-3 {\n padding-right: 1rem !important;\n}\n\n.pb-3,\n.py-3 {\n padding-bottom: 1rem !important;\n}\n\n.pl-3,\n.px-3 {\n padding-left: 1rem !important;\n}\n\n.p-4 {\n padding: 1.5rem !important;\n}\n\n.pt-4,\n.py-4 {\n padding-top: 1.5rem !important;\n}\n\n.pr-4,\n.px-4 {\n padding-right: 1.5rem !important;\n}\n\n.pb-4,\n.py-4 {\n padding-bottom: 1.5rem !important;\n}\n\n.pl-4,\n.px-4 {\n padding-left: 1.5rem !important;\n}\n\n.p-5 {\n padding: 3rem !important;\n}\n\n.pt-5,\n.py-5 {\n padding-top: 3rem !important;\n}\n\n.pr-5,\n.px-5 {\n padding-right: 3rem !important;\n}\n\n.pb-5,\n.py-5 {\n padding-bottom: 3rem !important;\n}\n\n.pl-5,\n.px-5 {\n padding-left: 3rem !important;\n}\n\n.m-auto {\n margin: auto !important;\n}\n\n.mt-auto,\n.my-auto {\n margin-top: auto !important;\n}\n\n.mr-auto,\n.mx-auto {\n margin-right: auto !important;\n}\n\n.mb-auto,\n.my-auto {\n margin-bottom: auto !important;\n}\n\n.ml-auto,\n.mx-auto {\n margin-left: auto !important;\n}\n\n@media (min-width: 576px) {\n .m-sm-0 {\n margin: 0 !important;\n }\n .mt-sm-0,\n .my-sm-0 {\n margin-top: 0 !important;\n }\n .mr-sm-0,\n .mx-sm-0 {\n margin-right: 0 !important;\n }\n .mb-sm-0,\n .my-sm-0 {\n margin-bottom: 0 !important;\n }\n .ml-sm-0,\n .mx-sm-0 {\n margin-left: 0 !important;\n }\n .m-sm-1 {\n margin: 0.25rem !important;\n }\n .mt-sm-1,\n .my-sm-1 {\n margin-top: 0.25rem !important;\n }\n .mr-sm-1,\n .mx-sm-1 {\n margin-right: 0.25rem !important;\n }\n .mb-sm-1,\n .my-sm-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-sm-1,\n .mx-sm-1 {\n margin-left: 0.25rem !important;\n }\n .m-sm-2 {\n margin: 0.5rem !important;\n }\n .mt-sm-2,\n .my-sm-2 {\n margin-top: 0.5rem !important;\n }\n .mr-sm-2,\n .mx-sm-2 {\n margin-right: 0.5rem !important;\n }\n .mb-sm-2,\n .my-sm-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-sm-2,\n .mx-sm-2 {\n margin-left: 0.5rem !important;\n }\n .m-sm-3 {\n margin: 1rem !important;\n }\n .mt-sm-3,\n .my-sm-3 {\n margin-top: 1rem !important;\n }\n .mr-sm-3,\n .mx-sm-3 {\n margin-right: 1rem !important;\n }\n .mb-sm-3,\n .my-sm-3 {\n margin-bottom: 1rem !important;\n }\n .ml-sm-3,\n .mx-sm-3 {\n margin-left: 1rem !important;\n }\n .m-sm-4 {\n margin: 1.5rem !important;\n }\n .mt-sm-4,\n .my-sm-4 {\n margin-top: 1.5rem !important;\n }\n .mr-sm-4,\n .mx-sm-4 {\n margin-right: 1.5rem !important;\n }\n .mb-sm-4,\n .my-sm-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-sm-4,\n .mx-sm-4 {\n margin-left: 1.5rem !important;\n }\n .m-sm-5 {\n margin: 3rem !important;\n }\n .mt-sm-5,\n .my-sm-5 {\n margin-top: 3rem !important;\n }\n .mr-sm-5,\n .mx-sm-5 {\n margin-right: 3rem !important;\n }\n .mb-sm-5,\n .my-sm-5 {\n margin-bottom: 3rem !important;\n }\n .ml-sm-5,\n .mx-sm-5 {\n margin-left: 3rem !important;\n }\n .p-sm-0 {\n padding: 0 !important;\n }\n .pt-sm-0,\n .py-sm-0 {\n padding-top: 0 !important;\n }\n .pr-sm-0,\n .px-sm-0 {\n padding-right: 0 !important;\n }\n .pb-sm-0,\n .py-sm-0 {\n padding-bottom: 0 !important;\n }\n .pl-sm-0,\n .px-sm-0 {\n padding-left: 0 !important;\n }\n .p-sm-1 {\n padding: 0.25rem !important;\n }\n .pt-sm-1,\n .py-sm-1 {\n padding-top: 0.25rem !important;\n }\n .pr-sm-1,\n .px-sm-1 {\n padding-right: 0.25rem !important;\n }\n .pb-sm-1,\n .py-sm-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-sm-1,\n .px-sm-1 {\n padding-left: 0.25rem !important;\n }\n .p-sm-2 {\n padding: 0.5rem !important;\n }\n .pt-sm-2,\n .py-sm-2 {\n padding-top: 0.5rem !important;\n }\n .pr-sm-2,\n .px-sm-2 {\n padding-right: 0.5rem !important;\n }\n .pb-sm-2,\n .py-sm-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-sm-2,\n .px-sm-2 {\n padding-left: 0.5rem !important;\n }\n .p-sm-3 {\n padding: 1rem !important;\n }\n .pt-sm-3,\n .py-sm-3 {\n padding-top: 1rem !important;\n }\n .pr-sm-3,\n .px-sm-3 {\n padding-right: 1rem !important;\n }\n .pb-sm-3,\n .py-sm-3 {\n padding-bottom: 1rem !important;\n }\n .pl-sm-3,\n .px-sm-3 {\n padding-left: 1rem !important;\n }\n .p-sm-4 {\n padding: 1.5rem !important;\n }\n .pt-sm-4,\n .py-sm-4 {\n padding-top: 1.5rem !important;\n }\n .pr-sm-4,\n .px-sm-4 {\n padding-right: 1.5rem !important;\n }\n .pb-sm-4,\n .py-sm-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-sm-4,\n .px-sm-4 {\n padding-left: 1.5rem !important;\n }\n .p-sm-5 {\n padding: 3rem !important;\n }\n .pt-sm-5,\n .py-sm-5 {\n padding-top: 3rem !important;\n }\n .pr-sm-5,\n .px-sm-5 {\n padding-right: 3rem !important;\n }\n .pb-sm-5,\n .py-sm-5 {\n padding-bottom: 3rem !important;\n }\n .pl-sm-5,\n .px-sm-5 {\n padding-left: 3rem !important;\n }\n .m-sm-auto {\n margin: auto !important;\n }\n .mt-sm-auto,\n .my-sm-auto {\n margin-top: auto !important;\n }\n .mr-sm-auto,\n .mx-sm-auto {\n margin-right: auto !important;\n }\n .mb-sm-auto,\n .my-sm-auto {\n margin-bottom: auto !important;\n }\n .ml-sm-auto,\n .mx-sm-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 768px) {\n .m-md-0 {\n margin: 0 !important;\n }\n .mt-md-0,\n .my-md-0 {\n margin-top: 0 !important;\n }\n .mr-md-0,\n .mx-md-0 {\n margin-right: 0 !important;\n }\n .mb-md-0,\n .my-md-0 {\n margin-bottom: 0 !important;\n }\n .ml-md-0,\n .mx-md-0 {\n margin-left: 0 !important;\n }\n .m-md-1 {\n margin: 0.25rem !important;\n }\n .mt-md-1,\n .my-md-1 {\n margin-top: 0.25rem !important;\n }\n .mr-md-1,\n .mx-md-1 {\n margin-right: 0.25rem !important;\n }\n .mb-md-1,\n .my-md-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-md-1,\n .mx-md-1 {\n margin-left: 0.25rem !important;\n }\n .m-md-2 {\n margin: 0.5rem !important;\n }\n .mt-md-2,\n .my-md-2 {\n margin-top: 0.5rem !important;\n }\n .mr-md-2,\n .mx-md-2 {\n margin-right: 0.5rem !important;\n }\n .mb-md-2,\n .my-md-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-md-2,\n .mx-md-2 {\n margin-left: 0.5rem !important;\n }\n .m-md-3 {\n margin: 1rem !important;\n }\n .mt-md-3,\n .my-md-3 {\n margin-top: 1rem !important;\n }\n .mr-md-3,\n .mx-md-3 {\n margin-right: 1rem !important;\n }\n .mb-md-3,\n .my-md-3 {\n margin-bottom: 1rem !important;\n }\n .ml-md-3,\n .mx-md-3 {\n margin-left: 1rem !important;\n }\n .m-md-4 {\n margin: 1.5rem !important;\n }\n .mt-md-4,\n .my-md-4 {\n margin-top: 1.5rem !important;\n }\n .mr-md-4,\n .mx-md-4 {\n margin-right: 1.5rem !important;\n }\n .mb-md-4,\n .my-md-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-md-4,\n .mx-md-4 {\n margin-left: 1.5rem !important;\n }\n .m-md-5 {\n margin: 3rem !important;\n }\n .mt-md-5,\n .my-md-5 {\n margin-top: 3rem !important;\n }\n .mr-md-5,\n .mx-md-5 {\n margin-right: 3rem !important;\n }\n .mb-md-5,\n .my-md-5 {\n margin-bottom: 3rem !important;\n }\n .ml-md-5,\n .mx-md-5 {\n margin-left: 3rem !important;\n }\n .p-md-0 {\n padding: 0 !important;\n }\n .pt-md-0,\n .py-md-0 {\n padding-top: 0 !important;\n }\n .pr-md-0,\n .px-md-0 {\n padding-right: 0 !important;\n }\n .pb-md-0,\n .py-md-0 {\n padding-bottom: 0 !important;\n }\n .pl-md-0,\n .px-md-0 {\n padding-left: 0 !important;\n }\n .p-md-1 {\n padding: 0.25rem !important;\n }\n .pt-md-1,\n .py-md-1 {\n padding-top: 0.25rem !important;\n }\n .pr-md-1,\n .px-md-1 {\n padding-right: 0.25rem !important;\n }\n .pb-md-1,\n .py-md-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-md-1,\n .px-md-1 {\n padding-left: 0.25rem !important;\n }\n .p-md-2 {\n padding: 0.5rem !important;\n }\n .pt-md-2,\n .py-md-2 {\n padding-top: 0.5rem !important;\n }\n .pr-md-2,\n .px-md-2 {\n padding-right: 0.5rem !important;\n }\n .pb-md-2,\n .py-md-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-md-2,\n .px-md-2 {\n padding-left: 0.5rem !important;\n }\n .p-md-3 {\n padding: 1rem !important;\n }\n .pt-md-3,\n .py-md-3 {\n padding-top: 1rem !important;\n }\n .pr-md-3,\n .px-md-3 {\n padding-right: 1rem !important;\n }\n .pb-md-3,\n .py-md-3 {\n padding-bottom: 1rem !important;\n }\n .pl-md-3,\n .px-md-3 {\n padding-left: 1rem !important;\n }\n .p-md-4 {\n padding: 1.5rem !important;\n }\n .pt-md-4,\n .py-md-4 {\n padding-top: 1.5rem !important;\n }\n .pr-md-4,\n .px-md-4 {\n padding-right: 1.5rem !important;\n }\n .pb-md-4,\n .py-md-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-md-4,\n .px-md-4 {\n padding-left: 1.5rem !important;\n }\n .p-md-5 {\n padding: 3rem !important;\n }\n .pt-md-5,\n .py-md-5 {\n padding-top: 3rem !important;\n }\n .pr-md-5,\n .px-md-5 {\n padding-right: 3rem !important;\n }\n .pb-md-5,\n .py-md-5 {\n padding-bottom: 3rem !important;\n }\n .pl-md-5,\n .px-md-5 {\n padding-left: 3rem !important;\n }\n .m-md-auto {\n margin: auto !important;\n }\n .mt-md-auto,\n .my-md-auto {\n margin-top: auto !important;\n }\n .mr-md-auto,\n .mx-md-auto {\n margin-right: auto !important;\n }\n .mb-md-auto,\n .my-md-auto {\n margin-bottom: auto !important;\n }\n .ml-md-auto,\n .mx-md-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 992px) {\n .m-lg-0 {\n margin: 0 !important;\n }\n .mt-lg-0,\n .my-lg-0 {\n margin-top: 0 !important;\n }\n .mr-lg-0,\n .mx-lg-0 {\n margin-right: 0 !important;\n }\n .mb-lg-0,\n .my-lg-0 {\n margin-bottom: 0 !important;\n }\n .ml-lg-0,\n .mx-lg-0 {\n margin-left: 0 !important;\n }\n .m-lg-1 {\n margin: 0.25rem !important;\n }\n .mt-lg-1,\n .my-lg-1 {\n margin-top: 0.25rem !important;\n }\n .mr-lg-1,\n .mx-lg-1 {\n margin-right: 0.25rem !important;\n }\n .mb-lg-1,\n .my-lg-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-lg-1,\n .mx-lg-1 {\n margin-left: 0.25rem !important;\n }\n .m-lg-2 {\n margin: 0.5rem !important;\n }\n .mt-lg-2,\n .my-lg-2 {\n margin-top: 0.5rem !important;\n }\n .mr-lg-2,\n .mx-lg-2 {\n margin-right: 0.5rem !important;\n }\n .mb-lg-2,\n .my-lg-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-lg-2,\n .mx-lg-2 {\n margin-left: 0.5rem !important;\n }\n .m-lg-3 {\n margin: 1rem !important;\n }\n .mt-lg-3,\n .my-lg-3 {\n margin-top: 1rem !important;\n }\n .mr-lg-3,\n .mx-lg-3 {\n margin-right: 1rem !important;\n }\n .mb-lg-3,\n .my-lg-3 {\n margin-bottom: 1rem !important;\n }\n .ml-lg-3,\n .mx-lg-3 {\n margin-left: 1rem !important;\n }\n .m-lg-4 {\n margin: 1.5rem !important;\n }\n .mt-lg-4,\n .my-lg-4 {\n margin-top: 1.5rem !important;\n }\n .mr-lg-4,\n .mx-lg-4 {\n margin-right: 1.5rem !important;\n }\n .mb-lg-4,\n .my-lg-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-lg-4,\n .mx-lg-4 {\n margin-left: 1.5rem !important;\n }\n .m-lg-5 {\n margin: 3rem !important;\n }\n .mt-lg-5,\n .my-lg-5 {\n margin-top: 3rem !important;\n }\n .mr-lg-5,\n .mx-lg-5 {\n margin-right: 3rem !important;\n }\n .mb-lg-5,\n .my-lg-5 {\n margin-bottom: 3rem !important;\n }\n .ml-lg-5,\n .mx-lg-5 {\n margin-left: 3rem !important;\n }\n .p-lg-0 {\n padding: 0 !important;\n }\n .pt-lg-0,\n .py-lg-0 {\n padding-top: 0 !important;\n }\n .pr-lg-0,\n .px-lg-0 {\n padding-right: 0 !important;\n }\n .pb-lg-0,\n .py-lg-0 {\n padding-bottom: 0 !important;\n }\n .pl-lg-0,\n .px-lg-0 {\n padding-left: 0 !important;\n }\n .p-lg-1 {\n padding: 0.25rem !important;\n }\n .pt-lg-1,\n .py-lg-1 {\n padding-top: 0.25rem !important;\n }\n .pr-lg-1,\n .px-lg-1 {\n padding-right: 0.25rem !important;\n }\n .pb-lg-1,\n .py-lg-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-lg-1,\n .px-lg-1 {\n padding-left: 0.25rem !important;\n }\n .p-lg-2 {\n padding: 0.5rem !important;\n }\n .pt-lg-2,\n .py-lg-2 {\n padding-top: 0.5rem !important;\n }\n .pr-lg-2,\n .px-lg-2 {\n padding-right: 0.5rem !important;\n }\n .pb-lg-2,\n .py-lg-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-lg-2,\n .px-lg-2 {\n padding-left: 0.5rem !important;\n }\n .p-lg-3 {\n padding: 1rem !important;\n }\n .pt-lg-3,\n .py-lg-3 {\n padding-top: 1rem !important;\n }\n .pr-lg-3,\n .px-lg-3 {\n padding-right: 1rem !important;\n }\n .pb-lg-3,\n .py-lg-3 {\n padding-bottom: 1rem !important;\n }\n .pl-lg-3,\n .px-lg-3 {\n padding-left: 1rem !important;\n }\n .p-lg-4 {\n padding: 1.5rem !important;\n }\n .pt-lg-4,\n .py-lg-4 {\n padding-top: 1.5rem !important;\n }\n .pr-lg-4,\n .px-lg-4 {\n padding-right: 1.5rem !important;\n }\n .pb-lg-4,\n .py-lg-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-lg-4,\n .px-lg-4 {\n padding-left: 1.5rem !important;\n }\n .p-lg-5 {\n padding: 3rem !important;\n }\n .pt-lg-5,\n .py-lg-5 {\n padding-top: 3rem !important;\n }\n .pr-lg-5,\n .px-lg-5 {\n padding-right: 3rem !important;\n }\n .pb-lg-5,\n .py-lg-5 {\n padding-bottom: 3rem !important;\n }\n .pl-lg-5,\n .px-lg-5 {\n padding-left: 3rem !important;\n }\n .m-lg-auto {\n margin: auto !important;\n }\n .mt-lg-auto,\n .my-lg-auto {\n margin-top: auto !important;\n }\n .mr-lg-auto,\n .mx-lg-auto {\n margin-right: auto !important;\n }\n .mb-lg-auto,\n .my-lg-auto {\n margin-bottom: auto !important;\n }\n .ml-lg-auto,\n .mx-lg-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 1200px) {\n .m-xl-0 {\n margin: 0 !important;\n }\n .mt-xl-0,\n .my-xl-0 {\n margin-top: 0 !important;\n }\n .mr-xl-0,\n .mx-xl-0 {\n margin-right: 0 !important;\n }\n .mb-xl-0,\n .my-xl-0 {\n margin-bottom: 0 !important;\n }\n .ml-xl-0,\n .mx-xl-0 {\n margin-left: 0 !important;\n }\n .m-xl-1 {\n margin: 0.25rem !important;\n }\n .mt-xl-1,\n .my-xl-1 {\n margin-top: 0.25rem !important;\n }\n .mr-xl-1,\n .mx-xl-1 {\n margin-right: 0.25rem !important;\n }\n .mb-xl-1,\n .my-xl-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-xl-1,\n .mx-xl-1 {\n margin-left: 0.25rem !important;\n }\n .m-xl-2 {\n margin: 0.5rem !important;\n }\n .mt-xl-2,\n .my-xl-2 {\n margin-top: 0.5rem !important;\n }\n .mr-xl-2,\n .mx-xl-2 {\n margin-right: 0.5rem !important;\n }\n .mb-xl-2,\n .my-xl-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-xl-2,\n .mx-xl-2 {\n margin-left: 0.5rem !important;\n }\n .m-xl-3 {\n margin: 1rem !important;\n }\n .mt-xl-3,\n .my-xl-3 {\n margin-top: 1rem !important;\n }\n .mr-xl-3,\n .mx-xl-3 {\n margin-right: 1rem !important;\n }\n .mb-xl-3,\n .my-xl-3 {\n margin-bottom: 1rem !important;\n }\n .ml-xl-3,\n .mx-xl-3 {\n margin-left: 1rem !important;\n }\n .m-xl-4 {\n margin: 1.5rem !important;\n }\n .mt-xl-4,\n .my-xl-4 {\n margin-top: 1.5rem !important;\n }\n .mr-xl-4,\n .mx-xl-4 {\n margin-right: 1.5rem !important;\n }\n .mb-xl-4,\n .my-xl-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-xl-4,\n .mx-xl-4 {\n margin-left: 1.5rem !important;\n }\n .m-xl-5 {\n margin: 3rem !important;\n }\n .mt-xl-5,\n .my-xl-5 {\n margin-top: 3rem !important;\n }\n .mr-xl-5,\n .mx-xl-5 {\n margin-right: 3rem !important;\n }\n .mb-xl-5,\n .my-xl-5 {\n margin-bottom: 3rem !important;\n }\n .ml-xl-5,\n .mx-xl-5 {\n margin-left: 3rem !important;\n }\n .p-xl-0 {\n padding: 0 !important;\n }\n .pt-xl-0,\n .py-xl-0 {\n padding-top: 0 !important;\n }\n .pr-xl-0,\n .px-xl-0 {\n padding-right: 0 !important;\n }\n .pb-xl-0,\n .py-xl-0 {\n padding-bottom: 0 !important;\n }\n .pl-xl-0,\n .px-xl-0 {\n padding-left: 0 !important;\n }\n .p-xl-1 {\n padding: 0.25rem !important;\n }\n .pt-xl-1,\n .py-xl-1 {\n padding-top: 0.25rem !important;\n }\n .pr-xl-1,\n .px-xl-1 {\n padding-right: 0.25rem !important;\n }\n .pb-xl-1,\n .py-xl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-xl-1,\n .px-xl-1 {\n padding-left: 0.25rem !important;\n }\n .p-xl-2 {\n padding: 0.5rem !important;\n }\n .pt-xl-2,\n .py-xl-2 {\n padding-top: 0.5rem !important;\n }\n .pr-xl-2,\n .px-xl-2 {\n padding-right: 0.5rem !important;\n }\n .pb-xl-2,\n .py-xl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-xl-2,\n .px-xl-2 {\n padding-left: 0.5rem !important;\n }\n .p-xl-3 {\n padding: 1rem !important;\n }\n .pt-xl-3,\n .py-xl-3 {\n padding-top: 1rem !important;\n }\n .pr-xl-3,\n .px-xl-3 {\n padding-right: 1rem !important;\n }\n .pb-xl-3,\n .py-xl-3 {\n padding-bottom: 1rem !important;\n }\n .pl-xl-3,\n .px-xl-3 {\n padding-left: 1rem !important;\n }\n .p-xl-4 {\n padding: 1.5rem !important;\n }\n .pt-xl-4,\n .py-xl-4 {\n padding-top: 1.5rem !important;\n }\n .pr-xl-4,\n .px-xl-4 {\n padding-right: 1.5rem !important;\n }\n .pb-xl-4,\n .py-xl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-xl-4,\n .px-xl-4 {\n padding-left: 1.5rem !important;\n }\n .p-xl-5 {\n padding: 3rem !important;\n }\n .pt-xl-5,\n .py-xl-5 {\n padding-top: 3rem !important;\n }\n .pr-xl-5,\n .px-xl-5 {\n padding-right: 3rem !important;\n }\n .pb-xl-5,\n .py-xl-5 {\n padding-bottom: 3rem !important;\n }\n .pl-xl-5,\n .px-xl-5 {\n padding-left: 3rem !important;\n }\n .m-xl-auto {\n margin: auto !important;\n }\n .mt-xl-auto,\n .my-xl-auto {\n margin-top: auto !important;\n }\n .mr-xl-auto,\n .mx-xl-auto {\n margin-right: auto !important;\n }\n .mb-xl-auto,\n .my-xl-auto {\n margin-bottom: auto !important;\n }\n .ml-xl-auto,\n .mx-xl-auto {\n margin-left: auto !important;\n }\n}\n\n.text-monospace {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n.text-justify {\n text-align: justify !important;\n}\n\n.text-nowrap {\n white-space: nowrap !important;\n}\n\n.text-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.text-left {\n text-align: left !important;\n}\n\n.text-right {\n text-align: right !important;\n}\n\n.text-center {\n text-align: center !important;\n}\n\n@media (min-width: 576px) {\n .text-sm-left {\n text-align: left !important;\n }\n .text-sm-right {\n text-align: right !important;\n }\n .text-sm-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 768px) {\n .text-md-left {\n text-align: left !important;\n }\n .text-md-right {\n text-align: right !important;\n }\n .text-md-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 992px) {\n .text-lg-left {\n text-align: left !important;\n }\n .text-lg-right {\n text-align: right !important;\n }\n .text-lg-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 1200px) {\n .text-xl-left {\n text-align: left !important;\n }\n .text-xl-right {\n text-align: right !important;\n }\n .text-xl-center {\n text-align: center !important;\n }\n}\n\n.text-lowercase {\n text-transform: lowercase !important;\n}\n\n.text-uppercase {\n text-transform: uppercase !important;\n}\n\n.text-capitalize {\n text-transform: capitalize !important;\n}\n\n.font-weight-light {\n font-weight: 300 !important;\n}\n\n.font-weight-normal {\n font-weight: 400 !important;\n}\n\n.font-weight-bold {\n font-weight: 700 !important;\n}\n\n.font-italic {\n font-style: italic !important;\n}\n\n.text-white {\n color: #fff !important;\n}\n\n.text-primary {\n color: #007bff !important;\n}\n\na.text-primary:hover, a.text-primary:focus {\n color: #0062cc !important;\n}\n\n.text-secondary {\n color: #6c757d !important;\n}\n\na.text-secondary:hover, a.text-secondary:focus {\n color: #545b62 !important;\n}\n\n.text-success {\n color: #28a745 !important;\n}\n\na.text-success:hover, a.text-success:focus {\n color: #1e7e34 !important;\n}\n\n.text-info {\n color: #17a2b8 !important;\n}\n\na.text-info:hover, a.text-info:focus {\n color: #117a8b !important;\n}\n\n.text-warning {\n color: #ffc107 !important;\n}\n\na.text-warning:hover, a.text-warning:focus {\n color: #d39e00 !important;\n}\n\n.text-danger {\n color: #dc3545 !important;\n}\n\na.text-danger:hover, a.text-danger:focus {\n color: #bd2130 !important;\n}\n\n.text-light {\n color: #f8f9fa !important;\n}\n\na.text-light:hover, a.text-light:focus {\n color: #dae0e5 !important;\n}\n\n.text-dark {\n color: #343a40 !important;\n}\n\na.text-dark:hover, a.text-dark:focus {\n color: #1d2124 !important;\n}\n\n.text-body {\n color: #212529 !important;\n}\n\n.text-muted {\n color: #6c757d !important;\n}\n\n.text-black-50 {\n color: rgba(0, 0, 0, 0.5) !important;\n}\n\n.text-white-50 {\n color: rgba(255, 255, 255, 0.5) !important;\n}\n\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n\n.visible {\n visibility: visible !important;\n}\n\n.invisible {\n visibility: hidden !important;\n}\n\n@media print {\n *,\n *::before,\n *::after {\n text-shadow: none !important;\n box-shadow: none !important;\n }\n a:not(.btn) {\n text-decoration: underline;\n }\n abbr[title]::after {\n content: \" (\" attr(title) \")\";\n }\n pre {\n white-space: pre-wrap !important;\n }\n pre,\n blockquote {\n border: 1px solid #adb5bd;\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n @page {\n size: a3;\n }\n body {\n min-width: 992px !important;\n }\n .container {\n min-width: 992px !important;\n }\n .navbar {\n display: none;\n }\n .badge {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #dee2e6 !important;\n }\n .table-dark {\n color: inherit;\n }\n .table-dark th,\n .table-dark td,\n .table-dark thead th,\n .table-dark tbody + tbody {\n border-color: #dee2e6;\n }\n .table .thead-dark th {\n color: inherit;\n border-color: #dee2e6;\n }\n}\n\n/*# sourceMappingURL=bootstrap.css.map */","// Hover mixin and `$enable-hover-media-query` are deprecated.\n//\n// Origally added during our alphas and maintained during betas, this mixin was\n// designed to prevent `:hover` stickiness on iOS-an issue where hover styles\n// would persist after initial touch.\n//\n// For backward compatibility, we've kept these mixins and updated them to\n// always return their regular pseudo-classes instead of a shimmed media query.\n//\n// Issue: https://github.com/twbs/bootstrap/issues/25195\n\n@mixin hover {\n &:hover { @content; }\n}\n\n@mixin hover-focus {\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin plain-hover-focus {\n &,\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin hover-focus-active {\n &:hover,\n &:focus,\n &:active {\n @content;\n }\n}\n","// stylelint-disable declaration-no-important, selector-list-comma-newline-after\n\n//\n// Headings\n//\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n margin-bottom: $headings-margin-bottom;\n font-family: $headings-font-family;\n font-weight: $headings-font-weight;\n line-height: $headings-line-height;\n color: $headings-color;\n}\n\nh1, .h1 { font-size: $h1-font-size; }\nh2, .h2 { font-size: $h2-font-size; }\nh3, .h3 { font-size: $h3-font-size; }\nh4, .h4 { font-size: $h4-font-size; }\nh5, .h5 { font-size: $h5-font-size; }\nh6, .h6 { font-size: $h6-font-size; }\n\n.lead {\n font-size: $lead-font-size;\n font-weight: $lead-font-weight;\n}\n\n// Type display classes\n.display-1 {\n font-size: $display1-size;\n font-weight: $display1-weight;\n line-height: $display-line-height;\n}\n.display-2 {\n font-size: $display2-size;\n font-weight: $display2-weight;\n line-height: $display-line-height;\n}\n.display-3 {\n font-size: $display3-size;\n font-weight: $display3-weight;\n line-height: $display-line-height;\n}\n.display-4 {\n font-size: $display4-size;\n font-weight: $display4-weight;\n line-height: $display-line-height;\n}\n\n\n//\n// Horizontal rules\n//\n\nhr {\n margin-top: $hr-margin-y;\n margin-bottom: $hr-margin-y;\n border: 0;\n border-top: $hr-border-width solid $hr-border-color;\n}\n\n\n//\n// Emphasis\n//\n\nsmall,\n.small {\n font-size: $small-font-size;\n font-weight: $font-weight-normal;\n}\n\nmark,\n.mark {\n padding: $mark-padding;\n background-color: $mark-bg;\n}\n\n\n//\n// Lists\n//\n\n.list-unstyled {\n @include list-unstyled;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n @include list-unstyled;\n}\n.list-inline-item {\n display: inline-block;\n\n &:not(:last-child) {\n margin-right: $list-inline-padding;\n }\n}\n\n\n//\n// Misc\n//\n\n// Builds on `abbr`\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\n\n// Blockquotes\n.blockquote {\n margin-bottom: $spacer;\n font-size: $blockquote-font-size;\n}\n\n.blockquote-footer {\n display: block;\n font-size: 80%; // back to default font-size\n color: $blockquote-small-color;\n\n &::before {\n content: \"\\2014 \\00A0\"; // em dash, nbsp\n }\n}\n","// Lists\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n@mixin list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n","// Responsive images (ensure images don't scale beyond their parents)\n//\n// This is purposefully opt-in via an explicit class rather than being the default for all ``s.\n// We previously tried the \"images are responsive by default\" approach in Bootstrap v2,\n// and abandoned it in Bootstrap v3 because it breaks lots of third-party widgets (including Google Maps)\n// which weren't expecting the images within themselves to be involuntarily resized.\n// See also https://github.com/twbs/bootstrap/issues/18178\n.img-fluid {\n @include img-fluid;\n}\n\n\n// Image thumbnails\n.img-thumbnail {\n padding: $thumbnail-padding;\n background-color: $thumbnail-bg;\n border: $thumbnail-border-width solid $thumbnail-border-color;\n @include border-radius($thumbnail-border-radius);\n @include box-shadow($thumbnail-box-shadow);\n\n // Keep them at most 100% wide\n @include img-fluid;\n}\n\n//\n// Figures\n//\n\n.figure {\n // Ensures the caption's text aligns with the image.\n display: inline-block;\n}\n\n.figure-img {\n margin-bottom: ($spacer / 2);\n line-height: 1;\n}\n\n.figure-caption {\n font-size: $figure-caption-font-size;\n color: $figure-caption-color;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n\n@mixin img-fluid {\n // Part 1: Set a maximum relative to the parent\n max-width: 100%;\n // Part 2: Override the height to auto, otherwise images will be stretched\n // when setting a width and height attribute on the img element.\n height: auto;\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size.\n\n// stylelint-disable indentation, media-query-list-comma-newline-after\n@mixin img-retina($file-1x, $file-2x, $width-1x, $height-1x) {\n background-image: url($file-1x);\n\n // Autoprefixer takes care of adding -webkit-min-device-pixel-ratio and -o-min-device-pixel-ratio,\n // but doesn't convert dppx=>dpi.\n // There's no such thing as unprefixed min-device-pixel-ratio since it's nonstandard.\n // Compatibility info: https://caniuse.com/#feat=css-media-resolution\n @media only screen and (min-resolution: 192dpi), // IE9-11 don't support dppx\n only screen and (min-resolution: 2dppx) { // Standardized\n background-image: url($file-2x);\n background-size: $width-1x $height-1x;\n }\n}\n","// Single side border-radius\n\n@mixin border-radius($radius: $border-radius) {\n @if $enable-rounded {\n border-radius: $radius;\n }\n}\n\n@mixin border-top-radius($radius) {\n @if $enable-rounded {\n border-top-left-radius: $radius;\n border-top-right-radius: $radius;\n }\n}\n\n@mixin border-right-radius($radius) {\n @if $enable-rounded {\n border-top-right-radius: $radius;\n border-bottom-right-radius: $radius;\n }\n}\n\n@mixin border-bottom-radius($radius) {\n @if $enable-rounded {\n border-bottom-right-radius: $radius;\n border-bottom-left-radius: $radius;\n }\n}\n\n@mixin border-left-radius($radius) {\n @if $enable-rounded {\n border-top-left-radius: $radius;\n border-bottom-left-radius: $radius;\n }\n}\n","// Inline code\ncode {\n font-size: $code-font-size;\n color: $code-color;\n word-break: break-word;\n\n // Streamline the style when inside anchors to avoid broken underline and more\n a > & {\n color: inherit;\n }\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: $kbd-padding-y $kbd-padding-x;\n font-size: $kbd-font-size;\n color: $kbd-color;\n background-color: $kbd-bg;\n @include border-radius($border-radius-sm);\n @include box-shadow($kbd-box-shadow);\n\n kbd {\n padding: 0;\n font-size: 100%;\n font-weight: $nested-kbd-font-weight;\n @include box-shadow(none);\n }\n}\n\n// Blocks of code\npre {\n display: block;\n font-size: $code-font-size;\n color: $pre-color;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n font-size: inherit;\n color: inherit;\n word-break: normal;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: $pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n@if $enable-grid-classes {\n .container {\n @include make-container();\n @include make-container-max-widths();\n }\n}\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but with 100% width for\n// fluid, full width layouts.\n\n@if $enable-grid-classes {\n .container-fluid {\n @include make-container();\n }\n}\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n@if $enable-grid-classes {\n .row {\n @include make-row();\n }\n\n // Remove the negative margin from default .row, then the horizontal padding\n // from all immediate children columns (to prevent runaway style inheritance).\n .no-gutters {\n margin-right: 0;\n margin-left: 0;\n\n > .col,\n > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n }\n }\n}\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n@if $enable-grid-classes {\n @include make-grid-columns();\n}\n","/// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n@mixin make-container() {\n width: 100%;\n padding-right: ($grid-gutter-width / 2);\n padding-left: ($grid-gutter-width / 2);\n margin-right: auto;\n margin-left: auto;\n}\n\n\n// For each breakpoint, define the maximum width of the container in a media query\n@mixin make-container-max-widths($max-widths: $container-max-widths, $breakpoints: $grid-breakpoints) {\n @each $breakpoint, $container-max-width in $max-widths {\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n max-width: $container-max-width;\n }\n }\n}\n\n@mixin make-row() {\n display: flex;\n flex-wrap: wrap;\n margin-right: ($grid-gutter-width / -2);\n margin-left: ($grid-gutter-width / -2);\n}\n\n@mixin make-col-ready() {\n position: relative;\n // Prevent columns from becoming too narrow when at smaller grid tiers by\n // always setting `width: 100%;`. This works because we use `flex` values\n // later on to override this initial width.\n width: 100%;\n min-height: 1px; // Prevent collapsing\n padding-right: ($grid-gutter-width / 2);\n padding-left: ($grid-gutter-width / 2);\n}\n\n@mixin make-col($size, $columns: $grid-columns) {\n flex: 0 0 percentage($size / $columns);\n // Add a `max-width` to ensure content within each column does not blow out\n // the width of the column. Applies to IE10+ and Firefox. Chrome and Safari\n // do not appear to require this.\n max-width: percentage($size / $columns);\n}\n\n@mixin make-col-offset($size, $columns: $grid-columns) {\n $num: $size / $columns;\n margin-left: if($num == 0, 0, percentage($num));\n}\n","// Breakpoint viewport sizes and media queries.\n//\n// Breakpoints are defined as a map of (name: minimum width), order from small to large:\n//\n// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)\n//\n// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default.\n\n// Name of the next breakpoint, or null for the last breakpoint.\n//\n// >> breakpoint-next(sm)\n// md\n// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// md\n// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl))\n// md\n@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {\n $n: index($breakpoint-names, $name);\n @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);\n}\n\n// Minimum breakpoint width. Null for the smallest (first) breakpoint.\n//\n// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// 576px\n@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {\n $min: map-get($breakpoints, $name);\n @return if($min != 0, $min, null);\n}\n\n// Maximum breakpoint width. Null for the largest (last) breakpoint.\n// The maximum value is calculated as the minimum of the next one less 0.02px\n// to work around the limitations of `min-` and `max-` prefixes and viewports with fractional widths.\n// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max\n// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari.\n// See https://bugs.webkit.org/show_bug.cgi?id=178261\n//\n// >> breakpoint-max(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// 767.98px\n@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {\n $next: breakpoint-next($name, $breakpoints);\n @return if($next, breakpoint-min($next, $breakpoints) - .02px, null);\n}\n\n// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash infront.\n// Useful for making responsive utilities.\n//\n// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// \"\" (Returns a blank string)\n// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// \"-sm\"\n@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {\n @return if(breakpoint-min($name, $breakpoints) == null, \"\", \"-#{$name}\");\n}\n\n// Media of at least the minimum breakpoint width. No query for the smallest breakpoint.\n// Makes the @content apply to the given breakpoint and wider.\n@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n @if $min {\n @media (min-width: $min) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media of at most the maximum breakpoint width. No query for the largest breakpoint.\n// Makes the @content apply to the given breakpoint and narrower.\n@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {\n $max: breakpoint-max($name, $breakpoints);\n @if $max {\n @media (max-width: $max) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media that spans multiple breakpoint widths.\n// Makes the @content apply between the min and max breakpoints\n@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($lower, $breakpoints);\n $max: breakpoint-max($upper, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($lower, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($upper, $breakpoints) {\n @content;\n }\n }\n}\n\n// Media between the breakpoint's minimum and maximum widths.\n// No minimum for the smallest breakpoint, and no maximum for the largest one.\n// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower.\n@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n $max: breakpoint-max($name, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($name, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($name, $breakpoints) {\n @content;\n }\n }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `$grid-columns`.\n\n@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) {\n // Common properties for all breakpoints\n %grid-column {\n position: relative;\n width: 100%;\n min-height: 1px; // Prevent columns from collapsing when empty\n padding-right: ($gutter / 2);\n padding-left: ($gutter / 2);\n }\n\n @each $breakpoint in map-keys($breakpoints) {\n $infix: breakpoint-infix($breakpoint, $breakpoints);\n\n // Allow columns to stretch full width below their breakpoints\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @extend %grid-column;\n }\n }\n .col#{$infix},\n .col#{$infix}-auto {\n @extend %grid-column;\n }\n\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n // Provide basic `.col-{bp}` classes for equal-width flexbox columns\n .col#{$infix} {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col#{$infix}-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none; // Reset earlier grid tiers\n }\n\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @include make-col($i, $columns);\n }\n }\n\n .order#{$infix}-first { order: -1; }\n\n .order#{$infix}-last { order: $columns + 1; }\n\n @for $i from 0 through $columns {\n .order#{$infix}-#{$i} { order: $i; }\n }\n\n // `$columns - 1` because offsetting by the width of an entire row isn't possible\n @for $i from 0 through ($columns - 1) {\n @if not ($infix == \"\" and $i == 0) { // Avoid emitting useless .offset-0\n .offset#{$infix}-#{$i} {\n @include make-col-offset($i, $columns);\n }\n }\n }\n }\n }\n}\n","//\n// Basic Bootstrap table\n//\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: $spacer;\n background-color: $table-bg; // Reset for nesting within parents with `background-color`.\n\n th,\n td {\n padding: $table-cell-padding;\n vertical-align: top;\n border-top: $table-border-width solid $table-border-color;\n }\n\n thead th {\n vertical-align: bottom;\n border-bottom: (2 * $table-border-width) solid $table-border-color;\n }\n\n tbody + tbody {\n border-top: (2 * $table-border-width) solid $table-border-color;\n }\n\n .table {\n background-color: $body-bg;\n }\n}\n\n\n//\n// Condensed table w/ half padding\n//\n\n.table-sm {\n th,\n td {\n padding: $table-cell-padding-sm;\n }\n}\n\n\n// Border versions\n//\n// Add or remove borders all around the table and between all the columns.\n\n.table-bordered {\n border: $table-border-width solid $table-border-color;\n\n th,\n td {\n border: $table-border-width solid $table-border-color;\n }\n\n thead {\n th,\n td {\n border-bottom-width: (2 * $table-border-width);\n }\n }\n}\n\n.table-borderless {\n th,\n td,\n thead th,\n tbody + tbody {\n border: 0;\n }\n}\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n tbody tr:nth-of-type(#{$table-striped-order}) {\n background-color: $table-accent-bg;\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n tbody tr {\n @include hover {\n background-color: $table-hover-bg;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n@each $color, $value in $theme-colors {\n @include table-row-variant($color, theme-color-level($color, -9));\n}\n\n@include table-row-variant(active, $table-active-bg);\n\n\n// Dark styles\n//\n// Same table markup, but inverted color scheme: dark background and light text.\n\n// stylelint-disable-next-line no-duplicate-selectors\n.table {\n .thead-dark {\n th {\n color: $table-dark-color;\n background-color: $table-dark-bg;\n border-color: $table-dark-border-color;\n }\n }\n\n .thead-light {\n th {\n color: $table-head-color;\n background-color: $table-head-bg;\n border-color: $table-border-color;\n }\n }\n}\n\n.table-dark {\n color: $table-dark-color;\n background-color: $table-dark-bg;\n\n th,\n td,\n thead th {\n border-color: $table-dark-border-color;\n }\n\n &.table-bordered {\n border: 0;\n }\n\n &.table-striped {\n tbody tr:nth-of-type(odd) {\n background-color: $table-dark-accent-bg;\n }\n }\n\n &.table-hover {\n tbody tr {\n @include hover {\n background-color: $table-dark-hover-bg;\n }\n }\n }\n}\n\n\n// Responsive tables\n//\n// Generate series of `.table-responsive-*` classes for configuring the screen\n// size of where your table will overflow.\n\n.table-responsive {\n @each $breakpoint in map-keys($grid-breakpoints) {\n $next: breakpoint-next($breakpoint, $grid-breakpoints);\n $infix: breakpoint-infix($next, $grid-breakpoints);\n\n &#{$infix} {\n @include media-breakpoint-down($breakpoint) {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar; // See https://github.com/twbs/bootstrap/pull/10057\n\n // Prevent double border on horizontal scroll due to use of `display: block;`\n > .table-bordered {\n border: 0;\n }\n }\n }\n }\n}\n","// Tables\n\n@mixin table-row-variant($state, $background) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table-#{$state} {\n &,\n > th,\n > td {\n background-color: $background;\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover {\n $hover-background: darken($background, 5%);\n\n .table-#{$state} {\n @include hover {\n background-color: $hover-background;\n\n > td,\n > th {\n background-color: $hover-background;\n }\n }\n }\n }\n}\n","// Bootstrap functions\n//\n// Utility mixins and functions for evalutating source code across our variables, maps, and mixins.\n\n// Ascending\n// Used to evaluate Sass maps like our grid breakpoints.\n@mixin _assert-ascending($map, $map-name) {\n $prev-key: null;\n $prev-num: null;\n @each $key, $num in $map {\n @if $prev-num == null {\n // Do nothing\n } @else if not comparable($prev-num, $num) {\n @warn \"Potentially invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num}, the value of the previous key '#{$prev-key}' !\";\n } @else if $prev-num >= $num {\n @warn \"Invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} which isn't greater than #{$prev-num}, the value of the previous key '#{$prev-key}' !\";\n }\n $prev-key: $key;\n $prev-num: $num;\n }\n}\n\n// Starts at zero\n// Another grid mixin that ensures the min-width of the lowest breakpoint starts at 0.\n@mixin _assert-starts-at-zero($map) {\n $values: map-values($map);\n $first-value: nth($values, 1);\n @if $first-value != 0 {\n @warn \"First breakpoint in `$grid-breakpoints` must start at 0, but starts at #{$first-value}.\";\n }\n}\n\n// Replace `$search` with `$replace` in `$string`\n// Used on our SVG icon backgrounds for custom forms.\n//\n// @author Hugo Giraudel\n// @param {String} $string - Initial string\n// @param {String} $search - Substring to replace\n// @param {String} $replace ('') - New value\n// @return {String} - Updated string\n@function str-replace($string, $search, $replace: \"\") {\n $index: str-index($string, $search);\n\n @if $index {\n @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);\n }\n\n @return $string;\n}\n\n// Color contrast\n@function color-yiq($color) {\n $r: red($color);\n $g: green($color);\n $b: blue($color);\n\n $yiq: (($r * 299) + ($g * 587) + ($b * 114)) / 1000;\n\n @if ($yiq >= $yiq-contrasted-threshold) {\n @return $yiq-text-dark;\n } @else {\n @return $yiq-text-light;\n }\n}\n\n// Retrieve color Sass maps\n@function color($key: \"blue\") {\n @return map-get($colors, $key);\n}\n\n@function theme-color($key: \"primary\") {\n @return map-get($theme-colors, $key);\n}\n\n@function gray($key: \"100\") {\n @return map-get($grays, $key);\n}\n\n// Request a theme color level\n@function theme-color-level($color-name: \"primary\", $level: 0) {\n $color: theme-color($color-name);\n $color-base: if($level > 0, $black, $white);\n $level: abs($level);\n\n @return mix($color-base, $color, $level * $theme-color-interval);\n}\n","// stylelint-disable selector-no-qualifying-type\n\n//\n// Textual form controls\n//\n\n.form-control {\n display: block;\n width: 100%;\n padding: $input-padding-y $input-padding-x;\n font-size: $font-size-base;\n line-height: $input-line-height;\n color: $input-color;\n background-color: $input-bg;\n background-clip: padding-box;\n border: $input-border-width solid $input-border-color;\n\n // Note: This has no effect on `s in CSS.\n @if $enable-rounded {\n // Manually use the if/else instead of the mixin to account for iOS override\n border-radius: $input-border-radius;\n } @else {\n // Otherwise undo the iOS default\n border-radius: 0;\n }\n\n @include box-shadow($input-box-shadow);\n @include transition($input-transition);\n\n // Unstyle the caret on ` receives focus\n // in IE and (under certain conditions) Edge, as it looks bad and cannot be made to\n // match the appearance of the native widget.\n // See https://github.com/twbs/bootstrap/issues/19398.\n color: $input-color;\n background-color: $input-bg;\n }\n}\n\n// Make file inputs better match text inputs by forcing them to new lines.\n.form-control-file,\n.form-control-range {\n display: block;\n width: 100%;\n}\n\n\n//\n// Labels\n//\n\n// For use with horizontal and inline forms, when you need the label (or legend)\n// text to align with the form controls.\n.col-form-label {\n padding-top: calc(#{$input-padding-y} + #{$input-border-width});\n padding-bottom: calc(#{$input-padding-y} + #{$input-border-width});\n margin-bottom: 0; // Override the `
` elements.\n\nbody {\n margin: 0; // 1\n font-family: $font-family-base;\n font-size: $font-size-base;\n font-weight: $font-weight-base;\n line-height: $line-height-base;\n color: $body-color;\n text-align: left; // 3\n background-color: $body-bg; // 2\n}\n\n// Suppress the focus outline on elements that cannot be accessed via keyboard.\n// This prevents an unwanted focus outline from appearing around elements that\n// might still respond to pointer events.\n//\n// Credit: https://github.com/suitcss/base\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\n\n// Content grouping\n//\n// 1. Add the correct box sizing in Firefox.\n// 2. Show the overflow in Edge and IE.\n\nhr {\n box-sizing: content-box; // 1\n height: 0; // 1\n overflow: visible; // 2\n}\n\n\n//\n// Typography\n//\n\n// Remove top margins from headings\n//\n// By default, `

`-`

` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n// stylelint-disable selector-list-comma-newline-after\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: $headings-margin-bottom;\n}\n// stylelint-enable selector-list-comma-newline-after\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `

`s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n// Abbreviations\n//\n// 1. Remove the bottom border in Firefox 39-.\n// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Duplicate behavior to the data-* attribute for our tooltip plugin\n\nabbr[title],\nabbr[data-original-title] { // 4\n text-decoration: underline; // 2\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n border-bottom: 0; // 1\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // Undo browser default\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\ndfn {\n font-style: italic; // Add the correct font style in Android 4.3-\n}\n\n// stylelint-disable font-weight-notation\nb,\nstrong {\n font-weight: bolder; // Add the correct font weight in Chrome, Edge, and Safari\n}\n// stylelint-enable font-weight-notation\n\nsmall {\n font-size: 80%; // Add the correct font size in all browsers\n}\n\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n//\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n//\n// Links\n//\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n background-color: transparent; // Remove the gray background on active links in IE 10.\n -webkit-text-decoration-skip: objects; // Remove gaps in links underline in iOS 8+ and Safari 8+.\n\n @include hover {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href)\n// which have not been made explicitly keyboard-focusable (without tabindex).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n\n @include hover-focus {\n color: inherit;\n text-decoration: none;\n }\n\n &:focus {\n outline: 0;\n }\n}\n\n\n//\n// Code\n//\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-monospace;\n font-size: 1em; // Correct the odd `em` font sizing in all browsers.\n}\n\npre {\n // Remove browser default top margin\n margin-top: 0;\n // Reset browser default of `1em` to use `rem`s\n margin-bottom: 1rem;\n // Don't allow content to break outside\n overflow: auto;\n // We have @viewport set which causes scrollbars to overlap content in IE11 and Edge, so\n // we force a non-overlapping, non-auto-hiding scrollbar to counteract.\n -ms-overflow-style: scrollbar;\n}\n\n\n//\n// Figures\n//\n\nfigure {\n // Apply a consistent margin strategy (matches our type styles).\n margin: 0 0 1rem;\n}\n\n\n//\n// Images and content\n//\n\nimg {\n vertical-align: middle;\n border-style: none; // Remove the border on images inside links in IE 10-.\n}\n\nsvg:not(:root) {\n overflow: hidden; // Hide the overflow in IE\n}\n\n\n//\n// Tables\n//\n\ntable {\n border-collapse: collapse; // Prevent double borders\n}\n\ncaption {\n padding-top: $table-cell-padding;\n padding-bottom: $table-cell-padding;\n color: $table-caption-color;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n // Matches default `

` alignment by inheriting from the ``, or the\n // closest parent with a set `text-align`.\n text-align: inherit;\n}\n\n\n//\n// Forms\n//\n\nlabel {\n // Allow labels to use `margin` for spacing.\n display: inline-block;\n margin-bottom: $label-margin-bottom;\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24093\nbutton {\n border-radius: 0;\n}\n\n// Work around a Firefox/IE bug where the transparent `button` background\n// results in a loss of the default `button` focus styles.\n//\n// Credit: https://github.com/suitcss/base/\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // Remove the margin in Firefox and Safari\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible; // Show the overflow in Edge\n}\n\nbutton,\nselect {\n text-transform: none; // Remove the inheritance of text transform in Firefox\n}\n\n// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`\n// controls in Android 4.\n// 2. Correct the inability to style clickable types in iOS and Safari.\nbutton,\nhtml [type=\"button\"], // 1\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button; // 2\n}\n\n// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box; // 1. Add the correct box sizing in IE 10-\n padding: 0; // 2. Remove the padding in IE 10-\n}\n\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n // Remove the default appearance of temporal inputs to avoid a Mobile Safari\n // bug where setting a custom line-height prevents text from being vertically\n // centered within the input.\n // See https://bugs.webkit.org/show_bug.cgi?id=139848\n // and https://github.com/twbs/bootstrap/issues/11266\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto; // Remove the default vertical scrollbar in IE.\n // Textareas should really only resize vertically so they don't break their (horizontal) containers.\n resize: vertical;\n}\n\nfieldset {\n // Browsers set a default `min-width: min-content;` on fieldsets,\n // unlike e.g. `
`s, which have `min-width: 0;` by default.\n // So we reset that to ensure fieldsets behave more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359\n // and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements\n min-width: 0;\n // Reset the default outline behavior of fieldsets so they don't affect page layout.\n padding: 0;\n margin: 0;\n border: 0;\n}\n\n// 1. Correct the text wrapping in Edge and IE.\n// 2. Correct the color inheritance from `fieldset` elements in IE.\nlegend {\n display: block;\n width: 100%;\n max-width: 100%; // 1\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit; // 2\n white-space: normal; // 1\n}\n\nprogress {\n vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.\n}\n\n// Correct the cursor style of increment and decrement buttons in Chrome.\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n // This overrides the extra rounded corners on search inputs in iOS so that our\n // `.form-control` class can properly style them. Note that this cannot simply\n // be added to `.form-control` as it's not specific enough. For details, see\n // https://github.com/twbs/bootstrap/issues/11586.\n outline-offset: -2px; // 2. Correct the outline style in Safari.\n -webkit-appearance: none;\n}\n\n//\n// Remove the inner padding and cancel buttons in Chrome and Safari on macOS.\n//\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// 1. Correct the inability to style clickable types in iOS and Safari.\n// 2. Change font properties to `inherit` in Safari.\n//\n\n::-webkit-file-upload-button {\n font: inherit; // 2\n -webkit-appearance: button; // 1\n}\n\n//\n// Correct element displays\n//\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item; // Add the correct display in all browsers\n cursor: pointer;\n}\n\ntemplate {\n display: none; // Add the correct display in IE\n}\n\n// Always hide an element with the `hidden` HTML attribute (from PureCSS).\n// Needed for proper display in IE 10-.\n[hidden] {\n display: none !important;\n}\n","/*!\n * Bootstrap v4.1.1 (https://getbootstrap.com/)\n * Copyright 2011-2018 The Bootstrap Authors\n * Copyright 2011-2018 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n:root {\n --blue: #007bff;\n --indigo: #6610f2;\n --purple: #6f42c1;\n --pink: #e83e8c;\n --red: #dc3545;\n --orange: #fd7e14;\n --yellow: #ffc107;\n --green: #28a745;\n --teal: #20c997;\n --cyan: #17a2b8;\n --white: #fff;\n --gray: #6c757d;\n --gray-dark: #343a40;\n --primary: #007bff;\n --secondary: #6c757d;\n --success: #28a745;\n --info: #17a2b8;\n --warning: #ffc107;\n --danger: #dc3545;\n --light: #f8f9fa;\n --dark: #343a40;\n --breakpoint-xs: 0;\n --breakpoint-sm: 576px;\n --breakpoint-md: 768px;\n --breakpoint-lg: 992px;\n --breakpoint-xl: 1200px;\n --font-family-sans-serif: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -ms-text-size-adjust: 100%;\n -ms-overflow-style: scrollbar;\n -webkit-tap-highlight-color: transparent;\n}\n\n@-ms-viewport {\n width: device-width;\n}\n\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\ndfn {\n font-style: italic;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n -webkit-text-decoration-skip: objects;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):focus {\n outline: 0;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n -ms-overflow-style: scrollbar;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: 0.5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nbutton,\nhtml [type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n margin-bottom: 0.5rem;\n font-family: inherit;\n font-weight: 500;\n line-height: 1.2;\n color: inherit;\n}\n\nh1, .h1 {\n font-size: 2.5rem;\n}\n\nh2, .h2 {\n font-size: 2rem;\n}\n\nh3, .h3 {\n font-size: 1.75rem;\n}\n\nh4, .h4 {\n font-size: 1.5rem;\n}\n\nh5, .h5 {\n font-size: 1.25rem;\n}\n\nh6, .h6 {\n font-size: 1rem;\n}\n\n.lead {\n font-size: 1.25rem;\n font-weight: 300;\n}\n\n.display-1 {\n font-size: 6rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-2 {\n font-size: 5.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-3 {\n font-size: 4.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-4 {\n font-size: 3.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\nhr {\n margin-top: 1rem;\n margin-bottom: 1rem;\n border: 0;\n border-top: 1px solid rgba(0, 0, 0, 0.1);\n}\n\nsmall,\n.small {\n font-size: 80%;\n font-weight: 400;\n}\n\nmark,\n.mark {\n padding: 0.2em;\n background-color: #fcf8e3;\n}\n\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline-item {\n display: inline-block;\n}\n\n.list-inline-item:not(:last-child) {\n margin-right: 0.5rem;\n}\n\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\n\n.blockquote {\n margin-bottom: 1rem;\n font-size: 1.25rem;\n}\n\n.blockquote-footer {\n display: block;\n font-size: 80%;\n color: #6c757d;\n}\n\n.blockquote-footer::before {\n content: \"\\2014 \\00A0\";\n}\n\n.img-fluid {\n max-width: 100%;\n height: auto;\n}\n\n.img-thumbnail {\n padding: 0.25rem;\n background-color: #fff;\n border: 1px solid #dee2e6;\n border-radius: 0.25rem;\n max-width: 100%;\n height: auto;\n}\n\n.figure {\n display: inline-block;\n}\n\n.figure-img {\n margin-bottom: 0.5rem;\n line-height: 1;\n}\n\n.figure-caption {\n font-size: 90%;\n color: #6c757d;\n}\n\ncode {\n font-size: 87.5%;\n color: #e83e8c;\n word-break: break-word;\n}\n\na > code {\n color: inherit;\n}\n\nkbd {\n padding: 0.2rem 0.4rem;\n font-size: 87.5%;\n color: #fff;\n background-color: #212529;\n border-radius: 0.2rem;\n}\n\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: 700;\n}\n\npre {\n display: block;\n font-size: 87.5%;\n color: #212529;\n}\n\npre code {\n font-size: inherit;\n color: inherit;\n word-break: normal;\n}\n\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n\n.container {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n@media (min-width: 576px) {\n .container {\n max-width: 540px;\n }\n}\n\n@media (min-width: 768px) {\n .container {\n max-width: 720px;\n }\n}\n\n@media (min-width: 992px) {\n .container {\n max-width: 960px;\n }\n}\n\n@media (min-width: 1200px) {\n .container {\n max-width: 1140px;\n }\n}\n\n.container-fluid {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n.row {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n margin-right: -15px;\n margin-left: -15px;\n}\n\n.no-gutters {\n margin-right: 0;\n margin-left: 0;\n}\n\n.no-gutters > .col,\n.no-gutters > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n}\n\n.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col,\n.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm,\n.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md,\n.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg,\n.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl,\n.col-xl-auto {\n position: relative;\n width: 100%;\n min-height: 1px;\n padding-right: 15px;\n padding-left: 15px;\n}\n\n.col {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n}\n\n.col-auto {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n}\n\n.col-1 {\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n}\n\n.col-2 {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n}\n\n.col-3 {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n}\n\n.col-4 {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n}\n\n.col-5 {\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n}\n\n.col-6 {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n}\n\n.col-7 {\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n}\n\n.col-8 {\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n}\n\n.col-9 {\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n}\n\n.col-10 {\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n}\n\n.col-11 {\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n}\n\n.col-12 {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n}\n\n.order-first {\n -ms-flex-order: -1;\n order: -1;\n}\n\n.order-last {\n -ms-flex-order: 13;\n order: 13;\n}\n\n.order-0 {\n -ms-flex-order: 0;\n order: 0;\n}\n\n.order-1 {\n -ms-flex-order: 1;\n order: 1;\n}\n\n.order-2 {\n -ms-flex-order: 2;\n order: 2;\n}\n\n.order-3 {\n -ms-flex-order: 3;\n order: 3;\n}\n\n.order-4 {\n -ms-flex-order: 4;\n order: 4;\n}\n\n.order-5 {\n -ms-flex-order: 5;\n order: 5;\n}\n\n.order-6 {\n -ms-flex-order: 6;\n order: 6;\n}\n\n.order-7 {\n -ms-flex-order: 7;\n order: 7;\n}\n\n.order-8 {\n -ms-flex-order: 8;\n order: 8;\n}\n\n.order-9 {\n -ms-flex-order: 9;\n order: 9;\n}\n\n.order-10 {\n -ms-flex-order: 10;\n order: 10;\n}\n\n.order-11 {\n -ms-flex-order: 11;\n order: 11;\n}\n\n.order-12 {\n -ms-flex-order: 12;\n order: 12;\n}\n\n.offset-1 {\n margin-left: 8.333333%;\n}\n\n.offset-2 {\n margin-left: 16.666667%;\n}\n\n.offset-3 {\n margin-left: 25%;\n}\n\n.offset-4 {\n margin-left: 33.333333%;\n}\n\n.offset-5 {\n margin-left: 41.666667%;\n}\n\n.offset-6 {\n margin-left: 50%;\n}\n\n.offset-7 {\n margin-left: 58.333333%;\n}\n\n.offset-8 {\n margin-left: 66.666667%;\n}\n\n.offset-9 {\n margin-left: 75%;\n}\n\n.offset-10 {\n margin-left: 83.333333%;\n}\n\n.offset-11 {\n margin-left: 91.666667%;\n}\n\n@media (min-width: 576px) {\n .col-sm {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-sm-auto {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-sm-1 {\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-sm-2 {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-sm-3 {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-sm-4 {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-sm-5 {\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-sm-6 {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-sm-7 {\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-sm-8 {\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-sm-9 {\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-sm-10 {\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-sm-11 {\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-sm-12 {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-sm-first {\n -ms-flex-order: -1;\n order: -1;\n }\n .order-sm-last {\n -ms-flex-order: 13;\n order: 13;\n }\n .order-sm-0 {\n -ms-flex-order: 0;\n order: 0;\n }\n .order-sm-1 {\n -ms-flex-order: 1;\n order: 1;\n }\n .order-sm-2 {\n -ms-flex-order: 2;\n order: 2;\n }\n .order-sm-3 {\n -ms-flex-order: 3;\n order: 3;\n }\n .order-sm-4 {\n -ms-flex-order: 4;\n order: 4;\n }\n .order-sm-5 {\n -ms-flex-order: 5;\n order: 5;\n }\n .order-sm-6 {\n -ms-flex-order: 6;\n order: 6;\n }\n .order-sm-7 {\n -ms-flex-order: 7;\n order: 7;\n }\n .order-sm-8 {\n -ms-flex-order: 8;\n order: 8;\n }\n .order-sm-9 {\n -ms-flex-order: 9;\n order: 9;\n }\n .order-sm-10 {\n -ms-flex-order: 10;\n order: 10;\n }\n .order-sm-11 {\n -ms-flex-order: 11;\n order: 11;\n }\n .order-sm-12 {\n -ms-flex-order: 12;\n order: 12;\n }\n .offset-sm-0 {\n margin-left: 0;\n }\n .offset-sm-1 {\n margin-left: 8.333333%;\n }\n .offset-sm-2 {\n margin-left: 16.666667%;\n }\n .offset-sm-3 {\n margin-left: 25%;\n }\n .offset-sm-4 {\n margin-left: 33.333333%;\n }\n .offset-sm-5 {\n margin-left: 41.666667%;\n }\n .offset-sm-6 {\n margin-left: 50%;\n }\n .offset-sm-7 {\n margin-left: 58.333333%;\n }\n .offset-sm-8 {\n margin-left: 66.666667%;\n }\n .offset-sm-9 {\n margin-left: 75%;\n }\n .offset-sm-10 {\n margin-left: 83.333333%;\n }\n .offset-sm-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 768px) {\n .col-md {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-md-auto {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-md-1 {\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-md-2 {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-md-3 {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-md-4 {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-md-5 {\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-md-6 {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-md-7 {\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-md-8 {\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-md-9 {\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-md-10 {\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-md-11 {\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-md-12 {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-md-first {\n -ms-flex-order: -1;\n order: -1;\n }\n .order-md-last {\n -ms-flex-order: 13;\n order: 13;\n }\n .order-md-0 {\n -ms-flex-order: 0;\n order: 0;\n }\n .order-md-1 {\n -ms-flex-order: 1;\n order: 1;\n }\n .order-md-2 {\n -ms-flex-order: 2;\n order: 2;\n }\n .order-md-3 {\n -ms-flex-order: 3;\n order: 3;\n }\n .order-md-4 {\n -ms-flex-order: 4;\n order: 4;\n }\n .order-md-5 {\n -ms-flex-order: 5;\n order: 5;\n }\n .order-md-6 {\n -ms-flex-order: 6;\n order: 6;\n }\n .order-md-7 {\n -ms-flex-order: 7;\n order: 7;\n }\n .order-md-8 {\n -ms-flex-order: 8;\n order: 8;\n }\n .order-md-9 {\n -ms-flex-order: 9;\n order: 9;\n }\n .order-md-10 {\n -ms-flex-order: 10;\n order: 10;\n }\n .order-md-11 {\n -ms-flex-order: 11;\n order: 11;\n }\n .order-md-12 {\n -ms-flex-order: 12;\n order: 12;\n }\n .offset-md-0 {\n margin-left: 0;\n }\n .offset-md-1 {\n margin-left: 8.333333%;\n }\n .offset-md-2 {\n margin-left: 16.666667%;\n }\n .offset-md-3 {\n margin-left: 25%;\n }\n .offset-md-4 {\n margin-left: 33.333333%;\n }\n .offset-md-5 {\n margin-left: 41.666667%;\n }\n .offset-md-6 {\n margin-left: 50%;\n }\n .offset-md-7 {\n margin-left: 58.333333%;\n }\n .offset-md-8 {\n margin-left: 66.666667%;\n }\n .offset-md-9 {\n margin-left: 75%;\n }\n .offset-md-10 {\n margin-left: 83.333333%;\n }\n .offset-md-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 992px) {\n .col-lg {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-lg-auto {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-lg-1 {\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-lg-2 {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-lg-3 {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-lg-4 {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-lg-5 {\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-lg-6 {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-lg-7 {\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-lg-8 {\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-lg-9 {\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-lg-10 {\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-lg-11 {\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-lg-12 {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-lg-first {\n -ms-flex-order: -1;\n order: -1;\n }\n .order-lg-last {\n -ms-flex-order: 13;\n order: 13;\n }\n .order-lg-0 {\n -ms-flex-order: 0;\n order: 0;\n }\n .order-lg-1 {\n -ms-flex-order: 1;\n order: 1;\n }\n .order-lg-2 {\n -ms-flex-order: 2;\n order: 2;\n }\n .order-lg-3 {\n -ms-flex-order: 3;\n order: 3;\n }\n .order-lg-4 {\n -ms-flex-order: 4;\n order: 4;\n }\n .order-lg-5 {\n -ms-flex-order: 5;\n order: 5;\n }\n .order-lg-6 {\n -ms-flex-order: 6;\n order: 6;\n }\n .order-lg-7 {\n -ms-flex-order: 7;\n order: 7;\n }\n .order-lg-8 {\n -ms-flex-order: 8;\n order: 8;\n }\n .order-lg-9 {\n -ms-flex-order: 9;\n order: 9;\n }\n .order-lg-10 {\n -ms-flex-order: 10;\n order: 10;\n }\n .order-lg-11 {\n -ms-flex-order: 11;\n order: 11;\n }\n .order-lg-12 {\n -ms-flex-order: 12;\n order: 12;\n }\n .offset-lg-0 {\n margin-left: 0;\n }\n .offset-lg-1 {\n margin-left: 8.333333%;\n }\n .offset-lg-2 {\n margin-left: 16.666667%;\n }\n .offset-lg-3 {\n margin-left: 25%;\n }\n .offset-lg-4 {\n margin-left: 33.333333%;\n }\n .offset-lg-5 {\n margin-left: 41.666667%;\n }\n .offset-lg-6 {\n margin-left: 50%;\n }\n .offset-lg-7 {\n margin-left: 58.333333%;\n }\n .offset-lg-8 {\n margin-left: 66.666667%;\n }\n .offset-lg-9 {\n margin-left: 75%;\n }\n .offset-lg-10 {\n margin-left: 83.333333%;\n }\n .offset-lg-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 1200px) {\n .col-xl {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-xl-auto {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-xl-1 {\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-xl-2 {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-xl-3 {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-xl-4 {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-xl-5 {\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-xl-6 {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-xl-7 {\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-xl-8 {\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-xl-9 {\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-xl-10 {\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-xl-11 {\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-xl-12 {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-xl-first {\n -ms-flex-order: -1;\n order: -1;\n }\n .order-xl-last {\n -ms-flex-order: 13;\n order: 13;\n }\n .order-xl-0 {\n -ms-flex-order: 0;\n order: 0;\n }\n .order-xl-1 {\n -ms-flex-order: 1;\n order: 1;\n }\n .order-xl-2 {\n -ms-flex-order: 2;\n order: 2;\n }\n .order-xl-3 {\n -ms-flex-order: 3;\n order: 3;\n }\n .order-xl-4 {\n -ms-flex-order: 4;\n order: 4;\n }\n .order-xl-5 {\n -ms-flex-order: 5;\n order: 5;\n }\n .order-xl-6 {\n -ms-flex-order: 6;\n order: 6;\n }\n .order-xl-7 {\n -ms-flex-order: 7;\n order: 7;\n }\n .order-xl-8 {\n -ms-flex-order: 8;\n order: 8;\n }\n .order-xl-9 {\n -ms-flex-order: 9;\n order: 9;\n }\n .order-xl-10 {\n -ms-flex-order: 10;\n order: 10;\n }\n .order-xl-11 {\n -ms-flex-order: 11;\n order: 11;\n }\n .order-xl-12 {\n -ms-flex-order: 12;\n order: 12;\n }\n .offset-xl-0 {\n margin-left: 0;\n }\n .offset-xl-1 {\n margin-left: 8.333333%;\n }\n .offset-xl-2 {\n margin-left: 16.666667%;\n }\n .offset-xl-3 {\n margin-left: 25%;\n }\n .offset-xl-4 {\n margin-left: 33.333333%;\n }\n .offset-xl-5 {\n margin-left: 41.666667%;\n }\n .offset-xl-6 {\n margin-left: 50%;\n }\n .offset-xl-7 {\n margin-left: 58.333333%;\n }\n .offset-xl-8 {\n margin-left: 66.666667%;\n }\n .offset-xl-9 {\n margin-left: 75%;\n }\n .offset-xl-10 {\n margin-left: 83.333333%;\n }\n .offset-xl-11 {\n margin-left: 91.666667%;\n }\n}\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: 1rem;\n background-color: transparent;\n}\n\n.table th,\n.table td {\n padding: 0.75rem;\n vertical-align: top;\n border-top: 1px solid #dee2e6;\n}\n\n.table thead th {\n vertical-align: bottom;\n border-bottom: 2px solid #dee2e6;\n}\n\n.table tbody + tbody {\n border-top: 2px solid #dee2e6;\n}\n\n.table .table {\n background-color: #fff;\n}\n\n.table-sm th,\n.table-sm td {\n padding: 0.3rem;\n}\n\n.table-bordered {\n border: 1px solid #dee2e6;\n}\n\n.table-bordered th,\n.table-bordered td {\n border: 1px solid #dee2e6;\n}\n\n.table-bordered thead th,\n.table-bordered thead td {\n border-bottom-width: 2px;\n}\n\n.table-borderless th,\n.table-borderless td,\n.table-borderless thead th,\n.table-borderless tbody + tbody {\n border: 0;\n}\n\n.table-striped tbody tr:nth-of-type(odd) {\n background-color: rgba(0, 0, 0, 0.05);\n}\n\n.table-hover tbody tr:hover {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-primary,\n.table-primary > th,\n.table-primary > td {\n background-color: #b8daff;\n}\n\n.table-hover .table-primary:hover {\n background-color: #9fcdff;\n}\n\n.table-hover .table-primary:hover > td,\n.table-hover .table-primary:hover > th {\n background-color: #9fcdff;\n}\n\n.table-secondary,\n.table-secondary > th,\n.table-secondary > td {\n background-color: #d6d8db;\n}\n\n.table-hover .table-secondary:hover {\n background-color: #c8cbcf;\n}\n\n.table-hover .table-secondary:hover > td,\n.table-hover .table-secondary:hover > th {\n background-color: #c8cbcf;\n}\n\n.table-success,\n.table-success > th,\n.table-success > td {\n background-color: #c3e6cb;\n}\n\n.table-hover .table-success:hover {\n background-color: #b1dfbb;\n}\n\n.table-hover .table-success:hover > td,\n.table-hover .table-success:hover > th {\n background-color: #b1dfbb;\n}\n\n.table-info,\n.table-info > th,\n.table-info > td {\n background-color: #bee5eb;\n}\n\n.table-hover .table-info:hover {\n background-color: #abdde5;\n}\n\n.table-hover .table-info:hover > td,\n.table-hover .table-info:hover > th {\n background-color: #abdde5;\n}\n\n.table-warning,\n.table-warning > th,\n.table-warning > td {\n background-color: #ffeeba;\n}\n\n.table-hover .table-warning:hover {\n background-color: #ffe8a1;\n}\n\n.table-hover .table-warning:hover > td,\n.table-hover .table-warning:hover > th {\n background-color: #ffe8a1;\n}\n\n.table-danger,\n.table-danger > th,\n.table-danger > td {\n background-color: #f5c6cb;\n}\n\n.table-hover .table-danger:hover {\n background-color: #f1b0b7;\n}\n\n.table-hover .table-danger:hover > td,\n.table-hover .table-danger:hover > th {\n background-color: #f1b0b7;\n}\n\n.table-light,\n.table-light > th,\n.table-light > td {\n background-color: #fdfdfe;\n}\n\n.table-hover .table-light:hover {\n background-color: #ececf6;\n}\n\n.table-hover .table-light:hover > td,\n.table-hover .table-light:hover > th {\n background-color: #ececf6;\n}\n\n.table-dark,\n.table-dark > th,\n.table-dark > td {\n background-color: #c6c8ca;\n}\n\n.table-hover .table-dark:hover {\n background-color: #b9bbbe;\n}\n\n.table-hover .table-dark:hover > td,\n.table-hover .table-dark:hover > th {\n background-color: #b9bbbe;\n}\n\n.table-active,\n.table-active > th,\n.table-active > td {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover > td,\n.table-hover .table-active:hover > th {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table .thead-dark th {\n color: #fff;\n background-color: #212529;\n border-color: #32383e;\n}\n\n.table .thead-light th {\n color: #495057;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n\n.table-dark {\n color: #fff;\n background-color: #212529;\n}\n\n.table-dark th,\n.table-dark td,\n.table-dark thead th {\n border-color: #32383e;\n}\n\n.table-dark.table-bordered {\n border: 0;\n}\n\n.table-dark.table-striped tbody tr:nth-of-type(odd) {\n background-color: rgba(255, 255, 255, 0.05);\n}\n\n.table-dark.table-hover tbody tr:hover {\n background-color: rgba(255, 255, 255, 0.075);\n}\n\n@media (max-width: 575.98px) {\n .table-responsive-sm {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-sm > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 767.98px) {\n .table-responsive-md {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-md > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 991.98px) {\n .table-responsive-lg {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-lg > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 1199.98px) {\n .table-responsive-xl {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-xl > .table-bordered {\n border: 0;\n }\n}\n\n.table-responsive {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n}\n\n.table-responsive > .table-bordered {\n border: 0;\n}\n\n.form-control {\n display: block;\n width: 100%;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n line-height: 1.5;\n color: #495057;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .form-control {\n transition: none;\n }\n}\n\n.form-control::-ms-expand {\n background-color: transparent;\n border: 0;\n}\n\n.form-control:focus {\n color: #495057;\n background-color: #fff;\n border-color: #80bdff;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.form-control::-webkit-input-placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control::-moz-placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control:-ms-input-placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control::-ms-input-placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control::placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control:disabled, .form-control[readonly] {\n background-color: #e9ecef;\n opacity: 1;\n}\n\nselect.form-control:not([size]):not([multiple]) {\n height: calc(2.25rem + 2px);\n}\n\nselect.form-control:focus::-ms-value {\n color: #495057;\n background-color: #fff;\n}\n\n.form-control-file,\n.form-control-range {\n display: block;\n width: 100%;\n}\n\n.col-form-label {\n padding-top: calc(0.375rem + 1px);\n padding-bottom: calc(0.375rem + 1px);\n margin-bottom: 0;\n font-size: inherit;\n line-height: 1.5;\n}\n\n.col-form-label-lg {\n padding-top: calc(0.5rem + 1px);\n padding-bottom: calc(0.5rem + 1px);\n font-size: 1.25rem;\n line-height: 1.5;\n}\n\n.col-form-label-sm {\n padding-top: calc(0.25rem + 1px);\n padding-bottom: calc(0.25rem + 1px);\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.form-control-plaintext {\n display: block;\n width: 100%;\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n margin-bottom: 0;\n line-height: 1.5;\n color: #212529;\n background-color: transparent;\n border: solid transparent;\n border-width: 1px 0;\n}\n\n.form-control-plaintext.form-control-sm, .input-group-sm > .form-control-plaintext.form-control,\n.input-group-sm > .input-group-prepend > .form-control-plaintext.input-group-text,\n.input-group-sm > .input-group-append > .form-control-plaintext.input-group-text,\n.input-group-sm > .input-group-prepend > .form-control-plaintext.btn,\n.input-group-sm > .input-group-append > .form-control-plaintext.btn, .form-control-plaintext.form-control-lg, .input-group-lg > .form-control-plaintext.form-control,\n.input-group-lg > .input-group-prepend > .form-control-plaintext.input-group-text,\n.input-group-lg > .input-group-append > .form-control-plaintext.input-group-text,\n.input-group-lg > .input-group-prepend > .form-control-plaintext.btn,\n.input-group-lg > .input-group-append > .form-control-plaintext.btn {\n padding-right: 0;\n padding-left: 0;\n}\n\n.form-control-sm, .input-group-sm > .form-control,\n.input-group-sm > .input-group-prepend > .input-group-text,\n.input-group-sm > .input-group-append > .input-group-text,\n.input-group-sm > .input-group-prepend > .btn,\n.input-group-sm > .input-group-append > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\nselect.form-control-sm:not([size]):not([multiple]), .input-group-sm > select.form-control:not([size]):not([multiple]),\n.input-group-sm > .input-group-prepend > select.input-group-text:not([size]):not([multiple]),\n.input-group-sm > .input-group-append > select.input-group-text:not([size]):not([multiple]),\n.input-group-sm > .input-group-prepend > select.btn:not([size]):not([multiple]),\n.input-group-sm > .input-group-append > select.btn:not([size]):not([multiple]) {\n height: calc(1.8125rem + 2px);\n}\n\n.form-control-lg, .input-group-lg > .form-control,\n.input-group-lg > .input-group-prepend > .input-group-text,\n.input-group-lg > .input-group-append > .input-group-text,\n.input-group-lg > .input-group-prepend > .btn,\n.input-group-lg > .input-group-append > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\nselect.form-control-lg:not([size]):not([multiple]), .input-group-lg > select.form-control:not([size]):not([multiple]),\n.input-group-lg > .input-group-prepend > select.input-group-text:not([size]):not([multiple]),\n.input-group-lg > .input-group-append > select.input-group-text:not([size]):not([multiple]),\n.input-group-lg > .input-group-prepend > select.btn:not([size]):not([multiple]),\n.input-group-lg > .input-group-append > select.btn:not([size]):not([multiple]) {\n height: calc(2.875rem + 2px);\n}\n\n.form-group {\n margin-bottom: 1rem;\n}\n\n.form-text {\n display: block;\n margin-top: 0.25rem;\n}\n\n.form-row {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n margin-right: -5px;\n margin-left: -5px;\n}\n\n.form-row > .col,\n.form-row > [class*=\"col-\"] {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.form-check {\n position: relative;\n display: block;\n padding-left: 1.25rem;\n}\n\n.form-check-input {\n position: absolute;\n margin-top: 0.3rem;\n margin-left: -1.25rem;\n}\n\n.form-check-input:disabled ~ .form-check-label {\n color: #6c757d;\n}\n\n.form-check-label {\n margin-bottom: 0;\n}\n\n.form-check-inline {\n display: -ms-inline-flexbox;\n display: inline-flex;\n -ms-flex-align: center;\n align-items: center;\n padding-left: 0;\n margin-right: 0.75rem;\n}\n\n.form-check-inline .form-check-input {\n position: static;\n margin-top: 0;\n margin-right: 0.3125rem;\n margin-left: 0;\n}\n\n.valid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 80%;\n color: #28a745;\n}\n\n.valid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: .5rem;\n margin-top: .1rem;\n font-size: .875rem;\n line-height: 1;\n color: #fff;\n background-color: rgba(40, 167, 69, 0.8);\n border-radius: .2rem;\n}\n\n.was-validated .form-control:valid, .form-control.is-valid, .was-validated\n.custom-select:valid,\n.custom-select.is-valid {\n border-color: #28a745;\n}\n\n.was-validated .form-control:valid:focus, .form-control.is-valid:focus, .was-validated\n.custom-select:valid:focus,\n.custom-select.is-valid:focus {\n border-color: #28a745;\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .form-control:valid ~ .valid-feedback,\n.was-validated .form-control:valid ~ .valid-tooltip, .form-control.is-valid ~ .valid-feedback,\n.form-control.is-valid ~ .valid-tooltip, .was-validated\n.custom-select:valid ~ .valid-feedback,\n.was-validated\n.custom-select:valid ~ .valid-tooltip,\n.custom-select.is-valid ~ .valid-feedback,\n.custom-select.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .form-control-file:valid ~ .valid-feedback,\n.was-validated .form-control-file:valid ~ .valid-tooltip, .form-control-file.is-valid ~ .valid-feedback,\n.form-control-file.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {\n color: #28a745;\n}\n\n.was-validated .form-check-input:valid ~ .valid-feedback,\n.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback,\n.form-check-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label {\n color: #28a745;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before {\n background-color: #71dd8a;\n}\n\n.was-validated .custom-control-input:valid ~ .valid-feedback,\n.was-validated .custom-control-input:valid ~ .valid-tooltip, .custom-control-input.is-valid ~ .valid-feedback,\n.custom-control-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before {\n background-color: #34ce57;\n}\n\n.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label {\n border-color: #28a745;\n}\n\n.was-validated .custom-file-input:valid ~ .custom-file-label::before, .custom-file-input.is-valid ~ .custom-file-label::before {\n border-color: inherit;\n}\n\n.was-validated .custom-file-input:valid ~ .valid-feedback,\n.was-validated .custom-file-input:valid ~ .valid-tooltip, .custom-file-input.is-valid ~ .valid-feedback,\n.custom-file-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.invalid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 80%;\n color: #dc3545;\n}\n\n.invalid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: .5rem;\n margin-top: .1rem;\n font-size: .875rem;\n line-height: 1;\n color: #fff;\n background-color: rgba(220, 53, 69, 0.8);\n border-radius: .2rem;\n}\n\n.was-validated .form-control:invalid, .form-control.is-invalid, .was-validated\n.custom-select:invalid,\n.custom-select.is-invalid {\n border-color: #dc3545;\n}\n\n.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus, .was-validated\n.custom-select:invalid:focus,\n.custom-select.is-invalid:focus {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .form-control:invalid ~ .invalid-feedback,\n.was-validated .form-control:invalid ~ .invalid-tooltip, .form-control.is-invalid ~ .invalid-feedback,\n.form-control.is-invalid ~ .invalid-tooltip, .was-validated\n.custom-select:invalid ~ .invalid-feedback,\n.was-validated\n.custom-select:invalid ~ .invalid-tooltip,\n.custom-select.is-invalid ~ .invalid-feedback,\n.custom-select.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .form-control-file:invalid ~ .invalid-feedback,\n.was-validated .form-control-file:invalid ~ .invalid-tooltip, .form-control-file.is-invalid ~ .invalid-feedback,\n.form-control-file.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {\n color: #dc3545;\n}\n\n.was-validated .form-check-input:invalid ~ .invalid-feedback,\n.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback,\n.form-check-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label {\n color: #dc3545;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before {\n background-color: #efa2a9;\n}\n\n.was-validated .custom-control-input:invalid ~ .invalid-feedback,\n.was-validated .custom-control-input:invalid ~ .invalid-tooltip, .custom-control-input.is-invalid ~ .invalid-feedback,\n.custom-control-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before {\n background-color: #e4606d;\n}\n\n.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label {\n border-color: #dc3545;\n}\n\n.was-validated .custom-file-input:invalid ~ .custom-file-label::before, .custom-file-input.is-invalid ~ .custom-file-label::before {\n border-color: inherit;\n}\n\n.was-validated .custom-file-input:invalid ~ .invalid-feedback,\n.was-validated .custom-file-input:invalid ~ .invalid-tooltip, .custom-file-input.is-invalid ~ .invalid-feedback,\n.custom-file-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.form-inline {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-flow: row wrap;\n flex-flow: row wrap;\n -ms-flex-align: center;\n align-items: center;\n}\n\n.form-inline .form-check {\n width: 100%;\n}\n\n@media (min-width: 576px) {\n .form-inline label {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: center;\n justify-content: center;\n margin-bottom: 0;\n }\n .form-inline .form-group {\n display: -ms-flexbox;\n display: flex;\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n -ms-flex-flow: row wrap;\n flex-flow: row wrap;\n -ms-flex-align: center;\n align-items: center;\n margin-bottom: 0;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-plaintext {\n display: inline-block;\n }\n .form-inline .input-group,\n .form-inline .custom-select {\n width: auto;\n }\n .form-inline .form-check {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: center;\n justify-content: center;\n width: auto;\n padding-left: 0;\n }\n .form-inline .form-check-input {\n position: relative;\n margin-top: 0;\n margin-right: 0.25rem;\n margin-left: 0;\n }\n .form-inline .custom-control {\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: center;\n justify-content: center;\n }\n .form-inline .custom-control-label {\n margin-bottom: 0;\n }\n}\n\n.btn {\n display: inline-block;\n font-weight: 400;\n text-align: center;\n white-space: nowrap;\n vertical-align: middle;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n border: 1px solid transparent;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n line-height: 1.5;\n border-radius: 0.25rem;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .btn {\n transition: none;\n }\n}\n\n.btn:hover, .btn:focus {\n text-decoration: none;\n}\n\n.btn:focus, .btn.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.btn.disabled, .btn:disabled {\n opacity: 0.65;\n}\n\n.btn:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.btn:not(:disabled):not(.disabled):active, .btn:not(:disabled):not(.disabled).active {\n background-image: none;\n}\n\na.btn.disabled,\nfieldset:disabled a.btn {\n pointer-events: none;\n}\n\n.btn-primary {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-primary:hover {\n color: #fff;\n background-color: #0069d9;\n border-color: #0062cc;\n}\n\n.btn-primary:focus, .btn-primary.focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-primary.disabled, .btn-primary:disabled {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active,\n.show > .btn-primary.dropdown-toggle {\n color: #fff;\n background-color: #0062cc;\n border-color: #005cbf;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-secondary {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-secondary:hover {\n color: #fff;\n background-color: #5a6268;\n border-color: #545b62;\n}\n\n.btn-secondary:focus, .btn-secondary.focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-secondary.disabled, .btn-secondary:disabled {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-secondary.dropdown-toggle {\n color: #fff;\n background-color: #545b62;\n border-color: #4e555b;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-success {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-success:hover {\n color: #fff;\n background-color: #218838;\n border-color: #1e7e34;\n}\n\n.btn-success:focus, .btn-success.focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-success.disabled, .btn-success:disabled {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active,\n.show > .btn-success.dropdown-toggle {\n color: #fff;\n background-color: #1e7e34;\n border-color: #1c7430;\n}\n\n.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-info {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-info:hover {\n color: #fff;\n background-color: #138496;\n border-color: #117a8b;\n}\n\n.btn-info:focus, .btn-info.focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-info.disabled, .btn-info:disabled {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active,\n.show > .btn-info.dropdown-toggle {\n color: #fff;\n background-color: #117a8b;\n border-color: #10707f;\n}\n\n.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-warning {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-warning:hover {\n color: #212529;\n background-color: #e0a800;\n border-color: #d39e00;\n}\n\n.btn-warning:focus, .btn-warning.focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-warning.disabled, .btn-warning:disabled {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active,\n.show > .btn-warning.dropdown-toggle {\n color: #212529;\n background-color: #d39e00;\n border-color: #c69500;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-danger {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-danger:hover {\n color: #fff;\n background-color: #c82333;\n border-color: #bd2130;\n}\n\n.btn-danger:focus, .btn-danger.focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-danger.disabled, .btn-danger:disabled {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active,\n.show > .btn-danger.dropdown-toggle {\n color: #fff;\n background-color: #bd2130;\n border-color: #b21f2d;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-light {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-light:hover {\n color: #212529;\n background-color: #e2e6ea;\n border-color: #dae0e5;\n}\n\n.btn-light:focus, .btn-light.focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-light.disabled, .btn-light:disabled {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active,\n.show > .btn-light.dropdown-toggle {\n color: #212529;\n background-color: #dae0e5;\n border-color: #d3d9df;\n}\n\n.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-dark {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-dark:hover {\n color: #fff;\n background-color: #23272b;\n border-color: #1d2124;\n}\n\n.btn-dark:focus, .btn-dark.focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-dark.disabled, .btn-dark:disabled {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active,\n.show > .btn-dark.dropdown-toggle {\n color: #fff;\n background-color: #1d2124;\n border-color: #171a1d;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-primary {\n color: #007bff;\n background-color: transparent;\n background-image: none;\n border-color: #007bff;\n}\n\n.btn-outline-primary:hover {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:focus, .btn-outline-primary.focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-primary.disabled, .btn-outline-primary:disabled {\n color: #007bff;\n background-color: transparent;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-primary.dropdown-toggle {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-secondary {\n color: #6c757d;\n background-color: transparent;\n background-image: none;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:hover {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:focus, .btn-outline-secondary.focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-secondary.disabled, .btn-outline-secondary:disabled {\n color: #6c757d;\n background-color: transparent;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-secondary.dropdown-toggle {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-success {\n color: #28a745;\n background-color: transparent;\n background-image: none;\n border-color: #28a745;\n}\n\n.btn-outline-success:hover {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:focus, .btn-outline-success.focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-success.disabled, .btn-outline-success:disabled {\n color: #28a745;\n background-color: transparent;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active,\n.show > .btn-outline-success.dropdown-toggle {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-info {\n color: #17a2b8;\n background-color: transparent;\n background-image: none;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:hover {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:focus, .btn-outline-info.focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-info.disabled, .btn-outline-info:disabled {\n color: #17a2b8;\n background-color: transparent;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active,\n.show > .btn-outline-info.dropdown-toggle {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-warning {\n color: #ffc107;\n background-color: transparent;\n background-image: none;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:hover {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:focus, .btn-outline-warning.focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-warning.disabled, .btn-outline-warning:disabled {\n color: #ffc107;\n background-color: transparent;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active,\n.show > .btn-outline-warning.dropdown-toggle {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-danger {\n color: #dc3545;\n background-color: transparent;\n background-image: none;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:hover {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:focus, .btn-outline-danger.focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-danger.disabled, .btn-outline-danger:disabled {\n color: #dc3545;\n background-color: transparent;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active,\n.show > .btn-outline-danger.dropdown-toggle {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-light {\n color: #f8f9fa;\n background-color: transparent;\n background-image: none;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:hover {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:focus, .btn-outline-light.focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-light.disabled, .btn-outline-light:disabled {\n color: #f8f9fa;\n background-color: transparent;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active,\n.show > .btn-outline-light.dropdown-toggle {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-dark {\n color: #343a40;\n background-color: transparent;\n background-image: none;\n border-color: #343a40;\n}\n\n.btn-outline-dark:hover {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:focus, .btn-outline-dark.focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-dark.disabled, .btn-outline-dark:disabled {\n color: #343a40;\n background-color: transparent;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active,\n.show > .btn-outline-dark.dropdown-toggle {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-link {\n font-weight: 400;\n color: #007bff;\n background-color: transparent;\n}\n\n.btn-link:hover {\n color: #0056b3;\n text-decoration: underline;\n background-color: transparent;\n border-color: transparent;\n}\n\n.btn-link:focus, .btn-link.focus {\n text-decoration: underline;\n border-color: transparent;\n box-shadow: none;\n}\n\n.btn-link:disabled, .btn-link.disabled {\n color: #6c757d;\n pointer-events: none;\n}\n\n.btn-lg, .btn-group-lg > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\n.btn-sm, .btn-group-sm > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\n.btn-block {\n display: block;\n width: 100%;\n}\n\n.btn-block + .btn-block {\n margin-top: 0.5rem;\n}\n\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n\n.fade {\n transition: opacity 0.15s linear;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .fade {\n transition: none;\n }\n}\n\n.fade:not(.show) {\n opacity: 0;\n}\n\n.collapse:not(.show) {\n display: none;\n}\n\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n transition: height 0.35s ease;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .collapsing {\n transition: none;\n }\n}\n\n.dropup,\n.dropright,\n.dropdown,\n.dropleft {\n position: relative;\n}\n\n.dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid;\n border-right: 0.3em solid transparent;\n border-bottom: 0;\n border-left: 0.3em solid transparent;\n}\n\n.dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 10rem;\n padding: 0.5rem 0;\n margin: 0.125rem 0 0;\n font-size: 1rem;\n color: #212529;\n text-align: left;\n list-style: none;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 0.25rem;\n}\n\n.dropdown-menu-right {\n right: 0;\n left: auto;\n}\n\n.dropup .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-top: 0;\n margin-bottom: 0.125rem;\n}\n\n.dropup .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0;\n border-right: 0.3em solid transparent;\n border-bottom: 0.3em solid;\n border-left: 0.3em solid transparent;\n}\n\n.dropup .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropright .dropdown-menu {\n top: 0;\n right: auto;\n left: 100%;\n margin-top: 0;\n margin-left: 0.125rem;\n}\n\n.dropright .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0;\n border-bottom: 0.3em solid transparent;\n border-left: 0.3em solid;\n}\n\n.dropright .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropright .dropdown-toggle::after {\n vertical-align: 0;\n}\n\n.dropleft .dropdown-menu {\n top: 0;\n right: 100%;\n left: auto;\n margin-top: 0;\n margin-right: 0.125rem;\n}\n\n.dropleft .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n}\n\n.dropleft .dropdown-toggle::after {\n display: none;\n}\n\n.dropleft .dropdown-toggle::before {\n display: inline-block;\n width: 0;\n height: 0;\n margin-right: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0.3em solid;\n border-bottom: 0.3em solid transparent;\n}\n\n.dropleft .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropleft .dropdown-toggle::before {\n vertical-align: 0;\n}\n\n.dropdown-menu[x-placement^=\"top\"], .dropdown-menu[x-placement^=\"right\"], .dropdown-menu[x-placement^=\"bottom\"], .dropdown-menu[x-placement^=\"left\"] {\n right: auto;\n bottom: auto;\n}\n\n.dropdown-divider {\n height: 0;\n margin: 0.5rem 0;\n overflow: hidden;\n border-top: 1px solid #e9ecef;\n}\n\n.dropdown-item {\n display: block;\n width: 100%;\n padding: 0.25rem 1.5rem;\n clear: both;\n font-weight: 400;\n color: #212529;\n text-align: inherit;\n white-space: nowrap;\n background-color: transparent;\n border: 0;\n}\n\n.dropdown-item:hover, .dropdown-item:focus {\n color: #16181b;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n\n.dropdown-item.active, .dropdown-item:active {\n color: #fff;\n text-decoration: none;\n background-color: #007bff;\n}\n\n.dropdown-item.disabled, .dropdown-item:disabled {\n color: #6c757d;\n background-color: transparent;\n}\n\n.dropdown-menu.show {\n display: block;\n}\n\n.dropdown-header {\n display: block;\n padding: 0.5rem 1.5rem;\n margin-bottom: 0;\n font-size: 0.875rem;\n color: #6c757d;\n white-space: nowrap;\n}\n\n.dropdown-item-text {\n display: block;\n padding: 0.25rem 1.5rem;\n color: #212529;\n}\n\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: -ms-inline-flexbox;\n display: inline-flex;\n vertical-align: middle;\n}\n\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n -ms-flex: 0 1 auto;\n flex: 0 1 auto;\n}\n\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover {\n z-index: 1;\n}\n\n.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active,\n.btn-group-vertical > .btn:focus,\n.btn-group-vertical > .btn:active,\n.btn-group-vertical > .btn.active {\n z-index: 1;\n}\n\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group,\n.btn-group-vertical .btn + .btn,\n.btn-group-vertical .btn + .btn-group,\n.btn-group-vertical .btn-group + .btn,\n.btn-group-vertical .btn-group + .btn-group {\n margin-left: -1px;\n}\n\n.btn-toolbar {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n -ms-flex-pack: start;\n justify-content: flex-start;\n}\n\n.btn-toolbar .input-group {\n width: auto;\n}\n\n.btn-group > .btn:first-child {\n margin-left: 0;\n}\n\n.btn-group > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group > .btn-group:not(:last-child) > .btn {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.btn-group > .btn:not(:first-child),\n.btn-group > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.dropdown-toggle-split {\n padding-right: 0.5625rem;\n padding-left: 0.5625rem;\n}\n\n.dropdown-toggle-split::after,\n.dropup .dropdown-toggle-split::after,\n.dropright .dropdown-toggle-split::after {\n margin-left: 0;\n}\n\n.dropleft .dropdown-toggle-split::before {\n margin-right: 0;\n}\n\n.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {\n padding-right: 0.375rem;\n padding-left: 0.375rem;\n}\n\n.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {\n padding-right: 0.75rem;\n padding-left: 0.75rem;\n}\n\n.btn-group-vertical {\n -ms-flex-direction: column;\n flex-direction: column;\n -ms-flex-align: start;\n align-items: flex-start;\n -ms-flex-pack: center;\n justify-content: center;\n}\n\n.btn-group-vertical .btn,\n.btn-group-vertical .btn-group {\n width: 100%;\n}\n\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n}\n\n.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group-vertical > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.btn-group-toggle > .btn,\n.btn-group-toggle > .btn-group > .btn {\n margin-bottom: 0;\n}\n\n.btn-group-toggle > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn input[type=\"checkbox\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n\n.input-group {\n position: relative;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n -ms-flex-align: stretch;\n align-items: stretch;\n width: 100%;\n}\n\n.input-group > .form-control,\n.input-group > .custom-select,\n.input-group > .custom-file {\n position: relative;\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n width: 1%;\n margin-bottom: 0;\n}\n\n.input-group > .form-control:focus,\n.input-group > .custom-select:focus,\n.input-group > .custom-file:focus {\n z-index: 3;\n}\n\n.input-group > .form-control + .form-control,\n.input-group > .form-control + .custom-select,\n.input-group > .form-control + .custom-file,\n.input-group > .custom-select + .form-control,\n.input-group > .custom-select + .custom-select,\n.input-group > .custom-select + .custom-file,\n.input-group > .custom-file + .form-control,\n.input-group > .custom-file + .custom-select,\n.input-group > .custom-file + .custom-file {\n margin-left: -1px;\n}\n\n.input-group > .form-control:not(:last-child),\n.input-group > .custom-select:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .form-control:not(:first-child),\n.input-group > .custom-select:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.input-group > .custom-file {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n}\n\n.input-group > .custom-file:not(:last-child) .custom-file-label,\n.input-group > .custom-file:not(:last-child) .custom-file-label::after {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .custom-file:not(:first-child) .custom-file-label {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.input-group-prepend,\n.input-group-append {\n display: -ms-flexbox;\n display: flex;\n}\n\n.input-group-prepend .btn,\n.input-group-append .btn {\n position: relative;\n z-index: 2;\n}\n\n.input-group-prepend .btn + .btn,\n.input-group-prepend .btn + .input-group-text,\n.input-group-prepend .input-group-text + .input-group-text,\n.input-group-prepend .input-group-text + .btn,\n.input-group-append .btn + .btn,\n.input-group-append .btn + .input-group-text,\n.input-group-append .input-group-text + .input-group-text,\n.input-group-append .input-group-text + .btn {\n margin-left: -1px;\n}\n\n.input-group-prepend {\n margin-right: -1px;\n}\n\n.input-group-append {\n margin-left: -1px;\n}\n\n.input-group-text {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n padding: 0.375rem 0.75rem;\n margin-bottom: 0;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #495057;\n text-align: center;\n white-space: nowrap;\n background-color: #e9ecef;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.input-group-text input[type=\"radio\"],\n.input-group-text input[type=\"checkbox\"] {\n margin-top: 0;\n}\n\n.input-group > .input-group-prepend > .btn,\n.input-group > .input-group-prepend > .input-group-text,\n.input-group > .input-group-append:not(:last-child) > .btn,\n.input-group > .input-group-append:not(:last-child) > .input-group-text,\n.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .input-group-append > .btn,\n.input-group > .input-group-append > .input-group-text,\n.input-group > .input-group-prepend:not(:first-child) > .btn,\n.input-group > .input-group-prepend:not(:first-child) > .input-group-text,\n.input-group > .input-group-prepend:first-child > .btn:not(:first-child),\n.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.custom-control {\n position: relative;\n display: block;\n min-height: 1.5rem;\n padding-left: 1.5rem;\n}\n\n.custom-control-inline {\n display: -ms-inline-flexbox;\n display: inline-flex;\n margin-right: 1rem;\n}\n\n.custom-control-input {\n position: absolute;\n z-index: -1;\n opacity: 0;\n}\n\n.custom-control-input:checked ~ .custom-control-label::before {\n color: #fff;\n background-color: #007bff;\n}\n\n.custom-control-input:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-control-input:active ~ .custom-control-label::before {\n color: #fff;\n background-color: #b3d7ff;\n}\n\n.custom-control-input:disabled ~ .custom-control-label {\n color: #6c757d;\n}\n\n.custom-control-input:disabled ~ .custom-control-label::before {\n background-color: #e9ecef;\n}\n\n.custom-control-label {\n position: relative;\n margin-bottom: 0;\n}\n\n.custom-control-label::before {\n position: absolute;\n top: 0.25rem;\n left: -1.5rem;\n display: block;\n width: 1rem;\n height: 1rem;\n pointer-events: none;\n content: \"\";\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n background-color: #dee2e6;\n}\n\n.custom-control-label::after {\n position: absolute;\n top: 0.25rem;\n left: -1.5rem;\n display: block;\n width: 1rem;\n height: 1rem;\n content: \"\";\n background-repeat: no-repeat;\n background-position: center center;\n background-size: 50% 50%;\n}\n\n.custom-checkbox .custom-control-label::before {\n border-radius: 0.25rem;\n}\n\n.custom-checkbox .custom-control-input:checked ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E\");\n}\n\n.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-radio .custom-control-label::before {\n border-radius: 50%;\n}\n\n.custom-radio .custom-control-input:checked ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-radio .custom-control-input:checked ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E\");\n}\n\n.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-select {\n display: inline-block;\n width: 100%;\n height: calc(2.25rem + 2px);\n padding: 0.375rem 1.75rem 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n vertical-align: middle;\n background: #fff url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E\") no-repeat right 0.75rem center;\n background-size: 8px 10px;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n.custom-select:focus {\n border-color: #80bdff;\n outline: 0;\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075), 0 0 5px rgba(128, 189, 255, 0.5);\n}\n\n.custom-select:focus::-ms-value {\n color: #495057;\n background-color: #fff;\n}\n\n.custom-select[multiple], .custom-select[size]:not([size=\"1\"]) {\n height: auto;\n padding-right: 0.75rem;\n background-image: none;\n}\n\n.custom-select:disabled {\n color: #6c757d;\n background-color: #e9ecef;\n}\n\n.custom-select::-ms-expand {\n opacity: 0;\n}\n\n.custom-select-sm {\n height: calc(1.8125rem + 2px);\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n font-size: 75%;\n}\n\n.custom-select-lg {\n height: calc(2.875rem + 2px);\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n font-size: 125%;\n}\n\n.custom-file {\n position: relative;\n display: inline-block;\n width: 100%;\n height: calc(2.25rem + 2px);\n margin-bottom: 0;\n}\n\n.custom-file-input {\n position: relative;\n z-index: 2;\n width: 100%;\n height: calc(2.25rem + 2px);\n margin: 0;\n opacity: 0;\n}\n\n.custom-file-input:focus ~ .custom-file-label {\n border-color: #80bdff;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-file-input:focus ~ .custom-file-label::after {\n border-color: #80bdff;\n}\n\n.custom-file-input:lang(en) ~ .custom-file-label::after {\n content: \"Browse\";\n}\n\n.custom-file-label {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1;\n height: calc(2.25rem + 2px);\n padding: 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n background-color: #fff;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.custom-file-label::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n z-index: 3;\n display: block;\n height: 2.25rem;\n padding: 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n content: \"Browse\";\n background-color: #e9ecef;\n border-left: 1px solid #ced4da;\n border-radius: 0 0.25rem 0.25rem 0;\n}\n\n.custom-range {\n width: 100%;\n padding-left: 0;\n background-color: transparent;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n.custom-range:focus {\n outline: none;\n}\n\n.custom-range::-moz-focus-outer {\n border: 0;\n}\n\n.custom-range::-webkit-slider-thumb {\n width: 1rem;\n height: 1rem;\n margin-top: -0.25rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n -webkit-appearance: none;\n appearance: none;\n}\n\n.custom-range::-webkit-slider-thumb:focus {\n outline: none;\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-webkit-slider-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-webkit-slider-runnable-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: #dee2e6;\n border-color: transparent;\n border-radius: 1rem;\n}\n\n.custom-range::-moz-range-thumb {\n width: 1rem;\n height: 1rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n -moz-appearance: none;\n appearance: none;\n}\n\n.custom-range::-moz-range-thumb:focus {\n outline: none;\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-moz-range-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-moz-range-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: #dee2e6;\n border-color: transparent;\n border-radius: 1rem;\n}\n\n.custom-range::-ms-thumb {\n width: 1rem;\n height: 1rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n appearance: none;\n}\n\n.custom-range::-ms-thumb:focus {\n outline: none;\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-ms-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-ms-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: transparent;\n border-color: transparent;\n border-width: 0.5rem;\n}\n\n.custom-range::-ms-fill-lower {\n background-color: #dee2e6;\n border-radius: 1rem;\n}\n\n.custom-range::-ms-fill-upper {\n margin-right: 15px;\n background-color: #dee2e6;\n border-radius: 1rem;\n}\n\n.nav {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.nav-link {\n display: block;\n padding: 0.5rem 1rem;\n}\n\n.nav-link:hover, .nav-link:focus {\n text-decoration: none;\n}\n\n.nav-link.disabled {\n color: #6c757d;\n}\n\n.nav-tabs {\n border-bottom: 1px solid #dee2e6;\n}\n\n.nav-tabs .nav-item {\n margin-bottom: -1px;\n}\n\n.nav-tabs .nav-link {\n border: 1px solid transparent;\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {\n border-color: #e9ecef #e9ecef #dee2e6;\n}\n\n.nav-tabs .nav-link.disabled {\n color: #6c757d;\n background-color: transparent;\n border-color: transparent;\n}\n\n.nav-tabs .nav-link.active,\n.nav-tabs .nav-item.show .nav-link {\n color: #495057;\n background-color: #fff;\n border-color: #dee2e6 #dee2e6 #fff;\n}\n\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.nav-pills .nav-link {\n border-radius: 0.25rem;\n}\n\n.nav-pills .nav-link.active,\n.nav-pills .show > .nav-link {\n color: #fff;\n background-color: #007bff;\n}\n\n.nav-fill .nav-item {\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n text-align: center;\n}\n\n.nav-justified .nav-item {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n text-align: center;\n}\n\n.tab-content > .tab-pane {\n display: none;\n}\n\n.tab-content > .active {\n display: block;\n}\n\n.navbar {\n position: relative;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: justify;\n justify-content: space-between;\n padding: 0.5rem 1rem;\n}\n\n.navbar > .container,\n.navbar > .container-fluid {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: justify;\n justify-content: space-between;\n}\n\n.navbar-brand {\n display: inline-block;\n padding-top: 0.3125rem;\n padding-bottom: 0.3125rem;\n margin-right: 1rem;\n font-size: 1.25rem;\n line-height: inherit;\n white-space: nowrap;\n}\n\n.navbar-brand:hover, .navbar-brand:focus {\n text-decoration: none;\n}\n\n.navbar-nav {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.navbar-nav .nav-link {\n padding-right: 0;\n padding-left: 0;\n}\n\n.navbar-nav .dropdown-menu {\n position: static;\n float: none;\n}\n\n.navbar-text {\n display: inline-block;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n\n.navbar-collapse {\n -ms-flex-preferred-size: 100%;\n flex-basis: 100%;\n -ms-flex-positive: 1;\n flex-grow: 1;\n -ms-flex-align: center;\n align-items: center;\n}\n\n.navbar-toggler {\n padding: 0.25rem 0.75rem;\n font-size: 1.25rem;\n line-height: 1;\n background-color: transparent;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.navbar-toggler:hover, .navbar-toggler:focus {\n text-decoration: none;\n}\n\n.navbar-toggler:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.navbar-toggler-icon {\n display: inline-block;\n width: 1.5em;\n height: 1.5em;\n vertical-align: middle;\n content: \"\";\n background: no-repeat center center;\n background-size: 100% 100%;\n}\n\n@media (max-width: 575.98px) {\n .navbar-expand-sm > .container,\n .navbar-expand-sm > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 576px) {\n .navbar-expand-sm {\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n -ms-flex-pack: start;\n justify-content: flex-start;\n }\n .navbar-expand-sm .navbar-nav {\n -ms-flex-direction: row;\n flex-direction: row;\n }\n .navbar-expand-sm .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-sm .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-sm > .container,\n .navbar-expand-sm > .container-fluid {\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n }\n .navbar-expand-sm .navbar-collapse {\n display: -ms-flexbox !important;\n display: flex !important;\n -ms-flex-preferred-size: auto;\n flex-basis: auto;\n }\n .navbar-expand-sm .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 767.98px) {\n .navbar-expand-md > .container,\n .navbar-expand-md > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 768px) {\n .navbar-expand-md {\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n -ms-flex-pack: start;\n justify-content: flex-start;\n }\n .navbar-expand-md .navbar-nav {\n -ms-flex-direction: row;\n flex-direction: row;\n }\n .navbar-expand-md .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-md .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-md > .container,\n .navbar-expand-md > .container-fluid {\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n }\n .navbar-expand-md .navbar-collapse {\n display: -ms-flexbox !important;\n display: flex !important;\n -ms-flex-preferred-size: auto;\n flex-basis: auto;\n }\n .navbar-expand-md .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 991.98px) {\n .navbar-expand-lg > .container,\n .navbar-expand-lg > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 992px) {\n .navbar-expand-lg {\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n -ms-flex-pack: start;\n justify-content: flex-start;\n }\n .navbar-expand-lg .navbar-nav {\n -ms-flex-direction: row;\n flex-direction: row;\n }\n .navbar-expand-lg .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-lg .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-lg > .container,\n .navbar-expand-lg > .container-fluid {\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n }\n .navbar-expand-lg .navbar-collapse {\n display: -ms-flexbox !important;\n display: flex !important;\n -ms-flex-preferred-size: auto;\n flex-basis: auto;\n }\n .navbar-expand-lg .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 1199.98px) {\n .navbar-expand-xl > .container,\n .navbar-expand-xl > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 1200px) {\n .navbar-expand-xl {\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n -ms-flex-pack: start;\n justify-content: flex-start;\n }\n .navbar-expand-xl .navbar-nav {\n -ms-flex-direction: row;\n flex-direction: row;\n }\n .navbar-expand-xl .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-xl .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-xl > .container,\n .navbar-expand-xl > .container-fluid {\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n }\n .navbar-expand-xl .navbar-collapse {\n display: -ms-flexbox !important;\n display: flex !important;\n -ms-flex-preferred-size: auto;\n flex-basis: auto;\n }\n .navbar-expand-xl .navbar-toggler {\n display: none;\n }\n}\n\n.navbar-expand {\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n -ms-flex-pack: start;\n justify-content: flex-start;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n}\n\n.navbar-expand .navbar-nav {\n -ms-flex-direction: row;\n flex-direction: row;\n}\n\n.navbar-expand .navbar-nav .dropdown-menu {\n position: absolute;\n}\n\n.navbar-expand .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid {\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n}\n\n.navbar-expand .navbar-collapse {\n display: -ms-flexbox !important;\n display: flex !important;\n -ms-flex-preferred-size: auto;\n flex-basis: auto;\n}\n\n.navbar-expand .navbar-toggler {\n display: none;\n}\n\n.navbar-light .navbar-brand {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-nav .nav-link {\n color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus {\n color: rgba(0, 0, 0, 0.7);\n}\n\n.navbar-light .navbar-nav .nav-link.disabled {\n color: rgba(0, 0, 0, 0.3);\n}\n\n.navbar-light .navbar-nav .show > .nav-link,\n.navbar-light .navbar-nav .active > .nav-link,\n.navbar-light .navbar-nav .nav-link.show,\n.navbar-light .navbar-nav .nav-link.active {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-toggler {\n color: rgba(0, 0, 0, 0.5);\n border-color: rgba(0, 0, 0, 0.1);\n}\n\n.navbar-light .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\");\n}\n\n.navbar-light .navbar-text {\n color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-text a {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-dark .navbar-brand {\n color: #fff;\n}\n\n.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus {\n color: #fff;\n}\n\n.navbar-dark .navbar-nav .nav-link {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus {\n color: rgba(255, 255, 255, 0.75);\n}\n\n.navbar-dark .navbar-nav .nav-link.disabled {\n color: rgba(255, 255, 255, 0.25);\n}\n\n.navbar-dark .navbar-nav .show > .nav-link,\n.navbar-dark .navbar-nav .active > .nav-link,\n.navbar-dark .navbar-nav .nav-link.show,\n.navbar-dark .navbar-nav .nav-link.active {\n color: #fff;\n}\n\n.navbar-dark .navbar-toggler {\n color: rgba(255, 255, 255, 0.5);\n border-color: rgba(255, 255, 255, 0.1);\n}\n\n.navbar-dark .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\");\n}\n\n.navbar-dark .navbar-text {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.navbar-dark .navbar-text a {\n color: #fff;\n}\n\n.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus {\n color: #fff;\n}\n\n.card {\n position: relative;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n min-width: 0;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: border-box;\n border: 1px solid rgba(0, 0, 0, 0.125);\n border-radius: 0.25rem;\n}\n\n.card > hr {\n margin-right: 0;\n margin-left: 0;\n}\n\n.card > .list-group:first-child .list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.card > .list-group:last-child .list-group-item:last-child {\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.card-body {\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n padding: 1.25rem;\n}\n\n.card-title {\n margin-bottom: 0.75rem;\n}\n\n.card-subtitle {\n margin-top: -0.375rem;\n margin-bottom: 0;\n}\n\n.card-text:last-child {\n margin-bottom: 0;\n}\n\n.card-link:hover {\n text-decoration: none;\n}\n\n.card-link + .card-link {\n margin-left: 1.25rem;\n}\n\n.card-header {\n padding: 0.75rem 1.25rem;\n margin-bottom: 0;\n background-color: rgba(0, 0, 0, 0.03);\n border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card-header:first-child {\n border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0;\n}\n\n.card-header + .list-group .list-group-item:first-child {\n border-top: 0;\n}\n\n.card-footer {\n padding: 0.75rem 1.25rem;\n background-color: rgba(0, 0, 0, 0.03);\n border-top: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card-footer:last-child {\n border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px);\n}\n\n.card-header-tabs {\n margin-right: -0.625rem;\n margin-bottom: -0.75rem;\n margin-left: -0.625rem;\n border-bottom: 0;\n}\n\n.card-header-pills {\n margin-right: -0.625rem;\n margin-left: -0.625rem;\n}\n\n.card-img-overlay {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n padding: 1.25rem;\n}\n\n.card-img {\n width: 100%;\n border-radius: calc(0.25rem - 1px);\n}\n\n.card-img-top {\n width: 100%;\n border-top-left-radius: calc(0.25rem - 1px);\n border-top-right-radius: calc(0.25rem - 1px);\n}\n\n.card-img-bottom {\n width: 100%;\n border-bottom-right-radius: calc(0.25rem - 1px);\n border-bottom-left-radius: calc(0.25rem - 1px);\n}\n\n.card-deck {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n}\n\n.card-deck .card {\n margin-bottom: 15px;\n}\n\n@media (min-width: 576px) {\n .card-deck {\n -ms-flex-flow: row wrap;\n flex-flow: row wrap;\n margin-right: -15px;\n margin-left: -15px;\n }\n .card-deck .card {\n display: -ms-flexbox;\n display: flex;\n -ms-flex: 1 0 0%;\n flex: 1 0 0%;\n -ms-flex-direction: column;\n flex-direction: column;\n margin-right: 15px;\n margin-bottom: 0;\n margin-left: 15px;\n }\n}\n\n.card-group {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n}\n\n.card-group > .card {\n margin-bottom: 15px;\n}\n\n@media (min-width: 576px) {\n .card-group {\n -ms-flex-flow: row wrap;\n flex-flow: row wrap;\n }\n .card-group > .card {\n -ms-flex: 1 0 0%;\n flex: 1 0 0%;\n margin-bottom: 0;\n }\n .card-group > .card + .card {\n margin-left: 0;\n border-left: 0;\n }\n .card-group > .card:first-child {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n }\n .card-group > .card:first-child .card-img-top,\n .card-group > .card:first-child .card-header {\n border-top-right-radius: 0;\n }\n .card-group > .card:first-child .card-img-bottom,\n .card-group > .card:first-child .card-footer {\n border-bottom-right-radius: 0;\n }\n .card-group > .card:last-child {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n .card-group > .card:last-child .card-img-top,\n .card-group > .card:last-child .card-header {\n border-top-left-radius: 0;\n }\n .card-group > .card:last-child .card-img-bottom,\n .card-group > .card:last-child .card-footer {\n border-bottom-left-radius: 0;\n }\n .card-group > .card:only-child {\n border-radius: 0.25rem;\n }\n .card-group > .card:only-child .card-img-top,\n .card-group > .card:only-child .card-header {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n }\n .card-group > .card:only-child .card-img-bottom,\n .card-group > .card:only-child .card-footer {\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n }\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) {\n border-radius: 0;\n }\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-top,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-header,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-footer {\n border-radius: 0;\n }\n}\n\n.card-columns .card {\n margin-bottom: 0.75rem;\n}\n\n@media (min-width: 576px) {\n .card-columns {\n -webkit-column-count: 3;\n -moz-column-count: 3;\n column-count: 3;\n -webkit-column-gap: 1.25rem;\n -moz-column-gap: 1.25rem;\n column-gap: 1.25rem;\n orphans: 1;\n widows: 1;\n }\n .card-columns .card {\n display: inline-block;\n width: 100%;\n }\n}\n\n.accordion .card:not(:first-of-type):not(:last-of-type) {\n border-bottom: 0;\n border-radius: 0;\n}\n\n.accordion .card:not(:first-of-type) .card-header:first-child {\n border-radius: 0;\n}\n\n.accordion .card:first-of-type {\n border-bottom: 0;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.accordion .card:last-of-type {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.breadcrumb {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n padding: 0.75rem 1rem;\n margin-bottom: 1rem;\n list-style: none;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.breadcrumb-item + .breadcrumb-item {\n padding-left: 0.5rem;\n}\n\n.breadcrumb-item + .breadcrumb-item::before {\n display: inline-block;\n padding-right: 0.5rem;\n color: #6c757d;\n content: \"/\";\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n text-decoration: underline;\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n text-decoration: none;\n}\n\n.breadcrumb-item.active {\n color: #6c757d;\n}\n\n.pagination {\n display: -ms-flexbox;\n display: flex;\n padding-left: 0;\n list-style: none;\n border-radius: 0.25rem;\n}\n\n.page-link {\n position: relative;\n display: block;\n padding: 0.5rem 0.75rem;\n margin-left: -1px;\n line-height: 1.25;\n color: #007bff;\n background-color: #fff;\n border: 1px solid #dee2e6;\n}\n\n.page-link:hover {\n z-index: 2;\n color: #0056b3;\n text-decoration: none;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n\n.page-link:focus {\n z-index: 2;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.page-link:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.page-item:first-child .page-link {\n margin-left: 0;\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.page-item:last-child .page-link {\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n}\n\n.page-item.active .page-link {\n z-index: 1;\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.page-item.disabled .page-link {\n color: #6c757d;\n pointer-events: none;\n cursor: auto;\n background-color: #fff;\n border-color: #dee2e6;\n}\n\n.pagination-lg .page-link {\n padding: 0.75rem 1.5rem;\n font-size: 1.25rem;\n line-height: 1.5;\n}\n\n.pagination-lg .page-item:first-child .page-link {\n border-top-left-radius: 0.3rem;\n border-bottom-left-radius: 0.3rem;\n}\n\n.pagination-lg .page-item:last-child .page-link {\n border-top-right-radius: 0.3rem;\n border-bottom-right-radius: 0.3rem;\n}\n\n.pagination-sm .page-link {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.pagination-sm .page-item:first-child .page-link {\n border-top-left-radius: 0.2rem;\n border-bottom-left-radius: 0.2rem;\n}\n\n.pagination-sm .page-item:last-child .page-link {\n border-top-right-radius: 0.2rem;\n border-bottom-right-radius: 0.2rem;\n}\n\n.badge {\n display: inline-block;\n padding: 0.25em 0.4em;\n font-size: 75%;\n font-weight: 700;\n line-height: 1;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: 0.25rem;\n}\n\n.badge:empty {\n display: none;\n}\n\n.btn .badge {\n position: relative;\n top: -1px;\n}\n\n.badge-pill {\n padding-right: 0.6em;\n padding-left: 0.6em;\n border-radius: 10rem;\n}\n\n.badge-primary {\n color: #fff;\n background-color: #007bff;\n}\n\n.badge-primary[href]:hover, .badge-primary[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #0062cc;\n}\n\n.badge-secondary {\n color: #fff;\n background-color: #6c757d;\n}\n\n.badge-secondary[href]:hover, .badge-secondary[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #545b62;\n}\n\n.badge-success {\n color: #fff;\n background-color: #28a745;\n}\n\n.badge-success[href]:hover, .badge-success[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #1e7e34;\n}\n\n.badge-info {\n color: #fff;\n background-color: #17a2b8;\n}\n\n.badge-info[href]:hover, .badge-info[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #117a8b;\n}\n\n.badge-warning {\n color: #212529;\n background-color: #ffc107;\n}\n\n.badge-warning[href]:hover, .badge-warning[href]:focus {\n color: #212529;\n text-decoration: none;\n background-color: #d39e00;\n}\n\n.badge-danger {\n color: #fff;\n background-color: #dc3545;\n}\n\n.badge-danger[href]:hover, .badge-danger[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #bd2130;\n}\n\n.badge-light {\n color: #212529;\n background-color: #f8f9fa;\n}\n\n.badge-light[href]:hover, .badge-light[href]:focus {\n color: #212529;\n text-decoration: none;\n background-color: #dae0e5;\n}\n\n.badge-dark {\n color: #fff;\n background-color: #343a40;\n}\n\n.badge-dark[href]:hover, .badge-dark[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #1d2124;\n}\n\n.jumbotron {\n padding: 2rem 1rem;\n margin-bottom: 2rem;\n background-color: #e9ecef;\n border-radius: 0.3rem;\n}\n\n@media (min-width: 576px) {\n .jumbotron {\n padding: 4rem 2rem;\n }\n}\n\n.jumbotron-fluid {\n padding-right: 0;\n padding-left: 0;\n border-radius: 0;\n}\n\n.alert {\n position: relative;\n padding: 0.75rem 1.25rem;\n margin-bottom: 1rem;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.alert-heading {\n color: inherit;\n}\n\n.alert-link {\n font-weight: 700;\n}\n\n.alert-dismissible {\n padding-right: 4rem;\n}\n\n.alert-dismissible .close {\n position: absolute;\n top: 0;\n right: 0;\n padding: 0.75rem 1.25rem;\n color: inherit;\n}\n\n.alert-primary {\n color: #004085;\n background-color: #cce5ff;\n border-color: #b8daff;\n}\n\n.alert-primary hr {\n border-top-color: #9fcdff;\n}\n\n.alert-primary .alert-link {\n color: #002752;\n}\n\n.alert-secondary {\n color: #383d41;\n background-color: #e2e3e5;\n border-color: #d6d8db;\n}\n\n.alert-secondary hr {\n border-top-color: #c8cbcf;\n}\n\n.alert-secondary .alert-link {\n color: #202326;\n}\n\n.alert-success {\n color: #155724;\n background-color: #d4edda;\n border-color: #c3e6cb;\n}\n\n.alert-success hr {\n border-top-color: #b1dfbb;\n}\n\n.alert-success .alert-link {\n color: #0b2e13;\n}\n\n.alert-info {\n color: #0c5460;\n background-color: #d1ecf1;\n border-color: #bee5eb;\n}\n\n.alert-info hr {\n border-top-color: #abdde5;\n}\n\n.alert-info .alert-link {\n color: #062c33;\n}\n\n.alert-warning {\n color: #856404;\n background-color: #fff3cd;\n border-color: #ffeeba;\n}\n\n.alert-warning hr {\n border-top-color: #ffe8a1;\n}\n\n.alert-warning .alert-link {\n color: #533f03;\n}\n\n.alert-danger {\n color: #721c24;\n background-color: #f8d7da;\n border-color: #f5c6cb;\n}\n\n.alert-danger hr {\n border-top-color: #f1b0b7;\n}\n\n.alert-danger .alert-link {\n color: #491217;\n}\n\n.alert-light {\n color: #818182;\n background-color: #fefefe;\n border-color: #fdfdfe;\n}\n\n.alert-light hr {\n border-top-color: #ececf6;\n}\n\n.alert-light .alert-link {\n color: #686868;\n}\n\n.alert-dark {\n color: #1b1e21;\n background-color: #d6d8d9;\n border-color: #c6c8ca;\n}\n\n.alert-dark hr {\n border-top-color: #b9bbbe;\n}\n\n.alert-dark .alert-link {\n color: #040505;\n}\n\n@-webkit-keyframes progress-bar-stripes {\n from {\n background-position: 1rem 0;\n }\n to {\n background-position: 0 0;\n }\n}\n\n@keyframes progress-bar-stripes {\n from {\n background-position: 1rem 0;\n }\n to {\n background-position: 0 0;\n }\n}\n\n.progress {\n display: -ms-flexbox;\n display: flex;\n height: 1rem;\n overflow: hidden;\n font-size: 0.75rem;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.progress-bar {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n -ms-flex-pack: center;\n justify-content: center;\n color: #fff;\n text-align: center;\n white-space: nowrap;\n background-color: #007bff;\n transition: width 0.6s ease;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .progress-bar {\n transition: none;\n }\n}\n\n.progress-bar-striped {\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-size: 1rem 1rem;\n}\n\n.progress-bar-animated {\n -webkit-animation: progress-bar-stripes 1s linear infinite;\n animation: progress-bar-stripes 1s linear infinite;\n}\n\n.media {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: start;\n align-items: flex-start;\n}\n\n.media-body {\n -ms-flex: 1;\n flex: 1;\n}\n\n.list-group {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n}\n\n.list-group-item-action {\n width: 100%;\n color: #495057;\n text-align: inherit;\n}\n\n.list-group-item-action:hover, .list-group-item-action:focus {\n color: #495057;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n\n.list-group-item-action:active {\n color: #212529;\n background-color: #e9ecef;\n}\n\n.list-group-item {\n position: relative;\n display: block;\n padding: 0.75rem 1.25rem;\n margin-bottom: -1px;\n background-color: #fff;\n border: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.list-group-item:last-child {\n margin-bottom: 0;\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.list-group-item:hover, .list-group-item:focus {\n z-index: 1;\n text-decoration: none;\n}\n\n.list-group-item.disabled, .list-group-item:disabled {\n color: #6c757d;\n background-color: #fff;\n}\n\n.list-group-item.active {\n z-index: 2;\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.list-group-flush .list-group-item {\n border-right: 0;\n border-left: 0;\n border-radius: 0;\n}\n\n.list-group-flush:first-child .list-group-item:first-child {\n border-top: 0;\n}\n\n.list-group-flush:last-child .list-group-item:last-child {\n border-bottom: 0;\n}\n\n.list-group-item-primary {\n color: #004085;\n background-color: #b8daff;\n}\n\n.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus {\n color: #004085;\n background-color: #9fcdff;\n}\n\n.list-group-item-primary.list-group-item-action.active {\n color: #fff;\n background-color: #004085;\n border-color: #004085;\n}\n\n.list-group-item-secondary {\n color: #383d41;\n background-color: #d6d8db;\n}\n\n.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus {\n color: #383d41;\n background-color: #c8cbcf;\n}\n\n.list-group-item-secondary.list-group-item-action.active {\n color: #fff;\n background-color: #383d41;\n border-color: #383d41;\n}\n\n.list-group-item-success {\n color: #155724;\n background-color: #c3e6cb;\n}\n\n.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus {\n color: #155724;\n background-color: #b1dfbb;\n}\n\n.list-group-item-success.list-group-item-action.active {\n color: #fff;\n background-color: #155724;\n border-color: #155724;\n}\n\n.list-group-item-info {\n color: #0c5460;\n background-color: #bee5eb;\n}\n\n.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus {\n color: #0c5460;\n background-color: #abdde5;\n}\n\n.list-group-item-info.list-group-item-action.active {\n color: #fff;\n background-color: #0c5460;\n border-color: #0c5460;\n}\n\n.list-group-item-warning {\n color: #856404;\n background-color: #ffeeba;\n}\n\n.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus {\n color: #856404;\n background-color: #ffe8a1;\n}\n\n.list-group-item-warning.list-group-item-action.active {\n color: #fff;\n background-color: #856404;\n border-color: #856404;\n}\n\n.list-group-item-danger {\n color: #721c24;\n background-color: #f5c6cb;\n}\n\n.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus {\n color: #721c24;\n background-color: #f1b0b7;\n}\n\n.list-group-item-danger.list-group-item-action.active {\n color: #fff;\n background-color: #721c24;\n border-color: #721c24;\n}\n\n.list-group-item-light {\n color: #818182;\n background-color: #fdfdfe;\n}\n\n.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus {\n color: #818182;\n background-color: #ececf6;\n}\n\n.list-group-item-light.list-group-item-action.active {\n color: #fff;\n background-color: #818182;\n border-color: #818182;\n}\n\n.list-group-item-dark {\n color: #1b1e21;\n background-color: #c6c8ca;\n}\n\n.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus {\n color: #1b1e21;\n background-color: #b9bbbe;\n}\n\n.list-group-item-dark.list-group-item-action.active {\n color: #fff;\n background-color: #1b1e21;\n border-color: #1b1e21;\n}\n\n.close {\n float: right;\n font-size: 1.5rem;\n font-weight: 700;\n line-height: 1;\n color: #000;\n text-shadow: 0 1px 0 #fff;\n opacity: .5;\n}\n\n.close:hover, .close:focus {\n color: #000;\n text-decoration: none;\n opacity: .75;\n}\n\n.close:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\nbutton.close {\n padding: 0;\n background-color: transparent;\n border: 0;\n -webkit-appearance: none;\n}\n\n.modal-open {\n overflow: hidden;\n}\n\n.modal {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1050;\n display: none;\n overflow: hidden;\n outline: 0;\n}\n\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 0.5rem;\n pointer-events: none;\n}\n\n.modal.fade .modal-dialog {\n transition: -webkit-transform 0.3s ease-out;\n transition: transform 0.3s ease-out;\n transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out;\n -webkit-transform: translate(0, -25%);\n transform: translate(0, -25%);\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .modal.fade .modal-dialog {\n transition: none;\n }\n}\n\n.modal.show .modal-dialog {\n -webkit-transform: translate(0, 0);\n transform: translate(0, 0);\n}\n\n.modal-dialog-centered {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n min-height: calc(100% - (0.5rem * 2));\n}\n\n.modal-content {\n position: relative;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n width: 100%;\n pointer-events: auto;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n outline: 0;\n}\n\n.modal-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1040;\n background-color: #000;\n}\n\n.modal-backdrop.fade {\n opacity: 0;\n}\n\n.modal-backdrop.show {\n opacity: 0.5;\n}\n\n.modal-header {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: start;\n align-items: flex-start;\n -ms-flex-pack: justify;\n justify-content: space-between;\n padding: 1rem;\n border-bottom: 1px solid #e9ecef;\n border-top-left-radius: 0.3rem;\n border-top-right-radius: 0.3rem;\n}\n\n.modal-header .close {\n padding: 1rem;\n margin: -1rem -1rem -1rem auto;\n}\n\n.modal-title {\n margin-bottom: 0;\n line-height: 1.5;\n}\n\n.modal-body {\n position: relative;\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n padding: 1rem;\n}\n\n.modal-footer {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: end;\n justify-content: flex-end;\n padding: 1rem;\n border-top: 1px solid #e9ecef;\n}\n\n.modal-footer > :not(:first-child) {\n margin-left: .25rem;\n}\n\n.modal-footer > :not(:last-child) {\n margin-right: .25rem;\n}\n\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n\n@media (min-width: 576px) {\n .modal-dialog {\n max-width: 500px;\n margin: 1.75rem auto;\n }\n .modal-dialog-centered {\n min-height: calc(100% - (1.75rem * 2));\n }\n .modal-sm {\n max-width: 300px;\n }\n}\n\n@media (min-width: 992px) {\n .modal-lg {\n max-width: 800px;\n }\n}\n\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n opacity: 0;\n}\n\n.tooltip.show {\n opacity: 0.9;\n}\n\n.tooltip .arrow {\n position: absolute;\n display: block;\n width: 0.8rem;\n height: 0.4rem;\n}\n\n.tooltip .arrow::before {\n position: absolute;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-tooltip-top, .bs-tooltip-auto[x-placement^=\"top\"] {\n padding: 0.4rem 0;\n}\n\n.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^=\"top\"] .arrow {\n bottom: 0;\n}\n\n.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^=\"top\"] .arrow::before {\n top: 0;\n border-width: 0.4rem 0.4rem 0;\n border-top-color: #000;\n}\n\n.bs-tooltip-right, .bs-tooltip-auto[x-placement^=\"right\"] {\n padding: 0 0.4rem;\n}\n\n.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^=\"right\"] .arrow {\n left: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n\n.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^=\"right\"] .arrow::before {\n right: 0;\n border-width: 0.4rem 0.4rem 0.4rem 0;\n border-right-color: #000;\n}\n\n.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^=\"bottom\"] {\n padding: 0.4rem 0;\n}\n\n.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow {\n top: 0;\n}\n\n.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow::before {\n bottom: 0;\n border-width: 0 0.4rem 0.4rem;\n border-bottom-color: #000;\n}\n\n.bs-tooltip-left, .bs-tooltip-auto[x-placement^=\"left\"] {\n padding: 0 0.4rem;\n}\n\n.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^=\"left\"] .arrow {\n right: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n\n.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^=\"left\"] .arrow::before {\n left: 0;\n border-width: 0.4rem 0 0.4rem 0.4rem;\n border-left-color: #000;\n}\n\n.tooltip-inner {\n max-width: 200px;\n padding: 0.25rem 0.5rem;\n color: #fff;\n text-align: center;\n background-color: #000;\n border-radius: 0.25rem;\n}\n\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: block;\n max-width: 276px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n}\n\n.popover .arrow {\n position: absolute;\n display: block;\n width: 1rem;\n height: 0.5rem;\n margin: 0 0.3rem;\n}\n\n.popover .arrow::before, .popover .arrow::after {\n position: absolute;\n display: block;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-popover-top, .bs-popover-auto[x-placement^=\"top\"] {\n margin-bottom: 0.5rem;\n}\n\n.bs-popover-top .arrow, .bs-popover-auto[x-placement^=\"top\"] .arrow {\n bottom: calc((0.5rem + 1px) * -1);\n}\n\n.bs-popover-top .arrow::before, .bs-popover-auto[x-placement^=\"top\"] .arrow::before,\n.bs-popover-top .arrow::after,\n.bs-popover-auto[x-placement^=\"top\"] .arrow::after {\n border-width: 0.5rem 0.5rem 0;\n}\n\n.bs-popover-top .arrow::before, .bs-popover-auto[x-placement^=\"top\"] .arrow::before {\n bottom: 0;\n border-top-color: rgba(0, 0, 0, 0.25);\n}\n\n\n.bs-popover-top .arrow::after,\n.bs-popover-auto[x-placement^=\"top\"] .arrow::after {\n bottom: 1px;\n border-top-color: #fff;\n}\n\n.bs-popover-right, .bs-popover-auto[x-placement^=\"right\"] {\n margin-left: 0.5rem;\n}\n\n.bs-popover-right .arrow, .bs-popover-auto[x-placement^=\"right\"] .arrow {\n left: calc((0.5rem + 1px) * -1);\n width: 0.5rem;\n height: 1rem;\n margin: 0.3rem 0;\n}\n\n.bs-popover-right .arrow::before, .bs-popover-auto[x-placement^=\"right\"] .arrow::before,\n.bs-popover-right .arrow::after,\n.bs-popover-auto[x-placement^=\"right\"] .arrow::after {\n border-width: 0.5rem 0.5rem 0.5rem 0;\n}\n\n.bs-popover-right .arrow::before, .bs-popover-auto[x-placement^=\"right\"] .arrow::before {\n left: 0;\n border-right-color: rgba(0, 0, 0, 0.25);\n}\n\n\n.bs-popover-right .arrow::after,\n.bs-popover-auto[x-placement^=\"right\"] .arrow::after {\n left: 1px;\n border-right-color: #fff;\n}\n\n.bs-popover-bottom, .bs-popover-auto[x-placement^=\"bottom\"] {\n margin-top: 0.5rem;\n}\n\n.bs-popover-bottom .arrow, .bs-popover-auto[x-placement^=\"bottom\"] .arrow {\n top: calc((0.5rem + 1px) * -1);\n}\n\n.bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::before,\n.bs-popover-bottom .arrow::after,\n.bs-popover-auto[x-placement^=\"bottom\"] .arrow::after {\n border-width: 0 0.5rem 0.5rem 0.5rem;\n}\n\n.bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::before {\n top: 0;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n}\n\n\n.bs-popover-bottom .arrow::after,\n.bs-popover-auto[x-placement^=\"bottom\"] .arrow::after {\n top: 1px;\n border-bottom-color: #fff;\n}\n\n.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^=\"bottom\"] .popover-header::before {\n position: absolute;\n top: 0;\n left: 50%;\n display: block;\n width: 1rem;\n margin-left: -0.5rem;\n content: \"\";\n border-bottom: 1px solid #f7f7f7;\n}\n\n.bs-popover-left, .bs-popover-auto[x-placement^=\"left\"] {\n margin-right: 0.5rem;\n}\n\n.bs-popover-left .arrow, .bs-popover-auto[x-placement^=\"left\"] .arrow {\n right: calc((0.5rem + 1px) * -1);\n width: 0.5rem;\n height: 1rem;\n margin: 0.3rem 0;\n}\n\n.bs-popover-left .arrow::before, .bs-popover-auto[x-placement^=\"left\"] .arrow::before,\n.bs-popover-left .arrow::after,\n.bs-popover-auto[x-placement^=\"left\"] .arrow::after {\n border-width: 0.5rem 0 0.5rem 0.5rem;\n}\n\n.bs-popover-left .arrow::before, .bs-popover-auto[x-placement^=\"left\"] .arrow::before {\n right: 0;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n\n\n.bs-popover-left .arrow::after,\n.bs-popover-auto[x-placement^=\"left\"] .arrow::after {\n right: 1px;\n border-left-color: #fff;\n}\n\n.popover-header {\n padding: 0.5rem 0.75rem;\n margin-bottom: 0;\n font-size: 1rem;\n color: inherit;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-top-left-radius: calc(0.3rem - 1px);\n border-top-right-radius: calc(0.3rem - 1px);\n}\n\n.popover-header:empty {\n display: none;\n}\n\n.popover-body {\n padding: 0.5rem 0.75rem;\n color: #212529;\n}\n\n.carousel {\n position: relative;\n}\n\n.carousel-inner {\n position: relative;\n width: 100%;\n overflow: hidden;\n}\n\n.carousel-item {\n position: relative;\n display: none;\n -ms-flex-align: center;\n align-items: center;\n width: 100%;\n transition: -webkit-transform 0.6s ease;\n transition: transform 0.6s ease;\n transition: transform 0.6s ease, -webkit-transform 0.6s ease;\n -webkit-backface-visibility: hidden;\n backface-visibility: hidden;\n -webkit-perspective: 1000px;\n perspective: 1000px;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .carousel-item {\n transition: none;\n }\n}\n\n.carousel-item.active,\n.carousel-item-next,\n.carousel-item-prev {\n display: block;\n}\n\n.carousel-item-next,\n.carousel-item-prev {\n position: absolute;\n top: 0;\n}\n\n.carousel-item-next.carousel-item-left,\n.carousel-item-prev.carousel-item-right {\n -webkit-transform: translateX(0);\n transform: translateX(0);\n}\n\n@supports ((-webkit-transform-style: preserve-3d) or (transform-style: preserve-3d)) {\n .carousel-item-next.carousel-item-left,\n .carousel-item-prev.carousel-item-right {\n -webkit-transform: translate3d(0, 0, 0);\n transform: translate3d(0, 0, 0);\n }\n}\n\n.carousel-item-next,\n.active.carousel-item-right {\n -webkit-transform: translateX(100%);\n transform: translateX(100%);\n}\n\n@supports ((-webkit-transform-style: preserve-3d) or (transform-style: preserve-3d)) {\n .carousel-item-next,\n .active.carousel-item-right {\n -webkit-transform: translate3d(100%, 0, 0);\n transform: translate3d(100%, 0, 0);\n }\n}\n\n.carousel-item-prev,\n.active.carousel-item-left {\n -webkit-transform: translateX(-100%);\n transform: translateX(-100%);\n}\n\n@supports ((-webkit-transform-style: preserve-3d) or (transform-style: preserve-3d)) {\n .carousel-item-prev,\n .active.carousel-item-left {\n -webkit-transform: translate3d(-100%, 0, 0);\n transform: translate3d(-100%, 0, 0);\n }\n}\n\n.carousel-fade .carousel-item {\n opacity: 0;\n transition-duration: .6s;\n transition-property: opacity;\n}\n\n.carousel-fade .carousel-item.active,\n.carousel-fade .carousel-item-next.carousel-item-left,\n.carousel-fade .carousel-item-prev.carousel-item-right {\n opacity: 1;\n}\n\n.carousel-fade .active.carousel-item-left,\n.carousel-fade .active.carousel-item-right {\n opacity: 0;\n}\n\n.carousel-fade .carousel-item-next,\n.carousel-fade .carousel-item-prev,\n.carousel-fade .carousel-item.active,\n.carousel-fade .active.carousel-item-left,\n.carousel-fade .active.carousel-item-prev {\n -webkit-transform: translateX(0);\n transform: translateX(0);\n}\n\n@supports ((-webkit-transform-style: preserve-3d) or (transform-style: preserve-3d)) {\n .carousel-fade .carousel-item-next,\n .carousel-fade .carousel-item-prev,\n .carousel-fade .carousel-item.active,\n .carousel-fade .active.carousel-item-left,\n .carousel-fade .active.carousel-item-prev {\n -webkit-transform: translate3d(0, 0, 0);\n transform: translate3d(0, 0, 0);\n }\n}\n\n.carousel-control-prev,\n.carousel-control-next {\n position: absolute;\n top: 0;\n bottom: 0;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: center;\n justify-content: center;\n width: 15%;\n color: #fff;\n text-align: center;\n opacity: 0.5;\n}\n\n.carousel-control-prev:hover, .carousel-control-prev:focus,\n.carousel-control-next:hover,\n.carousel-control-next:focus {\n color: #fff;\n text-decoration: none;\n outline: 0;\n opacity: .9;\n}\n\n.carousel-control-prev {\n left: 0;\n}\n\n.carousel-control-next {\n right: 0;\n}\n\n.carousel-control-prev-icon,\n.carousel-control-next-icon {\n display: inline-block;\n width: 20px;\n height: 20px;\n background: transparent no-repeat center center;\n background-size: 100% 100%;\n}\n\n.carousel-control-prev-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E\");\n}\n\n.carousel-control-next-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E\");\n}\n\n.carousel-indicators {\n position: absolute;\n right: 0;\n bottom: 10px;\n left: 0;\n z-index: 15;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-pack: center;\n justify-content: center;\n padding-left: 0;\n margin-right: 15%;\n margin-left: 15%;\n list-style: none;\n}\n\n.carousel-indicators li {\n position: relative;\n -ms-flex: 0 1 auto;\n flex: 0 1 auto;\n width: 30px;\n height: 3px;\n margin-right: 3px;\n margin-left: 3px;\n text-indent: -999px;\n cursor: pointer;\n background-color: rgba(255, 255, 255, 0.5);\n}\n\n.carousel-indicators li::before {\n position: absolute;\n top: -10px;\n left: 0;\n display: inline-block;\n width: 100%;\n height: 10px;\n content: \"\";\n}\n\n.carousel-indicators li::after {\n position: absolute;\n bottom: -10px;\n left: 0;\n display: inline-block;\n width: 100%;\n height: 10px;\n content: \"\";\n}\n\n.carousel-indicators .active {\n background-color: #fff;\n}\n\n.carousel-caption {\n position: absolute;\n right: 15%;\n bottom: 20px;\n left: 15%;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #fff;\n text-align: center;\n}\n\n.align-baseline {\n vertical-align: baseline !important;\n}\n\n.align-top {\n vertical-align: top !important;\n}\n\n.align-middle {\n vertical-align: middle !important;\n}\n\n.align-bottom {\n vertical-align: bottom !important;\n}\n\n.align-text-bottom {\n vertical-align: text-bottom !important;\n}\n\n.align-text-top {\n vertical-align: text-top !important;\n}\n\n.bg-primary {\n background-color: #007bff !important;\n}\n\na.bg-primary:hover, a.bg-primary:focus,\nbutton.bg-primary:hover,\nbutton.bg-primary:focus {\n background-color: #0062cc !important;\n}\n\n.bg-secondary {\n background-color: #6c757d !important;\n}\n\na.bg-secondary:hover, a.bg-secondary:focus,\nbutton.bg-secondary:hover,\nbutton.bg-secondary:focus {\n background-color: #545b62 !important;\n}\n\n.bg-success {\n background-color: #28a745 !important;\n}\n\na.bg-success:hover, a.bg-success:focus,\nbutton.bg-success:hover,\nbutton.bg-success:focus {\n background-color: #1e7e34 !important;\n}\n\n.bg-info {\n background-color: #17a2b8 !important;\n}\n\na.bg-info:hover, a.bg-info:focus,\nbutton.bg-info:hover,\nbutton.bg-info:focus {\n background-color: #117a8b !important;\n}\n\n.bg-warning {\n background-color: #ffc107 !important;\n}\n\na.bg-warning:hover, a.bg-warning:focus,\nbutton.bg-warning:hover,\nbutton.bg-warning:focus {\n background-color: #d39e00 !important;\n}\n\n.bg-danger {\n background-color: #dc3545 !important;\n}\n\na.bg-danger:hover, a.bg-danger:focus,\nbutton.bg-danger:hover,\nbutton.bg-danger:focus {\n background-color: #bd2130 !important;\n}\n\n.bg-light {\n background-color: #f8f9fa !important;\n}\n\na.bg-light:hover, a.bg-light:focus,\nbutton.bg-light:hover,\nbutton.bg-light:focus {\n background-color: #dae0e5 !important;\n}\n\n.bg-dark {\n background-color: #343a40 !important;\n}\n\na.bg-dark:hover, a.bg-dark:focus,\nbutton.bg-dark:hover,\nbutton.bg-dark:focus {\n background-color: #1d2124 !important;\n}\n\n.bg-white {\n background-color: #fff !important;\n}\n\n.bg-transparent {\n background-color: transparent !important;\n}\n\n.border {\n border: 1px solid #dee2e6 !important;\n}\n\n.border-top {\n border-top: 1px solid #dee2e6 !important;\n}\n\n.border-right {\n border-right: 1px solid #dee2e6 !important;\n}\n\n.border-bottom {\n border-bottom: 1px solid #dee2e6 !important;\n}\n\n.border-left {\n border-left: 1px solid #dee2e6 !important;\n}\n\n.border-0 {\n border: 0 !important;\n}\n\n.border-top-0 {\n border-top: 0 !important;\n}\n\n.border-right-0 {\n border-right: 0 !important;\n}\n\n.border-bottom-0 {\n border-bottom: 0 !important;\n}\n\n.border-left-0 {\n border-left: 0 !important;\n}\n\n.border-primary {\n border-color: #007bff !important;\n}\n\n.border-secondary {\n border-color: #6c757d !important;\n}\n\n.border-success {\n border-color: #28a745 !important;\n}\n\n.border-info {\n border-color: #17a2b8 !important;\n}\n\n.border-warning {\n border-color: #ffc107 !important;\n}\n\n.border-danger {\n border-color: #dc3545 !important;\n}\n\n.border-light {\n border-color: #f8f9fa !important;\n}\n\n.border-dark {\n border-color: #343a40 !important;\n}\n\n.border-white {\n border-color: #fff !important;\n}\n\n.rounded {\n border-radius: 0.25rem !important;\n}\n\n.rounded-top {\n border-top-left-radius: 0.25rem !important;\n border-top-right-radius: 0.25rem !important;\n}\n\n.rounded-right {\n border-top-right-radius: 0.25rem !important;\n border-bottom-right-radius: 0.25rem !important;\n}\n\n.rounded-bottom {\n border-bottom-right-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-left {\n border-top-left-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-circle {\n border-radius: 50% !important;\n}\n\n.rounded-0 {\n border-radius: 0 !important;\n}\n\n.clearfix::after {\n display: block;\n clear: both;\n content: \"\";\n}\n\n.d-none {\n display: none !important;\n}\n\n.d-inline {\n display: inline !important;\n}\n\n.d-inline-block {\n display: inline-block !important;\n}\n\n.d-block {\n display: block !important;\n}\n\n.d-table {\n display: table !important;\n}\n\n.d-table-row {\n display: table-row !important;\n}\n\n.d-table-cell {\n display: table-cell !important;\n}\n\n.d-flex {\n display: -ms-flexbox !important;\n display: flex !important;\n}\n\n.d-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n}\n\n@media (min-width: 576px) {\n .d-sm-none {\n display: none !important;\n }\n .d-sm-inline {\n display: inline !important;\n }\n .d-sm-inline-block {\n display: inline-block !important;\n }\n .d-sm-block {\n display: block !important;\n }\n .d-sm-table {\n display: table !important;\n }\n .d-sm-table-row {\n display: table-row !important;\n }\n .d-sm-table-cell {\n display: table-cell !important;\n }\n .d-sm-flex {\n display: -ms-flexbox !important;\n display: flex !important;\n }\n .d-sm-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 768px) {\n .d-md-none {\n display: none !important;\n }\n .d-md-inline {\n display: inline !important;\n }\n .d-md-inline-block {\n display: inline-block !important;\n }\n .d-md-block {\n display: block !important;\n }\n .d-md-table {\n display: table !important;\n }\n .d-md-table-row {\n display: table-row !important;\n }\n .d-md-table-cell {\n display: table-cell !important;\n }\n .d-md-flex {\n display: -ms-flexbox !important;\n display: flex !important;\n }\n .d-md-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 992px) {\n .d-lg-none {\n display: none !important;\n }\n .d-lg-inline {\n display: inline !important;\n }\n .d-lg-inline-block {\n display: inline-block !important;\n }\n .d-lg-block {\n display: block !important;\n }\n .d-lg-table {\n display: table !important;\n }\n .d-lg-table-row {\n display: table-row !important;\n }\n .d-lg-table-cell {\n display: table-cell !important;\n }\n .d-lg-flex {\n display: -ms-flexbox !important;\n display: flex !important;\n }\n .d-lg-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 1200px) {\n .d-xl-none {\n display: none !important;\n }\n .d-xl-inline {\n display: inline !important;\n }\n .d-xl-inline-block {\n display: inline-block !important;\n }\n .d-xl-block {\n display: block !important;\n }\n .d-xl-table {\n display: table !important;\n }\n .d-xl-table-row {\n display: table-row !important;\n }\n .d-xl-table-cell {\n display: table-cell !important;\n }\n .d-xl-flex {\n display: -ms-flexbox !important;\n display: flex !important;\n }\n .d-xl-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n@media print {\n .d-print-none {\n display: none !important;\n }\n .d-print-inline {\n display: inline !important;\n }\n .d-print-inline-block {\n display: inline-block !important;\n }\n .d-print-block {\n display: block !important;\n }\n .d-print-table {\n display: table !important;\n }\n .d-print-table-row {\n display: table-row !important;\n }\n .d-print-table-cell {\n display: table-cell !important;\n }\n .d-print-flex {\n display: -ms-flexbox !important;\n display: flex !important;\n }\n .d-print-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n.embed-responsive {\n position: relative;\n display: block;\n width: 100%;\n padding: 0;\n overflow: hidden;\n}\n\n.embed-responsive::before {\n display: block;\n content: \"\";\n}\n\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n height: 100%;\n border: 0;\n}\n\n.embed-responsive-21by9::before {\n padding-top: 42.857143%;\n}\n\n.embed-responsive-16by9::before {\n padding-top: 56.25%;\n}\n\n.embed-responsive-4by3::before {\n padding-top: 75%;\n}\n\n.embed-responsive-1by1::before {\n padding-top: 100%;\n}\n\n.flex-row {\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n}\n\n.flex-column {\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n}\n\n.flex-row-reverse {\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n}\n\n.flex-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n}\n\n.flex-fill {\n -ms-flex: 1 1 auto !important;\n flex: 1 1 auto !important;\n}\n\n.flex-grow-0 {\n -ms-flex-positive: 0 !important;\n flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n -ms-flex-negative: 0 !important;\n flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n -ms-flex-negative: 1 !important;\n flex-shrink: 1 !important;\n}\n\n.justify-content-start {\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n}\n\n.justify-content-end {\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n}\n\n.justify-content-center {\n -ms-flex-pack: center !important;\n justify-content: center !important;\n}\n\n.justify-content-between {\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n}\n\n.justify-content-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n}\n\n.align-items-start {\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n}\n\n.align-items-end {\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n}\n\n.align-items-center {\n -ms-flex-align: center !important;\n align-items: center !important;\n}\n\n.align-items-baseline {\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n}\n\n.align-items-stretch {\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n}\n\n.align-content-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n}\n\n.align-content-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n}\n\n.align-content-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n}\n\n.align-content-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n}\n\n.align-content-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n}\n\n.align-content-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n}\n\n.align-self-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n}\n\n.align-self-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n}\n\n.align-self-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n}\n\n.align-self-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n}\n\n.align-self-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n}\n\n.align-self-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n}\n\n@media (min-width: 576px) {\n .flex-sm-row {\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n .flex-sm-column {\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n }\n .flex-sm-row-reverse {\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n }\n .flex-sm-column-reverse {\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n }\n .flex-sm-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n }\n .flex-sm-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n }\n .flex-sm-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n }\n .flex-sm-fill {\n -ms-flex: 1 1 auto !important;\n flex: 1 1 auto !important;\n }\n .flex-sm-grow-0 {\n -ms-flex-positive: 0 !important;\n flex-grow: 0 !important;\n }\n .flex-sm-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n }\n .flex-sm-shrink-0 {\n -ms-flex-negative: 0 !important;\n flex-shrink: 0 !important;\n }\n .flex-sm-shrink-1 {\n -ms-flex-negative: 1 !important;\n flex-shrink: 1 !important;\n }\n .justify-content-sm-start {\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n .justify-content-sm-end {\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n }\n .justify-content-sm-center {\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n .justify-content-sm-between {\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n }\n .justify-content-sm-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .align-items-sm-start {\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n }\n .align-items-sm-end {\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n }\n .align-items-sm-center {\n -ms-flex-align: center !important;\n align-items: center !important;\n }\n .align-items-sm-baseline {\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n }\n .align-items-sm-stretch {\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n }\n .align-content-sm-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n }\n .align-content-sm-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n }\n .align-content-sm-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n }\n .align-content-sm-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n }\n .align-content-sm-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n }\n .align-content-sm-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n }\n .align-self-sm-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n }\n .align-self-sm-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n }\n .align-self-sm-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n }\n .align-self-sm-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n }\n .align-self-sm-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n }\n .align-self-sm-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 768px) {\n .flex-md-row {\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n .flex-md-column {\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n }\n .flex-md-row-reverse {\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n }\n .flex-md-column-reverse {\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n }\n .flex-md-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n }\n .flex-md-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n }\n .flex-md-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n }\n .flex-md-fill {\n -ms-flex: 1 1 auto !important;\n flex: 1 1 auto !important;\n }\n .flex-md-grow-0 {\n -ms-flex-positive: 0 !important;\n flex-grow: 0 !important;\n }\n .flex-md-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n }\n .flex-md-shrink-0 {\n -ms-flex-negative: 0 !important;\n flex-shrink: 0 !important;\n }\n .flex-md-shrink-1 {\n -ms-flex-negative: 1 !important;\n flex-shrink: 1 !important;\n }\n .justify-content-md-start {\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n .justify-content-md-end {\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n }\n .justify-content-md-center {\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n .justify-content-md-between {\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n }\n .justify-content-md-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .align-items-md-start {\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n }\n .align-items-md-end {\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n }\n .align-items-md-center {\n -ms-flex-align: center !important;\n align-items: center !important;\n }\n .align-items-md-baseline {\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n }\n .align-items-md-stretch {\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n }\n .align-content-md-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n }\n .align-content-md-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n }\n .align-content-md-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n }\n .align-content-md-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n }\n .align-content-md-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n }\n .align-content-md-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n }\n .align-self-md-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n }\n .align-self-md-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n }\n .align-self-md-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n }\n .align-self-md-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n }\n .align-self-md-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n }\n .align-self-md-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 992px) {\n .flex-lg-row {\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n .flex-lg-column {\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n }\n .flex-lg-row-reverse {\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n }\n .flex-lg-column-reverse {\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n }\n .flex-lg-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n }\n .flex-lg-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n }\n .flex-lg-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n }\n .flex-lg-fill {\n -ms-flex: 1 1 auto !important;\n flex: 1 1 auto !important;\n }\n .flex-lg-grow-0 {\n -ms-flex-positive: 0 !important;\n flex-grow: 0 !important;\n }\n .flex-lg-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n }\n .flex-lg-shrink-0 {\n -ms-flex-negative: 0 !important;\n flex-shrink: 0 !important;\n }\n .flex-lg-shrink-1 {\n -ms-flex-negative: 1 !important;\n flex-shrink: 1 !important;\n }\n .justify-content-lg-start {\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n .justify-content-lg-end {\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n }\n .justify-content-lg-center {\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n .justify-content-lg-between {\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n }\n .justify-content-lg-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .align-items-lg-start {\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n }\n .align-items-lg-end {\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n }\n .align-items-lg-center {\n -ms-flex-align: center !important;\n align-items: center !important;\n }\n .align-items-lg-baseline {\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n }\n .align-items-lg-stretch {\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n }\n .align-content-lg-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n }\n .align-content-lg-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n }\n .align-content-lg-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n }\n .align-content-lg-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n }\n .align-content-lg-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n }\n .align-content-lg-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n }\n .align-self-lg-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n }\n .align-self-lg-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n }\n .align-self-lg-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n }\n .align-self-lg-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n }\n .align-self-lg-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n }\n .align-self-lg-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 1200px) {\n .flex-xl-row {\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n .flex-xl-column {\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n }\n .flex-xl-row-reverse {\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n }\n .flex-xl-column-reverse {\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n }\n .flex-xl-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n }\n .flex-xl-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n }\n .flex-xl-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n }\n .flex-xl-fill {\n -ms-flex: 1 1 auto !important;\n flex: 1 1 auto !important;\n }\n .flex-xl-grow-0 {\n -ms-flex-positive: 0 !important;\n flex-grow: 0 !important;\n }\n .flex-xl-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n }\n .flex-xl-shrink-0 {\n -ms-flex-negative: 0 !important;\n flex-shrink: 0 !important;\n }\n .flex-xl-shrink-1 {\n -ms-flex-negative: 1 !important;\n flex-shrink: 1 !important;\n }\n .justify-content-xl-start {\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n .justify-content-xl-end {\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n }\n .justify-content-xl-center {\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n .justify-content-xl-between {\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n }\n .justify-content-xl-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .align-items-xl-start {\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n }\n .align-items-xl-end {\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n }\n .align-items-xl-center {\n -ms-flex-align: center !important;\n align-items: center !important;\n }\n .align-items-xl-baseline {\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n }\n .align-items-xl-stretch {\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n }\n .align-content-xl-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n }\n .align-content-xl-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n }\n .align-content-xl-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n }\n .align-content-xl-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n }\n .align-content-xl-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n }\n .align-content-xl-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n }\n .align-self-xl-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n }\n .align-self-xl-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n }\n .align-self-xl-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n }\n .align-self-xl-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n }\n .align-self-xl-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n }\n .align-self-xl-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n }\n}\n\n.float-left {\n float: left !important;\n}\n\n.float-right {\n float: right !important;\n}\n\n.float-none {\n float: none !important;\n}\n\n@media (min-width: 576px) {\n .float-sm-left {\n float: left !important;\n }\n .float-sm-right {\n float: right !important;\n }\n .float-sm-none {\n float: none !important;\n }\n}\n\n@media (min-width: 768px) {\n .float-md-left {\n float: left !important;\n }\n .float-md-right {\n float: right !important;\n }\n .float-md-none {\n float: none !important;\n }\n}\n\n@media (min-width: 992px) {\n .float-lg-left {\n float: left !important;\n }\n .float-lg-right {\n float: right !important;\n }\n .float-lg-none {\n float: none !important;\n }\n}\n\n@media (min-width: 1200px) {\n .float-xl-left {\n float: left !important;\n }\n .float-xl-right {\n float: right !important;\n }\n .float-xl-none {\n float: none !important;\n }\n}\n\n.position-static {\n position: static !important;\n}\n\n.position-relative {\n position: relative !important;\n}\n\n.position-absolute {\n position: absolute !important;\n}\n\n.position-fixed {\n position: fixed !important;\n}\n\n.position-sticky {\n position: -webkit-sticky !important;\n position: sticky !important;\n}\n\n.fixed-top {\n position: fixed;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n\n.fixed-bottom {\n position: fixed;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1030;\n}\n\n@supports ((position: -webkit-sticky) or (position: sticky)) {\n .sticky-top {\n position: -webkit-sticky;\n position: sticky;\n top: 0;\n z-index: 1020;\n }\n}\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n\n.sr-only-focusable:active, .sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n overflow: visible;\n clip: auto;\n white-space: normal;\n}\n\n.shadow-sm {\n box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;\n}\n\n.shadow {\n box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;\n}\n\n.shadow-lg {\n box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;\n}\n\n.shadow-none {\n box-shadow: none !important;\n}\n\n.w-25 {\n width: 25% !important;\n}\n\n.w-50 {\n width: 50% !important;\n}\n\n.w-75 {\n width: 75% !important;\n}\n\n.w-100 {\n width: 100% !important;\n}\n\n.w-auto {\n width: auto !important;\n}\n\n.h-25 {\n height: 25% !important;\n}\n\n.h-50 {\n height: 50% !important;\n}\n\n.h-75 {\n height: 75% !important;\n}\n\n.h-100 {\n height: 100% !important;\n}\n\n.h-auto {\n height: auto !important;\n}\n\n.mw-100 {\n max-width: 100% !important;\n}\n\n.mh-100 {\n max-height: 100% !important;\n}\n\n.m-0 {\n margin: 0 !important;\n}\n\n.mt-0,\n.my-0 {\n margin-top: 0 !important;\n}\n\n.mr-0,\n.mx-0 {\n margin-right: 0 !important;\n}\n\n.mb-0,\n.my-0 {\n margin-bottom: 0 !important;\n}\n\n.ml-0,\n.mx-0 {\n margin-left: 0 !important;\n}\n\n.m-1 {\n margin: 0.25rem !important;\n}\n\n.mt-1,\n.my-1 {\n margin-top: 0.25rem !important;\n}\n\n.mr-1,\n.mx-1 {\n margin-right: 0.25rem !important;\n}\n\n.mb-1,\n.my-1 {\n margin-bottom: 0.25rem !important;\n}\n\n.ml-1,\n.mx-1 {\n margin-left: 0.25rem !important;\n}\n\n.m-2 {\n margin: 0.5rem !important;\n}\n\n.mt-2,\n.my-2 {\n margin-top: 0.5rem !important;\n}\n\n.mr-2,\n.mx-2 {\n margin-right: 0.5rem !important;\n}\n\n.mb-2,\n.my-2 {\n margin-bottom: 0.5rem !important;\n}\n\n.ml-2,\n.mx-2 {\n margin-left: 0.5rem !important;\n}\n\n.m-3 {\n margin: 1rem !important;\n}\n\n.mt-3,\n.my-3 {\n margin-top: 1rem !important;\n}\n\n.mr-3,\n.mx-3 {\n margin-right: 1rem !important;\n}\n\n.mb-3,\n.my-3 {\n margin-bottom: 1rem !important;\n}\n\n.ml-3,\n.mx-3 {\n margin-left: 1rem !important;\n}\n\n.m-4 {\n margin: 1.5rem !important;\n}\n\n.mt-4,\n.my-4 {\n margin-top: 1.5rem !important;\n}\n\n.mr-4,\n.mx-4 {\n margin-right: 1.5rem !important;\n}\n\n.mb-4,\n.my-4 {\n margin-bottom: 1.5rem !important;\n}\n\n.ml-4,\n.mx-4 {\n margin-left: 1.5rem !important;\n}\n\n.m-5 {\n margin: 3rem !important;\n}\n\n.mt-5,\n.my-5 {\n margin-top: 3rem !important;\n}\n\n.mr-5,\n.mx-5 {\n margin-right: 3rem !important;\n}\n\n.mb-5,\n.my-5 {\n margin-bottom: 3rem !important;\n}\n\n.ml-5,\n.mx-5 {\n margin-left: 3rem !important;\n}\n\n.p-0 {\n padding: 0 !important;\n}\n\n.pt-0,\n.py-0 {\n padding-top: 0 !important;\n}\n\n.pr-0,\n.px-0 {\n padding-right: 0 !important;\n}\n\n.pb-0,\n.py-0 {\n padding-bottom: 0 !important;\n}\n\n.pl-0,\n.px-0 {\n padding-left: 0 !important;\n}\n\n.p-1 {\n padding: 0.25rem !important;\n}\n\n.pt-1,\n.py-1 {\n padding-top: 0.25rem !important;\n}\n\n.pr-1,\n.px-1 {\n padding-right: 0.25rem !important;\n}\n\n.pb-1,\n.py-1 {\n padding-bottom: 0.25rem !important;\n}\n\n.pl-1,\n.px-1 {\n padding-left: 0.25rem !important;\n}\n\n.p-2 {\n padding: 0.5rem !important;\n}\n\n.pt-2,\n.py-2 {\n padding-top: 0.5rem !important;\n}\n\n.pr-2,\n.px-2 {\n padding-right: 0.5rem !important;\n}\n\n.pb-2,\n.py-2 {\n padding-bottom: 0.5rem !important;\n}\n\n.pl-2,\n.px-2 {\n padding-left: 0.5rem !important;\n}\n\n.p-3 {\n padding: 1rem !important;\n}\n\n.pt-3,\n.py-3 {\n padding-top: 1rem !important;\n}\n\n.pr-3,\n.px-3 {\n padding-right: 1rem !important;\n}\n\n.pb-3,\n.py-3 {\n padding-bottom: 1rem !important;\n}\n\n.pl-3,\n.px-3 {\n padding-left: 1rem !important;\n}\n\n.p-4 {\n padding: 1.5rem !important;\n}\n\n.pt-4,\n.py-4 {\n padding-top: 1.5rem !important;\n}\n\n.pr-4,\n.px-4 {\n padding-right: 1.5rem !important;\n}\n\n.pb-4,\n.py-4 {\n padding-bottom: 1.5rem !important;\n}\n\n.pl-4,\n.px-4 {\n padding-left: 1.5rem !important;\n}\n\n.p-5 {\n padding: 3rem !important;\n}\n\n.pt-5,\n.py-5 {\n padding-top: 3rem !important;\n}\n\n.pr-5,\n.px-5 {\n padding-right: 3rem !important;\n}\n\n.pb-5,\n.py-5 {\n padding-bottom: 3rem !important;\n}\n\n.pl-5,\n.px-5 {\n padding-left: 3rem !important;\n}\n\n.m-auto {\n margin: auto !important;\n}\n\n.mt-auto,\n.my-auto {\n margin-top: auto !important;\n}\n\n.mr-auto,\n.mx-auto {\n margin-right: auto !important;\n}\n\n.mb-auto,\n.my-auto {\n margin-bottom: auto !important;\n}\n\n.ml-auto,\n.mx-auto {\n margin-left: auto !important;\n}\n\n@media (min-width: 576px) {\n .m-sm-0 {\n margin: 0 !important;\n }\n .mt-sm-0,\n .my-sm-0 {\n margin-top: 0 !important;\n }\n .mr-sm-0,\n .mx-sm-0 {\n margin-right: 0 !important;\n }\n .mb-sm-0,\n .my-sm-0 {\n margin-bottom: 0 !important;\n }\n .ml-sm-0,\n .mx-sm-0 {\n margin-left: 0 !important;\n }\n .m-sm-1 {\n margin: 0.25rem !important;\n }\n .mt-sm-1,\n .my-sm-1 {\n margin-top: 0.25rem !important;\n }\n .mr-sm-1,\n .mx-sm-1 {\n margin-right: 0.25rem !important;\n }\n .mb-sm-1,\n .my-sm-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-sm-1,\n .mx-sm-1 {\n margin-left: 0.25rem !important;\n }\n .m-sm-2 {\n margin: 0.5rem !important;\n }\n .mt-sm-2,\n .my-sm-2 {\n margin-top: 0.5rem !important;\n }\n .mr-sm-2,\n .mx-sm-2 {\n margin-right: 0.5rem !important;\n }\n .mb-sm-2,\n .my-sm-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-sm-2,\n .mx-sm-2 {\n margin-left: 0.5rem !important;\n }\n .m-sm-3 {\n margin: 1rem !important;\n }\n .mt-sm-3,\n .my-sm-3 {\n margin-top: 1rem !important;\n }\n .mr-sm-3,\n .mx-sm-3 {\n margin-right: 1rem !important;\n }\n .mb-sm-3,\n .my-sm-3 {\n margin-bottom: 1rem !important;\n }\n .ml-sm-3,\n .mx-sm-3 {\n margin-left: 1rem !important;\n }\n .m-sm-4 {\n margin: 1.5rem !important;\n }\n .mt-sm-4,\n .my-sm-4 {\n margin-top: 1.5rem !important;\n }\n .mr-sm-4,\n .mx-sm-4 {\n margin-right: 1.5rem !important;\n }\n .mb-sm-4,\n .my-sm-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-sm-4,\n .mx-sm-4 {\n margin-left: 1.5rem !important;\n }\n .m-sm-5 {\n margin: 3rem !important;\n }\n .mt-sm-5,\n .my-sm-5 {\n margin-top: 3rem !important;\n }\n .mr-sm-5,\n .mx-sm-5 {\n margin-right: 3rem !important;\n }\n .mb-sm-5,\n .my-sm-5 {\n margin-bottom: 3rem !important;\n }\n .ml-sm-5,\n .mx-sm-5 {\n margin-left: 3rem !important;\n }\n .p-sm-0 {\n padding: 0 !important;\n }\n .pt-sm-0,\n .py-sm-0 {\n padding-top: 0 !important;\n }\n .pr-sm-0,\n .px-sm-0 {\n padding-right: 0 !important;\n }\n .pb-sm-0,\n .py-sm-0 {\n padding-bottom: 0 !important;\n }\n .pl-sm-0,\n .px-sm-0 {\n padding-left: 0 !important;\n }\n .p-sm-1 {\n padding: 0.25rem !important;\n }\n .pt-sm-1,\n .py-sm-1 {\n padding-top: 0.25rem !important;\n }\n .pr-sm-1,\n .px-sm-1 {\n padding-right: 0.25rem !important;\n }\n .pb-sm-1,\n .py-sm-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-sm-1,\n .px-sm-1 {\n padding-left: 0.25rem !important;\n }\n .p-sm-2 {\n padding: 0.5rem !important;\n }\n .pt-sm-2,\n .py-sm-2 {\n padding-top: 0.5rem !important;\n }\n .pr-sm-2,\n .px-sm-2 {\n padding-right: 0.5rem !important;\n }\n .pb-sm-2,\n .py-sm-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-sm-2,\n .px-sm-2 {\n padding-left: 0.5rem !important;\n }\n .p-sm-3 {\n padding: 1rem !important;\n }\n .pt-sm-3,\n .py-sm-3 {\n padding-top: 1rem !important;\n }\n .pr-sm-3,\n .px-sm-3 {\n padding-right: 1rem !important;\n }\n .pb-sm-3,\n .py-sm-3 {\n padding-bottom: 1rem !important;\n }\n .pl-sm-3,\n .px-sm-3 {\n padding-left: 1rem !important;\n }\n .p-sm-4 {\n padding: 1.5rem !important;\n }\n .pt-sm-4,\n .py-sm-4 {\n padding-top: 1.5rem !important;\n }\n .pr-sm-4,\n .px-sm-4 {\n padding-right: 1.5rem !important;\n }\n .pb-sm-4,\n .py-sm-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-sm-4,\n .px-sm-4 {\n padding-left: 1.5rem !important;\n }\n .p-sm-5 {\n padding: 3rem !important;\n }\n .pt-sm-5,\n .py-sm-5 {\n padding-top: 3rem !important;\n }\n .pr-sm-5,\n .px-sm-5 {\n padding-right: 3rem !important;\n }\n .pb-sm-5,\n .py-sm-5 {\n padding-bottom: 3rem !important;\n }\n .pl-sm-5,\n .px-sm-5 {\n padding-left: 3rem !important;\n }\n .m-sm-auto {\n margin: auto !important;\n }\n .mt-sm-auto,\n .my-sm-auto {\n margin-top: auto !important;\n }\n .mr-sm-auto,\n .mx-sm-auto {\n margin-right: auto !important;\n }\n .mb-sm-auto,\n .my-sm-auto {\n margin-bottom: auto !important;\n }\n .ml-sm-auto,\n .mx-sm-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 768px) {\n .m-md-0 {\n margin: 0 !important;\n }\n .mt-md-0,\n .my-md-0 {\n margin-top: 0 !important;\n }\n .mr-md-0,\n .mx-md-0 {\n margin-right: 0 !important;\n }\n .mb-md-0,\n .my-md-0 {\n margin-bottom: 0 !important;\n }\n .ml-md-0,\n .mx-md-0 {\n margin-left: 0 !important;\n }\n .m-md-1 {\n margin: 0.25rem !important;\n }\n .mt-md-1,\n .my-md-1 {\n margin-top: 0.25rem !important;\n }\n .mr-md-1,\n .mx-md-1 {\n margin-right: 0.25rem !important;\n }\n .mb-md-1,\n .my-md-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-md-1,\n .mx-md-1 {\n margin-left: 0.25rem !important;\n }\n .m-md-2 {\n margin: 0.5rem !important;\n }\n .mt-md-2,\n .my-md-2 {\n margin-top: 0.5rem !important;\n }\n .mr-md-2,\n .mx-md-2 {\n margin-right: 0.5rem !important;\n }\n .mb-md-2,\n .my-md-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-md-2,\n .mx-md-2 {\n margin-left: 0.5rem !important;\n }\n .m-md-3 {\n margin: 1rem !important;\n }\n .mt-md-3,\n .my-md-3 {\n margin-top: 1rem !important;\n }\n .mr-md-3,\n .mx-md-3 {\n margin-right: 1rem !important;\n }\n .mb-md-3,\n .my-md-3 {\n margin-bottom: 1rem !important;\n }\n .ml-md-3,\n .mx-md-3 {\n margin-left: 1rem !important;\n }\n .m-md-4 {\n margin: 1.5rem !important;\n }\n .mt-md-4,\n .my-md-4 {\n margin-top: 1.5rem !important;\n }\n .mr-md-4,\n .mx-md-4 {\n margin-right: 1.5rem !important;\n }\n .mb-md-4,\n .my-md-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-md-4,\n .mx-md-4 {\n margin-left: 1.5rem !important;\n }\n .m-md-5 {\n margin: 3rem !important;\n }\n .mt-md-5,\n .my-md-5 {\n margin-top: 3rem !important;\n }\n .mr-md-5,\n .mx-md-5 {\n margin-right: 3rem !important;\n }\n .mb-md-5,\n .my-md-5 {\n margin-bottom: 3rem !important;\n }\n .ml-md-5,\n .mx-md-5 {\n margin-left: 3rem !important;\n }\n .p-md-0 {\n padding: 0 !important;\n }\n .pt-md-0,\n .py-md-0 {\n padding-top: 0 !important;\n }\n .pr-md-0,\n .px-md-0 {\n padding-right: 0 !important;\n }\n .pb-md-0,\n .py-md-0 {\n padding-bottom: 0 !important;\n }\n .pl-md-0,\n .px-md-0 {\n padding-left: 0 !important;\n }\n .p-md-1 {\n padding: 0.25rem !important;\n }\n .pt-md-1,\n .py-md-1 {\n padding-top: 0.25rem !important;\n }\n .pr-md-1,\n .px-md-1 {\n padding-right: 0.25rem !important;\n }\n .pb-md-1,\n .py-md-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-md-1,\n .px-md-1 {\n padding-left: 0.25rem !important;\n }\n .p-md-2 {\n padding: 0.5rem !important;\n }\n .pt-md-2,\n .py-md-2 {\n padding-top: 0.5rem !important;\n }\n .pr-md-2,\n .px-md-2 {\n padding-right: 0.5rem !important;\n }\n .pb-md-2,\n .py-md-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-md-2,\n .px-md-2 {\n padding-left: 0.5rem !important;\n }\n .p-md-3 {\n padding: 1rem !important;\n }\n .pt-md-3,\n .py-md-3 {\n padding-top: 1rem !important;\n }\n .pr-md-3,\n .px-md-3 {\n padding-right: 1rem !important;\n }\n .pb-md-3,\n .py-md-3 {\n padding-bottom: 1rem !important;\n }\n .pl-md-3,\n .px-md-3 {\n padding-left: 1rem !important;\n }\n .p-md-4 {\n padding: 1.5rem !important;\n }\n .pt-md-4,\n .py-md-4 {\n padding-top: 1.5rem !important;\n }\n .pr-md-4,\n .px-md-4 {\n padding-right: 1.5rem !important;\n }\n .pb-md-4,\n .py-md-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-md-4,\n .px-md-4 {\n padding-left: 1.5rem !important;\n }\n .p-md-5 {\n padding: 3rem !important;\n }\n .pt-md-5,\n .py-md-5 {\n padding-top: 3rem !important;\n }\n .pr-md-5,\n .px-md-5 {\n padding-right: 3rem !important;\n }\n .pb-md-5,\n .py-md-5 {\n padding-bottom: 3rem !important;\n }\n .pl-md-5,\n .px-md-5 {\n padding-left: 3rem !important;\n }\n .m-md-auto {\n margin: auto !important;\n }\n .mt-md-auto,\n .my-md-auto {\n margin-top: auto !important;\n }\n .mr-md-auto,\n .mx-md-auto {\n margin-right: auto !important;\n }\n .mb-md-auto,\n .my-md-auto {\n margin-bottom: auto !important;\n }\n .ml-md-auto,\n .mx-md-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 992px) {\n .m-lg-0 {\n margin: 0 !important;\n }\n .mt-lg-0,\n .my-lg-0 {\n margin-top: 0 !important;\n }\n .mr-lg-0,\n .mx-lg-0 {\n margin-right: 0 !important;\n }\n .mb-lg-0,\n .my-lg-0 {\n margin-bottom: 0 !important;\n }\n .ml-lg-0,\n .mx-lg-0 {\n margin-left: 0 !important;\n }\n .m-lg-1 {\n margin: 0.25rem !important;\n }\n .mt-lg-1,\n .my-lg-1 {\n margin-top: 0.25rem !important;\n }\n .mr-lg-1,\n .mx-lg-1 {\n margin-right: 0.25rem !important;\n }\n .mb-lg-1,\n .my-lg-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-lg-1,\n .mx-lg-1 {\n margin-left: 0.25rem !important;\n }\n .m-lg-2 {\n margin: 0.5rem !important;\n }\n .mt-lg-2,\n .my-lg-2 {\n margin-top: 0.5rem !important;\n }\n .mr-lg-2,\n .mx-lg-2 {\n margin-right: 0.5rem !important;\n }\n .mb-lg-2,\n .my-lg-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-lg-2,\n .mx-lg-2 {\n margin-left: 0.5rem !important;\n }\n .m-lg-3 {\n margin: 1rem !important;\n }\n .mt-lg-3,\n .my-lg-3 {\n margin-top: 1rem !important;\n }\n .mr-lg-3,\n .mx-lg-3 {\n margin-right: 1rem !important;\n }\n .mb-lg-3,\n .my-lg-3 {\n margin-bottom: 1rem !important;\n }\n .ml-lg-3,\n .mx-lg-3 {\n margin-left: 1rem !important;\n }\n .m-lg-4 {\n margin: 1.5rem !important;\n }\n .mt-lg-4,\n .my-lg-4 {\n margin-top: 1.5rem !important;\n }\n .mr-lg-4,\n .mx-lg-4 {\n margin-right: 1.5rem !important;\n }\n .mb-lg-4,\n .my-lg-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-lg-4,\n .mx-lg-4 {\n margin-left: 1.5rem !important;\n }\n .m-lg-5 {\n margin: 3rem !important;\n }\n .mt-lg-5,\n .my-lg-5 {\n margin-top: 3rem !important;\n }\n .mr-lg-5,\n .mx-lg-5 {\n margin-right: 3rem !important;\n }\n .mb-lg-5,\n .my-lg-5 {\n margin-bottom: 3rem !important;\n }\n .ml-lg-5,\n .mx-lg-5 {\n margin-left: 3rem !important;\n }\n .p-lg-0 {\n padding: 0 !important;\n }\n .pt-lg-0,\n .py-lg-0 {\n padding-top: 0 !important;\n }\n .pr-lg-0,\n .px-lg-0 {\n padding-right: 0 !important;\n }\n .pb-lg-0,\n .py-lg-0 {\n padding-bottom: 0 !important;\n }\n .pl-lg-0,\n .px-lg-0 {\n padding-left: 0 !important;\n }\n .p-lg-1 {\n padding: 0.25rem !important;\n }\n .pt-lg-1,\n .py-lg-1 {\n padding-top: 0.25rem !important;\n }\n .pr-lg-1,\n .px-lg-1 {\n padding-right: 0.25rem !important;\n }\n .pb-lg-1,\n .py-lg-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-lg-1,\n .px-lg-1 {\n padding-left: 0.25rem !important;\n }\n .p-lg-2 {\n padding: 0.5rem !important;\n }\n .pt-lg-2,\n .py-lg-2 {\n padding-top: 0.5rem !important;\n }\n .pr-lg-2,\n .px-lg-2 {\n padding-right: 0.5rem !important;\n }\n .pb-lg-2,\n .py-lg-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-lg-2,\n .px-lg-2 {\n padding-left: 0.5rem !important;\n }\n .p-lg-3 {\n padding: 1rem !important;\n }\n .pt-lg-3,\n .py-lg-3 {\n padding-top: 1rem !important;\n }\n .pr-lg-3,\n .px-lg-3 {\n padding-right: 1rem !important;\n }\n .pb-lg-3,\n .py-lg-3 {\n padding-bottom: 1rem !important;\n }\n .pl-lg-3,\n .px-lg-3 {\n padding-left: 1rem !important;\n }\n .p-lg-4 {\n padding: 1.5rem !important;\n }\n .pt-lg-4,\n .py-lg-4 {\n padding-top: 1.5rem !important;\n }\n .pr-lg-4,\n .px-lg-4 {\n padding-right: 1.5rem !important;\n }\n .pb-lg-4,\n .py-lg-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-lg-4,\n .px-lg-4 {\n padding-left: 1.5rem !important;\n }\n .p-lg-5 {\n padding: 3rem !important;\n }\n .pt-lg-5,\n .py-lg-5 {\n padding-top: 3rem !important;\n }\n .pr-lg-5,\n .px-lg-5 {\n padding-right: 3rem !important;\n }\n .pb-lg-5,\n .py-lg-5 {\n padding-bottom: 3rem !important;\n }\n .pl-lg-5,\n .px-lg-5 {\n padding-left: 3rem !important;\n }\n .m-lg-auto {\n margin: auto !important;\n }\n .mt-lg-auto,\n .my-lg-auto {\n margin-top: auto !important;\n }\n .mr-lg-auto,\n .mx-lg-auto {\n margin-right: auto !important;\n }\n .mb-lg-auto,\n .my-lg-auto {\n margin-bottom: auto !important;\n }\n .ml-lg-auto,\n .mx-lg-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 1200px) {\n .m-xl-0 {\n margin: 0 !important;\n }\n .mt-xl-0,\n .my-xl-0 {\n margin-top: 0 !important;\n }\n .mr-xl-0,\n .mx-xl-0 {\n margin-right: 0 !important;\n }\n .mb-xl-0,\n .my-xl-0 {\n margin-bottom: 0 !important;\n }\n .ml-xl-0,\n .mx-xl-0 {\n margin-left: 0 !important;\n }\n .m-xl-1 {\n margin: 0.25rem !important;\n }\n .mt-xl-1,\n .my-xl-1 {\n margin-top: 0.25rem !important;\n }\n .mr-xl-1,\n .mx-xl-1 {\n margin-right: 0.25rem !important;\n }\n .mb-xl-1,\n .my-xl-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-xl-1,\n .mx-xl-1 {\n margin-left: 0.25rem !important;\n }\n .m-xl-2 {\n margin: 0.5rem !important;\n }\n .mt-xl-2,\n .my-xl-2 {\n margin-top: 0.5rem !important;\n }\n .mr-xl-2,\n .mx-xl-2 {\n margin-right: 0.5rem !important;\n }\n .mb-xl-2,\n .my-xl-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-xl-2,\n .mx-xl-2 {\n margin-left: 0.5rem !important;\n }\n .m-xl-3 {\n margin: 1rem !important;\n }\n .mt-xl-3,\n .my-xl-3 {\n margin-top: 1rem !important;\n }\n .mr-xl-3,\n .mx-xl-3 {\n margin-right: 1rem !important;\n }\n .mb-xl-3,\n .my-xl-3 {\n margin-bottom: 1rem !important;\n }\n .ml-xl-3,\n .mx-xl-3 {\n margin-left: 1rem !important;\n }\n .m-xl-4 {\n margin: 1.5rem !important;\n }\n .mt-xl-4,\n .my-xl-4 {\n margin-top: 1.5rem !important;\n }\n .mr-xl-4,\n .mx-xl-4 {\n margin-right: 1.5rem !important;\n }\n .mb-xl-4,\n .my-xl-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-xl-4,\n .mx-xl-4 {\n margin-left: 1.5rem !important;\n }\n .m-xl-5 {\n margin: 3rem !important;\n }\n .mt-xl-5,\n .my-xl-5 {\n margin-top: 3rem !important;\n }\n .mr-xl-5,\n .mx-xl-5 {\n margin-right: 3rem !important;\n }\n .mb-xl-5,\n .my-xl-5 {\n margin-bottom: 3rem !important;\n }\n .ml-xl-5,\n .mx-xl-5 {\n margin-left: 3rem !important;\n }\n .p-xl-0 {\n padding: 0 !important;\n }\n .pt-xl-0,\n .py-xl-0 {\n padding-top: 0 !important;\n }\n .pr-xl-0,\n .px-xl-0 {\n padding-right: 0 !important;\n }\n .pb-xl-0,\n .py-xl-0 {\n padding-bottom: 0 !important;\n }\n .pl-xl-0,\n .px-xl-0 {\n padding-left: 0 !important;\n }\n .p-xl-1 {\n padding: 0.25rem !important;\n }\n .pt-xl-1,\n .py-xl-1 {\n padding-top: 0.25rem !important;\n }\n .pr-xl-1,\n .px-xl-1 {\n padding-right: 0.25rem !important;\n }\n .pb-xl-1,\n .py-xl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-xl-1,\n .px-xl-1 {\n padding-left: 0.25rem !important;\n }\n .p-xl-2 {\n padding: 0.5rem !important;\n }\n .pt-xl-2,\n .py-xl-2 {\n padding-top: 0.5rem !important;\n }\n .pr-xl-2,\n .px-xl-2 {\n padding-right: 0.5rem !important;\n }\n .pb-xl-2,\n .py-xl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-xl-2,\n .px-xl-2 {\n padding-left: 0.5rem !important;\n }\n .p-xl-3 {\n padding: 1rem !important;\n }\n .pt-xl-3,\n .py-xl-3 {\n padding-top: 1rem !important;\n }\n .pr-xl-3,\n .px-xl-3 {\n padding-right: 1rem !important;\n }\n .pb-xl-3,\n .py-xl-3 {\n padding-bottom: 1rem !important;\n }\n .pl-xl-3,\n .px-xl-3 {\n padding-left: 1rem !important;\n }\n .p-xl-4 {\n padding: 1.5rem !important;\n }\n .pt-xl-4,\n .py-xl-4 {\n padding-top: 1.5rem !important;\n }\n .pr-xl-4,\n .px-xl-4 {\n padding-right: 1.5rem !important;\n }\n .pb-xl-4,\n .py-xl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-xl-4,\n .px-xl-4 {\n padding-left: 1.5rem !important;\n }\n .p-xl-5 {\n padding: 3rem !important;\n }\n .pt-xl-5,\n .py-xl-5 {\n padding-top: 3rem !important;\n }\n .pr-xl-5,\n .px-xl-5 {\n padding-right: 3rem !important;\n }\n .pb-xl-5,\n .py-xl-5 {\n padding-bottom: 3rem !important;\n }\n .pl-xl-5,\n .px-xl-5 {\n padding-left: 3rem !important;\n }\n .m-xl-auto {\n margin: auto !important;\n }\n .mt-xl-auto,\n .my-xl-auto {\n margin-top: auto !important;\n }\n .mr-xl-auto,\n .mx-xl-auto {\n margin-right: auto !important;\n }\n .mb-xl-auto,\n .my-xl-auto {\n margin-bottom: auto !important;\n }\n .ml-xl-auto,\n .mx-xl-auto {\n margin-left: auto !important;\n }\n}\n\n.text-monospace {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n.text-justify {\n text-align: justify !important;\n}\n\n.text-nowrap {\n white-space: nowrap !important;\n}\n\n.text-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.text-left {\n text-align: left !important;\n}\n\n.text-right {\n text-align: right !important;\n}\n\n.text-center {\n text-align: center !important;\n}\n\n@media (min-width: 576px) {\n .text-sm-left {\n text-align: left !important;\n }\n .text-sm-right {\n text-align: right !important;\n }\n .text-sm-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 768px) {\n .text-md-left {\n text-align: left !important;\n }\n .text-md-right {\n text-align: right !important;\n }\n .text-md-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 992px) {\n .text-lg-left {\n text-align: left !important;\n }\n .text-lg-right {\n text-align: right !important;\n }\n .text-lg-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 1200px) {\n .text-xl-left {\n text-align: left !important;\n }\n .text-xl-right {\n text-align: right !important;\n }\n .text-xl-center {\n text-align: center !important;\n }\n}\n\n.text-lowercase {\n text-transform: lowercase !important;\n}\n\n.text-uppercase {\n text-transform: uppercase !important;\n}\n\n.text-capitalize {\n text-transform: capitalize !important;\n}\n\n.font-weight-light {\n font-weight: 300 !important;\n}\n\n.font-weight-normal {\n font-weight: 400 !important;\n}\n\n.font-weight-bold {\n font-weight: 700 !important;\n}\n\n.font-italic {\n font-style: italic !important;\n}\n\n.text-white {\n color: #fff !important;\n}\n\n.text-primary {\n color: #007bff !important;\n}\n\na.text-primary:hover, a.text-primary:focus {\n color: #0062cc !important;\n}\n\n.text-secondary {\n color: #6c757d !important;\n}\n\na.text-secondary:hover, a.text-secondary:focus {\n color: #545b62 !important;\n}\n\n.text-success {\n color: #28a745 !important;\n}\n\na.text-success:hover, a.text-success:focus {\n color: #1e7e34 !important;\n}\n\n.text-info {\n color: #17a2b8 !important;\n}\n\na.text-info:hover, a.text-info:focus {\n color: #117a8b !important;\n}\n\n.text-warning {\n color: #ffc107 !important;\n}\n\na.text-warning:hover, a.text-warning:focus {\n color: #d39e00 !important;\n}\n\n.text-danger {\n color: #dc3545 !important;\n}\n\na.text-danger:hover, a.text-danger:focus {\n color: #bd2130 !important;\n}\n\n.text-light {\n color: #f8f9fa !important;\n}\n\na.text-light:hover, a.text-light:focus {\n color: #dae0e5 !important;\n}\n\n.text-dark {\n color: #343a40 !important;\n}\n\na.text-dark:hover, a.text-dark:focus {\n color: #1d2124 !important;\n}\n\n.text-body {\n color: #212529 !important;\n}\n\n.text-muted {\n color: #6c757d !important;\n}\n\n.text-black-50 {\n color: rgba(0, 0, 0, 0.5) !important;\n}\n\n.text-white-50 {\n color: rgba(255, 255, 255, 0.5) !important;\n}\n\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n\n.visible {\n visibility: visible !important;\n}\n\n.invisible {\n visibility: hidden !important;\n}\n\n@media print {\n *,\n *::before,\n *::after {\n text-shadow: none !important;\n box-shadow: none !important;\n }\n a:not(.btn) {\n text-decoration: underline;\n }\n abbr[title]::after {\n content: \" (\" attr(title) \")\";\n }\n pre {\n white-space: pre-wrap !important;\n }\n pre,\n blockquote {\n border: 1px solid #adb5bd;\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n @page {\n size: a3;\n }\n body {\n min-width: 992px !important;\n }\n .container {\n min-width: 992px !important;\n }\n .navbar {\n display: none;\n }\n .badge {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #dee2e6 !important;\n }\n .table-dark {\n color: inherit;\n }\n .table-dark th,\n .table-dark td,\n .table-dark thead th,\n .table-dark tbody + tbody {\n border-color: #dee2e6;\n }\n .table .thead-dark th {\n color: inherit;\n border-color: #dee2e6;\n }\n}\n/*# sourceMappingURL=bootstrap.css.map */","/*!\n * Bootstrap v4.1.1 (https://getbootstrap.com/)\n * Copyright 2011-2018 The Bootstrap Authors\n * Copyright 2011-2018 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n:root {\n --blue: #007bff;\n --indigo: #6610f2;\n --purple: #6f42c1;\n --pink: #e83e8c;\n --red: #dc3545;\n --orange: #fd7e14;\n --yellow: #ffc107;\n --green: #28a745;\n --teal: #20c997;\n --cyan: #17a2b8;\n --white: #fff;\n --gray: #6c757d;\n --gray-dark: #343a40;\n --primary: #007bff;\n --secondary: #6c757d;\n --success: #28a745;\n --info: #17a2b8;\n --warning: #ffc107;\n --danger: #dc3545;\n --light: #f8f9fa;\n --dark: #343a40;\n --breakpoint-xs: 0;\n --breakpoint-sm: 576px;\n --breakpoint-md: 768px;\n --breakpoint-lg: 992px;\n --breakpoint-xl: 1200px;\n --font-family-sans-serif: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -ms-text-size-adjust: 100%;\n -ms-overflow-style: scrollbar;\n -webkit-tap-highlight-color: transparent;\n}\n\n@-ms-viewport {\n width: device-width;\n}\n\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\ndfn {\n font-style: italic;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n -webkit-text-decoration-skip: objects;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):focus {\n outline: 0;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n -ms-overflow-style: scrollbar;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: 0.5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nbutton,\nhtml [type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n margin-bottom: 0.5rem;\n font-family: inherit;\n font-weight: 500;\n line-height: 1.2;\n color: inherit;\n}\n\nh1, .h1 {\n font-size: 2.5rem;\n}\n\nh2, .h2 {\n font-size: 2rem;\n}\n\nh3, .h3 {\n font-size: 1.75rem;\n}\n\nh4, .h4 {\n font-size: 1.5rem;\n}\n\nh5, .h5 {\n font-size: 1.25rem;\n}\n\nh6, .h6 {\n font-size: 1rem;\n}\n\n.lead {\n font-size: 1.25rem;\n font-weight: 300;\n}\n\n.display-1 {\n font-size: 6rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-2 {\n font-size: 5.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-3 {\n font-size: 4.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-4 {\n font-size: 3.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\nhr {\n margin-top: 1rem;\n margin-bottom: 1rem;\n border: 0;\n border-top: 1px solid rgba(0, 0, 0, 0.1);\n}\n\nsmall,\n.small {\n font-size: 80%;\n font-weight: 400;\n}\n\nmark,\n.mark {\n padding: 0.2em;\n background-color: #fcf8e3;\n}\n\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline-item {\n display: inline-block;\n}\n\n.list-inline-item:not(:last-child) {\n margin-right: 0.5rem;\n}\n\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\n\n.blockquote {\n margin-bottom: 1rem;\n font-size: 1.25rem;\n}\n\n.blockquote-footer {\n display: block;\n font-size: 80%;\n color: #6c757d;\n}\n\n.blockquote-footer::before {\n content: \"\\2014 \\00A0\";\n}\n\n.img-fluid {\n max-width: 100%;\n height: auto;\n}\n\n.img-thumbnail {\n padding: 0.25rem;\n background-color: #fff;\n border: 1px solid #dee2e6;\n border-radius: 0.25rem;\n max-width: 100%;\n height: auto;\n}\n\n.figure {\n display: inline-block;\n}\n\n.figure-img {\n margin-bottom: 0.5rem;\n line-height: 1;\n}\n\n.figure-caption {\n font-size: 90%;\n color: #6c757d;\n}\n\ncode {\n font-size: 87.5%;\n color: #e83e8c;\n word-break: break-word;\n}\n\na > code {\n color: inherit;\n}\n\nkbd {\n padding: 0.2rem 0.4rem;\n font-size: 87.5%;\n color: #fff;\n background-color: #212529;\n border-radius: 0.2rem;\n}\n\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: 700;\n}\n\npre {\n display: block;\n font-size: 87.5%;\n color: #212529;\n}\n\npre code {\n font-size: inherit;\n color: inherit;\n word-break: normal;\n}\n\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n\n.container {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n@media (min-width: 576px) {\n .container {\n max-width: 540px;\n }\n}\n\n@media (min-width: 768px) {\n .container {\n max-width: 720px;\n }\n}\n\n@media (min-width: 992px) {\n .container {\n max-width: 960px;\n }\n}\n\n@media (min-width: 1200px) {\n .container {\n max-width: 1140px;\n }\n}\n\n.container-fluid {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n.row {\n display: flex;\n flex-wrap: wrap;\n margin-right: -15px;\n margin-left: -15px;\n}\n\n.no-gutters {\n margin-right: 0;\n margin-left: 0;\n}\n\n.no-gutters > .col,\n.no-gutters > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n}\n\n.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col,\n.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm,\n.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md,\n.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg,\n.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl,\n.col-xl-auto {\n position: relative;\n width: 100%;\n min-height: 1px;\n padding-right: 15px;\n padding-left: 15px;\n}\n\n.col {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n}\n\n.col-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n}\n\n.col-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n}\n\n.col-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n}\n\n.col-3 {\n flex: 0 0 25%;\n max-width: 25%;\n}\n\n.col-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n}\n\n.col-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n}\n\n.col-6 {\n flex: 0 0 50%;\n max-width: 50%;\n}\n\n.col-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n}\n\n.col-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n}\n\n.col-9 {\n flex: 0 0 75%;\n max-width: 75%;\n}\n\n.col-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n}\n\n.col-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n}\n\n.col-12 {\n flex: 0 0 100%;\n max-width: 100%;\n}\n\n.order-first {\n order: -1;\n}\n\n.order-last {\n order: 13;\n}\n\n.order-0 {\n order: 0;\n}\n\n.order-1 {\n order: 1;\n}\n\n.order-2 {\n order: 2;\n}\n\n.order-3 {\n order: 3;\n}\n\n.order-4 {\n order: 4;\n}\n\n.order-5 {\n order: 5;\n}\n\n.order-6 {\n order: 6;\n}\n\n.order-7 {\n order: 7;\n}\n\n.order-8 {\n order: 8;\n}\n\n.order-9 {\n order: 9;\n}\n\n.order-10 {\n order: 10;\n}\n\n.order-11 {\n order: 11;\n}\n\n.order-12 {\n order: 12;\n}\n\n.offset-1 {\n margin-left: 8.333333%;\n}\n\n.offset-2 {\n margin-left: 16.666667%;\n}\n\n.offset-3 {\n margin-left: 25%;\n}\n\n.offset-4 {\n margin-left: 33.333333%;\n}\n\n.offset-5 {\n margin-left: 41.666667%;\n}\n\n.offset-6 {\n margin-left: 50%;\n}\n\n.offset-7 {\n margin-left: 58.333333%;\n}\n\n.offset-8 {\n margin-left: 66.666667%;\n}\n\n.offset-9 {\n margin-left: 75%;\n}\n\n.offset-10 {\n margin-left: 83.333333%;\n}\n\n.offset-11 {\n margin-left: 91.666667%;\n}\n\n@media (min-width: 576px) {\n .col-sm {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-sm-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-sm-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-sm-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-sm-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-sm-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-sm-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-sm-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-sm-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-sm-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-sm-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-sm-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-sm-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-sm-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-sm-first {\n order: -1;\n }\n .order-sm-last {\n order: 13;\n }\n .order-sm-0 {\n order: 0;\n }\n .order-sm-1 {\n order: 1;\n }\n .order-sm-2 {\n order: 2;\n }\n .order-sm-3 {\n order: 3;\n }\n .order-sm-4 {\n order: 4;\n }\n .order-sm-5 {\n order: 5;\n }\n .order-sm-6 {\n order: 6;\n }\n .order-sm-7 {\n order: 7;\n }\n .order-sm-8 {\n order: 8;\n }\n .order-sm-9 {\n order: 9;\n }\n .order-sm-10 {\n order: 10;\n }\n .order-sm-11 {\n order: 11;\n }\n .order-sm-12 {\n order: 12;\n }\n .offset-sm-0 {\n margin-left: 0;\n }\n .offset-sm-1 {\n margin-left: 8.333333%;\n }\n .offset-sm-2 {\n margin-left: 16.666667%;\n }\n .offset-sm-3 {\n margin-left: 25%;\n }\n .offset-sm-4 {\n margin-left: 33.333333%;\n }\n .offset-sm-5 {\n margin-left: 41.666667%;\n }\n .offset-sm-6 {\n margin-left: 50%;\n }\n .offset-sm-7 {\n margin-left: 58.333333%;\n }\n .offset-sm-8 {\n margin-left: 66.666667%;\n }\n .offset-sm-9 {\n margin-left: 75%;\n }\n .offset-sm-10 {\n margin-left: 83.333333%;\n }\n .offset-sm-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 768px) {\n .col-md {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-md-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-md-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-md-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-md-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-md-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-md-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-md-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-md-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-md-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-md-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-md-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-md-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-md-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-md-first {\n order: -1;\n }\n .order-md-last {\n order: 13;\n }\n .order-md-0 {\n order: 0;\n }\n .order-md-1 {\n order: 1;\n }\n .order-md-2 {\n order: 2;\n }\n .order-md-3 {\n order: 3;\n }\n .order-md-4 {\n order: 4;\n }\n .order-md-5 {\n order: 5;\n }\n .order-md-6 {\n order: 6;\n }\n .order-md-7 {\n order: 7;\n }\n .order-md-8 {\n order: 8;\n }\n .order-md-9 {\n order: 9;\n }\n .order-md-10 {\n order: 10;\n }\n .order-md-11 {\n order: 11;\n }\n .order-md-12 {\n order: 12;\n }\n .offset-md-0 {\n margin-left: 0;\n }\n .offset-md-1 {\n margin-left: 8.333333%;\n }\n .offset-md-2 {\n margin-left: 16.666667%;\n }\n .offset-md-3 {\n margin-left: 25%;\n }\n .offset-md-4 {\n margin-left: 33.333333%;\n }\n .offset-md-5 {\n margin-left: 41.666667%;\n }\n .offset-md-6 {\n margin-left: 50%;\n }\n .offset-md-7 {\n margin-left: 58.333333%;\n }\n .offset-md-8 {\n margin-left: 66.666667%;\n }\n .offset-md-9 {\n margin-left: 75%;\n }\n .offset-md-10 {\n margin-left: 83.333333%;\n }\n .offset-md-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 992px) {\n .col-lg {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-lg-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-lg-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-lg-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-lg-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-lg-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-lg-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-lg-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-lg-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-lg-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-lg-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-lg-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-lg-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-lg-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-lg-first {\n order: -1;\n }\n .order-lg-last {\n order: 13;\n }\n .order-lg-0 {\n order: 0;\n }\n .order-lg-1 {\n order: 1;\n }\n .order-lg-2 {\n order: 2;\n }\n .order-lg-3 {\n order: 3;\n }\n .order-lg-4 {\n order: 4;\n }\n .order-lg-5 {\n order: 5;\n }\n .order-lg-6 {\n order: 6;\n }\n .order-lg-7 {\n order: 7;\n }\n .order-lg-8 {\n order: 8;\n }\n .order-lg-9 {\n order: 9;\n }\n .order-lg-10 {\n order: 10;\n }\n .order-lg-11 {\n order: 11;\n }\n .order-lg-12 {\n order: 12;\n }\n .offset-lg-0 {\n margin-left: 0;\n }\n .offset-lg-1 {\n margin-left: 8.333333%;\n }\n .offset-lg-2 {\n margin-left: 16.666667%;\n }\n .offset-lg-3 {\n margin-left: 25%;\n }\n .offset-lg-4 {\n margin-left: 33.333333%;\n }\n .offset-lg-5 {\n margin-left: 41.666667%;\n }\n .offset-lg-6 {\n margin-left: 50%;\n }\n .offset-lg-7 {\n margin-left: 58.333333%;\n }\n .offset-lg-8 {\n margin-left: 66.666667%;\n }\n .offset-lg-9 {\n margin-left: 75%;\n }\n .offset-lg-10 {\n margin-left: 83.333333%;\n }\n .offset-lg-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 1200px) {\n .col-xl {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-xl-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-xl-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-xl-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-xl-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-xl-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-xl-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-xl-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-xl-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-xl-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-xl-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-xl-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-xl-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-xl-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-xl-first {\n order: -1;\n }\n .order-xl-last {\n order: 13;\n }\n .order-xl-0 {\n order: 0;\n }\n .order-xl-1 {\n order: 1;\n }\n .order-xl-2 {\n order: 2;\n }\n .order-xl-3 {\n order: 3;\n }\n .order-xl-4 {\n order: 4;\n }\n .order-xl-5 {\n order: 5;\n }\n .order-xl-6 {\n order: 6;\n }\n .order-xl-7 {\n order: 7;\n }\n .order-xl-8 {\n order: 8;\n }\n .order-xl-9 {\n order: 9;\n }\n .order-xl-10 {\n order: 10;\n }\n .order-xl-11 {\n order: 11;\n }\n .order-xl-12 {\n order: 12;\n }\n .offset-xl-0 {\n margin-left: 0;\n }\n .offset-xl-1 {\n margin-left: 8.333333%;\n }\n .offset-xl-2 {\n margin-left: 16.666667%;\n }\n .offset-xl-3 {\n margin-left: 25%;\n }\n .offset-xl-4 {\n margin-left: 33.333333%;\n }\n .offset-xl-5 {\n margin-left: 41.666667%;\n }\n .offset-xl-6 {\n margin-left: 50%;\n }\n .offset-xl-7 {\n margin-left: 58.333333%;\n }\n .offset-xl-8 {\n margin-left: 66.666667%;\n }\n .offset-xl-9 {\n margin-left: 75%;\n }\n .offset-xl-10 {\n margin-left: 83.333333%;\n }\n .offset-xl-11 {\n margin-left: 91.666667%;\n }\n}\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: 1rem;\n background-color: transparent;\n}\n\n.table th,\n.table td {\n padding: 0.75rem;\n vertical-align: top;\n border-top: 1px solid #dee2e6;\n}\n\n.table thead th {\n vertical-align: bottom;\n border-bottom: 2px solid #dee2e6;\n}\n\n.table tbody + tbody {\n border-top: 2px solid #dee2e6;\n}\n\n.table .table {\n background-color: #fff;\n}\n\n.table-sm th,\n.table-sm td {\n padding: 0.3rem;\n}\n\n.table-bordered {\n border: 1px solid #dee2e6;\n}\n\n.table-bordered th,\n.table-bordered td {\n border: 1px solid #dee2e6;\n}\n\n.table-bordered thead th,\n.table-bordered thead td {\n border-bottom-width: 2px;\n}\n\n.table-borderless th,\n.table-borderless td,\n.table-borderless thead th,\n.table-borderless tbody + tbody {\n border: 0;\n}\n\n.table-striped tbody tr:nth-of-type(odd) {\n background-color: rgba(0, 0, 0, 0.05);\n}\n\n.table-hover tbody tr:hover {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-primary,\n.table-primary > th,\n.table-primary > td {\n background-color: #b8daff;\n}\n\n.table-hover .table-primary:hover {\n background-color: #9fcdff;\n}\n\n.table-hover .table-primary:hover > td,\n.table-hover .table-primary:hover > th {\n background-color: #9fcdff;\n}\n\n.table-secondary,\n.table-secondary > th,\n.table-secondary > td {\n background-color: #d6d8db;\n}\n\n.table-hover .table-secondary:hover {\n background-color: #c8cbcf;\n}\n\n.table-hover .table-secondary:hover > td,\n.table-hover .table-secondary:hover > th {\n background-color: #c8cbcf;\n}\n\n.table-success,\n.table-success > th,\n.table-success > td {\n background-color: #c3e6cb;\n}\n\n.table-hover .table-success:hover {\n background-color: #b1dfbb;\n}\n\n.table-hover .table-success:hover > td,\n.table-hover .table-success:hover > th {\n background-color: #b1dfbb;\n}\n\n.table-info,\n.table-info > th,\n.table-info > td {\n background-color: #bee5eb;\n}\n\n.table-hover .table-info:hover {\n background-color: #abdde5;\n}\n\n.table-hover .table-info:hover > td,\n.table-hover .table-info:hover > th {\n background-color: #abdde5;\n}\n\n.table-warning,\n.table-warning > th,\n.table-warning > td {\n background-color: #ffeeba;\n}\n\n.table-hover .table-warning:hover {\n background-color: #ffe8a1;\n}\n\n.table-hover .table-warning:hover > td,\n.table-hover .table-warning:hover > th {\n background-color: #ffe8a1;\n}\n\n.table-danger,\n.table-danger > th,\n.table-danger > td {\n background-color: #f5c6cb;\n}\n\n.table-hover .table-danger:hover {\n background-color: #f1b0b7;\n}\n\n.table-hover .table-danger:hover > td,\n.table-hover .table-danger:hover > th {\n background-color: #f1b0b7;\n}\n\n.table-light,\n.table-light > th,\n.table-light > td {\n background-color: #fdfdfe;\n}\n\n.table-hover .table-light:hover {\n background-color: #ececf6;\n}\n\n.table-hover .table-light:hover > td,\n.table-hover .table-light:hover > th {\n background-color: #ececf6;\n}\n\n.table-dark,\n.table-dark > th,\n.table-dark > td {\n background-color: #c6c8ca;\n}\n\n.table-hover .table-dark:hover {\n background-color: #b9bbbe;\n}\n\n.table-hover .table-dark:hover > td,\n.table-hover .table-dark:hover > th {\n background-color: #b9bbbe;\n}\n\n.table-active,\n.table-active > th,\n.table-active > td {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover > td,\n.table-hover .table-active:hover > th {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table .thead-dark th {\n color: #fff;\n background-color: #212529;\n border-color: #32383e;\n}\n\n.table .thead-light th {\n color: #495057;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n\n.table-dark {\n color: #fff;\n background-color: #212529;\n}\n\n.table-dark th,\n.table-dark td,\n.table-dark thead th {\n border-color: #32383e;\n}\n\n.table-dark.table-bordered {\n border: 0;\n}\n\n.table-dark.table-striped tbody tr:nth-of-type(odd) {\n background-color: rgba(255, 255, 255, 0.05);\n}\n\n.table-dark.table-hover tbody tr:hover {\n background-color: rgba(255, 255, 255, 0.075);\n}\n\n@media (max-width: 575.98px) {\n .table-responsive-sm {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-sm > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 767.98px) {\n .table-responsive-md {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-md > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 991.98px) {\n .table-responsive-lg {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-lg > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 1199.98px) {\n .table-responsive-xl {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-xl > .table-bordered {\n border: 0;\n }\n}\n\n.table-responsive {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n}\n\n.table-responsive > .table-bordered {\n border: 0;\n}\n\n.form-control {\n display: block;\n width: 100%;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n line-height: 1.5;\n color: #495057;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .form-control {\n transition: none;\n }\n}\n\n.form-control::-ms-expand {\n background-color: transparent;\n border: 0;\n}\n\n.form-control:focus {\n color: #495057;\n background-color: #fff;\n border-color: #80bdff;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.form-control::placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control:disabled, .form-control[readonly] {\n background-color: #e9ecef;\n opacity: 1;\n}\n\nselect.form-control:not([size]):not([multiple]) {\n height: calc(2.25rem + 2px);\n}\n\nselect.form-control:focus::-ms-value {\n color: #495057;\n background-color: #fff;\n}\n\n.form-control-file,\n.form-control-range {\n display: block;\n width: 100%;\n}\n\n.col-form-label {\n padding-top: calc(0.375rem + 1px);\n padding-bottom: calc(0.375rem + 1px);\n margin-bottom: 0;\n font-size: inherit;\n line-height: 1.5;\n}\n\n.col-form-label-lg {\n padding-top: calc(0.5rem + 1px);\n padding-bottom: calc(0.5rem + 1px);\n font-size: 1.25rem;\n line-height: 1.5;\n}\n\n.col-form-label-sm {\n padding-top: calc(0.25rem + 1px);\n padding-bottom: calc(0.25rem + 1px);\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.form-control-plaintext {\n display: block;\n width: 100%;\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n margin-bottom: 0;\n line-height: 1.5;\n color: #212529;\n background-color: transparent;\n border: solid transparent;\n border-width: 1px 0;\n}\n\n.form-control-plaintext.form-control-sm, .input-group-sm > .form-control-plaintext.form-control,\n.input-group-sm > .input-group-prepend > .form-control-plaintext.input-group-text,\n.input-group-sm > .input-group-append > .form-control-plaintext.input-group-text,\n.input-group-sm > .input-group-prepend > .form-control-plaintext.btn,\n.input-group-sm > .input-group-append > .form-control-plaintext.btn, .form-control-plaintext.form-control-lg, .input-group-lg > .form-control-plaintext.form-control,\n.input-group-lg > .input-group-prepend > .form-control-plaintext.input-group-text,\n.input-group-lg > .input-group-append > .form-control-plaintext.input-group-text,\n.input-group-lg > .input-group-prepend > .form-control-plaintext.btn,\n.input-group-lg > .input-group-append > .form-control-plaintext.btn {\n padding-right: 0;\n padding-left: 0;\n}\n\n.form-control-sm, .input-group-sm > .form-control,\n.input-group-sm > .input-group-prepend > .input-group-text,\n.input-group-sm > .input-group-append > .input-group-text,\n.input-group-sm > .input-group-prepend > .btn,\n.input-group-sm > .input-group-append > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\nselect.form-control-sm:not([size]):not([multiple]), .input-group-sm > select.form-control:not([size]):not([multiple]),\n.input-group-sm > .input-group-prepend > select.input-group-text:not([size]):not([multiple]),\n.input-group-sm > .input-group-append > select.input-group-text:not([size]):not([multiple]),\n.input-group-sm > .input-group-prepend > select.btn:not([size]):not([multiple]),\n.input-group-sm > .input-group-append > select.btn:not([size]):not([multiple]) {\n height: calc(1.8125rem + 2px);\n}\n\n.form-control-lg, .input-group-lg > .form-control,\n.input-group-lg > .input-group-prepend > .input-group-text,\n.input-group-lg > .input-group-append > .input-group-text,\n.input-group-lg > .input-group-prepend > .btn,\n.input-group-lg > .input-group-append > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\nselect.form-control-lg:not([size]):not([multiple]), .input-group-lg > select.form-control:not([size]):not([multiple]),\n.input-group-lg > .input-group-prepend > select.input-group-text:not([size]):not([multiple]),\n.input-group-lg > .input-group-append > select.input-group-text:not([size]):not([multiple]),\n.input-group-lg > .input-group-prepend > select.btn:not([size]):not([multiple]),\n.input-group-lg > .input-group-append > select.btn:not([size]):not([multiple]) {\n height: calc(2.875rem + 2px);\n}\n\n.form-group {\n margin-bottom: 1rem;\n}\n\n.form-text {\n display: block;\n margin-top: 0.25rem;\n}\n\n.form-row {\n display: flex;\n flex-wrap: wrap;\n margin-right: -5px;\n margin-left: -5px;\n}\n\n.form-row > .col,\n.form-row > [class*=\"col-\"] {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.form-check {\n position: relative;\n display: block;\n padding-left: 1.25rem;\n}\n\n.form-check-input {\n position: absolute;\n margin-top: 0.3rem;\n margin-left: -1.25rem;\n}\n\n.form-check-input:disabled ~ .form-check-label {\n color: #6c757d;\n}\n\n.form-check-label {\n margin-bottom: 0;\n}\n\n.form-check-inline {\n display: inline-flex;\n align-items: center;\n padding-left: 0;\n margin-right: 0.75rem;\n}\n\n.form-check-inline .form-check-input {\n position: static;\n margin-top: 0;\n margin-right: 0.3125rem;\n margin-left: 0;\n}\n\n.valid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 80%;\n color: #28a745;\n}\n\n.valid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: .5rem;\n margin-top: .1rem;\n font-size: .875rem;\n line-height: 1;\n color: #fff;\n background-color: rgba(40, 167, 69, 0.8);\n border-radius: .2rem;\n}\n\n.was-validated .form-control:valid, .form-control.is-valid, .was-validated\n.custom-select:valid,\n.custom-select.is-valid {\n border-color: #28a745;\n}\n\n.was-validated .form-control:valid:focus, .form-control.is-valid:focus, .was-validated\n.custom-select:valid:focus,\n.custom-select.is-valid:focus {\n border-color: #28a745;\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .form-control:valid ~ .valid-feedback,\n.was-validated .form-control:valid ~ .valid-tooltip, .form-control.is-valid ~ .valid-feedback,\n.form-control.is-valid ~ .valid-tooltip, .was-validated\n.custom-select:valid ~ .valid-feedback,\n.was-validated\n.custom-select:valid ~ .valid-tooltip,\n.custom-select.is-valid ~ .valid-feedback,\n.custom-select.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .form-control-file:valid ~ .valid-feedback,\n.was-validated .form-control-file:valid ~ .valid-tooltip, .form-control-file.is-valid ~ .valid-feedback,\n.form-control-file.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {\n color: #28a745;\n}\n\n.was-validated .form-check-input:valid ~ .valid-feedback,\n.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback,\n.form-check-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label {\n color: #28a745;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before {\n background-color: #71dd8a;\n}\n\n.was-validated .custom-control-input:valid ~ .valid-feedback,\n.was-validated .custom-control-input:valid ~ .valid-tooltip, .custom-control-input.is-valid ~ .valid-feedback,\n.custom-control-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before {\n background-color: #34ce57;\n}\n\n.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label {\n border-color: #28a745;\n}\n\n.was-validated .custom-file-input:valid ~ .custom-file-label::before, .custom-file-input.is-valid ~ .custom-file-label::before {\n border-color: inherit;\n}\n\n.was-validated .custom-file-input:valid ~ .valid-feedback,\n.was-validated .custom-file-input:valid ~ .valid-tooltip, .custom-file-input.is-valid ~ .valid-feedback,\n.custom-file-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.invalid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 80%;\n color: #dc3545;\n}\n\n.invalid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: .5rem;\n margin-top: .1rem;\n font-size: .875rem;\n line-height: 1;\n color: #fff;\n background-color: rgba(220, 53, 69, 0.8);\n border-radius: .2rem;\n}\n\n.was-validated .form-control:invalid, .form-control.is-invalid, .was-validated\n.custom-select:invalid,\n.custom-select.is-invalid {\n border-color: #dc3545;\n}\n\n.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus, .was-validated\n.custom-select:invalid:focus,\n.custom-select.is-invalid:focus {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .form-control:invalid ~ .invalid-feedback,\n.was-validated .form-control:invalid ~ .invalid-tooltip, .form-control.is-invalid ~ .invalid-feedback,\n.form-control.is-invalid ~ .invalid-tooltip, .was-validated\n.custom-select:invalid ~ .invalid-feedback,\n.was-validated\n.custom-select:invalid ~ .invalid-tooltip,\n.custom-select.is-invalid ~ .invalid-feedback,\n.custom-select.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .form-control-file:invalid ~ .invalid-feedback,\n.was-validated .form-control-file:invalid ~ .invalid-tooltip, .form-control-file.is-invalid ~ .invalid-feedback,\n.form-control-file.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {\n color: #dc3545;\n}\n\n.was-validated .form-check-input:invalid ~ .invalid-feedback,\n.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback,\n.form-check-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label {\n color: #dc3545;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before {\n background-color: #efa2a9;\n}\n\n.was-validated .custom-control-input:invalid ~ .invalid-feedback,\n.was-validated .custom-control-input:invalid ~ .invalid-tooltip, .custom-control-input.is-invalid ~ .invalid-feedback,\n.custom-control-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before {\n background-color: #e4606d;\n}\n\n.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label {\n border-color: #dc3545;\n}\n\n.was-validated .custom-file-input:invalid ~ .custom-file-label::before, .custom-file-input.is-invalid ~ .custom-file-label::before {\n border-color: inherit;\n}\n\n.was-validated .custom-file-input:invalid ~ .invalid-feedback,\n.was-validated .custom-file-input:invalid ~ .invalid-tooltip, .custom-file-input.is-invalid ~ .invalid-feedback,\n.custom-file-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.form-inline {\n display: flex;\n flex-flow: row wrap;\n align-items: center;\n}\n\n.form-inline .form-check {\n width: 100%;\n}\n\n@media (min-width: 576px) {\n .form-inline label {\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 0;\n }\n .form-inline .form-group {\n display: flex;\n flex: 0 0 auto;\n flex-flow: row wrap;\n align-items: center;\n margin-bottom: 0;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-plaintext {\n display: inline-block;\n }\n .form-inline .input-group,\n .form-inline .custom-select {\n width: auto;\n }\n .form-inline .form-check {\n display: flex;\n align-items: center;\n justify-content: center;\n width: auto;\n padding-left: 0;\n }\n .form-inline .form-check-input {\n position: relative;\n margin-top: 0;\n margin-right: 0.25rem;\n margin-left: 0;\n }\n .form-inline .custom-control {\n align-items: center;\n justify-content: center;\n }\n .form-inline .custom-control-label {\n margin-bottom: 0;\n }\n}\n\n.btn {\n display: inline-block;\n font-weight: 400;\n text-align: center;\n white-space: nowrap;\n vertical-align: middle;\n user-select: none;\n border: 1px solid transparent;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n line-height: 1.5;\n border-radius: 0.25rem;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .btn {\n transition: none;\n }\n}\n\n.btn:hover, .btn:focus {\n text-decoration: none;\n}\n\n.btn:focus, .btn.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.btn.disabled, .btn:disabled {\n opacity: 0.65;\n}\n\n.btn:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.btn:not(:disabled):not(.disabled):active, .btn:not(:disabled):not(.disabled).active {\n background-image: none;\n}\n\na.btn.disabled,\nfieldset:disabled a.btn {\n pointer-events: none;\n}\n\n.btn-primary {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-primary:hover {\n color: #fff;\n background-color: #0069d9;\n border-color: #0062cc;\n}\n\n.btn-primary:focus, .btn-primary.focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-primary.disabled, .btn-primary:disabled {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active,\n.show > .btn-primary.dropdown-toggle {\n color: #fff;\n background-color: #0062cc;\n border-color: #005cbf;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-secondary {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-secondary:hover {\n color: #fff;\n background-color: #5a6268;\n border-color: #545b62;\n}\n\n.btn-secondary:focus, .btn-secondary.focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-secondary.disabled, .btn-secondary:disabled {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-secondary.dropdown-toggle {\n color: #fff;\n background-color: #545b62;\n border-color: #4e555b;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-success {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-success:hover {\n color: #fff;\n background-color: #218838;\n border-color: #1e7e34;\n}\n\n.btn-success:focus, .btn-success.focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-success.disabled, .btn-success:disabled {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active,\n.show > .btn-success.dropdown-toggle {\n color: #fff;\n background-color: #1e7e34;\n border-color: #1c7430;\n}\n\n.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-info {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-info:hover {\n color: #fff;\n background-color: #138496;\n border-color: #117a8b;\n}\n\n.btn-info:focus, .btn-info.focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-info.disabled, .btn-info:disabled {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active,\n.show > .btn-info.dropdown-toggle {\n color: #fff;\n background-color: #117a8b;\n border-color: #10707f;\n}\n\n.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-warning {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-warning:hover {\n color: #212529;\n background-color: #e0a800;\n border-color: #d39e00;\n}\n\n.btn-warning:focus, .btn-warning.focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-warning.disabled, .btn-warning:disabled {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active,\n.show > .btn-warning.dropdown-toggle {\n color: #212529;\n background-color: #d39e00;\n border-color: #c69500;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-danger {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-danger:hover {\n color: #fff;\n background-color: #c82333;\n border-color: #bd2130;\n}\n\n.btn-danger:focus, .btn-danger.focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-danger.disabled, .btn-danger:disabled {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active,\n.show > .btn-danger.dropdown-toggle {\n color: #fff;\n background-color: #bd2130;\n border-color: #b21f2d;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-light {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-light:hover {\n color: #212529;\n background-color: #e2e6ea;\n border-color: #dae0e5;\n}\n\n.btn-light:focus, .btn-light.focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-light.disabled, .btn-light:disabled {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active,\n.show > .btn-light.dropdown-toggle {\n color: #212529;\n background-color: #dae0e5;\n border-color: #d3d9df;\n}\n\n.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-dark {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-dark:hover {\n color: #fff;\n background-color: #23272b;\n border-color: #1d2124;\n}\n\n.btn-dark:focus, .btn-dark.focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-dark.disabled, .btn-dark:disabled {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active,\n.show > .btn-dark.dropdown-toggle {\n color: #fff;\n background-color: #1d2124;\n border-color: #171a1d;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-primary {\n color: #007bff;\n background-color: transparent;\n background-image: none;\n border-color: #007bff;\n}\n\n.btn-outline-primary:hover {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:focus, .btn-outline-primary.focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-primary.disabled, .btn-outline-primary:disabled {\n color: #007bff;\n background-color: transparent;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-primary.dropdown-toggle {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-secondary {\n color: #6c757d;\n background-color: transparent;\n background-image: none;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:hover {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:focus, .btn-outline-secondary.focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-secondary.disabled, .btn-outline-secondary:disabled {\n color: #6c757d;\n background-color: transparent;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-secondary.dropdown-toggle {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-success {\n color: #28a745;\n background-color: transparent;\n background-image: none;\n border-color: #28a745;\n}\n\n.btn-outline-success:hover {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:focus, .btn-outline-success.focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-success.disabled, .btn-outline-success:disabled {\n color: #28a745;\n background-color: transparent;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active,\n.show > .btn-outline-success.dropdown-toggle {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-info {\n color: #17a2b8;\n background-color: transparent;\n background-image: none;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:hover {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:focus, .btn-outline-info.focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-info.disabled, .btn-outline-info:disabled {\n color: #17a2b8;\n background-color: transparent;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active,\n.show > .btn-outline-info.dropdown-toggle {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-warning {\n color: #ffc107;\n background-color: transparent;\n background-image: none;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:hover {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:focus, .btn-outline-warning.focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-warning.disabled, .btn-outline-warning:disabled {\n color: #ffc107;\n background-color: transparent;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active,\n.show > .btn-outline-warning.dropdown-toggle {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-danger {\n color: #dc3545;\n background-color: transparent;\n background-image: none;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:hover {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:focus, .btn-outline-danger.focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-danger.disabled, .btn-outline-danger:disabled {\n color: #dc3545;\n background-color: transparent;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active,\n.show > .btn-outline-danger.dropdown-toggle {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-light {\n color: #f8f9fa;\n background-color: transparent;\n background-image: none;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:hover {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:focus, .btn-outline-light.focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-light.disabled, .btn-outline-light:disabled {\n color: #f8f9fa;\n background-color: transparent;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active,\n.show > .btn-outline-light.dropdown-toggle {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-dark {\n color: #343a40;\n background-color: transparent;\n background-image: none;\n border-color: #343a40;\n}\n\n.btn-outline-dark:hover {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:focus, .btn-outline-dark.focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-dark.disabled, .btn-outline-dark:disabled {\n color: #343a40;\n background-color: transparent;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active,\n.show > .btn-outline-dark.dropdown-toggle {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-link {\n font-weight: 400;\n color: #007bff;\n background-color: transparent;\n}\n\n.btn-link:hover {\n color: #0056b3;\n text-decoration: underline;\n background-color: transparent;\n border-color: transparent;\n}\n\n.btn-link:focus, .btn-link.focus {\n text-decoration: underline;\n border-color: transparent;\n box-shadow: none;\n}\n\n.btn-link:disabled, .btn-link.disabled {\n color: #6c757d;\n pointer-events: none;\n}\n\n.btn-lg, .btn-group-lg > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\n.btn-sm, .btn-group-sm > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\n.btn-block {\n display: block;\n width: 100%;\n}\n\n.btn-block + .btn-block {\n margin-top: 0.5rem;\n}\n\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n\n.fade {\n transition: opacity 0.15s linear;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .fade {\n transition: none;\n }\n}\n\n.fade:not(.show) {\n opacity: 0;\n}\n\n.collapse:not(.show) {\n display: none;\n}\n\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n transition: height 0.35s ease;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .collapsing {\n transition: none;\n }\n}\n\n.dropup,\n.dropright,\n.dropdown,\n.dropleft {\n position: relative;\n}\n\n.dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid;\n border-right: 0.3em solid transparent;\n border-bottom: 0;\n border-left: 0.3em solid transparent;\n}\n\n.dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 10rem;\n padding: 0.5rem 0;\n margin: 0.125rem 0 0;\n font-size: 1rem;\n color: #212529;\n text-align: left;\n list-style: none;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 0.25rem;\n}\n\n.dropdown-menu-right {\n right: 0;\n left: auto;\n}\n\n.dropup .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-top: 0;\n margin-bottom: 0.125rem;\n}\n\n.dropup .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0;\n border-right: 0.3em solid transparent;\n border-bottom: 0.3em solid;\n border-left: 0.3em solid transparent;\n}\n\n.dropup .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropright .dropdown-menu {\n top: 0;\n right: auto;\n left: 100%;\n margin-top: 0;\n margin-left: 0.125rem;\n}\n\n.dropright .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0;\n border-bottom: 0.3em solid transparent;\n border-left: 0.3em solid;\n}\n\n.dropright .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropright .dropdown-toggle::after {\n vertical-align: 0;\n}\n\n.dropleft .dropdown-menu {\n top: 0;\n right: 100%;\n left: auto;\n margin-top: 0;\n margin-right: 0.125rem;\n}\n\n.dropleft .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n}\n\n.dropleft .dropdown-toggle::after {\n display: none;\n}\n\n.dropleft .dropdown-toggle::before {\n display: inline-block;\n width: 0;\n height: 0;\n margin-right: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0.3em solid;\n border-bottom: 0.3em solid transparent;\n}\n\n.dropleft .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropleft .dropdown-toggle::before {\n vertical-align: 0;\n}\n\n.dropdown-menu[x-placement^=\"top\"], .dropdown-menu[x-placement^=\"right\"], .dropdown-menu[x-placement^=\"bottom\"], .dropdown-menu[x-placement^=\"left\"] {\n right: auto;\n bottom: auto;\n}\n\n.dropdown-divider {\n height: 0;\n margin: 0.5rem 0;\n overflow: hidden;\n border-top: 1px solid #e9ecef;\n}\n\n.dropdown-item {\n display: block;\n width: 100%;\n padding: 0.25rem 1.5rem;\n clear: both;\n font-weight: 400;\n color: #212529;\n text-align: inherit;\n white-space: nowrap;\n background-color: transparent;\n border: 0;\n}\n\n.dropdown-item:hover, .dropdown-item:focus {\n color: #16181b;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n\n.dropdown-item.active, .dropdown-item:active {\n color: #fff;\n text-decoration: none;\n background-color: #007bff;\n}\n\n.dropdown-item.disabled, .dropdown-item:disabled {\n color: #6c757d;\n background-color: transparent;\n}\n\n.dropdown-menu.show {\n display: block;\n}\n\n.dropdown-header {\n display: block;\n padding: 0.5rem 1.5rem;\n margin-bottom: 0;\n font-size: 0.875rem;\n color: #6c757d;\n white-space: nowrap;\n}\n\n.dropdown-item-text {\n display: block;\n padding: 0.25rem 1.5rem;\n color: #212529;\n}\n\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-flex;\n vertical-align: middle;\n}\n\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n flex: 0 1 auto;\n}\n\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover {\n z-index: 1;\n}\n\n.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active,\n.btn-group-vertical > .btn:focus,\n.btn-group-vertical > .btn:active,\n.btn-group-vertical > .btn.active {\n z-index: 1;\n}\n\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group,\n.btn-group-vertical .btn + .btn,\n.btn-group-vertical .btn + .btn-group,\n.btn-group-vertical .btn-group + .btn,\n.btn-group-vertical .btn-group + .btn-group {\n margin-left: -1px;\n}\n\n.btn-toolbar {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-start;\n}\n\n.btn-toolbar .input-group {\n width: auto;\n}\n\n.btn-group > .btn:first-child {\n margin-left: 0;\n}\n\n.btn-group > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group > .btn-group:not(:last-child) > .btn {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.btn-group > .btn:not(:first-child),\n.btn-group > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.dropdown-toggle-split {\n padding-right: 0.5625rem;\n padding-left: 0.5625rem;\n}\n\n.dropdown-toggle-split::after,\n.dropup .dropdown-toggle-split::after,\n.dropright .dropdown-toggle-split::after {\n margin-left: 0;\n}\n\n.dropleft .dropdown-toggle-split::before {\n margin-right: 0;\n}\n\n.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {\n padding-right: 0.375rem;\n padding-left: 0.375rem;\n}\n\n.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {\n padding-right: 0.75rem;\n padding-left: 0.75rem;\n}\n\n.btn-group-vertical {\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n\n.btn-group-vertical .btn,\n.btn-group-vertical .btn-group {\n width: 100%;\n}\n\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n}\n\n.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group-vertical > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.btn-group-toggle > .btn,\n.btn-group-toggle > .btn-group > .btn {\n margin-bottom: 0;\n}\n\n.btn-group-toggle > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn input[type=\"checkbox\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n\n.input-group {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-items: stretch;\n width: 100%;\n}\n\n.input-group > .form-control,\n.input-group > .custom-select,\n.input-group > .custom-file {\n position: relative;\n flex: 1 1 auto;\n width: 1%;\n margin-bottom: 0;\n}\n\n.input-group > .form-control:focus,\n.input-group > .custom-select:focus,\n.input-group > .custom-file:focus {\n z-index: 3;\n}\n\n.input-group > .form-control + .form-control,\n.input-group > .form-control + .custom-select,\n.input-group > .form-control + .custom-file,\n.input-group > .custom-select + .form-control,\n.input-group > .custom-select + .custom-select,\n.input-group > .custom-select + .custom-file,\n.input-group > .custom-file + .form-control,\n.input-group > .custom-file + .custom-select,\n.input-group > .custom-file + .custom-file {\n margin-left: -1px;\n}\n\n.input-group > .form-control:not(:last-child),\n.input-group > .custom-select:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .form-control:not(:first-child),\n.input-group > .custom-select:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.input-group > .custom-file {\n display: flex;\n align-items: center;\n}\n\n.input-group > .custom-file:not(:last-child) .custom-file-label,\n.input-group > .custom-file:not(:last-child) .custom-file-label::after {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .custom-file:not(:first-child) .custom-file-label {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.input-group-prepend,\n.input-group-append {\n display: flex;\n}\n\n.input-group-prepend .btn,\n.input-group-append .btn {\n position: relative;\n z-index: 2;\n}\n\n.input-group-prepend .btn + .btn,\n.input-group-prepend .btn + .input-group-text,\n.input-group-prepend .input-group-text + .input-group-text,\n.input-group-prepend .input-group-text + .btn,\n.input-group-append .btn + .btn,\n.input-group-append .btn + .input-group-text,\n.input-group-append .input-group-text + .input-group-text,\n.input-group-append .input-group-text + .btn {\n margin-left: -1px;\n}\n\n.input-group-prepend {\n margin-right: -1px;\n}\n\n.input-group-append {\n margin-left: -1px;\n}\n\n.input-group-text {\n display: flex;\n align-items: center;\n padding: 0.375rem 0.75rem;\n margin-bottom: 0;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #495057;\n text-align: center;\n white-space: nowrap;\n background-color: #e9ecef;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.input-group-text input[type=\"radio\"],\n.input-group-text input[type=\"checkbox\"] {\n margin-top: 0;\n}\n\n.input-group > .input-group-prepend > .btn,\n.input-group > .input-group-prepend > .input-group-text,\n.input-group > .input-group-append:not(:last-child) > .btn,\n.input-group > .input-group-append:not(:last-child) > .input-group-text,\n.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .input-group-append > .btn,\n.input-group > .input-group-append > .input-group-text,\n.input-group > .input-group-prepend:not(:first-child) > .btn,\n.input-group > .input-group-prepend:not(:first-child) > .input-group-text,\n.input-group > .input-group-prepend:first-child > .btn:not(:first-child),\n.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.custom-control {\n position: relative;\n display: block;\n min-height: 1.5rem;\n padding-left: 1.5rem;\n}\n\n.custom-control-inline {\n display: inline-flex;\n margin-right: 1rem;\n}\n\n.custom-control-input {\n position: absolute;\n z-index: -1;\n opacity: 0;\n}\n\n.custom-control-input:checked ~ .custom-control-label::before {\n color: #fff;\n background-color: #007bff;\n}\n\n.custom-control-input:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-control-input:active ~ .custom-control-label::before {\n color: #fff;\n background-color: #b3d7ff;\n}\n\n.custom-control-input:disabled ~ .custom-control-label {\n color: #6c757d;\n}\n\n.custom-control-input:disabled ~ .custom-control-label::before {\n background-color: #e9ecef;\n}\n\n.custom-control-label {\n position: relative;\n margin-bottom: 0;\n}\n\n.custom-control-label::before {\n position: absolute;\n top: 0.25rem;\n left: -1.5rem;\n display: block;\n width: 1rem;\n height: 1rem;\n pointer-events: none;\n content: \"\";\n user-select: none;\n background-color: #dee2e6;\n}\n\n.custom-control-label::after {\n position: absolute;\n top: 0.25rem;\n left: -1.5rem;\n display: block;\n width: 1rem;\n height: 1rem;\n content: \"\";\n background-repeat: no-repeat;\n background-position: center center;\n background-size: 50% 50%;\n}\n\n.custom-checkbox .custom-control-label::before {\n border-radius: 0.25rem;\n}\n\n.custom-checkbox .custom-control-input:checked ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E\");\n}\n\n.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-radio .custom-control-label::before {\n border-radius: 50%;\n}\n\n.custom-radio .custom-control-input:checked ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-radio .custom-control-input:checked ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E\");\n}\n\n.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-select {\n display: inline-block;\n width: 100%;\n height: calc(2.25rem + 2px);\n padding: 0.375rem 1.75rem 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n vertical-align: middle;\n background: #fff url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E\") no-repeat right 0.75rem center;\n background-size: 8px 10px;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n appearance: none;\n}\n\n.custom-select:focus {\n border-color: #80bdff;\n outline: 0;\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075), 0 0 5px rgba(128, 189, 255, 0.5);\n}\n\n.custom-select:focus::-ms-value {\n color: #495057;\n background-color: #fff;\n}\n\n.custom-select[multiple], .custom-select[size]:not([size=\"1\"]) {\n height: auto;\n padding-right: 0.75rem;\n background-image: none;\n}\n\n.custom-select:disabled {\n color: #6c757d;\n background-color: #e9ecef;\n}\n\n.custom-select::-ms-expand {\n opacity: 0;\n}\n\n.custom-select-sm {\n height: calc(1.8125rem + 2px);\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n font-size: 75%;\n}\n\n.custom-select-lg {\n height: calc(2.875rem + 2px);\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n font-size: 125%;\n}\n\n.custom-file {\n position: relative;\n display: inline-block;\n width: 100%;\n height: calc(2.25rem + 2px);\n margin-bottom: 0;\n}\n\n.custom-file-input {\n position: relative;\n z-index: 2;\n width: 100%;\n height: calc(2.25rem + 2px);\n margin: 0;\n opacity: 0;\n}\n\n.custom-file-input:focus ~ .custom-file-label {\n border-color: #80bdff;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-file-input:focus ~ .custom-file-label::after {\n border-color: #80bdff;\n}\n\n.custom-file-input:lang(en) ~ .custom-file-label::after {\n content: \"Browse\";\n}\n\n.custom-file-label {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1;\n height: calc(2.25rem + 2px);\n padding: 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n background-color: #fff;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.custom-file-label::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n z-index: 3;\n display: block;\n height: 2.25rem;\n padding: 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n content: \"Browse\";\n background-color: #e9ecef;\n border-left: 1px solid #ced4da;\n border-radius: 0 0.25rem 0.25rem 0;\n}\n\n.custom-range {\n width: 100%;\n padding-left: 0;\n background-color: transparent;\n appearance: none;\n}\n\n.custom-range:focus {\n outline: none;\n}\n\n.custom-range::-moz-focus-outer {\n border: 0;\n}\n\n.custom-range::-webkit-slider-thumb {\n width: 1rem;\n height: 1rem;\n margin-top: -0.25rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n appearance: none;\n}\n\n.custom-range::-webkit-slider-thumb:focus {\n outline: none;\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-webkit-slider-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-webkit-slider-runnable-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: #dee2e6;\n border-color: transparent;\n border-radius: 1rem;\n}\n\n.custom-range::-moz-range-thumb {\n width: 1rem;\n height: 1rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n appearance: none;\n}\n\n.custom-range::-moz-range-thumb:focus {\n outline: none;\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-moz-range-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-moz-range-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: #dee2e6;\n border-color: transparent;\n border-radius: 1rem;\n}\n\n.custom-range::-ms-thumb {\n width: 1rem;\n height: 1rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n appearance: none;\n}\n\n.custom-range::-ms-thumb:focus {\n outline: none;\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-ms-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-ms-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: transparent;\n border-color: transparent;\n border-width: 0.5rem;\n}\n\n.custom-range::-ms-fill-lower {\n background-color: #dee2e6;\n border-radius: 1rem;\n}\n\n.custom-range::-ms-fill-upper {\n margin-right: 15px;\n background-color: #dee2e6;\n border-radius: 1rem;\n}\n\n.nav {\n display: flex;\n flex-wrap: wrap;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.nav-link {\n display: block;\n padding: 0.5rem 1rem;\n}\n\n.nav-link:hover, .nav-link:focus {\n text-decoration: none;\n}\n\n.nav-link.disabled {\n color: #6c757d;\n}\n\n.nav-tabs {\n border-bottom: 1px solid #dee2e6;\n}\n\n.nav-tabs .nav-item {\n margin-bottom: -1px;\n}\n\n.nav-tabs .nav-link {\n border: 1px solid transparent;\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {\n border-color: #e9ecef #e9ecef #dee2e6;\n}\n\n.nav-tabs .nav-link.disabled {\n color: #6c757d;\n background-color: transparent;\n border-color: transparent;\n}\n\n.nav-tabs .nav-link.active,\n.nav-tabs .nav-item.show .nav-link {\n color: #495057;\n background-color: #fff;\n border-color: #dee2e6 #dee2e6 #fff;\n}\n\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.nav-pills .nav-link {\n border-radius: 0.25rem;\n}\n\n.nav-pills .nav-link.active,\n.nav-pills .show > .nav-link {\n color: #fff;\n background-color: #007bff;\n}\n\n.nav-fill .nav-item {\n flex: 1 1 auto;\n text-align: center;\n}\n\n.nav-justified .nav-item {\n flex-basis: 0;\n flex-grow: 1;\n text-align: center;\n}\n\n.tab-content > .tab-pane {\n display: none;\n}\n\n.tab-content > .active {\n display: block;\n}\n\n.navbar {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n padding: 0.5rem 1rem;\n}\n\n.navbar > .container,\n.navbar > .container-fluid {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n}\n\n.navbar-brand {\n display: inline-block;\n padding-top: 0.3125rem;\n padding-bottom: 0.3125rem;\n margin-right: 1rem;\n font-size: 1.25rem;\n line-height: inherit;\n white-space: nowrap;\n}\n\n.navbar-brand:hover, .navbar-brand:focus {\n text-decoration: none;\n}\n\n.navbar-nav {\n display: flex;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.navbar-nav .nav-link {\n padding-right: 0;\n padding-left: 0;\n}\n\n.navbar-nav .dropdown-menu {\n position: static;\n float: none;\n}\n\n.navbar-text {\n display: inline-block;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n\n.navbar-collapse {\n flex-basis: 100%;\n flex-grow: 1;\n align-items: center;\n}\n\n.navbar-toggler {\n padding: 0.25rem 0.75rem;\n font-size: 1.25rem;\n line-height: 1;\n background-color: transparent;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.navbar-toggler:hover, .navbar-toggler:focus {\n text-decoration: none;\n}\n\n.navbar-toggler:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.navbar-toggler-icon {\n display: inline-block;\n width: 1.5em;\n height: 1.5em;\n vertical-align: middle;\n content: \"\";\n background: no-repeat center center;\n background-size: 100% 100%;\n}\n\n@media (max-width: 575.98px) {\n .navbar-expand-sm > .container,\n .navbar-expand-sm > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 576px) {\n .navbar-expand-sm {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-sm .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-sm .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-sm .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-sm > .container,\n .navbar-expand-sm > .container-fluid {\n flex-wrap: nowrap;\n }\n .navbar-expand-sm .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-sm .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 767.98px) {\n .navbar-expand-md > .container,\n .navbar-expand-md > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 768px) {\n .navbar-expand-md {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-md .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-md .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-md .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-md > .container,\n .navbar-expand-md > .container-fluid {\n flex-wrap: nowrap;\n }\n .navbar-expand-md .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-md .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 991.98px) {\n .navbar-expand-lg > .container,\n .navbar-expand-lg > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 992px) {\n .navbar-expand-lg {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-lg .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-lg .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-lg .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-lg > .container,\n .navbar-expand-lg > .container-fluid {\n flex-wrap: nowrap;\n }\n .navbar-expand-lg .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-lg .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 1199.98px) {\n .navbar-expand-xl > .container,\n .navbar-expand-xl > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 1200px) {\n .navbar-expand-xl {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-xl .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-xl .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-xl .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-xl > .container,\n .navbar-expand-xl > .container-fluid {\n flex-wrap: nowrap;\n }\n .navbar-expand-xl .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-xl .navbar-toggler {\n display: none;\n }\n}\n\n.navbar-expand {\n flex-flow: row nowrap;\n justify-content: flex-start;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n}\n\n.navbar-expand .navbar-nav {\n flex-direction: row;\n}\n\n.navbar-expand .navbar-nav .dropdown-menu {\n position: absolute;\n}\n\n.navbar-expand .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid {\n flex-wrap: nowrap;\n}\n\n.navbar-expand .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n}\n\n.navbar-expand .navbar-toggler {\n display: none;\n}\n\n.navbar-light .navbar-brand {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-nav .nav-link {\n color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus {\n color: rgba(0, 0, 0, 0.7);\n}\n\n.navbar-light .navbar-nav .nav-link.disabled {\n color: rgba(0, 0, 0, 0.3);\n}\n\n.navbar-light .navbar-nav .show > .nav-link,\n.navbar-light .navbar-nav .active > .nav-link,\n.navbar-light .navbar-nav .nav-link.show,\n.navbar-light .navbar-nav .nav-link.active {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-toggler {\n color: rgba(0, 0, 0, 0.5);\n border-color: rgba(0, 0, 0, 0.1);\n}\n\n.navbar-light .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\");\n}\n\n.navbar-light .navbar-text {\n color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-text a {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-dark .navbar-brand {\n color: #fff;\n}\n\n.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus {\n color: #fff;\n}\n\n.navbar-dark .navbar-nav .nav-link {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus {\n color: rgba(255, 255, 255, 0.75);\n}\n\n.navbar-dark .navbar-nav .nav-link.disabled {\n color: rgba(255, 255, 255, 0.25);\n}\n\n.navbar-dark .navbar-nav .show > .nav-link,\n.navbar-dark .navbar-nav .active > .nav-link,\n.navbar-dark .navbar-nav .nav-link.show,\n.navbar-dark .navbar-nav .nav-link.active {\n color: #fff;\n}\n\n.navbar-dark .navbar-toggler {\n color: rgba(255, 255, 255, 0.5);\n border-color: rgba(255, 255, 255, 0.1);\n}\n\n.navbar-dark .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\");\n}\n\n.navbar-dark .navbar-text {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.navbar-dark .navbar-text a {\n color: #fff;\n}\n\n.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus {\n color: #fff;\n}\n\n.card {\n position: relative;\n display: flex;\n flex-direction: column;\n min-width: 0;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: border-box;\n border: 1px solid rgba(0, 0, 0, 0.125);\n border-radius: 0.25rem;\n}\n\n.card > hr {\n margin-right: 0;\n margin-left: 0;\n}\n\n.card > .list-group:first-child .list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.card > .list-group:last-child .list-group-item:last-child {\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.card-body {\n flex: 1 1 auto;\n padding: 1.25rem;\n}\n\n.card-title {\n margin-bottom: 0.75rem;\n}\n\n.card-subtitle {\n margin-top: -0.375rem;\n margin-bottom: 0;\n}\n\n.card-text:last-child {\n margin-bottom: 0;\n}\n\n.card-link:hover {\n text-decoration: none;\n}\n\n.card-link + .card-link {\n margin-left: 1.25rem;\n}\n\n.card-header {\n padding: 0.75rem 1.25rem;\n margin-bottom: 0;\n background-color: rgba(0, 0, 0, 0.03);\n border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card-header:first-child {\n border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0;\n}\n\n.card-header + .list-group .list-group-item:first-child {\n border-top: 0;\n}\n\n.card-footer {\n padding: 0.75rem 1.25rem;\n background-color: rgba(0, 0, 0, 0.03);\n border-top: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card-footer:last-child {\n border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px);\n}\n\n.card-header-tabs {\n margin-right: -0.625rem;\n margin-bottom: -0.75rem;\n margin-left: -0.625rem;\n border-bottom: 0;\n}\n\n.card-header-pills {\n margin-right: -0.625rem;\n margin-left: -0.625rem;\n}\n\n.card-img-overlay {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n padding: 1.25rem;\n}\n\n.card-img {\n width: 100%;\n border-radius: calc(0.25rem - 1px);\n}\n\n.card-img-top {\n width: 100%;\n border-top-left-radius: calc(0.25rem - 1px);\n border-top-right-radius: calc(0.25rem - 1px);\n}\n\n.card-img-bottom {\n width: 100%;\n border-bottom-right-radius: calc(0.25rem - 1px);\n border-bottom-left-radius: calc(0.25rem - 1px);\n}\n\n.card-deck {\n display: flex;\n flex-direction: column;\n}\n\n.card-deck .card {\n margin-bottom: 15px;\n}\n\n@media (min-width: 576px) {\n .card-deck {\n flex-flow: row wrap;\n margin-right: -15px;\n margin-left: -15px;\n }\n .card-deck .card {\n display: flex;\n flex: 1 0 0%;\n flex-direction: column;\n margin-right: 15px;\n margin-bottom: 0;\n margin-left: 15px;\n }\n}\n\n.card-group {\n display: flex;\n flex-direction: column;\n}\n\n.card-group > .card {\n margin-bottom: 15px;\n}\n\n@media (min-width: 576px) {\n .card-group {\n flex-flow: row wrap;\n }\n .card-group > .card {\n flex: 1 0 0%;\n margin-bottom: 0;\n }\n .card-group > .card + .card {\n margin-left: 0;\n border-left: 0;\n }\n .card-group > .card:first-child {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n }\n .card-group > .card:first-child .card-img-top,\n .card-group > .card:first-child .card-header {\n border-top-right-radius: 0;\n }\n .card-group > .card:first-child .card-img-bottom,\n .card-group > .card:first-child .card-footer {\n border-bottom-right-radius: 0;\n }\n .card-group > .card:last-child {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n .card-group > .card:last-child .card-img-top,\n .card-group > .card:last-child .card-header {\n border-top-left-radius: 0;\n }\n .card-group > .card:last-child .card-img-bottom,\n .card-group > .card:last-child .card-footer {\n border-bottom-left-radius: 0;\n }\n .card-group > .card:only-child {\n border-radius: 0.25rem;\n }\n .card-group > .card:only-child .card-img-top,\n .card-group > .card:only-child .card-header {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n }\n .card-group > .card:only-child .card-img-bottom,\n .card-group > .card:only-child .card-footer {\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n }\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) {\n border-radius: 0;\n }\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-top,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-header,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-footer {\n border-radius: 0;\n }\n}\n\n.card-columns .card {\n margin-bottom: 0.75rem;\n}\n\n@media (min-width: 576px) {\n .card-columns {\n column-count: 3;\n column-gap: 1.25rem;\n orphans: 1;\n widows: 1;\n }\n .card-columns .card {\n display: inline-block;\n width: 100%;\n }\n}\n\n.accordion .card:not(:first-of-type):not(:last-of-type) {\n border-bottom: 0;\n border-radius: 0;\n}\n\n.accordion .card:not(:first-of-type) .card-header:first-child {\n border-radius: 0;\n}\n\n.accordion .card:first-of-type {\n border-bottom: 0;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.accordion .card:last-of-type {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.breadcrumb {\n display: flex;\n flex-wrap: wrap;\n padding: 0.75rem 1rem;\n margin-bottom: 1rem;\n list-style: none;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.breadcrumb-item + .breadcrumb-item {\n padding-left: 0.5rem;\n}\n\n.breadcrumb-item + .breadcrumb-item::before {\n display: inline-block;\n padding-right: 0.5rem;\n color: #6c757d;\n content: \"/\";\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n text-decoration: underline;\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n text-decoration: none;\n}\n\n.breadcrumb-item.active {\n color: #6c757d;\n}\n\n.pagination {\n display: flex;\n padding-left: 0;\n list-style: none;\n border-radius: 0.25rem;\n}\n\n.page-link {\n position: relative;\n display: block;\n padding: 0.5rem 0.75rem;\n margin-left: -1px;\n line-height: 1.25;\n color: #007bff;\n background-color: #fff;\n border: 1px solid #dee2e6;\n}\n\n.page-link:hover {\n z-index: 2;\n color: #0056b3;\n text-decoration: none;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n\n.page-link:focus {\n z-index: 2;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.page-link:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.page-item:first-child .page-link {\n margin-left: 0;\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.page-item:last-child .page-link {\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n}\n\n.page-item.active .page-link {\n z-index: 1;\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.page-item.disabled .page-link {\n color: #6c757d;\n pointer-events: none;\n cursor: auto;\n background-color: #fff;\n border-color: #dee2e6;\n}\n\n.pagination-lg .page-link {\n padding: 0.75rem 1.5rem;\n font-size: 1.25rem;\n line-height: 1.5;\n}\n\n.pagination-lg .page-item:first-child .page-link {\n border-top-left-radius: 0.3rem;\n border-bottom-left-radius: 0.3rem;\n}\n\n.pagination-lg .page-item:last-child .page-link {\n border-top-right-radius: 0.3rem;\n border-bottom-right-radius: 0.3rem;\n}\n\n.pagination-sm .page-link {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.pagination-sm .page-item:first-child .page-link {\n border-top-left-radius: 0.2rem;\n border-bottom-left-radius: 0.2rem;\n}\n\n.pagination-sm .page-item:last-child .page-link {\n border-top-right-radius: 0.2rem;\n border-bottom-right-radius: 0.2rem;\n}\n\n.badge {\n display: inline-block;\n padding: 0.25em 0.4em;\n font-size: 75%;\n font-weight: 700;\n line-height: 1;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: 0.25rem;\n}\n\n.badge:empty {\n display: none;\n}\n\n.btn .badge {\n position: relative;\n top: -1px;\n}\n\n.badge-pill {\n padding-right: 0.6em;\n padding-left: 0.6em;\n border-radius: 10rem;\n}\n\n.badge-primary {\n color: #fff;\n background-color: #007bff;\n}\n\n.badge-primary[href]:hover, .badge-primary[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #0062cc;\n}\n\n.badge-secondary {\n color: #fff;\n background-color: #6c757d;\n}\n\n.badge-secondary[href]:hover, .badge-secondary[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #545b62;\n}\n\n.badge-success {\n color: #fff;\n background-color: #28a745;\n}\n\n.badge-success[href]:hover, .badge-success[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #1e7e34;\n}\n\n.badge-info {\n color: #fff;\n background-color: #17a2b8;\n}\n\n.badge-info[href]:hover, .badge-info[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #117a8b;\n}\n\n.badge-warning {\n color: #212529;\n background-color: #ffc107;\n}\n\n.badge-warning[href]:hover, .badge-warning[href]:focus {\n color: #212529;\n text-decoration: none;\n background-color: #d39e00;\n}\n\n.badge-danger {\n color: #fff;\n background-color: #dc3545;\n}\n\n.badge-danger[href]:hover, .badge-danger[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #bd2130;\n}\n\n.badge-light {\n color: #212529;\n background-color: #f8f9fa;\n}\n\n.badge-light[href]:hover, .badge-light[href]:focus {\n color: #212529;\n text-decoration: none;\n background-color: #dae0e5;\n}\n\n.badge-dark {\n color: #fff;\n background-color: #343a40;\n}\n\n.badge-dark[href]:hover, .badge-dark[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #1d2124;\n}\n\n.jumbotron {\n padding: 2rem 1rem;\n margin-bottom: 2rem;\n background-color: #e9ecef;\n border-radius: 0.3rem;\n}\n\n@media (min-width: 576px) {\n .jumbotron {\n padding: 4rem 2rem;\n }\n}\n\n.jumbotron-fluid {\n padding-right: 0;\n padding-left: 0;\n border-radius: 0;\n}\n\n.alert {\n position: relative;\n padding: 0.75rem 1.25rem;\n margin-bottom: 1rem;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.alert-heading {\n color: inherit;\n}\n\n.alert-link {\n font-weight: 700;\n}\n\n.alert-dismissible {\n padding-right: 4rem;\n}\n\n.alert-dismissible .close {\n position: absolute;\n top: 0;\n right: 0;\n padding: 0.75rem 1.25rem;\n color: inherit;\n}\n\n.alert-primary {\n color: #004085;\n background-color: #cce5ff;\n border-color: #b8daff;\n}\n\n.alert-primary hr {\n border-top-color: #9fcdff;\n}\n\n.alert-primary .alert-link {\n color: #002752;\n}\n\n.alert-secondary {\n color: #383d41;\n background-color: #e2e3e5;\n border-color: #d6d8db;\n}\n\n.alert-secondary hr {\n border-top-color: #c8cbcf;\n}\n\n.alert-secondary .alert-link {\n color: #202326;\n}\n\n.alert-success {\n color: #155724;\n background-color: #d4edda;\n border-color: #c3e6cb;\n}\n\n.alert-success hr {\n border-top-color: #b1dfbb;\n}\n\n.alert-success .alert-link {\n color: #0b2e13;\n}\n\n.alert-info {\n color: #0c5460;\n background-color: #d1ecf1;\n border-color: #bee5eb;\n}\n\n.alert-info hr {\n border-top-color: #abdde5;\n}\n\n.alert-info .alert-link {\n color: #062c33;\n}\n\n.alert-warning {\n color: #856404;\n background-color: #fff3cd;\n border-color: #ffeeba;\n}\n\n.alert-warning hr {\n border-top-color: #ffe8a1;\n}\n\n.alert-warning .alert-link {\n color: #533f03;\n}\n\n.alert-danger {\n color: #721c24;\n background-color: #f8d7da;\n border-color: #f5c6cb;\n}\n\n.alert-danger hr {\n border-top-color: #f1b0b7;\n}\n\n.alert-danger .alert-link {\n color: #491217;\n}\n\n.alert-light {\n color: #818182;\n background-color: #fefefe;\n border-color: #fdfdfe;\n}\n\n.alert-light hr {\n border-top-color: #ececf6;\n}\n\n.alert-light .alert-link {\n color: #686868;\n}\n\n.alert-dark {\n color: #1b1e21;\n background-color: #d6d8d9;\n border-color: #c6c8ca;\n}\n\n.alert-dark hr {\n border-top-color: #b9bbbe;\n}\n\n.alert-dark .alert-link {\n color: #040505;\n}\n\n@keyframes progress-bar-stripes {\n from {\n background-position: 1rem 0;\n }\n to {\n background-position: 0 0;\n }\n}\n\n.progress {\n display: flex;\n height: 1rem;\n overflow: hidden;\n font-size: 0.75rem;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.progress-bar {\n display: flex;\n flex-direction: column;\n justify-content: center;\n color: #fff;\n text-align: center;\n white-space: nowrap;\n background-color: #007bff;\n transition: width 0.6s ease;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .progress-bar {\n transition: none;\n }\n}\n\n.progress-bar-striped {\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-size: 1rem 1rem;\n}\n\n.progress-bar-animated {\n animation: progress-bar-stripes 1s linear infinite;\n}\n\n.media {\n display: flex;\n align-items: flex-start;\n}\n\n.media-body {\n flex: 1;\n}\n\n.list-group {\n display: flex;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n}\n\n.list-group-item-action {\n width: 100%;\n color: #495057;\n text-align: inherit;\n}\n\n.list-group-item-action:hover, .list-group-item-action:focus {\n color: #495057;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n\n.list-group-item-action:active {\n color: #212529;\n background-color: #e9ecef;\n}\n\n.list-group-item {\n position: relative;\n display: block;\n padding: 0.75rem 1.25rem;\n margin-bottom: -1px;\n background-color: #fff;\n border: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.list-group-item:last-child {\n margin-bottom: 0;\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.list-group-item:hover, .list-group-item:focus {\n z-index: 1;\n text-decoration: none;\n}\n\n.list-group-item.disabled, .list-group-item:disabled {\n color: #6c757d;\n background-color: #fff;\n}\n\n.list-group-item.active {\n z-index: 2;\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.list-group-flush .list-group-item {\n border-right: 0;\n border-left: 0;\n border-radius: 0;\n}\n\n.list-group-flush:first-child .list-group-item:first-child {\n border-top: 0;\n}\n\n.list-group-flush:last-child .list-group-item:last-child {\n border-bottom: 0;\n}\n\n.list-group-item-primary {\n color: #004085;\n background-color: #b8daff;\n}\n\n.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus {\n color: #004085;\n background-color: #9fcdff;\n}\n\n.list-group-item-primary.list-group-item-action.active {\n color: #fff;\n background-color: #004085;\n border-color: #004085;\n}\n\n.list-group-item-secondary {\n color: #383d41;\n background-color: #d6d8db;\n}\n\n.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus {\n color: #383d41;\n background-color: #c8cbcf;\n}\n\n.list-group-item-secondary.list-group-item-action.active {\n color: #fff;\n background-color: #383d41;\n border-color: #383d41;\n}\n\n.list-group-item-success {\n color: #155724;\n background-color: #c3e6cb;\n}\n\n.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus {\n color: #155724;\n background-color: #b1dfbb;\n}\n\n.list-group-item-success.list-group-item-action.active {\n color: #fff;\n background-color: #155724;\n border-color: #155724;\n}\n\n.list-group-item-info {\n color: #0c5460;\n background-color: #bee5eb;\n}\n\n.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus {\n color: #0c5460;\n background-color: #abdde5;\n}\n\n.list-group-item-info.list-group-item-action.active {\n color: #fff;\n background-color: #0c5460;\n border-color: #0c5460;\n}\n\n.list-group-item-warning {\n color: #856404;\n background-color: #ffeeba;\n}\n\n.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus {\n color: #856404;\n background-color: #ffe8a1;\n}\n\n.list-group-item-warning.list-group-item-action.active {\n color: #fff;\n background-color: #856404;\n border-color: #856404;\n}\n\n.list-group-item-danger {\n color: #721c24;\n background-color: #f5c6cb;\n}\n\n.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus {\n color: #721c24;\n background-color: #f1b0b7;\n}\n\n.list-group-item-danger.list-group-item-action.active {\n color: #fff;\n background-color: #721c24;\n border-color: #721c24;\n}\n\n.list-group-item-light {\n color: #818182;\n background-color: #fdfdfe;\n}\n\n.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus {\n color: #818182;\n background-color: #ececf6;\n}\n\n.list-group-item-light.list-group-item-action.active {\n color: #fff;\n background-color: #818182;\n border-color: #818182;\n}\n\n.list-group-item-dark {\n color: #1b1e21;\n background-color: #c6c8ca;\n}\n\n.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus {\n color: #1b1e21;\n background-color: #b9bbbe;\n}\n\n.list-group-item-dark.list-group-item-action.active {\n color: #fff;\n background-color: #1b1e21;\n border-color: #1b1e21;\n}\n\n.close {\n float: right;\n font-size: 1.5rem;\n font-weight: 700;\n line-height: 1;\n color: #000;\n text-shadow: 0 1px 0 #fff;\n opacity: .5;\n}\n\n.close:hover, .close:focus {\n color: #000;\n text-decoration: none;\n opacity: .75;\n}\n\n.close:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\nbutton.close {\n padding: 0;\n background-color: transparent;\n border: 0;\n -webkit-appearance: none;\n}\n\n.modal-open {\n overflow: hidden;\n}\n\n.modal {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1050;\n display: none;\n overflow: hidden;\n outline: 0;\n}\n\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 0.5rem;\n pointer-events: none;\n}\n\n.modal.fade .modal-dialog {\n transition: transform 0.3s ease-out;\n transform: translate(0, -25%);\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .modal.fade .modal-dialog {\n transition: none;\n }\n}\n\n.modal.show .modal-dialog {\n transform: translate(0, 0);\n}\n\n.modal-dialog-centered {\n display: flex;\n align-items: center;\n min-height: calc(100% - (0.5rem * 2));\n}\n\n.modal-content {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n pointer-events: auto;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n outline: 0;\n}\n\n.modal-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1040;\n background-color: #000;\n}\n\n.modal-backdrop.fade {\n opacity: 0;\n}\n\n.modal-backdrop.show {\n opacity: 0.5;\n}\n\n.modal-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n padding: 1rem;\n border-bottom: 1px solid #e9ecef;\n border-top-left-radius: 0.3rem;\n border-top-right-radius: 0.3rem;\n}\n\n.modal-header .close {\n padding: 1rem;\n margin: -1rem -1rem -1rem auto;\n}\n\n.modal-title {\n margin-bottom: 0;\n line-height: 1.5;\n}\n\n.modal-body {\n position: relative;\n flex: 1 1 auto;\n padding: 1rem;\n}\n\n.modal-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n padding: 1rem;\n border-top: 1px solid #e9ecef;\n}\n\n.modal-footer > :not(:first-child) {\n margin-left: .25rem;\n}\n\n.modal-footer > :not(:last-child) {\n margin-right: .25rem;\n}\n\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n\n@media (min-width: 576px) {\n .modal-dialog {\n max-width: 500px;\n margin: 1.75rem auto;\n }\n .modal-dialog-centered {\n min-height: calc(100% - (1.75rem * 2));\n }\n .modal-sm {\n max-width: 300px;\n }\n}\n\n@media (min-width: 992px) {\n .modal-lg {\n max-width: 800px;\n }\n}\n\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n opacity: 0;\n}\n\n.tooltip.show {\n opacity: 0.9;\n}\n\n.tooltip .arrow {\n position: absolute;\n display: block;\n width: 0.8rem;\n height: 0.4rem;\n}\n\n.tooltip .arrow::before {\n position: absolute;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-tooltip-top, .bs-tooltip-auto[x-placement^=\"top\"] {\n padding: 0.4rem 0;\n}\n\n.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^=\"top\"] .arrow {\n bottom: 0;\n}\n\n.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^=\"top\"] .arrow::before {\n top: 0;\n border-width: 0.4rem 0.4rem 0;\n border-top-color: #000;\n}\n\n.bs-tooltip-right, .bs-tooltip-auto[x-placement^=\"right\"] {\n padding: 0 0.4rem;\n}\n\n.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^=\"right\"] .arrow {\n left: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n\n.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^=\"right\"] .arrow::before {\n right: 0;\n border-width: 0.4rem 0.4rem 0.4rem 0;\n border-right-color: #000;\n}\n\n.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^=\"bottom\"] {\n padding: 0.4rem 0;\n}\n\n.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow {\n top: 0;\n}\n\n.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow::before {\n bottom: 0;\n border-width: 0 0.4rem 0.4rem;\n border-bottom-color: #000;\n}\n\n.bs-tooltip-left, .bs-tooltip-auto[x-placement^=\"left\"] {\n padding: 0 0.4rem;\n}\n\n.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^=\"left\"] .arrow {\n right: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n\n.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^=\"left\"] .arrow::before {\n left: 0;\n border-width: 0.4rem 0 0.4rem 0.4rem;\n border-left-color: #000;\n}\n\n.tooltip-inner {\n max-width: 200px;\n padding: 0.25rem 0.5rem;\n color: #fff;\n text-align: center;\n background-color: #000;\n border-radius: 0.25rem;\n}\n\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: block;\n max-width: 276px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n}\n\n.popover .arrow {\n position: absolute;\n display: block;\n width: 1rem;\n height: 0.5rem;\n margin: 0 0.3rem;\n}\n\n.popover .arrow::before, .popover .arrow::after {\n position: absolute;\n display: block;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-popover-top, .bs-popover-auto[x-placement^=\"top\"] {\n margin-bottom: 0.5rem;\n}\n\n.bs-popover-top .arrow, .bs-popover-auto[x-placement^=\"top\"] .arrow {\n bottom: calc((0.5rem + 1px) * -1);\n}\n\n.bs-popover-top .arrow::before, .bs-popover-auto[x-placement^=\"top\"] .arrow::before,\n.bs-popover-top .arrow::after,\n.bs-popover-auto[x-placement^=\"top\"] .arrow::after {\n border-width: 0.5rem 0.5rem 0;\n}\n\n.bs-popover-top .arrow::before, .bs-popover-auto[x-placement^=\"top\"] .arrow::before {\n bottom: 0;\n border-top-color: rgba(0, 0, 0, 0.25);\n}\n\n\n.bs-popover-top .arrow::after,\n.bs-popover-auto[x-placement^=\"top\"] .arrow::after {\n bottom: 1px;\n border-top-color: #fff;\n}\n\n.bs-popover-right, .bs-popover-auto[x-placement^=\"right\"] {\n margin-left: 0.5rem;\n}\n\n.bs-popover-right .arrow, .bs-popover-auto[x-placement^=\"right\"] .arrow {\n left: calc((0.5rem + 1px) * -1);\n width: 0.5rem;\n height: 1rem;\n margin: 0.3rem 0;\n}\n\n.bs-popover-right .arrow::before, .bs-popover-auto[x-placement^=\"right\"] .arrow::before,\n.bs-popover-right .arrow::after,\n.bs-popover-auto[x-placement^=\"right\"] .arrow::after {\n border-width: 0.5rem 0.5rem 0.5rem 0;\n}\n\n.bs-popover-right .arrow::before, .bs-popover-auto[x-placement^=\"right\"] .arrow::before {\n left: 0;\n border-right-color: rgba(0, 0, 0, 0.25);\n}\n\n\n.bs-popover-right .arrow::after,\n.bs-popover-auto[x-placement^=\"right\"] .arrow::after {\n left: 1px;\n border-right-color: #fff;\n}\n\n.bs-popover-bottom, .bs-popover-auto[x-placement^=\"bottom\"] {\n margin-top: 0.5rem;\n}\n\n.bs-popover-bottom .arrow, .bs-popover-auto[x-placement^=\"bottom\"] .arrow {\n top: calc((0.5rem + 1px) * -1);\n}\n\n.bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::before,\n.bs-popover-bottom .arrow::after,\n.bs-popover-auto[x-placement^=\"bottom\"] .arrow::after {\n border-width: 0 0.5rem 0.5rem 0.5rem;\n}\n\n.bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::before {\n top: 0;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n}\n\n\n.bs-popover-bottom .arrow::after,\n.bs-popover-auto[x-placement^=\"bottom\"] .arrow::after {\n top: 1px;\n border-bottom-color: #fff;\n}\n\n.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^=\"bottom\"] .popover-header::before {\n position: absolute;\n top: 0;\n left: 50%;\n display: block;\n width: 1rem;\n margin-left: -0.5rem;\n content: \"\";\n border-bottom: 1px solid #f7f7f7;\n}\n\n.bs-popover-left, .bs-popover-auto[x-placement^=\"left\"] {\n margin-right: 0.5rem;\n}\n\n.bs-popover-left .arrow, .bs-popover-auto[x-placement^=\"left\"] .arrow {\n right: calc((0.5rem + 1px) * -1);\n width: 0.5rem;\n height: 1rem;\n margin: 0.3rem 0;\n}\n\n.bs-popover-left .arrow::before, .bs-popover-auto[x-placement^=\"left\"] .arrow::before,\n.bs-popover-left .arrow::after,\n.bs-popover-auto[x-placement^=\"left\"] .arrow::after {\n border-width: 0.5rem 0 0.5rem 0.5rem;\n}\n\n.bs-popover-left .arrow::before, .bs-popover-auto[x-placement^=\"left\"] .arrow::before {\n right: 0;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n\n\n.bs-popover-left .arrow::after,\n.bs-popover-auto[x-placement^=\"left\"] .arrow::after {\n right: 1px;\n border-left-color: #fff;\n}\n\n.popover-header {\n padding: 0.5rem 0.75rem;\n margin-bottom: 0;\n font-size: 1rem;\n color: inherit;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-top-left-radius: calc(0.3rem - 1px);\n border-top-right-radius: calc(0.3rem - 1px);\n}\n\n.popover-header:empty {\n display: none;\n}\n\n.popover-body {\n padding: 0.5rem 0.75rem;\n color: #212529;\n}\n\n.carousel {\n position: relative;\n}\n\n.carousel-inner {\n position: relative;\n width: 100%;\n overflow: hidden;\n}\n\n.carousel-item {\n position: relative;\n display: none;\n align-items: center;\n width: 100%;\n transition: transform 0.6s ease;\n backface-visibility: hidden;\n perspective: 1000px;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .carousel-item {\n transition: none;\n }\n}\n\n.carousel-item.active,\n.carousel-item-next,\n.carousel-item-prev {\n display: block;\n}\n\n.carousel-item-next,\n.carousel-item-prev {\n position: absolute;\n top: 0;\n}\n\n.carousel-item-next.carousel-item-left,\n.carousel-item-prev.carousel-item-right {\n transform: translateX(0);\n}\n\n@supports (transform-style: preserve-3d) {\n .carousel-item-next.carousel-item-left,\n .carousel-item-prev.carousel-item-right {\n transform: translate3d(0, 0, 0);\n }\n}\n\n.carousel-item-next,\n.active.carousel-item-right {\n transform: translateX(100%);\n}\n\n@supports (transform-style: preserve-3d) {\n .carousel-item-next,\n .active.carousel-item-right {\n transform: translate3d(100%, 0, 0);\n }\n}\n\n.carousel-item-prev,\n.active.carousel-item-left {\n transform: translateX(-100%);\n}\n\n@supports (transform-style: preserve-3d) {\n .carousel-item-prev,\n .active.carousel-item-left {\n transform: translate3d(-100%, 0, 0);\n }\n}\n\n.carousel-fade .carousel-item {\n opacity: 0;\n transition-duration: .6s;\n transition-property: opacity;\n}\n\n.carousel-fade .carousel-item.active,\n.carousel-fade .carousel-item-next.carousel-item-left,\n.carousel-fade .carousel-item-prev.carousel-item-right {\n opacity: 1;\n}\n\n.carousel-fade .active.carousel-item-left,\n.carousel-fade .active.carousel-item-right {\n opacity: 0;\n}\n\n.carousel-fade .carousel-item-next,\n.carousel-fade .carousel-item-prev,\n.carousel-fade .carousel-item.active,\n.carousel-fade .active.carousel-item-left,\n.carousel-fade .active.carousel-item-prev {\n transform: translateX(0);\n}\n\n@supports (transform-style: preserve-3d) {\n .carousel-fade .carousel-item-next,\n .carousel-fade .carousel-item-prev,\n .carousel-fade .carousel-item.active,\n .carousel-fade .active.carousel-item-left,\n .carousel-fade .active.carousel-item-prev {\n transform: translate3d(0, 0, 0);\n }\n}\n\n.carousel-control-prev,\n.carousel-control-next {\n position: absolute;\n top: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 15%;\n color: #fff;\n text-align: center;\n opacity: 0.5;\n}\n\n.carousel-control-prev:hover, .carousel-control-prev:focus,\n.carousel-control-next:hover,\n.carousel-control-next:focus {\n color: #fff;\n text-decoration: none;\n outline: 0;\n opacity: .9;\n}\n\n.carousel-control-prev {\n left: 0;\n}\n\n.carousel-control-next {\n right: 0;\n}\n\n.carousel-control-prev-icon,\n.carousel-control-next-icon {\n display: inline-block;\n width: 20px;\n height: 20px;\n background: transparent no-repeat center center;\n background-size: 100% 100%;\n}\n\n.carousel-control-prev-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E\");\n}\n\n.carousel-control-next-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E\");\n}\n\n.carousel-indicators {\n position: absolute;\n right: 0;\n bottom: 10px;\n left: 0;\n z-index: 15;\n display: flex;\n justify-content: center;\n padding-left: 0;\n margin-right: 15%;\n margin-left: 15%;\n list-style: none;\n}\n\n.carousel-indicators li {\n position: relative;\n flex: 0 1 auto;\n width: 30px;\n height: 3px;\n margin-right: 3px;\n margin-left: 3px;\n text-indent: -999px;\n cursor: pointer;\n background-color: rgba(255, 255, 255, 0.5);\n}\n\n.carousel-indicators li::before {\n position: absolute;\n top: -10px;\n left: 0;\n display: inline-block;\n width: 100%;\n height: 10px;\n content: \"\";\n}\n\n.carousel-indicators li::after {\n position: absolute;\n bottom: -10px;\n left: 0;\n display: inline-block;\n width: 100%;\n height: 10px;\n content: \"\";\n}\n\n.carousel-indicators .active {\n background-color: #fff;\n}\n\n.carousel-caption {\n position: absolute;\n right: 15%;\n bottom: 20px;\n left: 15%;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #fff;\n text-align: center;\n}\n\n.align-baseline {\n vertical-align: baseline !important;\n}\n\n.align-top {\n vertical-align: top !important;\n}\n\n.align-middle {\n vertical-align: middle !important;\n}\n\n.align-bottom {\n vertical-align: bottom !important;\n}\n\n.align-text-bottom {\n vertical-align: text-bottom !important;\n}\n\n.align-text-top {\n vertical-align: text-top !important;\n}\n\n.bg-primary {\n background-color: #007bff !important;\n}\n\na.bg-primary:hover, a.bg-primary:focus,\nbutton.bg-primary:hover,\nbutton.bg-primary:focus {\n background-color: #0062cc !important;\n}\n\n.bg-secondary {\n background-color: #6c757d !important;\n}\n\na.bg-secondary:hover, a.bg-secondary:focus,\nbutton.bg-secondary:hover,\nbutton.bg-secondary:focus {\n background-color: #545b62 !important;\n}\n\n.bg-success {\n background-color: #28a745 !important;\n}\n\na.bg-success:hover, a.bg-success:focus,\nbutton.bg-success:hover,\nbutton.bg-success:focus {\n background-color: #1e7e34 !important;\n}\n\n.bg-info {\n background-color: #17a2b8 !important;\n}\n\na.bg-info:hover, a.bg-info:focus,\nbutton.bg-info:hover,\nbutton.bg-info:focus {\n background-color: #117a8b !important;\n}\n\n.bg-warning {\n background-color: #ffc107 !important;\n}\n\na.bg-warning:hover, a.bg-warning:focus,\nbutton.bg-warning:hover,\nbutton.bg-warning:focus {\n background-color: #d39e00 !important;\n}\n\n.bg-danger {\n background-color: #dc3545 !important;\n}\n\na.bg-danger:hover, a.bg-danger:focus,\nbutton.bg-danger:hover,\nbutton.bg-danger:focus {\n background-color: #bd2130 !important;\n}\n\n.bg-light {\n background-color: #f8f9fa !important;\n}\n\na.bg-light:hover, a.bg-light:focus,\nbutton.bg-light:hover,\nbutton.bg-light:focus {\n background-color: #dae0e5 !important;\n}\n\n.bg-dark {\n background-color: #343a40 !important;\n}\n\na.bg-dark:hover, a.bg-dark:focus,\nbutton.bg-dark:hover,\nbutton.bg-dark:focus {\n background-color: #1d2124 !important;\n}\n\n.bg-white {\n background-color: #fff !important;\n}\n\n.bg-transparent {\n background-color: transparent !important;\n}\n\n.border {\n border: 1px solid #dee2e6 !important;\n}\n\n.border-top {\n border-top: 1px solid #dee2e6 !important;\n}\n\n.border-right {\n border-right: 1px solid #dee2e6 !important;\n}\n\n.border-bottom {\n border-bottom: 1px solid #dee2e6 !important;\n}\n\n.border-left {\n border-left: 1px solid #dee2e6 !important;\n}\n\n.border-0 {\n border: 0 !important;\n}\n\n.border-top-0 {\n border-top: 0 !important;\n}\n\n.border-right-0 {\n border-right: 0 !important;\n}\n\n.border-bottom-0 {\n border-bottom: 0 !important;\n}\n\n.border-left-0 {\n border-left: 0 !important;\n}\n\n.border-primary {\n border-color: #007bff !important;\n}\n\n.border-secondary {\n border-color: #6c757d !important;\n}\n\n.border-success {\n border-color: #28a745 !important;\n}\n\n.border-info {\n border-color: #17a2b8 !important;\n}\n\n.border-warning {\n border-color: #ffc107 !important;\n}\n\n.border-danger {\n border-color: #dc3545 !important;\n}\n\n.border-light {\n border-color: #f8f9fa !important;\n}\n\n.border-dark {\n border-color: #343a40 !important;\n}\n\n.border-white {\n border-color: #fff !important;\n}\n\n.rounded {\n border-radius: 0.25rem !important;\n}\n\n.rounded-top {\n border-top-left-radius: 0.25rem !important;\n border-top-right-radius: 0.25rem !important;\n}\n\n.rounded-right {\n border-top-right-radius: 0.25rem !important;\n border-bottom-right-radius: 0.25rem !important;\n}\n\n.rounded-bottom {\n border-bottom-right-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-left {\n border-top-left-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-circle {\n border-radius: 50% !important;\n}\n\n.rounded-0 {\n border-radius: 0 !important;\n}\n\n.clearfix::after {\n display: block;\n clear: both;\n content: \"\";\n}\n\n.d-none {\n display: none !important;\n}\n\n.d-inline {\n display: inline !important;\n}\n\n.d-inline-block {\n display: inline-block !important;\n}\n\n.d-block {\n display: block !important;\n}\n\n.d-table {\n display: table !important;\n}\n\n.d-table-row {\n display: table-row !important;\n}\n\n.d-table-cell {\n display: table-cell !important;\n}\n\n.d-flex {\n display: flex !important;\n}\n\n.d-inline-flex {\n display: inline-flex !important;\n}\n\n@media (min-width: 576px) {\n .d-sm-none {\n display: none !important;\n }\n .d-sm-inline {\n display: inline !important;\n }\n .d-sm-inline-block {\n display: inline-block !important;\n }\n .d-sm-block {\n display: block !important;\n }\n .d-sm-table {\n display: table !important;\n }\n .d-sm-table-row {\n display: table-row !important;\n }\n .d-sm-table-cell {\n display: table-cell !important;\n }\n .d-sm-flex {\n display: flex !important;\n }\n .d-sm-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 768px) {\n .d-md-none {\n display: none !important;\n }\n .d-md-inline {\n display: inline !important;\n }\n .d-md-inline-block {\n display: inline-block !important;\n }\n .d-md-block {\n display: block !important;\n }\n .d-md-table {\n display: table !important;\n }\n .d-md-table-row {\n display: table-row !important;\n }\n .d-md-table-cell {\n display: table-cell !important;\n }\n .d-md-flex {\n display: flex !important;\n }\n .d-md-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 992px) {\n .d-lg-none {\n display: none !important;\n }\n .d-lg-inline {\n display: inline !important;\n }\n .d-lg-inline-block {\n display: inline-block !important;\n }\n .d-lg-block {\n display: block !important;\n }\n .d-lg-table {\n display: table !important;\n }\n .d-lg-table-row {\n display: table-row !important;\n }\n .d-lg-table-cell {\n display: table-cell !important;\n }\n .d-lg-flex {\n display: flex !important;\n }\n .d-lg-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 1200px) {\n .d-xl-none {\n display: none !important;\n }\n .d-xl-inline {\n display: inline !important;\n }\n .d-xl-inline-block {\n display: inline-block !important;\n }\n .d-xl-block {\n display: block !important;\n }\n .d-xl-table {\n display: table !important;\n }\n .d-xl-table-row {\n display: table-row !important;\n }\n .d-xl-table-cell {\n display: table-cell !important;\n }\n .d-xl-flex {\n display: flex !important;\n }\n .d-xl-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media print {\n .d-print-none {\n display: none !important;\n }\n .d-print-inline {\n display: inline !important;\n }\n .d-print-inline-block {\n display: inline-block !important;\n }\n .d-print-block {\n display: block !important;\n }\n .d-print-table {\n display: table !important;\n }\n .d-print-table-row {\n display: table-row !important;\n }\n .d-print-table-cell {\n display: table-cell !important;\n }\n .d-print-flex {\n display: flex !important;\n }\n .d-print-inline-flex {\n display: inline-flex !important;\n }\n}\n\n.embed-responsive {\n position: relative;\n display: block;\n width: 100%;\n padding: 0;\n overflow: hidden;\n}\n\n.embed-responsive::before {\n display: block;\n content: \"\";\n}\n\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n height: 100%;\n border: 0;\n}\n\n.embed-responsive-21by9::before {\n padding-top: 42.857143%;\n}\n\n.embed-responsive-16by9::before {\n padding-top: 56.25%;\n}\n\n.embed-responsive-4by3::before {\n padding-top: 75%;\n}\n\n.embed-responsive-1by1::before {\n padding-top: 100%;\n}\n\n.flex-row {\n flex-direction: row !important;\n}\n\n.flex-column {\n flex-direction: column !important;\n}\n\n.flex-row-reverse {\n flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n flex-direction: column-reverse !important;\n}\n\n.flex-wrap {\n flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n}\n\n.flex-fill {\n flex: 1 1 auto !important;\n}\n\n.flex-grow-0 {\n flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n flex-shrink: 1 !important;\n}\n\n.justify-content-start {\n justify-content: flex-start !important;\n}\n\n.justify-content-end {\n justify-content: flex-end !important;\n}\n\n.justify-content-center {\n justify-content: center !important;\n}\n\n.justify-content-between {\n justify-content: space-between !important;\n}\n\n.justify-content-around {\n justify-content: space-around !important;\n}\n\n.align-items-start {\n align-items: flex-start !important;\n}\n\n.align-items-end {\n align-items: flex-end !important;\n}\n\n.align-items-center {\n align-items: center !important;\n}\n\n.align-items-baseline {\n align-items: baseline !important;\n}\n\n.align-items-stretch {\n align-items: stretch !important;\n}\n\n.align-content-start {\n align-content: flex-start !important;\n}\n\n.align-content-end {\n align-content: flex-end !important;\n}\n\n.align-content-center {\n align-content: center !important;\n}\n\n.align-content-between {\n align-content: space-between !important;\n}\n\n.align-content-around {\n align-content: space-around !important;\n}\n\n.align-content-stretch {\n align-content: stretch !important;\n}\n\n.align-self-auto {\n align-self: auto !important;\n}\n\n.align-self-start {\n align-self: flex-start !important;\n}\n\n.align-self-end {\n align-self: flex-end !important;\n}\n\n.align-self-center {\n align-self: center !important;\n}\n\n.align-self-baseline {\n align-self: baseline !important;\n}\n\n.align-self-stretch {\n align-self: stretch !important;\n}\n\n@media (min-width: 576px) {\n .flex-sm-row {\n flex-direction: row !important;\n }\n .flex-sm-column {\n flex-direction: column !important;\n }\n .flex-sm-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-sm-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-sm-wrap {\n flex-wrap: wrap !important;\n }\n .flex-sm-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-sm-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .flex-sm-fill {\n flex: 1 1 auto !important;\n }\n .flex-sm-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-sm-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-sm-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-sm-shrink-1 {\n flex-shrink: 1 !important;\n }\n .justify-content-sm-start {\n justify-content: flex-start !important;\n }\n .justify-content-sm-end {\n justify-content: flex-end !important;\n }\n .justify-content-sm-center {\n justify-content: center !important;\n }\n .justify-content-sm-between {\n justify-content: space-between !important;\n }\n .justify-content-sm-around {\n justify-content: space-around !important;\n }\n .align-items-sm-start {\n align-items: flex-start !important;\n }\n .align-items-sm-end {\n align-items: flex-end !important;\n }\n .align-items-sm-center {\n align-items: center !important;\n }\n .align-items-sm-baseline {\n align-items: baseline !important;\n }\n .align-items-sm-stretch {\n align-items: stretch !important;\n }\n .align-content-sm-start {\n align-content: flex-start !important;\n }\n .align-content-sm-end {\n align-content: flex-end !important;\n }\n .align-content-sm-center {\n align-content: center !important;\n }\n .align-content-sm-between {\n align-content: space-between !important;\n }\n .align-content-sm-around {\n align-content: space-around !important;\n }\n .align-content-sm-stretch {\n align-content: stretch !important;\n }\n .align-self-sm-auto {\n align-self: auto !important;\n }\n .align-self-sm-start {\n align-self: flex-start !important;\n }\n .align-self-sm-end {\n align-self: flex-end !important;\n }\n .align-self-sm-center {\n align-self: center !important;\n }\n .align-self-sm-baseline {\n align-self: baseline !important;\n }\n .align-self-sm-stretch {\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 768px) {\n .flex-md-row {\n flex-direction: row !important;\n }\n .flex-md-column {\n flex-direction: column !important;\n }\n .flex-md-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-md-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-md-wrap {\n flex-wrap: wrap !important;\n }\n .flex-md-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-md-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .flex-md-fill {\n flex: 1 1 auto !important;\n }\n .flex-md-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-md-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-md-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-md-shrink-1 {\n flex-shrink: 1 !important;\n }\n .justify-content-md-start {\n justify-content: flex-start !important;\n }\n .justify-content-md-end {\n justify-content: flex-end !important;\n }\n .justify-content-md-center {\n justify-content: center !important;\n }\n .justify-content-md-between {\n justify-content: space-between !important;\n }\n .justify-content-md-around {\n justify-content: space-around !important;\n }\n .align-items-md-start {\n align-items: flex-start !important;\n }\n .align-items-md-end {\n align-items: flex-end !important;\n }\n .align-items-md-center {\n align-items: center !important;\n }\n .align-items-md-baseline {\n align-items: baseline !important;\n }\n .align-items-md-stretch {\n align-items: stretch !important;\n }\n .align-content-md-start {\n align-content: flex-start !important;\n }\n .align-content-md-end {\n align-content: flex-end !important;\n }\n .align-content-md-center {\n align-content: center !important;\n }\n .align-content-md-between {\n align-content: space-between !important;\n }\n .align-content-md-around {\n align-content: space-around !important;\n }\n .align-content-md-stretch {\n align-content: stretch !important;\n }\n .align-self-md-auto {\n align-self: auto !important;\n }\n .align-self-md-start {\n align-self: flex-start !important;\n }\n .align-self-md-end {\n align-self: flex-end !important;\n }\n .align-self-md-center {\n align-self: center !important;\n }\n .align-self-md-baseline {\n align-self: baseline !important;\n }\n .align-self-md-stretch {\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 992px) {\n .flex-lg-row {\n flex-direction: row !important;\n }\n .flex-lg-column {\n flex-direction: column !important;\n }\n .flex-lg-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-lg-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-lg-wrap {\n flex-wrap: wrap !important;\n }\n .flex-lg-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-lg-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .flex-lg-fill {\n flex: 1 1 auto !important;\n }\n .flex-lg-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-lg-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-lg-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-lg-shrink-1 {\n flex-shrink: 1 !important;\n }\n .justify-content-lg-start {\n justify-content: flex-start !important;\n }\n .justify-content-lg-end {\n justify-content: flex-end !important;\n }\n .justify-content-lg-center {\n justify-content: center !important;\n }\n .justify-content-lg-between {\n justify-content: space-between !important;\n }\n .justify-content-lg-around {\n justify-content: space-around !important;\n }\n .align-items-lg-start {\n align-items: flex-start !important;\n }\n .align-items-lg-end {\n align-items: flex-end !important;\n }\n .align-items-lg-center {\n align-items: center !important;\n }\n .align-items-lg-baseline {\n align-items: baseline !important;\n }\n .align-items-lg-stretch {\n align-items: stretch !important;\n }\n .align-content-lg-start {\n align-content: flex-start !important;\n }\n .align-content-lg-end {\n align-content: flex-end !important;\n }\n .align-content-lg-center {\n align-content: center !important;\n }\n .align-content-lg-between {\n align-content: space-between !important;\n }\n .align-content-lg-around {\n align-content: space-around !important;\n }\n .align-content-lg-stretch {\n align-content: stretch !important;\n }\n .align-self-lg-auto {\n align-self: auto !important;\n }\n .align-self-lg-start {\n align-self: flex-start !important;\n }\n .align-self-lg-end {\n align-self: flex-end !important;\n }\n .align-self-lg-center {\n align-self: center !important;\n }\n .align-self-lg-baseline {\n align-self: baseline !important;\n }\n .align-self-lg-stretch {\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 1200px) {\n .flex-xl-row {\n flex-direction: row !important;\n }\n .flex-xl-column {\n flex-direction: column !important;\n }\n .flex-xl-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-xl-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-xl-wrap {\n flex-wrap: wrap !important;\n }\n .flex-xl-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-xl-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .flex-xl-fill {\n flex: 1 1 auto !important;\n }\n .flex-xl-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-xl-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-xl-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-xl-shrink-1 {\n flex-shrink: 1 !important;\n }\n .justify-content-xl-start {\n justify-content: flex-start !important;\n }\n .justify-content-xl-end {\n justify-content: flex-end !important;\n }\n .justify-content-xl-center {\n justify-content: center !important;\n }\n .justify-content-xl-between {\n justify-content: space-between !important;\n }\n .justify-content-xl-around {\n justify-content: space-around !important;\n }\n .align-items-xl-start {\n align-items: flex-start !important;\n }\n .align-items-xl-end {\n align-items: flex-end !important;\n }\n .align-items-xl-center {\n align-items: center !important;\n }\n .align-items-xl-baseline {\n align-items: baseline !important;\n }\n .align-items-xl-stretch {\n align-items: stretch !important;\n }\n .align-content-xl-start {\n align-content: flex-start !important;\n }\n .align-content-xl-end {\n align-content: flex-end !important;\n }\n .align-content-xl-center {\n align-content: center !important;\n }\n .align-content-xl-between {\n align-content: space-between !important;\n }\n .align-content-xl-around {\n align-content: space-around !important;\n }\n .align-content-xl-stretch {\n align-content: stretch !important;\n }\n .align-self-xl-auto {\n align-self: auto !important;\n }\n .align-self-xl-start {\n align-self: flex-start !important;\n }\n .align-self-xl-end {\n align-self: flex-end !important;\n }\n .align-self-xl-center {\n align-self: center !important;\n }\n .align-self-xl-baseline {\n align-self: baseline !important;\n }\n .align-self-xl-stretch {\n align-self: stretch !important;\n }\n}\n\n.float-left {\n float: left !important;\n}\n\n.float-right {\n float: right !important;\n}\n\n.float-none {\n float: none !important;\n}\n\n@media (min-width: 576px) {\n .float-sm-left {\n float: left !important;\n }\n .float-sm-right {\n float: right !important;\n }\n .float-sm-none {\n float: none !important;\n }\n}\n\n@media (min-width: 768px) {\n .float-md-left {\n float: left !important;\n }\n .float-md-right {\n float: right !important;\n }\n .float-md-none {\n float: none !important;\n }\n}\n\n@media (min-width: 992px) {\n .float-lg-left {\n float: left !important;\n }\n .float-lg-right {\n float: right !important;\n }\n .float-lg-none {\n float: none !important;\n }\n}\n\n@media (min-width: 1200px) {\n .float-xl-left {\n float: left !important;\n }\n .float-xl-right {\n float: right !important;\n }\n .float-xl-none {\n float: none !important;\n }\n}\n\n.position-static {\n position: static !important;\n}\n\n.position-relative {\n position: relative !important;\n}\n\n.position-absolute {\n position: absolute !important;\n}\n\n.position-fixed {\n position: fixed !important;\n}\n\n.position-sticky {\n position: sticky !important;\n}\n\n.fixed-top {\n position: fixed;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n\n.fixed-bottom {\n position: fixed;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1030;\n}\n\n@supports (position: sticky) {\n .sticky-top {\n position: sticky;\n top: 0;\n z-index: 1020;\n }\n}\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n\n.sr-only-focusable:active, .sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n overflow: visible;\n clip: auto;\n white-space: normal;\n}\n\n.shadow-sm {\n box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;\n}\n\n.shadow {\n box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;\n}\n\n.shadow-lg {\n box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;\n}\n\n.shadow-none {\n box-shadow: none !important;\n}\n\n.w-25 {\n width: 25% !important;\n}\n\n.w-50 {\n width: 50% !important;\n}\n\n.w-75 {\n width: 75% !important;\n}\n\n.w-100 {\n width: 100% !important;\n}\n\n.w-auto {\n width: auto !important;\n}\n\n.h-25 {\n height: 25% !important;\n}\n\n.h-50 {\n height: 50% !important;\n}\n\n.h-75 {\n height: 75% !important;\n}\n\n.h-100 {\n height: 100% !important;\n}\n\n.h-auto {\n height: auto !important;\n}\n\n.mw-100 {\n max-width: 100% !important;\n}\n\n.mh-100 {\n max-height: 100% !important;\n}\n\n.m-0 {\n margin: 0 !important;\n}\n\n.mt-0,\n.my-0 {\n margin-top: 0 !important;\n}\n\n.mr-0,\n.mx-0 {\n margin-right: 0 !important;\n}\n\n.mb-0,\n.my-0 {\n margin-bottom: 0 !important;\n}\n\n.ml-0,\n.mx-0 {\n margin-left: 0 !important;\n}\n\n.m-1 {\n margin: 0.25rem !important;\n}\n\n.mt-1,\n.my-1 {\n margin-top: 0.25rem !important;\n}\n\n.mr-1,\n.mx-1 {\n margin-right: 0.25rem !important;\n}\n\n.mb-1,\n.my-1 {\n margin-bottom: 0.25rem !important;\n}\n\n.ml-1,\n.mx-1 {\n margin-left: 0.25rem !important;\n}\n\n.m-2 {\n margin: 0.5rem !important;\n}\n\n.mt-2,\n.my-2 {\n margin-top: 0.5rem !important;\n}\n\n.mr-2,\n.mx-2 {\n margin-right: 0.5rem !important;\n}\n\n.mb-2,\n.my-2 {\n margin-bottom: 0.5rem !important;\n}\n\n.ml-2,\n.mx-2 {\n margin-left: 0.5rem !important;\n}\n\n.m-3 {\n margin: 1rem !important;\n}\n\n.mt-3,\n.my-3 {\n margin-top: 1rem !important;\n}\n\n.mr-3,\n.mx-3 {\n margin-right: 1rem !important;\n}\n\n.mb-3,\n.my-3 {\n margin-bottom: 1rem !important;\n}\n\n.ml-3,\n.mx-3 {\n margin-left: 1rem !important;\n}\n\n.m-4 {\n margin: 1.5rem !important;\n}\n\n.mt-4,\n.my-4 {\n margin-top: 1.5rem !important;\n}\n\n.mr-4,\n.mx-4 {\n margin-right: 1.5rem !important;\n}\n\n.mb-4,\n.my-4 {\n margin-bottom: 1.5rem !important;\n}\n\n.ml-4,\n.mx-4 {\n margin-left: 1.5rem !important;\n}\n\n.m-5 {\n margin: 3rem !important;\n}\n\n.mt-5,\n.my-5 {\n margin-top: 3rem !important;\n}\n\n.mr-5,\n.mx-5 {\n margin-right: 3rem !important;\n}\n\n.mb-5,\n.my-5 {\n margin-bottom: 3rem !important;\n}\n\n.ml-5,\n.mx-5 {\n margin-left: 3rem !important;\n}\n\n.p-0 {\n padding: 0 !important;\n}\n\n.pt-0,\n.py-0 {\n padding-top: 0 !important;\n}\n\n.pr-0,\n.px-0 {\n padding-right: 0 !important;\n}\n\n.pb-0,\n.py-0 {\n padding-bottom: 0 !important;\n}\n\n.pl-0,\n.px-0 {\n padding-left: 0 !important;\n}\n\n.p-1 {\n padding: 0.25rem !important;\n}\n\n.pt-1,\n.py-1 {\n padding-top: 0.25rem !important;\n}\n\n.pr-1,\n.px-1 {\n padding-right: 0.25rem !important;\n}\n\n.pb-1,\n.py-1 {\n padding-bottom: 0.25rem !important;\n}\n\n.pl-1,\n.px-1 {\n padding-left: 0.25rem !important;\n}\n\n.p-2 {\n padding: 0.5rem !important;\n}\n\n.pt-2,\n.py-2 {\n padding-top: 0.5rem !important;\n}\n\n.pr-2,\n.px-2 {\n padding-right: 0.5rem !important;\n}\n\n.pb-2,\n.py-2 {\n padding-bottom: 0.5rem !important;\n}\n\n.pl-2,\n.px-2 {\n padding-left: 0.5rem !important;\n}\n\n.p-3 {\n padding: 1rem !important;\n}\n\n.pt-3,\n.py-3 {\n padding-top: 1rem !important;\n}\n\n.pr-3,\n.px-3 {\n padding-right: 1rem !important;\n}\n\n.pb-3,\n.py-3 {\n padding-bottom: 1rem !important;\n}\n\n.pl-3,\n.px-3 {\n padding-left: 1rem !important;\n}\n\n.p-4 {\n padding: 1.5rem !important;\n}\n\n.pt-4,\n.py-4 {\n padding-top: 1.5rem !important;\n}\n\n.pr-4,\n.px-4 {\n padding-right: 1.5rem !important;\n}\n\n.pb-4,\n.py-4 {\n padding-bottom: 1.5rem !important;\n}\n\n.pl-4,\n.px-4 {\n padding-left: 1.5rem !important;\n}\n\n.p-5 {\n padding: 3rem !important;\n}\n\n.pt-5,\n.py-5 {\n padding-top: 3rem !important;\n}\n\n.pr-5,\n.px-5 {\n padding-right: 3rem !important;\n}\n\n.pb-5,\n.py-5 {\n padding-bottom: 3rem !important;\n}\n\n.pl-5,\n.px-5 {\n padding-left: 3rem !important;\n}\n\n.m-auto {\n margin: auto !important;\n}\n\n.mt-auto,\n.my-auto {\n margin-top: auto !important;\n}\n\n.mr-auto,\n.mx-auto {\n margin-right: auto !important;\n}\n\n.mb-auto,\n.my-auto {\n margin-bottom: auto !important;\n}\n\n.ml-auto,\n.mx-auto {\n margin-left: auto !important;\n}\n\n@media (min-width: 576px) {\n .m-sm-0 {\n margin: 0 !important;\n }\n .mt-sm-0,\n .my-sm-0 {\n margin-top: 0 !important;\n }\n .mr-sm-0,\n .mx-sm-0 {\n margin-right: 0 !important;\n }\n .mb-sm-0,\n .my-sm-0 {\n margin-bottom: 0 !important;\n }\n .ml-sm-0,\n .mx-sm-0 {\n margin-left: 0 !important;\n }\n .m-sm-1 {\n margin: 0.25rem !important;\n }\n .mt-sm-1,\n .my-sm-1 {\n margin-top: 0.25rem !important;\n }\n .mr-sm-1,\n .mx-sm-1 {\n margin-right: 0.25rem !important;\n }\n .mb-sm-1,\n .my-sm-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-sm-1,\n .mx-sm-1 {\n margin-left: 0.25rem !important;\n }\n .m-sm-2 {\n margin: 0.5rem !important;\n }\n .mt-sm-2,\n .my-sm-2 {\n margin-top: 0.5rem !important;\n }\n .mr-sm-2,\n .mx-sm-2 {\n margin-right: 0.5rem !important;\n }\n .mb-sm-2,\n .my-sm-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-sm-2,\n .mx-sm-2 {\n margin-left: 0.5rem !important;\n }\n .m-sm-3 {\n margin: 1rem !important;\n }\n .mt-sm-3,\n .my-sm-3 {\n margin-top: 1rem !important;\n }\n .mr-sm-3,\n .mx-sm-3 {\n margin-right: 1rem !important;\n }\n .mb-sm-3,\n .my-sm-3 {\n margin-bottom: 1rem !important;\n }\n .ml-sm-3,\n .mx-sm-3 {\n margin-left: 1rem !important;\n }\n .m-sm-4 {\n margin: 1.5rem !important;\n }\n .mt-sm-4,\n .my-sm-4 {\n margin-top: 1.5rem !important;\n }\n .mr-sm-4,\n .mx-sm-4 {\n margin-right: 1.5rem !important;\n }\n .mb-sm-4,\n .my-sm-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-sm-4,\n .mx-sm-4 {\n margin-left: 1.5rem !important;\n }\n .m-sm-5 {\n margin: 3rem !important;\n }\n .mt-sm-5,\n .my-sm-5 {\n margin-top: 3rem !important;\n }\n .mr-sm-5,\n .mx-sm-5 {\n margin-right: 3rem !important;\n }\n .mb-sm-5,\n .my-sm-5 {\n margin-bottom: 3rem !important;\n }\n .ml-sm-5,\n .mx-sm-5 {\n margin-left: 3rem !important;\n }\n .p-sm-0 {\n padding: 0 !important;\n }\n .pt-sm-0,\n .py-sm-0 {\n padding-top: 0 !important;\n }\n .pr-sm-0,\n .px-sm-0 {\n padding-right: 0 !important;\n }\n .pb-sm-0,\n .py-sm-0 {\n padding-bottom: 0 !important;\n }\n .pl-sm-0,\n .px-sm-0 {\n padding-left: 0 !important;\n }\n .p-sm-1 {\n padding: 0.25rem !important;\n }\n .pt-sm-1,\n .py-sm-1 {\n padding-top: 0.25rem !important;\n }\n .pr-sm-1,\n .px-sm-1 {\n padding-right: 0.25rem !important;\n }\n .pb-sm-1,\n .py-sm-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-sm-1,\n .px-sm-1 {\n padding-left: 0.25rem !important;\n }\n .p-sm-2 {\n padding: 0.5rem !important;\n }\n .pt-sm-2,\n .py-sm-2 {\n padding-top: 0.5rem !important;\n }\n .pr-sm-2,\n .px-sm-2 {\n padding-right: 0.5rem !important;\n }\n .pb-sm-2,\n .py-sm-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-sm-2,\n .px-sm-2 {\n padding-left: 0.5rem !important;\n }\n .p-sm-3 {\n padding: 1rem !important;\n }\n .pt-sm-3,\n .py-sm-3 {\n padding-top: 1rem !important;\n }\n .pr-sm-3,\n .px-sm-3 {\n padding-right: 1rem !important;\n }\n .pb-sm-3,\n .py-sm-3 {\n padding-bottom: 1rem !important;\n }\n .pl-sm-3,\n .px-sm-3 {\n padding-left: 1rem !important;\n }\n .p-sm-4 {\n padding: 1.5rem !important;\n }\n .pt-sm-4,\n .py-sm-4 {\n padding-top: 1.5rem !important;\n }\n .pr-sm-4,\n .px-sm-4 {\n padding-right: 1.5rem !important;\n }\n .pb-sm-4,\n .py-sm-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-sm-4,\n .px-sm-4 {\n padding-left: 1.5rem !important;\n }\n .p-sm-5 {\n padding: 3rem !important;\n }\n .pt-sm-5,\n .py-sm-5 {\n padding-top: 3rem !important;\n }\n .pr-sm-5,\n .px-sm-5 {\n padding-right: 3rem !important;\n }\n .pb-sm-5,\n .py-sm-5 {\n padding-bottom: 3rem !important;\n }\n .pl-sm-5,\n .px-sm-5 {\n padding-left: 3rem !important;\n }\n .m-sm-auto {\n margin: auto !important;\n }\n .mt-sm-auto,\n .my-sm-auto {\n margin-top: auto !important;\n }\n .mr-sm-auto,\n .mx-sm-auto {\n margin-right: auto !important;\n }\n .mb-sm-auto,\n .my-sm-auto {\n margin-bottom: auto !important;\n }\n .ml-sm-auto,\n .mx-sm-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 768px) {\n .m-md-0 {\n margin: 0 !important;\n }\n .mt-md-0,\n .my-md-0 {\n margin-top: 0 !important;\n }\n .mr-md-0,\n .mx-md-0 {\n margin-right: 0 !important;\n }\n .mb-md-0,\n .my-md-0 {\n margin-bottom: 0 !important;\n }\n .ml-md-0,\n .mx-md-0 {\n margin-left: 0 !important;\n }\n .m-md-1 {\n margin: 0.25rem !important;\n }\n .mt-md-1,\n .my-md-1 {\n margin-top: 0.25rem !important;\n }\n .mr-md-1,\n .mx-md-1 {\n margin-right: 0.25rem !important;\n }\n .mb-md-1,\n .my-md-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-md-1,\n .mx-md-1 {\n margin-left: 0.25rem !important;\n }\n .m-md-2 {\n margin: 0.5rem !important;\n }\n .mt-md-2,\n .my-md-2 {\n margin-top: 0.5rem !important;\n }\n .mr-md-2,\n .mx-md-2 {\n margin-right: 0.5rem !important;\n }\n .mb-md-2,\n .my-md-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-md-2,\n .mx-md-2 {\n margin-left: 0.5rem !important;\n }\n .m-md-3 {\n margin: 1rem !important;\n }\n .mt-md-3,\n .my-md-3 {\n margin-top: 1rem !important;\n }\n .mr-md-3,\n .mx-md-3 {\n margin-right: 1rem !important;\n }\n .mb-md-3,\n .my-md-3 {\n margin-bottom: 1rem !important;\n }\n .ml-md-3,\n .mx-md-3 {\n margin-left: 1rem !important;\n }\n .m-md-4 {\n margin: 1.5rem !important;\n }\n .mt-md-4,\n .my-md-4 {\n margin-top: 1.5rem !important;\n }\n .mr-md-4,\n .mx-md-4 {\n margin-right: 1.5rem !important;\n }\n .mb-md-4,\n .my-md-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-md-4,\n .mx-md-4 {\n margin-left: 1.5rem !important;\n }\n .m-md-5 {\n margin: 3rem !important;\n }\n .mt-md-5,\n .my-md-5 {\n margin-top: 3rem !important;\n }\n .mr-md-5,\n .mx-md-5 {\n margin-right: 3rem !important;\n }\n .mb-md-5,\n .my-md-5 {\n margin-bottom: 3rem !important;\n }\n .ml-md-5,\n .mx-md-5 {\n margin-left: 3rem !important;\n }\n .p-md-0 {\n padding: 0 !important;\n }\n .pt-md-0,\n .py-md-0 {\n padding-top: 0 !important;\n }\n .pr-md-0,\n .px-md-0 {\n padding-right: 0 !important;\n }\n .pb-md-0,\n .py-md-0 {\n padding-bottom: 0 !important;\n }\n .pl-md-0,\n .px-md-0 {\n padding-left: 0 !important;\n }\n .p-md-1 {\n padding: 0.25rem !important;\n }\n .pt-md-1,\n .py-md-1 {\n padding-top: 0.25rem !important;\n }\n .pr-md-1,\n .px-md-1 {\n padding-right: 0.25rem !important;\n }\n .pb-md-1,\n .py-md-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-md-1,\n .px-md-1 {\n padding-left: 0.25rem !important;\n }\n .p-md-2 {\n padding: 0.5rem !important;\n }\n .pt-md-2,\n .py-md-2 {\n padding-top: 0.5rem !important;\n }\n .pr-md-2,\n .px-md-2 {\n padding-right: 0.5rem !important;\n }\n .pb-md-2,\n .py-md-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-md-2,\n .px-md-2 {\n padding-left: 0.5rem !important;\n }\n .p-md-3 {\n padding: 1rem !important;\n }\n .pt-md-3,\n .py-md-3 {\n padding-top: 1rem !important;\n }\n .pr-md-3,\n .px-md-3 {\n padding-right: 1rem !important;\n }\n .pb-md-3,\n .py-md-3 {\n padding-bottom: 1rem !important;\n }\n .pl-md-3,\n .px-md-3 {\n padding-left: 1rem !important;\n }\n .p-md-4 {\n padding: 1.5rem !important;\n }\n .pt-md-4,\n .py-md-4 {\n padding-top: 1.5rem !important;\n }\n .pr-md-4,\n .px-md-4 {\n padding-right: 1.5rem !important;\n }\n .pb-md-4,\n .py-md-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-md-4,\n .px-md-4 {\n padding-left: 1.5rem !important;\n }\n .p-md-5 {\n padding: 3rem !important;\n }\n .pt-md-5,\n .py-md-5 {\n padding-top: 3rem !important;\n }\n .pr-md-5,\n .px-md-5 {\n padding-right: 3rem !important;\n }\n .pb-md-5,\n .py-md-5 {\n padding-bottom: 3rem !important;\n }\n .pl-md-5,\n .px-md-5 {\n padding-left: 3rem !important;\n }\n .m-md-auto {\n margin: auto !important;\n }\n .mt-md-auto,\n .my-md-auto {\n margin-top: auto !important;\n }\n .mr-md-auto,\n .mx-md-auto {\n margin-right: auto !important;\n }\n .mb-md-auto,\n .my-md-auto {\n margin-bottom: auto !important;\n }\n .ml-md-auto,\n .mx-md-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 992px) {\n .m-lg-0 {\n margin: 0 !important;\n }\n .mt-lg-0,\n .my-lg-0 {\n margin-top: 0 !important;\n }\n .mr-lg-0,\n .mx-lg-0 {\n margin-right: 0 !important;\n }\n .mb-lg-0,\n .my-lg-0 {\n margin-bottom: 0 !important;\n }\n .ml-lg-0,\n .mx-lg-0 {\n margin-left: 0 !important;\n }\n .m-lg-1 {\n margin: 0.25rem !important;\n }\n .mt-lg-1,\n .my-lg-1 {\n margin-top: 0.25rem !important;\n }\n .mr-lg-1,\n .mx-lg-1 {\n margin-right: 0.25rem !important;\n }\n .mb-lg-1,\n .my-lg-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-lg-1,\n .mx-lg-1 {\n margin-left: 0.25rem !important;\n }\n .m-lg-2 {\n margin: 0.5rem !important;\n }\n .mt-lg-2,\n .my-lg-2 {\n margin-top: 0.5rem !important;\n }\n .mr-lg-2,\n .mx-lg-2 {\n margin-right: 0.5rem !important;\n }\n .mb-lg-2,\n .my-lg-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-lg-2,\n .mx-lg-2 {\n margin-left: 0.5rem !important;\n }\n .m-lg-3 {\n margin: 1rem !important;\n }\n .mt-lg-3,\n .my-lg-3 {\n margin-top: 1rem !important;\n }\n .mr-lg-3,\n .mx-lg-3 {\n margin-right: 1rem !important;\n }\n .mb-lg-3,\n .my-lg-3 {\n margin-bottom: 1rem !important;\n }\n .ml-lg-3,\n .mx-lg-3 {\n margin-left: 1rem !important;\n }\n .m-lg-4 {\n margin: 1.5rem !important;\n }\n .mt-lg-4,\n .my-lg-4 {\n margin-top: 1.5rem !important;\n }\n .mr-lg-4,\n .mx-lg-4 {\n margin-right: 1.5rem !important;\n }\n .mb-lg-4,\n .my-lg-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-lg-4,\n .mx-lg-4 {\n margin-left: 1.5rem !important;\n }\n .m-lg-5 {\n margin: 3rem !important;\n }\n .mt-lg-5,\n .my-lg-5 {\n margin-top: 3rem !important;\n }\n .mr-lg-5,\n .mx-lg-5 {\n margin-right: 3rem !important;\n }\n .mb-lg-5,\n .my-lg-5 {\n margin-bottom: 3rem !important;\n }\n .ml-lg-5,\n .mx-lg-5 {\n margin-left: 3rem !important;\n }\n .p-lg-0 {\n padding: 0 !important;\n }\n .pt-lg-0,\n .py-lg-0 {\n padding-top: 0 !important;\n }\n .pr-lg-0,\n .px-lg-0 {\n padding-right: 0 !important;\n }\n .pb-lg-0,\n .py-lg-0 {\n padding-bottom: 0 !important;\n }\n .pl-lg-0,\n .px-lg-0 {\n padding-left: 0 !important;\n }\n .p-lg-1 {\n padding: 0.25rem !important;\n }\n .pt-lg-1,\n .py-lg-1 {\n padding-top: 0.25rem !important;\n }\n .pr-lg-1,\n .px-lg-1 {\n padding-right: 0.25rem !important;\n }\n .pb-lg-1,\n .py-lg-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-lg-1,\n .px-lg-1 {\n padding-left: 0.25rem !important;\n }\n .p-lg-2 {\n padding: 0.5rem !important;\n }\n .pt-lg-2,\n .py-lg-2 {\n padding-top: 0.5rem !important;\n }\n .pr-lg-2,\n .px-lg-2 {\n padding-right: 0.5rem !important;\n }\n .pb-lg-2,\n .py-lg-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-lg-2,\n .px-lg-2 {\n padding-left: 0.5rem !important;\n }\n .p-lg-3 {\n padding: 1rem !important;\n }\n .pt-lg-3,\n .py-lg-3 {\n padding-top: 1rem !important;\n }\n .pr-lg-3,\n .px-lg-3 {\n padding-right: 1rem !important;\n }\n .pb-lg-3,\n .py-lg-3 {\n padding-bottom: 1rem !important;\n }\n .pl-lg-3,\n .px-lg-3 {\n padding-left: 1rem !important;\n }\n .p-lg-4 {\n padding: 1.5rem !important;\n }\n .pt-lg-4,\n .py-lg-4 {\n padding-top: 1.5rem !important;\n }\n .pr-lg-4,\n .px-lg-4 {\n padding-right: 1.5rem !important;\n }\n .pb-lg-4,\n .py-lg-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-lg-4,\n .px-lg-4 {\n padding-left: 1.5rem !important;\n }\n .p-lg-5 {\n padding: 3rem !important;\n }\n .pt-lg-5,\n .py-lg-5 {\n padding-top: 3rem !important;\n }\n .pr-lg-5,\n .px-lg-5 {\n padding-right: 3rem !important;\n }\n .pb-lg-5,\n .py-lg-5 {\n padding-bottom: 3rem !important;\n }\n .pl-lg-5,\n .px-lg-5 {\n padding-left: 3rem !important;\n }\n .m-lg-auto {\n margin: auto !important;\n }\n .mt-lg-auto,\n .my-lg-auto {\n margin-top: auto !important;\n }\n .mr-lg-auto,\n .mx-lg-auto {\n margin-right: auto !important;\n }\n .mb-lg-auto,\n .my-lg-auto {\n margin-bottom: auto !important;\n }\n .ml-lg-auto,\n .mx-lg-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 1200px) {\n .m-xl-0 {\n margin: 0 !important;\n }\n .mt-xl-0,\n .my-xl-0 {\n margin-top: 0 !important;\n }\n .mr-xl-0,\n .mx-xl-0 {\n margin-right: 0 !important;\n }\n .mb-xl-0,\n .my-xl-0 {\n margin-bottom: 0 !important;\n }\n .ml-xl-0,\n .mx-xl-0 {\n margin-left: 0 !important;\n }\n .m-xl-1 {\n margin: 0.25rem !important;\n }\n .mt-xl-1,\n .my-xl-1 {\n margin-top: 0.25rem !important;\n }\n .mr-xl-1,\n .mx-xl-1 {\n margin-right: 0.25rem !important;\n }\n .mb-xl-1,\n .my-xl-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-xl-1,\n .mx-xl-1 {\n margin-left: 0.25rem !important;\n }\n .m-xl-2 {\n margin: 0.5rem !important;\n }\n .mt-xl-2,\n .my-xl-2 {\n margin-top: 0.5rem !important;\n }\n .mr-xl-2,\n .mx-xl-2 {\n margin-right: 0.5rem !important;\n }\n .mb-xl-2,\n .my-xl-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-xl-2,\n .mx-xl-2 {\n margin-left: 0.5rem !important;\n }\n .m-xl-3 {\n margin: 1rem !important;\n }\n .mt-xl-3,\n .my-xl-3 {\n margin-top: 1rem !important;\n }\n .mr-xl-3,\n .mx-xl-3 {\n margin-right: 1rem !important;\n }\n .mb-xl-3,\n .my-xl-3 {\n margin-bottom: 1rem !important;\n }\n .ml-xl-3,\n .mx-xl-3 {\n margin-left: 1rem !important;\n }\n .m-xl-4 {\n margin: 1.5rem !important;\n }\n .mt-xl-4,\n .my-xl-4 {\n margin-top: 1.5rem !important;\n }\n .mr-xl-4,\n .mx-xl-4 {\n margin-right: 1.5rem !important;\n }\n .mb-xl-4,\n .my-xl-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-xl-4,\n .mx-xl-4 {\n margin-left: 1.5rem !important;\n }\n .m-xl-5 {\n margin: 3rem !important;\n }\n .mt-xl-5,\n .my-xl-5 {\n margin-top: 3rem !important;\n }\n .mr-xl-5,\n .mx-xl-5 {\n margin-right: 3rem !important;\n }\n .mb-xl-5,\n .my-xl-5 {\n margin-bottom: 3rem !important;\n }\n .ml-xl-5,\n .mx-xl-5 {\n margin-left: 3rem !important;\n }\n .p-xl-0 {\n padding: 0 !important;\n }\n .pt-xl-0,\n .py-xl-0 {\n padding-top: 0 !important;\n }\n .pr-xl-0,\n .px-xl-0 {\n padding-right: 0 !important;\n }\n .pb-xl-0,\n .py-xl-0 {\n padding-bottom: 0 !important;\n }\n .pl-xl-0,\n .px-xl-0 {\n padding-left: 0 !important;\n }\n .p-xl-1 {\n padding: 0.25rem !important;\n }\n .pt-xl-1,\n .py-xl-1 {\n padding-top: 0.25rem !important;\n }\n .pr-xl-1,\n .px-xl-1 {\n padding-right: 0.25rem !important;\n }\n .pb-xl-1,\n .py-xl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-xl-1,\n .px-xl-1 {\n padding-left: 0.25rem !important;\n }\n .p-xl-2 {\n padding: 0.5rem !important;\n }\n .pt-xl-2,\n .py-xl-2 {\n padding-top: 0.5rem !important;\n }\n .pr-xl-2,\n .px-xl-2 {\n padding-right: 0.5rem !important;\n }\n .pb-xl-2,\n .py-xl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-xl-2,\n .px-xl-2 {\n padding-left: 0.5rem !important;\n }\n .p-xl-3 {\n padding: 1rem !important;\n }\n .pt-xl-3,\n .py-xl-3 {\n padding-top: 1rem !important;\n }\n .pr-xl-3,\n .px-xl-3 {\n padding-right: 1rem !important;\n }\n .pb-xl-3,\n .py-xl-3 {\n padding-bottom: 1rem !important;\n }\n .pl-xl-3,\n .px-xl-3 {\n padding-left: 1rem !important;\n }\n .p-xl-4 {\n padding: 1.5rem !important;\n }\n .pt-xl-4,\n .py-xl-4 {\n padding-top: 1.5rem !important;\n }\n .pr-xl-4,\n .px-xl-4 {\n padding-right: 1.5rem !important;\n }\n .pb-xl-4,\n .py-xl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-xl-4,\n .px-xl-4 {\n padding-left: 1.5rem !important;\n }\n .p-xl-5 {\n padding: 3rem !important;\n }\n .pt-xl-5,\n .py-xl-5 {\n padding-top: 3rem !important;\n }\n .pr-xl-5,\n .px-xl-5 {\n padding-right: 3rem !important;\n }\n .pb-xl-5,\n .py-xl-5 {\n padding-bottom: 3rem !important;\n }\n .pl-xl-5,\n .px-xl-5 {\n padding-left: 3rem !important;\n }\n .m-xl-auto {\n margin: auto !important;\n }\n .mt-xl-auto,\n .my-xl-auto {\n margin-top: auto !important;\n }\n .mr-xl-auto,\n .mx-xl-auto {\n margin-right: auto !important;\n }\n .mb-xl-auto,\n .my-xl-auto {\n margin-bottom: auto !important;\n }\n .ml-xl-auto,\n .mx-xl-auto {\n margin-left: auto !important;\n }\n}\n\n.text-monospace {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n.text-justify {\n text-align: justify !important;\n}\n\n.text-nowrap {\n white-space: nowrap !important;\n}\n\n.text-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.text-left {\n text-align: left !important;\n}\n\n.text-right {\n text-align: right !important;\n}\n\n.text-center {\n text-align: center !important;\n}\n\n@media (min-width: 576px) {\n .text-sm-left {\n text-align: left !important;\n }\n .text-sm-right {\n text-align: right !important;\n }\n .text-sm-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 768px) {\n .text-md-left {\n text-align: left !important;\n }\n .text-md-right {\n text-align: right !important;\n }\n .text-md-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 992px) {\n .text-lg-left {\n text-align: left !important;\n }\n .text-lg-right {\n text-align: right !important;\n }\n .text-lg-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 1200px) {\n .text-xl-left {\n text-align: left !important;\n }\n .text-xl-right {\n text-align: right !important;\n }\n .text-xl-center {\n text-align: center !important;\n }\n}\n\n.text-lowercase {\n text-transform: lowercase !important;\n}\n\n.text-uppercase {\n text-transform: uppercase !important;\n}\n\n.text-capitalize {\n text-transform: capitalize !important;\n}\n\n.font-weight-light {\n font-weight: 300 !important;\n}\n\n.font-weight-normal {\n font-weight: 400 !important;\n}\n\n.font-weight-bold {\n font-weight: 700 !important;\n}\n\n.font-italic {\n font-style: italic !important;\n}\n\n.text-white {\n color: #fff !important;\n}\n\n.text-primary {\n color: #007bff !important;\n}\n\na.text-primary:hover, a.text-primary:focus {\n color: #0062cc !important;\n}\n\n.text-secondary {\n color: #6c757d !important;\n}\n\na.text-secondary:hover, a.text-secondary:focus {\n color: #545b62 !important;\n}\n\n.text-success {\n color: #28a745 !important;\n}\n\na.text-success:hover, a.text-success:focus {\n color: #1e7e34 !important;\n}\n\n.text-info {\n color: #17a2b8 !important;\n}\n\na.text-info:hover, a.text-info:focus {\n color: #117a8b !important;\n}\n\n.text-warning {\n color: #ffc107 !important;\n}\n\na.text-warning:hover, a.text-warning:focus {\n color: #d39e00 !important;\n}\n\n.text-danger {\n color: #dc3545 !important;\n}\n\na.text-danger:hover, a.text-danger:focus {\n color: #bd2130 !important;\n}\n\n.text-light {\n color: #f8f9fa !important;\n}\n\na.text-light:hover, a.text-light:focus {\n color: #dae0e5 !important;\n}\n\n.text-dark {\n color: #343a40 !important;\n}\n\na.text-dark:hover, a.text-dark:focus {\n color: #1d2124 !important;\n}\n\n.text-body {\n color: #212529 !important;\n}\n\n.text-muted {\n color: #6c757d !important;\n}\n\n.text-black-50 {\n color: rgba(0, 0, 0, 0.5) !important;\n}\n\n.text-white-50 {\n color: rgba(255, 255, 255, 0.5) !important;\n}\n\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n\n.visible {\n visibility: visible !important;\n}\n\n.invisible {\n visibility: hidden !important;\n}\n\n@media print {\n *,\n *::before,\n *::after {\n text-shadow: none !important;\n box-shadow: none !important;\n }\n a:not(.btn) {\n text-decoration: underline;\n }\n abbr[title]::after {\n content: \" (\" attr(title) \")\";\n }\n pre {\n white-space: pre-wrap !important;\n }\n pre,\n blockquote {\n border: 1px solid #adb5bd;\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n @page {\n size: a3;\n }\n body {\n min-width: 992px !important;\n }\n .container {\n min-width: 992px !important;\n }\n .navbar {\n display: none;\n }\n .badge {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #dee2e6 !important;\n }\n .table-dark {\n color: inherit;\n }\n .table-dark th,\n .table-dark td,\n .table-dark thead th,\n .table-dark tbody + tbody {\n border-color: #dee2e6;\n }\n .table .thead-dark th {\n color: inherit;\n border-color: #dee2e6;\n }\n}\n\n/*# sourceMappingURL=bootstrap.css.map */","// Hover mixin and `$enable-hover-media-query` are deprecated.\n//\n// Origally added during our alphas and maintained during betas, this mixin was\n// designed to prevent `:hover` stickiness on iOS-an issue where hover styles\n// would persist after initial touch.\n//\n// For backward compatibility, we've kept these mixins and updated them to\n// always return their regular pseudo-classes instead of a shimmed media query.\n//\n// Issue: https://github.com/twbs/bootstrap/issues/25195\n\n@mixin hover {\n &:hover { @content; }\n}\n\n@mixin hover-focus {\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin plain-hover-focus {\n &,\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin hover-focus-active {\n &:hover,\n &:focus,\n &:active {\n @content;\n }\n}\n","// stylelint-disable declaration-no-important, selector-list-comma-newline-after\n\n//\n// Headings\n//\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n margin-bottom: $headings-margin-bottom;\n font-family: $headings-font-family;\n font-weight: $headings-font-weight;\n line-height: $headings-line-height;\n color: $headings-color;\n}\n\nh1, .h1 { font-size: $h1-font-size; }\nh2, .h2 { font-size: $h2-font-size; }\nh3, .h3 { font-size: $h3-font-size; }\nh4, .h4 { font-size: $h4-font-size; }\nh5, .h5 { font-size: $h5-font-size; }\nh6, .h6 { font-size: $h6-font-size; }\n\n.lead {\n font-size: $lead-font-size;\n font-weight: $lead-font-weight;\n}\n\n// Type display classes\n.display-1 {\n font-size: $display1-size;\n font-weight: $display1-weight;\n line-height: $display-line-height;\n}\n.display-2 {\n font-size: $display2-size;\n font-weight: $display2-weight;\n line-height: $display-line-height;\n}\n.display-3 {\n font-size: $display3-size;\n font-weight: $display3-weight;\n line-height: $display-line-height;\n}\n.display-4 {\n font-size: $display4-size;\n font-weight: $display4-weight;\n line-height: $display-line-height;\n}\n\n\n//\n// Horizontal rules\n//\n\nhr {\n margin-top: $hr-margin-y;\n margin-bottom: $hr-margin-y;\n border: 0;\n border-top: $hr-border-width solid $hr-border-color;\n}\n\n\n//\n// Emphasis\n//\n\nsmall,\n.small {\n font-size: $small-font-size;\n font-weight: $font-weight-normal;\n}\n\nmark,\n.mark {\n padding: $mark-padding;\n background-color: $mark-bg;\n}\n\n\n//\n// Lists\n//\n\n.list-unstyled {\n @include list-unstyled;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n @include list-unstyled;\n}\n.list-inline-item {\n display: inline-block;\n\n &:not(:last-child) {\n margin-right: $list-inline-padding;\n }\n}\n\n\n//\n// Misc\n//\n\n// Builds on `abbr`\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\n\n// Blockquotes\n.blockquote {\n margin-bottom: $spacer;\n font-size: $blockquote-font-size;\n}\n\n.blockquote-footer {\n display: block;\n font-size: 80%; // back to default font-size\n color: $blockquote-small-color;\n\n &::before {\n content: \"\\2014 \\00A0\"; // em dash, nbsp\n }\n}\n","// Lists\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n@mixin list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n","// Responsive images (ensure images don't scale beyond their parents)\n//\n// This is purposefully opt-in via an explicit class rather than being the default for all ``s.\n// We previously tried the \"images are responsive by default\" approach in Bootstrap v2,\n// and abandoned it in Bootstrap v3 because it breaks lots of third-party widgets (including Google Maps)\n// which weren't expecting the images within themselves to be involuntarily resized.\n// See also https://github.com/twbs/bootstrap/issues/18178\n.img-fluid {\n @include img-fluid;\n}\n\n\n// Image thumbnails\n.img-thumbnail {\n padding: $thumbnail-padding;\n background-color: $thumbnail-bg;\n border: $thumbnail-border-width solid $thumbnail-border-color;\n @include border-radius($thumbnail-border-radius);\n @include box-shadow($thumbnail-box-shadow);\n\n // Keep them at most 100% wide\n @include img-fluid;\n}\n\n//\n// Figures\n//\n\n.figure {\n // Ensures the caption's text aligns with the image.\n display: inline-block;\n}\n\n.figure-img {\n margin-bottom: ($spacer / 2);\n line-height: 1;\n}\n\n.figure-caption {\n font-size: $figure-caption-font-size;\n color: $figure-caption-color;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n\n@mixin img-fluid {\n // Part 1: Set a maximum relative to the parent\n max-width: 100%;\n // Part 2: Override the height to auto, otherwise images will be stretched\n // when setting a width and height attribute on the img element.\n height: auto;\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size.\n\n// stylelint-disable indentation, media-query-list-comma-newline-after\n@mixin img-retina($file-1x, $file-2x, $width-1x, $height-1x) {\n background-image: url($file-1x);\n\n // Autoprefixer takes care of adding -webkit-min-device-pixel-ratio and -o-min-device-pixel-ratio,\n // but doesn't convert dppx=>dpi.\n // There's no such thing as unprefixed min-device-pixel-ratio since it's nonstandard.\n // Compatibility info: https://caniuse.com/#feat=css-media-resolution\n @media only screen and (min-resolution: 192dpi), // IE9-11 don't support dppx\n only screen and (min-resolution: 2dppx) { // Standardized\n background-image: url($file-2x);\n background-size: $width-1x $height-1x;\n }\n}\n","// Single side border-radius\n\n@mixin border-radius($radius: $border-radius) {\n @if $enable-rounded {\n border-radius: $radius;\n }\n}\n\n@mixin border-top-radius($radius) {\n @if $enable-rounded {\n border-top-left-radius: $radius;\n border-top-right-radius: $radius;\n }\n}\n\n@mixin border-right-radius($radius) {\n @if $enable-rounded {\n border-top-right-radius: $radius;\n border-bottom-right-radius: $radius;\n }\n}\n\n@mixin border-bottom-radius($radius) {\n @if $enable-rounded {\n border-bottom-right-radius: $radius;\n border-bottom-left-radius: $radius;\n }\n}\n\n@mixin border-left-radius($radius) {\n @if $enable-rounded {\n border-top-left-radius: $radius;\n border-bottom-left-radius: $radius;\n }\n}\n","// Inline code\ncode {\n font-size: $code-font-size;\n color: $code-color;\n word-break: break-word;\n\n // Streamline the style when inside anchors to avoid broken underline and more\n a > & {\n color: inherit;\n }\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: $kbd-padding-y $kbd-padding-x;\n font-size: $kbd-font-size;\n color: $kbd-color;\n background-color: $kbd-bg;\n @include border-radius($border-radius-sm);\n @include box-shadow($kbd-box-shadow);\n\n kbd {\n padding: 0;\n font-size: 100%;\n font-weight: $nested-kbd-font-weight;\n @include box-shadow(none);\n }\n}\n\n// Blocks of code\npre {\n display: block;\n font-size: $code-font-size;\n color: $pre-color;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n font-size: inherit;\n color: inherit;\n word-break: normal;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: $pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n@if $enable-grid-classes {\n .container {\n @include make-container();\n @include make-container-max-widths();\n }\n}\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but with 100% width for\n// fluid, full width layouts.\n\n@if $enable-grid-classes {\n .container-fluid {\n @include make-container();\n }\n}\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n@if $enable-grid-classes {\n .row {\n @include make-row();\n }\n\n // Remove the negative margin from default .row, then the horizontal padding\n // from all immediate children columns (to prevent runaway style inheritance).\n .no-gutters {\n margin-right: 0;\n margin-left: 0;\n\n > .col,\n > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n }\n }\n}\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n@if $enable-grid-classes {\n @include make-grid-columns();\n}\n","/// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n@mixin make-container() {\n width: 100%;\n padding-right: ($grid-gutter-width / 2);\n padding-left: ($grid-gutter-width / 2);\n margin-right: auto;\n margin-left: auto;\n}\n\n\n// For each breakpoint, define the maximum width of the container in a media query\n@mixin make-container-max-widths($max-widths: $container-max-widths, $breakpoints: $grid-breakpoints) {\n @each $breakpoint, $container-max-width in $max-widths {\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n max-width: $container-max-width;\n }\n }\n}\n\n@mixin make-row() {\n display: flex;\n flex-wrap: wrap;\n margin-right: ($grid-gutter-width / -2);\n margin-left: ($grid-gutter-width / -2);\n}\n\n@mixin make-col-ready() {\n position: relative;\n // Prevent columns from becoming too narrow when at smaller grid tiers by\n // always setting `width: 100%;`. This works because we use `flex` values\n // later on to override this initial width.\n width: 100%;\n min-height: 1px; // Prevent collapsing\n padding-right: ($grid-gutter-width / 2);\n padding-left: ($grid-gutter-width / 2);\n}\n\n@mixin make-col($size, $columns: $grid-columns) {\n flex: 0 0 percentage($size / $columns);\n // Add a `max-width` to ensure content within each column does not blow out\n // the width of the column. Applies to IE10+ and Firefox. Chrome and Safari\n // do not appear to require this.\n max-width: percentage($size / $columns);\n}\n\n@mixin make-col-offset($size, $columns: $grid-columns) {\n $num: $size / $columns;\n margin-left: if($num == 0, 0, percentage($num));\n}\n","// Breakpoint viewport sizes and media queries.\n//\n// Breakpoints are defined as a map of (name: minimum width), order from small to large:\n//\n// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)\n//\n// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default.\n\n// Name of the next breakpoint, or null for the last breakpoint.\n//\n// >> breakpoint-next(sm)\n// md\n// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// md\n// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl))\n// md\n@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {\n $n: index($breakpoint-names, $name);\n @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);\n}\n\n// Minimum breakpoint width. Null for the smallest (first) breakpoint.\n//\n// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// 576px\n@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {\n $min: map-get($breakpoints, $name);\n @return if($min != 0, $min, null);\n}\n\n// Maximum breakpoint width. Null for the largest (last) breakpoint.\n// The maximum value is calculated as the minimum of the next one less 0.02px\n// to work around the limitations of `min-` and `max-` prefixes and viewports with fractional widths.\n// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max\n// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari.\n// See https://bugs.webkit.org/show_bug.cgi?id=178261\n//\n// >> breakpoint-max(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// 767.98px\n@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {\n $next: breakpoint-next($name, $breakpoints);\n @return if($next, breakpoint-min($next, $breakpoints) - .02px, null);\n}\n\n// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash infront.\n// Useful for making responsive utilities.\n//\n// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// \"\" (Returns a blank string)\n// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// \"-sm\"\n@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {\n @return if(breakpoint-min($name, $breakpoints) == null, \"\", \"-#{$name}\");\n}\n\n// Media of at least the minimum breakpoint width. No query for the smallest breakpoint.\n// Makes the @content apply to the given breakpoint and wider.\n@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n @if $min {\n @media (min-width: $min) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media of at most the maximum breakpoint width. No query for the largest breakpoint.\n// Makes the @content apply to the given breakpoint and narrower.\n@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {\n $max: breakpoint-max($name, $breakpoints);\n @if $max {\n @media (max-width: $max) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media that spans multiple breakpoint widths.\n// Makes the @content apply between the min and max breakpoints\n@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($lower, $breakpoints);\n $max: breakpoint-max($upper, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($lower, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($upper, $breakpoints) {\n @content;\n }\n }\n}\n\n// Media between the breakpoint's minimum and maximum widths.\n// No minimum for the smallest breakpoint, and no maximum for the largest one.\n// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower.\n@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n $max: breakpoint-max($name, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($name, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($name, $breakpoints) {\n @content;\n }\n }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `$grid-columns`.\n\n@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) {\n // Common properties for all breakpoints\n %grid-column {\n position: relative;\n width: 100%;\n min-height: 1px; // Prevent columns from collapsing when empty\n padding-right: ($gutter / 2);\n padding-left: ($gutter / 2);\n }\n\n @each $breakpoint in map-keys($breakpoints) {\n $infix: breakpoint-infix($breakpoint, $breakpoints);\n\n // Allow columns to stretch full width below their breakpoints\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @extend %grid-column;\n }\n }\n .col#{$infix},\n .col#{$infix}-auto {\n @extend %grid-column;\n }\n\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n // Provide basic `.col-{bp}` classes for equal-width flexbox columns\n .col#{$infix} {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col#{$infix}-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none; // Reset earlier grid tiers\n }\n\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @include make-col($i, $columns);\n }\n }\n\n .order#{$infix}-first { order: -1; }\n\n .order#{$infix}-last { order: $columns + 1; }\n\n @for $i from 0 through $columns {\n .order#{$infix}-#{$i} { order: $i; }\n }\n\n // `$columns - 1` because offsetting by the width of an entire row isn't possible\n @for $i from 0 through ($columns - 1) {\n @if not ($infix == \"\" and $i == 0) { // Avoid emitting useless .offset-0\n .offset#{$infix}-#{$i} {\n @include make-col-offset($i, $columns);\n }\n }\n }\n }\n }\n}\n","//\n// Basic Bootstrap table\n//\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: $spacer;\n background-color: $table-bg; // Reset for nesting within parents with `background-color`.\n\n th,\n td {\n padding: $table-cell-padding;\n vertical-align: top;\n border-top: $table-border-width solid $table-border-color;\n }\n\n thead th {\n vertical-align: bottom;\n border-bottom: (2 * $table-border-width) solid $table-border-color;\n }\n\n tbody + tbody {\n border-top: (2 * $table-border-width) solid $table-border-color;\n }\n\n .table {\n background-color: $body-bg;\n }\n}\n\n\n//\n// Condensed table w/ half padding\n//\n\n.table-sm {\n th,\n td {\n padding: $table-cell-padding-sm;\n }\n}\n\n\n// Border versions\n//\n// Add or remove borders all around the table and between all the columns.\n\n.table-bordered {\n border: $table-border-width solid $table-border-color;\n\n th,\n td {\n border: $table-border-width solid $table-border-color;\n }\n\n thead {\n th,\n td {\n border-bottom-width: (2 * $table-border-width);\n }\n }\n}\n\n.table-borderless {\n th,\n td,\n thead th,\n tbody + tbody {\n border: 0;\n }\n}\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n tbody tr:nth-of-type(#{$table-striped-order}) {\n background-color: $table-accent-bg;\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n tbody tr {\n @include hover {\n background-color: $table-hover-bg;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n@each $color, $value in $theme-colors {\n @include table-row-variant($color, theme-color-level($color, -9));\n}\n\n@include table-row-variant(active, $table-active-bg);\n\n\n// Dark styles\n//\n// Same table markup, but inverted color scheme: dark background and light text.\n\n// stylelint-disable-next-line no-duplicate-selectors\n.table {\n .thead-dark {\n th {\n color: $table-dark-color;\n background-color: $table-dark-bg;\n border-color: $table-dark-border-color;\n }\n }\n\n .thead-light {\n th {\n color: $table-head-color;\n background-color: $table-head-bg;\n border-color: $table-border-color;\n }\n }\n}\n\n.table-dark {\n color: $table-dark-color;\n background-color: $table-dark-bg;\n\n th,\n td,\n thead th {\n border-color: $table-dark-border-color;\n }\n\n &.table-bordered {\n border: 0;\n }\n\n &.table-striped {\n tbody tr:nth-of-type(odd) {\n background-color: $table-dark-accent-bg;\n }\n }\n\n &.table-hover {\n tbody tr {\n @include hover {\n background-color: $table-dark-hover-bg;\n }\n }\n }\n}\n\n\n// Responsive tables\n//\n// Generate series of `.table-responsive-*` classes for configuring the screen\n// size of where your table will overflow.\n\n.table-responsive {\n @each $breakpoint in map-keys($grid-breakpoints) {\n $next: breakpoint-next($breakpoint, $grid-breakpoints);\n $infix: breakpoint-infix($next, $grid-breakpoints);\n\n &#{$infix} {\n @include media-breakpoint-down($breakpoint) {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar; // See https://github.com/twbs/bootstrap/pull/10057\n\n // Prevent double border on horizontal scroll due to use of `display: block;`\n > .table-bordered {\n border: 0;\n }\n }\n }\n }\n}\n","// Tables\n\n@mixin table-row-variant($state, $background) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table-#{$state} {\n &,\n > th,\n > td {\n background-color: $background;\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover {\n $hover-background: darken($background, 5%);\n\n .table-#{$state} {\n @include hover {\n background-color: $hover-background;\n\n > td,\n > th {\n background-color: $hover-background;\n }\n }\n }\n }\n}\n","// stylelint-disable selector-no-qualifying-type\n\n//\n// Textual form controls\n//\n\n.form-control {\n display: block;\n width: 100%;\n padding: $input-padding-y $input-padding-x;\n font-size: $font-size-base;\n line-height: $input-line-height;\n color: $input-color;\n background-color: $input-bg;\n background-clip: padding-box;\n border: $input-border-width solid $input-border-color;\n\n // Note: This has no effect on `s in CSS.\n @if $enable-rounded {\n // Manually use the if/else instead of the mixin to account for iOS override\n border-radius: $input-border-radius;\n } @else {\n // Otherwise undo the iOS default\n border-radius: 0;\n }\n\n @include box-shadow($input-box-shadow);\n @include transition($input-transition);\n\n // Unstyle the caret on ` receives focus\n // in IE and (under certain conditions) Edge, as it looks bad and cannot be made to\n // match the appearance of the native widget.\n // See https://github.com/twbs/bootstrap/issues/19398.\n color: $input-color;\n background-color: $input-bg;\n }\n}\n\n// Make file inputs better match text inputs by forcing them to new lines.\n.form-control-file,\n.form-control-range {\n display: block;\n width: 100%;\n}\n\n\n//\n// Labels\n//\n\n// For use with horizontal and inline forms, when you need the label (or legend)\n// text to align with the form controls.\n.col-form-label {\n padding-top: calc(#{$input-padding-y} + #{$input-border-width});\n padding-bottom: calc(#{$input-padding-y} + #{$input-border-width});\n margin-bottom: 0; // Override the `
- - + diff --git a/htdocs/js/bootstrap-notify.js b/htdocs/js/bootstrap-notify.js deleted file mode 100644 index b942f15..0000000 --- a/htdocs/js/bootstrap-notify.js +++ /dev/null @@ -1,349 +0,0 @@ -/* -* Project: Bootstrap Notify = v3.1.3 -* Description: Turns standard Bootstrap alerts into "Growl-like" notifications. -* Author: Mouse0270 aka Robert McIntosh -* License: MIT License -* Website: https://github.com/mouse0270/bootstrap-growl -*/ -(function (factory) { - if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define(['jquery'], factory); - } else if (typeof exports === 'object') { - // Node/CommonJS - factory(require('jquery')); - } else { - // Browser globals - factory(jQuery); - } -}(function ($) { - // Create the defaults once - var defaults = { - element: 'body', - position: null, - type: "info", - allow_dismiss: true, - newest_on_top: false, - showProgressbar: false, - placement: { - from: "top", - align: "right" - }, - offset: 20, - spacing: 10, - z_index: 1031, - delay: 5000, - timer: 1000, - url_target: '_blank', - mouse_over: null, - animate: { - enter: 'animated fadeInDown', - exit: 'animated fadeOutUp' - }, - onShow: null, - onShown: null, - onClose: null, - onClosed: null, - icon_type: 'class', - template: '' - }; - - String.format = function() { - var str = arguments[0]; - for (var i = 1; i < arguments.length; i++) { - str = str.replace(RegExp("\\{" + (i - 1) + "\\}", "gm"), arguments[i]); - } - return str; - }; - - function Notify ( element, content, options ) { - // Setup Content of Notify - var content = { - content: { - message: typeof content == 'object' ? content.message : content, - title: content.title ? content.title : '', - icon: content.icon ? content.icon : '', - url: content.url ? content.url : '#', - target: content.target ? content.target : '-' - } - }; - - options = $.extend(true, {}, content, options); - this.settings = $.extend(true, {}, defaults, options); - this._defaults = defaults; - if (this.settings.content.target == "-") { - this.settings.content.target = this.settings.url_target; - } - this.animations = { - start: 'webkitAnimationStart oanimationstart MSAnimationStart animationstart', - end: 'webkitAnimationEnd oanimationend MSAnimationEnd animationend' - } - - if (typeof this.settings.offset == 'number') { - this.settings.offset = { - x: this.settings.offset, - y: this.settings.offset - }; - } - this.init(); - }; - - $.extend(Notify.prototype, { - init: function () { - var self = this; - - this.buildNotify(); - if (this.settings.content.icon) { - this.setIcon(); - } - if (this.settings.content.url != "#") { - this.styleURL(); - } - this.placement(); - this.bind(); - - this.notify = { - $ele: this.$ele, - update: function(command, update) { - var commands = {}; - if (typeof command == "string") { - commands[command] = update; - }else{ - commands = command; - } - for (var command in commands) { - switch (command) { - case "type": - this.$ele.removeClass('alert-' + self.settings.type); - this.$ele.find('[data-notify="progressbar"] > .progress-bar').removeClass('progress-bar-' + self.settings.type); - self.settings.type = commands[command]; - this.$ele.addClass('alert-' + commands[command]).find('[data-notify="progressbar"] > .progress-bar').addClass('progress-bar-' + commands[command]); - break; - case "icon": - var $icon = this.$ele.find('[data-notify="icon"]'); - if (self.settings.icon_type.toLowerCase() == 'class') { - $icon.removeClass(self.settings.content.icon).addClass(commands[command]); - }else{ - if (!$icon.is('img')) { - $icon.find('img'); - } - $icon.attr('src', commands[command]); - } - break; - case "progress": - var newDelay = self.settings.delay - (self.settings.delay * (commands[command] / 100)); - this.$ele.data('notify-delay', newDelay); - this.$ele.find('[data-notify="progressbar"] > div').attr('aria-valuenow', commands[command]).css('width', commands[command] + '%'); - break; - case "url": - this.$ele.find('[data-notify="url"]').attr('href', commands[command]); - break; - case "target": - this.$ele.find('[data-notify="url"]').attr('target', commands[command]); - break; - default: - this.$ele.find('[data-notify="' + command +'"]').html(commands[command]); - }; - } - var posX = this.$ele.outerHeight() + parseInt(self.settings.spacing) + parseInt(self.settings.offset.y); - self.reposition(posX); - }, - close: function() { - self.close(); - } - }; - }, - buildNotify: function () { - var content = this.settings.content; - this.$ele = $(String.format(this.settings.template, this.settings.type, content.title, content.message, content.url, content.target)); - this.$ele.attr('data-notify-position', this.settings.placement.from + '-' + this.settings.placement.align); - if (!this.settings.allow_dismiss) { - this.$ele.find('[data-notify="dismiss"]').css('display', 'none'); - } - if ((this.settings.delay <= 0 && !this.settings.showProgressbar) || !this.settings.showProgressbar) { - this.$ele.find('[data-notify="progressbar"]').remove(); - } - }, - setIcon: function() { - if (this.settings.icon_type.toLowerCase() == 'class') { - this.$ele.find('[data-notify="icon"]').addClass(this.settings.content.icon); - }else{ - if (this.$ele.find('[data-notify="icon"]').is('img')) { - this.$ele.find('[data-notify="icon"]').attr('src', this.settings.content.icon); - }else{ - this.$ele.find('[data-notify="icon"]').append('Notify Icon'); - } - } - }, - styleURL: function() { - this.$ele.find('[data-notify="url"]').css({ - backgroundImage: 'url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)', - height: '100%', - left: '0px', - position: 'absolute', - top: '0px', - width: '100%', - zIndex: this.settings.z_index + 1 - }); - this.$ele.find('[data-notify="dismiss"]').css({ - position: 'absolute', - right: '10px', - top: '5px', - zIndex: this.settings.z_index + 2 - }); - }, - placement: function() { - var self = this, - offsetAmt = this.settings.offset.y, - css = { - display: 'inline-block', - margin: '0px auto', - position: this.settings.position ? this.settings.position : (this.settings.element === 'body' ? 'fixed' : 'absolute'), - transition: 'all .5s ease-in-out', - zIndex: this.settings.z_index - }, - hasAnimation = false, - settings = this.settings; - - $('[data-notify-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]:not([data-closing="true"])').each(function() { - return offsetAmt = Math.max(offsetAmt, parseInt($(this).css(settings.placement.from)) + parseInt($(this).outerHeight()) + parseInt(settings.spacing)); - }); - if (this.settings.newest_on_top == true) { - offsetAmt = this.settings.offset.y; - } - css[this.settings.placement.from] = offsetAmt+'px'; - - switch (this.settings.placement.align) { - case "left": - case "right": - css[this.settings.placement.align] = this.settings.offset.x+'px'; - break; - case "center": - css.left = 0; - css.right = 0; - break; - } - this.$ele.css(css).addClass(this.settings.animate.enter); - $.each(Array('webkit', 'moz', 'o', 'ms', ''), function(index, prefix) { - self.$ele[0].style[prefix+'AnimationIterationCount'] = 1; - }); - - $(this.settings.element).append(this.$ele); - - if (this.settings.newest_on_top == true) { - offsetAmt = (parseInt(offsetAmt)+parseInt(this.settings.spacing)) + this.$ele.outerHeight(); - this.reposition(offsetAmt); - } - - if ($.isFunction(self.settings.onShow)) { - self.settings.onShow.call(this.$ele); - } - - this.$ele.one(this.animations.start, function(event) { - hasAnimation = true; - }).one(this.animations.end, function(event) { - if ($.isFunction(self.settings.onShown)) { - self.settings.onShown.call(this); - } - }); - - setTimeout(function() { - if (!hasAnimation) { - if ($.isFunction(self.settings.onShown)) { - self.settings.onShown.call(this); - } - } - }, 600); - }, - bind: function() { - var self = this; - - this.$ele.find('[data-notify="dismiss"]').on('click', function() { - self.close(); - }) - - this.$ele.mouseover(function(e) { - $(this).data('data-hover', "true"); - }).mouseout(function(e) { - $(this).data('data-hover', "false"); - }); - this.$ele.data('data-hover', "false"); - - if (this.settings.delay > 0) { - self.$ele.data('notify-delay', self.settings.delay); - var timer = setInterval(function() { - var delay = parseInt(self.$ele.data('notify-delay')) - self.settings.timer; - if ((self.$ele.data('data-hover') === 'false' && self.settings.mouse_over == "pause") || self.settings.mouse_over != "pause") { - var percent = ((self.settings.delay - delay) / self.settings.delay) * 100; - self.$ele.data('notify-delay', delay); - self.$ele.find('[data-notify="progressbar"] > div').attr('aria-valuenow', percent).css('width', percent + '%'); - } - if (delay <= -(self.settings.timer)) { - clearInterval(timer); - self.close(); - } - }, self.settings.timer); - } - }, - close: function() { - var self = this, - $successors = null, - posX = parseInt(this.$ele.css(this.settings.placement.from)), - hasAnimation = false; - - this.$ele.data('closing', 'true').addClass(this.settings.animate.exit); - self.reposition(posX); - - if ($.isFunction(self.settings.onClose)) { - self.settings.onClose.call(this.$ele); - } - - this.$ele.one(this.animations.start, function(event) { - hasAnimation = true; - }).one(this.animations.end, function(event) { - $(this).remove(); - if ($.isFunction(self.settings.onClosed)) { - self.settings.onClosed.call(this); - } - }); - - setTimeout(function() { - if (!hasAnimation) { - self.$ele.remove(); - if (self.settings.onClosed) { - self.settings.onClosed(self.$ele); - } - } - }, 600); - }, - reposition: function(posX) { - var self = this, - notifies = '[data-notify-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]:not([data-closing="true"])', - $elements = this.$ele.nextAll(notifies); - if (this.settings.newest_on_top == true) { - $elements = this.$ele.prevAll(notifies); - } - $elements.each(function() { - $(this).css(self.settings.placement.from, posX); - posX = (parseInt(posX)+parseInt(self.settings.spacing)) + $(this).outerHeight(); - }); - } - }); - - $.notify = function ( content, options ) { - var plugin = new Notify( this, content, options ); - return plugin.notify; - }; - $.notifyDefaults = function( options ) { - defaults = $.extend(true, {}, defaults, options); - return defaults; - }; - $.notifyClose = function( command ) { - if (typeof command === "undefined" || command == "all") { - $('[data-notify]').find('[data-notify="dismiss"]').trigger('click'); - }else{ - $('[data-notify-position="'+command+'"]').find('[data-notify="dismiss"]').trigger('click'); - } - }; - -})); \ No newline at end of file diff --git a/htdocs/js/bootstrap-slider.js b/htdocs/js/bootstrap-slider.js deleted file mode 100644 index febbfbd..0000000 --- a/htdocs/js/bootstrap-slider.js +++ /dev/null @@ -1,1878 +0,0 @@ -/*! ======================================================= - VERSION 10.0.2 -========================================================= */ -"use strict"; - -var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; - -/*! ========================================================= - * bootstrap-slider.js - * - * Maintainers: - * Kyle Kemp - * - Twitter: @seiyria - * - Github: seiyria - * Rohit Kalkur - * - Twitter: @Rovolutionary - * - Github: rovolution - * - * ========================================================= - * - * bootstrap-slider is released under the MIT License - * Copyright (c) 2017 Kyle Kemp, Rohit Kalkur, and contributors - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * ========================================================= */ - -/** - * Bridget makes jQuery widgets - * v1.0.1 - * MIT license - */ -var windowIsDefined = (typeof window === "undefined" ? "undefined" : _typeof(window)) === "object"; - -(function (factory) { - if (typeof define === "function" && define.amd) { - define(["jquery"], factory); - } else if ((typeof module === "undefined" ? "undefined" : _typeof(module)) === "object" && module.exports) { - var jQuery; - try { - jQuery = require("jquery"); - } catch (err) { - jQuery = null; - } - module.exports = factory(jQuery); - } else if (window) { - window.Slider = factory(window.jQuery); - } -})(function ($) { - // Constants - var NAMESPACE_MAIN = 'slider'; - var NAMESPACE_ALTERNATE = 'bootstrapSlider'; - - // Polyfill console methods - if (windowIsDefined && !window.console) { - window.console = {}; - } - if (windowIsDefined && !window.console.log) { - window.console.log = function () {}; - } - if (windowIsDefined && !window.console.warn) { - window.console.warn = function () {}; - } - - // Reference to Slider constructor - var Slider; - - (function ($) { - - 'use strict'; - - // -------------------------- utils -------------------------- // - - var slice = Array.prototype.slice; - - function noop() {} - - // -------------------------- definition -------------------------- // - - function defineBridget($) { - - // bail if no jQuery - if (!$) { - return; - } - - // -------------------------- addOptionMethod -------------------------- // - - /** - * adds option method -> $().plugin('option', {...}) - * @param {Function} PluginClass - constructor class - */ - function addOptionMethod(PluginClass) { - // don't overwrite original option method - if (PluginClass.prototype.option) { - return; - } - - // option setter - PluginClass.prototype.option = function (opts) { - // bail out if not an object - if (!$.isPlainObject(opts)) { - return; - } - this.options = $.extend(true, this.options, opts); - }; - } - - // -------------------------- plugin bridge -------------------------- // - - // helper function for logging errors - // $.error breaks jQuery chaining - var logError = typeof console === 'undefined' ? noop : function (message) { - console.error(message); - }; - - /** - * jQuery plugin bridge, access methods like $elem.plugin('method') - * @param {String} namespace - plugin name - * @param {Function} PluginClass - constructor class - */ - function bridge(namespace, PluginClass) { - // add to jQuery fn namespace - $.fn[namespace] = function (options) { - if (typeof options === 'string') { - // call plugin method when first argument is a string - // get arguments for method - var args = slice.call(arguments, 1); - - for (var i = 0, len = this.length; i < len; i++) { - var elem = this[i]; - var instance = $.data(elem, namespace); - if (!instance) { - logError("cannot call methods on " + namespace + " prior to initialization; " + "attempted to call '" + options + "'"); - continue; - } - if (!$.isFunction(instance[options]) || options.charAt(0) === '_') { - logError("no such method '" + options + "' for " + namespace + " instance"); - continue; - } - - // trigger method with arguments - var returnValue = instance[options].apply(instance, args); - - // break look and return first value if provided - if (returnValue !== undefined && returnValue !== instance) { - return returnValue; - } - } - // return this if no return value - return this; - } else { - var objects = this.map(function () { - var instance = $.data(this, namespace); - if (instance) { - // apply options & init - instance.option(options); - instance._init(); - } else { - // initialize new instance - instance = new PluginClass(this, options); - $.data(this, namespace, instance); - } - return $(this); - }); - - if (!objects || objects.length > 1) { - return objects; - } else { - return objects[0]; - } - } - }; - } - - // -------------------------- bridget -------------------------- // - - /** - * converts a Prototypical class into a proper jQuery plugin - * the class must have a ._init method - * @param {String} namespace - plugin name, used in $().pluginName - * @param {Function} PluginClass - constructor class - */ - $.bridget = function (namespace, PluginClass) { - addOptionMethod(PluginClass); - bridge(namespace, PluginClass); - }; - - return $.bridget; - } - - // get jquery from browser global - defineBridget($); - })($); - - /************************************************* - BOOTSTRAP-SLIDER SOURCE CODE - **************************************************/ - - (function ($) { - - var ErrorMsgs = { - formatInvalidInputErrorMsg: function formatInvalidInputErrorMsg(input) { - return "Invalid input value '" + input + "' passed in"; - }, - callingContextNotSliderInstance: "Calling context element does not have instance of Slider bound to it. Check your code to make sure the JQuery object returned from the call to the slider() initializer is calling the method" - }; - - var SliderScale = { - linear: { - toValue: function toValue(percentage) { - var rawValue = percentage / 100 * (this.options.max - this.options.min); - var shouldAdjustWithBase = true; - if (this.options.ticks_positions.length > 0) { - var minv, - maxv, - minp, - maxp = 0; - for (var i = 1; i < this.options.ticks_positions.length; i++) { - if (percentage <= this.options.ticks_positions[i]) { - minv = this.options.ticks[i - 1]; - minp = this.options.ticks_positions[i - 1]; - maxv = this.options.ticks[i]; - maxp = this.options.ticks_positions[i]; - - break; - } - } - var partialPercentage = (percentage - minp) / (maxp - minp); - rawValue = minv + partialPercentage * (maxv - minv); - shouldAdjustWithBase = false; - } - - var adjustment = shouldAdjustWithBase ? this.options.min : 0; - var value = adjustment + Math.round(rawValue / this.options.step) * this.options.step; - if (value < this.options.min) { - return this.options.min; - } else if (value > this.options.max) { - return this.options.max; - } else { - return value; - } - }, - toPercentage: function toPercentage(value) { - if (this.options.max === this.options.min) { - return 0; - } - - if (this.options.ticks_positions.length > 0) { - var minv, - maxv, - minp, - maxp = 0; - for (var i = 0; i < this.options.ticks.length; i++) { - if (value <= this.options.ticks[i]) { - minv = i > 0 ? this.options.ticks[i - 1] : 0; - minp = i > 0 ? this.options.ticks_positions[i - 1] : 0; - maxv = this.options.ticks[i]; - maxp = this.options.ticks_positions[i]; - - break; - } - } - if (i > 0) { - var partialPercentage = (value - minv) / (maxv - minv); - return minp + partialPercentage * (maxp - minp); - } - } - - return 100 * (value - this.options.min) / (this.options.max - this.options.min); - } - }, - - logarithmic: { - /* Based on http://stackoverflow.com/questions/846221/logarithmic-slider */ - toValue: function toValue(percentage) { - var min = this.options.min === 0 ? 0 : Math.log(this.options.min); - var max = Math.log(this.options.max); - var value = Math.exp(min + (max - min) * percentage / 100); - if (Math.round(value) === this.options.max) { - return this.options.max; - } - value = this.options.min + Math.round((value - this.options.min) / this.options.step) * this.options.step; - /* Rounding to the nearest step could exceed the min or - * max, so clip to those values. */ - if (value < this.options.min) { - return this.options.min; - } else if (value > this.options.max) { - return this.options.max; - } else { - return value; - } - }, - toPercentage: function toPercentage(value) { - if (this.options.max === this.options.min) { - return 0; - } else { - var max = Math.log(this.options.max); - var min = this.options.min === 0 ? 0 : Math.log(this.options.min); - var v = value === 0 ? 0 : Math.log(value); - return 100 * (v - min) / (max - min); - } - } - } - }; - - /************************************************* - CONSTRUCTOR - **************************************************/ - Slider = function Slider(element, options) { - createNewSlider.call(this, element, options); - return this; - }; - - function createNewSlider(element, options) { - - /* - The internal state object is used to store data about the current 'state' of slider. - This includes values such as the `value`, `enabled`, etc... - */ - this._state = { - value: null, - enabled: null, - offset: null, - size: null, - percentage: null, - inDrag: false, - over: false - }; - - // The objects used to store the reference to the tick methods if ticks_tooltip is on - this.ticksCallbackMap = {}; - this.handleCallbackMap = {}; - - if (typeof element === "string") { - this.element = document.querySelector(element); - } else if (element instanceof HTMLElement) { - this.element = element; - } - - /************************************************* - Process Options - **************************************************/ - options = options ? options : {}; - var optionTypes = Object.keys(this.defaultOptions); - - for (var i = 0; i < optionTypes.length; i++) { - var optName = optionTypes[i]; - - // First check if an option was passed in via the constructor - var val = options[optName]; - // If no data attrib, then check data atrributes - val = typeof val !== 'undefined' ? val : getDataAttrib(this.element, optName); - // Finally, if nothing was specified, use the defaults - val = val !== null ? val : this.defaultOptions[optName]; - - // Set all options on the instance of the Slider - if (!this.options) { - this.options = {}; - } - this.options[optName] = val; - } - - // Check options.rtl - if (this.options.rtl === 'auto') { - this.options.rtl = window.getComputedStyle(this.element).direction === 'rtl'; - } - - /* - Validate `tooltip_position` against 'orientation` - - if `tooltip_position` is incompatible with orientation, swith it to a default compatible with specified `orientation` - -- default for "vertical" -> "right", "left" if rtl - -- default for "horizontal" -> "top" - */ - if (this.options.orientation === "vertical" && (this.options.tooltip_position === "top" || this.options.tooltip_position === "bottom")) { - if (this.options.rtl) { - this.options.tooltip_position = "left"; - } else { - this.options.tooltip_position = "right"; - } - } else if (this.options.orientation === "horizontal" && (this.options.tooltip_position === "left" || this.options.tooltip_position === "right")) { - - this.options.tooltip_position = "top"; - } - - function getDataAttrib(element, optName) { - var dataName = "data-slider-" + optName.replace(/_/g, '-'); - var dataValString = element.getAttribute(dataName); - - try { - return JSON.parse(dataValString); - } catch (err) { - return dataValString; - } - } - - /************************************************* - Create Markup - **************************************************/ - - var origWidth = this.element.style.width; - var updateSlider = false; - var parent = this.element.parentNode; - var sliderTrackSelection; - var sliderTrackLow, sliderTrackHigh; - var sliderMinHandle; - var sliderMaxHandle; - - if (this.sliderElem) { - updateSlider = true; - } else { - /* Create elements needed for slider */ - this.sliderElem = document.createElement("div"); - this.sliderElem.className = "slider"; - - /* Create slider track elements */ - var sliderTrack = document.createElement("div"); - sliderTrack.className = "slider-track"; - - sliderTrackLow = document.createElement("div"); - sliderTrackLow.className = "slider-track-low"; - - sliderTrackSelection = document.createElement("div"); - sliderTrackSelection.className = "slider-selection"; - - sliderTrackHigh = document.createElement("div"); - sliderTrackHigh.className = "slider-track-high"; - - sliderMinHandle = document.createElement("div"); - sliderMinHandle.className = "slider-handle min-slider-handle"; - sliderMinHandle.setAttribute('role', 'slider'); - sliderMinHandle.setAttribute('aria-valuemin', this.options.min); - sliderMinHandle.setAttribute('aria-valuemax', this.options.max); - - sliderMaxHandle = document.createElement("div"); - sliderMaxHandle.className = "slider-handle max-slider-handle"; - sliderMaxHandle.setAttribute('role', 'slider'); - sliderMaxHandle.setAttribute('aria-valuemin', this.options.min); - sliderMaxHandle.setAttribute('aria-valuemax', this.options.max); - - sliderTrack.appendChild(sliderTrackLow); - sliderTrack.appendChild(sliderTrackSelection); - sliderTrack.appendChild(sliderTrackHigh); - - /* Create highlight range elements */ - this.rangeHighlightElements = []; - var rangeHighlightsOpts = this.options.rangeHighlights; - if (Array.isArray(rangeHighlightsOpts) && rangeHighlightsOpts.length > 0) { - for (var j = 0; j < rangeHighlightsOpts.length; j++) { - var rangeHighlightElement = document.createElement("div"); - var customClassString = rangeHighlightsOpts[j].class || ""; - rangeHighlightElement.className = "slider-rangeHighlight slider-selection " + customClassString; - this.rangeHighlightElements.push(rangeHighlightElement); - sliderTrack.appendChild(rangeHighlightElement); - } - } - - /* Add aria-labelledby to handle's */ - var isLabelledbyArray = Array.isArray(this.options.labelledby); - if (isLabelledbyArray && this.options.labelledby[0]) { - sliderMinHandle.setAttribute('aria-labelledby', this.options.labelledby[0]); - } - if (isLabelledbyArray && this.options.labelledby[1]) { - sliderMaxHandle.setAttribute('aria-labelledby', this.options.labelledby[1]); - } - if (!isLabelledbyArray && this.options.labelledby) { - sliderMinHandle.setAttribute('aria-labelledby', this.options.labelledby); - sliderMaxHandle.setAttribute('aria-labelledby', this.options.labelledby); - } - - /* Create ticks */ - this.ticks = []; - if (Array.isArray(this.options.ticks) && this.options.ticks.length > 0) { - this.ticksContainer = document.createElement('div'); - this.ticksContainer.className = 'slider-tick-container'; - - for (i = 0; i < this.options.ticks.length; i++) { - var tick = document.createElement('div'); - tick.className = 'slider-tick'; - if (this.options.ticks_tooltip) { - var tickListenerReference = this._addTickListener(); - var enterCallback = tickListenerReference.addMouseEnter(this, tick, i); - var leaveCallback = tickListenerReference.addMouseLeave(this, tick); - - this.ticksCallbackMap[i] = { - mouseEnter: enterCallback, - mouseLeave: leaveCallback - }; - } - this.ticks.push(tick); - this.ticksContainer.appendChild(tick); - } - - sliderTrackSelection.className += " tick-slider-selection"; - } - - this.tickLabels = []; - if (Array.isArray(this.options.ticks_labels) && this.options.ticks_labels.length > 0) { - this.tickLabelContainer = document.createElement('div'); - this.tickLabelContainer.className = 'slider-tick-label-container'; - - for (i = 0; i < this.options.ticks_labels.length; i++) { - var label = document.createElement('div'); - var noTickPositionsSpecified = this.options.ticks_positions.length === 0; - var tickLabelsIndex = this.options.reversed && noTickPositionsSpecified ? this.options.ticks_labels.length - (i + 1) : i; - label.className = 'slider-tick-label'; - label.innerHTML = this.options.ticks_labels[tickLabelsIndex]; - - this.tickLabels.push(label); - this.tickLabelContainer.appendChild(label); - } - } - - var createAndAppendTooltipSubElements = function createAndAppendTooltipSubElements(tooltipElem) { - var arrow = document.createElement("div"); - arrow.className = "tooltip-arrow"; - - var inner = document.createElement("div"); - inner.className = "tooltip-inner"; - - tooltipElem.appendChild(arrow); - tooltipElem.appendChild(inner); - }; - - /* Create tooltip elements */ - var sliderTooltip = document.createElement("div"); - sliderTooltip.className = "tooltip tooltip-main"; - sliderTooltip.setAttribute('role', 'presentation'); - createAndAppendTooltipSubElements(sliderTooltip); - - var sliderTooltipMin = document.createElement("div"); - sliderTooltipMin.className = "tooltip tooltip-min"; - sliderTooltipMin.setAttribute('role', 'presentation'); - createAndAppendTooltipSubElements(sliderTooltipMin); - - var sliderTooltipMax = document.createElement("div"); - sliderTooltipMax.className = "tooltip tooltip-max"; - sliderTooltipMax.setAttribute('role', 'presentation'); - createAndAppendTooltipSubElements(sliderTooltipMax); - - /* Append components to sliderElem */ - this.sliderElem.appendChild(sliderTrack); - this.sliderElem.appendChild(sliderTooltip); - this.sliderElem.appendChild(sliderTooltipMin); - this.sliderElem.appendChild(sliderTooltipMax); - - if (this.tickLabelContainer) { - this.sliderElem.appendChild(this.tickLabelContainer); - } - if (this.ticksContainer) { - this.sliderElem.appendChild(this.ticksContainer); - } - - this.sliderElem.appendChild(sliderMinHandle); - this.sliderElem.appendChild(sliderMaxHandle); - - /* Append slider element to parent container, right before the original element */ - parent.insertBefore(this.sliderElem, this.element); - - /* Hide original element */ - this.element.style.display = "none"; - } - /* If JQuery exists, cache JQ references */ - if ($) { - this.$element = $(this.element); - this.$sliderElem = $(this.sliderElem); - } - - /************************************************* - Setup - **************************************************/ - this.eventToCallbackMap = {}; - this.sliderElem.id = this.options.id; - - this.touchCapable = 'ontouchstart' in window || window.DocumentTouch && document instanceof window.DocumentTouch; - - this.touchX = 0; - this.touchY = 0; - - this.tooltip = this.sliderElem.querySelector('.tooltip-main'); - this.tooltipInner = this.tooltip.querySelector('.tooltip-inner'); - - this.tooltip_min = this.sliderElem.querySelector('.tooltip-min'); - this.tooltipInner_min = this.tooltip_min.querySelector('.tooltip-inner'); - - this.tooltip_max = this.sliderElem.querySelector('.tooltip-max'); - this.tooltipInner_max = this.tooltip_max.querySelector('.tooltip-inner'); - - if (SliderScale[this.options.scale]) { - this.options.scale = SliderScale[this.options.scale]; - } - - if (updateSlider === true) { - // Reset classes - this._removeClass(this.sliderElem, 'slider-horizontal'); - this._removeClass(this.sliderElem, 'slider-vertical'); - this._removeClass(this.sliderElem, 'slider-rtl'); - this._removeClass(this.tooltip, 'hide'); - this._removeClass(this.tooltip_min, 'hide'); - this._removeClass(this.tooltip_max, 'hide'); - - // Undo existing inline styles for track - ["left", "right", "top", "width", "height"].forEach(function (prop) { - this._removeProperty(this.trackLow, prop); - this._removeProperty(this.trackSelection, prop); - this._removeProperty(this.trackHigh, prop); - }, this); - - // Undo inline styles on handles - [this.handle1, this.handle2].forEach(function (handle) { - this._removeProperty(handle, 'left'); - this._removeProperty(handle, 'right'); - this._removeProperty(handle, 'top'); - }, this); - - // Undo inline styles and classes on tooltips - [this.tooltip, this.tooltip_min, this.tooltip_max].forEach(function (tooltip) { - this._removeProperty(tooltip, 'left'); - this._removeProperty(tooltip, 'right'); - this._removeProperty(tooltip, 'top'); - - this._removeClass(tooltip, 'right'); - this._removeClass(tooltip, 'left'); - this._removeClass(tooltip, 'top'); - }, this); - } - - if (this.options.orientation === 'vertical') { - this._addClass(this.sliderElem, 'slider-vertical'); - this.stylePos = 'top'; - this.mousePos = 'pageY'; - this.sizePos = 'offsetHeight'; - } else { - this._addClass(this.sliderElem, 'slider-horizontal'); - this.sliderElem.style.width = origWidth; - this.options.orientation = 'horizontal'; - if (this.options.rtl) { - this.stylePos = 'right'; - } else { - this.stylePos = 'left'; - } - this.mousePos = 'clientX'; - this.sizePos = 'offsetWidth'; - } - // specific rtl class - if (this.options.rtl) { - this._addClass(this.sliderElem, 'slider-rtl'); - } - this._setTooltipPosition(); - /* In case ticks are specified, overwrite the min and max bounds */ - if (Array.isArray(this.options.ticks) && this.options.ticks.length > 0) { - this.options.max = Math.max.apply(Math, this.options.ticks); - this.options.min = Math.min.apply(Math, this.options.ticks); - } - - if (Array.isArray(this.options.value)) { - this.options.range = true; - this._state.value = this.options.value; - } else if (this.options.range) { - // User wants a range, but value is not an array - this._state.value = [this.options.value, this.options.max]; - } else { - this._state.value = this.options.value; - } - - this.trackLow = sliderTrackLow || this.trackLow; - this.trackSelection = sliderTrackSelection || this.trackSelection; - this.trackHigh = sliderTrackHigh || this.trackHigh; - - if (this.options.selection === 'none') { - this._addClass(this.trackLow, 'hide'); - this._addClass(this.trackSelection, 'hide'); - this._addClass(this.trackHigh, 'hide'); - } else if (this.options.selection === 'after' || this.options.selection === 'before') { - this._removeClass(this.trackLow, 'hide'); - this._removeClass(this.trackSelection, 'hide'); - this._removeClass(this.trackHigh, 'hide'); - } - - this.handle1 = sliderMinHandle || this.handle1; - this.handle2 = sliderMaxHandle || this.handle2; - - if (updateSlider === true) { - // Reset classes - this._removeClass(this.handle1, 'round triangle'); - this._removeClass(this.handle2, 'round triangle hide'); - - for (i = 0; i < this.ticks.length; i++) { - this._removeClass(this.ticks[i], 'round triangle hide'); - } - } - - var availableHandleModifiers = ['round', 'triangle', 'custom']; - var isValidHandleType = availableHandleModifiers.indexOf(this.options.handle) !== -1; - if (isValidHandleType) { - this._addClass(this.handle1, this.options.handle); - this._addClass(this.handle2, this.options.handle); - - for (i = 0; i < this.ticks.length; i++) { - this._addClass(this.ticks[i], this.options.handle); - } - } - - this._state.offset = this._offset(this.sliderElem); - this._state.size = this.sliderElem[this.sizePos]; - this.setValue(this._state.value); - - /****************************************** - Bind Event Listeners - ******************************************/ - - // Bind keyboard handlers - this.handle1Keydown = this._keydown.bind(this, 0); - this.handle1.addEventListener("keydown", this.handle1Keydown, false); - - this.handle2Keydown = this._keydown.bind(this, 1); - this.handle2.addEventListener("keydown", this.handle2Keydown, false); - - this.mousedown = this._mousedown.bind(this); - this.touchstart = this._touchstart.bind(this); - this.touchmove = this._touchmove.bind(this); - - if (this.touchCapable) { - // Test for passive event support - var supportsPassive = false; - try { - var opts = Object.defineProperty({}, 'passive', { - get: function get() { - supportsPassive = true; - } - }); - window.addEventListener("test", null, opts); - } catch (e) {} - // Use our detect's results. passive applied if supported, capture will be false either way. - var eventOptions = supportsPassive ? { passive: true } : false; - // Bind touch handlers - this.sliderElem.addEventListener("touchstart", this.touchstart, eventOptions); - this.sliderElem.addEventListener("touchmove", this.touchmove, eventOptions); - } - this.sliderElem.addEventListener("mousedown", this.mousedown, false); - - // Bind window handlers - this.resize = this._resize.bind(this); - window.addEventListener("resize", this.resize, false); - - // Bind tooltip-related handlers - if (this.options.tooltip === 'hide') { - this._addClass(this.tooltip, 'hide'); - this._addClass(this.tooltip_min, 'hide'); - this._addClass(this.tooltip_max, 'hide'); - } else if (this.options.tooltip === 'always') { - this._showTooltip(); - this._alwaysShowTooltip = true; - } else { - this.showTooltip = this._showTooltip.bind(this); - this.hideTooltip = this._hideTooltip.bind(this); - - if (this.options.ticks_tooltip) { - var callbackHandle = this._addTickListener(); - //create handle1 listeners and store references in map - var mouseEnter = callbackHandle.addMouseEnter(this, this.handle1); - var mouseLeave = callbackHandle.addMouseLeave(this, this.handle1); - this.handleCallbackMap.handle1 = { - mouseEnter: mouseEnter, - mouseLeave: mouseLeave - }; - //create handle2 listeners and store references in map - mouseEnter = callbackHandle.addMouseEnter(this, this.handle2); - mouseLeave = callbackHandle.addMouseLeave(this, this.handle2); - this.handleCallbackMap.handle2 = { - mouseEnter: mouseEnter, - mouseLeave: mouseLeave - }; - } else { - this.sliderElem.addEventListener("mouseenter", this.showTooltip, false); - this.sliderElem.addEventListener("mouseleave", this.hideTooltip, false); - } - - this.handle1.addEventListener("focus", this.showTooltip, false); - this.handle1.addEventListener("blur", this.hideTooltip, false); - - this.handle2.addEventListener("focus", this.showTooltip, false); - this.handle2.addEventListener("blur", this.hideTooltip, false); - } - - if (this.options.enabled) { - this.enable(); - } else { - this.disable(); - } - } - - /************************************************* - INSTANCE PROPERTIES/METHODS - - Any methods bound to the prototype are considered - part of the plugin's `public` interface - **************************************************/ - Slider.prototype = { - _init: function _init() {}, // NOTE: Must exist to support bridget - - constructor: Slider, - - defaultOptions: { - id: "", - min: 0, - max: 10, - step: 1, - precision: 0, - orientation: 'horizontal', - value: 5, - range: false, - selection: 'before', - tooltip: 'show', - tooltip_split: false, - handle: 'round', - reversed: false, - rtl: 'auto', - enabled: true, - formatter: function formatter(val) { - if (Array.isArray(val)) { - return val[0] + " : " + val[1]; - } else { - return val; - } - }, - natural_arrow_keys: false, - ticks: [], - ticks_positions: [], - ticks_labels: [], - ticks_snap_bounds: 0, - ticks_tooltip: false, - scale: 'linear', - focus: false, - tooltip_position: null, - labelledby: null, - rangeHighlights: [] - }, - - getElement: function getElement() { - return this.sliderElem; - }, - - getValue: function getValue() { - if (this.options.range) { - return this._state.value; - } else { - return this._state.value[0]; - } - }, - - setValue: function setValue(val, triggerSlideEvent, triggerChangeEvent) { - if (!val) { - val = 0; - } - var oldValue = this.getValue(); - this._state.value = this._validateInputValue(val); - var applyPrecision = this._applyPrecision.bind(this); - - if (this.options.range) { - this._state.value[0] = applyPrecision(this._state.value[0]); - this._state.value[1] = applyPrecision(this._state.value[1]); - - this._state.value[0] = Math.max(this.options.min, Math.min(this.options.max, this._state.value[0])); - this._state.value[1] = Math.max(this.options.min, Math.min(this.options.max, this._state.value[1])); - } else { - this._state.value = applyPrecision(this._state.value); - this._state.value = [Math.max(this.options.min, Math.min(this.options.max, this._state.value))]; - this._addClass(this.handle2, 'hide'); - if (this.options.selection === 'after') { - this._state.value[1] = this.options.max; - } else { - this._state.value[1] = this.options.min; - } - } - - if (this.options.max > this.options.min) { - this._state.percentage = [this._toPercentage(this._state.value[0]), this._toPercentage(this._state.value[1]), this.options.step * 100 / (this.options.max - this.options.min)]; - } else { - this._state.percentage = [0, 0, 100]; - } - - this._layout(); - var newValue = this.options.range ? this._state.value : this._state.value[0]; - - this._setDataVal(newValue); - if (triggerSlideEvent === true) { - this._trigger('slide', newValue); - } - if (oldValue !== newValue && triggerChangeEvent === true) { - this._trigger('change', { - oldValue: oldValue, - newValue: newValue - }); - } - - return this; - }, - - destroy: function destroy() { - // Remove event handlers on slider elements - this._removeSliderEventHandlers(); - - // Remove the slider from the DOM - this.sliderElem.parentNode.removeChild(this.sliderElem); - /* Show original element */ - this.element.style.display = ""; - - // Clear out custom event bindings - this._cleanUpEventCallbacksMap(); - - // Remove data values - this.element.removeAttribute("data"); - - // Remove JQuery handlers/data - if ($) { - this._unbindJQueryEventHandlers(); - this.$element.removeData('slider'); - } - }, - - disable: function disable() { - this._state.enabled = false; - this.handle1.removeAttribute("tabindex"); - this.handle2.removeAttribute("tabindex"); - this._addClass(this.sliderElem, 'slider-disabled'); - this._trigger('slideDisabled'); - - return this; - }, - - enable: function enable() { - this._state.enabled = true; - this.handle1.setAttribute("tabindex", 0); - this.handle2.setAttribute("tabindex", 0); - this._removeClass(this.sliderElem, 'slider-disabled'); - this._trigger('slideEnabled'); - - return this; - }, - - toggle: function toggle() { - if (this._state.enabled) { - this.disable(); - } else { - this.enable(); - } - return this; - }, - - isEnabled: function isEnabled() { - return this._state.enabled; - }, - - on: function on(evt, callback) { - this._bindNonQueryEventHandler(evt, callback); - return this; - }, - - off: function off(evt, callback) { - if ($) { - this.$element.off(evt, callback); - this.$sliderElem.off(evt, callback); - } else { - this._unbindNonQueryEventHandler(evt, callback); - } - }, - - getAttribute: function getAttribute(attribute) { - if (attribute) { - return this.options[attribute]; - } else { - return this.options; - } - }, - - setAttribute: function setAttribute(attribute, value) { - this.options[attribute] = value; - return this; - }, - - refresh: function refresh() { - this._removeSliderEventHandlers(); - createNewSlider.call(this, this.element, this.options); - if ($) { - // Bind new instance of slider to the element - $.data(this.element, 'slider', this); - } - return this; - }, - - relayout: function relayout() { - this._resize(); - this._layout(); - return this; - }, - - /******************************+ - HELPERS - - Any method that is not part of the public interface. - - Place it underneath this comment block and write its signature like so: - _fnName : function() {...} - ********************************/ - _removeSliderEventHandlers: function _removeSliderEventHandlers() { - // Remove keydown event listeners - this.handle1.removeEventListener("keydown", this.handle1Keydown, false); - this.handle2.removeEventListener("keydown", this.handle2Keydown, false); - - //remove the listeners from the ticks and handles if they had their own listeners - if (this.options.ticks_tooltip) { - var ticks = this.ticksContainer.getElementsByClassName('slider-tick'); - for (var i = 0; i < ticks.length; i++) { - ticks[i].removeEventListener('mouseenter', this.ticksCallbackMap[i].mouseEnter, false); - ticks[i].removeEventListener('mouseleave', this.ticksCallbackMap[i].mouseLeave, false); - } - this.handle1.removeEventListener('mouseenter', this.handleCallbackMap.handle1.mouseEnter, false); - this.handle2.removeEventListener('mouseenter', this.handleCallbackMap.handle2.mouseEnter, false); - this.handle1.removeEventListener('mouseleave', this.handleCallbackMap.handle1.mouseLeave, false); - this.handle2.removeEventListener('mouseleave', this.handleCallbackMap.handle2.mouseLeave, false); - } - - this.handleCallbackMap = null; - this.ticksCallbackMap = null; - - if (this.showTooltip) { - this.handle1.removeEventListener("focus", this.showTooltip, false); - this.handle2.removeEventListener("focus", this.showTooltip, false); - } - if (this.hideTooltip) { - this.handle1.removeEventListener("blur", this.hideTooltip, false); - this.handle2.removeEventListener("blur", this.hideTooltip, false); - } - - // Remove event listeners from sliderElem - if (this.showTooltip) { - this.sliderElem.removeEventListener("mouseenter", this.showTooltip, false); - } - if (this.hideTooltip) { - this.sliderElem.removeEventListener("mouseleave", this.hideTooltip, false); - } - this.sliderElem.removeEventListener("touchstart", this.touchstart, false); - this.sliderElem.removeEventListener("touchmove", this.touchmove, false); - this.sliderElem.removeEventListener("mousedown", this.mousedown, false); - - // Remove window event listener - window.removeEventListener("resize", this.resize, false); - }, - _bindNonQueryEventHandler: function _bindNonQueryEventHandler(evt, callback) { - if (this.eventToCallbackMap[evt] === undefined) { - this.eventToCallbackMap[evt] = []; - } - this.eventToCallbackMap[evt].push(callback); - }, - _unbindNonQueryEventHandler: function _unbindNonQueryEventHandler(evt, callback) { - var callbacks = this.eventToCallbackMap[evt]; - if (callbacks !== undefined) { - for (var i = 0; i < callbacks.length; i++) { - if (callbacks[i] === callback) { - callbacks.splice(i, 1); - break; - } - } - } - }, - _cleanUpEventCallbacksMap: function _cleanUpEventCallbacksMap() { - var eventNames = Object.keys(this.eventToCallbackMap); - for (var i = 0; i < eventNames.length; i++) { - var eventName = eventNames[i]; - delete this.eventToCallbackMap[eventName]; - } - }, - _showTooltip: function _showTooltip() { - if (this.options.tooltip_split === false) { - this._addClass(this.tooltip, 'in'); - this.tooltip_min.style.display = 'none'; - this.tooltip_max.style.display = 'none'; - } else { - this._addClass(this.tooltip_min, 'in'); - this._addClass(this.tooltip_max, 'in'); - this.tooltip.style.display = 'none'; - } - this._state.over = true; - }, - _hideTooltip: function _hideTooltip() { - if (this._state.inDrag === false && this.alwaysShowTooltip !== true) { - this._removeClass(this.tooltip, 'in'); - this._removeClass(this.tooltip_min, 'in'); - this._removeClass(this.tooltip_max, 'in'); - } - this._state.over = false; - }, - _setToolTipOnMouseOver: function _setToolTipOnMouseOver(tempState) { - var formattedTooltipVal = this.options.formatter(!tempState ? this._state.value[0] : tempState.value[0]); - var positionPercentages = !tempState ? getPositionPercentages(this._state, this.options.reversed) : getPositionPercentages(tempState, this.options.reversed); - this._setText(this.tooltipInner, formattedTooltipVal); - - this.tooltip.style[this.stylePos] = positionPercentages[0] + "%"; - - function getPositionPercentages(state, reversed) { - if (reversed) { - return [100 - state.percentage[0], this.options.range ? 100 - state.percentage[1] : state.percentage[1]]; - } - return [state.percentage[0], state.percentage[1]]; - } - }, - _addTickListener: function _addTickListener() { - return { - addMouseEnter: function addMouseEnter(reference, tick, index) { - var enter = function enter() { - var tempState = reference._state; - var idString = index >= 0 ? index : this.attributes['aria-valuenow'].value; - var hoverIndex = parseInt(idString, 10); - tempState.value[0] = hoverIndex; - tempState.percentage[0] = reference.options.ticks_positions[hoverIndex]; - reference._setToolTipOnMouseOver(tempState); - reference._showTooltip(); - }; - tick.addEventListener("mouseenter", enter, false); - return enter; - }, - addMouseLeave: function addMouseLeave(reference, tick) { - var leave = function leave() { - reference._hideTooltip(); - }; - tick.addEventListener("mouseleave", leave, false); - return leave; - } - }; - }, - _layout: function _layout() { - var positionPercentages; - - if (this.options.reversed) { - positionPercentages = [100 - this._state.percentage[0], this.options.range ? 100 - this._state.percentage[1] : this._state.percentage[1]]; - } else { - positionPercentages = [this._state.percentage[0], this._state.percentage[1]]; - } - - this.handle1.style[this.stylePos] = positionPercentages[0] + "%"; - this.handle1.setAttribute('aria-valuenow', this._state.value[0]); - if (isNaN(this.options.formatter(this._state.value[0]))) { - this.handle1.setAttribute('aria-valuetext', this.options.formatter(this._state.value[0])); - } - - this.handle2.style[this.stylePos] = positionPercentages[1] + "%"; - this.handle2.setAttribute('aria-valuenow', this._state.value[1]); - if (isNaN(this.options.formatter(this._state.value[1]))) { - this.handle2.setAttribute('aria-valuetext', this.options.formatter(this._state.value[1])); - } - - /* Position highlight range elements */ - if (this.rangeHighlightElements.length > 0 && Array.isArray(this.options.rangeHighlights) && this.options.rangeHighlights.length > 0) { - for (var _i = 0; _i < this.options.rangeHighlights.length; _i++) { - var startPercent = this._toPercentage(this.options.rangeHighlights[_i].start); - var endPercent = this._toPercentage(this.options.rangeHighlights[_i].end); - - if (this.options.reversed) { - var sp = 100 - endPercent; - endPercent = 100 - startPercent; - startPercent = sp; - } - - var currentRange = this._createHighlightRange(startPercent, endPercent); - - if (currentRange) { - if (this.options.orientation === 'vertical') { - this.rangeHighlightElements[_i].style.top = currentRange.start + "%"; - this.rangeHighlightElements[_i].style.height = currentRange.size + "%"; - } else { - if (this.options.rtl) { - this.rangeHighlightElements[_i].style.right = currentRange.start + "%"; - } else { - this.rangeHighlightElements[_i].style.left = currentRange.start + "%"; - } - this.rangeHighlightElements[_i].style.width = currentRange.size + "%"; - } - } else { - this.rangeHighlightElements[_i].style.display = "none"; - } - } - } - - /* Position ticks and labels */ - if (Array.isArray(this.options.ticks) && this.options.ticks.length > 0) { - - var styleSize = this.options.orientation === 'vertical' ? 'height' : 'width'; - var styleMargin; - if (this.options.orientation === 'vertical') { - styleMargin = 'marginTop'; - } else { - if (this.options.rtl) { - styleMargin = 'marginRight'; - } else { - styleMargin = 'marginLeft'; - } - } - var labelSize = this._state.size / (this.options.ticks.length - 1); - - if (this.tickLabelContainer) { - var extraMargin = 0; - if (this.options.ticks_positions.length === 0) { - if (this.options.orientation !== 'vertical') { - this.tickLabelContainer.style[styleMargin] = -labelSize / 2 + "px"; - } - - extraMargin = this.tickLabelContainer.offsetHeight; - } else { - /* Chidren are position absolute, calculate height by finding the max offsetHeight of a child */ - for (i = 0; i < this.tickLabelContainer.childNodes.length; i++) { - if (this.tickLabelContainer.childNodes[i].offsetHeight > extraMargin) { - extraMargin = this.tickLabelContainer.childNodes[i].offsetHeight; - } - } - } - if (this.options.orientation === 'horizontal') { - this.sliderElem.style.marginBottom = extraMargin + "px"; - } - } - for (var i = 0; i < this.options.ticks.length; i++) { - - var percentage = this.options.ticks_positions[i] || this._toPercentage(this.options.ticks[i]); - - if (this.options.reversed) { - percentage = 100 - percentage; - } - - this.ticks[i].style[this.stylePos] = percentage + "%"; - - /* Set class labels to denote whether ticks are in the selection */ - this._removeClass(this.ticks[i], 'in-selection'); - if (!this.options.range) { - if (this.options.selection === 'after' && percentage >= positionPercentages[0]) { - this._addClass(this.ticks[i], 'in-selection'); - } else if (this.options.selection === 'before' && percentage <= positionPercentages[0]) { - this._addClass(this.ticks[i], 'in-selection'); - } - } else if (percentage >= positionPercentages[0] && percentage <= positionPercentages[1]) { - this._addClass(this.ticks[i], 'in-selection'); - } - - if (this.tickLabels[i]) { - this.tickLabels[i].style[styleSize] = labelSize + "px"; - - if (this.options.orientation !== 'vertical' && this.options.ticks_positions[i] !== undefined) { - this.tickLabels[i].style.position = 'absolute'; - this.tickLabels[i].style[this.stylePos] = percentage + "%"; - this.tickLabels[i].style[styleMargin] = -labelSize / 2 + 'px'; - } else if (this.options.orientation === 'vertical') { - if (this.options.rtl) { - this.tickLabels[i].style['marginRight'] = this.sliderElem.offsetWidth + "px"; - } else { - this.tickLabels[i].style['marginLeft'] = this.sliderElem.offsetWidth + "px"; - } - this.tickLabelContainer.style[styleMargin] = this.sliderElem.offsetWidth / 2 * -1 + 'px'; - } - } - } - } - - var formattedTooltipVal; - - if (this.options.range) { - formattedTooltipVal = this.options.formatter(this._state.value); - this._setText(this.tooltipInner, formattedTooltipVal); - this.tooltip.style[this.stylePos] = (positionPercentages[1] + positionPercentages[0]) / 2 + "%"; - - var innerTooltipMinText = this.options.formatter(this._state.value[0]); - this._setText(this.tooltipInner_min, innerTooltipMinText); - - var innerTooltipMaxText = this.options.formatter(this._state.value[1]); - this._setText(this.tooltipInner_max, innerTooltipMaxText); - - this.tooltip_min.style[this.stylePos] = positionPercentages[0] + "%"; - - this.tooltip_max.style[this.stylePos] = positionPercentages[1] + "%"; - } else { - formattedTooltipVal = this.options.formatter(this._state.value[0]); - this._setText(this.tooltipInner, formattedTooltipVal); - - this.tooltip.style[this.stylePos] = positionPercentages[0] + "%"; - } - - if (this.options.orientation === 'vertical') { - this.trackLow.style.top = '0'; - this.trackLow.style.height = Math.min(positionPercentages[0], positionPercentages[1]) + '%'; - - this.trackSelection.style.top = Math.min(positionPercentages[0], positionPercentages[1]) + '%'; - this.trackSelection.style.height = Math.abs(positionPercentages[0] - positionPercentages[1]) + '%'; - - this.trackHigh.style.bottom = '0'; - this.trackHigh.style.height = 100 - Math.min(positionPercentages[0], positionPercentages[1]) - Math.abs(positionPercentages[0] - positionPercentages[1]) + '%'; - } else { - if (this.stylePos === 'right') { - this.trackLow.style.right = '0'; - } else { - this.trackLow.style.left = '0'; - } - this.trackLow.style.width = Math.min(positionPercentages[0], positionPercentages[1]) + '%'; - - if (this.stylePos === 'right') { - this.trackSelection.style.right = Math.min(positionPercentages[0], positionPercentages[1]) + '%'; - } else { - this.trackSelection.style.left = Math.min(positionPercentages[0], positionPercentages[1]) + '%'; - } - this.trackSelection.style.width = Math.abs(positionPercentages[0] - positionPercentages[1]) + '%'; - - if (this.stylePos === 'right') { - this.trackHigh.style.left = '0'; - } else { - this.trackHigh.style.right = '0'; - } - this.trackHigh.style.width = 100 - Math.min(positionPercentages[0], positionPercentages[1]) - Math.abs(positionPercentages[0] - positionPercentages[1]) + '%'; - - var offset_min = this.tooltip_min.getBoundingClientRect(); - var offset_max = this.tooltip_max.getBoundingClientRect(); - - if (this.options.tooltip_position === 'bottom') { - if (offset_min.right > offset_max.left) { - this._removeClass(this.tooltip_max, 'bottom'); - this._addClass(this.tooltip_max, 'top'); - this.tooltip_max.style.top = ''; - this.tooltip_max.style.bottom = 22 + 'px'; - } else { - this._removeClass(this.tooltip_max, 'top'); - this._addClass(this.tooltip_max, 'bottom'); - this.tooltip_max.style.top = this.tooltip_min.style.top; - this.tooltip_max.style.bottom = ''; - } - } else { - if (offset_min.right > offset_max.left) { - this._removeClass(this.tooltip_max, 'top'); - this._addClass(this.tooltip_max, 'bottom'); - this.tooltip_max.style.top = 18 + 'px'; - } else { - this._removeClass(this.tooltip_max, 'bottom'); - this._addClass(this.tooltip_max, 'top'); - this.tooltip_max.style.top = this.tooltip_min.style.top; - } - } - } - }, - _createHighlightRange: function _createHighlightRange(start, end) { - if (this._isHighlightRange(start, end)) { - if (start > end) { - return { 'start': end, 'size': start - end }; - } - return { 'start': start, 'size': end - start }; - } - return null; - }, - _isHighlightRange: function _isHighlightRange(start, end) { - if (0 <= start && start <= 100 && 0 <= end && end <= 100) { - return true; - } else { - return false; - } - }, - _resize: function _resize(ev) { - /*jshint unused:false*/ - this._state.offset = this._offset(this.sliderElem); - this._state.size = this.sliderElem[this.sizePos]; - this._layout(); - }, - _removeProperty: function _removeProperty(element, prop) { - if (element.style.removeProperty) { - element.style.removeProperty(prop); - } else { - element.style.removeAttribute(prop); - } - }, - _mousedown: function _mousedown(ev) { - if (!this._state.enabled) { - return false; - } - - if (ev.preventDefault) { - ev.preventDefault(); - } - - this._state.offset = this._offset(this.sliderElem); - this._state.size = this.sliderElem[this.sizePos]; - - var percentage = this._getPercentage(ev); - - if (this.options.range) { - var diff1 = Math.abs(this._state.percentage[0] - percentage); - var diff2 = Math.abs(this._state.percentage[1] - percentage); - this._state.dragged = diff1 < diff2 ? 0 : 1; - this._adjustPercentageForRangeSliders(percentage); - } else { - this._state.dragged = 0; - } - - this._state.percentage[this._state.dragged] = percentage; - this._layout(); - - if (this.touchCapable) { - document.removeEventListener("touchmove", this.mousemove, false); - document.removeEventListener("touchend", this.mouseup, false); - } - - if (this.mousemove) { - document.removeEventListener("mousemove", this.mousemove, false); - } - if (this.mouseup) { - document.removeEventListener("mouseup", this.mouseup, false); - } - - this.mousemove = this._mousemove.bind(this); - this.mouseup = this._mouseup.bind(this); - - if (this.touchCapable) { - // Touch: Bind touch events: - document.addEventListener("touchmove", this.mousemove, false); - document.addEventListener("touchend", this.mouseup, false); - } - // Bind mouse events: - document.addEventListener("mousemove", this.mousemove, false); - document.addEventListener("mouseup", this.mouseup, false); - - this._state.inDrag = true; - var newValue = this._calculateValue(); - - this._trigger('slideStart', newValue); - - this._setDataVal(newValue); - this.setValue(newValue, false, true); - - ev.returnValue = false; - - if (this.options.focus) { - this._triggerFocusOnHandle(this._state.dragged); - } - - return true; - }, - _touchstart: function _touchstart(ev) { - if (ev.changedTouches === undefined) { - this._mousedown(ev); - return; - } - - var touch = ev.changedTouches[0]; - this.touchX = touch.pageX; - this.touchY = touch.pageY; - }, - _triggerFocusOnHandle: function _triggerFocusOnHandle(handleIdx) { - if (handleIdx === 0) { - this.handle1.focus(); - } - if (handleIdx === 1) { - this.handle2.focus(); - } - }, - _keydown: function _keydown(handleIdx, ev) { - if (!this._state.enabled) { - return false; - } - - var dir; - switch (ev.keyCode) { - case 37: // left - case 40: - // down - dir = -1; - break; - case 39: // right - case 38: - // up - dir = 1; - break; - } - if (!dir) { - return; - } - - // use natural arrow keys instead of from min to max - if (this.options.natural_arrow_keys) { - var ifVerticalAndNotReversed = this.options.orientation === 'vertical' && !this.options.reversed; - var ifHorizontalAndReversed = this.options.orientation === 'horizontal' && this.options.reversed; // @todo control with rtl - - if (ifVerticalAndNotReversed || ifHorizontalAndReversed) { - dir = -dir; - } - } - - var val = this._state.value[handleIdx] + dir * this.options.step; - var percentage = val / this.options.max * 100; - this._state.keyCtrl = handleIdx; - if (this.options.range) { - this._adjustPercentageForRangeSliders(percentage); - var val1 = !this._state.keyCtrl ? val : this._state.value[0]; - var val2 = this._state.keyCtrl ? val : this._state.value[1]; - val = [val1, val2]; - } - - this._trigger('slideStart', val); - this._setDataVal(val); - this.setValue(val, true, true); - - this._setDataVal(val); - this._trigger('slideStop', val); - this._layout(); - - this._pauseEvent(ev); - delete this._state.keyCtrl; - - return false; - }, - _pauseEvent: function _pauseEvent(ev) { - if (ev.stopPropagation) { - ev.stopPropagation(); - } - if (ev.preventDefault) { - ev.preventDefault(); - } - ev.cancelBubble = true; - ev.returnValue = false; - }, - _mousemove: function _mousemove(ev) { - if (!this._state.enabled) { - return false; - } - - var percentage = this._getPercentage(ev); - this._adjustPercentageForRangeSliders(percentage); - this._state.percentage[this._state.dragged] = percentage; - this._layout(); - - var val = this._calculateValue(true); - this.setValue(val, true, true); - - return false; - }, - _touchmove: function _touchmove(ev) { - if (ev.changedTouches === undefined) { - return; - } - - var touch = ev.changedTouches[0]; - - var xDiff = touch.pageX - this.touchX; - var yDiff = touch.pageY - this.touchY; - - if (!this._state.inDrag) { - // Vertical Slider - if (this.options.orientation === 'vertical' && xDiff <= 5 && xDiff >= -5 && (yDiff >= 15 || yDiff <= -15)) { - this._mousedown(ev); - } - // Horizontal slider. - else if (yDiff <= 5 && yDiff >= -5 && (xDiff >= 15 || xDiff <= -15)) { - this._mousedown(ev); - } - } - }, - _adjustPercentageForRangeSliders: function _adjustPercentageForRangeSliders(percentage) { - if (this.options.range) { - var precision = this._getNumDigitsAfterDecimalPlace(percentage); - precision = precision ? precision - 1 : 0; - var percentageWithAdjustedPrecision = this._applyToFixedAndParseFloat(percentage, precision); - if (this._state.dragged === 0 && this._applyToFixedAndParseFloat(this._state.percentage[1], precision) < percentageWithAdjustedPrecision) { - this._state.percentage[0] = this._state.percentage[1]; - this._state.dragged = 1; - } else if (this._state.dragged === 1 && this._applyToFixedAndParseFloat(this._state.percentage[0], precision) > percentageWithAdjustedPrecision) { - this._state.percentage[1] = this._state.percentage[0]; - this._state.dragged = 0; - } else if (this._state.keyCtrl === 0 && this._state.value[1] / this.options.max * 100 < percentage) { - this._state.percentage[0] = this._state.percentage[1]; - this._state.keyCtrl = 1; - this.handle2.focus(); - } else if (this._state.keyCtrl === 1 && this._state.value[0] / this.options.max * 100 > percentage) { - this._state.percentage[1] = this._state.percentage[0]; - this._state.keyCtrl = 0; - this.handle1.focus(); - } - } - }, - _mouseup: function _mouseup() { - if (!this._state.enabled) { - return false; - } - if (this.touchCapable) { - // Touch: Unbind touch event handlers: - document.removeEventListener("touchmove", this.mousemove, false); - document.removeEventListener("touchend", this.mouseup, false); - } - // Unbind mouse event handlers: - document.removeEventListener("mousemove", this.mousemove, false); - document.removeEventListener("mouseup", this.mouseup, false); - - this._state.inDrag = false; - if (this._state.over === false) { - this._hideTooltip(); - } - var val = this._calculateValue(true); - - this._layout(); - this._setDataVal(val); - this._trigger('slideStop', val); - - return false; - }, - _calculateValue: function _calculateValue(snapToClosestTick) { - var val; - if (this.options.range) { - val = [this.options.min, this.options.max]; - if (this._state.percentage[0] !== 0) { - val[0] = this._toValue(this._state.percentage[0]); - val[0] = this._applyPrecision(val[0]); - } - if (this._state.percentage[1] !== 100) { - val[1] = this._toValue(this._state.percentage[1]); - val[1] = this._applyPrecision(val[1]); - } - } else { - val = this._toValue(this._state.percentage[0]); - val = parseFloat(val); - val = this._applyPrecision(val); - } - - if (snapToClosestTick) { - var min = [val, Infinity]; - for (var i = 0; i < this.options.ticks.length; i++) { - var diff = Math.abs(this.options.ticks[i] - val); - if (diff <= min[1]) { - min = [this.options.ticks[i], diff]; - } - } - if (min[1] <= this.options.ticks_snap_bounds) { - return min[0]; - } - } - - return val; - }, - _applyPrecision: function _applyPrecision(val) { - var precision = this.options.precision || this._getNumDigitsAfterDecimalPlace(this.options.step); - return this._applyToFixedAndParseFloat(val, precision); - }, - _getNumDigitsAfterDecimalPlace: function _getNumDigitsAfterDecimalPlace(num) { - var match = ('' + num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/); - if (!match) { - return 0; - } - return Math.max(0, (match[1] ? match[1].length : 0) - (match[2] ? +match[2] : 0)); - }, - _applyToFixedAndParseFloat: function _applyToFixedAndParseFloat(num, toFixedInput) { - var truncatedNum = num.toFixed(toFixedInput); - return parseFloat(truncatedNum); - }, - /* - Credits to Mike Samuel for the following method! - Source: http://stackoverflow.com/questions/10454518/javascript-how-to-retrieve-the-number-of-decimals-of-a-string-number - */ - _getPercentage: function _getPercentage(ev) { - if (this.touchCapable && (ev.type === 'touchstart' || ev.type === 'touchmove')) { - ev = ev.touches[0]; - } - - var eventPosition = ev[this.mousePos]; - var sliderOffset = this._state.offset[this.stylePos]; - var distanceToSlide = eventPosition - sliderOffset; - if (this.stylePos === 'right') { - distanceToSlide = -distanceToSlide; - } - // Calculate what percent of the length the slider handle has slid - var percentage = distanceToSlide / this._state.size * 100; - percentage = Math.round(percentage / this._state.percentage[2]) * this._state.percentage[2]; - if (this.options.reversed) { - percentage = 100 - percentage; - } - - // Make sure the percent is within the bounds of the slider. - // 0% corresponds to the 'min' value of the slide - // 100% corresponds to the 'max' value of the slide - return Math.max(0, Math.min(100, percentage)); - }, - _validateInputValue: function _validateInputValue(val) { - if (!isNaN(+val)) { - return +val; - } else if (Array.isArray(val)) { - this._validateArray(val); - return val; - } else { - throw new Error(ErrorMsgs.formatInvalidInputErrorMsg(val)); - } - }, - _validateArray: function _validateArray(val) { - for (var i = 0; i < val.length; i++) { - var input = val[i]; - if (typeof input !== 'number') { - throw new Error(ErrorMsgs.formatInvalidInputErrorMsg(input)); - } - } - }, - _setDataVal: function _setDataVal(val) { - this.element.setAttribute('data-value', val); - this.element.setAttribute('value', val); - this.element.value = val; - }, - _trigger: function _trigger(evt, val) { - val = val || val === 0 ? val : undefined; - - var callbackFnArray = this.eventToCallbackMap[evt]; - if (callbackFnArray && callbackFnArray.length) { - for (var i = 0; i < callbackFnArray.length; i++) { - var callbackFn = callbackFnArray[i]; - callbackFn(val); - } - } - - /* If JQuery exists, trigger JQuery events */ - if ($) { - this._triggerJQueryEvent(evt, val); - } - }, - _triggerJQueryEvent: function _triggerJQueryEvent(evt, val) { - var eventData = { - type: evt, - value: val - }; - this.$element.trigger(eventData); - this.$sliderElem.trigger(eventData); - }, - _unbindJQueryEventHandlers: function _unbindJQueryEventHandlers() { - this.$element.off(); - this.$sliderElem.off(); - }, - _setText: function _setText(element, text) { - if (typeof element.textContent !== "undefined") { - element.textContent = text; - } else if (typeof element.innerText !== "undefined") { - element.innerText = text; - } - }, - _removeClass: function _removeClass(element, classString) { - var classes = classString.split(" "); - var newClasses = element.className; - - for (var i = 0; i < classes.length; i++) { - var classTag = classes[i]; - var regex = new RegExp("(?:\\s|^)" + classTag + "(?:\\s|$)"); - newClasses = newClasses.replace(regex, " "); - } - - element.className = newClasses.trim(); - }, - _addClass: function _addClass(element, classString) { - var classes = classString.split(" "); - var newClasses = element.className; - - for (var i = 0; i < classes.length; i++) { - var classTag = classes[i]; - var regex = new RegExp("(?:\\s|^)" + classTag + "(?:\\s|$)"); - var ifClassExists = regex.test(newClasses); - - if (!ifClassExists) { - newClasses += " " + classTag; - } - } - - element.className = newClasses.trim(); - }, - _offsetLeft: function _offsetLeft(obj) { - return obj.getBoundingClientRect().left; - }, - _offsetRight: function _offsetRight(obj) { - return obj.getBoundingClientRect().right; - }, - _offsetTop: function _offsetTop(obj) { - var offsetTop = obj.offsetTop; - while ((obj = obj.offsetParent) && !isNaN(obj.offsetTop)) { - offsetTop += obj.offsetTop; - if (obj.tagName !== 'BODY') { - offsetTop -= obj.scrollTop; - } - } - return offsetTop; - }, - _offset: function _offset(obj) { - return { - left: this._offsetLeft(obj), - right: this._offsetRight(obj), - top: this._offsetTop(obj) - }; - }, - _css: function _css(elementRef, styleName, value) { - if ($) { - $.style(elementRef, styleName, value); - } else { - var style = styleName.replace(/^-ms-/, "ms-").replace(/-([\da-z])/gi, function (all, letter) { - return letter.toUpperCase(); - }); - elementRef.style[style] = value; - } - }, - _toValue: function _toValue(percentage) { - return this.options.scale.toValue.apply(this, [percentage]); - }, - _toPercentage: function _toPercentage(value) { - return this.options.scale.toPercentage.apply(this, [value]); - }, - _setTooltipPosition: function _setTooltipPosition() { - var tooltips = [this.tooltip, this.tooltip_min, this.tooltip_max]; - if (this.options.orientation === 'vertical') { - var tooltipPos; - if (this.options.tooltip_position) { - tooltipPos = this.options.tooltip_position; - } else { - if (this.options.rtl) { - tooltipPos = 'left'; - } else { - tooltipPos = 'right'; - } - } - var oppositeSide = tooltipPos === 'left' ? 'right' : 'left'; - tooltips.forEach(function (tooltip) { - this._addClass(tooltip, tooltipPos); - tooltip.style[oppositeSide] = '100%'; - }.bind(this)); - } else if (this.options.tooltip_position === 'bottom') { - tooltips.forEach(function (tooltip) { - this._addClass(tooltip, 'bottom'); - tooltip.style.top = 22 + 'px'; - }.bind(this)); - } else { - tooltips.forEach(function (tooltip) { - this._addClass(tooltip, 'top'); - tooltip.style.top = -this.tooltip.outerHeight - 14 + 'px'; - }.bind(this)); - } - } - }; - - /********************************* - Attach to global namespace - *********************************/ - if ($ && $.fn) { - var autoRegisterNamespace = void 0; - - if (!$.fn.slider) { - $.bridget(NAMESPACE_MAIN, Slider); - autoRegisterNamespace = NAMESPACE_MAIN; - } else { - if (windowIsDefined) { - window.console.warn("bootstrap-slider.js - WARNING: $.fn.slider namespace is already bound. Use the $.fn.bootstrapSlider namespace instead."); - } - autoRegisterNamespace = NAMESPACE_ALTERNATE; - } - $.bridget(NAMESPACE_ALTERNATE, Slider); - - // Auto-Register data-provide="slider" Elements - $(function () { - $("input[data-provide=slider]")[autoRegisterNamespace](); - }); - } - })($); - - return Slider; -}); diff --git a/htdocs/js/bootstrap.bundle.js b/htdocs/js/bootstrap.bundle.js deleted file mode 100644 index adb3400..0000000 --- a/htdocs/js/bootstrap.bundle.js +++ /dev/null @@ -1,6444 +0,0 @@ -/*! - * Bootstrap v4.1.1 (https://getbootstrap.com/) - * Copyright 2011-2018 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('jquery')) : - typeof define === 'function' && define.amd ? define(['exports', 'jquery'], factory) : - (factory((global.bootstrap = {}),global.jQuery)); -}(this, (function (exports,$) { 'use strict'; - - $ = $ && $.hasOwnProperty('default') ? $['default'] : $; - - function _defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, descriptor.key, descriptor); - } - } - - function _createClass(Constructor, protoProps, staticProps) { - if (protoProps) _defineProperties(Constructor.prototype, protoProps); - if (staticProps) _defineProperties(Constructor, staticProps); - return Constructor; - } - - function _defineProperty(obj, key, value) { - if (key in obj) { - Object.defineProperty(obj, key, { - value: value, - enumerable: true, - configurable: true, - writable: true - }); - } else { - obj[key] = value; - } - - return obj; - } - - function _objectSpread(target) { - for (var i = 1; i < arguments.length; i++) { - var source = arguments[i] != null ? arguments[i] : {}; - var ownKeys = Object.keys(source); - - if (typeof Object.getOwnPropertySymbols === 'function') { - ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { - return Object.getOwnPropertyDescriptor(source, sym).enumerable; - })); - } - - ownKeys.forEach(function (key) { - _defineProperty(target, key, source[key]); - }); - } - - return target; - } - - function _inheritsLoose(subClass, superClass) { - subClass.prototype = Object.create(superClass.prototype); - subClass.prototype.constructor = subClass; - subClass.__proto__ = superClass; - } - - /** - * -------------------------------------------------------------------------- - * Bootstrap (v4.1.1): util.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * -------------------------------------------------------------------------- - */ - - var Util = function ($$$1) { - /** - * ------------------------------------------------------------------------ - * Private TransitionEnd Helpers - * ------------------------------------------------------------------------ - */ - var TRANSITION_END = 'transitionend'; - var MAX_UID = 1000000; - var MILLISECONDS_MULTIPLIER = 1000; // Shoutout AngusCroll (https://goo.gl/pxwQGp) - - function toType(obj) { - return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase(); - } - - function getSpecialTransitionEndEvent() { - return { - bindType: TRANSITION_END, - delegateType: TRANSITION_END, - handle: function handle(event) { - if ($$$1(event.target).is(this)) { - return event.handleObj.handler.apply(this, arguments); // eslint-disable-line prefer-rest-params - } - - return undefined; // eslint-disable-line no-undefined - } - }; - } - - function transitionEndEmulator(duration) { - var _this = this; - - var called = false; - $$$1(this).one(Util.TRANSITION_END, function () { - called = true; - }); - setTimeout(function () { - if (!called) { - Util.triggerTransitionEnd(_this); - } - }, duration); - return this; - } - - function setTransitionEndSupport() { - $$$1.fn.emulateTransitionEnd = transitionEndEmulator; - $$$1.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent(); - } - /** - * -------------------------------------------------------------------------- - * Public Util Api - * -------------------------------------------------------------------------- - */ - - - var Util = { - TRANSITION_END: 'bsTransitionEnd', - getUID: function getUID(prefix) { - do { - // eslint-disable-next-line no-bitwise - prefix += ~~(Math.random() * MAX_UID); // "~~" acts like a faster Math.floor() here - } while (document.getElementById(prefix)); - - return prefix; - }, - getSelectorFromElement: function getSelectorFromElement(element) { - var selector = element.getAttribute('data-target'); - - if (!selector || selector === '#') { - selector = element.getAttribute('href') || ''; - } - - try { - var $selector = $$$1(document).find(selector); - return $selector.length > 0 ? selector : null; - } catch (err) { - return null; - } - }, - getTransitionDurationFromElement: function getTransitionDurationFromElement(element) { - if (!element) { - return 0; - } // Get transition-duration of the element - - - var transitionDuration = $$$1(element).css('transition-duration'); - var floatTransitionDuration = parseFloat(transitionDuration); // Return 0 if element or transition duration is not found - - if (!floatTransitionDuration) { - return 0; - } // If multiple durations are defined, take the first - - - transitionDuration = transitionDuration.split(',')[0]; - return parseFloat(transitionDuration) * MILLISECONDS_MULTIPLIER; - }, - reflow: function reflow(element) { - return element.offsetHeight; - }, - triggerTransitionEnd: function triggerTransitionEnd(element) { - $$$1(element).trigger(TRANSITION_END); - }, - // TODO: Remove in v5 - supportsTransitionEnd: function supportsTransitionEnd() { - return Boolean(TRANSITION_END); - }, - isElement: function isElement(obj) { - return (obj[0] || obj).nodeType; - }, - typeCheckConfig: function typeCheckConfig(componentName, config, configTypes) { - for (var property in configTypes) { - if (Object.prototype.hasOwnProperty.call(configTypes, property)) { - var expectedTypes = configTypes[property]; - var value = config[property]; - var valueType = value && Util.isElement(value) ? 'element' : toType(value); - - if (!new RegExp(expectedTypes).test(valueType)) { - throw new Error(componentName.toUpperCase() + ": " + ("Option \"" + property + "\" provided type \"" + valueType + "\" ") + ("but expected type \"" + expectedTypes + "\".")); - } - } - } - } - }; - setTransitionEndSupport(); - return Util; - }($); - - /** - * -------------------------------------------------------------------------- - * Bootstrap (v4.1.1): alert.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * -------------------------------------------------------------------------- - */ - - var Alert = function ($$$1) { - /** - * ------------------------------------------------------------------------ - * Constants - * ------------------------------------------------------------------------ - */ - var NAME = 'alert'; - var VERSION = '4.1.1'; - var DATA_KEY = 'bs.alert'; - var EVENT_KEY = "." + DATA_KEY; - var DATA_API_KEY = '.data-api'; - var JQUERY_NO_CONFLICT = $$$1.fn[NAME]; - var Selector = { - DISMISS: '[data-dismiss="alert"]' - }; - var Event = { - CLOSE: "close" + EVENT_KEY, - CLOSED: "closed" + EVENT_KEY, - CLICK_DATA_API: "click" + EVENT_KEY + DATA_API_KEY - }; - var ClassName = { - ALERT: 'alert', - FADE: 'fade', - SHOW: 'show' - /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ - */ - - }; - - var Alert = - /*#__PURE__*/ - function () { - function Alert(element) { - this._element = element; - } // Getters - - - var _proto = Alert.prototype; - - // Public - _proto.close = function close(element) { - var rootElement = this._element; - - if (element) { - rootElement = this._getRootElement(element); - } - - var customEvent = this._triggerCloseEvent(rootElement); - - if (customEvent.isDefaultPrevented()) { - return; - } - - this._removeElement(rootElement); - }; - - _proto.dispose = function dispose() { - $$$1.removeData(this._element, DATA_KEY); - this._element = null; - }; // Private - - - _proto._getRootElement = function _getRootElement(element) { - var selector = Util.getSelectorFromElement(element); - var parent = false; - - if (selector) { - parent = $$$1(selector)[0]; - } - - if (!parent) { - parent = $$$1(element).closest("." + ClassName.ALERT)[0]; - } - - return parent; - }; - - _proto._triggerCloseEvent = function _triggerCloseEvent(element) { - var closeEvent = $$$1.Event(Event.CLOSE); - $$$1(element).trigger(closeEvent); - return closeEvent; - }; - - _proto._removeElement = function _removeElement(element) { - var _this = this; - - $$$1(element).removeClass(ClassName.SHOW); - - if (!$$$1(element).hasClass(ClassName.FADE)) { - this._destroyElement(element); - - return; - } - - var transitionDuration = Util.getTransitionDurationFromElement(element); - $$$1(element).one(Util.TRANSITION_END, function (event) { - return _this._destroyElement(element, event); - }).emulateTransitionEnd(transitionDuration); - }; - - _proto._destroyElement = function _destroyElement(element) { - $$$1(element).detach().trigger(Event.CLOSED).remove(); - }; // Static - - - Alert._jQueryInterface = function _jQueryInterface(config) { - return this.each(function () { - var $element = $$$1(this); - var data = $element.data(DATA_KEY); - - if (!data) { - data = new Alert(this); - $element.data(DATA_KEY, data); - } - - if (config === 'close') { - data[config](this); - } - }); - }; - - Alert._handleDismiss = function _handleDismiss(alertInstance) { - return function (event) { - if (event) { - event.preventDefault(); - } - - alertInstance.close(this); - }; - }; - - _createClass(Alert, null, [{ - key: "VERSION", - get: function get() { - return VERSION; - } - }]); - - return Alert; - }(); - /** - * ------------------------------------------------------------------------ - * Data Api implementation - * ------------------------------------------------------------------------ - */ - - - $$$1(document).on(Event.CLICK_DATA_API, Selector.DISMISS, Alert._handleDismiss(new Alert())); - /** - * ------------------------------------------------------------------------ - * jQuery - * ------------------------------------------------------------------------ - */ - - $$$1.fn[NAME] = Alert._jQueryInterface; - $$$1.fn[NAME].Constructor = Alert; - - $$$1.fn[NAME].noConflict = function () { - $$$1.fn[NAME] = JQUERY_NO_CONFLICT; - return Alert._jQueryInterface; - }; - - return Alert; - }($); - - /** - * -------------------------------------------------------------------------- - * Bootstrap (v4.1.1): button.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * -------------------------------------------------------------------------- - */ - - var Button = function ($$$1) { - /** - * ------------------------------------------------------------------------ - * Constants - * ------------------------------------------------------------------------ - */ - var NAME = 'button'; - var VERSION = '4.1.1'; - var DATA_KEY = 'bs.button'; - var EVENT_KEY = "." + DATA_KEY; - var DATA_API_KEY = '.data-api'; - var JQUERY_NO_CONFLICT = $$$1.fn[NAME]; - var ClassName = { - ACTIVE: 'active', - BUTTON: 'btn', - FOCUS: 'focus' - }; - var Selector = { - DATA_TOGGLE_CARROT: '[data-toggle^="button"]', - DATA_TOGGLE: '[data-toggle="buttons"]', - INPUT: 'input', - ACTIVE: '.active', - BUTTON: '.btn' - }; - var Event = { - CLICK_DATA_API: "click" + EVENT_KEY + DATA_API_KEY, - FOCUS_BLUR_DATA_API: "focus" + EVENT_KEY + DATA_API_KEY + " " + ("blur" + EVENT_KEY + DATA_API_KEY) - /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ - */ - - }; - - var Button = - /*#__PURE__*/ - function () { - function Button(element) { - this._element = element; - } // Getters - - - var _proto = Button.prototype; - - // Public - _proto.toggle = function toggle() { - var triggerChangeEvent = true; - var addAriaPressed = true; - var rootElement = $$$1(this._element).closest(Selector.DATA_TOGGLE)[0]; - - if (rootElement) { - var input = $$$1(this._element).find(Selector.INPUT)[0]; - - if (input) { - if (input.type === 'radio') { - if (input.checked && $$$1(this._element).hasClass(ClassName.ACTIVE)) { - triggerChangeEvent = false; - } else { - var activeElement = $$$1(rootElement).find(Selector.ACTIVE)[0]; - - if (activeElement) { - $$$1(activeElement).removeClass(ClassName.ACTIVE); - } - } - } - - if (triggerChangeEvent) { - if (input.hasAttribute('disabled') || rootElement.hasAttribute('disabled') || input.classList.contains('disabled') || rootElement.classList.contains('disabled')) { - return; - } - - input.checked = !$$$1(this._element).hasClass(ClassName.ACTIVE); - $$$1(input).trigger('change'); - } - - input.focus(); - addAriaPressed = false; - } - } - - if (addAriaPressed) { - this._element.setAttribute('aria-pressed', !$$$1(this._element).hasClass(ClassName.ACTIVE)); - } - - if (triggerChangeEvent) { - $$$1(this._element).toggleClass(ClassName.ACTIVE); - } - }; - - _proto.dispose = function dispose() { - $$$1.removeData(this._element, DATA_KEY); - this._element = null; - }; // Static - - - Button._jQueryInterface = function _jQueryInterface(config) { - return this.each(function () { - var data = $$$1(this).data(DATA_KEY); - - if (!data) { - data = new Button(this); - $$$1(this).data(DATA_KEY, data); - } - - if (config === 'toggle') { - data[config](); - } - }); - }; - - _createClass(Button, null, [{ - key: "VERSION", - get: function get() { - return VERSION; - } - }]); - - return Button; - }(); - /** - * ------------------------------------------------------------------------ - * Data Api implementation - * ------------------------------------------------------------------------ - */ - - - $$$1(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE_CARROT, function (event) { - event.preventDefault(); - var button = event.target; - - if (!$$$1(button).hasClass(ClassName.BUTTON)) { - button = $$$1(button).closest(Selector.BUTTON); - } - - Button._jQueryInterface.call($$$1(button), 'toggle'); - }).on(Event.FOCUS_BLUR_DATA_API, Selector.DATA_TOGGLE_CARROT, function (event) { - var button = $$$1(event.target).closest(Selector.BUTTON)[0]; - $$$1(button).toggleClass(ClassName.FOCUS, /^focus(in)?$/.test(event.type)); - }); - /** - * ------------------------------------------------------------------------ - * jQuery - * ------------------------------------------------------------------------ - */ - - $$$1.fn[NAME] = Button._jQueryInterface; - $$$1.fn[NAME].Constructor = Button; - - $$$1.fn[NAME].noConflict = function () { - $$$1.fn[NAME] = JQUERY_NO_CONFLICT; - return Button._jQueryInterface; - }; - - return Button; - }($); - - /** - * -------------------------------------------------------------------------- - * Bootstrap (v4.1.1): carousel.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * -------------------------------------------------------------------------- - */ - - var Carousel = function ($$$1) { - /** - * ------------------------------------------------------------------------ - * Constants - * ------------------------------------------------------------------------ - */ - var NAME = 'carousel'; - var VERSION = '4.1.1'; - var DATA_KEY = 'bs.carousel'; - var EVENT_KEY = "." + DATA_KEY; - var DATA_API_KEY = '.data-api'; - var JQUERY_NO_CONFLICT = $$$1.fn[NAME]; - var ARROW_LEFT_KEYCODE = 37; // KeyboardEvent.which value for left arrow key - - var ARROW_RIGHT_KEYCODE = 39; // KeyboardEvent.which value for right arrow key - - var TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch - - var Default = { - interval: 5000, - keyboard: true, - slide: false, - pause: 'hover', - wrap: true - }; - var DefaultType = { - interval: '(number|boolean)', - keyboard: 'boolean', - slide: '(boolean|string)', - pause: '(string|boolean)', - wrap: 'boolean' - }; - var Direction = { - NEXT: 'next', - PREV: 'prev', - LEFT: 'left', - RIGHT: 'right' - }; - var Event = { - SLIDE: "slide" + EVENT_KEY, - SLID: "slid" + EVENT_KEY, - KEYDOWN: "keydown" + EVENT_KEY, - MOUSEENTER: "mouseenter" + EVENT_KEY, - MOUSELEAVE: "mouseleave" + EVENT_KEY, - TOUCHEND: "touchend" + EVENT_KEY, - LOAD_DATA_API: "load" + EVENT_KEY + DATA_API_KEY, - CLICK_DATA_API: "click" + EVENT_KEY + DATA_API_KEY - }; - var ClassName = { - CAROUSEL: 'carousel', - ACTIVE: 'active', - SLIDE: 'slide', - RIGHT: 'carousel-item-right', - LEFT: 'carousel-item-left', - NEXT: 'carousel-item-next', - PREV: 'carousel-item-prev', - ITEM: 'carousel-item' - }; - var Selector = { - ACTIVE: '.active', - ACTIVE_ITEM: '.active.carousel-item', - ITEM: '.carousel-item', - NEXT_PREV: '.carousel-item-next, .carousel-item-prev', - INDICATORS: '.carousel-indicators', - DATA_SLIDE: '[data-slide], [data-slide-to]', - DATA_RIDE: '[data-ride="carousel"]' - /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ - */ - - }; - - var Carousel = - /*#__PURE__*/ - function () { - function Carousel(element, config) { - this._items = null; - this._interval = null; - this._activeElement = null; - this._isPaused = false; - this._isSliding = false; - this.touchTimeout = null; - this._config = this._getConfig(config); - this._element = $$$1(element)[0]; - this._indicatorsElement = $$$1(this._element).find(Selector.INDICATORS)[0]; - - this._addEventListeners(); - } // Getters - - - var _proto = Carousel.prototype; - - // Public - _proto.next = function next() { - if (!this._isSliding) { - this._slide(Direction.NEXT); - } - }; - - _proto.nextWhenVisible = function nextWhenVisible() { - // Don't call next when the page isn't visible - // or the carousel or its parent isn't visible - if (!document.hidden && $$$1(this._element).is(':visible') && $$$1(this._element).css('visibility') !== 'hidden') { - this.next(); - } - }; - - _proto.prev = function prev() { - if (!this._isSliding) { - this._slide(Direction.PREV); - } - }; - - _proto.pause = function pause(event) { - if (!event) { - this._isPaused = true; - } - - if ($$$1(this._element).find(Selector.NEXT_PREV)[0]) { - Util.triggerTransitionEnd(this._element); - this.cycle(true); - } - - clearInterval(this._interval); - this._interval = null; - }; - - _proto.cycle = function cycle(event) { - if (!event) { - this._isPaused = false; - } - - if (this._interval) { - clearInterval(this._interval); - this._interval = null; - } - - if (this._config.interval && !this._isPaused) { - this._interval = setInterval((document.visibilityState ? this.nextWhenVisible : this.next).bind(this), this._config.interval); - } - }; - - _proto.to = function to(index) { - var _this = this; - - this._activeElement = $$$1(this._element).find(Selector.ACTIVE_ITEM)[0]; - - var activeIndex = this._getItemIndex(this._activeElement); - - if (index > this._items.length - 1 || index < 0) { - return; - } - - if (this._isSliding) { - $$$1(this._element).one(Event.SLID, function () { - return _this.to(index); - }); - return; - } - - if (activeIndex === index) { - this.pause(); - this.cycle(); - return; - } - - var direction = index > activeIndex ? Direction.NEXT : Direction.PREV; - - this._slide(direction, this._items[index]); - }; - - _proto.dispose = function dispose() { - $$$1(this._element).off(EVENT_KEY); - $$$1.removeData(this._element, DATA_KEY); - this._items = null; - this._config = null; - this._element = null; - this._interval = null; - this._isPaused = null; - this._isSliding = null; - this._activeElement = null; - this._indicatorsElement = null; - }; // Private - - - _proto._getConfig = function _getConfig(config) { - config = _objectSpread({}, Default, config); - Util.typeCheckConfig(NAME, config, DefaultType); - return config; - }; - - _proto._addEventListeners = function _addEventListeners() { - var _this2 = this; - - if (this._config.keyboard) { - $$$1(this._element).on(Event.KEYDOWN, function (event) { - return _this2._keydown(event); - }); - } - - if (this._config.pause === 'hover') { - $$$1(this._element).on(Event.MOUSEENTER, function (event) { - return _this2.pause(event); - }).on(Event.MOUSELEAVE, function (event) { - return _this2.cycle(event); - }); - - if ('ontouchstart' in document.documentElement) { - // If it's a touch-enabled device, mouseenter/leave are fired as - // part of the mouse compatibility events on first tap - the carousel - // would stop cycling until user tapped out of it; - // here, we listen for touchend, explicitly pause the carousel - // (as if it's the second time we tap on it, mouseenter compat event - // is NOT fired) and after a timeout (to allow for mouse compatibility - // events to fire) we explicitly restart cycling - $$$1(this._element).on(Event.TOUCHEND, function () { - _this2.pause(); - - if (_this2.touchTimeout) { - clearTimeout(_this2.touchTimeout); - } - - _this2.touchTimeout = setTimeout(function (event) { - return _this2.cycle(event); - }, TOUCHEVENT_COMPAT_WAIT + _this2._config.interval); - }); - } - } - }; - - _proto._keydown = function _keydown(event) { - if (/input|textarea/i.test(event.target.tagName)) { - return; - } - - switch (event.which) { - case ARROW_LEFT_KEYCODE: - event.preventDefault(); - this.prev(); - break; - - case ARROW_RIGHT_KEYCODE: - event.preventDefault(); - this.next(); - break; - - default: - } - }; - - _proto._getItemIndex = function _getItemIndex(element) { - this._items = $$$1.makeArray($$$1(element).parent().find(Selector.ITEM)); - return this._items.indexOf(element); - }; - - _proto._getItemByDirection = function _getItemByDirection(direction, activeElement) { - var isNextDirection = direction === Direction.NEXT; - var isPrevDirection = direction === Direction.PREV; - - var activeIndex = this._getItemIndex(activeElement); - - var lastItemIndex = this._items.length - 1; - var isGoingToWrap = isPrevDirection && activeIndex === 0 || isNextDirection && activeIndex === lastItemIndex; - - if (isGoingToWrap && !this._config.wrap) { - return activeElement; - } - - var delta = direction === Direction.PREV ? -1 : 1; - var itemIndex = (activeIndex + delta) % this._items.length; - return itemIndex === -1 ? this._items[this._items.length - 1] : this._items[itemIndex]; - }; - - _proto._triggerSlideEvent = function _triggerSlideEvent(relatedTarget, eventDirectionName) { - var targetIndex = this._getItemIndex(relatedTarget); - - var fromIndex = this._getItemIndex($$$1(this._element).find(Selector.ACTIVE_ITEM)[0]); - - var slideEvent = $$$1.Event(Event.SLIDE, { - relatedTarget: relatedTarget, - direction: eventDirectionName, - from: fromIndex, - to: targetIndex - }); - $$$1(this._element).trigger(slideEvent); - return slideEvent; - }; - - _proto._setActiveIndicatorElement = function _setActiveIndicatorElement(element) { - if (this._indicatorsElement) { - $$$1(this._indicatorsElement).find(Selector.ACTIVE).removeClass(ClassName.ACTIVE); - - var nextIndicator = this._indicatorsElement.children[this._getItemIndex(element)]; - - if (nextIndicator) { - $$$1(nextIndicator).addClass(ClassName.ACTIVE); - } - } - }; - - _proto._slide = function _slide(direction, element) { - var _this3 = this; - - var activeElement = $$$1(this._element).find(Selector.ACTIVE_ITEM)[0]; - - var activeElementIndex = this._getItemIndex(activeElement); - - var nextElement = element || activeElement && this._getItemByDirection(direction, activeElement); - - var nextElementIndex = this._getItemIndex(nextElement); - - var isCycling = Boolean(this._interval); - var directionalClassName; - var orderClassName; - var eventDirectionName; - - if (direction === Direction.NEXT) { - directionalClassName = ClassName.LEFT; - orderClassName = ClassName.NEXT; - eventDirectionName = Direction.LEFT; - } else { - directionalClassName = ClassName.RIGHT; - orderClassName = ClassName.PREV; - eventDirectionName = Direction.RIGHT; - } - - if (nextElement && $$$1(nextElement).hasClass(ClassName.ACTIVE)) { - this._isSliding = false; - return; - } - - var slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName); - - if (slideEvent.isDefaultPrevented()) { - return; - } - - if (!activeElement || !nextElement) { - // Some weirdness is happening, so we bail - return; - } - - this._isSliding = true; - - if (isCycling) { - this.pause(); - } - - this._setActiveIndicatorElement(nextElement); - - var slidEvent = $$$1.Event(Event.SLID, { - relatedTarget: nextElement, - direction: eventDirectionName, - from: activeElementIndex, - to: nextElementIndex - }); - - if ($$$1(this._element).hasClass(ClassName.SLIDE)) { - $$$1(nextElement).addClass(orderClassName); - Util.reflow(nextElement); - $$$1(activeElement).addClass(directionalClassName); - $$$1(nextElement).addClass(directionalClassName); - var transitionDuration = Util.getTransitionDurationFromElement(activeElement); - $$$1(activeElement).one(Util.TRANSITION_END, function () { - $$$1(nextElement).removeClass(directionalClassName + " " + orderClassName).addClass(ClassName.ACTIVE); - $$$1(activeElement).removeClass(ClassName.ACTIVE + " " + orderClassName + " " + directionalClassName); - _this3._isSliding = false; - setTimeout(function () { - return $$$1(_this3._element).trigger(slidEvent); - }, 0); - }).emulateTransitionEnd(transitionDuration); - } else { - $$$1(activeElement).removeClass(ClassName.ACTIVE); - $$$1(nextElement).addClass(ClassName.ACTIVE); - this._isSliding = false; - $$$1(this._element).trigger(slidEvent); - } - - if (isCycling) { - this.cycle(); - } - }; // Static - - - Carousel._jQueryInterface = function _jQueryInterface(config) { - return this.each(function () { - var data = $$$1(this).data(DATA_KEY); - - var _config = _objectSpread({}, Default, $$$1(this).data()); - - if (typeof config === 'object') { - _config = _objectSpread({}, _config, config); - } - - var action = typeof config === 'string' ? config : _config.slide; - - if (!data) { - data = new Carousel(this, _config); - $$$1(this).data(DATA_KEY, data); - } - - if (typeof config === 'number') { - data.to(config); - } else if (typeof action === 'string') { - if (typeof data[action] === 'undefined') { - throw new TypeError("No method named \"" + action + "\""); - } - - data[action](); - } else if (_config.interval) { - data.pause(); - data.cycle(); - } - }); - }; - - Carousel._dataApiClickHandler = function _dataApiClickHandler(event) { - var selector = Util.getSelectorFromElement(this); - - if (!selector) { - return; - } - - var target = $$$1(selector)[0]; - - if (!target || !$$$1(target).hasClass(ClassName.CAROUSEL)) { - return; - } - - var config = _objectSpread({}, $$$1(target).data(), $$$1(this).data()); - - var slideIndex = this.getAttribute('data-slide-to'); - - if (slideIndex) { - config.interval = false; - } - - Carousel._jQueryInterface.call($$$1(target), config); - - if (slideIndex) { - $$$1(target).data(DATA_KEY).to(slideIndex); - } - - event.preventDefault(); - }; - - _createClass(Carousel, null, [{ - key: "VERSION", - get: function get() { - return VERSION; - } - }, { - key: "Default", - get: function get() { - return Default; - } - }]); - - return Carousel; - }(); - /** - * ------------------------------------------------------------------------ - * Data Api implementation - * ------------------------------------------------------------------------ - */ - - - $$$1(document).on(Event.CLICK_DATA_API, Selector.DATA_SLIDE, Carousel._dataApiClickHandler); - $$$1(window).on(Event.LOAD_DATA_API, function () { - $$$1(Selector.DATA_RIDE).each(function () { - var $carousel = $$$1(this); - - Carousel._jQueryInterface.call($carousel, $carousel.data()); - }); - }); - /** - * ------------------------------------------------------------------------ - * jQuery - * ------------------------------------------------------------------------ - */ - - $$$1.fn[NAME] = Carousel._jQueryInterface; - $$$1.fn[NAME].Constructor = Carousel; - - $$$1.fn[NAME].noConflict = function () { - $$$1.fn[NAME] = JQUERY_NO_CONFLICT; - return Carousel._jQueryInterface; - }; - - return Carousel; - }($); - - /** - * -------------------------------------------------------------------------- - * Bootstrap (v4.1.1): collapse.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * -------------------------------------------------------------------------- - */ - - var Collapse = function ($$$1) { - /** - * ------------------------------------------------------------------------ - * Constants - * ------------------------------------------------------------------------ - */ - var NAME = 'collapse'; - var VERSION = '4.1.1'; - var DATA_KEY = 'bs.collapse'; - var EVENT_KEY = "." + DATA_KEY; - var DATA_API_KEY = '.data-api'; - var JQUERY_NO_CONFLICT = $$$1.fn[NAME]; - var Default = { - toggle: true, - parent: '' - }; - var DefaultType = { - toggle: 'boolean', - parent: '(string|element)' - }; - var Event = { - SHOW: "show" + EVENT_KEY, - SHOWN: "shown" + EVENT_KEY, - HIDE: "hide" + EVENT_KEY, - HIDDEN: "hidden" + EVENT_KEY, - CLICK_DATA_API: "click" + EVENT_KEY + DATA_API_KEY - }; - var ClassName = { - SHOW: 'show', - COLLAPSE: 'collapse', - COLLAPSING: 'collapsing', - COLLAPSED: 'collapsed' - }; - var Dimension = { - WIDTH: 'width', - HEIGHT: 'height' - }; - var Selector = { - ACTIVES: '.show, .collapsing', - DATA_TOGGLE: '[data-toggle="collapse"]' - /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ - */ - - }; - - var Collapse = - /*#__PURE__*/ - function () { - function Collapse(element, config) { - this._isTransitioning = false; - this._element = element; - this._config = this._getConfig(config); - this._triggerArray = $$$1.makeArray($$$1("[data-toggle=\"collapse\"][href=\"#" + element.id + "\"]," + ("[data-toggle=\"collapse\"][data-target=\"#" + element.id + "\"]"))); - var tabToggles = $$$1(Selector.DATA_TOGGLE); - - for (var i = 0; i < tabToggles.length; i++) { - var elem = tabToggles[i]; - var selector = Util.getSelectorFromElement(elem); - - if (selector !== null && $$$1(selector).filter(element).length > 0) { - this._selector = selector; - - this._triggerArray.push(elem); - } - } - - this._parent = this._config.parent ? this._getParent() : null; - - if (!this._config.parent) { - this._addAriaAndCollapsedClass(this._element, this._triggerArray); - } - - if (this._config.toggle) { - this.toggle(); - } - } // Getters - - - var _proto = Collapse.prototype; - - // Public - _proto.toggle = function toggle() { - if ($$$1(this._element).hasClass(ClassName.SHOW)) { - this.hide(); - } else { - this.show(); - } - }; - - _proto.show = function show() { - var _this = this; - - if (this._isTransitioning || $$$1(this._element).hasClass(ClassName.SHOW)) { - return; - } - - var actives; - var activesData; - - if (this._parent) { - actives = $$$1.makeArray($$$1(this._parent).find(Selector.ACTIVES).filter("[data-parent=\"" + this._config.parent + "\"]")); - - if (actives.length === 0) { - actives = null; - } - } - - if (actives) { - activesData = $$$1(actives).not(this._selector).data(DATA_KEY); - - if (activesData && activesData._isTransitioning) { - return; - } - } - - var startEvent = $$$1.Event(Event.SHOW); - $$$1(this._element).trigger(startEvent); - - if (startEvent.isDefaultPrevented()) { - return; - } - - if (actives) { - Collapse._jQueryInterface.call($$$1(actives).not(this._selector), 'hide'); - - if (!activesData) { - $$$1(actives).data(DATA_KEY, null); - } - } - - var dimension = this._getDimension(); - - $$$1(this._element).removeClass(ClassName.COLLAPSE).addClass(ClassName.COLLAPSING); - this._element.style[dimension] = 0; - - if (this._triggerArray.length > 0) { - $$$1(this._triggerArray).removeClass(ClassName.COLLAPSED).attr('aria-expanded', true); - } - - this.setTransitioning(true); - - var complete = function complete() { - $$$1(_this._element).removeClass(ClassName.COLLAPSING).addClass(ClassName.COLLAPSE).addClass(ClassName.SHOW); - _this._element.style[dimension] = ''; - - _this.setTransitioning(false); - - $$$1(_this._element).trigger(Event.SHOWN); - }; - - var capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1); - var scrollSize = "scroll" + capitalizedDimension; - var transitionDuration = Util.getTransitionDurationFromElement(this._element); - $$$1(this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration); - this._element.style[dimension] = this._element[scrollSize] + "px"; - }; - - _proto.hide = function hide() { - var _this2 = this; - - if (this._isTransitioning || !$$$1(this._element).hasClass(ClassName.SHOW)) { - return; - } - - var startEvent = $$$1.Event(Event.HIDE); - $$$1(this._element).trigger(startEvent); - - if (startEvent.isDefaultPrevented()) { - return; - } - - var dimension = this._getDimension(); - - this._element.style[dimension] = this._element.getBoundingClientRect()[dimension] + "px"; - Util.reflow(this._element); - $$$1(this._element).addClass(ClassName.COLLAPSING).removeClass(ClassName.COLLAPSE).removeClass(ClassName.SHOW); - - if (this._triggerArray.length > 0) { - for (var i = 0; i < this._triggerArray.length; i++) { - var trigger = this._triggerArray[i]; - var selector = Util.getSelectorFromElement(trigger); - - if (selector !== null) { - var $elem = $$$1(selector); - - if (!$elem.hasClass(ClassName.SHOW)) { - $$$1(trigger).addClass(ClassName.COLLAPSED).attr('aria-expanded', false); - } - } - } - } - - this.setTransitioning(true); - - var complete = function complete() { - _this2.setTransitioning(false); - - $$$1(_this2._element).removeClass(ClassName.COLLAPSING).addClass(ClassName.COLLAPSE).trigger(Event.HIDDEN); - }; - - this._element.style[dimension] = ''; - var transitionDuration = Util.getTransitionDurationFromElement(this._element); - $$$1(this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration); - }; - - _proto.setTransitioning = function setTransitioning(isTransitioning) { - this._isTransitioning = isTransitioning; - }; - - _proto.dispose = function dispose() { - $$$1.removeData(this._element, DATA_KEY); - this._config = null; - this._parent = null; - this._element = null; - this._triggerArray = null; - this._isTransitioning = null; - }; // Private - - - _proto._getConfig = function _getConfig(config) { - config = _objectSpread({}, Default, config); - config.toggle = Boolean(config.toggle); // Coerce string values - - Util.typeCheckConfig(NAME, config, DefaultType); - return config; - }; - - _proto._getDimension = function _getDimension() { - var hasWidth = $$$1(this._element).hasClass(Dimension.WIDTH); - return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT; - }; - - _proto._getParent = function _getParent() { - var _this3 = this; - - var parent = null; - - if (Util.isElement(this._config.parent)) { - parent = this._config.parent; // It's a jQuery object - - if (typeof this._config.parent.jquery !== 'undefined') { - parent = this._config.parent[0]; - } - } else { - parent = $$$1(this._config.parent)[0]; - } - - var selector = "[data-toggle=\"collapse\"][data-parent=\"" + this._config.parent + "\"]"; - $$$1(parent).find(selector).each(function (i, element) { - _this3._addAriaAndCollapsedClass(Collapse._getTargetFromElement(element), [element]); - }); - return parent; - }; - - _proto._addAriaAndCollapsedClass = function _addAriaAndCollapsedClass(element, triggerArray) { - if (element) { - var isOpen = $$$1(element).hasClass(ClassName.SHOW); - - if (triggerArray.length > 0) { - $$$1(triggerArray).toggleClass(ClassName.COLLAPSED, !isOpen).attr('aria-expanded', isOpen); - } - } - }; // Static - - - Collapse._getTargetFromElement = function _getTargetFromElement(element) { - var selector = Util.getSelectorFromElement(element); - return selector ? $$$1(selector)[0] : null; - }; - - Collapse._jQueryInterface = function _jQueryInterface(config) { - return this.each(function () { - var $this = $$$1(this); - var data = $this.data(DATA_KEY); - - var _config = _objectSpread({}, Default, $this.data(), typeof config === 'object' && config ? config : {}); - - if (!data && _config.toggle && /show|hide/.test(config)) { - _config.toggle = false; - } - - if (!data) { - data = new Collapse(this, _config); - $this.data(DATA_KEY, data); - } - - if (typeof config === 'string') { - if (typeof data[config] === 'undefined') { - throw new TypeError("No method named \"" + config + "\""); - } - - data[config](); - } - }); - }; - - _createClass(Collapse, null, [{ - key: "VERSION", - get: function get() { - return VERSION; - } - }, { - key: "Default", - get: function get() { - return Default; - } - }]); - - return Collapse; - }(); - /** - * ------------------------------------------------------------------------ - * Data Api implementation - * ------------------------------------------------------------------------ - */ - - - $$$1(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { - // preventDefault only for elements (which change the URL) not inside the collapsible element - if (event.currentTarget.tagName === 'A') { - event.preventDefault(); - } - - var $trigger = $$$1(this); - var selector = Util.getSelectorFromElement(this); - $$$1(selector).each(function () { - var $target = $$$1(this); - var data = $target.data(DATA_KEY); - var config = data ? 'toggle' : $trigger.data(); - - Collapse._jQueryInterface.call($target, config); - }); - }); - /** - * ------------------------------------------------------------------------ - * jQuery - * ------------------------------------------------------------------------ - */ - - $$$1.fn[NAME] = Collapse._jQueryInterface; - $$$1.fn[NAME].Constructor = Collapse; - - $$$1.fn[NAME].noConflict = function () { - $$$1.fn[NAME] = JQUERY_NO_CONFLICT; - return Collapse._jQueryInterface; - }; - - return Collapse; - }($); - - /**! - * @fileOverview Kickass library to create and place poppers near their reference elements. - * @version 1.14.3 - * @license - * Copyright (c) 2016 Federico Zivolo and contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined'; - - var longerTimeoutBrowsers = ['Edge', 'Trident', 'Firefox']; - var timeoutDuration = 0; - for (var i = 0; i < longerTimeoutBrowsers.length; i += 1) { - if (isBrowser && navigator.userAgent.indexOf(longerTimeoutBrowsers[i]) >= 0) { - timeoutDuration = 1; - break; - } - } - - function microtaskDebounce(fn) { - var called = false; - return function () { - if (called) { - return; - } - called = true; - window.Promise.resolve().then(function () { - called = false; - fn(); - }); - }; - } - - function taskDebounce(fn) { - var scheduled = false; - return function () { - if (!scheduled) { - scheduled = true; - setTimeout(function () { - scheduled = false; - fn(); - }, timeoutDuration); - } - }; - } - - var supportsMicroTasks = isBrowser && window.Promise; - - /** - * Create a debounced version of a method, that's asynchronously deferred - * but called in the minimum time possible. - * - * @method - * @memberof Popper.Utils - * @argument {Function} fn - * @returns {Function} - */ - var debounce = supportsMicroTasks ? microtaskDebounce : taskDebounce; - - /** - * Check if the given variable is a function - * @method - * @memberof Popper.Utils - * @argument {Any} functionToCheck - variable to check - * @returns {Boolean} answer to: is a function? - */ - function isFunction(functionToCheck) { - var getType = {}; - return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; - } - - /** - * Get CSS computed property of the given element - * @method - * @memberof Popper.Utils - * @argument {Eement} element - * @argument {String} property - */ - function getStyleComputedProperty(element, property) { - if (element.nodeType !== 1) { - return []; - } - // NOTE: 1 DOM access here - var css = getComputedStyle(element, null); - return property ? css[property] : css; - } - - /** - * Returns the parentNode or the host of the element - * @method - * @memberof Popper.Utils - * @argument {Element} element - * @returns {Element} parent - */ - function getParentNode(element) { - if (element.nodeName === 'HTML') { - return element; - } - return element.parentNode || element.host; - } - - /** - * Returns the scrolling parent of the given element - * @method - * @memberof Popper.Utils - * @argument {Element} element - * @returns {Element} scroll parent - */ - function getScrollParent(element) { - // Return body, `getScroll` will take care to get the correct `scrollTop` from it - if (!element) { - return document.body; - } - - switch (element.nodeName) { - case 'HTML': - case 'BODY': - return element.ownerDocument.body; - case '#document': - return element.body; - } - - // Firefox want us to check `-x` and `-y` variations as well - - var _getStyleComputedProp = getStyleComputedProperty(element), - overflow = _getStyleComputedProp.overflow, - overflowX = _getStyleComputedProp.overflowX, - overflowY = _getStyleComputedProp.overflowY; - - if (/(auto|scroll|overlay)/.test(overflow + overflowY + overflowX)) { - return element; - } - - return getScrollParent(getParentNode(element)); - } - - var isIE11 = isBrowser && !!(window.MSInputMethodContext && document.documentMode); - var isIE10 = isBrowser && /MSIE 10/.test(navigator.userAgent); - - /** - * Determines if the browser is Internet Explorer - * @method - * @memberof Popper.Utils - * @param {Number} version to check - * @returns {Boolean} isIE - */ - function isIE(version) { - if (version === 11) { - return isIE11; - } - if (version === 10) { - return isIE10; - } - return isIE11 || isIE10; - } - - /** - * Returns the offset parent of the given element - * @method - * @memberof Popper.Utils - * @argument {Element} element - * @returns {Element} offset parent - */ - function getOffsetParent(element) { - if (!element) { - return document.documentElement; - } - - var noOffsetParent = isIE(10) ? document.body : null; - - // NOTE: 1 DOM access here - var offsetParent = element.offsetParent; - // Skip hidden elements which don't have an offsetParent - while (offsetParent === noOffsetParent && element.nextElementSibling) { - offsetParent = (element = element.nextElementSibling).offsetParent; - } - - var nodeName = offsetParent && offsetParent.nodeName; - - if (!nodeName || nodeName === 'BODY' || nodeName === 'HTML') { - return element ? element.ownerDocument.documentElement : document.documentElement; - } - - // .offsetParent will return the closest TD or TABLE in case - // no offsetParent is present, I hate this job... - if (['TD', 'TABLE'].indexOf(offsetParent.nodeName) !== -1 && getStyleComputedProperty(offsetParent, 'position') === 'static') { - return getOffsetParent(offsetParent); - } - - return offsetParent; - } - - function isOffsetContainer(element) { - var nodeName = element.nodeName; - - if (nodeName === 'BODY') { - return false; - } - return nodeName === 'HTML' || getOffsetParent(element.firstElementChild) === element; - } - - /** - * Finds the root node (document, shadowDOM root) of the given element - * @method - * @memberof Popper.Utils - * @argument {Element} node - * @returns {Element} root node - */ - function getRoot(node) { - if (node.parentNode !== null) { - return getRoot(node.parentNode); - } - - return node; - } - - /** - * Finds the offset parent common to the two provided nodes - * @method - * @memberof Popper.Utils - * @argument {Element} element1 - * @argument {Element} element2 - * @returns {Element} common offset parent - */ - function findCommonOffsetParent(element1, element2) { - // This check is needed to avoid errors in case one of the elements isn't defined for any reason - if (!element1 || !element1.nodeType || !element2 || !element2.nodeType) { - return document.documentElement; - } - - // Here we make sure to give as "start" the element that comes first in the DOM - var order = element1.compareDocumentPosition(element2) & Node.DOCUMENT_POSITION_FOLLOWING; - var start = order ? element1 : element2; - var end = order ? element2 : element1; - - // Get common ancestor container - var range = document.createRange(); - range.setStart(start, 0); - range.setEnd(end, 0); - var commonAncestorContainer = range.commonAncestorContainer; - - // Both nodes are inside #document - - if (element1 !== commonAncestorContainer && element2 !== commonAncestorContainer || start.contains(end)) { - if (isOffsetContainer(commonAncestorContainer)) { - return commonAncestorContainer; - } - - return getOffsetParent(commonAncestorContainer); - } - - // one of the nodes is inside shadowDOM, find which one - var element1root = getRoot(element1); - if (element1root.host) { - return findCommonOffsetParent(element1root.host, element2); - } else { - return findCommonOffsetParent(element1, getRoot(element2).host); - } - } - - /** - * Gets the scroll value of the given element in the given side (top and left) - * @method - * @memberof Popper.Utils - * @argument {Element} element - * @argument {String} side `top` or `left` - * @returns {number} amount of scrolled pixels - */ - function getScroll(element) { - var side = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'top'; - - var upperSide = side === 'top' ? 'scrollTop' : 'scrollLeft'; - var nodeName = element.nodeName; - - if (nodeName === 'BODY' || nodeName === 'HTML') { - var html = element.ownerDocument.documentElement; - var scrollingElement = element.ownerDocument.scrollingElement || html; - return scrollingElement[upperSide]; - } - - return element[upperSide]; - } - - /* - * Sum or subtract the element scroll values (left and top) from a given rect object - * @method - * @memberof Popper.Utils - * @param {Object} rect - Rect object you want to change - * @param {HTMLElement} element - The element from the function reads the scroll values - * @param {Boolean} subtract - set to true if you want to subtract the scroll values - * @return {Object} rect - The modifier rect object - */ - function includeScroll(rect, element) { - var subtract = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; - - var scrollTop = getScroll(element, 'top'); - var scrollLeft = getScroll(element, 'left'); - var modifier = subtract ? -1 : 1; - rect.top += scrollTop * modifier; - rect.bottom += scrollTop * modifier; - rect.left += scrollLeft * modifier; - rect.right += scrollLeft * modifier; - return rect; - } - - /* - * Helper to detect borders of a given element - * @method - * @memberof Popper.Utils - * @param {CSSStyleDeclaration} styles - * Result of `getStyleComputedProperty` on the given element - * @param {String} axis - `x` or `y` - * @return {number} borders - The borders size of the given axis - */ - - function getBordersSize(styles, axis) { - var sideA = axis === 'x' ? 'Left' : 'Top'; - var sideB = sideA === 'Left' ? 'Right' : 'Bottom'; - - return parseFloat(styles['border' + sideA + 'Width'], 10) + parseFloat(styles['border' + sideB + 'Width'], 10); - } - - function getSize(axis, body, html, computedStyle) { - return Math.max(body['offset' + axis], body['scroll' + axis], html['client' + axis], html['offset' + axis], html['scroll' + axis], isIE(10) ? html['offset' + axis] + computedStyle['margin' + (axis === 'Height' ? 'Top' : 'Left')] + computedStyle['margin' + (axis === 'Height' ? 'Bottom' : 'Right')] : 0); - } - - function getWindowSizes() { - var body = document.body; - var html = document.documentElement; - var computedStyle = isIE(10) && getComputedStyle(html); - - return { - height: getSize('Height', body, html, computedStyle), - width: getSize('Width', body, html, computedStyle) - }; - } - - var classCallCheck = function (instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - }; - - var createClass = function () { - function defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, descriptor.key, descriptor); - } - } - - return function (Constructor, protoProps, staticProps) { - if (protoProps) defineProperties(Constructor.prototype, protoProps); - if (staticProps) defineProperties(Constructor, staticProps); - return Constructor; - }; - }(); - - - - - - var defineProperty = function (obj, key, value) { - if (key in obj) { - Object.defineProperty(obj, key, { - value: value, - enumerable: true, - configurable: true, - writable: true - }); - } else { - obj[key] = value; - } - - return obj; - }; - - var _extends = Object.assign || function (target) { - for (var i = 1; i < arguments.length; i++) { - var source = arguments[i]; - - for (var key in source) { - if (Object.prototype.hasOwnProperty.call(source, key)) { - target[key] = source[key]; - } - } - } - - return target; - }; - - /** - * Given element offsets, generate an output similar to getBoundingClientRect - * @method - * @memberof Popper.Utils - * @argument {Object} offsets - * @returns {Object} ClientRect like output - */ - function getClientRect(offsets) { - return _extends({}, offsets, { - right: offsets.left + offsets.width, - bottom: offsets.top + offsets.height - }); - } - - /** - * Get bounding client rect of given element - * @method - * @memberof Popper.Utils - * @param {HTMLElement} element - * @return {Object} client rect - */ - function getBoundingClientRect(element) { - var rect = {}; - - // IE10 10 FIX: Please, don't ask, the element isn't - // considered in DOM in some circumstances... - // This isn't reproducible in IE10 compatibility mode of IE11 - try { - if (isIE(10)) { - rect = element.getBoundingClientRect(); - var scrollTop = getScroll(element, 'top'); - var scrollLeft = getScroll(element, 'left'); - rect.top += scrollTop; - rect.left += scrollLeft; - rect.bottom += scrollTop; - rect.right += scrollLeft; - } else { - rect = element.getBoundingClientRect(); - } - } catch (e) {} - - var result = { - left: rect.left, - top: rect.top, - width: rect.right - rect.left, - height: rect.bottom - rect.top - }; - - // subtract scrollbar size from sizes - var sizes = element.nodeName === 'HTML' ? getWindowSizes() : {}; - var width = sizes.width || element.clientWidth || result.right - result.left; - var height = sizes.height || element.clientHeight || result.bottom - result.top; - - var horizScrollbar = element.offsetWidth - width; - var vertScrollbar = element.offsetHeight - height; - - // if an hypothetical scrollbar is detected, we must be sure it's not a `border` - // we make this check conditional for performance reasons - if (horizScrollbar || vertScrollbar) { - var styles = getStyleComputedProperty(element); - horizScrollbar -= getBordersSize(styles, 'x'); - vertScrollbar -= getBordersSize(styles, 'y'); - - result.width -= horizScrollbar; - result.height -= vertScrollbar; - } - - return getClientRect(result); - } - - function getOffsetRectRelativeToArbitraryNode(children, parent) { - var fixedPosition = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; - - var isIE10 = isIE(10); - var isHTML = parent.nodeName === 'HTML'; - var childrenRect = getBoundingClientRect(children); - var parentRect = getBoundingClientRect(parent); - var scrollParent = getScrollParent(children); - - var styles = getStyleComputedProperty(parent); - var borderTopWidth = parseFloat(styles.borderTopWidth, 10); - var borderLeftWidth = parseFloat(styles.borderLeftWidth, 10); - - // In cases where the parent is fixed, we must ignore negative scroll in offset calc - if (fixedPosition && parent.nodeName === 'HTML') { - parentRect.top = Math.max(parentRect.top, 0); - parentRect.left = Math.max(parentRect.left, 0); - } - var offsets = getClientRect({ - top: childrenRect.top - parentRect.top - borderTopWidth, - left: childrenRect.left - parentRect.left - borderLeftWidth, - width: childrenRect.width, - height: childrenRect.height - }); - offsets.marginTop = 0; - offsets.marginLeft = 0; - - // Subtract margins of documentElement in case it's being used as parent - // we do this only on HTML because it's the only element that behaves - // differently when margins are applied to it. The margins are included in - // the box of the documentElement, in the other cases not. - if (!isIE10 && isHTML) { - var marginTop = parseFloat(styles.marginTop, 10); - var marginLeft = parseFloat(styles.marginLeft, 10); - - offsets.top -= borderTopWidth - marginTop; - offsets.bottom -= borderTopWidth - marginTop; - offsets.left -= borderLeftWidth - marginLeft; - offsets.right -= borderLeftWidth - marginLeft; - - // Attach marginTop and marginLeft because in some circumstances we may need them - offsets.marginTop = marginTop; - offsets.marginLeft = marginLeft; - } - - if (isIE10 && !fixedPosition ? parent.contains(scrollParent) : parent === scrollParent && scrollParent.nodeName !== 'BODY') { - offsets = includeScroll(offsets, parent); - } - - return offsets; - } - - function getViewportOffsetRectRelativeToArtbitraryNode(element) { - var excludeScroll = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - - var html = element.ownerDocument.documentElement; - var relativeOffset = getOffsetRectRelativeToArbitraryNode(element, html); - var width = Math.max(html.clientWidth, window.innerWidth || 0); - var height = Math.max(html.clientHeight, window.innerHeight || 0); - - var scrollTop = !excludeScroll ? getScroll(html) : 0; - var scrollLeft = !excludeScroll ? getScroll(html, 'left') : 0; - - var offset = { - top: scrollTop - relativeOffset.top + relativeOffset.marginTop, - left: scrollLeft - relativeOffset.left + relativeOffset.marginLeft, - width: width, - height: height - }; - - return getClientRect(offset); - } - - /** - * Check if the given element is fixed or is inside a fixed parent - * @method - * @memberof Popper.Utils - * @argument {Element} element - * @argument {Element} customContainer - * @returns {Boolean} answer to "isFixed?" - */ - function isFixed(element) { - var nodeName = element.nodeName; - if (nodeName === 'BODY' || nodeName === 'HTML') { - return false; - } - if (getStyleComputedProperty(element, 'position') === 'fixed') { - return true; - } - return isFixed(getParentNode(element)); - } - - /** - * Finds the first parent of an element that has a transformed property defined - * @method - * @memberof Popper.Utils - * @argument {Element} element - * @returns {Element} first transformed parent or documentElement - */ - - function getFixedPositionOffsetParent(element) { - // This check is needed to avoid errors in case one of the elements isn't defined for any reason - if (!element || !element.parentElement || isIE()) { - return document.documentElement; - } - var el = element.parentElement; - while (el && getStyleComputedProperty(el, 'transform') === 'none') { - el = el.parentElement; - } - return el || document.documentElement; - } - - /** - * Computed the boundaries limits and return them - * @method - * @memberof Popper.Utils - * @param {HTMLElement} popper - * @param {HTMLElement} reference - * @param {number} padding - * @param {HTMLElement} boundariesElement - Element used to define the boundaries - * @param {Boolean} fixedPosition - Is in fixed position mode - * @returns {Object} Coordinates of the boundaries - */ - function getBoundaries(popper, reference, padding, boundariesElement) { - var fixedPosition = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; - - // NOTE: 1 DOM access here - - var boundaries = { top: 0, left: 0 }; - var offsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, reference); - - // Handle viewport case - if (boundariesElement === 'viewport') { - boundaries = getViewportOffsetRectRelativeToArtbitraryNode(offsetParent, fixedPosition); - } else { - // Handle other cases based on DOM element used as boundaries - var boundariesNode = void 0; - if (boundariesElement === 'scrollParent') { - boundariesNode = getScrollParent(getParentNode(reference)); - if (boundariesNode.nodeName === 'BODY') { - boundariesNode = popper.ownerDocument.documentElement; - } - } else if (boundariesElement === 'window') { - boundariesNode = popper.ownerDocument.documentElement; - } else { - boundariesNode = boundariesElement; - } - - var offsets = getOffsetRectRelativeToArbitraryNode(boundariesNode, offsetParent, fixedPosition); - - // In case of HTML, we need a different computation - if (boundariesNode.nodeName === 'HTML' && !isFixed(offsetParent)) { - var _getWindowSizes = getWindowSizes(), - height = _getWindowSizes.height, - width = _getWindowSizes.width; - - boundaries.top += offsets.top - offsets.marginTop; - boundaries.bottom = height + offsets.top; - boundaries.left += offsets.left - offsets.marginLeft; - boundaries.right = width + offsets.left; - } else { - // for all the other DOM elements, this one is good - boundaries = offsets; - } - } - - // Add paddings - boundaries.left += padding; - boundaries.top += padding; - boundaries.right -= padding; - boundaries.bottom -= padding; - - return boundaries; - } - - function getArea(_ref) { - var width = _ref.width, - height = _ref.height; - - return width * height; - } - - /** - * Utility used to transform the `auto` placement to the placement with more - * available space. - * @method - * @memberof Popper.Utils - * @argument {Object} data - The data object generated by update method - * @argument {Object} options - Modifiers configuration and options - * @returns {Object} The data object, properly modified - */ - function computeAutoPlacement(placement, refRect, popper, reference, boundariesElement) { - var padding = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 0; - - if (placement.indexOf('auto') === -1) { - return placement; - } - - var boundaries = getBoundaries(popper, reference, padding, boundariesElement); - - var rects = { - top: { - width: boundaries.width, - height: refRect.top - boundaries.top - }, - right: { - width: boundaries.right - refRect.right, - height: boundaries.height - }, - bottom: { - width: boundaries.width, - height: boundaries.bottom - refRect.bottom - }, - left: { - width: refRect.left - boundaries.left, - height: boundaries.height - } - }; - - var sortedAreas = Object.keys(rects).map(function (key) { - return _extends({ - key: key - }, rects[key], { - area: getArea(rects[key]) - }); - }).sort(function (a, b) { - return b.area - a.area; - }); - - var filteredAreas = sortedAreas.filter(function (_ref2) { - var width = _ref2.width, - height = _ref2.height; - return width >= popper.clientWidth && height >= popper.clientHeight; - }); - - var computedPlacement = filteredAreas.length > 0 ? filteredAreas[0].key : sortedAreas[0].key; - - var variation = placement.split('-')[1]; - - return computedPlacement + (variation ? '-' + variation : ''); - } - - /** - * Get offsets to the reference element - * @method - * @memberof Popper.Utils - * @param {Object} state - * @param {Element} popper - the popper element - * @param {Element} reference - the reference element (the popper will be relative to this) - * @param {Element} fixedPosition - is in fixed position mode - * @returns {Object} An object containing the offsets which will be applied to the popper - */ - function getReferenceOffsets(state, popper, reference) { - var fixedPosition = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; - - var commonOffsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, reference); - return getOffsetRectRelativeToArbitraryNode(reference, commonOffsetParent, fixedPosition); - } - - /** - * Get the outer sizes of the given element (offset size + margins) - * @method - * @memberof Popper.Utils - * @argument {Element} element - * @returns {Object} object containing width and height properties - */ - function getOuterSizes(element) { - var styles = getComputedStyle(element); - var x = parseFloat(styles.marginTop) + parseFloat(styles.marginBottom); - var y = parseFloat(styles.marginLeft) + parseFloat(styles.marginRight); - var result = { - width: element.offsetWidth + y, - height: element.offsetHeight + x - }; - return result; - } - - /** - * Get the opposite placement of the given one - * @method - * @memberof Popper.Utils - * @argument {String} placement - * @returns {String} flipped placement - */ - function getOppositePlacement(placement) { - var hash = { left: 'right', right: 'left', bottom: 'top', top: 'bottom' }; - return placement.replace(/left|right|bottom|top/g, function (matched) { - return hash[matched]; - }); - } - - /** - * Get offsets to the popper - * @method - * @memberof Popper.Utils - * @param {Object} position - CSS position the Popper will get applied - * @param {HTMLElement} popper - the popper element - * @param {Object} referenceOffsets - the reference offsets (the popper will be relative to this) - * @param {String} placement - one of the valid placement options - * @returns {Object} popperOffsets - An object containing the offsets which will be applied to the popper - */ - function getPopperOffsets(popper, referenceOffsets, placement) { - placement = placement.split('-')[0]; - - // Get popper node sizes - var popperRect = getOuterSizes(popper); - - // Add position, width and height to our offsets object - var popperOffsets = { - width: popperRect.width, - height: popperRect.height - }; - - // depending by the popper placement we have to compute its offsets slightly differently - var isHoriz = ['right', 'left'].indexOf(placement) !== -1; - var mainSide = isHoriz ? 'top' : 'left'; - var secondarySide = isHoriz ? 'left' : 'top'; - var measurement = isHoriz ? 'height' : 'width'; - var secondaryMeasurement = !isHoriz ? 'height' : 'width'; - - popperOffsets[mainSide] = referenceOffsets[mainSide] + referenceOffsets[measurement] / 2 - popperRect[measurement] / 2; - if (placement === secondarySide) { - popperOffsets[secondarySide] = referenceOffsets[secondarySide] - popperRect[secondaryMeasurement]; - } else { - popperOffsets[secondarySide] = referenceOffsets[getOppositePlacement(secondarySide)]; - } - - return popperOffsets; - } - - /** - * Mimics the `find` method of Array - * @method - * @memberof Popper.Utils - * @argument {Array} arr - * @argument prop - * @argument value - * @returns index or -1 - */ - function find(arr, check) { - // use native find if supported - if (Array.prototype.find) { - return arr.find(check); - } - - // use `filter` to obtain the same behavior of `find` - return arr.filter(check)[0]; - } - - /** - * Return the index of the matching object - * @method - * @memberof Popper.Utils - * @argument {Array} arr - * @argument prop - * @argument value - * @returns index or -1 - */ - function findIndex(arr, prop, value) { - // use native findIndex if supported - if (Array.prototype.findIndex) { - return arr.findIndex(function (cur) { - return cur[prop] === value; - }); - } - - // use `find` + `indexOf` if `findIndex` isn't supported - var match = find(arr, function (obj) { - return obj[prop] === value; - }); - return arr.indexOf(match); - } - - /** - * Loop trough the list of modifiers and run them in order, - * each of them will then edit the data object. - * @method - * @memberof Popper.Utils - * @param {dataObject} data - * @param {Array} modifiers - * @param {String} ends - Optional modifier name used as stopper - * @returns {dataObject} - */ - function runModifiers(modifiers, data, ends) { - var modifiersToRun = ends === undefined ? modifiers : modifiers.slice(0, findIndex(modifiers, 'name', ends)); - - modifiersToRun.forEach(function (modifier) { - if (modifier['function']) { - // eslint-disable-line dot-notation - console.warn('`modifier.function` is deprecated, use `modifier.fn`!'); - } - var fn = modifier['function'] || modifier.fn; // eslint-disable-line dot-notation - if (modifier.enabled && isFunction(fn)) { - // Add properties to offsets to make them a complete clientRect object - // we do this before each modifier to make sure the previous one doesn't - // mess with these values - data.offsets.popper = getClientRect(data.offsets.popper); - data.offsets.reference = getClientRect(data.offsets.reference); - - data = fn(data, modifier); - } - }); - - return data; - } - - /** - * Updates the position of the popper, computing the new offsets and applying - * the new style.
- * Prefer `scheduleUpdate` over `update` because of performance reasons. - * @method - * @memberof Popper - */ - function update() { - // if popper is destroyed, don't perform any further update - if (this.state.isDestroyed) { - return; - } - - var data = { - instance: this, - styles: {}, - arrowStyles: {}, - attributes: {}, - flipped: false, - offsets: {} - }; - - // compute reference element offsets - data.offsets.reference = getReferenceOffsets(this.state, this.popper, this.reference, this.options.positionFixed); - - // compute auto placement, store placement inside the data object, - // modifiers will be able to edit `placement` if needed - // and refer to originalPlacement to know the original value - data.placement = computeAutoPlacement(this.options.placement, data.offsets.reference, this.popper, this.reference, this.options.modifiers.flip.boundariesElement, this.options.modifiers.flip.padding); - - // store the computed placement inside `originalPlacement` - data.originalPlacement = data.placement; - - data.positionFixed = this.options.positionFixed; - - // compute the popper offsets - data.offsets.popper = getPopperOffsets(this.popper, data.offsets.reference, data.placement); - - data.offsets.popper.position = this.options.positionFixed ? 'fixed' : 'absolute'; - - // run the modifiers - data = runModifiers(this.modifiers, data); - - // the first `update` will call `onCreate` callback - // the other ones will call `onUpdate` callback - if (!this.state.isCreated) { - this.state.isCreated = true; - this.options.onCreate(data); - } else { - this.options.onUpdate(data); - } - } - - /** - * Helper used to know if the given modifier is enabled. - * @method - * @memberof Popper.Utils - * @returns {Boolean} - */ - function isModifierEnabled(modifiers, modifierName) { - return modifiers.some(function (_ref) { - var name = _ref.name, - enabled = _ref.enabled; - return enabled && name === modifierName; - }); - } - - /** - * Get the prefixed supported property name - * @method - * @memberof Popper.Utils - * @argument {String} property (camelCase) - * @returns {String} prefixed property (camelCase or PascalCase, depending on the vendor prefix) - */ - function getSupportedPropertyName(property) { - var prefixes = [false, 'ms', 'Webkit', 'Moz', 'O']; - var upperProp = property.charAt(0).toUpperCase() + property.slice(1); - - for (var i = 0; i < prefixes.length; i++) { - var prefix = prefixes[i]; - var toCheck = prefix ? '' + prefix + upperProp : property; - if (typeof document.body.style[toCheck] !== 'undefined') { - return toCheck; - } - } - return null; - } - - /** - * Destroy the popper - * @method - * @memberof Popper - */ - function destroy() { - this.state.isDestroyed = true; - - // touch DOM only if `applyStyle` modifier is enabled - if (isModifierEnabled(this.modifiers, 'applyStyle')) { - this.popper.removeAttribute('x-placement'); - this.popper.style.position = ''; - this.popper.style.top = ''; - this.popper.style.left = ''; - this.popper.style.right = ''; - this.popper.style.bottom = ''; - this.popper.style.willChange = ''; - this.popper.style[getSupportedPropertyName('transform')] = ''; - } - - this.disableEventListeners(); - - // remove the popper if user explicity asked for the deletion on destroy - // do not use `remove` because IE11 doesn't support it - if (this.options.removeOnDestroy) { - this.popper.parentNode.removeChild(this.popper); - } - return this; - } - - /** - * Get the window associated with the element - * @argument {Element} element - * @returns {Window} - */ - function getWindow(element) { - var ownerDocument = element.ownerDocument; - return ownerDocument ? ownerDocument.defaultView : window; - } - - function attachToScrollParents(scrollParent, event, callback, scrollParents) { - var isBody = scrollParent.nodeName === 'BODY'; - var target = isBody ? scrollParent.ownerDocument.defaultView : scrollParent; - target.addEventListener(event, callback, { passive: true }); - - if (!isBody) { - attachToScrollParents(getScrollParent(target.parentNode), event, callback, scrollParents); - } - scrollParents.push(target); - } - - /** - * Setup needed event listeners used to update the popper position - * @method - * @memberof Popper.Utils - * @private - */ - function setupEventListeners(reference, options, state, updateBound) { - // Resize event listener on window - state.updateBound = updateBound; - getWindow(reference).addEventListener('resize', state.updateBound, { passive: true }); - - // Scroll event listener on scroll parents - var scrollElement = getScrollParent(reference); - attachToScrollParents(scrollElement, 'scroll', state.updateBound, state.scrollParents); - state.scrollElement = scrollElement; - state.eventsEnabled = true; - - return state; - } - - /** - * It will add resize/scroll events and start recalculating - * position of the popper element when they are triggered. - * @method - * @memberof Popper - */ - function enableEventListeners() { - if (!this.state.eventsEnabled) { - this.state = setupEventListeners(this.reference, this.options, this.state, this.scheduleUpdate); - } - } - - /** - * Remove event listeners used to update the popper position - * @method - * @memberof Popper.Utils - * @private - */ - function removeEventListeners(reference, state) { - // Remove resize event listener on window - getWindow(reference).removeEventListener('resize', state.updateBound); - - // Remove scroll event listener on scroll parents - state.scrollParents.forEach(function (target) { - target.removeEventListener('scroll', state.updateBound); - }); - - // Reset state - state.updateBound = null; - state.scrollParents = []; - state.scrollElement = null; - state.eventsEnabled = false; - return state; - } - - /** - * It will remove resize/scroll events and won't recalculate popper position - * when they are triggered. It also won't trigger onUpdate callback anymore, - * unless you call `update` method manually. - * @method - * @memberof Popper - */ - function disableEventListeners() { - if (this.state.eventsEnabled) { - cancelAnimationFrame(this.scheduleUpdate); - this.state = removeEventListeners(this.reference, this.state); - } - } - - /** - * Tells if a given input is a number - * @method - * @memberof Popper.Utils - * @param {*} input to check - * @return {Boolean} - */ - function isNumeric(n) { - return n !== '' && !isNaN(parseFloat(n)) && isFinite(n); - } - - /** - * Set the style to the given popper - * @method - * @memberof Popper.Utils - * @argument {Element} element - Element to apply the style to - * @argument {Object} styles - * Object with a list of properties and values which will be applied to the element - */ - function setStyles(element, styles) { - Object.keys(styles).forEach(function (prop) { - var unit = ''; - // add unit if the value is numeric and is one of the following - if (['width', 'height', 'top', 'right', 'bottom', 'left'].indexOf(prop) !== -1 && isNumeric(styles[prop])) { - unit = 'px'; - } - element.style[prop] = styles[prop] + unit; - }); - } - - /** - * Set the attributes to the given popper - * @method - * @memberof Popper.Utils - * @argument {Element} element - Element to apply the attributes to - * @argument {Object} styles - * Object with a list of properties and values which will be applied to the element - */ - function setAttributes(element, attributes) { - Object.keys(attributes).forEach(function (prop) { - var value = attributes[prop]; - if (value !== false) { - element.setAttribute(prop, attributes[prop]); - } else { - element.removeAttribute(prop); - } - }); - } - - /** - * @function - * @memberof Modifiers - * @argument {Object} data - The data object generated by `update` method - * @argument {Object} data.styles - List of style properties - values to apply to popper element - * @argument {Object} data.attributes - List of attribute properties - values to apply to popper element - * @argument {Object} options - Modifiers configuration and options - * @returns {Object} The same data object - */ - function applyStyle(data) { - // any property present in `data.styles` will be applied to the popper, - // in this way we can make the 3rd party modifiers add custom styles to it - // Be aware, modifiers could override the properties defined in the previous - // lines of this modifier! - setStyles(data.instance.popper, data.styles); - - // any property present in `data.attributes` will be applied to the popper, - // they will be set as HTML attributes of the element - setAttributes(data.instance.popper, data.attributes); - - // if arrowElement is defined and arrowStyles has some properties - if (data.arrowElement && Object.keys(data.arrowStyles).length) { - setStyles(data.arrowElement, data.arrowStyles); - } - - return data; - } - - /** - * Set the x-placement attribute before everything else because it could be used - * to add margins to the popper margins needs to be calculated to get the - * correct popper offsets. - * @method - * @memberof Popper.modifiers - * @param {HTMLElement} reference - The reference element used to position the popper - * @param {HTMLElement} popper - The HTML element used as popper - * @param {Object} options - Popper.js options - */ - function applyStyleOnLoad(reference, popper, options, modifierOptions, state) { - // compute reference element offsets - var referenceOffsets = getReferenceOffsets(state, popper, reference, options.positionFixed); - - // compute auto placement, store placement inside the data object, - // modifiers will be able to edit `placement` if needed - // and refer to originalPlacement to know the original value - var placement = computeAutoPlacement(options.placement, referenceOffsets, popper, reference, options.modifiers.flip.boundariesElement, options.modifiers.flip.padding); - - popper.setAttribute('x-placement', placement); - - // Apply `position` to popper before anything else because - // without the position applied we can't guarantee correct computations - setStyles(popper, { position: options.positionFixed ? 'fixed' : 'absolute' }); - - return options; - } - - /** - * @function - * @memberof Modifiers - * @argument {Object} data - The data object generated by `update` method - * @argument {Object} options - Modifiers configuration and options - * @returns {Object} The data object, properly modified - */ - function computeStyle(data, options) { - var x = options.x, - y = options.y; - var popper = data.offsets.popper; - - // Remove this legacy support in Popper.js v2 - - var legacyGpuAccelerationOption = find(data.instance.modifiers, function (modifier) { - return modifier.name === 'applyStyle'; - }).gpuAcceleration; - if (legacyGpuAccelerationOption !== undefined) { - console.warn('WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!'); - } - var gpuAcceleration = legacyGpuAccelerationOption !== undefined ? legacyGpuAccelerationOption : options.gpuAcceleration; - - var offsetParent = getOffsetParent(data.instance.popper); - var offsetParentRect = getBoundingClientRect(offsetParent); - - // Styles - var styles = { - position: popper.position - }; - - // Avoid blurry text by using full pixel integers. - // For pixel-perfect positioning, top/bottom prefers rounded - // values, while left/right prefers floored values. - var offsets = { - left: Math.floor(popper.left), - top: Math.round(popper.top), - bottom: Math.round(popper.bottom), - right: Math.floor(popper.right) - }; - - var sideA = x === 'bottom' ? 'top' : 'bottom'; - var sideB = y === 'right' ? 'left' : 'right'; - - // if gpuAcceleration is set to `true` and transform is supported, - // we use `translate3d` to apply the position to the popper we - // automatically use the supported prefixed version if needed - var prefixedProperty = getSupportedPropertyName('transform'); - - // now, let's make a step back and look at this code closely (wtf?) - // If the content of the popper grows once it's been positioned, it - // may happen that the popper gets misplaced because of the new content - // overflowing its reference element - // To avoid this problem, we provide two options (x and y), which allow - // the consumer to define the offset origin. - // If we position a popper on top of a reference element, we can set - // `x` to `top` to make the popper grow towards its top instead of - // its bottom. - var left = void 0, - top = void 0; - if (sideA === 'bottom') { - top = -offsetParentRect.height + offsets.bottom; - } else { - top = offsets.top; - } - if (sideB === 'right') { - left = -offsetParentRect.width + offsets.right; - } else { - left = offsets.left; - } - if (gpuAcceleration && prefixedProperty) { - styles[prefixedProperty] = 'translate3d(' + left + 'px, ' + top + 'px, 0)'; - styles[sideA] = 0; - styles[sideB] = 0; - styles.willChange = 'transform'; - } else { - // othwerise, we use the standard `top`, `left`, `bottom` and `right` properties - var invertTop = sideA === 'bottom' ? -1 : 1; - var invertLeft = sideB === 'right' ? -1 : 1; - styles[sideA] = top * invertTop; - styles[sideB] = left * invertLeft; - styles.willChange = sideA + ', ' + sideB; - } - - // Attributes - var attributes = { - 'x-placement': data.placement - }; - - // Update `data` attributes, styles and arrowStyles - data.attributes = _extends({}, attributes, data.attributes); - data.styles = _extends({}, styles, data.styles); - data.arrowStyles = _extends({}, data.offsets.arrow, data.arrowStyles); - - return data; - } - - /** - * Helper used to know if the given modifier depends from another one.
- * It checks if the needed modifier is listed and enabled. - * @method - * @memberof Popper.Utils - * @param {Array} modifiers - list of modifiers - * @param {String} requestingName - name of requesting modifier - * @param {String} requestedName - name of requested modifier - * @returns {Boolean} - */ - function isModifierRequired(modifiers, requestingName, requestedName) { - var requesting = find(modifiers, function (_ref) { - var name = _ref.name; - return name === requestingName; - }); - - var isRequired = !!requesting && modifiers.some(function (modifier) { - return modifier.name === requestedName && modifier.enabled && modifier.order < requesting.order; - }); - - if (!isRequired) { - var _requesting = '`' + requestingName + '`'; - var requested = '`' + requestedName + '`'; - console.warn(requested + ' modifier is required by ' + _requesting + ' modifier in order to work, be sure to include it before ' + _requesting + '!'); - } - return isRequired; - } - - /** - * @function - * @memberof Modifiers - * @argument {Object} data - The data object generated by update method - * @argument {Object} options - Modifiers configuration and options - * @returns {Object} The data object, properly modified - */ - function arrow(data, options) { - var _data$offsets$arrow; - - // arrow depends on keepTogether in order to work - if (!isModifierRequired(data.instance.modifiers, 'arrow', 'keepTogether')) { - return data; - } - - var arrowElement = options.element; - - // if arrowElement is a string, suppose it's a CSS selector - if (typeof arrowElement === 'string') { - arrowElement = data.instance.popper.querySelector(arrowElement); - - // if arrowElement is not found, don't run the modifier - if (!arrowElement) { - return data; - } - } else { - // if the arrowElement isn't a query selector we must check that the - // provided DOM node is child of its popper node - if (!data.instance.popper.contains(arrowElement)) { - console.warn('WARNING: `arrow.element` must be child of its popper element!'); - return data; - } - } - - var placement = data.placement.split('-')[0]; - var _data$offsets = data.offsets, - popper = _data$offsets.popper, - reference = _data$offsets.reference; - - var isVertical = ['left', 'right'].indexOf(placement) !== -1; - - var len = isVertical ? 'height' : 'width'; - var sideCapitalized = isVertical ? 'Top' : 'Left'; - var side = sideCapitalized.toLowerCase(); - var altSide = isVertical ? 'left' : 'top'; - var opSide = isVertical ? 'bottom' : 'right'; - var arrowElementSize = getOuterSizes(arrowElement)[len]; - - // - // extends keepTogether behavior making sure the popper and its - // reference have enough pixels in conjuction - // - - // top/left side - if (reference[opSide] - arrowElementSize < popper[side]) { - data.offsets.popper[side] -= popper[side] - (reference[opSide] - arrowElementSize); - } - // bottom/right side - if (reference[side] + arrowElementSize > popper[opSide]) { - data.offsets.popper[side] += reference[side] + arrowElementSize - popper[opSide]; - } - data.offsets.popper = getClientRect(data.offsets.popper); - - // compute center of the popper - var center = reference[side] + reference[len] / 2 - arrowElementSize / 2; - - // Compute the sideValue using the updated popper offsets - // take popper margin in account because we don't have this info available - var css = getStyleComputedProperty(data.instance.popper); - var popperMarginSide = parseFloat(css['margin' + sideCapitalized], 10); - var popperBorderSide = parseFloat(css['border' + sideCapitalized + 'Width'], 10); - var sideValue = center - data.offsets.popper[side] - popperMarginSide - popperBorderSide; - - // prevent arrowElement from being placed not contiguously to its popper - sideValue = Math.max(Math.min(popper[len] - arrowElementSize, sideValue), 0); - - data.arrowElement = arrowElement; - data.offsets.arrow = (_data$offsets$arrow = {}, defineProperty(_data$offsets$arrow, side, Math.round(sideValue)), defineProperty(_data$offsets$arrow, altSide, ''), _data$offsets$arrow); - - return data; - } - - /** - * Get the opposite placement variation of the given one - * @method - * @memberof Popper.Utils - * @argument {String} placement variation - * @returns {String} flipped placement variation - */ - function getOppositeVariation(variation) { - if (variation === 'end') { - return 'start'; - } else if (variation === 'start') { - return 'end'; - } - return variation; - } - - /** - * List of accepted placements to use as values of the `placement` option.
- * Valid placements are: - * - `auto` - * - `top` - * - `right` - * - `bottom` - * - `left` - * - * Each placement can have a variation from this list: - * - `-start` - * - `-end` - * - * Variations are interpreted easily if you think of them as the left to right - * written languages. Horizontally (`top` and `bottom`), `start` is left and `end` - * is right.
- * Vertically (`left` and `right`), `start` is top and `end` is bottom. - * - * Some valid examples are: - * - `top-end` (on top of reference, right aligned) - * - `right-start` (on right of reference, top aligned) - * - `bottom` (on bottom, centered) - * - `auto-right` (on the side with more space available, alignment depends by placement) - * - * @static - * @type {Array} - * @enum {String} - * @readonly - * @method placements - * @memberof Popper - */ - var placements = ['auto-start', 'auto', 'auto-end', 'top-start', 'top', 'top-end', 'right-start', 'right', 'right-end', 'bottom-end', 'bottom', 'bottom-start', 'left-end', 'left', 'left-start']; - - // Get rid of `auto` `auto-start` and `auto-end` - var validPlacements = placements.slice(3); - - /** - * Given an initial placement, returns all the subsequent placements - * clockwise (or counter-clockwise). - * - * @method - * @memberof Popper.Utils - * @argument {String} placement - A valid placement (it accepts variations) - * @argument {Boolean} counter - Set to true to walk the placements counterclockwise - * @returns {Array} placements including their variations - */ - function clockwise(placement) { - var counter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - - var index = validPlacements.indexOf(placement); - var arr = validPlacements.slice(index + 1).concat(validPlacements.slice(0, index)); - return counter ? arr.reverse() : arr; - } - - var BEHAVIORS = { - FLIP: 'flip', - CLOCKWISE: 'clockwise', - COUNTERCLOCKWISE: 'counterclockwise' - }; - - /** - * @function - * @memberof Modifiers - * @argument {Object} data - The data object generated by update method - * @argument {Object} options - Modifiers configuration and options - * @returns {Object} The data object, properly modified - */ - function flip(data, options) { - // if `inner` modifier is enabled, we can't use the `flip` modifier - if (isModifierEnabled(data.instance.modifiers, 'inner')) { - return data; - } - - if (data.flipped && data.placement === data.originalPlacement) { - // seems like flip is trying to loop, probably there's not enough space on any of the flippable sides - return data; - } - - var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, options.boundariesElement, data.positionFixed); - - var placement = data.placement.split('-')[0]; - var placementOpposite = getOppositePlacement(placement); - var variation = data.placement.split('-')[1] || ''; - - var flipOrder = []; - - switch (options.behavior) { - case BEHAVIORS.FLIP: - flipOrder = [placement, placementOpposite]; - break; - case BEHAVIORS.CLOCKWISE: - flipOrder = clockwise(placement); - break; - case BEHAVIORS.COUNTERCLOCKWISE: - flipOrder = clockwise(placement, true); - break; - default: - flipOrder = options.behavior; - } - - flipOrder.forEach(function (step, index) { - if (placement !== step || flipOrder.length === index + 1) { - return data; - } - - placement = data.placement.split('-')[0]; - placementOpposite = getOppositePlacement(placement); - - var popperOffsets = data.offsets.popper; - var refOffsets = data.offsets.reference; - - // using floor because the reference offsets may contain decimals we are not going to consider here - var floor = Math.floor; - var overlapsRef = placement === 'left' && floor(popperOffsets.right) > floor(refOffsets.left) || placement === 'right' && floor(popperOffsets.left) < floor(refOffsets.right) || placement === 'top' && floor(popperOffsets.bottom) > floor(refOffsets.top) || placement === 'bottom' && floor(popperOffsets.top) < floor(refOffsets.bottom); - - var overflowsLeft = floor(popperOffsets.left) < floor(boundaries.left); - var overflowsRight = floor(popperOffsets.right) > floor(boundaries.right); - var overflowsTop = floor(popperOffsets.top) < floor(boundaries.top); - var overflowsBottom = floor(popperOffsets.bottom) > floor(boundaries.bottom); - - var overflowsBoundaries = placement === 'left' && overflowsLeft || placement === 'right' && overflowsRight || placement === 'top' && overflowsTop || placement === 'bottom' && overflowsBottom; - - // flip the variation if required - var isVertical = ['top', 'bottom'].indexOf(placement) !== -1; - var flippedVariation = !!options.flipVariations && (isVertical && variation === 'start' && overflowsLeft || isVertical && variation === 'end' && overflowsRight || !isVertical && variation === 'start' && overflowsTop || !isVertical && variation === 'end' && overflowsBottom); - - if (overlapsRef || overflowsBoundaries || flippedVariation) { - // this boolean to detect any flip loop - data.flipped = true; - - if (overlapsRef || overflowsBoundaries) { - placement = flipOrder[index + 1]; - } - - if (flippedVariation) { - variation = getOppositeVariation(variation); - } - - data.placement = placement + (variation ? '-' + variation : ''); - - // this object contains `position`, we want to preserve it along with - // any additional property we may add in the future - data.offsets.popper = _extends({}, data.offsets.popper, getPopperOffsets(data.instance.popper, data.offsets.reference, data.placement)); - - data = runModifiers(data.instance.modifiers, data, 'flip'); - } - }); - return data; - } - - /** - * @function - * @memberof Modifiers - * @argument {Object} data - The data object generated by update method - * @argument {Object} options - Modifiers configuration and options - * @returns {Object} The data object, properly modified - */ - function keepTogether(data) { - var _data$offsets = data.offsets, - popper = _data$offsets.popper, - reference = _data$offsets.reference; - - var placement = data.placement.split('-')[0]; - var floor = Math.floor; - var isVertical = ['top', 'bottom'].indexOf(placement) !== -1; - var side = isVertical ? 'right' : 'bottom'; - var opSide = isVertical ? 'left' : 'top'; - var measurement = isVertical ? 'width' : 'height'; - - if (popper[side] < floor(reference[opSide])) { - data.offsets.popper[opSide] = floor(reference[opSide]) - popper[measurement]; - } - if (popper[opSide] > floor(reference[side])) { - data.offsets.popper[opSide] = floor(reference[side]); - } - - return data; - } - - /** - * Converts a string containing value + unit into a px value number - * @function - * @memberof {modifiers~offset} - * @private - * @argument {String} str - Value + unit string - * @argument {String} measurement - `height` or `width` - * @argument {Object} popperOffsets - * @argument {Object} referenceOffsets - * @returns {Number|String} - * Value in pixels, or original string if no values were extracted - */ - function toValue(str, measurement, popperOffsets, referenceOffsets) { - // separate value from unit - var split = str.match(/((?:\-|\+)?\d*\.?\d*)(.*)/); - var value = +split[1]; - var unit = split[2]; - - // If it's not a number it's an operator, I guess - if (!value) { - return str; - } - - if (unit.indexOf('%') === 0) { - var element = void 0; - switch (unit) { - case '%p': - element = popperOffsets; - break; - case '%': - case '%r': - default: - element = referenceOffsets; - } - - var rect = getClientRect(element); - return rect[measurement] / 100 * value; - } else if (unit === 'vh' || unit === 'vw') { - // if is a vh or vw, we calculate the size based on the viewport - var size = void 0; - if (unit === 'vh') { - size = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); - } else { - size = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); - } - return size / 100 * value; - } else { - // if is an explicit pixel unit, we get rid of the unit and keep the value - // if is an implicit unit, it's px, and we return just the value - return value; - } - } - - /** - * Parse an `offset` string to extrapolate `x` and `y` numeric offsets. - * @function - * @memberof {modifiers~offset} - * @private - * @argument {String} offset - * @argument {Object} popperOffsets - * @argument {Object} referenceOffsets - * @argument {String} basePlacement - * @returns {Array} a two cells array with x and y offsets in numbers - */ - function parseOffset(offset, popperOffsets, referenceOffsets, basePlacement) { - var offsets = [0, 0]; - - // Use height if placement is left or right and index is 0 otherwise use width - // in this way the first offset will use an axis and the second one - // will use the other one - var useHeight = ['right', 'left'].indexOf(basePlacement) !== -1; - - // Split the offset string to obtain a list of values and operands - // The regex addresses values with the plus or minus sign in front (+10, -20, etc) - var fragments = offset.split(/(\+|\-)/).map(function (frag) { - return frag.trim(); - }); - - // Detect if the offset string contains a pair of values or a single one - // they could be separated by comma or space - var divider = fragments.indexOf(find(fragments, function (frag) { - return frag.search(/,|\s/) !== -1; - })); - - if (fragments[divider] && fragments[divider].indexOf(',') === -1) { - console.warn('Offsets separated by white space(s) are deprecated, use a comma (,) instead.'); - } - - // If divider is found, we divide the list of values and operands to divide - // them by ofset X and Y. - var splitRegex = /\s*,\s*|\s+/; - var ops = divider !== -1 ? [fragments.slice(0, divider).concat([fragments[divider].split(splitRegex)[0]]), [fragments[divider].split(splitRegex)[1]].concat(fragments.slice(divider + 1))] : [fragments]; - - // Convert the values with units to absolute pixels to allow our computations - ops = ops.map(function (op, index) { - // Most of the units rely on the orientation of the popper - var measurement = (index === 1 ? !useHeight : useHeight) ? 'height' : 'width'; - var mergeWithPrevious = false; - return op - // This aggregates any `+` or `-` sign that aren't considered operators - // e.g.: 10 + +5 => [10, +, +5] - .reduce(function (a, b) { - if (a[a.length - 1] === '' && ['+', '-'].indexOf(b) !== -1) { - a[a.length - 1] = b; - mergeWithPrevious = true; - return a; - } else if (mergeWithPrevious) { - a[a.length - 1] += b; - mergeWithPrevious = false; - return a; - } else { - return a.concat(b); - } - }, []) - // Here we convert the string values into number values (in px) - .map(function (str) { - return toValue(str, measurement, popperOffsets, referenceOffsets); - }); - }); - - // Loop trough the offsets arrays and execute the operations - ops.forEach(function (op, index) { - op.forEach(function (frag, index2) { - if (isNumeric(frag)) { - offsets[index] += frag * (op[index2 - 1] === '-' ? -1 : 1); - } - }); - }); - return offsets; - } - - /** - * @function - * @memberof Modifiers - * @argument {Object} data - The data object generated by update method - * @argument {Object} options - Modifiers configuration and options - * @argument {Number|String} options.offset=0 - * The offset value as described in the modifier description - * @returns {Object} The data object, properly modified - */ - function offset(data, _ref) { - var offset = _ref.offset; - var placement = data.placement, - _data$offsets = data.offsets, - popper = _data$offsets.popper, - reference = _data$offsets.reference; - - var basePlacement = placement.split('-')[0]; - - var offsets = void 0; - if (isNumeric(+offset)) { - offsets = [+offset, 0]; - } else { - offsets = parseOffset(offset, popper, reference, basePlacement); - } - - if (basePlacement === 'left') { - popper.top += offsets[0]; - popper.left -= offsets[1]; - } else if (basePlacement === 'right') { - popper.top += offsets[0]; - popper.left += offsets[1]; - } else if (basePlacement === 'top') { - popper.left += offsets[0]; - popper.top -= offsets[1]; - } else if (basePlacement === 'bottom') { - popper.left += offsets[0]; - popper.top += offsets[1]; - } - - data.popper = popper; - return data; - } - - /** - * @function - * @memberof Modifiers - * @argument {Object} data - The data object generated by `update` method - * @argument {Object} options - Modifiers configuration and options - * @returns {Object} The data object, properly modified - */ - function preventOverflow(data, options) { - var boundariesElement = options.boundariesElement || getOffsetParent(data.instance.popper); - - // If offsetParent is the reference element, we really want to - // go one step up and use the next offsetParent as reference to - // avoid to make this modifier completely useless and look like broken - if (data.instance.reference === boundariesElement) { - boundariesElement = getOffsetParent(boundariesElement); - } - - // NOTE: DOM access here - // resets the popper's position so that the document size can be calculated excluding - // the size of the popper element itself - var transformProp = getSupportedPropertyName('transform'); - var popperStyles = data.instance.popper.style; // assignment to help minification - var top = popperStyles.top, - left = popperStyles.left, - transform = popperStyles[transformProp]; - - popperStyles.top = ''; - popperStyles.left = ''; - popperStyles[transformProp] = ''; - - var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, boundariesElement, data.positionFixed); - - // NOTE: DOM access here - // restores the original style properties after the offsets have been computed - popperStyles.top = top; - popperStyles.left = left; - popperStyles[transformProp] = transform; - - options.boundaries = boundaries; - - var order = options.priority; - var popper = data.offsets.popper; - - var check = { - primary: function primary(placement) { - var value = popper[placement]; - if (popper[placement] < boundaries[placement] && !options.escapeWithReference) { - value = Math.max(popper[placement], boundaries[placement]); - } - return defineProperty({}, placement, value); - }, - secondary: function secondary(placement) { - var mainSide = placement === 'right' ? 'left' : 'top'; - var value = popper[mainSide]; - if (popper[placement] > boundaries[placement] && !options.escapeWithReference) { - value = Math.min(popper[mainSide], boundaries[placement] - (placement === 'right' ? popper.width : popper.height)); - } - return defineProperty({}, mainSide, value); - } - }; - - order.forEach(function (placement) { - var side = ['left', 'top'].indexOf(placement) !== -1 ? 'primary' : 'secondary'; - popper = _extends({}, popper, check[side](placement)); - }); - - data.offsets.popper = popper; - - return data; - } - - /** - * @function - * @memberof Modifiers - * @argument {Object} data - The data object generated by `update` method - * @argument {Object} options - Modifiers configuration and options - * @returns {Object} The data object, properly modified - */ - function shift(data) { - var placement = data.placement; - var basePlacement = placement.split('-')[0]; - var shiftvariation = placement.split('-')[1]; - - // if shift shiftvariation is specified, run the modifier - if (shiftvariation) { - var _data$offsets = data.offsets, - reference = _data$offsets.reference, - popper = _data$offsets.popper; - - var isVertical = ['bottom', 'top'].indexOf(basePlacement) !== -1; - var side = isVertical ? 'left' : 'top'; - var measurement = isVertical ? 'width' : 'height'; - - var shiftOffsets = { - start: defineProperty({}, side, reference[side]), - end: defineProperty({}, side, reference[side] + reference[measurement] - popper[measurement]) - }; - - data.offsets.popper = _extends({}, popper, shiftOffsets[shiftvariation]); - } - - return data; - } - - /** - * @function - * @memberof Modifiers - * @argument {Object} data - The data object generated by update method - * @argument {Object} options - Modifiers configuration and options - * @returns {Object} The data object, properly modified - */ - function hide(data) { - if (!isModifierRequired(data.instance.modifiers, 'hide', 'preventOverflow')) { - return data; - } - - var refRect = data.offsets.reference; - var bound = find(data.instance.modifiers, function (modifier) { - return modifier.name === 'preventOverflow'; - }).boundaries; - - if (refRect.bottom < bound.top || refRect.left > bound.right || refRect.top > bound.bottom || refRect.right < bound.left) { - // Avoid unnecessary DOM access if visibility hasn't changed - if (data.hide === true) { - return data; - } - - data.hide = true; - data.attributes['x-out-of-boundaries'] = ''; - } else { - // Avoid unnecessary DOM access if visibility hasn't changed - if (data.hide === false) { - return data; - } - - data.hide = false; - data.attributes['x-out-of-boundaries'] = false; - } - - return data; - } - - /** - * @function - * @memberof Modifiers - * @argument {Object} data - The data object generated by `update` method - * @argument {Object} options - Modifiers configuration and options - * @returns {Object} The data object, properly modified - */ - function inner(data) { - var placement = data.placement; - var basePlacement = placement.split('-')[0]; - var _data$offsets = data.offsets, - popper = _data$offsets.popper, - reference = _data$offsets.reference; - - var isHoriz = ['left', 'right'].indexOf(basePlacement) !== -1; - - var subtractLength = ['top', 'left'].indexOf(basePlacement) === -1; - - popper[isHoriz ? 'left' : 'top'] = reference[basePlacement] - (subtractLength ? popper[isHoriz ? 'width' : 'height'] : 0); - - data.placement = getOppositePlacement(placement); - data.offsets.popper = getClientRect(popper); - - return data; - } - - /** - * Modifier function, each modifier can have a function of this type assigned - * to its `fn` property.
- * These functions will be called on each update, this means that you must - * make sure they are performant enough to avoid performance bottlenecks. - * - * @function ModifierFn - * @argument {dataObject} data - The data object generated by `update` method - * @argument {Object} options - Modifiers configuration and options - * @returns {dataObject} The data object, properly modified - */ - - /** - * Modifiers are plugins used to alter the behavior of your poppers.
- * Popper.js uses a set of 9 modifiers to provide all the basic functionalities - * needed by the library. - * - * Usually you don't want to override the `order`, `fn` and `onLoad` props. - * All the other properties are configurations that could be tweaked. - * @namespace modifiers - */ - var modifiers = { - /** - * Modifier used to shift the popper on the start or end of its reference - * element.
- * It will read the variation of the `placement` property.
- * It can be one either `-end` or `-start`. - * @memberof modifiers - * @inner - */ - shift: { - /** @prop {number} order=100 - Index used to define the order of execution */ - order: 100, - /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ - enabled: true, - /** @prop {ModifierFn} */ - fn: shift - }, - - /** - * The `offset` modifier can shift your popper on both its axis. - * - * It accepts the following units: - * - `px` or unitless, interpreted as pixels - * - `%` or `%r`, percentage relative to the length of the reference element - * - `%p`, percentage relative to the length of the popper element - * - `vw`, CSS viewport width unit - * - `vh`, CSS viewport height unit - * - * For length is intended the main axis relative to the placement of the popper.
- * This means that if the placement is `top` or `bottom`, the length will be the - * `width`. In case of `left` or `right`, it will be the height. - * - * You can provide a single value (as `Number` or `String`), or a pair of values - * as `String` divided by a comma or one (or more) white spaces.
- * The latter is a deprecated method because it leads to confusion and will be - * removed in v2.
- * Additionally, it accepts additions and subtractions between different units. - * Note that multiplications and divisions aren't supported. - * - * Valid examples are: - * ``` - * 10 - * '10%' - * '10, 10' - * '10%, 10' - * '10 + 10%' - * '10 - 5vh + 3%' - * '-10px + 5vh, 5px - 6%' - * ``` - * > **NB**: If you desire to apply offsets to your poppers in a way that may make them overlap - * > with their reference element, unfortunately, you will have to disable the `flip` modifier. - * > More on this [reading this issue](https://github.com/FezVrasta/popper.js/issues/373) - * - * @memberof modifiers - * @inner - */ - offset: { - /** @prop {number} order=200 - Index used to define the order of execution */ - order: 200, - /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ - enabled: true, - /** @prop {ModifierFn} */ - fn: offset, - /** @prop {Number|String} offset=0 - * The offset value as described in the modifier description - */ - offset: 0 - }, - - /** - * Modifier used to prevent the popper from being positioned outside the boundary. - * - * An scenario exists where the reference itself is not within the boundaries.
- * We can say it has "escaped the boundaries" — or just "escaped".
- * In this case we need to decide whether the popper should either: - * - * - detach from the reference and remain "trapped" in the boundaries, or - * - if it should ignore the boundary and "escape with its reference" - * - * When `escapeWithReference` is set to`true` and reference is completely - * outside its boundaries, the popper will overflow (or completely leave) - * the boundaries in order to remain attached to the edge of the reference. - * - * @memberof modifiers - * @inner - */ - preventOverflow: { - /** @prop {number} order=300 - Index used to define the order of execution */ - order: 300, - /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ - enabled: true, - /** @prop {ModifierFn} */ - fn: preventOverflow, - /** - * @prop {Array} [priority=['left','right','top','bottom']] - * Popper will try to prevent overflow following these priorities by default, - * then, it could overflow on the left and on top of the `boundariesElement` - */ - priority: ['left', 'right', 'top', 'bottom'], - /** - * @prop {number} padding=5 - * Amount of pixel used to define a minimum distance between the boundaries - * and the popper this makes sure the popper has always a little padding - * between the edges of its container - */ - padding: 5, - /** - * @prop {String|HTMLElement} boundariesElement='scrollParent' - * Boundaries used by the modifier, can be `scrollParent`, `window`, - * `viewport` or any DOM element. - */ - boundariesElement: 'scrollParent' - }, - - /** - * Modifier used to make sure the reference and its popper stay near eachothers - * without leaving any gap between the two. Expecially useful when the arrow is - * enabled and you want to assure it to point to its reference element. - * It cares only about the first axis, you can still have poppers with margin - * between the popper and its reference element. - * @memberof modifiers - * @inner - */ - keepTogether: { - /** @prop {number} order=400 - Index used to define the order of execution */ - order: 400, - /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ - enabled: true, - /** @prop {ModifierFn} */ - fn: keepTogether - }, - - /** - * This modifier is used to move the `arrowElement` of the popper to make - * sure it is positioned between the reference element and its popper element. - * It will read the outer size of the `arrowElement` node to detect how many - * pixels of conjuction are needed. - * - * It has no effect if no `arrowElement` is provided. - * @memberof modifiers - * @inner - */ - arrow: { - /** @prop {number} order=500 - Index used to define the order of execution */ - order: 500, - /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ - enabled: true, - /** @prop {ModifierFn} */ - fn: arrow, - /** @prop {String|HTMLElement} element='[x-arrow]' - Selector or node used as arrow */ - element: '[x-arrow]' - }, - - /** - * Modifier used to flip the popper's placement when it starts to overlap its - * reference element. - * - * Requires the `preventOverflow` modifier before it in order to work. - * - * **NOTE:** this modifier will interrupt the current update cycle and will - * restart it if it detects the need to flip the placement. - * @memberof modifiers - * @inner - */ - flip: { - /** @prop {number} order=600 - Index used to define the order of execution */ - order: 600, - /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ - enabled: true, - /** @prop {ModifierFn} */ - fn: flip, - /** - * @prop {String|Array} behavior='flip' - * The behavior used to change the popper's placement. It can be one of - * `flip`, `clockwise`, `counterclockwise` or an array with a list of valid - * placements (with optional variations). - */ - behavior: 'flip', - /** - * @prop {number} padding=5 - * The popper will flip if it hits the edges of the `boundariesElement` - */ - padding: 5, - /** - * @prop {String|HTMLElement} boundariesElement='viewport' - * The element which will define the boundaries of the popper position, - * the popper will never be placed outside of the defined boundaries - * (except if keepTogether is enabled) - */ - boundariesElement: 'viewport' - }, - - /** - * Modifier used to make the popper flow toward the inner of the reference element. - * By default, when this modifier is disabled, the popper will be placed outside - * the reference element. - * @memberof modifiers - * @inner - */ - inner: { - /** @prop {number} order=700 - Index used to define the order of execution */ - order: 700, - /** @prop {Boolean} enabled=false - Whether the modifier is enabled or not */ - enabled: false, - /** @prop {ModifierFn} */ - fn: inner - }, - - /** - * Modifier used to hide the popper when its reference element is outside of the - * popper boundaries. It will set a `x-out-of-boundaries` attribute which can - * be used to hide with a CSS selector the popper when its reference is - * out of boundaries. - * - * Requires the `preventOverflow` modifier before it in order to work. - * @memberof modifiers - * @inner - */ - hide: { - /** @prop {number} order=800 - Index used to define the order of execution */ - order: 800, - /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ - enabled: true, - /** @prop {ModifierFn} */ - fn: hide - }, - - /** - * Computes the style that will be applied to the popper element to gets - * properly positioned. - * - * Note that this modifier will not touch the DOM, it just prepares the styles - * so that `applyStyle` modifier can apply it. This separation is useful - * in case you need to replace `applyStyle` with a custom implementation. - * - * This modifier has `850` as `order` value to maintain backward compatibility - * with previous versions of Popper.js. Expect the modifiers ordering method - * to change in future major versions of the library. - * - * @memberof modifiers - * @inner - */ - computeStyle: { - /** @prop {number} order=850 - Index used to define the order of execution */ - order: 850, - /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ - enabled: true, - /** @prop {ModifierFn} */ - fn: computeStyle, - /** - * @prop {Boolean} gpuAcceleration=true - * If true, it uses the CSS 3d transformation to position the popper. - * Otherwise, it will use the `top` and `left` properties. - */ - gpuAcceleration: true, - /** - * @prop {string} [x='bottom'] - * Where to anchor the X axis (`bottom` or `top`). AKA X offset origin. - * Change this if your popper should grow in a direction different from `bottom` - */ - x: 'bottom', - /** - * @prop {string} [x='left'] - * Where to anchor the Y axis (`left` or `right`). AKA Y offset origin. - * Change this if your popper should grow in a direction different from `right` - */ - y: 'right' - }, - - /** - * Applies the computed styles to the popper element. - * - * All the DOM manipulations are limited to this modifier. This is useful in case - * you want to integrate Popper.js inside a framework or view library and you - * want to delegate all the DOM manipulations to it. - * - * Note that if you disable this modifier, you must make sure the popper element - * has its position set to `absolute` before Popper.js can do its work! - * - * Just disable this modifier and define you own to achieve the desired effect. - * - * @memberof modifiers - * @inner - */ - applyStyle: { - /** @prop {number} order=900 - Index used to define the order of execution */ - order: 900, - /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ - enabled: true, - /** @prop {ModifierFn} */ - fn: applyStyle, - /** @prop {Function} */ - onLoad: applyStyleOnLoad, - /** - * @deprecated since version 1.10.0, the property moved to `computeStyle` modifier - * @prop {Boolean} gpuAcceleration=true - * If true, it uses the CSS 3d transformation to position the popper. - * Otherwise, it will use the `top` and `left` properties. - */ - gpuAcceleration: undefined - } - }; - - /** - * The `dataObject` is an object containing all the informations used by Popper.js - * this object get passed to modifiers and to the `onCreate` and `onUpdate` callbacks. - * @name dataObject - * @property {Object} data.instance The Popper.js instance - * @property {String} data.placement Placement applied to popper - * @property {String} data.originalPlacement Placement originally defined on init - * @property {Boolean} data.flipped True if popper has been flipped by flip modifier - * @property {Boolean} data.hide True if the reference element is out of boundaries, useful to know when to hide the popper. - * @property {HTMLElement} data.arrowElement Node used as arrow by arrow modifier - * @property {Object} data.styles Any CSS property defined here will be applied to the popper, it expects the JavaScript nomenclature (eg. `marginBottom`) - * @property {Object} data.arrowStyles Any CSS property defined here will be applied to the popper arrow, it expects the JavaScript nomenclature (eg. `marginBottom`) - * @property {Object} data.boundaries Offsets of the popper boundaries - * @property {Object} data.offsets The measurements of popper, reference and arrow elements. - * @property {Object} data.offsets.popper `top`, `left`, `width`, `height` values - * @property {Object} data.offsets.reference `top`, `left`, `width`, `height` values - * @property {Object} data.offsets.arrow] `top` and `left` offsets, only one of them will be different from 0 - */ - - /** - * Default options provided to Popper.js constructor.
- * These can be overriden using the `options` argument of Popper.js.
- * To override an option, simply pass as 3rd argument an object with the same - * structure of this object, example: - * ``` - * new Popper(ref, pop, { - * modifiers: { - * preventOverflow: { enabled: false } - * } - * }) - * ``` - * @type {Object} - * @static - * @memberof Popper - */ - var Defaults = { - /** - * Popper's placement - * @prop {Popper.placements} placement='bottom' - */ - placement: 'bottom', - - /** - * Set this to true if you want popper to position it self in 'fixed' mode - * @prop {Boolean} positionFixed=false - */ - positionFixed: false, - - /** - * Whether events (resize, scroll) are initially enabled - * @prop {Boolean} eventsEnabled=true - */ - eventsEnabled: true, - - /** - * Set to true if you want to automatically remove the popper when - * you call the `destroy` method. - * @prop {Boolean} removeOnDestroy=false - */ - removeOnDestroy: false, - - /** - * Callback called when the popper is created.
- * By default, is set to no-op.
- * Access Popper.js instance with `data.instance`. - * @prop {onCreate} - */ - onCreate: function onCreate() {}, - - /** - * Callback called when the popper is updated, this callback is not called - * on the initialization/creation of the popper, but only on subsequent - * updates.
- * By default, is set to no-op.
- * Access Popper.js instance with `data.instance`. - * @prop {onUpdate} - */ - onUpdate: function onUpdate() {}, - - /** - * List of modifiers used to modify the offsets before they are applied to the popper. - * They provide most of the functionalities of Popper.js - * @prop {modifiers} - */ - modifiers: modifiers - }; - - /** - * @callback onCreate - * @param {dataObject} data - */ - - /** - * @callback onUpdate - * @param {dataObject} data - */ - - // Utils - // Methods - var Popper = function () { - /** - * Create a new Popper.js instance - * @class Popper - * @param {HTMLElement|referenceObject} reference - The reference element used to position the popper - * @param {HTMLElement} popper - The HTML element used as popper. - * @param {Object} options - Your custom options to override the ones defined in [Defaults](#defaults) - * @return {Object} instance - The generated Popper.js instance - */ - function Popper(reference, popper) { - var _this = this; - - var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; - classCallCheck(this, Popper); - - this.scheduleUpdate = function () { - return requestAnimationFrame(_this.update); - }; - - // make update() debounced, so that it only runs at most once-per-tick - this.update = debounce(this.update.bind(this)); - - // with {} we create a new object with the options inside it - this.options = _extends({}, Popper.Defaults, options); - - // init state - this.state = { - isDestroyed: false, - isCreated: false, - scrollParents: [] - }; - - // get reference and popper elements (allow jQuery wrappers) - this.reference = reference && reference.jquery ? reference[0] : reference; - this.popper = popper && popper.jquery ? popper[0] : popper; - - // Deep merge modifiers options - this.options.modifiers = {}; - Object.keys(_extends({}, Popper.Defaults.modifiers, options.modifiers)).forEach(function (name) { - _this.options.modifiers[name] = _extends({}, Popper.Defaults.modifiers[name] || {}, options.modifiers ? options.modifiers[name] : {}); - }); - - // Refactoring modifiers' list (Object => Array) - this.modifiers = Object.keys(this.options.modifiers).map(function (name) { - return _extends({ - name: name - }, _this.options.modifiers[name]); - }) - // sort the modifiers by order - .sort(function (a, b) { - return a.order - b.order; - }); - - // modifiers have the ability to execute arbitrary code when Popper.js get inited - // such code is executed in the same order of its modifier - // they could add new properties to their options configuration - // BE AWARE: don't add options to `options.modifiers.name` but to `modifierOptions`! - this.modifiers.forEach(function (modifierOptions) { - if (modifierOptions.enabled && isFunction(modifierOptions.onLoad)) { - modifierOptions.onLoad(_this.reference, _this.popper, _this.options, modifierOptions, _this.state); - } - }); - - // fire the first update to position the popper in the right place - this.update(); - - var eventsEnabled = this.options.eventsEnabled; - if (eventsEnabled) { - // setup event listeners, they will take care of update the position in specific situations - this.enableEventListeners(); - } - - this.state.eventsEnabled = eventsEnabled; - } - - // We can't use class properties because they don't get listed in the - // class prototype and break stuff like Sinon stubs - - - createClass(Popper, [{ - key: 'update', - value: function update$$1() { - return update.call(this); - } - }, { - key: 'destroy', - value: function destroy$$1() { - return destroy.call(this); - } - }, { - key: 'enableEventListeners', - value: function enableEventListeners$$1() { - return enableEventListeners.call(this); - } - }, { - key: 'disableEventListeners', - value: function disableEventListeners$$1() { - return disableEventListeners.call(this); - } - - /** - * Schedule an update, it will run on the next UI update available - * @method scheduleUpdate - * @memberof Popper - */ - - - /** - * Collection of utilities useful when writing custom modifiers. - * Starting from version 1.7, this method is available only if you - * include `popper-utils.js` before `popper.js`. - * - * **DEPRECATION**: This way to access PopperUtils is deprecated - * and will be removed in v2! Use the PopperUtils module directly instead. - * Due to the high instability of the methods contained in Utils, we can't - * guarantee them to follow semver. Use them at your own risk! - * @static - * @private - * @type {Object} - * @deprecated since version 1.8 - * @member Utils - * @memberof Popper - */ - - }]); - return Popper; - }(); - - /** - * The `referenceObject` is an object that provides an interface compatible with Popper.js - * and lets you use it as replacement of a real DOM node.
- * You can use this method to position a popper relatively to a set of coordinates - * in case you don't have a DOM node to use as reference. - * - * ``` - * new Popper(referenceObject, popperNode); - * ``` - * - * NB: This feature isn't supported in Internet Explorer 10 - * @name referenceObject - * @property {Function} data.getBoundingClientRect - * A function that returns a set of coordinates compatible with the native `getBoundingClientRect` method. - * @property {number} data.clientWidth - * An ES6 getter that will return the width of the virtual reference element. - * @property {number} data.clientHeight - * An ES6 getter that will return the height of the virtual reference element. - */ - - - Popper.Utils = (typeof window !== 'undefined' ? window : global).PopperUtils; - Popper.placements = placements; - Popper.Defaults = Defaults; - - /** - * -------------------------------------------------------------------------- - * Bootstrap (v4.1.1): dropdown.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * -------------------------------------------------------------------------- - */ - - var Dropdown = function ($$$1) { - /** - * ------------------------------------------------------------------------ - * Constants - * ------------------------------------------------------------------------ - */ - var NAME = 'dropdown'; - var VERSION = '4.1.1'; - var DATA_KEY = 'bs.dropdown'; - var EVENT_KEY = "." + DATA_KEY; - var DATA_API_KEY = '.data-api'; - var JQUERY_NO_CONFLICT = $$$1.fn[NAME]; - var ESCAPE_KEYCODE = 27; // KeyboardEvent.which value for Escape (Esc) key - - var SPACE_KEYCODE = 32; // KeyboardEvent.which value for space key - - var TAB_KEYCODE = 9; // KeyboardEvent.which value for tab key - - var ARROW_UP_KEYCODE = 38; // KeyboardEvent.which value for up arrow key - - var ARROW_DOWN_KEYCODE = 40; // KeyboardEvent.which value for down arrow key - - var RIGHT_MOUSE_BUTTON_WHICH = 3; // MouseEvent.which value for the right button (assuming a right-handed mouse) - - var REGEXP_KEYDOWN = new RegExp(ARROW_UP_KEYCODE + "|" + ARROW_DOWN_KEYCODE + "|" + ESCAPE_KEYCODE); - var Event = { - HIDE: "hide" + EVENT_KEY, - HIDDEN: "hidden" + EVENT_KEY, - SHOW: "show" + EVENT_KEY, - SHOWN: "shown" + EVENT_KEY, - CLICK: "click" + EVENT_KEY, - CLICK_DATA_API: "click" + EVENT_KEY + DATA_API_KEY, - KEYDOWN_DATA_API: "keydown" + EVENT_KEY + DATA_API_KEY, - KEYUP_DATA_API: "keyup" + EVENT_KEY + DATA_API_KEY - }; - var ClassName = { - DISABLED: 'disabled', - SHOW: 'show', - DROPUP: 'dropup', - DROPRIGHT: 'dropright', - DROPLEFT: 'dropleft', - MENURIGHT: 'dropdown-menu-right', - MENULEFT: 'dropdown-menu-left', - POSITION_STATIC: 'position-static' - }; - var Selector = { - DATA_TOGGLE: '[data-toggle="dropdown"]', - FORM_CHILD: '.dropdown form', - MENU: '.dropdown-menu', - NAVBAR_NAV: '.navbar-nav', - VISIBLE_ITEMS: '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)' - }; - var AttachmentMap = { - TOP: 'top-start', - TOPEND: 'top-end', - BOTTOM: 'bottom-start', - BOTTOMEND: 'bottom-end', - RIGHT: 'right-start', - RIGHTEND: 'right-end', - LEFT: 'left-start', - LEFTEND: 'left-end' - }; - var Default = { - offset: 0, - flip: true, - boundary: 'scrollParent', - reference: 'toggle', - display: 'dynamic' - }; - var DefaultType = { - offset: '(number|string|function)', - flip: 'boolean', - boundary: '(string|element)', - reference: '(string|element)', - display: 'string' - /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ - */ - - }; - - var Dropdown = - /*#__PURE__*/ - function () { - function Dropdown(element, config) { - this._element = element; - this._popper = null; - this._config = this._getConfig(config); - this._menu = this._getMenuElement(); - this._inNavbar = this._detectNavbar(); - - this._addEventListeners(); - } // Getters - - - var _proto = Dropdown.prototype; - - // Public - _proto.toggle = function toggle() { - if (this._element.disabled || $$$1(this._element).hasClass(ClassName.DISABLED)) { - return; - } - - var parent = Dropdown._getParentFromElement(this._element); - - var isActive = $$$1(this._menu).hasClass(ClassName.SHOW); - - Dropdown._clearMenus(); - - if (isActive) { - return; - } - - var relatedTarget = { - relatedTarget: this._element - }; - var showEvent = $$$1.Event(Event.SHOW, relatedTarget); - $$$1(parent).trigger(showEvent); - - if (showEvent.isDefaultPrevented()) { - return; - } // Disable totally Popper.js for Dropdown in Navbar - - - if (!this._inNavbar) { - /** - * Check for Popper dependency - * Popper - https://popper.js.org - */ - if (typeof Popper === 'undefined') { - throw new TypeError('Bootstrap dropdown require Popper.js (https://popper.js.org)'); - } - - var referenceElement = this._element; - - if (this._config.reference === 'parent') { - referenceElement = parent; - } else if (Util.isElement(this._config.reference)) { - referenceElement = this._config.reference; // Check if it's jQuery element - - if (typeof this._config.reference.jquery !== 'undefined') { - referenceElement = this._config.reference[0]; - } - } // If boundary is not `scrollParent`, then set position to `static` - // to allow the menu to "escape" the scroll parent's boundaries - // https://github.com/twbs/bootstrap/issues/24251 - - - if (this._config.boundary !== 'scrollParent') { - $$$1(parent).addClass(ClassName.POSITION_STATIC); - } - - this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig()); - } // If this is a touch-enabled device we add extra - // empty mouseover listeners to the body's immediate children; - // only needed because of broken event delegation on iOS - // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html - - - if ('ontouchstart' in document.documentElement && $$$1(parent).closest(Selector.NAVBAR_NAV).length === 0) { - $$$1(document.body).children().on('mouseover', null, $$$1.noop); - } - - this._element.focus(); - - this._element.setAttribute('aria-expanded', true); - - $$$1(this._menu).toggleClass(ClassName.SHOW); - $$$1(parent).toggleClass(ClassName.SHOW).trigger($$$1.Event(Event.SHOWN, relatedTarget)); - }; - - _proto.dispose = function dispose() { - $$$1.removeData(this._element, DATA_KEY); - $$$1(this._element).off(EVENT_KEY); - this._element = null; - this._menu = null; - - if (this._popper !== null) { - this._popper.destroy(); - - this._popper = null; - } - }; - - _proto.update = function update() { - this._inNavbar = this._detectNavbar(); - - if (this._popper !== null) { - this._popper.scheduleUpdate(); - } - }; // Private - - - _proto._addEventListeners = function _addEventListeners() { - var _this = this; - - $$$1(this._element).on(Event.CLICK, function (event) { - event.preventDefault(); - event.stopPropagation(); - - _this.toggle(); - }); - }; - - _proto._getConfig = function _getConfig(config) { - config = _objectSpread({}, this.constructor.Default, $$$1(this._element).data(), config); - Util.typeCheckConfig(NAME, config, this.constructor.DefaultType); - return config; - }; - - _proto._getMenuElement = function _getMenuElement() { - if (!this._menu) { - var parent = Dropdown._getParentFromElement(this._element); - - this._menu = $$$1(parent).find(Selector.MENU)[0]; - } - - return this._menu; - }; - - _proto._getPlacement = function _getPlacement() { - var $parentDropdown = $$$1(this._element).parent(); - var placement = AttachmentMap.BOTTOM; // Handle dropup - - if ($parentDropdown.hasClass(ClassName.DROPUP)) { - placement = AttachmentMap.TOP; - - if ($$$1(this._menu).hasClass(ClassName.MENURIGHT)) { - placement = AttachmentMap.TOPEND; - } - } else if ($parentDropdown.hasClass(ClassName.DROPRIGHT)) { - placement = AttachmentMap.RIGHT; - } else if ($parentDropdown.hasClass(ClassName.DROPLEFT)) { - placement = AttachmentMap.LEFT; - } else if ($$$1(this._menu).hasClass(ClassName.MENURIGHT)) { - placement = AttachmentMap.BOTTOMEND; - } - - return placement; - }; - - _proto._detectNavbar = function _detectNavbar() { - return $$$1(this._element).closest('.navbar').length > 0; - }; - - _proto._getPopperConfig = function _getPopperConfig() { - var _this2 = this; - - var offsetConf = {}; - - if (typeof this._config.offset === 'function') { - offsetConf.fn = function (data) { - data.offsets = _objectSpread({}, data.offsets, _this2._config.offset(data.offsets) || {}); - return data; - }; - } else { - offsetConf.offset = this._config.offset; - } - - var popperConfig = { - placement: this._getPlacement(), - modifiers: { - offset: offsetConf, - flip: { - enabled: this._config.flip - }, - preventOverflow: { - boundariesElement: this._config.boundary - } - } // Disable Popper.js if we have a static display - - }; - - if (this._config.display === 'static') { - popperConfig.modifiers.applyStyle = { - enabled: false - }; - } - - return popperConfig; - }; // Static - - - Dropdown._jQueryInterface = function _jQueryInterface(config) { - return this.each(function () { - var data = $$$1(this).data(DATA_KEY); - - var _config = typeof config === 'object' ? config : null; - - if (!data) { - data = new Dropdown(this, _config); - $$$1(this).data(DATA_KEY, data); - } - - if (typeof config === 'string') { - if (typeof data[config] === 'undefined') { - throw new TypeError("No method named \"" + config + "\""); - } - - data[config](); - } - }); - }; - - Dropdown._clearMenus = function _clearMenus(event) { - if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH || event.type === 'keyup' && event.which !== TAB_KEYCODE)) { - return; - } - - var toggles = $$$1.makeArray($$$1(Selector.DATA_TOGGLE)); - - for (var i = 0; i < toggles.length; i++) { - var parent = Dropdown._getParentFromElement(toggles[i]); - - var context = $$$1(toggles[i]).data(DATA_KEY); - var relatedTarget = { - relatedTarget: toggles[i] - }; - - if (!context) { - continue; - } - - var dropdownMenu = context._menu; - - if (!$$$1(parent).hasClass(ClassName.SHOW)) { - continue; - } - - if (event && (event.type === 'click' && /input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) && $$$1.contains(parent, event.target)) { - continue; - } - - var hideEvent = $$$1.Event(Event.HIDE, relatedTarget); - $$$1(parent).trigger(hideEvent); - - if (hideEvent.isDefaultPrevented()) { - continue; - } // If this is a touch-enabled device we remove the extra - // empty mouseover listeners we added for iOS support - - - if ('ontouchstart' in document.documentElement) { - $$$1(document.body).children().off('mouseover', null, $$$1.noop); - } - - toggles[i].setAttribute('aria-expanded', 'false'); - $$$1(dropdownMenu).removeClass(ClassName.SHOW); - $$$1(parent).removeClass(ClassName.SHOW).trigger($$$1.Event(Event.HIDDEN, relatedTarget)); - } - }; - - Dropdown._getParentFromElement = function _getParentFromElement(element) { - var parent; - var selector = Util.getSelectorFromElement(element); - - if (selector) { - parent = $$$1(selector)[0]; - } - - return parent || element.parentNode; - }; // eslint-disable-next-line complexity - - - Dropdown._dataApiKeydownHandler = function _dataApiKeydownHandler(event) { - // If not input/textarea: - // - And not a key in REGEXP_KEYDOWN => not a dropdown command - // If input/textarea: - // - If space key => not a dropdown command - // - If key is other than escape - // - If key is not up or down => not a dropdown command - // - If trigger inside the menu => not a dropdown command - if (/input|textarea/i.test(event.target.tagName) ? event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE && (event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE || $$$1(event.target).closest(Selector.MENU).length) : !REGEXP_KEYDOWN.test(event.which)) { - return; - } - - event.preventDefault(); - event.stopPropagation(); - - if (this.disabled || $$$1(this).hasClass(ClassName.DISABLED)) { - return; - } - - var parent = Dropdown._getParentFromElement(this); - - var isActive = $$$1(parent).hasClass(ClassName.SHOW); - - if (!isActive && (event.which !== ESCAPE_KEYCODE || event.which !== SPACE_KEYCODE) || isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) { - if (event.which === ESCAPE_KEYCODE) { - var toggle = $$$1(parent).find(Selector.DATA_TOGGLE)[0]; - $$$1(toggle).trigger('focus'); - } - - $$$1(this).trigger('click'); - return; - } - - var items = $$$1(parent).find(Selector.VISIBLE_ITEMS).get(); - - if (items.length === 0) { - return; - } - - var index = items.indexOf(event.target); - - if (event.which === ARROW_UP_KEYCODE && index > 0) { - // Up - index--; - } - - if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) { - // Down - index++; - } - - if (index < 0) { - index = 0; - } - - items[index].focus(); - }; - - _createClass(Dropdown, null, [{ - key: "VERSION", - get: function get() { - return VERSION; - } - }, { - key: "Default", - get: function get() { - return Default; - } - }, { - key: "DefaultType", - get: function get() { - return DefaultType; - } - }]); - - return Dropdown; - }(); - /** - * ------------------------------------------------------------------------ - * Data Api implementation - * ------------------------------------------------------------------------ - */ - - - $$$1(document).on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler).on(Event.KEYDOWN_DATA_API, Selector.MENU, Dropdown._dataApiKeydownHandler).on(Event.CLICK_DATA_API + " " + Event.KEYUP_DATA_API, Dropdown._clearMenus).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { - event.preventDefault(); - event.stopPropagation(); - - Dropdown._jQueryInterface.call($$$1(this), 'toggle'); - }).on(Event.CLICK_DATA_API, Selector.FORM_CHILD, function (e) { - e.stopPropagation(); - }); - /** - * ------------------------------------------------------------------------ - * jQuery - * ------------------------------------------------------------------------ - */ - - $$$1.fn[NAME] = Dropdown._jQueryInterface; - $$$1.fn[NAME].Constructor = Dropdown; - - $$$1.fn[NAME].noConflict = function () { - $$$1.fn[NAME] = JQUERY_NO_CONFLICT; - return Dropdown._jQueryInterface; - }; - - return Dropdown; - }($, Popper); - - /** - * -------------------------------------------------------------------------- - * Bootstrap (v4.1.1): modal.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * -------------------------------------------------------------------------- - */ - - var Modal = function ($$$1) { - /** - * ------------------------------------------------------------------------ - * Constants - * ------------------------------------------------------------------------ - */ - var NAME = 'modal'; - var VERSION = '4.1.1'; - var DATA_KEY = 'bs.modal'; - var EVENT_KEY = "." + DATA_KEY; - var DATA_API_KEY = '.data-api'; - var JQUERY_NO_CONFLICT = $$$1.fn[NAME]; - var ESCAPE_KEYCODE = 27; // KeyboardEvent.which value for Escape (Esc) key - - var Default = { - backdrop: true, - keyboard: true, - focus: true, - show: true - }; - var DefaultType = { - backdrop: '(boolean|string)', - keyboard: 'boolean', - focus: 'boolean', - show: 'boolean' - }; - var Event = { - HIDE: "hide" + EVENT_KEY, - HIDDEN: "hidden" + EVENT_KEY, - SHOW: "show" + EVENT_KEY, - SHOWN: "shown" + EVENT_KEY, - FOCUSIN: "focusin" + EVENT_KEY, - RESIZE: "resize" + EVENT_KEY, - CLICK_DISMISS: "click.dismiss" + EVENT_KEY, - KEYDOWN_DISMISS: "keydown.dismiss" + EVENT_KEY, - MOUSEUP_DISMISS: "mouseup.dismiss" + EVENT_KEY, - MOUSEDOWN_DISMISS: "mousedown.dismiss" + EVENT_KEY, - CLICK_DATA_API: "click" + EVENT_KEY + DATA_API_KEY - }; - var ClassName = { - SCROLLBAR_MEASURER: 'modal-scrollbar-measure', - BACKDROP: 'modal-backdrop', - OPEN: 'modal-open', - FADE: 'fade', - SHOW: 'show' - }; - var Selector = { - DIALOG: '.modal-dialog', - DATA_TOGGLE: '[data-toggle="modal"]', - DATA_DISMISS: '[data-dismiss="modal"]', - FIXED_CONTENT: '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top', - STICKY_CONTENT: '.sticky-top', - NAVBAR_TOGGLER: '.navbar-toggler' - /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ - */ - - }; - - var Modal = - /*#__PURE__*/ - function () { - function Modal(element, config) { - this._config = this._getConfig(config); - this._element = element; - this._dialog = $$$1(element).find(Selector.DIALOG)[0]; - this._backdrop = null; - this._isShown = false; - this._isBodyOverflowing = false; - this._ignoreBackdropClick = false; - this._scrollbarWidth = 0; - } // Getters - - - var _proto = Modal.prototype; - - // Public - _proto.toggle = function toggle(relatedTarget) { - return this._isShown ? this.hide() : this.show(relatedTarget); - }; - - _proto.show = function show(relatedTarget) { - var _this = this; - - if (this._isTransitioning || this._isShown) { - return; - } - - if ($$$1(this._element).hasClass(ClassName.FADE)) { - this._isTransitioning = true; - } - - var showEvent = $$$1.Event(Event.SHOW, { - relatedTarget: relatedTarget - }); - $$$1(this._element).trigger(showEvent); - - if (this._isShown || showEvent.isDefaultPrevented()) { - return; - } - - this._isShown = true; - - this._checkScrollbar(); - - this._setScrollbar(); - - this._adjustDialog(); - - $$$1(document.body).addClass(ClassName.OPEN); - - this._setEscapeEvent(); - - this._setResizeEvent(); - - $$$1(this._element).on(Event.CLICK_DISMISS, Selector.DATA_DISMISS, function (event) { - return _this.hide(event); - }); - $$$1(this._dialog).on(Event.MOUSEDOWN_DISMISS, function () { - $$$1(_this._element).one(Event.MOUSEUP_DISMISS, function (event) { - if ($$$1(event.target).is(_this._element)) { - _this._ignoreBackdropClick = true; - } - }); - }); - - this._showBackdrop(function () { - return _this._showElement(relatedTarget); - }); - }; - - _proto.hide = function hide(event) { - var _this2 = this; - - if (event) { - event.preventDefault(); - } - - if (this._isTransitioning || !this._isShown) { - return; - } - - var hideEvent = $$$1.Event(Event.HIDE); - $$$1(this._element).trigger(hideEvent); - - if (!this._isShown || hideEvent.isDefaultPrevented()) { - return; - } - - this._isShown = false; - var transition = $$$1(this._element).hasClass(ClassName.FADE); - - if (transition) { - this._isTransitioning = true; - } - - this._setEscapeEvent(); - - this._setResizeEvent(); - - $$$1(document).off(Event.FOCUSIN); - $$$1(this._element).removeClass(ClassName.SHOW); - $$$1(this._element).off(Event.CLICK_DISMISS); - $$$1(this._dialog).off(Event.MOUSEDOWN_DISMISS); - - if (transition) { - var transitionDuration = Util.getTransitionDurationFromElement(this._element); - $$$1(this._element).one(Util.TRANSITION_END, function (event) { - return _this2._hideModal(event); - }).emulateTransitionEnd(transitionDuration); - } else { - this._hideModal(); - } - }; - - _proto.dispose = function dispose() { - $$$1.removeData(this._element, DATA_KEY); - $$$1(window, document, this._element, this._backdrop).off(EVENT_KEY); - this._config = null; - this._element = null; - this._dialog = null; - this._backdrop = null; - this._isShown = null; - this._isBodyOverflowing = null; - this._ignoreBackdropClick = null; - this._scrollbarWidth = null; - }; - - _proto.handleUpdate = function handleUpdate() { - this._adjustDialog(); - }; // Private - - - _proto._getConfig = function _getConfig(config) { - config = _objectSpread({}, Default, config); - Util.typeCheckConfig(NAME, config, DefaultType); - return config; - }; - - _proto._showElement = function _showElement(relatedTarget) { - var _this3 = this; - - var transition = $$$1(this._element).hasClass(ClassName.FADE); - - if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) { - // Don't move modal's DOM position - document.body.appendChild(this._element); - } - - this._element.style.display = 'block'; - - this._element.removeAttribute('aria-hidden'); - - this._element.scrollTop = 0; - - if (transition) { - Util.reflow(this._element); - } - - $$$1(this._element).addClass(ClassName.SHOW); - - if (this._config.focus) { - this._enforceFocus(); - } - - var shownEvent = $$$1.Event(Event.SHOWN, { - relatedTarget: relatedTarget - }); - - var transitionComplete = function transitionComplete() { - if (_this3._config.focus) { - _this3._element.focus(); - } - - _this3._isTransitioning = false; - $$$1(_this3._element).trigger(shownEvent); - }; - - if (transition) { - var transitionDuration = Util.getTransitionDurationFromElement(this._element); - $$$1(this._dialog).one(Util.TRANSITION_END, transitionComplete).emulateTransitionEnd(transitionDuration); - } else { - transitionComplete(); - } - }; - - _proto._enforceFocus = function _enforceFocus() { - var _this4 = this; - - $$$1(document).off(Event.FOCUSIN) // Guard against infinite focus loop - .on(Event.FOCUSIN, function (event) { - if (document !== event.target && _this4._element !== event.target && $$$1(_this4._element).has(event.target).length === 0) { - _this4._element.focus(); - } - }); - }; - - _proto._setEscapeEvent = function _setEscapeEvent() { - var _this5 = this; - - if (this._isShown && this._config.keyboard) { - $$$1(this._element).on(Event.KEYDOWN_DISMISS, function (event) { - if (event.which === ESCAPE_KEYCODE) { - event.preventDefault(); - - _this5.hide(); - } - }); - } else if (!this._isShown) { - $$$1(this._element).off(Event.KEYDOWN_DISMISS); - } - }; - - _proto._setResizeEvent = function _setResizeEvent() { - var _this6 = this; - - if (this._isShown) { - $$$1(window).on(Event.RESIZE, function (event) { - return _this6.handleUpdate(event); - }); - } else { - $$$1(window).off(Event.RESIZE); - } - }; - - _proto._hideModal = function _hideModal() { - var _this7 = this; - - this._element.style.display = 'none'; - - this._element.setAttribute('aria-hidden', true); - - this._isTransitioning = false; - - this._showBackdrop(function () { - $$$1(document.body).removeClass(ClassName.OPEN); - - _this7._resetAdjustments(); - - _this7._resetScrollbar(); - - $$$1(_this7._element).trigger(Event.HIDDEN); - }); - }; - - _proto._removeBackdrop = function _removeBackdrop() { - if (this._backdrop) { - $$$1(this._backdrop).remove(); - this._backdrop = null; - } - }; - - _proto._showBackdrop = function _showBackdrop(callback) { - var _this8 = this; - - var animate = $$$1(this._element).hasClass(ClassName.FADE) ? ClassName.FADE : ''; - - if (this._isShown && this._config.backdrop) { - this._backdrop = document.createElement('div'); - this._backdrop.className = ClassName.BACKDROP; - - if (animate) { - $$$1(this._backdrop).addClass(animate); - } - - $$$1(this._backdrop).appendTo(document.body); - $$$1(this._element).on(Event.CLICK_DISMISS, function (event) { - if (_this8._ignoreBackdropClick) { - _this8._ignoreBackdropClick = false; - return; - } - - if (event.target !== event.currentTarget) { - return; - } - - if (_this8._config.backdrop === 'static') { - _this8._element.focus(); - } else { - _this8.hide(); - } - }); - - if (animate) { - Util.reflow(this._backdrop); - } - - $$$1(this._backdrop).addClass(ClassName.SHOW); - - if (!callback) { - return; - } - - if (!animate) { - callback(); - return; - } - - var backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop); - $$$1(this._backdrop).one(Util.TRANSITION_END, callback).emulateTransitionEnd(backdropTransitionDuration); - } else if (!this._isShown && this._backdrop) { - $$$1(this._backdrop).removeClass(ClassName.SHOW); - - var callbackRemove = function callbackRemove() { - _this8._removeBackdrop(); - - if (callback) { - callback(); - } - }; - - if ($$$1(this._element).hasClass(ClassName.FADE)) { - var _backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop); - - $$$1(this._backdrop).one(Util.TRANSITION_END, callbackRemove).emulateTransitionEnd(_backdropTransitionDuration); - } else { - callbackRemove(); - } - } else if (callback) { - callback(); - } - }; // ---------------------------------------------------------------------- - // the following methods are used to handle overflowing modals - // todo (fat): these should probably be refactored out of modal.js - // ---------------------------------------------------------------------- - - - _proto._adjustDialog = function _adjustDialog() { - var isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight; - - if (!this._isBodyOverflowing && isModalOverflowing) { - this._element.style.paddingLeft = this._scrollbarWidth + "px"; - } - - if (this._isBodyOverflowing && !isModalOverflowing) { - this._element.style.paddingRight = this._scrollbarWidth + "px"; - } - }; - - _proto._resetAdjustments = function _resetAdjustments() { - this._element.style.paddingLeft = ''; - this._element.style.paddingRight = ''; - }; - - _proto._checkScrollbar = function _checkScrollbar() { - var rect = document.body.getBoundingClientRect(); - this._isBodyOverflowing = rect.left + rect.right < window.innerWidth; - this._scrollbarWidth = this._getScrollbarWidth(); - }; - - _proto._setScrollbar = function _setScrollbar() { - var _this9 = this; - - if (this._isBodyOverflowing) { - // Note: DOMNode.style.paddingRight returns the actual value or '' if not set - // while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set - // Adjust fixed content padding - $$$1(Selector.FIXED_CONTENT).each(function (index, element) { - var actualPadding = $$$1(element)[0].style.paddingRight; - var calculatedPadding = $$$1(element).css('padding-right'); - $$$1(element).data('padding-right', actualPadding).css('padding-right', parseFloat(calculatedPadding) + _this9._scrollbarWidth + "px"); - }); // Adjust sticky content margin - - $$$1(Selector.STICKY_CONTENT).each(function (index, element) { - var actualMargin = $$$1(element)[0].style.marginRight; - var calculatedMargin = $$$1(element).css('margin-right'); - $$$1(element).data('margin-right', actualMargin).css('margin-right', parseFloat(calculatedMargin) - _this9._scrollbarWidth + "px"); - }); // Adjust navbar-toggler margin - - $$$1(Selector.NAVBAR_TOGGLER).each(function (index, element) { - var actualMargin = $$$1(element)[0].style.marginRight; - var calculatedMargin = $$$1(element).css('margin-right'); - $$$1(element).data('margin-right', actualMargin).css('margin-right', parseFloat(calculatedMargin) + _this9._scrollbarWidth + "px"); - }); // Adjust body padding - - var actualPadding = document.body.style.paddingRight; - var calculatedPadding = $$$1(document.body).css('padding-right'); - $$$1(document.body).data('padding-right', actualPadding).css('padding-right', parseFloat(calculatedPadding) + this._scrollbarWidth + "px"); - } - }; - - _proto._resetScrollbar = function _resetScrollbar() { - // Restore fixed content padding - $$$1(Selector.FIXED_CONTENT).each(function (index, element) { - var padding = $$$1(element).data('padding-right'); - - if (typeof padding !== 'undefined') { - $$$1(element).css('padding-right', padding).removeData('padding-right'); - } - }); // Restore sticky content and navbar-toggler margin - - $$$1(Selector.STICKY_CONTENT + ", " + Selector.NAVBAR_TOGGLER).each(function (index, element) { - var margin = $$$1(element).data('margin-right'); - - if (typeof margin !== 'undefined') { - $$$1(element).css('margin-right', margin).removeData('margin-right'); - } - }); // Restore body padding - - var padding = $$$1(document.body).data('padding-right'); - - if (typeof padding !== 'undefined') { - $$$1(document.body).css('padding-right', padding).removeData('padding-right'); - } - }; - - _proto._getScrollbarWidth = function _getScrollbarWidth() { - // thx d.walsh - var scrollDiv = document.createElement('div'); - scrollDiv.className = ClassName.SCROLLBAR_MEASURER; - document.body.appendChild(scrollDiv); - var scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth; - document.body.removeChild(scrollDiv); - return scrollbarWidth; - }; // Static - - - Modal._jQueryInterface = function _jQueryInterface(config, relatedTarget) { - return this.each(function () { - var data = $$$1(this).data(DATA_KEY); - - var _config = _objectSpread({}, Default, $$$1(this).data(), typeof config === 'object' && config ? config : {}); - - if (!data) { - data = new Modal(this, _config); - $$$1(this).data(DATA_KEY, data); - } - - if (typeof config === 'string') { - if (typeof data[config] === 'undefined') { - throw new TypeError("No method named \"" + config + "\""); - } - - data[config](relatedTarget); - } else if (_config.show) { - data.show(relatedTarget); - } - }); - }; - - _createClass(Modal, null, [{ - key: "VERSION", - get: function get() { - return VERSION; - } - }, { - key: "Default", - get: function get() { - return Default; - } - }]); - - return Modal; - }(); - /** - * ------------------------------------------------------------------------ - * Data Api implementation - * ------------------------------------------------------------------------ - */ - - - $$$1(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { - var _this10 = this; - - var target; - var selector = Util.getSelectorFromElement(this); - - if (selector) { - target = $$$1(selector)[0]; - } - - var config = $$$1(target).data(DATA_KEY) ? 'toggle' : _objectSpread({}, $$$1(target).data(), $$$1(this).data()); - - if (this.tagName === 'A' || this.tagName === 'AREA') { - event.preventDefault(); - } - - var $target = $$$1(target).one(Event.SHOW, function (showEvent) { - if (showEvent.isDefaultPrevented()) { - // Only register focus restorer if modal will actually get shown - return; - } - - $target.one(Event.HIDDEN, function () { - if ($$$1(_this10).is(':visible')) { - _this10.focus(); - } - }); - }); - - Modal._jQueryInterface.call($$$1(target), config, this); - }); - /** - * ------------------------------------------------------------------------ - * jQuery - * ------------------------------------------------------------------------ - */ - - $$$1.fn[NAME] = Modal._jQueryInterface; - $$$1.fn[NAME].Constructor = Modal; - - $$$1.fn[NAME].noConflict = function () { - $$$1.fn[NAME] = JQUERY_NO_CONFLICT; - return Modal._jQueryInterface; - }; - - return Modal; - }($); - - /** - * -------------------------------------------------------------------------- - * Bootstrap (v4.1.1): tooltip.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * -------------------------------------------------------------------------- - */ - - var Tooltip = function ($$$1) { - /** - * ------------------------------------------------------------------------ - * Constants - * ------------------------------------------------------------------------ - */ - var NAME = 'tooltip'; - var VERSION = '4.1.1'; - var DATA_KEY = 'bs.tooltip'; - var EVENT_KEY = "." + DATA_KEY; - var JQUERY_NO_CONFLICT = $$$1.fn[NAME]; - var CLASS_PREFIX = 'bs-tooltip'; - var BSCLS_PREFIX_REGEX = new RegExp("(^|\\s)" + CLASS_PREFIX + "\\S+", 'g'); - var DefaultType = { - animation: 'boolean', - template: 'string', - title: '(string|element|function)', - trigger: 'string', - delay: '(number|object)', - html: 'boolean', - selector: '(string|boolean)', - placement: '(string|function)', - offset: '(number|string)', - container: '(string|element|boolean)', - fallbackPlacement: '(string|array)', - boundary: '(string|element)' - }; - var AttachmentMap = { - AUTO: 'auto', - TOP: 'top', - RIGHT: 'right', - BOTTOM: 'bottom', - LEFT: 'left' - }; - var Default = { - animation: true, - template: '', - trigger: 'hover focus', - title: '', - delay: 0, - html: false, - selector: false, - placement: 'top', - offset: 0, - container: false, - fallbackPlacement: 'flip', - boundary: 'scrollParent' - }; - var HoverState = { - SHOW: 'show', - OUT: 'out' - }; - var Event = { - HIDE: "hide" + EVENT_KEY, - HIDDEN: "hidden" + EVENT_KEY, - SHOW: "show" + EVENT_KEY, - SHOWN: "shown" + EVENT_KEY, - INSERTED: "inserted" + EVENT_KEY, - CLICK: "click" + EVENT_KEY, - FOCUSIN: "focusin" + EVENT_KEY, - FOCUSOUT: "focusout" + EVENT_KEY, - MOUSEENTER: "mouseenter" + EVENT_KEY, - MOUSELEAVE: "mouseleave" + EVENT_KEY - }; - var ClassName = { - FADE: 'fade', - SHOW: 'show' - }; - var Selector = { - TOOLTIP: '.tooltip', - TOOLTIP_INNER: '.tooltip-inner', - ARROW: '.arrow' - }; - var Trigger = { - HOVER: 'hover', - FOCUS: 'focus', - CLICK: 'click', - MANUAL: 'manual' - /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ - */ - - }; - - var Tooltip = - /*#__PURE__*/ - function () { - function Tooltip(element, config) { - /** - * Check for Popper dependency - * Popper - https://popper.js.org - */ - if (typeof Popper === 'undefined') { - throw new TypeError('Bootstrap tooltips require Popper.js (https://popper.js.org)'); - } // private - - - this._isEnabled = true; - this._timeout = 0; - this._hoverState = ''; - this._activeTrigger = {}; - this._popper = null; // Protected - - this.element = element; - this.config = this._getConfig(config); - this.tip = null; - - this._setListeners(); - } // Getters - - - var _proto = Tooltip.prototype; - - // Public - _proto.enable = function enable() { - this._isEnabled = true; - }; - - _proto.disable = function disable() { - this._isEnabled = false; - }; - - _proto.toggleEnabled = function toggleEnabled() { - this._isEnabled = !this._isEnabled; - }; - - _proto.toggle = function toggle(event) { - if (!this._isEnabled) { - return; - } - - if (event) { - var dataKey = this.constructor.DATA_KEY; - var context = $$$1(event.currentTarget).data(dataKey); - - if (!context) { - context = new this.constructor(event.currentTarget, this._getDelegateConfig()); - $$$1(event.currentTarget).data(dataKey, context); - } - - context._activeTrigger.click = !context._activeTrigger.click; - - if (context._isWithActiveTrigger()) { - context._enter(null, context); - } else { - context._leave(null, context); - } - } else { - if ($$$1(this.getTipElement()).hasClass(ClassName.SHOW)) { - this._leave(null, this); - - return; - } - - this._enter(null, this); - } - }; - - _proto.dispose = function dispose() { - clearTimeout(this._timeout); - $$$1.removeData(this.element, this.constructor.DATA_KEY); - $$$1(this.element).off(this.constructor.EVENT_KEY); - $$$1(this.element).closest('.modal').off('hide.bs.modal'); - - if (this.tip) { - $$$1(this.tip).remove(); - } - - this._isEnabled = null; - this._timeout = null; - this._hoverState = null; - this._activeTrigger = null; - - if (this._popper !== null) { - this._popper.destroy(); - } - - this._popper = null; - this.element = null; - this.config = null; - this.tip = null; - }; - - _proto.show = function show() { - var _this = this; - - if ($$$1(this.element).css('display') === 'none') { - throw new Error('Please use show on visible elements'); - } - - var showEvent = $$$1.Event(this.constructor.Event.SHOW); - - if (this.isWithContent() && this._isEnabled) { - $$$1(this.element).trigger(showEvent); - var isInTheDom = $$$1.contains(this.element.ownerDocument.documentElement, this.element); - - if (showEvent.isDefaultPrevented() || !isInTheDom) { - return; - } - - var tip = this.getTipElement(); - var tipId = Util.getUID(this.constructor.NAME); - tip.setAttribute('id', tipId); - this.element.setAttribute('aria-describedby', tipId); - this.setContent(); - - if (this.config.animation) { - $$$1(tip).addClass(ClassName.FADE); - } - - var placement = typeof this.config.placement === 'function' ? this.config.placement.call(this, tip, this.element) : this.config.placement; - - var attachment = this._getAttachment(placement); - - this.addAttachmentClass(attachment); - var container = this.config.container === false ? document.body : $$$1(this.config.container); - $$$1(tip).data(this.constructor.DATA_KEY, this); - - if (!$$$1.contains(this.element.ownerDocument.documentElement, this.tip)) { - $$$1(tip).appendTo(container); - } - - $$$1(this.element).trigger(this.constructor.Event.INSERTED); - this._popper = new Popper(this.element, tip, { - placement: attachment, - modifiers: { - offset: { - offset: this.config.offset - }, - flip: { - behavior: this.config.fallbackPlacement - }, - arrow: { - element: Selector.ARROW - }, - preventOverflow: { - boundariesElement: this.config.boundary - } - }, - onCreate: function onCreate(data) { - if (data.originalPlacement !== data.placement) { - _this._handlePopperPlacementChange(data); - } - }, - onUpdate: function onUpdate(data) { - _this._handlePopperPlacementChange(data); - } - }); - $$$1(tip).addClass(ClassName.SHOW); // If this is a touch-enabled device we add extra - // empty mouseover listeners to the body's immediate children; - // only needed because of broken event delegation on iOS - // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html - - if ('ontouchstart' in document.documentElement) { - $$$1(document.body).children().on('mouseover', null, $$$1.noop); - } - - var complete = function complete() { - if (_this.config.animation) { - _this._fixTransition(); - } - - var prevHoverState = _this._hoverState; - _this._hoverState = null; - $$$1(_this.element).trigger(_this.constructor.Event.SHOWN); - - if (prevHoverState === HoverState.OUT) { - _this._leave(null, _this); - } - }; - - if ($$$1(this.tip).hasClass(ClassName.FADE)) { - var transitionDuration = Util.getTransitionDurationFromElement(this.tip); - $$$1(this.tip).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration); - } else { - complete(); - } - } - }; - - _proto.hide = function hide(callback) { - var _this2 = this; - - var tip = this.getTipElement(); - var hideEvent = $$$1.Event(this.constructor.Event.HIDE); - - var complete = function complete() { - if (_this2._hoverState !== HoverState.SHOW && tip.parentNode) { - tip.parentNode.removeChild(tip); - } - - _this2._cleanTipClass(); - - _this2.element.removeAttribute('aria-describedby'); - - $$$1(_this2.element).trigger(_this2.constructor.Event.HIDDEN); - - if (_this2._popper !== null) { - _this2._popper.destroy(); - } - - if (callback) { - callback(); - } - }; - - $$$1(this.element).trigger(hideEvent); - - if (hideEvent.isDefaultPrevented()) { - return; - } - - $$$1(tip).removeClass(ClassName.SHOW); // If this is a touch-enabled device we remove the extra - // empty mouseover listeners we added for iOS support - - if ('ontouchstart' in document.documentElement) { - $$$1(document.body).children().off('mouseover', null, $$$1.noop); - } - - this._activeTrigger[Trigger.CLICK] = false; - this._activeTrigger[Trigger.FOCUS] = false; - this._activeTrigger[Trigger.HOVER] = false; - - if ($$$1(this.tip).hasClass(ClassName.FADE)) { - var transitionDuration = Util.getTransitionDurationFromElement(tip); - $$$1(tip).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration); - } else { - complete(); - } - - this._hoverState = ''; - }; - - _proto.update = function update() { - if (this._popper !== null) { - this._popper.scheduleUpdate(); - } - }; // Protected - - - _proto.isWithContent = function isWithContent() { - return Boolean(this.getTitle()); - }; - - _proto.addAttachmentClass = function addAttachmentClass(attachment) { - $$$1(this.getTipElement()).addClass(CLASS_PREFIX + "-" + attachment); - }; - - _proto.getTipElement = function getTipElement() { - this.tip = this.tip || $$$1(this.config.template)[0]; - return this.tip; - }; - - _proto.setContent = function setContent() { - var $tip = $$$1(this.getTipElement()); - this.setElementContent($tip.find(Selector.TOOLTIP_INNER), this.getTitle()); - $tip.removeClass(ClassName.FADE + " " + ClassName.SHOW); - }; - - _proto.setElementContent = function setElementContent($element, content) { - var html = this.config.html; - - if (typeof content === 'object' && (content.nodeType || content.jquery)) { - // Content is a DOM node or a jQuery - if (html) { - if (!$$$1(content).parent().is($element)) { - $element.empty().append(content); - } - } else { - $element.text($$$1(content).text()); - } - } else { - $element[html ? 'html' : 'text'](content); - } - }; - - _proto.getTitle = function getTitle() { - var title = this.element.getAttribute('data-original-title'); - - if (!title) { - title = typeof this.config.title === 'function' ? this.config.title.call(this.element) : this.config.title; - } - - return title; - }; // Private - - - _proto._getAttachment = function _getAttachment(placement) { - return AttachmentMap[placement.toUpperCase()]; - }; - - _proto._setListeners = function _setListeners() { - var _this3 = this; - - var triggers = this.config.trigger.split(' '); - triggers.forEach(function (trigger) { - if (trigger === 'click') { - $$$1(_this3.element).on(_this3.constructor.Event.CLICK, _this3.config.selector, function (event) { - return _this3.toggle(event); - }); - } else if (trigger !== Trigger.MANUAL) { - var eventIn = trigger === Trigger.HOVER ? _this3.constructor.Event.MOUSEENTER : _this3.constructor.Event.FOCUSIN; - var eventOut = trigger === Trigger.HOVER ? _this3.constructor.Event.MOUSELEAVE : _this3.constructor.Event.FOCUSOUT; - $$$1(_this3.element).on(eventIn, _this3.config.selector, function (event) { - return _this3._enter(event); - }).on(eventOut, _this3.config.selector, function (event) { - return _this3._leave(event); - }); - } - - $$$1(_this3.element).closest('.modal').on('hide.bs.modal', function () { - return _this3.hide(); - }); - }); - - if (this.config.selector) { - this.config = _objectSpread({}, this.config, { - trigger: 'manual', - selector: '' - }); - } else { - this._fixTitle(); - } - }; - - _proto._fixTitle = function _fixTitle() { - var titleType = typeof this.element.getAttribute('data-original-title'); - - if (this.element.getAttribute('title') || titleType !== 'string') { - this.element.setAttribute('data-original-title', this.element.getAttribute('title') || ''); - this.element.setAttribute('title', ''); - } - }; - - _proto._enter = function _enter(event, context) { - var dataKey = this.constructor.DATA_KEY; - context = context || $$$1(event.currentTarget).data(dataKey); - - if (!context) { - context = new this.constructor(event.currentTarget, this._getDelegateConfig()); - $$$1(event.currentTarget).data(dataKey, context); - } - - if (event) { - context._activeTrigger[event.type === 'focusin' ? Trigger.FOCUS : Trigger.HOVER] = true; - } - - if ($$$1(context.getTipElement()).hasClass(ClassName.SHOW) || context._hoverState === HoverState.SHOW) { - context._hoverState = HoverState.SHOW; - return; - } - - clearTimeout(context._timeout); - context._hoverState = HoverState.SHOW; - - if (!context.config.delay || !context.config.delay.show) { - context.show(); - return; - } - - context._timeout = setTimeout(function () { - if (context._hoverState === HoverState.SHOW) { - context.show(); - } - }, context.config.delay.show); - }; - - _proto._leave = function _leave(event, context) { - var dataKey = this.constructor.DATA_KEY; - context = context || $$$1(event.currentTarget).data(dataKey); - - if (!context) { - context = new this.constructor(event.currentTarget, this._getDelegateConfig()); - $$$1(event.currentTarget).data(dataKey, context); - } - - if (event) { - context._activeTrigger[event.type === 'focusout' ? Trigger.FOCUS : Trigger.HOVER] = false; - } - - if (context._isWithActiveTrigger()) { - return; - } - - clearTimeout(context._timeout); - context._hoverState = HoverState.OUT; - - if (!context.config.delay || !context.config.delay.hide) { - context.hide(); - return; - } - - context._timeout = setTimeout(function () { - if (context._hoverState === HoverState.OUT) { - context.hide(); - } - }, context.config.delay.hide); - }; - - _proto._isWithActiveTrigger = function _isWithActiveTrigger() { - for (var trigger in this._activeTrigger) { - if (this._activeTrigger[trigger]) { - return true; - } - } - - return false; - }; - - _proto._getConfig = function _getConfig(config) { - config = _objectSpread({}, this.constructor.Default, $$$1(this.element).data(), typeof config === 'object' && config ? config : {}); - - if (typeof config.delay === 'number') { - config.delay = { - show: config.delay, - hide: config.delay - }; - } - - if (typeof config.title === 'number') { - config.title = config.title.toString(); - } - - if (typeof config.content === 'number') { - config.content = config.content.toString(); - } - - Util.typeCheckConfig(NAME, config, this.constructor.DefaultType); - return config; - }; - - _proto._getDelegateConfig = function _getDelegateConfig() { - var config = {}; - - if (this.config) { - for (var key in this.config) { - if (this.constructor.Default[key] !== this.config[key]) { - config[key] = this.config[key]; - } - } - } - - return config; - }; - - _proto._cleanTipClass = function _cleanTipClass() { - var $tip = $$$1(this.getTipElement()); - var tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX); - - if (tabClass !== null && tabClass.length > 0) { - $tip.removeClass(tabClass.join('')); - } - }; - - _proto._handlePopperPlacementChange = function _handlePopperPlacementChange(data) { - this._cleanTipClass(); - - this.addAttachmentClass(this._getAttachment(data.placement)); - }; - - _proto._fixTransition = function _fixTransition() { - var tip = this.getTipElement(); - var initConfigAnimation = this.config.animation; - - if (tip.getAttribute('x-placement') !== null) { - return; - } - - $$$1(tip).removeClass(ClassName.FADE); - this.config.animation = false; - this.hide(); - this.show(); - this.config.animation = initConfigAnimation; - }; // Static - - - Tooltip._jQueryInterface = function _jQueryInterface(config) { - return this.each(function () { - var data = $$$1(this).data(DATA_KEY); - - var _config = typeof config === 'object' && config; - - if (!data && /dispose|hide/.test(config)) { - return; - } - - if (!data) { - data = new Tooltip(this, _config); - $$$1(this).data(DATA_KEY, data); - } - - if (typeof config === 'string') { - if (typeof data[config] === 'undefined') { - throw new TypeError("No method named \"" + config + "\""); - } - - data[config](); - } - }); - }; - - _createClass(Tooltip, null, [{ - key: "VERSION", - get: function get() { - return VERSION; - } - }, { - key: "Default", - get: function get() { - return Default; - } - }, { - key: "NAME", - get: function get() { - return NAME; - } - }, { - key: "DATA_KEY", - get: function get() { - return DATA_KEY; - } - }, { - key: "Event", - get: function get() { - return Event; - } - }, { - key: "EVENT_KEY", - get: function get() { - return EVENT_KEY; - } - }, { - key: "DefaultType", - get: function get() { - return DefaultType; - } - }]); - - return Tooltip; - }(); - /** - * ------------------------------------------------------------------------ - * jQuery - * ------------------------------------------------------------------------ - */ - - - $$$1.fn[NAME] = Tooltip._jQueryInterface; - $$$1.fn[NAME].Constructor = Tooltip; - - $$$1.fn[NAME].noConflict = function () { - $$$1.fn[NAME] = JQUERY_NO_CONFLICT; - return Tooltip._jQueryInterface; - }; - - return Tooltip; - }($, Popper); - - /** - * -------------------------------------------------------------------------- - * Bootstrap (v4.1.1): popover.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * -------------------------------------------------------------------------- - */ - - var Popover = function ($$$1) { - /** - * ------------------------------------------------------------------------ - * Constants - * ------------------------------------------------------------------------ - */ - var NAME = 'popover'; - var VERSION = '4.1.1'; - var DATA_KEY = 'bs.popover'; - var EVENT_KEY = "." + DATA_KEY; - var JQUERY_NO_CONFLICT = $$$1.fn[NAME]; - var CLASS_PREFIX = 'bs-popover'; - var BSCLS_PREFIX_REGEX = new RegExp("(^|\\s)" + CLASS_PREFIX + "\\S+", 'g'); - - var Default = _objectSpread({}, Tooltip.Default, { - placement: 'right', - trigger: 'click', - content: '', - template: '' - }); - - var DefaultType = _objectSpread({}, Tooltip.DefaultType, { - content: '(string|element|function)' - }); - - var ClassName = { - FADE: 'fade', - SHOW: 'show' - }; - var Selector = { - TITLE: '.popover-header', - CONTENT: '.popover-body' - }; - var Event = { - HIDE: "hide" + EVENT_KEY, - HIDDEN: "hidden" + EVENT_KEY, - SHOW: "show" + EVENT_KEY, - SHOWN: "shown" + EVENT_KEY, - INSERTED: "inserted" + EVENT_KEY, - CLICK: "click" + EVENT_KEY, - FOCUSIN: "focusin" + EVENT_KEY, - FOCUSOUT: "focusout" + EVENT_KEY, - MOUSEENTER: "mouseenter" + EVENT_KEY, - MOUSELEAVE: "mouseleave" + EVENT_KEY - /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ - */ - - }; - - var Popover = - /*#__PURE__*/ - function (_Tooltip) { - _inheritsLoose(Popover, _Tooltip); - - function Popover() { - return _Tooltip.apply(this, arguments) || this; - } - - var _proto = Popover.prototype; - - // Overrides - _proto.isWithContent = function isWithContent() { - return this.getTitle() || this._getContent(); - }; - - _proto.addAttachmentClass = function addAttachmentClass(attachment) { - $$$1(this.getTipElement()).addClass(CLASS_PREFIX + "-" + attachment); - }; - - _proto.getTipElement = function getTipElement() { - this.tip = this.tip || $$$1(this.config.template)[0]; - return this.tip; - }; - - _proto.setContent = function setContent() { - var $tip = $$$1(this.getTipElement()); // We use append for html objects to maintain js events - - this.setElementContent($tip.find(Selector.TITLE), this.getTitle()); - - var content = this._getContent(); - - if (typeof content === 'function') { - content = content.call(this.element); - } - - this.setElementContent($tip.find(Selector.CONTENT), content); - $tip.removeClass(ClassName.FADE + " " + ClassName.SHOW); - }; // Private - - - _proto._getContent = function _getContent() { - return this.element.getAttribute('data-content') || this.config.content; - }; - - _proto._cleanTipClass = function _cleanTipClass() { - var $tip = $$$1(this.getTipElement()); - var tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX); - - if (tabClass !== null && tabClass.length > 0) { - $tip.removeClass(tabClass.join('')); - } - }; // Static - - - Popover._jQueryInterface = function _jQueryInterface(config) { - return this.each(function () { - var data = $$$1(this).data(DATA_KEY); - - var _config = typeof config === 'object' ? config : null; - - if (!data && /destroy|hide/.test(config)) { - return; - } - - if (!data) { - data = new Popover(this, _config); - $$$1(this).data(DATA_KEY, data); - } - - if (typeof config === 'string') { - if (typeof data[config] === 'undefined') { - throw new TypeError("No method named \"" + config + "\""); - } - - data[config](); - } - }); - }; - - _createClass(Popover, null, [{ - key: "VERSION", - // Getters - get: function get() { - return VERSION; - } - }, { - key: "Default", - get: function get() { - return Default; - } - }, { - key: "NAME", - get: function get() { - return NAME; - } - }, { - key: "DATA_KEY", - get: function get() { - return DATA_KEY; - } - }, { - key: "Event", - get: function get() { - return Event; - } - }, { - key: "EVENT_KEY", - get: function get() { - return EVENT_KEY; - } - }, { - key: "DefaultType", - get: function get() { - return DefaultType; - } - }]); - - return Popover; - }(Tooltip); - /** - * ------------------------------------------------------------------------ - * jQuery - * ------------------------------------------------------------------------ - */ - - - $$$1.fn[NAME] = Popover._jQueryInterface; - $$$1.fn[NAME].Constructor = Popover; - - $$$1.fn[NAME].noConflict = function () { - $$$1.fn[NAME] = JQUERY_NO_CONFLICT; - return Popover._jQueryInterface; - }; - - return Popover; - }($); - - /** - * -------------------------------------------------------------------------- - * Bootstrap (v4.1.1): scrollspy.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * -------------------------------------------------------------------------- - */ - - var ScrollSpy = function ($$$1) { - /** - * ------------------------------------------------------------------------ - * Constants - * ------------------------------------------------------------------------ - */ - var NAME = 'scrollspy'; - var VERSION = '4.1.1'; - var DATA_KEY = 'bs.scrollspy'; - var EVENT_KEY = "." + DATA_KEY; - var DATA_API_KEY = '.data-api'; - var JQUERY_NO_CONFLICT = $$$1.fn[NAME]; - var Default = { - offset: 10, - method: 'auto', - target: '' - }; - var DefaultType = { - offset: 'number', - method: 'string', - target: '(string|element)' - }; - var Event = { - ACTIVATE: "activate" + EVENT_KEY, - SCROLL: "scroll" + EVENT_KEY, - LOAD_DATA_API: "load" + EVENT_KEY + DATA_API_KEY - }; - var ClassName = { - DROPDOWN_ITEM: 'dropdown-item', - DROPDOWN_MENU: 'dropdown-menu', - ACTIVE: 'active' - }; - var Selector = { - DATA_SPY: '[data-spy="scroll"]', - ACTIVE: '.active', - NAV_LIST_GROUP: '.nav, .list-group', - NAV_LINKS: '.nav-link', - NAV_ITEMS: '.nav-item', - LIST_ITEMS: '.list-group-item', - DROPDOWN: '.dropdown', - DROPDOWN_ITEMS: '.dropdown-item', - DROPDOWN_TOGGLE: '.dropdown-toggle' - }; - var OffsetMethod = { - OFFSET: 'offset', - POSITION: 'position' - /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ - */ - - }; - - var ScrollSpy = - /*#__PURE__*/ - function () { - function ScrollSpy(element, config) { - var _this = this; - - this._element = element; - this._scrollElement = element.tagName === 'BODY' ? window : element; - this._config = this._getConfig(config); - this._selector = this._config.target + " " + Selector.NAV_LINKS + "," + (this._config.target + " " + Selector.LIST_ITEMS + ",") + (this._config.target + " " + Selector.DROPDOWN_ITEMS); - this._offsets = []; - this._targets = []; - this._activeTarget = null; - this._scrollHeight = 0; - $$$1(this._scrollElement).on(Event.SCROLL, function (event) { - return _this._process(event); - }); - this.refresh(); - - this._process(); - } // Getters - - - var _proto = ScrollSpy.prototype; - - // Public - _proto.refresh = function refresh() { - var _this2 = this; - - var autoMethod = this._scrollElement === this._scrollElement.window ? OffsetMethod.OFFSET : OffsetMethod.POSITION; - var offsetMethod = this._config.method === 'auto' ? autoMethod : this._config.method; - var offsetBase = offsetMethod === OffsetMethod.POSITION ? this._getScrollTop() : 0; - this._offsets = []; - this._targets = []; - this._scrollHeight = this._getScrollHeight(); - var targets = $$$1.makeArray($$$1(this._selector)); - targets.map(function (element) { - var target; - var targetSelector = Util.getSelectorFromElement(element); - - if (targetSelector) { - target = $$$1(targetSelector)[0]; - } - - if (target) { - var targetBCR = target.getBoundingClientRect(); - - if (targetBCR.width || targetBCR.height) { - // TODO (fat): remove sketch reliance on jQuery position/offset - return [$$$1(target)[offsetMethod]().top + offsetBase, targetSelector]; - } - } - - return null; - }).filter(function (item) { - return item; - }).sort(function (a, b) { - return a[0] - b[0]; - }).forEach(function (item) { - _this2._offsets.push(item[0]); - - _this2._targets.push(item[1]); - }); - }; - - _proto.dispose = function dispose() { - $$$1.removeData(this._element, DATA_KEY); - $$$1(this._scrollElement).off(EVENT_KEY); - this._element = null; - this._scrollElement = null; - this._config = null; - this._selector = null; - this._offsets = null; - this._targets = null; - this._activeTarget = null; - this._scrollHeight = null; - }; // Private - - - _proto._getConfig = function _getConfig(config) { - config = _objectSpread({}, Default, typeof config === 'object' && config ? config : {}); - - if (typeof config.target !== 'string') { - var id = $$$1(config.target).attr('id'); - - if (!id) { - id = Util.getUID(NAME); - $$$1(config.target).attr('id', id); - } - - config.target = "#" + id; - } - - Util.typeCheckConfig(NAME, config, DefaultType); - return config; - }; - - _proto._getScrollTop = function _getScrollTop() { - return this._scrollElement === window ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop; - }; - - _proto._getScrollHeight = function _getScrollHeight() { - return this._scrollElement.scrollHeight || Math.max(document.body.scrollHeight, document.documentElement.scrollHeight); - }; - - _proto._getOffsetHeight = function _getOffsetHeight() { - return this._scrollElement === window ? window.innerHeight : this._scrollElement.getBoundingClientRect().height; - }; - - _proto._process = function _process() { - var scrollTop = this._getScrollTop() + this._config.offset; - - var scrollHeight = this._getScrollHeight(); - - var maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight(); - - if (this._scrollHeight !== scrollHeight) { - this.refresh(); - } - - if (scrollTop >= maxScroll) { - var target = this._targets[this._targets.length - 1]; - - if (this._activeTarget !== target) { - this._activate(target); - } - - return; - } - - if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) { - this._activeTarget = null; - - this._clear(); - - return; - } - - for (var i = this._offsets.length; i--;) { - var isActiveTarget = this._activeTarget !== this._targets[i] && scrollTop >= this._offsets[i] && (typeof this._offsets[i + 1] === 'undefined' || scrollTop < this._offsets[i + 1]); - - if (isActiveTarget) { - this._activate(this._targets[i]); - } - } - }; - - _proto._activate = function _activate(target) { - this._activeTarget = target; - - this._clear(); - - var queries = this._selector.split(','); // eslint-disable-next-line arrow-body-style - - - queries = queries.map(function (selector) { - return selector + "[data-target=\"" + target + "\"]," + (selector + "[href=\"" + target + "\"]"); - }); - var $link = $$$1(queries.join(',')); - - if ($link.hasClass(ClassName.DROPDOWN_ITEM)) { - $link.closest(Selector.DROPDOWN).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE); - $link.addClass(ClassName.ACTIVE); - } else { - // Set triggered link as active - $link.addClass(ClassName.ACTIVE); // Set triggered links parents as active - // With both
'+a.data[e].track+""+a.data[e].title+ +"
searchSearching
searchSearching
'+(a.data[d].pos+ 1)+""+a.data[d].title+""+a.data[d].artist+""+a.data[d].album+""+e+":"+(10>f?"0":"")+f+"
error_outlineNo results, please refine your search!
folder_open'+basename(a.data[e].dir)+"
error_outlineNo entries found.
',b<=c.length?c[b-1].id!=e&&$(c[b-1]).replaceWith(f):$("#BrowseDatabaseAlbumCards").append(f),(b>c.length||c[b-1].id!=e)&&sendAPI({cmd:"MPD_API_GET_ARTISTALBUMTITLES", data:{albumartist:a.searchstr,album:a.data[d].value}},parseListTitles);for(d=c.length;d>b;d--)$(c[d-1]).remove();setPagination(a.totalEntities)}doSetFilterLetter("#BrowseDatabaseFilter")}} -function parseListTitles(a){if("Browse"===app.current.app||"Database"===app.current.tab||"Album"===app.current.view){var b=genId(a.album),c=$("#card"+b+" > div > table > tbody");$("#card"+b+" > img").attr("src","/library/"+a.data[0].uri.replace(/\/[^\/]+$/,"/")+settings.coverimage).attr("uri",a.data[0].uri.replace(/\/[^\/]+$/,"")).attr("data-album",encodeURI(a.album));var d="",e;for(e in a.data)d+=''+a.data[e].track+""+a.data[e].title+ -"";c.append(d);$("#card"+b+" > img").on({click:function(){sendAPI({cmd:"MPD_API_ADD_TRACK",data:{track:decodeURI($(this).attr("uri"))}});showNotification('"'+decodeURI($(this).attr("data-album"))+'" added',"","","success")}});$("#tbl"+b+" > tbody > tr").on({click:function(){sendAPI({cmd:"MPD_API_ADD_TRACK",data:{track:decodeURI($(this).attr("uri"))}});showNotification('"'+$("td:nth-last-child(1)",this).text()+'" added',"","","success")}})}} +function parseListTitles(a){if("Browse"===app.current.app||"Database"===app.current.tab||"Album"===app.current.view){var b=genId(a.album),c=$("#card"+b+" > div > table > tbody");$("#card"+b+" > img").attr("src",a.cover).attr("uri",a.data[0].uri.replace(/\/[^\/]+$/,"")).attr("data-album",encodeURI(a.album));var d="",e;for(e in a.data)d+=''+a.data[e].track+""+a.data[e].title+"";c.append(d);$("#card"+b+" > img").on({click:function(){sendAPI({cmd:"MPD_API_ADD_TRACK", +data:{track:decodeURI($(this).attr("uri"))}});showNotification('"'+decodeURI($(this).attr("data-album"))+'" added',"","","success")}});$("#tbl"+b+" > tbody > tr").on({click:function(){sendAPI({cmd:"MPD_API_ADD_TRACK",data:{track:decodeURI($(this).attr("uri"))}});showNotification('"'+$("td:nth-last-child(1)",this).text()+'" added',"","","success")}})}} function setPagination(a){var b=Math.ceil(a/settings.max_elements_per_page),c=app.current.app+(void 0==app.current.tab?"":app.current.tab);0==b&&(b=1);$("#"+c+"PaginationTopPage").text("Page "+(app.current.page/settings.max_elements_per_page+1)+" / "+b);$("#"+c+"PaginationBottomPage").text("Page "+(app.current.page/settings.max_elements_per_page+1)+" / "+b);if(1'+(d+1)+""),$("#"+c+"PaginationBottomPages").append('")}else $("#"+c+"PaginationTopPage").addClass("disabled").attr("disabled", "disabled"),$("#"+c+"PaginationBottomPage").addClass("disabled").attr("disabled","disabled");a>app.current.page+settings.max_elements_per_page?($("#"+c+"PaginationTopNext").removeClass("disabled").removeAttr("disabled"),$("#"+c+"PaginationBottomNext").removeClass("disabled").removeAttr("disabled"),$("#"+c+"ButtonsBottom").removeClass("hide")):($("#"+c+"PaginationTopNext").addClass("disabled").attr("disabled","disabled"),$("#"+c+"PaginationBottomNext").addClass("disabled").attr("disabled","disabled"), @@ -64,25 +63,23 @@ function updateVolumeIcon(a){-1==a?($("#volumePrct").text("Volumecontrol disable function updatePlayIcon(a){1==a.data.state?($("#btnPlay > span").text("play_arrow"),playstate="stop"):2==a.data.state?($("#btnPlay > span").text("pause"),playstate="play"):($("#btnPlay > span").text("play_arrow"),playstate="pause");-1==a.data.nextsongpos?$("#btnNext").addClass("disabled").attr("disabled","disabled"):$("#btnNext").removeClass("disabled").removeAttr("disabled");0>=a.data.songpos?$("#btnPrev").addClass("disabled").attr("disabled","disabled"):$("#btnPrev").removeClass("disabled").removeAttr("disabled"); 0==a.data.queue_length?$("#btnPlay").addClass("disabled").attr("disabled","disabled"):$("#btnPlay").removeClass("disabled").removeAttr("disabled")}function sendAPI(a,b){$.ajax({url:"/api",contentType:"application/json",method:"POST",data:JSON.stringify(a),success:b})}function updateDB(a){sendAPI({cmd:"MPD_API_UPDATE_DB"});showNotification("Updating MPD Database...","","","success");a.preventDefault()} function clickPlay(){"play"!=playstate?sendAPI({cmd:"MPD_API_SET_PLAY"}):sendAPI({cmd:"MPD_API_SET_PAUSE"})}function clickStop(){sendAPI({cmd:"MPD_API_SET_STOP"})}function clickPrev(){sendAPI({cmd:"MPD_API_SET_PREV"})}function clickNext(){sendAPI({cmd:"MPD_API_SET_NEXT"})}function setLocalStream(a,b){var c="http://";c="127.0.0.1"==a||"localhost"==a?c+window.location.hostname:c+a;settings.mpdstream=c+(":"+b+"/")} -function delQueueSong(a,b){b.stopPropagation();$("#btntrashmodeup").hasClass("btn-success")?sendAPI({cmd:"MPD_API_RM_RANGE",data:{start:0,end:a.index()+1}}):$("#btntrashmodesingle").hasClass("btn-success")?sendAPI({cmd:"MPD_API_RM_TRACK",data:{track:a.attr("trackid")}}):$("#btntrashmodedown").hasClass("btn-success")&&sendAPI({cmd:"MPD_API_RM_RANGE",data:{start:a.index(),end:-1}})}function delPlaylist(a){sendAPI({cmd:"MPD_API_RM_PLAYLIST",data:{plist:decodeURI(a.attr("uri"))}});a.remove()} -function basename(a){return a.split("/").reverse()[0]}function toggleBtn(a){$(a).hasClass("btn-success")?$(a).removeClass("btn-success").addClass("btn-secondary"):$(a).removeClass("btn-secondary").addClass("btn-success")}$("#btnrandom").on("click",function(a){toggleBtn(this)});$("#btnconsume").on("click",function(a){toggleBtn(this)});$("#btnsingle").on("click",function(a){toggleBtn(this)});$("#btnrepeat").on("click",function(a){toggleBtn(this)});$("#btnnotifyPage").on("click",function(a){toggleBtn(this)}); -$("#btnnotifyWeb").on("click",function(a){toggleBtn(this)});$("#cardBrowseNavFilesystem").on("click",function(a){app.goto("Browse","Filesystem");a.preventDefault()});$("#cardBrowseNavDatabase").on("click",function(a){app.goto("Browse","Database");a.preventDefault()});$("#btnBrowseDatabaseArtist").on("click",function(a){app.goto("Browse","Database","Artist");a.preventDefault()});$("#cardBrowseNavPlaylists").on("click",function(a){app.goto("Browse","Playlists");a.preventDefault()}); +function delQueueSong(a,b){b.stopPropagation();$("#btntrashmodeup").hasClass("active")?sendAPI({cmd:"MPD_API_RM_RANGE",data:{start:0,end:a.index()+1}}):$("#btntrashmodesingle").hasClass("active")?sendAPI({cmd:"MPD_API_RM_TRACK",data:{track:a.attr("trackid")}}):$("#btntrashmodedown").hasClass("active")&&sendAPI({cmd:"MPD_API_RM_RANGE",data:{start:a.index(),end:-1}})}function delPlaylist(a){sendAPI({cmd:"MPD_API_RM_PLAYLIST",data:{plist:decodeURI(a.attr("uri"))}});a.remove()} +function basename(a){return a.split("/").reverse()[0]}$("#cardBrowseNavFilesystem").on("click",function(a){app.goto("Browse","Filesystem");a.preventDefault()});$("#cardBrowseNavDatabase").on("click",function(a){app.goto("Browse","Database");a.preventDefault()});$("#btnBrowseDatabaseArtist").on("click",function(a){app.goto("Browse","Database","Artist");a.preventDefault()});$("#cardBrowseNavPlaylists").on("click",function(a){app.goto("Browse","Playlists");a.preventDefault()}); $("#cardBrowseNavFilesystem").on("click",function(a){app.goto("Browse","Filesystem");a.preventDefault()});$("#navPlayback").on("click",function(a){app.goto("Playback");a.preventDefault()});$("#navQueue").on("click",function(a){app.goto("Queue");a.preventDefault()});$("#navBrowse").on("click",function(a){app.goto("Browse");a.preventDefault()});$("#navSearch").on("click",function(a){app.goto("Search");a.preventDefault()}); -function confirmSettings(){var a=!0;if(!$("#inputCrossfade").is(":disabled")){var b=parseInt($("#inputCrossfade").val());isNaN(b)?($("#inputCrossfade").popover({content:"Must be a number",trigger:"manual"}),$("#inputCrossfade").popover("show"),$("#inputCrossfade").focus(),a=!1):$("#inputCrossfade").val(b)}$("#inputMixrampdb").is(":disabled")||(b=parseFloat($("#inputMixrampdb").val()),isNaN(b)?($("#inputMixrampdb").popover({content:"Must be a number",trigger:"manual"}),$("#inputMixrampdb").popover("show"), -$("#inputMixrampdb").focus(),a=!1):$("#inputMixrampdb").val(b));$("#inputMixrampdelay").is(":disabled")||("nan"==$("#inputMixrampdelay").val()&&$("#inputMixrampdelay").val("-1"),b=parseFloat($("#inputMixrampdelay").val()),isNaN(b)?($("#inputMixrampdelay").popover({content:"Must be a number, -1 to disable",trigger:"manual"}),$("#inputMixrampdelay").popover("show"),$("#inputMixrampdelay").focus(),a=!1):$("#inputMixrampdelay").val(b));1==a&&(sendAPI({cmd:"MPD_API_SET_SETTINGS",data:{consume:$("#btnconsume").hasClass("btn-success")? -1:0,random:$("#btnrandom").hasClass("btn-success")?1:0,single:$("#btnsingle").hasClass("btn-success")?1:0,repeat:$("#btnrepeat").hasClass("btn-success")?1:0,replaygain:$("#selectReplaygain").val(),crossfade:$("#inputCrossfade").val(),mixrampdb:$("#inputMixrampdb").val(),mixrampdelay:$("#inputMixrampdelay").val(),notificationWeb:$("#btnnotifyWeb").hasClass("btn-success")?1:0,notificationPage:$("#btnnotifyPage").hasClass("btn-success")?1:0}},getSettings),$("#settings").modal("hide"))} -function toggleoutput(a,b){sendAPI({cmd:"MPD_API_TOGGLE_OUTPUT",data:{output:b,state:$(a).hasClass("btn-success")?0:1}})}$("#trashmodebtns > button").on("click",function(a){$("#trashmodebtns").children("button").removeClass("btn-success").addClass("btn-secondary");$(this).removeClass("btn-secondary").addClass("btn-success")});$("#search > input").keypress(function(a){13==a.which&&$("#mainMenu > a").dropdown("toggle")}); -$("#search").submit(function(){app.goto("Search",void 0,void 0,app.current.page+"/Any Tag/"+$("#search > input").val());return!1});$("#search2").submit(function(){return!1});function addAllFromSearch(){if(2<=app.current.search.length){sendAPI({cmd:"MPD_API_SEARCH_ADD",data:{filter:app.current.filter,searchstr:+app.current.search}});var a=$("#SearchList >tbody >tr").length;showNotification("Added "+a+" songs from search","","","success")}} -$("#searchstr2").keyup(function(a){app.current.page=0;app.current.search=$(this).val();app.goto("Search",void 0,void 0,app.current.page+"/"+app.current.filter+"/"+app.current.search)}); -$("#searchtags2 > button").on("click",function(a){$("#searchtags2 > button").removeClass("btn-success").addClass("btn-secondary");$(this).removeClass("btn-secondary").addClass("btn-success");app.current.filter=$(this).text();app.goto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.search)}); +function confirmSettings(){var a=!0;if(!$("#inputCrossfade").is(":disabled")){var b=parseInt($("#inputCrossfade").val());isNaN(b)?(document.getElementById("inputCrossfade").classList.add("is-invalid"),a=!1):$("#inputCrossfade").val(b)}$("#inputMixrampdb").is(":disabled")||(b=parseFloat($("#inputMixrampdb").val()),isNaN(b)?(document.getElementById("inputMixrampdb").classList.add("is-invalid"),a=!1):$("#inputMixrampdb").val(b));$("#inputMixrampdelay").is(":disabled")||("nan"==$("#inputMixrampdelay").val()&& +$("#inputMixrampdelay").val("-1"),b=parseFloat($("#inputMixrampdelay").val()),isNaN(b)?(document.getElementById("inputMixrampdelay").classList.add("is-invalid"),a=!1):$("#inputMixrampdelay").val(b));1==a?(sendAPI({cmd:"MPD_API_SET_SETTINGS",data:{consume:$("#btnconsume").hasClass("active")?1:0,random:$("#btnrandom").hasClass("active")?1:0,single:$("#btnsingle").hasClass("active")?1:0,repeat:$("#btnrepeat").hasClass("active")?1:0,replaygain:$("#selectReplaygain").val(),crossfade:$("#inputCrossfade").val(), +mixrampdb:$("#inputMixrampdb").val(),mixrampdelay:$("#inputMixrampdelay").val(),notificationWeb:$("#btnnotifyWeb").hasClass("active")?1:0,notificationPage:$("#btnnotifyPage").hasClass("active")?1:0}},getSettings),$("#settings").modal("hide")):document.getElementById("settingsFrm").classList.add("was-validated")}function toggleoutput(a,b){sendAPI({cmd:"MPD_API_TOGGLE_OUTPUT",data:{output:b,state:$(a).hasClass("active")?0:1}})} +$("#trashmodebtns > button").on("click",function(a){$("#trashmodebtns").children("button").removeClass("active");$(this).addClass("active")});$("#search > input").keypress(function(a){13==a.which&&$("#mainMenu > a").dropdown("toggle")});$("#search").submit(function(){app.goto("Search",void 0,void 0,app.current.page+"/Any Tag/"+$("#search > input").val());return!1});$("#search2").submit(function(){return!1}); +function addAllFromSearch(){if(2<=app.current.search.length){sendAPI({cmd:"MPD_API_SEARCH_ADD",data:{filter:app.current.filter,searchstr:+app.current.search}});var a=$("#SearchList >tbody >tr").length;showNotification("Added "+a+" songs from search","","","success")}}$("#searchstr2").keyup(function(a){app.current.page=0;app.current.search=$(this).val();app.goto("Search",void 0,void 0,app.current.page+"/"+app.current.filter+"/"+app.current.search)}); +$("#searchtags2 > button").on("click",function(a){$("#searchtags2 > button").removeClass("active");$(this).removeClass("btn-secondary").addClass("active");app.current.filter=$(this).text();app.goto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.search)}); $("#searchqueuestr").keyup(function(a){app.current.page=0;app.current.search=$(this).val();app.goto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.search)}); -$("#searchqueuetag > button").on("click",function(a){$("#searchqueuetag > button").removeClass("btn-success").addClass("btn-secondary");$(this).removeClass("btn-secondary").addClass("btn-success");app.current.filter=$(this).text();$("#searchqueuetagdesc").text(app.current.filter);app.goto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.search)});$("#searchqueue").submit(function(){return!1});$("#searchqueue").submit(function(){return!1}); +$("#searchqueuetag > button").on("click",function(a){$("#searchqueuetag > button").removeClass("active");$(this).removeClass("btn-secondary").addClass("active");app.current.filter=$(this).text();$("#searchqueuetagdesc").text(app.current.filter);app.goto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.search)});$("#searchqueue").submit(function(){return!1});$("#searchqueue").submit(function(){return!1}); function scrollToTop(){document.body.scrollTop=0;document.documentElement.scrollTop=0}function gotoPage(a,b,c){switch(a){case "next":app.current.page+=settings.max_elements_per_page;break;case "prev":app.current.page-=settings.max_elements_per_page;0>=app.current.page&&(app.current.page=0);break;default:app.current.page=a}app.goto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.search);c.preventDefault()} function addStream(){0<$("#streamurl").val().length&&sendAPI({cmd:"MPD_API_ADD_TRACK",data:{uri:$("#streamurl").val()}});$("#streamurl").val("");$("#addstream").modal("hide")}function saveQueue(){0<$("#playlistname").val().length&&sendAPI({cmd:"MPD_API_SAVE_QUEUE",data:{plist:$("#playlistname").val()}});$("#savequeue").modal("hide")} function showNotification(a,b,c,d){1==settings.notificationWeb&&(b=new Notification(a,{icon:"assets/favicon.ico",body:b}),setTimeout(function(a){a.close()},3E3,b));1==settings.notificationPage&&$.notify({title:a,message:c},{type:d,offset:{y:60,x:20},template:''})}function notificationsSupported(){return"Notification"in window} -function songChange(a){if(last_song!=a.data.title+a.data.artist+a.data.album+a.data.uri+a.data.currentsongid){var b="",c="",d="myMPD: ";if("undefined"!=typeof a.data.uri&&0"+a.data.artist,d+=a.data.artist+" - ",$("#artist").text(a.data.artist)):$("#artist").text("");"undefined"!=typeof a.data.album&&0 button").removeClass("btn-success").addClass("btn-secondary");"0"==app.current.filter?($(a).text("Filter: #"),$(a+"Letters > button").each(function(){"#"==$(this).text()&&$(this).addClass("btn-success")})):"-"!=app.current.filter?($(a).text("Filter: "+app.current.filter),$(a+"Letters > button").each(function(){$(this).text()==app.current.filter&&$(this).addClass("btn-success")})):$(a).text("Filter")} +function songChange(a){if(last_song!=a.data.title+a.data.artist+a.data.album+a.data.uri+a.data.currentsongid){var b="",c="",d="myMPD: ";$("#album-cover").css("backgroundImage",'url("'+a.data.cover+'")');"undefined"!=typeof a.data.artist&&0"+a.data.artist,d+=a.data.artist+" - ",$("#artist").text(a.data.artist)):$("#artist").text("");"undefined"!=typeof a.data.album&&0 button").removeClass("active");"0"==app.current.filter?($(a).text("Filter: #"),$(a+"Letters > button").each(function(){"#"==$(this).text()&&$(this).addClass("active")})):"-"!=app.current.filter?($(a).text("Filter: "+app.current.filter),$(a+"Letters > button").each(function(){$(this).text()==app.current.filter&&$(this).addClass("active")})):$(a).text("Filter")} function add_filter(a){$(a).append('');$(a).append('');for(i=65;90>=i;i++){var b=String.fromCharCode(i);$(a).append('")}} function chVolume(a){a=volumeBar.slider("getValue")+a;0>a?a=0:100d?"0":""):"")+d+"\u2009m "+(10>a?"0":"")+a+"\u2009s"}function genId(a){return"id"+a.replace(/[^\w]/g,"")};

iI$b$SO$mK(oO| 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
KpdRJio-TJ(p=E$$nut(zCwGC0rwgsz# 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
RQa8* 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 Pv(sy@xEW&j7xn#LgnEZy diff --git a/src/mongoose-6.11/src/common/platforms/esp32/rom/rom_functions.S b/src/mongoose-6.11/src/common/platforms/esp32/rom/rom_functions.S deleted file mode 100644 index ec3b76f..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp32/rom/rom_functions.S +++ /dev/null @@ -1,1852 +0,0 @@ -// From esp-idf/components/esp32/ld/esp32.rom.ld: -// sort -k 5 esp32.rom.ld | perl -npe 's/=/,/; s/;//' - -PROVIDE ( __month_lengths , 0x3ff9609c ) -PROVIDE ( __ctype_ptr__ , 0x3ff96350 ) -PROVIDE ( _ctype_ , 0x3ff96354 ) -PROVIDE ( __sf_fake_stderr , 0x3ff96458 ) -PROVIDE ( __sf_fake_stdout , 0x3ff96478 ) -PROVIDE ( __sf_fake_stdin , 0x3ff96498 ) -PROVIDE ( __mb_cur_max , 0x3ff96530 ) -PROVIDE ( __wctomb , 0x3ff96540 ) -PROVIDE ( __nsau_data , 0x3ff96544 ) -PROVIDE ( __popcount_tab , 0x3ff96544 ) -PROVIDE ( lmp_ext_desc_tab , 0x3ff96d9c ) -PROVIDE ( lmp_desc_tab , 0x3ff96e6c ) -PROVIDE ( co_sca2ppm , 0x3ff971e8 ) -PROVIDE ( one_bits , 0x3ff971f8 ) -PROVIDE ( dbg_default_handler , 0x3ff97218 ) -PROVIDE ( ecc_Jacobian_InfinityPoint256 , 0x3ff972e8 ) -PROVIDE ( veryBigHexP256 , 0x3ff9736c ) -PROVIDE ( bigHexP256 , 0x3ff973bc ) -PROVIDE ( DebugE256SecretKey , 0x3ff973e8 ) -PROVIDE ( DebugE256PublicKey_y , 0x3ff97408 ) -PROVIDE ( DebugE256PublicKey_x , 0x3ff97428 ) -PROVIDE ( maxSecretKey_256 , 0x3ff97448 ) -PROVIDE ( BasePoint_y_256 , 0x3ff97468 ) -PROVIDE ( BasePoint_x_256 , 0x3ff97488 ) -PROVIDE ( hci_evt_le_desc_tab , 0x3ff974b4 ) -PROVIDE ( hci_evt_dbg_desc_tab , 0x3ff9750c ) -PROVIDE ( hci_evt_desc_tab , 0x3ff9751c ) -PROVIDE ( hci_cmd_desc_root_tab , 0x3ff976d4 ) -PROVIDE ( hci_cmd_desc_tab_vs , 0x3ff97714 ) -PROVIDE ( hci_cmd_desc_tab_le , 0x3ff97870 ) -PROVIDE ( hci_cmd_desc_tab_testing , 0x3ff97a98 ) -PROVIDE ( hci_cmd_desc_tab_stat_par , 0x3ff97ac8 ) -PROVIDE ( hci_cmd_desc_tab_info_par , 0x3ff97b1c ) -PROVIDE ( hci_cmd_desc_tab_ctrl_bb , 0x3ff97b70 ) -PROVIDE ( hci_cmd_desc_tab_lk_pol , 0x3ff97f3c ) -PROVIDE ( hci_cmd_desc_tab_lk_ctrl , 0x3ff97fc0 ) -PROVIDE ( lb_default_handler , 0x3ff982b8 ) -PROVIDE ( lc_default_handler , 0x3ff98648 ) -PROVIDE ( ld_pcm_settings_dft , 0x3ff98a0c ) -PROVIDE ( ld_acl_edr_sizes , 0x3ff98a14 ) -PROVIDE ( ld_acl_edr_types , 0x3ff98a22 ) -PROVIDE ( ld_acl_br_sizes , 0x3ff98a2a ) -PROVIDE ( ld_acl_br_types , 0x3ff98a36 ) -PROVIDE ( ld_sync_train_channels , 0x3ff98a3c ) -PROVIDE ( llc_default_handler , 0x3ff98b3c ) -PROVIDE ( lld_pdu_llcp_pk_desc_tab , 0x3ff98b68 ) -PROVIDE ( lld_pdu_adv_pk_desc_tab , 0x3ff98c70 ) -PROVIDE ( llm_local_data_len_values , 0x3ff98d1c ) -PROVIDE ( llm_local_le_states , 0x3ff98d28 ) -PROVIDE ( llm_local_le_feats , 0x3ff98d30 ) -PROVIDE ( llm_local_cmds , 0x3ff98d38 ) -PROVIDE ( llm_default_handler , 0x3ff98d80 ) -PROVIDE ( LLM_AA_CT2 , 0x3ff98d88 ) -PROVIDE ( LLM_AA_CT1 , 0x3ff98d8a ) -PROVIDE ( lm_default_handler , 0x3ff990e0 ) -PROVIDE ( lm_n_page_tab , 0x3ff990e8 ) -PROVIDE ( lm_local_supp_feats , 0x3ff990ee ) -PROVIDE ( rwip_coex_cfg , 0x3ff9914c ) -PROVIDE ( rwip_priority , 0x3ff99159 ) -PROVIDE ( exc_cause_table , 0x3ff991d0 ) -PROVIDE ( spi_modes , 0x3ff99270 ) -PROVIDE ( sha_blk_bits_bytes , 0x3ff99288 ) -PROVIDE ( sha_blk_hash_bytes , 0x3ff9928c ) -PROVIDE ( sha_blk_bits , 0x3ff99290 ) -PROVIDE ( dh_group18_prime , 0x3ff9a0dc ) -PROVIDE ( dh_group18_generator , 0x3ff9a4dc ) -PROVIDE ( dh_group17_prime , 0x3ff9a4dd ) -PROVIDE ( dh_group17_generator , 0x3ff9a7dd ) -PROVIDE ( dh_group16_prime , 0x3ff9a7de ) -PROVIDE ( dh_group16_generator , 0x3ff9a9de ) -PROVIDE ( dh_group15_prime , 0x3ff9a9df ) -PROVIDE ( dh_group15_generator , 0x3ff9ab5f ) -PROVIDE ( dh_group14_prime , 0x3ff9ab60 ) -PROVIDE ( dh_group14_generator , 0x3ff9ac60 ) -PROVIDE ( dh_group5_prime , 0x3ff9ac61 ) -PROVIDE ( dh_group5_generator , 0x3ff9ad21 ) -PROVIDE ( dh_group2_prime , 0x3ff9ad22 ) -PROVIDE ( dh_group2_generator , 0x3ff9ada2 ) -PROVIDE ( dh_group1_prime , 0x3ff9ada3 ) -PROVIDE ( dh_group1_generator , 0x3ff9ae03 ) -PROVIDE ( Xthal_intlevel , 0x3ff9c2b4 ) -PROVIDE ( syscall_table_ptr_app , 0x3ffae020 ) -PROVIDE ( syscall_table_ptr_pro , 0x3ffae024 ) -PROVIDE ( _tzname , 0x3ffae030 ) -PROVIDE ( _timezone , 0x3ffae0a0 ) -PROVIDE ( _daylight , 0x3ffae0a4 ) -PROVIDE ( _global_impure_ptr , 0x3ffae0b0 ) -PROVIDE ( environ , 0x3ffae0b4 ) -PROVIDE ( rom_phyFuns , 0x3ffae0c0 ) -PROVIDE ( g_phyFuns_instance , 0x3ffae0c4 ) -PROVIDE ( g_rom_flashchip , 0x3ffae270 ) -PROVIDE ( SPI_flashchip_data , 0x3ffae270 ) -PROVIDE ( dummy_len_plus , 0x3ffae290 ) -PROVIDE ( sig_matrix , 0x3ffae293 ) -PROVIDE ( r_btdm_option_data , 0x3ffae6e0 ) -PROVIDE ( _data_start_btdm , 0x3ffae6e0) -PROVIDE ( co_default_bdaddr , 0x3ffae704 ) -PROVIDE ( r_ip_funcs_p , 0x3ffae70c ) -PROVIDE ( r_ip_funcs , 0x3ffae710 ) -PROVIDE ( r_import_rf_phy_func_p , 0x3ffafd64 ) -PROVIDE ( r_modules_funcs_p , 0x3ffafd68 ) -PROVIDE ( r_modules_funcs , 0x3ffafd6c ) -PROVIDE ( _data_end_btdm , 0x3ffaff10) -PROVIDE ( _bss_start_btdm , 0x3ffb8000) -PROVIDE ( co_null_bdaddr , 0x3ffb80e0 ) -PROVIDE ( ld_sco_env , 0x3ffb824c ) -PROVIDE ( ld_acl_env , 0x3ffb8258 ) -PROVIDE ( ld_bcst_acl_env , 0x3ffb8274 ) -PROVIDE ( ld_csb_rx_env , 0x3ffb8278 ) -PROVIDE ( ld_csb_tx_env , 0x3ffb827c ) -PROVIDE ( ld_fm_env , 0x3ffb8284 ) -PROVIDE ( ld_inq_env , 0x3ffb82e4 ) -PROVIDE ( ld_iscan_env , 0x3ffb82e8 ) -PROVIDE ( ld_page_env , 0x3ffb82f0 ) -PROVIDE ( ld_pca_env , 0x3ffb82f4 ) -PROVIDE ( ld_pscan_env , 0x3ffb8308 ) -PROVIDE ( ld_sched_env , 0x3ffb830c ) -PROVIDE ( ld_sscan_env , 0x3ffb832c ) -PROVIDE ( ld_strain_env , 0x3ffb8330 ) -PROVIDE ( ld_active_ch_map , 0x3ffb8334 ) -PROVIDE ( r_import_rf_phy_func , 0x3ffb8354 ) -PROVIDE ( r_plf_funcs_p , 0x3ffb8360 ) -PROVIDE ( RFPLL_ICP_TABLE , 0x3ffb8b7c ) -PROVIDE ( bt_util_buf_env , 0x3ffb8bd4 ) -PROVIDE ( sw_to_hw , 0x3ffb8d40 ) -PROVIDE ( dbg_state , 0x3ffb8d5d ) -PROVIDE ( ecc_env , 0x3ffb8d60 ) -PROVIDE ( em_buf_env , 0x3ffb8d74 ) -PROVIDE ( hci_fc_env , 0x3ffb9340 ) -PROVIDE ( hci_env , 0x3ffb9350 ) -PROVIDE ( ke_env , 0x3ffb93cc ) -PROVIDE ( lb_env , 0x3ffb9424 ) -PROVIDE ( lb_state , 0x3ffb94e8 ) -PROVIDE ( lc_env , 0x3ffb94ec ) -PROVIDE ( lc_state , 0x3ffb9508 ) -PROVIDE ( ld_env , 0x3ffb9510 ) -PROVIDE ( ld_env , 0x3ffb9510 ) -PROVIDE ( ld_sched_params , 0x3ffb96c0 ) -PROVIDE ( ld_sched_params , 0x3ffb96c0 ) -PROVIDE ( llc_env , 0x3ffb96d0 ) -PROVIDE ( llc_state , 0x3ffb96f8 ) -PROVIDE ( lld_evt_env , 0x3ffb9704 ) -PROVIDE ( llm_le_env , 0x3ffb976c ) -PROVIDE ( llm_state , 0x3ffb985c ) -PROVIDE ( lm_env , 0x3ffb9860 ) -PROVIDE ( lm_state , 0x3ffb9a1c ) -PROVIDE ( rwip_rf , 0x3ffbdb28 ) -PROVIDE ( _bss_end_btdm , 0x3ffbff70) -PROVIDE ( gpio_pending_mask , 0x3ffe0038 ) -PROVIDE ( app_gpio_arg , 0x3ffe003c ) -PROVIDE ( app_gpio_handler , 0x3ffe0040 ) -PROVIDE ( gpio_pending_mask_high , 0x3ffe0044 ) -PROVIDE ( gTxMsg , 0x3ffe0050 ) -PROVIDE ( RecvBuff , 0x3ffe009c ) -PROVIDE ( UartDev , 0x3ffe019c ) -PROVIDE ( ets_readySet_ , 0x3ffe01f0 ) -PROVIDE ( ets_intr_count , 0x3ffe03fc ) -PROVIDE ( user_code_start , 0x3ffe0400 ) -PROVIDE ( ets_startup_callback , 0x3ffe0404 ) -PROVIDE ( debug_timer , 0x3ffe042c ) -PROVIDE ( debug_timerfn , 0x3ffe0430 ) -PROVIDE ( _xtos_exc_handler_table , 0x3ffe0448 ) -PROVIDE ( _xtos_c_handler_table , 0x3ffe0548 ) -PROVIDE ( _Pri_4_HandlerAddress , 0x3ffe0648 ) -PROVIDE ( _Pri_5_HandlerAddress , 0x3ffe064c ) -PROVIDE ( _xtos_enabled , 0x3ffe0650 ) -PROVIDE ( _xtos_intstruct , 0x3ffe0650 ) -PROVIDE ( _xtos_vpri_enabled , 0x3ffe0654 ) -PROVIDE ( _xtos_interrupt_table , 0x3ffe0658 ) -PROVIDE ( _xtos_interrupt_mask_table , 0x3ffe0758 ) -PROVIDE ( _stack_sentry , 0x3ffe1320 ) -PROVIDE ( __stack , 0x3ffe3f20 ) -PROVIDE ( _stack_sentry_app , 0x3ffe5230 ) -PROVIDE ( __stack_app , 0x3ffe7e30 ) -PROVIDE ( _WindowOverflow4 , 0x40000000 ) -PROVIDE ( _xtos_alloca_handler , 0x40000010 ) -PROVIDE ( _WindowUnderflow4 , 0x40000040 ) -PROVIDE ( _WindowOverflow8 , 0x40000080 ) -PROVIDE ( _WindowUnderflow8 , 0x400000c0 ) -PROVIDE ( _WindowOverflow12 , 0x40000100 ) -PROVIDE ( _WindowUnderflow12 , 0x40000140 ) -PROVIDE ( _Level2Vector , 0x40000180 ) -PROVIDE ( _Level3Vector , 0x400001c0 ) -PROVIDE ( _Level4Vector , 0x40000200 ) -PROVIDE ( _Level5Vector , 0x40000240 ) -PROVIDE ( _DebugExceptionVector , 0x40000280 ) -PROVIDE ( _NMIExceptionVector , 0x400002c0 ) -PROVIDE ( _KernelExceptionVector , 0x40000300 ) -PROVIDE ( _UserExceptionVector , 0x40000340 ) -PROVIDE ( _DoubleExceptionVector , 0x400003c0 ) -PROVIDE ( _ResetVector , 0x40000400 ) -PROVIDE ( _ResetHandler , 0x40000450 ) -PROVIDE ( _stext , 0x40000560 ) -PROVIDE ( _start , 0x40000704 ) -PROVIDE ( _xtos_set_exception_handler , 0x4000074c ) -PROVIDE ( _xtos_syscall_handler , 0x40000790 ) -PROVIDE ( _SyscallException , 0x400007cf ) -PROVIDE ( _xtos_l1int_handler , 0x40000814 ) -PROVIDE ( _LevelOneInterrupt , 0x40000835 ) -PROVIDE ( _xtos_restore_intlevel , 0x40000928 ) -PROVIDE ( _xtos_set_vpri , 0x40000934 ) -PROVIDE ( _Level2FromVector , 0x40000954 ) -PROVIDE ( _Level3FromVector , 0x40000a28 ) -PROVIDE ( _Level4FromVector , 0x40000af8 ) -PROVIDE ( _Level5FromVector , 0x40000c68 ) -PROVIDE ( _xtos_cause3_handler , 0x40000dd8 ) -PROVIDE ( _xtos_c_wrapper_handler , 0x40000de8 ) -PROVIDE ( _GeneralException , 0x40000e14 ) -PROVIDE ( creat , 0x40000e8c ) -PROVIDE ( _isatty_r , 0x40000ea0 ) -PROVIDE ( asctime_r , 0x40000ec8 ) -PROVIDE ( isalnum , 0x40000f04 ) -PROVIDE ( isalpha , 0x40000f18 ) -PROVIDE ( isblank , 0x40000f2c ) -PROVIDE ( iscntrl , 0x40000f50 ) -PROVIDE ( isdigit , 0x40000f64 ) -PROVIDE ( islower , 0x40000f78 ) -PROVIDE ( isgraph , 0x40000f94 ) -PROVIDE ( isprint , 0x40000fa8 ) -PROVIDE ( ispunct , 0x40000fc0 ) -PROVIDE ( isspace , 0x40000fd4 ) -PROVIDE ( isupper , 0x40000fe8 ) -PROVIDE ( srand , 0x40001004 ) -PROVIDE ( rand , 0x40001058 ) -PROVIDE ( rand_r , 0x400010d4 ) -PROVIDE ( __sread , 0x40001118 ) -PROVIDE ( __seofread , 0x40001148 ) -PROVIDE ( __swrite , 0x40001150 ) -PROVIDE ( __sseek , 0x40001184 ) -PROVIDE ( __sclose , 0x400011b8 ) -PROVIDE ( strcasecmp , 0x400011cc ) -PROVIDE ( strcasestr , 0x40001210 ) -PROVIDE ( strcmp , 0x40001274 ) -PROVIDE ( strcoll , 0x40001398 ) -PROVIDE ( strcpy , 0x400013ac ) -PROVIDE ( strdup , 0x4000143c ) -PROVIDE ( _strdup_r , 0x40001450 ) -PROVIDE ( strlcat , 0x40001470 ) -PROVIDE ( strlen , 0x400014c0 ) -PROVIDE ( strlwr , 0x40001524 ) -PROVIDE ( strncasecmp , 0x40001550 ) -PROVIDE ( strncpy , 0x400015d4 ) -PROVIDE ( strndup , 0x400016b0 ) -PROVIDE ( _strndup_r , 0x400016c4 ) -PROVIDE ( strrchr , 0x40001708 ) -PROVIDE ( strsep , 0x40001734 ) -PROVIDE ( strupr , 0x4000174c ) -PROVIDE ( close , 0x40001778 ) -PROVIDE ( open , 0x4000178c ) -PROVIDE ( read , 0x400017dc ) -PROVIDE ( sbrk , 0x400017f4 ) -PROVIDE ( times , 0x40001808 ) -PROVIDE ( write , 0x4000181c ) -PROVIDE ( __get_current_time_locale , 0x40001834 ) -PROVIDE ( __time_load_locale , 0x4000183c ) -PROVIDE ( time , 0x40001844 ) -PROVIDE ( tolower , 0x40001868 ) -PROVIDE ( toupper , 0x40001884 ) -PROVIDE ( __tzcalc_limits , 0x400018a0 ) -PROVIDE ( __tz_lock , 0x40001a04 ) -PROVIDE ( __tz_unlock , 0x40001a10 ) -PROVIDE ( tzset , 0x40001a1c ) -PROVIDE ( _tzset_r , 0x40001a28 ) -PROVIDE ( _cleanup_r , 0x40001d48 ) -PROVIDE ( __sfmoreglue , 0x40001dc8 ) -PROVIDE ( _cleanup , 0x40001df8 ) -PROVIDE ( __sfp_lock_acquire , 0x40001e08 ) -PROVIDE ( __sfp_lock_release , 0x40001e14 ) -PROVIDE ( __sinit_lock_acquire , 0x40001e20 ) -PROVIDE ( __sinit_lock_release , 0x40001e2c ) -PROVIDE ( __sinit , 0x40001e38 ) -PROVIDE ( __sfp , 0x40001e90 ) -PROVIDE ( __fp_lock_all , 0x40001f1c ) -PROVIDE ( __fp_unlock_all , 0x40001f30 ) -PROVIDE ( _findenv_r , 0x40001f44 ) -PROVIDE ( _getenv_r , 0x40001fbc ) -PROVIDE ( __gettzinfo , 0x40001fcc ) -PROVIDE ( __env_lock , 0x40001fd4 ) -PROVIDE ( __env_unlock , 0x40001fe0 ) -PROVIDE ( _fclose_r , 0x40001fec ) -PROVIDE ( fclose , 0x400020ac ) -PROVIDE ( __negsf2 , 0x400020c0 ) -PROVIDE ( __addsf3 , 0x400020e8 ) -PROVIDE ( __subsf3 , 0x400021d0 ) -PROVIDE ( __divsf3 , 0x4000234c ) -PROVIDE ( __fixsfsi , 0x4000240c ) -PROVIDE ( __fixsfdi , 0x4000244c ) -PROVIDE ( __fixunssfsi , 0x400024ac ) -PROVIDE ( __fixunssfdi , 0x40002504 ) -PROVIDE ( __adddf3 , 0x40002590 ) -PROVIDE ( __subdf3 , 0x400026e4 ) -PROVIDE ( __divdf3 , 0x40002954 ) -PROVIDE ( __fixdfsi , 0x40002a78 ) -PROVIDE ( __fixdfdi , 0x40002ac4 ) -PROVIDE ( __fixunsdfsi , 0x40002b30 ) -PROVIDE ( __truncdfsf2 , 0x40002b90 ) -PROVIDE ( __extendsfdf2 , 0x40002c34 ) -PROVIDE ( __addvsi3 , 0x40002c98 ) -PROVIDE ( __addvdi3 , 0x40002cbc ) -PROVIDE ( __subvsi3 , 0x40002cf8 ) -PROVIDE ( __subvdi3 , 0x40002d20 ) -PROVIDE ( __mulvsi3 , 0x40002d60 ) -PROVIDE ( __mulvdi3 , 0x40002d78 ) -PROVIDE ( __negvsi2 , 0x40002e78 ) -PROVIDE ( __negvdi2 , 0x40002e98 ) -PROVIDE ( __popcountsi2 , 0x40002ed0 ) -PROVIDE ( __popcountdi2 , 0x40002ef8 ) -PROVIDE ( __paritysi2 , 0x40002f3c ) -PROVIDE ( rom_phy_disable_agc , 0x40002f6c ) -PROVIDE ( rom_phy_enable_agc , 0x40002f88 ) -PROVIDE ( rom_disable_agc , 0x40002fa4 ) -PROVIDE ( rom_enable_agc , 0x40002fcc ) -PROVIDE ( rom_phy_disable_cca , 0x40003000 ) -PROVIDE ( rom_phy_enable_cca , 0x4000302c ) -PROVIDE ( rom_pow_usr , 0x40003044 ) -PROVIDE ( rom_set_loopback_gain , 0x40003060 ) -PROVIDE ( rom_set_cal_rxdc , 0x400030b8 ) -PROVIDE ( rom_loopback_mode_en , 0x400030f8 ) -PROVIDE ( rom_get_data_sat , 0x4000312c ) -PROVIDE ( rom_set_pbus_mem , 0x400031a4 ) -PROVIDE ( rom_write_gain_mem , 0x4000348c ) -PROVIDE ( rom_rx_gain_force , 0x4000351c ) -PROVIDE ( rom_set_txclk_en , 0x40003564 ) -PROVIDE ( rom_set_rxclk_en , 0x40003594 ) -PROVIDE ( rom_start_tx_tone_step , 0x400035d0 ) -PROVIDE ( rom_start_tx_tone , 0x400036b4 ) -PROVIDE ( rom_clk_force_on_vit , 0x40003710 ) -PROVIDE ( rom_bb_rx_ht20_cen_bcov_en , 0x40003734 ) -PROVIDE ( rom_bb_tx_ht20_cen , 0x40003760 ) -PROVIDE ( rom_spur_reg_write_one_tone , 0x400037f0 ) -PROVIDE ( rom_spur_coef_cfg , 0x40003ac8 ) -PROVIDE ( rom_bb_wdg_test_en , 0x40003b70 ) -PROVIDE ( rom_bb_bss_cbw40_dig , 0x40003bac ) -PROVIDE ( rom_noise_floor_auto_set , 0x40003bdc ) -PROVIDE ( rom_phy_get_noisefloor , 0x40003c2c ) -PROVIDE ( rom_check_noise_floor , 0x40003c78 ) -PROVIDE ( rom_set_noise_floor , 0x40003d48 ) -PROVIDE ( rom_chip_v7_rx_rifs_en , 0x40003d90 ) -PROVIDE ( rom_rtc_mem_backup , 0x40003db4 ) -PROVIDE ( rom_rtc_mem_recovery , 0x40003df4 ) -PROVIDE ( rom_gen_rx_gain_table , 0x40003e3c ) -PROVIDE ( rom_stop_tx_tone , 0x40003f98 ) -PROVIDE ( rom_bb_bss_bw_40_en , 0x4000401c ) -PROVIDE ( rom_mhz2ieee , 0x4000404c ) -PROVIDE ( rom_cbw2040_cfg , 0x400040b0 ) -PROVIDE ( phy_get_romfuncs , 0x40004100 ) -PROVIDE ( rom_chip_i2c_readReg , 0x40004110 ) -PROVIDE ( rom_i2c_readReg , 0x40004148 ) -PROVIDE ( rom_chip_i2c_writeReg , 0x40004168 ) -PROVIDE ( rom_i2c_writeReg , 0x400041a4 ) -PROVIDE ( rom_i2c_readReg_Mask , 0x400041c0 ) -PROVIDE ( rom_i2c_writeReg_Mask , 0x400041fc ) -PROVIDE ( rom_pbus_force_mode , 0x40004270 ) -PROVIDE ( rom_pbus_rd_addr , 0x40004334 ) -PROVIDE ( rom_pbus_rd_shift , 0x40004374 ) -PROVIDE ( rom_pbus_force_test , 0x400043c0 ) -PROVIDE ( rom_pbus_rd , 0x40004414 ) -PROVIDE ( rom_pbus_debugmode , 0x40004458 ) -PROVIDE ( rom_pbus_workmode , 0x4000446c ) -PROVIDE ( rom_pbus_set_rxgain , 0x40004480 ) -PROVIDE ( rom_pbus_xpd_rx_off , 0x40004508 ) -PROVIDE ( rom_pbus_xpd_rx_on , 0x4000453c ) -PROVIDE ( rom_pbus_xpd_tx_off , 0x40004590 ) -PROVIDE ( rom_pbus_xpd_tx_on , 0x400045e0 ) -PROVIDE ( rom_pbus_set_dco , 0x40004638 ) -PROVIDE ( rom_rfpll_reset , 0x40004680 ) -PROVIDE ( rom_restart_cal , 0x400046e0 ) -PROVIDE ( rom_write_rfpll_sdm , 0x40004740 ) -PROVIDE ( rom_wait_rfpll_cal_end , 0x400047a8 ) -PROVIDE ( rom_rfpll_set_freq , 0x400047f8 ) -PROVIDE ( rom_set_channel_freq , 0x40004880 ) -PROVIDE ( rom_phy_freq_correct , 0x40004b44 ) -PROVIDE ( rom_set_rf_freq_offset , 0x40004ca8 ) -PROVIDE ( rom_chip_v7_rx_init , 0x40004cec ) -PROVIDE ( rom_chip_v7_tx_init , 0x40004d18 ) -PROVIDE ( rom_chip_v7_bt_init , 0x40004d8c ) -PROVIDE ( rom_txbbgain_to_index , 0x40004dc0 ) -PROVIDE ( rom_index_to_txbbgain , 0x40004df8 ) -PROVIDE ( rom_txdc_cal_init , 0x40004e10 ) -PROVIDE ( rom_txdc_cal_v70 , 0x40004ea4 ) -PROVIDE ( rom_en_pwdet , 0x4000506c ) -PROVIDE ( rom_txcal_work_mode , 0x4000510c ) -PROVIDE ( rom_txiq_set_reg , 0x40005154 ) -PROVIDE ( rom_read_sar_dout , 0x400051c0 ) -PROVIDE ( rom_get_fm_sar_dout , 0x40005204 ) -PROVIDE ( rom_txtone_linear_pwr , 0x40005290 ) -PROVIDE ( rom_txiq_get_mis_pwr , 0x400052dc ) -PROVIDE ( rom_txiq_cover , 0x4000538c ) -PROVIDE ( rom_abs_temp , 0x400054f0 ) -PROVIDE ( rom_iq_est_enable , 0x40005514 ) -PROVIDE ( rom_iq_est_disable , 0x40005590 ) -PROVIDE ( rom_dc_iq_est , 0x400055c8 ) -PROVIDE ( rom_pbus_rx_dco_cal , 0x40005620 ) -PROVIDE ( rom_rxiq_get_mis , 0x400058e4 ) -PROVIDE ( rom_rxiq_set_reg , 0x40005a00 ) -PROVIDE ( rom_rxiq_cover_mg_mp , 0x40005a68 ) -PROVIDE ( rom_rfcal_rxiq , 0x40005b4c ) -PROVIDE ( rom_get_rfcal_rxiq_data , 0x40005bbc ) -PROVIDE ( rom_set_chan_cal_interp , 0x40005ce0 ) -PROVIDE ( rom_set_txcap_reg , 0x40005d50 ) -PROVIDE ( rom_rfcal_txcap , 0x40005dec ) -PROVIDE ( rom_linear_to_db , 0x40005f64 ) -PROVIDE ( rom_get_power_db , 0x40005fc8 ) -PROVIDE ( rom_meas_tone_pwr_db , 0x40006004 ) -PROVIDE ( rom_rfcal_pwrctrl , 0x40006058 ) -PROVIDE ( rom_tx_atten_set_interp , 0x400061cc ) -PROVIDE ( rom_target_power_add_backoff , 0x40006268 ) -PROVIDE ( rom_get_rf_gain_qdb , 0x40006290 ) -PROVIDE ( rom_correct_rf_ana_gain , 0x400062a8 ) -PROVIDE ( rom_phy_get_vdd33 , 0x4000642c ) -PROVIDE ( rom_get_sar_dout , 0x40006564 ) -PROVIDE ( rom_get_pwctrl_correct , 0x400065d4 ) -PROVIDE ( rom_tx_pwctrl_bg_init , 0x4000662c ) -PROVIDE ( ets_set_idle_cb , 0x40006674 ) -PROVIDE ( ets_task , 0x40006688 ) -PROVIDE ( ets_run , 0x400066bc ) -PROVIDE ( ets_post , 0x4000673c ) -PROVIDE ( ets_intr_lock , 0x400067b0 ) -PROVIDE ( ets_intr_unlock , 0x400067c4 ) -PROVIDE ( ets_waiti0 , 0x400067d8 ) -PROVIDE ( ets_isr_attach , 0x400067ec ) -PROVIDE ( ets_isr_mask , 0x400067fc ) -PROVIDE ( ets_isr_unmask , 0x40006808 ) -PROVIDE ( intr_matrix_set , 0x4000681c ) -PROVIDE ( ets_set_user_start , 0x4000687c ) -PROVIDE ( ets_set_startup_callback , 0x4000688c ) -PROVIDE ( ets_set_appcpu_boot_addr , 0x4000689c ) -PROVIDE ( check_pos , 0x400068b8 ) -PROVIDE ( ets_unpack_flash_code_legacy , 0x4000694c ) -PROVIDE ( ets_unpack_flash_code , 0x40007018 ) -PROVIDE ( rom_main , 0x400076c4 ) -PROVIDE ( ets_install_putc1 , 0x40007d18 ) -PROVIDE ( ets_install_uart_printf , 0x40007d28 ) -PROVIDE ( ets_install_putc2 , 0x40007d38 ) -PROVIDE ( ets_printf , 0x40007d54 ) -PROVIDE ( calc_rtc_memory_crc , 0x40008170 ) -PROVIDE ( rtc_get_reset_reason , 0x400081d4 ) -PROVIDE ( rtc_get_wakeup_cause , 0x400081f4 ) -PROVIDE ( set_rtc_memory_crc , 0x40008208 ) -PROVIDE ( rtc_boot_control , 0x4000821c ) -PROVIDE ( software_reset , 0x4000824c ) -PROVIDE ( software_reset_cpu , 0x40008264 ) -PROVIDE ( rtc_select_apb_bridge , 0x40008288 ) -PROVIDE ( ets_timer_setfn , 0x40008350 ) -PROVIDE ( ets_timer_arm , 0x40008368 ) -PROVIDE ( ets_timer_arm_us , 0x400083ac ) -PROVIDE ( ets_timer_disarm , 0x400083ec ) -PROVIDE ( ets_timer_done , 0x40008428 ) -PROVIDE ( ets_timer_handler_isr , 0x40008454 ) -PROVIDE ( ets_timer_init , 0x400084e8 ) -PROVIDE ( ets_delay_us , 0x40008534 ) -PROVIDE ( ets_update_cpu_frequency , 0x40008550 ) -PROVIDE ( ets_get_cpu_frequency , 0x4000855c ) -PROVIDE ( ets_get_xtal_scale , 0x4000856c ) -PROVIDE ( ets_get_detected_xtal_freq , 0x40008588 ) -PROVIDE ( ets_efuse_read_op , 0x40008600 ) -PROVIDE ( ets_efuse_program_op , 0x40008628 ) -PROVIDE ( ets_efuse_get_spiconfig , 0x40008658 ) -PROVIDE ( ets_efuse_get_8M_clock , 0x40008710 ) -PROVIDE ( UartConnCheck , 0x40008738 ) -PROVIDE ( FlashDwnLdStartMsgProc , 0x40008820 ) -PROVIDE ( FilePacketSendReqMsgProc , 0x40008860 ) -PROVIDE ( FlashDwnLdStopReqMsgProc , 0x400088ec ) -PROVIDE ( FlashDwnLdParamCfgMsgProc , 0x4000891c ) -PROVIDE ( MemDwnLdStartMsgProc , 0x40008948 ) -PROVIDE ( MemPacketSendReqMsgProc , 0x40008978 ) -PROVIDE ( MemDwnLdStopReqMsgProc , 0x400089dc ) -PROVIDE ( UartConnectProc , 0x40008a04 ) -PROVIDE ( UartRegWriteProc , 0x40008a14 ) -PROVIDE ( UartRegReadProc , 0x40008a58 ) -PROVIDE ( UartSpiAttachProc , 0x40008a6c ) -PROVIDE ( UartSpiReadProc , 0x40008a80 ) -PROVIDE ( UartSetBaudProc , 0x40008aac ) -PROVIDE ( FlashDwnLdDeflatedStartMsgProc , 0x40008ad8 ) -PROVIDE ( FilePacketSendDeflatedReqMsgProc , 0x40008b24 ) -PROVIDE ( FlashDwnLdStopDeflatedReqMsgProc , 0x40008c18 ) -PROVIDE ( VerifyFlashMd5Proc , 0x40008c44 ) -PROVIDE ( UartDwnLdProc , 0x40008ce8 ) -PROVIDE ( uart_rx_intr_handler , 0x40008f4c ) -PROVIDE ( uartAttach , 0x40008fd0 ) -PROVIDE ( uart_tx_switch , 0x40009028 ) -PROVIDE ( uart_baudrate_detect , 0x40009034 ) -PROVIDE ( uart_div_modify , 0x400090cc ) -PROVIDE ( Uart_Init , 0x40009120 ) -PROVIDE ( uart_tx_one_char , 0x40009200 ) -PROVIDE ( uart_tx_one_char2 , 0x4000922c ) -PROVIDE ( uart_tx_flush , 0x40009258 ) -PROVIDE ( uart_tx_wait_idle , 0x40009278 ) -PROVIDE ( uart_rx_one_char_block , 0x400092a4 ) -PROVIDE ( uart_rx_one_char , 0x400092d0 ) -PROVIDE ( UartRxString , 0x400092fc ) -PROVIDE ( send_packet , 0x40009340 ) -PROVIDE ( SendMsg , 0x40009384 ) -PROVIDE ( uart_rx_readbuff , 0x40009394 ) -PROVIDE ( uart_buff_switch , 0x400093c0 ) -PROVIDE ( recv_packet , 0x40009424 ) -PROVIDE ( RcvMsg , 0x4000954c ) -PROVIDE ( UartGetCmdLn , 0x40009564 ) -PROVIDE ( GetUartDevice , 0x40009598 ) -PROVIDE ( mmu_init , 0x400095a4 ) -PROVIDE ( cache_flash_mmu_set , 0x400095e0 ) -PROVIDE ( cache_sram_mmu_set , 0x400097f4 ) -PROVIDE ( Cache_Read_Init , 0x40009950 ) -PROVIDE ( Cache_Flush , 0x40009a14 ) -PROVIDE ( Cache_Read_Enable , 0x40009a84 ) -PROVIDE ( Cache_Read_Disable , 0x40009ab8 ) -PROVIDE ( gpio_output_set , 0x40009b24 ) -PROVIDE ( gpio_output_set_high , 0x40009b5c ) -PROVIDE ( gpio_input_get , 0x40009b88 ) -PROVIDE ( gpio_input_get_high , 0x40009b9c ) -PROVIDE ( gpio_register_set , 0x40009bbc ) -PROVIDE ( gpio_init , 0x40009c20 ) -PROVIDE ( gpio_register_get , 0x40009cbc ) -PROVIDE ( gpio_intr_pending , 0x40009cec ) -PROVIDE ( gpio_intr_pending_high , 0x40009cf8 ) -PROVIDE ( gpio_pin_intr_state_set , 0x40009d04 ) -PROVIDE ( gpio_intr_ack , 0x40009dd4 ) -PROVIDE ( gpio_intr_ack_high , 0x40009e1c ) -PROVIDE ( gpio_intr_handler_register , 0x40009e6c ) -PROVIDE ( gpio_pin_wakeup_enable , 0x40009e7c ) -PROVIDE ( gpio_pin_wakeup_disable , 0x40009eb0 ) -PROVIDE ( gpio_matrix_in , 0x40009edc ) -PROVIDE ( gpio_matrix_out , 0x40009f0c ) -PROVIDE ( gpio_pad_select_gpio , 0x40009fdc ) -PROVIDE ( gpio_pad_set_drv , 0x4000a11c ) -PROVIDE ( gpio_pad_pullup , 0x4000a22c ) -PROVIDE ( gpio_pad_pulldown , 0x4000a348 ) -PROVIDE ( gpio_pad_unhold , 0x4000a484 ) -PROVIDE ( gpio_pad_hold , 0x4000a734 ) -PROVIDE ( lldesc_build_chain , 0x4000a850 ) -PROVIDE ( lldesc_num2link , 0x4000a948 ) -PROVIDE ( lldesc_set_owner , 0x4000a974 ) -PROVIDE ( roundup2 , 0x4000ab7c ) -PROVIDE ( multofup , 0x4000ab8c ) -PROVIDE ( sip_alloc_to_host_evt , 0x4000ab9c ) -PROVIDE ( sip_to_host_evt_send_done , 0x4000ac04 ) -PROVIDE ( sip_reclaim_tx_data_pkt , 0x4000ad5c ) -PROVIDE ( sip_reclaim_from_host_cmd , 0x4000adbc ) -PROVIDE ( sip_install_rx_ctrl_cb , 0x4000ae10 ) -PROVIDE ( sip_install_rx_data_cb , 0x4000ae20 ) -PROVIDE ( sip_get_state , 0x4000ae2c ) -PROVIDE ( sip_init_attach , 0x4000ae58 ) -PROVIDE ( sip_post_init , 0x4000aed8 ) -PROVIDE ( sip_to_host_chain_append , 0x4000aef8 ) -PROVIDE ( sip_send , 0x4000af54 ) -PROVIDE ( sip_get_ptr , 0x4000b34c ) -PROVIDE ( sip_after_tx_complete , 0x4000b358 ) -PROVIDE ( sip_is_active , 0x4000b3c0 ) -PROVIDE ( slc_has_pkt_to_host , 0x4000b5fc ) -PROVIDE ( slc_reattach , 0x4000b62c ) -PROVIDE ( slc_enable , 0x4000b64c ) -PROVIDE ( slc_send_to_host_chain , 0x4000b6a0 ) -PROVIDE ( slc_to_host_chain_recycle , 0x4000b758 ) -PROVIDE ( slc_from_host_chain_fetch , 0x4000b7e8 ) -PROVIDE ( slc_set_host_io_max_window , 0x4000b89c ) -PROVIDE ( slc_init_attach , 0x4000b918 ) -PROVIDE ( slc_init_credit , 0x4000badc ) -PROVIDE ( slc_add_credits , 0x4000baf4 ) -PROVIDE ( slc_from_host_chain_recycle , 0x4000bb10 ) -PROVIDE ( abort , 0x4000bba4 ) -PROVIDE ( _malloc_r , 0x4000bbb4 ) -PROVIDE ( _free_r , 0x4000bbcc ) -PROVIDE ( _realloc_r , 0x4000bbe0 ) -PROVIDE ( _calloc_r , 0x4000bbf8 ) -PROVIDE ( _system_r , 0x4000bc10 ) -PROVIDE ( _rename_r , 0x4000bc28 ) -PROVIDE ( _times_r , 0x4000bc40 ) -PROVIDE ( _gettimeofday_r , 0x4000bc58 ) -PROVIDE ( _raise_r , 0x4000bc70 ) -PROVIDE ( _unlink_r , 0x4000bc84 ) -PROVIDE ( _link_r , 0x4000bc9c ) -PROVIDE ( _stat_r , 0x4000bcb4 ) -PROVIDE ( _fstat_r , 0x4000bccc ) -PROVIDE ( _sbrk_r , 0x4000bce4 ) -PROVIDE ( _getpid_r , 0x4000bcfc ) -PROVIDE ( _kill_r , 0x4000bd10 ) -PROVIDE ( _exit_r , 0x4000bd28 ) -PROVIDE ( _close_r , 0x4000bd3c ) -PROVIDE ( _open_r , 0x4000bd54 ) -PROVIDE ( _write_r , 0x4000bd70 ) -PROVIDE ( _lseek_r , 0x4000bd8c ) -PROVIDE ( _read_r , 0x4000bda8 ) -PROVIDE ( _lock_init , 0x4000bdc4 ) -PROVIDE ( _lock_init_recursive , 0x4000bdd8 ) -PROVIDE ( _lock_close , 0x4000bdec ) -PROVIDE ( _lock_close_recursive , 0x4000be00 ) -PROVIDE ( _lock_acquire , 0x4000be14 ) -PROVIDE ( _lock_acquire_recursive , 0x4000be28 ) -PROVIDE ( _lock_try_acquire , 0x4000be3c ) -PROVIDE ( _lock_try_acquire_recursive , 0x4000be50 ) -PROVIDE ( _lock_release , 0x4000be64 ) -PROVIDE ( _lock_release_recursive , 0x4000be78 ) -PROVIDE ( __getreent , 0x4000be8c ) -PROVIDE ( malloc , 0x4000bea0 ) -PROVIDE ( free , 0x4000beb8 ) -PROVIDE ( realloc , 0x4000becc ) -PROVIDE ( calloc , 0x4000bee4 ) -PROVIDE ( _printf_float , 0x4000befc ) -PROVIDE ( _scanf_float , 0x4000bf18 ) -PROVIDE ( _xtos_set_interrupt_handler_arg , 0x4000bf34 ) -PROVIDE ( _xtos_set_interrupt_handler , 0x4000bf78 ) -PROVIDE ( _xtos_ints_on , 0x4000bf88 ) -PROVIDE ( _xtos_ints_off , 0x4000bfac ) -PROVIDE ( _xtos_p_none , 0x4000bfd4 ) -PROVIDE ( _xtos_set_intlevel , 0x4000bfdc ) -PROVIDE ( _xtos_set_min_intlevel , 0x4000bff8 ) -PROVIDE ( _xtos_unhandled_interrupt , 0x4000c01c ) -PROVIDE ( _xtos_unhandled_exception , 0x4000c024 ) -PROVIDE ( _xtos_return_from_exc , 0x4000c034 ) -PROVIDE ( xthal_get_ccount , 0x4000c050 ) -PROVIDE ( xthal_set_ccompare , 0x4000c058 ) -PROVIDE ( xthal_get_ccompare , 0x4000c078 ) -PROVIDE ( xthal_bcopy , 0x4000c098 ) -PROVIDE ( xthal_memcpy , 0x4000c0bc ) -PROVIDE ( xthal_copy123 , 0x4000c124 ) -PROVIDE ( xthal_get_interrupt , 0x4000c1e4 ) -PROVIDE ( xthal_get_intread , 0x4000c1e4 ) -PROVIDE ( xthal_set_intclear , 0x4000c1ec ) -PROVIDE ( bzero , 0x4000c1f4 ) -PROVIDE ( isascii , 0x4000c20c ) -PROVIDE ( memccpy , 0x4000c220 ) -PROVIDE ( memchr , 0x4000c244 ) -PROVIDE ( memcmp , 0x4000c260 ) -PROVIDE ( memcpy , 0x4000c2c8 ) -PROVIDE ( memmove , 0x4000c3c0 ) -PROVIDE ( memrchr , 0x4000c400 ) -PROVIDE ( memset , 0x4000c44c ) -PROVIDE ( __sccl , 0x4000c498 ) -PROVIDE ( strcat , 0x4000c518 ) -PROVIDE ( strchr , 0x4000c53c ) -PROVIDE ( strcspn , 0x4000c558 ) -PROVIDE ( strlcpy , 0x4000c584 ) -PROVIDE ( strncat , 0x4000c5c4 ) -PROVIDE ( strncmp , 0x4000c5f4 ) -PROVIDE ( strnlen , 0x4000c628 ) -PROVIDE ( strspn , 0x4000c648 ) -PROVIDE ( strstr , 0x4000c674 ) -PROVIDE ( __strtok_r , 0x4000c6a8 ) -PROVIDE ( strtok_r , 0x4000c70c ) -PROVIDE ( toascii , 0x4000c720 ) -PROVIDE ( __dummy_lock , 0x4000c728 ) -PROVIDE ( __dummy_lock_try , 0x4000c730 ) -PROVIDE ( _fwalk , 0x4000c738 ) -PROVIDE ( _fwalk_reent , 0x4000c770 ) -PROVIDE ( __mulsi3 , 0x4000c7b0 ) -PROVIDE ( __divsi3 , 0x4000c7b8 ) -PROVIDE ( __modsi3 , 0x4000c7c0 ) -PROVIDE ( __udivsi3 , 0x4000c7c8 ) -PROVIDE ( __umodsi3 , 0x4000c7d0 ) -PROVIDE ( __umulsidi3 , 0x4000c7d8 ) -PROVIDE ( __clzsi2 , 0x4000c7e8 ) -PROVIDE ( __ctzsi2 , 0x4000c7f0 ) -PROVIDE ( __ffssi2 , 0x4000c804 ) -PROVIDE ( __ashldi3 , 0x4000c818 ) -PROVIDE ( __ashrdi3 , 0x4000c830 ) -PROVIDE ( __lshrdi3 , 0x4000c84c ) -PROVIDE ( __floatunsisf , 0x4000c864 ) -PROVIDE ( __floatsisf , 0x4000c870 ) -PROVIDE ( __floatundisf , 0x4000c8b0 ) -PROVIDE ( __floatdisf , 0x4000c8c0 ) -PROVIDE ( __floatunsidf , 0x4000c938 ) -PROVIDE ( __floatsidf , 0x4000c944 ) -PROVIDE ( __floatundidf , 0x4000c978 ) -PROVIDE ( __floatdidf , 0x4000c988 ) -PROVIDE ( __muldi3 , 0x4000c9fc ) -PROVIDE ( __negdi2 , 0x4000ca14 ) -PROVIDE ( __ffsdi2 , 0x4000ca2c ) -PROVIDE ( __clzdi2 , 0x4000ca50 ) -PROVIDE ( __ctzdi2 , 0x4000ca64 ) -PROVIDE ( __divdi3 , 0x4000ca84 ) -PROVIDE ( __moddi3 , 0x4000cd4c ) -PROVIDE ( __udivdi3 , 0x4000cff8 ) -PROVIDE ( __umoddi3 , 0x4000d280 ) -PROVIDE ( _data_start_btdm_rom , 0x4000d4f4 ) -PROVIDE ( _data_end_btdm_rom , 0x4000d4f8 ) -PROVIDE ( _data_start , 0x4000d4f8 ) -PROVIDE ( _rom_store_table , 0x4000d4f8 ) -PROVIDE ( _data_end , 0x4000d5c8 ) -PROVIDE ( _etext , 0x4000d66c ) -PROVIDE ( _rom_store , 0x4000d66c ) -PROVIDE ( btdm_r_btdm_option_data_p_get , 0x40010004 ) -PROVIDE ( btdm_r_data_init , 0x4001002c ) -PROVIDE ( btdm_r_btdm_rom_version_get , 0x40010078 ) -PROVIDE ( r_bt_util_buf_init , 0x400100e4 ) -PROVIDE ( r_bt_util_buf_lmp_tx_alloc , 0x400101d0 ) -PROVIDE ( r_bt_util_buf_lmp_tx_free , 0x400101ec ) -PROVIDE ( r_bt_util_buf_acl_rx_alloc , 0x40010218 ) -PROVIDE ( r_bt_util_buf_acl_rx_free , 0x40010234 ) -PROVIDE ( r_bt_util_buf_acl_tx_alloc , 0x40010268 ) -PROVIDE ( r_bt_util_buf_acl_tx_free , 0x40010280 ) -PROVIDE ( r_bt_util_buf_sync_init , 0x400102c4 ) -PROVIDE ( r_bt_util_buf_sync_clear , 0x400103c8 ) -PROVIDE ( r_bt_util_buf_sync_tx_alloc , 0x400103ec ) -PROVIDE ( r_bt_util_buf_sync_tx_free , 0x40010428 ) -PROVIDE ( r_bt_util_buf_sync_rx_alloc , 0x40010468 ) -PROVIDE ( r_bt_util_buf_sync_rx_free , 0x4001049c ) -PROVIDE ( r_E1 , 0x400108e8 ) -PROVIDE ( r_E21 , 0x40010968 ) -PROVIDE ( r_E22 , 0x400109b4 ) -PROVIDE ( r_E3 , 0x40010a58 ) -PROVIDE ( r_KPrimC , 0x40010ad4 ) -PROVIDE ( r_XorKey , 0x400112c0 ) -PROVIDE ( r_LM_MakeRandVec , 0x400112d8 ) -PROVIDE ( r_lmp_pack , 0x4001135c ) -PROVIDE ( r_lmp_unpack , 0x4001149c ) -PROVIDE ( r_lm_n_is_zero , 0x40012170 ) -PROVIDE ( r_lm_sp_sha256_calculate , 0x400121a0 ) -PROVIDE ( r_lm_sp_n_one , 0x400123a4 ) -PROVIDE ( r_lm_sp_n192_convert_wnaf , 0x400123c0 ) -PROVIDE ( r_lm_sp_p192_point_to_inf , 0x40012458 ) -PROVIDE ( r_lm_sp_p192_point_jacobian_to_affine , 0x40012468 ) -PROVIDE ( r_lm_sp_p192_points_jacobian_to_affine , 0x400124e4 ) -PROVIDE ( r_lm_sp_pre_compute_points , 0x40012640 ) -PROVIDE ( r_lm_sp_p192_dbl , 0x4001268c ) -PROVIDE ( r_lm_sp_p192_add , 0x40012828 ) -PROVIDE ( r_lm_sp_p192_invert , 0x40012b6c ) -PROVIDE ( r_lm_f1 , 0x40012bb8 ) -PROVIDE ( r_lm_f2 , 0x40012cfc ) -PROVIDE ( r_lm_oob_f1 , 0x40012e54 ) -PROVIDE ( r_lm_g , 0x40012f90 ) -PROVIDE ( r_lm_f3 , 0x40013050 ) -PROVIDE ( r_lm_get_nonce , 0x400131c4 ) -PROVIDE ( r_lm_dhkey_calc_init , 0x40013234 ) -PROVIDE ( r_lm_dhkey_compare , 0x400132d8 ) -PROVIDE ( r_F1_256 , 0x400133e4 ) -PROVIDE ( r_G_256 , 0x40013470 ) -PROVIDE ( r_F2_256 , 0x40013568 ) -PROVIDE ( r_F3_256 , 0x40013664 ) -PROVIDE ( r_H3 , 0x40013760 ) -PROVIDE ( r_H4 , 0x40013830 ) -PROVIDE ( r_H5 , 0x400138dc ) -PROVIDE ( r_HMAC , 0x40013968 ) -PROVIDE ( r_hashConcat , 0x40013a38 ) -PROVIDE ( r_SHA_256 , 0x40013a90 ) -PROVIDE ( r_co_list_init , 0x40013f14 ) -PROVIDE ( r_co_list_pool_init , 0x40013f30 ) -PROVIDE ( r_co_list_push_back , 0x40013fb8 ) -PROVIDE ( r_co_list_push_front , 0x40013ff4 ) -PROVIDE ( r_co_list_pop_front , 0x40014028 ) -PROVIDE ( r_co_list_extract , 0x4001404c ) -PROVIDE ( r_co_list_extract_after , 0x40014118 ) -PROVIDE ( r_co_list_find , 0x4001419c ) -PROVIDE ( r_co_list_merge , 0x400141bc ) -PROVIDE ( r_co_list_insert_before , 0x40014200 ) -PROVIDE ( r_co_list_insert_after , 0x40014254 ) -PROVIDE ( r_co_list_size , 0x400142ac ) -PROVIDE ( r_co_list_check_size_available , 0x400142c4 ) -PROVIDE ( r_co_bytes_to_string , 0x400142e4 ) -PROVIDE ( r_co_bdaddr_compare , 0x40014324 ) -PROVIDE ( r_co_slot_to_duration , 0x40014348 ) -PROVIDE ( r_co_nb_good_channels , 0x40014360 ) -PROVIDE ( r_dbg_init , 0x40014394 ) -PROVIDE ( r_dbg_platform_reset_complete , 0x400143d0 ) -PROVIDE ( r_dbg_swdiag_init , 0x40014470 ) -PROVIDE ( r_dbg_swdiag_read , 0x400144a4 ) -PROVIDE ( r_dbg_swdiag_write , 0x400144d0 ) -PROVIDE ( r_ea_elt_cancel , 0x400150d0 ) -PROVIDE ( r_ea_init , 0x40015228 ) -PROVIDE ( r_ea_elt_create , 0x40015264 ) -PROVIDE ( r_ea_elt_insert , 0x400152a8 ) -PROVIDE ( r_ea_elt_remove , 0x400154f0 ) -PROVIDE ( r_ea_interval_create , 0x4001555c ) -PROVIDE ( r_ea_interval_insert , 0x4001557c ) -PROVIDE ( r_ea_interval_remove , 0x40015590 ) -PROVIDE ( r_ea_interval_delete , 0x400155a8 ) -PROVIDE ( r_ea_finetimer_isr , 0x400155d4 ) -PROVIDE ( r_ea_sw_isr , 0x40015724 ) -PROVIDE ( r_ea_offset_req , 0x40015748 ) -PROVIDE ( r_ea_time_get_halfslot_rounded , 0x40015894 ) -PROVIDE ( r_ea_time_get_slot_rounded , 0x400158d4 ) -PROVIDE ( r_ea_sleep_check , 0x40015928 ) -PROVIDE ( r_ea_interval_duration_req , 0x4001597c ) -PROVIDE ( r_ea_alarm_set , 0x40015a10 ) -PROVIDE ( r_ea_alarm_clear , 0x40015ab4 ) -PROVIDE ( notEqual256 , 0x40015b04 ) -PROVIDE ( AddBigHex256 , 0x40015b28 ) -PROVIDE ( Add2SelfBigHex256 , 0x40015b7c ) -PROVIDE ( SubtractBigHex256 , 0x40015bcc ) -PROVIDE ( SubtractFromSelfBigHex256 , 0x40015c20 ) -PROVIDE ( AddP256 , 0x40015c74 ) -PROVIDE ( AddBigHexModP256 , 0x40015c98 ) -PROVIDE ( AddPdiv2_256 , 0x40015ce0 ) -PROVIDE ( SubtractFromSelfBigHexSign256 , 0x40015dc8 ) -PROVIDE ( SubtractBigHexMod256 , 0x40015e8c ) -PROVIDE ( SubtractBigHexUint32_256 , 0x40015f8c ) -PROVIDE ( MultiplyByU32ModP256 , 0x40015fdc ) -PROVIDE ( specialModP256 , 0x4001600c ) -PROVIDE ( MultiplyBigHexModP256 , 0x400160b8 ) -PROVIDE ( MultiplyBigHexByUint32_256 , 0x40016214 ) -PROVIDE ( GF_Jacobian_Point_Double256 , 0x40016260 ) -PROVIDE ( GF_Jacobian_Point_Addition256 , 0x400163a4 ) -PROVIDE ( bigHexInversion256 , 0x400168f0 ) -PROVIDE ( GF_Point_Jacobian_To_Affine256 , 0x40016b0c ) -PROVIDE ( r_ecc_init , 0x40016dbc ) -PROVIDE ( r_ecc_generate_key256 , 0x40016e00 ) -PROVIDE ( r_ecc_abort_key256_generation , 0x40017070 ) -PROVIDE ( r_ecc_gen_new_public_key , 0x400170c0 ) -PROVIDE ( r_ecc_gen_new_secret_key , 0x400170e4 ) -PROVIDE ( r_ecc_get_debug_Keys , 0x40017224 ) -PROVIDE ( r_em_buf_init , 0x4001729c ) -PROVIDE ( r_em_buf_rx_free , 0x400173c4 ) -PROVIDE ( r_em_buf_rx_buff_addr_get , 0x400173e8 ) -PROVIDE ( r_em_buf_tx_buff_addr_get , 0x40017404 ) -PROVIDE ( r_em_buf_tx_free , 0x4001741c ) -PROVIDE ( r_h4tl_init , 0x40017878 ) -PROVIDE ( r_h4tl_write , 0x400178d0 ) -PROVIDE ( r_h4tl_start , 0x40017924 ) -PROVIDE ( r_h4tl_stop , 0x40017934 ) -PROVIDE ( r_hci_fc_init , 0x40017974 ) -PROVIDE ( r_hci_fc_acl_buf_size_set , 0x40017988 ) -PROVIDE ( r_hci_fc_sync_buf_size_set , 0x400179b0 ) -PROVIDE ( r_hci_fc_acl_en , 0x400179d8 ) -PROVIDE ( r_hci_fc_sync_en , 0x40017a30 ) -PROVIDE ( r_hci_fc_acl_packet_sent , 0x40017a3c ) -PROVIDE ( r_hci_fc_sync_packet_sent , 0x40017a54 ) -PROVIDE ( r_hci_fc_host_nb_acl_pkts_complete , 0x40017a6c ) -PROVIDE ( r_hci_fc_host_nb_sync_pkts_complete , 0x40017a88 ) -PROVIDE ( r_hci_fc_check_host_available_nb_acl_packets , 0x40017aa4 ) -PROVIDE ( r_hci_fc_check_host_available_nb_sync_packets , 0x40017ac8 ) -PROVIDE ( r_hci_look_for_cmd_desc , 0x40018454 ) -PROVIDE ( r_hci_look_for_evt_desc , 0x400184a0 ) -PROVIDE ( r_hci_look_for_dbg_evt_desc , 0x400184c4 ) -PROVIDE ( r_hci_look_for_le_evt_desc , 0x400184e0 ) -PROVIDE ( r_hci_init , 0x40018538 ) -PROVIDE ( r_hci_reset , 0x4001856c ) -PROVIDE ( r_hci_send_2_host , 0x400185bc ) -PROVIDE ( r_hci_bt_acl_bdaddr_register , 0x40018900 ) -PROVIDE ( r_hci_bt_acl_conhdl_register , 0x4001895c ) -PROVIDE ( r_hci_bt_acl_bdaddr_unregister , 0x400189ac ) -PROVIDE ( r_hci_evt_mask_set , 0x400189e4 ) -PROVIDE ( r_hci_evt_filter_add , 0x40018a64 ) -PROVIDE ( r_hci_voice_settings_get , 0x40018bdc ) -PROVIDE ( r_hci_voice_settings_set , 0x40018be8 ) -PROVIDE ( r_hci_tl_send , 0x40019228 ) -PROVIDE ( r_hci_tl_init , 0x40019290 ) -PROVIDE ( r_hci_cmd_get_max_param_size , 0x400192d0 ) -PROVIDE ( r_hci_cmd_received , 0x400192f8 ) -PROVIDE ( r_hci_acl_tx_data_alloc , 0x4001951c ) -PROVIDE ( r_hci_acl_tx_data_received , 0x40019654 ) -PROVIDE ( r_hci_sync_tx_data_alloc , 0x40019754 ) -PROVIDE ( r_hci_sync_tx_data_received , 0x400197c0 ) -PROVIDE ( r_hci_util_pack , 0x40019874 ) -PROVIDE ( r_hci_util_unpack , 0x40019998 ) -PROVIDE ( btdm_r_ip_func_p_get , 0x40019af0 ) -PROVIDE ( btdm_r_ip_func_p_set , 0x40019afc ) -PROVIDE ( btdm_r_ble_bt_handler_tab_p_get , 0x40019b0c ) -PROVIDE ( r_ke_event_init , 0x40019b90 ) -PROVIDE ( r_ke_event_callback_set , 0x40019ba8 ) -PROVIDE ( r_ke_event_set , 0x40019be0 ) -PROVIDE ( r_ke_event_clear , 0x40019c2c ) -PROVIDE ( r_ke_event_get , 0x40019c78 ) -PROVIDE ( r_ke_event_get_all , 0x40019cc0 ) -PROVIDE ( r_ke_event_flush , 0x40019ccc ) -PROVIDE ( r_ke_event_schedule , 0x40019cdc ) -PROVIDE ( r_ke_mem_init , 0x40019d3c ) -PROVIDE ( r_ke_mem_is_empty , 0x40019d8c ) -PROVIDE ( r_ke_check_malloc , 0x40019de0 ) -PROVIDE ( r_ke_malloc , 0x40019eb4 ) -PROVIDE ( r_ke_free , 0x4001a014 ) -PROVIDE ( r_ke_is_free , 0x4001a184 ) -PROVIDE ( r_ke_get_mem_usage , 0x4001a1a0 ) -PROVIDE ( r_ke_get_max_mem_usage , 0x4001a1c8 ) -PROVIDE ( r_ke_msg_alloc , 0x4001a1e0 ) -PROVIDE ( r_ke_msg_send , 0x4001a234 ) -PROVIDE ( r_ke_msg_send_basic , 0x4001a26c ) -PROVIDE ( r_ke_msg_forward , 0x4001a290 ) -PROVIDE ( r_ke_msg_forward_new_id , 0x4001a2ac ) -PROVIDE ( r_ke_msg_free , 0x4001a2cc ) -PROVIDE ( r_ke_msg_dest_id_get , 0x4001a2e0 ) -PROVIDE ( r_ke_msg_src_id_get , 0x4001a2ec ) -PROVIDE ( r_ke_msg_in_queue , 0x4001a2f8 ) -PROVIDE ( r_ke_init , 0x4001a318 ) -PROVIDE ( r_ke_flush , 0x4001a374 ) -PROVIDE ( r_ke_sleep_check , 0x4001a3d8 ) -PROVIDE ( r_ke_stats_get , 0x4001a3f0 ) -PROVIDE ( r_ke_task_init , 0x4001a650 ) -PROVIDE ( r_ke_task_create , 0x4001a674 ) -PROVIDE ( r_ke_task_delete , 0x4001a6c0 ) -PROVIDE ( r_ke_state_set , 0x4001a6fc ) -PROVIDE ( r_ke_state_get , 0x4001a7d8 ) -PROVIDE ( r_ke_msg_discard , 0x4001a850 ) -PROVIDE ( r_ke_msg_save , 0x4001a858 ) -PROVIDE ( r_ke_task_msg_flush , 0x4001a860 ) -PROVIDE ( r_ke_task_check , 0x4001a8a4 ) -PROVIDE ( r_ke_timer_init , 0x4001aa9c ) -PROVIDE ( r_ke_timer_set , 0x4001aac0 ) -PROVIDE ( r_ke_timer_clear , 0x4001ab90 ) -PROVIDE ( r_ke_timer_active , 0x4001ac08 ) -PROVIDE ( r_ke_timer_adjust_all , 0x4001ac30 ) -PROVIDE ( r_ke_timer_sleep_check , 0x4001ac50 ) -PROVIDE ( r_LM_ExtractMaxEncKeySize , 0x4001aca4 ) -PROVIDE ( r_lb_init , 0x4001acd4 ) -PROVIDE ( r_lb_reset , 0x4001ad38 ) -PROVIDE ( r_lb_util_get_nb_broadcast , 0x4001ad80 ) -PROVIDE ( r_lb_util_set_nb_broadcast , 0x4001ad8c ) -PROVIDE ( r_lb_util_get_res_lt_addr , 0x4001ad98 ) -PROVIDE ( r_lb_util_get_csb_mode , 0x4001ada4 ) -PROVIDE ( r_lb_send_lmp , 0x4001adbc ) -PROVIDE ( r_lb_clk_adj_activate , 0x4001ae70 ) -PROVIDE ( r_lb_clk_adj_id_get , 0x4001af14 ) -PROVIDE ( r_lb_clk_adj_period_update , 0x4001af20 ) -PROVIDE ( r_lb_send_pdu_clk_adj , 0x4001af3c ) -PROVIDE ( r_lb_mst_key_cmp , 0x4001af74 ) -PROVIDE ( r_lb_mst_key , 0x4001afc0 ) -PROVIDE ( r_lb_mst_key_restart_enc , 0x4001b0d4 ) -PROVIDE ( r_lb_mst_start_act_bcst_enc , 0x4001b198 ) -PROVIDE ( r_lb_mst_stop_act_bcst_enc , 0x4001b24c ) -PROVIDE ( r_LM_GetMasterKey , 0x4001b260 ) -PROVIDE ( r_LM_GetMasterKeyRand , 0x4001b274 ) -PROVIDE ( r_LM_GetMasterEncRand , 0x4001b288 ) -PROVIDE ( r_LM_GetMasterEncKeySize , 0x4001b29c ) -PROVIDE ( lb_hci_cmd_handler_tab_p_get , 0x4001c18c ) -PROVIDE ( lb_default_state_tab_p_get , 0x4001c198 ) -PROVIDE ( r_lc_send_lmp , 0x4001c1a8 ) -PROVIDE ( r_lc_send_pdu_acc , 0x4001c21c ) -PROVIDE ( r_lc_send_pdu_acc_ext4 , 0x4001c240 ) -PROVIDE ( r_lc_send_pdu_not_acc , 0x4001c26c ) -PROVIDE ( r_lc_send_pdu_not_acc_ext4 , 0x4001c294 ) -PROVIDE ( r_lc_send_pdu_set_afh , 0x4001c2c8 ) -PROVIDE ( r_lc_send_pdu_au_rand , 0x4001c308 ) -PROVIDE ( r_lc_send_pdu_in_rand , 0x4001c338 ) -PROVIDE ( r_lc_send_pdu_comb_key , 0x4001c368 ) -PROVIDE ( r_lc_send_pdu_unit_key , 0x4001c398 ) -PROVIDE ( r_lc_send_pdu_max_slot , 0x4001c3c8 ) -PROVIDE ( r_lc_send_pdu_max_slot_req , 0x4001c3ec ) -PROVIDE ( r_lc_send_pdu_encaps_payl , 0x4001c410 ) -PROVIDE ( r_lc_send_pdu_encaps_head , 0x4001c440 ) -PROVIDE ( r_lc_send_pdu_clk_adj_ack , 0x4001c46c ) -PROVIDE ( r_lc_send_pdu_clk_adj_req , 0x4001c494 ) -PROVIDE ( r_lc_send_pdu_ptt_req , 0x4001c4c0 ) -PROVIDE ( r_lc_send_pdu_sp_nb , 0x4001c4e8 ) -PROVIDE ( r_lc_send_pdu_sp_cfm , 0x4001c518 ) -PROVIDE ( r_lc_send_pdu_sres , 0x4001c548 ) -PROVIDE ( r_lc_send_pdu_sco_lk_rem_req , 0x4001c580 ) -PROVIDE ( r_lc_send_pdu_esco_lk_rem_req , 0x4001c5a8 ) -PROVIDE ( r_lc_send_pdu_auto_rate , 0x4001c5d0 ) -PROVIDE ( r_lc_send_pdu_sniff_req , 0x4001c5f0 ) -PROVIDE ( r_lc_send_pdu_lsto , 0x4001c64c ) -PROVIDE ( r_lc_send_pdu_enc_key_sz_req , 0x4001c670 ) -PROVIDE ( r_lc_send_pdu_feats_res , 0x4001c694 ) -PROVIDE ( r_lc_send_pdu_tim_acc , 0x4001c6cc ) -PROVIDE ( r_lc_send_pdu_feats_ext_req , 0x4001c6ec ) -PROVIDE ( r_lc_send_pdu_io_cap_res , 0x4001c72c ) -PROVIDE ( r_lc_send_pdu_num_comp_fail , 0x4001c770 ) -PROVIDE ( r_lc_send_pdu_pause_enc_aes_req , 0x4001c794 ) -PROVIDE ( r_lc_send_pdu_paus_enc_req , 0x4001c7c0 ) -PROVIDE ( r_lc_send_pdu_resu_enc_req , 0x4001c7e4 ) -PROVIDE ( r_lc_send_pdu_setup_cmp , 0x4001c808 ) -PROVIDE ( r_lc_send_pdu_qos_req , 0x4001c82c ) -PROVIDE ( r_lc_send_pdu_slot_off , 0x4001c854 ) -PROVIDE ( r_lc_send_pdu_unsniff_req , 0x4001c894 ) -PROVIDE ( r_lc_send_pdu_vers_req , 0x4001c8b4 ) -PROVIDE ( r_lc_send_pdu_dhkey_chk , 0x4001c8e8 ) -PROVIDE ( r_lc_cmd_stat_send , 0x4001c914 ) -PROVIDE ( r_lc_init , 0x4001c948 ) -PROVIDE ( r_lc_reset , 0x4001c99c ) -PROVIDE ( r_lc_start , 0x4001ca28 ) -PROVIDE ( r_lc_afh_set , 0x4001cc74 ) -PROVIDE ( r_lc_auth_cmp , 0x4001cd54 ) -PROVIDE ( r_lc_calc_link_key , 0x4001ce7c ) -PROVIDE ( r_lc_chg_pkt_type_cont , 0x4001cfbc ) -PROVIDE ( r_lc_chg_pkt_type_cmp , 0x4001d038 ) -PROVIDE ( r_lc_chg_pkt_type_retry , 0x4001d0ac ) -PROVIDE ( r_lc_afh_start , 0x4001d240 ) -PROVIDE ( r_lc_chk_to , 0x4001d2a8 ) -PROVIDE ( r_lc_comb_key_svr , 0x4001d30c ) -PROVIDE ( r_lc_con_cmp , 0x4001d44c ) -PROVIDE ( r_lc_con_cmp_evt_send , 0x4001d4fc ) -PROVIDE ( r_lc_dhkey , 0x4001d564 ) -PROVIDE ( r_lc_locepr_rsw , 0x4001d5d0 ) -PROVIDE ( r_lc_locepr_lkref , 0x4001d648 ) -PROVIDE ( r_lc_enc_key_refresh , 0x4001d720 ) -PROVIDE ( r_lc_end_chk_colli , 0x4001d858 ) -PROVIDE ( r_lc_enc_cmp , 0x4001d8bc ) -PROVIDE ( r_lc_end_of_sniff_nego , 0x4001d9a4 ) -PROVIDE ( r_lc_epr_cmp , 0x4001da88 ) -PROVIDE ( r_lc_epr_change_lk , 0x4001db38 ) -PROVIDE ( r_lc_rsw_done , 0x4001db94 ) -PROVIDE ( r_lc_rsw_clean_up , 0x4001dc70 ) -PROVIDE ( r_lc_epr_rsw_cmp , 0x4001dd40 ) -PROVIDE ( r_lc_enter_sniff_mode , 0x4001ddb8 ) -PROVIDE ( r_lc_init_calc_f3 , 0x4001deb0 ) -PROVIDE ( r_lc_init_start_mutual_auth , 0x4001df60 ) -PROVIDE ( r_lc_init_passkey_loop , 0x4001dfc0 ) -PROVIDE ( r_lc_initiator_epr , 0x4001e064 ) -PROVIDE ( r_lc_epr_resp , 0x4001e0b4 ) -PROVIDE ( r_lc_key_exch_end , 0x4001e140 ) -PROVIDE ( r_lc_legacy_pair , 0x4001e1c0 ) -PROVIDE ( r_lc_local_switch , 0x4001e22c ) -PROVIDE ( r_lc_local_trans_mode , 0x4001e2e4 ) -PROVIDE ( r_lc_local_untrans_mode , 0x4001e3a0 ) -PROVIDE ( r_lc_max_slot_mgt , 0x4001e410 ) -PROVIDE ( r_lc_mutual_auth_end2 , 0x4001e4f4 ) -PROVIDE ( r_lc_mutual_auth_end , 0x4001e670 ) -PROVIDE ( r_lc_mst_key , 0x4001e7c0 ) -PROVIDE ( r_lc_mst_send_mst_key , 0x4001e8f4 ) -PROVIDE ( r_lc_mst_qos_done , 0x4001ea80 ) -PROVIDE ( r_lc_pairing_cont , 0x4001eafc ) -PROVIDE ( r_lc_passkey_comm , 0x4001ed20 ) -PROVIDE ( r_lc_proc_rcv_dhkey , 0x4001edec ) -PROVIDE ( r_lc_ptt , 0x4001ee2c ) -PROVIDE ( r_lc_ptt_cmp , 0x4001eeec ) -PROVIDE ( r_lc_qos_setup , 0x4001ef50 ) -PROVIDE ( r_lc_rd_rem_name , 0x4001efd0 ) -PROVIDE ( r_lc_restore_afh_reporting , 0x4001f028 ) -PROVIDE ( r_lc_rem_switch , 0x4001f070 ) -PROVIDE ( r_lc_rem_enc , 0x4001f124 ) -PROVIDE ( r_lc_rem_nego_trans_mode , 0x4001f1b4 ) -PROVIDE ( r_lc_rem_name_cont , 0x4001f290 ) -PROVIDE ( r_lc_rem_trans_mode , 0x4001f314 ) -PROVIDE ( r_lc_rem_untrans_mode , 0x4001f36c ) -PROVIDE ( r_lc_sec_auth_compute_sres , 0x4001f3ec ) -PROVIDE ( r_lc_resp_sec_auth , 0x4001f4a0 ) -PROVIDE ( r_lc_resp_auth , 0x4001f518 ) -PROVIDE ( r_lc_resp_oob_wait_nonce , 0x4001f66c ) -PROVIDE ( r_lc_resp_oob_nonce , 0x4001f694 ) -PROVIDE ( r_lc_resp_calc_f3 , 0x4001f710 ) -PROVIDE ( r_lc_resp_wait_dhkey_cont , 0x4001f86c ) -PROVIDE ( r_lc_release , 0x4001f8a8 ) -PROVIDE ( r_lc_restart_enc , 0x4001f8ec ) -PROVIDE ( r_lc_restart_enc_cont , 0x4001f940 ) -PROVIDE ( r_lc_restore_to , 0x4001f9e0 ) -PROVIDE ( r_lc_ret_sniff_max_slot_chg , 0x4001fa30 ) -PROVIDE ( r_lc_start_lmp_to , 0x4001fae8 ) -PROVIDE ( r_lc_start_enc , 0x4001fb28 ) -PROVIDE ( r_lc_start_enc_key_size , 0x4001fd9c ) -PROVIDE ( r_lc_start_key_exch , 0x4001fe10 ) -PROVIDE ( r_lc_start_passkey , 0x4001feac ) -PROVIDE ( r_lc_start_passkey_loop , 0x4001ff88 ) -PROVIDE ( r_lc_start_oob , 0x4001fffc ) -PROVIDE ( r_lc_resp_num_comp , 0x40020074 ) -PROVIDE ( r_lc_stop_enc , 0x40020110 ) -PROVIDE ( r_lc_stop_afh_report , 0x40020184 ) -PROVIDE ( r_lc_skip_hl_oob_req , 0x400201bc ) -PROVIDE ( r_lc_send_enc_mode , 0x40020220 ) -PROVIDE ( r_lc_semi_key_cmp , 0x40020294 ) -PROVIDE ( r_lc_detach , 0x4002037c ) -PROVIDE ( r_lc_switch_cmp , 0x40020448 ) -PROVIDE ( r_lc_sp_fail , 0x40020470 ) -PROVIDE ( r_lc_sp_oob_tid_fail , 0x400204cc ) -PROVIDE ( r_lc_sniff_sub_mode , 0x400204fc ) -PROVIDE ( r_lc_sniff_max_slot_chg , 0x40020590 ) -PROVIDE ( r_lc_upd_to , 0x4002065c ) -PROVIDE ( r_lc_unit_key_svr , 0x400206d8 ) -PROVIDE ( r_lc_unsniff_cont , 0x40020750 ) -PROVIDE ( r_lc_rem_unsniff , 0x400207a0 ) -PROVIDE ( r_lc_unsniff_cmp , 0x40020810 ) -PROVIDE ( r_lc_resp_pair , 0x400208a4 ) -PROVIDE ( r_lc_feat , 0x40020984 ) -PROVIDE ( r_lc_hl_connect , 0x400209e8 ) -PROVIDE ( r_lc_version , 0x40020a30 ) -PROVIDE ( r_lc_loc_sniff , 0x40020a6c ) -PROVIDE ( r_lc_rem_sniff_sub_rate , 0x40020b10 ) -PROVIDE ( r_lc_unsniff , 0x40020c50 ) -PROVIDE ( r_lc_rem_sniff , 0x40020ca4 ) -PROVIDE ( r_lc_ext_feat , 0x40020d6c ) -PROVIDE ( r_lc_pair , 0x40020ddc ) -PROVIDE ( r_lc_loc_auth , 0x40020ecc ) -PROVIDE ( r_lc_packet_type , 0x40021038 ) -PROVIDE ( r_lc_sniff_slot_unchange , 0x40021100 ) -PROVIDE ( r_lc_ssr_nego , 0x4002125c ) -PROVIDE ( r_lc_conn_seq_done , 0x40021334 ) -PROVIDE ( r_lc_send_enc_chg_evt , 0x4002134c ) -PROVIDE ( r_lc_sp_end , 0x400213a8 ) -PROVIDE ( r_lc_prepare_all_links_for_clk_adj , 0x40021430 ) -PROVIDE ( r_lc_sco_init , 0x40021dc8 ) -PROVIDE ( r_lc_sco_reset , 0x40021dfc ) -PROVIDE ( r_lc_sco_detach , 0x40021e40 ) -PROVIDE ( r_lc_sco_release , 0x40021eec ) -PROVIDE ( r_lc_sco_host_request , 0x40021f4c ) -PROVIDE ( r_lc_sco_host_accept , 0x40022118 ) -PROVIDE ( r_lc_sco_host_reject , 0x400222b8 ) -PROVIDE ( r_lc_sco_host_request_disc , 0x4002235c ) -PROVIDE ( r_lc_sco_peer_request , 0x4002240c ) -PROVIDE ( r_lc_sco_peer_accept , 0x40022780 ) -PROVIDE ( r_lc_sco_peer_reject , 0x40022824 ) -PROVIDE ( r_lc_sco_peer_request_disc , 0x400228ec ) -PROVIDE ( r_lc_sco_peer_accept_disc , 0x40022a08 ) -PROVIDE ( r_lc_sco_peer_reject_disc , 0x40022a8c ) -PROVIDE ( r_lc_sco_baseband_ack , 0x40022b00 ) -PROVIDE ( r_lc_sco_timeout , 0x40022bd4 ) -PROVIDE ( r_lc_sniff_init , 0x40022cac ) -PROVIDE ( r_lc_sniff_reset , 0x40022cc8 ) -PROVIDE ( r_LM_AddSniff , 0x40022d20 ) -PROVIDE ( r_LM_RemoveSniff , 0x40023124 ) -PROVIDE ( r_LM_SniffSubratingHlReq , 0x40023154 ) -PROVIDE ( r_LM_SniffSubratingPeerReq , 0x400231dc ) -PROVIDE ( r_LM_GetSniffSubratingParam , 0x4002325c ) -PROVIDE ( r_LM_SniffSubrateNegoRequired , 0x40023334 ) -PROVIDE ( r_LM_ComputeSniffSubRate , 0x400233ac ) -PROVIDE ( r_LM_RemoveSniffSubrating , 0x400233c4 ) -PROVIDE ( r_LM_GetLinkTimeout , 0x400233ec ) -PROVIDE ( lc_hci_cmd_handler_tab_p_get , 0x4002f488 ) -PROVIDE ( lc_default_state_tab_p_get , 0x4002f494 ) -PROVIDE ( r_lc_util_get_max_packet_size , 0x4002f4ac ) -PROVIDE ( r_lc_util_set_loc_trans_coll , 0x4002f500 ) -PROVIDE ( r_lc_util_get_offset_clkn , 0x4002f51c ) -PROVIDE ( r_lc_util_get_offset_clke , 0x4002f538 ) -PROVIDE ( r_LM_ComputePacketType , 0x4002f554 ) -PROVIDE ( r_LM_UpdateAclPacketType , 0x4002f584 ) -PROVIDE ( r_LM_UpdateAclEdrPacketType , 0x4002f5d8 ) -PROVIDE ( r_LM_SuppressAclPacket , 0x4002f658 ) -PROVIDE ( r_LM_MaxSlot , 0x4002f694 ) -PROVIDE ( r_LM_GetQoSParam , 0x4002f6e0 ) -PROVIDE ( r_LM_GetSwitchInstant , 0x4002f7f8 ) -PROVIDE ( r_LM_MakeCof , 0x4002f84c ) -PROVIDE ( r_LM_GetAFHSwitchInstant , 0x4002f86c ) -PROVIDE ( r_LM_CheckSwitchInstant , 0x4002f8c0 ) -PROVIDE ( r_LM_CheckEdrFeatureRequest , 0x4002f90c ) -PROVIDE ( r_LM_GetFeature , 0x4002f924 ) -PROVIDE ( r_lm_look_for_stored_link_key , 0x4002f948 ) -PROVIDE ( r_lc_util_convert_pref_rate_to_packet_type , 0x4002f9b0 ) -PROVIDE ( r_lm_get_common_pkt_types , 0x4002fa1c ) -PROVIDE ( r_ld_acl_init , 0x40034d08 ) -PROVIDE ( r_ld_acl_reset , 0x40034d24 ) -PROVIDE ( r_ld_acl_start , 0x40034ddc ) -PROVIDE ( r_ld_acl_stop , 0x4003532c ) -PROVIDE ( r_ld_acl_flow_off , 0x40035400 ) -PROVIDE ( r_ld_acl_flow_on , 0x4003541c ) -PROVIDE ( r_ld_acl_data_tx , 0x4003544c ) -PROVIDE ( r_ld_acl_data_flush , 0x400357bc ) -PROVIDE ( r_ld_acl_lmp_tx , 0x40035b34 ) -PROVIDE ( r_ld_acl_lmp_flush , 0x40035d80 ) -PROVIDE ( r_ld_acl_rsw_req , 0x40035e74 ) -PROVIDE ( r_ld_acl_flush_timeout_get , 0x40035f9c ) -PROVIDE ( r_ld_acl_flush_timeout_set , 0x40035fe0 ) -PROVIDE ( r_ld_acl_t_poll_get , 0x40036024 ) -PROVIDE ( r_ld_acl_t_poll_set , 0x40036068 ) -PROVIDE ( r_ld_acl_sniff_trans , 0x400360a8 ) -PROVIDE ( r_ld_acl_sniff , 0x4003617c ) -PROVIDE ( r_ld_acl_unsniff , 0x400361e0 ) -PROVIDE ( r_ld_acl_ssr_set , 0x40036274 ) -PROVIDE ( r_ld_acl_tx_enc , 0x400362f8 ) -PROVIDE ( r_ld_acl_rx_enc , 0x40036344 ) -PROVIDE ( r_ld_acl_bcst_rx_dec , 0x40036394 ) -PROVIDE ( r_ld_acl_enc_key_load , 0x40036404 ) -PROVIDE ( r_ld_acl_clock_offset_get , 0x400364c0 ) -PROVIDE ( r_ld_acl_slot_offset_get , 0x4003653c ) -PROVIDE ( r_ld_acl_slot_offset_set , 0x40036658 ) -PROVIDE ( r_ld_acl_lsto_get , 0x400366b4 ) -PROVIDE ( r_ld_acl_lsto_set , 0x400366f8 ) -PROVIDE ( r_ld_acl_timing_accuracy_set , 0x4003673c ) -PROVIDE ( r_ld_acl_edr_set , 0x4003678c ) -PROVIDE ( r_ld_acl_allowed_tx_packet_types_set , 0x40036810 ) -PROVIDE ( r_ld_acl_current_tx_power_get , 0x400368f0 ) -PROVIDE ( r_ld_acl_clk_set , 0x40036950 ) -PROVIDE ( r_ld_acl_clk_adj_set , 0x40036a00 ) -PROVIDE ( r_ld_acl_clk_off_get , 0x40036b00 ) -PROVIDE ( r_ld_acl_bit_off_get , 0x40036b18 ) -PROVIDE ( r_ld_acl_role_get , 0x40036b30 ) -PROVIDE ( r_ld_acl_afh_set , 0x40036b60 ) -PROVIDE ( r_ld_acl_afh_prepare , 0x40036c84 ) -PROVIDE ( r_ld_acl_afh_confirm , 0x40036d40 ) -PROVIDE ( r_ld_acl_active_hop_types_get , 0x40036e10 ) -PROVIDE ( r_ld_acl_rx_max_slot_get , 0x40036e58 ) -PROVIDE ( r_ld_acl_rx_max_slot_set , 0x40036ea0 ) -PROVIDE ( r_ld_acl_test_mode_set , 0x40036f24 ) -PROVIDE ( r_ld_acl_rssi_delta_get , 0x40037028 ) -PROVIDE ( r_ld_sco_start , 0x40037110 ) -PROVIDE ( r_ld_sco_update , 0x40037a74 ) -PROVIDE ( r_ld_sco_stop , 0x40037c40 ) -PROVIDE ( r_ld_sco_audio_isr , 0x40037cc8 ) -PROVIDE ( r_ld_sco_data_tx , 0x40037ee8 ) -PROVIDE ( r_ld_bcst_acl_start , 0x4003882c ) -PROVIDE ( r_ld_bcst_acl_init , 0x40038bd0 ) -PROVIDE ( r_ld_bcst_acl_reset , 0x40038bdc ) -PROVIDE ( r_ld_bcst_lmp_tx , 0x40038bf8 ) -PROVIDE ( r_ld_bcst_acl_data_tx , 0x40038d3c ) -PROVIDE ( r_ld_bcst_afh_update , 0x40038f3c ) -PROVIDE ( r_ld_bcst_tx_enc , 0x40038ff8 ) -PROVIDE ( r_ld_bcst_enc_key_load , 0x4003906c ) -PROVIDE ( r_ld_csb_rx_init , 0x40039690 ) -PROVIDE ( r_ld_csb_rx_reset , 0x4003969c ) -PROVIDE ( r_ld_csb_rx_start , 0x4003972c ) -PROVIDE ( r_ld_csb_rx_afh_update , 0x40039af4 ) -PROVIDE ( r_ld_csb_rx_stop , 0x40039bb8 ) -PROVIDE ( r_ld_csb_tx_init , 0x4003a0e8 ) -PROVIDE ( r_ld_csb_tx_reset , 0x4003a0f8 ) -PROVIDE ( r_ld_csb_tx_en , 0x4003a1c0 ) -PROVIDE ( r_ld_csb_tx_dis , 0x4003a5e8 ) -PROVIDE ( r_ld_csb_tx_afh_update , 0x4003a5fc ) -PROVIDE ( r_ld_csb_tx_set_data , 0x4003a6c0 ) -PROVIDE ( r_ld_csb_tx_clr_data , 0x4003a71c ) -PROVIDE ( r_ld_fm_init , 0x4003a760 ) -PROVIDE ( r_ld_fm_reset , 0x4003a794 ) -PROVIDE ( r_ld_fm_clk_isr , 0x4003a7a8 ) -PROVIDE ( r_ld_fm_rx_isr , 0x4003a7f4 ) -PROVIDE ( r_ld_fm_frame_isr , 0x4003a82c ) -PROVIDE ( r_ld_fm_sket_isr , 0x4003a8a4 ) -PROVIDE ( r_ld_fm_prog_enable , 0x4003a944 ) -PROVIDE ( r_ld_fm_prog_disable , 0x4003a984 ) -PROVIDE ( r_ld_fm_prog_push , 0x4003a9d4 ) -PROVIDE ( r_ld_fm_prog_check , 0x4003ab28 ) -PROVIDE ( r_ld_inq_init , 0x4003b15c ) -PROVIDE ( r_ld_inq_reset , 0x4003b168 ) -PROVIDE ( r_ld_inq_start , 0x4003b1f0 ) -PROVIDE ( r_ld_inq_stop , 0x4003b4f0 ) -PROVIDE ( r_ld_iscan_init , 0x4003b9f0 ) -PROVIDE ( r_ld_iscan_reset , 0x4003ba14 ) -PROVIDE ( r_ld_iscan_restart , 0x4003ba44 ) -PROVIDE ( r_ld_iscan_start , 0x4003bb28 ) -PROVIDE ( r_ld_iscan_stop , 0x4003bf1c ) -PROVIDE ( r_ld_iscan_eir_set , 0x4003bfa0 ) -PROVIDE ( r_ld_iscan_eir_get , 0x4003c118 ) -PROVIDE ( r_ld_iscan_tx_pwr_get , 0x4003c138 ) -PROVIDE ( r_ld_channel_assess , 0x4003c184 ) -PROVIDE ( r_ld_init , 0x4003c294 ) -PROVIDE ( r_ld_reset , 0x4003c714 ) -PROVIDE ( r_ld_read_clock , 0x4003c9e4 ) -PROVIDE ( r_ld_bd_addr_get , 0x4003ca20 ) -PROVIDE ( r_ld_class_of_dev_get , 0x4003ca34 ) -PROVIDE ( r_ld_class_of_dev_set , 0x4003ca50 ) -PROVIDE ( r_ld_version_get , 0x4003ca6c ) -PROVIDE ( r_ld_timing_accuracy_get , 0x4003caac ) -PROVIDE ( r_ld_active_check , 0x4003cac4 ) -PROVIDE ( r_ld_afh_ch_assess_data_get , 0x4003caec ) -PROVIDE ( r_ld_wlcoex_set , 0x4003caf8 ) -PROVIDE ( r_ld_page_init , 0x4003d808 ) -PROVIDE ( r_ld_page_reset , 0x4003d814 ) -PROVIDE ( r_ld_page_start , 0x4003d848 ) -PROVIDE ( r_ld_page_stop , 0x4003da54 ) -PROVIDE ( r_ld_pca_init , 0x4003deb4 ) -PROVIDE ( r_ld_pca_reset , 0x4003df0c ) -PROVIDE ( r_ld_pca_local_config , 0x4003df6c ) -PROVIDE ( r_ld_pca_reporting_enable , 0x4003e018 ) -PROVIDE ( r_ld_pca_update_target_offset , 0x4003e050 ) -PROVIDE ( r_ld_pca_mws_frame_sync , 0x4003e104 ) -PROVIDE ( r_ld_pca_mws_moment_offset_gt , 0x4003e278 ) -PROVIDE ( r_ld_pca_mws_moment_offset_lt , 0x4003e280 ) -PROVIDE ( r_ld_pca_coarse_clock_adjust , 0x4003e324 ) -PROVIDE ( r_ld_pca_initiate_clock_dragging , 0x4003e4ac ) -PROVIDE ( r_ld_pscan_evt_handler , 0x4003f238 ) -PROVIDE ( r_ld_pscan_init , 0x4003f474 ) -PROVIDE ( r_ld_pscan_reset , 0x4003f498 ) -PROVIDE ( r_ld_pscan_restart , 0x4003f4b8 ) -PROVIDE ( r_ld_pscan_start , 0x4003f514 ) -PROVIDE ( r_ld_pscan_stop , 0x4003f618 ) -PROVIDE ( r_ld_sched_compute , 0x4003f6f8 ) -PROVIDE ( r_ld_sched_init , 0x4003f7ac ) -PROVIDE ( r_ld_sched_reset , 0x4003f7d4 ) -PROVIDE ( r_ld_sched_iscan_add , 0x4003f7e8 ) -PROVIDE ( r_ld_sched_iscan_remove , 0x4003f808 ) -PROVIDE ( r_ld_sched_pscan_add , 0x4003f828 ) -PROVIDE ( r_ld_sched_pscan_remove , 0x4003f848 ) -PROVIDE ( r_ld_sched_sscan_add , 0x4003f868 ) -PROVIDE ( r_ld_sched_sscan_remove , 0x4003f888 ) -PROVIDE ( r_ld_sched_inq_add , 0x4003f8a8 ) -PROVIDE ( r_ld_sched_inq_remove , 0x4003f8d0 ) -PROVIDE ( r_ld_sched_page_add , 0x4003f910 ) -PROVIDE ( r_ld_sched_page_remove , 0x4003f938 ) -PROVIDE ( r_ld_sched_acl_add , 0x4003f978 ) -PROVIDE ( r_ld_sched_acl_remove , 0x4003f99c ) -PROVIDE ( r_ld_sched_sniff_add , 0x4003f9c4 ) -PROVIDE ( r_ld_sched_sniff_remove , 0x4003fa0c ) -PROVIDE ( r_ld_sched_sco_add , 0x4003fa4c ) -PROVIDE ( r_ld_sched_sco_remove , 0x4003fa9c ) -PROVIDE ( r_ld_sscan_init , 0x400402f0 ) -PROVIDE ( r_ld_sscan_reset , 0x400402fc ) -PROVIDE ( r_ld_sscan_activated , 0x4004031c ) -PROVIDE ( r_ld_sscan_start , 0x40040384 ) -PROVIDE ( r_ld_strain_init , 0x400409f4 ) -PROVIDE ( r_ld_strain_reset , 0x40040a00 ) -PROVIDE ( r_ld_strain_start , 0x40040a8c ) -PROVIDE ( r_ld_strain_stop , 0x40040df0 ) -PROVIDE ( r_ld_util_fhs_unpk , 0x40040e54 ) -PROVIDE ( r_ld_util_bch_create , 0x40040fcc ) -PROVIDE ( r_ld_util_fhs_pk , 0x400411c8 ) -PROVIDE ( r_ld_util_active_master_afh_map_set , 0x40041308 ) -PROVIDE ( r_ld_util_active_master_afh_map_get , 0x4004131c ) -PROVIDE ( r_ld_util_stp_unpk , 0x40041324 ) -PROVIDE ( r_ld_util_stp_pk , 0x400413f4 ) -PROVIDE ( r_llc_ch_assess_local , 0x40041494 ) -PROVIDE ( r_llc_ch_assess_get_local_ch_map , 0x4004150c ) -PROVIDE ( r_llc_ch_assess_get_current_ch_map , 0x40041574 ) -PROVIDE ( r_llc_ch_assess_merge_ch , 0x40041588 ) -PROVIDE ( r_llc_ch_assess_reass_ch , 0x400415c0 ) -PROVIDE ( llc_hci_cmd_handler_tab_p_get , 0x40042358 ) -PROVIDE ( llc_hci_command_handler , 0x40042360 ) -PROVIDE ( llc_hci_acl_data_tx_handler , 0x40042398 ) -PROVIDE ( llcp_pdu_handler_tab_p_get , 0x40043f64 ) -PROVIDE ( r_llc_llcp_version_ind_pdu_send , 0x40043f6c ) -PROVIDE ( r_llc_llcp_ch_map_update_pdu_send , 0x40043f94 ) -PROVIDE ( r_llc_llcp_pause_enc_req_pdu_send , 0x40043fd8 ) -PROVIDE ( r_llc_llcp_pause_enc_rsp_pdu_send , 0x40044010 ) -PROVIDE ( r_llc_llcp_enc_req_pdu_send , 0x40044064 ) -PROVIDE ( r_llc_llcp_enc_rsp_pdu_send , 0x40044160 ) -PROVIDE ( r_llc_llcp_start_enc_rsp_pdu_send , 0x400441f8 ) -PROVIDE ( r_llc_llcp_reject_ind_pdu_send , 0x4004425c ) -PROVIDE ( r_llc_llcp_con_update_pdu_send , 0x400442c4 ) -PROVIDE ( r_llc_llcp_con_param_req_pdu_send , 0x400442fc ) -PROVIDE ( r_llc_llcp_con_param_rsp_pdu_send , 0x40044358 ) -PROVIDE ( r_llc_llcp_feats_req_pdu_send , 0x400443b4 ) -PROVIDE ( r_llc_llcp_feats_rsp_pdu_send , 0x400443f0 ) -PROVIDE ( r_llc_llcp_start_enc_req_pdu_send , 0x4004441c ) -PROVIDE ( r_llc_llcp_terminate_ind_pdu_send , 0x400444b0 ) -PROVIDE ( r_llc_llcp_unknown_rsp_send_pdu , 0x40044534 ) -PROVIDE ( r_llc_llcp_ping_req_pdu_send , 0x4004454c ) -PROVIDE ( r_llc_llcp_ping_rsp_pdu_send , 0x40044560 ) -PROVIDE ( r_llc_llcp_length_req_pdu_send , 0x40044574 ) -PROVIDE ( r_llc_llcp_length_rsp_pdu_send , 0x400445ac ) -PROVIDE ( r_llc_llcp_tester_send , 0x400445e4 ) -PROVIDE ( r_llc_llcp_recv_handler , 0x40044678 ) -PROVIDE ( r_llc_llcp_get_autorize , 0x4004475c ) -PROVIDE ( r_llc_init , 0x40044778 ) -PROVIDE ( r_llc_reset , 0x400447b8 ) -PROVIDE ( r_llc_start , 0x400447f4 ) -PROVIDE ( r_llc_stop , 0x400449ac ) -PROVIDE ( r_llc_discon_event_complete_send , 0x40044a30 ) -PROVIDE ( r_llc_le_con_cmp_evt_send , 0x40044a78 ) -PROVIDE ( r_llc_con_update_complete_send , 0x40044d68 ) -PROVIDE ( r_llc_ltk_req_send , 0x40044dc0 ) -PROVIDE ( r_llc_feats_rd_event_send , 0x40044e0c ) -PROVIDE ( r_llc_version_rd_event_send , 0x40044e60 ) -PROVIDE ( r_llc_common_cmd_complete_send , 0x40044eac ) -PROVIDE ( r_llc_common_cmd_status_send , 0x40044ee0 ) -PROVIDE ( r_llc_common_flush_occurred_send , 0x40044f0c ) -PROVIDE ( r_llc_common_enc_key_ref_comp_evt_send , 0x40044f38 ) -PROVIDE ( r_llc_common_enc_change_evt_send , 0x40044f6c ) -PROVIDE ( r_llc_common_nb_of_pkt_comp_evt_send , 0x40045000 ) -PROVIDE ( r_llc_con_update_ind , 0x40045038 ) -PROVIDE ( r_llc_lsto_con_update , 0x40045098 ) -PROVIDE ( r_llc_map_update_ind , 0x400450f0 ) -PROVIDE ( r_llc_con_update_finished , 0x4004518c ) -PROVIDE ( r_llc_map_update_finished , 0x40045260 ) -PROVIDE ( llc_default_state_tab_p_get , 0x40046058 ) -PROVIDE ( r_llc_util_get_free_conhdl , 0x400460c8 ) -PROVIDE ( r_llc_util_get_nb_active_link , 0x40046100 ) -PROVIDE ( r_llc_util_dicon_procedure , 0x40046130 ) -PROVIDE ( r_llc_util_update_channel_map , 0x400461ac ) -PROVIDE ( r_llc_util_set_llcp_discard_enable , 0x400461c8 ) -PROVIDE ( r_llc_util_set_auth_payl_to_margin , 0x400461f4 ) -PROVIDE ( r_llc_util_clear_operation_ptr , 0x40046234 ) -PROVIDE ( r_llc_util_bw_mgt , 0x4004629c ) -PROVIDE ( r_llc_end_evt_defer , 0x40046330 ) -PROVIDE ( r_llc_pdu_llcp_tx_ack_defer , 0x400463ac ) -PROVIDE ( r_llc_pdu_acl_tx_ack_defer , 0x400464dc ) -PROVIDE ( r_llc_pdu_defer , 0x40046528 ) -PROVIDE ( r_lld_evt_delete_elt_handler , 0x40046974 ) -PROVIDE ( r_lld_evt_delete_elt_push , 0x40046a3c ) -PROVIDE ( r_lld_evt_channel_next , 0x40046aac ) -PROVIDE ( r_lld_evt_init , 0x40046b3c ) -PROVIDE ( r_lld_evt_init_evt , 0x40046cd0 ) -PROVIDE ( r_lld_evt_restart , 0x40046d50 ) -PROVIDE ( r_lld_evt_elt_insert , 0x400474c8 ) -PROVIDE ( r_lld_evt_elt_delete , 0x40047538 ) -PROVIDE ( r_lld_evt_drift_compute , 0x40047670 ) -PROVIDE ( r_lld_evt_schedule_next_instant , 0x400476a8 ) -PROVIDE ( r_lld_evt_schedule_next , 0x400477dc ) -PROVIDE ( r_lld_evt_schedule , 0x40047908 ) -PROVIDE ( r_lld_evt_prevent_stop , 0x40047adc ) -PROVIDE ( r_lld_evt_scan_create , 0x40047ae8 ) -PROVIDE ( r_lld_evt_move_to_master , 0x40047ba0 ) -PROVIDE ( r_lld_evt_update_create , 0x40047cd8 ) -PROVIDE ( r_lld_evt_move_to_slave , 0x40047e18 ) -PROVIDE ( r_lld_evt_slave_update , 0x40048138 ) -PROVIDE ( r_lld_evt_adv_create , 0x400481f4 ) -PROVIDE ( r_lld_evt_deffered_elt_handler , 0x400482bc ) -PROVIDE ( r_lld_evt_end , 0x400483e8 ) -PROVIDE ( r_lld_evt_rx , 0x40048578 ) -PROVIDE ( r_lld_evt_canceled , 0x400485c8 ) -PROVIDE ( r_lld_evt_end_isr , 0x4004862c ) -PROVIDE ( r_lld_evt_rx_isr , 0x40048678 ) -PROVIDE ( r_lld_init , 0x4004873c ) -PROVIDE ( r_lld_core_reset , 0x40048a9c ) -PROVIDE ( r_lld_adv_start , 0x40048b38 ) -PROVIDE ( r_lld_adv_stop , 0x40048ea0 ) -PROVIDE ( r_lld_scan_start , 0x40048ee0 ) -PROVIDE ( r_lld_scan_stop , 0x40049190 ) -PROVIDE ( r_lld_con_start , 0x400491f8 ) -PROVIDE ( r_lld_move_to_master , 0x400499e0 ) -PROVIDE ( r_lld_con_update_req , 0x40049b60 ) -PROVIDE ( r_lld_con_update_after_param_req , 0x40049bcc ) -PROVIDE ( r_lld_con_param_rsp , 0x40049e00 ) -PROVIDE ( r_lld_con_param_req , 0x40049f0c ) -PROVIDE ( r_lld_con_stop , 0x40049fdc ) -PROVIDE ( r_lld_get_mode , 0x40049ff8 ) -PROVIDE ( r_lld_move_to_slave , 0x4004a024 ) -PROVIDE ( r_lld_ch_map_ind , 0x4004a2f4 ) -PROVIDE ( r_lld_con_update_ind , 0x4004a30c ) -PROVIDE ( r_lld_crypt_isr , 0x4004a324 ) -PROVIDE ( r_lld_test_mode_tx , 0x4004a350 ) -PROVIDE ( r_lld_test_mode_rx , 0x4004a540 ) -PROVIDE ( r_lld_test_stop , 0x4004a710 ) -PROVIDE ( r_lld_ral_renew_req , 0x4004a73c ) -PROVIDE ( r_lld_pdu_check , 0x4004ac34 ) -PROVIDE ( r_lld_pdu_tx_loop , 0x4004ae40 ) -PROVIDE ( r_lld_pdu_data_tx_push , 0x4004aecc ) -PROVIDE ( r_lld_pdu_data_send , 0x4004b018 ) -PROVIDE ( r_lld_pdu_tx_push , 0x4004b080 ) -PROVIDE ( r_lld_pdu_tx_prog , 0x4004b120 ) -PROVIDE ( r_lld_pdu_tx_flush , 0x4004b414 ) -PROVIDE ( r_lld_pdu_adv_pack , 0x4004b488 ) -PROVIDE ( r_lld_pdu_rx_handler , 0x4004b4d4 ) -PROVIDE ( r_lld_pdu_send_packet , 0x4004b774 ) -PROVIDE ( r_lld_util_instant_get , 0x4004b890 ) -PROVIDE ( r_lld_util_get_bd_address , 0x4004b8ac ) -PROVIDE ( r_lld_util_set_bd_address , 0x4004b8f8 ) -PROVIDE ( r_lld_util_ral_force_rpa_renew , 0x4004b980 ) -PROVIDE ( r_lld_util_freq2chnl , 0x4004b9e4 ) -PROVIDE ( r_lld_util_get_local_offset , 0x4004ba10 ) -PROVIDE ( r_lld_util_get_peer_offset , 0x4004ba24 ) -PROVIDE ( r_lld_util_connection_param_set , 0x4004ba40 ) -PROVIDE ( r_lld_util_dle_set_cs_fields , 0x4004ba90 ) -PROVIDE ( r_lld_util_anchor_point_move , 0x4004bacc ) -PROVIDE ( r_lld_util_flush_list , 0x4004bbd8 ) -PROVIDE ( r_lld_util_instant_ongoing , 0x4004bbfc ) -PROVIDE ( r_lld_util_compute_ce_max , 0x4004bc0c ) -PROVIDE ( r_lld_util_elt_programmed , 0x4004bce0 ) -PROVIDE ( r_lld_util_priority_set , 0x4004bd10 ) -PROVIDE ( r_lld_util_priority_update , 0x4004bd78 ) -PROVIDE ( r_lld_util_get_tx_pkt_cnt , 0x4004bd80 ) -PROVIDE ( r_lld_util_eff_tx_time_set , 0x4004bd88 ) -PROVIDE ( r_lld_wlcoex_set , 0x4004bd98 ) -PROVIDE ( llm_hci_cmd_handler_tab_p_get , 0x4004c920 ) -PROVIDE ( hci_command_handler , 0x4004c928 ) -PROVIDE ( r_llm_init , 0x4004c9f8 ) -PROVIDE ( r_llm_ble_ready , 0x4004cc34 ) -PROVIDE ( r_llm_con_req_ind , 0x4004cc54 ) -PROVIDE ( r_llm_le_adv_report_ind , 0x4004cdf4 ) -PROVIDE ( r_llm_con_req_tx_cfm , 0x4004d158 ) -PROVIDE ( r_llm_common_cmd_complete_send , 0x4004d288 ) -PROVIDE ( r_llm_common_cmd_status_send , 0x4004d2b4 ) -PROVIDE ( r_llm_test_mode_start_tx , 0x4004d2fc ) -PROVIDE ( r_llm_test_mode_start_rx , 0x4004d534 ) -PROVIDE ( r_llm_set_adv_param , 0x4004d5f4 ) -PROVIDE ( r_llm_set_adv_en , 0x4004d7ec ) -PROVIDE ( r_llm_set_adv_data , 0x4004d960 ) -PROVIDE ( r_llm_set_scan_rsp_data , 0x4004da14 ) -PROVIDE ( r_llm_set_scan_param , 0x4004dac8 ) -PROVIDE ( r_llm_set_scan_en , 0x4004db64 ) -PROVIDE ( r_llm_wl_clr , 0x4004dc54 ) -PROVIDE ( r_llm_wl_dev_add , 0x4004dcc0 ) -PROVIDE ( r_llm_wl_dev_rem , 0x4004dcfc ) -PROVIDE ( r_llm_wl_dev_add_hdl , 0x4004dd38 ) -PROVIDE ( r_llm_wl_dev_rem_hdl , 0x4004dde0 ) -PROVIDE ( r_llm_create_con , 0x4004de78 ) -PROVIDE ( r_llm_encryption_done , 0x4004dff8 ) -PROVIDE ( r_llm_encryption_start , 0x4004e128 ) -PROVIDE ( r_llm_ral_clear , 0x4004e1fc ) -PROVIDE ( r_llm_ral_dev_add , 0x4004e23c ) -PROVIDE ( r_llm_ral_dev_rm , 0x4004e3bc ) -PROVIDE ( r_llm_ral_get_rpa , 0x4004e400 ) -PROVIDE ( r_llm_ral_set_timeout , 0x4004e4a0 ) -PROVIDE ( r_llm_ral_update , 0x4004e4f8 ) -PROVIDE ( llm_default_state_tab_p_get , 0x4004e718 ) -PROVIDE ( r_llm_util_bd_addr_wl_position , 0x4004e720 ) -PROVIDE ( r_llm_util_bd_addr_in_wl , 0x4004e788 ) -PROVIDE ( r_llm_util_check_address_validity , 0x4004e7e4 ) -PROVIDE ( r_llm_util_check_map_validity , 0x4004e800 ) -PROVIDE ( r_llm_util_apply_bd_addr , 0x4004e868 ) -PROVIDE ( r_llm_util_set_public_addr , 0x4004e89c ) -PROVIDE ( r_llm_util_check_evt_mask , 0x4004e8b0 ) -PROVIDE ( r_llm_util_get_channel_map , 0x4004e8d4 ) -PROVIDE ( r_llm_util_get_supp_features , 0x4004e8e8 ) -PROVIDE ( r_llm_util_adv_data_update , 0x4004e8fc ) -PROVIDE ( r_llm_util_bl_check , 0x4004e930 ) -PROVIDE ( r_llm_util_bl_add , 0x4004e9ac ) -PROVIDE ( r_llm_util_bl_rem , 0x4004ea70 ) -PROVIDE ( r_llm_util_bd_addr_in_ral , 0x4004eb08 ) -PROVIDE ( r_llm_end_evt_defer , 0x4004eb6c ) -PROVIDE ( r_llm_pdu_defer , 0x4004ec48 ) -PROVIDE ( r_lm_init , 0x4004ed34 ) -PROVIDE ( r_lm_lt_addr_alloc , 0x4004ef1c ) -PROVIDE ( r_lm_lt_addr_reserve , 0x4004ef48 ) -PROVIDE ( r_lm_lt_addr_free , 0x4004ef74 ) -PROVIDE ( r_lm_get_nb_acl , 0x4004ef9c ) -PROVIDE ( r_lm_role_switch_start , 0x4004efe0 ) -PROVIDE ( r_lm_role_switch_finished , 0x4004f028 ) -PROVIDE ( r_lm_read_features , 0x4004f0d8 ) -PROVIDE ( r_lm_acl_disc , 0x4004f148 ) -PROVIDE ( r_lm_get_auth_en , 0x4004f1ac ) -PROVIDE ( r_lm_get_sp_en , 0x4004f1c0 ) -PROVIDE ( r_lm_get_sec_con_host_supp , 0x4004f1d4 ) -PROVIDE ( r_LM_GetPINType , 0x4004f1e8 ) -PROVIDE ( r_LM_GetConnectionAcceptTimeout , 0x4004f1f4 ) -PROVIDE ( r_LM_GetLocalNameSeg , 0x4004f200 ) -PROVIDE ( r_lm_get_loopback_mode , 0x4004f248 ) -PROVIDE ( r_lm_get_pub_key_192 , 0x4004f258 ) -PROVIDE ( r_lm_get_priv_key_192 , 0x4004f278 ) -PROVIDE ( r_lm_get_pub_key_256 , 0x4004f298 ) -PROVIDE ( r_lm_get_priv_key_256 , 0x4004f2b8 ) -PROVIDE ( r_lm_get_oob_local_data_192 , 0x4004f2d4 ) -PROVIDE ( r_lm_get_oob_local_data_256 , 0x4004f318 ) -PROVIDE ( r_lm_get_oob_local_commit , 0x4004f374 ) -PROVIDE ( r_lm_sp_debug_mode_get , 0x4004f398 ) -PROVIDE ( r_lm_debug_key_compare_192 , 0x4004f3a8 ) -PROVIDE ( r_lm_debug_key_compare_256 , 0x4004f3d0 ) -PROVIDE ( r_lm_dut_mode_en_get , 0x4004f3ec ) -PROVIDE ( r_lm_afh_ch_ass_en_get , 0x4004f3f8 ) -PROVIDE ( r_lm_sync_flow_ctrl_en_get , 0x4004f404 ) -PROVIDE ( r_lm_afh_host_ch_class_get , 0x4004f410 ) -PROVIDE ( r_lm_afh_peer_ch_class_set , 0x4004f418 ) -PROVIDE ( r_lm_afh_master_ch_map_get , 0x4004f43c ) -PROVIDE ( r_lm_afh_activate_timer , 0x4004f444 ) -PROVIDE ( r_lm_is_acl_con , 0x4004f47c ) -PROVIDE ( r_lm_is_acl_con_role , 0x4004f49c ) -PROVIDE ( r_lm_is_clk_adj_instant_pending , 0x4004f4c8 ) -PROVIDE ( r_lm_clk_adj_instant_pending_set , 0x4004f4d8 ) -PROVIDE ( r_lm_is_clk_adj_ack_pending , 0x4004f4e8 ) -PROVIDE ( r_lm_num_clk_adj_ack_pending_set , 0x4004f500 ) -PROVIDE ( r_lm_clk_adj_ack_pending_clear , 0x4004f514 ) -PROVIDE ( r_lm_local_ext_fr_configured , 0x4004f540 ) -PROVIDE ( r_lm_pca_sscan_link_set , 0x4004f550 ) -PROVIDE ( r_lm_pca_sscan_link_get , 0x4004f560 ) -PROVIDE ( r_lm_get_sync_param , 0x400503b4 ) -PROVIDE ( r_lm_init_sync , 0x400512d8 ) -PROVIDE ( r_lm_reset_sync , 0x40051304 ) -PROVIDE ( r_lm_check_active_sync , 0x40051334 ) -PROVIDE ( r_lm_add_sync , 0x40051358 ) -PROVIDE ( r_lm_modif_sync , 0x40051578 ) -PROVIDE ( r_lm_check_sync_hl_rsp , 0x4005169c ) -PROVIDE ( r_lm_get_synchdl , 0x4005175c ) -PROVIDE ( r_lm_look_for_sync , 0x40051774 ) -PROVIDE ( r_lm_get_nb_sync_link , 0x4005179c ) -PROVIDE ( r_lm_get_min_sync_intv , 0x400517a8 ) -PROVIDE ( r_lm_remove_sync , 0x400517c8 ) -PROVIDE ( r_lm_sco_nego_end , 0x40051828 ) -PROVIDE ( r_lm_master_clk_adj_req_handler , 0x40054180 ) -PROVIDE ( lm_hci_cmd_handler_tab_p_get , 0x4005425c ) -PROVIDE ( lm_default_state_tab_p_get , 0x40054268 ) -PROVIDE ( btdm_r_modules_func_p_set , 0x40054270 ) -PROVIDE ( btdm_r_modules_func_p_get , 0x4005427c ) -PROVIDE ( btdm_r_plf_func_p_set , 0x40054288 ) -PROVIDE ( btdm_r_import_rf_phy_func_p_get , 0x40054298 ) -PROVIDE ( r_nvds_init , 0x40054410 ) -PROVIDE ( r_nvds_get , 0x40054488 ) -PROVIDE ( r_nvds_del , 0x400544c4 ) -PROVIDE ( r_nvds_lock , 0x400544fc ) -PROVIDE ( r_nvds_put , 0x40054534 ) -PROVIDE ( r_rf_rw_bt_init , 0x40054868 ) -PROVIDE ( r_rf_rw_le_init , 0x400549d0 ) -PROVIDE ( r_rf_rw_init , 0x40054b0c ) -PROVIDE ( r_rwble_init , 0x40054bf4 ) -PROVIDE ( r_rwble_reset , 0x40054ce8 ) -PROVIDE ( r_rwble_sleep_check , 0x40054d78 ) -PROVIDE ( r_rwble_activity_ongoing_check , 0x40054d8c ) -PROVIDE ( r_rwble_version , 0x40054dac ) -PROVIDE ( r_rwble_isr , 0x40054e08 ) -PROVIDE ( r_rwbt_init , 0x40055160 ) -PROVIDE ( r_rwbt_reset , 0x400551bc ) -PROVIDE ( r_rwbt_version , 0x4005520c ) -PROVIDE ( r_rwbt_isr , 0x40055248 ) -PROVIDE ( r_rwbt_sleep_check , 0x4005577c ) -PROVIDE ( r_rwbt_sleep_enter , 0x400557a4 ) -PROVIDE ( r_rwbt_sleep_wakeup , 0x400557fc ) -PROVIDE ( r_rwbt_sleep_wakeup_end , 0x400558cc ) -PROVIDE ( rwip_rf_p_get , 0x400558f4 ) -PROVIDE ( r_rwip_check_wakeup_boundary , 0x400558fc ) -PROVIDE ( r_rwip_init , 0x4005595c ) -PROVIDE ( r_rwip_reset , 0x40055ab8 ) -PROVIDE ( r_rwip_version , 0x40055b20 ) -PROVIDE ( r_rwip_schedule , 0x40055b38 ) -PROVIDE ( r_rwip_sleep , 0x40055b5c ) -PROVIDE ( r_rwip_wakeup , 0x40055dc4 ) -PROVIDE ( r_rwip_wakeup_end , 0x40055e18 ) -PROVIDE ( r_rwip_wakeup_delay_set , 0x40055e4c ) -PROVIDE ( r_rwip_prevent_sleep_set , 0x40055e64 ) -PROVIDE ( r_rwip_prevent_sleep_clear , 0x40055ec8 ) -PROVIDE ( r_rwip_sleep_enable , 0x40055f30 ) -PROVIDE ( r_rwip_ext_wakeup_enable , 0x40055f3c ) -PROVIDE ( r_rwip_pca_clock_dragging_only , 0x40055f48 ) -PROVIDE ( r_rwip_wlcoex_set , 0x40055f60 ) -PROVIDE ( r_rwip_assert_err , 0x40055f88 ) -PROVIDE ( r_ke_queue_extract , 0x40055fd0 ) -PROVIDE ( r_ke_queue_insert , 0x40056020 ) -PROVIDE ( __utoa , 0x400561f0 ) -PROVIDE ( utoa , 0x40056258 ) -PROVIDE ( setjmp , 0x40056268 ) -PROVIDE ( longjmp , 0x400562cc ) -PROVIDE ( abs , 0x40056340 ) -PROVIDE ( div , 0x40056348 ) -PROVIDE ( labs , 0x40056370 ) -PROVIDE ( ldiv , 0x40056378 ) -PROVIDE ( qsort , 0x40056424 ) -PROVIDE ( __itoa , 0x40056678 ) -PROVIDE ( itoa , 0x400566b4 ) -PROVIDE ( atoi , 0x400566c4 ) -PROVIDE ( _atoi_r , 0x400566d4 ) -PROVIDE ( atol , 0x400566ec ) -PROVIDE ( _atol_r , 0x400566fc ) -PROVIDE ( _strtol_r , 0x40056714 ) -PROVIDE ( strtol , 0x4005681c ) -PROVIDE ( _strtoul_r , 0x40056834 ) -PROVIDE ( strtoul , 0x4005692c ) -PROVIDE ( _iprintf_r , 0x40056944 ) -PROVIDE ( _printf_r , 0x40056944 ) -PROVIDE ( iprintf , 0x40056978 ) -PROVIDE ( printf , 0x40056978 ) -PROVIDE ( viprintf , 0x400569b4 ) -PROVIDE ( vprintf , 0x400569b4 ) -PROVIDE ( _viprintf_r , 0x400569e4 ) -PROVIDE ( _vprintf_r , 0x400569e4 ) -PROVIDE ( _vsniprintf_r , 0x40056a14 ) -PROVIDE ( _vsnprintf_r , 0x40056a14 ) -PROVIDE ( vsniprintf , 0x40056a68 ) -PROVIDE ( vsnprintf , 0x40056a68 ) -PROVIDE ( _vsiprintf_r , 0x40056a90 ) -PROVIDE ( _vsprintf_r , 0x40056a90 ) -PROVIDE ( vsiprintf , 0x40056ac4 ) -PROVIDE ( vsprintf , 0x40056ac4 ) -PROVIDE ( _sniprintf_r , 0x40056ae4 ) -PROVIDE ( _snprintf_r , 0x40056ae4 ) -PROVIDE ( sniprintf , 0x40056b4c ) -PROVIDE ( snprintf , 0x40056b4c ) -PROVIDE ( _siprintf_r , 0x40056bbc ) -PROVIDE ( _sprintf_r , 0x40056bbc ) -PROVIDE ( siprintf , 0x40056c08 ) -PROVIDE ( sprintf , 0x40056c08 ) -PROVIDE ( _asniprintf_r , 0x40056c64 ) -PROVIDE ( _asnprintf_r , 0x40056c64 ) -PROVIDE ( asniprintf , 0x40056cd8 ) -PROVIDE ( asnprintf , 0x40056cd8 ) -PROVIDE ( _asiprintf_r , 0x40056d4c ) -PROVIDE ( _asprintf_r , 0x40056d4c ) -PROVIDE ( asiprintf , 0x40056d9c ) -PROVIDE ( asprintf , 0x40056d9c ) -PROVIDE ( _vasniprintf_r , 0x40056df8 ) -PROVIDE ( _vasnprintf_r , 0x40056df8 ) -PROVIDE ( vasniprintf , 0x40056e58 ) -PROVIDE ( vasnprintf , 0x40056e58 ) -PROVIDE ( _vasiprintf_r , 0x40056e80 ) -PROVIDE ( _vasprintf_r , 0x40056e80 ) -PROVIDE ( vasiprintf , 0x40056eb8 ) -PROVIDE ( vasprintf , 0x40056eb8 ) -PROVIDE ( _fiprintf_r , 0x40056ed8 ) -PROVIDE ( _fprintf_r , 0x40056ed8 ) -PROVIDE ( fiprintf , 0x40056efc ) -PROVIDE ( fprintf , 0x40056efc ) -PROVIDE ( __ssputs_r , 0x40056f2c ) -PROVIDE ( __ssprint_r , 0x40056ff8 ) -PROVIDE ( _svfiprintf_r , 0x40057100 ) -PROVIDE ( _svfprintf_r , 0x40057100 ) -PROVIDE ( _printf_common , 0x40057338 ) -PROVIDE ( _printf_i , 0x40057404 ) -PROVIDE ( __sfputs_r , 0x40057790 ) -PROVIDE ( __sprint_r , 0x400577e4 ) -PROVIDE ( _vfiprintf_r , 0x40057850 ) -PROVIDE ( _vfprintf_r , 0x40057850 ) -PROVIDE ( vfiprintf , 0x40057ae8 ) -PROVIDE ( vfprintf , 0x40057ae8 ) -PROVIDE ( __svfiscanf_r , 0x40057b08 ) -PROVIDE ( __svfscanf_r , 0x40057b08 ) -PROVIDE ( vfiscanf , 0x40057eb8 ) -PROVIDE ( vfscanf , 0x40057eb8 ) -PROVIDE ( __svfscanf , 0x40057f04 ) -PROVIDE ( _vfiscanf_r , 0x40057f24 ) -PROVIDE ( _vfscanf_r , 0x40057f24 ) -PROVIDE ( _sungetc_r , 0x40057f6c ) -PROVIDE ( __ssrefill_r , 0x40057fec ) -PROVIDE ( __ssvfiscanf_r , 0x4005802c ) -PROVIDE ( __ssvfscanf_r , 0x4005802c ) -PROVIDE ( _scanf_chars , 0x40058384 ) -PROVIDE ( _scanf_i , 0x4005845c ) -PROVIDE ( viscanf , 0x40058698 ) -PROVIDE ( vscanf , 0x40058698 ) -PROVIDE ( _viscanf_r , 0x400586c8 ) -PROVIDE ( _vscanf_r , 0x400586c8 ) -PROVIDE ( _vsiscanf_r , 0x400586f8 ) -PROVIDE ( _vsscanf_r , 0x400586f8 ) -PROVIDE ( vsiscanf , 0x40058740 ) -PROVIDE ( vsscanf , 0x40058740 ) -PROVIDE ( iscanf , 0x40058760 ) -PROVIDE ( scanf , 0x40058760 ) -PROVIDE ( _iscanf_r , 0x4005879c ) -PROVIDE ( _scanf_r , 0x4005879c ) -PROVIDE ( siscanf , 0x400587d0 ) -PROVIDE ( sscanf , 0x400587d0 ) -PROVIDE ( _siscanf_r , 0x40058830 ) -PROVIDE ( _sscanf_r , 0x40058830 ) -PROVIDE ( fiscanf , 0x40058884 ) -PROVIDE ( fscanf , 0x40058884 ) -PROVIDE ( _fiscanf_r , 0x400588b4 ) -PROVIDE ( _fscanf_r , 0x400588b4 ) -PROVIDE ( _wcrtomb_r , 0x400588d8 ) -PROVIDE ( wcrtomb , 0x40058920 ) -PROVIDE ( __sfvwrite_r , 0x4005893c ) -PROVIDE ( __swbuf_r , 0x40058bec ) -PROVIDE ( __swbuf , 0x40058cb4 ) -PROVIDE ( __swsetup_r , 0x40058cc8 ) -PROVIDE ( __fputwc , 0x40058da0 ) -PROVIDE ( _fputwc_r , 0x40058e4c ) -PROVIDE ( fputwc , 0x40058ea8 ) -PROVIDE ( __ascii_wctomb , 0x40058ef0 ) -PROVIDE ( _wctomb_r , 0x40058f14 ) -PROVIDE ( __submore , 0x40058f3c ) -PROVIDE ( _ungetc_r , 0x40058fa0 ) -PROVIDE ( ungetc , 0x400590f4 ) -PROVIDE ( __smakebuf_r , 0x40059108 ) -PROVIDE ( __sflush_r , 0x400591e0 ) -PROVIDE ( _fflush_r , 0x40059320 ) -PROVIDE ( fflush , 0x40059394 ) -PROVIDE ( __srefill_r , 0x400593d4 ) -PROVIDE ( _setlocale_r , 0x4005950c ) -PROVIDE ( __locale_charset , 0x40059540 ) -PROVIDE ( __locale_mb_cur_max , 0x40059548 ) -PROVIDE ( __locale_msgcharset , 0x40059550 ) -PROVIDE ( __locale_cjk_lang , 0x40059558 ) -PROVIDE ( _localeconv_r , 0x40059560 ) -PROVIDE ( setlocale , 0x40059568 ) -PROVIDE ( localeconv , 0x4005957c ) -PROVIDE ( asctime , 0x40059588 ) -PROVIDE ( ctime , 0x400595b0 ) -PROVIDE ( ctime_r , 0x400595c4 ) -PROVIDE ( localtime , 0x400595dc ) -PROVIDE ( localtime_r , 0x400595fc ) -PROVIDE ( gmtime , 0x40059848 ) -PROVIDE ( gmtime_r , 0x40059868 ) -PROVIDE ( strftime , 0x40059ab4 ) -PROVIDE ( mktime , 0x4005a5e8 ) -PROVIDE ( start_tb_console , 0x4005a980 ) -PROVIDE ( ets_sha_enable , 0x4005c07c ) -PROVIDE ( ets_sha_disable , 0x4005c0a8 ) -PROVIDE ( ets_sha_init , 0x4005c0d4 ) -PROVIDE ( ets_sha_finish , 0x4005c104 ) -PROVIDE ( ets_sha_update , 0x4005c2a0 ) -PROVIDE ( ets_bigint_enable , 0x4005c498 ) -PROVIDE ( ets_bigint_disable , 0x4005c4e0 ) -PROVIDE ( ets_bigint_wait_finish , 0x4005c520 ) -PROVIDE ( ets_bigint_mod_power_prepare , 0x4005c54c ) -PROVIDE ( ets_bigint_mod_power_getz , 0x4005c614 ) -PROVIDE ( ets_bigint_mult_prepare , 0x4005c630 ) -PROVIDE ( ets_bigint_mult_getz , 0x4005c6e8 ) -PROVIDE ( ets_bigint_montgomery_mult_prepare , 0x4005c6fc ) -PROVIDE ( ets_bigint_montgomery_mult_getz , 0x4005c7a4 ) -PROVIDE ( ets_bigint_mod_mult_prepare , 0x4005c7b4 ) -PROVIDE ( ets_bigint_mod_mult_getz , 0x4005c818 ) -PROVIDE ( ets_aes_enable , 0x4005c8cc ) -PROVIDE ( ets_aes_disable , 0x4005c8f8 ) -PROVIDE ( ets_aes_set_endian , 0x4005c928 ) -PROVIDE ( ets_aes_setkey_enc , 0x4005c97c ) -PROVIDE ( ets_aes_setkey_dec , 0x4005c994 ) -PROVIDE ( ets_aes_crypt , 0x4005c9b8 ) -PROVIDE ( ets_secure_boot_start , 0x4005ca34 ) -PROVIDE ( ets_secure_boot_finish , 0x4005ca84 ) -PROVIDE ( ets_secure_boot_hash , 0x4005cad4 ) -PROVIDE ( ets_secure_boot_obtain , 0x4005cb14 ) -PROVIDE ( ets_secure_boot_check , 0x4005cb40 ) -PROVIDE ( ets_secure_boot_rd_iv , 0x4005cb84 ) -PROVIDE ( ets_secure_boot_rd_abstract , 0x4005cba8 ) -PROVIDE ( ets_secure_boot_check_start , 0x4005cbcc ) -PROVIDE ( ets_secure_boot_check_finish , 0x4005cc04 ) -PROVIDE ( aes_128_cbc_encrypt , 0x4005cc18 ) -PROVIDE ( aes_128_cbc_decrypt , 0x4005cc7c ) -PROVIDE ( aes_unwrap , 0x4005ccf0 ) -PROVIDE ( base64_encode , 0x4005cdbc ) -PROVIDE ( base64_decode , 0x4005ced8 ) -PROVIDE ( crc32_le , 0x4005cfec ) -PROVIDE ( crc32_be , 0x4005d024 ) -PROVIDE ( crc16_le , 0x4005d05c ) -PROVIDE ( crc16_be , 0x4005d09c ) -PROVIDE ( crc8_le , 0x4005d0e0 ) -PROVIDE ( crc8_be , 0x4005d114 ) -PROVIDE ( esp_crc8 , 0x4005d144 ) -PROVIDE ( hmac_md5_vector , 0x4005d17c ) -PROVIDE ( hmac_md5 , 0x4005d264 ) -PROVIDE ( MD5Init , 0x4005da7c ) -PROVIDE ( MD5Update , 0x4005da9c ) -PROVIDE ( MD5Final , 0x4005db1c ) -PROVIDE ( md5_vector , 0x4005db80 ) -PROVIDE ( mz_adler32 , 0x4005edbc ) -PROVIDE ( mz_crc32 , 0x4005ee88 ) -PROVIDE ( mz_free , 0x4005eed4 ) -PROVIDE ( tinfl_decompress , 0x4005ef30 ) -PROVIDE ( tinfl_decompress_mem_to_mem , 0x40060050 ) -PROVIDE ( tinfl_decompress_mem_to_callback , 0x40060090 ) -PROVIDE ( tdefl_compress , 0x400600bc ) -PROVIDE ( tdefl_compress_buffer , 0x400607f4 ) -PROVIDE ( tdefl_init , 0x40060810 ) -PROVIDE ( tdefl_get_prev_return_status , 0x400608d0 ) -PROVIDE ( tdefl_get_adler32 , 0x400608d8 ) -PROVIDE ( tdefl_compress_mem_to_output , 0x400608e0 ) -PROVIDE ( tdefl_compress_mem_to_mem , 0x40060900 ) -PROVIDE ( tdefl_write_image_to_png_file_in_memory_ex , 0x40060910 ) -PROVIDE ( tdefl_write_image_to_png_file_in_memory , 0x4006091c ) -PROVIDE ( rc4_skip , 0x40060928 ) -PROVIDE ( hmac_sha1_vector , 0x400609e4 ) -PROVIDE ( hmac_sha1 , 0x40060acc ) -PROVIDE ( sha1_prf , 0x40060ae8 ) -PROVIDE ( sha1_vector , 0x40060b64 ) -PROVIDE ( pbkdf2_sha1 , 0x40060ba4 ) -PROVIDE ( hmac_sha256_vector , 0x40060c84 ) -PROVIDE ( hmac_sha256 , 0x40060d58 ) -PROVIDE ( sha256_prf , 0x40060d70 ) -PROVIDE ( sha256_vector , 0x40060e08 ) -PROVIDE ( jd_prepare , 0x40060fa8 ) -PROVIDE ( jd_decomp , 0x400613e8 ) -PROVIDE ( spi_dummy_len_fix , 0x40061d90 ) -PROVIDE ( SelectSpiQIO , 0x40061ddc ) -PROVIDE ( SetSpiDrvs , 0x40061e78 ) -PROVIDE ( SelectSpiFunction , 0x40061f84 ) -PROVIDE ( SPI_user_command_read , 0x400621b0 ) -PROVIDE ( SPI_read_status , 0x4006226c ) -PROVIDE ( SPI_write_status , 0x400622f0 ) -PROVIDE ( SPI_write_enable , 0x40062320 ) -PROVIDE ( SPI_read_status_high , 0x40062448 ) -PROVIDE ( SPI_Common_Command , 0x4006246c ) -PROVIDE ( spi_cache_sram_init , 0x400626e4 ) -PROVIDE ( SPIUnlockXXX , 0x400628b0 ) -PROVIDE ( SPILock , 0x400628f0 ) -PROVIDE ( SPIReadModeCnfig , 0x40062944 ) -PROVIDE ( spi_flash_attach , 0x40062a6c ) -PROVIDE ( SPIMasterReadModeCnfig , 0x40062b64 ) -PROVIDE ( SPIClkConfig , 0x40062bc8 ) -PROVIDE ( SPIEraseChip , 0x40062c14 ) -PROVIDE ( SPIEraseBlock , 0x40062c4c ) -PROVIDE ( SPIEraseSector , 0x40062ccc ) -PROVIDE ( SPIWrite , 0x40062d50 ) -PROVIDE ( SPI_Write_Encrypt_Enable , 0x40062df4 ) -PROVIDE ( SPI_Prepare_Encrypt_Data , 0x40062e1c ) -PROVIDE ( SPI_Write_Encrypt_Disable , 0x40062e60 ) -PROVIDE ( SPI_Encrypt_Write , 0x40062e78 ) -PROVIDE ( SPIRead , 0x40062ed8 ) -PROVIDE ( SPIEraseArea , 0x400631ac ) -PROVIDE ( SPIParamCfg , 0x40063238 ) -PROVIDE ( __mulsf3 , 0x400632c8 ) -PROVIDE ( __eqsf2 , 0x40063374 ) -PROVIDE ( __nesf2 , 0x40063374 ) -PROVIDE ( __gtsf2 , 0x400633a0 ) -PROVIDE ( __lesf2 , 0x400633c0 ) -PROVIDE ( __gesf2 , 0x4006340c ) -PROVIDE ( __ltsf2 , 0x4006342c ) -PROVIDE ( __unordsf2 , 0x40063478 ) -PROVIDE ( __negdf2 , 0x400634a0 ) -PROVIDE ( __muldf3 , 0x4006358c ) -PROVIDE ( __eqdf2 , 0x400636a8 ) -PROVIDE ( __nedf2 , 0x400636a8 ) -PROVIDE ( __gtdf2 , 0x400636dc ) -PROVIDE ( __ledf2 , 0x40063704 ) -PROVIDE ( __gedf2 , 0x40063768 ) -PROVIDE ( __ltdf2 , 0x40063790 ) -PROVIDE ( __unorddf2 , 0x400637f4 ) -PROVIDE ( __cmpdi2 , 0x40063820 ) -PROVIDE ( __ucmpdi2 , 0x40063840 ) -PROVIDE ( __clear_cache , 0x40063860 ) -PROVIDE ( __absvsi2 , 0x40063868 ) -PROVIDE ( __absvdi2 , 0x4006387c ) -PROVIDE ( __powisf2 , 0x4006389c ) -PROVIDE ( __powidf2 , 0x400638d4 ) -PROVIDE ( __mulsc3 , 0x40063934 ) -PROVIDE ( __muldc3 , 0x40063bf4 ) -PROVIDE ( __divsc3 , 0x40064200 ) -PROVIDE ( __divdc3 , 0x40064460 ) -PROVIDE ( __bswapsi2 , 0x4006499c ) -PROVIDE ( __bswapdi2 , 0x400649c4 ) -PROVIDE ( __clrsbsi2 , 0x40064a20 ) -PROVIDE ( __clrsbdi2 , 0x40064a38 ) -PROVIDE ( __gcc_bcmp , 0x40064a70 ) -PROVIDE ( __udiv_w_sdiv , 0x40064aa8 ) -PROVIDE ( __udivmoddi4 , 0x40064ab0 ) diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/Makefile b/src/mongoose-6.11/src/common/platforms/esp32/stubs/Makefile deleted file mode 100644 index 857512a..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp32/stubs/Makefile +++ /dev/null @@ -1,51 +0,0 @@ -# -# Copyright (c) 2015 Cesanta Software Limited -# All rights reserved -# - -STUB = stub_hello.c -LIBS = -PARAMS = -PORT = /dev/ttyUSB0 - -BUILD_DIR = .build -COMMON_STUB_DIR = ../../esp -STUB_ELF = $(BUILD_DIR)/$(patsubst %.c,%.elf,$(notdir $(STUB))) -STUB_JSON ?= $(BUILD_DIR)/$(patsubst %.c,%.json,$(notdir $(STUB))) -SDK = $(shell cat ../../../../fw/platforms/esp32/sdk.version) -XT_CC = xtensa-esp32-elf-gcc - -.PHONY: all clean run wrap - -all: $(STUB_ELF) - -$(STUB_ELF): $(STUB) $(LIBS) - @echo " CC $^ -> $@" - @[ -d $(BUILD_DIR) ] || mkdir $(BUILD_DIR) - @docker run --rm -i -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \ - "cd /src/common/platforms/esp32/stubs && \ - $(XT_CC) -std=c99 -Wall -Werror -Os -DESP32 \ - -mtext-section-literals -mlongcalls -nostdlib -fno-builtin \ - -I. -I/src/common/platforms/esp \ - -I/opt/Espressif/esp-idf/components/esp32/include \ - -I/opt/Espressif/esp-idf/components/soc/esp32/include \ - -L/opt/Espressif/esp-idf -Wl,-static \ - -ffunction-sections -Wl,--gc-sections \ - -Tstub.ld -o $@ $^" - -wrap: $(STUB_JSON) - -$(STUB_JSON): $(STUB_ELF) $(COMMON_STUB_DIR)/esptool.py - @echo " WRAP $< -> $@" - @docker run --rm -i -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \ - "cd /src/common/platforms/esp32/stubs && \ - $(COMMON_STUB_DIR)/esptool.py wrap_stub $<" > $@ - -run: $(STUB_JSON) - @echo " RUN $< $(PARAMS) -> $(PORT)" - @docker run --rm -i --privileged -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \ - "cd /src/common/platforms/esp32/stubs && \ - $(COMMON_STUB_DIR)/esptool.py --port $(PORT) run_stub $< $(PARAMS)" - -clean: - @rm -rf $(BUILD_DIR) diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/README.md b/src/mongoose-6.11/src/common/platforms/esp32/stubs/README.md deleted file mode 100644 index 9265771..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp32/stubs/README.md +++ /dev/null @@ -1,13 +0,0 @@ -This is a ESP boot loader stub development environment. - -Code produced in this environment can be loaded and executed -in the bootloader environment. Usually it is used to implement -functionality not found in the bootloader. - -Stubs can be executed using the `run_stub` command of the modified esptool.py provided. -`wrap_stub` produces a JSON represenattion of the stub that can later be reused -or built into other tools. - -Example usage: - $ make run STUB=stub_flash_size.c PORT=/dev/ttyUSB0 - $ make run STUB=stub_md5.c PORT=/dev/ttyUSB0 PARAMS="0x11000 10000 1" diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/led.c b/src/mongoose-6.11/src/common/platforms/esp32/stubs/led.c deleted file mode 100644 index 6cfc766..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp32/stubs/led.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2014-2017 Cesanta Software Limited - * All rights reserved - */ - -#include "soc/gpio_reg.h" - -void led_setup(int io) { - if (io < 32) { - WRITE_PERI_REG(GPIO_ENABLE_W1TS_REG, 1 << io); - } else { - WRITE_PERI_REG(GPIO_ENABLE1_W1TS_REG, 1 << (io - 32)); - } -} - -void led_on(int io) { - if (io < 32) { - WRITE_PERI_REG(GPIO_OUT_W1TS_REG, 1 << io); - } else { - WRITE_PERI_REG(GPIO_OUT1_W1TS_REG, 1 << (io - 32)); - } -} - -void led_off(int io) { - if (io < 32) { - WRITE_PERI_REG(GPIO_OUT_W1TC_REG, 1 << io); - } else { - WRITE_PERI_REG(GPIO_OUT1_W1TC_REG, 1 << (io - 32)); - } -} - -void led_toggle(int io) { - if (READ_PERI_REG(GPIO_OUT_REG & (1 << io))) { - WRITE_PERI_REG(GPIO_OUT_W1TC_REG, 1 << io); - } else { - WRITE_PERI_REG(GPIO_OUT_W1TS_REG, 1 << io); - } -} diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/led.h b/src/mongoose-6.11/src/common/platforms/esp32/stubs/led.h deleted file mode 100644 index 505a3f6..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp32/stubs/led.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (c) 2014-2017 Cesanta Software Limited - * All rights reserved - */ - -#pragma once - -void led_setup(int io); -void led_on(int io); -void led_off(int io); -void led_toggle(int io); diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/rom_functions.h b/src/mongoose-6.11/src/common/platforms/esp32/stubs/rom_functions.h deleted file mode 100644 index 1b2eee4..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp32/stubs/rom_functions.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef CS_COMMON_PLATFORMS_ESP32_STUBS_ROM_FUNCTIONS_H_ -#define CS_COMMON_PLATFORMS_ESP32_STUBS_ROM_FUNCTIONS_H_ - -#include "rom/ets_sys.h" -#include "rom/spi_flash.h" -#include "rom/md5_hash.h" -#include "rom/uart.h" -#include "rom/rtc.h" - -#endif /* CS_COMMON_PLATFORMS_ESP32_STUBS_ROM_FUNCTIONS_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/sdkconfig.h b/src/mongoose-6.11/src/common/platforms/esp32/stubs/sdkconfig.h deleted file mode 100644 index 0ecac13..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp32/stubs/sdkconfig.h +++ /dev/null @@ -1 +0,0 @@ -#define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1 diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/stub.ld b/src/mongoose-6.11/src/common/platforms/esp32/stubs/stub.ld deleted file mode 100644 index 6c2af65..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp32/stubs/stub.ld +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2016 Cesanta Software Limited - * All rights reserved - */ - -MEMORY { - iram : org = 0x40090000, len = 0x10000 - /* DRAM startin at 0x3FFC0000 gets stomped by something before mem_finish - * and is thus not suitable for initialized data, but works fine for BSS. */ - dram_bss : org = 0x3FFC0000, len = 0x10000 - dram : org = 0x3FFD0000, len = 0x10000 -} - -ENTRY(stub_main) - -SECTIONS { - .params 0x40090000 : { - _params_start = ABSOLUTE(.); - *(.params) - _params_end = ABSOLUTE(.); - } > iram - - .text : ALIGN(4) { - _code_start = ABSOLUTE(.); - *(.literal) - *(.text .text.*) - } > iram - - .bss : ALIGN(4) { - _bss_start = ABSOLUTE(.); - *(.bss) - _bss_end = ABSOLUTE(.); - } > dram - - .data : ALIGN(4) { - _data_start = ABSOLUTE(.); - *(.data) - *(.rodata .rodata.*) - } > dram -} - -INCLUDE "components/esp32/ld/esp32.rom.ld" -INCLUDE "components/esp32/ld/esp32.rom.spiram_incompatible_fns.ld" - -PROVIDE(esp_rom_spiflash_attach = 0x40062a6c); -PROVIDE(esp_rom_spiflash_config_clk = 0x40062bc8); diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/stub_hello.c b/src/mongoose-6.11/src/common/platforms/esp32/stubs/stub_hello.c deleted file mode 100644 index a4c75eb..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp32/stubs/stub_hello.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2015 Cesanta Software Limited - * All rights reserved - * - * - * Stub template. - */ - -#include -#include -#include "slip.h" - -/* Define the args vector and put it into the ".params" section. */ -uint32_t params[3] __attribute__((section(".params"))); - -/* Define a function called stub_main. Do not return or reboot. - * Use send_packet to communicate to the host. */ - -const char *hello = "Hello"; - -static char buf[1024]; -extern uint32_t _bss_start, _bss_end; - -void stub_main(void) { - uint32_t greeting = 0x4941484f; - SLIP_send(&greeting, 4); - memset(&_bss_start, 0, (&_bss_end - &_bss_start)); - buf[1] = 123; - SLIP_send(hello, 5); - while (1) { - } -} diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/uart.c b/src/mongoose-6.11/src/common/platforms/esp32/stubs/uart.c deleted file mode 100644 index c6eb25d..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp32/stubs/uart.c +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#include "uart.h" - -#include "rom_functions.h" - -void set_baud_rate(uint32_t uart_no, uint32_t baud_rate) { - uint32_t master_freq = ets_get_detected_xtal_freq() << 4; - master_freq += (baud_rate / 2); - uint32_t div = master_freq / baud_rate; - uart_div_modify(uart_no, div); -} diff --git a/src/mongoose-6.11/src/common/platforms/esp32/stubs/uart.h b/src/mongoose-6.11/src/common/platforms/esp32/stubs/uart.h deleted file mode 100644 index aa3c4ed..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp32/stubs/uart.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_ESP32_STUBS_UART_H_ -#define CS_COMMON_PLATFORMS_ESP32_STUBS_UART_H_ - -#include - -void set_baud_rate(uint32_t uart_no, uint32_t baud_rate); - -#endif /* CS_COMMON_PLATFORMS_ESP32_STUBS_UART_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/common.mk b/src/mongoose-6.11/src/common/platforms/esp8266/common.mk deleted file mode 100644 index 5345221..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/common.mk +++ /dev/null @@ -1,76 +0,0 @@ -CFLAGS_EXTRA ?= - -XTENSA_TOOLS_ROOT ?= /opt/Espressif/crosstool-NG/builds/xtensa-lx106-elf/bin -SDK_PATH ?= /opt/Espressif/ESP8266_SDK -ESPTOOL ?= esptool.py -ESPPORT ?= /dev/ttyACM0 -ESPSPEED ?= 230400 -# For flash = > 16Mbit -ESPFLASHARGS = --flash_mode dio --flash_size 32m - -VERBOSE ?= 0 - -CC := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc -CXX := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-g++ -AR := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-ar -LD := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc -OBJCOPY := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-objcopy -NM := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-nm -CC_WRAPPER ?= - -define link -$(vecho) "LD $@" -$(Q) $(CC_WRAPPER) $(LD) $(LIBDIRS) -T$(LD_SCRIPT) $(LDFLAGS) -o $@ \ --Wl,-Map=$@.map -Wl,--start-group $1 -Wl,--end-group -endef - -define compile_params -$(vecho) "$5 $1 -> $2" -$(Q) $(CC_WRAPPER) $3 -MD -MP $(INCDIRS) $4 -c $1 -o $2 -endef - -define compile -$(call compile_params,$<,$@, $(CC), $(CFLAGS),"CC") -endef - -define compile_cxx -$(call compile_params,$<,$@, $(CXX), $(CXXFLAGS),"CXX") -endef - -# some of these flags works around for gdb 7.5.x stacktrace issue -# while still allowing -Os to remove padding between data in .rodata -# section, allowing us to gain about 1k of ram. -# text section is 4k bigger, but we care more about ram at the moment. -# TODO(mkm): figure out which flag(s). -NO_Os_FLAGS= -fno-expensive-optimizations -fno-thread-jumps \ - -fno-align-functions -fno-align-jumps \ - -fno-align-loops -fno-align-labels -fno-caller-saves \ - -fno-crossjumping -fno-cse-follow-jumps -fno-cse-skip-blocks \ - -fno-delete-null-pointer-checks -fno-devirtualize \ - -fno-gcse -fno-gcse-lm -fno-hoist-adjacent-loads \ - -fno-inline-small-functions -fno-indirect-inlining -fno-partial-inlining \ - -fno-ipa-cp -fno-ipa-sra -fno-peephole2 -fno-optimize-sibling-calls -fno-optimize-strlen \ - -fno-reorder-blocks -fno-reorder-blocks-and-partition -fno-reorder-functions \ - -fno-sched-interblock -fno-sched-spec -fno-rerun-cse-after-loop \ - -fno-schedule-insns -fno-schedule-insns2 -fno-strict-aliasing -fno-strict-overflow \ - -fno-tree-builtin-call-dce -fno-tree-switch-conversion -fno-tree-tail-merge \ - -fno-tree-pre -fno-tree-vrp - -C_CXX_FLAGS = -W -Wall -Werror -Wundef -Wno-comment -Wno-variadic-macros -Wpointer-arith \ - -Os $(NO_Os_FLAGS) -g3 \ - -Wl,-EL -fno-inline-functions \ - -D_XOPEN_SOURCE=500 \ - -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DSTATIC=static \ - -Wno-parentheses \ - -DIRAM='__attribute__((section(".fast.text")))' \ - -DICACHE_RAM_ATTR=IRAM \ - -DNOINSTR='__attribute__((no_instrument_function))' \ - -DCS_PLATFORM=3 \ - -ffunction-sections -fdata-sections - -CFLAGS = -std=c99 $(C_CXX_FLAGS) -CXXFLAGS = -std=gnu++11 -fno-exceptions $(C_CXX_FLAGS) - -# linker flags used to generate the main object file -LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start \ - -Wl,-static -Wl,--gc-sections diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/esp_crypto.c b/src/mongoose-6.11/src/common/platforms/esp8266/esp_crypto.c deleted file mode 100644 index 822dab1..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/esp_crypto.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#include -#include -#include - -#include "mongoose/mongoose.h" - -#ifdef RTOS_SDK -#include "esp_libc.h" -#else -#include "osapi.h" -#endif - -extern int sha1_vector(size_t num_msgs, const uint8_t *msgs[], - const size_t *msg_lens, uint8_t *digest); - -extern int md5_vector(size_t num_msgs, const uint8_t *msgs[], - const size_t *msg_lens, uint8_t *digest); - -/* For digest auth. */ -void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[], - const size_t *msg_lens, uint8_t *digest) { - (void) md5_vector(num_msgs, msgs, msg_lens, digest); -} - -/* For WebSocket handshake. */ -void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[], - const size_t *msg_lens, uint8_t *digest) { - (void) sha1_vector(num_msgs, msgs, msg_lens, digest); -} - -#if MG_ENABLE_SSL - -#include "mbedtls/aes.h" -#include "mbedtls/sha256.h" - -#define AES_PRIV_NR_POS (4 * 15) - -/* - * Crypto functions in ROM/SDK. - * They come from wpa_supplicant, you can find them here https://w1.fi/cgit/ - * - * Note that ROM version of the key setup function is older, does not take the - * number of bits argument and only supports AES-128. This prototype doesn't - * suit it, but since the difference is in the last aegument, it doesn't matter. - */ - -extern void rijndaelKeySetupDec(void *ctx, const uint8_t *key, int bits); -extern int rijndaelKeySetupEnc(void *ctx, const uint8_t *key, int bits); -void aes_encrypt(void *ctx, const uint8_t *plain, uint8_t *crypt); -void aes_decrypt(void *ctx, const uint8_t *crypt, uint8_t *plain); - -/* - * AES that comes with wpa_supplicant allocates its own AES context in - * aes_{encrypt,decrypt}_init. Ideally, we'd take that pointer and store it in - * our mbedtls_aes_context, but then a lot of space would be wasted. - * We do not call _init and go directly to key setup functions and poke number - * of rounds into the right place too. This is a bit hacky, but works fine. - * There is also a difference between older function in ROM and the one coming - * with SDK which is newer: the older one actually takes two arguments, not 3. - * But it doesn't matter, extra argument doesn't hurt and this works with both. - */ -int mbedtls_aes_setkey_enc(mbedtls_aes_context *ctx, const unsigned char *key, - unsigned int keybits) { - if (keybits != 128) return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH; - ((uint32_t *) ctx)[AES_PRIV_NR_POS] = 10; - rijndaelKeySetupEnc(ctx, key, 128); - return 0; -} - -int mbedtls_aes_setkey_dec(mbedtls_aes_context *ctx, const unsigned char *key, - unsigned int keybits) { - if (keybits != 128) return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH; - ((uint32_t *) ctx)[AES_PRIV_NR_POS] = 10; - rijndaelKeySetupDec(ctx, key, 128); - return 0; -} - -int mbedtls_internal_aes_encrypt(mbedtls_aes_context *ctx, - const unsigned char input[16], - unsigned char output[16]) { - aes_encrypt(ctx, input, output); - return 0; -} - -int mbedtls_internal_aes_decrypt(mbedtls_aes_context *ctx, - const unsigned char input[16], - unsigned char output[16]) { - aes_decrypt(ctx, input, output); - return 0; -} - -/* os_get_random uses hardware RNG, so it's cool. */ -int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len) { - os_get_random(buf, len); - (void) ctx; - return 0; -} - -/* For CryptoAuthLib host crypto. */ -int atcac_sw_sha2_256(const uint8_t *data, size_t data_size, - uint8_t digest[32]) { - mbedtls_sha256(data, data_size, digest, false /* is_224 */); - return 0; -} - -#endif /* MG_ENABLE_SSL */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/esp_hw_wdt_register.h b/src/mongoose-6.11/src/common/platforms/esp8266/esp_hw_wdt_register.h deleted file mode 100644 index 32c2f3c..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/esp_hw_wdt_register.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2014-2017 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_ESP8266_ESP_HW_WDT_REGISTER_H_ -#define CS_COMMON_PLATFORMS_ESP8266_ESP_HW_WDT_REGISTER_H_ - -#ifdef RTOS_SDK -#include -#else -#include -#endif - -#define REG_WDT_BASE 0x60000900 - -#define WDT_CTL (REG_WDT_BASE + 0x0) -#define WDT_CTL_ENABLE (BIT(0)) -#define WDT_CTL_STAGE1_NO_RESET (BIT(1)) -#define WDT_CTL_STAGE1_DISABLE (BIT(2)) -#define WDT_CTL_UNK3 (BIT(3)) -#define WDT_CTL_UNK4 (BIT(4)) -#define WDT_CTL_UNK5 (BIT(5)) - -/* Bits 3, 4, 5 - ???; set to 1 by ROM. */ - -#define WDT_RELOAD_STAGE0 (REG_WDT_BASE + 0x4) -#define WDT_RELOAD_STAGE0_V (0xf) -#define WDT_RELOAD_STAGE0_S (0) - -#define WDT_RELOAD_STAGE1 (REG_WDT_BASE + 0x8) -#define WDT_RELOAD_STAGE1_V (0xf) -#define WDT_RELOAD_STAGE1_S (0) - -#define WDT_COUNT (REG_WDT_BASE + 0xc) /* Counts at CPU_CLK (80 MHz) */ -#define WDT_COUNT_V (0xffffffff) -#define WDT_COUNT_S (0) - -#define WDT_STAGE (REG_WDT_BASE + 0x10) -#define WDT_STAGE_V (1) -#define WDT_STAGE_S (0) - -#define WDT_RESET (REG_WDT_BASE + 0x14) -#define WDT_RESET_V (0xff) -#define WDT_RESET_S (0) - -#define WDT_RESET_STAGE (REG_WDT_BASE + 0x18) -#define WDT_RESET_STAGE_V (0xff) -#define WDT_RESET_STAGE_S (0) - -#define WDT_RESET_VALUE 0x73 - -#endif /* CS_COMMON_PLATFORMS_ESP8266_ESP_HW_WDT_REGISTER_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/esp_missing_includes.h b/src/mongoose-6.11/src/common/platforms/esp8266/esp_missing_includes.h deleted file mode 100644 index fc7d69f..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/esp_missing_includes.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_ESP8266_ESP_MISSING_INCLUDES_H_ -#define CS_COMMON_PLATFORMS_ESP8266_ESP_MISSING_INCLUDES_H_ - -#include -#include - -void pp_soft_wdt_init(void); -void pp_soft_wdt_stop(void); -void pp_soft_wdt_feed(void); -void pp_soft_wdt_restart(void); -void system_soft_wdt_stop(void); /* Alias for pp_soft_wdt_stop */ - -void Cache_Read_Disable(void); -void Cache_Read_Enable(uint32_t, uint32_t, uint32_t); -void Cache_Read_Disable_2(void); -void Cache_Read_Enable_2(void); -void Cache_Read_Enable_New(void); - -int SPIEraseBlock(uint32_t block); -uint32_t SPIRead(uint32_t addr, void *dst, uint32_t size); - -#ifndef RTOS_SDK - -#include - -/* There are no declarations for these anywhere in the SDK (as of 1.2.0). */ -void ets_isr_mask(unsigned intr); -void ets_isr_unmask(unsigned intr); -void system_restart_local(void); -int os_printf_plus(const char *format, ...); - -void ets_wdt_init(void); -void ets_wdt_enable(uint32_t mode, uint32_t arg1, uint32_t arg2); -void ets_wdt_disable(void); -void ets_wdt_restore(uint32_t mode); -uint32_t ets_wdt_get_mode(void); - -void _xtos_l1int_handler(void); -void _xtos_set_exception_handler(); -void xthal_set_intenable(unsigned); - -/* These are present in mem.h but are commented out. */ -void *pvPortMalloc(size_t xWantedSize, const char *file, int line); -void vPortFree(void *pv, const char *file, int line); -void *pvPortZalloc(size_t size, const char *file, int line); -void *pvPortRealloc(void *pv, size_t size, const char *file, int line); - -#else /* !RTOS_SDK */ - -#define BIT(nr) (1UL << (nr)) -void system_soft_wdt_feed(void); -void system_soft_wdt_restart(void); -void ets_putc(char c); - -#endif /* RTOS_SDK */ - -void _ResetVector(void); - -#endif /* CS_COMMON_PLATFORMS_ESP8266_ESP_MISSING_INCLUDES_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/esp_ssl_krypton.h b/src/mongoose-6.11/src/common/platforms/esp8266/esp_ssl_krypton.h deleted file mode 100644 index 086c6b5..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/esp_ssl_krypton.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_PLATFORMS_ESP8266_ESP_SSL_KRYPTON_H_ -#define CS_COMMON_PLATFORMS_ESP8266_ESP_SSL_KRYPTON_H_ - -#include "krypton/krypton.h" - -struct mg_connection; - -void mg_lwip_ssl_do_hs(struct mg_connection *nc); -void mg_lwip_ssl_send(struct mg_connection *nc); -void mg_lwip_ssl_recv(struct mg_connection *nc); - -#endif /* CS_COMMON_PLATFORMS_ESP8266_ESP_SSL_KRYPTON_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/esp_umm_malloc.c b/src/mongoose-6.11/src/common/platforms/esp8266/esp_umm_malloc.c deleted file mode 100644 index 3dd4a0e..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/esp_umm_malloc.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -#include -#include -#include "common/umm_malloc/umm_malloc.h" -#include "esp_umm_malloc.h" - -#if ESP_UMM_ENABLE - -/* - * ESP-specific glue for the `umm_malloc`. - * - * In SDK (https://github.com/cesanta/esp-open-sdk), there is an archive - * `sdk/lib/libmain.a` which contains some files, including `mem_manager.o`. - * - * The `mem_manager.o` contains all the heap-related functions: `pvPortMalloc`, - * etc. We have weaken all symbols from `mem_manager.o` by - * `xtensa-lx106-elf-objcopy` (see exact commands in Dockerfile: - * `docker/esp8266/Dockerfile-esp8266-build-oss`), and provide our own - * implementations in this file. - * - * ------------------------------------ - * - * NOTE that not all public functions from `mem_manager.o` need to be replaced: - * some of them are used only internally: - * - * - system_show_malloc() - * - pvShowMalloc() - * - prvInsertBlockIntoUsedList() - * - prvRemoveBlockFromUsedList() - * - check_memleak_debug_enable() - * - vPortInitialiseBlocks() - * - * So when we replace all the rest (`pvPortMalloc`, etc), we can check with - * `objdump` that resulting binary (for SJ, it's `fw.out`) doesn't contain - * any of the "internal" functions. - * - * ------------------------------------ - * - * NOTE that to make linker actually consider implementations in this file, - * you should explicitly reference some function from it. This is what - * `esp_umm_init()` is for: it is a dummy no-op function that must be called - * from somewhere outside. - * - * If you don't do this, linker will merely garbage-collect this file, and - * will use heap implementation from SDK. - */ - -void *pvPortMalloc(size_t size, const char *file, unsigned line) { - (void) file; - (void) line; - - return umm_malloc(size); -} - -void *pvPortCalloc(size_t num, size_t size, const char *file, unsigned line) { - (void) file; - (void) line; - - return umm_calloc(num, size); -} - -void *pvPortZalloc(size_t size, const char *file, unsigned line) { - void *ret; - - (void) file; - (void) line; - - ret = umm_malloc(size); - if (ret != NULL) memset(ret, 0, size); - return ret; -} - -void *pvPortRealloc(void *ptr, size_t size, const char *file, unsigned line) { - (void) file; - (void) line; - - return umm_realloc(ptr, size); -} - -void vPortFree(void *ptr, const char *file, unsigned line) { - (void) file; - (void) line; - - umm_free(ptr); -} - -size_t xPortGetFreeHeapSize(void) { - return umm_free_heap_size(); -} - -size_t xPortWantedSizeAlign(void) { - return 4; -} - -void esp_umm_init(void) { - /* Nothing to do, see header for details */ -} - -void esp_umm_oom_cb(size_t size, size_t blocks_cnt) { - fprintf(stderr, "E:M %u (%u blocks)\n", (unsigned int) size, - (unsigned int) blocks_cnt); -} - -#endif /* ESP_UMM_ENABLE */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/esp_umm_malloc.h b/src/mongoose-6.11/src/common/platforms/esp8266/esp_umm_malloc.h deleted file mode 100644 index efb6f4b..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/esp_umm_malloc.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -#if ESP_UMM_ENABLE - -/* - * This is a no-op dummy function that is merely needed because we have to - * reference any function from the file `esp_umm_malloc.c`, so that linker - * won't garbage-collect the whole compilation unit. - */ -void esp_umm_init(void); - -/* - * Callback that gets called by umm_malloc in case of Out-of-memory error. - * - * `size` is the size requested by user, and `block_cnt` is a number of heap - * blocks that umm_malloc failed to allocate - */ -void esp_umm_oom_cb(size_t size, size_t blocks_cnt); - -#endif /* CS_COMMON_PLATFORMS_ESP8266_ESP_UMM_MALLOC_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/Makefile b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/Makefile deleted file mode 100644 index 4cccf2a..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# -# Makefile for esptool2 -# https://github.com/raburton/esp8266 -# - -CFLAGS = -O2 -Wall -CC = gcc -LD = gcc -BUILD_DIR = ../../build - -all: $(BUILD_DIR) $(BUILD_DIR)/esptool2 - -$(BUILD_DIR): - @mkdir -p $(BUILD_DIR) - -$(BUILD_DIR)/esptool2.o: esptool2.c esptool2.h esptool2_elf.h elf.h - @echo "CC $<" - $(CC) $(CFLAGS) -c $< -o $@ - -$(BUILD_DIR)/esptool2_elf.o: esptool2_elf.c esptool2.h esptool2_elf.h elf.h - @echo "CC $<" - $(CC) $(CFLAGS) -c $< -o $@ - -$(BUILD_DIR)/esptool2: $(BUILD_DIR)/esptool2.o $(BUILD_DIR)/esptool2_elf.o - @echo "LD $@" - $(LD) -o $@ $^ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/elf.h b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/elf.h deleted file mode 100644 index a699718..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/elf.h +++ /dev/null @@ -1,76 +0,0 @@ -// Based on a small portion of ELF.h from LLVM - -//===-- llvm/Support/ELF.h - ELF constants and data structures --*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. -// -//===----------------------------------------------------------------------===// -// -// This header contains common, non-processor-specific data structures and -// constants for the ELF file format. -// -// The details of the ELF32 bits in this file are largely based on the Tool -// Interface Standard (TIS) Executable and Linking Format (ELF) Specification -// Version 1.2, May 1995. The ELF64 stuff is based on ELF-64 Object File Format -// Version 1.5, Draft 2, May 1998 as well as OpenBSD header files. -// -//===----------------------------------------------------------------------===// - -#ifndef CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ELF_H_ -#define CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ELF_H_ - -typedef uint32_t Elf32_Addr; // Program address -typedef uint32_t Elf32_Off; // File offset -typedef uint16_t Elf32_Half; -typedef uint32_t Elf32_Word; -typedef int32_t Elf32_Sword; - -// e_ident size and indices -enum { - EI_MAG0 = 0, // File identification index. - EI_MAG1 = 1, // File identification index. - EI_MAG2 = 2, // File identification index. - EI_MAG3 = 3, // File identification index. - EI_CLASS = 4, // File class. - EI_DATA = 5, // Data encoding. - EI_VERSION = 6, // File version. - EI_OSABI = 7, // OS/ABI identification. - EI_ABIVERSION = 8, // ABI version. - EI_PAD = 9, // Start of padding bytes. - EI_NIDENT = 16 // Number of bytes in e_ident. -}; - -typedef struct { - unsigned char e_ident[EI_NIDENT]; // ELF Identification bytes - Elf32_Half e_type; // Type of file (see ET_* below) - Elf32_Half e_machine; // Required architecture for this file (see EM_*) - Elf32_Word e_version; // Must be equal to 1 - Elf32_Addr e_entry; // Address to jump to in order to start program - Elf32_Off e_phoff; // Program header table's file offset, in bytes - Elf32_Off e_shoff; // Section header table's file offset, in bytes - Elf32_Word e_flags; // Processor-specific flags - Elf32_Half e_ehsize; // Size of ELF header, in bytes - Elf32_Half e_phentsize; // Size of an entry in the program header table - Elf32_Half e_phnum; // Number of entries in the program header table - Elf32_Half e_shentsize; // Size of an entry in the section header table - Elf32_Half e_shnum; // Number of entries in the section header table - Elf32_Half e_shstrndx; // Sect hdr table index of sect name string table -} Elf32_Ehdr; - -typedef struct { - Elf32_Word sh_name; // Section name (index into string table) - Elf32_Word sh_type; // Section type (SHT_*) - Elf32_Word sh_flags; // Section flags (SHF_*) - Elf32_Addr sh_addr; // Address where section is to be loaded - Elf32_Off sh_offset; // File offset of section data, in bytes - Elf32_Word sh_size; // Size of section, in bytes - Elf32_Word sh_link; // Section type-specific header table index link - Elf32_Word sh_info; // Section type-specific extra information - Elf32_Word sh_addralign; // Section address alignment - Elf32_Word sh_entsize; // Size of records contained within the section -} Elf32_Shdr; - -#endif /* CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ELF_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2.c b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2.c deleted file mode 100644 index aa9b457..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2.c +++ /dev/null @@ -1,561 +0,0 @@ -/********************************************************************************** -* -* Copyright (c) 2015 Richard A Burton -* -* This file is part of esptool2. -* -* esptool2 is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* esptool2 is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with esptool2. If not, see . -* -**********************************************************************************/ -/* clang-format off */ - -#include -#include -#include - -#include "esptool2.h" -#include "esptool2_elf.h" - -#define IMAGE_PADDING 16 -#define SECTION_PADDING 4 -#define IROM_SECTION_PADDING 4096 -#define CHECKSUM_INIT 0xEF -#define BIN_MAGIC_IROM 0xEA -#define BIN_MAGIC_FLASH 0xE9 - -typedef struct { - Elf32_Addr addr; - Elf32_Word size; -} Section_Header; - -typedef struct { - unsigned char magic; - unsigned char count; - unsigned char byte2; - unsigned char byte3; - Elf32_Addr entry; -} Image_Header; - -static const char PADDING[IROM_SECTION_PADDING] = {0}; - -static bool debugon = false; -static bool quieton = false; - -// Print a standard info message (unless quiet mode) -void print(const char* format, ...) { - va_list args; - - if (!quieton) { - va_start(args, format); - vprintf(format, args); - va_end(args); - } -} - -// Print a debug message (if debug mode) -void debug(const char* format, ...) { - va_list args; - - if (debugon) { - va_start(args, format); - vprintf(format, args); - va_end(args); - } -} - -// Print an error message (always) -void error(const char* format, ...) { - va_list args; - - va_start(args, format); - vfprintf(stderr, format, args); - va_end(args); -} - -// Write an elf section (by name) to an existing file. -// Parameters: -// headed - add a header to the output -// zeroaddr - force zero entry point in header (default is the real entry point) -// padded - output will be padded to multiple of SECTION_PADDING bytes -// chksum - pointer to existing checksum to add this data to (zero if not needed) -// Produces error message on failure (so caller doesn't need to). -bool WriteElfSection(MyElf_File *elf, FILE *outfile, char* name, bool headed, - bool zeroaddr, int padto, unsigned char *chksum) { - - int i, pad = 0; - bool ret = false; - unsigned char *bindata = 0; - Section_Header sechead; - MyElf_Section *sect; - - // get elf section header - sect = GetElfSection(elf, name); - if(!sect) { - error("Error: Section '%s' not found in elf file.\r\n", name); - goto end_function; - } - - // create image section header - sechead.addr = (zeroaddr ? 0 : sect->address); - sechead.size = sect->size; - - // do we need to pad the section? - if (padto) { - pad = sechead.size % padto; - if (pad > 0) { - pad = padto - pad; - sechead.size += pad; - } - } - - debug("Adding section '%s', addr: 0x%08x, size: %d (+%d bytes(s) padding).\r\n", - name, sect->address, sect->size, pad); - - // get elf section binary data - bindata = GetElfSectionData(elf, sect); - if (!bindata) { - goto end_function; - } - - // write section (and pad if required) - if((headed && fwrite(&sechead, 1, sizeof(sechead), outfile) != sizeof(sechead)) - || fwrite(bindata, 1, sect->size, outfile) != sect->size - || (pad > 0 && fwrite(PADDING, 1, pad, outfile) != pad)) { - error("Error: Failed to write section '%s' to image file.\r\n", name); - goto end_function; - } - - // include section data in the checksum - if(chksum) { - for(i = 0; i < (int)sect->size; i++) { - *chksum ^= bindata[i]; - } - } - - ret = true; - -end_function: - if (bindata) free(bindata); - return ret; -} - -// Load an elf file and export a section of it to a new file, without -// header, padding or checksum. For exporting the .irom0.text library. -// Produces error message on failure (so caller doesn't need to). -bool ExportElfSection(char *infile, char *outfile, char *name) { - - bool ret = false; - FILE *fd = 0; - MyElf_File *elf = 0; - - // load elf file - elf = LoadElf(infile); - if (!elf) { - goto end_function; - } - - // open output file - fd = fopen(outfile, "wb"); - if(!fd) { - error("Error: Can't open output file '%s' for writing.\r\n", outfile); - goto end_function; - } - - // actually do the export - ret = WriteElfSection(elf, fd, name, false, false, false, 0); - -end_function: - // clean up - if (fd) fclose(fd); - UnloadElf(elf); - - return ret; -} - -// Create the main binary firmware image, from specified elf sections. -// Can produce for standard standalone app (separate .irom0.text) -// or sdk bootloaded apps (integrated .irom0.text). -// Choice of type requires appropriately linked elf file. -// Produces error message on failure (so caller doesn't need to). -bool CreateHeaderFile(char *elffile, char *imagefile, char *sections[], int numsec) { - - bool ret = false; - int i; - unsigned int j, len; - FILE *outfile = 0; - MyElf_File *elf = 0; - MyElf_Section *sect; - unsigned char *bindata = 0; - char name[31]; - - // load elf file - elf = LoadElf(elffile); - if (!elf) { - goto end_function; - } - - // open output file - outfile = fopen(imagefile, "wb"); - if(outfile == NULL) { - error("Error: Failed to open output file '%s' for writing.\r\n", imagefile); - goto end_function; - } - - // add entry point - fprintf(outfile, "const uint32 entry_addr = 0x%08x;\r\n", elf->header.e_entry); - - // add sections - for (i = 0; i < numsec; i++) { - // get elf section header - sect = GetElfSection(elf, sections[i]); - if(!sect) { - error("Error: Section '%s' not found in elf file.\r\n", sections[i]); - goto end_function; - } - - // simple name fix name - strncpy(name, sect->name, 31); - len = strlen(name); - for (j = 0; j < len; j++) { - if (name[j] == '.') name[j] = '_'; - } - - // add address, length and start the data block - debug("Adding section '%s', addr: 0x%08x, size: %d.\r\n", sections[i], sect->address, sect->size); - fprintf(outfile, "\r\nconst uint32 %s_addr = 0x%08x;\r\nconst uint32 %s_len = %d;\r\nconst uint8 %s_data[] = {", - name, sect->address, name, sect->size, name); - - // get elf section binary data - bindata = GetElfSectionData(elf, sect); - if (!bindata) { - goto end_function; - } - - // add the data and finish off the block - for (j = 0; j < sect->size; j++) { - if (j % 16 == 0) fprintf(outfile, "\r\n 0x%02x,", bindata[j]); - else fprintf(outfile, " 0x%02x,", bindata[j]); - } - fprintf(outfile, "\r\n};\r\n"); - free(bindata); - bindata = 0; - } - - // if we got this far everything worked! - ret = true; - -end_function: - // clean up - if (outfile) fclose(outfile); - if (elf) UnloadElf(elf); - if (bindata) free(bindata); - - return ret; -} - -// Create the main binary firmware image, from specified elf sections. -// Can produce for standard standalone app (separate .irom0.text) -// or sdk bootloaded apps (integrated .irom0.text). -// Choice of type requires appropriately linked elf file. -// Produces error message on failure (so caller doesn't need to). -bool CreateBinFile(char *elffile, char *imagefile, int bootver, unsigned char mode, - unsigned char clock, unsigned char size, bool iromchksum, char *sections[], int numsec) { - - bool ret = false; - int i, pad, len; - unsigned char chksum = CHECKSUM_INIT; - unsigned char *data = 0; - FILE *outfile = 0; - MyElf_File *elf = 0; - Image_Header imghead; - - // load elf file - elf = LoadElf(elffile); - if (!elf) { - goto end_function; - } - - // open output file - outfile = fopen(imagefile, "wb"); - if(outfile == NULL) { - error("Error: Failed to open output file '%s' for writing.\r\n", imagefile); - goto end_function; - } - - // set options common to standard and boot v1.2+ headers - imghead.byte2 = mode; - //imghead.byte3 = (int)((int)size << 4 | clock) && 0xff; - imghead.byte3 = ((size << 4) | clock) & 0xff; - imghead.entry = elf->header.e_entry; - debug("Size = %02x\r\n", size); - debug("Byte2 = %02x\r\n", imghead.byte2); - debug("Byte3 = %02x\r\n", imghead.byte3); - debug("Entry = %08x\r\n", imghead.entry); - - // boot v1.2+ header - if (bootver == 2) { - // extra header - imghead.magic = BIN_MAGIC_IROM; - imghead.count = 4; // probably a version number here, not a count - if(fwrite(&imghead, 1, sizeof(imghead), outfile) != sizeof(imghead)) { - error("Error: Failed to write header to image file.\r\n"); - goto end_function; - } - if(!WriteElfSection(elf, outfile, ".irom0.text", true, true, IROM_SECTION_PADDING, (iromchksum ? &chksum : 0))) { - goto end_function; - } - } - - // standard header - imghead.magic = BIN_MAGIC_FLASH; - imghead.count = numsec; - // write header - if(fwrite(&imghead, 1, sizeof(imghead), outfile) != sizeof(imghead)) { - error("Error: Failed to write header to image file.\r\n"); - goto end_function; - } - - // add sections - for (i = 0; i < numsec; i++) { - if(!WriteElfSection(elf, outfile, sections[i], true, false, SECTION_PADDING, &chksum)) { - goto end_function; - } - } - - // get image length (plus a byte for the checksum) - len = ftell(outfile) + 1; - - // do we need to pad the image? - pad = len % IMAGE_PADDING; - if (pad > 0) { - pad = IMAGE_PADDING - pad; - debug("Padding image with %d byte(s).\r\n", pad); - if(fwrite(PADDING, 1, pad, outfile) != pad) { - error("Error: Failed to write padding to image file.\r\n"); - goto end_function; - } - } - - // write checksum - if(fwrite(&chksum, 1, 1, outfile) != 1) { - error("Error: Failed to write checksum to image file.\r\n"); - goto end_function; - } - - // boot v1.1 - if(bootver == 1) { - // write 'ff' padding up to the position of the library - len = 0x10000 - ftell(outfile); - debug("Adding boot v1.1 padding, %d bytes of '0xff'.\r\n", len); - data = (unsigned char*)malloc(len); - memset(data, 0xff, len); - if(fwrite(data, 1, len, outfile) != len) { - error("Error: Failed to write boot v1.1 spacer.\r\n"); - goto end_function; - } - - // write the library - if(!WriteElfSection(elf, outfile, ".irom0.text", false, false, 0, 0)) { - goto end_function; - } - } - - // if we got this far everything worked! - ret = true; - -end_function: - // clean up - if (outfile) fclose(outfile); - if (data) free(data); - if (elf) UnloadElf(elf); - - return ret; -} - -int main(int argc, char *argv[]) { - - int i; - char *infile; - char *outfile; - int numstr; - bool binfile = false; - bool libfile = false; - bool headerfile = false; - bool paramerror = false; - bool iromchksum = false; - int bootver = 0; - /* Overwrite-friendly by default */ - unsigned char mode = 0xff; - unsigned char size = 0xff; - unsigned char clock = 0; - int opts = 0; - - // parse options - for (i = 1; i < argc; i++) { - if (!strcmp(argv[i], "-bin")) { - binfile = true; - opts++; - } else if (!strcmp(argv[i], "-lib")) { - libfile = true; - opts++; - } else if (!strcmp(argv[i], "-header")) { - headerfile = true; - opts++; - } else if (!strcmp(argv[i], "-quiet")) { - quieton = true; - } else if (!strcmp(argv[i], "-debug")) { - debugon = true; - } else if (!strcmp(argv[i], "-boot0")) { - bootver = 0; - } else if (!strcmp(argv[i], "-boot1")) { - bootver = 1; - } else if (!strcmp(argv[i], "-boot2")) { - bootver = 2; - } else if (!strcmp(argv[i], "-qio")) { - mode = 0; - } else if (!strcmp(argv[i], "-qout")) { - mode = 1; - } else if (!strcmp(argv[i], "-dio")) { - mode = 2; - } else if (!strcmp(argv[i], "-dout")) { - mode = 3; - } else if (!strcmp(argv[i], "-256")) { - size = 1; - } else if (!strcmp(argv[i], "-512")) { - size = 0; - } else if (!strcmp(argv[i], "-1024")) { - size = 2; - } else if (!strcmp(argv[i], "-2048")) { - size = 3; - } else if (!strcmp(argv[i], "-4096")) { - size = 4; - } else if (!strcmp(argv[i], "-20")) { - clock = 2; - } else if (!strcmp(argv[i], "-26.7")) { - clock = 1; - } else if (!strcmp(argv[i], "-40")) { - clock = 0; - } else if (!strcmp(argv[i], "-80")) { - clock = 15; - } else if (!strcmp(argv[i], "-iromchksum")) { - iromchksum = true; - } else if (!strcmp(argv[i], "--")) { - i++; - break; - } else if (argv[i][0] == '-') { - paramerror = true; - break; - } else { - break; - } - } - - print("esptool2 v2.0.0 - (c) 2015 Richard A Burton \r\n"); - print("This program is licensed under the GPL v3.\r\n"); - print("See the file LICENSE for details.\r\n\r\n"); - - if (paramerror) { - error("Error: Unrecognised option '%s'.\r\n", argv[i]); - return -1; - } - - if (argc < 2) { - print("Usage:\r\n"); - print("esptool2 -lib [options] \r\n"); - print("esptool2 -bin [options] ...\r\n"); - print("esptool2 -header [options] ...\r\n"); - print("\r\n"); - print(" -lib\r\n"); - print(" Export the sdk library (.irom0.text), for a standalone app.\r\n"); - print(" e.g. esptool2 -elf esp8266_iot.out out.bin\r\n"); - print("\r\n"); - print(" -header\r\n"); - print(" Export elf sections as bytes to a C header file.\r\n"); - print(" e.g. esptool2 -elf esp8266_iot.out out.h .text .data .rodata\r\n"); - print("\r\n"); - print(" -bin\r\n"); - print(" Create binary program image, for standalone and bootloaded apps, with\r\n"); - print(" specified elf sections. Includes sdk library for bootloaded apps.\r\n"); - print(" e.g. esptool2 -bin esp8266_iot.out out.bin .text .data .rodata\r\n"); - print(" Options:\r\n"); - print(" bootloader: -boot0 -boot1 -boot2 (default -boot0)\r\n"); - print(" -boot0 = standalone app, not built for bootloader use\r\n"); - print(" -boot1 = built for bootloader v1.1\r\n"); - print(" -boot2 = built for bootloader v1.2+ (use for rBoot roms)\r\n"); - print(" (elf file must have been linked appropriately for chosen option)\r\n"); - print(" spi size (kb): -256 -512 -1024 -2048 -4096 (default -512)\r\n"); - print(" spi mode: -qio -qout -dio -dout (default -qio)\r\n"); - print(" spi speed: -20 -26.7 -40 -80 (default -40)\r\n"); - print(" include irom in checksum: -iromchksum (also needs enabling in rBoot)\r\n"); - print("\r\n"); - print("General options:\r\n"); - print(" -quiet prints only error messages\r\n"); - print(" -debug print extra debug information\r\n"); - print(" -- no more options follow (needed if your elf file starts with a '-')\r\n"); - print("\r\n"); - print("Returns:\r\n"); - print(" 0 on success\r\n"); - print(" -1 on failure\r\n"); - print("\r\n"); - return -1; - } - - // validate command line options - if (opts != 1) { - error("Error: You must specify -bin OR -lib OR -header for build type.\r\n"); - return -1; - } - - if (quieton && debugon) { - error("Error: You cannot specify -quiet and -debug.\r\n"); - return -1; - } - - // check enough parameters - if ((libfile && i + 2 > argc) || ((binfile | headerfile) && i + 3 > argc)) { - error("Error: Not enough arguments supplied.\r\n"); - return -1; - } else if (libfile && i + 2 < argc) { - error("Error: Too many arguments supplied.\r\n"); - return -1; - } - - // get parameters - infile = argv[i++]; - outfile = argv[i++]; - numstr = argc - i; - - // do it - if (binfile) { - if (!CreateBinFile(infile, outfile, bootver, mode, clock, size, iromchksum, &argv[i], numstr)) { - remove(outfile); - return -1; - } - } else if (headerfile) { - if (!CreateHeaderFile(infile, outfile, &argv[i], numstr)) { - remove(outfile); - return -1; - } - } else { - if (!ExportElfSection(infile, outfile, ".irom0.text")) { - remove(outfile); - return -1; - } - - } - - print("Successfully created '%s'.\r\n", outfile); - return 0; - -} diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2.h b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2.h deleted file mode 100644 index b0eba08..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2.h +++ /dev/null @@ -1,44 +0,0 @@ -/********************************************************************************** -* -* Copyright (c) 2015 Richard A Burton -* -* This file is part of esptool2. -* -* esptool2 is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* esptool2 is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with esptool2. If not, see . -* -**********************************************************************************/ - -#ifndef CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ESPTOOL2_H_ -#define CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ESPTOOL2_H_ - -#ifdef WIN32 -typedef signed __int8 int8_t; -typedef signed __int16 int16_t; -typedef signed __int32 int32_t; -typedef unsigned __int8 uint8_t; -typedef unsigned __int16 uint16_t; -typedef unsigned __int32 uint32_t; -#else -#include -#endif - -#define true 1 -#define false 0 -#define bool char - -void debug( const char* format, ... ); -void print( const char* format, ... ); -void error( const char* format, ... ); - -#endif /* CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ESPTOOL2_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2_elf.c b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2_elf.c deleted file mode 100644 index 88ede4e..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2_elf.c +++ /dev/null @@ -1,183 +0,0 @@ -/********************************************************************************** -* -* Copyright (c) 2015 Richard A Burton -* -* This file is part of esptool2. -* -* esptool2 is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* esptool2 is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with esptool2. If not, see . -* -**********************************************************************************/ - -#include -#include - -#include "esptool2.h" -#include "esptool2_elf.h" - -// Find a section in an elf file by name. -// Returns pointer to section if found, else returns zero. -// Does not produce any messages. -MyElf_Section* GetElfSection(MyElf_File *elf, char *name) { - - int i; - - for(i = 0; i < elf->header.e_shnum - 1; i++) { - if(!strcmp(name, elf->sections[i].name)) { - debug("Found section '%s'.\r\n", name); - return &elf->sections[i]; - } - } - - debug("Could not find section '%s'.\r\n", name); - return 0; -} - -// Reads an elf section (actual data) from the elf file. -// Returns a pointer to newly allocated memory (or zero on error), -// which should be freed by the caller when finished with. -// Produces error message on failure (so caller doesn't need to). -unsigned char* GetElfSectionData(MyElf_File *elf, MyElf_Section *section) { - - unsigned char *data = 0; - - if (section->size && section->offset) { - - data = (unsigned char*)malloc(section->size); - if(!data) { - error("Error: Out of memory!\r\n"); - return 0; - } - - if(fseek(elf->fd, section->offset, SEEK_SET) || - fread(data, 1, section->size, elf->fd) != section->size) { - error("Error: Can't read section '%s' data from elf file.\r\n", section->name); - free(data); - return 0; - } - - } else { - error("Error: Section '%s' has no data to read.\r\n", section->name); - } - - return data; -} - -// Opens an elf file and reads the string table and file & section headers. -// Returns a pointer to a MyElf_File structure (or zero on error). -// UnloadElf should be called to dispose of the MyElf_File structure. -// Produces error message on failure (so caller doesn't need to). -MyElf_File* LoadElf(char *infile) { - - int i; - MyElf_File *elf; - Elf32_Shdr temp; - - // allocate the elf structure - elf = (MyElf_File*)malloc(sizeof(MyElf_File)); - if(!elf) { - error("Error: Out of memory!\r\n"); - goto error_exit; - } - memset(elf, 0, sizeof(MyElf_File)); - - // open the file - elf->fd = fopen(infile, "rb"); - if(!elf->fd) { - error("Error: Can't open elf file '%s'.\r\n", infile); - goto error_exit; - } - - // read the header - if(fread(&elf->header, 1, sizeof(Elf32_Ehdr), elf->fd) != sizeof(Elf32_Ehdr)) { - error("Error: Can't read elf file header.\r\n"); - goto error_exit; - } - - // check the file header - if (memcmp(elf->header.e_ident, "\x7f" "ELF", 4)) { - error("Error: Input files doesn't look like an elf file (bad header).\r\n"); - goto error_exit; - } - - // is there a string table section (we need one) - if(!elf->header.e_shstrndx) { - error("Error: Elf file does not contain a string table.\r\n"); - goto error_exit; - } - - // get the string table section header - if(fseek(elf->fd, elf->header.e_shoff + (elf->header.e_shentsize * elf->header.e_shstrndx), SEEK_SET) || - fread(&temp, 1, sizeof(Elf32_Shdr), elf->fd) != sizeof(Elf32_Shdr)) { - - error("Error: Can't read string table section from elf file.\r\n"); - goto error_exit; - } - - // read the actual string table - if(!temp.sh_size) { - error("Error: Elf file contains an empty string table.\r\n"); - goto error_exit; - } - elf->strings = (char*)malloc(temp.sh_size); - if(!elf->strings) { - error("Error: Out of memory!\r\n"); - goto error_exit; - } - if(fseek(elf->fd, temp.sh_offset, SEEK_SET) || - fread(elf->strings, 1, temp.sh_size, elf->fd) != temp.sh_size) { - error("Error: Failed to read string stable from elf file.\r\n"); - goto error_exit; - } - - // read section headers - elf->sections = (MyElf_Section*)malloc(sizeof(MyElf_Section) * elf->header.e_shnum); - if(!elf->sections) { - error("Error: Out of memory!\r\n"); - goto error_exit; - } - for(i = 1; i < elf->header.e_shnum; i++) { - if(fseek(elf->fd, elf->header.e_shoff + (elf->header.e_shentsize * i), SEEK_SET) - || fread(&temp, 1, sizeof(Elf32_Shdr), elf->fd) != sizeof(Elf32_Shdr)) { - error("Error: Can't read section %d from elf file.\r\n", i); - break; - } - debug("Read section %d '%s'.\r\n", i, elf->strings + temp.sh_name); - elf->sections[i-1].address = temp.sh_addr; - elf->sections[i-1].offset = temp.sh_offset; - elf->sections[i-1].size = temp.sh_size; - elf->sections[i-1].name = elf->strings + temp.sh_name; - } - - return elf; - -error_exit: - if (elf) { - if (elf->fd) fclose(elf->fd); - if (elf->strings) free(elf->strings); - free(elf); - } - return 0; - -} - -// Close an elf file and dispose of the MyElf_File structure. -void UnloadElf(MyElf_File *elf) { - if (elf) { - debug("Unloading elf file.\r\n"); - if(elf->fd) fclose(elf->fd); - if(elf->strings) free(elf->strings); - if(elf->sections) free(elf->sections); - free(elf); - } -} diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2_elf.h b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2_elf.h deleted file mode 100644 index ef98511..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/esptool2_elf.h +++ /dev/null @@ -1,48 +0,0 @@ -/********************************************************************************** -* -* Copyright (c) 2015 Richard A Burton -* -* This file is part of esptool2. -* -* esptool2 is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* esptool2 is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with esptool2. If not, see . -* -**********************************************************************************/ - -#ifndef CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ESPTOOL2_ELF_H_ -#define CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ESPTOOL2_ELF_H_ - -#include - -#include "elf.h" - -typedef struct { - Elf32_Off offset; - Elf32_Addr address; - Elf32_Word size; - char *name; -} MyElf_Section; - -typedef struct { - FILE *fd; - Elf32_Ehdr header; - char *strings; - MyElf_Section *sections; -} MyElf_File; - -MyElf_File* LoadElf(char *infile); -void UnloadElf(MyElf_File *e_object); -MyElf_Section* GetElfSection(MyElf_File *e_object, char *name); -unsigned char* GetElfSectionData(MyElf_File *e_object, MyElf_Section *section); - -#endif /* CS_COMMON_PLATFORMS_ESP8266_RBOOT_ESPTOOL2_ESPTOOL2_ELF_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/license.txt b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/license.txt deleted file mode 100644 index 94a9ed0..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/license.txt +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/readme.txt b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/readme.txt deleted file mode 100644 index eb9f0ef..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/esptool2/readme.txt +++ /dev/null @@ -1,19 +0,0 @@ -Esptool2 -richardaburton@gmail.com -http://richard.burtons.org/ - -Esptool2 is a tool for creating rom images for the ESP8266. It is an alternative -to using the SDK supplied shell script/Makefile/python script combo, which is a -mess. It was inspired by the windows esptool v0.0.2 by mamalala and found on -www.esp8266.com but made somewhat simpler in code and usage. It also adds -support for boot loader v1.2+ rom types, which was the main reason I wrote it. - -It was written for my own use and the name was simply to distinguish it for the -other version on my system. The 2 is not intended to imply it is better than the -original. The original has since been updated to v0.0.3 which can write to the -flash, but I currently have no intention to add that to esptool2, it is purely a -rom creating utility. It has become an integral part of my build process now and -has added functionality needed for building the rBoot boot loader. Since I have -released rBoot I needed to release this as well. - -Run tool for full usage instructions, or look at the code. diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-api.c b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-api.c deleted file mode 100644 index 2d2e83d..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-api.c +++ /dev/null @@ -1,152 +0,0 @@ -////////////////////////////////////////////////// -// rBoot OTA and config API for ESP8266. -// Copyright 2015 Richard A Burton -// richardaburton@gmail.com -// See license.txt for license terms. -// OTA code based on SDK sample from Espressif. -////////////////////////////////////////////////// - -#ifdef RBOOT_INTEGRATION -#include -#endif - -#include -#include -#include - -#ifdef RTOS_SDK -#include "spi_flash.h" -#else -#include -#endif - -#include "rboot-api.h" -#include "../rboot-private.h" -#include "esp_missing_includes.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// get the rboot config -rboot_config ICACHE_FLASH_ATTR rboot_get_config(void) { - rboot_config conf; - spi_flash_read(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32*)&conf, sizeof(rboot_config)); - return conf; -} - -// write the rboot config -// preserves the contents of the rest of the sector, -// so the rest of the sector can be used to store user data -// updates checksum automatically (if enabled) -bool ICACHE_FLASH_ATTR rboot_set_config(rboot_config *conf) { - uint32_t buffer[sizeof(*conf) / sizeof(uint32_t) + 1]; -#ifdef BOOT_CONFIG_CHKSUM - uint8 chksum; - uint8 *ptr; -#endif - -#ifdef BOOT_CONFIG_CHKSUM - chksum = CHKSUM_INIT; - for (ptr = (uint8*)conf; ptr < &conf->chksum; ptr++) { - chksum ^= *ptr; - } - conf->chksum = chksum; -#endif - - memset(buffer, 0xff, sizeof(buffer)); - memcpy(buffer, conf, sizeof(*conf)); - if (spi_flash_erase_sector(BOOT_CONFIG_SECTOR) != SPI_FLASH_RESULT_OK || - spi_flash_write(BOOT_CONFIG_SECTOR * SECTOR_SIZE, buffer, sizeof(buffer)) != SPI_FLASH_RESULT_OK) { - return false; - } - return true; -} - -// get current boot rom -uint8 ICACHE_FLASH_ATTR rboot_get_current_rom(void) { - rboot_config conf; - conf = rboot_get_config(); - return conf.current_rom; -} - -// set current boot rom -bool ICACHE_FLASH_ATTR rboot_set_current_rom(uint8 rom) { - rboot_config conf; - conf = rboot_get_config(); - if (rom >= conf.count) return false; - conf.current_rom = rom; - return rboot_set_config(&conf); -} - -// create the write status struct, based on supplied start address -rboot_write_status ICACHE_FLASH_ATTR rboot_write_init(uint32 start_addr) { - rboot_write_status status = {0}; - status.start_addr = start_addr; - status.start_sector = start_addr / SECTOR_SIZE; - //status.max_sector_count = 200; - //os_printf("init addr: 0x%08x\r\n", start_addr); - - return status; -} - -// function to do the actual writing to flash -// call repeatedly with more data (max len per write is the flash sector size (4k)) -bool ICACHE_FLASH_ATTR rboot_write_flash(rboot_write_status *status, uint8 *data, uint16 len) { - - bool ret = false; - uint8 *buffer; - - if (data == NULL || len == 0) { - return true; - } - - // get a buffer - buffer = (uint8 *) calloc(1, len + status->extra_count); - if (!buffer) { - //os_printf("No ram!\r\n"); - return false; - } - - // copy in any remaining bytes from last chunk - memcpy(buffer, status->extra_bytes, status->extra_count); - // copy in new data - memcpy(buffer + status->extra_count, data, len); - - // calculate length, must be multiple of 4 - // save any remaining bytes for next go - len += status->extra_count; - status->extra_count = len % 4; - len -= status->extra_count; - memcpy(status->extra_bytes, buffer + len, status->extra_count); - - // check data will fit - //if (status->start_addr + len < (status->start_sector + status->max_sector_count) * SECTOR_SIZE) { - - if (len > SECTOR_SIZE) { - // to support larger writes we would need to erase current - // (if not already done), next and possibly later sectors too - } else { - // check if the sector the write finishes in has been erased yet, - // this is fine as long as data len < sector size - if (status->last_sector_erased != (status->start_addr + len) / SECTOR_SIZE) { - status->last_sector_erased = (status->start_addr + len) / SECTOR_SIZE; - spi_flash_erase_sector(status->last_sector_erased); - } - } - - // write current chunk - //os_printf("write addr: 0x%08x, len: 0x%04x\r\n", status->start_addr, len); - if (spi_flash_write(status->start_addr, (uint32 *)buffer, len) == SPI_FLASH_RESULT_OK) { - ret = true; - status->start_addr += len; - } - //} - - free(buffer); - return ret; -} - -#ifdef __cplusplus -} -#endif diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-api.h b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-api.h deleted file mode 100644 index c4601d3..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-api.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_APPCODE_RBOOT_API_H_ -#define CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_APPCODE_RBOOT_API_H_ - -////////////////////////////////////////////////// -// rBoot OTA and config API for ESP8266. -// Copyright 2015 Richard A Burton -// richardaburton@gmail.com -// See license.txt for license terms. -// OTA code based on SDK sample from Espressif. -////////////////////////////////////////////////// - -#include "../rboot.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - uint32 start_addr; - uint32 start_sector; - //uint32 max_sector_count; - uint32 last_sector_erased; - uint8 extra_count; - uint8 extra_bytes[4]; -} rboot_write_status; - -rboot_config ICACHE_FLASH_ATTR rboot_get_config(void); -bool ICACHE_FLASH_ATTR rboot_set_config(rboot_config *conf); -uint8 ICACHE_FLASH_ATTR rboot_get_current_rom(void); -bool ICACHE_FLASH_ATTR rboot_set_current_rom(uint8 rom); -rboot_write_status ICACHE_FLASH_ATTR rboot_write_init(uint32 start_addr); -bool ICACHE_FLASH_ATTR rboot_write_flash(rboot_write_status *status, uint8 *data, uint16 len); - -#ifdef __cplusplus -} -#endif - -#endif /* CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_APPCODE_RBOOT_API_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-bigflash.c b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-bigflash.c deleted file mode 100644 index cd8a180..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/appcode/rboot-bigflash.c +++ /dev/null @@ -1,61 +0,0 @@ -////////////////////////////////////////////////// -// rBoot open source boot loader for ESP8266. -// Copyright 2015 Richard A Burton -// richardaburton@gmail.com -// See license.txt for license terms. -////////////////////////////////////////////////// - -/* clang-format off */ - -#ifdef RBOOT_INTEGRATION -#include -#endif - -typedef unsigned int uint32; -typedef unsigned char uint8; - -#include - -#if 0 //def BOOT_BIG_FLASH - -// plain sdk defaults to iram -#ifndef IRAM_ATTR -#define IRAM_ATTR -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -extern void Cache_Read_Disable(); -extern uint32 SPIRead(uint32, void*, uint32); -extern void Cache_Read_Enable(uint32, uint32, uint32); - -uint8 rBoot_mmap_1 = 0xff; -uint8 rBoot_mmap_2 = 0xff; - -// this function must remain in iram -IRAM NOINSTR void __wrap_Cache_Read_Enable_New(void) { - if (rBoot_mmap_1 == 0xff) { - uint32 addr; - rboot_config conf; - - Cache_Read_Disable(); - - SPIRead(BOOT_CONFIG_SECTOR * SECTOR_SIZE, &conf, sizeof(rboot_config)); - - addr = conf.roms[conf.current_rom]; - addr /= 0x100000; - - rBoot_mmap_2 = addr / 2; - rBoot_mmap_1 = addr % 2; - } - - Cache_Read_Enable(rBoot_mmap_1, rBoot_mmap_2, 1); -} - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/firmware/rboot.bin b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/firmware/rboot.bin deleted file mode 100644 index a928e0b0e8fb3b8f63ad57135a110795e3570d3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2464 zcmb7Fe{56N6~6b`FF&7&ZD^JmTHX6dmeAqUenAj2MZD*xAtnScC4s7~x|i4v9*zy> z1xi&7Mk%dCx>4BF3aP58x{0;>!BW(&8xtX9fkp1NWL3q~RdX|@0{s=Gbfqf1+jFlS zE&ujOpMAb_&i(Fp@4LRfCLy$?#H}Ob){`aZ8$TZ%u-DZ5$X?R`SOL%h>jB#UQNT{X z0l?FMqk!iDS-@$)S->U0+kiZv=AgX>xe>YrF>+(D=g15Qg8pt!2gKQR0sPTMOGifamegW-P*4tpLCDfA4(?+Om@gAOx%nG<@N;dgkT zVktLFf5Y&U=ve?`yb67l;lB#bHDQiZ3{MEoHDQjQGdwOh*L;ED3!Km_!{>Rxjgu8U zqoQ&oOLccw$-{%grk;pCv ziLeA8+vUfv&=k|-ZXgL+^g)v-(*$q3Bn5KrkCqr2M&r!gc0h|FJXn~nWb>qFnLKKg zj~b;VwS40$BAW*F`vUnZ0qE2>1W3c6 zJ{@@Kxcl(8P)n=oK;)Ca_#(I=94-EkgcdK=KM7nMzV~iUzRO8IZ(SU268w9G;&%k; zERb&t@?e1|cl%Wd?Pvnci%r>{{j;}ABk$Y6yv=vR*R;4U@}3PgI14+rE|SLIWXdeL zhqSsvS&ZMcVWqig)rCGdR!%2vpV}bxjLM-&54~tNJMk2+M<#^W(4XlA8^6UfiZp%~ zQYSqPQl$o8tEG2dgCi=#i3p9dP#=O52p-FiHMawrzTv~~ZGKlDI~jnxSvk~-N6mGT z+4+_JE72^(%YU&}=IFhTLcKQ{5h2t4syXiTSyNrKz4h|hpe_X}S z-4)Wc0AJ;0zYCVWAkRjw=kR}d;`)0%_uN*gUO79XTOm{LaegJyPOzX-W0bC&qTy-U zF-zB;@Q9_|M)Ln)%~=Unnn&=!|Iyn3S4Ae2PFrM8uu*Fa5ysM$N1vC=8M zf51#68~f4&t_swiGiwi5> zxYJ6cQ!ONv=qGW$G9j=vldux0eiDtv&B3Hadimz`rBm@lek zJ}VX#6@H zVo~8oyqq|^gIIJqiJQq}ui3YQSZNa9OP1CZdN#v+bx}UL*PQdh{T0j?2J^W$q)C4| iUGQh_0Zp?v87q39TNn3UR}^tS0lx -#endif - -#include "rboot-private.h" - -usercode* NOINLINE load_rom(uint32 readpos) { - - uint8 buffer[BUFFER_SIZE]; - uint8 sectcount; - uint8 *writepos; - uint32 remaining; - usercode* usercode; - - rom_header *header = (rom_header*)buffer; - section_header *section = (section_header*)buffer; - - // read rom header - SPIRead(readpos, header, sizeof(rom_header)); - readpos += sizeof(rom_header); - - // create function pointer for entry point - usercode = header->entry; - - // copy all the sections - for (sectcount = header->count; sectcount > 0; sectcount--) { - - // read section header - SPIRead(readpos, section, sizeof(section_header)); - readpos += sizeof(section_header); - - // get section address and length - writepos = section->address; - remaining = section->length; - - while (remaining > 0) { - // work out how much to read, up to 16 bytes at a time - uint32 readlen = (remaining < BUFFER_SIZE) ? remaining : BUFFER_SIZE; - // read the block - SPIRead(readpos, buffer, readlen); - readpos += readlen; - // copy the block - ets_memcpy(writepos, buffer, readlen); - // increment next write position - writepos += readlen; - // decrement remaining count - remaining -= readlen; - } - } - - return usercode; -} - -#ifdef BOOT_NO_ASM - -void call_user_start(uint32 readpos) { - usercode* user; - user = load_rom(readpos); - user(); -} - -#else - -void call_user_start(uint32 readpos) { - __asm volatile ( - "mov a15, a0\n" // store return addr, we already splatted a15! - "call0 load_rom\n" // load the rom - "mov a0, a15\n" // restore return addr - "jx a2\n" // now jump to the rom code - ); -} - -#endif diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot-stage2a.ld b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot-stage2a.ld deleted file mode 100644 index 7af83ea..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot-stage2a.ld +++ /dev/null @@ -1,211 +0,0 @@ -/* This linker script generated from xt-genldscripts.tpp for LSP . */ -/* Linker Script for ld -N */ -MEMORY -{ - dport0_0_seg : org = 0x3FF00000, len = 0x10 - dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x4010FC00, len = 0x400 - irom0_0_seg : org = 0x40240000, len = 0x3C000 -} - -PHDRS -{ - dport0_0_phdr PT_LOAD; - dram0_0_phdr PT_LOAD; - dram0_0_bss_phdr PT_LOAD; - iram1_0_phdr PT_LOAD; - irom0_0_phdr PT_LOAD; -} - - -/* Default entry point: */ -ENTRY(call_user_start) -EXTERN(_DebugExceptionVector) -EXTERN(_DoubleExceptionVector) -EXTERN(_KernelExceptionVector) -EXTERN(_NMIExceptionVector) -EXTERN(_UserExceptionVector) -PROVIDE(_memmap_vecbase_reset = 0x40000000); -/* Various memory-map dependent cache attribute settings: */ -_memmap_cacheattr_wb_base = 0x00000110; -_memmap_cacheattr_wt_base = 0x00000110; -_memmap_cacheattr_bp_base = 0x00000220; -_memmap_cacheattr_unused_mask = 0xFFFFF00F; -_memmap_cacheattr_wb_trapnull = 0x2222211F; -_memmap_cacheattr_wba_trapnull = 0x2222211F; -_memmap_cacheattr_wbna_trapnull = 0x2222211F; -_memmap_cacheattr_wt_trapnull = 0x2222211F; -_memmap_cacheattr_bp_trapnull = 0x2222222F; -_memmap_cacheattr_wb_strict = 0xFFFFF11F; -_memmap_cacheattr_wt_strict = 0xFFFFF11F; -_memmap_cacheattr_bp_strict = 0xFFFFF22F; -_memmap_cacheattr_wb_allvalid = 0x22222112; -_memmap_cacheattr_wt_allvalid = 0x22222112; -_memmap_cacheattr_bp_allvalid = 0x22222222; -PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_wb_trapnull); - -SECTIONS -{ - - .dport0.rodata : ALIGN(4) - { - _dport0_rodata_start = ABSOLUTE(.); - *(.dport0.rodata) - *(.dport.rodata) - _dport0_rodata_end = ABSOLUTE(.); - } >dport0_0_seg :dport0_0_phdr - - .dport0.literal : ALIGN(4) - { - _dport0_literal_start = ABSOLUTE(.); - *(.dport0.literal) - *(.dport.literal) - _dport0_literal_end = ABSOLUTE(.); - } >dport0_0_seg :dport0_0_phdr - - .dport0.data : ALIGN(4) - { - _dport0_data_start = ABSOLUTE(.); - *(.dport0.data) - *(.dport.data) - _dport0_data_end = ABSOLUTE(.); - } >dport0_0_seg :dport0_0_phdr - - .data : ALIGN(4) - { - _data_start = ABSOLUTE(.); - *(.data) - *(.data.*) - *(.gnu.linkonce.d.*) - *(.data1) - *(.sdata) - *(.sdata.*) - *(.gnu.linkonce.s.*) - *(.sdata2) - *(.sdata2.*) - *(.gnu.linkonce.s2.*) - *(.jcr) - _data_end = ABSOLUTE(.); - } >dram0_0_seg :dram0_0_phdr - - .rodata : ALIGN(4) - { - _rodata_start = ABSOLUTE(.); - *(.rodata) - *(.rodata.*) - *(.gnu.linkonce.r.*) - *(.rodata1) - __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); - *(.xt_except_table) - *(.gcc_except_table) - *(.gnu.linkonce.e.*) - *(.gnu.version_r) - *(.eh_frame) - /* C++ constructor and destructor tables, properly ordered: */ - KEEP (*crtbegin.o(.ctors)) - KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) - KEEP (*(SORT(.ctors.*))) - KEEP (*(.ctors)) - KEEP (*crtbegin.o(.dtors)) - KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) - KEEP (*(SORT(.dtors.*))) - KEEP (*(.dtors)) - /* C++ exception handlers table: */ - __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); - *(.xt_except_desc) - *(.gnu.linkonce.h.*) - __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); - *(.xt_except_desc_end) - *(.dynamic) - *(.gnu.version_d) - . = ALIGN(4); /* this table MUST be 4-byte aligned */ - _bss_table_start = ABSOLUTE(.); - LONG(_bss_start) - LONG(_bss_end) - _bss_table_end = ABSOLUTE(.); - _rodata_end = ABSOLUTE(.); - } >dram0_0_seg :dram0_0_phdr - - .bss ALIGN(8) (NOLOAD) : ALIGN(4) - { - . = ALIGN (8); - _bss_start = ABSOLUTE(.); - *(.dynsbss) - *(.sbss) - *(.sbss.*) - *(.gnu.linkonce.sb.*) - *(.scommon) - *(.sbss2) - *(.sbss2.*) - *(.gnu.linkonce.sb2.*) - *(.dynbss) - *(.bss) - *(.bss.*) - *(.gnu.linkonce.b.*) - *(COMMON) - . = ALIGN (8); - _bss_end = ABSOLUTE(.); - _heap_start = ABSOLUTE(.); -/* _stack_sentry = ALIGN(0x8); */ - } >dram0_0_seg :dram0_0_bss_phdr -/* __stack = 0x3ffc8000; */ - - .text : ALIGN(4) - { - _stext = .; - _text_start = ABSOLUTE(.); - *(.UserEnter.text) - . = ALIGN(16); - *(.DebugExceptionVector.text) - . = ALIGN(16); - *(.NMIExceptionVector.text) - . = ALIGN(16); - *(.KernelExceptionVector.text) - LONG(0) - LONG(0) - LONG(0) - LONG(0) - . = ALIGN(16); - *(.UserExceptionVector.text) - LONG(0) - LONG(0) - LONG(0) - LONG(0) - . = ALIGN(16); - *(.DoubleExceptionVector.text) - LONG(0) - LONG(0) - LONG(0) - LONG(0) - . = ALIGN (16); - *(.entry.text) - *(.init.literal) - *(.init) - *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) - *(.fini.literal) - *(.fini) - *(.gnu.version) - _text_end = ABSOLUTE(.); - _etext = .; - } >iram1_0_seg :iram1_0_phdr - - .lit4 : ALIGN(4) - { - _lit4_start = ABSOLUTE(.); - *(*.lit4) - *(.lit4.*) - *(.gnu.linkonce.lit4.*) - _lit4_end = ABSOLUTE(.); - } >iram1_0_seg :iram1_0_phdr - - .irom0.text : ALIGN(4) - { - _irom0_text_start = ABSOLUTE(.); - *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text) - _irom0_text_end = ABSOLUTE(.); - } >irom0_0_seg :irom0_0_phdr - -} - -/* get ROM code address */ -INCLUDE "rboot_rom.ld" diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.c b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.c deleted file mode 100644 index 4ac5df1..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.c +++ /dev/null @@ -1,414 +0,0 @@ -////////////////////////////////////////////////// -// rBoot open source boot loader for ESP8266. -// Copyright 2015 Richard A Burton -// richardaburton@gmail.com -// See license.txt for license terms. -////////////////////////////////////////////////// -/* clang-format off */ - -#ifdef RBOOT_INTEGRATION -#include -#endif - -#include "rboot-private.h" -#include - -static uint32 check_image(uint32 readpos) { - - uint8 buffer[BUFFER_SIZE]; - uint8 sectcount; - uint8 sectcurrent; - uint8 *writepos; - uint8 chksum = CHKSUM_INIT; - uint32 loop; - uint32 remaining; - uint32 romaddr; - - rom_header_new *header = (rom_header_new*)buffer; - section_header *section = (section_header*)buffer; - - if (readpos == 0 || readpos == 0xffffffff) { - return 0; - } - - // read rom header - if (SPIRead(readpos, header, sizeof(rom_header_new)) != 0) { - return 0; - } - - // check header type - if (header->magic == ROM_MAGIC) { - // old type, no extra header or irom section to skip over - romaddr = readpos; - readpos += sizeof(rom_header); - sectcount = header->count; - } else if (header->magic == ROM_MAGIC_NEW1 && header->count == ROM_MAGIC_NEW2) { - // new type, has extra header and irom section first - romaddr = readpos + header->len + sizeof(rom_header_new); -#ifdef BOOT_IROM_CHKSUM - // we will set the real section count later, when we read the header - sectcount = 0xff; - // just skip the first part of the header - // rest is processed for the chksum - readpos += sizeof(rom_header); -#else - // skip the extra header and irom section - readpos = romaddr; - // read the normal header that follows - if (SPIRead(readpos, header, sizeof(rom_header)) != 0) { - return 0; - } - sectcount = header->count; - readpos += sizeof(rom_header); -#endif - } else { - return 0; - } - - // test each section - for (sectcurrent = 0; sectcurrent < sectcount; sectcurrent++) { - - // read section header - if (SPIRead(readpos, section, sizeof(section_header)) != 0) { - return 0; - } - readpos += sizeof(section_header); - - // get section address and length - writepos = section->address; - remaining = section->length; - - while (remaining > 0) { - // work out how much to read, up to BUFFER_SIZE - uint32 readlen = (remaining < BUFFER_SIZE) ? remaining : BUFFER_SIZE; - // read the block - if (SPIRead(readpos, buffer, readlen) != 0) { - return 0; - } - // increment next read and write positions - readpos += readlen; - writepos += readlen; - // decrement remaining count - remaining -= readlen; - // add to chksum - for (loop = 0; loop < readlen; loop++) { - chksum ^= buffer[loop]; - } - } - -#ifdef BOOT_IROM_CHKSUM - if (sectcount == 0xff) { - // just processed the irom section, now - // read the normal header that follows - if (SPIRead(readpos, header, sizeof(rom_header)) != 0) { - return 0; - } - sectcount = header->count + 1; - readpos += sizeof(rom_header); - } -#endif - } - - // round up to next 16 and get checksum - readpos = readpos | 0x0f; - if (SPIRead(readpos, buffer, 1) != 0) { - return 0; - } - - // compare calculated and stored checksums - if (buffer[0] != chksum) { - return 0; - } - - return romaddr; -} - -#define ETS_UNCACHED_ADDR(addr) (addr) -#define READ_PERI_REG(addr) (*((volatile uint32 *)ETS_UNCACHED_ADDR(addr))) -#define WRITE_PERI_REG(addr, val) (*((volatile uint32 *)ETS_UNCACHED_ADDR(addr))) = (uint32)(val) -#define PERIPHS_RTC_BASEADDR 0x60000700 -#define REG_RTC_BASE PERIPHS_RTC_BASEADDR -#define RTC_GPIO_OUT (REG_RTC_BASE + 0x068) -#define RTC_GPIO_ENABLE (REG_RTC_BASE + 0x074) -#define RTC_GPIO_IN_DATA (REG_RTC_BASE + 0x08C) -#define RTC_GPIO_CONF (REG_RTC_BASE + 0x090) -#define PAD_XPD_DCDC_CONF (REG_RTC_BASE + 0x0A0) -static uint32 get_gpio16(void) { - // set output level to 1 - WRITE_PERI_REG(RTC_GPIO_OUT, (READ_PERI_REG(RTC_GPIO_OUT) & (uint32)0xfffffffe) | (uint32)(1)); - - // read level - WRITE_PERI_REG(PAD_XPD_DCDC_CONF, (READ_PERI_REG(PAD_XPD_DCDC_CONF) & 0xffffffbc) | (uint32)0x1); // mux configuration for XPD_DCDC and rtc_gpio0 connection - WRITE_PERI_REG(RTC_GPIO_CONF, (READ_PERI_REG(RTC_GPIO_CONF) & (uint32)0xfffffffe) | (uint32)0x0); //mux configuration for out enable - WRITE_PERI_REG(RTC_GPIO_ENABLE, READ_PERI_REG(RTC_GPIO_ENABLE) & (uint32)0xfffffffe); //out disable - - uint32 x = (READ_PERI_REG(RTC_GPIO_IN_DATA) & 1); - - return x; -} - -#ifdef BOOT_CONFIG_CHKSUM -// calculate checksum for block of data -// from start up to (but excluding) end -static uint8 calc_chksum(uint8 *start, uint8 *end) { - uint8 chksum = CHKSUM_INIT; - while(start < end) { - chksum ^= *start; - start++; - } - return chksum; -} -#endif - -#define UART_CLKDIV_26MHZ(B) (52000000 + B / 2) / B - -// prevent this function being placed inline with main -// to keep main's stack size as small as possible -// don't mark as static or it'll be optimised out when -// using the assembler stub -uint32 NOINLINE find_image(void) { - - uint8 flag; - uint32 runAddr; - uint32 flashsize; - int32 romToBoot; - uint8 gpio_boot = FALSE; - uint8 updateConfig = FALSE; - uint8 buffer[SECTOR_SIZE]; - - rboot_config *romconf = (rboot_config*)buffer; - rom_header *header = (rom_header*)buffer; - - // delay to slow boot (help see messages when debugging) - //ets_delay_us(2000000); - - uart_div_modify(0, UART_CLKDIV_26MHZ(115200)); - ets_delay_us(1000); - - ets_printf("\r\nrBoot v1.2.1 - richardaburton@gmail.com\r\n"); - - // read rom header - SPIRead(0, header, sizeof(rom_header)); - - // print and get flash size - ets_printf("Flash Size: "); - flag = header->flags2 >> 4; - if (flag == 0) { - ets_printf("4 Mbit\r\n"); - flashsize = 0x80000; - } else if (flag == 1) { - ets_printf("2 Mbit\r\n"); - flashsize = 0x40000; - } else if (flag == 2) { - ets_printf("8 Mbit\r\n"); - flashsize = 0x100000; - } else { -#ifdef BOOT_BIG_FLASH - if (flag == 3) { - ets_printf("16 Mbit\r\n"); - flashsize = 0x200000; - } else if (flag == 4) { - ets_printf("32 Mbit\r\n"); - flashsize = 0x400000; - } else if (flag == 8) { - ets_printf("64 Mbit\r\n"); - flashsize = 0x800000; - } else if (flag == 9) { - ets_printf("128 Mbit\r\n"); - flashsize = 0x1000000; - } else { - ets_printf("unknown\r\n"); - flashsize = 0x100000; // assume 8Mbit - } -#else - ets_printf("8 Mbit\r\n"); - flashsize = 0x100000; // limit to 8Mbit -#endif - } - - // print spi mode - ets_printf("Flash Mode: "); - if (header->flags1 == 0) { - ets_printf("QIO\r\n"); - } else if (header->flags1 == 1) { - ets_printf("QOUT\r\n"); - } else if (header->flags1 == 2) { - ets_printf("DIO\r\n"); - } else if (header->flags1 == 3) { - ets_printf("DOUT\r\n"); - } else { - ets_printf("unknown\r\n"); - } - - // print spi speed - ets_printf("Flash Speed: "); - flag = header->flags2 & 0x0f; - if (flag == 0) ets_printf("40 MHz\r\n"); - else if (flag == 1) ets_printf("26.7 MHz\r\n"); - else if (flag == 2) ets_printf("20 MHz\r\n"); - else if (flag == 0x0f) ets_printf("80 MHz\r\n"); - else ets_printf("unknown\r\n"); - - // print enabled options -#ifdef BOOT_BIG_FLASH - ets_printf("rBoot Option: Big flash\r\n"); -#endif -#ifdef BOOT_CONFIG_CHKSUM - ets_printf("rBoot Option: Config chksum\r\n"); -#endif -#ifdef BOOT_IROM_CHKSUM - ets_printf("rBoot Option: irom chksum\r\n"); -#endif - - ets_printf("\r\n"); - - // read boot config - SPIRead(BOOT_CONFIG_SECTOR * SECTOR_SIZE, buffer, SECTOR_SIZE); - // fresh install or old version? - if (romconf->magic != BOOT_CONFIG_MAGIC || romconf->version != BOOT_CONFIG_VERSION -#ifdef BOOT_CONFIG_CHKSUM - || romconf->chksum != calc_chksum((uint8*)romconf, (uint8*)&romconf->chksum) -#endif - ) { - /* Modified by Cesanta */ - ets_printf("Writing default boot config @ 0x%x.\r\n", BOOT_CONFIG_SECTOR * SECTOR_SIZE); - ets_memset(romconf, 0x00, sizeof(rboot_config)); - romconf->magic = BOOT_CONFIG_MAGIC; - romconf->version = BOOT_CONFIG_VERSION; - romconf->count = 2; - romconf->mode = MODE_STANDARD; - /* FWx_ADDR, FWx_FS_ADDR and FS_SIZE, FW_SIZE must be defined by -D */ - romconf->roms[0] = FW1_ADDR; - romconf->roms[1] = FW2_ADDR; - romconf->fs_addresses[0] = FW1_FS_ADDR; - romconf->fs_addresses[1] = FW2_FS_ADDR; - romconf->fs_sizes[0] = romconf->fs_sizes[1] = FS_SIZE; - romconf->roms_sizes[0] = romconf->roms_sizes[1] = FW_SIZE; -#ifdef BOOT_CONFIG_CHKSUM - romconf->chksum = calc_chksum((uint8*)romconf, (uint8*)&romconf->chksum); -#endif - // write new config sector - SPIEraseSector(BOOT_CONFIG_SECTOR); - SPIWrite(BOOT_CONFIG_SECTOR * SECTOR_SIZE, buffer, SECTOR_SIZE); - } - - // if gpio mode enabled check status of the gpio - if ((romconf->mode & MODE_GPIO_ROM) && (get_gpio16() == 0)) { - ets_printf("Booting GPIO-selected.\r\n"); - romToBoot = romconf->previous_rom; - /* - * Modified by Cesanta - * Make FD current - */ - updateConfig = TRUE; - romconf->fw_updated = 0; - romconf->is_first_boot = 0; - gpio_boot = TRUE; - } else if (romconf->current_rom >= romconf->count) { - // if invalid rom selected try rom 0 - ets_printf("Invalid rom selected, defaulting.\r\n"); - romToBoot = 0; - romconf->current_rom = 0; - romconf->fw_updated = 0; - romconf->is_first_boot = 0; - updateConfig = TRUE; - } else { - /* Modified by Cesanta */ - if (romconf->is_first_boot != 0) { - ets_printf("First boot, attempt %d\n", romconf->boot_attempts); - /* boot is unconfirmed */ - if (romconf->boot_attempts == 0) { - /* haven't try to load yes */ - ets_printf("Boot is unconfirmed\r\n"); - romconf->boot_attempts++; - } else { - ets_printf("Boot failed, fallback to fw #%d\r\n", - romconf->previous_rom); - romconf->current_rom = romconf->previous_rom; - /* clear fw update flag, to avoid post-update acttions */ - romconf->fw_updated = 0; - romconf->boot_attempts = 0; - } - - updateConfig = TRUE; - } - /* End of Cesanta modifications */ - // try rom selected in the config - romToBoot = romconf->current_rom; - } - - // try to find a good rom - do { - runAddr = check_image(romconf->roms[romToBoot]); - if (runAddr == 0) { - ets_printf("Rom %d is bad.\r\n", romToBoot); - if (gpio_boot) { - // don't switch to backup for gpio-selected rom - ets_printf("GPIO boot failed.\r\n"); - return 0; - } else { - // for normal mode try each previous rom - // until we find a good one or run out - updateConfig = TRUE; - romToBoot--; - if (romToBoot < 0) romToBoot = romconf->count - 1; - if (romToBoot == romconf->current_rom) { - // tried them all and all are bad! - ets_printf("No good rom available.\r\n"); - return 0; - } - } - } - } while (runAddr == 0); - - // re-write config, if required - if (updateConfig) { - romconf->current_rom = romToBoot; -#ifdef BOOT_CONFIG_CHKSUM - romconf->chksum = calc_chksum((uint8*)romconf, (uint8*)&romconf->chksum); -#endif - SPIEraseSector(BOOT_CONFIG_SECTOR); - SPIWrite(BOOT_CONFIG_SECTOR * SECTOR_SIZE, buffer, SECTOR_SIZE); - } - - ets_printf("Booting rom %d (0x%x).\r\n", romToBoot, romconf->roms[romToBoot]); - // copy the loader to top of iram - ets_memcpy((void*)_text_addr, _text_data, _text_len); - // return address to load from - return runAddr; - -} - -#ifdef BOOT_NO_ASM - -// small stub method to ensure minimum stack space used -void call_user_start(void) { - uint32 addr; - stage2a *loader; - - addr = find_image(); - if (addr != 0) { - loader = (stage2a*)entry_addr; - loader(addr); - } -} - -#else - -// assembler stub uses no stack space -// works with gcc -void call_user_start(void) { - __asm volatile ( - "mov a15, a0\n" // store return addr, hope nobody wanted a15! - "call0 find_image\n" // find a good rom to boot - "mov a0, a15\n" // restore return addr - "bnez a2, 1f\n" // ?success - "ret\n" // no, return - "1:\n" // yes... - "movi a3, entry_addr\n" // actually gives us a pointer to entry_addr - "l32i a3, a3, 0\n" // now really load entry_addr - "jx a3\n" // now jump to it - ); -} - -#endif diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.h b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.h deleted file mode 100644 index 3949d74..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_RBOOT_H_ -#define CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_RBOOT_H_ - -////////////////////////////////////////////////// -// rBoot open source boot loader for ESP8266. -// Copyright 2015 Richard A Burton -// richardaburton@gmail.com -// See license.txt for license terms. -////////////////////////////////////////////////// - -#ifdef __cplusplus -extern "C" { -#endif - -// uncomment to use only c code -// if you aren't using gcc you may need to do this -//#define BOOT_NO_ASM - -// uncomment to have a checksum on the boot config -//#define BOOT_CONFIG_CHKSUM - -// uncomment to enable big flash support (>1MB) -//#define BOOT_BIG_FLASH - -// uncomment to include .irom0.text section in the checksum -// roms must be built with esptool2 using -iromchksum option -//#define BOOT_IROM_CHKSUM - -// increase if required -#define MAX_ROMS 4 - -#define CHKSUM_INIT 0xef - -#define SECTOR_SIZE 0x1000 - -#ifndef BOOT_CONFIG_ADDR -#define BOOT_CONFIG_ADDR 0x1000 -#endif - -#define BOOT_CONFIG_SECTOR (BOOT_CONFIG_ADDR / SECTOR_SIZE) - -#define BOOT_CONFIG_MAGIC 0xe1 -#define BOOT_CONFIG_VERSION 0x01 - -#define MODE_STANDARD 0x00 -#define MODE_GPIO_ROM 0x01 - -// boot config structure -// rom addresses must be multiples of 0x1000 (flash sector aligned) -// without BOOT_BIG_FLASH only the first 8Mbit of the chip will be memory mapped -// so rom slots containing .irom0.text sections must remain below 0x100000 -// slots beyond this will only be accessible via spi read calls, so -// use these for stored resources, not code -// with BOOT_BIG_FLASH the flash will be mapped in chunks of 8MBit, so roms can -// be anywhere, but must not straddle two 8MBit blocks -typedef struct { - uint8 magic; // our magic - uint8 version; // config struct version - uint8 mode; // boot loader mode - uint8 current_rom; // currently selected rom - uint8 gpio_rom; // rom to use for gpio boot - uint8 count; // number of roms in use - uint8 previous_rom; // previously selected rom - uint8 is_first_boot; - uint8 boot_attempts; - uint8 fw_updated; - uint8 padding[2]; - uint32 roms[MAX_ROMS]; // flash addresses of the roms - uint32 roms_sizes[MAX_ROMS]; // sizes of the roms - uint32 fs_addresses[MAX_ROMS]; // file system addresses - uint32 fs_sizes[MAX_ROMS]; // file system sizes - uint32 user_flags; -#ifdef BOOT_CONFIG_CHKSUM - uint8 chksum; // config chksum -#endif -} rboot_config; - -#ifdef __cplusplus -} -#endif - -#endif /* CS_COMMON_PLATFORMS_ESP8266_RBOOT_RBOOT_RBOOT_H_ */ diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.ld b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.ld deleted file mode 100644 index 5e25740..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.ld +++ /dev/null @@ -1,2 +0,0 @@ -INCLUDE "eagle.app.v6.ld" -INCLUDE "rboot_rom.ld" diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.mk b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.mk deleted file mode 100644 index f7f3987..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot.mk +++ /dev/null @@ -1,89 +0,0 @@ -# -# Makefile for rBoot -# https://github.com/raburton/esp8266 -# - -ESPTOOL2 ?= ../../build/esptool2 -SDK_BASE ?= /opt/Espressif/ESP8266_SDK -SPI_SIZE ?= -RBOOT_INTEGRATION ?= -RBOOT_EXTRA_INCDIR ?= - -# RBOOT_BUILD_BASE and RBOOT_GEN_BASE should be provided via makefile parameters -RBOOT_BUILD_BASE ?= -# RBOOT_GEN_BASE is the directory for generated inputs -RBOOT_GEN_BASE ?= - -ifndef XTENSA_BINDIR -CC := xtensa-lx106-elf-gcc -LD := xtensa-lx106-elf-gcc -else -CC := $(addprefix $(XTENSA_BINDIR)/,xtensa-lx106-elf-gcc) -LD := $(addprefix $(XTENSA_BINDIR)/,xtensa-lx106-elf-gcc) -endif -CC_WRAPPER ?= - -CFLAGS = -Os -O3 -Wpointer-arith -Wundef -Werror -Wl,-EL \ - -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals \ - -D__ets__ -DIRAM='__attribute__((section(".fast.text")))' \ - -DNOINSTR='__attribute__((no_instrument_function))' \ - -DICACHE_FLASH $(CFLAGS_EXTRA) - -LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static -L $(SDK_BASE)/ld/ - -LD_SCRIPT = rboot.ld - -E2_OPTS = -quiet -bin -boot0 - -ifeq ($(RBOOT_BIG_FLASH),1) - CFLAGS += -DBOOT_BIG_FLASH -endif -ifeq ($(RBOOT_INTEGRATION),1) - CFLAGS += -DRBOOT_INTEGRATION -endif -ifeq ($(SPI_SIZE), 256K) - E2_OPTS += -256 -else ifeq ($(SPI_SIZE), 512K) - E2_OPTS += -512 -else ifeq ($(SPI_SIZE), 1M) - E2_OPTS += -1024 -else ifeq ($(SPI_SIZE), 2M) - E2_OPTS += -2048 -else ifeq ($(SPI_SIZE), 4M) - E2_OPTS += -4096 -endif - -RBOOT_EXTRA_INCDIR := $(addprefix -I,$(RBOOT_EXTRA_INCDIR)) - -.SECONDARY: - -all: $(RBOOT_BUILD_BASE)/rboot.bin - -$(RBOOT_BUILD_BASE)/rboot-stage2a.o: rboot-stage2a.c rboot-private.h rboot.h - @echo "CC $<" - @$(CC_WRAPPER) $(CC) $(CFLAGS) $(RBOOT_EXTRA_INCDIR) -c $< -o $@ - -$(RBOOT_BUILD_BASE)/rboot-stage2a.elf: $(RBOOT_BUILD_BASE)/rboot-stage2a.o - @echo "LD $@" - @$(CC_WRAPPER) $(LD) -Trboot-stage2a.ld $(LDFLAGS) -Wl,--start-group $^ -Wl,--end-group -o $@ - -$(RBOOT_GEN_BASE)/rboot-hex2a.h: $(RBOOT_BUILD_BASE)/rboot-stage2a.elf - @echo "E2 $@" - @$(ESPTOOL2) -quiet -header $< $@ .text - -$(RBOOT_BUILD_BASE)/rboot.o: rboot.c rboot-private.h rboot.h $(RBOOT_GEN_BASE)/rboot-hex2a.h - @echo "CC $<" - @$(CC) $(CFLAGS) -I$(RBOOT_GEN_BASE) $(RBOOT_EXTRA_INCDIR) -c $< -o $@ - -$(RBOOT_BUILD_BASE)/%.o: %.c %.h - @echo "CC $<" - @$(CC) $(CFLAGS) $(RBOOT_EXTRA_INCDIR) -c $< -o $@ - -$(RBOOT_BUILD_BASE)/%.elf: $(RBOOT_BUILD_BASE)/%.o - @echo "LD $@" - @$(LD) -T$(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group $^ -Wl,--end-group -o $@ - -$(RBOOT_BUILD_BASE)/%.bin: $(RBOOT_BUILD_BASE)/%.elf - @echo "E2 $@" - @$(ESPTOOL2) $(E2_OPTS) $< $@ .text .rodata - diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot_rom.ld b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot_rom.ld deleted file mode 100644 index 69e862c..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/rboot_rom.ld +++ /dev/null @@ -1,8 +0,0 @@ -PROVIDE ( ets_delay_us = 0x40002ecc ); -PROVIDE ( ets_memcpy = 0x400018b4 ); -PROVIDE ( ets_memset = 0x400018a4 ); -PROVIDE ( ets_printf = 0x400024cc ); -PROVIDE ( SPIEraseSector = 0x40004a00 ); -PROVIDE ( SPIRead = 0x40004b1c ); -PROVIDE ( SPIWrite = 0x40004a4c ); -PROVIDE ( uart_div_modify = 0x400039d8 ); diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/readme-api.txt b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/readme-api.txt deleted file mode 100644 index 2304748..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/readme-api.txt +++ /dev/null @@ -1,40 +0,0 @@ -rBoot - User API for rBoot on the ESP8266 ------------------------------------------- -by Richard A Burton, richardaburton@gmail.com -http://richard.burtons.org/ - -This provides a few simple APIs for getting & setting rBoot config and for -writing data from OTA updates. API source files are in the appcode directory. - -Actual OTA network code is implementation specific and no longer included in -rBoot itself, see the rBoot sample projects for this code (which you can then -use in your own projects). - - rboot_config rboot_get_config(); - Returns rboot_config (defined in rboot.h) allowing you to modify any values - in it, including the rom layout. - - bool rboot_set_config(rboot_config *conf); - Saves the rboot_config structure back to sector 2 of the flash, while - maintaining the contents of the rest of the sector. You can use the rest of - this sector for your app settings, as long as you protect this structure - when you do so. - - uint8 rboot_get_current_rom(); - Get the currently selected boot rom (the currently running rom, as long as - you haven't changed it since boot). - - bool rboot_set_current_rom(uint8 rom); - Set the current boot rom, which will be used when next restarted. - - rboot_write_status rboot_write_init(uint32 start_addr); - Call once before starting to pass data to write to the flash. start_addr is - the address on the SPI flash to write from. Returns a status structure which - must be passed back on each write. The contents of the structure should not - be modified by the calling code. - - bool rboot_write_flash(rboot_write_status *status, uint8 *data, uint16 len); - Call repeatedly to write data to the flash, starting at the address - specified on the prior call to rboot_write_init. Current write position is - tracked automatically. This method is likely to be called each time a packet - of OTA data is received over the network. diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/readme.txt b/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/readme.txt deleted file mode 100644 index 944e1f2..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rboot/rboot/readme.txt +++ /dev/null @@ -1,218 +0,0 @@ -rBoot - An open source boot loader for the ESP8266 --------------------------------------------------- -by Richard A Burton, richardaburton@gmail.com -http://richard.burtons.org/ - - -rBoot is designed to be a flexible open source boot loader, a replacement for -the binary blob supplied with the SDK. It has the following advantages over the -Espressif loader: - - - Open source (written in C). - - Supports up to 256 roms. - - Roms can be variable size. - - Able to test multiple roms to find a valid backup (without resetting). - - Flash layout can be changed on the fly (with care and appropriately linked - rom images). - - GPIO support for rom selection. - - Wastes no stack space (SDK boot loader uses 144 bytes). - - Documented config structure to allow easy editing from user code. - - Can validate .irom0.text section with checksum. - -Limitations ------------ -The ESP8266 can only map 8Mbits (1MB) of flash to memory, but which 8Mbits to -map is selectable. This allows individual roms to be up to 1MB in size, so long -as they do not straddle an 8Mbit boundary on the flash. This means you could -have four 1MB roms or 8 512KB roms on a 32Mbit flash (such as on the ESP-12), or -a combination. Note, however, that you could not have, for example, a 512KB rom -followed immediately by a 1MB rom because the 2nd rom would then straddle an -8MBit boundary. By default support for using more than the first 8Mbit of the -flash is disabled, because it requires several steps to get it working. See -below for instructions. - -Building --------- -A Makefile is included, which should work with the gcc xtensa cross compiler. -There are two source files, the first is compiled and included as data in the -second. When run this code is copied to memory and executed (there is a good -reason for this, see my blog for an explanation). The make file will handle this -for you, but you'll need my esptool2 (see github). - -To use the Makefile set SDK_BASE to point to the root of the Espressif SDK and -either set XTENSA_BINDIR to the gcc xtensa bin directory or include it in your -PATH. These can be set as environment variables or by editing the Makefile. - -Two small assembler stub functions allow the bootloader to launch the user code -without reserving any space on the stack (while the SDK boot loader uses 144 -bytes). This compiles fine with GCC, but if you use another compiler and it -will not compile/work for you then uncomment the #define BOOT_NO_ASM in rboot.h -to use a C version of these functions (this uses 32 bytes). - -Tested with SDK v1.3 and GCC v4.8.2. - -Installation ------------- -Simply write rboot.bin to the first sector of the flash. Remember to set your -flash size correctly with your chosen flash tool (e.g. for esptool.py use the --fs option). When run rBoot will create it's own config at the start of sector -two for a simple two rom system. You can can then write your two roms to flash -addresses 0x2000 and (half chip size + 0x2000). E.g. for 8Mbit flash: - esptool.py -fs 8m 0x0000 rboot.bin 0x2000 user1.bin 0x82000 user2.bin - -Note: your device may need other options specified. E.g. The nodemcu devkit v1.0 -(commonly, but incorrectly, sold as v2) also needs the "-fm dio" option. - -For more interesting rom layouts you'll need to write an rBoot config sector -manually, see next step. - -The two testload bin files can be flashed in place of normal user roms for -testing rBoot. You do not need these for normal use. - -rBoot Config ------------- -typedef struct { - uint8 magic; // our magic - uint8 version; // config struct version - uint8 mode; // boot loader mode - uint8 current_rom; // currently selected rom - uint8 gpio_rom; // rom to use for gpio boot - uint8 count; // number of roms in use - uint8 unused[2]; // padding - uint32 roms[MAX_ROMS]; // flash addresses of the roms -#ifdef BOOT_CONFIG_CHKSUM - uint8 chksum; // boot config chksum -#endif -} rboot_config; - -Write a config structure as above to address 0x1000 on the flash. If you want -more than 4 roms (default) just increase MAX_ROMS when you compile rBoot. -Think about how you intend to layout your flash before you start! -Rom addresses must be sector aligned i.e start on a multiple of 4096. - - - magic should have value 0xe1 (defined as BOOT_CONFIG_MAGIC). - - version is used in case the config structure changes after deployment. It is - defined as 0x01 (BOOT_CONFIG_VERSION). I don't intend to increase this, but - you should if you choose to reflash the bootloader after deployment and - the config structure has changed. - - mode can be 0x00 (MODE_STANDARD) or 0x01 (MODE_GPIO_ROM). If you set GPIO - you will need to set gpio_rom as well. The sample GPIO code uses GPIO 16 on - a nodemcu dev board, if you want to use a different GPIO you'll need to - adapt the code in rBoot slightly. - - current_rom is the rom to boot, numbered 0 to count-1. - - gpio_rom is the rom to boot when the GPIO is triggered at boot. - - count is the number of roms available (may be less than MAX_ROMS, but not - more). - - unused[2] is padding so the uint32 rom addresses are 4 bytes aligned. - - roms is the array of flash address for the roms. The default generated - config will contain two entries: 0x00002000 and 0x00082000. - - chksum (if enabled, not by deafult) should be the xor of 0xef followed by - each of the bytes of the config structure up to (but obviously not - including) the chksum byte itself. - -Linking user code ------------------ -Each rom will need to be linked with an appropriate linker file, specifying -where it will reside on the flash. If you are only flashing one rom to multiple -places on the flash it must be linked multiple times to produce the set of rom -images. This is the same as with the SDK loader. - -Because there are endless possibilities for layout with this loader I don't -supply sample linker files. Instead I'll tell you how to make them. - -For each rom slot on the flash take a copy of the eagle.app.v6.ld linker script -from the sdk. You then need to modify just one line in it for each rom: - irom0_0_seg : org = 0x40240000, len = 0x3C000 - -Change the org address to be 0x40200000 (base memory mapped location of the -flash) + flash address + 0x10 (offset of data after the header). -The logical place for your first rom is the third sector, address 0x2000. - 0x40200000 + 0x2000 + 0x10 = 0x40202010 -If you use the default generated config the loader will expect to find the -second rom at flash address half-chip-size + 0x2000 (e.g. 0x82000 on an 8MBit -flash) so the irom0_0_seg should be: - 0x40200000 + 0x82000 + 0x10 = 0x40282010 -Due to the limitation of mapped flash (max 8MBit) if you use a larger chip and -do not have big flash support enabled the second rom in the default config will -still be placed at 0x082000, not truly half-chip-size + 0x2000. -Ideally you should also adjust the len to help detect over sized sections at -link time, but more important is the overall size of the rom which you need to -ensure fits in the space you have allocated for it in your flash layout plan. - -Then simply compile and link as you would normally for OTA updates with the SDK -boot loader, except using the linker scripts you've just prepared rather than -the ones supplied with the SDK. Remember when building roms to create them as -'new' type roms (for use with SDK boot loader v1.2+). Or if using my esptool2 -use the -boot2 option. Note: the test loads included with rBoot are built with --boot0 because they do not contain a .irom0.text section (and so the value of -irom0_0_seg in the linker file is irrelevant to them) but 'normal' user apps -always do. - -irom checksum -------------- -The SDK boot loader checksum only covers sections loaded into ram (data and some -code). Most of the SDK and user code remains on the flash and that is not -included in the checksum. This means you could attempt to boot a corrupt rom -and, because it looks ok to the boot loader, there will be no attempt to switch -to a backup rom. rBoot improves on this by allowing the .irom0.text section to -be included in the checksum. To enable this uncomment #define BOOT_IROM_CHKSUM -in rboot.h and build your roms with esptool2 using the -iromchksum option. - -Big flash support ------------------ -This only needs to be enabled if you wish to be able to memory map more than the -first 8MBit of the flash. Note you can still only map 8Mbit at a time. Use this -if you want to have multiple 1MB roms, or more smaller roms than will fit in -8Mbits. If you have a large flash but only need, for example, two 512KB roms you -do not need to enable this mode. - -Support in rBoot is enabled by uncommenting the #define BOOT_BIG_FLASH in -rboot.h. - -Thinking about your linker files is either simpler or more complicated, -depending on your usage of the flash. If you intend to use multiple 1MB roms you -will only need one linker file and you only need to link once for OTA updates. -Although when you perform an OTA update the rom will be written to a different -position on the flash, each 8Mbit of flash is mapped (separately) to 0x40200000. -So when any given rom is run the code will appear at the same place in memory -regardless of where it is on the flash. Your base address for the linker would -be 0x40202010. (Actually all but the first rom could base at 0x40200010 (because -they don't need to leave space for rBoot and config) but then you're just making -it more complicated again!) -If you wanted eight 512KB roms you would need two linker files - one for the -first half of any given 8Mbits of flash and another for the second half. Just -remember you are really laying out within a single 8MBit area, which can then be -replicated multiple times on the flash. - -Now the clever bit - rBoot needs to hijack the memory mapping code to select -which 8Mbits gets mapped. There is no API for this, but we can override the SDK -function. First we need to slightly modify the SDK library libmain.a, like so: - - xtensa-lx106-elf-objcopy -W Cache_Read_Enable_New libmain.a libmain2.a - -This produces a version of libmain with a 'weakened' Cache_Read_Enable_New -function, which we can then override with our own. Modify your Makefile to link -against the library main2 instead of main. -Next add rboot-bigflash.c (from the appcode directory) & rboot.h to your project -- this adds the replacement Cache_Read_Enable_New to your code. - -Getting gcc to apply the override correctly can be slightly tricky (I'm not sure -why, it shouldn't be). One option is to add "-u Cache_Read_Enable_New" to your -LD_FLAGS and change the order of objects on the LD command so your objects/.a -file is before the libraries. Another way that seems easier was to #include -rboot-bigflash.c into the main .c file, rather than compiling it to a separate -object file. I can't make any sense of that, but I suggest you uncomment the -message in the Cache_Read_Enable_New function when you first build with it, to -make sure you are getting your version into the rom. - -Now when rBoot starts your rom, the SDK code linked in it that normally performs -the memory mapping will delegate part of that task to rBoot code (linked in your -rom, not in rBoot itself) to choose which part of the flash to map. - -Integration into other frameworks ---------------------------------- -If you wish to integrate rBoot into a development framework (e.g. Sming) you -can set the define RBOOT_INTEGRATION and at compile time the file -rboot-integration.h will be included into the source. This should allow you to -set some platform specific options without having to modify the source of rBoot -which makes it easier to integrate and maintain. diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rom/.gitattributes b/src/mongoose-6.11/src/common/platforms/esp8266/rom/.gitattributes deleted file mode 100644 index b2dd225..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rom/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -rom.bin -nodiff -rom.elf -nodiff diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rom/disasm.sh b/src/mongoose-6.11/src/common/platforms/esp8266/rom/disasm.sh deleted file mode 100755 index 50c06f2..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rom/disasm.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -xtensa-lx106-elf-gcc -Wl,-N,-Ttext,0x40000000 -nostdlib rom.S -o rom.elf && \ - xtensa-lx106-elf-objdump -d rom.elf > ESP8266_ROM.txt diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.S b/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.S deleted file mode 100644 index ac27596..0000000 --- a/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.S +++ /dev/null @@ -1,101 +0,0 @@ -.text -.org 0 -.globl _start - -// xtensa-esp108-elf-gcc -Wl,-N,-Ttext,0x40000000 -nostdlib rom.S -o rom.elf - -here = . -#define PROVIDE(name, addr) name = here + addr - 0x40000000 - -#include "rom_functions.S" - -PROVIDE(SPI_chip_erase, 0x40004080) -PROVIDE(SPIFlashModeConfig, 0x40004568) - -PROVIDE(_c_0x80000000, 0x400003a4) -PROVIDE(_c_0x00400000, 0x40000414) -PROVIDE(_c_0x00ffffff, 0x40000418) -PROVIDE(_p_user_start, 0x40000fb8) // 0x3fffdcd0 -PROVIDE(_c_0x60000200, 0x40000fc4) -PROVIDE(_s_ets_build, 0x40000fc8) // Jan 8 2013 -PROVIDE(_s_ets_banner, 0x40000fcc) // ets %s,rst cause:%d, boot mode:(%d,%d) -PROVIDE(_s_format_s_s, 0x40000fd0) // %s %s -PROVIDE(_s_ets_main_c, 0x40000fd4) // ets_main.c -PROVIDE(_s_user_code_done, 0x40000fe0) // user code done -PROVIDE(_s_wdt_reset, 0x40000fe4) // wdt reset -PROVIDE(_s_unk_reset, 0x40000fe8) // unknown reset -PROVIDE(_l_boot_mode_6, 0x40001044) -PROVIDE(_l_boot_mode_0_1_2_3, 0x4000104a) -PROVIDE(_l_rst_cause_ge_3, 0x40001075) -PROVIDE(_l_rst_cause_ge_3_lt_7, 0x40001084) -PROVIDE(_l_rst_cause_3_soft_wdt, 0x4000108a) -PROVIDE(_l_load_from_flash, 0x400010a8) -PROVIDE(_l_run_user_code, 0x400010be) -PROVIDE(_l_wdt_reset, 0x40001118) -PROVIDE(_l_rst_cause_1_2, 0x40001121) -PROVIDE(_l_rst_cause_unknown, 0x40001130) -PROVIDE(_l_boot_mode_7, 0x40001148) -PROVIDE(_l_boot_mode_4, 0x40001150) -PROVIDE(_c_0x60000600, 0x4000115c) -PROVIDE(_s_waiting_for_host, 0x4000119c) // waiting for host -PROVIDE(_x_unk1160, 0x40001160) -PROVIDE(_c_0x3fffa000_uart_buf, 0x400011a0) -PROVIDE(_c_0x2000, 0x400011a4) -PROVIDE(_c_0x40100000, 0x400011a8) // default user_start -PROVIDE(_x_boot2, 0x400011ac) // arg: 0x3fffdcd0 (&user_start) -PROVIDE(_l_strapping_2_eq_0, 0x400011d2) -PROVIDE(_l_124d, 0x4000124d) -PROVIDE(_l_boot_mode2_eq_2, 0x40001265) -PROVIDE(_l_boot_mode_eq_2, 0x40001268) -PROVIDE(_l_boot_mode_eq_1, 0x4000127a) -PROVIDE(_l_setup_uart, 0x40001291) -PROVIDE(_l_boot_mode_eq_3, 0x400012b0) -PROVIDE(_x_load_from_flash, 0x40001308) -PROVIDE(_c_0x00001000, 0x40001994) -PROVIDE(_c_0xffdfffff, 0x400025dc) -PROVIDE(_l_rr_not_dsleep, 0x40002624) -PROVIDE(_c_100000, 0x40002664) -PROVIDE(_c_0x3feffe00, 0x40002e5c) -PROVIDE(_p_cpu_freq, 0x40002ec8) -PROVIDE(_x_wdt_interval, 0x40002f14) // arg: 3 -> 11, 6 -> 12, 12 -> 13 -PROVIDE(_p_wdt_cfg, 0x40002f30) -PROVIDE(_wdt_soft_timer_fn2, 0x40002f3c) -PROVIDE(_l_wdt_soft_timer_fn2_exit, 0x40002f59) -PROVIDE(_l_wdt_soft_timer_fn2_feed, 0x40002f60) -PROVIDE(_l_wdt_soft_timer_fn2_mode2_stage1_feed, 0x40002f74) -PROVIDE(_wdt_soft_timer_fn, 0x40002f88) -PROVIDE(_p_wdt_soft_timer_fn, 0x40002f98) -PROVIDE(_p_wdt_soft_timer, 0x40002f9c) // 0x3fffdde0 -PROVIDE(_l_skip_wdt_imask, 0x40002fc4) -PROVIDE(_l_wdt_enable_and_exit, 0x40002fea) -PROVIDE(_l_wdt_mode_1, 0x4000309c) -PROVIDE(_l_wdt_mode_2_4, 0x4000300a) -PROVIDE(_l_wdt_mode_3, 0x40003060) -PROVIDE(_l_recv_req, 0x400033a1) -PROVIDE(_l_send_response, 0x400033be) -PROVIDE(_l_send_resp_pkt, 0x400033da) -PROVIDE(_l_process_req, 0x40003424) -PROVIDE(_l_34a3, 0x400034a3) -PROVIDE(_l_34b9, 0x400034b9) -PROVIDE(_c_0x00001800, 0x40003534) -PROVIDE(_c_0x60000a00, 0x40003f54) -PROVIDE(_c_0x01000000, 0x400040bc) -PROVIDE(_x_SPI_erase_sector, 0x400040c0) -PROVIDE(_l_SPI_erase_sector_align_ok, 0x400040dc) -PROVIDE(_c_0x00800000, 0x4000411c) -PROVIDE(_x_SPI_erase_block, 0x40004120) -PROVIDE(_c_0x20000000, 0x4000416c) -PROVIDE(_c_0x08000000, 0x400043c4) -PROVIDE(_c_0x04000000, 0x400043fc) -PROVIDE(_c_0x40000000, 0x40004438) -PROVIDE(_p_flashchip, 0x40004874) -PROVIDE(_s_bootup, 0x400054cc) // bootup , addr 0x%08x -PROVIDE(_p_sip_ctx, 0x40005130) -PROVIDE(_l_sip_cmd_out, 0x4000550a) -PROVIDE(_c_0x00010000, 0x40005de0) - -.text - -_start: -.incbin "rom.bin" -_end: diff --git a/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.bin b/src/mongoose-6.11/src/common/platforms/esp8266/rom/rom.bin deleted file mode 100644 index fe4d1956655081254d74cee85fc9341e84765e60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65536 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