1 /*
2  * MOC - music on console
3  * Copyright (C) 2004 - 2006 Damian Pietras <daper@daper.net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  */
11 
12 #ifdef HAVE_CONFIG_H
13 # include "config.h"
14 #endif
15 
16 #include <stdarg.h>
17 #include <locale.h>
18 #include <unistd.h>
19 #include <assert.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <time.h>
25 #include <signal.h>
26 #include <ctype.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <sys/time.h>
30 #include <sys/wait.h>
31 #ifdef HAVE_SYS_SELECT_H
32 # include <sys/select.h>
33 #endif
34 
35 /* Include dirent for various systems */
36 #ifdef HAVE_DIRENT_H
37 # include <dirent.h>
38 #else
39 # define dirent direct
40 # if HAVE_SYS_NDIR_H
41 #  include <sys/ndir.h>
42 # endif
43 #endif
44 
45 #define DEBUG
46 
47 #include "common.h"
48 #include "log.h"
49 #include "interface_elements.h"
50 #include "interface.h"
51 #include "lists.h"
52 #include "playlist.h"
53 #include "playlist_file.h"
54 #include "protocol.h"
55 #include "keys.h"
56 #include "options.h"
57 #include "files.h"
58 #include "decoder.h"
59 #include "themes.h"
60 #include "softmixer.h"
61 #include "utf8.h"
62 
63 #define INTERFACE_LOG	"mocp_client_log"
64 #define PLAYLIST_FILE	"playlist.m3u"
65 
66 #define QUEUE_CLEAR_THRESH 128
67 
68 /* Socket of the server connection. */
69 static int srv_sock = -1;
70 
71 static struct plist *playlist = NULL; /* our playlist */
72 static struct plist *queue = NULL; /* our queue */
73 static struct plist *dir_plist = NULL; /* contents of the current directory */
74 
75 /* Queue for events coming from the server. */
76 static struct event_queue events;
77 
78 /* Current working directory (the directory we show). */
79 static char cwd[PATH_MAX] = "";
80 
81 /* If the user presses quit, or we receive a termination signal. */
82 static volatile enum want_quit want_quit = NO_QUIT;
83 
84 /* If user presses CTRL-C, set this to 1.  This should interrupt long
85  * operations which block the interface. */
86 static volatile int wants_interrupt = 0;
87 
88 #ifdef SIGWINCH
89 /* If we get SIGWINCH. */
90 static volatile int want_resize = 0;
91 #endif
92 
93 /* Are we waiting for the playlist we have loaded and sent to the clients? */
94 static int waiting_for_plist_load = 0;
95 
96 /* Information about the currently played file. */
97 static struct file_info curr_file;
98 
99 /* Silent seeking - where we are in seconds. -1 - no seeking. */
100 static int silent_seek_pos = -1;
101 static time_t silent_seek_key_last = (time_t)0; /* when the silent seek key was
102 						   last used */
103 
104 /* When the menu was last moved (arrow keys, page up, etc.) */
105 static time_t last_menu_move_time = (time_t)0;
106 
sig_quit(int sig ATTR_UNUSED)107 static void sig_quit (int sig ATTR_UNUSED)
108 {
109 	log_signal (sig);
110 	want_quit = QUIT_CLIENT;
111 }
112 
sig_interrupt(int sig ATTR_UNUSED)113 static void sig_interrupt (int sig ATTR_UNUSED)
114 {
115 	log_signal (sig);
116 	wants_interrupt = 1;
117 }
118 
119 #ifdef SIGWINCH
sig_winch(int sig ATTR_UNUSED)120 static void sig_winch (int sig ATTR_UNUSED)
121 {
122 	log_signal (sig);
123 	want_resize = 1;
124 }
125 #endif
126 
user_wants_interrupt()127 int user_wants_interrupt ()
128 {
129 	return wants_interrupt;
130 }
131 
clear_interrupt()132 static void clear_interrupt ()
133 {
134 	wants_interrupt = 0;
135 }
136 
send_int_to_srv(const int num)137 static void send_int_to_srv (const int num)
138 {
139 	if (!send_int(srv_sock, num))
140 		fatal ("Can't send() int to the server!");
141 }
142 
send_str_to_srv(const char * str)143 static void send_str_to_srv (const char *str)
144 {
145 	if (!send_str(srv_sock, str))
146 		fatal ("Can't send() string to the server!");
147 }
148 
send_item_to_srv(const struct plist_item * item)149 static void send_item_to_srv (const struct plist_item *item)
150 {
151 	if (!send_item(srv_sock, item))
152 		fatal ("Can't send() item to the server!");
153 }
154 
get_int_from_srv()155 static int get_int_from_srv ()
156 {
157 	int num;
158 
159 	if (!get_int(srv_sock, &num))
160 		fatal ("Can't receive value from the server!");
161 
162 	return num;
163 }
164 
165 /* Returned memory is malloc()ed. */
get_str_from_srv()166 static char *get_str_from_srv ()
167 {
168 	char *str = get_str (srv_sock);
169 
170 	if (!str)
171 		fatal ("Can't receive string from the server!");
172 
173 	return str;
174 }
175 
recv_tags_from_srv()176 static struct file_tags *recv_tags_from_srv ()
177 {
178 	struct file_tags *tags = recv_tags (srv_sock);
179 
180 	if (!tags)
181 		fatal ("Can't receive tags from the server!");
182 
183 	return tags;
184 }
185 
186 /* Noblocking version of get_int_from_srv(): return 0 if there are no data. */
get_int_from_srv_noblock(int * num)187 static int get_int_from_srv_noblock (int *num)
188 {
189 	enum noblock_io_status st;
190 
191 	if ((st = get_int_noblock(srv_sock, num)) == NB_IO_ERR)
192 		fatal ("Can't receive value from the server!");
193 
194 	return st == NB_IO_OK ? 1 : 0;
195 }
196 
recv_item_from_srv()197 static struct plist_item *recv_item_from_srv ()
198 {
199 	struct plist_item *item;
200 
201 	if (!(item = recv_item(srv_sock)))
202 		fatal ("Can't receive item from the server!");
203 
204 	return item;
205 }
206 
recv_tags_data_from_srv()207 static struct tag_ev_response *recv_tags_data_from_srv ()
208 {
209 	struct tag_ev_response *r;
210 
211 	r = (struct tag_ev_response *)xmalloc (sizeof(struct tag_ev_response));
212 
213 	r->file = get_str_from_srv ();
214 	if (!(r->tags = recv_tags(srv_sock)))
215 		fatal ("Can't receive tags event's data from the server!");
216 
217 	return r;
218 }
219 
recv_move_ev_data_from_srv()220 static struct move_ev_data *recv_move_ev_data_from_srv ()
221 {
222 	struct move_ev_data *d;
223 
224 	if (!(d = recv_move_ev_data(srv_sock)))
225 		fatal ("Can't receive move data from the server!");
226 
227 	return d;
228 }
229 
230 /* Receive data for the given type of event and return them. Return NULL if
231  * there is no data for the event. */
get_event_data(const int type)232 static void *get_event_data (const int type)
233 {
234 	switch (type) {
235 		case EV_PLIST_ADD:
236 		case EV_QUEUE_ADD:
237 			return recv_item_from_srv ();
238 		case EV_PLIST_DEL:
239 		case EV_QUEUE_DEL:
240 		case EV_STATUS_MSG:
241 		case EV_SRV_ERROR:
242 			return get_str_from_srv ();
243 		case EV_FILE_TAGS:
244 			return recv_tags_data_from_srv ();
245 		case EV_PLIST_MOVE:
246 		case EV_QUEUE_MOVE:
247 			return recv_move_ev_data_from_srv ();
248 	}
249 
250 	return NULL;
251 }
252 
253 /* Wait for EV_DATA handling other events. */
wait_for_data()254 static void wait_for_data ()
255 {
256 	int event;
257 
258 	do {
259 		event = get_int_from_srv ();
260 		if (event == EV_EXIT)
261 			interface_fatal ("The server exited!");
262 		if (event != EV_DATA)
263 			event_push (&events, event, get_event_data(event));
264 	 } while (event != EV_DATA);
265 }
266 
267 /* Get an integer value from the server that will arrive after EV_DATA. */
get_data_int()268 static int get_data_int ()
269 {
270 	wait_for_data ();
271 	return get_int_from_srv ();
272 }
273 
274 /* Get a string value from the server that will arrive after EV_DATA. */
get_data_str()275 static char *get_data_str ()
276 {
277 	wait_for_data ();
278 	return get_str_from_srv ();
279 }
280 
get_data_tags()281 static struct file_tags *get_data_tags ()
282 {
283 	wait_for_data ();
284 	return recv_tags_from_srv ();
285 }
286 
send_tags_request(const char * file,const int tags_sel)287 static int send_tags_request (const char *file, const int tags_sel)
288 {
289 	assert (file != NULL);
290 	assert (tags_sel != 0);
291 
292 	if (file_type(file) == F_SOUND) {
293 		send_int_to_srv (CMD_GET_FILE_TAGS);
294 		send_str_to_srv (file);
295 		send_int_to_srv (tags_sel);
296 		debug ("Asking for tags for %s", file);
297 
298 		return 1;
299 	}
300 	else {
301 		debug ("Not sending tags request for URL (%s)", file);
302 		return 0;
303 	}
304 }
305 
306 /* Send all items from this playlist to other clients. */
send_items_to_clients(const struct plist * plist)307 static void send_items_to_clients (const struct plist *plist)
308 {
309 	int i;
310 
311 	for (i = 0; i < plist->num; i++)
312 		if (!plist_deleted(plist, i)) {
313 			send_int_to_srv (CMD_CLI_PLIST_ADD);
314 			send_item_to_srv (&plist->items[i]);
315 		}
316 }
317 
init_playlists()318 static void init_playlists ()
319 {
320 	dir_plist = (struct plist *)xmalloc (sizeof(struct plist));
321 	plist_init (dir_plist);
322 	playlist = (struct plist *)xmalloc (sizeof(struct plist));
323 	plist_init (playlist);
324 	queue = (struct plist *)xmalloc (sizeof(struct plist));
325 	plist_init (queue);
326 
327 	/* set serial numbers for the playlist */
328 	send_int_to_srv (CMD_GET_SERIAL);
329 	plist_set_serial (playlist, get_data_int());
330 }
331 
file_info_reset(struct file_info * f)332 static void file_info_reset (struct file_info *f)
333 {
334 	f->file = NULL;
335 	f->tags = NULL;
336 	f->title = NULL;
337 	f->bitrate = -1;
338 	f->rate = -1;
339 	f->curr_time = -1;
340 	f->total_time = -1;
341 	f->channels = 1;
342 	f->state = STATE_STOP;
343 }
344 
file_info_cleanup(struct file_info * f)345 static void file_info_cleanup (struct file_info *f)
346 {
347 	if (f->tags)
348 		tags_free (f->tags);
349 	if (f->file)
350 		free (f->file);
351 	if (f->title)
352 		free (f->title);
353 
354 	f->file = NULL;
355 	f->tags = NULL;
356 	f->title = NULL;
357 }
358 
359 /* Initialise the block marker information. */
file_info_block_init(struct file_info * f)360 static void file_info_block_init (struct file_info *f)
361 {
362 	f->block_file = NULL;
363 }
364 
365 /* Reset the block start and end markers. */
file_info_block_reset(struct file_info * f)366 static void file_info_block_reset (struct file_info *f)
367 {
368 	if (f->block_file)
369 		free (f->block_file);
370 	f->block_file = NULL;
371 }
372 
373 /* Enter the current time into a block start or end marker. */
file_info_block_mark(int * marker)374 static void file_info_block_mark (int *marker)
375 {
376 	if (curr_file.state == STATE_STOP) {
377 		error ("Cannot make block marks while stopped.");
378 	} else if (file_type (curr_file.file) == F_URL) {
379 		error ("Cannot make block marks in URLs.");
380 	} else if (file_type (curr_file.file) != F_SOUND) {
381 		error ("Cannot make block marks in non-audio files.");
382 	} else if (!curr_file.block_file) {
383 		error ("Cannot make block marks in files of unknown duration.");
384 	} else {
385 		*marker = curr_file.curr_time;
386 		iface_set_block (curr_file.block_start, curr_file.block_end);
387 	}
388 }
389 
390 /* Get an integer option from the server (like shuffle) and set it. */
sync_int_option(const char * name)391 static void sync_int_option (const char *name)
392 {
393 	int value;
394 
395 	send_int_to_srv (CMD_GET_OPTION);
396 	send_str_to_srv (name);
397 	value = get_data_int ();
398 	options_set_int (name, value);
399 	iface_set_option_state (name, value);
400 }
401 
402 /* Get the server options and set our options like them. */
get_server_options()403 static void get_server_options ()
404 {
405 	sync_int_option ("Shuffle");
406 	sync_int_option ("Repeat");
407 	sync_int_option ("AutoNext");
408 }
409 
get_server_plist_serial()410 static int get_server_plist_serial ()
411 {
412 	send_int_to_srv (CMD_PLIST_GET_SERIAL);
413 	return get_data_int ();
414 }
415 
get_mixer_value()416 static int get_mixer_value ()
417 {
418 	send_int_to_srv (CMD_GET_MIXER);
419 	return get_data_int ();
420 }
421 
get_state()422 static int get_state ()
423 {
424 	send_int_to_srv (CMD_GET_STATE);
425 	return get_data_int ();
426 }
427 
get_channels()428 static int get_channels ()
429 {
430 	send_int_to_srv (CMD_GET_CHANNELS);
431 	return get_data_int ();
432 }
433 
get_rate()434 static int get_rate ()
435 {
436 	send_int_to_srv (CMD_GET_RATE);
437 	return get_data_int ();
438 }
439 
get_bitrate()440 static int get_bitrate ()
441 {
442 	send_int_to_srv (CMD_GET_BITRATE);
443 	return get_data_int ();
444 }
445 
get_avg_bitrate()446 static int get_avg_bitrate ()
447 {
448 	send_int_to_srv (CMD_GET_AVG_BITRATE);
449 	return get_data_int ();
450 }
451 
get_curr_time()452 static int get_curr_time ()
453 {
454 	send_int_to_srv (CMD_GET_CTIME);
455 	return get_data_int ();
456 }
457 
get_curr_file()458 static char *get_curr_file ()
459 {
460 	send_int_to_srv (CMD_GET_SNAME);
461 	return get_data_str ();
462 }
463 
update_mixer_value()464 static void update_mixer_value ()
465 {
466 	int val;
467 
468 	val = get_mixer_value ();
469 	iface_set_mixer_value (MAX(val, 0));
470 }
471 
update_mixer_name()472 static void update_mixer_name ()
473 {
474 	char *name;
475 
476 	send_int_to_srv (CMD_GET_MIXER_CHANNEL_NAME);
477 	name = get_data_str();
478 	debug ("Mixer name: %s", name);
479 
480 	iface_set_mixer_name (name);
481 
482 	free (name);
483 
484 	update_mixer_value ();
485 }
486 
487 /* Make new cwd path from CWD and this path. */
set_cwd(const char * path)488 static void set_cwd (const char *path)
489 {
490 	if (path[0] == '/')
491 		strcpy (cwd, "/"); /* for absolute path */
492 	else if (!cwd[0]) {
493 		if (!getcwd(cwd, sizeof(cwd)))
494 			fatal ("Can't get CWD: %s", strerror(errno));
495 	}
496 
497 	resolve_path (cwd, sizeof(cwd), path);
498 }
499 
500 /* Try to find the directory we can start and set cwd to it. */
set_start_dir()501 static void set_start_dir ()
502 {
503 	if (!getcwd(cwd, sizeof (cwd))) {
504 		if (errno == ERANGE)
505 			fatal ("CWD is larger than PATH_MAX!");
506 		strncpy (cwd, get_home (), sizeof (cwd));
507 		if (cwd[sizeof (cwd) - 1])
508 			fatal ("Home directory path is longer than PATH_MAX!");
509 	}
510 }
511 
512 /* Set cwd to last directory written to a file, return 1 on success. */
read_last_dir()513 static int read_last_dir ()
514 {
515 	FILE *dir_file;
516 	int res = 1;
517 	int read;
518 
519 	if (!(dir_file = fopen(create_file_name("last_directory"), "r")))
520 		return 0;
521 
522 	if ((read = fread(cwd, sizeof(char), sizeof(cwd)-1, dir_file)) == 0)
523 		res = 0;
524 	else
525 		cwd[read] = 0;
526 
527 	fclose (dir_file);
528 	return res;
529 }
530 
531 /* Check if dir2 is in dir1. */
is_subdir(const char * dir1,const char * dir2)532 static int is_subdir (const char *dir1, const char *dir2)
533 {
534 	return !strncmp(dir1, dir2, strlen(dir1)) ? 1 : 0;
535 }
536 
sort_strcmp_func(const void * a,const void * b)537 static int sort_strcmp_func (const void *a, const void *b)
538 {
539 	return strcoll (*(char **)a, *(char **)b);
540 }
541 
sort_dirs_func(const void * a,const void * b)542 static int sort_dirs_func (const void *a, const void *b)
543 {
544 	char *sa = *(char **)a;
545 	char *sb = *(char **)b;
546 
547 	/* '../' is always first */
548 	if (!strcmp(sa, "../"))
549 		return -1;
550 	if (!strcmp(sb, "../"))
551 		return 1;
552 
553 	return strcoll (sa, sb);
554 }
555 
get_tags_setting()556 static int get_tags_setting ()
557 {
558 	int needed_tags = 0;
559 
560 	if (options_get_int("ReadTags"))
561 		needed_tags |= TAGS_COMMENTS;
562 	if (!strcasecmp(options_get_str("ShowTime"), "yes"))
563 		needed_tags |= TAGS_TIME;
564 
565 	return needed_tags;
566 }
567 
568 /* For each file in the playlist, send a request for all the given tags if
569  * the file is missing any of those tags.  Return the number of requests. */
ask_for_tags(const struct plist * plist,const int tags_sel)570 static int ask_for_tags (const struct plist *plist, const int tags_sel)
571 {
572 	int i;
573 	int req = 0;
574 
575 	assert (plist != NULL);
576 
577 	if (tags_sel != 0) {
578 		for (i = 0; i < plist->num; i++) {
579 			if (!plist_deleted(plist, i) &&
580 			    (!plist->items[i].tags ||
581 			     ~plist->items[i].tags->filled & tags_sel)) {
582 				char *file;
583 
584 				file = plist_get_file (plist, i);
585 				req += send_tags_request (file, tags_sel);
586 				free (file);
587 			}
588 		}
589 	}
590 
591 	return req;
592 }
593 
interface_message(const char * format,...)594 static void interface_message (const char *format, ...)
595 {
596 	va_list va;
597 	char *msg;
598 
599 	va_start (va, format);
600 	msg = format_msg_va (format, va);
601 	va_end (va);
602 
603 	iface_message (msg);
604 
605 	free (msg);
606 }
607 
608 /* Update tags (and titles) for the given item on the playlist with new tags. */
update_item_tags(struct plist * plist,const int num,const struct file_tags * tags)609 static void update_item_tags (struct plist *plist, const int num,
610 		const struct file_tags *tags)
611 {
612 	struct file_tags *old_tags = plist_get_tags (plist, num);
613 
614 	plist_set_tags (plist, num, tags);
615 
616 	/* Get the time from the old tags if it's not present in the new tags.
617 	 * FIXME: There is risk, that the file was modified and the time
618 	 * from the old tags is not valid. */
619 	if (!(tags->filled & TAGS_TIME) && old_tags && old_tags->time != -1)
620 		plist_set_item_time (plist, num, old_tags->time);
621 
622 	if (plist->items[num].title_tags) {
623 		free (plist->items[num].title_tags);
624 		plist->items[num].title_tags = NULL;
625 	}
626 
627 	make_tags_title (plist, num);
628 
629 	if (options_get_int ("ReadTags") && !plist->items[num].title_tags) {
630 		if (!plist->items[num].title_file)
631 			make_file_title (plist, num, options_get_int ("HideFileExtension"));
632 	}
633 
634 	if (old_tags)
635 		tags_free (old_tags);
636 }
637 
638 /* Truncate string at screen-upsetting whitespace. */
sanitise_string(char * str)639 static void sanitise_string (char *str)
640 {
641 	if (!str)
642 		return;
643 
644 	while (*str) {
645 		if (*str != ' ' && isspace (*str)) {
646 			*str = 0x00;
647 			break;
648 		}
649 		str++;
650 	}
651 }
652 
653 /* Handle EV_FILE_TAGS. */
ev_file_tags(const struct tag_ev_response * data)654 static void ev_file_tags (const struct tag_ev_response *data)
655 {
656 	int n;
657 
658 	assert (data != NULL);
659 	assert (data->file != NULL);
660 	assert (data->tags != NULL);
661 
662 	debug ("Received tags for %s", data->file);
663 
664 	sanitise_string (data->tags->title);
665 	sanitise_string (data->tags->artist);
666 	sanitise_string (data->tags->album);
667 
668 	if ((n = plist_find_fname(dir_plist, data->file)) != -1) {
669 		update_item_tags (dir_plist, n, data->tags);
670 		iface_update_item (IFACE_MENU_DIR, dir_plist, n);
671 	}
672 
673 	if ((n = plist_find_fname(playlist, data->file)) != -1) {
674 		update_item_tags (playlist, n, data->tags);
675 		iface_update_item (IFACE_MENU_PLIST, playlist, n);
676 	}
677 
678 	if (curr_file.file && !strcmp(data->file, curr_file.file)) {
679 
680 		debug ("Tags apply to the currently played file.");
681 
682 		if (data->tags->time != -1) {
683 			curr_file.total_time = data->tags->time;
684 			iface_set_total_time (curr_file.total_time);
685 			if (file_type (curr_file.file) == F_SOUND) {
686 				if (!curr_file.block_file) {
687 					curr_file.block_file = xstrdup(curr_file.file);
688 					curr_file.block_start = 0;
689 					curr_file.block_end = curr_file.total_time;
690 				}
691 				iface_set_block (curr_file.block_start, curr_file.block_end);
692 			}
693 		}
694 		else
695 			debug ("No time information");
696 
697 		if (data->tags->title) {
698 			if (curr_file.title)
699 				free (curr_file.title);
700 			curr_file.title = build_title (data->tags);
701 			iface_set_played_file_title (curr_file.title);
702 		}
703 
704 		if (curr_file.tags)
705 			tags_free (curr_file.tags);
706 		curr_file.tags = tags_dup (data->tags);
707 	}
708 }
709 
710 /* Update the current time. */
update_ctime()711 static void update_ctime ()
712 {
713 	curr_file.curr_time = get_curr_time ();
714 	if (silent_seek_pos == -1)
715 		iface_set_curr_time (curr_file.curr_time);
716 }
717 
718 /* Use new tags for current file title (for Internet streams). */
update_curr_tags()719 static void update_curr_tags ()
720 {
721 	if (curr_file.file && is_url(curr_file.file)) {
722 		if (curr_file.tags)
723 			tags_free (curr_file.tags);
724 		send_int_to_srv (CMD_GET_TAGS);
725 		curr_file.tags = get_data_tags ();
726 
727 		if (curr_file.tags->title) {
728 			if (curr_file.title)
729 				free (curr_file.title);
730 			curr_file.title = build_title (curr_file.tags);
731 			iface_set_played_file_title (curr_file.title);
732 		}
733 	}
734 }
735 
736 /* Make sure that the currently played file is visible if it is in one of our
737  * menus. */
follow_curr_file()738 static void follow_curr_file ()
739 {
740 	if (curr_file.file && file_type(curr_file.file) == F_SOUND
741 			&& last_menu_move_time <= time(NULL) - 2) {
742 		int server_plist_serial = get_server_plist_serial();
743 
744 		if (server_plist_serial == plist_get_serial(playlist))
745 			iface_make_visible (IFACE_MENU_PLIST, curr_file.file);
746 		else if (server_plist_serial == plist_get_serial(dir_plist))
747 			iface_make_visible (IFACE_MENU_DIR, curr_file.file);
748 		else
749 			logit ("Not my playlist.");
750 	}
751 }
752 
update_curr_file()753 static void update_curr_file ()
754 {
755 	char *file;
756 
757 	file = get_curr_file ();
758 
759 	if (!file[0] || curr_file.state == STATE_STOP) {
760 
761 		/* Nothing is played/paused. */
762 
763 		file_info_cleanup (&curr_file);
764 		file_info_reset (&curr_file);
765 		iface_set_played_file (NULL);
766 		iface_load_lyrics (NULL);
767 		free (file);
768 	}
769 	else if (file[0] &&
770 			(!curr_file.file || strcmp(file, curr_file.file))) {
771 
772 		/* played file has changed */
773 
774 		file_info_cleanup (&curr_file);
775 		if (curr_file.block_file && strcmp(file, curr_file.block_file))
776 			file_info_block_reset (&curr_file);
777 
778 		/* The total time could not get reset. */
779 		iface_set_total_time (-1);
780 
781 		iface_set_played_file (file);
782 		send_tags_request (file, TAGS_COMMENTS | TAGS_TIME);
783 		curr_file.file = file;
784 
785 		/* make a title that will be used until we get tags */
786 		if (file_type(file) == F_URL || !strchr(file, '/')) {
787 			curr_file.title = xstrdup (file);
788 			update_curr_tags ();
789 		}
790 		else
791 		{
792 			if (options_get_int ("FileNamesIconv"))
793 			{
794 				curr_file.title = files_iconv_str (
795 					strrchr(file, '/') + 1);
796 			}
797 			else
798 			{
799 				curr_file.title = xstrdup (
800 					strrchr(file, '/') + 1);
801 			}
802 		}
803 
804 		iface_set_played_file (file);
805 		iface_set_played_file_title (curr_file.title);
806 		/* Try to load the lyrics of the new file. */
807 		iface_load_lyrics (file);
808 		/* Silent seeking makes no sense if the playing file has changed. */
809 		silent_seek_pos = -1;
810 		iface_set_curr_time (curr_file.curr_time);
811 
812 		if (options_get_int("FollowPlayedFile"))
813 			follow_curr_file ();
814 	}
815 	else
816 		free (file);
817 }
818 
update_rate()819 static void update_rate ()
820 {
821 	curr_file.rate = get_rate ();
822 	iface_set_rate (curr_file.rate);
823 }
824 
update_channels()825 static void update_channels ()
826 {
827 	curr_file.channels = get_channels () == 2 ? 2 : 1;
828 	iface_set_channels (curr_file.channels);
829 }
830 
update_bitrate()831 static void update_bitrate ()
832 {
833 	curr_file.bitrate = get_bitrate ();
834 	iface_set_bitrate (curr_file.bitrate);
835 }
836 
837 /* Get and show the server state. */
update_state()838 static void update_state ()
839 {
840 	int old_state = curr_file.state;
841 
842 	/* play | stop | pause */
843 	curr_file.state = get_state ();
844 	iface_set_state (curr_file.state);
845 
846 	/* Silent seeking makes no sense if the state has changed. */
847 	if (old_state != curr_file.state)
848 		silent_seek_pos = -1;
849 
850 	update_curr_file ();
851 
852 	update_channels ();
853 	update_bitrate ();
854 	update_rate ();
855 	update_ctime ();
856 }
857 
858 /* Handle EV_PLIST_ADD. */
event_plist_add(const struct plist_item * item)859 static void event_plist_add (const struct plist_item *item)
860 {
861 	if (plist_find_fname(playlist, item->file) == -1) {
862 		int item_num = plist_add_from_item (playlist, item);
863 		int needed_tags = 0;
864 		int i;
865 
866 		if (options_get_int("ReadTags")
867 				&& (!item->tags || !item->tags->title))
868 			needed_tags |= TAGS_COMMENTS;
869 		if (!strcasecmp(options_get_str("ShowTime"), "yes")
870 				&& (!item->tags || item->tags->time == -1))
871 			needed_tags |= TAGS_TIME;
872 
873 		if (needed_tags)
874 			send_tags_request (item->file, needed_tags);
875 
876 		if (options_get_int ("ReadTags"))
877 			make_tags_title (playlist, item_num);
878 		else
879 			make_file_title (playlist, item_num,
880 					options_get_int ("HideFileExtension"));
881 
882 		/* Just calling iface_update_queue_positions (queue, playlist,
883 		 * NULL, NULL) is too slow in cases when we receive a large
884 		 * number of items from server (e.g., loading playlist w/
885 		 * SyncPlaylist on).  Since we know the filename in question,
886 		 * we try to find it in queue and eventually update the value.
887 		 */
888 		if ((i = plist_find_fname(queue, item->file)) != -1) {
889 			playlist->items[item_num].queue_pos
890 				= plist_get_position (queue, i);
891 		}
892 
893 		iface_add_to_plist (playlist, item_num);
894 
895 		if (waiting_for_plist_load) {
896 			if (iface_in_dir_menu())
897 				iface_switch_to_plist ();
898 			waiting_for_plist_load = 0;
899 		}
900 	}
901 }
902 
903 /* Handle EV_QUEUE_ADD. */
event_queue_add(const struct plist_item * item)904 static void event_queue_add (const struct plist_item *item)
905 {
906 	if (plist_find_fname(queue, item->file) == -1) {
907 		plist_add_from_item (queue, item);
908 		iface_set_files_in_queue (plist_count(queue));
909 		iface_update_queue_position_last (queue, playlist, dir_plist);
910 		logit ("Adding %s to queue", item->file);
911 	}
912 	else
913 		logit ("Adding file already present in queue");
914 }
915 
916 /* Get error message from the server and show it. */
update_error(char * err)917 static void update_error (char *err)
918 {
919 	error ("%s", err);
920 }
921 
922 /* Send the playlist to the server to be forwarded to another client. */
forward_playlist()923 static void forward_playlist ()
924 {
925 	int i;
926 
927 	debug ("Forwarding the playlist...");
928 
929 	send_int_to_srv (CMD_SEND_PLIST);
930 	send_int_to_srv (plist_get_serial(playlist));
931 
932 	for (i = 0; i < playlist->num; i++)
933 		if (!plist_deleted(playlist, i))
934 			send_item_to_srv (&playlist->items[i]);
935 
936 	send_item_to_srv (NULL);
937 }
938 
recv_server_plist(struct plist * plist)939 static int recv_server_plist (struct plist *plist)
940 {
941 	int end_of_list = 0;
942 	struct plist_item *item;
943 
944 	logit ("Asking server for the playlist from other client.");
945 	send_int_to_srv (CMD_GET_PLIST);
946 	logit ("Waiting for response");
947 	wait_for_data ();
948 
949 	if (!get_int_from_srv()) {
950 		debug ("There is no playlist");
951 		return 0; /* there are no other clients with a playlist */
952 	}
953 
954 	logit ("There is a playlist, getting...");
955 	wait_for_data ();
956 
957 	logit ("Transfer...");
958 
959 	plist_set_serial (plist, get_int_from_srv());
960 
961 	do {
962 		item = recv_item_from_srv ();
963 		if (item->file[0])
964 			plist_add_from_item (plist, item);
965 		else
966 			end_of_list = 1;
967 		plist_free_item_fields (item);
968 		free (item);
969 	} while (!end_of_list);
970 
971 	return 1;
972 }
973 
recv_server_queue(struct plist * queue)974 static void recv_server_queue (struct plist *queue)
975 {
976 	int end_of_list = 0;
977 	struct plist_item *item;
978 
979 	logit ("Asking server for the queue.");
980 	send_int_to_srv (CMD_GET_QUEUE);
981 	logit ("Waiting for response");
982 	wait_for_data (); /* There must always be (possibly empty) queue. */
983 
984 	do {
985 		item = recv_item_from_srv ();
986 		if (item->file[0])
987 			plist_add_from_item (queue, item);
988 		else
989 			end_of_list = 1;
990 		plist_free_item_fields (item);
991 		free (item);
992 	} while (!end_of_list);
993 }
994 
995 /* Clear the playlist locally. */
clear_playlist()996 static void clear_playlist ()
997 {
998 	if (iface_in_plist_menu())
999 		iface_switch_to_dir ();
1000 	plist_clear (playlist);
1001 	iface_clear_plist ();
1002 
1003 	if (!waiting_for_plist_load)
1004 		interface_message ("The playlist was cleared.");
1005 	iface_set_status ("");
1006 }
1007 
clear_queue()1008 static void clear_queue ()
1009 {
1010 	iface_clear_queue_positions (queue, playlist, dir_plist);
1011 
1012 	plist_clear (queue);
1013 	iface_set_files_in_queue (0);
1014 
1015 	interface_message ("The queue was cleared.");
1016 }
1017 
1018 /* Handle EV_PLIST_DEL. */
event_plist_del(char * file)1019 static void event_plist_del (char *file)
1020 {
1021 	int item = plist_find_fname (playlist, file);
1022 
1023 	if (item != -1) {
1024 		char *file;
1025 		int have_all_times;
1026 		int playlist_total_time;
1027 
1028 		file = plist_get_file (playlist, item);
1029 		plist_delete (playlist, item);
1030 
1031 		iface_del_plist_item (file);
1032 		playlist_total_time = plist_total_time (playlist,
1033 				&have_all_times);
1034 		iface_plist_set_total_time (playlist_total_time,
1035 				have_all_times);
1036 		free (file);
1037 
1038 		if (plist_count(playlist) == 0)
1039 			clear_playlist ();
1040 	}
1041 	else
1042 		logit ("Server requested deleting an item not present on the"
1043 				" playlist.");
1044 }
1045 
1046 /* Handle EV_QUEUE_DEL. */
event_queue_del(char * file)1047 static void event_queue_del (char *file)
1048 {
1049 	int item = plist_find_fname (queue, file);
1050 
1051 	if (item != -1) {
1052 		plist_delete (queue, item);
1053 
1054 		/* Free the deleted items occasionally.
1055 		 * QUEUE_CLEAR_THRESH is chosen to be two times
1056 		 * the initial size of the playlist. */
1057 		if (plist_count(queue) == 0
1058 				&& queue->num >= QUEUE_CLEAR_THRESH)
1059 			plist_clear (queue);
1060 
1061 		iface_set_files_in_queue (plist_count(queue));
1062 		iface_update_queue_positions (queue, playlist, dir_plist, file);
1063 		logit ("Deleting %s from queue", file);
1064 	}
1065 	else
1066 		logit ("Deleting an item not present in the queue");
1067 
1068 }
1069 
1070 /* Swap 2 file on the playlist. */
swap_playlist_items(const char * file1,const char * file2)1071 static void swap_playlist_items (const char *file1, const char *file2)
1072 {
1073 	assert (file1 != NULL);
1074 	assert (file2 != NULL);
1075 
1076 	plist_swap_files (playlist, file1, file2);
1077 	iface_swap_plist_items (file1, file2);
1078 }
1079 
1080 /* Handle EV_PLIST_MOVE. */
event_plist_move(const struct move_ev_data * d)1081 static void event_plist_move (const struct move_ev_data *d)
1082 {
1083 	assert (d != NULL);
1084 	assert (d->from != NULL);
1085 	assert (d->to != NULL);
1086 
1087 	swap_playlist_items (d->from, d->to);
1088 }
1089 
1090 /* Handle EV_QUEUE_MOVE. */
event_queue_move(const struct move_ev_data * d)1091 static void event_queue_move (const struct move_ev_data *d)
1092 {
1093 	assert (d != NULL);
1094 	assert (d->from != NULL);
1095 	assert (d->to != NULL);
1096 
1097 	plist_swap_files (queue, d->from, d->to);
1098 }
1099 
1100 /* Handle server event. */
server_event(const int event,void * data)1101 static void server_event (const int event, void *data)
1102 {
1103 	logit ("EVENT: 0x%02x", event);
1104 
1105 	switch (event) {
1106 		case EV_BUSY:
1107 			interface_fatal ("The server is busy; "
1108 			                 "another client is connected!");
1109 			break;
1110 		case EV_CTIME:
1111 			update_ctime ();
1112 			break;
1113 		case EV_STATE:
1114 			update_state ();
1115 			break;
1116 		case EV_EXIT:
1117 			interface_fatal ("The server exited!");
1118 			break;
1119 		case EV_BITRATE:
1120 			update_bitrate ();
1121 			break;
1122 		case EV_RATE:
1123 			update_rate ();
1124 			break;
1125 		case EV_CHANNELS:
1126 			update_channels ();
1127 			break;
1128 		case EV_SRV_ERROR:
1129 			update_error ((char *)data);
1130 			break;
1131 		case EV_OPTIONS:
1132 			get_server_options ();
1133 			break;
1134 		case EV_SEND_PLIST:
1135 			forward_playlist ();
1136 			break;
1137 		case EV_PLIST_ADD:
1138 			if (options_get_int("SyncPlaylist"))
1139 				event_plist_add ((struct plist_item *)data);
1140 			break;
1141 		case EV_PLIST_CLEAR:
1142 			if (options_get_int("SyncPlaylist"))
1143 				clear_playlist ();
1144 			break;
1145 		case EV_PLIST_DEL:
1146 			if (options_get_int("SyncPlaylist"))
1147 				event_plist_del ((char *)data);
1148 			break;
1149 		case EV_PLIST_MOVE:
1150 			if (options_get_int("SyncPlaylist"))
1151 				event_plist_move ((struct move_ev_data *)data);
1152 			break;
1153 		case EV_TAGS:
1154 			update_curr_tags ();
1155 			break;
1156 		case EV_STATUS_MSG:
1157 			iface_set_status ((char *)data);
1158 			break;
1159 		case EV_MIXER_CHANGE:
1160 			update_mixer_name ();
1161 			break;
1162 		case EV_FILE_TAGS:
1163 			ev_file_tags ((struct tag_ev_response *)data);
1164 			break;
1165 		case EV_AVG_BITRATE:
1166 			curr_file.avg_bitrate = get_avg_bitrate ();
1167 			break;
1168 		case EV_QUEUE_ADD:
1169 			event_queue_add ((struct plist_item *)data);
1170 			break;
1171 		case EV_QUEUE_DEL:
1172 			event_queue_del ((char *)data);
1173 			break;
1174 		case EV_QUEUE_CLEAR:
1175 			clear_queue ();
1176 			break;
1177 		case EV_QUEUE_MOVE:
1178 			event_queue_move ((struct move_ev_data *)data);
1179 			break;
1180 		case EV_AUDIO_START:
1181 			break;
1182 		case EV_AUDIO_STOP:
1183 			break;
1184 		default:
1185 			interface_fatal ("Unknown event: 0x%02x!", event);
1186 	}
1187 
1188 	free_event_data (event, data);
1189 }
1190 
1191 /* Send requests for the given tags for every file on the playlist and wait
1192  * for all responses. If no_iface has non-zero value, it will not access the
1193  * interface. */
fill_tags(struct plist * plist,const int tags_sel,const int no_iface)1194 static void fill_tags (struct plist *plist, const int tags_sel,
1195 		const int no_iface)
1196 {
1197 	int files;
1198 
1199 	assert (plist != NULL);
1200 	assert (tags_sel != 0);
1201 
1202 	iface_set_status ("Reading tags...");
1203 	files = ask_for_tags (plist, tags_sel);
1204 
1205 	/* Process events until we have all tags. */
1206 	while (files && !user_wants_interrupt()) {
1207 		int type;
1208 		void *data;
1209 
1210 		/* Event queue is not initialized if there is no interface. */
1211 		if (!no_iface && !event_queue_empty (&events)) {
1212 			struct event e = *event_get_first (&events);
1213 
1214 			type = e.type;
1215 			data = e.data;
1216 
1217 			event_pop (&events);
1218 
1219 		}
1220 		else {
1221 			type = get_int_from_srv ();
1222 			data = get_event_data (type);
1223 		}
1224 
1225 		if (type == EV_FILE_TAGS) {
1226 			struct tag_ev_response *ev
1227 				= (struct tag_ev_response *)data;
1228 			int n;
1229 
1230 			if ((n = plist_find_fname(plist, ev->file)) != -1) {
1231 				if ((ev->tags->filled & tags_sel))
1232 					files--;
1233 				update_item_tags (plist, n, ev->tags);
1234 			}
1235 		}
1236 		else if (no_iface)
1237 			abort (); /* can't handle other events without the interface */
1238 
1239 		if (!no_iface)
1240 			server_event (type, data);
1241 		else
1242 			free_event_data (type, data);
1243 	}
1244 
1245 	iface_set_status ("");
1246 }
1247 
1248 /* Load the directory content into dir_plist and switch the menu to it.
1249  * If dir is NULL, go to the cwd.  If reload is not zero, we are reloading
1250  * the current directory, so use iface_update_dir_content().
1251  * Return 1 on success, 0 on error. */
go_to_dir(const char * dir,const int reload)1252 static int go_to_dir (const char *dir, const int reload)
1253 {
1254 	struct plist *old_dir_plist;
1255 	char last_dir[PATH_MAX];
1256 	const char *new_dir = dir ? dir : cwd;
1257 	int going_up = 0;
1258 	lists_t_strs *dirs, *playlists;
1259 
1260 	iface_set_status ("Reading directory...");
1261 
1262 	if (dir && is_subdir(dir, cwd)) {
1263 		strcpy (last_dir, strrchr(cwd, '/') + 1);
1264 		strcat (last_dir, "/");
1265 		going_up = 1;
1266 	}
1267 
1268 	old_dir_plist = dir_plist;
1269 	dir_plist = (struct plist *)xmalloc (sizeof(struct plist));
1270 	plist_init (dir_plist);
1271 	dirs = lists_strs_new (FILES_LIST_INIT_SIZE);
1272 	playlists = lists_strs_new (FILES_LIST_INIT_SIZE);
1273 
1274 	if (!read_directory(new_dir, dirs, playlists, dir_plist)) {
1275 		iface_set_status ("");
1276 		plist_free (dir_plist);
1277 		lists_strs_free (dirs);
1278 		lists_strs_free (playlists);
1279 		free (dir_plist);
1280 		dir_plist = old_dir_plist;
1281 		return 0;
1282 	}
1283 
1284 	/* TODO: use CMD_ABORT_TAGS_REQUESTS (what if we requested tags for the
1285 	 playlist?) */
1286 
1287 	plist_free (old_dir_plist);
1288 	free (old_dir_plist);
1289 
1290 	if (dir) /* if dir is NULL, we went to cwd */
1291 		strcpy (cwd, dir);
1292 
1293 	switch_titles_file (dir_plist);
1294 
1295 	plist_sort_fname (dir_plist);
1296 	lists_strs_sort (dirs, sort_dirs_func);
1297 	lists_strs_sort (playlists, sort_strcmp_func);
1298 
1299 	ask_for_tags (dir_plist, get_tags_setting());
1300 
1301 	if (reload)
1302 		iface_update_dir_content (IFACE_MENU_DIR, dir_plist, dirs, playlists);
1303 	else
1304 		iface_set_dir_content (IFACE_MENU_DIR, dir_plist, dirs, playlists);
1305 	lists_strs_free (dirs);
1306 	lists_strs_free (playlists);
1307 	if (going_up)
1308 		iface_set_curr_item_title (last_dir);
1309 
1310 	iface_set_title (IFACE_MENU_DIR, cwd);
1311 	iface_update_queue_positions (queue, NULL, dir_plist, NULL);
1312 
1313 	if (iface_in_plist_menu())
1314 		iface_switch_to_dir ();
1315 
1316 	return 1;
1317 }
1318 
1319 /* Make sure that the server's playlist has different serial from ours. */
change_srv_plist_serial()1320 static void change_srv_plist_serial ()
1321 {
1322 	int serial;
1323 
1324 	do {
1325 		send_int_to_srv (CMD_GET_SERIAL);
1326 		serial = get_data_int ();
1327 	 } while (serial == plist_get_serial(playlist) ||
1328 	          serial == plist_get_serial(dir_plist));
1329 
1330 	send_int_to_srv (CMD_PLIST_SET_SERIAL);
1331 	send_int_to_srv (serial);
1332 }
1333 
1334 static void enter_first_dir ();
1335 
1336 /* Switch between the directory view and the playlist. */
toggle_menu()1337 static void toggle_menu ()
1338 {
1339 	int num;
1340 
1341 	if (iface_in_plist_menu()) {
1342 		if (!cwd[0])
1343 			/* we were at the playlist from the startup */
1344 			enter_first_dir ();
1345 		else
1346 			iface_switch_to_dir ();
1347 	}
1348 	else if ((num = plist_count(playlist)))
1349 		iface_switch_to_plist ();
1350 	else
1351 		error ("The playlist is empty.");
1352 }
1353 
1354 /* Load the playlist file and switch the menu to it. Return 1 on success. */
go_to_playlist(const char * file,const int load_serial,bool default_playlist)1355 static int go_to_playlist (const char *file, const int load_serial,
1356                            bool default_playlist)
1357 {
1358 	if (plist_count(playlist)) {
1359 		error ("Please clear the playlist, because "
1360 				"I'm not sure you want to do this.");
1361 		return 0;
1362 	}
1363 
1364 	plist_clear (playlist);
1365 
1366 	iface_set_status ("Loading playlist...");
1367 	if (plist_load(playlist, file, cwd, load_serial)) {
1368 
1369 		if (options_get_int("SyncPlaylist")) {
1370 			send_int_to_srv (CMD_LOCK);
1371 			if (!load_serial)
1372 				change_srv_plist_serial ();
1373 			send_int_to_srv (CMD_CLI_PLIST_CLEAR);
1374 			iface_set_status ("Notifying clients...");
1375 			send_items_to_clients (playlist);
1376 			iface_set_status ("");
1377 			waiting_for_plist_load = 1;
1378 			send_int_to_srv (CMD_UNLOCK);
1379 
1380 			/* We'll use the playlist received from the
1381 			 * server to be synchronized with other clients.
1382 			 */
1383 			plist_clear (playlist);
1384 		}
1385 		else {
1386 			if (!default_playlist)
1387 				toggle_menu ();
1388 			iface_set_dir_content (IFACE_MENU_PLIST, playlist, NULL, NULL);
1389 			iface_update_queue_positions (queue, playlist, NULL, NULL);
1390 		}
1391 
1392 		interface_message ("Playlist loaded.");
1393 	}
1394 	else {
1395 		interface_message ("The playlist is empty");
1396 		iface_set_status ("");
1397 		return 0;
1398 	}
1399 
1400 	return 1;
1401 }
1402 
1403 /* Enter to the initial directory or toggle to the initial playlist (only
1404  * if the function has not been called yet). */
enter_first_dir()1405 static void enter_first_dir ()
1406 {
1407 	static int first_run = 1;
1408 
1409 	if (options_get_int("StartInMusicDir")) {
1410 		char *music_dir;
1411 
1412 		if ((music_dir = options_get_str("MusicDir"))) {
1413 			set_cwd (music_dir);
1414 			if (first_run && file_type(music_dir) == F_PLAYLIST
1415 					&& plist_count(playlist) == 0
1416 					&& go_to_playlist(music_dir, 0, false)) {
1417 				cwd[0] = 0;
1418 				first_run = 0;
1419 			}
1420 			else if (file_type(cwd) == F_DIR
1421 					&& go_to_dir(NULL, 0)) {
1422 				first_run = 0;
1423 				return;
1424 			}
1425 		}
1426 		else
1427 			error ("MusicDir is not set");
1428 	}
1429 
1430 	if (!(read_last_dir() && go_to_dir(NULL, 0))) {
1431 		set_start_dir ();
1432 		if (!go_to_dir(NULL, 0))
1433 			interface_fatal ("Can't enter any directory!");
1434 	}
1435 
1436 	first_run = 0;
1437 }
1438 
1439 /* Request the playlist from the server (given by another client).  Make
1440  * the titles.  Return 0 if such a list doesn't exist. */
get_server_playlist(struct plist * plist)1441 static int get_server_playlist (struct plist *plist)
1442 {
1443 	iface_set_status ("Getting the playlist...");
1444 	debug ("Getting the playlist...");
1445 	if (recv_server_plist(plist)) {
1446 		ask_for_tags (plist, get_tags_setting());
1447 		if (options_get_bool ("ReadTags"))
1448 			switch_titles_tags (plist);
1449 		else
1450 			switch_titles_file (plist);
1451 		iface_set_status ("");
1452 		return 1;
1453 	}
1454 
1455 	iface_set_status ("");
1456 
1457 	return 0;
1458 }
1459 
1460 /* Get the playlist from another client and use it as our playlist.
1461  * Return 0 if there is no client with a playlist. */
use_server_playlist()1462 static int use_server_playlist ()
1463 {
1464 	if (get_server_playlist(playlist)) {
1465 		iface_set_dir_content (IFACE_MENU_PLIST, playlist, NULL, NULL);
1466 		iface_update_queue_positions (queue, playlist, NULL, NULL);
1467 		return 1;
1468 	}
1469 
1470 	return 0;
1471 }
1472 
use_server_queue()1473 static void use_server_queue ()
1474 {
1475 	iface_set_status ("Getting the queue...");
1476 	debug ("Getting the queue...");
1477 
1478 	recv_server_queue(queue);
1479 	iface_set_files_in_queue (plist_count(queue));
1480 	iface_update_queue_positions (queue, playlist, dir_plist, NULL);
1481 	iface_set_status ("");
1482 }
1483 
1484 /* Process a single directory argument. */
process_dir_arg(const char * dir)1485 static void process_dir_arg (const char *dir)
1486 {
1487 	set_cwd (dir);
1488 	if (!go_to_dir (NULL, 0))
1489 		enter_first_dir ();
1490 }
1491 
1492 /* Process a single playlist argument. */
process_plist_arg(const char * file)1493 static void process_plist_arg (const char *file)
1494 {
1495 	char path[PATH_MAX + 1];   /* the playlist's directory */
1496 	char *slash;
1497 
1498 	if (file[0] == '/')
1499 		strcpy (path, "/");
1500 	else if (!getcwd (path, sizeof (path)))
1501 		interface_fatal ("Can't get CWD: %s", strerror (errno));
1502 
1503 	resolve_path (path, sizeof (path), file);
1504 	slash = strrchr (path, '/');
1505 	assert (slash != NULL);
1506 	*slash = 0;
1507 
1508 	iface_set_status ("Loading playlist...");
1509 	plist_load (playlist, file, path, 0);
1510 	iface_set_status ("");
1511 }
1512 
1513 /* Process a list of arguments. */
process_multiple_args(lists_t_strs * args)1514 static void process_multiple_args (lists_t_strs *args)
1515 {
1516 	int size, ix;
1517 	const char *arg;
1518 	char this_cwd[PATH_MAX];
1519 
1520 	if (!getcwd (this_cwd, sizeof (cwd)))
1521 		interface_fatal ("Can't get CWD: %s", strerror (errno));
1522 
1523 	size = lists_strs_size (args);
1524 
1525 	for (ix = 0; ix < size; ix += 1) {
1526 		int dir;
1527 		char path[2 * PATH_MAX];
1528 
1529 		arg = lists_strs_at (args, ix);
1530 		dir = is_dir (arg);
1531 
1532 		if (is_url (arg)) {
1533 			strncpy (path, arg, sizeof (path));
1534 			path[sizeof(path) - 1] = 0;
1535 		}
1536 		else {
1537 			if (arg[0] == '/')
1538 				strcpy (path, "/");
1539 			else
1540 				strcpy (path, this_cwd);
1541 			resolve_path (path, sizeof (path), arg);
1542 		}
1543 
1544 		if (dir == 1)
1545 			read_directory_recurr (path, playlist);
1546 		else if (!dir && (is_sound_file (path) || is_url (path))) {
1547 			if (plist_find_fname (playlist, path) == -1)
1548 				plist_add (playlist, path);
1549 		}
1550 		else if (is_plist_file (path)) {
1551 			char *plist_dir, *slash;
1552 
1553 			/* Here we've chosen to resolve the playlist's relative paths
1554 			 * with respect to the directory of the playlist (or of the
1555 			 * symlink being used to reference it).  If some other base is
1556 			 * desired, then we probably need to introduce a new option. */
1557 
1558 			plist_dir = xstrdup (path);
1559 			slash = strrchr (plist_dir, '/');
1560 			assert (slash != NULL);
1561 			*slash = 0;
1562 
1563 			plist_load (playlist, path, plist_dir, 0);
1564 
1565 			free (plist_dir);
1566 		}
1567 	}
1568 }
1569 
1570 /* Process file names passed as arguments. */
process_args(lists_t_strs * args)1571 static void process_args (lists_t_strs *args)
1572 {
1573 	int size;
1574 	const char *arg;
1575 
1576 	size = lists_strs_size (args);
1577 	arg = lists_strs_at (args, 0);
1578 
1579 	if (size == 1 && is_dir (arg) == 1) {
1580 		process_dir_arg (arg);
1581 		return;
1582 	}
1583 
1584 	if (size == 1 && is_plist_file (arg))
1585 		process_plist_arg (arg);
1586 	else
1587 		process_multiple_args (args);
1588 
1589 	if (plist_count (playlist) && !options_get_int ("SyncPlaylist")) {
1590 		switch_titles_file (playlist);
1591 		ask_for_tags (playlist, get_tags_setting ());
1592 		iface_set_dir_content (IFACE_MENU_PLIST, playlist, NULL, NULL);
1593 		iface_update_queue_positions (queue, playlist, NULL, NULL);
1594 		iface_switch_to_plist ();
1595 	}
1596 	else
1597 		enter_first_dir ();
1598 }
1599 
1600 /* Load the playlist from .moc directory. */
load_playlist()1601 static void load_playlist ()
1602 {
1603 	char *plist_file = create_file_name (PLAYLIST_FILE);
1604 
1605 	if (file_type(plist_file) == F_PLAYLIST) {
1606 		go_to_playlist (plist_file, 1, true);
1607 
1608 		/* We don't want to switch to the playlist after loading. */
1609 		waiting_for_plist_load = 0;
1610 	}
1611 }
1612 
1613 #ifdef SIGWINCH
1614 /* Handle resizing xterm. */
do_resize()1615 static void do_resize ()
1616 {
1617 	iface_resize ();
1618 	logit ("resize");
1619 	want_resize = 0;
1620 }
1621 #endif
1622 
1623 /* Strip the last directory from the path. Returned memory is mallod()ed. */
dir_up(const char * path)1624 static char *dir_up (const char *path)
1625 {
1626 	char *slash;
1627 	char *dir;
1628 
1629 	assert (path != NULL);
1630 
1631 	dir = xstrdup (path);
1632 	slash = strrchr (dir, '/');
1633 	assert (slash != NULL);
1634 	if (slash == dir)
1635 		*(slash + 1) = 0;
1636 	else
1637 		*slash = 0;
1638 
1639 	return dir;
1640 }
1641 
go_dir_up()1642 static void go_dir_up ()
1643 {
1644 	char *dir;
1645 
1646 	dir = dir_up (cwd);
1647 	go_to_dir (dir, 0);
1648 	free (dir);
1649 }
1650 
1651 /* Return a generated playlist serial from the server and make sure
1652  * it's not the same as our playlist's serial. */
get_safe_serial()1653 static int get_safe_serial ()
1654 {
1655 	int serial;
1656 
1657 	do {
1658 		send_int_to_srv (CMD_GET_SERIAL);
1659 		serial = get_data_int ();
1660 	} while (playlist &&
1661 			serial == plist_get_serial(playlist)); /* check only the
1662 							   playlist, because dir_plist has serial
1663 							   -1 */
1664 
1665 	return serial;
1666 }
1667 
1668 /* Send the playlist to the server. If clear != 0, clear the server's playlist
1669  * before sending. */
send_playlist(struct plist * plist,const int clear)1670 static void send_playlist (struct plist *plist, const int clear)
1671 {
1672 	int i;
1673 
1674 	if (clear)
1675 		send_int_to_srv (CMD_LIST_CLEAR);
1676 
1677 	for (i = 0; i < plist->num; i++) {
1678 		if (!plist_deleted(plist, i)) {
1679 			send_int_to_srv (CMD_LIST_ADD);
1680 			send_str_to_srv (plist->items[i].file);
1681 		}
1682 	}
1683 }
1684 
1685 /* Send the playlist to the server if necessary and request playing this
1686  * item. */
play_it(const char * file)1687 static void play_it (const char *file)
1688 {
1689 	struct plist *curr_plist;
1690 
1691 	assert (file != NULL);
1692 
1693 	if (iface_in_dir_menu())
1694 		curr_plist = dir_plist;
1695 	else
1696 		curr_plist = playlist;
1697 
1698 	send_int_to_srv (CMD_LOCK);
1699 
1700 	if (plist_get_serial(curr_plist) == -1 || get_server_plist_serial()
1701 			!= plist_get_serial(curr_plist)) {
1702 		int serial;
1703 
1704 		logit ("The server has different playlist");
1705 
1706 		serial = get_safe_serial();
1707 		plist_set_serial (curr_plist, serial);
1708 		send_int_to_srv (CMD_PLIST_SET_SERIAL);
1709 		send_int_to_srv (serial);
1710 
1711 		send_playlist (curr_plist, 1);
1712 	}
1713 	else
1714 		logit ("The server already has my playlist");
1715 	send_int_to_srv (CMD_PLAY);
1716 	send_str_to_srv (file);
1717 
1718 	send_int_to_srv (CMD_UNLOCK);
1719 }
1720 
1721 /* Action when the user selected a file. */
go_file()1722 static void go_file ()
1723 {
1724 	enum file_type type = iface_curritem_get_type ();
1725 	char *file = iface_get_curr_file ();
1726 
1727 	if (!file)
1728 		return;
1729 
1730 	if (type == F_SOUND || type == F_URL)
1731 		play_it (file);
1732 	else if (type == F_DIR && iface_in_dir_menu()) {
1733 		if (!strcmp(file, ".."))
1734 			go_dir_up ();
1735 		else
1736 			go_to_dir (file, 0);
1737 	}
1738 	else if (type == F_PLAYLIST)
1739 		go_to_playlist (file, 0, false);
1740 
1741 	free (file);
1742 }
1743 
1744 /* pause/unpause */
switch_pause()1745 static void switch_pause ()
1746 {
1747 	switch (curr_file.state) {
1748 		case STATE_PLAY:
1749 			send_int_to_srv (CMD_PAUSE);
1750 			break;
1751 		case STATE_PAUSE:
1752 			send_int_to_srv (CMD_UNPAUSE);
1753 			break;
1754 		default:
1755 			logit ("User pressed pause when not playing.");
1756 	}
1757 }
1758 
set_mixer(int val)1759 static void set_mixer (int val)
1760 {
1761 	val = CLAMP(0, val, 100);
1762 	send_int_to_srv (CMD_SET_MIXER);
1763 	send_int_to_srv (val);
1764 }
1765 
adjust_mixer(const int diff)1766 static void adjust_mixer (const int diff)
1767 {
1768 	set_mixer (get_mixer_value() + diff);
1769 }
1770 
1771 /* Recursively add the content of a directory to the playlist. */
add_dir_plist()1772 static void add_dir_plist ()
1773 {
1774 	struct plist plist;
1775 	char *file;
1776 	enum file_type type;
1777 
1778 	if (iface_in_plist_menu()) {
1779 		error ("Can't add to the playlist a file from the "
1780 				"playlist.");
1781 		return;
1782 	}
1783 
1784 	file = iface_get_curr_file ();
1785 
1786 	if (!file)
1787 		return;
1788 
1789 	type = iface_curritem_get_type ();
1790 	if (type != F_DIR && type != F_PLAYLIST) {
1791 		error ("This is not a directory or a playlist.");
1792 		free (file);
1793 		return;
1794 	}
1795 
1796 	if (!strcmp(file, "..")) {
1797 		error ("Can't add '..'.");
1798 		free (file);
1799 		return;
1800 	}
1801 
1802 	iface_set_status ("Reading directories...");
1803 	plist_init (&plist);
1804 
1805 	if (type == F_DIR) {
1806 		read_directory_recurr (file, &plist);
1807 		plist_sort_fname (&plist);
1808 	}
1809 	else
1810 		plist_load (&plist, file, cwd, 0);
1811 
1812 	send_int_to_srv (CMD_LOCK);
1813 
1814 	plist_remove_common_items (&plist, playlist);
1815 
1816 	/* Add the new files to the server's playlist if the server has our
1817 	 * playlist. */
1818 	if (get_server_plist_serial() == plist_get_serial(playlist))
1819 		send_playlist (&plist, 0);
1820 
1821 	if (options_get_int("SyncPlaylist")) {
1822 		iface_set_status ("Notifying clients...");
1823 		send_items_to_clients (&plist);
1824 		iface_set_status ("");
1825 	}
1826 	else {
1827 		int i;
1828 
1829 		switch_titles_file (&plist);
1830 		ask_for_tags (&plist, get_tags_setting());
1831 
1832 		for (i = 0; i < plist.num; i++)
1833 			if (!plist_deleted(&plist, i))
1834 				iface_add_to_plist (&plist, i);
1835 		plist_cat (playlist, &plist);
1836 	}
1837 
1838 	send_int_to_srv (CMD_UNLOCK);
1839 
1840 	plist_free (&plist);
1841 	free (file);
1842 }
1843 
1844 /* To avoid lots of locks and unlocks, this assumes a lock is sent before
1845  * the first call and an unlock after the last.
1846  *
1847  * It's also assumed to be in the menu.
1848  */
remove_file_from_playlist(const char * file)1849 static void remove_file_from_playlist (const char *file)
1850 {
1851 	assert (file != NULL);
1852 	assert (plist_count(playlist) > 0);
1853 
1854 	if (options_get_int("SyncPlaylist")) {
1855 		send_int_to_srv (CMD_CLI_PLIST_DEL);
1856 		send_str_to_srv (file);
1857 	}
1858 	else {
1859 		int n = plist_find_fname (playlist, file);
1860 
1861 		assert (n != -1);
1862 
1863 		plist_delete (playlist, n);
1864 		iface_del_plist_item (file);
1865 
1866 		if (plist_count(playlist) == 0)
1867 			clear_playlist ();
1868 	}
1869 
1870 	/* Delete this item from the server's playlist if it has our
1871 	 * playlist. */
1872 	if (get_server_plist_serial() == plist_get_serial(playlist)) {
1873 		send_int_to_srv (CMD_DELETE);
1874 		send_str_to_srv (file);
1875 	}
1876 }
1877 
1878 /* Remove all dead entries (point to non-existent or unreadable). */
remove_dead_entries_plist()1879 static void remove_dead_entries_plist ()
1880 {
1881 	const char *file = NULL;
1882 	int i;
1883 
1884 	if (! iface_in_plist_menu()) {
1885 		error ("Can't prune when not in the playlist.");
1886 		return;
1887 	}
1888 
1889 	send_int_to_srv (CMD_LOCK);
1890 	for (i = 0, file = plist_get_next_dead_entry(playlist, &i);
1891 	     file != NULL;
1892 	     file = plist_get_next_dead_entry(playlist, &i)) {
1893 		remove_file_from_playlist (file);
1894 	}
1895 	send_int_to_srv (CMD_UNLOCK);
1896 }
1897 
1898 /* Add the currently selected file to the playlist. */
add_file_plist()1899 static void add_file_plist ()
1900 {
1901 	char *file;
1902 
1903 	if (iface_in_plist_menu()) {
1904 		error ("Can't add to the playlist a file from the playlist.");
1905 		return;
1906 	}
1907 
1908 	if (iface_curritem_get_type() == F_DIR) {
1909 		add_dir_plist();
1910 		return;
1911 	}
1912 
1913 	file = iface_get_curr_file ();
1914 
1915 	if (!file)
1916 		return;
1917 
1918 	if (iface_curritem_get_type() != F_SOUND) {
1919 		error ("You can only add a file using this command.");
1920 		free (file);
1921 		return;
1922 	}
1923 
1924 	if (plist_find_fname(playlist, file) == -1) {
1925 		struct plist_item *item = &dir_plist->items[
1926 			plist_find_fname(dir_plist, file)];
1927 
1928 		send_int_to_srv (CMD_LOCK);
1929 
1930 		if (options_get_int("SyncPlaylist")) {
1931 			send_int_to_srv (CMD_CLI_PLIST_ADD);
1932 			send_item_to_srv (item);
1933 		}
1934 		else {
1935 			int added;
1936 
1937 			added = plist_add_from_item (playlist, item);
1938 			iface_add_to_plist (playlist, added);
1939 		}
1940 
1941 		/* Add to the server's playlist if the server has our
1942 		 * playlist. */
1943 		if (get_server_plist_serial() == plist_get_serial(playlist)) {
1944 			send_int_to_srv (CMD_LIST_ADD);
1945 			send_str_to_srv (file);
1946 		}
1947 		send_int_to_srv (CMD_UNLOCK);
1948 	}
1949 	else
1950 		error ("The file is already on the playlist.");
1951 
1952 	iface_menu_key (KEY_CMD_MENU_DOWN);
1953 
1954 	free (file);
1955 }
1956 
queue_toggle_file()1957 static void queue_toggle_file ()
1958 {
1959 	char *file;
1960 
1961 	file = iface_get_curr_file ();
1962 
1963 	if (!file)
1964 		return;
1965 
1966 	if (iface_curritem_get_type() != F_SOUND
1967 			&& iface_curritem_get_type() != F_URL) {
1968 		error ("You can only add a file or URL using this command.");
1969 		free (file);
1970 		return;
1971 	}
1972 
1973 	/* Check if the file is already in the queue; if it isn't, add it,
1974 	 * otherwise, remove it. */
1975 
1976 	if (plist_find_fname(queue, file) == -1) {
1977 		/* Add item to the server's queue. */
1978 		send_int_to_srv (CMD_QUEUE_ADD);
1979 		send_str_to_srv (file);
1980 
1981 		logit ("Added to queue: %s", file);
1982 	}
1983 	else {
1984 		/* Delete this item from the server's queue. */
1985 		send_int_to_srv (CMD_QUEUE_DEL);
1986 		send_str_to_srv (file);
1987 
1988 		logit ("Removed from queue: %s", file);
1989 	}
1990 
1991 	iface_menu_key (KEY_CMD_MENU_DOWN);
1992 
1993 	free (file);
1994 }
1995 
toggle_option(const char * name)1996 static void toggle_option (const char *name)
1997 {
1998 	send_int_to_srv (CMD_SET_OPTION);
1999 	send_str_to_srv (name);
2000 	send_int_to_srv (!options_get_int(name));
2001 	sync_int_option (name);
2002 }
2003 
toggle_show_time()2004 static void toggle_show_time ()
2005 {
2006 	if (!strcasecmp (options_get_str ("ShowTime"), "yes")) {
2007 		options_set_str ("ShowTime", "IfAvailable");
2008 		iface_set_status ("ShowTime: IfAvailable");
2009 	}
2010 	else if (!strcasecmp (options_get_str ("ShowTime"), "no")) {
2011 		options_set_str ("ShowTime", "yes");
2012 		iface_update_show_time ();
2013 		ask_for_tags (dir_plist, TAGS_TIME);
2014 		ask_for_tags (playlist, TAGS_TIME);
2015 		iface_set_status ("ShowTime: yes");
2016 
2017 	}
2018 	else { /* IfAvailable */
2019 		options_set_str ("ShowTime", "no");
2020 		iface_update_show_time ();
2021 		iface_set_status ("ShowTime: no");
2022 	}
2023 }
2024 
toggle_show_format()2025 static void toggle_show_format ()
2026 {
2027 	int show_format = !options_get_int("ShowFormat");
2028 
2029 	options_set_int ("ShowFormat", show_format);
2030 	if (show_format)
2031 		iface_set_status ("ShowFormat: yes");
2032 	else
2033 		iface_set_status ("ShowFormat: no");
2034 
2035 	iface_update_show_format ();
2036 }
2037 
2038 /* Reread the directory. */
reread_dir()2039 static void reread_dir ()
2040 {
2041 	go_to_dir (NULL, 1);
2042 }
2043 
2044 /* Clear the playlist on user request. */
cmd_clear_playlist()2045 static void cmd_clear_playlist ()
2046 {
2047 	if (options_get_int("SyncPlaylist")) {
2048 		send_int_to_srv (CMD_LOCK);
2049 		send_int_to_srv (CMD_CLI_PLIST_CLEAR);
2050 		change_srv_plist_serial ();
2051 		send_int_to_srv (CMD_UNLOCK);
2052 	}
2053 	else
2054 		clear_playlist ();
2055 }
2056 
cmd_clear_queue()2057 static void cmd_clear_queue ()
2058 {
2059 	send_int_to_srv (CMD_QUEUE_CLEAR);
2060 }
2061 
go_to_music_dir()2062 static void go_to_music_dir ()
2063 {
2064 	const char *musicdir_optn;
2065 	char music_dir[PATH_MAX] = "/";
2066 
2067 	musicdir_optn = options_get_str ("MusicDir");
2068 
2069 	if (!musicdir_optn) {
2070 		error ("MusicDir not defined");
2071 		return;
2072 	}
2073 
2074 	resolve_path (music_dir, sizeof(music_dir), musicdir_optn);
2075 
2076 	switch (file_type (music_dir)) {
2077 	case F_DIR:
2078 		go_to_dir (music_dir, 0);
2079 		break;
2080 	case F_PLAYLIST:
2081 		go_to_playlist (music_dir, 0, false);
2082 		break;
2083 	default:
2084 		error ("MusicDir is neither a directory nor a playlist!");
2085 	}
2086 }
2087 
2088 /* Make a directory from the string resolving ~, './' and '..'.
2089  * Return the directory, the memory is malloc()ed.
2090  * Return NULL on error. */
make_dir(const char * str)2091 static char *make_dir (const char *str)
2092 {
2093 	char *dir;
2094 	int add_slash = 0;
2095 
2096 	dir = (char *)xmalloc (sizeof(char) * (PATH_MAX + 1));
2097 
2098 	dir[PATH_MAX] = 0;
2099 
2100 	/* If the string ends with a slash and is not just '/', add this
2101 	 * slash. */
2102 	if (strlen(str) > 1 && str[strlen(str)-1] == '/')
2103 		add_slash = 1;
2104 
2105 	if (str[0] == '~') {
2106 		strncpy (dir, get_home (), PATH_MAX);
2107 
2108 		if (dir[PATH_MAX]) {
2109 			logit ("Path too long!");
2110 			return NULL;
2111 		}
2112 
2113 		if (!strcmp(str, "~"))
2114 			add_slash = 1;
2115 
2116 		str++;
2117 	}
2118 	else if (str[0] != '/')
2119 		strcpy (dir, cwd);
2120 	else
2121 		strcpy (dir, "/");
2122 
2123 	resolve_path (dir, PATH_MAX, str);
2124 
2125 	if (add_slash && strlen(dir) < PATH_MAX)
2126 		strcat (dir, "/");
2127 
2128 	return dir;
2129 }
2130 
entry_key_go_dir(const struct iface_key * k)2131 static void entry_key_go_dir (const struct iface_key *k)
2132 {
2133 	if (k->type == IFACE_KEY_CHAR && k->key.ucs == '\t') {
2134 		char *dir;
2135 		char *complete_dir;
2136 		char buf[PATH_MAX+1];
2137 		char *entry_text;
2138 
2139 		entry_text = iface_entry_get_text ();
2140 		if (!(dir = make_dir(entry_text))) {
2141 			free (entry_text);
2142 			return;
2143 		}
2144 		free (entry_text);
2145 
2146 		complete_dir = find_match_dir (dir);
2147 
2148 		strncpy (buf, complete_dir ? complete_dir : dir, sizeof(buf));
2149 		if (complete_dir)
2150 			free (complete_dir);
2151 
2152 		iface_entry_set_text (buf);
2153 		free (dir);
2154 	}
2155 	else if (k->type == IFACE_KEY_CHAR && k->key.ucs == '\n') {
2156 		char *entry_text = iface_entry_get_text ();
2157 
2158 		if (entry_text[0]) {
2159 			char *dir = make_dir (entry_text);
2160 
2161 			iface_entry_history_add ();
2162 
2163 			if (dir) {
2164 				/* strip trailing slash */
2165 				if (dir[strlen(dir)-1] == '/'
2166 						&& strcmp(dir, "/"))
2167 					dir[strlen(dir)-1] = 0;
2168 				go_to_dir (dir, 0);
2169 				free (dir);
2170 			}
2171 		}
2172 
2173 		iface_entry_disable ();
2174 		free (entry_text);
2175 	}
2176 	else
2177 		iface_entry_handle_key (k);
2178 }
2179 
2180 /* Request playing from the specified URL. */
play_from_url(const char * url)2181 static void play_from_url (const char *url)
2182 {
2183 	send_int_to_srv (CMD_LOCK);
2184 
2185 	change_srv_plist_serial ();
2186 	send_int_to_srv (CMD_LIST_CLEAR);
2187 	send_int_to_srv (CMD_LIST_ADD);
2188 	send_str_to_srv (url);
2189 
2190 	send_int_to_srv (CMD_PLAY);
2191 	send_str_to_srv ("");
2192 
2193 	send_int_to_srv (CMD_UNLOCK);
2194 }
2195 
2196 /* Return malloc()ed string that is a copy of str without leading and trailing
2197  * white spaces. */
strip_white_spaces(const char * str)2198 static char *strip_white_spaces (const char *str)
2199 {
2200 	char *clean;
2201 	int n;
2202 
2203 	assert (str != NULL);
2204 
2205 	n = strlen (str);
2206 
2207 	/* Strip trailing. */
2208 	while (n > 0 && isblank(str[n-1]))
2209 		n--;
2210 
2211 	/* Strip leading whitespace. */
2212 	while (*str && isblank(*str)) {
2213 		str++;
2214 		n--;
2215 	}
2216 
2217 	if (n > 0) {
2218 		clean = (char *)xmalloc ((n + 1) * sizeof(char));
2219 		strncpy (clean, str, n);
2220 		clean[n] = 0;
2221 	}
2222 	else
2223 		clean = xstrdup ("");
2224 
2225 	return clean;
2226 }
2227 
entry_key_go_url(const struct iface_key * k)2228 static void entry_key_go_url (const struct iface_key *k)
2229 {
2230 	if (k->type == IFACE_KEY_CHAR && k->key.ucs == '\n') {
2231 		char *entry_text = iface_entry_get_text ();
2232 
2233 		if (entry_text[0]) {
2234 			char *clean_url = strip_white_spaces (entry_text);
2235 
2236 			iface_entry_history_add ();
2237 
2238 			if (is_url(clean_url))
2239 				play_from_url (clean_url);
2240 			else
2241 				error ("Not a valid URL.");
2242 
2243 			free (clean_url);
2244 		}
2245 
2246 		free (entry_text);
2247 		iface_entry_disable ();
2248 	}
2249 	else
2250 		iface_entry_handle_key (k);
2251 }
2252 
add_url_to_plist(const char * url)2253 static void add_url_to_plist (const char *url)
2254 {
2255 	assert (url != NULL);
2256 
2257 	if (plist_find_fname(playlist, url) == -1) {
2258 		send_int_to_srv (CMD_LOCK);
2259 
2260 		if (options_get_int("SyncPlaylist")) {
2261 			struct plist_item *item = plist_new_item ();
2262 
2263 			item->file = xstrdup (url);
2264 			item->title_file = xstrdup (url);
2265 
2266 			send_int_to_srv (CMD_CLI_PLIST_ADD);
2267 			send_item_to_srv (item);
2268 
2269 			plist_free_item_fields (item);
2270 			free (item);
2271 		}
2272 		else {
2273 			int added;
2274 
2275 			added = plist_add (playlist, url);
2276 			make_file_title (playlist, added, 0);
2277 			iface_add_to_plist (playlist, added);
2278 		}
2279 
2280 		/* Add to the server's playlist if the server has our
2281 		 * playlist. */
2282 		if (get_server_plist_serial() == plist_get_serial(playlist)) {
2283 			send_int_to_srv (CMD_LIST_ADD);
2284 			send_str_to_srv (url);
2285 		}
2286 		send_int_to_srv (CMD_UNLOCK);
2287 	}
2288 	else
2289 		error ("URL already on the playlist");
2290 }
2291 
entry_key_add_url(const struct iface_key * k)2292 static void entry_key_add_url (const struct iface_key *k)
2293 {
2294 	if (k->type == IFACE_KEY_CHAR && k->key.ucs == '\n') {
2295 		char *entry_text = iface_entry_get_text ();
2296 
2297 		if (entry_text[0]) {
2298 			char *clean_url = strip_white_spaces (entry_text);
2299 
2300 			iface_entry_history_add ();
2301 
2302 			if (is_url(clean_url))
2303 				add_url_to_plist (clean_url);
2304 			else
2305 				error ("Not a valid URL.");
2306 
2307 			free (clean_url);
2308 		}
2309 
2310 		free (entry_text);
2311 		iface_entry_disable ();
2312 	}
2313 	else
2314 		iface_entry_handle_key (k);
2315 }
2316 
entry_key_search(const struct iface_key * k)2317 static void entry_key_search (const struct iface_key *k)
2318 {
2319 	if (k->type == IFACE_KEY_CHAR && k->key.ucs == '\n') {
2320 		char *file = iface_get_curr_file ();
2321 		char *text = iface_entry_get_text ();
2322 
2323 		iface_entry_disable ();
2324 
2325 		if (text[0]) {
2326 
2327 			if (!strcmp(file, "..")) {
2328 				free (file);
2329 				file = dir_up (cwd);
2330 			}
2331 
2332 			if (is_url(file))
2333 				play_from_url (file);
2334 			else if (file_type(file) == F_DIR)
2335 				go_to_dir (file, 0);
2336 			else if (file_type(file) == F_PLAYLIST)
2337 				go_to_playlist (file, 0, false);
2338 			else
2339 				play_it (file);
2340 		}
2341 
2342 		free (text);
2343 		free (file);
2344 	}
2345 	else
2346 		iface_entry_handle_key (k);
2347 }
2348 
save_playlist(const char * file,const char * cwd,const int save_serial)2349 static void save_playlist (const char *file, const char *cwd,
2350 		const int save_serial)
2351 {
2352 	iface_set_status ("Saving the playlist...");
2353 	fill_tags (playlist, TAGS_COMMENTS | TAGS_TIME, 0);
2354 	if (!user_wants_interrupt()) {
2355 		if (plist_save(playlist, file, cwd, save_serial))
2356 			interface_message ("Playlist saved");
2357 	}
2358 	else
2359 		iface_set_status ("Aborted");
2360 	iface_set_status ("");
2361 }
2362 
entry_key_plist_save(const struct iface_key * k)2363 static void entry_key_plist_save (const struct iface_key *k)
2364 {
2365 	if (k->type == IFACE_KEY_CHAR && k->key.ucs == '\n') {
2366 		char *text = iface_entry_get_text ();
2367 
2368 		iface_entry_disable ();
2369 
2370 		if (text[0]) {
2371 			char *ext = ext_pos (text);
2372 			char *file;
2373 
2374 			/* add extension if necessary */
2375 			if (!ext || strcmp(ext, "m3u")) {
2376 				char *tmp = (char *)xmalloc((strlen(text) + 5) *
2377 						sizeof(char));
2378 
2379 				sprintf (tmp, "%s.m3u", text);
2380 				free (text);
2381 				text = tmp;
2382 			}
2383 
2384 			file = make_dir (text);
2385 
2386 			if (file_exists(file)) {
2387 				iface_make_entry (ENTRY_PLIST_OVERWRITE);
2388 				iface_entry_set_file (file);
2389 			}
2390 			else {
2391 				save_playlist (file, strchr(text, '/')
2392 						? NULL : cwd, 0);
2393 
2394 				if (iface_in_dir_menu())
2395 					reread_dir ();
2396 			}
2397 
2398 			free (file);
2399 		}
2400 
2401 		free (text);
2402 	}
2403 	else
2404 		iface_entry_handle_key (k);
2405 }
2406 
entry_key_plist_overwrite(const struct iface_key * k)2407 static void entry_key_plist_overwrite (const struct iface_key *k)
2408 {
2409 	if (k->type == IFACE_KEY_CHAR && toupper(k->key.ucs) == 'Y') {
2410 		char *file = iface_entry_get_file ();
2411 
2412 		assert (file != NULL);
2413 
2414 		iface_entry_disable ();
2415 
2416 		save_playlist (file, NULL, 0); /* FIXME: not always NULL! */
2417 		if (iface_in_dir_menu())
2418 			reread_dir ();
2419 
2420 		free (file);
2421 	}
2422 	else if (k->type == IFACE_KEY_CHAR && toupper(k->key.ucs) == 'N') {
2423 		iface_entry_disable ();
2424 		iface_message ("Not overwriting.");
2425 	}
2426 }
2427 
entry_key_user_query(const struct iface_key * k)2428 static void entry_key_user_query (const struct iface_key *k)
2429 {
2430 	if (k->type == IFACE_KEY_CHAR && k->key.ucs == '\n') {
2431 		char *entry_text = iface_entry_get_text ();
2432 		iface_entry_disable ();
2433 		iface_user_reply (entry_text);
2434 		free (entry_text);
2435 	}
2436 	else
2437 		iface_entry_handle_key (k);
2438 }
2439 
2440 /* Handle keys while in an entry. */
entry_key(const struct iface_key * k)2441 static void entry_key (const struct iface_key *k)
2442 {
2443 	switch (iface_get_entry_type()) {
2444 		case ENTRY_GO_DIR:
2445 			entry_key_go_dir (k);
2446 			break;
2447 		case ENTRY_GO_URL:
2448 			entry_key_go_url (k);
2449 			break;
2450 		case ENTRY_ADD_URL:
2451 			entry_key_add_url (k);
2452 			break;
2453 		case ENTRY_SEARCH:
2454 			entry_key_search (k);
2455 			break;
2456 		case ENTRY_PLIST_SAVE:
2457 			entry_key_plist_save (k);
2458 			break;
2459 		case ENTRY_PLIST_OVERWRITE:
2460 			entry_key_plist_overwrite (k);
2461 			break;
2462 		case ENTRY_USER_QUERY:
2463 			entry_key_user_query (k);
2464 			break;
2465 		default:
2466 			abort (); /* BUG */
2467 	}
2468 }
2469 
2470 /* Update items in the menu for all items on the playlist. */
update_iface_menu(const enum iface_menu menu,const struct plist * plist)2471 static void update_iface_menu (const enum iface_menu menu,
2472 		const struct plist *plist)
2473 {
2474 	int i;
2475 
2476 	assert (plist != NULL);
2477 
2478 	for (i = 0; i < plist->num; i++)
2479 		if (!plist_deleted(plist, i))
2480 			iface_update_item (menu, plist, i);
2481 }
2482 
2483 /* Switch ReadTags options and update the menu. */
switch_read_tags()2484 static void switch_read_tags ()
2485 {
2486 	if (options_get_int ("ReadTags")) {
2487 		options_set_int ("ReadTags", 0);
2488 		switch_titles_file (dir_plist);
2489 		switch_titles_file (playlist);
2490 		iface_set_status ("ReadTags: no");
2491 	}
2492 	else {
2493 		options_set_int ("ReadTags", 1);
2494 		ask_for_tags (dir_plist, TAGS_COMMENTS);
2495 		ask_for_tags (playlist, TAGS_COMMENTS);
2496 		switch_titles_tags (dir_plist);
2497 		switch_titles_tags (playlist);
2498 		iface_set_status ("ReadTags: yes");
2499 	}
2500 
2501 	update_iface_menu (IFACE_MENU_DIR, dir_plist);
2502 	update_iface_menu (IFACE_MENU_PLIST, playlist);
2503 }
2504 
seek(const int sec)2505 static void seek (const int sec)
2506 {
2507 	send_int_to_srv (CMD_SEEK);
2508 	send_int_to_srv (sec);
2509 }
2510 
jump_to(const int sec)2511 static void jump_to (const int sec)
2512 {
2513 	send_int_to_srv (CMD_JUMP_TO);
2514 	send_int_to_srv (sec);
2515 }
2516 
delete_item()2517 static void delete_item ()
2518 {
2519 	char *file;
2520 
2521 	if (!iface_in_plist_menu()) {
2522 		error ("You can only delete an item from the playlist.");
2523 		return;
2524 	}
2525 
2526 	assert (plist_count(playlist) > 0);
2527 
2528 	file = iface_get_curr_file ();
2529 
2530 	send_int_to_srv (CMD_LOCK);
2531 	remove_file_from_playlist (file);
2532 	send_int_to_srv (CMD_UNLOCK);
2533 
2534 	free (file);
2535 }
2536 
2537 /* Select the file that is currently played. */
go_to_playing_file()2538 static void go_to_playing_file ()
2539 {
2540 	if (curr_file.file && file_type(curr_file.file) == F_SOUND) {
2541 		if (plist_find_fname(playlist, curr_file.file) != -1)
2542 			iface_switch_to_plist ();
2543 		else if (plist_find_fname(dir_plist,  curr_file.file) != -1)
2544 			iface_switch_to_dir ();
2545 		else {
2546 			char *slash;
2547 			char *file = xstrdup (curr_file.file);
2548 
2549 			slash = strrchr (file, '/');
2550 			assert (slash != NULL);
2551 			*slash = 0;
2552 
2553 			if (file[0])
2554 				go_to_dir (file, 0);
2555 			else
2556 				go_to_dir ("/", 0);
2557 
2558 			iface_switch_to_dir ();
2559 			free (file);
2560 		}
2561 
2562 		iface_select_file (curr_file.file);
2563 	}
2564 }
2565 
2566 /* Return the time like the standard time() function, but rounded i.e. if we
2567  * have 11.8 seconds, return 12 seconds. */
rounded_time()2568 static time_t rounded_time ()
2569 {
2570 	struct timeval exact_time;
2571 	time_t curr_time;
2572 
2573 	if (gettimeofday(&exact_time, NULL) == -1)
2574 		interface_fatal ("gettimeofday() failed: %s", strerror(errno));
2575 
2576 	curr_time = exact_time.tv_sec;
2577 	if (exact_time.tv_usec > 500000)
2578 		curr_time++;
2579 
2580 	return curr_time;
2581 }
2582 
2583 /* Handle silent seek key. */
seek_silent(const int sec)2584 static void seek_silent (const int sec)
2585 {
2586 	if (curr_file.state == STATE_PLAY && curr_file.file
2587 			&& !is_url(curr_file.file)) {
2588 		if (silent_seek_pos == -1) {
2589 			silent_seek_pos = curr_file.curr_time + sec;
2590 		}
2591 		else
2592 			silent_seek_pos += sec;
2593 
2594 		silent_seek_pos = CLAMP(0, silent_seek_pos, curr_file.total_time);
2595 
2596 		silent_seek_key_last = rounded_time ();
2597 		iface_set_curr_time (silent_seek_pos);
2598 	}
2599 }
2600 
2601 /* Move the current playlist item (direction: 1 - up, -1 - down). */
move_item(const int direction)2602 static void move_item (const int direction)
2603 {
2604 	char *file;
2605 	int second;
2606 	char *second_file;
2607 
2608 	if (!iface_in_plist_menu()) {
2609 		error ("You can move only playlist items.");
2610 		return;
2611 	}
2612 
2613 	if (!(file = iface_get_curr_file()))
2614 		return;
2615 
2616 	second = plist_find_fname (playlist, file);
2617 	assert (second != -1);
2618 
2619 	if (direction == -1)
2620 		second = plist_next (playlist, second);
2621 	else if (direction == 1)
2622 		second = plist_prev (playlist, second);
2623 	else
2624 		abort (); /* BUG */
2625 
2626 	if (second == -1) {
2627 		free (file);
2628 		return;
2629 	}
2630 
2631 	second_file = plist_get_file (playlist, second);
2632 
2633 	send_int_to_srv (CMD_LOCK);
2634 
2635 	if (options_get_int("SyncPlaylist")) {
2636 		send_int_to_srv (CMD_CLI_PLIST_MOVE);
2637 		send_str_to_srv (file);
2638 		send_str_to_srv (second_file);
2639 	}
2640 	else
2641 		swap_playlist_items (file, second_file);
2642 
2643 	/* update the server's playlist */
2644 	if (get_server_plist_serial() == plist_get_serial(playlist)) {
2645 		send_int_to_srv (CMD_LIST_MOVE);
2646 		send_str_to_srv (file);
2647 		send_str_to_srv (second_file);
2648 	}
2649 
2650 	send_int_to_srv (CMD_UNLOCK);
2651 
2652 	free (second_file);
2653 	free (file);
2654 }
2655 
2656 /* Handle releasing silent seek key. */
do_silent_seek()2657 static void do_silent_seek ()
2658 {
2659 	time_t curr_time = time(NULL);
2660 
2661 	if (silent_seek_pos != -1 && silent_seek_key_last < curr_time) {
2662 		seek (silent_seek_pos - curr_file.curr_time - 1);
2663 		silent_seek_pos = -1;
2664 		iface_set_curr_time (curr_file.curr_time);
2665 	}
2666 }
2667 
2668 /* Handle the 'next' command. */
cmd_next()2669 static void cmd_next ()
2670 {
2671 	if (curr_file.state != STATE_STOP)
2672 		send_int_to_srv (CMD_NEXT);
2673 	else if (plist_count(playlist)) {
2674 		if (plist_get_serial(playlist) != -1
2675 				|| get_server_plist_serial()
2676 				!= plist_get_serial(playlist)) {
2677 			int serial;
2678 
2679 			send_int_to_srv (CMD_LOCK);
2680 
2681 			send_playlist (playlist, 1);
2682 			serial = get_safe_serial();
2683 			plist_set_serial (playlist, serial);
2684 			send_int_to_srv (CMD_PLIST_SET_SERIAL);
2685 			send_int_to_srv (plist_get_serial(playlist));
2686 
2687 			send_int_to_srv (CMD_UNLOCK);
2688 		}
2689 
2690 		send_int_to_srv (CMD_PLAY);
2691 		send_str_to_srv ("");
2692 	}
2693 }
2694 
2695 /* Add themes found in the directory to the list of theme files. */
add_themes_to_list(lists_t_strs * themes,const char * themes_dir)2696 static void add_themes_to_list (lists_t_strs *themes, const char *themes_dir)
2697 {
2698 	DIR *dir;
2699 	struct dirent *entry;
2700 
2701 	assert (themes);
2702 	assert (themes_dir);
2703 
2704 	if (!(dir = opendir(themes_dir))) {
2705 		logit ("Can't open themes directory %s: %s", themes_dir,
2706 				strerror(errno));
2707 		return;
2708 	}
2709 
2710 	while ((entry = readdir(dir))) {
2711 		char file[PATH_MAX];
2712 
2713 		if (entry->d_name[0] == '.')
2714 			continue;
2715 
2716 		/* Filter out backup files (*~). */
2717 		if (entry->d_name[strlen(entry->d_name)-1] == '~')
2718 			continue;
2719 
2720 		if (snprintf(file, sizeof(file), "%s/%s", themes_dir,
2721 					entry->d_name) >= (int)sizeof(file))
2722 			continue;
2723 
2724 		lists_strs_append (themes, file);
2725 	}
2726 
2727 	closedir (dir);
2728 }
2729 
2730 /* Compare two pathnames based on filename. */
themes_cmp(const void * a,const void * b)2731 static int themes_cmp (const void *a, const void *b)
2732 {
2733 	int result;
2734 	char *sa = *(char **)a;
2735 	char *sb = *(char **)b;
2736 
2737 	result = strcoll (strrchr (sa, '/') + 1, strrchr (sb, '/') + 1);
2738 	if (result == 0)
2739 		result = strcoll (sa, sb);
2740 
2741 	return result;
2742 }
2743 
2744 /* Add themes found in the directories to the theme selection menu.
2745  * Return the number of items added. */
add_themes_to_menu(const char * user_themes,const char * system_themes)2746 static int add_themes_to_menu (const char *user_themes,
2747                                const char *system_themes)
2748 {
2749 	int ix;
2750 	lists_t_strs *themes;
2751 
2752 	assert (user_themes);
2753 	assert (system_themes);
2754 
2755 	themes = lists_strs_new (16);
2756 	add_themes_to_list (themes, user_themes);
2757 	add_themes_to_list (themes, system_themes);
2758 	lists_strs_sort (themes, themes_cmp);
2759 
2760 	for (ix = 0; ix < lists_strs_size (themes); ix += 1) {
2761 		char *file;
2762 
2763 		file = lists_strs_at (themes, ix);
2764 		iface_add_file (file, strrchr (file, '/') + 1, F_THEME);
2765 	}
2766 
2767 	lists_strs_free (themes);
2768 
2769 	return ix;
2770 }
2771 
make_theme_menu()2772 static void make_theme_menu ()
2773 {
2774 	iface_switch_to_theme_menu ();
2775 
2776 	if (add_themes_to_menu (create_file_name ("themes"),
2777 	                        SYSTEM_THEMES_DIR) == 0) {
2778 		if (!cwd[0])
2779 			enter_first_dir (); /* we were at the playlist from the startup */
2780 		else
2781 			iface_switch_to_dir ();
2782 
2783 		error ("No themes found.");
2784 	}
2785 
2786 	iface_update_theme_selection (get_current_theme ());
2787 	iface_refresh ();
2788 }
2789 
2790 /* Use theme from the currently selected file. */
use_theme()2791 static void use_theme ()
2792 {
2793 	char *file = iface_get_curr_file ();
2794 
2795 	assert (iface_curritem_get_type() == F_THEME);
2796 
2797 	if (!file)
2798 		return;
2799 
2800 	themes_switch_theme (file);
2801 	iface_update_attrs ();
2802 	iface_refresh ();
2803 
2804 	free (file);
2805 }
2806 
2807 /* Handle keys while in the theme menu. */
theme_menu_key(const struct iface_key * k)2808 static void theme_menu_key (const struct iface_key *k)
2809 {
2810 	if (!iface_key_is_resize(k)) {
2811 		enum key_cmd cmd = get_key_cmd (CON_MENU, k);
2812 
2813 		switch (cmd) {
2814 			case KEY_CMD_GO:
2815 				use_theme ();
2816 				break;
2817 			case KEY_CMD_MENU_DOWN:
2818 			case KEY_CMD_MENU_UP:
2819 			case KEY_CMD_MENU_NPAGE:
2820 			case KEY_CMD_MENU_PPAGE:
2821 			case KEY_CMD_MENU_FIRST:
2822 			case KEY_CMD_MENU_LAST:
2823 				iface_menu_key (cmd);
2824 				break;
2825 			default:
2826 				iface_switch_to_dir ();
2827 				logit ("Bad key");
2828 		}
2829 	}
2830 }
2831 
2832 /* Make sure that we have tags and a title for this file which is in a menu. */
make_sure_tags_exist(const char * file)2833 static void make_sure_tags_exist (const char *file)
2834 {
2835 	struct plist *plist;
2836 	int item_num;
2837 
2838 	if (file_type(file) != F_SOUND)
2839 		return;
2840 
2841 	if ((item_num = plist_find_fname(dir_plist, file)) != -1)
2842 		plist = dir_plist;
2843 	else if ((item_num = plist_find_fname(playlist, file)) != -1)
2844 		plist = playlist;
2845 	else
2846 		return;
2847 
2848 	if (!plist->items[item_num].tags
2849 			|| plist->items[item_num].tags->filled
2850 				!= (TAGS_COMMENTS | TAGS_TIME)) {
2851 		int got_it = 0;
2852 
2853 		send_tags_request (file, TAGS_COMMENTS | TAGS_TIME);
2854 
2855 		while (!got_it) {
2856 			int type = get_int_from_srv ();
2857 			void *data = get_event_data (type);
2858 
2859 			if (type == EV_FILE_TAGS) {
2860 				struct tag_ev_response *ev
2861 					= (struct tag_ev_response *)data;
2862 
2863 				if (!strcmp(ev->file, file))
2864 					got_it = 1;
2865 			}
2866 
2867 			server_event (type, data);
2868 		}
2869 	}
2870 }
2871 
2872 /* Request tags from the server for a file in the playlist or the directory
2873  * menu, wait until they arrive and return them (malloc()ed). */
get_tags(const char * file)2874 static struct file_tags *get_tags (const char *file)
2875 {
2876 	struct plist *plist;
2877 	int item_num;
2878 
2879 	make_sure_tags_exist (file);
2880 
2881 	if ((item_num = plist_find_fname(dir_plist, file)) != -1)
2882 		plist = dir_plist;
2883 	else if ((item_num = plist_find_fname(playlist, file)) != -1)
2884 		plist = playlist;
2885 	else
2886 		return tags_new ();
2887 
2888 	if (file_type(file) == F_SOUND)
2889 		return tags_dup (plist->items[item_num].tags);
2890 
2891 	return tags_new ();
2892 }
2893 
2894 /* Get the title of a file (malloc()ed) that is present in a menu. */
get_title(const char * file)2895 static char *get_title (const char *file)
2896 {
2897 	struct plist *plist;
2898 	int item_num;
2899 
2900 	make_sure_tags_exist (file);
2901 
2902 	if ((item_num = plist_find_fname(dir_plist, file)) != -1)
2903 		plist = dir_plist;
2904 	else if ((item_num = plist_find_fname(playlist, file)) != -1)
2905 		plist = playlist;
2906 	else
2907 		return NULL;
2908 
2909 	return xstrdup (plist->items[item_num].title_tags
2910 			? plist->items[item_num].title_tags
2911 			: plist->items[item_num].title_file);
2912 }
2913 
2914 /* Substitute arguments for custom command that begin with '%'.
2915  * The new value is returned. */
custom_cmd_substitute(const char * arg)2916 static char *custom_cmd_substitute (const char *arg)
2917 {
2918 	char *result = NULL;
2919 	char *file = NULL;
2920 	struct file_tags *tags = NULL;
2921 
2922 	if (strlen (arg) == 2 && arg[0] == '%') {
2923 		switch (arg[1]) {
2924 		case 'i':
2925 			file = iface_get_curr_file ();
2926 			result = get_title (file);
2927 			break;
2928 		case 't':
2929 			file = iface_get_curr_file ();
2930 			tags = get_tags (file);
2931 			result = xstrdup (tags->title);
2932 			break;
2933 		case 'a':
2934 			file = iface_get_curr_file ();
2935 			tags = get_tags (file);
2936 			result = xstrdup (tags->album);
2937 			break;
2938 		case 'r':
2939 			file = iface_get_curr_file ();
2940 			tags = get_tags (file);
2941 			result = xstrdup (tags->artist);
2942 			break;
2943 		case 'n':
2944 			file = iface_get_curr_file ();
2945 			tags = get_tags (file);
2946 			result = (char *) xmalloc (sizeof (char) * 10);
2947 			snprintf (result, 10, "%d", tags->track);
2948 			break;
2949 		case 'm':
2950 			file = iface_get_curr_file ();
2951 			tags = get_tags (file);
2952 			result = (char *) xmalloc (sizeof (char) * 10);
2953 			snprintf (result, 10, "%d", tags->time);
2954 			break;
2955 		case 'f':
2956 			result = iface_get_curr_file ();
2957 			break;
2958 		case 'I':
2959 			result = xstrdup (curr_file.title);
2960 			break;
2961 		case 'T':
2962 			if (curr_file.tags && curr_file.tags->title)
2963 				result = xstrdup (curr_file.tags->title);
2964 			break;
2965 		case 'A':
2966 			if (curr_file.tags && curr_file.tags->album)
2967 				result = xstrdup (curr_file.tags->album);
2968 			break;
2969 		case 'R':
2970 			if (curr_file.tags && curr_file.tags->artist)
2971 				result = xstrdup (curr_file.tags->artist);
2972 			break;
2973 		case 'N':
2974 			if (curr_file.tags && curr_file.tags->track != -1) {
2975 				result = (char *) xmalloc (sizeof (char) * 10);
2976 				snprintf (result, 10, "%d", curr_file.tags->track);
2977 			}
2978 			break;
2979 		case 'M':
2980 			if (curr_file.tags && curr_file.tags->time != -1) {
2981 				result = (char *) xmalloc (sizeof (char) * 10);
2982 				snprintf (result, 10, "%d", curr_file.tags->time);
2983 			}
2984 			break;
2985 		case 'F':
2986 			if (curr_file.file)
2987 				result = xstrdup (curr_file.file);
2988 			break;
2989 		case 'S':
2990 			if (curr_file.file && curr_file.block_file) {
2991 				result = (char *) xmalloc (sizeof (char) * 10);
2992 				snprintf (result, 10, "%d", curr_file.block_start);
2993 			}
2994 			break;
2995 		case 'E':
2996 			if (curr_file.file && curr_file.block_file) {
2997 				result = (char *) xmalloc (sizeof (char) * 10);
2998 				snprintf (result, 10, "%d", curr_file.block_end);
2999 			}
3000 			break;
3001 		default:
3002 			result = xstrdup (arg);
3003 		}
3004 	}
3005 	else
3006 		result = xstrdup (arg);
3007 
3008 	/* Replace nonexisting data with an empty string. */
3009 	if (!result)
3010 		result = xstrdup ("");
3011 
3012 	free (file);
3013 	if (tags)
3014 		tags_free (tags);
3015 
3016 	return result;
3017 }
3018 
run_external_cmd(char ** args,const int arg_num ATTR_UNUSED)3019 static void run_external_cmd (char **args, const int arg_num ATTR_UNUSED)
3020 {
3021 	pid_t child;
3022 
3023 	assert (args != NULL);
3024 	assert (arg_num >= 1);
3025 	assert (args[0] != NULL);
3026 	assert (args[arg_num] == NULL);
3027 
3028 	iface_temporary_exit ();
3029 
3030 	child = fork();
3031 	if (child == -1)
3032 		error ("fork() failed: %s", strerror (errno));
3033 	else {
3034 		int status;
3035 
3036 		if (child == 0) { /* I'm a child. */
3037 
3038 			putchar ('\n');
3039 			execvp (args[0], args);
3040 
3041 			/* We have an error. */
3042 			fprintf (stderr, "\nError executing %s: %s\n", args[0],
3043 					strerror(errno));
3044 			sleep (2);
3045 			exit (EXIT_FAILURE);
3046 		}
3047 
3048 		/* parent */
3049 		waitpid (child, &status, 0);
3050 		if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
3051 			fprintf (stderr, "\nCommand exited with error (status %d).\n",
3052 			                 WEXITSTATUS(status));
3053 			sleep (2);
3054 		}
3055 		iface_restore ();
3056 	}
3057 }
3058 
3059 /* Exec (execvp()) a custom command (ExecCommand[1-10] options). */
exec_custom_command(const char * option)3060 static void exec_custom_command (const char *option)
3061 {
3062 	char *cmd, *arg;
3063 	char **args;
3064 	int ix, arg_num;
3065 	lists_t_strs *cmd_list, *arg_list;
3066 
3067 	assert (option != NULL);
3068 
3069 	cmd = options_get_str (option);
3070 	if (!cmd || !cmd[0]) {
3071 		error ("%s is not set", option);
3072 		return;
3073 	}
3074 
3075 	/* Split into arguments. */
3076 	cmd_list = lists_strs_new (4);
3077 	arg_num = lists_strs_tokenise (cmd_list, cmd);
3078 	if (arg_num == 0) {
3079 		error ("Malformed %s option", option);
3080 		lists_strs_free (cmd_list);
3081 		return;
3082 	}
3083 
3084 	arg_list = lists_strs_new (lists_strs_size (cmd_list));
3085 	for (ix = 0; ix < arg_num; ix += 1) {
3086 		arg = custom_cmd_substitute (lists_strs_at (cmd_list, ix));
3087 		lists_strs_push (arg_list, arg);
3088 	}
3089 	lists_strs_free (cmd_list);
3090 
3091 	cmd = lists_strs_fmt (arg_list, " %s");
3092 	logit ("Running command:%s", cmd);
3093 	free (cmd);
3094 
3095 	args = lists_strs_save (arg_list);
3096 	lists_strs_free (arg_list);
3097 	run_external_cmd (args, arg_num);
3098 	free (args);
3099 
3100 	if (iface_in_dir_menu())
3101 		reread_dir ();
3102 }
3103 
go_to_fast_dir(const int num)3104 static void go_to_fast_dir (const int num)
3105 {
3106 	char option_name[20];
3107 
3108 	assert (RANGE(1, num, 10));
3109 
3110 	sprintf (option_name, "FastDir%d", num);
3111 
3112 	if (options_get_str(option_name)) {
3113 		char dir[PATH_MAX] = "/";
3114 
3115 		resolve_path (dir, sizeof(dir), options_get_str(option_name));
3116 		go_to_dir (dir, 0);
3117 	}
3118 	else
3119 		error ("%s is not defined", option_name);
3120 }
3121 
toggle_playlist_full_paths(void)3122 static void toggle_playlist_full_paths (void)
3123 {
3124 	int new_val = !options_get_int ("PlaylistFullPaths");
3125 
3126 	options_set_int ("PlaylistFullPaths", new_val);
3127 
3128 	if (new_val)
3129 		iface_set_status ("PlaylistFullPaths: on");
3130 	else
3131 		iface_set_status ("PlaylistFullPaths: off");
3132 
3133 	update_iface_menu (IFACE_MENU_PLIST, playlist);
3134 }
3135 
3136 /* Handle key. */
menu_key(const struct iface_key * k)3137 static void menu_key (const struct iface_key *k)
3138 {
3139 	if (iface_in_help ())
3140 		iface_handle_help_key (k);
3141 	else if (iface_in_lyrics ())
3142 		iface_handle_lyrics_key (k);
3143 	else if (iface_in_entry ())
3144 		entry_key (k);
3145 	else if (iface_in_theme_menu ())
3146 		theme_menu_key (k);
3147 	else if (!iface_key_is_resize (k)) {
3148 		enum key_cmd cmd = get_key_cmd (CON_MENU, k);
3149 
3150 		switch (cmd) {
3151 			case KEY_CMD_QUIT_CLIENT:
3152 				want_quit = QUIT_CLIENT;
3153 				break;
3154 			case KEY_CMD_GO:
3155 				go_file ();
3156 				break;
3157 			case KEY_CMD_MENU_DOWN:
3158 			case KEY_CMD_MENU_UP:
3159 			case KEY_CMD_MENU_NPAGE:
3160 			case KEY_CMD_MENU_PPAGE:
3161 			case KEY_CMD_MENU_FIRST:
3162 			case KEY_CMD_MENU_LAST:
3163 				iface_menu_key (cmd);
3164 				last_menu_move_time = time (NULL);
3165 				break;
3166 			case KEY_CMD_QUIT:
3167 				want_quit = QUIT_SERVER;
3168 				break;
3169 			case KEY_CMD_STOP:
3170 				send_int_to_srv (CMD_STOP);
3171 				break;
3172 			case KEY_CMD_NEXT:
3173 				cmd_next ();
3174 				break;
3175 			case KEY_CMD_PREVIOUS:
3176 				send_int_to_srv (CMD_PREV);
3177 				break;
3178 			case KEY_CMD_PAUSE:
3179 				switch_pause ();
3180 				break;
3181 			case KEY_CMD_TOGGLE_READ_TAGS:
3182 				switch_read_tags ();
3183 				break;
3184 			case KEY_CMD_TOGGLE_SHUFFLE:
3185 				toggle_option ("Shuffle");
3186 				break;
3187 			case KEY_CMD_TOGGLE_REPEAT:
3188 				toggle_option ("Repeat");
3189 				break;
3190 			case KEY_CMD_TOGGLE_AUTO_NEXT:
3191 				toggle_option ("AutoNext");
3192 				break;
3193 			case KEY_CMD_TOGGLE_MENU:
3194 				toggle_menu ();
3195 				break;
3196 			case KEY_CMD_TOGGLE_PLAYLIST_FULL_PATHS:
3197 				toggle_playlist_full_paths ();
3198 				break;
3199 			case KEY_CMD_PLIST_ADD_FILE:
3200 				add_file_plist ();
3201 				break;
3202 			case KEY_CMD_PLIST_CLEAR:
3203 				cmd_clear_playlist ();
3204 				break;
3205 			case KEY_CMD_PLIST_ADD_DIR:
3206 				add_dir_plist ();
3207 				break;
3208 			case KEY_CMD_PLIST_REMOVE_DEAD_ENTRIES:
3209 				remove_dead_entries_plist ();
3210 				break;
3211 			case KEY_CMD_MIXER_DEC_1:
3212 				adjust_mixer (-1);
3213 				break;
3214 			case KEY_CMD_MIXER_DEC_5:
3215 				adjust_mixer (-5);
3216 				break;
3217 			case KEY_CMD_MIXER_INC_5:
3218 				adjust_mixer (+5);
3219 				break;
3220 			case KEY_CMD_MIXER_INC_1:
3221 				adjust_mixer (+1);
3222 				break;
3223 			case KEY_CMD_SEEK_BACKWARD:
3224 				seek (-options_get_int ("SeekTime"));
3225 				break;
3226 			case KEY_CMD_SEEK_FORWARD:
3227 				seek (options_get_int ("SeekTime"));
3228 				break;
3229 			case KEY_CMD_HELP:
3230 				iface_switch_to_help ();
3231 				break;
3232 			case KEY_CMD_LYRICS:
3233 				iface_switch_to_lyrics ();
3234 				break;
3235 			case KEY_CMD_HIDE_MESSAGE:
3236 				iface_disable_message ();
3237 				break;
3238 			case KEY_CMD_REFRESH:
3239 				iface_refresh ();
3240 				break;
3241 			case KEY_CMD_RELOAD:
3242 				if (iface_in_dir_menu ())
3243 					reread_dir ();
3244 				break;
3245 			case KEY_CMD_TOGGLE_SHOW_HIDDEN_FILES:
3246 				options_set_int ("ShowHiddenFiles",
3247 				                 !options_get_int ("ShowHiddenFiles"));
3248 				if (iface_in_dir_menu ())
3249 					reread_dir ();
3250 				break;
3251 			case KEY_CMD_GO_MUSIC_DIR:
3252 				go_to_music_dir ();
3253 				break;
3254 			case KEY_CMD_PLIST_DEL:
3255 				delete_item ();
3256 				break;
3257 			case KEY_CMD_MENU_SEARCH:
3258 				iface_make_entry (ENTRY_SEARCH);
3259 				break;
3260 			case KEY_CMD_PLIST_SAVE:
3261 				if (plist_count (playlist))
3262 					iface_make_entry (ENTRY_PLIST_SAVE);
3263 				else
3264 					error ("The playlist is empty.");
3265 				break;
3266 			case KEY_CMD_TOGGLE_SHOW_TIME:
3267 				toggle_show_time ();
3268 				break;
3269 			case KEY_CMD_TOGGLE_SHOW_FORMAT:
3270 				toggle_show_format ();
3271 				break;
3272 			case KEY_CMD_GO_TO_PLAYING_FILE:
3273 				go_to_playing_file ();
3274 				break;
3275 			case KEY_CMD_GO_DIR:
3276 				iface_make_entry (ENTRY_GO_DIR);
3277 				break;
3278 			case KEY_CMD_GO_URL:
3279 				iface_make_entry (ENTRY_GO_URL);
3280 				break;
3281 			case KEY_CMD_GO_DIR_UP:
3282 				go_dir_up ();
3283 				break;
3284 			case KEY_CMD_WRONG:
3285 				error ("Bad command");
3286 				break;
3287 			case KEY_CMD_SEEK_FORWARD_5:
3288 				seek_silent (options_get_int ("SilentSeekTime"));
3289 				break;
3290 			case KEY_CMD_SEEK_BACKWARD_5:
3291 				seek_silent (-options_get_int ("SilentSeekTime"));
3292 				break;
3293 			case KEY_CMD_VOLUME_10:
3294 				set_mixer (10);
3295 				break;
3296 			case KEY_CMD_VOLUME_20:
3297 				set_mixer (20);
3298 				break;
3299 			case KEY_CMD_VOLUME_30:
3300 				set_mixer (30);
3301 				break;
3302 			case KEY_CMD_VOLUME_40:
3303 				set_mixer (40);
3304 				break;
3305 			case KEY_CMD_VOLUME_50:
3306 				set_mixer (50);
3307 				break;
3308 			case KEY_CMD_VOLUME_60:
3309 				set_mixer (60);
3310 				break;
3311 			case KEY_CMD_VOLUME_70:
3312 				set_mixer (70);
3313 				break;
3314 			case KEY_CMD_VOLUME_80:
3315 				set_mixer (80);
3316 				break;
3317 			case KEY_CMD_VOLUME_90:
3318 				set_mixer (90);
3319 				break;
3320 			case KEY_CMD_MARK_START:
3321 				file_info_block_mark (&curr_file.block_start);
3322 				break;
3323 			case KEY_CMD_MARK_END:
3324 				file_info_block_mark (&curr_file.block_end);
3325 				break;
3326 			case KEY_CMD_FAST_DIR_1:
3327 				go_to_fast_dir (1);
3328 				break;
3329 			case KEY_CMD_FAST_DIR_2:
3330 				go_to_fast_dir (2);
3331 				break;
3332 			case KEY_CMD_FAST_DIR_3:
3333 				go_to_fast_dir (3);
3334 				break;
3335 			case KEY_CMD_FAST_DIR_4:
3336 				go_to_fast_dir (4);
3337 				break;
3338 			case KEY_CMD_FAST_DIR_5:
3339 				go_to_fast_dir (5);
3340 				break;
3341 			case KEY_CMD_FAST_DIR_6:
3342 				go_to_fast_dir (6);
3343 				break;
3344 			case KEY_CMD_FAST_DIR_7:
3345 				go_to_fast_dir (7);
3346 				break;
3347 			case KEY_CMD_FAST_DIR_8:
3348 				go_to_fast_dir (8);
3349 				break;
3350 			case KEY_CMD_FAST_DIR_9:
3351 				go_to_fast_dir (9);
3352 				break;
3353 			case KEY_CMD_FAST_DIR_10:
3354 				go_to_fast_dir (10);
3355 				break;
3356 			case KEY_CMD_TOGGLE_MIXER:
3357 				debug ("Toggle mixer.");
3358 				send_int_to_srv (CMD_TOGGLE_MIXER_CHANNEL);
3359 				break;
3360 			case KEY_CMD_TOGGLE_SOFTMIXER:
3361 				debug ("Toggle softmixer.");
3362 				send_int_to_srv (CMD_TOGGLE_SOFTMIXER);
3363 				break;
3364 			case KEY_CMD_TOGGLE_EQUALIZER:
3365 				debug ("Toggle equalizer.");
3366 				send_int_to_srv (CMD_TOGGLE_EQUALIZER);
3367 				break;
3368 			case KEY_CMD_EQUALIZER_REFRESH:
3369 				debug ("Equalizer Refresh.");
3370 				send_int_to_srv (CMD_EQUALIZER_REFRESH);
3371 				break;
3372 			case KEY_CMD_EQUALIZER_PREV:
3373 				debug ("Equalizer Prev.");
3374 				send_int_to_srv (CMD_EQUALIZER_PREV);
3375 				break;
3376 			case KEY_CMD_EQUALIZER_NEXT:
3377 				debug ("Equalizer Next.");
3378 				send_int_to_srv (CMD_EQUALIZER_NEXT);
3379 				break;
3380 			case KEY_CMD_TOGGLE_MAKE_MONO:
3381 				debug ("Toggle Mono-Mixing.");
3382 				send_int_to_srv (CMD_TOGGLE_MAKE_MONO);
3383 				break;
3384 			case KEY_CMD_TOGGLE_LAYOUT:
3385 				iface_toggle_layout ();
3386 				break;
3387 			case KEY_CMD_TOGGLE_PERCENT:
3388 				iface_toggle_percent ();
3389 				break;
3390 			case KEY_CMD_PLIST_MOVE_UP:
3391 				move_item (1);
3392 				break;
3393 			case KEY_CMD_PLIST_MOVE_DOWN:
3394 				move_item (-1);
3395 				break;
3396 			case KEY_CMD_ADD_STREAM:
3397 				iface_make_entry (ENTRY_ADD_URL);
3398 				break;
3399 			case KEY_CMD_THEME_MENU:
3400 				make_theme_menu ();
3401 				break;
3402 			case KEY_CMD_EXEC1:
3403 				exec_custom_command ("ExecCommand1");
3404 				break;
3405 			case KEY_CMD_EXEC2:
3406 				exec_custom_command ("ExecCommand2");
3407 				break;
3408 			case KEY_CMD_EXEC3:
3409 				exec_custom_command ("ExecCommand3");
3410 				break;
3411 			case KEY_CMD_EXEC4:
3412 				exec_custom_command ("ExecCommand4");
3413 				break;
3414 			case KEY_CMD_EXEC5:
3415 				exec_custom_command ("ExecCommand5");
3416 				break;
3417 			case KEY_CMD_EXEC6:
3418 				exec_custom_command ("ExecCommand6");
3419 				break;
3420 			case KEY_CMD_EXEC7:
3421 				exec_custom_command ("ExecCommand7");
3422 				break;
3423 			case KEY_CMD_EXEC8:
3424 				exec_custom_command ("ExecCommand8");
3425 				break;
3426 			case KEY_CMD_EXEC9:
3427 				exec_custom_command ("ExecCommand9");
3428 				break;
3429 			case KEY_CMD_EXEC10:
3430 				exec_custom_command ("ExecCommand10");
3431 				break;
3432 			case KEY_CMD_QUEUE_TOGGLE_FILE:
3433 				queue_toggle_file ();
3434 				break;
3435 			case KEY_CMD_QUEUE_CLEAR:
3436 				cmd_clear_queue ();
3437 				break;
3438 			default:
3439 				abort ();
3440 		}
3441 	}
3442 }
3443 
3444 /* Get event from the server and handle it. */
get_and_handle_event()3445 static void get_and_handle_event ()
3446 {
3447 	int type;
3448 
3449 	if (!get_int_from_srv_noblock(&type)) {
3450 		debug ("Getting event would block.");
3451 		return;
3452 	}
3453 
3454 	server_event (type, get_event_data(type));
3455 }
3456 
3457 /* Handle events from the queue. */
dequeue_events()3458 static void dequeue_events ()
3459 {
3460 	struct event *e;
3461 
3462 	debug ("Dequeuing events...");
3463 
3464 	while ((e = event_get_first(&events))) {
3465 		server_event (e->type, e->data);
3466 		event_pop (&events);
3467 	}
3468 
3469 	debug ("done");
3470 }
3471 
3472 /* Action after CTRL-C was pressed. */
handle_interrupt()3473 static void handle_interrupt ()
3474 {
3475 	if (iface_in_entry())
3476 		iface_entry_disable ();
3477 }
3478 
init_interface(const int sock,const int logging,lists_t_strs * args)3479 void init_interface (const int sock, const int logging, lists_t_strs *args)
3480 {
3481 	FILE *logfp;
3482 
3483 	logit ("Starting MOC Interface");
3484 
3485 	logfp = NULL;
3486 	if (logging) {
3487 		logfp = fopen (INTERFACE_LOG, "a");
3488 		if (!logfp)
3489 			fatal ("Can't open client log file: %s", strerror (errno));
3490 	}
3491 	log_init_stream (logfp, INTERFACE_LOG);
3492 
3493 	/* Set locale according to the environment variables. */
3494 	if (!setlocale(LC_CTYPE, ""))
3495 		logit ("Could not set locale!");
3496 
3497 	srv_sock = sock;
3498 
3499 	file_info_reset (&curr_file);
3500 	file_info_block_init (&curr_file);
3501 	init_playlists ();
3502 	event_queue_init (&events);
3503 	keys_init ();
3504 	windows_init ();
3505 	get_server_options ();
3506 	update_mixer_name ();
3507 
3508 	signal (SIGQUIT, sig_quit);
3509 	signal (SIGTERM, sig_quit);
3510 	signal (SIGHUP, sig_quit);
3511 	signal (SIGINT, sig_interrupt);
3512 
3513 #ifdef SIGWINCH
3514 	signal (SIGWINCH, sig_winch);
3515 #endif
3516 
3517 	if (!lists_strs_empty (args)) {
3518 		process_args (args);
3519 
3520 		if (plist_count(playlist) == 0) {
3521 			if (!options_get_int("SyncPlaylist") || !use_server_playlist())
3522 				load_playlist ();
3523 			send_int_to_srv (CMD_SEND_PLIST_EVENTS);
3524 		}
3525 		else if (options_get_int("SyncPlaylist")) {
3526 			struct plist tmp_plist;
3527 
3528 			/* We have made the playlist from command line. */
3529 
3530 			/* The playlist should be now clear, but this will give
3531 			 * us the serial number of the playlist used by other
3532 			 * clients. */
3533 			plist_init (&tmp_plist);
3534 			get_server_playlist (&tmp_plist);
3535 
3536 			send_int_to_srv (CMD_SEND_PLIST_EVENTS);
3537 
3538 			send_int_to_srv (CMD_LOCK);
3539 			send_int_to_srv (CMD_CLI_PLIST_CLEAR);
3540 
3541 			plist_set_serial (playlist,
3542 					plist_get_serial(&tmp_plist));
3543 			plist_free (&tmp_plist);
3544 
3545 			change_srv_plist_serial ();
3546 
3547 			iface_set_status ("Notifying clients...");
3548 			send_items_to_clients (playlist);
3549 			iface_set_status ("");
3550 			plist_clear (playlist);
3551 			waiting_for_plist_load = 1;
3552 			send_int_to_srv (CMD_UNLOCK);
3553 
3554 			/* Now enter_first_dir() should not go to the music
3555 			 * directory. */
3556 			options_set_int ("StartInMusicDir", 0);
3557 		}
3558 	}
3559 	else {
3560 		send_int_to_srv (CMD_SEND_PLIST_EVENTS);
3561 		if (!options_get_int("SyncPlaylist") || !use_server_playlist())
3562 			load_playlist ();
3563 		enter_first_dir ();
3564 	}
3565 
3566 	/* Ask the server for queue. */
3567 	use_server_queue ();
3568 
3569 	if (options_get_int("SyncPlaylist"))
3570 		send_int_to_srv (CMD_CAN_SEND_PLIST);
3571 
3572 	update_state ();
3573 
3574 	if (options_get_int("CanStartInPlaylist")
3575 			&& curr_file.file
3576 			&& plist_find_fname(playlist, curr_file.file) != -1)
3577 		iface_switch_to_plist ();
3578 }
3579 
interface_loop()3580 void interface_loop ()
3581 {
3582 	while (want_quit == NO_QUIT) {
3583 		fd_set fds;
3584 		int ret;
3585 		struct timeval timeout = { 1, 0 };
3586 
3587 		FD_ZERO (&fds);
3588 		FD_SET (srv_sock, &fds);
3589 		FD_SET (STDIN_FILENO, &fds);
3590 
3591 		dequeue_events ();
3592 		ret = select (srv_sock + 1, &fds, NULL, NULL, &timeout);
3593 		if (ret == -1 && !want_quit && errno != EINTR)
3594 			interface_fatal ("select() failed: %s", strerror(errno));
3595 
3596 		iface_tick ();
3597 
3598 		if (ret == 0)
3599 			do_silent_seek ();
3600 
3601 #ifdef SIGWINCH
3602 		if (want_resize)
3603 			do_resize ();
3604 #endif
3605 
3606 		if (ret > 0) {
3607 			if (FD_ISSET(STDIN_FILENO, &fds)) {
3608 				struct iface_key k;
3609 
3610 				iface_get_key (&k);
3611 
3612 				clear_interrupt ();
3613 				menu_key (&k);
3614 			}
3615 
3616 			if (!want_quit) {
3617 				if (FD_ISSET(srv_sock, &fds))
3618 					get_and_handle_event ();
3619 				do_silent_seek ();
3620 			}
3621 		}
3622 		else if (user_wants_interrupt())
3623 			handle_interrupt ();
3624 
3625 		if (!want_quit)
3626 			update_mixer_value ();
3627 	}
3628 }
3629 
3630 /* Save the current directory path to a file. */
save_curr_dir()3631 static void save_curr_dir ()
3632 {
3633 	FILE *dir_file;
3634 
3635 	if (!(dir_file = fopen(create_file_name("last_directory"), "w"))) {
3636 		error ("Can't save current directory: %s", strerror(errno));
3637 		return;
3638 	}
3639 
3640 	fprintf (dir_file, "%s", cwd);
3641 	fclose (dir_file);
3642 }
3643 
3644 /* Save the playlist in .moc directory or remove the old playist if the
3645  * playlist is empty. */
save_playlist_in_moc()3646 static void save_playlist_in_moc ()
3647 {
3648 	char *plist_file = create_file_name (PLAYLIST_FILE);
3649 
3650 	if (plist_count(playlist) && options_get_int("SavePlaylist"))
3651 		save_playlist (plist_file, NULL, 1);
3652 	else
3653 		unlink (plist_file);
3654 }
3655 
interface_end()3656 void interface_end ()
3657 {
3658 	save_curr_dir ();
3659 	save_playlist_in_moc ();
3660 	if (want_quit == QUIT_SERVER)
3661 		send_int_to_srv (CMD_QUIT);
3662 	else
3663 		send_int_to_srv (CMD_DISCONNECT);
3664 	close (srv_sock);
3665 	srv_sock = -1;
3666 
3667 	windows_end ();
3668 	keys_cleanup ();
3669 
3670 	plist_free (dir_plist);
3671 	plist_free (playlist);
3672 	plist_free (queue);
3673 	free (dir_plist);
3674 	free (playlist);
3675 	free (queue);
3676 
3677 	event_queue_free (&events);
3678 
3679 	logit ("Interface exited");
3680 
3681 	log_close ();
3682 }
3683 
interface_fatal(const char * format,...)3684 void interface_fatal (const char *format, ...)
3685 {
3686 	char *msg;
3687 	va_list va;
3688 
3689 	va_start (va, format);
3690 	msg = format_msg_va (format, va);
3691 	va_end (va);
3692 
3693 	windows_end ();
3694 	fatal ("%s", msg);
3695 }
3696 
interface_error(const char * msg)3697 void interface_error (const char *msg)
3698 {
3699 	iface_error (msg);
3700 }
3701 
interface_cmdline_clear_plist(int server_sock)3702 void interface_cmdline_clear_plist (int server_sock)
3703 {
3704 	struct plist plist;
3705 	int serial;
3706 	srv_sock = server_sock; /* the interface is not initialized, so set it
3707 				   here */
3708 
3709 	plist_init (&plist);
3710 
3711 	if (options_get_int("SyncPlaylist"))
3712 		send_int_to_srv (CMD_CLI_PLIST_CLEAR);
3713 
3714 	if (recv_server_plist(&plist) && plist_get_serial(&plist)
3715 			== get_server_plist_serial()) {
3716 		send_int_to_srv (CMD_LOCK);
3717 		send_int_to_srv (CMD_GET_SERIAL);
3718 		serial = get_data_int ();
3719 		send_int_to_srv (CMD_PLIST_SET_SERIAL);
3720 		send_int_to_srv (serial);
3721 		send_int_to_srv (CMD_LIST_CLEAR);
3722 		send_int_to_srv (CMD_UNLOCK);
3723 	}
3724 
3725 	unlink (create_file_name (PLAYLIST_FILE));
3726 
3727 	plist_free (&plist);
3728 }
3729 
add_recursively(struct plist * plist,lists_t_strs * args)3730 static void add_recursively (struct plist *plist, lists_t_strs *args)
3731 {
3732 	int ix;
3733 
3734 	for (ix = 0; ix < lists_strs_size (args); ix += 1) {
3735 		int dir;
3736 		char path[PATH_MAX + 1];
3737 		const char *arg;
3738 
3739 		arg = lists_strs_at (args, ix);
3740 
3741 		if (!is_url (arg) && arg[0] != '/') {
3742 			if (arg[0] == '/')
3743 				strcpy (path, "/");
3744 			else {
3745 				strncpy (path, cwd, sizeof (path));
3746 				path[sizeof (path) - 1] = 0;
3747 			}
3748 			resolve_path (path, sizeof (path), arg);
3749 		}
3750 		else {
3751 			strncpy (path, arg, sizeof (path));
3752 			path[sizeof (path) - 1] = 0;
3753 
3754 			if (!is_url (arg))
3755 				resolve_path (path, sizeof (path), "");
3756 		}
3757 
3758 		dir = is_dir (path);
3759 
3760 		if (dir == 1)
3761 			read_directory_recurr (path, plist);
3762 		else if (is_plist_file (arg))
3763 			plist_load (plist, arg, cwd, 0);
3764 		else if ((is_url (path) || is_sound_file (path))
3765 				&& plist_find_fname (plist, path) == -1) {
3766 			int added = plist_add (plist, path);
3767 
3768 			if (is_url (path))
3769 				make_file_title (plist, added, 0);
3770 		}
3771 	}
3772 }
3773 
interface_cmdline_append(int server_sock,lists_t_strs * args)3774 void interface_cmdline_append (int server_sock, lists_t_strs *args)
3775 {
3776 	srv_sock = server_sock; /* the interface is not initialized, so set it
3777 				   here */
3778 
3779 	if (options_get_int("SyncPlaylist")) {
3780 		struct plist clients_plist;
3781 		struct plist new;
3782 
3783 		plist_init (&clients_plist);
3784 		plist_init (&new);
3785 
3786 		if (!getcwd(cwd, sizeof(cwd)))
3787 			fatal ("Can't get CWD: %s", strerror(errno));
3788 
3789 		if (recv_server_plist(&clients_plist)) {
3790 			add_recursively (&new, args);
3791 			plist_sort_fname (&new);
3792 
3793 			send_int_to_srv (CMD_LOCK);
3794 
3795 			plist_remove_common_items (&new, &clients_plist);
3796 			send_items_to_clients (&new);
3797 
3798 			if (get_server_plist_serial()
3799 					== plist_get_serial(&clients_plist))
3800 				send_playlist (&new, 0);
3801 			send_int_to_srv (CMD_UNLOCK);
3802 		}
3803 		else {
3804 			struct plist saved_plist;
3805 
3806 			plist_init (&saved_plist);
3807 
3808 			/* this checks if the file exists */
3809 			if (file_type (create_file_name (PLAYLIST_FILE))
3810 						== F_PLAYLIST)
3811 					plist_load (&saved_plist,
3812 						create_file_name (PLAYLIST_FILE),
3813 						cwd, 1);
3814 			add_recursively (&new, args);
3815 			plist_sort_fname (&new);
3816 
3817 			send_int_to_srv (CMD_LOCK);
3818 			plist_remove_common_items (&new, &saved_plist);
3819 			if (plist_get_serial(&saved_plist))
3820 				plist_set_serial(&saved_plist,
3821 						get_safe_serial());
3822 			plist_set_serial(&new, plist_get_serial(&saved_plist));
3823 			send_playlist (&new, 0);
3824 			send_int_to_srv (CMD_UNLOCK);
3825 
3826 			plist_cat (&saved_plist, &new);
3827 			if (options_get_int("SavePlaylist")) {
3828 				fill_tags (&saved_plist, TAGS_COMMENTS
3829 						| TAGS_TIME, 1);
3830 				plist_save (&saved_plist,
3831 						create_file_name (PLAYLIST_FILE),
3832 						NULL, 1);
3833 			}
3834 
3835 			plist_free (&saved_plist);
3836 		}
3837 
3838 		plist_free (&clients_plist);
3839 		plist_free (&new);
3840 	}
3841 }
3842 
interface_cmdline_play_first(int server_sock)3843 void interface_cmdline_play_first (int server_sock)
3844 {
3845 	struct plist plist;
3846 
3847 	srv_sock = server_sock; /* the interface is not initialized, so set it
3848 				   here */
3849 
3850 	if (!getcwd(cwd, sizeof(cwd)))
3851 		fatal ("Can't get CWD: %s", strerror(errno));
3852 	plist_init (&plist);
3853 
3854 	send_int_to_srv (CMD_GET_SERIAL);
3855 	plist_set_serial (&plist, get_data_int());
3856 
3857 	/* the second condition will checks if the file exists */
3858 	if (!recv_server_plist(&plist)
3859 			&& file_type (create_file_name (PLAYLIST_FILE))
3860 			== F_PLAYLIST)
3861 		plist_load (&plist, create_file_name (PLAYLIST_FILE), cwd, 1);
3862 
3863 	send_int_to_srv (CMD_LOCK);
3864 	if (get_server_plist_serial() != plist_get_serial(&plist)) {
3865 		send_playlist (&plist, 1);
3866 		send_int_to_srv (CMD_PLIST_SET_SERIAL);
3867 		send_int_to_srv (plist_get_serial(&plist));
3868 	}
3869 
3870 	send_int_to_srv (CMD_PLAY);
3871 	send_str_to_srv ("");
3872 
3873 	plist_free (&plist);
3874 }
3875 
3876 /* Request tags from the server, wait until they arrive and return them
3877  * (malloc()ed). This function assumes that the interface is not initialized. */
get_tags_no_iface(const char * file,const int tags_sel)3878 static struct file_tags *get_tags_no_iface (const char *file,
3879 		const int tags_sel)
3880 {
3881 	struct file_tags *tags = NULL;
3882 
3883 	assert (file_type(file) == F_SOUND);
3884 
3885 	send_tags_request (file, tags_sel);
3886 
3887 	while (!tags) {
3888 		int type = get_int_from_srv ();
3889 		void *data = get_event_data (type);
3890 
3891 		if (type == EV_FILE_TAGS) {
3892 			struct tag_ev_response *ev
3893 				= (struct tag_ev_response *)data;
3894 
3895 			if (!strcmp(ev->file, file))
3896 				tags = tags_dup (ev->tags);
3897 
3898 			free_tag_ev_data (ev);
3899 		}
3900 		else {
3901 			/* We can't handle other events, since this function
3902 			 * is to be invoked without the interface. */
3903 			logit ("Server sent an event which I didn't expect!");
3904 			abort ();
3905 		}
3906 	}
3907 
3908 	return tags;
3909 }
3910 
interface_cmdline_file_info(const int server_sock)3911 void interface_cmdline_file_info (const int server_sock)
3912 {
3913 	srv_sock = server_sock;	/* the interface is not initialized, so set it
3914 				   here */
3915 	init_playlists ();
3916 	file_info_reset (&curr_file);
3917 	file_info_block_init (&curr_file);
3918 
3919 	curr_file.state = get_state ();
3920 
3921 	if (curr_file.state == STATE_STOP)
3922 		puts ("State: STOP");
3923 	else {
3924 		int left;
3925 		char curr_time_str[6];
3926 		char time_left_str[6];
3927 		char time_str[6];
3928 		char *title;
3929 
3930 		if (curr_file.state == STATE_PLAY)
3931 			puts ("State: PLAY");
3932 		else if (curr_file.state == STATE_PAUSE)
3933 			puts ("State: PAUSE");
3934 
3935 		curr_file.file = get_curr_file ();
3936 
3937 		if (curr_file.file[0]) {
3938 
3939 			/* get tags */
3940 			if (file_type(curr_file.file) == F_URL) {
3941 				send_int_to_srv (CMD_GET_TAGS);
3942 				curr_file.tags = get_data_tags ();
3943 			}
3944 			else
3945 				curr_file.tags = get_tags_no_iface (
3946 						curr_file.file,
3947 						TAGS_COMMENTS | TAGS_TIME);
3948 
3949 			/* get the title */
3950 			if (curr_file.tags->title)
3951 				title = build_title (curr_file.tags);
3952 			else
3953 				title = xstrdup ("");
3954 		}
3955 		else
3956 			title = xstrdup ("");
3957 
3958 		curr_file.channels = get_channels ();
3959 		curr_file.rate = get_rate ();
3960 		curr_file.bitrate = get_bitrate ();
3961 		curr_file.curr_time = get_curr_time ();
3962 		curr_file.avg_bitrate = get_avg_bitrate ();
3963 
3964 		if (curr_file.tags->time != -1)
3965 			sec_to_min (time_str, curr_file.tags->time);
3966 		else
3967 			time_str[0] = 0;
3968 
3969 		if (curr_file.curr_time != -1) {
3970 			sec_to_min (curr_time_str, curr_file.curr_time);
3971 
3972 			if (curr_file.tags->time != -1) {
3973 				sec_to_min (curr_time_str, curr_file.curr_time);
3974 				left = curr_file.tags->time -
3975 					curr_file.curr_time;
3976 				sec_to_min (time_left_str, MAX(left, 0));
3977 			}
3978 		}
3979 		else {
3980 			strcpy (curr_time_str, "00:00");
3981 			time_left_str[0] = 0;
3982 		}
3983 
3984 		printf ("File: %s\n", curr_file.file);
3985 		printf ("Title: %s\n", title);
3986 
3987 		if (curr_file.tags) {
3988 			printf ("Artist: %s\n",
3989 					curr_file.tags->artist
3990 					? curr_file.tags->artist : "");
3991 			printf ("SongTitle: %s\n",
3992 					curr_file.tags->title
3993 					? curr_file.tags->title : "");
3994 			printf ("Album: %s\n",
3995 					curr_file.tags->album
3996 					? curr_file.tags->album : "");
3997 		}
3998 
3999 		if (curr_file.tags->time != -1) {
4000 			printf ("TotalTime: %s\n", time_str);
4001 			printf ("TimeLeft: %s\n", time_left_str);
4002 			printf ("TotalSec: %d\n", curr_file.tags->time);
4003 		}
4004 
4005 		printf ("CurrentTime: %s\n", curr_time_str);
4006 		printf ("CurrentSec: %d\n", curr_file.curr_time);
4007 
4008 		printf ("Bitrate: %dkbps\n", MAX(curr_file.bitrate, 0));
4009 		printf ("AvgBitrate: %dkbps\n", MAX(curr_file.avg_bitrate, 0));
4010 		printf ("Rate: %dkHz\n", curr_file.rate);
4011 
4012 		file_info_cleanup (&curr_file);
4013 		free (title);
4014 	}
4015 
4016 	plist_free (dir_plist);
4017 	plist_free (playlist);
4018 	plist_free (queue);
4019 }
4020 
interface_cmdline_enqueue(int server_sock,lists_t_strs * args)4021 void interface_cmdline_enqueue (int server_sock, lists_t_strs *args)
4022 {
4023 	int ix;
4024 
4025 	/* the interface is not initialized, so set it here */
4026 	srv_sock = server_sock;
4027 
4028 	if (!getcwd (cwd, sizeof (cwd)))
4029 		fatal ("Can't get CWD: %s", strerror(errno));
4030 
4031 	for (ix = 0; ix < lists_strs_size (args); ix += 1) {
4032 		const char *arg;
4033 
4034 		arg = lists_strs_at (args, ix);
4035 		if (is_sound_file (arg) || is_url (arg)) {
4036 			char *path = absolute_path (arg, cwd);
4037 			send_int_to_srv (CMD_QUEUE_ADD);
4038 			send_str_to_srv (path);
4039 			free (path);
4040 		}
4041 	}
4042 }
4043 
interface_cmdline_playit(int server_sock,lists_t_strs * args)4044 void interface_cmdline_playit (int server_sock, lists_t_strs *args)
4045 {
4046 	struct plist plist;
4047 	int ix;
4048 
4049 	srv_sock = server_sock; /* the interface is not initialized, so set it
4050 				   here */
4051 
4052 	if (!getcwd(cwd, sizeof(cwd)))
4053 		fatal ("Can't get CWD: %s", strerror(errno));
4054 
4055 	plist_init (&plist);
4056 
4057 	for (ix = 0; ix < lists_strs_size (args); ix += 1) {
4058 		const char *arg;
4059 
4060 		arg = lists_strs_at (args, ix);
4061 		if (is_url(arg) || is_sound_file(arg)) {
4062 			char *path = absolute_path (arg, cwd);
4063 			plist_add (&plist, path);
4064 			free (path);
4065 		}
4066 	}
4067 
4068 	if (plist_count(&plist)) {
4069 		int serial;
4070 
4071 		send_int_to_srv (CMD_LOCK);
4072 
4073 		send_playlist (&plist, 1);
4074 
4075 		send_int_to_srv (CMD_GET_SERIAL);
4076 		serial = get_data_int ();
4077 		send_int_to_srv (CMD_PLIST_SET_SERIAL);
4078 		send_int_to_srv (serial);
4079 
4080 		send_int_to_srv (CMD_UNLOCK);
4081 
4082 		send_int_to_srv (CMD_PLAY);
4083 		send_str_to_srv ("");
4084 	}
4085 	else
4086 		fatal ("No files added - no sound files on command line!");
4087 
4088 	plist_free (&plist);
4089 }
4090 
interface_cmdline_seek_by(int server_sock,const int seek_by)4091 void interface_cmdline_seek_by (int server_sock, const int seek_by)
4092 {
4093 	srv_sock = server_sock; /* the interface is not initialized, so set it
4094 				   here */
4095 	seek (seek_by);
4096 }
4097 
interface_cmdline_jump_to(int server_sock,const int pos)4098 void interface_cmdline_jump_to (int server_sock, const int pos)
4099 {
4100 	srv_sock = server_sock; /* the interface is not initialized, so set it here */
4101 	jump_to (pos);
4102 }
4103 
interface_cmdline_jump_to_percent(int server_sock,const int percent)4104 void interface_cmdline_jump_to_percent (int server_sock, const int percent)
4105 {
4106 	srv_sock = server_sock; /* the interface is not initialized, so set it here */
4107 	curr_file.file = get_curr_file ();
4108 	int new_pos;
4109 
4110 	if (percent >= 100) {
4111 		fprintf (stderr, "Can't jump beyond the end of file.\n");
4112 		return;
4113 	}
4114 
4115 	if (!curr_file.file[0]) {
4116 		fprintf (stderr, "Nothing is played.\n");
4117 		return;
4118 	}
4119 
4120 	if (file_type(curr_file.file) == F_URL) {
4121 		fprintf (stderr, "Can't seek in network stream.\n");
4122 		return;
4123 	}
4124 
4125 	curr_file.tags = get_tags_no_iface (curr_file.file,TAGS_TIME);
4126 	new_pos = (percent*curr_file.tags->time)/100;
4127 	printf("Jumping to: %ds. Total time is: %ds\n", new_pos, curr_file.tags->time);
4128 	jump_to (new_pos);
4129 }
4130 
interface_cmdline_adj_volume(int server_sock,const char * arg)4131 void interface_cmdline_adj_volume (int server_sock, const char *arg)
4132 {
4133 	srv_sock = server_sock;
4134 
4135 	if(arg[0] == '+')
4136 		adjust_mixer(atoi(arg + 1));
4137 	else if(arg[0] == '-')
4138 		adjust_mixer(atoi(arg)); /* atoi can handle -  */
4139 	else if (arg[0] != 0)
4140 		set_mixer(atoi(arg));
4141 }
4142 
interface_cmdline_set(int server_sock,char * arg,const int val)4143 void interface_cmdline_set (int server_sock, char *arg, const int val)
4144 {
4145 	srv_sock = server_sock;
4146 	char *last = NULL;
4147 	char *tok;
4148 
4149 	tok = strtok_r(arg, ",", &last);
4150 
4151 	while(tok) {
4152 
4153 		if(!strncmp (tok, "shuffle", 8) || !strncmp (tok,"s",2))
4154 			tok = "Shuffle";
4155 		else if(!strncmp (tok, "autonext", 9) || !strncmp (tok, "n",2))
4156 			tok = "AutoNext";
4157 		else if(!strncmp (tok, "repeat", 7) || !strncmp (tok, "r", 2))
4158 			tok = "Repeat";
4159 		else {
4160 			fprintf (stderr, "Unknown option '%s'\n", tok);
4161 			break;
4162 		}
4163 
4164 		if(val == 2) {
4165 			send_int_to_srv (CMD_GET_OPTION);
4166 			send_str_to_srv (tok);
4167 			options_set_int (tok, get_data_int());
4168 		}
4169 
4170 		send_int_to_srv (CMD_SET_OPTION);
4171 		send_str_to_srv (tok);
4172 
4173 		if(val == 2)
4174 			send_int_to_srv (!options_get_int(tok));
4175 		else
4176 			send_int_to_srv (val);
4177 
4178 		tok = strtok_r (NULL, ",", &last);
4179 	}
4180 }
4181 
4182 /* Print formatted info
4183 State       %state
4184 File        %file
4185 Title       %title
4186 Artist      %artist
4187 SongTitle   %song
4188 Album       %album
4189 TotalTime   %tt
4190 TimeLeft    %tl
4191 TotalSec    %ts
4192 CurrentTime %ct
4193 CurrentSec  %cs
4194 Bitrate     %b
4195 Rate        %r
4196 */
interface_cmdline_formatted_info(const int server_sock,const char * format_str)4197 void interface_cmdline_formatted_info (const int server_sock,
4198 		const char *format_str)
4199 {
4200 	typedef struct {
4201 		char *state;
4202 		char *file;
4203 		char *title;
4204 		char *artist;
4205 		char *song;
4206 		char *album;
4207 		char *totaltime;
4208 		char *timeleft;
4209 		char *totalsec;
4210 		char *currenttime;
4211 		char *currentsec;
4212 		char *bitrate;
4213 		char *rate;
4214 	} info_t;
4215 
4216 	char curr_time_str[6];
4217 	char time_left_str[6];
4218 	char time_str[6];
4219 	char time_sec_str[5];
4220 	char curr_time_sec_str[5];
4221 	char file_bitrate_str[4];
4222 	char file_rate_str[3];
4223 
4224 	char *fmt, *str;
4225 	info_t str_info;
4226 
4227 	srv_sock = server_sock;	/* the interface is not initialized, so set it
4228 				   here */
4229 	init_playlists ();
4230 	file_info_reset (&curr_file);
4231 	file_info_block_init (&curr_file);
4232 
4233 	curr_file.state = get_state ();
4234 
4235 	/* extra paranoid about struct data */
4236 	memset(&str_info, 0, sizeof(str_info));
4237 	curr_time_str[0] = time_left_str[0] = time_str[0] =
4238 	  time_sec_str[0] = curr_time_sec_str[0] =
4239 	  file_bitrate_str[0] = file_rate_str[0] = '\0';
4240 
4241 	str_info.currenttime = curr_time_str;
4242 	str_info.timeleft = time_left_str;
4243 	str_info.totaltime = time_str;
4244 	str_info.totalsec = time_sec_str;
4245 	str_info.currentsec = curr_time_sec_str;
4246 	str_info.bitrate = file_bitrate_str;
4247 	str_info.rate = file_rate_str;
4248 
4249 	if (curr_file.state == STATE_STOP)
4250 		str_info.state = "STOP";
4251 	else {
4252 		int left;
4253 
4254 		if (curr_file.state == STATE_PLAY)
4255 			str_info.state = "PLAY";
4256 		else if (curr_file.state == STATE_PAUSE)
4257 			str_info.state = "PAUSE";
4258 
4259 		curr_file.file = get_curr_file ();
4260 
4261 		if (curr_file.file[0]) {
4262 
4263 			/* get tags */
4264 			if (file_type(curr_file.file) == F_URL) {
4265 				send_int_to_srv (CMD_GET_TAGS);
4266 				curr_file.tags = get_data_tags ();
4267 			}
4268 			else
4269 				curr_file.tags = get_tags_no_iface (
4270 						curr_file.file,
4271 						TAGS_COMMENTS | TAGS_TIME);
4272 
4273 			/* get the title */
4274 			if (curr_file.tags->title)
4275 				str_info.title = build_title (curr_file.tags);
4276 			else
4277 				str_info.title = xstrdup ("");
4278 		}
4279 		else
4280 			str_info.title = xstrdup ("");
4281 
4282 		curr_file.channels = get_channels ();
4283 		curr_file.rate = get_rate ();
4284 		curr_file.bitrate = get_bitrate ();
4285 		curr_file.curr_time = get_curr_time ();
4286 
4287 		if (curr_file.tags->time != -1)
4288 			sec_to_min (time_str, curr_file.tags->time);
4289 		else
4290 			time_str[0] = 0;
4291 
4292 		if (curr_file.curr_time != -1) {
4293 			sec_to_min (curr_time_str, curr_file.curr_time);
4294 
4295 			if (curr_file.tags->time != -1) {
4296 				sec_to_min (curr_time_str, curr_file.curr_time);
4297 				left = curr_file.tags->time -
4298 					curr_file.curr_time;
4299 				sec_to_min (time_left_str, MAX(left, 0));
4300 			}
4301 		}
4302 		else {
4303 			strcpy (curr_time_str, "00:00");
4304 			time_left_str[0] = 0;
4305 		}
4306 
4307 		str_info.file = curr_file.file;
4308 
4309 		str_info.artist =
4310 					curr_file.tags->artist ? curr_file.tags->artist : NULL;
4311 		str_info.song = curr_file.tags->title ? curr_file.tags->title : NULL;
4312 		str_info.album = curr_file.tags->album ? curr_file.tags->album : NULL;
4313 
4314 		if (curr_file.tags->time != -1)
4315 			snprintf(time_sec_str, 5, "%d", curr_file.tags->time);
4316 
4317 		snprintf(curr_time_sec_str, 5, "%d", curr_file.curr_time);
4318 		snprintf(file_bitrate_str, 4, "%d", MAX(curr_file.bitrate, 0));
4319 		snprintf(file_rate_str, 3, "%d", curr_file.rate);
4320 	}
4321 
4322 	/* string with formatting tags */
4323 	fmt = xstrdup(format_str);
4324 
4325 	fmt = str_repl(fmt, "%state", str_info.state);
4326 	fmt = str_repl(fmt, "%file",
4327 			str_info.file ? str_info.file : "");
4328 	fmt = str_repl(fmt, "%title",
4329 			str_info.title ? str_info.title : "");
4330 	fmt = str_repl(fmt, "%artist",
4331 			str_info.artist ? str_info.artist : "");
4332 	fmt = str_repl(fmt, "%song",
4333 			str_info.song ? str_info.song : "");
4334 	fmt = str_repl(fmt, "%album",
4335 			str_info.album ? str_info.album : "");
4336 	fmt = str_repl(fmt, "%tt",
4337 			str_info.totaltime ? str_info.totaltime : "");
4338 	fmt = str_repl(fmt, "%tl",
4339 			str_info.timeleft ? str_info.timeleft : "");
4340 	fmt = str_repl(fmt, "%ts",
4341 			str_info.totalsec ? str_info.totalsec : "");
4342 	fmt = str_repl(fmt, "%ct",
4343 			str_info.currenttime ? str_info.currenttime : "");
4344 	fmt = str_repl(fmt, "%cs",
4345 			str_info.currentsec ? str_info.currentsec : "");
4346 	fmt = str_repl(fmt, "%b",
4347 			str_info.bitrate ? str_info.bitrate : "");
4348 	fmt = str_repl(fmt, "%r",
4349 			str_info.rate ? str_info.rate : "");
4350 	fmt = str_repl(fmt, "\\n", "\n");
4351 
4352 	str = build_title_with_format (curr_file.tags, fmt);
4353 
4354 	printf("%s\n", str);
4355 	free(str);
4356 	free(fmt);
4357 
4358 	if (str_info.title)
4359 		free(str_info.title);
4360 
4361 	if (curr_file.state != STATE_STOP)
4362 		file_info_cleanup (&curr_file);
4363 
4364 	plist_free (dir_plist);
4365 	plist_free (playlist);
4366 	plist_free (queue);
4367 
4368 }
4369