aac: Collect all interesting ID3 frames
[cmus.git] / main.c
blobb68ba2536cd731181330dec5794045fabefb3c69
1 /*
2 * Copyright 2005-2006 Timo Hirvonen
3 */
5 #include "prog.h"
6 #include "file.h"
7 #include "path.h"
8 #include "xmalloc.h"
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <sys/types.h>
13 #include <sys/socket.h>
14 #include <sys/un.h>
15 #include <netinet/in.h>
16 #include <arpa/inet.h>
17 #include <netdb.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stdarg.h>
21 #include <errno.h>
23 static int sock;
24 static int raw_args = 0;
25 static char *passwd;
27 static void gethostbyname_failed(void)
29 const char *error = "Unknown error.";
31 switch (h_errno) {
32 case HOST_NOT_FOUND:
33 case NO_ADDRESS:
34 error = "Host not found.";
35 break;
36 case NO_RECOVERY:
37 error = "A non-recoverable name server error.";
38 break;
39 case TRY_AGAIN:
40 error = "A temporary error occurred on an authoritative name server.";
41 break;
43 die("gethostbyname: %s\n", error);
46 static void write_line(const char *line)
48 if (write_all(sock, line, strlen(line)) == -1)
49 die_errno("write");
52 static void send_cmd(const char *format, ...)
54 char buf[512];
55 va_list ap;
57 va_start(ap, format);
58 vsnprintf(buf, sizeof(buf), format, ap);
59 va_end(ap);
61 write_line(buf);
64 static void remote_connect(const char *address)
66 union {
67 struct sockaddr sa;
68 struct sockaddr_un un;
69 struct sockaddr_in in;
70 } addr;
71 int addrlen;
73 if (strchr(address, '/')) {
74 if (passwd)
75 warn("password ignored for unix connections\n");
76 passwd = NULL;
78 addr.sa.sa_family = AF_UNIX;
79 strncpy(addr.un.sun_path, address, sizeof(addr.un.sun_path) - 1);
81 addrlen = sizeof(addr.un);
82 } else {
83 char *s = strchr(address, ':');
84 int port = DEFAULT_PORT;
85 struct hostent *hent;
87 if (!passwd)
88 die("password required for tcp/ip connection\n");
90 if (s) {
91 *s++ = 0;
92 port = atoi(s);
94 hent = gethostbyname(address);
95 if (!hent)
96 gethostbyname_failed();
98 addr.sa.sa_family = hent->h_addrtype;
99 switch (addr.sa.sa_family) {
100 case AF_INET:
101 memcpy(&addr.in.sin_addr, hent->h_addr_list[0], hent->h_length);
102 addr.in.sin_port = htons(port);
104 addrlen = sizeof(addr.in);
105 break;
106 default:
107 die("unsupported address type\n");
111 sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
112 if (sock == -1)
113 die_errno("socket");
115 if (connect(sock, &addr.sa, addrlen)) {
116 if (errno == ENOENT || errno == ECONNREFUSED) {
117 /* "cmus-remote -C" can be used to check if cmus is running */
118 if (!raw_args)
119 warn("cmus is not running\n");
120 exit(1);
122 die_errno("connect");
125 if (passwd)
126 send_cmd("%s\n", passwd);
129 static char *file_url_absolute(const char *str)
131 char *absolute;
133 if (strncmp(str, "http://", 7) == 0)
134 return xstrdup(str);
136 absolute = path_absolute(str);
137 if (absolute == NULL)
138 die_errno("get_current_dir_name");
139 return absolute;
142 enum flags {
143 FLAG_SERVER,
144 FLAG_PASSWD,
145 FLAG_HELP,
146 FLAG_VERSION,
148 FLAG_PLAY,
149 FLAG_PAUSE,
150 FLAG_STOP,
151 FLAG_NEXT,
152 FLAG_PREV,
153 FLAG_FILE,
154 FLAG_REPEAT,
155 FLAG_SHUFFLE,
156 FLAG_VOLUME,
157 FLAG_SEEK,
159 FLAG_LIBRARY,
160 FLAG_PLAYLIST,
161 FLAG_QUEUE,
162 FLAG_CLEAR,
164 FLAG_RAW
165 #define NR_FLAGS (FLAG_RAW + 1)
168 static struct option options[NR_FLAGS + 1] = {
169 { 0, "server", 1 },
170 { 0, "passwd", 1 },
171 { 0, "help", 0 },
172 { 0, "version", 0 },
174 { 'p', "play", 0 },
175 { 'u', "pause", 0 },
176 { 's', "stop", 0 },
177 { 'n', "next", 0 },
178 { 'r', "prev", 0 },
179 { 'f', "file", 1 },
180 { 'R', "repeat", 0 },
181 { 'S', "shuffle", 0 },
182 { 'v', "volume", 1 },
183 { 'k', "seek", 1 },
185 { 'l', "library", 0 },
186 { 'P', "playlist", 0 },
187 { 'q', "queue", 0 },
188 { 'c', "clear", 0 },
190 { 'C', "raw", 0 },
191 { 0, NULL, 0 }
194 static int flags[NR_FLAGS] = { 0, };
196 static const char *usage =
197 "Usage: %s [OPTION]... [FILE|DIR|PLAYLIST]...\n"
198 " or: %s -C COMMAND...\n"
199 " or: %s\n"
200 "Control cmus through socket.\n"
201 "\n"
202 " --server ADDR connect using ADDR instead of ~/.cmus/socket\n"
203 " ADDR is either a UNIX socket or host[:port]\n"
204 " WARNING: using TCP/IP is insecure!\n"
205 " --passwd PASSWD password to use for TCP/IP connection\n"
206 " --help display this help and exit\n"
207 " --version " VERSION "\n"
208 "\n"
209 "Cooked mode:\n"
210 " -p, --play player-play\n"
211 " -u, --pause player-pause\n"
212 " -s, --stop player-stop\n"
213 " -n, --next player-next\n"
214 " -r, --prev player-prev\n"
215 " -f, --file player-play FILE\n"
216 " -R, --repeat toggle repeat\n"
217 " -S, --shuffle toggle shuffle\n"
218 " -v, --volume VOL vol VOL\n"
219 " -k, --seek SEEK seek SEEK\n"
220 "\n"
221 " -l, --library modify library instead of playlist\n"
222 " -P, --playlist modify playlist (default)\n"
223 " -q, --queue modify play queue instead of playlist\n"
224 " -c, --clear clear playlist, library (-l) or play queue (-q)\n"
225 "\n"
226 " Add FILE/DIR/PLAYLIST to playlist, library (-l) or play queue (-q).\n"
227 "\n"
228 "Raw mode:\n"
229 " -C, --raw treat arguments (instead of stdin) as raw commands\n"
230 "\n"
231 " By default cmus-remote reads raw commands from stdin (one command per line).\n"
232 "\n"
233 "Report bugs to <cmus-devel@lists.sourceforge.net>.\n";
235 int main(int argc, char *argv[])
237 char server_buf[256];
238 char *server = NULL;
239 char *play_file = NULL;
240 char *volume = NULL;
241 char *seek = NULL;
242 int i, nr_cmds = 0;
243 int context = 'p';
245 program_name = argv[0];
246 argv++;
247 while (1) {
248 int idx;
249 char *arg;
251 idx = get_option(&argv, options, &arg);
252 if (idx < 0)
253 break;
255 flags[idx] = 1;
256 switch ((enum flags)idx) {
257 case FLAG_HELP:
258 printf(usage, program_name, program_name, program_name);
259 return 0;
260 case FLAG_VERSION:
261 printf("cmus " VERSION "\nCopyright 2004-2006 Timo Hirvonen\n");
262 return 0;
263 case FLAG_SERVER:
264 server = arg;
265 break;
266 case FLAG_PASSWD:
267 passwd = arg;
268 break;
269 case FLAG_VOLUME:
270 volume = arg;
271 nr_cmds++;
272 break;
273 case FLAG_SEEK:
274 seek = arg;
275 nr_cmds++;
276 break;
277 case FLAG_FILE:
278 play_file = arg;
279 nr_cmds++;
280 break;
281 case FLAG_LIBRARY:
282 context = 'l';
283 break;
284 case FLAG_PLAYLIST:
285 context = 'p';
286 break;
287 case FLAG_QUEUE:
288 context = 'q';
289 break;
290 case FLAG_PLAY:
291 case FLAG_PAUSE:
292 case FLAG_STOP:
293 case FLAG_NEXT:
294 case FLAG_PREV:
295 case FLAG_REPEAT:
296 case FLAG_SHUFFLE:
297 case FLAG_CLEAR:
298 nr_cmds++;
299 break;
300 case FLAG_RAW:
301 raw_args = 1;
302 break;
306 if (nr_cmds && raw_args)
307 die("don't mix raw and cooked stuff\n");
309 if (server == NULL) {
310 const char *config_dir = getenv("CMUS_HOME");
312 if (config_dir && config_dir[0]) {
313 snprintf(server_buf, sizeof(server_buf), "%s/socket", config_dir);
314 } else {
315 const char *home = getenv("HOME");
317 if (!home)
318 die("error: environment variable HOME not set\n");
319 snprintf(server_buf, sizeof(server_buf), "%s/.cmus/socket", home);
321 server = server_buf;
324 remote_connect(server);
326 if (raw_args) {
327 while (*argv)
328 send_cmd("%s\n", *argv++);
329 return 0;
332 if (nr_cmds == 0 && argv[0] == NULL) {
333 char line[512];
335 while (fgets(line, sizeof(line), stdin))
336 write_line(line);
337 return 0;
340 if (flags[FLAG_CLEAR])
341 send_cmd("clear -%c\n", context);
342 for (i = 0; argv[i]; i++) {
343 char *filename = file_url_absolute(argv[i]);
345 send_cmd("add -%c %s\n", context, filename);
346 free(filename);
348 if (flags[FLAG_REPEAT])
349 send_cmd("toggle repeat\n");
350 if (flags[FLAG_SHUFFLE])
351 send_cmd("toggle shuffle\n");
352 if (flags[FLAG_STOP])
353 send_cmd("player-stop\n");
354 if (flags[FLAG_NEXT])
355 send_cmd("player-next\n");
356 if (flags[FLAG_PREV])
357 send_cmd("player-prev\n");
358 if (flags[FLAG_PLAY])
359 send_cmd("player-play\n");
360 if (flags[FLAG_PAUSE])
361 send_cmd("player-pause\n");
362 if (flags[FLAG_FILE])
363 send_cmd("player-play %s\n", file_url_absolute(play_file));
364 if (volume)
365 send_cmd("vol %s\n", volume);
366 if (seek)
367 send_cmd("seek %s\n", seek);
368 return 0;