2 * This file is part of DisOrder.
3 * Copyright (C) 2004, 2005, 2006 Richard Kettlewell
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, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
30 #include <sys/types.h>
31 #include <sys/socket.h>
37 #include "configuration.h"
42 #include "inputline.h"
49 const char *playing_states
[] = {
62 /* the head of the queue is played next, so normally we add to the tail */
63 struct queue_entry qhead
= { &qhead
, &qhead
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
65 /* the head of the recent list is the oldest thing, the tail the most recently
67 struct queue_entry phead
= { &phead
, &phead
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
71 /* add new entry @n@ to a doubly linked list just after @b@ */
72 static void l_add(struct queue_entry
*b
, struct queue_entry
*n
) {
79 /* remove an entry from a doubly-linked list */
80 static void l_remove(struct queue_entry
*node
) {
81 node
->next
->prev
= node
->prev
;
82 node
->prev
->next
= node
->next
;
85 #define VALUE(q, offset, type) *(type *)((char *)q + offset)
87 static int unmarshall_long(char *data
, struct queue_entry
*q
,
89 void (*error_handler
)(const char *, void *),
91 if(xstrtol(&VALUE(q
, offset
, long), data
, 0, 0)) {
92 error_handler(strerror(errno
), u
);
98 static const char *marshall_long(const struct queue_entry
*q
, size_t offset
) {
102 n
= byte_snprintf(buffer
, sizeof buffer
, "%ld", VALUE(q
, offset
, long));
104 fatal(errno
, "error converting int");
105 else if((size_t)n
>= sizeof buffer
)
106 fatal(0, "long converted to decimal is too long");
107 return xstrdup(buffer
);
110 static int unmarshall_string(char *data
, struct queue_entry
*q
,
112 void attribute((unused
)) (*error_handler
)(const char *, void *),
113 void attribute((unused
)) *u
) {
114 VALUE(q
, offset
, char *) = data
;
118 static const char *marshall_string(const struct queue_entry
*q
, size_t offset
) {
119 return VALUE(q
, offset
, char *);
122 static int unmarshall_time_t(char *data
, struct queue_entry
*q
,
124 void (*error_handler
)(const char *, void *),
128 if(xstrtoll(&ul
, data
, 0, 0)) {
129 error_handler(strerror(errno
), u
);
132 VALUE(q
, offset
, time_t) = ul
;
136 static const char *marshall_time_t(const struct queue_entry
*q
, size_t offset
) {
140 n
= byte_snprintf(buffer
, sizeof buffer
,
141 "%"PRIdMAX
, (intmax_t)VALUE(q
, offset
, time_t));
143 fatal(errno
, "error converting time");
144 else if((size_t)n
>= sizeof buffer
)
145 fatal(0, "time converted to decimal is too long");
146 return xstrdup(buffer
);
149 static int unmarshall_state(char *data
, struct queue_entry
*q
,
151 void (*error_handler
)(const char *, void *),
155 if((n
= table_find(playing_states
, 0, sizeof (char *),
156 sizeof playing_states
/ sizeof *playing_states
,
158 D(("state=[%s] n=%d", data
, n
));
159 error_handler("invalid state", u
);
162 VALUE(q
, offset
, enum playing_state
) = n
;
166 static const char *marshall_state(const struct queue_entry
*q
, size_t offset
) {
167 return playing_states
[VALUE(q
, offset
, enum playing_state
)];
170 #define F(n, h) { #n, offsetof(struct queue_entry, n), marshall_##h, unmarshall_##h }
172 static const struct field
{
175 const char *(*marshall
)(const struct queue_entry
*q
, size_t offset
);
176 int (*unmarshall
)(char *data
, struct queue_entry
*q
, size_t offset
,
177 void (*error_handler
)(const char *, void *),
180 /* Keep this table sorted. */
184 F(scratched
, string
),
187 F(submitter
, string
),
193 int queue_unmarshall(struct queue_entry
*q
, const char *s
,
194 void (*error_handler
)(const char *, void *),
199 if(!(vec
= split(s
, &nvec
, SPLIT_QUOTES
, error_handler
, u
)))
201 return queue_unmarshall_vec(q
, nvec
, vec
, error_handler
, u
);
204 int queue_unmarshall_vec(struct queue_entry
*q
, int nvec
, char **vec
,
205 void (*error_handler
)(const char *, void *),
210 error_handler("invalid marshalled queue format", u
);
214 D(("key %s value %s", vec
[0], vec
[1]));
215 if((n
= TABLE_FIND(fields
, struct field
, name
, *vec
)) < 0) {
216 error_handler("unknown key in queue data", u
);
219 if(fields
[n
].unmarshall(vec
[1], q
, fields
[n
].offset
, error_handler
, u
))
227 void queue_fix_sofar(struct queue_entry
*q
) {
230 /* Fake up SOFAR field for currently-playing tracks that don't have it filled
231 * in by the speaker process. XXX this horrible bodge should go away when we
232 * have a more general implementation of pausing as that field will always
233 * have to be right for the playing track. */
234 if((q
->state
== playing_started
235 || q
->state
== playing_paused
)
236 && q
->type
& DISORDER_PLAYER_PAUSES
237 && (q
->type
& DISORDER_PLAYER_TYPEMASK
) != DISORDER_PLAYER_RAW
) {
239 if(q
->uptopause
== -1) /* Don't know how far thru. */
241 else if(q
->lastresumed
) /* Has been paused and resumed. */
242 sofar
= q
->uptopause
+ time(0) - q
->lastresumed
;
243 else /* Currently paused. */
244 sofar
= q
->uptopause
;
245 } else /* Never been paused. */
246 sofar
= time(0) - q
->played
;
251 char *queue_marshall(const struct queue_entry
*q
) {
253 const char *vec
[sizeof fields
/ sizeof *fields
], *v
;
257 for(n
= 0; n
< sizeof fields
/ sizeof *fields
; ++n
)
258 if((v
= fields
[n
].marshall(q
, fields
[n
].offset
))) {
259 vec
[n
] = quoteutf8(v
);
260 len
+= strlen(vec
[n
]) + strlen(fields
[n
].name
) + 2;
263 s
= r
= xmalloc_noptr(len
);
264 for(n
= 0; n
< sizeof fields
/ sizeof *fields
; ++n
)
267 s
+= strlen(strcpy(s
, fields
[n
].name
));
269 s
+= strlen(strcpy(s
, vec
[n
]));
274 static void queue_read_error(const char *msg
,
276 fatal(0, "error parsing queue %s: %s", (const char *)u
, msg
);
279 static void queue_do_read(struct queue_entry
*head
, const char *path
) {
282 struct queue_entry
*q
;
284 if(!(fp
= fopen(path
, "r"))) {
286 return; /* no queue */
287 fatal(errno
, "error opening %s", path
);
289 head
->next
= head
->prev
= head
;
290 while(!inputline(path
, fp
, &buffer
, '\n')) {
291 q
= xmalloc(sizeof *q
);
292 queue_unmarshall(q
, buffer
, queue_read_error
, (void *)path
);
296 fatal(0, "incomplete queue entry in %s", path
);
297 l_add(head
->prev
, q
);
299 if(ferror(fp
)) fatal(errno
, "error reading %s", path
);
303 void queue_read(void) {
304 queue_do_read(&qhead
, config_get_file("queue"));
307 void recent_read(void) {
308 struct queue_entry
*q
;
310 queue_do_read(&phead
, config_get_file("recent"));
311 /* reset pcount after loading */
320 static void queue_do_write(const struct queue_entry
*head
, const char *path
) {
323 struct queue_entry
*q
;
325 byte_xasprintf(&tmp
, "%s.new", path
);
326 if(!(fp
= fopen(tmp
, "w"))) fatal(errno
, "error opening %s", tmp
);
327 for(q
= head
->next
; q
!= head
; q
= q
->next
)
328 if(fprintf(fp
, "%s\n", queue_marshall(q
)) < 0)
329 fatal(errno
, "error writing %s", tmp
);
330 if(fclose(fp
) < 0) fatal(errno
, "error closing %s", tmp
);
331 if(rename(tmp
, path
) < 0) fatal(errno
, "error replacing %s", path
);
334 void queue_write(void) {
335 queue_do_write(&qhead
, config_get_file("queue"));
338 void recent_write(void) {
339 queue_do_write(&phead
, config_get_file("recent"));
342 void queue_id(struct queue_entry
*q
) {
343 static unsigned long serial
;
347 a
[0] = serial
++ & 0xFFFFFFFFUL
;
348 a
[1] = time(0) & 0xFFFFFFFFUL
;
349 a
[2] = getpid() & 0xFFFFFFFFUL
;
350 basen(a
, 3, buffer
, sizeof buffer
, 62);
351 q
->id
= xstrdup(buffer
);
354 struct queue_entry
*queue_add(const char *track
, const char *submitter
,
356 struct queue_entry
*q
, *beforeme
;
358 q
= xmalloc(sizeof *q
);
359 q
->track
= xstrdup(track
);
360 q
->submitter
= submitter ?
xstrdup(submitter
) : 0;
361 q
->state
= playing_unplayed
;
369 l_add(qhead
.prev
, q
);
371 case WHERE_BEFORE_RANDOM
:
372 /* We want to find the point in the queue before the block of random tracks
375 while(beforeme
->prev
!= &qhead
376 && beforeme
->prev
->state
== playing_random
)
377 beforeme
= beforeme
->prev
;
378 l_add(beforeme
->prev
, q
);
381 /* submitter will be a null pointer for a scratch */
383 notify_queue(track
, submitter
);
384 eventlog_raw("queue", queue_marshall(q
), (const char *)0);
388 int queue_move(struct queue_entry
*q
, int delta
, const char *who
) {
392 /* not the most efficient approach but hopefuly relatively comprehensible:
393 * the idea is that for each step we determine which nodes are affected, and
394 * fill in all the links starting at the 'prev' end and moving towards the
397 while(delta
> 0 && q
->prev
!= &qhead
) {
398 struct queue_entry
*n
, *p
, *pp
;
413 while(delta
< 0 && q
->next
!= &qhead
) {
414 struct queue_entry
*n
, *p
, *nn
;
430 info("user %s moved %s", who
, q
->id
);
431 notify_queue_move(q
->track
, who
);
432 sprintf(buffer
, "%d", moved
);
433 eventlog("moved", who
, (char *)0);
439 static int find_in_list(struct queue_entry
*needle
,
440 int nqs
, struct queue_entry
**qs
) {
443 for(n
= 0; n
< nqs
; ++n
)
449 void queue_moveafter(struct queue_entry
*target
,
450 int nqs
, struct queue_entry
**qs
,
452 struct queue_entry
*q
;
459 while(find_in_list(target
, nqs
, qs
))
460 target
= target
->prev
;
462 for(n
= 0; n
< nqs
; ++n
) {
467 /* Log the individual tracks */
468 info("user %s moved %s", who
, q
->id
);
469 notify_queue_move(q
->track
, who
);
471 /* Report that the queue changed to the event log */
472 eventlog("moved", who
, (char *)0);
475 void queue_remove(struct queue_entry
*which
, const char *who
) {
477 info("user %s removed %s", who
, which
->id
);
478 notify_queue_move(which
->track
, who
);
480 eventlog("removed", which
->id
, who
, (const char *)0);
484 struct queue_entry
*queue_find(const char *key
) {
485 struct queue_entry
*q
;
488 q
!= &qhead
&& strcmp(q
->track
, key
) && strcmp(q
->id
, key
);
491 return q
!= &qhead ? q
: 0;
494 void queue_played(struct queue_entry
*q
) {
495 while(pcount
&& pcount
>= config
->history
) {
496 eventlog("recent_removed", phead
.next
->id
, (char *)0);
497 l_remove(phead
.next
);
500 if(config
->history
) {
501 eventlog_raw("recent_added", queue_marshall(q
), (char *)0);
502 l_add(phead
.prev
, q
);