2 * Prints/restores MPD's state.
3 * Copyright (c) 2005 by Avuton Olrich (avuton/AT/gmail.com)
4 * Copyright (c) 2005-2007 by Michal Nazarewicz (mina86/AT/mina86.com)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 * This is part of Tiny Applications Collection
20 * -> http://tinyapps.sourceforge.net/
23 #define _POSIX_C_SOURCE 2
25 #define _DEFAULT_SOURCE
27 #include "libmpdclient.h"
35 static const char *program_name
= 0;
36 #define ERR(msg, arg) fprintf(stderr, "%s: " msg "\n", program_name, arg)
37 #define ERR2(msg, arg, arg2) fprintf(stderr, "%s: " msg "\n", \
38 program_name, arg, arg2)
41 #define DEFAULT_HOST "localhost"
42 #define DEFAULT_PORT "6600"
43 #define LINELENGTH FILENAME_MAX+50
46 /********** Some global variables and stuff **********/
47 static char opt_skip_state
, opt_skip_playlist
, opt_skip_outputs
= 0,
48 opt_restore
= 0, opt_add
= 0;
49 static mpd_Connection
*conn
= 0;
50 static char *hostarg
= 0;
51 static const char *portarg
= 0;
54 /********** Functions declaration **********/
55 static void usage(void);
56 static void parse_args(int argc
, char **argv
);
57 static void connect_to_mpd(void);
58 static void print_error_and_exit(int exit_code
);
60 static void print_status(void);
61 static void print_playlist(void);
62 static void print_outputs(void);
63 static void restore(void);
67 /********** Main **********/
68 int main(int argc
, char **argv
) {
69 parse_args(argc
, argv
);
74 mpd_closeConnection(conn
);
78 if (!opt_skip_state
) print_status();
79 if (!opt_skip_playlist
) print_playlist();
80 if (!opt_skip_outputs
) print_outputs();
81 mpd_closeConnection(conn
);
87 /********** Usage information **********/
88 static void usage(void) {
89 printf("mpd-state 0.12.1 (c) 2005 by Avuton Olrich & Michal Nazarewicz\n"
90 "usage: %s [ -rasSpPoO ] [ <host> [ <port> ]]]\n"
91 " -r restere the state read from stdin (default is to\n"
92 " print status to stdout)\n"
93 " -a add to the playlist (instead of replacing the playlist)\n"
95 " -S ommit everything but state\n"
96 " -p ommit playlsit\n"
97 " -P ommit everything but playlsit\n"
98 " -o ommit outputs (useful if you are using old MPD)\n"
99 " -O ommit everything but outputs\n",
101 /* be nice to C89 and make string no longer then 509 chars */
102 puts( " <host> hostname MPD is running; if not set MPD_HOST is used;\n"
103 " if that is also missing '" DEFAULT_HOST
"' is assumed\n"
104 " <port> port MPD is listining; if not set MPD_PORT is used;\n"
105 " if that is also missing " DEFAULT_PORT
" is assumed");
110 /********** Parse arguments **********/
111 static void parse_args(int argc
, char **argv
) {
114 program_name
= strrchr(argv
[0], '/');
115 program_name
= program_name
? program_name
+ 1 : *argv
;
117 if (argc
>1 && !strcmp(argv
[1], "--help")) {
123 while ((opt
= getopt(argc
, argv
, "-hraspo"))!=-1) {
125 case 'h': usage(); exit(0);
126 case 'r': opt_restore
= 1; break;
127 case 'a': opt_add
= 1; break;
128 case 's': opt_skip_state
= 1; break;
129 case 'S': opt_skip_state
= 0;
130 opt_skip_playlist
= opt_skip_outputs
= 1; break;
131 case 'p': opt_skip_playlist
= 1; break;
132 case 'P': opt_skip_playlist
= 0;
133 opt_skip_state
= opt_skip_outputs
= 1; break;
134 case 'o': opt_skip_outputs
= 1; break;
135 case 'O': opt_skip_outputs
= 0;
136 opt_skip_state
= opt_skip_playlist
= 1; break;
139 if (!hostarg
) { hostarg
= optarg
; break; }
140 if (!portarg
) { portarg
= optarg
; break; }
141 ERR("invalid argument: %s", optarg
);
145 ERR("invalid option: %c", optopt
);
153 /********** Connects to MPD **********/
154 static void connect_to_mpd(void) {
155 const char *host
, *password
;
159 /* Host and password */
160 if (!hostarg
&& !(hostarg
= getenv("MPD_HOST"))) {
161 hostarg
= (char*)DEFAULT_HOST
;
165 size_t count
= strlen(hostarg
) + 1;
166 test
= malloc(count
);
167 hostarg
= memcpy(test
, hostarg
, count
);
169 test
= strchr(hostarg
, '@');
180 if (!portarg
&& !(portarg
= getenv("MPD_PORT"))) {
181 portarg
= DEFAULT_PORT
;
184 port
= strtol(portarg
, &test
,10);
185 if (port
<0 || *test
!='\0') {
186 ERR("invalid port: %s", portarg
);
191 conn
= mpd_newConnection(host
, port
, 10);
193 ERR2("could not connect to MPD (%s:%ld)", host
, port
);
195 print_error_and_exit(2);
202 mpd_sendPasswordCommand(conn
, password
);
204 ERR("%s", "could not connect to MPD, invalid password");
205 print_error_and_exit(2);
207 mpd_finishCommand(conn
);
208 print_error_and_exit(2);
214 /********** Restore state from stdin **********/
215 static void restore(void) {
216 int state
= 0, seconds
= MPD_PLAY_AT_BEGINNING
, songnum
= 0;
217 char buffer
[LINELENGTH
], *word
, *str
;
219 mpd_sendCommandListBegin(conn
);
220 if (!opt_skip_playlist
&& !opt_add
) mpd_sendClearCommand(conn
);
222 while (fgets(buffer
, LINELENGTH
, stdin
)) {
223 word
= strtok(buffer
, ": \f\r\t\v\n");
224 str
= strtok(0, ": \f\r\t\v\n");
226 if (!strcmp(word
, "state")) {
227 if (!strcmp(word
, "play")) state
= MPD_STATUS_STATE_PLAY
;
228 else if (!strcmp(word
, "pause")) state
= MPD_STATUS_STATE_PAUSE
;
229 else if (!strcmp(word
, "stop")) state
= MPD_STATUS_STATE_STOP
;
231 ERR("invalid state: %s", word
);
235 } else if (!strcmp(word
, "current")) {
237 } else if (!strcmp(word
, "time")) {
240 } else if (!strcmp(word
, "volume")) {
241 if (!opt_skip_state
) mpd_sendSetvolCommand(conn
, atoi(str
));
242 } else if (!strcmp(word
, "random")) {
243 if (!opt_skip_state
) mpd_sendRandomCommand(conn
, atoi(str
));
244 } else if (!strcmp(word
, "repeat")) {
245 if (!opt_skip_state
) mpd_sendRepeatCommand(conn
, atoi(str
));
246 } else if (!strcmp(word
, "crossfade")) {
247 if (!opt_skip_state
) mpd_sendCrossfadeCommand(conn
, atoi(str
));
249 } else if (!strcmp(word
, "playlist_begin")) {
250 while (fgets(buffer
, LINELENGTH
, stdin
) &&
251 strcmp(word
, "playlist_end\n") &&
252 (str
= strchr(buffer
, ':'))) {
253 if (!opt_skip_playlist
) continue;
256 str
[strlen(str
)-1] = '\0';
257 mpd_sendAddCommand(conn
, str
);
260 } else if (!strcmp(word
,"outputs_begin")) {
261 while (fgets(buffer
, LINELENGTH
, stdin
) &&
262 strcmp(word
, "outputs_end\n") &&
263 (str
= strchr(buffer
, ':'))) {
264 if (opt_skip_outputs
) continue;
267 mpd_sendEnableOutputCommand(conn
, atoi(buffer
));
269 mpd_sendDisableOutputCommand(conn
, atoi(buffer
));
275 if (!opt_skip_state
&& state
!=MPD_STATUS_STATE_STOP
) {
276 mpd_sendSeekCommand(conn
, songnum
, seconds
);
279 mpd_sendCommandListEnd(conn
);
280 print_error_and_exit(2);
281 mpd_finishCommand(conn
);
286 /********** Prints MPD's status to stdout **********/
287 static void print_status(void) {
290 mpd_sendStatusCommand(conn
);
291 print_error_and_exit(3);
292 status
= mpd_getStatus(conn
);
293 print_error_and_exit(3);
294 mpd_nextListOkCommand(conn
);
295 print_error_and_exit(3);
297 printf("state: %s\n", status
->state
==MPD_STATUS_STATE_PLAY
? "play"
298 : (status
->state
==MPD_STATUS_STATE_PAUSE
? "pause" : "stop"));
299 printf("current: %i\n", status
->song
);
300 printf("time: %i\n", status
->elapsedTime
);
301 printf("random: %i\n", status
->random
? 1 : 0 );
302 printf("repeat: %i\n", status
->repeat
? 1 : 0 );
303 printf("crossfade: %i\n",status
->crossfade
? 1 : 0 );
304 printf("volume: %i\n", status
->volume
);
306 mpd_finishCommand(conn
);
307 mpd_freeStatus(status
);
308 print_error_and_exit(3);
313 /********** Prints playlsit to stdout **********/
314 static void print_playlist(void) {
315 mpd_InfoEntity
*entity
;
317 puts("playlist_begin");
319 mpd_sendPlaylistInfoCommand(conn
,-1);
320 while ((entity
= mpd_getNextInfoEntity(conn
))) {
321 if (entity
->type
==MPD_INFO_ENTITY_TYPE_SONG
) {
322 mpd_Song
*song
= entity
->info
.song
;
323 printf("%i:%s\n", song
->pos
, song
->file
);
325 mpd_freeInfoEntity(entity
);
327 mpd_finishCommand(conn
);
329 puts("playlist_end");
330 print_error_and_exit(3);
335 /********** Prints outputs to stdout **********/
336 static void print_outputs(void) {
337 mpd_OutputEntity
* output
;
338 puts("outputs_begin");
340 mpd_sendOutputsCommand(conn
);
341 while ((output
= mpd_getNextOutput(conn
))) {
342 if (output
->id
>= 0) {
343 printf("%i:%i:%s\n", output
->id
, output
->enabled
, output
->name
);
345 mpd_freeOutputElement(output
);
347 mpd_finishCommand(conn
);
350 print_error_and_exit(3);
355 /********** Prints error end exits if there is any **********/
356 static void print_error_and_exit(int error_code
) {
358 ERR("error: %s", conn
->errorStr
);