rendered paste body/*
* A very simple despotify client, to show how the library is used.
*
* $Id: simple.c 514 2010-12-13 22:15:51Z dstien $
*
*/
#include <locale.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <wchar.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "aes.c"
#include "audio.h"
#include "despotify.h"
#include "channel.h"
#include "util.h"
static int listen_fd = -1;
static int client_fd = -1;
static void* audio_device;
char *wrapper_read_command(void);
int wrapper_listen(int port);
void wrapper_wprintf(wchar_t *fmt, ...);
static int simple_aes_callback(CHANNEL* ch,
unsigned char* buf,
unsigned short len);
/****** PCM thread stuff: ***********/
static pthread_t thread;
static pthread_mutex_t thread_mutex;
static pthread_cond_t thread_cond;
static enum {
PAUSE,
PLAY,
EXIT
} play_state = PAUSE;
static void thread_play(void)
{
pthread_mutex_lock(&thread_mutex);
play_state = PLAY;
pthread_cond_signal(&thread_cond);
pthread_mutex_unlock(&thread_mutex);
}
static void thread_pause(void)
{
pthread_mutex_lock(&thread_mutex);
play_state = PAUSE;
pthread_mutex_unlock(&thread_mutex);
}
static void thread_exit(void)
{
pthread_mutex_lock(&thread_mutex);
play_state = EXIT;
pthread_cond_signal(&thread_cond);
pthread_mutex_unlock(&thread_mutex);
pthread_join(thread, NULL);
}
static void* thread_loop(void* arg)
{
struct despotify_session* ds = arg;
struct pcm_data pcm;
pthread_mutex_init(&thread_mutex, NULL);
pthread_cond_init(&thread_cond, NULL);
bool loop = true;
while (loop) {
switch (play_state) {
case PAUSE:
pthread_mutex_lock(&thread_mutex);
pthread_cond_wait(&thread_cond, &thread_mutex);
pthread_mutex_unlock(&thread_mutex);
break;
case PLAY: {
int rc = despotify_get_pcm(ds, &pcm);
if (rc == 0)
audio_play_pcm(audio_device, &pcm);
else {
wrapper_wprintf(L"despotify_get_pcm() returned error %d\n", rc);
exit(-1);
}
break;
}
case EXIT:
loop = false;
break;
}
}
pthread_cond_destroy(&thread_cond);
pthread_mutex_destroy(&thread_mutex);
return NULL;
}
/**************** UI (main) thread stuff: ***************/
struct playlist* get_playlist(struct playlist* rootlist, int num)
{
struct playlist* p = rootlist;
if (!p) {
wrapper_wprintf(L"Stored lists not loaded. Run 'list' without parameter to load.\n");
}
else {
/* skip to playlist number <num> */
for (int i = 1; i < num && p; i++)
p = p->next;
if (!p)
wrapper_wprintf(L"Invalid playlist number %d\n", num);
}
return p;
}
void print_list_of_lists(struct playlist* rootlist)
{
if (!rootlist) {
wrapper_wprintf(L" <no stored playlists>\n");
}
else {
int count=1;
for (struct playlist* p = rootlist; p; p = p->next)
wrapper_wprintf(L"%2d: %-40s %3d %c %s\n", count++, p->name, p->num_tracks,
p->is_collaborative ? '*' : ' ', p->author);
}
}
void print_tracks(struct track* head)
{
if (!head) {
wrapper_wprintf(L" <empty playlist>\n");
return;
}
int count = 1;
for (struct track* t = head; t; t = t->next) {
if (t->has_meta_data) {
wrapper_wprintf(L"%3d: %-40s %2d:%02d -|- tid: %s fid: %s br: %d key: %s", count++, t->title,
t->length / 60000, t->length % 60000 / 1000, t->track_id, t->file_id, t->file_bitrate, t->key);
for (struct artist* a = t->artist; a; a = a->next)
wrapper_wprintf(L"%s%s", a->name, a->next ? ", " : "");
wrapper_wprintf(L" %s\n", t->playable ? "" : "(Unplayable)");
}
else
wrapper_wprintf(L"%3d: N/A\n", count++);
}
}
void print_track_full(struct track* t)
{
if(t->has_meta_data) {
wrapper_wprintf(L"\nTitle: %s\nAlbum: %s\nArtist(s): ",
t->title, t->album);
for (struct artist* a = t->artist; a; a = a->next)
wrapper_wprintf(L"%s%s", a->name, a->next ? ", " : "");
wrapper_wprintf(L"\nYear: %d\nLength: %02d:%02d\n\n",
t->year, t->length / 60000, t->length % 60000 / 1000);
} else {
wrapper_wprintf(L" <track has no metadata>\n");
}
}
void print_album(struct album_browse* a)
{
wrapper_wprintf(L"\nName: %s\nYear: %d\n",
a->name, a->year);
print_tracks(a->tracks);
}
void print_artist(struct artist_browse* a)
{
wrapper_wprintf(L"\nName: %s\n"
"Genres: %s\n"
"Years active: %s\n"
"%d albums:\n",
a->name, a->genres, a->years_active, a->num_albums);
for (struct album_browse* al = a->albums; al; al = al->next)
wrapper_wprintf(L" %s (%d)\n", al->name, al->year);
}
void print_playlist(struct playlist* pls)
{
wrapper_wprintf(L"\nName: %s\nAuthor: %s\n",
pls->name, pls->author);
print_tracks(pls->tracks);
}
void print_search(struct search_result *search)
{
if (search->suggestion[0])
wrapper_wprintf(L"\nDid you mean \"%s\"?\n", search->suggestion);
if (search->total_artists > 0) {
wrapper_wprintf(L"\nArtists found (%d):\n", search->total_artists);
for (struct artist* artist = search->artists; artist; artist = artist->next)
wrapper_wprintf(L" %s -|- %s\n", artist->name, artist->id);
}
if (search->total_albums > 0) {
wrapper_wprintf(L"\nAlbums found (%d):\n", search->total_albums);
for (struct album* album = search->albums; album; album = album->next)
wrapper_wprintf(L" %s -|- %s\n", album->name, album->id);
}
if (search->total_tracks > 0) {
wrapper_wprintf(L"\nTracks found (%d/%d):\n", search->playlist->num_tracks, search->total_tracks);
print_tracks(search->tracks);
}
}
void print_info(struct despotify_session* ds)
{
struct user_info* user = ds->user_info;
wrapper_wprintf(L"Username : %s\n", user->username);
wrapper_wprintf(L"Country : %s\n", user->country);
wrapper_wprintf(L"Account type : %s\n", user->type);
wrapper_wprintf(L"Account expiry : %s", ctime(&user->expiry));
wrapper_wprintf(L"Host : %s:%d\n", user->server_host, user->server_port);
wrapper_wprintf(L"Last ping : %s", ctime(&user->last_ping));
if (strncmp(user->type, "premium", 7)) {
wrapper_wprintf(L"\n=================================================\n"
" N O T I C E\n"
" You do not have a premium account.\n"
" Spotify services will not be available.\n"
"=================================================\n");
}
}
void print_help(void)
{
wrapper_wprintf(L"\nAvailable commands:\n\n"
"list [num] List stored playlists\n"
"rename [num] [string] Rename playlist\n"
"collab [num] Toggle playlist collaboration\n"
"\n"
"search [string] Search for [string] or get next 100 results\n"
"artist [num] Show information about artist for track [num]\n"
"album [num] List album for track [num]\n"
"uri [string] Display info about Spotify URI\n"
"portrait [num] Save artist portrait to portrait.jpg\n"
"cover [num] Save album cover art to cover.jpg\n"
"\n"
"play [num] Play track [num] in the last viewed list\n"
"playalbum [num] Play album for track [num]\n"
"next Play next track in the current list\n"
"stop, pause, resume Control playback\n"
"\n"
"info Details about your account and current connection\n"
"help This text\n"
"quit Quit\n");
}
/* called by channel */
static int simple_aes_callback(CHANNEL* ch,
unsigned char* buf,
unsigned short len)
{
wrapper_wprintf (L"got callback");
if (ch->state == CHANNEL_DATA) {
wrapper_wprintf (L"w1\n");
struct despotify_session* ds = ch->private;
struct track* t = ds->track;
wrapper_wprintf (L"w2\n");
/*if (t->key)
DSFYfree(t->key);*/
wrapper_wprintf (L"w3\n");
t->key = malloc(len);
memcpy(t->key, buf, len);
/* Expand file key */
rijndaelKeySetupEnc (ds->aes.state, t->key, 128);
wrapper_wprintf (L"w4\n");
/* Set initial IV */
memcpy(ds->aes.IV,
"\x72\xe0\x67\xfb\xdd\xcb\xcf\x77"
"\xeb\xe8\xbc\x64\x3f\x63\x0d\x93",
16);
char foo[1024];
hex_bytes_to_ascii(t->key,foo,len);
wrapper_wprintf (L"Got AES key %s - %d\n",foo,len);
wrapper_wprintf (L"w5\n");
/* Add SND_CMD_START to buffer chain,
with a copy of the track struct */
struct track* copy = malloc(sizeof(struct track));
memcpy(copy, ds->track, sizeof(struct track));
// snd_ioctl(ds, SND_CMD_START, copy, 0);
}
return 0;
}
void command_loop(struct despotify_session* ds)
{
bool loop = true;
char *buf;
struct playlist* rootlist = NULL;
struct playlist* searchlist = NULL;
struct playlist* lastlist = NULL;
struct search_result *search = NULL;
struct album_browse* playalbum = NULL;
print_help();
do {
wprintf(L"\n> ");
fflush(stdout);
if((buf = wrapper_read_command()) == NULL)
break;
/* list */
if (!strncmp(buf, "list", 4)) {
int num = 0;
if(strlen(buf) > 5)
num = atoi(buf + 5);
if (num) {
struct playlist* p = get_playlist(rootlist, num);
if (p) {
print_tracks(p->tracks);
lastlist = p;
}
}
else {
if (!rootlist)
rootlist = despotify_get_stored_playlists(ds);
print_list_of_lists(rootlist);
}
}
/* rename */
else if (!strncmp(buf, "rename", 6)) {
int num = 0;
char *name = 0;
if (strlen(buf) > 9) {
num = atoi(buf + 7);
name = strchr(buf + 7, ' ') + 1;
}
if (num && name && name[0]) {
struct playlist* p = get_playlist(rootlist, num);
if (p) {
if (despotify_rename_playlist(ds, p, name))
wrapper_wprintf(L"Renamed playlist %d to \"%s\".\n", num, name);
else
wrapper_wprintf(L"Rename failed: %s\n", despotify_get_error(ds));
}
}
else
wrapper_wprintf(L"Usage: rename [num] [string]\n");
}
/* collab */
else if (!strncmp(buf, "collab", 6)) {
int num = 0;
if (strlen(buf) > 7)
num = atoi(buf + 7);
if (num) {
struct playlist* p = get_playlist(rootlist, num);
if (p) {
if (despotify_set_playlist_collaboration(ds, p, !p->is_collaborative))
wrapper_wprintf(L"Changed playlist %d collaboration to %s.\n",
num, p->is_collaborative ? "ON" : "OFF");
else
wrapper_wprintf(L"Setting playlist collaboration state failed: %s\n",
despotify_get_error(ds));
}
}
else
wrapper_wprintf(L"Usage: collab [num]\n");
}
/* search */
else if (!strncmp(buf, "search", 6)) {
if (buf[7]) {
thread_pause();
despotify_stop(ds); /* since we replace the list */
if (search)
despotify_free_search(search);
search = despotify_search(ds, buf + 7, MAX_SEARCH_RESULTS);
if (!search) {
wrapper_wprintf(L"Search failed: %s\n", despotify_get_error(ds));
continue;
}
searchlist = search->playlist;
}
else if (searchlist && (searchlist->num_tracks < search->total_tracks))
if (!despotify_search_more(ds, search, searchlist->num_tracks, MAX_SEARCH_RESULTS)) {
wrapper_wprintf(L"Search failed: %s\n", despotify_get_error(ds));
continue;
}
if (searchlist) {
print_search(search);
lastlist = searchlist;
}
else
wrapper_wprintf(L"No previous search\n");
}
/* artist */
else if (!strncmp(buf, "artist", 6)) {
int num = 0;
if(strlen(buf) > 7)
num = atoi(buf + 7);
if (!num) {
wrapper_wprintf(L"usage: artist [num]\n");
continue;
}
if (!lastlist) {
wrapper_wprintf(L"No playlist\n");
continue;
}
/* find the requested track */
struct track* t = lastlist->tracks;
for (int i=1; i<num; i++)
t = t->next;
for (struct artist* aptr = t->artist; aptr; aptr = aptr->next) {
struct artist_browse* a = despotify_get_artist(ds, aptr->id);
print_artist(a);
despotify_free_artist_browse(a);
}
}
else if (!strncmp(buf, "albumid", 7)) {
wrapper_wprintf(L"searching for id %s\n", buf+8);
struct album_browse* a = despotify_get_album(ds, buf+8);
if (a) {
print_album(a);
despotify_free_album_browse(a);
}
else
wrapper_wprintf(L"Got no album for id %s\n", buf+8);
}
else if (!strncmp(buf, "key", 3)) {
char k_track[1024];
char k_file[1024];
if (sscanf (buf, "key %32s %40s", k_track, k_file) == 2) {
k_file[40] = k_track[32] = 0;
if (strlen (k_file) != 40 || strlen (k_track) != 32) {
wrapper_wprintf (L"501 0 WARN File ID must be provided in hex as 40 character and track ID must be provided in hex as 32 characters\n");
}
wrapper_wprintf(L"requesting key for %s|%s\n", k_track,k_file);
struct track* t = despotify_get_track(ds, k_track);
char fid[20], tid[16];
hex_ascii_to_bytes(k_file, fid, sizeof fid);
hex_ascii_to_bytes(k_track, tid, sizeof tid);
ds->track = t;
int error = cmd_aeskey(ds->session, fid, tid, simple_aes_callback, ds);
if (error) {
wrapper_wprintf(L"cmd_aeskey() failed for %s\n", t->title);
return;
}
wrapper_wprintf (L"key is: %s",t->key);
}else
{
wrapper_wprintf(L"foo");
}
}
/* album */
else if (!strncmp(buf, "album", 5)) {
int num = 0;
if(strlen(buf) > 6)
num = atoi(buf + 6);
if (!num) {
wrapper_wprintf(L"usage: album [num]\n");
continue;
}
if (!lastlist) {
wrapper_wprintf(L"No playlist\n");
continue;
}
/* find the requested track */
struct track* t = lastlist->tracks;
for (int i=1; i<num; i++)
t = t->next;
if (t) {
struct album_browse* a = despotify_get_album(ds, t->album_id);
if (a) {
print_album(a);
despotify_free_album_browse(a);
}
else
wrapper_wprintf(L"Got no album for id %s\n", t->album_id);
}
}
/* playalbum */
else if (!strncmp(buf, "playalbum", 9)) {
int num = 0;
if(strlen(buf) > 10)
num = atoi(buf + 10);
if (!num) {
wrapper_wprintf(L"usage: playalbum [num]\n");
continue;
}
if (!lastlist) {
wrapper_wprintf(L"No playlist\n");
continue;
}
/* find the requested track */
struct track* t = lastlist->tracks;
for (int i=1; i<num; i++)
t = t->next;
if (t) {
thread_pause();
despotify_stop(ds);
if (playalbum)
despotify_free_album_browse(playalbum);
playalbum = despotify_get_album(ds, t->album_id);
if (playalbum) {
despotify_play(ds, playalbum->tracks, true);
thread_play();
}
else
wrapper_wprintf(L"Got no album for id %s\n", t->album_id);
}
}
/* uri */
else if (!strncmp(buf, "uri", 3)) {
char *uri = buf + 4;
if(strlen(uri) == 0) {
wrapper_wprintf(L"usage: info <uri>\n");
continue;
}
struct link* link = despotify_link_from_uri(uri);
struct album_browse* al;
struct artist_browse* ar;
struct playlist* pls;
struct search_result* s;
struct track* t;
switch(link->type) {
case LINK_TYPE_ALBUM:
al = despotify_link_get_album(ds, link);
if(al) {
print_album(al);
despotify_free_album_browse(al);
}
break;
case LINK_TYPE_ARTIST:
ar = despotify_link_get_artist(ds, link);
if(ar) {
print_artist(ar);
despotify_free_artist_browse(ar);
}
break;
case LINK_TYPE_PLAYLIST:
pls = despotify_link_get_playlist(ds, link);
if(pls) {
print_playlist(pls);
despotify_free_playlist(pls);
}
break;
case LINK_TYPE_SEARCH:
s = despotify_link_get_search(ds, link);
if(s) {
print_search(s);
despotify_free_search(s);
}
break;
case LINK_TYPE_TRACK:
t = despotify_link_get_track(ds, link);
if(t) {
print_track_full(t);
despotify_free_track(t);
}
break;
default:
wrapper_wprintf(L"%s is a invalid Spotify URI\n", uri);
}
despotify_free_link(link);
}
/* portrait */
else if (!strncmp(buf, "portrait", 8)) {
int num = 0;
if(strlen(buf) > 9)
num = atoi(buf + 9);
if (!num) {
wrapper_wprintf(L"usage: portrait [num]\n");
continue;
}
if (!lastlist) {
wrapper_wprintf(L"No playlist\n");
continue;
}
/* find the requested artist */
struct track* t = lastlist->tracks;
for (int i=1; i<num; i++)
t = t->next;
struct artist_browse* a = despotify_get_artist(ds, t->artist->id);
if (a && a->portrait_id[0]) {
int len;
void* portrait = despotify_get_image(ds, a->portrait_id, &len);
if (portrait && len) {
wrapper_wprintf(L"Writing %d bytes into portrait.jpg\n", len);
FILE* f = fopen("portrait.jpg", "w");
if (f) {
fwrite(portrait, len, 1, f);
fclose(f);
}
free(portrait);
}
}
else
wrapper_wprintf(L"Artist %s has no portrait.\n", a->name);
despotify_free_artist_browse(a);
}
/* cover */
else if (!strncmp(buf, "cover", 5)) {
int num = 0;
if(strlen(buf) > 6)
num = atoi(buf + 6);
if (!num) {
wrapper_wprintf(L"usage: cover [num]\n");
continue;
}
if (!lastlist) {
wrapper_wprintf(L"No playlist\n");
continue;
}
/* find the requested album */
struct track* t = lastlist->tracks;
for (int i=1; i<num; i++)
t = t->next;
if (t) {
struct album_browse* a = despotify_get_album(ds, t->album_id);
if (a && a->cover_id[0]) {
int len;
void* cover = despotify_get_image(ds, a->cover_id, &len);
if (cover && len) {
wrapper_wprintf(L"Writing %d bytes into cover.jpg\n", len);
FILE* f = fopen("cover.jpg", "w");
if (f) {
fwrite(cover, len, 1, f);
fclose(f);
}
free(cover);
}
}
else
wrapper_wprintf(L"Album %s has no cover.\n", t->album);
despotify_free_album_browse(a);
}
}
/* play */
else if (!strncmp(buf, "play", 4) || !strncmp(buf, "next", 4)) {
if (!lastlist) {
wrapper_wprintf(L"No list to play from. Use 'list' or 'search' to select a list.\n");
continue;
}
/* skip to track <num>, else play next */
struct track* t;
if (buf[4]) {
int listoffset = atoi(buf + 5);
t = lastlist->tracks;
for (int i=1; i<listoffset && t; i++)
t = t->next;
if (t) {
despotify_play(ds, t, true);
thread_play();
}
else
wrapper_wprintf(L"Invalid track number %d\n", listoffset);
}
else {
despotify_next(ds);
}
}
/* stop */
else if (!strncmp(buf, "stop", 4)) {
thread_pause();
despotify_stop(ds);
}
/* pause */
else if (!strncmp(buf, "pause", 5)) {
thread_pause();
}
/* resume */
else if (!strncmp(buf, "resume", 5)) {
thread_play();
}
/* info */
else if (!strncmp(buf, "info", 4)) {
print_info(ds);
}
/* help */
else if (!strncmp(buf, "help", 4)) {
print_help();
}
/* quit */
else if (!strncmp(buf, "quit", 4)) {
loop = false;
}
} while(loop);
if (rootlist)
despotify_free_playlist(rootlist);
if (search)
despotify_free_search(search);
if (playalbum)
despotify_free_album_browse(playalbum);
}
void callback(struct despotify_session* ds, int signal, void* data, void* callback_data)
{
static int seconds = -1;
(void)ds; (void)callback_data; /* don't warn about unused parameters */
switch (signal) {
case DESPOTIFY_NEW_TRACK: {
struct track* t = data;
wrapper_wprintf(L"New track: %s / %s (%d:%02d) %d kbit/s\n",
t->title, t->artist->name,
t->length / 60000, t->length % 60000 / 1000,
t->file_bitrate / 1000);
break;
}
case DESPOTIFY_TIME_TELL:
if ((int)(*((double*)data)) != seconds) {
seconds = *((double*)data);
wrapper_wprintf(L"Time: %d:%02d\r", seconds / 60, seconds % 60);
}
break;
case DESPOTIFY_END_OF_PLAYLIST:
wrapper_wprintf(L"End of playlist\n");
thread_pause();
break;
}
}
int main(int argc, char** argv)
{
setlocale(LC_ALL, "");
if (argc < 3) {
wrapper_wprintf(L"Usage: %s <username> <password> [remote control port]\n", argv[0]);
return 1;
}
DSFYDEBUG("$Id: simple.c 514 2010-12-13 22:15:51Z dstien $\n");
if (!despotify_init())
{
wrapper_wprintf(L"despotify_init() failed\n");
return 1;
}
struct despotify_session* ds = despotify_init_client(callback, NULL, true, true);
if (!ds) {
wrapper_wprintf(L"despotify_init_client() failed\n");
return 1;
}
pthread_create(&thread, NULL, &thread_loop, ds);
if(argc == 4 && wrapper_listen(atoi(argv[3]))) {
wrapper_wprintf(L"wrapper_listen() failed to listen on local port %s\n", argv[3]);
return 1;
}
if (!despotify_authenticate(ds, argv[1], argv[2])) {
printf( "Authentication failed: %s\n", despotify_get_error(ds));
despotify_exit(ds);
return 1;
}
audio_device = audio_init();
#if 0
{
struct track* t = despotify_get_track(ds, "d1b264bb6bcd46be852ceba8ac5e6582");
despotify_play(ds, t, false);
thread_play();
while(1) {
sleep(1);
}
}
#else
print_info(ds);
command_loop(ds);
#endif
thread_exit();
audio_exit(audio_device);
despotify_exit(ds);
if (!despotify_cleanup())
{
wrapper_wprintf(L"despotify_cleanup() failed\n");
return 1;
}
return 0;
}
int wrapper_listen(int port) {
struct sockaddr_in sin;
int value;
memset(&sin, 0, sizeof(sin));
sin.sin_family = PF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = inet_addr("127.0.0.1");
if((listen_fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
return -1;
value = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(int));
if(bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0
|| listen(listen_fd, 1) < 0) {
close(listen_fd);
listen_fd = -1;
return -1;
}
return 0;
}
char *wrapper_read_command(void) {
static char sock_buf[256] = { 0 };
static char stdin_buf[256] = { 0 };
static int sock_buf_len;
static int stdin_buf_len;
static char *command = NULL;
fd_set rfds;
int max_fd = 0;
char *ptr;
int ret;
if(command) {
free(command);
command = NULL;
}
for(;;) {
FD_ZERO(&rfds);
if(isatty(0))
FD_SET(0, &rfds);
if(listen_fd != -1 && client_fd == -1) {
FD_SET(listen_fd, &rfds);
max_fd = listen_fd;
}
else if(client_fd != -1) {
FD_SET(client_fd, &rfds);
max_fd = client_fd;
}
if(select(max_fd + 1, &rfds, NULL, NULL, NULL) < 0)
break;
if(FD_ISSET(0, &rfds)) {
ret = read(0, stdin_buf + stdin_buf_len, sizeof(stdin_buf) - stdin_buf_len - 1);
if(ret > 0) {
stdin_buf_len += ret;
stdin_buf[stdin_buf_len] = 0;
}
}
if(listen_fd >= 0 && FD_ISSET(listen_fd, &rfds)) {
client_fd = accept(listen_fd, NULL, NULL);
continue;
}
if(client_fd >= 0 && FD_ISSET(client_fd, &rfds)) {
ret = read(client_fd, sock_buf + sock_buf_len, sizeof(sock_buf) - sock_buf_len - 1);
if(ret > 0) {
sock_buf_len += ret;
sock_buf[sock_buf_len] = 0;
}
else {
close(client_fd);
client_fd = -1;
sock_buf_len = 0;
*sock_buf = 0;
}
}
if((ptr = strchr(stdin_buf, '\n')) != NULL) {
*ptr++ = 0;
if(strlen(stdin_buf))
command = strdup(stdin_buf);
stdin_buf_len -= ptr - stdin_buf;
memmove(stdin_buf, ptr, stdin_buf_len + 1);
if(command)
break;
}
else if((ptr = strchr(sock_buf, '\n')) != NULL) {
*ptr++ = 0;
if(strlen(sock_buf))
command = strdup(sock_buf);
sock_buf_len -= ptr - sock_buf;
memmove(sock_buf, ptr, sock_buf_len + 1);
if(command)
break;
}
}
return command;
}
void wrapper_wprintf(wchar_t *fmt, ...) {
wchar_t wstr[4096];
char mbstr[4096];
char *ptr;
va_list ap;
int ret, written;
va_start(ap, fmt);
ret = vswprintf(wstr, sizeof(wstr) - 1, fmt, ap);
va_end(ap);
if(ret >= (int)sizeof(wstr))
return;
wcstombs(mbstr, wstr, sizeof(mbstr) - 1);
mbstr[sizeof(mbstr) - 1] = 0;
if(isatty(0)) {
wprintf(L"%S", wstr);
fflush(stdout);
}
if(client_fd == -1)
return;
ptr = mbstr;
ret = strlen(mbstr);
while(ret > 0) {
if((written = write(client_fd, ptr, ret)) <= 0) {
close(client_fd);
client_fd = -1;
break;
}
ptr += written;
ret -= written;
}
}