|
@ -1,15 +1,17 @@ |
|
|
--- a/main.c
|
|
|
|
|
|
+++ b/main.c
|
|
|
|
|
|
@@ -78,6 +78,7 @@ static void usage(const char *argv0) {
|
|
|
|
|
|
|
|
|
Index: squeezelite-1.9.1-1130/main.c
|
|
|
|
|
|
===================================================================
|
|
|
|
|
|
--- squeezelite-1.9.1-1130.orig/main.c
|
|
|
|
|
|
+++ squeezelite-1.9.1-1130/main.c
|
|
|
|
|
|
@@ -84,6 +84,7 @@ static void usage(const char *argv0) {
|
|
|
#if IR |
|
|
#if IR |
|
|
" -i [<filename>]\tEnable lirc remote control support (lirc config file ~/.lircrc used if filename not specified)\n" |
|
|
" -i [<filename>]\tEnable lirc remote control support (lirc config file ~/.lircrc used if filename not specified)\n" |
|
|
#endif |
|
|
#endif |
|
|
+ " -I <interface>\t\tNetwork interface used to send discovery\n"
|
|
|
|
|
|
|
|
|
+ " -I <interface>\tNetwork interface used to send discovery\n"
|
|
|
" -m <mac addr>\t\tSet mac address, format: ab:cd:ef:12:34:56\n" |
|
|
" -m <mac addr>\t\tSet mac address, format: ab:cd:ef:12:34:56\n" |
|
|
" -M <modelname>\tSet the squeezelite player model name sent to the server (default: " MODEL_NAME_STRING ")\n" |
|
|
" -M <modelname>\tSet the squeezelite player model name sent to the server (default: " MODEL_NAME_STRING ")\n" |
|
|
" -n <name>\t\tSet the player name\n" |
|
|
" -n <name>\t\tSet the player name\n" |
|
|
@@ -233,6 +234,8 @@ int main(int argc, char **argv) {
|
|
|
|
|
|
extern bool pcm_check_header; |
|
|
|
|
|
|
|
|
@@ -267,6 +268,8 @@ int main(int argc, char **argv) {
|
|
|
|
|
|
extern bool user_rates; |
|
|
char *logfile = NULL; |
|
|
char *logfile = NULL; |
|
|
u8_t mac[6]; |
|
|
u8_t mac[6]; |
|
|
+ char *iface = NULL;
|
|
|
+ char *iface = NULL;
|
|
@ -17,7 +19,7 @@ |
|
|
unsigned stream_buf_size = STREAMBUF_SIZE; |
|
|
unsigned stream_buf_size = STREAMBUF_SIZE; |
|
|
unsigned output_buf_size = 0; // set later |
|
|
unsigned output_buf_size = 0; // set later |
|
|
unsigned rates[MAX_SUPPORTED_SAMPLERATES] = { 0 }; |
|
|
unsigned rates[MAX_SUPPORTED_SAMPLERATES] = { 0 }; |
|
|
@@ -271,6 +274,7 @@ int main(int argc, char **argv) {
|
|
|
|
|
|
|
|
|
@@ -307,6 +310,7 @@ int main(int argc, char **argv) {
|
|
|
|
|
|
|
|
|
int maxSampleRate = 0; |
|
|
int maxSampleRate = 0; |
|
|
|
|
|
|
|
@ -25,7 +27,7 @@ |
|
|
char *optarg = NULL; |
|
|
char *optarg = NULL; |
|
|
int optind = 1; |
|
|
int optind = 1; |
|
|
int i; |
|
|
int i; |
|
|
@@ -278,8 +282,6 @@ int main(int argc, char **argv) {
|
|
|
|
|
|
|
|
|
@@ -314,8 +318,6 @@ int main(int argc, char **argv) {
|
|
|
#define MAXCMDLINE 512 |
|
|
#define MAXCMDLINE 512 |
|
|
char cmdline[MAXCMDLINE] = ""; |
|
|
char cmdline[MAXCMDLINE] = ""; |
|
|
|
|
|
|
|
@ -34,16 +36,16 @@ |
|
|
for (i = 0; i < argc && (strlen(argv[i]) + strlen(cmdline) + 2 < MAXCMDLINE); i++) { |
|
|
for (i = 0; i < argc && (strlen(argv[i]) + strlen(cmdline) + 2 < MAXCMDLINE); i++) { |
|
|
strcat(cmdline, argv[i]); |
|
|
strcat(cmdline, argv[i]); |
|
|
strcat(cmdline, " "); |
|
|
strcat(cmdline, " "); |
|
|
@@ -287,7 +289,7 @@ int main(int argc, char **argv) {
|
|
|
|
|
|
|
|
|
@@ -323,7 +325,7 @@ int main(int argc, char **argv) {
|
|
|
|
|
|
|
|
|
while (optind < argc && strlen(argv[optind]) >= 2 && argv[optind][0] == '-') { |
|
|
while (optind < argc && strlen(argv[optind]) >= 2 && argv[optind][0] == '-') { |
|
|
char *opt = argv[optind] + 1; |
|
|
char *opt = argv[optind] + 1; |
|
|
- if (strstr("oabcCdefmMnNpPrs"
|
|
|
- if (strstr("oabcCdefmMnNpPrs"
|
|
|
+ if (strstr("oabcCdefImMnNpPrs"
|
|
|
+ if (strstr("oabcCdefImMnNpPrs"
|
|
|
#if ALSA |
|
|
#if ALSA |
|
|
"UV" |
|
|
|
|
|
|
|
|
"UVO" |
|
|
#endif |
|
|
#endif |
|
|
@@ -382,6 +384,9 @@ int main(int argc, char **argv) {
|
|
|
|
|
|
|
|
|
@@ -424,6 +426,9 @@ int main(int argc, char **argv) {
|
|
|
case 'f': |
|
|
case 'f': |
|
|
logfile = optarg; |
|
|
logfile = optarg; |
|
|
break; |
|
|
break; |
|
@ -53,7 +55,7 @@ |
|
|
case 'm': |
|
|
case 'm': |
|
|
{ |
|
|
{ |
|
|
int byte = 0; |
|
|
int byte = 0; |
|
|
@@ -667,6 +672,11 @@ int main(int argc, char **argv) {
|
|
|
|
|
|
|
|
|
@@ -733,6 +738,11 @@ int main(int argc, char **argv) {
|
|
|
winsock_init(); |
|
|
winsock_init(); |
|
|
#endif |
|
|
#endif |
|
|
|
|
|
|
|
@ -65,7 +67,7 @@ |
|
|
stream_init(log_stream, stream_buf_size); |
|
|
stream_init(log_stream, stream_buf_size); |
|
|
|
|
|
|
|
|
if (!strcmp(output_device, "-")) { |
|
|
if (!strcmp(output_device, "-")) { |
|
|
@@ -710,7 +720,7 @@ int main(int argc, char **argv) {
|
|
|
|
|
|
|
|
|
@@ -776,7 +786,7 @@ int main(int argc, char **argv) {
|
|
|
exit(1); |
|
|
exit(1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -74,9 +76,11 @@ |
|
|
|
|
|
|
|
|
decode_close(); |
|
|
decode_close(); |
|
|
stream_close(); |
|
|
stream_close(); |
|
|
--- a/squeezelite.h
|
|
|
|
|
|
+++ b/squeezelite.h
|
|
|
|
|
|
@@ -403,7 +403,7 @@ typedef enum { EVENT_TIMEOUT = 0, EVENT_
|
|
|
|
|
|
|
|
|
Index: squeezelite-1.9.1-1130/squeezelite.h
|
|
|
|
|
|
===================================================================
|
|
|
|
|
|
--- squeezelite-1.9.1-1130.orig/squeezelite.h
|
|
|
|
|
|
+++ squeezelite-1.9.1-1130/squeezelite.h
|
|
|
|
|
|
@@ -414,7 +414,7 @@ typedef enum { EVENT_TIMEOUT = 0, EVENT_
|
|
|
|
|
|
|
|
|
char *next_param(char *src, char c); |
|
|
char *next_param(char *src, char c); |
|
|
u32_t gettime_ms(void); |
|
|
u32_t gettime_ms(void); |
|
@ -85,7 +89,7 @@ |
|
|
void set_nonblock(sockfd s); |
|
|
void set_nonblock(sockfd s); |
|
|
int connect_timeout(sockfd sock, const struct sockaddr *addr, socklen_t addrlen, int timeout); |
|
|
int connect_timeout(sockfd sock, const struct sockaddr *addr, socklen_t addrlen, int timeout); |
|
|
void server_addr(char *server, in_addr_t *ip_ptr, unsigned *port_ptr); |
|
|
void server_addr(char *server, in_addr_t *ip_ptr, unsigned *port_ptr); |
|
|
@@ -459,7 +459,7 @@ void buf_init(struct buffer *buf, size_t
|
|
|
|
|
|
|
|
|
@@ -470,7 +470,7 @@ void buf_init(struct buffer *buf, size_t
|
|
|
void buf_destroy(struct buffer *buf); |
|
|
void buf_destroy(struct buffer *buf); |
|
|
|
|
|
|
|
|
// slimproto.c |
|
|
// slimproto.c |
|
@ -94,9 +98,11 @@ |
|
|
void slimproto_stop(void); |
|
|
void slimproto_stop(void); |
|
|
void wake_controller(void); |
|
|
void wake_controller(void); |
|
|
|
|
|
|
|
|
--- a/slimproto.c
|
|
|
|
|
|
+++ b/slimproto.c
|
|
|
|
|
|
@@ -119,7 +119,7 @@ void send_packet(u8_t *packet, size_t le
|
|
|
|
|
|
|
|
|
Index: squeezelite-1.9.1-1130/slimproto.c
|
|
|
|
|
|
===================================================================
|
|
|
|
|
|
--- squeezelite-1.9.1-1130.orig/slimproto.c
|
|
|
|
|
|
+++ squeezelite-1.9.1-1130/slimproto.c
|
|
|
|
|
|
@@ -113,7 +113,7 @@ void send_packet(u8_t *packet, size_t le
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -105,16 +111,16 @@ |
|
|
const char *base_cap = "Model=squeezelite,AccuratePlayPoints=1,HasDigitalOut=1,HasPolarityInversion=1,Firmware=" VERSION; |
|
|
const char *base_cap = "Model=squeezelite,AccuratePlayPoints=1,HasDigitalOut=1,HasPolarityInversion=1,Firmware=" VERSION; |
|
|
struct HELO_packet pkt; |
|
|
struct HELO_packet pkt; |
|
|
|
|
|
|
|
|
@@ -782,7 +782,7 @@ void wake_controller(void) {
|
|
|
|
|
|
|
|
|
@@ -757,7 +757,7 @@ void wake_controller(void) {
|
|
|
wake_signal(wake_e); |
|
|
wake_signal(wake_e); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
-in_addr_t discover_server(void) {
|
|
|
|
|
|
+in_addr_t discover_server(in_addr_t bcast_addr) {
|
|
|
|
|
|
|
|
|
-in_addr_t discover_server(char *default_server) {
|
|
|
|
|
|
+in_addr_t discover_server(char *default_server, in_addr_t bcast_addr) {
|
|
|
struct sockaddr_in d; |
|
|
struct sockaddr_in d; |
|
|
struct sockaddr_in s; |
|
|
struct sockaddr_in s; |
|
|
char *buf; |
|
|
char *buf; |
|
|
@@ -798,7 +798,7 @@ in_addr_t discover_server(void) {
|
|
|
|
|
|
|
|
|
@@ -774,7 +774,7 @@ in_addr_t discover_server(char *default_
|
|
|
memset(&d, 0, sizeof(d)); |
|
|
memset(&d, 0, sizeof(d)); |
|
|
d.sin_family = AF_INET; |
|
|
d.sin_family = AF_INET; |
|
|
d.sin_port = htons(PORT); |
|
|
d.sin_port = htons(PORT); |
|
@ -123,7 +129,7 @@ |
|
|
|
|
|
|
|
|
pollinfo.fd = disc_sock; |
|
|
pollinfo.fd = disc_sock; |
|
|
pollinfo.events = POLLIN; |
|
|
pollinfo.events = POLLIN; |
|
|
@@ -829,7 +829,7 @@ in_addr_t discover_server(void) {
|
|
|
|
|
|
|
|
|
@@ -809,7 +809,7 @@ in_addr_t discover_server(char *default_
|
|
|
#define FIXED_CAP_LEN 256 |
|
|
#define FIXED_CAP_LEN 256 |
|
|
#define VAR_CAP_LEN 128 |
|
|
#define VAR_CAP_LEN 128 |
|
|
|
|
|
|
|
@ -132,26 +138,28 @@ |
|
|
struct sockaddr_in serv_addr; |
|
|
struct sockaddr_in serv_addr; |
|
|
static char fixed_cap[FIXED_CAP_LEN], var_cap[VAR_CAP_LEN] = ""; |
|
|
static char fixed_cap[FIXED_CAP_LEN], var_cap[VAR_CAP_LEN] = ""; |
|
|
bool reconnect = false; |
|
|
bool reconnect = false; |
|
|
@@ -849,7 +849,7 @@ void slimproto(log_level level, char *se
|
|
|
|
|
|
|
|
|
@@ -830,7 +830,7 @@ void slimproto(log_level level, char *se
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (!slimproto_ip) { |
|
|
if (!slimproto_ip) { |
|
|
- slimproto_ip = discover_server();
|
|
|
|
|
|
+ slimproto_ip = discover_server(bcast_addr);
|
|
|
|
|
|
|
|
|
- slimproto_ip = discover_server(server);
|
|
|
|
|
|
+ slimproto_ip = discover_server(server, bcast_addr);
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (!slimproto_port) { |
|
|
if (!slimproto_port) { |
|
|
@@ -924,7 +924,7 @@ void slimproto(log_level level, char *se
|
|
|
|
|
|
|
|
|
@@ -911,7 +911,7 @@ void slimproto(log_level level, char *se
|
|
|
|
|
|
|
|
|
// rediscover server if it was not set at startup |
|
|
// rediscover server if it was not set at startup |
|
|
if (!server && ++failed_connect > 5) { |
|
|
if (!server && ++failed_connect > 5) { |
|
|
- slimproto_ip = serv_addr.sin_addr.s_addr = discover_server();
|
|
|
|
|
|
+ slimproto_ip = serv_addr.sin_addr.s_addr = discover_server(bcast_addr);
|
|
|
|
|
|
|
|
|
- slimproto_ip = serv_addr.sin_addr.s_addr = discover_server(NULL);
|
|
|
|
|
|
+ slimproto_ip = serv_addr.sin_addr.s_addr = discover_server(NULL, bcast_addr);
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
} else { |
|
|
} else { |
|
|
--- a/utils.c
|
|
|
|
|
|
+++ b/utils.c
|
|
|
|
|
|
|
|
|
Index: squeezelite-1.9.1-1130/utils.c
|
|
|
|
|
|
===================================================================
|
|
|
|
|
|
--- squeezelite-1.9.1-1130.orig/utils.c
|
|
|
|
|
|
+++ squeezelite-1.9.1-1130/utils.c
|
|
|
@@ -22,11 +22,11 @@
|
|
|
@@ -22,11 +22,11 @@
|
|
|
#include "squeezelite.h" |
|
|
#include "squeezelite.h" |
|
|
|
|
|
|
|
@ -185,7 +193,7 @@ |
|
|
// logging functions |
|
|
// logging functions |
|
|
const char *logtime(void) { |
|
|
const char *logtime(void) { |
|
|
static char buf[100]; |
|
|
static char buf[100]; |
|
|
@@ -114,58 +110,101 @@ u32_t gettime_ms(void) {
|
|
|
|
|
|
|
|
|
@@ -114,58 +110,94 @@ u32_t gettime_ms(void) {
|
|
|
#endif |
|
|
#endif |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -224,17 +232,16 @@ |
|
|
+ loglevel = level;
|
|
|
+ loglevel = level;
|
|
|
+
|
|
|
+
|
|
|
+ // Check for non-zero MAC
|
|
|
+ // Check for non-zero MAC
|
|
|
+ if ((mac[0] | mac[1] | mac[2]) != 0)
|
|
|
|
|
|
|
|
|
+ if (mac[0] | mac[1] | mac[2] != 0)
|
|
|
+ have_mac = 1;
|
|
|
+ have_mac = 1;
|
|
|
+
|
|
|
+
|
|
|
+ // Copy interface name, if it was provided.
|
|
|
|
|
|
+ if (iface != NULL)
|
|
|
|
|
|
+ {
|
|
|
|
|
|
+ if( strlen(iface) > sizeof(ifname) )
|
|
|
|
|
|
+ return -1;
|
|
|
|
|
|
|
|
|
|
|
|
+ strncpy(ifname, iface, sizeof(ifname));
|
|
|
|
|
|
+ have_ifname = 1;
|
|
|
|
|
|
|
|
|
+ // Copy interface name, if it was provided.
|
|
|
|
|
|
+ if (iface != NULL) {
|
|
|
|
|
|
+ if (strlen(iface) > sizeof(ifname))
|
|
|
|
|
|
+ return -1;
|
|
|
|
|
|
|
|
|
|
|
|
+ strncpy(ifname, iface, sizeof(ifname));
|
|
|
|
|
|
+ have_ifname = 1;
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
- mac[0] = mac[1] = mac[2] = mac[3] = mac[4] = mac[5] = 0;
|
|
|
- mac[0] = mac[1] = mac[2] = mac[3] = mac[4] = mac[5] = 0;
|
|
@ -243,22 +250,19 @@ |
|
|
+ for (ifa = addrs; ifa; ifa = ifa->ifa_next) {
|
|
|
+ for (ifa = addrs; ifa; ifa = ifa->ifa_next) {
|
|
|
+ // Skip LOOPBACK interfaces, DOWN interfaces and interfaces that
|
|
|
+ // Skip LOOPBACK interfaces, DOWN interfaces and interfaces that
|
|
|
+ // don't support BROADCAST.
|
|
|
+ // don't support BROADCAST.
|
|
|
+ if ((ifa->ifa_flags & IFF_LOOPBACK)
|
|
|
|
|
|
+ || !(ifa->ifa_flags & IFF_UP)
|
|
|
|
|
|
+ || !(ifa->ifa_flags & IFF_BROADCAST))
|
|
|
|
|
|
+ {
|
|
|
|
|
|
|
|
|
+ if (ifa->ifa_flags & IFF_LOOPBACK
|
|
|
|
|
|
+ || !ifa->ifa_flags & IFF_UP
|
|
|
|
|
|
+ || !ifa->ifa_flags & IFF_BROADCAST) {
|
|
|
+ continue;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
|
|
|
- int s = socket(AF_INET, SOCK_DGRAM, 0);
|
|
|
- int s = socket(AF_INET, SOCK_DGRAM, 0);
|
|
|
+ if (!have_ifname)
|
|
|
|
|
|
+ {
|
|
|
|
|
|
|
|
|
+ if (!have_ifname) {
|
|
|
+ // We have found a valid interface name. Keep it.
|
|
|
+ // We have found a valid interface name. Keep it.
|
|
|
+ strncpy(ifname, ifa->ifa_name, sizeof(ifname));
|
|
|
+ strncpy(ifname, ifa->ifa_name, sizeof(ifname));
|
|
|
+ have_ifname = 1;
|
|
|
+ have_ifname = 1;
|
|
|
+ } else {
|
|
|
+ } else {
|
|
|
+ if (strncmp(ifname, ifa->ifa_name, sizeof(ifname)) != 0)
|
|
|
|
|
|
+ {
|
|
|
|
|
|
|
|
|
+ if (strncmp(ifname, ifa->ifa_name, sizeof(ifname)) != 0) {
|
|
|
+ // This is not the interface we're looking for.
|
|
|
+ // This is not the interface we're looking for.
|
|
|
+ continue;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
@ -270,20 +274,17 @@ |
|
|
- if (ioctl(s, SIOCGIFCONF, &ifc) == 0) {
|
|
|
- if (ioctl(s, SIOCGIFCONF, &ifc) == 0) {
|
|
|
- ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq));
|
|
|
- ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq));
|
|
|
+ // Check address family.
|
|
|
+ // Check address family.
|
|
|
+ if ((ifa->ifa_addr->sa_family == AF_INET) &&
|
|
|
|
|
|
+ (((struct sockaddr_in *)ifa->ifa_broadaddr)->sin_addr.s_addr != 0))
|
|
|
|
|
|
+ {
|
|
|
|
|
|
|
|
|
+ if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET &&
|
|
|
|
|
|
+ ((struct sockaddr_in *)ifa->ifa_broadaddr)->sin_addr.s_addr != 0) {
|
|
|
+ // Get broadcast address and MAC address
|
|
|
+ // Get broadcast address and MAC address
|
|
|
+ bcast_addr = ((struct sockaddr_in *)ifa->ifa_broadaddr)->sin_addr.s_addr;
|
|
|
+ bcast_addr = ((struct sockaddr_in *)ifa->ifa_broadaddr)->sin_addr.s_addr;
|
|
|
+ break;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
|
|
|
+ {
|
|
|
|
|
|
|
|
|
+ else {
|
|
|
+ // Address is not IPv4
|
|
|
+ // Address is not IPv4
|
|
|
+ if (iface == NULL)
|
|
|
+ if (iface == NULL)
|
|
|
+ have_ifname = 0;
|
|
|
+ have_ifname = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
|
|
|
- for (ifr = ifc.ifc_req; ifr < ifend; ifr++) {
|
|
|
- for (ifr = ifc.ifc_req; ifr < ifend; ifr++) {
|
|
@ -296,10 +297,10 @@ |
|
|
- break;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
+ // Find MAC address matching interface
|
|
|
+ // Find MAC address matching interface
|
|
|
+ if (!have_mac && (bcast_addr != 0)) {
|
|
|
|
|
|
|
|
|
+ if (!have_mac && bcast_addr != 0) {
|
|
|
+ for (ifa = addrs; ifa; ifa = ifa->ifa_next) {
|
|
|
+ for (ifa = addrs; ifa; ifa = ifa->ifa_next) {
|
|
|
+ if ((ifa->ifa_addr->sa_family == PF_PACKET) &&
|
|
|
|
|
|
+ (strncmp(ifname, ifa->ifa_name, sizeof(ifname)) == 0)) {
|
|
|
|
|
|
|
|
|
+ if (ifa->ifa_addr && ifa->ifa_addr->sa_family == PF_PACKET &&
|
|
|
|
|
|
+ strncmp(ifname, ifa->ifa_name, sizeof(ifname)) == 0) {
|
|
|
+ sdl = (struct sockaddr *)(ifa->ifa_addr);
|
|
|
+ sdl = (struct sockaddr *)(ifa->ifa_addr);
|
|
|
+ ptr = (unsigned char *)sdl->sa_data;
|
|
|
+ ptr = (unsigned char *)sdl->sa_data;
|
|
|
+ memcpy(mac, ptr + 10, 6);
|
|
|
+ memcpy(mac, ptr + 10, 6);
|
|
@ -323,7 +324,7 @@ |
|
|
#if SUN |
|
|
#if SUN |
|
|
void get_mac(u8_t mac[]) { |
|
|
void get_mac(u8_t mac[]) { |
|
|
struct arpreq parpreq; |
|
|
struct arpreq parpreq; |
|
|
@@ -232,30 +271,6 @@ void get_mac(u8_t mac[]) {
|
|
|
|
|
|
|
|
|
@@ -232,30 +264,6 @@ void get_mac(u8_t mac[]) {
|
|
|
} |
|
|
} |
|
|
#endif |
|
|
#endif |
|
|
|
|
|
|
|
|