Update copyright notices.
[state-utils.git] / src / state-restore.c
bloba33d9614112ac2d040cce118e8449c2cdc388ead
1 /*
2 * state-utils
3 * (c) 2005-2009 by Avuton Olrich <avuton@gmail.com>
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.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #include <sys/types.h>
21 #include <pwd.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <getopt.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <unistd.h>
30 #include "libmpdclient.h"
31 #include "conn.h"
32 #include "state.h"
34 #define MPD_OUTPUT_STATE_DISABLED 0
35 #define LINELENGTH FILENAME_MAX+50 /* 50 to give adequate amount for command and args */
36 #define NEXTWORD(word) { word = strtok(NULL,":"); while(isspace(word[0])) { word++; } }
37 #define NEXTINT(ret,word) { NEXTWORD(word); ret = atoi(word); }
39 static struct Config {
40 short int amend;
41 short int noutput;
42 short int nplaylist;
43 unsigned int wait;
44 char *extopts;
45 } config = {
46 0, 0, 0, 0, NULL};
48 static void readandexit(char *filename, char *dirname)
50 errno = 0;
52 access(dirname, F_OK);
53 check_perror(errno, "Cannot read state file directory");
55 access(dirname, X_OK);
56 check_perror(errno, "Cannot read state file directory");
58 access(dirname, R_OK);
59 check_perror(errno, "Cannot read state file directory");
61 access(filename, F_OK);
62 check_perror(errno, "Cannot read state file");
65 static void restore_playlist(mpd_Connection * conn,
66 FILE * fp,
67 char *word, char buffer[LINELENGTH], int songnum)
69 char *string;
70 /* int number; */
71 int curnum = 0;
73 while (fgets(buffer, LINELENGTH, fp) &&
74 strcmp(word, "playlist_end\n") != 0 && strchr(buffer, ':')) {
75 if (config.nplaylist == 1 && songnum != curnum) {
76 curnum++;
77 continue;
80 * strtok_r would be called here, but this should
81 * be a bit more efficent since we discard the number.
83 string = strchr(buffer, ':');
84 *string = '\0';
85 string++;
86 string[strlen(string) - 1] = '\0';
87 /* number = atoi(buffer); */
88 mpd_sendAddCommand(conn, string);
89 curnum++;
93 static void restore_outputs(mpd_Connection * conn,
94 FILE * fp, char *word, char buffer[LINELENGTH])
96 int number, namlen, stat;
97 char *status, *name;
99 while (fgets(buffer, LINELENGTH, fp) &&
100 strcmp(word, "outputs_end\n") != 0 && strchr(buffer, ':')) {
101 number = atoi(buffer);
103 status = strchr(buffer, ':');
104 *status = '\0';
105 status++;
106 stat = atoi(status);
108 name = strchr(status, ':');
109 *name = '\0';
110 name++;
112 namlen = (strlen(name) - 1);
113 if (name[namlen] == '\n') {
114 name[namlen] = '\0';
117 if (stat == MPD_OUTPUT_STATE_DISABLED) {
118 mpd_sendDisableOutputCommand(conn, number);
119 } else {
120 mpd_sendEnableOutputCommand(conn, number);
125 static void handle_state_contents(mpd_Connection * conn, FILE * fp)
127 /* conv is a temporary converting variable */
128 int conv = 0;
131 * We need these stored, incase the state
132 * is play or pause
134 int state = 0;
135 int seconds = MPD_PLAY_AT_BEGINNING;
136 int songnum = 0;
137 int volume = -1;
139 char buffer[LINELENGTH];
140 char *word;
142 while (fgets(buffer, LINELENGTH, fp) && buffer != NULL) {
143 word = strtok(buffer, ":");
145 /* Special cases first */
146 /* notice the strcmp ! ==0 */
147 if (config.amend == 1 && strcmp(word, "playlist_begin\n")) {
148 ; /* Do nothing */
149 } else if (config.noutput == 1 &&
150 strcmp(word, "outputs_begin\n") == 0) {
151 ; /* Do nothing */
152 } else if (strcmp(word, "state") == 0) {
153 NEXTWORD(word);
154 if (strncmp(word, "play", strlen("play")) == 0) {
155 state = MPD_STATUS_STATE_PLAY;
156 } else if (strncmp(word, "pause", strlen("pause")) == 0) {
157 state = MPD_STATUS_STATE_PAUSE;
158 } else if (strncmp(word, "stop", strlen("stop")) == 0) {
159 state = MPD_STATUS_STATE_STOP;
160 } else {
161 puts("Invalid state\n");
162 exit(2);
164 } else if (strcmp(word, "current") == 0) {
165 NEXTINT(songnum, word);
166 } else if (strcmp(word, "volume") == 0) {
167 /* Setvol to 0, try to reduce the seeking sounds */
168 mpd_sendSetvolCommand(conn, volume);
170 NEXTINT(volume, word);
171 } else if (strcmp(word, "time") == 0) {
172 NEXTINT(seconds, word);
173 } else if (strcmp(word, "random") == 0) {
174 NEXTINT(conv, word);
175 mpd_sendRandomCommand(conn, conv);
176 } else if (strcmp(word, "repeat") == 0) {
177 NEXTINT(conv, word);
178 mpd_sendRepeatCommand(conn, conv);
179 } else if (strcmp(word, "single") == 0) {
180 NEXTINT(conv, word);
181 mpd_sendSingleCommand(conn, conv);
182 } else if (strcmp(word, "consume") == 0) {
183 NEXTINT(conv, word);
184 mpd_sendConsumeCommand(conn, conv);
185 } else if (strcmp(word, "crossfade") == 0) {
186 NEXTINT(conv, word);
187 mpd_sendCrossfadeCommand(conn, conv);
188 } else if (strcmp(word, "playlist_begin\n") == 0) {
189 if (config.amend == 0) {
190 mpd_sendClearCommand(conn);
192 restore_playlist(conn, fp, word, buffer, songnum);
193 } else if (strcmp(word, "outputs_begin\n") == 0) {
194 restore_outputs(conn, fp, word, buffer);
196 word = strtok(NULL, ":");
199 if (config.nplaylist == 1) {
200 mpd_sendSeekCommand(conn, 0, seconds);
201 } else if (state != MPD_STATUS_STATE_STOP && config.amend == 0) {
202 mpd_sendSeekCommand(conn, songnum, seconds);
203 if (state == MPD_STATUS_STATE_PAUSE) {
204 mpd_sendPauseCommand(conn, 1);
208 if (volume != -1) {
209 mpd_sendSetvolCommand(conn, volume);
213 __attribute__ ((noreturn))
214 static void print_usage(int exitnum)
216 printf("Usage: state-restore [OPTION] [STATE NAME]");
217 printf("\nRestore a MPD state.");
218 printf("\nExample: state-restore -a my_cool_state");
219 printf("\n\n");
220 printf
221 ("\n -a, --amend\t\tAmend the playlist to the current MPD playlist, ignoring the actual state.");
222 printf("\n -o, --no-outputs\tOmit the outputs when restoring");
223 printf("\n -p, --no-playlist\tOnly restore the playing song");
224 printf
225 ("\n -w, --wait=TIMEOUT\tWait until the music player has stopped to restore the playlist. Check MPD every TIMEOUT seconds.");
226 printf("\n\nReport bugs to Avuton Olrich <avuton@gmail.com>");
227 printf("\nSee 'man 1 state-restore' for more information\n");
228 exit(exitnum);
231 static FILE *get_file(void)
233 char filename[FILENAME_MAX];
234 char dirname[FILENAME_MAX];
235 FILE *fp;
237 sprintf(dirname, "%s/.mpd_states", get_home_dir());
239 if (!(config.extopts)) {
240 sprintf(filename, "%s/default", dirname);
241 } else {
242 sprintf(filename, "%s/%s", dirname, config.extopts);
245 fp = fopen(filename, "r");
246 if (!fp) {
247 readandexit(filename, dirname);
249 return fp;
252 static void parse_args(int argc, char **argv)
254 int c;
255 static struct option long_options[] = {
256 {"amend", 0, 0, 'a'},
257 {"help", 0, 0, 'h'},
258 {"no-outputs", 0, 0, 'o'},
259 {"no-playlist", 0, 0, 'p'},
260 {"wait", 1, 0, 'w'},
261 {0, 0, 0, 0}
264 while (1) {
265 int option_index = 0;
267 c = getopt_long(argc, argv, "ahopw:",
268 long_options, &option_index);
270 if (c == -1)
271 break;
273 switch (c) {
275 case 'a':
276 config.amend = 1;
277 break;
278 case 'o':
279 config.noutput = 1;
280 break;
281 case 'p':
282 config.nplaylist = 1;
283 break;
284 case 'w':
285 if (optarg) {
286 config.wait = atoi(optarg);
287 if (config.wait) {
288 if (config.wait < 1) {
289 puts("Wait time must be longer than 0");
290 print_usage(15);
292 } else {
293 puts("Wait time must be a digit between 1 and 65535");
294 print_usage(16);
297 * This really shouldn't be possible, but let's not take the chance
298 * this should be handled by getopts
300 } else {
301 puts("Unknown Error!");
302 print_usage(17);
304 break;
305 case 'h':
306 case '?':
307 print_usage(0);
308 break;
309 default:
310 print_usage(10);
311 break;
315 if (config.amend && config.nplaylist) {
316 puts("Incompatible options");
317 print_usage(12);
320 if (argc > optind) {
321 if ((argc - optind) == 1) {
322 config.extopts = argv[optind++];
323 } else if ((argc - optind) > 1) {
324 puts("Too many arguments, only restore one state at a time.");
325 print_usage(13);
330 static void handle_wait(mpd_Connection * conn)
332 mpd_Status *status;
334 while (1) {
335 if (!conn) {
336 conn = setup_connection(NULL, NULL);
338 mpd_sendStatusCommand(conn);
339 status = mpd_getStatus(conn);
340 if (status->state == MPD_STATUS_STATE_STOP) {
341 config.wait = 0;
343 printErrorAndExit(conn);
344 mpd_finishCommand(conn);
345 sleep(config.wait);
346 if (config.wait == 0) {
347 break;
351 mpd_freeStatus(status);
354 int main(int argc, char **argv)
356 FILE *fp;
357 mpd_Connection *conn;
359 parse_args(argc, argv);
361 fp = get_file();
363 conn = setup_connection(NULL, NULL);
364 printErrorAndExit(conn);
366 if (config.wait > 0) {
367 handle_wait(conn);
370 mpd_sendCommandListBegin(conn);
371 handle_state_contents(conn, fp);
372 mpd_sendCommandListEnd(conn);
373 printErrorAndExit(conn);
374 mpd_finishCommand(conn);
375 mpd_closeConnection(conn);
377 return 0;