[tpwd] Fix segfault when exactly one argument given
[tinyapps.git] / mpd-state.c
bloba848448071a03258b432096c3f47d2c7388db61e
1 /*
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
24 #define _BSD_SOURCE
25 #define _DEFAULT_SOURCE
27 #include "libmpdclient.h"
29 #include <unistd.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdlib.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);
70 connect_to_mpd();
72 if (opt_restore) {
73 restore();
74 mpd_closeConnection(conn);
75 return 0;
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);
82 return 0;
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"
94 " -s ommit state\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",
100 program_name);
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) {
112 int opt;
114 program_name = strrchr(argv[0], '/');
115 program_name = program_name ? program_name + 1 : *argv;
117 if (argc>1 && !strcmp(argv[1], "--help")) {
118 usage();
119 exit(0);
122 opterr = 0;
123 while ((opt = getopt(argc, argv, "-hraspo"))!=-1) {
124 switch (opt) {
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;
138 case 1:
139 if (!hostarg) { hostarg = optarg; break; }
140 if (!portarg) { portarg = optarg; break; }
141 ERR("invalid argument: %s", optarg);
142 exit(1);
144 default:
145 ERR("invalid option: %c", optopt);
146 exit(1);
153 /********** Connects to MPD **********/
154 static void connect_to_mpd(void) {
155 const char *host, *password;
156 char *test;
157 long port;
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, '@');
170 if (test) {
171 *test = 0;
172 host = test + 1;
173 password = hostarg;
174 } else {
175 host = hostarg;
176 password = 0;
179 /* Port */
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);
187 exit(1);
190 /* Connect */
191 conn = mpd_newConnection(host, port, 10);
192 if (conn->error) {
193 ERR2("could not connect to MPD (%s:%ld)", host, port);
194 free(hostarg);
195 print_error_and_exit(2);
197 free(hostarg);
200 /* Send password */
201 if (password) {
202 mpd_sendPasswordCommand(conn, password);
203 if (conn->error) {
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;
230 else {
231 ERR("invalid state: %s", word);
232 exit(3);
235 } else if (!strcmp(word, "current")) {
236 songnum = atoi(str);
237 } else if (!strcmp(word, "time")) {
238 seconds = atoi(str);
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;
255 ++str;
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;
266 if (atoi(str+1)) {
267 mpd_sendEnableOutputCommand(conn, atoi(buffer));
268 } else {
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) {
288 mpd_Status *status;
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);
349 puts("outputs_end");
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) {
357 if (conn->error) {
358 ERR("error: %s", conn->errorStr);
359 exit(error_code);