Improve searching URLs
[cmus.git] / main.c
bloba0a8bc11ba581d1136d6102d76c31b74eedf753a
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 " --passwd PASSWD password to use for TCP/IP connection\n"
205 " --help display this help and exit\n"
206 " --version " VERSION "\n"
207 "\n"
208 "Cooked mode:\n"
209 " -p, --play player-play\n"
210 " -u, --pause player-pause\n"
211 " -s, --stop player-stop\n"
212 " -n, --next player-next\n"
213 " -r, --prev player-prev\n"
214 " -f, --file player-play FILE\n"
215 " -R, --repeat toggle repeat\n"
216 " -S, --shuffle toggle shuffle\n"
217 " -v, --volume VOL vol VOL\n"
218 " -k, --seek SEEK seek SEEK\n"
219 "\n"
220 " -l, --library modify library instead of playlist\n"
221 " -P, --playlist modify playlist (default)\n"
222 " -q, --queue modify play queue instead of playlist\n"
223 " -c, --clear clear playlist, library (-l) or play queue (-q)\n"
224 "\n"
225 " Add FILE/DIR/PLAYLIST to playlist, library (-l) or play queue (-q).\n"
226 "\n"
227 "Raw mode:\n"
228 " -C, --raw treat arguments (instead of stdin) as raw commands\n"
229 "\n"
230 " By default cmus-remote reads raw commands from stdin (one command per line).\n"
231 "\n"
232 "Report bugs to <cmus-devel@lists.sourceforge.net>.\n";
234 int main(int argc, char *argv[])
236 char server_buf[256];
237 char *server = NULL;
238 char *play_file = NULL;
239 char *volume = NULL;
240 char *seek = NULL;
241 int i, nr_cmds = 0;
242 int context = 'p';
244 program_name = argv[0];
245 argv++;
246 while (1) {
247 int idx;
248 char *arg;
250 idx = get_option(&argv, options, &arg);
251 if (idx < 0)
252 break;
254 flags[idx] = 1;
255 switch ((enum flags)idx) {
256 case FLAG_HELP:
257 printf(usage, program_name, program_name, program_name);
258 return 0;
259 case FLAG_VERSION:
260 printf("cmus " VERSION "\nCopyright 2004-2006 Timo Hirvonen\n");
261 return 0;
262 case FLAG_SERVER:
263 server = arg;
264 break;
265 case FLAG_PASSWD:
266 passwd = arg;
267 break;
268 case FLAG_VOLUME:
269 volume = arg;
270 nr_cmds++;
271 break;
272 case FLAG_SEEK:
273 seek = arg;
274 nr_cmds++;
275 break;
276 case FLAG_FILE:
277 play_file = arg;
278 nr_cmds++;
279 break;
280 case FLAG_LIBRARY:
281 context = 'l';
282 break;
283 case FLAG_PLAYLIST:
284 context = 'p';
285 break;
286 case FLAG_QUEUE:
287 context = 'q';
288 break;
289 case FLAG_PLAY:
290 case FLAG_PAUSE:
291 case FLAG_STOP:
292 case FLAG_NEXT:
293 case FLAG_PREV:
294 case FLAG_REPEAT:
295 case FLAG_SHUFFLE:
296 case FLAG_CLEAR:
297 nr_cmds++;
298 break;
299 case FLAG_RAW:
300 raw_args = 1;
301 break;
305 if (nr_cmds && raw_args)
306 die("don't mix raw and cooked stuff\n");
308 if (server == NULL) {
309 const char *config_dir = getenv("CMUS_HOME");
311 if (config_dir && config_dir[0]) {
312 snprintf(server_buf, sizeof(server_buf), "%s/socket", config_dir);
313 } else {
314 const char *home = getenv("HOME");
316 if (!home)
317 die("error: environment variable HOME not set\n");
318 snprintf(server_buf, sizeof(server_buf), "%s/.cmus/socket", home);
320 server = server_buf;
323 remote_connect(server);
325 if (raw_args) {
326 while (*argv)
327 send_cmd("%s\n", *argv++);
328 return 0;
331 if (nr_cmds == 0 && argv[0] == NULL) {
332 char line[512];
334 while (fgets(line, sizeof(line), stdin))
335 write_line(line);
336 return 0;
339 if (flags[FLAG_CLEAR])
340 send_cmd("clear -%c\n", context);
341 for (i = 0; argv[i]; i++) {
342 char *filename = file_url_absolute(argv[i]);
344 send_cmd("add -%c %s\n", context, filename);
345 free(filename);
347 if (flags[FLAG_REPEAT])
348 send_cmd("toggle repeat\n");
349 if (flags[FLAG_SHUFFLE])
350 send_cmd("toggle shuffle\n");
351 if (flags[FLAG_STOP])
352 send_cmd("player-stop\n");
353 if (flags[FLAG_NEXT])
354 send_cmd("player-next\n");
355 if (flags[FLAG_PREV])
356 send_cmd("player-prev\n");
357 if (flags[FLAG_PLAY])
358 send_cmd("player-play\n");
359 if (flags[FLAG_PAUSE])
360 send_cmd("player-pause\n");
361 if (flags[FLAG_FILE])
362 send_cmd("player-play %s\n", file_url_absolute(play_file));
363 if (volume)
364 send_cmd("vol %s\n", volume);
365 if (seek)
366 send_cmd("seek %s\n", seek);
367 return 0;