Compare commits
2088 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2784d65618 | ||
![]() |
47ea69233b | ||
![]() |
a884e37de1 | ||
![]() |
0102a8665a | ||
![]() |
d34ae0850c | ||
![]() |
6526de024a | ||
![]() |
5e1e92626c | ||
![]() |
7fee85c80a | ||
![]() |
5d87a274a5 | ||
![]() |
57e862712a | ||
![]() |
ddb5390d88 | ||
![]() |
fce20e514e | ||
![]() |
af66ed2505 | ||
![]() |
ea4068757d | ||
![]() |
2b10ecfa37 | ||
![]() |
f7eb2b697e | ||
![]() |
da67260c95 | ||
![]() |
ab9c9068d4 | ||
![]() |
6b4d7d7315 | ||
![]() |
313d1d5d83 | ||
![]() |
b7d6133593 | ||
![]() |
5b6bb114ad | ||
![]() |
56f082c9d4 | ||
![]() |
a1b798e555 | ||
![]() |
c91e08fbfd | ||
![]() |
f882434547 | ||
![]() |
05ad335ae9 | ||
![]() |
7faeb2ff2b | ||
![]() |
fdd76b3461 | ||
![]() |
e490e5d0ab | ||
![]() |
3a05c421e0 | ||
![]() |
afc70c120e | ||
![]() |
d7f80eab68 | ||
![]() |
e30b356eb0 | ||
![]() |
09a0803116 | ||
![]() |
20ffedc745 | ||
![]() |
0b1ad27ba8 | ||
![]() |
6a1b2f0387 | ||
![]() |
fb34519b96 | ||
![]() |
91fed47648 | ||
![]() |
c05691b546 | ||
![]() |
6b3b8c6f2e | ||
![]() |
a191db84f2 | ||
![]() |
e4d69f38b0 | ||
![]() |
97fc001180 | ||
![]() |
42a09ff17a | ||
![]() |
c170fed6f9 | ||
![]() |
8e38b4f83c | ||
![]() |
db4ae19246 | ||
![]() |
82a89c6bfe | ||
![]() |
166c70cab3 | ||
![]() |
de78fe38c8 | ||
![]() |
96fa69ff6b | ||
![]() |
39d94bd3ea | ||
![]() |
695ca29274 | ||
![]() |
02e8da6c98 | ||
![]() |
0ea5f4ac3a | ||
![]() |
47c50c079d | ||
![]() |
3b0fea5fae | ||
![]() |
443516cdda | ||
![]() |
57e0cc5442 | ||
![]() |
73f45d87d5 | ||
![]() |
ae88ba986e | ||
![]() |
d6247902ec | ||
![]() |
a566c28a49 | ||
![]() |
2eddb63a83 | ||
![]() |
fc7d5b055d | ||
![]() |
87c8953e8e | ||
![]() |
45d27a52f1 | ||
![]() |
d22acc59c9 | ||
![]() |
c064e8d62f | ||
![]() |
099a2cb586 | ||
![]() |
042fe2a9d0 | ||
![]() |
ff1c1107f3 | ||
![]() |
41a3fd0fd0 | ||
![]() |
b9169a5670 | ||
![]() |
2ecd5fa28d | ||
![]() |
e719b6cc41 | ||
![]() |
67b8124a1d | ||
![]() |
51ec499c89 | ||
![]() |
3a4df25db2 | ||
![]() |
37cf78ef45 | ||
![]() |
727c622659 | ||
![]() |
74a778e65f | ||
![]() |
1fc0c9fe8a | ||
![]() |
740d8ec5ab | ||
![]() |
0bcc477d46 | ||
![]() |
35297f8d4f | ||
![]() |
6f4202408c | ||
![]() |
0b6548a282 | ||
![]() |
faf5821816 | ||
![]() |
188673b746 | ||
![]() |
287c70e361 | ||
![]() |
f6b44af998 | ||
![]() |
84c3d9674f | ||
![]() |
47d3758820 | ||
![]() |
573ff3a24f | ||
![]() |
32fcc22cb3 | ||
![]() |
daba1238b5 | ||
![]() |
d125567f4a | ||
![]() |
993eca9327 | ||
![]() |
e314844a4d | ||
![]() |
017bc564af | ||
![]() |
e076ff9085 | ||
![]() |
2789493a5f | ||
![]() |
4ed0635447 | ||
![]() |
1904e504be | ||
![]() |
834715ea2f | ||
![]() |
380a3bbff4 | ||
![]() |
6219326e02 | ||
![]() |
6c4de5106b | ||
![]() |
e936705840 | ||
![]() |
9b2603b6f6 | ||
![]() |
88856b847c | ||
![]() |
f679878f7b | ||
![]() |
f33acf8758 | ||
![]() |
a846a4c643 | ||
![]() |
899c2bb9cc | ||
![]() |
f8f95e2dbd | ||
![]() |
77c63511d8 | ||
![]() |
0be5a6ab2b | ||
![]() |
9802e74859 | ||
![]() |
8e7d6eb151 | ||
![]() |
5c18e4f114 | ||
![]() |
fd2eafa7c6 | ||
![]() |
422b8472fe | ||
![]() |
5b213b0504 | ||
![]() |
ad27d06979 | ||
![]() |
c4b1251d0a | ||
![]() |
01891f8815 | ||
![]() |
adb68450ce | ||
![]() |
2520f6fe49 | ||
![]() |
c9278bfcdf | ||
![]() |
8b838ff9ea | ||
![]() |
154bdf0bca | ||
![]() |
ed436c6f0c | ||
![]() |
ae5dd2da4f | ||
![]() |
f4b61e8c8d | ||
![]() |
e49a3d377f | ||
![]() |
b631b552bb | ||
![]() |
3f21581a81 | ||
![]() |
bb62ecf157 | ||
![]() |
a268ab35ea | ||
![]() |
62baec1841 | ||
![]() |
7bca61f5bb | ||
![]() |
ecf12a60e8 | ||
![]() |
6de85cb047 | ||
![]() |
39257717d8 | ||
![]() |
b54762a8f6 | ||
![]() |
bcae86196c | ||
![]() |
7adfea8ca2 | ||
![]() |
eab1a77683 | ||
![]() |
84f5e0c0de | ||
![]() |
2ce3900071 | ||
![]() |
bf2c1f3e9e | ||
![]() |
8ccd8a008d | ||
![]() |
9513c1a8d6 | ||
![]() |
0a1be2bfc3 | ||
![]() |
8c6727949c | ||
![]() |
ff9b717bf6 | ||
![]() |
8c834a4ff6 | ||
![]() |
8c01004219 | ||
![]() |
8555b65c50 | ||
![]() |
304fa5ecac | ||
![]() |
88e630170e | ||
![]() |
da8bdd62c8 | ||
![]() |
54abeab80b | ||
![]() |
1dc8a9f0e7 | ||
![]() |
a62d54425c | ||
![]() |
c8b2591ac2 | ||
![]() |
ee1d8e1091 | ||
![]() |
672fc8d79b | ||
![]() |
84ff1a680a | ||
![]() |
90c899407a | ||
![]() |
55868eecd0 | ||
![]() |
50dc98367c | ||
![]() |
c4d3030d24 | ||
![]() |
c6f101884b | ||
![]() |
c0c0526fc8 | ||
![]() |
375c88b110 | ||
![]() |
a65f63747b | ||
![]() |
a21edddf27 | ||
![]() |
a88c23b6e8 | ||
![]() |
216e37bf33 | ||
![]() |
7bb3987acc | ||
![]() |
6a147a17af | ||
![]() |
2b7529e905 | ||
![]() |
1ed321f964 | ||
![]() |
2aee1b86f3 | ||
![]() |
b5fc21b9f4 | ||
![]() |
44581dbef5 | ||
![]() |
205448c1e8 | ||
![]() |
2d11c6ab29 | ||
![]() |
6859c22b69 | ||
![]() |
2d5413fc3b | ||
![]() |
a6aa0e4cbf | ||
![]() |
163848ab3b | ||
![]() |
03747ba93e | ||
![]() |
095c390df7 | ||
![]() |
20597b3632 | ||
![]() |
4728735acf | ||
![]() |
9dcbd005f0 | ||
![]() |
0ad2eb34c7 | ||
![]() |
0e8a15e813 | ||
![]() |
5b0d6a59cf | ||
![]() |
fd3dc7e5fb | ||
![]() |
c37edfd3e9 | ||
![]() |
4a99b1df4b | ||
![]() |
12e9b7eafa | ||
![]() |
de1261ba28 | ||
![]() |
5ee5a89a7f | ||
![]() |
1ad2475f9e | ||
![]() |
5b5675cc12 | ||
![]() |
e699f6781e | ||
![]() |
5f13c1cd9c | ||
![]() |
a577944ab5 | ||
![]() |
17ec3b0c2d | ||
![]() |
fb75137540 | ||
![]() |
6f87164ad6 | ||
![]() |
a4d82cfe1e | ||
![]() |
9f21eee2ec | ||
![]() |
a40246d312 | ||
![]() |
4a5aad0948 | ||
![]() |
85ae7e9c9a | ||
![]() |
2098b94b47 | ||
![]() |
59ad6265a1 | ||
![]() |
067572c6dd | ||
![]() |
7f03f68fcc | ||
![]() |
de862f9f1b | ||
![]() |
b5e31c89c0 | ||
![]() |
e898400fbd | ||
![]() |
3ac7de7a22 | ||
![]() |
ade66e7ece | ||
![]() |
6673f9e39d | ||
![]() |
2db2c0f0b7 | ||
![]() |
17dc638c18 | ||
![]() |
d6e28c42e5 | ||
![]() |
6d475c40de | ||
![]() |
ed7891bf01 | ||
![]() |
e17da71a70 | ||
![]() |
610bef9ff5 | ||
![]() |
961c7d0f78 | ||
![]() |
c1ba47beee | ||
![]() |
ac8e5be9f4 | ||
![]() |
c76952534e | ||
![]() |
f0060718de | ||
![]() |
982ab9e496 | ||
![]() |
b74bcf2274 | ||
![]() |
645cb5833d | ||
![]() |
dc328e0c4a | ||
![]() |
92c85bd20d | ||
![]() |
f629eb8cb2 | ||
![]() |
e4f41ff61d | ||
![]() |
dae6ecb680 | ||
![]() |
a57a7b1a76 | ||
![]() |
03073b366b | ||
![]() |
1bd8a9e744 | ||
![]() |
0a5c991ab5 | ||
![]() |
93deb84499 | ||
![]() |
c4d4011c63 | ||
![]() |
3d12f8d246 | ||
![]() |
c3e720279c | ||
![]() |
f1027ed198 | ||
![]() |
fe3c5e4e8e | ||
![]() |
d110131ad6 | ||
![]() |
10bc1a9acc | ||
![]() |
3f899f83ff | ||
![]() |
fc05768374 | ||
![]() |
03b57df630 | ||
![]() |
4de3b6dc80 | ||
![]() |
c7bbfef19a | ||
![]() |
7f1130b52b | ||
![]() |
2d696f46c3 | ||
![]() |
2f43e4bc66 | ||
![]() |
f8c23488c9 | ||
![]() |
1e2cda9239 | ||
![]() |
20cba9e89f | ||
![]() |
f6d67ac260 | ||
![]() |
74904b9cf2 | ||
![]() |
82059645f1 | ||
![]() |
13e9f18403 | ||
![]() |
875821f2ba | ||
![]() |
cc3be3aeed | ||
![]() |
72af3c0489 | ||
![]() |
65e54f6ed1 | ||
![]() |
10196496f3 | ||
![]() |
86f08862e4 | ||
![]() |
83c726a34f | ||
![]() |
a0415f73d4 | ||
![]() |
93068f10ec | ||
![]() |
7142c81c05 | ||
![]() |
c0421a57da | ||
![]() |
8f1ec1dfdf | ||
![]() |
222dc8a239 | ||
![]() |
0e4d2e7277 | ||
![]() |
2bbff77e48 | ||
![]() |
3315c67f0f | ||
![]() |
b879bcb1eb | ||
![]() |
ae99135c8d | ||
![]() |
1defb38a6f | ||
![]() |
56bc9e12ee | ||
![]() |
116ebe0494 | ||
![]() |
5ec843dcc8 | ||
![]() |
a78b2d84ed | ||
![]() |
cf4d80fc65 | ||
![]() |
8118bc93a8 | ||
![]() |
c772bc45c6 | ||
![]() |
c1e7be3b8e | ||
![]() |
17ecc56e83 | ||
![]() |
6a42e6f6d4 | ||
![]() |
75ba961e97 | ||
![]() |
c2d5ce0ca2 | ||
![]() |
32645b80c4 | ||
![]() |
ff626ac763 | ||
![]() |
59f8144c50 | ||
![]() |
5a7c931293 | ||
![]() |
1373d40fea | ||
![]() |
3274bb54ad | ||
![]() |
7db124068f | ||
![]() |
39e2ffe212 | ||
![]() |
0a213ddf03 | ||
![]() |
5dc4cbdf82 | ||
![]() |
1434e5a22e | ||
![]() |
9acc1e1e97 | ||
![]() |
daa47546c7 | ||
![]() |
c5194d0a8f | ||
![]() |
7ece8ff85c | ||
![]() |
a4effeb3f9 | ||
![]() |
d3f28a1d7f | ||
![]() |
03cddd0acf | ||
![]() |
04a737e04c | ||
![]() |
966c54bef5 | ||
![]() |
efcd9dfc35 | ||
![]() |
db44a6e948 | ||
![]() |
d6553fc6a7 | ||
![]() |
fc9014f7ec | ||
![]() |
b39ab76118 | ||
![]() |
90777f78c9 | ||
![]() |
7cb803ad5c | ||
![]() |
8e063829c4 | ||
![]() |
139c6be370 | ||
![]() |
b6ac249c3c | ||
![]() |
5e103b5fb7 | ||
![]() |
de5be62da5 | ||
![]() |
b5f608d026 | ||
![]() |
58fe352fda | ||
![]() |
b105093dcd | ||
![]() |
d1c401fe7a | ||
![]() |
964aa42b04 | ||
![]() |
bf8660b21c | ||
![]() |
43863a70c8 | ||
![]() |
bd5a70c712 | ||
![]() |
909ff4b8a9 | ||
![]() |
bcfc62a3f2 | ||
![]() |
d1924867db | ||
![]() |
abfbd55305 | ||
![]() |
b3611524f4 | ||
![]() |
a63613dba0 | ||
![]() |
7fec2b02d4 | ||
![]() |
608a98c873 | ||
![]() |
f951e5356b | ||
![]() |
080ee87e07 | ||
![]() |
fa60b9ae94 | ||
![]() |
0d33ae52c6 | ||
![]() |
77e4b28092 | ||
![]() |
4817437d31 | ||
![]() |
354b5a9365 | ||
![]() |
58502b38d3 | ||
![]() |
d44880dfa9 | ||
![]() |
0c63632cc2 | ||
![]() |
b93523c0b1 | ||
![]() |
7ef40de98b | ||
![]() |
196ec25682 | ||
![]() |
c85af12d45 | ||
![]() |
e452d1f5b4 | ||
![]() |
ffea268d2e | ||
![]() |
8e676db633 | ||
![]() |
f6d74012b7 | ||
![]() |
32dfc11c23 | ||
![]() |
aad0ea6e23 | ||
![]() |
05de2e998c | ||
![]() |
24780d99e6 | ||
![]() |
7c1cf61728 | ||
![]() |
eb10d08671 | ||
![]() |
abb0549e4b | ||
![]() |
5327ea13ac | ||
![]() |
be8ceae6e6 | ||
![]() |
67ae033de7 | ||
![]() |
161f7ced96 | ||
![]() |
72bc11b180 | ||
![]() |
15c2538532 | ||
![]() |
fd9dd9343b | ||
![]() |
1b8a1d4140 | ||
![]() |
e132d10aec | ||
![]() |
3dbda2dda2 | ||
![]() |
a0105b45ae | ||
![]() |
551b7768fc | ||
![]() |
d6967db761 | ||
![]() |
b0967cae69 | ||
![]() |
401a799a1b | ||
![]() |
ba98518c69 | ||
![]() |
5e26e2ab1d | ||
![]() |
08eca827b6 | ||
![]() |
6b2b5af344 | ||
![]() |
f32fbd9ed1 | ||
![]() |
e3511d0ee0 | ||
![]() |
fa13a6616f | ||
![]() |
d4c2f91182 | ||
![]() |
084fd8df63 | ||
![]() |
328131b7aa | ||
![]() |
85003429af | ||
![]() |
6db77dcf75 | ||
![]() |
1cde86823d | ||
![]() |
dbd88e6aef | ||
![]() |
45418583e9 | ||
![]() |
77a1133723 | ||
![]() |
25c208d81d | ||
![]() |
67f87db511 | ||
![]() |
7de96275dd | ||
![]() |
e13d0bf656 | ||
![]() |
8ed9f7effa | ||
![]() |
12ab556477 | ||
![]() |
509f8dab89 | ||
![]() |
77429b6dd3 | ||
![]() |
84d20d9e43 | ||
![]() |
0c13703da3 | ||
![]() |
b97b7a7493 | ||
![]() |
be2951b45f | ||
![]() |
1dd1a705b5 | ||
![]() |
e4e80ff0cb | ||
![]() |
9508ea982b | ||
![]() |
47d655ea7f | ||
![]() |
62271bf6ce | ||
![]() |
0b27ac2f5c | ||
![]() |
6fd481df97 | ||
![]() |
b915e43391 | ||
![]() |
c96b295700 | ||
![]() |
9067da2df8 | ||
![]() |
8cf2f52f7a | ||
![]() |
681352ac3b | ||
![]() |
d9c662d51f | ||
![]() |
ad631d563b | ||
![]() |
7cbaf11dda | ||
![]() |
3be63549c0 | ||
![]() |
17c6db6c33 | ||
![]() |
86316b1828 | ||
![]() |
efc3a69dbf | ||
![]() |
ec883e1901 | ||
![]() |
060814daa8 | ||
![]() |
c53492a76a | ||
![]() |
43675717b8 | ||
![]() |
b21ed2fa36 | ||
![]() |
0214baad5a | ||
![]() |
c2d3ed2acc | ||
![]() |
0339c8d025 | ||
![]() |
0e0be0243b | ||
![]() |
a2ce2447a6 | ||
![]() |
9a1076256d | ||
![]() |
72ef38d4a7 | ||
![]() |
d397ce68dc | ||
![]() |
5e20b7976f | ||
![]() |
36f712b949 | ||
![]() |
a446775d80 | ||
![]() |
ccd7f0825a | ||
![]() |
d0896ea7c4 | ||
![]() |
a73d1e4b1c | ||
![]() |
d05bb2a0af | ||
![]() |
6765901687 | ||
![]() |
c5d05ac0cf | ||
![]() |
939003c1f1 | ||
![]() |
a10f3a8aec | ||
![]() |
ef663810a2 | ||
![]() |
ada67a6a4f | ||
![]() |
b6a5d1ad5a | ||
![]() |
d8c5a63bc2 | ||
![]() |
bcb2db62c9 | ||
![]() |
079ef93121 | ||
![]() |
b2789c598e | ||
![]() |
92a93c1217 | ||
![]() |
96b70835f9 | ||
![]() |
7fa3b7a267 | ||
![]() |
f3fc76e3c8 | ||
![]() |
3216f4b257 | ||
![]() |
ce1d897575 | ||
![]() |
17e108a10a | ||
![]() |
5bc4ab899f | ||
![]() |
44faf1080c | ||
![]() |
e354c5c2a8 | ||
![]() |
04bc9005ae | ||
![]() |
3c92c69bc7 | ||
![]() |
52ee132d92 | ||
![]() |
7ca0aedcfc | ||
![]() |
a2c4037a24 | ||
![]() |
d7b0073ce1 | ||
![]() |
585b68d2ae | ||
![]() |
89d2d648cc | ||
![]() |
7532f24d58 | ||
![]() |
69a5df2f98 | ||
![]() |
1c65908cdb | ||
![]() |
3aaf013dd1 | ||
![]() |
5f2705ab07 | ||
![]() |
9af620982c | ||
![]() |
1ee6a78cb7 | ||
![]() |
bf4ee48efa | ||
![]() |
08e6d222a2 | ||
![]() |
cf98b0e261 | ||
![]() |
a24589d46e | ||
![]() |
84533b6cad | ||
![]() |
8dca38e979 | ||
![]() |
662bed6a00 | ||
![]() |
ae5e0cb02b | ||
![]() |
0b3e1c4706 | ||
![]() |
ce08a7a932 | ||
![]() |
5d5f21bfc5 | ||
![]() |
fcccedc588 | ||
![]() |
0d73a49327 | ||
![]() |
6239dd96f2 | ||
![]() |
57a4700fb9 | ||
![]() |
2b676dc5fc | ||
![]() |
3f267b1795 | ||
![]() |
7a4c9f5f4c | ||
![]() |
3330aa6f6a | ||
![]() |
8929f88e6d | ||
![]() |
5348808bf5 | ||
![]() |
fccba1af2a | ||
![]() |
26d92c80ed | ||
![]() |
9605e24655 | ||
![]() |
7d0269d2ce | ||
![]() |
52ffdb0a55 | ||
![]() |
681d6bbdc5 | ||
![]() |
867b82b6de | ||
![]() |
5101ef4b02 | ||
![]() |
ee9e238179 | ||
![]() |
29030b54c9 | ||
![]() |
c9fcc7f148 | ||
![]() |
d1f4a31b5e | ||
![]() |
b691d3123b | ||
![]() |
f68e36f8c2 | ||
![]() |
4db119c01b | ||
![]() |
bbd7115564 | ||
![]() |
8d36367fe2 | ||
![]() |
3deca8fccd | ||
![]() |
7af24c4d3a | ||
![]() |
7f0ce4e94e | ||
![]() |
5c48d3fbba | ||
![]() |
00420ef9ca | ||
![]() |
b9d30595d6 | ||
![]() |
c1f4f1fdb6 | ||
![]() |
342333f72a | ||
![]() |
94cdc47786 | ||
![]() |
cc511e7b60 | ||
![]() |
bb2af791e9 | ||
![]() |
f3f4b332ae | ||
![]() |
84ac79bb08 | ||
![]() |
be0c8495cd | ||
![]() |
cbd0709d1c | ||
![]() |
371d635da8 | ||
![]() |
85216966fa | ||
![]() |
1c823e9d1f | ||
![]() |
25e338a098 | ||
![]() |
81175b0717 | ||
![]() |
d23c907a94 | ||
![]() |
018f4155eb | ||
![]() |
9ab0a1f5f1 | ||
![]() |
c043b337b1 | ||
![]() |
090bc6fa79 | ||
![]() |
b63db1c1aa | ||
![]() |
0287ac794e | ||
![]() |
0005221533 | ||
![]() |
23d2c0f1c6 | ||
![]() |
377b6f05ea | ||
![]() |
3ac2e9d31f | ||
![]() |
b76a29a69a | ||
![]() |
abe090ec1f | ||
![]() |
dff05c71e6 | ||
![]() |
f6f4742410 | ||
![]() |
977004c350 | ||
![]() |
fdc7d13ad1 | ||
![]() |
355dd5cb24 | ||
![]() |
123dd5fe2d | ||
![]() |
953e3190ca | ||
![]() |
44a0e21795 | ||
![]() |
fafaf567f9 | ||
![]() |
870cc1d928 | ||
![]() |
d86ee93801 | ||
![]() |
87b7328463 | ||
![]() |
3f846cf6b8 | ||
![]() |
f2ce8c3b62 | ||
![]() |
4223657ab8 | ||
![]() |
930128a7ea | ||
![]() |
db447440ff | ||
![]() |
b70d38dc10 | ||
![]() |
a27d105dcd | ||
![]() |
67e44b0f2c | ||
![]() |
bf840700e4 | ||
![]() |
47e16dbee3 | ||
![]() |
66f678023f | ||
![]() |
132971f8eb | ||
![]() |
3f04a4d635 | ||
![]() |
17c8e839b6 | ||
![]() |
348bdcd7b7 | ||
![]() |
a32443c63b | ||
![]() |
7e53934ce3 | ||
![]() |
7c2b553364 | ||
![]() |
409fc837ab | ||
![]() |
6d0ada7f45 | ||
![]() |
af63372d2b | ||
![]() |
3e91f757a9 | ||
![]() |
498491ec05 | ||
![]() |
85b77b81ca | ||
![]() |
5bf2ec5a74 | ||
![]() |
9e715089a4 | ||
![]() |
1a852bc365 | ||
![]() |
9326ce53ec | ||
![]() |
d8217c364a | ||
![]() |
3a34fd181d | ||
![]() |
fcb7233b25 | ||
![]() |
fe53a376a3 | ||
![]() |
65842cd99e | ||
![]() |
bf6ed643e0 | ||
![]() |
a0beb5fa26 | ||
![]() |
f54bcc1f16 | ||
![]() |
ca0d09c50f | ||
![]() |
81c3224076 | ||
![]() |
7a1d466fb2 | ||
![]() |
83f4c48c8a | ||
![]() |
acd742d225 | ||
![]() |
d1e7b4e381 | ||
![]() |
67f591a9ce | ||
![]() |
7ff988275f | ||
![]() |
363050f44c | ||
![]() |
dbe04c4207 | ||
![]() |
d59a332ef9 | ||
![]() |
105b431e4c | ||
![]() |
ba79f4c1f9 | ||
![]() |
8b2dcf7018 | ||
![]() |
44621f7326 | ||
![]() |
8ead8f7ea6 | ||
![]() |
7f9402bd22 | ||
![]() |
cbd38327e7 | ||
![]() |
06f898cc12 | ||
![]() |
6a9ab8bc0e | ||
![]() |
9a0061c511 | ||
![]() |
7a3aac1843 | ||
![]() |
da1f4b3ede | ||
![]() |
dac740ee17 | ||
![]() |
28030d7edb | ||
![]() |
648196319f | ||
![]() |
c75cb67c44 | ||
![]() |
cd1bb2bafa | ||
![]() |
a9d2dc6144 | ||
![]() |
46b9388bb0 | ||
![]() |
a1a97d663e | ||
![]() |
cac3c159bc | ||
![]() |
43166130b5 | ||
![]() |
14f21378e3 | ||
![]() |
bd42aeab46 | ||
![]() |
cde6a3a00c | ||
![]() |
96b763067e | ||
![]() |
dd5ba062cc | ||
![]() |
ba161ec572 | ||
![]() |
43f613d9be | ||
![]() |
2277d143fa | ||
![]() |
6b6d9e64bd | ||
![]() |
f016a99f24 | ||
![]() |
2eed9d64ce | ||
![]() |
1688b6dda9 | ||
![]() |
33aedc887a | ||
![]() |
258d0ea97e | ||
![]() |
d3641766a5 | ||
![]() |
b25d5c5d33 | ||
![]() |
352d7f477e | ||
![]() |
1fcf35ad3b | ||
![]() |
c8054e569a | ||
![]() |
ef48eca9ca | ||
![]() |
906d2fbadf | ||
![]() |
2b579aeb4f | ||
![]() |
e9e55b0812 | ||
![]() |
49a3845135 | ||
![]() |
21dac6c05d | ||
![]() |
1d9b84a5af | ||
![]() |
a688745bdc | ||
![]() |
459d824c50 | ||
![]() |
96019f4a02 | ||
![]() |
896015bf53 | ||
![]() |
df3e7a9248 | ||
![]() |
b2d3d15e97 | ||
![]() |
08dfd263ba | ||
![]() |
abaabe92d6 | ||
![]() |
257b42b87f | ||
![]() |
31bc94160a | ||
![]() |
9e5d2c5bb7 | ||
![]() |
2df2a989af | ||
![]() |
4a800b311f | ||
![]() |
a4a13a3825 | ||
![]() |
a28df6123f | ||
![]() |
c5c43c4541 | ||
![]() |
0954f580fa | ||
![]() |
3a2254c91f | ||
![]() |
4aeec4bb60 | ||
![]() |
750b2ad6a8 | ||
![]() |
86c276f538 | ||
![]() |
166569200a | ||
![]() |
e903d00968 | ||
![]() |
7d5d95ad53 | ||
![]() |
cc6c452854 | ||
![]() |
f492c78e2e | ||
![]() |
6b83d08228 | ||
![]() |
dc415b761e | ||
![]() |
f1034eb657 | ||
![]() |
ac9ebe1439 | ||
![]() |
7b5f7c041b | ||
![]() |
8ce9b53093 | ||
![]() |
0b9e52bd5f | ||
![]() |
5dd728d802 | ||
![]() |
ce7c97b6ba | ||
![]() |
9a4c049675 | ||
![]() |
cb8449a66d | ||
![]() |
621467717d | ||
![]() |
506c716cf2 | ||
![]() |
9f625b0a0d | ||
![]() |
bc1b4131cb | ||
![]() |
80ba3c5932 | ||
![]() |
e63420a8c2 | ||
![]() |
e02d8ad8d2 | ||
![]() |
d300e6bf21 | ||
![]() |
1729388634 | ||
![]() |
c654c7630a | ||
![]() |
3f3b26fb0e | ||
![]() |
0921180b90 | ||
![]() |
14df240f5b | ||
![]() |
2090911363 | ||
![]() |
3cc7be0fa6 | ||
![]() |
6728b8c1a3 | ||
![]() |
450c26c471 | ||
![]() |
7ec1121cc8 | ||
![]() |
f84e288ad7 | ||
![]() |
8e31366431 | ||
![]() |
1bfa04f80e | ||
![]() |
98cbc0ea79 | ||
![]() |
dca1115196 | ||
![]() |
96882175f1 | ||
![]() |
484841fc9e | ||
![]() |
8052c76489 | ||
![]() |
3477acee48 | ||
![]() |
2c4b998170 | ||
![]() |
0a9c7ff6cf | ||
![]() |
cd71038655 | ||
![]() |
1894aed261 | ||
![]() |
f3832bcaa8 | ||
![]() |
436335e9a3 | ||
![]() |
a30eb194d5 | ||
![]() |
8becbb8b08 | ||
![]() |
e0c2c77c2a | ||
![]() |
6f2e1c2415 | ||
![]() |
fc0ad8674b | ||
![]() |
19c67ab724 | ||
![]() |
a046b6e105 | ||
![]() |
8017301de5 | ||
![]() |
46528783ef | ||
![]() |
6f4bb6cd2c | ||
![]() |
2cf31e905b | ||
![]() |
28a60db5aa | ||
![]() |
496f70fc0d | ||
![]() |
9ede4c5f3c | ||
![]() |
214a526945 | ||
![]() |
d5b756413a | ||
![]() |
af99176581 | ||
![]() |
57e825dfe7 | ||
![]() |
e06dd129dd | ||
![]() |
00baddcd9e | ||
![]() |
7e92820c50 | ||
![]() |
5692e20fd5 | ||
![]() |
3b3c9334c8 | ||
![]() |
96dc0a318a | ||
![]() |
a4223aac38 | ||
![]() |
2bb751d9fa | ||
![]() |
3b620112ca | ||
![]() |
99526219b7 | ||
![]() |
d818b618af | ||
![]() |
c64700e7ae | ||
![]() |
5bd1917705 | ||
![]() |
178c6c20cd | ||
![]() |
92d71cc7fa | ||
![]() |
227eca7d28 | ||
![]() |
9399b0ea52 | ||
![]() |
17dca254a3 | ||
![]() |
f6b50d2387 | ||
![]() |
a9ce0218c1 | ||
![]() |
7bb5a960fd | ||
![]() |
e6b04d1b50 | ||
![]() |
12613356fc | ||
![]() |
3894450b10 | ||
![]() |
ec07f6ec83 | ||
![]() |
a7a10d03c9 | ||
![]() |
ef99d6ce3d | ||
![]() |
0ac06d77f1 | ||
![]() |
469e233428 | ||
![]() |
86a0f9cf9b | ||
![]() |
e6a91d291d | ||
![]() |
bf35972714 | ||
![]() |
e0c645a270 | ||
![]() |
e42734c3f3 | ||
![]() |
eab78ab99c | ||
![]() |
f2491c88c8 | ||
![]() |
1b175025fe | ||
![]() |
ccdf7184be | ||
![]() |
7764136211 | ||
![]() |
0307801d51 | ||
![]() |
72cf8dd8a0 | ||
![]() |
3c2b464dfa | ||
![]() |
361404fd59 | ||
![]() |
762c91b7f1 | ||
![]() |
f2a8d4d289 | ||
![]() |
9ee52d85d7 | ||
![]() |
5ed9f02c4d | ||
![]() |
378ebad1c8 | ||
![]() |
e44e0fab9f | ||
![]() |
c434077728 | ||
![]() |
6e47e79790 | ||
![]() |
d9ea3082fb | ||
![]() |
2d63c26936 | ||
![]() |
3cdd01aa1b | ||
![]() |
d25195447a | ||
![]() |
aa8ab2fc89 | ||
![]() |
d664baff26 | ||
![]() |
3275d4c6fa | ||
![]() |
cd0d003197 | ||
![]() |
cfeeb7af2e | ||
![]() |
ac887d3afb | ||
![]() |
e294ccae24 | ||
![]() |
72070f292b | ||
![]() |
823c618d24 | ||
![]() |
51cb6a0a44 | ||
![]() |
a1b1ec3d11 | ||
![]() |
595b6a4f6c | ||
![]() |
daa4647712 | ||
![]() |
c67668d6dd | ||
![]() |
9cfaa9c7b0 | ||
![]() |
c5ea586882 | ||
![]() |
f2a3a37dff | ||
![]() |
01dd540d7e | ||
![]() |
8142080633 | ||
![]() |
9920a3e8fc | ||
![]() |
f7d8e6c40c | ||
![]() |
4ecf09f9e6 | ||
![]() |
47a8369d79 | ||
![]() |
6805fa2fa0 | ||
![]() |
4d6b9611ae | ||
![]() |
16afdfd874 | ||
![]() |
701fff03d2 | ||
![]() |
8e0575ca9b | ||
![]() |
5e8f51a963 | ||
![]() |
be3d2188d6 | ||
![]() |
18e429a87e | ||
![]() |
3576a8fd9f | ||
![]() |
f260cd031c | ||
![]() |
18c4b536f2 | ||
![]() |
fa51db449f | ||
![]() |
cb9a05ac77 | ||
![]() |
f1f22019f5 | ||
![]() |
6d79a1cdfc | ||
![]() |
ad5eb2f8d6 | ||
![]() |
39d56d6b65 | ||
![]() |
a291415326 | ||
![]() |
4ad7456428 | ||
![]() |
73f36858bb | ||
![]() |
fe3f0332f7 | ||
![]() |
718fd97612 | ||
![]() |
f8ff45b212 | ||
![]() |
f5c0b0d316 | ||
![]() |
ba51045d9e | ||
![]() |
fa34dd7bd3 | ||
![]() |
9f0fb8f6a8 | ||
![]() |
a42f9fd4e2 | ||
![]() |
e66005563e | ||
![]() |
8cad20585d | ||
![]() |
a8b75dc4df | ||
![]() |
e4455962c6 | ||
![]() |
20f85745e7 | ||
![]() |
feb8d5b82f | ||
![]() |
eb8922f346 | ||
![]() |
84eb95466b | ||
![]() |
3dd8beb380 | ||
![]() |
ae77406b8e | ||
![]() |
2d16069211 | ||
![]() |
d18314fa05 | ||
![]() |
26a9ce7b29 | ||
![]() |
76417d4446 | ||
![]() |
cffc78ad6a | ||
![]() |
dcf55c7e32 | ||
![]() |
e565cd4404 | ||
![]() |
5934ccbb74 | ||
![]() |
e39969a0d0 | ||
![]() |
d68185aa28 | ||
![]() |
4cab151ed2 | ||
![]() |
8ac9b77e5c | ||
![]() |
1b9da5d978 | ||
![]() |
ab2b7358cb | ||
![]() |
8ceea85813 | ||
![]() |
d0a812d2df | ||
![]() |
3dad2e1c0f | ||
![]() |
70e4399334 | ||
![]() |
00f8c2d46f | ||
![]() |
0dd4b52b63 | ||
![]() |
88c17926e4 | ||
![]() |
541192c941 | ||
![]() |
2e9f054ec0 | ||
![]() |
2aa34882b7 | ||
![]() |
27f8ef2f33 | ||
![]() |
2cbe21c791 | ||
![]() |
0988056471 | ||
![]() |
95c3f57b30 | ||
![]() |
068f191c0d | ||
![]() |
6bf6c9215b | ||
![]() |
a65afa8090 | ||
![]() |
8155784990 | ||
![]() |
44725e485d | ||
![]() |
e98e2a0b07 | ||
![]() |
943064bb51 | ||
![]() |
7149a8ae4f | ||
![]() |
3c7cf94643 | ||
![]() |
3bd35d1883 | ||
![]() |
6f3d70b5e2 | ||
![]() |
257a0dee75 | ||
![]() |
e995cd928c | ||
![]() |
292d7c3fdf | ||
![]() |
0273cd44b0 | ||
![]() |
3203a7dd8c | ||
![]() |
86b0adc82c | ||
![]() |
ba49f20f68 | ||
![]() |
a3ee26da64 | ||
![]() |
a9b62a2ece | ||
![]() |
49567f1f3e | ||
![]() |
94fffb332b | ||
![]() |
dc2e64c92b | ||
![]() |
06fb7c671b | ||
![]() |
ef93cdf4a8 | ||
![]() |
4ad90e2d52 | ||
![]() |
7c0e4dfb56 | ||
![]() |
9ec069104f | ||
![]() |
7d21d60dba | ||
![]() |
ed8562c300 | ||
![]() |
22ec1c3498 | ||
![]() |
9cc90b1fbe | ||
![]() |
0c9f22507f | ||
![]() |
9c870e4276 | ||
![]() |
5cffe693b0 | ||
![]() |
e01615fd1e | ||
![]() |
c1daa857a5 | ||
![]() |
3928c4e9ae | ||
![]() |
32799fef5c | ||
![]() |
e1b03b4a71 | ||
![]() |
e6ed592b8a | ||
![]() |
6f4560184c | ||
![]() |
e5039c478a | ||
![]() |
8901514506 | ||
![]() |
21fe376d1d | ||
![]() |
e22ef6c481 | ||
![]() |
2bb01093c0 | ||
![]() |
36c814d26e | ||
![]() |
d769b74d61 | ||
![]() |
601495fa0f | ||
![]() |
1998633739 | ||
![]() |
0194217f9d | ||
![]() |
b0bbb8b693 | ||
![]() |
cab84af72e | ||
![]() |
74500eacca | ||
![]() |
1105e61f29 | ||
![]() |
b7cf279d6d | ||
![]() |
c614e550d9 | ||
![]() |
452a30d7af | ||
![]() |
49e79620fd | ||
![]() |
ae5a721df9 | ||
![]() |
0dd5f2915a | ||
![]() |
a0ebd444ad | ||
![]() |
0c6072c4e4 | ||
![]() |
8917264110 | ||
![]() |
e90d606f0a | ||
![]() |
5bb2d64ddb | ||
![]() |
bc66dc45e6 | ||
![]() |
7087935656 | ||
![]() |
5822daa63d | ||
![]() |
4808c7ef39 | ||
![]() |
7071126770 | ||
![]() |
2101daef5a | ||
![]() |
083065f433 | ||
![]() |
4cfc4a5a94 | ||
![]() |
1c7ee737e6 | ||
![]() |
39439b80f5 | ||
![]() |
396480cf94 | ||
![]() |
3e03527930 | ||
![]() |
4fa724461e | ||
![]() |
1e2018ce83 | ||
![]() |
3fcf17cb79 | ||
![]() |
f9fc3a42cc | ||
![]() |
76bc28ab1e | ||
![]() |
f47f86ef02 | ||
![]() |
31268ffa24 | ||
![]() |
0634be9724 | ||
![]() |
f56c6a18c1 | ||
![]() |
aa4c506fcd | ||
![]() |
d3a479b7fa | ||
![]() |
92fb0e829a | ||
![]() |
43a9928537 | ||
![]() |
c8b408beae | ||
![]() |
8e3982dd42 | ||
![]() |
e83f805b8f | ||
![]() |
be98afe34d | ||
![]() |
f53dadcc6d | ||
![]() |
90ab65f8c7 | ||
![]() |
abb0fcb203 | ||
![]() |
eef4f33a29 | ||
![]() |
0226440a2d | ||
![]() |
9d770d6084 | ||
![]() |
7f0297dc44 | ||
![]() |
49f3855407 | ||
![]() |
905d493fd7 | ||
![]() |
5def2a09bd | ||
![]() |
a98aa66620 | ||
![]() |
631a268996 | ||
![]() |
85074f5ac6 | ||
![]() |
12a8ed39d4 | ||
![]() |
b9ac5e9e76 | ||
![]() |
2758a734a2 | ||
![]() |
01f3131c81 | ||
![]() |
2ca9236864 | ||
![]() |
3bb711837e | ||
![]() |
ed8798e82f | ||
![]() |
38474961fd | ||
![]() |
3dd38e7b7f | ||
![]() |
3711a97657 | ||
![]() |
911411e630 | ||
![]() |
c02cc199bc | ||
![]() |
dd6c21e092 | ||
![]() |
7801ca86b9 | ||
![]() |
10fc288c91 | ||
![]() |
b8cda53bd3 | ||
![]() |
43806d524d | ||
![]() |
e0a97a030f | ||
![]() |
33ba288622 | ||
![]() |
d5516dee00 | ||
![]() |
06e0741a52 | ||
![]() |
0b93445380 | ||
![]() |
483ba5ea1c | ||
![]() |
18076ac9b7 | ||
![]() |
ad15ca7104 | ||
![]() |
53117ac204 | ||
![]() |
51a8c109ab | ||
![]() |
ea25e0ee55 | ||
![]() |
e6fdae431f | ||
![]() |
34afc272fd | ||
![]() |
a5673fcb28 | ||
![]() |
5c3c506638 | ||
![]() |
ecd5eb02c5 | ||
![]() |
3be57dc4a3 | ||
![]() |
a93caf3cfe | ||
![]() |
26ebfc04b0 | ||
![]() |
8782f6d232 | ||
![]() |
5faf443038 | ||
![]() |
8eacd13ce7 | ||
![]() |
d3e1b72d38 | ||
![]() |
b5e1bd5705 | ||
![]() |
4bef0a1e62 | ||
![]() |
3e5f09be0b | ||
![]() |
e86e77ad67 | ||
![]() |
3c4bb9ff4e | ||
![]() |
a890dadfd2 | ||
![]() |
6b4374583a | ||
![]() |
78f4171a7a | ||
![]() |
f8a99a391e | ||
![]() |
2564f763d7 | ||
![]() |
377a2b9e07 | ||
![]() |
045993001c | ||
![]() |
affc446690 | ||
![]() |
255ee646c2 | ||
![]() |
0308ec564e | ||
![]() |
6cafec8ae6 | ||
![]() |
8ef87be482 | ||
![]() |
6c57047362 | ||
![]() |
e29bc6912b | ||
![]() |
ce57b8b6df | ||
![]() |
d81a9029db | ||
![]() |
620ae7790b | ||
![]() |
23b2b7144b | ||
![]() |
bf4311cd9b | ||
![]() |
8f7adf79a3 | ||
![]() |
97b164d03b | ||
![]() |
61e7e531c2 | ||
![]() |
ceb652915f | ||
![]() |
cdaf504ab8 | ||
![]() |
9fb5aad157 | ||
![]() |
a6301d45f4 | ||
![]() |
b83d069266 | ||
![]() |
960b9a9664 | ||
![]() |
acb45caa42 | ||
![]() |
39060b528a | ||
![]() |
4a36323f1b | ||
![]() |
c6281b2680 | ||
![]() |
5b8b7d1412 | ||
![]() |
53e5139b99 | ||
![]() |
2b8ce83c9b | ||
![]() |
52638c68f5 | ||
![]() |
400ff1c812 | ||
![]() |
d360f17a59 | ||
![]() |
a6ee6be960 | ||
![]() |
47911f9544 | ||
![]() |
48025c2279 | ||
![]() |
a68302e50b | ||
![]() |
333a08ebf9 | ||
![]() |
989c9a7317 | ||
![]() |
975370c084 | ||
![]() |
c3c776bc6a | ||
![]() |
9f4b906e6c | ||
![]() |
804900df36 | ||
![]() |
3781f6cf39 | ||
![]() |
227da2377a | ||
![]() |
ffb3a9f526 | ||
![]() |
6936c0e2ab | ||
![]() |
108242042e | ||
![]() |
70652abf97 | ||
![]() |
e202b407ec | ||
![]() |
2cc89e60cc | ||
![]() |
d536944beb | ||
![]() |
1a8ef3cdab | ||
![]() |
7be33eba48 | ||
![]() |
46ed91b53d | ||
![]() |
73f6fc428a | ||
![]() |
44d260f911 | ||
![]() |
07249fc395 | ||
![]() |
bc1e8e01f3 | ||
![]() |
fe8e77e556 | ||
![]() |
b0833084a6 | ||
![]() |
3cacaa5dad | ||
![]() |
e89bd30db5 | ||
![]() |
2a9d933a81 | ||
![]() |
81f3d893d9 | ||
![]() |
05c91082e3 | ||
![]() |
7267558ba1 | ||
![]() |
ac6803e7b7 | ||
![]() |
ffabf8b013 | ||
![]() |
e9b71a0d28 | ||
![]() |
efbfe66f21 | ||
![]() |
6886063703 | ||
![]() |
c04e1ad401 | ||
![]() |
223b90d0d4 | ||
![]() |
692b2cfb79 | ||
![]() |
447a86c922 | ||
![]() |
e3a2bd3a1e | ||
![]() |
f0be6a4b9e | ||
![]() |
bdab5d5f6e | ||
![]() |
15a4246ead | ||
![]() |
9a71845700 | ||
![]() |
22f0a1547c | ||
![]() |
de0ab43bc1 | ||
![]() |
e12cc01aa4 | ||
![]() |
71c697288b | ||
![]() |
a7d1daee93 | ||
![]() |
77a99cc61d | ||
![]() |
fb337418a5 | ||
![]() |
9d312ab208 | ||
![]() |
06be0a1997 | ||
![]() |
9e4509b846 | ||
![]() |
fc1f0eeda7 | ||
![]() |
3d5c094804 | ||
![]() |
6abc94ec07 | ||
![]() |
6837e5a6a0 | ||
![]() |
9761abf3b5 | ||
![]() |
599a562170 | ||
![]() |
d29a251547 | ||
![]() |
31da4bc566 | ||
![]() |
0f1a180e15 | ||
![]() |
01a45a53aa | ||
![]() |
59400d38a9 | ||
![]() |
5155b0f608 | ||
![]() |
d919f8d50a | ||
![]() |
d67aa7c19d | ||
![]() |
7a982169c9 | ||
![]() |
f2510d60fa | ||
![]() |
1e0ea57dc4 | ||
![]() |
70b87f2eb6 | ||
![]() |
94b2ee627c | ||
![]() |
e30a2dd2d7 | ||
![]() |
7e07daf8cb | ||
![]() |
981906ecd1 | ||
![]() |
90fe4c5124 | ||
![]() |
2452447c81 | ||
![]() |
93f0bb8307 | ||
![]() |
47fc08bffe | ||
![]() |
c0f2024a2e | ||
![]() |
28f3e190c8 | ||
![]() |
c4090b670d | ||
![]() |
ddf112378b | ||
![]() |
b25cce464a | ||
![]() |
a2297558ff | ||
![]() |
84cdf0cacc | ||
![]() |
9e99e5bef9 | ||
![]() |
9023ba4a81 | ||
![]() |
83488848e1 | ||
![]() |
f5a92d6cc3 | ||
![]() |
3e8047e583 | ||
![]() |
440ac51cf0 | ||
![]() |
d3293b889d | ||
![]() |
b4b0b34e5a | ||
![]() |
fa3d1156a6 | ||
![]() |
0eb05b827f | ||
![]() |
0c245bc271 | ||
![]() |
d65ad1bf15 | ||
![]() |
43cbbe111b | ||
![]() |
9ceb8a717a | ||
![]() |
8331de424a | ||
![]() |
98dbdf72b3 | ||
![]() |
51a2d09eb7 | ||
![]() |
22f0ef6d6b | ||
![]() |
975deca85b | ||
![]() |
3bbb502387 | ||
![]() |
0023dffd0b | ||
![]() |
b715e522cf | ||
![]() |
4b0b8315a9 | ||
![]() |
a654f146d1 | ||
![]() |
67b46a151d | ||
![]() |
83c7e1e129 | ||
![]() |
a6371e2e66 | ||
![]() |
7768baa4d1 | ||
![]() |
a9a5907a0f | ||
![]() |
dc32d1f3f3 | ||
![]() |
cbdaa1369f | ||
![]() |
8fb20fcdf8 | ||
![]() |
72bf226608 | ||
![]() |
d4b5699403 | ||
![]() |
1dc27be015 | ||
![]() |
230a3eb400 | ||
![]() |
e39382dedd | ||
![]() |
fd016f4507 | ||
![]() |
9d728b365d | ||
![]() |
ddc0283339 | ||
![]() |
b8fdb452be | ||
![]() |
6b416ce6be | ||
![]() |
ef0392e854 | ||
![]() |
efbf184fe8 | ||
![]() |
dd577fb857 | ||
![]() |
ebd90dbb1a | ||
![]() |
c9e700f079 | ||
![]() |
3c2d73d161 | ||
![]() |
343de8b8ab | ||
![]() |
c645b906f3 | ||
![]() |
9c1d1ef268 | ||
![]() |
ade0483641 | ||
![]() |
03a401e477 | ||
![]() |
9994521b8c | ||
![]() |
e5fd1924db | ||
![]() |
cbdd042adc | ||
![]() |
a9419da09c | ||
![]() |
ccd7104cdc | ||
![]() |
1cf4b5ce47 | ||
![]() |
1badb3b5d5 | ||
![]() |
038d216f18 | ||
![]() |
9e3f843291 | ||
![]() |
e166ddf46f | ||
![]() |
31e1be7570 | ||
![]() |
d793b7c03f | ||
![]() |
9a715267ad | ||
![]() |
eafa432cc6 | ||
![]() |
d95e538020 | ||
![]() |
adbe8c409a | ||
![]() |
58e600f408 | ||
![]() |
d34e55c370 | ||
![]() |
fbcbcdc001 | ||
![]() |
4227a325a5 | ||
![]() |
d115507502 | ||
![]() |
43d8252050 | ||
![]() |
674b4ab647 | ||
![]() |
fe8fc1081a | ||
![]() |
c7748fedab | ||
![]() |
c392efb481 | ||
![]() |
1ddd9dd52a | ||
![]() |
aa171dcc18 | ||
![]() |
b4d8cb7bc1 | ||
![]() |
f672e4016f | ||
![]() |
76e3dec723 | ||
![]() |
d0c3fa0150 | ||
![]() |
609f6ce66d | ||
![]() |
7298b6c846 | ||
![]() |
acec9c4fd7 | ||
![]() |
9cdbde4f5e | ||
![]() |
f9147a0706 | ||
![]() |
33364edfb3 | ||
![]() |
5e9ccdec63 | ||
![]() |
071aca60be | ||
![]() |
133e4d5c51 | ||
![]() |
fd56461d5f | ||
![]() |
d5f59f8c86 | ||
![]() |
a5d8b27671 | ||
![]() |
f3f25d5d40 | ||
![]() |
f118812c34 | ||
![]() |
98dbc95913 | ||
![]() |
145c1d214c | ||
![]() |
b62a8e3e3e | ||
![]() |
e47240931a | ||
![]() |
a84874426d | ||
![]() |
f38dfd9231 | ||
![]() |
510c4a3ef1 | ||
![]() |
e7e52c24f5 | ||
![]() |
e2a574e2a0 | ||
![]() |
ba6ef53ef9 | ||
![]() |
c93a28c641 | ||
![]() |
7088a679a2 | ||
![]() |
04c02a1eb8 | ||
![]() |
1536b5a9d6 | ||
![]() |
bab756a5d0 | ||
![]() |
8c763fe458 | ||
![]() |
886255e38a | ||
![]() |
0240e75426 | ||
![]() |
7102ed8026 | ||
![]() |
ebf481e1a1 | ||
![]() |
5a52e91350 | ||
![]() |
18e458154a | ||
![]() |
015e1ab183 | ||
![]() |
2a4c799471 | ||
![]() |
41487426f5 | ||
![]() |
0d24250aa7 | ||
![]() |
2050e2f886 | ||
![]() |
013e8479af | ||
![]() |
27535a7f78 | ||
![]() |
333d226ed0 | ||
![]() |
04a9dec952 | ||
![]() |
6ee76b7154 | ||
![]() |
36b1c08ad7 | ||
![]() |
7b35d5e9d4 | ||
![]() |
6d4ca071a5 | ||
![]() |
684a3d30a6 | ||
![]() |
25740d27bf | ||
![]() |
fb0a52a34a | ||
![]() |
c852970cf6 | ||
![]() |
def21cc87e | ||
![]() |
488c1eb87b | ||
![]() |
acaa725478 | ||
![]() |
f351550534 | ||
![]() |
e8df7e8da5 | ||
![]() |
af4252bc80 | ||
![]() |
695ffedef9 | ||
![]() |
66ecf39efe | ||
![]() |
5ad21d7e98 | ||
![]() |
1c3e03837c | ||
![]() |
7968fa3779 | ||
![]() |
31009bb1f6 | ||
![]() |
3b8532f3fb | ||
![]() |
f45616e5f6 | ||
![]() |
ef5125f8f4 | ||
![]() |
bf2e07074b | ||
![]() |
20695ef369 | ||
![]() |
e391f4b17b | ||
![]() |
2bd344549b | ||
![]() |
e96779de48 | ||
![]() |
eb54337c40 | ||
![]() |
916a020173 | ||
![]() |
c2e4fe983d | ||
![]() |
81e898375b | ||
![]() |
3c0dea811d | ||
![]() |
a6ac0f8965 | ||
![]() |
4e1eb03287 | ||
![]() |
fcf0f8291d | ||
![]() |
0ed71fa027 | ||
![]() |
affe21f7c1 | ||
![]() |
9facf7897d | ||
![]() |
8422402c39 | ||
![]() |
9374e0f445 | ||
![]() |
19ed233118 | ||
![]() |
faa4fff4dd | ||
![]() |
2276e7677b | ||
![]() |
b5fde6dfa5 | ||
![]() |
784d666a8e | ||
![]() |
c035910df9 | ||
![]() |
efccb6ac82 | ||
![]() |
93f9c2ab6b | ||
![]() |
4a993cd79e | ||
![]() |
02325d2ede | ||
![]() |
9c83464b95 | ||
![]() |
b1bbd70f0f | ||
![]() |
c31d11bfe0 | ||
![]() |
c8ec85d649 | ||
![]() |
e291f3d257 | ||
![]() |
7d27d2ea5e | ||
![]() |
dc22846d58 | ||
![]() |
c9aaabb5d4 | ||
![]() |
335d5d5d72 | ||
![]() |
51d793bec1 | ||
![]() |
249dcd967e | ||
![]() |
302972e9fc | ||
![]() |
31b380b266 | ||
![]() |
a869dfea85 | ||
![]() |
12838c6294 | ||
![]() |
49c7102547 | ||
![]() |
5cc3338267 | ||
![]() |
1ae8972859 | ||
![]() |
adcd2c8eac | ||
![]() |
45ff355835 | ||
![]() |
f8bf3afeae | ||
![]() |
f703da1516 | ||
![]() |
a582deee2c | ||
![]() |
3047bdf653 | ||
![]() |
12be9e818f | ||
![]() |
a0478f98af | ||
![]() |
5e2c62db2f | ||
![]() |
d488d796f4 | ||
![]() |
f794b1e1aa | ||
![]() |
8c425c758c | ||
![]() |
0b9e912297 | ||
![]() |
d35d2b269f | ||
![]() |
fa84ed412d | ||
![]() |
16951099d1 | ||
![]() |
050ba302cb | ||
![]() |
c1f90a99f4 | ||
![]() |
510097cc37 | ||
![]() |
6a83721c55 | ||
![]() |
ca9795c30a | ||
![]() |
dc2fa246a9 | ||
![]() |
b46bb611b3 | ||
![]() |
aa55d759f5 | ||
![]() |
2043579f71 | ||
![]() |
346e31efa0 | ||
![]() |
b504d63117 | ||
![]() |
b4b1728b6f | ||
![]() |
21792386d8 | ||
![]() |
63a2ac21e1 | ||
![]() |
c42b206292 | ||
![]() |
733d6a6b16 | ||
![]() |
8d2725234e | ||
![]() |
0823711106 | ||
![]() |
860e9eb8c9 | ||
![]() |
3d2092ee23 | ||
![]() |
8855efebc0 | ||
![]() |
7725577a53 | ||
![]() |
668e3f664f | ||
![]() |
9e51733c71 | ||
![]() |
6b39a5621d | ||
![]() |
0d46e11826 | ||
![]() |
ff58207034 | ||
![]() |
b3d76b7e5c | ||
![]() |
4d6c816abb | ||
![]() |
f35ceaee0a | ||
![]() |
a64a2a65a9 | ||
![]() |
1a75abffa5 | ||
![]() |
c6a0f5d3f9 | ||
![]() |
bb75e3ea55 | ||
![]() |
814eebf976 | ||
![]() |
e08d4f28aa | ||
![]() |
6f3c65dc64 | ||
![]() |
0a3ada4fea | ||
![]() |
8bdf7917c4 | ||
![]() |
67d16a086e | ||
![]() |
b8018f12ba | ||
![]() |
607780b7f9 | ||
![]() |
af1c497759 | ||
![]() |
8e331cfc65 | ||
![]() |
edf811fa02 | ||
![]() |
b25b7693ba | ||
![]() |
9dc9459f3a | ||
![]() |
37b92967c5 | ||
![]() |
768504e956 | ||
![]() |
2571accfc3 | ||
![]() |
0d2abdb5d9 | ||
![]() |
79200a8611 | ||
![]() |
123ae985d5 | ||
![]() |
659d1c7cf7 | ||
![]() |
281cd7c057 | ||
![]() |
63e8766091 | ||
![]() |
18da582c87 | ||
![]() |
0562cf99ba | ||
![]() |
60ac702038 | ||
![]() |
1dedb96478 | ||
![]() |
7537722a44 | ||
![]() |
4ebd69193e | ||
![]() |
d4d92ac1a7 | ||
![]() |
5385d1fa80 | ||
![]() |
dbee2f1996 | ||
![]() |
6dd70926fa | ||
![]() |
5dfc0918c3 | ||
![]() |
2eb14658d3 | ||
![]() |
c1f196dbec | ||
![]() |
ce108acebb | ||
![]() |
0555e2c781 | ||
![]() |
6bfd1f1727 | ||
![]() |
baa77c8ae3 | ||
![]() |
7235dbadfd | ||
![]() |
ecec41025f | ||
![]() |
799843cc97 | ||
![]() |
16e91baa79 | ||
![]() |
f2536445f7 | ||
![]() |
ede70ee3a4 | ||
![]() |
66ed427a57 | ||
![]() |
c9e63e9fdb | ||
![]() |
ddf7f5c131 | ||
![]() |
660e40d07e | ||
![]() |
9f3db5a70b | ||
![]() |
1a59730782 | ||
![]() |
20029e7ce8 | ||
![]() |
fcb7950811 | ||
![]() |
4eb57e1e9a | ||
![]() |
1d52e2cc77 | ||
![]() |
209aceeb14 | ||
![]() |
ae70875f45 | ||
![]() |
eda7410f4c | ||
![]() |
e60141b4dd | ||
![]() |
5b21742095 | ||
![]() |
5665de5ee7 | ||
![]() |
d5b9be0393 | ||
![]() |
d2d9b45a81 | ||
![]() |
9ebbdb9b0b | ||
![]() |
48da345e79 | ||
![]() |
edbfa46cbc | ||
![]() |
90709a6de4 | ||
![]() |
992c2fa2d4 | ||
![]() |
ed915fed92 | ||
![]() |
8aa29d5a66 | ||
![]() |
9604e0aad2 | ||
![]() |
25d053cbf2 | ||
![]() |
055257a210 | ||
![]() |
50cfb997cc | ||
![]() |
d662c4c0cc | ||
![]() |
457d98c860 | ||
![]() |
6bb166aaaa | ||
![]() |
cca9bc5176 | ||
![]() |
8b6b25220d | ||
![]() |
3c3f1b7ff2 | ||
![]() |
57d89131e9 | ||
![]() |
7c6d1896a4 | ||
![]() |
ad83c7f704 | ||
![]() |
6b52d040b1 | ||
![]() |
404fa89937 | ||
![]() |
89377556cd | ||
![]() |
ec93114347 | ||
![]() |
df1c5ce314 | ||
![]() |
a0e4b6e266 | ||
![]() |
0de39b64cb | ||
![]() |
36a7b4e275 | ||
![]() |
d3cc8e0ac0 | ||
![]() |
c22cbbf828 | ||
![]() |
f930f37d35 | ||
![]() |
4feb57e895 | ||
![]() |
712e3eb120 | ||
![]() |
a2b5db0003 | ||
![]() |
2803ec2e96 | ||
![]() |
ddd4f675a2 | ||
![]() |
d271dd2cce | ||
![]() |
ebfdd37451 | ||
![]() |
f6d6110aaa | ||
![]() |
a0730cf264 | ||
![]() |
ec61b3a5fc | ||
![]() |
521f2294cb | ||
![]() |
2f0674807c | ||
![]() |
1a57fa095f | ||
![]() |
167242fec0 | ||
![]() |
81208d78ac | ||
![]() |
da8b01771f | ||
![]() |
725fbe946b | ||
![]() |
b99ecb4dc9 | ||
![]() |
208a96b211 | ||
![]() |
43774455cc | ||
![]() |
5b61e077e0 | ||
![]() |
921cc3e5db | ||
![]() |
51dce3d161 | ||
![]() |
c10f013fc2 | ||
![]() |
2874d68bdb | ||
![]() |
11e83eb7e7 | ||
![]() |
9ec9a8705e | ||
![]() |
170635e3a6 | ||
![]() |
a5d1444ef4 | ||
![]() |
55708b39c3 | ||
![]() |
8c5ebdff36 | ||
![]() |
1c84f324a1 | ||
![]() |
3dba09f339 | ||
![]() |
7ebf8e66c9 | ||
![]() |
62218fe59d | ||
![]() |
95d9bb6dfc | ||
![]() |
33986075ef | ||
![]() |
766905ba9f | ||
![]() |
a29c64b6c7 | ||
![]() |
777e1cabc5 | ||
![]() |
396e97fc94 | ||
![]() |
9c6a4505c8 | ||
![]() |
238c3adad1 | ||
![]() |
c1d0a8b5ce | ||
![]() |
08ce24ec3f | ||
![]() |
281b8714ef | ||
![]() |
d5be3cce9c | ||
![]() |
8dcefaf2e3 | ||
![]() |
48e3432a63 | ||
![]() |
69d3c611aa | ||
![]() |
36827e1134 | ||
![]() |
351ac4a2c0 | ||
![]() |
36fff59a38 | ||
![]() |
1e60a4386a | ||
![]() |
e9f1b53ae6 | ||
![]() |
5016839b90 | ||
![]() |
faf35e6082 | ||
![]() |
d07a6edd2f | ||
![]() |
553d4e9283 | ||
![]() |
7cef52478d | ||
![]() |
e7ce362d22 | ||
![]() |
c551c8b31b | ||
![]() |
9c36e71081 | ||
![]() |
c9c57af5f7 | ||
![]() |
2516496993 | ||
![]() |
3b565b5f97 | ||
![]() |
0742976138 | ||
![]() |
1a63663c85 | ||
![]() |
00a20fc8a5 | ||
![]() |
6ab4fb368c | ||
![]() |
fa39bb0a50 | ||
![]() |
645663cdfe | ||
![]() |
ee2bcbb41d | ||
![]() |
12b4ebf8d4 | ||
![]() |
8da4750ee2 | ||
![]() |
d22df2915c | ||
![]() |
e77d96cf89 | ||
![]() |
ae28ba84d2 | ||
![]() |
b3f3b01958 | ||
![]() |
52e9cab1c1 | ||
![]() |
1baaaa40cc | ||
![]() |
b8ed420058 | ||
![]() |
ed16ee3029 | ||
![]() |
7338b16c18 | ||
![]() |
f529441400 | ||
![]() |
03664d0426 | ||
![]() |
652cfb7caf | ||
![]() |
5540fbaec2 | ||
![]() |
0a0b473765 | ||
![]() |
df2d041483 | ||
![]() |
ffc6e19548 | ||
![]() |
d874d7661f | ||
![]() |
1d66e714e6 | ||
![]() |
d98a863b82 | ||
![]() |
3b6790c7dc | ||
![]() |
5ee3a9a9ca | ||
![]() |
dd26fa67f2 | ||
![]() |
89f5e60422 | ||
![]() |
c616165f81 | ||
![]() |
edac498d03 | ||
![]() |
d7d9dbd2c2 | ||
![]() |
f8eece22c8 | ||
![]() |
df563db294 | ||
![]() |
01b0d9eb97 | ||
![]() |
a7f13d841f | ||
![]() |
686d8bbe69 | ||
![]() |
4b36af4a34 | ||
![]() |
4a23a4bfee | ||
![]() |
ac3ad452c0 | ||
![]() |
027c01511c | ||
![]() |
4fdcc0496f | ||
![]() |
0c4a2bea69 | ||
![]() |
2ba3401238 | ||
![]() |
1735284a2a | ||
![]() |
8a3192ffc1 | ||
![]() |
ffea273a28 | ||
![]() |
1783aac438 | ||
![]() |
29bf3d2c04 | ||
![]() |
ee16fc958c | ||
![]() |
5d73215a8d | ||
![]() |
b7356bc526 | ||
![]() |
8ec8282f38 | ||
![]() |
43b7b98949 | ||
![]() |
f791065a98 | ||
![]() |
d95db28695 | ||
![]() |
851fb16e7c | ||
![]() |
a4cbaafd10 | ||
![]() |
c5f2cdb822 | ||
![]() |
1892d29be0 | ||
![]() |
9e5a49b8cb | ||
![]() |
767ade02f4 | ||
![]() |
6e05071a47 | ||
![]() |
c8a990b9d3 | ||
![]() |
6868ceae9b | ||
![]() |
cace646346 | ||
![]() |
abcc225763 | ||
![]() |
b9673fc521 | ||
![]() |
b0ea3f4261 | ||
![]() |
0103219f00 | ||
![]() |
e15b4f40d6 | ||
![]() |
c46239af22 | ||
![]() |
ef5cf40fa6 | ||
![]() |
837bd79b20 | ||
![]() |
3edd4a24af | ||
![]() |
535a61b5a9 | ||
![]() |
e87b7daab5 | ||
![]() |
f20689519d | ||
![]() |
ee9c460f74 | ||
![]() |
84ba14fa29 | ||
![]() |
3c75963352 | ||
![]() |
5f9dd8287c | ||
![]() |
d99f074eb7 | ||
![]() |
a989140a07 | ||
![]() |
420a4c163d | ||
![]() |
1bab735580 | ||
![]() |
a0fcd0cf22 | ||
![]() |
5d5c753c74 | ||
![]() |
7e7e3eb8d5 | ||
![]() |
2e5f8aeba1 | ||
![]() |
c2fe961db1 | ||
![]() |
2a6acc04a1 | ||
![]() |
a467128093 | ||
![]() |
abf41eb85c | ||
![]() |
d83dea4463 | ||
![]() |
ec43721c3c | ||
![]() |
26ec62714c | ||
![]() |
7873b5e78b | ||
![]() |
1b889c527f | ||
![]() |
9937ff8ac0 | ||
![]() |
95b2df8261 | ||
![]() |
1ebadf8620 | ||
![]() |
9179d9592d | ||
![]() |
f208217412 | ||
![]() |
43c5058682 | ||
![]() |
99949c8f6f | ||
![]() |
78c4351e04 | ||
![]() |
0a427890fe | ||
![]() |
e735abe334 | ||
![]() |
c0070b2f13 | ||
![]() |
cfedc6e9b4 | ||
![]() |
b66d7f7e0b | ||
![]() |
e01df06cd7 | ||
![]() |
6584897b69 | ||
![]() |
aa4f45b9a5 | ||
![]() |
006b8fa3f0 | ||
![]() |
6a01153ce4 | ||
![]() |
34aab116ae | ||
![]() |
33232face9 | ||
![]() |
b88b2b3d79 | ||
![]() |
744d729dab | ||
![]() |
71b5e43153 | ||
![]() |
8459f27312 | ||
![]() |
7dfbdef505 | ||
![]() |
94386374ff | ||
![]() |
4f093d5b97 | ||
![]() |
37420c342b | ||
![]() |
ef369c2e2b | ||
![]() |
1e89ca0994 | ||
![]() |
92bb10eed8 | ||
![]() |
8465c5fe0e | ||
![]() |
42e248a8da | ||
![]() |
e71a652985 | ||
![]() |
bfef0fbff3 | ||
![]() |
545685bc32 | ||
![]() |
13ad2b4dc2 | ||
![]() |
d019343017 | ||
![]() |
e977ec924c | ||
![]() |
b7d5652bf6 | ||
![]() |
725e48fce4 | ||
![]() |
43816c268b | ||
![]() |
72e80db823 | ||
![]() |
7b33c343f8 | ||
![]() |
6d0601b99f | ||
![]() |
b6e713711d | ||
![]() |
c53edeeb6c | ||
![]() |
dfb98417b3 | ||
![]() |
9716c3a30e | ||
![]() |
c4c44c4445 | ||
![]() |
0debe9bd6f | ||
![]() |
f1da118a6c | ||
![]() |
08a0bb756d | ||
![]() |
20c6159c04 | ||
![]() |
a47e9d1a4b | ||
![]() |
e93dd374a4 | ||
![]() |
0847ca4ec2 | ||
![]() |
572d8d0cc4 | ||
![]() |
c9a57d354d | ||
![]() |
52b77f6e31 | ||
![]() |
4c9782ee28 | ||
![]() |
26225307d3 | ||
![]() |
0129d1e158 | ||
![]() |
ece6037a1e | ||
![]() |
bea678a7cd | ||
![]() |
1fb3fbb4de | ||
![]() |
2c38d19af2 | ||
![]() |
311e627463 | ||
![]() |
8e1e36a0d5 | ||
![]() |
db0bb425e7 | ||
![]() |
e001dd2d45 | ||
![]() |
c38772c97f | ||
![]() |
3a31589fdd | ||
![]() |
460522bb67 | ||
![]() |
176debaf37 | ||
![]() |
072f83b240 | ||
![]() |
cbd6770f2f | ||
![]() |
0c3a7bcf12 | ||
![]() |
389fbaaca2 | ||
![]() |
9652efd107 | ||
![]() |
f3ea834322 | ||
![]() |
02a6a46e86 | ||
![]() |
75ad90abc7 | ||
![]() |
4104593180 | ||
![]() |
78e1704122 | ||
![]() |
b9e99575ce | ||
![]() |
accd262561 | ||
![]() |
3057d19cdf | ||
![]() |
8dd83a2cf3 | ||
![]() |
49b84f9229 | ||
![]() |
b43bf4dd74 | ||
![]() |
14424281a0 | ||
![]() |
2b3fd0d4d3 | ||
![]() |
894b9cfdb9 | ||
![]() |
7ecbb0454f | ||
![]() |
fa7fa2a55f | ||
![]() |
5c0576ca55 | ||
![]() |
4e909f9411 | ||
![]() |
99f49e266c | ||
![]() |
0458211ebc | ||
![]() |
8a7b9d9f45 | ||
![]() |
3fcf463f9e | ||
![]() |
f77cd63286 | ||
![]() |
2378c2d754 | ||
![]() |
bbdb4a67e1 | ||
![]() |
21851c0673 | ||
![]() |
7e219c362c | ||
![]() |
203f48d1fd | ||
![]() |
371c82b2bf | ||
![]() |
8176880173 | ||
![]() |
533a6b0240 | ||
![]() |
0c0400b6fc | ||
![]() |
2e83af7c21 | ||
![]() |
a3f7947ad2 | ||
![]() |
7d9d459ac2 | ||
![]() |
3ea1073809 | ||
![]() |
76a959a578 | ||
![]() |
f43873dc5f | ||
![]() |
921553d7bb | ||
![]() |
27946a981f | ||
![]() |
74617389c8 | ||
![]() |
b4a8b8c0d4 | ||
![]() |
ffa9f4b47f | ||
![]() |
667e22bbc7 | ||
![]() |
d4d838c8bb | ||
![]() |
c61a889c86 | ||
![]() |
567fe89a77 | ||
![]() |
950772ab8a | ||
![]() |
0bd0e2a3ec | ||
![]() |
47774ce882 | ||
![]() |
3c9bcdd347 | ||
![]() |
3934d2d30c | ||
![]() |
423ce9557a | ||
![]() |
947848ebf6 | ||
![]() |
5e22fe488e | ||
![]() |
c666cf1c44 | ||
![]() |
2be245b75e | ||
![]() |
17927f5c26 | ||
![]() |
754f26a97c | ||
![]() |
29241c4f83 | ||
![]() |
6f655eb9b9 | ||
![]() |
65dfd90141 | ||
![]() |
59abdbd2dd | ||
![]() |
a6d6873856 | ||
![]() |
e99df3a3be | ||
![]() |
8b657255cc | ||
![]() |
0e1dc79321 | ||
![]() |
0ef15e5fac | ||
![]() |
9ea8a840a9 | ||
![]() |
7b88089593 | ||
![]() |
b513943893 | ||
![]() |
edffc56600 | ||
![]() |
856b0e6886 | ||
![]() |
bf1eb46b87 | ||
![]() |
7e27d660e2 | ||
![]() |
53174ea45f | ||
![]() |
87b624f5d5 | ||
![]() |
e1efc71ad3 | ||
![]() |
f804a739b1 | ||
![]() |
8751783a1b | ||
![]() |
3a2ec50d5f | ||
![]() |
ce9aeed4cb | ||
![]() |
f2be6432a9 | ||
![]() |
e96f8c0444 | ||
![]() |
2bf3bc3e10 | ||
![]() |
28143f86f9 | ||
![]() |
76ec3d3248 | ||
![]() |
d0c85a5a96 | ||
![]() |
46ed717af4 | ||
![]() |
ad37c88f80 | ||
![]() |
8c6e8a6eb8 | ||
![]() |
dd33317f45 | ||
![]() |
c344d63fb3 | ||
![]() |
62557f4d6b | ||
![]() |
9c46cc4ea0 | ||
![]() |
a236a439cc | ||
![]() |
c779e2674a | ||
![]() |
a94d4be466 | ||
![]() |
b7d2d4cfe8 | ||
![]() |
a6c797ee4b | ||
![]() |
3e40b2249f | ||
![]() |
2fe1b5034d | ||
![]() |
b4430839a3 | ||
![]() |
412cf974a4 | ||
![]() |
3fc6beeff0 | ||
![]() |
2ff35c8bfa | ||
![]() |
169db88c20 | ||
![]() |
ca419c84b8 | ||
![]() |
aede71b1dc | ||
![]() |
14d573cbf1 | ||
![]() |
7819aa6b20 | ||
![]() |
7cc6b63aac | ||
![]() |
4af6362751 | ||
![]() |
d54075197f | ||
![]() |
5cce56199b | ||
![]() |
3cc12817f6 | ||
![]() |
68875ba600 | ||
![]() |
2119a16e24 | ||
![]() |
9f5c938ff3 | ||
![]() |
b42a8d2364 | ||
![]() |
61fc01e79e | ||
![]() |
cceaec1d74 | ||
![]() |
8e5f9c8160 | ||
![]() |
c620fd42f4 | ||
![]() |
2a859f870a | ||
![]() |
b680753db8 | ||
![]() |
35af940166 | ||
![]() |
bc0fec0afe | ||
![]() |
e3eca82cc3 | ||
![]() |
e7651d0d0f | ||
![]() |
6a6f28668f | ||
![]() |
dadf1339b5 | ||
![]() |
c2c4228722 | ||
![]() |
3d3bca5338 | ||
![]() |
6a3008d7ff | ||
![]() |
1401621913 | ||
![]() |
3c034b0a0c | ||
![]() |
395191bd75 | ||
![]() |
ec7d8fb6bd | ||
![]() |
062948b110 | ||
![]() |
7f3dc5f040 | ||
![]() |
dd0798a317 | ||
![]() |
9209ccfa40 | ||
![]() |
2525d32e17 | ||
![]() |
f3ac8a7cd9 | ||
![]() |
f3d95f70e2 | ||
![]() |
57526067f5 | ||
![]() |
0545bab35d | ||
![]() |
293836494d | ||
![]() |
ae8bda190e | ||
![]() |
34d9d8abd4 | ||
![]() |
bd67e986f4 | ||
![]() |
e587518d85 | ||
![]() |
210b6c38bd | ||
![]() |
9592c0b466 | ||
![]() |
a33537b2b9 | ||
![]() |
81d5c9757c | ||
![]() |
f34124a50b | ||
![]() |
74a39c715b | ||
![]() |
195496333b | ||
![]() |
9ccaa90439 | ||
![]() |
4733c5fef0 | ||
![]() |
a97ddc8cb9 | ||
![]() |
59a5b000e0 | ||
![]() |
d49a2ccb08 | ||
![]() |
4a75acb46c | ||
![]() |
453368078b | ||
![]() |
6e3b643bdf | ||
![]() |
a769352a74 | ||
![]() |
8a63c27925 | ||
![]() |
310895f060 | ||
![]() |
4428894aba | ||
![]() |
4f021cbced | ||
![]() |
ba31d176c8 | ||
![]() |
68edbc3e4a | ||
![]() |
5068227a46 | ||
![]() |
f8f3bc89e7 | ||
![]() |
635f7026b0 | ||
![]() |
0f1e4f0326 | ||
![]() |
89355edb8a | ||
![]() |
37c8f5c1da | ||
![]() |
5bba2df526 | ||
![]() |
1935694440 | ||
![]() |
12dd6ea8bb | ||
![]() |
a1f922b040 | ||
![]() |
017cf088a1 | ||
![]() |
df627cc417 | ||
![]() |
1b20300b73 | ||
![]() |
3b9ffea36f | ||
![]() |
e242f3999c | ||
![]() |
523f89cc8c | ||
![]() |
0575a6d652 | ||
![]() |
08b88714e0 | ||
![]() |
bcaff4b844 | ||
![]() |
82f336a78f | ||
![]() |
350aa33022 | ||
![]() |
d6290a2f1a | ||
![]() |
71e9d08863 | ||
![]() |
9729dc7594 | ||
![]() |
92c1b8f31e | ||
![]() |
9ffa2604f8 | ||
![]() |
03b1fad4d4 | ||
![]() |
72eb4c534f | ||
![]() |
47d936e9cc | ||
![]() |
8e08407090 | ||
![]() |
c0a4558c62 | ||
![]() |
5ecb6fecc4 | ||
![]() |
eea726740b | ||
![]() |
0ea4c970d7 | ||
![]() |
57936a1374 | ||
![]() |
9242fde6b8 | ||
![]() |
d1f653be65 | ||
![]() |
0035dceb0a | ||
![]() |
b159bc0c5f | ||
![]() |
a222c4879c | ||
![]() |
eb2f413cf0 | ||
![]() |
e54748d355 | ||
![]() |
08e2e2e791 | ||
![]() |
531c0067ec | ||
![]() |
e228caaca6 | ||
![]() |
9ac8f89b1d | ||
![]() |
c6a72a14ac | ||
![]() |
c74edd0e33 | ||
![]() |
98acf3f281 | ||
![]() |
fe30db935b | ||
![]() |
6303d54cbb | ||
![]() |
9d91aa23e6 | ||
![]() |
c2ada39fd3 | ||
![]() |
77b6e27500 | ||
![]() |
99640171e2 | ||
![]() |
38b3ab791f | ||
![]() |
a86fc52eea | ||
![]() |
6d3ed3f16f | ||
![]() |
be798998bf | ||
![]() |
c3b425d570 | ||
![]() |
a25743875f | ||
![]() |
124d1a5942 | ||
![]() |
eec77b3ae7 | ||
![]() |
640bac1aa4 | ||
![]() |
0b12fae84d | ||
![]() |
d9353c4fd3 | ||
![]() |
0c9fc2f809 | ||
![]() |
1a954748a0 | ||
![]() |
2abad0f479 | ||
![]() |
2fb40fe728 | ||
![]() |
b57330cf75 | ||
![]() |
8cd91fabf9 | ||
![]() |
e0580ee385 | ||
![]() |
144d092637 | ||
![]() |
2bde9afdb9 | ||
![]() |
f8b09c194f | ||
![]() |
0e69ad32c1 | ||
![]() |
9b4e14df71 | ||
![]() |
03f7803f26 | ||
![]() |
c6cbcc2c25 | ||
![]() |
27aa34d4ae | ||
![]() |
79e0db4ca0 | ||
![]() |
9ae3acf2e7 | ||
![]() |
731ea9b489 | ||
![]() |
18b30b5019 | ||
![]() |
24d51b9d14 | ||
![]() |
a1edc199df | ||
![]() |
db4aa81528 | ||
![]() |
319ba94a52 | ||
![]() |
1025f0be91 | ||
![]() |
77d3b5e8f1 | ||
![]() |
a0ad96a787 | ||
![]() |
39c5af5dbc | ||
![]() |
acca6a799b | ||
![]() |
b77e62260a | ||
![]() |
b6995ca011 | ||
![]() |
715844fd08 | ||
![]() |
2b29ca796e | ||
![]() |
5184008812 | ||
![]() |
cf696ce443 | ||
![]() |
1a2ea4c06c | ||
![]() |
a800afcbf8 | ||
![]() |
ff4534613a | ||
![]() |
b8fe2c74bc | ||
![]() |
be3b5199b0 | ||
![]() |
d849a40af6 | ||
![]() |
4f48c10312 | ||
![]() |
5274fee8a7 | ||
![]() |
5462f34ed0 | ||
![]() |
0958ed5844 | ||
![]() |
e3cba18532 | ||
![]() |
9bf7fde49f | ||
![]() |
4783ebc918 | ||
![]() |
8bda52783b | ||
![]() |
07feec7ea6 | ||
![]() |
e7471f589a | ||
![]() |
24208be5cf | ||
![]() |
8db5f66498 | ||
![]() |
be670bfd1f | ||
![]() |
19acf090ed | ||
![]() |
2de7f6b457 | ||
![]() |
a2e265e9a2 | ||
![]() |
28d7b29d72 | ||
![]() |
9cb4aaf3c2 | ||
![]() |
80dc602193 | ||
![]() |
c7f5a87258 |
24
.gitignore
vendored
24
.gitignore
vendored
@@ -1,12 +1,11 @@
|
||||
*.Plo
|
||||
*.Po
|
||||
*.a
|
||||
*.bz2
|
||||
*.d
|
||||
*.gz
|
||||
*.la
|
||||
*.lo
|
||||
*.o
|
||||
*.exe
|
||||
.deps
|
||||
.dirstamp
|
||||
Makefile
|
||||
@@ -32,14 +31,16 @@ libtool
|
||||
ltmain.sh
|
||||
missing
|
||||
mkinstalldirs
|
||||
/test-driver
|
||||
mpd
|
||||
mpd.exe
|
||||
mpd.service
|
||||
stamp-h1
|
||||
tags
|
||||
*~
|
||||
.#*
|
||||
.stgit*
|
||||
src/dsd2pcm/dsd2pcm
|
||||
src/win/mpd_win32_rc.rc
|
||||
doc/doxygen.conf
|
||||
doc/protocol.html
|
||||
doc/protocol
|
||||
@@ -61,3 +62,20 @@ test/dump_playlist
|
||||
test/run_normalize
|
||||
test/tmp
|
||||
test/run_inotify
|
||||
test/test_queue_priority
|
||||
test/run_ntp_server
|
||||
test/run_resolver
|
||||
test/run_tcp_connect
|
||||
test/test_pcm
|
||||
test/dump_rva2
|
||||
test/dump_text_file
|
||||
test/test_util
|
||||
test/test_byte_reverse
|
||||
test/test_mixramp
|
||||
test/test_vorbis_encoder
|
||||
test/DumpDatabase
|
||||
|
||||
/*.tar.gz
|
||||
/*.tar.bz2
|
||||
/*.tar.xz
|
||||
/mpd-*/
|
||||
|
94
AUTHORS
94
AUTHORS
@@ -1,67 +1,31 @@
|
||||
Current Developers
|
||||
------------------
|
||||
Music Player Daemon - http://www.musicpd.org
|
||||
Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
|
||||
Avuton Olrich <avuton@gmail.com>
|
||||
release manager
|
||||
The following people have contributed code to MPD:
|
||||
|
||||
Max Kellermann <max@duempel.org>
|
||||
lead developer
|
||||
|
||||
Laszlo Ashin <kodest@gmail.com>
|
||||
WavPack support
|
||||
|
||||
Viliam Mateicka <viliam.mateicka@gmail.com>
|
||||
FFmpeg support, mixer and archive API
|
||||
|
||||
Eric Wollesen <encoded@xmtp.net>
|
||||
encoder API, shout output
|
||||
|
||||
Thomas Jansen <mithi@mithi.net>
|
||||
multithreading tweaks, miscellaneous
|
||||
|
||||
Romain Bignon <romain@peerfuse.org>
|
||||
playlist manipulation
|
||||
|
||||
David Guibert <david.guibert@gmail.com>
|
||||
PulseAudio mixer
|
||||
|
||||
Jochen Keil <jochen.keil@gmail.com>
|
||||
CUE sheet support
|
||||
|
||||
Jeffrey Middleton <jefromi@gmail.com>
|
||||
playlist manipulation
|
||||
|
||||
Sean McNamara <smcnam@gmail.com>
|
||||
WIN32 compatibility
|
||||
|
||||
|
||||
Former Developers
|
||||
-----------------
|
||||
|
||||
Warren Dukes <warren.dukes@gmail.com>
|
||||
former lead developer and project founder
|
||||
|
||||
Niklas Hofer
|
||||
'next' and 'previous' patch
|
||||
|
||||
Jim Ramsay <i.am@jimramsay.com>
|
||||
Zerconf/avahi support
|
||||
|
||||
Guus Sliepen <guus@sliepen.eu.org>
|
||||
libsamplerate code
|
||||
|
||||
J. Alexander Treuman <jat@spatialrift.net>
|
||||
general, MP3, ID3, PulseAudio, format conversion, stored playlists
|
||||
AudioCompress, much much more...
|
||||
|
||||
Eric Wong
|
||||
former lead developer
|
||||
|
||||
José Anarch <anarchsss@gmail.com>
|
||||
JACK plugin
|
||||
|
||||
Patrik Weiskircher <pat@icore.at>
|
||||
Stored playlist commands
|
||||
|
||||
Nick Welch <mack@incise.org>
|
||||
Sighandlers
|
||||
Warren Dukes <warren.dukes@gmail.com>
|
||||
Avuton Olrich <avuton@gmail.com>
|
||||
Max Kellermann <max@duempel.org>
|
||||
Laszlo Ashin <kodest@gmail.com>
|
||||
Viliam Mateicka <viliam.mateicka@gmail.com>
|
||||
Eric Wollesen <encoded@xmtp.net>
|
||||
Thomas Jansen <mithi@mithi.net>
|
||||
Romain Bignon <romain@peerfuse.org>
|
||||
David Guibert <david.guibert@gmail.com>
|
||||
Jochen Keil <jochen.keil@gmail.com>
|
||||
Jeffrey Middleton <jefromi@gmail.com>
|
||||
Sean McNamara <smcnam@gmail.com>
|
||||
Niklas Hofer
|
||||
Jim Ramsay <i.am@jimramsay.com>
|
||||
Guus Sliepen <guus@sliepen.eu.org>
|
||||
J. Alexander Treuman <jat@spatialrift.net>
|
||||
Eric Wong
|
||||
José Anarch <anarchsss@gmail.com>
|
||||
Patrik Weiskircher <pat@icore.at>
|
||||
Nick Welch <mack@incise.org>
|
||||
Jonathan Neuschäfer <j.neuschaefer@gmx.net>
|
||||
Anton Khirnov <anton@khirnov.net>
|
||||
Simon Kagstrom <simon.kagstrom@gmail.com>
|
||||
Denis Krjuchkov <denis@crazydev.net>
|
||||
Jurgen Kramer <gtmkramer@xs4all.nl>
|
||||
Jean-Francois Dockes <jf@dockes.org>
|
||||
|
70
INSTALL
70
INSTALL
@@ -3,17 +3,20 @@
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This document is a very small amount of documentation about what is needed to
|
||||
install MPD. If more information is desired see the community wiki at
|
||||
http://mpd.wikia.com.
|
||||
install MPD. If more information is desired, read the user manual:
|
||||
|
||||
http://www.musicpd.org/doc/user/
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
gcc - http://gcc.gnu.org/
|
||||
Any other C99 compliant compiler should also work.
|
||||
gcc 4.6 or later - http://gcc.gnu.org/
|
||||
clang 3.2 or later - http://clang.llvm.org/
|
||||
Any other C++11 compliant compiler should also work.
|
||||
|
||||
GLib 2.12 - http://www.gtk.org/
|
||||
GLib 2.28 - http://www.gtk.org/
|
||||
General-purpose utility library.
|
||||
|
||||
|
||||
@@ -37,15 +40,9 @@ Linux. You will need libasound.
|
||||
FIFO
|
||||
This is a mostly undocumented, developer plugin to transmit raw data.
|
||||
|
||||
MVP - http://en.wikipedia.org/wiki/Hauppauge_MediaMVP
|
||||
A network media player.
|
||||
|
||||
OSS - http://www.opensound.com
|
||||
Open Sound System.
|
||||
|
||||
OSX - http://www.apple.com
|
||||
Necessary if you are on Mac OSX.
|
||||
|
||||
PulseAudio - http://www.pulseaudio.org/
|
||||
An advanced sound daemon. You will need libpulse.
|
||||
|
||||
@@ -59,9 +56,6 @@ You also need an encoder: either libvorbisenc (ogg), or liblame (mp3).
|
||||
OpenAL - http://kcat.strangesoft.net/openal.html
|
||||
Open Audio Library
|
||||
|
||||
libffado - http://www.ffado.org/
|
||||
For FireWire audio devices.
|
||||
|
||||
|
||||
Optional Input Dependencies
|
||||
---------------------------
|
||||
@@ -81,19 +75,17 @@ Alternative for MP3 support.
|
||||
Ogg Vorbis - http://www.xiph.org/ogg/vorbis/
|
||||
For Ogg Vorbis support. You will need libogg and libvorbis.
|
||||
|
||||
FLAC - http://flac.sourceforge.net/
|
||||
For FLAC support. You will need version 1.1.0 or higher of libflac.
|
||||
libopus - http://www.opus-codec.org/
|
||||
Opus codec support
|
||||
|
||||
OggFLAC - http://www.xiph.org/ogg/vorbis/ and http://flac.sourceforge.net/
|
||||
For OggFLAC support. You will need liboggflac, which can be built from the
|
||||
FLAC sources if libogg is already installed. Versions of flac 1.1.3 and
|
||||
greater will automatically detect and use OggFLAC if it's available.
|
||||
FLAC - http://flac.sourceforge.net/
|
||||
For FLAC support. You will need version 1.2 or higher of libFLAC.
|
||||
|
||||
Audio File - http://www.68k.org/~michael/audiofile/
|
||||
For WAVE, AIFF, and AU support. You will need libaudiofile.
|
||||
|
||||
FAAD2 - http://www.audiocoding.com/
|
||||
For MP4/AAC support. You will need libmp4ff.
|
||||
For MP4/AAC support.
|
||||
|
||||
libmpcdec - http://www.musepack.net/
|
||||
For Musepack support.
|
||||
@@ -101,16 +93,16 @@ For Musepack support.
|
||||
MikMod - http://mikmod.raphnet.net/
|
||||
For MOD support. You will need libmikmod.
|
||||
|
||||
libavcodec, libavformat (ffmpeg) - http://ffmpeg.mplayerhq.hu/
|
||||
libavcodec, libavformat (ffmpeg or libav) - http://ffmpeg.mplayerhq.hu/ http://libav.org/
|
||||
Multi-codec library.
|
||||
|
||||
libsidplay2 - http://sidplay2.sourceforge.net/
|
||||
For C64 SID support.
|
||||
|
||||
libfluidsynth - http://fluidsynth.resonance.org/
|
||||
For MIDI support (DO NOT USE - use libwildmidi instead)
|
||||
For MIDI support.
|
||||
|
||||
libwildmidi - http://wildmidi.sourceforge.net/
|
||||
libwildmidi 0.2.3 - http://wildmidi.sourceforge.net/
|
||||
For MIDI support.
|
||||
|
||||
libsndfile - http://www.mega-nerd.com/libsndfile/
|
||||
@@ -119,6 +111,12 @@ WAVE, AIFF, and many others.
|
||||
libwavpack - http://www.wavpack.com/
|
||||
For WavPack playback.
|
||||
|
||||
libadplug - http://adplug.sourceforge.net/
|
||||
For AdLib playback.
|
||||
|
||||
despotify - https://github.com/SimonKagstrom/despotify
|
||||
For Spotify playback.
|
||||
|
||||
|
||||
Optional Miscellaneous Dependencies
|
||||
-----------------------------------
|
||||
@@ -138,8 +136,11 @@ For playing MMS streams.
|
||||
SQLite - http://www.sqlite.org/
|
||||
For the sticker database.
|
||||
|
||||
libcue - http://libcue.sourceforge.net/
|
||||
For CUE sheet support.
|
||||
libcdio - http://www.gnu.org/software/libcdio/
|
||||
For playing audio CDs.
|
||||
|
||||
libsystemd-daemon - http://freedesktop.org/wiki/Software/systemd/
|
||||
For systemd activation.
|
||||
|
||||
|
||||
pkg-config
|
||||
@@ -162,13 +163,9 @@ Get the latest release from of MPD from <http://www.musicpd.org/>.
|
||||
Compile
|
||||
-------
|
||||
|
||||
1) unzip and untar the archive
|
||||
1) unpack the archive
|
||||
|
||||
$ tar zxvf mpd-x.x.x.tar.gz
|
||||
|
||||
or
|
||||
|
||||
$ tar jxvf mpd-x.x.x.tar.bz2
|
||||
$ tar xf mpd-x.x.x.tar.xz
|
||||
|
||||
2) change to directory created
|
||||
|
||||
@@ -195,9 +192,9 @@ Run
|
||||
|
||||
$ mpd <config file>
|
||||
|
||||
First default is ~/.mpdconf then ~/.mpd/mpd.conf then /etc/mpd.conf. If
|
||||
neither of these exist a mpd configuration file must be specified at
|
||||
runtime.
|
||||
First default is $XDG_CONFIG_HOME/mpd/mpd.conf then ~/.mpdconf then
|
||||
~/.mpd/mpd.conf then /etc/mpd.conf. If neither of these exist a mpd
|
||||
configuration file must be specified at runtime.
|
||||
|
||||
A sample config file is included with the source of MPD, mpdconf.example.
|
||||
|
||||
@@ -209,6 +206,5 @@ Using MPD
|
||||
---------
|
||||
|
||||
You can download many different interfaces for MPD at
|
||||
<http://mpd.wikia.com/wiki/Clients>
|
||||
|
||||
MPD can be interfaced directly using telnet (see COMMANDS, if you are brave).
|
||||
http://www.musicpd.org/clients/
|
||||
|
1820
Makefile.am
1820
Makefile.am
File diff suppressed because it is too large
Load Diff
268
NEWS
268
NEWS
@@ -1,3 +1,271 @@
|
||||
ver 0.18.9 (2014/03/02)
|
||||
* protocol
|
||||
- "findadd" requires the "add" permission
|
||||
* output
|
||||
- alsa: improved workaround for noise after manual song change
|
||||
* decoder
|
||||
- vorbis: fix linker failure when libvorbis/libogg are static
|
||||
* encoder
|
||||
- vorbis: fix another linker failure
|
||||
* output
|
||||
- pipe: fix hanging child process due to blocked signals
|
||||
* fix build failure due to missing signal.h include
|
||||
|
||||
ver 0.18.8 (2014/02/07)
|
||||
* decoder
|
||||
- ffmpeg: support libav v10_alpha1
|
||||
* encoder
|
||||
- vorbis: fix linker failure
|
||||
* output
|
||||
- roar: documentation
|
||||
* more robust Icy-Metadata parser
|
||||
* fix Solaris build failure
|
||||
|
||||
ver 0.18.7 (2014/01/13)
|
||||
* playlist
|
||||
- pls: fix crash after parser error
|
||||
- soundcloud: fix build failure with libyajl 2.0.1
|
||||
* decoder
|
||||
- faad: fix memory leak
|
||||
- mpcdec: reject libmpcdec SV7 in configure script
|
||||
* daemon: don't initialize supplementary groups when already running
|
||||
as the configured user
|
||||
|
||||
ver 0.18.6 (2013/12/24)
|
||||
* input
|
||||
- cdio_paranoia: support libcdio-paranoia 0.90
|
||||
* tags
|
||||
- riff: recognize upper-case "ID3" chunk name
|
||||
* decoder
|
||||
- ffmpeg: use relative timestamps
|
||||
* output
|
||||
- openal: fix build failure on Mac OS X
|
||||
- osx: fix build failure
|
||||
* mixer
|
||||
- alsa: fix build failure with uClibc
|
||||
* fix replay gain during cross-fade
|
||||
* accept files without metadata
|
||||
|
||||
ver 0.18.5 (2013/11/23)
|
||||
* configuration
|
||||
- fix crash when db_file is configured without music_directory
|
||||
- fix crash on "stats" without db_file/music_directory
|
||||
* database
|
||||
- proxy: auto-reload statistics
|
||||
- proxy: provide "db_update" in "stats" response
|
||||
* input
|
||||
- curl: work around stream resume bug (fixed in libcurl 7.32.0)
|
||||
* decoder
|
||||
- fluidsynth: auto-detect by default
|
||||
* clip 24 bit data from libsamplerate
|
||||
* fix ia64, mipsel and other little-endian architectures
|
||||
* fix build failures due to missing includes
|
||||
* fix build failure with static libmpdclient
|
||||
|
||||
ver 0.18.4 (2013/11/13)
|
||||
* decoder
|
||||
- dsdiff: fix byte order bug
|
||||
* fix build failures due to missing includes
|
||||
* libc++ compatibility
|
||||
|
||||
ver 0.18.3 (2013/11/08)
|
||||
* fix stuck MPD after song change (0.18.2 regression)
|
||||
|
||||
ver 0.18.2 (2013/11/07)
|
||||
* protocol:
|
||||
- "close" flushes the output buffer
|
||||
* input:
|
||||
- cdio_paranoia: add setting "default_byte_order"
|
||||
- curl: fix bug with redirected streams
|
||||
* playlist:
|
||||
- pls: fix reversed song order
|
||||
* decoder:
|
||||
- audiofile: require libaudiofile 0.3 due to API breakage
|
||||
- dsf: enable DSD128
|
||||
* enable buffering when starting playback (regression fix)
|
||||
* fix build failures due to missing includes
|
||||
* fix big-endian support
|
||||
|
||||
ver 0.18.1 (2013/11/04)
|
||||
* protocol:
|
||||
- always ignore whitespace at the end of the line
|
||||
* networking:
|
||||
- log UNIX domain path names instead of "localhost"
|
||||
- open listener sockets in the order they were configured
|
||||
- don't abort if IPv6 is not available
|
||||
* output:
|
||||
- alsa: avoid endless loop in Raspberry Pi workaround
|
||||
* filter:
|
||||
- autoconvert: fix "volume_normalization" with mp3 files
|
||||
* add missing files to source tarball
|
||||
|
||||
ver 0.18 (2013/10/31)
|
||||
* configuration:
|
||||
- allow tilde paths for socket
|
||||
- default filesystem charset is UTF-8 instead of ISO-8859-1
|
||||
- increase default buffer size to 4 MB
|
||||
* protocol:
|
||||
- new command "readcomments" lists arbitrary file tags
|
||||
- new command "toggleoutput"
|
||||
- "find"/"search" with "any" does not match file name
|
||||
- "search" and "find" with base URI (keyword "base")
|
||||
- search for album artist falls back to the artist tag
|
||||
- re-add the "volume" command
|
||||
* input:
|
||||
- curl: enable https
|
||||
- soup: plugin removed
|
||||
* playlist:
|
||||
- lastfm: remove defunct Last.fm support
|
||||
* decoder:
|
||||
- adplug: new decoder plugin using libadplug
|
||||
- dsf: don't play junk at the end of the "data" chunk
|
||||
- ffmpeg: drop support for pre-0.8 ffmpeg
|
||||
- flac: require libFLAC 1.2 or newer
|
||||
- flac: support FLAC files inside archives
|
||||
- opus: new decoder plugin for the Opus codec
|
||||
- vorbis: skip 16 bit quantisation, provide float samples
|
||||
- mikmod: add "loop" configuration parameter
|
||||
- modplug: add "loop_count" configuration parameter
|
||||
- mp4ff: obsolete plugin removed
|
||||
* encoder:
|
||||
- opus: new encoder plugin for the Opus codec
|
||||
- vorbis: accept floating point input samples
|
||||
* output:
|
||||
- new option "tags" may be used to disable sending tags to output
|
||||
- alsa: workaround for noise after manual song change
|
||||
- ffado: remove broken plugin
|
||||
- httpd: support HEAD requests
|
||||
- mvp: remove obsolete plugin
|
||||
- osx: disabled by default because it's unmaintained and unsupported
|
||||
* improved decoder/output error reporting
|
||||
* eliminate timer wakeup on idle MPD
|
||||
* fix unresponsive MPD while waiting for stream
|
||||
* port of the source code to C++11
|
||||
|
||||
ver 0.17.6 (2013/10/14)
|
||||
* mixer:
|
||||
- alsa: fix busy loop when USB sound device gets unplugged
|
||||
* decoder:
|
||||
- modplug: fix build with Debian package 1:0.8.8.4-4
|
||||
* stored playlists:
|
||||
- fix loading playlists with references to local files
|
||||
- obey filesystem_charset for URLs
|
||||
|
||||
ver 0.17.5 (2013/08/04)
|
||||
* protocol:
|
||||
- fix "playlistadd" with URI
|
||||
- fix "move" relative to current when there is no current song
|
||||
* decoder:
|
||||
- ffmpeg: support "application/flv"
|
||||
- mikmod: adapt to libmikmod 3.2
|
||||
* configure.ac:
|
||||
- detect system "ar"
|
||||
|
||||
ver 0.17.4 (2013/04/08)
|
||||
* protocol:
|
||||
- allow to omit END in ranges (START:END)
|
||||
- don't emit IDLE_PLAYER before audio format is known
|
||||
* decoder:
|
||||
- ffmpeg: support float planar audio (ffmpeg 1.1)
|
||||
- ffmpeg: fix AVFrame allocation
|
||||
* player:
|
||||
- implement missing "idle" events on output errors
|
||||
* clock: fix build failure
|
||||
|
||||
ver 0.17.3 (2013/01/06)
|
||||
* output:
|
||||
- osx: fix pops during playback
|
||||
- recorder: fix I/O error check
|
||||
- shout: fix memory leak in error handler
|
||||
- recorder, shout: support Ogg packets that span more than one page
|
||||
* decoder:
|
||||
- ffmpeg: ignore negative time stamps
|
||||
- ffmpeg: support planar audio
|
||||
* playlist:
|
||||
- cue: fix memory leak
|
||||
- cue: fix CUE files with only one track
|
||||
|
||||
ver 0.17.2 (2012/09/30)
|
||||
* protocol:
|
||||
- fix crash in local file check
|
||||
* decoder:
|
||||
- fluidsynth: remove throttle (requires libfluidsynth 1.1)
|
||||
- fluidsynth: stop playback at end of file
|
||||
- fluidsynth: check MIDI file format while scanning
|
||||
- fluidsynth: add sample rate setting
|
||||
- wavpack: support all APEv2 tags
|
||||
* output:
|
||||
- httpd: use monotonic clock, avoid hiccups after system clock adjustment
|
||||
- httpd: fix throttling bug after resuming playback
|
||||
* playlist:
|
||||
- cue: map "PERFORMER" to "artist" or "album artist"
|
||||
* mapper: fix non-UTF8 music directory name
|
||||
* mapper: fix potential crash in file permission check
|
||||
* playlist: fix use-after-free bug
|
||||
* playlist: fix memory leak
|
||||
* state_file: save song priorities
|
||||
* player: disable cross-fading in "single" mode
|
||||
* update: fix unsafe readlink() usage
|
||||
* configure.ac:
|
||||
- don't auto-detect the vorbis encoder when Tremor is enabled
|
||||
|
||||
ver 0.17.1 (2012/07/31)
|
||||
* protocol:
|
||||
- require appropriate permissions for searchadd{,pl}
|
||||
* tags:
|
||||
- aiff: support the AIFC format
|
||||
- ape: check for ID3 if no usable APE tag was found
|
||||
* playlist:
|
||||
- cue: support file types "MP3", "AIFF"
|
||||
* output:
|
||||
- fix noisy playback with conversion and software volume
|
||||
|
||||
ver 0.17 (2012/06/27)
|
||||
* protocol:
|
||||
- support client-to-client communication
|
||||
- "update" and "rescan" need only "CONTROL" permission
|
||||
- new command "seekcur" for simpler seeking within current song
|
||||
- new command "config" dumps location of music directory
|
||||
- add range parameter to command "load"
|
||||
- print extra "playlist" object for embedded CUE sheets
|
||||
- new commands "searchadd", "searchaddpl"
|
||||
* input:
|
||||
- cdio_paranoia: new input plugin to play audio CDs
|
||||
- curl: enable CURLOPT_NETRC
|
||||
- curl: non-blocking I/O
|
||||
- soup: new input plugin based on libsoup
|
||||
* tags:
|
||||
- RVA2: support separate album/track replay gain
|
||||
* decoder:
|
||||
- mpg123: implement seeking
|
||||
- ffmpeg: drop support for pre-0.5 ffmpeg
|
||||
- ffmpeg: support WebM
|
||||
- oggflac: delete this obsolete plugin
|
||||
- dsdiff: new decoder plugin
|
||||
* output:
|
||||
- alsa: support DSD-over-USB (dCS suggested standard)
|
||||
- httpd: support for streaming to a DLNA client
|
||||
- openal: improve buffer cancellation
|
||||
- osx: allow user to specify other audio devices
|
||||
- osx: implement 32 bit playback
|
||||
- shout: add possibility to set url
|
||||
- roar: new output plugin for RoarAudio
|
||||
- winmm: fail if wrong device specified instead of using default device
|
||||
* mixer:
|
||||
- alsa: listen for external volume changes
|
||||
* playlist:
|
||||
- allow references to songs outside the music directory
|
||||
- new CUE parser, without libcue
|
||||
- soundcloud: new plugin for accessing soundcloud.com
|
||||
* state_file: add option "restore_paused"
|
||||
* cue: show CUE track numbers
|
||||
* allow port specification in "bind_to_address" settings
|
||||
* support floating point samples
|
||||
* systemd socket activation
|
||||
* improve --version output
|
||||
* WIN32: fix renaming of stored playlists with non-ASCII names
|
||||
|
||||
|
||||
ver 0.16.8 (2012/04/04)
|
||||
* fix for libsamplerate assertion failure
|
||||
* decoder:
|
||||
|
@@ -16,7 +16,7 @@ if test -n "$AM_FORCE_VERSION"
|
||||
then
|
||||
AM_VERSIONS="$AM_FORCE_VERSION"
|
||||
else
|
||||
AM_VERSIONS='1.11 1.10'
|
||||
AM_VERSIONS='1.11'
|
||||
fi
|
||||
if test -n "$AC_FORCE_VERSION"
|
||||
then
|
||||
|
681
configure.ac
681
configure.ac
File diff suppressed because it is too large
Load Diff
@@ -32,23 +32,16 @@
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
don't write CPP when you can write C: use inline functions
|
||||
and enums instead of macros
|
||||
don't write CPP when you can write C++: use inline
|
||||
functions and constexpr instead of macros
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
the code should be C99 compliant, and must compile with
|
||||
<application>GCC</application>;
|
||||
<application>clang</application> support is highly desirable
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
C++ is ok (for integrating C++ only libraries), but it
|
||||
should be avoided
|
||||
the code should be C++11 compliant, and must compile with
|
||||
<application>GCC</application> 4.6 and
|
||||
<application>clang</application> 3.2
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
@@ -61,7 +54,7 @@
|
||||
foo(const char *abc, int xyz)
|
||||
{
|
||||
if (abc == NULL) {
|
||||
g_warning("Foo happened!\n");
|
||||
LogWarning("Foo happened!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -75,12 +68,28 @@ foo(const char *abc, int xyz)
|
||||
<chapter>
|
||||
<title>Hacking The Source</title>
|
||||
|
||||
<para>
|
||||
MPD sources are managed in a git repository on <ulink
|
||||
url="http://git.musicpd.org/">git.musicpd.org</ulink>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Always write your code against the latest git:
|
||||
</para>
|
||||
|
||||
<programlisting>git clone git://git.musicpd.org/master/mpd.git</programlisting>
|
||||
|
||||
<para>
|
||||
If you already have a clone, update it:
|
||||
</para>
|
||||
|
||||
<programlisting>git pull --rebase git://git.musicpd.org/master/mpd.git master</programlisting>
|
||||
|
||||
<para>
|
||||
You can do without "--rebase", but we recommend that you rebase
|
||||
your repository on the "master" repository all the time.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Configure with the options <option>--enable-debug
|
||||
--enable-werror</option>. Enable as many plugins as possible,
|
||||
@@ -90,8 +99,55 @@ foo(const char *abc, int xyz)
|
||||
<para>
|
||||
Don't mix several changes in one single patch. Create a
|
||||
separate patch for every change. Tools like
|
||||
<application>stgit</application> help you with that.
|
||||
<application>stgit</application> help you with that. This way,
|
||||
we can review your patches more easily, and we can pick the
|
||||
patches we like most first.
|
||||
</para>
|
||||
|
||||
|
||||
<section>
|
||||
<title> Basic stgit usage</title>
|
||||
|
||||
<para>
|
||||
stgit allows you to create a set of patches and refine all of
|
||||
them: you can go back to any patch at any time, and re-edit it
|
||||
(both the code and the commit message). You can reorder
|
||||
patches and insert new patches at any position. It encourages
|
||||
creating separate patches for tiny changes.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
stgit needs to be initialized on a git repository: stg init
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Before you edit the code, create a patch: stg new
|
||||
my-patch-name (stgit now asks you for the commit message).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Now edit the code. Once you're finished, you have to "refresh"
|
||||
the patch, i.e. your edits are incorporated into the patch you
|
||||
have created: stg refresh
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You may now continue editing the same patch, and refresh it as
|
||||
often as you like. Create more patches, edit and refresh them.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To view the list of patches, type stg series. To go back to a
|
||||
specific patch, type stg goto my-patch-name; now you can
|
||||
re-edit it (don't forget stg refresh when you're finished with
|
||||
that patch).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When the whole patch series is finished, convert stgit patches
|
||||
to git commits: stg commit
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
<chapter>
|
||||
@@ -99,7 +155,7 @@ foo(const char *abc, int xyz)
|
||||
|
||||
<para>
|
||||
Send your patches to the mailing list:
|
||||
musicpd-dev-team@lists.sourceforge.net
|
||||
mpd-devel@musicpd.org
|
||||
</para>
|
||||
</chapter>
|
||||
</book>
|
||||
|
@@ -31,7 +31,7 @@ PROJECT_NAME = MPD
|
||||
# This could be handy for archiving the generated documentation or
|
||||
# if some version control system is used.
|
||||
|
||||
PROJECT_NUMBER =
|
||||
PROJECT_NUMBER = @VERSION@
|
||||
|
||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
|
||||
# base path where the generated documentation will be put.
|
||||
@@ -481,7 +481,7 @@ FILE_VERSION_FILTER =
|
||||
# The QUIET tag can be used to turn on/off the messages that are generated
|
||||
# by doxygen. Possible values are YES and NO. If left blank NO is used.
|
||||
|
||||
QUIET = NO
|
||||
QUIET = YES
|
||||
|
||||
# The WARNINGS tag can be used to turn on/off the warning messages that are
|
||||
# generated by doxygen. Possible values are YES and NO. If left blank
|
||||
@@ -534,7 +534,7 @@ WARN_LOGFILE =
|
||||
# directories like "/usr/src/myproject". Separate the files or directories
|
||||
# with spaces.
|
||||
|
||||
INPUT = src/
|
||||
INPUT = @abs_top_srcdir@/src/
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
|
||||
@@ -1158,7 +1158,7 @@ INCLUDE_FILE_PATTERNS =
|
||||
# undefined via #undef or recursively expanded use the := operator
|
||||
# instead of the = operator.
|
||||
|
||||
PREDEFINED = G_GNUC_UNUSED=
|
||||
PREDEFINED =
|
||||
|
||||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
|
||||
# this tag can be used to specify a list of macro names that should be expanded.
|
20
doc/mpd.1
20
doc/mpd.1
@@ -12,29 +12,29 @@ stores info about all available music, and this info can be easily searched and
|
||||
retrieved. Player control, info retrieval, and playlist management can all be
|
||||
managed remotely.
|
||||
|
||||
MPD searches for a config file in \fB~/.mpdconf\fP then \fB/etc/mpd.conf\fP or
|
||||
uses CONF_FILE.
|
||||
MPD searches for a config file in \fB$XDG_CONFIG_HOME/mpd/mpd.conf\fP then
|
||||
\fB~/.mpdconf\fP then \fB/etc/mpd.conf\fP or uses CONF_FILE.
|
||||
|
||||
Read more about MPD at <\fBhttp://www.musicpd.org/\fP>.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BI --help
|
||||
.BI \-\-help
|
||||
Output a brief help message.
|
||||
.TP
|
||||
.BI --kill
|
||||
.BI \-\-kill
|
||||
Kill the currently running mpd session. The pid_file parameter must be
|
||||
specified in the config file for this to work.
|
||||
.TP
|
||||
.BI --no-daemon
|
||||
.BI \-\-no\-daemon
|
||||
Don't detach from console.
|
||||
.TP
|
||||
.BI --stderr
|
||||
.BI \-\-stderr
|
||||
Print messages stderr.
|
||||
.TP
|
||||
.BI --verbose
|
||||
.BI \-\-verbose
|
||||
Verbose logging.
|
||||
.TP
|
||||
.BI --version
|
||||
.BI \-\-version
|
||||
Print version information.
|
||||
.SH FILES
|
||||
.TP
|
||||
@@ -48,8 +48,8 @@ mpd.conf(5), mpc(1)
|
||||
.SH BUGS
|
||||
If you find a bug, please report it at
|
||||
.br
|
||||
<\fBhttp://www.musicpd.org/mantis/bug_report_page.php\fP>.
|
||||
<\fBhttp://bugs.musicpd.org/bug_report_page.php\fP>.
|
||||
.SH AUTHORS
|
||||
Warren Dukes <warren.dukes@gmail.com>
|
||||
Max Kellermann <max@duempel.org>
|
||||
|
||||
Special thanks to all the people that provided feedback and patches.
|
||||
|
217
doc/mpd.conf.5
217
doc/mpd.conf.5
@@ -3,8 +3,9 @@
|
||||
mpd.conf \- Music Player Daemon configuration file
|
||||
.SH DESCRIPTION
|
||||
\fBmpd.conf\fP is the configuration file for mpd(1). If not specified on the
|
||||
command line, MPD first searches for it at \fB~/.mpdconf\fP then at
|
||||
\fB~/.mpd/mpd.conf\fP and then in \fB/etc/mpd.conf\fP.
|
||||
command line, MPD first searches for it at \fB$XDG_CONFIG_HOME/mpd/mpd.conf\fP
|
||||
then at \fB~/.mpdconf\fP then at \fB~/.mpd/mpd.conf\fP and then in
|
||||
\fB/etc/mpd.conf\fP.
|
||||
|
||||
Lines beginning with a "#" character are comments. All other non-empty lines
|
||||
specify parameters and their values. These lines contain the parameter name
|
||||
@@ -27,31 +28,24 @@ paths.
|
||||
|
||||
See \fBdocs/mpdconf.example\fP in the source tarball for an example
|
||||
configuration file.
|
||||
|
||||
This manual is not complete, it lists only the most important options.
|
||||
Please read the MPD user manual for a complete configuration guide:
|
||||
<\fBhttp://www.musicpd.org/doc/user/\fP>
|
||||
.SH REQUIRED PARAMETERS
|
||||
.TP
|
||||
.B follow_outside_symlinks <yes or no>
|
||||
Control if MPD will follow symbolic links pointing outside the music dir.
|
||||
You must recreate the database after changing this option.
|
||||
The default is "yes".
|
||||
.TP
|
||||
.B follow_inside_symlinks <yes or no>
|
||||
Control if MPD will follow symbolic links pointing inside the music dir,
|
||||
potentially adding duplicates to the database.
|
||||
You must recreate the database after changing this option.
|
||||
The default is "yes".
|
||||
.TP
|
||||
.B db_file <file>
|
||||
This specifies where the db file will be stored.
|
||||
.TP
|
||||
.B sticker_file <file>
|
||||
The location of the sticker database. This is a database which
|
||||
manages dynamic information attached to songs.
|
||||
.TP
|
||||
.B log_file <file>
|
||||
This specifies where the log file should be located.
|
||||
The special value "syslog" makes MPD use the local syslog daemon.
|
||||
.SH OPTIONAL PARAMETERS
|
||||
.TP
|
||||
.B sticker_file <file>
|
||||
The location of the sticker database. This is a database which
|
||||
manages dynamic information attached to songs.
|
||||
.TP
|
||||
.B pid_file <file>
|
||||
This specifies the file to save mpd's process ID in.
|
||||
.TP
|
||||
@@ -69,6 +63,9 @@ mpd will be saved to this file when mpd is terminated by a TERM signal or by
|
||||
the "kill" command. When mpd is restarted, it will read the state file and
|
||||
restore the state of mpd (including the playlist).
|
||||
.TP
|
||||
.B restore_paused <yes or no>
|
||||
Put MPD into pause mode instead of starting playback after startup.
|
||||
.TP
|
||||
.B user <username>
|
||||
This specifies the user that MPD will run as, if set. MPD should
|
||||
never run as root, and you may use this option to make MPD change its
|
||||
@@ -80,8 +77,13 @@ This specifies which address mpd binds to and listens on. Multiple
|
||||
bind_to_address parameters may be specified. The default is "any", which binds
|
||||
to all available addresses.
|
||||
|
||||
To bind to a Unix domain socket, specify an absolute path. For a
|
||||
system-wide MPD, we suggest the path "\fB/var/run/mpd/socket\fP".
|
||||
You can set a port that is different from the global port setting,
|
||||
e.g. "localhost:6602". IPv6 addresses must be enclosed in square
|
||||
brackets if you want to configure a port, e.g. "[::1]:6602".
|
||||
|
||||
To bind to a Unix domain socket, specify an absolute path or a path starting
|
||||
with a tilde (~). For a system-wide MPD, we suggest the path
|
||||
"\fB/var/run/mpd/socket\fP".
|
||||
.TP
|
||||
.B port <port>
|
||||
This specifies the port that mpd listens on. The default is 6600.
|
||||
@@ -92,6 +94,17 @@ reports from what address a connection is opened, and when it is closed, and
|
||||
"verbose" records excessive amounts of information for debugging purposes. The
|
||||
default is "default".
|
||||
.TP
|
||||
.B follow_outside_symlinks <yes or no>
|
||||
Control if MPD will follow symbolic links pointing outside the music dir.
|
||||
You must recreate the database after changing this option.
|
||||
The default is "yes".
|
||||
.TP
|
||||
.B follow_inside_symlinks <yes or no>
|
||||
Control if MPD will follow symbolic links pointing inside the music dir,
|
||||
potentially adding duplicates to the database.
|
||||
You must recreate the database after changing this option.
|
||||
The default is "yes".
|
||||
.TP
|
||||
.B zeroconf_enabled <yes or no>
|
||||
If yes, and MPD has been compiled with support for Avahi or Bonjour, service
|
||||
information will be published with Zeroconf. The default is yes.
|
||||
@@ -168,7 +181,7 @@ only choice) if MPD was compiled without libsamplerate.
|
||||
.RE
|
||||
.IP
|
||||
For an up-to-date list of available converters, please see the libsamplerate
|
||||
documentation (available online at <\fBhttp://www.mega-nerd.com/SRC/\fP>).
|
||||
documentation (available online at <\fBhttp://www.mega\-nerd.com/SRC/\fP>).
|
||||
.TP
|
||||
.B replaygain <off or album or track or auto>
|
||||
If specified, mpd will adjust the volume of songs played using ReplayGain tags
|
||||
@@ -179,14 +192,14 @@ tags if random play is activated otherwise the album ReplayGain tags. Currently
|
||||
only FLAC, Ogg Vorbis, Musepack, and MP3 (through ID3v2 ReplayGain tags, not
|
||||
APEv2) are supported.
|
||||
.TP
|
||||
.B replaygain_preamp <-15 to 15>
|
||||
.B replaygain_preamp <\-15 to 15>
|
||||
This is the gain (in dB) applied to songs with ReplayGain tags.
|
||||
.TP
|
||||
.B volume_normalization <yes or no>
|
||||
If yes, mpd will normalize the volume of songs as they play. The default is no.
|
||||
.TP
|
||||
.B audio_buffer_size <size in KiB>
|
||||
This specifies the size of the audio buffer in kibibytes. The default is 2048,
|
||||
This specifies the size of the audio buffer in kibibytes. The default is 4096,
|
||||
large enough for nearly 12 seconds of CD-quality audio.
|
||||
.TP
|
||||
.B buffer_before_play <0-100%>
|
||||
@@ -209,7 +222,7 @@ default is 5.
|
||||
.TP
|
||||
.B max_playlist_length <number>
|
||||
This specifies the maximum number of songs that can be in the playlist. The
|
||||
default is 4096.
|
||||
default is 16384.
|
||||
.TP
|
||||
.B max_command_list_size <size in KiB>
|
||||
This specifies the maximum size a command list can be. The default is 2048.
|
||||
@@ -220,12 +233,12 @@ is 8192.
|
||||
.TP
|
||||
.B filesystem_charset <charset>
|
||||
This specifies the character set used for the filesystem. A list of supported
|
||||
character sets can be obtained by running "iconv -l". The default is
|
||||
character sets can be obtained by running "iconv \-l". The default is
|
||||
determined from the locale when the db was originally created.
|
||||
.TP
|
||||
.B id3v1_encoding <charset>
|
||||
This specifies the character set which ID3v1 tags are encoded in. A list of
|
||||
supported character sets can be obtained by running "iconv -l". The default is
|
||||
supported character sets can be obtained by running "iconv \-l". The default is
|
||||
to let libid3tag convert them (from ISO-8859-1, as the standard specifies) and
|
||||
do no additional conversion.
|
||||
.TP
|
||||
@@ -245,25 +258,37 @@ when saving playlists. The default is "no".
|
||||
This specifies the tag types that will be scanned for and made available to
|
||||
clients. Note that you must recreate (not update) your database for changes to
|
||||
this parameter to take effect. Possible values are artist, album, title,
|
||||
track, name, genre, date, composer, performer, comment, and disc. Multiple
|
||||
tags may be specified as a comma separated list. An example value is
|
||||
"artist,album,title,track". The special value "none" may be used alone to
|
||||
disable all metadata. The default is to use all known tag types except for
|
||||
comments.
|
||||
track, name, genre, date, composer, performer, comment, disc,
|
||||
musicbrainz_artistid, musicbrainz_albumid, musicbrainz_albumartistid,
|
||||
musicbrainz_trackid. Multiple tags may be specified as a comma separated list.
|
||||
An example value is "artist,album,title,track". The special value "none" may
|
||||
be used alone to disable all metadata. The default is to use all known tag
|
||||
types except for comments and those starting with "musicbrainz".
|
||||
.TP
|
||||
.B auto_update <yes or no>
|
||||
This specifies the wheter to support automatic update of music database when
|
||||
This specifies the whether to support automatic update of music database when
|
||||
files are changed in music_directory. The default is to disable autoupdate
|
||||
of database.
|
||||
.TP
|
||||
.B auto_update_depth <N>
|
||||
Limit the depth of the directories being watched, 0 means only watch
|
||||
the music directory itself. There is no limit by default.
|
||||
.TP
|
||||
.B despotify_user <name>
|
||||
This specifies the user to use when logging in to Spotify using the despotify plugins.
|
||||
.TP
|
||||
.B despotify_password <name>
|
||||
This specifies the password to use when logging in to Spotify using the despotify plugins.
|
||||
.TP
|
||||
.B despotify_high_bitrate <yes or no>
|
||||
This specifies if the requested bitrate for Spotify should be high or not. Higher sounds
|
||||
better but requires more processing and higher bandwidth. Default is yes.
|
||||
.TP
|
||||
.SH REQUIRED AUDIO OUTPUT PARAMETERS
|
||||
.TP
|
||||
.B type <type>
|
||||
This specifies the audio output type. See the list of supported outputs in mpd
|
||||
--version for possible values.
|
||||
\-\-version for possible values.
|
||||
.TP
|
||||
.B name <name>
|
||||
This specifies a unique name for the audio output.
|
||||
@@ -338,134 +363,6 @@ errors on bandwidth-limited devices. Some users have reported good results
|
||||
with this set to 50000, but not all devices support values this high. Most
|
||||
users do not need to change this. The default is 256000000 / sample_rate(kHz),
|
||||
or 5804 microseconds for CD-quality audio.
|
||||
.SH OPTIONAL OSS OUTPUT PARAMETERS
|
||||
.TP
|
||||
.B device <dev>
|
||||
This specifies the device to use for audio output. The default is "/dev/dsp".
|
||||
.TP
|
||||
.B mixer_device <mixer dev>
|
||||
This specifies which mixer to use. The default is "/dev/mixer".
|
||||
.TP
|
||||
.B mixer_control <mixer ctrl>
|
||||
This specifies which mixer control to use (sometimes referred to as the
|
||||
"device"). The default is to use the main PCM mixer. An example is "Pcm".
|
||||
.SH OPTIONAL PULSE OUTPUT PARAMETERS
|
||||
.TP
|
||||
.B server <server list>
|
||||
A space separated list of servers to try to connect to. See
|
||||
<\fBhttp://www.pulseaudio.org/wiki/ServerStrings\fP> for more details. The
|
||||
default is to let PulseAudio choose a server.
|
||||
If you specify more than one server name, MPD tries to connect to one
|
||||
after another until it successfully establishes a connection.
|
||||
.TP
|
||||
.B sink <sink>
|
||||
The sink to output to. The default is to let PulseAudio choose a sink.
|
||||
.SH OPTIONAL JACK OUTPUT PARAMETERS
|
||||
.TP
|
||||
.B client_name <name>
|
||||
The client name to use when connecting to JACK. The output ports <name>:left
|
||||
and <name>:right will also be created for the left and right channels,
|
||||
respectively.
|
||||
.TP
|
||||
.B ports <left_port,right_port>
|
||||
This specifies the left and right ports to connect to for the left and right
|
||||
channels, respectively. The default is to let JACK choose a pair of ports.
|
||||
.TP
|
||||
.B ringbuffer_size <size in bytes>
|
||||
This specifies the size of the ringbuffer in bytes. The default is 32768.
|
||||
.SH OPTIONAL AO OUTPUT PARAMETERS
|
||||
.TP
|
||||
.B driver <driver>
|
||||
This specifies the libao driver to use for audio output. Possible values
|
||||
depend on what libao drivers are available. See
|
||||
<\fBhttp://www.xiph.org/ao/doc/drivers.html\fP> for information on some
|
||||
commonly used drivers. Typical values for Linux include "oss" and "alsa09".
|
||||
The default is "default", which causes libao to select an appropriate plugin.
|
||||
.TP
|
||||
.B options <opts>
|
||||
This specifies the options to use for the selected libao driver. For oss, the
|
||||
only option available is "dsp". For alsa09, the available options are: "dev",
|
||||
"buf_size", and "periods". See <\fBhttp://www.xiph.org/ao/doc/drivers.html\fP>
|
||||
for available options for some commonly used drivers. Options are assigned
|
||||
using "=", and ";" is used to separate options. An example for oss:
|
||||
"dsp=/dev/dsp". An example for alsa09: "dev=hw:0,0;buf_size=4096". The
|
||||
default is "".
|
||||
.TP
|
||||
.B write_size <size in bytes>
|
||||
This specifies how many bytes to write to the audio device at once. This
|
||||
parameter is to work around a bug in older versions of libao on sound cards
|
||||
with very small buffers. The default is 1024.
|
||||
.SH REQUIRED FIFO OUTPUT PARAMETERS
|
||||
.TP
|
||||
.B path <path>
|
||||
This specifies the path of the FIFO to output to. Must be an absolute path.
|
||||
If the path does not exist it will be created when mpd is started, and removed
|
||||
when mpd is stopped. The FIFO will be created with the same user and group as
|
||||
mpd is running as. Default permissions can be modified by using the builtin
|
||||
shell command "umask". If a FIFO already exists at the specified path it will
|
||||
be reused, and will \fBnot\fP be removed when mpd is stopped. You can use the
|
||||
"mkfifo" command to create this, and then you may modify the permissions to
|
||||
your liking.
|
||||
.SH REQUIRED SHOUT OUTPUT PARAMETERS
|
||||
.TP
|
||||
.B name <name>
|
||||
This specifies not only the unique audio output name, but also the stream
|
||||
title.
|
||||
.TP
|
||||
.B host <hostname>
|
||||
This specifies the hostname of the icecast server to connect to.
|
||||
.TP
|
||||
.B port <port>
|
||||
This specifies the port of the icecast server to connect to.
|
||||
.TP
|
||||
.B mount <mountpoint>
|
||||
This specifies the icecast mountpoint to use.
|
||||
.TP
|
||||
.B password <password>
|
||||
This specifies the password to use when logging in to the icecast server.
|
||||
.TP
|
||||
.B quality <quality>
|
||||
This specifies the encoding quality to use. The value must be between 0
|
||||
and 10. Fractional values, such as 2.5, are permitted. Either the quality or
|
||||
the bitrate parameter must be specified, but not both. For Ogg, a
|
||||
higher quality number produces higher quality output. For MP3, it's
|
||||
just the opposite, with lower numbers producing higher quality output.
|
||||
.TP
|
||||
.B bitrate <kbps>
|
||||
This specifies the bitrate to use for encoding. Either the quality or the
|
||||
bitrate parameter must be specified, but not both.
|
||||
.TP
|
||||
.B format <sample_rate:bits:channels>
|
||||
This specifies the sample rate, bits per sample, and number of channels to use
|
||||
for encoding.
|
||||
.SH OPTIONAL SHOUT OUTPUT PARAMETERS
|
||||
.TP
|
||||
.B encoding <encoding>
|
||||
This specifies which output encoding to use. Should be either "ogg"
|
||||
or "mp3", "mp3" is needed for shoutcast streaming. The default is "ogg".
|
||||
.TP
|
||||
.B protocol <protocol>
|
||||
This specifies the protocol that wil be used to connect to the
|
||||
icecast/shoutcast server. The options are "shoutcast", "icecast1" and
|
||||
"icecast2". The default is "icecast2".
|
||||
.TP
|
||||
.B user <username>
|
||||
This specifies the username to use when logging in to the icecast server. The
|
||||
default is "source".
|
||||
.TP
|
||||
.B public <yes or no>
|
||||
This specifies whether to request that the stream be listed in all public
|
||||
stream directories that the icecast server knows about. The default is no.
|
||||
.TP
|
||||
.B timeout <seconds>
|
||||
This specifies the number of seconds to wait before giving up on trying to
|
||||
connect to the icecast server. The default is 2 seconds.
|
||||
.TP
|
||||
.B description <description>
|
||||
This specifies a description of the stream.
|
||||
.TP
|
||||
.B genre <genre>
|
||||
This specifies the genre(s) of the stream.
|
||||
.SH FILES
|
||||
.TP
|
||||
.BI ~/.mpdconf
|
||||
|
@@ -1,5 +1,5 @@
|
||||
# An example configuration file for MPD
|
||||
# See the mpd.conf man page for a more detailed description of each parameter.
|
||||
# An example configuration file for MPD.
|
||||
# Read the user manual for documentation: http://www.musicpd.org/doc/user/
|
||||
|
||||
|
||||
# Files and directories #######################################################
|
||||
@@ -103,15 +103,19 @@
|
||||
#
|
||||
#gapless_mp3_playback "yes"
|
||||
#
|
||||
# Setting "restore_paused" to "yes" puts MPD into pause mode instead
|
||||
# of starting playback after startup.
|
||||
#
|
||||
#restore_paused "no"
|
||||
#
|
||||
# This setting enables MPD to create playlists in a format usable by other
|
||||
# music players.
|
||||
#
|
||||
#save_absolute_paths_in_playlists "no"
|
||||
#
|
||||
# This setting defines a list of tag types that will be extracted during the
|
||||
# audio file discovery process. Optionally, 'comment' can be added to this
|
||||
# list.
|
||||
#
|
||||
# This setting defines a list of tag types that will be extracted during the
|
||||
# audio file discovery process. The complete list of possible values can be
|
||||
# found in the mpd.conf man page.
|
||||
#metadata_to_use "artist,album,title,track,name,genre,date,composer,performer,disc"
|
||||
#
|
||||
# This setting enables automatic update of MPD's database when files in
|
||||
@@ -171,6 +175,15 @@
|
||||
###############################################################################
|
||||
|
||||
|
||||
# Database #######################################################################
|
||||
#
|
||||
|
||||
#database {
|
||||
# plugin "proxy"
|
||||
# host "other.mpd.host"
|
||||
# port "6600"
|
||||
#}
|
||||
|
||||
# Input #######################################################################
|
||||
#
|
||||
|
||||
@@ -191,16 +204,12 @@ input {
|
||||
# blocks. Setting this block is optional, though the server will only attempt
|
||||
# autodetection for one sound card.
|
||||
#
|
||||
# See <http://mpd.wikia.com/wiki/Configuration#Audio_Outputs> for examples of
|
||||
# other audio outputs.
|
||||
#
|
||||
# An example of an ALSA output:
|
||||
#
|
||||
#audio_output {
|
||||
# type "alsa"
|
||||
# name "My ALSA Device"
|
||||
## device "hw:0,0" # optional
|
||||
## format "44100:16:2" # optional
|
||||
## mixer_type "hardware" # optional
|
||||
## mixer_device "default" # optional
|
||||
## mixer_control "PCM" # optional
|
||||
@@ -213,7 +222,6 @@ input {
|
||||
# type "oss"
|
||||
# name "My OSS Device"
|
||||
## device "/dev/dsp" # optional
|
||||
## format "44100:16:2" # optional
|
||||
## mixer_type "hardware" # optional
|
||||
## mixer_device "/dev/mixer" # optional
|
||||
## mixer_control "PCM" # optional
|
||||
@@ -235,6 +243,7 @@ input {
|
||||
## protocol "icecast2" # optional
|
||||
## user "source" # optional
|
||||
## description "My Stream Description" # optional
|
||||
## url "http://example.com" # optional
|
||||
## genre "jazz" # optional
|
||||
## public "no" # optional
|
||||
## timeout "2" # optional
|
||||
@@ -276,6 +285,25 @@ input {
|
||||
## sink "remote_server_sink" # optional
|
||||
#}
|
||||
#
|
||||
# An example of a winmm output (Windows multimedia API).
|
||||
#
|
||||
#audio_output {
|
||||
# type "winmm"
|
||||
# name "My WinMM output"
|
||||
## device "Digital Audio (S/PDIF) (High Definition Audio Device)" # optional
|
||||
# or
|
||||
## device "0" # optional
|
||||
## mixer_type "hardware" # optional
|
||||
#}
|
||||
#
|
||||
# An example of an openal output.
|
||||
#
|
||||
#audio_output {
|
||||
# type "openal"
|
||||
# name "My OpenAL output"
|
||||
## device "Digital Audio (S/PDIF) (High Definition Audio Device)" # optional
|
||||
#}
|
||||
#
|
||||
## Example "pipe" output:
|
||||
#
|
||||
#audio_output {
|
||||
@@ -297,12 +325,6 @@ input {
|
||||
# mixer_type "none" # optional
|
||||
#}
|
||||
#
|
||||
# This setting will change all decoded audio to be converted to the specified
|
||||
# format before being passed to the audio outputs. By default, this setting is
|
||||
# disabled.
|
||||
#
|
||||
#audio_output_format "44100:16:2"
|
||||
#
|
||||
# If MPD has been compiled with libsamplerate support, this setting specifies
|
||||
# the sample rate converter to use. Possible values can be found in the
|
||||
# mpd.conf man page or the libsamplerate documentation. By default, this is
|
||||
@@ -316,8 +338,11 @@ input {
|
||||
# Normalization automatic volume adjustments ##################################
|
||||
#
|
||||
# This setting specifies the type of ReplayGain to use. This setting can have
|
||||
# the argument "off", "album" or "track". See <http://www.replaygain.org>
|
||||
# for more details. This setting is off by default.
|
||||
# the argument "off", "album", "track" or "auto". "auto" is a special mode that
|
||||
# chooses between "track" and "album" depending on the current state of
|
||||
# random playback. If random playback is enabled then "track" mode is used.
|
||||
# See <http://www.replaygain.org> for more details about ReplayGain.
|
||||
# This setting is off by default.
|
||||
#
|
||||
#replaygain "album"
|
||||
#
|
||||
@@ -326,6 +351,20 @@ input {
|
||||
#
|
||||
#replaygain_preamp "0"
|
||||
#
|
||||
# This setting sets the pre-amp used for files that do NOT have ReplayGain tags.
|
||||
# By default this setting is disabled.
|
||||
#
|
||||
#replaygain_missing_preamp "0"
|
||||
#
|
||||
# This setting enables or disables ReplayGain limiting.
|
||||
# MPD calculates actual amplification based on the ReplayGain tags
|
||||
# and replaygain_preamp / replaygain_missing_preamp setting.
|
||||
# If replaygain_limit is enabled MPD will never amplify audio signal
|
||||
# above its original level. If replaygain_limit is disabled such amplification
|
||||
# might occur. By default this setting is enabled.
|
||||
#
|
||||
#replaygain_limit "yes"
|
||||
#
|
||||
# This setting enables on-the-fly normalization volume adjustment. This will
|
||||
# result in the volume of all playing audio to be adjusted so the output has
|
||||
# equal "loudness". This setting is disabled by default.
|
||||
@@ -341,7 +380,7 @@ input {
|
||||
# this may have undesired effects. Don't change this if you don't know what you
|
||||
# are doing.
|
||||
#
|
||||
#audio_buffer_size "2048"
|
||||
#audio_buffer_size "4096"
|
||||
#
|
||||
# This setting controls the percentage of the buffer which is filled before
|
||||
# beginning to play. Increasing this reduces the chance of audio file skipping,
|
||||
@@ -366,7 +405,6 @@ input {
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
|
||||
# Character Encoding ##########################################################
|
||||
#
|
||||
# If file or directory names do not display correctly for your locale then you
|
||||
|
396
doc/protocol.xml
396
doc/protocol.xml
@@ -59,7 +59,7 @@
|
||||
<para>
|
||||
All data between the client and the server is encoded in
|
||||
UTF-8. (Note: In UTF-8 all standard ansi characters, 0-127 are
|
||||
the same as a standard ansi encoding. Also, no ansi character
|
||||
the same as in standard ansi encoding. Also, no ansi character
|
||||
appears in any multi-byte characters. So, you can use
|
||||
standard C functions like <function>strlen</function>, and
|
||||
<function>strcpy</function> just fine with UTF-8 encoded
|
||||
@@ -203,6 +203,47 @@
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
<chapter>
|
||||
<title>Recipes</title>
|
||||
|
||||
<section>
|
||||
<title>Queuing</title>
|
||||
|
||||
<para>
|
||||
Often, users run MPD with "<link
|
||||
linkend="command_random">random</link>" enabled, but want to
|
||||
be able to insert songs "before" the rest of the playlist.
|
||||
That is commonly called "queuing".
|
||||
</para>
|
||||
|
||||
<para>
|
||||
MPD implements this by allowing the client to specify a
|
||||
"priority" for each song in the playlist (commands <link
|
||||
linkend="command_prio"><command>prio</command></link> and
|
||||
<link
|
||||
linkend="command_prioid"><command>prioid</command></link>). A
|
||||
higher priority means that the song is going to be played
|
||||
before the other songs.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In "random" mode, MPD maintains an internal randomized
|
||||
sequence of songs. In this sequence, songs with a higher
|
||||
priority come first, and all songs with the same priority are
|
||||
shuffled (by default, all songs are shuffled, because all have
|
||||
the same priority "0"). When you increase the priority of a
|
||||
song, it is moved to the front of the sequence according to
|
||||
its new priority, but always after the current one. A song
|
||||
that has been played already (it's "before" the current song
|
||||
in that sequence) will only be scheduled for repeated playback
|
||||
if its priority has become bigger than the priority of the
|
||||
current song. Decreasing the priority of a song will moved it
|
||||
farther to the end of the sequence. Changing the priority of
|
||||
the current song has no effect on the sequence.
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
<chapter>
|
||||
<title>Command reference</title>
|
||||
|
||||
@@ -318,6 +359,25 @@
|
||||
<option>crossfade</option>, replay gain
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<returnvalue>sticker</returnvalue>: the sticker database
|
||||
has been modified.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<returnvalue>subscription</returnvalue>: a client
|
||||
has subscribed or unsubscribed to a channel
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<returnvalue>message</returnvalue>: a message was
|
||||
received on a channel this client is subscribed to;
|
||||
this event is only emitted when the queue is empty
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
While a client is waiting for <command>idle</command>
|
||||
@@ -484,7 +544,7 @@
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>updatings_db</varname>:
|
||||
<varname>updating_db</varname>:
|
||||
<returnvalue>job id</returnvalue>
|
||||
</para>
|
||||
</listitem>
|
||||
@@ -677,7 +737,11 @@
|
||||
Sets the replay gain mode. One of
|
||||
<parameter>off</parameter>,
|
||||
<parameter>track</parameter>,
|
||||
<parameter>album</parameter>.
|
||||
<parameter>album</parameter>,
|
||||
<parameter>auto</parameter><footnote
|
||||
id="replay_gain_auto_since_0_16">
|
||||
<simpara>added in MPD 0.16</simpara>
|
||||
</footnote>.
|
||||
</para>
|
||||
<para>
|
||||
Changing the mode during playback may take several
|
||||
@@ -704,6 +768,25 @@
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry id="command_volume">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>volume</command>
|
||||
<arg choice="req"><replaceable>CHANGE</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Changes volume by amount <varname>CHANGE</varname>.
|
||||
</para>
|
||||
<note>
|
||||
<para>
|
||||
<command>volume</command> is deprecated, use
|
||||
<command>setvol</command> instead.
|
||||
</para>
|
||||
</note>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</section>
|
||||
|
||||
@@ -814,6 +897,23 @@
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_seekcur">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>seekcur</command>
|
||||
<arg choice="req"><replaceable>TIME</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Seeks to the position <varname>TIME</varname> within the
|
||||
current song. If prefixed by '+' or '-', then the time
|
||||
is relative to the current playing position.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_stop">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
@@ -1073,6 +1173,46 @@ OK
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_prio">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>prio</command>
|
||||
<arg choice="req"><replaceable>PRIORITY</replaceable></arg>
|
||||
<arg choice="req" rep="repeat"><replaceable>START:END</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Set the priority of the specified songs. A higher
|
||||
priority means that it will be played first when
|
||||
"random" mode is enabled.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
A priority is an integer between 0 and 255. The default
|
||||
priority of new songs is 0.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_prioid">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>prioid</command>
|
||||
<arg choice="req"><replaceable>PRIORITY</replaceable></arg>
|
||||
<arg choice="req" rep="repeat"><replaceable>ID</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Same as <link
|
||||
linkend="command_prio"><command>prio</command></link>,
|
||||
but address the songs with their id.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_shuffle">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
@@ -1191,12 +1331,14 @@ OK
|
||||
<cmdsynopsis>
|
||||
<command>load</command>
|
||||
<arg choice="req"><replaceable>NAME</replaceable></arg>
|
||||
<arg choice="opt"><replaceable>START:END</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Loads the playlist into the current queue. Playlist
|
||||
plugins are supported.
|
||||
plugins are supported. A range may be specified to load
|
||||
only a part of the playlist.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@@ -1341,9 +1483,13 @@ OK
|
||||
<para>
|
||||
Finds songs in the db that are exactly
|
||||
<varname>WHAT</varname>. <varname>TYPE</varname> can
|
||||
be any tag supported by MPD, or one of the two special
|
||||
be any tag supported by MPD, or one of the three special
|
||||
parameters — <parameter>file</parameter> to search by
|
||||
full path (relative to database root), and
|
||||
|
||||
full path (relative to the music directory),
|
||||
<parameter>in</parameter> to restrict the search to
|
||||
songs in the given directory (also relative to the music
|
||||
directory) and
|
||||
<parameter>any</parameter> to match against all
|
||||
available tags. <varname>WHAT</varname> is what to find.
|
||||
</para>
|
||||
@@ -1433,6 +1579,37 @@ OK
|
||||
the list of stored playlists. This behavior is
|
||||
deprecated; use "listplaylists" instead.
|
||||
</para>
|
||||
<para>
|
||||
Clients that are connected via UNIX domain socket may
|
||||
use this command to read the tags of an arbitrary local
|
||||
file (URI beginning with "file:///").
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry id="command_readcomments">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>readcomments</command>
|
||||
<arg><replaceable>URI</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Read "comments" (i.e. key-value pairs) from the file
|
||||
specified by "URI". This "URI" can be a path relative
|
||||
to the music directory or a URL in the form
|
||||
"file:///foo/bar.ogg".
|
||||
</para>
|
||||
<para>
|
||||
The response consists of lines in the form "KEY: VALUE".
|
||||
Comments with suspicious characters (e.g. newlines) are
|
||||
ignored silently.
|
||||
</para>
|
||||
<para>
|
||||
The meaning of these depends on the codec, and not all
|
||||
decoder plugins support it. For example, on Ogg files,
|
||||
this lists the Vorbis comments.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry id="command_search">
|
||||
@@ -1453,6 +1630,51 @@ OK
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry id="command_searchadd">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>searchadd</command>
|
||||
<arg choice="req"><replaceable>TYPE</replaceable></arg>
|
||||
<arg choice="req"><replaceable>WHAT</replaceable></arg>
|
||||
<arg choice="opt"><replaceable>...</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Searches for any song that contains <varname>WHAT</varname>
|
||||
in tag <varname>TYPE</varname> and adds them to current playlist.
|
||||
</para>
|
||||
<para>
|
||||
Parameters have the same meaning as for <command>find</command>,
|
||||
except that search is not case sensitive.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry id="command_searchaddpl">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>searchaddpl</command>
|
||||
<arg choice="req"><replaceable>NAME</replaceable></arg>
|
||||
<arg choice="req"><replaceable>TYPE</replaceable></arg>
|
||||
<arg choice="req"><replaceable>WHAT</replaceable></arg>
|
||||
<arg choice="opt"><replaceable>...</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Searches for any song that contains <varname>WHAT</varname>
|
||||
in tag <varname>TYPE</varname> and adds them to the playlist
|
||||
named <varname>NAME</varname>.
|
||||
</para>
|
||||
<para>
|
||||
If a playlist by that name doesn't exist it is created.
|
||||
</para>
|
||||
<para>
|
||||
Parameters have the same meaning as for <command>find</command>,
|
||||
except that search is not case sensitive.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry id="command_update">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
@@ -1627,7 +1849,10 @@ OK
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Closes the connection to MPD.
|
||||
Closes the connection to MPD. MPD will try to send the
|
||||
remaining output buffer before it actually closes the
|
||||
connection, but that cannot be guaranteed. This command
|
||||
will not generate a response.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@@ -1681,6 +1906,7 @@ OK
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>disableoutput</command>
|
||||
<arg choice="req"><replaceable>ID</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
@@ -1693,6 +1919,7 @@ OK
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>enableoutput</command>
|
||||
<arg choice="req"><replaceable>ID</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
@@ -1701,6 +1928,20 @@ OK
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry id="command_toggleoutput">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>toggleoutput</command>
|
||||
<arg choice="req"><replaceable>ID</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Turns an output on or off, depending on the current
|
||||
state.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry id="command_outputs">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
@@ -1720,6 +1961,47 @@ OK
|
||||
<title>Reflection</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry id="command_config">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>config</command>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Dumps configuration values that may be interesting for
|
||||
the client. This command is only permitted to "local"
|
||||
clients (connected via UNIX domain socket).
|
||||
</para>
|
||||
<para>
|
||||
The following response attributes are available:
|
||||
</para>
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>
|
||||
Name
|
||||
</entry>
|
||||
<entry>
|
||||
Description
|
||||
</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>music_directory</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
The absolute path of the music directory.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry id="command_commands">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
@@ -1790,5 +2072,105 @@ suffix: mpc</programlisting>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Client to client</title>
|
||||
|
||||
<para>
|
||||
Clients can communicate with each others over "channels". A
|
||||
channel is created by a client subscribing to it. More than
|
||||
one client can be subscribed to a channel at a time; all of
|
||||
them will receive the messages which get sent to it.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Each time a client subscribes or unsubscribes, the global idle
|
||||
event <varname>subscription</varname> is generated. In
|
||||
conjunction with the <command>channels</command> command, this
|
||||
may be used to auto-detect clients providing additional
|
||||
services.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
New messages are indicated by the <varname>message</varname>
|
||||
idle event.
|
||||
</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry id="command_subscribe">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>subscribe</command>
|
||||
<arg choice="req"><replaceable>NAME</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Subscribe to a channel. The channel is created if it
|
||||
does not exist already. The name may consist of
|
||||
alphanumeric ASCII characters plus underscore, dash, dot
|
||||
and colon.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_unsubscribe">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>unsubscribe</command>
|
||||
<arg choice="req"><replaceable>NAME</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Unsubscribe from a channel.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_channels">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>channels</command>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Obtain a list of all channels. The response is a list
|
||||
of "channel:" lines.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_readmessages">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>readmessages</command>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Reads messages for this client. The response is a list
|
||||
of "channel:" and "message:" lines.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_sendmessage">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>sendmessage</command>
|
||||
<arg choice="req"><replaceable>CHANNEL</replaceable></arg>
|
||||
<arg choice="req"><replaceable>TEXT</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Send a message to the specified channel.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</section>
|
||||
</chapter>
|
||||
</book>
|
||||
|
870
doc/user.xml
870
doc/user.xml
File diff suppressed because it is too large
Load Diff
63
m4/ax_append_compile_flags.m4
Normal file
63
m4/ax_append_compile_flags.m4
Normal file
@@ -0,0 +1,63 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_append_compile_flags.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_APPEND_COMPILE_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# For every FLAG1, FLAG2 it is checked whether the compiler works with the
|
||||
# flag. If it does, the flag is added FLAGS-VARIABLE
|
||||
#
|
||||
# If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
|
||||
# CFLAGS) is used. During the check the flag is always added to the
|
||||
# current language's flags.
|
||||
#
|
||||
# If EXTRA-FLAGS is defined, it is added to the current language's default
|
||||
# flags (e.g. CFLAGS) when the check is done. The check is thus made with
|
||||
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
|
||||
# force the compiler to issue an error when a bad flag is given.
|
||||
#
|
||||
# NOTE: This macro depends on the AX_APPEND_FLAG and
|
||||
# AX_CHECK_COMPILE_FLAG. Please keep this macro in sync with
|
||||
# AX_APPEND_LINK_FLAGS.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 2
|
||||
|
||||
AC_DEFUN([AX_APPEND_COMPILE_FLAGS],
|
||||
[for flag in $1; do
|
||||
AX_CHECK_COMPILE_FLAG([$flag], [AX_APPEND_FLAG([$flag], [$2])], [], [$3])
|
||||
done
|
||||
])dnl AX_APPEND_COMPILE_FLAGS
|
69
m4/ax_append_flag.m4
Normal file
69
m4/ax_append_flag.m4
Normal file
@@ -0,0 +1,69 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_append_flag.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# FLAG is appended to the FLAGS-VARIABLE shell variable, with a space
|
||||
# added in between.
|
||||
#
|
||||
# If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
|
||||
# CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains
|
||||
# FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly
|
||||
# FLAG.
|
||||
#
|
||||
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
|
||||
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 2
|
||||
|
||||
AC_DEFUN([AX_APPEND_FLAG],
|
||||
[AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX
|
||||
AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])])dnl
|
||||
AS_VAR_SET_IF(FLAGS,
|
||||
[case " AS_VAR_GET(FLAGS) " in
|
||||
*" $1 "*)
|
||||
AC_RUN_LOG([: FLAGS already contains $1])
|
||||
;;
|
||||
*)
|
||||
AC_RUN_LOG([: FLAGS="$FLAGS $1"])
|
||||
AS_VAR_SET(FLAGS, ["AS_VAR_GET(FLAGS) $1"])
|
||||
;;
|
||||
esac],
|
||||
[AS_VAR_SET(FLAGS,["$1"])])
|
||||
AS_VAR_POPDEF([FLAGS])dnl
|
||||
])dnl AX_APPEND_FLAG
|
61
m4/ax_append_link_flags.m4
Normal file
61
m4/ax_append_link_flags.m4
Normal file
@@ -0,0 +1,61 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_append_link_flags.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_APPEND_LINK_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# For every FLAG1, FLAG2 it is checked whether the linker works with the
|
||||
# flag. If it does, the flag is added FLAGS-VARIABLE
|
||||
#
|
||||
# If FLAGS-VARIABLE is not specified, the linker's flags (LDFLAGS) is
|
||||
# used. During the check the flag is always added to the linker's flags.
|
||||
#
|
||||
# If EXTRA-FLAGS is defined, it is added to the linker's default flags
|
||||
# when the check is done. The check is thus made with the flags: "LDFLAGS
|
||||
# EXTRA-FLAGS FLAG". This can for example be used to force the linker to
|
||||
# issue an error when a bad flag is given.
|
||||
#
|
||||
# NOTE: This macro depends on the AX_APPEND_FLAG and AX_CHECK_LINK_FLAG.
|
||||
# Please keep this macro in sync with AX_APPEND_COMPILE_FLAGS.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 2
|
||||
|
||||
AC_DEFUN([AX_APPEND_LINK_FLAGS],
|
||||
[for flag in $1; do
|
||||
AX_CHECK_LINK_FLAG([$flag], [AX_APPEND_FLAG([$flag], [m4_default([$2], [LDFLAGS])])], [], [$3])
|
||||
done
|
||||
])dnl AX_APPEND_LINK_FLAGS
|
72
m4/ax_check_compile_flag.m4
Normal file
72
m4/ax_check_compile_flag.m4
Normal file
@@ -0,0 +1,72 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check whether the given FLAG works with the current language's compiler
|
||||
# or gives an error. (Warnings, however, are ignored)
|
||||
#
|
||||
# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
|
||||
# success/failure.
|
||||
#
|
||||
# If EXTRA-FLAGS is defined, it is added to the current language's default
|
||||
# flags (e.g. CFLAGS) when the check is done. The check is thus made with
|
||||
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
|
||||
# force the compiler to issue an error when a bad flag is given.
|
||||
#
|
||||
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
|
||||
# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
|
||||
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 2
|
||||
|
||||
AC_DEFUN([AX_CHECK_COMPILE_FLAG],
|
||||
[AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX
|
||||
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
|
||||
AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
|
||||
ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
|
||||
_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM()],
|
||||
[AS_VAR_SET(CACHEVAR,[yes])],
|
||||
[AS_VAR_SET(CACHEVAR,[no])])
|
||||
_AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
|
||||
AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes],
|
||||
[m4_default([$2], :)],
|
||||
[m4_default([$3], :)])
|
||||
AS_VAR_POPDEF([CACHEVAR])dnl
|
||||
])dnl AX_CHECK_COMPILE_FLAGS
|
71
m4/ax_check_link_flag.m4
Normal file
71
m4/ax_check_link_flag.m4
Normal file
@@ -0,0 +1,71 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check whether the given FLAG works with the linker or gives an error.
|
||||
# (Warnings, however, are ignored)
|
||||
#
|
||||
# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
|
||||
# success/failure.
|
||||
#
|
||||
# If EXTRA-FLAGS is defined, it is added to the linker's default flags
|
||||
# when the check is done. The check is thus made with the flags: "LDFLAGS
|
||||
# EXTRA-FLAGS FLAG". This can for example be used to force the linker to
|
||||
# issue an error when a bad flag is given.
|
||||
#
|
||||
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
|
||||
# macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
|
||||
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 2
|
||||
|
||||
AC_DEFUN([AX_CHECK_LINK_FLAG],
|
||||
[AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl
|
||||
AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [
|
||||
ax_check_save_flags=$LDFLAGS
|
||||
LDFLAGS="$LDFLAGS $4 $1"
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM()],
|
||||
[AS_VAR_SET(CACHEVAR,[yes])],
|
||||
[AS_VAR_SET(CACHEVAR,[no])])
|
||||
LDFLAGS=$ax_check_save_flags])
|
||||
AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes],
|
||||
[m4_default([$2], :)],
|
||||
[m4_default([$3], :)])
|
||||
AS_VAR_POPDEF([CACHEVAR])dnl
|
||||
])dnl AX_CHECK_LINK_FLAGS
|
107
m4/ax_cxx_compile_stdcxx_0x.m4
Normal file
107
m4/ax_cxx_compile_stdcxx_0x.m4
Normal file
@@ -0,0 +1,107 @@
|
||||
# ============================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_0x.html
|
||||
# ============================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CXX_COMPILE_STDCXX_0X
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check for baseline language coverage in the compiler for the C++0x
|
||||
# standard.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 7
|
||||
|
||||
AU_ALIAS([AC_CXX_COMPILE_STDCXX_0X], [AX_CXX_COMPILE_STDCXX_0X])
|
||||
AC_DEFUN([AX_CXX_COMPILE_STDCXX_0X], [
|
||||
AC_CACHE_CHECK(if g++ supports C++0x features without additional flags,
|
||||
ax_cv_cxx_compile_cxx0x_native,
|
||||
[AC_LANG_SAVE
|
||||
AC_LANG_CPLUSPLUS
|
||||
AC_TRY_COMPILE([
|
||||
template <typename T>
|
||||
struct check
|
||||
{
|
||||
static_assert(sizeof(int) <= sizeof(T), "not big enough");
|
||||
};
|
||||
|
||||
typedef check<check<bool>> right_angle_brackets;
|
||||
|
||||
int a;
|
||||
decltype(a) b;
|
||||
|
||||
typedef check<int> check_type;
|
||||
check_type c;
|
||||
check_type&& cr = static_cast<check_type&&>(c);],,
|
||||
ax_cv_cxx_compile_cxx0x_native=yes, ax_cv_cxx_compile_cxx0x_native=no)
|
||||
AC_LANG_RESTORE
|
||||
])
|
||||
|
||||
AC_CACHE_CHECK(if g++ supports C++0x features with -std=c++0x,
|
||||
ax_cv_cxx_compile_cxx0x_cxx,
|
||||
[AC_LANG_SAVE
|
||||
AC_LANG_CPLUSPLUS
|
||||
ac_save_CXXFLAGS="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS -std=c++0x"
|
||||
AC_TRY_COMPILE([
|
||||
template <typename T>
|
||||
struct check
|
||||
{
|
||||
static_assert(sizeof(int) <= sizeof(T), "not big enough");
|
||||
};
|
||||
|
||||
typedef check<check<bool>> right_angle_brackets;
|
||||
|
||||
int a;
|
||||
decltype(a) b;
|
||||
|
||||
typedef check<int> check_type;
|
||||
check_type c;
|
||||
check_type&& cr = static_cast<check_type&&>(c);],,
|
||||
ax_cv_cxx_compile_cxx0x_cxx=yes, ax_cv_cxx_compile_cxx0x_cxx=no)
|
||||
CXXFLAGS="$ac_save_CXXFLAGS"
|
||||
AC_LANG_RESTORE
|
||||
])
|
||||
|
||||
AC_CACHE_CHECK(if g++ supports C++0x features with -std=gnu++0x,
|
||||
ax_cv_cxx_compile_cxx0x_gxx,
|
||||
[AC_LANG_SAVE
|
||||
AC_LANG_CPLUSPLUS
|
||||
ac_save_CXXFLAGS="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS -std=gnu++0x"
|
||||
AC_TRY_COMPILE([
|
||||
template <typename T>
|
||||
struct check
|
||||
{
|
||||
static_assert(sizeof(int) <= sizeof(T), "not big enough");
|
||||
};
|
||||
|
||||
typedef check<check<bool>> right_angle_brackets;
|
||||
|
||||
int a;
|
||||
decltype(a) b;
|
||||
|
||||
typedef check<int> check_type;
|
||||
check_type c;
|
||||
check_type&& cr = static_cast<check_type&&>(c);],,
|
||||
ax_cv_cxx_compile_cxx0x_gxx=yes, ax_cv_cxx_compile_cxx0x_gxx=no)
|
||||
CXXFLAGS="$ac_save_CXXFLAGS"
|
||||
AC_LANG_RESTORE
|
||||
])
|
||||
|
||||
if test "$ax_cv_cxx_compile_cxx0x_native" = yes ||
|
||||
test "$ax_cv_cxx_compile_cxx0x_cxx" = yes ||
|
||||
test "$ax_cv_cxx_compile_cxx0x_gxx" = yes; then
|
||||
AC_DEFINE(HAVE_STDCXX_0X,,[Define if g++ supports C++0x features. ])
|
||||
fi
|
||||
])
|
138
m4/faad.m4
138
m4/faad.m4
@@ -8,116 +8,51 @@ AC_ARG_ENABLE(aac,
|
||||
[disable AAC support (default: enable)]),,
|
||||
enable_aac=yes)
|
||||
|
||||
AC_ARG_WITH(faad,
|
||||
AS_HELP_STRING([--with-faad=PFX],
|
||||
[prefix where faad2 is installed (optional)]),,
|
||||
faad_prefix="")
|
||||
AC_ARG_WITH(faad-libraries,
|
||||
AS_HELP_STRING([--with-faad-libraries=DIR],
|
||||
[directory where faad2 library is installed (optional)]),,
|
||||
faad_libraries="")
|
||||
AC_ARG_WITH(faad-includes,
|
||||
AS_HELP_STRING([--with-faad-includes=DIR],
|
||||
[directory where faad2 header files are installed (optional)]),,
|
||||
faad_includes="")
|
||||
|
||||
if test x$enable_aac = xyes; then
|
||||
if test "x$faad_libraries" != "x" ; then
|
||||
FAAD_LIBS="-L$faad_libraries"
|
||||
elif test "x$faad_prefix" != "x" ; then
|
||||
FAAD_LIBS="-L$faad_prefix/lib"
|
||||
fi
|
||||
|
||||
FAAD_LIBS="$FAAD_LIBS -lfaad"
|
||||
|
||||
if test "x$faad_includes" != "x" ; then
|
||||
FAAD_CFLAGS="-I$faad_includes"
|
||||
elif test "x$faad_prefix" != "x" ; then
|
||||
FAAD_CFLAGS="-I$faad_prefix/include"
|
||||
fi
|
||||
FAAD_LIBS="-lfaad"
|
||||
FAAD_CFLAGS=""
|
||||
|
||||
oldcflags=$CFLAGS
|
||||
oldlibs=$LIBS
|
||||
oldcppflags=$CPPFLAGS
|
||||
CFLAGS="$CFLAGS $MPD_CFLAGS $FAAD_CFLAGS -I."
|
||||
LIBS="$LIBS $MPD_LIBS $FAAD_LIBS"
|
||||
CFLAGS="$CFLAGS $FAAD_CFLAGS"
|
||||
LIBS="$LIBS $FAAD_LIBS"
|
||||
CPPFLAGS=$CFLAGS
|
||||
AC_CHECK_HEADER(faad.h,,enable_aac=no)
|
||||
if test x$enable_aac = xyes; then
|
||||
AC_CHECK_DECL(FAAD2_VERSION,,enable_aac=no,[#include <faad.h>])
|
||||
fi
|
||||
if test x$enable_aac = xyes; then
|
||||
AC_CHECK_DECL(faacDecInit2,,enable_aac=no,[#include <faad.h>])
|
||||
AC_CHECK_LIB(faad,NeAACDecInit2,,enable_aac=no)
|
||||
fi
|
||||
if test x$enable_aac = xyes; then
|
||||
AC_CHECK_LIB(faad,faacDecInit2,[MPD_LIBS="$MPD_LIBS $FAAD_LIBS";MPD_CFLAGS="$MPD_CFLAGS $FAAD_CFLAGS"],enable_aac=no)
|
||||
if test x$enable_aac = xno; then
|
||||
enable_aac=yes
|
||||
AC_CHECK_LIB(faad,NeAACDecInit2,[MPD_LIBS="$MPD_LIBS $FAAD_LIBS";MPD_CFLAGS="$MPD_CFLAGS $FAAD_CFLAGS"],enable_aac=no)
|
||||
fi
|
||||
fi
|
||||
if test x$enable_aac = xyes; then
|
||||
AC_MSG_CHECKING(that FAAD2 uses buffer and bufferlen)
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||
#include <faad.h>
|
||||
|
||||
int main() {
|
||||
char buffer;
|
||||
long bufferlen = 0;
|
||||
faacDecHandle decoder;
|
||||
faacDecFrameInfo frameInfo;
|
||||
faacDecConfigurationPtr config;
|
||||
unsigned char channels;
|
||||
long sampleRate;
|
||||
mp4AudioSpecificConfig mp4ASC;
|
||||
|
||||
decoder = faacDecOpen();
|
||||
config = faacDecGetCurrentConfiguration(decoder);
|
||||
config->outputFormat = FAAD_FMT_16BIT;
|
||||
faacDecSetConfiguration(decoder,config);
|
||||
AudioSpecificConfig(&buffer, bufferlen, &mp4ASC);
|
||||
faacDecInit(decoder,&buffer,bufferlen,&sampleRate,&channels);
|
||||
faacDecInit2(decoder,&buffer,bufferlen,&sampleRate,&channels);
|
||||
faacDecDecode(decoder,&frameInfo,&buffer,bufferlen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
])],[AC_MSG_RESULT(yes);AC_DEFINE(HAVE_FAAD_BUFLEN_FUNCS,1,[Define if FAAD2 uses buflen in function calls])],[AC_MSG_RESULT(no);
|
||||
AC_MSG_CHECKING(that FAAD2 can even be used)
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||
#include <faad.h>
|
||||
|
||||
int main() {
|
||||
char buffer;
|
||||
faacDecHandle decoder;
|
||||
faacDecFrameInfo frameInfo;
|
||||
faacDecConfigurationPtr config;
|
||||
NeAACDecHandle decoder;
|
||||
NeAACDecFrameInfo frameInfo;
|
||||
NeAACDecConfigurationPtr config;
|
||||
unsigned char channels;
|
||||
long sampleRate;
|
||||
long bufferlen = 0;
|
||||
unsigned long dummy1_32;
|
||||
unsigned char dummy2_8, dummy3_8, dummy4_8, dummy5_8, dummy6_8,
|
||||
dummy7_8, dummy8_8;
|
||||
|
||||
decoder = faacDecOpen();
|
||||
config = faacDecGetCurrentConfiguration(decoder);
|
||||
decoder = NeAACDecOpen();
|
||||
config = NeAACDecGetCurrentConfiguration(decoder);
|
||||
config->outputFormat = FAAD_FMT_16BIT;
|
||||
faacDecSetConfiguration(decoder,config);
|
||||
AudioSpecificConfig(&buffer,&dummy1_32,&dummy2_8,
|
||||
&dummy3_8,&dummy4_8,&dummy5_8,
|
||||
&dummy6_8,&dummy7_8,&dummy8_8);
|
||||
faacDecInit(decoder,&buffer,&sampleRate,&channels);
|
||||
faacDecInit2(decoder,&buffer,bufferlen,&sampleRate,&channels);
|
||||
faacDecDecode(decoder,&frameInfo,&buffer);
|
||||
faacDecClose(decoder);
|
||||
NeAACDecSetConfiguration(decoder,config);
|
||||
NeAACDecInit(decoder,&buffer,bufferlen,&sampleRate,&channels);
|
||||
NeAACDecInit2(decoder,&buffer,bufferlen,&sampleRate,&channels);
|
||||
NeAACDecDecode(decoder,&frameInfo,&buffer,bufferlen);
|
||||
NeAACDecClose(decoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
])],AC_MSG_RESULT(yes),[AC_MSG_RESULT(no);enable_aac=no])
|
||||
])
|
||||
fi
|
||||
if test x$enable_aac = xyes; then
|
||||
AC_CHECK_MEMBERS([faacDecConfiguration.downMatrix,faacDecConfiguration.dontUpSampleImplicitSBR,faacDecFrameInfo.samplerate],,,[#include <faad.h>])
|
||||
AC_DEFINE(HAVE_FAAD,1,[Define to use FAAD2 for AAC decoding])
|
||||
else
|
||||
AC_MSG_WARN([faad2 lib needed for MP4/AAC support -- disabling MP4/AAC support])
|
||||
@@ -131,8 +66,8 @@ if test x$enable_aac = xyes; then
|
||||
oldcflags=$CFLAGS
|
||||
oldlibs=$LIBS
|
||||
oldcppflags=$CPPFLAGS
|
||||
CFLAGS="$CFLAGS $MPD_CFLAGS $FAAD_CFLAGS -Werror"
|
||||
LIBS="$LIBS $MPD_LIBS $FAAD_LIBS"
|
||||
CFLAGS="$CFLAGS $FAAD_CFLAGS -Werror"
|
||||
LIBS="$LIBS $FAAD_LIBS"
|
||||
CPPFLAGS=$CFLAGS
|
||||
|
||||
AC_MSG_CHECKING(for broken libfaad headers)
|
||||
@@ -145,7 +80,7 @@ int main() {
|
||||
unsigned char channels;
|
||||
uint32_t sample_rate;
|
||||
|
||||
faacDecInit2(NULL, NULL, 0, &sample_rate, &channels);
|
||||
NeAACDecInit2(NULL, NULL, 0, &sample_rate, &channels);
|
||||
return 0;
|
||||
}
|
||||
])],
|
||||
@@ -153,40 +88,15 @@ int main() {
|
||||
[AC_MSG_RESULT(broken);
|
||||
AC_DEFINE(HAVE_FAAD_LONG, 1, [Define if faad.h uses the broken "unsigned long" pointers])])
|
||||
|
||||
CFLAGS=$oldcflags
|
||||
LIBS=$oldlibs
|
||||
CPPFLAGS=$oldcppflags
|
||||
fi
|
||||
|
||||
if test x$enable_aac = xyes; then
|
||||
enable_mp4=yes
|
||||
MP4FF_LIBS="-lmp4ff"
|
||||
|
||||
oldcflags=$CFLAGS
|
||||
oldlibs=$LIBS
|
||||
oldcppflags=$CPPFLAGS
|
||||
CFLAGS="$CFLAGS $FAAD_CFLAGS"
|
||||
LIBS="$LIBS $FAAD_LIBS $MP4FF_LIBS"
|
||||
CPPFLAGS=$CFLAGS
|
||||
|
||||
AC_CHECK_HEADER(mp4ff.h,,enable_mp4=no)
|
||||
|
||||
if test x$enable_mp4 = xyes; then
|
||||
AC_CHECK_LIB(mp4ff,mp4ff_open_read,,enable_mp4=no)
|
||||
fi
|
||||
|
||||
if test x$enable_mp4 = xyes; then
|
||||
AC_SUBST(MP4FF_LIBS)
|
||||
AC_DEFINE(HAVE_MP4, 1, [Define to use FAAD2+mp4ff for MP4 decoding])
|
||||
else
|
||||
AC_MSG_WARN([libmp4ff needed for MP4 support -- disabling MP4 support])
|
||||
unset MP4FF_LIBS
|
||||
fi
|
||||
|
||||
CFLAGS=$oldcflags
|
||||
LIBS=$oldlibs
|
||||
CPPFLAGS=$oldcppflags
|
||||
else
|
||||
enable_mp4=no
|
||||
FAAD_LIBS=""
|
||||
FAAD_CFLAGS=""
|
||||
fi
|
||||
|
||||
AC_SUBST(FAAD_CFLAGS)
|
||||
AC_SUBST(FAAD_LIBS)
|
||||
|
||||
])
|
||||
|
@@ -73,8 +73,26 @@ AC_DEFUN([MPD_AUTO_PKG_LIB], [
|
||||
[eval "found_$1=yes"],
|
||||
AC_CHECK_LIB($4, $5,
|
||||
[eval "found_$1=yes $2_LIBS='$6' $2_CFLAGS='$7'"],
|
||||
[eval "found_$1=no"]))
|
||||
[eval "found_$1=no"],
|
||||
[$6]))
|
||||
fi
|
||||
|
||||
MPD_AUTO_RESULT([$1], [$8], [$9])
|
||||
])
|
||||
|
||||
dnl Wrapper for AC_CHECK_LIB.
|
||||
dnl
|
||||
dnl Parameters: varname1, varname2, libname, symname, libs, cflags, description, errmsg
|
||||
AC_DEFUN([MPD_AUTO_LIB], [
|
||||
AC_SUBST([$2_LIBS], [])
|
||||
AC_SUBST([$2_CFLAGS], [])
|
||||
|
||||
if eval "test x`echo '$'enable_$1` != xno"; then
|
||||
AC_CHECK_LIB($3, $4,
|
||||
[eval "found_$1=yes $2_LIBS='$5' $2_CFLAGS='$6'"],
|
||||
[eval "found_$1=no"],
|
||||
[$5])
|
||||
fi
|
||||
|
||||
MPD_AUTO_RESULT([$1], [$7], [$8])
|
||||
])
|
||||
|
@@ -1,18 +0,0 @@
|
||||
AC_DEFUN([MPD_CHECK_FLAG],[
|
||||
var=`echo "$1" | tr "=-" "__"`
|
||||
AC_CACHE_CHECK([whether the C compiler accepts $1],
|
||||
[mpd_check_cflag_$var],[
|
||||
save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $1"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||
int main(void) { return 0; }
|
||||
])], [ eval "mpd_check_cflag_$var=yes"
|
||||
], [ eval "mpd_check_cflag_$var=no" ])
|
||||
CFLAGS="$save_CFLAGS"
|
||||
])
|
||||
if eval "test x`echo '$mpd_check_cflag_'$var` = xyes"
|
||||
then
|
||||
AM_CFLAGS="$AM_CFLAGS $1"
|
||||
fi
|
||||
])
|
||||
])
|
12
m4/mpd_func.m4
Normal file
12
m4/mpd_func.m4
Normal file
@@ -0,0 +1,12 @@
|
||||
dnl MPD_OPTIONAL_FUNC(name, func, macro)
|
||||
dnl
|
||||
dnl Allow the user to enable or disable the use of a function. If the
|
||||
dnl option is not specified, the function is auto-detected.
|
||||
AC_DEFUN([MPD_OPTIONAL_FUNC], [
|
||||
AC_ARG_ENABLE([$1],
|
||||
AS_HELP_STRING([--enable-$1],
|
||||
[use the function "$1" (default: auto)]),
|
||||
[test xenable_$1 = xyes && AC_DEFINE([$3], 1, [Define to use $1])],
|
||||
[AC_CHECK_FUNC([$2],
|
||||
[AC_DEFINE([$3], 1, [Define to use $1])],)])
|
||||
])
|
@@ -20,7 +20,7 @@ AC_DEFUN([STRUCT_UCRED],[
|
||||
mpd_cv_have_struct_ucred=no)
|
||||
if test x$mpd_cv_have_struct_ucred = xyes; then
|
||||
# :(
|
||||
MPD_CFLAGS="$MPD_CFLAGS -D_GNU_SOURCE"
|
||||
CFLAGS="$CFLAGS -D_GNU_SOURCE"
|
||||
fi
|
||||
fi
|
||||
])
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[Unit]
|
||||
Description=Music Player Daemon
|
||||
After=sound.target
|
||||
After=network.target sound.target
|
||||
|
||||
[Service]
|
||||
ExecStart=@prefix@/bin/mpd --no-daemon
|
||||
|
@@ -1,25 +0,0 @@
|
||||
#!/bin/sh
|
||||
PWD=`pwd`
|
||||
|
||||
## If we're not in the scripts directory
|
||||
## assume the base directory.
|
||||
if test "`basename $PWD`" = "scripts"; then
|
||||
cd ../
|
||||
else
|
||||
MYOLDPWD=`pwd`
|
||||
cd `dirname $0`/../
|
||||
fi
|
||||
|
||||
if test -e Makefile
|
||||
then
|
||||
make distclean
|
||||
fi
|
||||
./autogen.sh
|
||||
make
|
||||
make dist
|
||||
|
||||
if test "`basename $PWD`" = "scripts"; then
|
||||
cd contrib/
|
||||
else
|
||||
cd $MYOLDPWD
|
||||
fi
|
@@ -1,6 +0,0 @@
|
||||
#!/bin/sh
|
||||
indent -npro -kr -i8 -ts8 -sob -l80 -ss -ncs -cdw -cd0 -c0 -cp0 "$@"
|
||||
|
||||
# there doesn't seem to be an indent switch for this, but this
|
||||
# forces goto labels to the left-most column, without indentation
|
||||
perl -i -p -e 's/^\s+(\w+):/$1:/g unless /^\s+default:/' "$@"
|
@@ -1,93 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
#
|
||||
# This shell script tests the build of MPD with various compile-time
|
||||
# options.
|
||||
#
|
||||
# Author: Max Kellermann <max@duempel.org>
|
||||
|
||||
PREFIX=/tmp/mpd
|
||||
rm -rf $PREFIX
|
||||
|
||||
test "x$MAKE" != x || MAKE=make
|
||||
|
||||
export CFLAGS="-Os"
|
||||
|
||||
test -x configure || NOCONFIGURE=1 ./autogen.sh
|
||||
|
||||
# all features on
|
||||
./configure --prefix=$PREFIX/full \
|
||||
--disable-dependency-tracking --enable-debug --enable-werror \
|
||||
--enable-un \
|
||||
--enable-modplug \
|
||||
--enable-ao --enable-mikmod --enable-mvp
|
||||
$MAKE install
|
||||
$MAKE distclean
|
||||
|
||||
# no UN, no oggvorbis, no flac, enable oggflac
|
||||
./configure --prefix=$PREFIX/small \
|
||||
--disable-dependency-tracking --enable-debug --enable-werror \
|
||||
--disable-un \
|
||||
--disable-flac --disable-vorbis --enable-oggflac
|
||||
$MAKE install
|
||||
$MAKE distclean
|
||||
|
||||
# strip down (disable TCP, disable nearly all plugins)
|
||||
CFLAGS="$CFLAGS -DNDEBUG" \
|
||||
./configure --prefix=$PREFIX/tiny \
|
||||
--disable-dependency-tracking --disable-debug --enable-werror \
|
||||
--disable-tcp \
|
||||
--disable-curl \
|
||||
--disable-id3 --disable-lsr \
|
||||
--disable-ao --disable-alsa --disable-jack --disable-pulse --disable-fifo \
|
||||
--disable-shout-ogg --disable-shout-mp3 --disable-lame-encoder \
|
||||
--disable-ffmpeg --disable-wavpack --disable-mpc --disable-aac \
|
||||
--disable-flac --disable-vorbis --disable-oggflac --disable-audiofile \
|
||||
--disable-cue \
|
||||
--with-zeroconf=no
|
||||
$MAKE install
|
||||
$MAKE distclean
|
||||
|
||||
# shout: ogg without mp3
|
||||
# sndfile instead of modplug
|
||||
./configure --prefix=$PREFIX/shout_ogg \
|
||||
--disable-dependency-tracking --disable-debug --enable-werror \
|
||||
--disable-tcp \
|
||||
--disable-curl \
|
||||
--disable-id3 --disable-lsr \
|
||||
--disable-ao --disable-alsa --disable-jack --disable-pulse --disable-fifo \
|
||||
--enable-shout-ogg --disable-shout-mp3 --disable-lame-encoder \
|
||||
--disable-ffmpeg --disable-wavpack --disable-mpc --disable-aac \
|
||||
--disable-flac --enable-vorbis --disable-oggflac --disable-audiofile \
|
||||
--disable-modplug --enable-sndfile \
|
||||
--with-zeroconf=no
|
||||
$MAKE install
|
||||
$MAKE distclean
|
||||
|
||||
# shout: mp3 without ogg
|
||||
./configure --prefix=$PREFIX/shout_mp3 \
|
||||
--disable-dependency-tracking --disable-debug --enable-werror \
|
||||
--disable-tcp \
|
||||
--disable-curl \
|
||||
--disable-id3 --disable-lsr \
|
||||
--disable-ao --disable-alsa --disable-jack --disable-pulse --disable-fifo \
|
||||
--disable-shout-ogg --enable-shout-mp3 --enable-lame-encoder \
|
||||
--disable-ffmpeg --disable-wavpack --disable-mpc --disable-aac \
|
||||
--disable-flac --disable-vorbis --disable-oggflac --disable-audiofile \
|
||||
--with-zeroconf=no
|
||||
$MAKE install
|
||||
$MAKE distclean
|
||||
|
||||
# oggvorbis + oggflac
|
||||
./configure --prefix=$PREFIX/oggvorbisflac \
|
||||
--disable-dependency-tracking --disable-debug --enable-werror \
|
||||
--disable-tcp \
|
||||
--disable-curl \
|
||||
--disable-id3 --disable-lsr \
|
||||
--disable-mp3 \
|
||||
--disable-ao --disable-alsa --disable-jack --disable-pulse --disable-fifo \
|
||||
--disable-shout-ogg --disable-shout-mp3 --disable-lame-encoder \
|
||||
--disable-ffmpeg --disable-wavpack --disable-mpc --disable-aac \
|
||||
--disable-flac --enable-vorbis --enable-oggflac --disable-audiofile \
|
||||
--with-zeroconf=no
|
||||
$MAKE install
|
||||
$MAKE distclean
|
23
src/ArchiveDomain.cxx
Normal file
23
src/ArchiveDomain.cxx
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "ArchiveDomain.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
|
||||
const Domain archive_domain("archive");
|
25
src/ArchiveDomain.hxx
Normal file
25
src/ArchiveDomain.hxx
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_ARCHIVE_DOMAIN_HXX
|
||||
#define MPD_ARCHIVE_DOMAIN_HXX
|
||||
|
||||
extern const class Domain archive_domain;
|
||||
|
||||
#endif
|
58
src/ArchiveFile.hxx
Normal file
58
src/ArchiveFile.hxx
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_ARCHIVE_FILE_HXX
|
||||
#define MPD_ARCHIVE_FILE_HXX
|
||||
|
||||
class Mutex;
|
||||
class Cond;
|
||||
class Error;
|
||||
|
||||
class ArchiveFile {
|
||||
public:
|
||||
const struct archive_plugin &plugin;
|
||||
|
||||
ArchiveFile(const struct archive_plugin &_plugin)
|
||||
:plugin(_plugin) {}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Use Close() instead of delete.
|
||||
*/
|
||||
~ArchiveFile() {}
|
||||
|
||||
public:
|
||||
virtual void Close() = 0;
|
||||
|
||||
/**
|
||||
* Visit all entries inside this archive.
|
||||
*/
|
||||
virtual void Visit(ArchiveVisitor &visitor) = 0;
|
||||
|
||||
/**
|
||||
* Opens an InputStream of a file within the archive.
|
||||
*
|
||||
* @param path the path within the archive
|
||||
*/
|
||||
virtual InputStream *OpenStream(const char *path,
|
||||
Mutex &mutex, Cond &cond,
|
||||
Error &error) = 0;
|
||||
};
|
||||
|
||||
#endif
|
90
src/ArchiveList.cxx
Normal file
90
src/ArchiveList.cxx
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ArchiveList.hxx"
|
||||
#include "ArchivePlugin.hxx"
|
||||
#include "util/StringUtil.hxx"
|
||||
#include "archive/Bzip2ArchivePlugin.hxx"
|
||||
#include "archive/Iso9660ArchivePlugin.hxx"
|
||||
#include "archive/ZzipArchivePlugin.hxx"
|
||||
#include "util/Macros.hxx"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
const struct archive_plugin *const archive_plugins[] = {
|
||||
#ifdef HAVE_BZ2
|
||||
&bz2_archive_plugin,
|
||||
#endif
|
||||
#ifdef HAVE_ZZIP
|
||||
&zzip_archive_plugin,
|
||||
#endif
|
||||
#ifdef HAVE_ISO9660
|
||||
&iso9660_archive_plugin,
|
||||
#endif
|
||||
nullptr
|
||||
};
|
||||
|
||||
/** which plugins have been initialized successfully? */
|
||||
static bool archive_plugins_enabled[ARRAY_SIZE(archive_plugins) - 1];
|
||||
|
||||
#define archive_plugins_for_each_enabled(plugin) \
|
||||
archive_plugins_for_each(plugin) \
|
||||
if (archive_plugins_enabled[archive_plugin_iterator - archive_plugins])
|
||||
|
||||
const struct archive_plugin *
|
||||
archive_plugin_from_suffix(const char *suffix)
|
||||
{
|
||||
if (suffix == nullptr)
|
||||
return nullptr;
|
||||
|
||||
archive_plugins_for_each_enabled(plugin)
|
||||
if (plugin->suffixes != nullptr &&
|
||||
string_array_contains(plugin->suffixes, suffix))
|
||||
return plugin;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const struct archive_plugin *
|
||||
archive_plugin_from_name(const char *name)
|
||||
{
|
||||
archive_plugins_for_each_enabled(plugin)
|
||||
if (strcmp(plugin->name, name) == 0)
|
||||
return plugin;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void archive_plugin_init_all(void)
|
||||
{
|
||||
for (unsigned i = 0; archive_plugins[i] != nullptr; ++i) {
|
||||
const struct archive_plugin *plugin = archive_plugins[i];
|
||||
if (plugin->init == nullptr || archive_plugins[i]->init())
|
||||
archive_plugins_enabled[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void archive_plugin_deinit_all(void)
|
||||
{
|
||||
archive_plugins_for_each_enabled(plugin)
|
||||
if (plugin->finish != nullptr)
|
||||
plugin->finish();
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,13 +17,19 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_ARCHIVE_LIST_H
|
||||
#define MPD_ARCHIVE_LIST_H
|
||||
|
||||
#include <stdio.h>
|
||||
#ifndef MPD_ARCHIVE_LIST_HXX
|
||||
#define MPD_ARCHIVE_LIST_HXX
|
||||
|
||||
struct archive_plugin;
|
||||
|
||||
extern const struct archive_plugin *const archive_plugins[];
|
||||
|
||||
#define archive_plugins_for_each(plugin) \
|
||||
for (const struct archive_plugin *plugin, \
|
||||
*const*archive_plugin_iterator = &archive_plugins[0]; \
|
||||
(plugin = *archive_plugin_iterator) != nullptr; \
|
||||
++archive_plugin_iterator)
|
||||
|
||||
/* interface for using plugins */
|
||||
|
||||
const struct archive_plugin *
|
||||
@@ -32,8 +38,6 @@ archive_plugin_from_suffix(const char *suffix);
|
||||
const struct archive_plugin *
|
||||
archive_plugin_from_name(const char *name);
|
||||
|
||||
void archive_plugin_print_all_suffixes(FILE * fp);
|
||||
|
||||
/* this is where we "load" all the "plugins" ;-) */
|
||||
void archive_plugin_init_all(void);
|
||||
|
104
src/ArchiveLookup.cxx
Normal file
104
src/ArchiveLookup.cxx
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h" /* must be first for large file support */
|
||||
#include "ArchiveLookup.hxx"
|
||||
#include "ArchiveDomain.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
gcc_pure
|
||||
static char *
|
||||
FindSlash(char *p, size_t i)
|
||||
{
|
||||
for (; i > 0; --i)
|
||||
if (p[i] == '/')
|
||||
return p + i;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
static const char *
|
||||
FindSuffix(const char *p, const char *i)
|
||||
{
|
||||
for (; i > p; --i) {
|
||||
if (*i == '.')
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
archive_lookup(char *pathname, const char **archive,
|
||||
const char **inpath, const char **suffix)
|
||||
{
|
||||
size_t idx = strlen(pathname);
|
||||
|
||||
char *slash = nullptr;
|
||||
|
||||
while (true) {
|
||||
//try to stat if its real directory
|
||||
struct stat st_info;
|
||||
if (stat(pathname, &st_info) == -1) {
|
||||
if (errno != ENOTDIR) {
|
||||
FormatErrno(archive_domain,
|
||||
"Failed to stat %s", pathname);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
//is something found ins original path (is not an archive)
|
||||
if (slash == nullptr)
|
||||
return false;
|
||||
|
||||
//its a file ?
|
||||
if (S_ISREG(st_info.st_mode)) {
|
||||
//so the upper should be file
|
||||
*archive = pathname;
|
||||
*inpath = slash + 1;
|
||||
|
||||
//try to get suffix
|
||||
*suffix = FindSuffix(pathname, slash - 1);
|
||||
return true;
|
||||
} else {
|
||||
FormatError(archive_domain,
|
||||
"Not a regular file: %s",
|
||||
pathname);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//find one dir up
|
||||
if (slash != nullptr)
|
||||
*slash = '/';
|
||||
|
||||
slash = FindSlash(pathname, idx - 1);
|
||||
if (slash == nullptr)
|
||||
return false;
|
||||
|
||||
*slash = 0;
|
||||
idx = slash - pathname;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,17 +17,8 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h" /* must be first for large file support */
|
||||
#include "archive_api.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <glib.h>
|
||||
#ifndef MPD_ARCHIVE_LOOKUP_HXX
|
||||
#define MPD_ARCHIVE_LOOKUP_HXX
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -46,66 +37,9 @@
|
||||
* inarchive pathname: Talco - Combat Circus/12 - A la pachenka.mp3
|
||||
* and suffix: zip
|
||||
*/
|
||||
bool
|
||||
archive_lookup(char *pathname, const char **archive,
|
||||
const char **inpath, const char **suffix);
|
||||
|
||||
bool archive_lookup(char *pathname, char **archive, char **inpath, char **suffix)
|
||||
{
|
||||
char *pathdupe;
|
||||
int len, idx;
|
||||
struct stat st_info;
|
||||
bool ret = false;
|
||||
|
||||
*archive = NULL;
|
||||
*inpath = NULL;
|
||||
*suffix = NULL;
|
||||
|
||||
pathdupe = g_strdup(pathname);
|
||||
len = idx = strlen(pathname);
|
||||
|
||||
while (idx > 0) {
|
||||
//try to stat if its real directory
|
||||
if (stat(pathdupe, &st_info) == -1) {
|
||||
if (errno != ENOTDIR) {
|
||||
g_warning("stat %s failed (errno=%d)\n", pathdupe, errno);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
//is something found ins original path (is not an archive)
|
||||
if (idx == len) {
|
||||
break;
|
||||
}
|
||||
//its a file ?
|
||||
if (S_ISREG(st_info.st_mode)) {
|
||||
//so the upper should be file
|
||||
pathname[idx] = 0;
|
||||
ret = true;
|
||||
*archive = pathname;
|
||||
*inpath = pathname + idx+1;
|
||||
|
||||
//try to get suffix
|
||||
*suffix = NULL;
|
||||
while (idx > 0) {
|
||||
if (pathname[idx] == '.') {
|
||||
*suffix = pathname + idx + 1;
|
||||
break;
|
||||
}
|
||||
idx--;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
g_warning("not a regular file %s\n", pathdupe);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//find one dir up
|
||||
while (idx > 0) {
|
||||
if (pathdupe[idx] == '/') {
|
||||
pathdupe[idx] = 0;
|
||||
break;
|
||||
}
|
||||
idx--;
|
||||
}
|
||||
}
|
||||
g_free(pathdupe);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
39
src/ArchivePlugin.cxx
Normal file
39
src/ArchivePlugin.cxx
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ArchivePlugin.hxx"
|
||||
#include "ArchiveFile.hxx"
|
||||
#include "util/Error.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
ArchiveFile *
|
||||
archive_file_open(const struct archive_plugin *plugin, const char *path,
|
||||
Error &error)
|
||||
{
|
||||
assert(plugin != nullptr);
|
||||
assert(plugin->open != nullptr);
|
||||
assert(path != nullptr);
|
||||
|
||||
ArchiveFile *file = plugin->open(path, error);
|
||||
assert((file == nullptr) == error.IsDefined());
|
||||
|
||||
return file;
|
||||
}
|
62
src/ArchivePlugin.hxx
Normal file
62
src/ArchivePlugin.hxx
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_ARCHIVE_PLUGIN_HXX
|
||||
#define MPD_ARCHIVE_PLUGIN_HXX
|
||||
|
||||
struct InputStream;
|
||||
class ArchiveFile;
|
||||
class ArchiveVisitor;
|
||||
class Error;
|
||||
|
||||
struct archive_plugin {
|
||||
const char *name;
|
||||
|
||||
/**
|
||||
* optional, set this to nullptr if the archive plugin doesn't
|
||||
* have/need one this must false if there is an error and
|
||||
* true otherwise
|
||||
*/
|
||||
bool (*init)(void);
|
||||
|
||||
/**
|
||||
* optional, set this to nullptr if the archive plugin doesn't
|
||||
* have/need one
|
||||
*/
|
||||
void (*finish)(void);
|
||||
|
||||
/**
|
||||
* tryes to open archive file and associates handle with archive
|
||||
* returns pointer to handle used is all operations with this archive
|
||||
* or nullptr when opening fails
|
||||
*/
|
||||
ArchiveFile *(*open)(const char *path_fs, Error &error);
|
||||
|
||||
/**
|
||||
* suffixes handled by this plugin.
|
||||
* last element in these arrays must always be a nullptr
|
||||
*/
|
||||
const char *const*suffixes;
|
||||
};
|
||||
|
||||
ArchiveFile *
|
||||
archive_file_open(const struct archive_plugin *plugin, const char *path,
|
||||
Error &error);
|
||||
|
||||
#endif
|
28
src/ArchiveVisitor.hxx
Normal file
28
src/ArchiveVisitor.hxx
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_ARCHIVE_VISITOR_HXX
|
||||
#define MPD_ARCHIVE_VISITOR_HXX
|
||||
|
||||
class ArchiveVisitor {
|
||||
public:
|
||||
virtual void VisitArchiveEntry(const char *path_utf8) = 0;
|
||||
};
|
||||
|
||||
#endif
|
@@ -33,6 +33,9 @@ struct Compressor {
|
||||
struct Compressor *Compressor_new(unsigned int history)
|
||||
{
|
||||
struct Compressor *obj = malloc(sizeof(struct Compressor));
|
||||
if (obj == NULL)
|
||||
/* out of memory, not much we can do */
|
||||
abort();
|
||||
|
||||
obj->prefs.target = TARGET;
|
||||
obj->prefs.maxgain = GAINMAX;
|
||||
@@ -61,6 +64,10 @@ void Compressor_delete(struct Compressor *obj)
|
||||
static int *resizeArray(int *data, int newsz, int oldsz)
|
||||
{
|
||||
data = realloc(data, newsz*sizeof(int));
|
||||
if (data == NULL)
|
||||
/* out of memory, not much we can do */
|
||||
abort();
|
||||
|
||||
if (newsz > oldsz)
|
||||
memset(data + oldsz, 0, sizeof(int)*(newsz - oldsz));
|
||||
return data;
|
||||
|
@@ -19,6 +19,10 @@ struct CompressorConfig {
|
||||
|
||||
struct Compressor;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//! Create a new compressor (use history value of 0 for default)
|
||||
struct Compressor *Compressor_new(unsigned int history);
|
||||
|
||||
@@ -34,7 +38,12 @@ struct CompressorConfig *Compressor_getConfig(struct Compressor *);
|
||||
//! Process 16-bit signed data
|
||||
void Compressor_Process_int16(struct Compressor *, int16_t *data, unsigned int count);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
//! TODO: Compressor_Process_int32, Compressor_Process_float, others as needed
|
||||
|
||||
//! TODO: functions for getting at the peak/gain/clip history buffers (for monitoring)
|
||||
|
||||
#endif
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -18,42 +18,35 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "audio.h"
|
||||
#include "audio_format.h"
|
||||
#include "audio_parser.h"
|
||||
#include "output_internal.h"
|
||||
#include "output_plugin.h"
|
||||
#include "output_all.h"
|
||||
#include "conf.h"
|
||||
#include "mpd_error.h"
|
||||
#include "AudioConfig.hxx"
|
||||
#include "AudioFormat.hxx"
|
||||
#include "AudioParser.hxx"
|
||||
#include "ConfigData.hxx"
|
||||
#include "ConfigGlobal.hxx"
|
||||
#include "ConfigOption.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "system/FatalError.hxx"
|
||||
|
||||
#include <glib.h>
|
||||
static AudioFormat configured_audio_format;
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static struct audio_format configured_audio_format;
|
||||
|
||||
void getOutputAudioFormat(const struct audio_format *inAudioFormat,
|
||||
struct audio_format *outAudioFormat)
|
||||
AudioFormat
|
||||
getOutputAudioFormat(AudioFormat inAudioFormat)
|
||||
{
|
||||
*outAudioFormat = *inAudioFormat;
|
||||
audio_format_mask_apply(outAudioFormat, &configured_audio_format);
|
||||
AudioFormat out_audio_format = inAudioFormat;
|
||||
out_audio_format.ApplyMask(configured_audio_format);
|
||||
return out_audio_format;
|
||||
}
|
||||
|
||||
void initAudioConfig(void)
|
||||
{
|
||||
const struct config_param *param = config_get_param(CONF_AUDIO_OUTPUT_FORMAT);
|
||||
GError *error = NULL;
|
||||
bool ret;
|
||||
|
||||
if (param == NULL)
|
||||
if (param == nullptr)
|
||||
return;
|
||||
|
||||
ret = audio_format_parse(&configured_audio_format, param->value,
|
||||
true, &error);
|
||||
if (!ret)
|
||||
MPD_ERROR("error parsing \"%s\" at line %i: %s",
|
||||
CONF_AUDIO_OUTPUT_FORMAT, param->line,
|
||||
error->message);
|
||||
Error error;
|
||||
if (!audio_format_parse(configured_audio_format, param->value.c_str(),
|
||||
true, error))
|
||||
FormatFatalError("error parsing line %i: %s",
|
||||
param->line, error.GetMessage());
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,15 +17,13 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_AUDIO_H
|
||||
#define MPD_AUDIO_H
|
||||
#ifndef MPD_AUDIO_CONFIG_HXX
|
||||
#define MPD_AUDIO_CONFIG_HXX
|
||||
|
||||
#include <stdbool.h>
|
||||
struct AudioFormat;
|
||||
|
||||
struct audio_format;
|
||||
|
||||
void getOutputAudioFormat(const struct audio_format *inFormat,
|
||||
struct audio_format *outFormat);
|
||||
AudioFormat
|
||||
getOutputAudioFormat(AudioFormat inFormat);
|
||||
|
||||
/* make sure initPlayerData is called before this function!! */
|
||||
void initAudioConfig(void);
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,56 +17,69 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "audio_format.h"
|
||||
#include "AudioFormat.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if G_BYTE_ORDER == G_BIG_ENDIAN
|
||||
#define REVERSE_ENDIAN_SUFFIX "_le"
|
||||
#else
|
||||
#define REVERSE_ENDIAN_SUFFIX "_be"
|
||||
#endif
|
||||
void
|
||||
AudioFormat::ApplyMask(AudioFormat mask)
|
||||
{
|
||||
assert(IsValid());
|
||||
assert(mask.IsMaskValid());
|
||||
|
||||
if (mask.sample_rate != 0)
|
||||
sample_rate = mask.sample_rate;
|
||||
|
||||
if (mask.format != SampleFormat::UNDEFINED)
|
||||
format = mask.format;
|
||||
|
||||
if (mask.channels != 0)
|
||||
channels = mask.channels;
|
||||
|
||||
assert(IsValid());
|
||||
}
|
||||
|
||||
const char *
|
||||
sample_format_to_string(enum sample_format format)
|
||||
sample_format_to_string(SampleFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case SAMPLE_FORMAT_UNDEFINED:
|
||||
case SampleFormat::UNDEFINED:
|
||||
return "?";
|
||||
|
||||
case SAMPLE_FORMAT_S8:
|
||||
case SampleFormat::S8:
|
||||
return "8";
|
||||
|
||||
case SAMPLE_FORMAT_S16:
|
||||
case SampleFormat::S16:
|
||||
return "16";
|
||||
|
||||
case SAMPLE_FORMAT_S24:
|
||||
return "24_3";
|
||||
|
||||
case SAMPLE_FORMAT_S24_P32:
|
||||
case SampleFormat::S24_P32:
|
||||
return "24";
|
||||
|
||||
case SAMPLE_FORMAT_S32:
|
||||
case SampleFormat::S32:
|
||||
return "32";
|
||||
|
||||
case SampleFormat::FLOAT:
|
||||
return "f";
|
||||
|
||||
case SampleFormat::DSD:
|
||||
return "dsd";
|
||||
}
|
||||
|
||||
/* unreachable */
|
||||
assert(false);
|
||||
return "?";
|
||||
gcc_unreachable();
|
||||
}
|
||||
|
||||
const char *
|
||||
audio_format_to_string(const struct audio_format *af,
|
||||
audio_format_to_string(const AudioFormat af,
|
||||
struct audio_format_string *s)
|
||||
{
|
||||
assert(af != NULL);
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
snprintf(s->buffer, sizeof(s->buffer), "%u:%s%s:%u",
|
||||
af->sample_rate, sample_format_to_string(af->format),
|
||||
af->reverse_endian ? REVERSE_ENDIAN_SUFFIX : "",
|
||||
af->channels);
|
||||
snprintf(s->buffer, sizeof(s->buffer), "%u:%s:%u",
|
||||
af.sample_rate, sample_format_to_string(af.format),
|
||||
af.channels);
|
||||
|
||||
return s->buffer;
|
||||
}
|
315
src/AudioFormat.hxx
Normal file
315
src/AudioFormat.hxx
Normal file
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_AUDIO_FORMAT_HXX
|
||||
#define MPD_AUDIO_FORMAT_HXX
|
||||
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(WIN32) && GCC_CHECK_VERSION(4,6)
|
||||
/* on WIN32, "FLOAT" is already defined, and this triggers -Wshadow */
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
|
||||
enum class SampleFormat : uint8_t {
|
||||
UNDEFINED = 0,
|
||||
|
||||
S8,
|
||||
S16,
|
||||
|
||||
/**
|
||||
* Signed 24 bit integer samples, packed in 32 bit integers
|
||||
* (the most significant byte is filled with the sign bit).
|
||||
*/
|
||||
S24_P32,
|
||||
|
||||
S32,
|
||||
|
||||
/**
|
||||
* 32 bit floating point samples in the host's format. The
|
||||
* range is -1.0f to +1.0f.
|
||||
*/
|
||||
FLOAT,
|
||||
|
||||
/**
|
||||
* Direct Stream Digital. 1-bit samples; each frame has one
|
||||
* byte (8 samples) per channel.
|
||||
*/
|
||||
DSD,
|
||||
};
|
||||
|
||||
#if defined(WIN32) && GCC_CHECK_VERSION(4,6)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
static constexpr unsigned MAX_CHANNELS = 8;
|
||||
|
||||
/**
|
||||
* This structure describes the format of a raw PCM stream.
|
||||
*/
|
||||
struct AudioFormat {
|
||||
/**
|
||||
* The sample rate in Hz. A better name for this attribute is
|
||||
* "frame rate", because technically, you have two samples per
|
||||
* frame in stereo sound.
|
||||
*/
|
||||
uint32_t sample_rate;
|
||||
|
||||
/**
|
||||
* The format samples are stored in. See the #sample_format
|
||||
* enum for valid values.
|
||||
*/
|
||||
SampleFormat format;
|
||||
|
||||
/**
|
||||
* The number of channels. Only mono (1) and stereo (2) are
|
||||
* fully supported currently.
|
||||
*/
|
||||
uint8_t channels;
|
||||
|
||||
AudioFormat() = default;
|
||||
|
||||
constexpr AudioFormat(uint32_t _sample_rate,
|
||||
SampleFormat _format, uint8_t _channels)
|
||||
:sample_rate(_sample_rate),
|
||||
format(_format), channels(_channels) {}
|
||||
|
||||
static constexpr AudioFormat Undefined() {
|
||||
return AudioFormat(0, SampleFormat::UNDEFINED,0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the #audio_format object, i.e. sets all attributes to an
|
||||
* undefined (invalid) value.
|
||||
*/
|
||||
void Clear() {
|
||||
sample_rate = 0;
|
||||
format = SampleFormat::UNDEFINED;
|
||||
channels = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the object has a defined value.
|
||||
*/
|
||||
constexpr bool IsDefined() const {
|
||||
return sample_rate != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the object is full, i.e. all attributes are
|
||||
* defined. This is more complete than IsDefined(), but
|
||||
* slower.
|
||||
*/
|
||||
constexpr bool IsFullyDefined() const {
|
||||
return sample_rate != 0 && format != SampleFormat::UNDEFINED &&
|
||||
channels != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the object has at least one defined value.
|
||||
*/
|
||||
constexpr bool IsMaskDefined() const {
|
||||
return sample_rate != 0 || format != SampleFormat::UNDEFINED ||
|
||||
channels != 0;
|
||||
}
|
||||
|
||||
bool IsValid() const;
|
||||
bool IsMaskValid() const;
|
||||
|
||||
constexpr bool operator==(const AudioFormat other) const {
|
||||
return sample_rate == other.sample_rate &&
|
||||
format == other.format &&
|
||||
channels == other.channels;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const AudioFormat other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
void ApplyMask(AudioFormat mask);
|
||||
|
||||
/**
|
||||
* Returns the size of each (mono) sample in bytes.
|
||||
*/
|
||||
unsigned GetSampleSize() const;
|
||||
|
||||
/**
|
||||
* Returns the size of each full frame in bytes.
|
||||
*/
|
||||
unsigned GetFrameSize() const;
|
||||
|
||||
/**
|
||||
* Returns the floating point factor which converts a time
|
||||
* span to a storage size in bytes.
|
||||
*/
|
||||
double GetTimeToSize() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Buffer for audio_format_string().
|
||||
*/
|
||||
struct audio_format_string {
|
||||
char buffer[24];
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether the sample rate is valid.
|
||||
*
|
||||
* @param sample_rate the sample rate in Hz
|
||||
*/
|
||||
static constexpr inline bool
|
||||
audio_valid_sample_rate(unsigned sample_rate)
|
||||
{
|
||||
return sample_rate > 0 && sample_rate < (1 << 30);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the sample format is valid.
|
||||
*
|
||||
* @param bits the number of significant bits per sample
|
||||
*/
|
||||
static inline bool
|
||||
audio_valid_sample_format(SampleFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case SampleFormat::S8:
|
||||
case SampleFormat::S16:
|
||||
case SampleFormat::S24_P32:
|
||||
case SampleFormat::S32:
|
||||
case SampleFormat::FLOAT:
|
||||
case SampleFormat::DSD:
|
||||
return true;
|
||||
|
||||
case SampleFormat::UNDEFINED:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the number of channels is valid.
|
||||
*/
|
||||
static constexpr inline bool
|
||||
audio_valid_channel_count(unsigned channels)
|
||||
{
|
||||
return channels >= 1 && channels <= MAX_CHANNELS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false if the format is not valid for playback with MPD.
|
||||
* This function performs some basic validity checks.
|
||||
*/
|
||||
inline bool
|
||||
AudioFormat::IsValid() const
|
||||
{
|
||||
return audio_valid_sample_rate(sample_rate) &&
|
||||
audio_valid_sample_format(format) &&
|
||||
audio_valid_channel_count(channels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false if the format mask is not valid for playback with
|
||||
* MPD. This function performs some basic validity checks.
|
||||
*/
|
||||
inline bool
|
||||
AudioFormat::IsMaskValid() const
|
||||
{
|
||||
return (sample_rate == 0 ||
|
||||
audio_valid_sample_rate(sample_rate)) &&
|
||||
(format == SampleFormat::UNDEFINED ||
|
||||
audio_valid_sample_format(format)) &&
|
||||
(channels == 0 || audio_valid_channel_count(channels));
|
||||
}
|
||||
|
||||
gcc_const
|
||||
static inline unsigned
|
||||
sample_format_size(SampleFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case SampleFormat::S8:
|
||||
return 1;
|
||||
|
||||
case SampleFormat::S16:
|
||||
return 2;
|
||||
|
||||
case SampleFormat::S24_P32:
|
||||
case SampleFormat::S32:
|
||||
case SampleFormat::FLOAT:
|
||||
return 4;
|
||||
|
||||
case SampleFormat::DSD:
|
||||
/* each frame has 8 samples per channel */
|
||||
return 1;
|
||||
|
||||
case SampleFormat::UNDEFINED:
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(false);
|
||||
gcc_unreachable();
|
||||
}
|
||||
|
||||
inline unsigned
|
||||
AudioFormat::GetSampleSize() const
|
||||
{
|
||||
return sample_format_size(format);
|
||||
}
|
||||
|
||||
inline unsigned
|
||||
AudioFormat::GetFrameSize() const
|
||||
{
|
||||
return GetSampleSize() * channels;
|
||||
}
|
||||
|
||||
inline double
|
||||
AudioFormat::GetTimeToSize() const
|
||||
{
|
||||
return sample_rate * GetFrameSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a #sample_format enum into a string, e.g. for printing it
|
||||
* in a log file.
|
||||
*
|
||||
* @param format a #sample_format enum value
|
||||
* @return the string
|
||||
*/
|
||||
gcc_pure gcc_malloc
|
||||
const char *
|
||||
sample_format_to_string(SampleFormat format);
|
||||
|
||||
/**
|
||||
* Renders the #audio_format object into a string, e.g. for printing
|
||||
* it in a log file.
|
||||
*
|
||||
* @param af the #audio_format object
|
||||
* @param s a buffer to print into
|
||||
* @return the string, or nullptr if the #audio_format object is invalid
|
||||
*/
|
||||
gcc_pure gcc_malloc
|
||||
const char *
|
||||
audio_format_to_string(AudioFormat af,
|
||||
struct audio_format_string *s);
|
||||
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -23,26 +23,19 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "audio_parser.h"
|
||||
#include "audio_format.h"
|
||||
#include "audio_check.h"
|
||||
#include "AudioParser.hxx"
|
||||
#include "AudioFormat.hxx"
|
||||
#include "CheckAudioFormat.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* The GLib quark used for errors reported by this library.
|
||||
*/
|
||||
static inline GQuark
|
||||
audio_parser_quark(void)
|
||||
{
|
||||
return g_quark_from_static_string("audio_parser");
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_sample_rate(const char *src, bool mask, uint32_t *sample_rate_r,
|
||||
const char **endptr_r, GError **error_r)
|
||||
const char **endptr_r, Error &error)
|
||||
{
|
||||
unsigned long value;
|
||||
char *endptr;
|
||||
@@ -55,10 +48,10 @@ parse_sample_rate(const char *src, bool mask, uint32_t *sample_rate_r,
|
||||
|
||||
value = strtoul(src, &endptr, 10);
|
||||
if (endptr == src) {
|
||||
g_set_error(error_r, audio_parser_quark(), 0,
|
||||
"Failed to parse the sample rate");
|
||||
error.Set(audio_format_domain,
|
||||
"Failed to parse the sample rate");
|
||||
return false;
|
||||
} else if (!audio_check_sample_rate(value, error_r))
|
||||
} else if (!audio_check_sample_rate(value, error))
|
||||
return false;
|
||||
|
||||
*sample_rate_r = value;
|
||||
@@ -68,50 +61,62 @@ parse_sample_rate(const char *src, bool mask, uint32_t *sample_rate_r,
|
||||
|
||||
static bool
|
||||
parse_sample_format(const char *src, bool mask,
|
||||
enum sample_format *sample_format_r,
|
||||
const char **endptr_r, GError **error_r)
|
||||
SampleFormat *sample_format_r,
|
||||
const char **endptr_r, Error &error)
|
||||
{
|
||||
unsigned long value;
|
||||
char *endptr;
|
||||
enum sample_format sample_format;
|
||||
SampleFormat sample_format;
|
||||
|
||||
if (mask && *src == '*') {
|
||||
*sample_format_r = SAMPLE_FORMAT_UNDEFINED;
|
||||
*sample_format_r = SampleFormat::UNDEFINED;
|
||||
*endptr_r = src + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (*src == 'f') {
|
||||
*sample_format_r = SampleFormat::FLOAT;
|
||||
*endptr_r = src + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (memcmp(src, "dsd", 3) == 0) {
|
||||
*sample_format_r = SampleFormat::DSD;
|
||||
*endptr_r = src + 3;
|
||||
return true;
|
||||
}
|
||||
|
||||
value = strtoul(src, &endptr, 10);
|
||||
if (endptr == src) {
|
||||
g_set_error(error_r, audio_parser_quark(), 0,
|
||||
"Failed to parse the sample format");
|
||||
error.Set(audio_format_domain,
|
||||
"Failed to parse the sample format");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (value) {
|
||||
case 8:
|
||||
sample_format = SAMPLE_FORMAT_S8;
|
||||
sample_format = SampleFormat::S8;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
sample_format = SAMPLE_FORMAT_S16;
|
||||
sample_format = SampleFormat::S16;
|
||||
break;
|
||||
|
||||
case 24:
|
||||
if (memcmp(endptr, "_3", 2) == 0) {
|
||||
sample_format = SAMPLE_FORMAT_S24;
|
||||
if (memcmp(endptr, "_3", 2) == 0)
|
||||
/* for backwards compatibility */
|
||||
endptr += 2;
|
||||
} else
|
||||
sample_format = SAMPLE_FORMAT_S24_P32;
|
||||
|
||||
sample_format = SampleFormat::S24_P32;
|
||||
break;
|
||||
|
||||
case 32:
|
||||
sample_format = SAMPLE_FORMAT_S32;
|
||||
sample_format = SampleFormat::S32;
|
||||
break;
|
||||
|
||||
default:
|
||||
g_set_error(error_r, audio_parser_quark(), 0,
|
||||
"Invalid sample format: %lu", value);
|
||||
error.Format(audio_format_domain,
|
||||
"Invalid sample format: %lu", value);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -124,7 +129,7 @@ parse_sample_format(const char *src, bool mask,
|
||||
|
||||
static bool
|
||||
parse_channel_count(const char *src, bool mask, uint8_t *channels_r,
|
||||
const char **endptr_r, GError **error_r)
|
||||
const char **endptr_r, Error &error)
|
||||
{
|
||||
unsigned long value;
|
||||
char *endptr;
|
||||
@@ -137,10 +142,10 @@ parse_channel_count(const char *src, bool mask, uint8_t *channels_r,
|
||||
|
||||
value = strtoul(src, &endptr, 10);
|
||||
if (endptr == src) {
|
||||
g_set_error(error_r, audio_parser_quark(), 0,
|
||||
"Failed to parse the channel count");
|
||||
error.Set(audio_format_domain,
|
||||
"Failed to parse the channel count");
|
||||
return false;
|
||||
} else if (!audio_check_channel_count(value, error_r))
|
||||
} else if (!audio_check_channel_count(value, error))
|
||||
return false;
|
||||
|
||||
*channels_r = value;
|
||||
@@ -149,51 +154,60 @@ parse_channel_count(const char *src, bool mask, uint8_t *channels_r,
|
||||
}
|
||||
|
||||
bool
|
||||
audio_format_parse(struct audio_format *dest, const char *src,
|
||||
bool mask, GError **error_r)
|
||||
audio_format_parse(AudioFormat &dest, const char *src,
|
||||
bool mask, Error &error)
|
||||
{
|
||||
uint32_t rate;
|
||||
enum sample_format sample_format;
|
||||
SampleFormat sample_format;
|
||||
uint8_t channels;
|
||||
|
||||
audio_format_clear(dest);
|
||||
dest.Clear();
|
||||
|
||||
/* parse sample rate */
|
||||
|
||||
if (!parse_sample_rate(src, mask, &rate, &src, error_r))
|
||||
#if GCC_CHECK_VERSION(4,7)
|
||||
/* workaround -Wmaybe-uninitialized false positive */
|
||||
rate = 0;
|
||||
#endif
|
||||
|
||||
if (!parse_sample_rate(src, mask, &rate, &src, error))
|
||||
return false;
|
||||
|
||||
if (*src++ != ':') {
|
||||
g_set_error(error_r, audio_parser_quark(), 0,
|
||||
"Sample format missing");
|
||||
error.Set(audio_format_domain, "Sample format missing");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* parse sample format */
|
||||
|
||||
if (!parse_sample_format(src, mask, &sample_format, &src, error_r))
|
||||
#if GCC_CHECK_VERSION(4,7)
|
||||
/* workaround -Wmaybe-uninitialized false positive */
|
||||
sample_format = SampleFormat::UNDEFINED;
|
||||
#endif
|
||||
|
||||
if (!parse_sample_format(src, mask, &sample_format, &src, error))
|
||||
return false;
|
||||
|
||||
if (*src++ != ':') {
|
||||
g_set_error(error_r, audio_parser_quark(), 0,
|
||||
"Channel count missing");
|
||||
error.Set(audio_format_domain, "Channel count missing");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* parse channel count */
|
||||
|
||||
if (!parse_channel_count(src, mask, &channels, &src, error_r))
|
||||
if (!parse_channel_count(src, mask, &channels, &src, error))
|
||||
return false;
|
||||
|
||||
if (*src != 0) {
|
||||
g_set_error(error_r, audio_parser_quark(), 0,
|
||||
error.Format(audio_format_domain,
|
||||
"Extra data after channel count: %s", src);
|
||||
return false;
|
||||
}
|
||||
|
||||
audio_format_init(dest, rate, sample_format, channels);
|
||||
assert(mask ? audio_format_mask_valid(dest)
|
||||
: audio_format_valid(dest));
|
||||
dest = AudioFormat(rate, sample_format, channels);
|
||||
assert(mask
|
||||
? dest.IsMaskValid()
|
||||
: dest.IsValid());
|
||||
|
||||
return true;
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -22,14 +22,11 @@
|
||||
* Parser functions for audio related objects.
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_PARSER_H
|
||||
#define AUDIO_PARSER_H
|
||||
#ifndef MPD_AUDIO_PARSER_HXX
|
||||
#define MPD_AUDIO_PARSER_HXX
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct audio_format;
|
||||
struct AudioFormat;
|
||||
class Error;
|
||||
|
||||
/**
|
||||
* Parses a string in the form "SAMPLE_RATE:BITS:CHANNELS" into an
|
||||
@@ -38,12 +35,12 @@ struct audio_format;
|
||||
* @param dest the destination #audio_format struct
|
||||
* @param src the input string
|
||||
* @param mask if true, then "*" is allowed for any number of items
|
||||
* @param error_r location to store the error occuring, or NULL to
|
||||
* @param error_r location to store the error occurring, or NULL to
|
||||
* ignore errors
|
||||
* @return true on success
|
||||
*/
|
||||
bool
|
||||
audio_format_parse(struct audio_format *dest, const char *src,
|
||||
bool mask, GError **error_r);
|
||||
audio_format_parse(AudioFormat &dest, const char *src,
|
||||
bool mask, Error &error);
|
||||
|
||||
#endif
|
151
src/AvahiPoll.cxx
Normal file
151
src/AvahiPoll.cxx
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "AvahiPoll.hxx"
|
||||
#include "event/Loop.hxx"
|
||||
#include "event/SocketMonitor.hxx"
|
||||
#include "event/TimeoutMonitor.hxx"
|
||||
|
||||
static unsigned
|
||||
FromAvahiWatchEvent(AvahiWatchEvent e)
|
||||
{
|
||||
return (e & AVAHI_WATCH_IN ? SocketMonitor::READ : 0) |
|
||||
(e & AVAHI_WATCH_OUT ? SocketMonitor::WRITE : 0) |
|
||||
(e & AVAHI_WATCH_ERR ? SocketMonitor::ERROR : 0) |
|
||||
(e & AVAHI_WATCH_HUP ? SocketMonitor::HANGUP : 0);
|
||||
}
|
||||
|
||||
static AvahiWatchEvent
|
||||
ToAvahiWatchEvent(unsigned e)
|
||||
{
|
||||
return AvahiWatchEvent((e & SocketMonitor::READ ? AVAHI_WATCH_IN : 0) |
|
||||
(e & SocketMonitor::WRITE ? AVAHI_WATCH_OUT : 0) |
|
||||
(e & SocketMonitor::ERROR ? AVAHI_WATCH_ERR : 0) |
|
||||
(e & SocketMonitor::HANGUP ? AVAHI_WATCH_HUP : 0));
|
||||
}
|
||||
|
||||
struct AvahiWatch final : private SocketMonitor {
|
||||
private:
|
||||
const AvahiWatchCallback callback;
|
||||
void *const userdata;
|
||||
|
||||
AvahiWatchEvent received;
|
||||
|
||||
public:
|
||||
AvahiWatch(int _fd, AvahiWatchEvent _event,
|
||||
AvahiWatchCallback _callback, void *_userdata,
|
||||
EventLoop &_loop)
|
||||
:SocketMonitor(_fd, _loop),
|
||||
callback(_callback), userdata(_userdata),
|
||||
received(AvahiWatchEvent(0)) {
|
||||
Schedule(FromAvahiWatchEvent(_event));
|
||||
}
|
||||
|
||||
~AvahiWatch() {
|
||||
Steal();
|
||||
}
|
||||
|
||||
static void WatchUpdate(AvahiWatch *w, AvahiWatchEvent event) {
|
||||
w->Schedule(FromAvahiWatchEvent(event));
|
||||
}
|
||||
|
||||
static AvahiWatchEvent WatchGetEvents(AvahiWatch *w) {
|
||||
return w->received;
|
||||
}
|
||||
|
||||
static void WatchFree(AvahiWatch *w) {
|
||||
delete w;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool OnSocketReady(unsigned flags) {
|
||||
received = ToAvahiWatchEvent(flags);
|
||||
callback(this, Get(), received, userdata);
|
||||
received = AvahiWatchEvent(0);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr unsigned
|
||||
TimevalToMS(const timeval &tv)
|
||||
{
|
||||
return tv.tv_sec * 1000 + (tv.tv_usec + 500) / 1000;
|
||||
}
|
||||
|
||||
struct AvahiTimeout final : private TimeoutMonitor {
|
||||
private:
|
||||
const AvahiTimeoutCallback callback;
|
||||
void *const userdata;
|
||||
|
||||
public:
|
||||
AvahiTimeout(const struct timeval *tv,
|
||||
AvahiTimeoutCallback _callback, void *_userdata,
|
||||
EventLoop &_loop)
|
||||
:TimeoutMonitor(_loop),
|
||||
callback(_callback), userdata(_userdata) {
|
||||
if (tv != nullptr)
|
||||
Schedule(TimevalToMS(*tv));
|
||||
}
|
||||
|
||||
static void TimeoutUpdate(AvahiTimeout *t, const struct timeval *tv) {
|
||||
if (tv != nullptr)
|
||||
t->Schedule(TimevalToMS(*tv));
|
||||
else
|
||||
t->Cancel();
|
||||
}
|
||||
|
||||
static void TimeoutFree(AvahiTimeout *t) {
|
||||
delete t;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void OnTimeout() {
|
||||
callback(this, userdata);
|
||||
}
|
||||
};
|
||||
|
||||
MyAvahiPoll::MyAvahiPoll(EventLoop &_loop):event_loop(_loop)
|
||||
{
|
||||
watch_new = WatchNew;
|
||||
watch_update = AvahiWatch::WatchUpdate;
|
||||
watch_get_events = AvahiWatch::WatchGetEvents;
|
||||
watch_free = AvahiWatch::WatchFree;
|
||||
timeout_new = TimeoutNew;
|
||||
timeout_update = AvahiTimeout::TimeoutUpdate;
|
||||
timeout_free = AvahiTimeout::TimeoutFree;
|
||||
}
|
||||
|
||||
AvahiWatch *
|
||||
MyAvahiPoll::WatchNew(const AvahiPoll *api, int fd, AvahiWatchEvent event,
|
||||
AvahiWatchCallback callback, void *userdata) {
|
||||
const MyAvahiPoll &poll = *(const MyAvahiPoll *)api;
|
||||
|
||||
return new AvahiWatch(fd, event, callback, userdata,
|
||||
poll.event_loop);
|
||||
}
|
||||
|
||||
AvahiTimeout *
|
||||
MyAvahiPoll::TimeoutNew(const AvahiPoll *api, const struct timeval *tv,
|
||||
AvahiTimeoutCallback callback, void *userdata) {
|
||||
const MyAvahiPoll &poll = *(const MyAvahiPoll *)api;
|
||||
|
||||
return new AvahiTimeout(tv, callback, userdata,
|
||||
poll.event_loop);
|
||||
}
|
48
src/AvahiPoll.hxx
Normal file
48
src/AvahiPoll.hxx
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_AVAHI_POLL_HXX
|
||||
#define MPD_AVAHI_POLL_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <avahi-common/watch.h>
|
||||
|
||||
class EventLoop;
|
||||
|
||||
class MyAvahiPoll final : public AvahiPoll {
|
||||
EventLoop &event_loop;
|
||||
|
||||
public:
|
||||
MyAvahiPoll(EventLoop &_loop);
|
||||
|
||||
private:
|
||||
static AvahiWatch *WatchNew(const AvahiPoll *api, int fd,
|
||||
AvahiWatchEvent event,
|
||||
AvahiWatchCallback callback,
|
||||
void *userdata);
|
||||
|
||||
static AvahiTimeout *TimeoutNew(const AvahiPoll *api,
|
||||
const struct timeval *tv,
|
||||
AvahiTimeoutCallback callback,
|
||||
void *userdata);
|
||||
};
|
||||
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,17 +17,22 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "audio_check.h"
|
||||
#include "audio_format.h"
|
||||
#include "config.h"
|
||||
#include "CheckAudioFormat.hxx"
|
||||
#include "AudioFormat.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
const Domain audio_format_domain("audio_format");
|
||||
|
||||
bool
|
||||
audio_check_sample_rate(unsigned long sample_rate, GError **error_r)
|
||||
audio_check_sample_rate(unsigned long sample_rate, Error &error)
|
||||
{
|
||||
if (!audio_valid_sample_rate(sample_rate)) {
|
||||
g_set_error(error_r, audio_format_quark(), 0,
|
||||
"Invalid sample rate: %lu", sample_rate);
|
||||
error.Format(audio_format_domain,
|
||||
"Invalid sample rate: %lu", sample_rate);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -35,11 +40,12 @@ audio_check_sample_rate(unsigned long sample_rate, GError **error_r)
|
||||
}
|
||||
|
||||
bool
|
||||
audio_check_sample_format(enum sample_format sample_format, GError **error_r)
|
||||
audio_check_sample_format(SampleFormat sample_format, Error &error)
|
||||
{
|
||||
if (!audio_valid_sample_format(sample_format)) {
|
||||
g_set_error(error_r, audio_format_quark(), 0,
|
||||
"Invalid sample format: %u", sample_format);
|
||||
error.Format(audio_format_domain,
|
||||
"Invalid sample format: %u",
|
||||
unsigned(sample_format));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -47,11 +53,11 @@ audio_check_sample_format(enum sample_format sample_format, GError **error_r)
|
||||
}
|
||||
|
||||
bool
|
||||
audio_check_channel_count(unsigned channels, GError **error_r)
|
||||
audio_check_channel_count(unsigned channels, Error &error)
|
||||
{
|
||||
if (!audio_valid_channel_count(channels)) {
|
||||
g_set_error(error_r, audio_format_quark(), 0,
|
||||
"Invalid channel count: %u", channels);
|
||||
error.Format(audio_format_domain,
|
||||
"Invalid channel count: %u", channels);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -59,15 +65,15 @@ audio_check_channel_count(unsigned channels, GError **error_r)
|
||||
}
|
||||
|
||||
bool
|
||||
audio_format_init_checked(struct audio_format *af, unsigned long sample_rate,
|
||||
enum sample_format sample_format, unsigned channels,
|
||||
GError **error_r)
|
||||
audio_format_init_checked(AudioFormat &af, unsigned long sample_rate,
|
||||
SampleFormat sample_format, unsigned channels,
|
||||
Error &error)
|
||||
{
|
||||
if (audio_check_sample_rate(sample_rate, error_r) &&
|
||||
audio_check_sample_format(sample_format, error_r) &&
|
||||
audio_check_channel_count(channels, error_r)) {
|
||||
audio_format_init(af, sample_rate, sample_format, channels);
|
||||
assert(audio_format_valid(af));
|
||||
if (audio_check_sample_rate(sample_rate, error) &&
|
||||
audio_check_sample_format(sample_format, error) &&
|
||||
audio_check_channel_count(channels, error)) {
|
||||
af = AudioFormat(sample_rate, sample_format, channels);
|
||||
assert(af.IsValid());
|
||||
return true;
|
||||
} else
|
||||
return false;
|
46
src/CheckAudioFormat.hxx
Normal file
46
src/CheckAudioFormat.hxx
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CHECK_AUDIO_FORMAT_HXX
|
||||
#define MPD_CHECK_AUDIO_FORMAT_HXX
|
||||
|
||||
#include "AudioFormat.hxx"
|
||||
|
||||
class Error;
|
||||
|
||||
extern const class Domain audio_format_domain;
|
||||
|
||||
bool
|
||||
audio_check_sample_rate(unsigned long sample_rate, Error &error);
|
||||
|
||||
bool
|
||||
audio_check_sample_format(SampleFormat sample_format, Error &error);
|
||||
|
||||
bool
|
||||
audio_check_channel_count(unsigned sample_format, Error &error);
|
||||
|
||||
/**
|
||||
* Wrapper for audio_format_init(), which checks all attributes.
|
||||
*/
|
||||
bool
|
||||
audio_format_init_checked(AudioFormat &af, unsigned long sample_rate,
|
||||
SampleFormat sample_format, unsigned channels,
|
||||
Error &error);
|
||||
|
||||
#endif
|
24
src/Client.cxx
Normal file
24
src/Client.cxx
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ClientInternal.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
|
||||
const Domain client_domain("client");
|
187
src/Client.hxx
Normal file
187
src/Client.hxx
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CLIENT_H
|
||||
#define MPD_CLIENT_H
|
||||
|
||||
#include "check.h"
|
||||
#include "ClientMessage.hxx"
|
||||
#include "command/CommandListBuilder.hxx"
|
||||
#include "event/FullyBufferedSocket.hxx"
|
||||
#include "event/TimeoutMonitor.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
struct sockaddr;
|
||||
class EventLoop;
|
||||
struct Partition;
|
||||
|
||||
class Client final : private FullyBufferedSocket, TimeoutMonitor {
|
||||
public:
|
||||
Partition &partition;
|
||||
struct playlist &playlist;
|
||||
struct PlayerControl &player_control;
|
||||
|
||||
unsigned permission;
|
||||
|
||||
/** the uid of the client process, or -1 if unknown */
|
||||
int uid;
|
||||
|
||||
CommandListBuilder cmd_list;
|
||||
|
||||
unsigned int num; /* client number */
|
||||
|
||||
/** is this client waiting for an "idle" response? */
|
||||
bool idle_waiting;
|
||||
|
||||
/** idle flags pending on this client, to be sent as soon as
|
||||
the client enters "idle" */
|
||||
unsigned idle_flags;
|
||||
|
||||
/** idle flags that the client wants to receive */
|
||||
unsigned idle_subscriptions;
|
||||
|
||||
/**
|
||||
* A list of channel names this client is subscribed to.
|
||||
*/
|
||||
std::set<std::string> subscriptions;
|
||||
|
||||
/**
|
||||
* The number of subscriptions in #subscriptions. Used to
|
||||
* limit the number of subscriptions.
|
||||
*/
|
||||
unsigned num_subscriptions;
|
||||
|
||||
/**
|
||||
* A list of messages this client has received.
|
||||
*/
|
||||
std::list<ClientMessage> messages;
|
||||
|
||||
Client(EventLoop &loop, Partition &partition,
|
||||
int fd, int uid, int num);
|
||||
|
||||
bool IsConnected() const {
|
||||
return FullyBufferedSocket::IsDefined();
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
bool IsExpired() const {
|
||||
return !FullyBufferedSocket::IsDefined();
|
||||
}
|
||||
|
||||
void Close();
|
||||
void SetExpired();
|
||||
|
||||
using FullyBufferedSocket::Write;
|
||||
|
||||
/**
|
||||
* returns the uid of the client process, or a negative value
|
||||
* if the uid is unknown
|
||||
*/
|
||||
int GetUID() const {
|
||||
return uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this client running on the same machine, connected with
|
||||
* a local (UNIX domain) socket?
|
||||
*/
|
||||
bool IsLocal() const {
|
||||
return uid > 0;
|
||||
}
|
||||
|
||||
unsigned GetPermission() const {
|
||||
return permission;
|
||||
}
|
||||
|
||||
void SetPermission(unsigned _permission) {
|
||||
permission = _permission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send "idle" response to this client.
|
||||
*/
|
||||
void IdleNotify();
|
||||
void IdleAdd(unsigned flags);
|
||||
bool IdleWait(unsigned flags);
|
||||
|
||||
enum class SubscribeResult {
|
||||
/** success */
|
||||
OK,
|
||||
|
||||
/** invalid channel name */
|
||||
INVALID,
|
||||
|
||||
/** already subscribed to this channel */
|
||||
ALREADY,
|
||||
|
||||
/** too many subscriptions */
|
||||
FULL,
|
||||
};
|
||||
|
||||
gcc_pure
|
||||
bool IsSubscribed(const char *channel_name) const {
|
||||
return subscriptions.find(channel_name) != subscriptions.end();
|
||||
}
|
||||
|
||||
SubscribeResult Subscribe(const char *channel);
|
||||
bool Unsubscribe(const char *channel);
|
||||
void UnsubscribeAll();
|
||||
bool PushMessage(const ClientMessage &msg);
|
||||
|
||||
private:
|
||||
/* virtual methods from class BufferedSocket */
|
||||
virtual InputResult OnSocketInput(void *data, size_t length) override;
|
||||
virtual void OnSocketError(Error &&error) override;
|
||||
virtual void OnSocketClosed() override;
|
||||
|
||||
/* virtual methods from class TimeoutMonitor */
|
||||
virtual void OnTimeout() override;
|
||||
};
|
||||
|
||||
void client_manager_init(void);
|
||||
|
||||
void
|
||||
client_new(EventLoop &loop, Partition &partition,
|
||||
int fd, const struct sockaddr *sa, size_t sa_length, int uid);
|
||||
|
||||
/**
|
||||
* Write a C string to the client.
|
||||
*/
|
||||
void client_puts(Client &client, const char *s);
|
||||
|
||||
/**
|
||||
* Write a printf-like formatted string to the client.
|
||||
*/
|
||||
void client_vprintf(Client &client, const char *fmt, va_list args);
|
||||
|
||||
/**
|
||||
* Write a printf-like formatted string to the client.
|
||||
*/
|
||||
gcc_printf(2,3)
|
||||
void
|
||||
client_printf(Client &client, const char *fmt, ...);
|
||||
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -18,16 +18,20 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "mixer_api.h"
|
||||
|
||||
#undef G_LOG_DOMAIN
|
||||
#define G_LOG_DOMAIN "mixer"
|
||||
#include "ClientInternal.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
void
|
||||
mixer_init(struct mixer *mixer, const struct mixer_plugin *plugin)
|
||||
Client::OnSocketError(Error &&error)
|
||||
{
|
||||
mixer->plugin = plugin;
|
||||
mixer->mutex = g_mutex_new();
|
||||
mixer->open = false;
|
||||
mixer->failed = false;
|
||||
FormatError(error, "error on client %d", num);
|
||||
|
||||
SetExpired();
|
||||
}
|
||||
|
||||
void
|
||||
Client::OnSocketClosed()
|
||||
{
|
||||
SetExpired();
|
||||
}
|
43
src/ClientExpire.cxx
Normal file
43
src/ClientExpire.cxx
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ClientInternal.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
void
|
||||
Client::SetExpired()
|
||||
{
|
||||
if (IsExpired())
|
||||
return;
|
||||
|
||||
FullyBufferedSocket::Close();
|
||||
TimeoutMonitor::Schedule(0);
|
||||
}
|
||||
|
||||
void
|
||||
Client::OnTimeout()
|
||||
{
|
||||
if (!IsExpired()) {
|
||||
assert(!idle_waiting);
|
||||
FormatDebug(client_domain, "[%u] timeout", num);
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
70
src/ClientFile.cxx
Normal file
70
src/ClientFile.cxx
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ClientFile.hxx"
|
||||
#include "Client.hxx"
|
||||
#include "protocol/Ack.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
bool
|
||||
client_allow_file(const Client &client, Path path_fs, Error &error)
|
||||
{
|
||||
#ifdef WIN32
|
||||
(void)client;
|
||||
(void)path_fs;
|
||||
|
||||
error.Set(ack_domain, ACK_ERROR_PERMISSION, "Access denied");
|
||||
return false;
|
||||
#else
|
||||
const int uid = client.GetUID();
|
||||
if (uid >= 0 && (uid_t)uid == geteuid())
|
||||
/* always allow access if user runs his own MPD
|
||||
instance */
|
||||
return true;
|
||||
|
||||
if (uid <= 0) {
|
||||
/* unauthenticated client */
|
||||
error.Set(ack_domain, ACK_ERROR_PERMISSION, "Access denied");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (!StatFile(path_fs, st)) {
|
||||
error.SetErrno();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (st.st_uid != (uid_t)uid && (st.st_mode & 0444) != 0444) {
|
||||
/* client is not owner */
|
||||
error.Set(ack_domain, ACK_ERROR_PERMISSION, "Access denied");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
40
src/ClientFile.hxx
Normal file
40
src/ClientFile.hxx
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CLIENT_FILE_HXX
|
||||
#define MPD_CLIENT_FILE_HXX
|
||||
|
||||
class Client;
|
||||
class Path;
|
||||
class Error;
|
||||
|
||||
/**
|
||||
* Is this client allowed to use the specified local file?
|
||||
*
|
||||
* Note that this function is vulnerable to timing/symlink attacks.
|
||||
* We cannot fix this as long as there are plugins that open a file by
|
||||
* its name, and not by file descriptor / callbacks.
|
||||
*
|
||||
* @param path_fs the absolute path name in filesystem encoding
|
||||
* @return true if access is allowed
|
||||
*/
|
||||
bool
|
||||
client_allow_file(const Client &client, Path path_fs, Error &error);
|
||||
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -18,18 +18,14 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "client_internal.h"
|
||||
#include "conf.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include "ClientInternal.hxx"
|
||||
#include "ClientList.hxx"
|
||||
#include "ConfigGlobal.hxx"
|
||||
|
||||
#define CLIENT_TIMEOUT_DEFAULT (60)
|
||||
#define CLIENT_MAX_CONNECTIONS_DEFAULT (10)
|
||||
#define CLIENT_MAX_COMMAND_LIST_DEFAULT (2048*1024)
|
||||
#define CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT (8192*1024)
|
||||
|
||||
/* set this to zero to indicate we have no possible clients */
|
||||
unsigned int client_max_connections;
|
||||
int client_timeout;
|
||||
size_t client_max_command_list_size;
|
||||
size_t client_max_output_buffer_size;
|
||||
@@ -38,9 +34,6 @@ void client_manager_init(void)
|
||||
{
|
||||
client_timeout = config_get_positive(CONF_CONN_TIMEOUT,
|
||||
CLIENT_TIMEOUT_DEFAULT);
|
||||
client_max_connections =
|
||||
config_get_positive(CONF_MAX_CONN,
|
||||
CLIENT_MAX_CONNECTIONS_DEFAULT);
|
||||
client_max_command_list_size =
|
||||
config_get_positive(CONF_MAX_COMMAND_LIST_SIZE,
|
||||
CLIENT_MAX_COMMAND_LIST_DEFAULT / 1024)
|
||||
@@ -51,23 +44,3 @@ void client_manager_init(void)
|
||||
CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT / 1024)
|
||||
* 1024;
|
||||
}
|
||||
|
||||
static void client_close_all(void)
|
||||
{
|
||||
while (!client_list_is_empty()) {
|
||||
struct client *client = client_list_get_first();
|
||||
|
||||
client_close(client);
|
||||
}
|
||||
|
||||
assert(client_list_is_empty());
|
||||
}
|
||||
|
||||
void client_manager_deinit(void)
|
||||
{
|
||||
client_close_all();
|
||||
|
||||
client_max_connections = 0;
|
||||
|
||||
client_deinit_expire();
|
||||
}
|
75
src/ClientIdle.cxx
Normal file
75
src/ClientIdle.cxx
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ClientInternal.hxx"
|
||||
#include "Idle.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
void
|
||||
Client::IdleNotify()
|
||||
{
|
||||
assert(idle_waiting);
|
||||
assert(idle_flags != 0);
|
||||
|
||||
unsigned flags = idle_flags;
|
||||
idle_flags = 0;
|
||||
idle_waiting = false;
|
||||
|
||||
const char *const*idle_names = idle_get_names();
|
||||
for (unsigned i = 0; idle_names[i]; ++i) {
|
||||
if (flags & (1 << i) & idle_subscriptions)
|
||||
client_printf(*this, "changed: %s\n",
|
||||
idle_names[i]);
|
||||
}
|
||||
|
||||
client_puts(*this, "OK\n");
|
||||
|
||||
TimeoutMonitor::ScheduleSeconds(client_timeout);
|
||||
}
|
||||
|
||||
void
|
||||
Client::IdleAdd(unsigned flags)
|
||||
{
|
||||
if (IsExpired())
|
||||
return;
|
||||
|
||||
idle_flags |= flags;
|
||||
if (idle_waiting && (idle_flags & idle_subscriptions))
|
||||
IdleNotify();
|
||||
}
|
||||
|
||||
bool
|
||||
Client::IdleWait(unsigned flags)
|
||||
{
|
||||
assert(!idle_waiting);
|
||||
|
||||
idle_waiting = true;
|
||||
idle_subscriptions = flags;
|
||||
|
||||
if (idle_flags & idle_subscriptions) {
|
||||
IdleNotify();
|
||||
return true;
|
||||
} else {
|
||||
/* disable timeouts while in "idle" */
|
||||
TimeoutMonitor::Cancel();
|
||||
return false;
|
||||
}
|
||||
}
|
39
src/ClientInternal.hxx
Normal file
39
src/ClientInternal.hxx
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CLIENT_INTERNAL_HXX
|
||||
#define MPD_CLIENT_INTERNAL_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "Client.hxx"
|
||||
#include "command/CommandResult.hxx"
|
||||
|
||||
static constexpr unsigned CLIENT_MAX_SUBSCRIPTIONS = 16;
|
||||
static constexpr unsigned CLIENT_MAX_MESSAGES = 64;
|
||||
|
||||
extern const class Domain client_domain;
|
||||
|
||||
extern int client_timeout;
|
||||
extern size_t client_max_command_list_size;
|
||||
extern size_t client_max_output_buffer_size;
|
||||
|
||||
CommandResult
|
||||
client_process_line(Client &client, char *line);
|
||||
|
||||
#endif
|
56
src/ClientList.cxx
Normal file
56
src/ClientList.cxx
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ClientList.hxx"
|
||||
#include "ClientInternal.hxx"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
void
|
||||
ClientList::Remove(Client &client)
|
||||
{
|
||||
assert(size > 0);
|
||||
assert(!list.empty());
|
||||
|
||||
auto i = std::find(list.begin(), list.end(), &client);
|
||||
assert(i != list.end());
|
||||
list.erase(i);
|
||||
--size;
|
||||
}
|
||||
|
||||
void
|
||||
ClientList::CloseAll()
|
||||
{
|
||||
while (!list.empty())
|
||||
list.front()->Close();
|
||||
|
||||
assert(size == 0);
|
||||
}
|
||||
|
||||
void
|
||||
ClientList::IdleAdd(unsigned flags)
|
||||
{
|
||||
assert(flags != 0);
|
||||
|
||||
for (const auto &client : list)
|
||||
client->IdleAdd(flags);
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,38 +17,48 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_STATS_H
|
||||
#define MPD_STATS_H
|
||||
#ifndef MPD_CLIENT_LIST_HXX
|
||||
#define MPD_CLIENT_LIST_HXX
|
||||
|
||||
#include <glib.h>
|
||||
#include <list>
|
||||
|
||||
struct client;
|
||||
class Client;
|
||||
|
||||
struct stats {
|
||||
GTimer *timer;
|
||||
class ClientList {
|
||||
const unsigned max_size;
|
||||
|
||||
/** number of song files in the music directory */
|
||||
unsigned song_count;
|
||||
unsigned size;
|
||||
std::list<Client *> list;
|
||||
|
||||
/** sum of all song durations in the music directory (in
|
||||
seconds) */
|
||||
unsigned long song_duration;
|
||||
public:
|
||||
ClientList(unsigned _max_size)
|
||||
:max_size(_max_size), size(0) {}
|
||||
~ClientList() {
|
||||
CloseAll();
|
||||
}
|
||||
|
||||
/** number of distinct artist names in the music directory */
|
||||
unsigned artist_count;
|
||||
std::list<Client *>::iterator begin() {
|
||||
return list.begin();
|
||||
}
|
||||
|
||||
/** number of distinct album names in the music directory */
|
||||
unsigned album_count;
|
||||
std::list<Client *>::iterator end() {
|
||||
return list.end();
|
||||
}
|
||||
|
||||
bool IsFull() const {
|
||||
return size >= max_size;
|
||||
}
|
||||
|
||||
void Add(Client &client) {
|
||||
list.push_front(&client);
|
||||
++size;
|
||||
}
|
||||
|
||||
void Remove(Client &client);
|
||||
|
||||
void CloseAll();
|
||||
|
||||
void IdleAdd(unsigned flags);
|
||||
};
|
||||
|
||||
extern struct stats stats;
|
||||
|
||||
void stats_global_init(void);
|
||||
|
||||
void stats_global_finish(void);
|
||||
|
||||
void stats_update(void);
|
||||
|
||||
int stats_print(struct client *client);
|
||||
|
||||
#endif
|
41
src/ClientMessage.cxx
Normal file
41
src/ClientMessage.cxx
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "ClientMessage.hxx"
|
||||
#include "util/CharUtil.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
gcc_const
|
||||
static bool
|
||||
valid_channel_char(const char ch)
|
||||
{
|
||||
return IsAlphaNumericASCII(ch) ||
|
||||
ch == '_' || ch == '-' || ch == '.' || ch == ':';
|
||||
}
|
||||
|
||||
bool
|
||||
client_message_valid_channel_name(const char *name)
|
||||
{
|
||||
do {
|
||||
if (!valid_channel_char(*name))
|
||||
return false;
|
||||
} while (*++name != 0);
|
||||
|
||||
return true;
|
||||
}
|
52
src/ClientMessage.hxx
Normal file
52
src/ClientMessage.hxx
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CLIENT_MESSAGE_H
|
||||
#define MPD_CLIENT_MESSAGE_H
|
||||
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* A client-to-client message.
|
||||
*/
|
||||
class ClientMessage {
|
||||
std::string channel, message;
|
||||
|
||||
public:
|
||||
template<typename T, typename U>
|
||||
ClientMessage(T &&_channel, U &&_message)
|
||||
:channel(std::forward<T>(_channel)),
|
||||
message(std::forward<U>(_message)) {}
|
||||
|
||||
const char *GetChannel() const {
|
||||
return channel.c_str();
|
||||
}
|
||||
|
||||
const char *GetMessage() const {
|
||||
return message.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
gcc_pure
|
||||
bool
|
||||
client_message_valid_channel_name(const char *name);
|
||||
|
||||
#endif
|
126
src/ClientNew.cxx
Normal file
126
src/ClientNew.cxx
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ClientInternal.hxx"
|
||||
#include "ClientList.hxx"
|
||||
#include "Partition.hxx"
|
||||
#include "Instance.hxx"
|
||||
#include "system/fd_util.h"
|
||||
#include "system/Resolver.hxx"
|
||||
#include "Permission.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef HAVE_LIBWRAP
|
||||
#include <tcpd.h>
|
||||
#endif
|
||||
|
||||
static const char GREETING[] = "OK MPD " PROTOCOL_VERSION "\n";
|
||||
|
||||
Client::Client(EventLoop &_loop, Partition &_partition,
|
||||
int _fd, int _uid, int _num)
|
||||
:FullyBufferedSocket(_fd, _loop, 16384, client_max_output_buffer_size),
|
||||
TimeoutMonitor(_loop),
|
||||
partition(_partition),
|
||||
playlist(partition.playlist), player_control(partition.pc),
|
||||
permission(getDefaultPermissions()),
|
||||
uid(_uid),
|
||||
num(_num),
|
||||
idle_waiting(false), idle_flags(0),
|
||||
num_subscriptions(0)
|
||||
{
|
||||
TimeoutMonitor::ScheduleSeconds(client_timeout);
|
||||
}
|
||||
|
||||
void
|
||||
client_new(EventLoop &loop, Partition &partition,
|
||||
int fd, const struct sockaddr *sa, size_t sa_length, int uid)
|
||||
{
|
||||
static unsigned int next_client_num;
|
||||
char *remote;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
#ifdef HAVE_LIBWRAP
|
||||
if (sa->sa_family != AF_UNIX) {
|
||||
char *hostaddr = sockaddr_to_string(sa, sa_length,
|
||||
IgnoreError());
|
||||
const char *progname = g_get_prgname();
|
||||
|
||||
struct request_info req;
|
||||
request_init(&req, RQ_FILE, fd, RQ_DAEMON, progname, 0);
|
||||
|
||||
fromhost(&req);
|
||||
|
||||
if (!hosts_access(&req)) {
|
||||
/* tcp wrappers says no */
|
||||
FormatWarning(client_domain,
|
||||
"libwrap refused connection (libwrap=%s) from %s",
|
||||
progname, hostaddr);
|
||||
|
||||
g_free(hostaddr);
|
||||
close_socket(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
g_free(hostaddr);
|
||||
}
|
||||
#endif /* HAVE_WRAP */
|
||||
|
||||
ClientList &client_list = *partition.instance.client_list;
|
||||
if (client_list.IsFull()) {
|
||||
LogWarning(client_domain, "Max connections reached");
|
||||
close_socket(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
Client *client = new Client(loop, partition, fd, uid,
|
||||
next_client_num++);
|
||||
|
||||
(void)send(fd, GREETING, sizeof(GREETING) - 1, 0);
|
||||
|
||||
client_list.Add(*client);
|
||||
|
||||
remote = sockaddr_to_string(sa, sa_length, IgnoreError());
|
||||
FormatInfo(client_domain, "[%u] opened from %s", client->num, remote);
|
||||
g_free(remote);
|
||||
}
|
||||
|
||||
void
|
||||
Client::Close()
|
||||
{
|
||||
partition.instance.client_list->Remove(*this);
|
||||
|
||||
SetExpired();
|
||||
|
||||
FormatInfo(client_domain, "[%u] closed", num);
|
||||
delete this;
|
||||
}
|
141
src/ClientProcess.cxx
Normal file
141
src/ClientProcess.cxx
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ClientInternal.hxx"
|
||||
#include "protocol/Result.hxx"
|
||||
#include "command/AllCommands.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define CLIENT_LIST_MODE_BEGIN "command_list_begin"
|
||||
#define CLIENT_LIST_OK_MODE_BEGIN "command_list_ok_begin"
|
||||
#define CLIENT_LIST_MODE_END "command_list_end"
|
||||
|
||||
static CommandResult
|
||||
client_process_command_list(Client &client, bool list_ok,
|
||||
std::list<std::string> &&list)
|
||||
{
|
||||
CommandResult ret = CommandResult::OK;
|
||||
unsigned num = 0;
|
||||
|
||||
for (auto &&i : list) {
|
||||
char *cmd = &*i.begin();
|
||||
|
||||
FormatDebug(client_domain, "process command \"%s\"", cmd);
|
||||
ret = command_process(client, num++, cmd);
|
||||
FormatDebug(client_domain, "command returned %i", ret);
|
||||
if (ret != CommandResult::OK || client.IsExpired())
|
||||
break;
|
||||
else if (list_ok)
|
||||
client_puts(client, "list_OK\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
CommandResult
|
||||
client_process_line(Client &client, char *line)
|
||||
{
|
||||
CommandResult ret;
|
||||
|
||||
if (strcmp(line, "noidle") == 0) {
|
||||
if (client.idle_waiting) {
|
||||
/* send empty idle response and leave idle mode */
|
||||
client.idle_waiting = false;
|
||||
command_success(client);
|
||||
}
|
||||
|
||||
/* do nothing if the client wasn't idling: the client
|
||||
has already received the full idle response from
|
||||
client_idle_notify(), which he can now evaluate */
|
||||
|
||||
return CommandResult::OK;
|
||||
} else if (client.idle_waiting) {
|
||||
/* during idle mode, clients must not send anything
|
||||
except "noidle" */
|
||||
FormatWarning(client_domain,
|
||||
"[%u] command \"%s\" during idle",
|
||||
client.num, line);
|
||||
return CommandResult::CLOSE;
|
||||
}
|
||||
|
||||
if (client.cmd_list.IsActive()) {
|
||||
if (strcmp(line, CLIENT_LIST_MODE_END) == 0) {
|
||||
FormatDebug(client_domain,
|
||||
"[%u] process command list",
|
||||
client.num);
|
||||
|
||||
auto &&cmd_list = client.cmd_list.Commit();
|
||||
|
||||
ret = client_process_command_list(client,
|
||||
client.cmd_list.IsOKMode(),
|
||||
std::move(cmd_list));
|
||||
FormatDebug(client_domain,
|
||||
"[%u] process command "
|
||||
"list returned %i", client.num, ret);
|
||||
|
||||
if (ret == CommandResult::CLOSE ||
|
||||
client.IsExpired())
|
||||
return CommandResult::CLOSE;
|
||||
|
||||
if (ret == CommandResult::OK)
|
||||
command_success(client);
|
||||
|
||||
client.cmd_list.Reset();
|
||||
} else {
|
||||
if (!client.cmd_list.Add(line)) {
|
||||
FormatWarning(client_domain,
|
||||
"[%u] command list size "
|
||||
"is larger than the max (%lu)",
|
||||
client.num,
|
||||
(unsigned long)client_max_command_list_size);
|
||||
return CommandResult::CLOSE;
|
||||
}
|
||||
|
||||
ret = CommandResult::OK;
|
||||
}
|
||||
} else {
|
||||
if (strcmp(line, CLIENT_LIST_MODE_BEGIN) == 0) {
|
||||
client.cmd_list.Begin(false);
|
||||
ret = CommandResult::OK;
|
||||
} else if (strcmp(line, CLIENT_LIST_OK_MODE_BEGIN) == 0) {
|
||||
client.cmd_list.Begin(true);
|
||||
ret = CommandResult::OK;
|
||||
} else {
|
||||
FormatDebug(client_domain,
|
||||
"[%u] process command \"%s\"",
|
||||
client.num, line);
|
||||
ret = command_process(client, 0, line);
|
||||
FormatDebug(client_domain,
|
||||
"[%u] command returned %i",
|
||||
client.num, ret);
|
||||
|
||||
if (ret == CommandResult::CLOSE ||
|
||||
client.IsExpired())
|
||||
return CommandResult::CLOSE;
|
||||
|
||||
if (ret == CommandResult::OK)
|
||||
command_success(client);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
76
src/ClientRead.cxx
Normal file
76
src/ClientRead.cxx
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ClientInternal.hxx"
|
||||
#include "Main.hxx"
|
||||
#include "event/Loop.hxx"
|
||||
#include "util/CharUtil.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
BufferedSocket::InputResult
|
||||
Client::OnSocketInput(void *data, size_t length)
|
||||
{
|
||||
char *p = (char *)data;
|
||||
char *newline = (char *)memchr(p, '\n', length);
|
||||
if (newline == nullptr)
|
||||
return InputResult::MORE;
|
||||
|
||||
TimeoutMonitor::ScheduleSeconds(client_timeout);
|
||||
|
||||
BufferedSocket::ConsumeInput(newline + 1 - p);
|
||||
|
||||
/* skip whitespace at the end of the line */
|
||||
while (newline > p && IsWhitespaceOrNull(newline[-1]))
|
||||
--newline;
|
||||
|
||||
/* terminate the string at the end of the line */
|
||||
*newline = 0;
|
||||
|
||||
CommandResult result = client_process_line(*this, p);
|
||||
switch (result) {
|
||||
case CommandResult::OK:
|
||||
case CommandResult::IDLE:
|
||||
case CommandResult::ERROR:
|
||||
break;
|
||||
|
||||
case CommandResult::KILL:
|
||||
Close();
|
||||
main_loop->Break();
|
||||
return InputResult::CLOSED;
|
||||
|
||||
case CommandResult::FINISH:
|
||||
if (Flush())
|
||||
Close();
|
||||
return InputResult::CLOSED;
|
||||
|
||||
case CommandResult::CLOSE:
|
||||
Close();
|
||||
return InputResult::CLOSED;
|
||||
}
|
||||
|
||||
if (IsExpired()) {
|
||||
Close();
|
||||
return InputResult::CLOSED;
|
||||
}
|
||||
|
||||
return InputResult::AGAIN;
|
||||
}
|
92
src/ClientSubscribe.cxx
Normal file
92
src/ClientSubscribe.cxx
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ClientInternal.hxx"
|
||||
#include "Idle.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
bool Unsubscribe(const char *channel);
|
||||
void UnsubscribeAll();
|
||||
bool PushMessage(const ClientMessage &msg);
|
||||
|
||||
Client::SubscribeResult
|
||||
Client::Subscribe(const char *channel)
|
||||
{
|
||||
assert(channel != nullptr);
|
||||
|
||||
if (!client_message_valid_channel_name(channel))
|
||||
return Client::SubscribeResult::INVALID;
|
||||
|
||||
if (num_subscriptions >= CLIENT_MAX_SUBSCRIPTIONS)
|
||||
return Client::SubscribeResult::FULL;
|
||||
|
||||
auto r = subscriptions.insert(channel);
|
||||
if (!r.second)
|
||||
return Client::SubscribeResult::ALREADY;
|
||||
|
||||
++num_subscriptions;
|
||||
|
||||
idle_add(IDLE_SUBSCRIPTION);
|
||||
|
||||
return Client::SubscribeResult::OK;
|
||||
}
|
||||
|
||||
bool
|
||||
Client::Unsubscribe(const char *channel)
|
||||
{
|
||||
const auto i = subscriptions.find(channel);
|
||||
if (i == subscriptions.end())
|
||||
return false;
|
||||
|
||||
assert(num_subscriptions > 0);
|
||||
|
||||
subscriptions.erase(i);
|
||||
--num_subscriptions;
|
||||
|
||||
idle_add(IDLE_SUBSCRIPTION);
|
||||
|
||||
assert((num_subscriptions == 0) ==
|
||||
subscriptions.empty());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Client::UnsubscribeAll()
|
||||
{
|
||||
subscriptions.clear();
|
||||
num_subscriptions = 0;
|
||||
}
|
||||
|
||||
bool
|
||||
Client::PushMessage(const ClientMessage &msg)
|
||||
{
|
||||
if (messages.size() >= CLIENT_MAX_MESSAGES ||
|
||||
!IsSubscribed(msg.GetChannel()))
|
||||
return false;
|
||||
|
||||
if (messages.empty())
|
||||
IdleAdd(IDLE_MESSAGE);
|
||||
|
||||
messages.push_back(msg);
|
||||
return true;
|
||||
}
|
61
src/ClientWrite.cxx
Normal file
61
src/ClientWrite.cxx
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ClientInternal.hxx"
|
||||
#include "util/FormatString.hxx"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Write a block of data to the client.
|
||||
*/
|
||||
static void
|
||||
client_write(Client &client, const char *data, size_t length)
|
||||
{
|
||||
/* if the client is going to be closed, do nothing */
|
||||
if (client.IsExpired() || length == 0)
|
||||
return;
|
||||
|
||||
client.Write(data, length);
|
||||
}
|
||||
|
||||
void
|
||||
client_puts(Client &client, const char *s)
|
||||
{
|
||||
client_write(client, s, strlen(s));
|
||||
}
|
||||
|
||||
void
|
||||
client_vprintf(Client &client, const char *fmt, va_list args)
|
||||
{
|
||||
char *p = FormatNewV(fmt, args);
|
||||
client_write(client, p, strlen(p));
|
||||
delete[] p;
|
||||
}
|
||||
|
||||
void
|
||||
client_printf(Client &client, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
client_vprintf(client, fmt, args);
|
||||
va_end(args);
|
||||
}
|
253
src/CommandLine.cxx
Normal file
253
src/CommandLine.cxx
Normal file
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "CommandLine.hxx"
|
||||
#include "ls.hxx"
|
||||
#include "LogInit.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "ConfigGlobal.hxx"
|
||||
#include "DecoderList.hxx"
|
||||
#include "DecoderPlugin.hxx"
|
||||
#include "OutputList.hxx"
|
||||
#include "OutputPlugin.hxx"
|
||||
#include "InputRegistry.hxx"
|
||||
#include "InputPlugin.hxx"
|
||||
#include "PlaylistRegistry.hxx"
|
||||
#include "PlaylistPlugin.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/Traits.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "system/FatalError.hxx"
|
||||
|
||||
#ifdef ENABLE_ENCODER
|
||||
#include "EncoderList.hxx"
|
||||
#include "EncoderPlugin.hxx"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
#include "ArchiveList.hxx"
|
||||
#include "ArchivePlugin.hxx"
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#define CONFIG_FILE_LOCATION "\\mpd\\mpd.conf"
|
||||
#else
|
||||
#define USER_CONFIG_FILE_LOCATION1 ".mpdconf"
|
||||
#define USER_CONFIG_FILE_LOCATION2 ".mpd/mpd.conf"
|
||||
#define USER_CONFIG_FILE_LOCATION_XDG "mpd/mpd.conf"
|
||||
#endif
|
||||
|
||||
static constexpr Domain cmdline_domain("cmdline");
|
||||
|
||||
gcc_noreturn
|
||||
static void version(void)
|
||||
{
|
||||
puts("Music Player Daemon " VERSION "\n"
|
||||
"\n"
|
||||
"Copyright (C) 2003-2007 Warren Dukes <warren.dukes@gmail.com>\n"
|
||||
"Copyright (C) 2008-2013 Max Kellermann <max@duempel.org>\n"
|
||||
"This is free software; see the source for copying conditions. There is NO\n"
|
||||
"warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
|
||||
"\n"
|
||||
"Decoders plugins:");
|
||||
|
||||
decoder_plugins_for_each([](const DecoderPlugin &plugin){
|
||||
printf(" [%s]", plugin.name);
|
||||
|
||||
const char *const*suffixes = plugin.suffixes;
|
||||
if (suffixes != nullptr)
|
||||
for (; *suffixes != nullptr; ++suffixes)
|
||||
printf(" %s", *suffixes);
|
||||
|
||||
puts("");
|
||||
});
|
||||
|
||||
puts("\n"
|
||||
"Output plugins:");
|
||||
audio_output_plugins_for_each(plugin)
|
||||
printf(" %s", plugin->name);
|
||||
puts("");
|
||||
|
||||
#ifdef ENABLE_ENCODER
|
||||
puts("\n"
|
||||
"Encoder plugins:");
|
||||
encoder_plugins_for_each(plugin)
|
||||
printf(" %s", plugin->name);
|
||||
puts("");
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
puts("\n"
|
||||
"Archive plugins:");
|
||||
archive_plugins_for_each(plugin) {
|
||||
printf(" [%s]", plugin->name);
|
||||
|
||||
const char *const*suffixes = plugin->suffixes;
|
||||
if (suffixes != nullptr)
|
||||
for (; *suffixes != nullptr; ++suffixes)
|
||||
printf(" %s", *suffixes);
|
||||
|
||||
puts("");
|
||||
}
|
||||
#endif
|
||||
|
||||
puts("\n"
|
||||
"Input plugins:");
|
||||
input_plugins_for_each(plugin)
|
||||
printf(" %s", plugin->name);
|
||||
|
||||
puts("\n\n"
|
||||
"Playlist plugins:");
|
||||
playlist_plugins_for_each(plugin)
|
||||
printf(" %s", plugin->name);
|
||||
|
||||
puts("\n\n"
|
||||
"Protocols:");
|
||||
print_supported_uri_schemes_to_fp(stdout);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static const char *summary =
|
||||
"Music Player Daemon - a daemon for playing music.";
|
||||
|
||||
gcc_pure
|
||||
static AllocatedPath
|
||||
PathBuildChecked(const AllocatedPath &a, PathTraits::const_pointer b)
|
||||
{
|
||||
if (a.IsNull())
|
||||
return AllocatedPath::Null();
|
||||
|
||||
return AllocatedPath::Build(a, b);
|
||||
}
|
||||
|
||||
bool
|
||||
parse_cmdline(int argc, char **argv, struct options *options,
|
||||
Error &error)
|
||||
{
|
||||
GOptionContext *context;
|
||||
bool ret;
|
||||
static gboolean option_version,
|
||||
option_no_daemon,
|
||||
option_no_config;
|
||||
const GOptionEntry entries[] = {
|
||||
{ "kill", 0, 0, G_OPTION_ARG_NONE, &options->kill,
|
||||
"kill the currently running mpd session", nullptr },
|
||||
{ "no-config", 0, 0, G_OPTION_ARG_NONE, &option_no_config,
|
||||
"don't read from config", nullptr },
|
||||
{ "no-daemon", 0, 0, G_OPTION_ARG_NONE, &option_no_daemon,
|
||||
"don't detach from console", nullptr },
|
||||
{ "stdout", 0, 0, G_OPTION_ARG_NONE, &options->log_stderr,
|
||||
nullptr, nullptr },
|
||||
{ "stderr", 0, 0, G_OPTION_ARG_NONE, &options->log_stderr,
|
||||
"print messages to stderr", nullptr },
|
||||
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &options->verbose,
|
||||
"verbose logging", nullptr },
|
||||
{ "version", 'V', 0, G_OPTION_ARG_NONE, &option_version,
|
||||
"print version number", nullptr },
|
||||
{ nullptr, 0, 0, G_OPTION_ARG_NONE, nullptr, nullptr, nullptr }
|
||||
};
|
||||
|
||||
options->kill = false;
|
||||
options->daemon = true;
|
||||
options->log_stderr = false;
|
||||
options->verbose = false;
|
||||
|
||||
context = g_option_context_new("[path/to/mpd.conf]");
|
||||
g_option_context_add_main_entries(context, entries, nullptr);
|
||||
|
||||
g_option_context_set_summary(context, summary);
|
||||
|
||||
GError *gerror = nullptr;
|
||||
ret = g_option_context_parse(context, &argc, &argv, &gerror);
|
||||
g_option_context_free(context);
|
||||
|
||||
if (!ret)
|
||||
FatalError("option parsing failed", gerror);
|
||||
|
||||
if (option_version)
|
||||
version();
|
||||
|
||||
/* initialize the logging library, so the configuration file
|
||||
parser can use it already */
|
||||
log_early_init(options->verbose);
|
||||
|
||||
options->daemon = !option_no_daemon;
|
||||
|
||||
if (option_no_config) {
|
||||
LogDebug(cmdline_domain,
|
||||
"Ignoring config, using daemon defaults");
|
||||
return true;
|
||||
} else if (argc <= 1) {
|
||||
/* default configuration file path */
|
||||
|
||||
#ifdef WIN32
|
||||
AllocatedPath path = PathBuildChecked(AllocatedPath::FromUTF8(g_get_user_config_dir()),
|
||||
CONFIG_FILE_LOCATION);
|
||||
if (!path.IsNull() && FileExists(path))
|
||||
return ReadConfigFile(path, error);
|
||||
|
||||
const char *const*system_config_dirs =
|
||||
g_get_system_config_dirs();
|
||||
|
||||
for (unsigned i = 0; system_config_dirs[i] != nullptr; ++i) {
|
||||
path = PathBuildChecked(AllocatedPath::FromUTF8(system_config_dirs[i]),
|
||||
CONFIG_FILE_LOCATION);
|
||||
if (!path.IsNull() && FileExists(path))
|
||||
return ReadConfigFile(path, error);
|
||||
}
|
||||
#else
|
||||
AllocatedPath path = PathBuildChecked(AllocatedPath::FromUTF8(g_get_user_config_dir()),
|
||||
USER_CONFIG_FILE_LOCATION_XDG);
|
||||
if (!path.IsNull() && FileExists(path))
|
||||
return ReadConfigFile(path, error);
|
||||
|
||||
path = PathBuildChecked(AllocatedPath::FromUTF8(g_get_home_dir()),
|
||||
USER_CONFIG_FILE_LOCATION1);
|
||||
if (!path.IsNull() && FileExists(path))
|
||||
return ReadConfigFile(path, error);
|
||||
|
||||
path = PathBuildChecked(AllocatedPath::FromUTF8(g_get_home_dir()),
|
||||
USER_CONFIG_FILE_LOCATION2);
|
||||
if (!path.IsNull() && FileExists(path))
|
||||
return ReadConfigFile(path, error);
|
||||
|
||||
path = AllocatedPath::FromUTF8(SYSTEM_CONFIG_FILE_LOCATION);
|
||||
if (!path.IsNull() && FileExists(path))
|
||||
return ReadConfigFile(path, error);
|
||||
#endif
|
||||
|
||||
error.Set(cmdline_domain, "No configuration file found");
|
||||
return false;
|
||||
} else if (argc == 2) {
|
||||
/* specified configuration file */
|
||||
return ReadConfigFile(Path::FromFS(argv[1]), error);
|
||||
} else {
|
||||
error.Set(cmdline_domain, "too many arguments");
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,12 +17,12 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef CMDLINE_H
|
||||
#define CMDLINE_H
|
||||
#ifndef MPD_COMMAND_LINE_HXX
|
||||
#define MPD_COMMAND_LINE_HXX
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
class Error;
|
||||
|
||||
struct options {
|
||||
gboolean kill;
|
||||
@@ -33,6 +33,6 @@ struct options {
|
||||
|
||||
bool
|
||||
parse_cmdline(int argc, char **argv, struct options *options,
|
||||
GError **error_r);
|
||||
Error &error);
|
||||
|
||||
#endif
|
174
src/Compiler.h
Normal file
174
src/Compiler.h
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef COMPILER_H
|
||||
#define COMPILER_H
|
||||
|
||||
#define GCC_CHECK_VERSION(major, minor) \
|
||||
(defined(__GNUC__) && \
|
||||
(__GNUC__ > (major) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))))
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define GCC_VERSION (__GNUC__ * 10000 \
|
||||
+ __GNUC_MINOR__ * 100 \
|
||||
+ __GNUC_PATCHLEVEL__)
|
||||
#else
|
||||
#define GCC_VERSION 0
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
# define CLANG_VERSION (__clang_major__ * 10000 \
|
||||
+ __clang_minor__ * 100 \
|
||||
+ __clang_patchlevel__)
|
||||
# if __clang_major__ < 3
|
||||
# error Sorry, your clang version is too old. You need at least version 3.1.
|
||||
# endif
|
||||
#elif defined(__GNUC__)
|
||||
# if !GCC_CHECK_VERSION(4,6)
|
||||
# error Sorry, your gcc version is too old. You need at least version 4.6.
|
||||
# endif
|
||||
#else
|
||||
# warning Untested compiler. Use at your own risk!
|
||||
#endif
|
||||
|
||||
#if GCC_CHECK_VERSION(4,0)
|
||||
|
||||
/* GCC 4.x */
|
||||
|
||||
#define gcc_const __attribute__((const))
|
||||
#define gcc_deprecated __attribute__((deprecated))
|
||||
#define gcc_may_alias __attribute__((may_alias))
|
||||
#define gcc_malloc __attribute__((malloc))
|
||||
#define gcc_noreturn __attribute__((noreturn))
|
||||
#define gcc_packed __attribute__((packed))
|
||||
#define gcc_printf(a,b) __attribute__((format(printf, a, b)))
|
||||
#define gcc_pure __attribute__((pure))
|
||||
#define gcc_sentinel __attribute__((sentinel))
|
||||
#define gcc_unused __attribute__((unused))
|
||||
#define gcc_warn_unused_result __attribute__((warn_unused_result))
|
||||
|
||||
#define gcc_nonnull(...) __attribute__((nonnull(__VA_ARGS__)))
|
||||
#define gcc_nonnull_all __attribute__((nonnull))
|
||||
|
||||
#define gcc_likely(x) __builtin_expect (!!(x), 1)
|
||||
#define gcc_unlikely(x) __builtin_expect (!!(x), 0)
|
||||
|
||||
#define gcc_aligned(n) __attribute__((aligned(n)))
|
||||
|
||||
#define gcc_visibility_hidden __attribute__((visibility("hidden")))
|
||||
#define gcc_visibility_default __attribute__((visibility("default")))
|
||||
|
||||
#define gcc_always_inline __attribute__((always_inline))
|
||||
|
||||
#else
|
||||
|
||||
/* generic C compiler */
|
||||
|
||||
#define gcc_const
|
||||
#define gcc_deprecated
|
||||
#define gcc_may_alias
|
||||
#define gcc_malloc
|
||||
#define gcc_noreturn
|
||||
#define gcc_packed
|
||||
#define gcc_printf(a,b)
|
||||
#define gcc_pure
|
||||
#define gcc_sentinel
|
||||
#define gcc_unused
|
||||
#define gcc_warn_unused_result
|
||||
|
||||
#define gcc_nonnull(...)
|
||||
#define gcc_nonnull_all
|
||||
|
||||
#define gcc_likely(x) (x)
|
||||
#define gcc_unlikely(x) (x)
|
||||
|
||||
#define gcc_aligned(n)
|
||||
|
||||
#define gcc_visibility_hidden
|
||||
#define gcc_visibility_default
|
||||
|
||||
#define gcc_always_inline inline
|
||||
|
||||
#endif
|
||||
|
||||
#if GCC_CHECK_VERSION(4,3)
|
||||
|
||||
#define gcc_hot __attribute__((hot))
|
||||
#define gcc_cold __attribute__((cold))
|
||||
|
||||
#else /* ! GCC_UNUSED >= 40300 */
|
||||
|
||||
#define gcc_hot
|
||||
#define gcc_cold
|
||||
|
||||
#endif /* ! GCC_UNUSED >= 40300 */
|
||||
|
||||
#if GCC_CHECK_VERSION(4,6) && !defined(__clang__)
|
||||
#define gcc_flatten __attribute__((flatten))
|
||||
#else
|
||||
#define gcc_flatten
|
||||
#endif
|
||||
|
||||
#ifndef __cplusplus
|
||||
/* plain C99 has "restrict" */
|
||||
#define gcc_restrict restrict
|
||||
#elif GCC_CHECK_VERSION(4,0)
|
||||
/* "__restrict__" is a GCC extension for C++ */
|
||||
#define gcc_restrict __restrict__
|
||||
#else
|
||||
/* disable it on other compilers */
|
||||
#define gcc_restrict
|
||||
#endif
|
||||
|
||||
/* C++11 features */
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
||||
/* support for C++11 "override" was added in gcc 4.7 */
|
||||
#if !defined(__clang__) && !GCC_CHECK_VERSION(4,7)
|
||||
#define override
|
||||
#define final
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) || GCC_CHECK_VERSION(4,8)
|
||||
#define gcc_alignas(T, fallback) alignas(T)
|
||||
#else
|
||||
#define gcc_alignas(T, fallback) gcc_aligned(fallback)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef __has_feature
|
||||
// define dummy macro for non-clang compilers
|
||||
#define __has_feature(x) 0
|
||||
#endif
|
||||
|
||||
#if __has_feature(attribute_unused_on_fields)
|
||||
#define gcc_unused_field gcc_unused
|
||||
#else
|
||||
#define gcc_unused_field
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define gcc_unreachable() __builtin_unreachable()
|
||||
#else
|
||||
#define gcc_unreachable()
|
||||
#endif
|
||||
|
||||
#endif
|
160
src/ConfigData.cxx
Normal file
160
src/ConfigData.cxx
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ConfigData.hxx"
|
||||
#include "ConfigParser.hxx"
|
||||
#include "ConfigPath.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "system/FatalError.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int
|
||||
block_param::GetIntValue() const
|
||||
{
|
||||
char *endptr;
|
||||
long value2 = strtol(value.c_str(), &endptr, 0);
|
||||
if (*endptr != 0)
|
||||
FormatFatalError("Not a valid number in line %i", line);
|
||||
|
||||
return value2;
|
||||
}
|
||||
|
||||
unsigned
|
||||
block_param::GetUnsignedValue() const
|
||||
{
|
||||
char *endptr;
|
||||
unsigned long value2 = strtoul(value.c_str(), &endptr, 0);
|
||||
if (*endptr != 0)
|
||||
FormatFatalError("Not a valid number in line %i", line);
|
||||
|
||||
return (unsigned)value2;
|
||||
}
|
||||
|
||||
bool
|
||||
block_param::GetBoolValue() const
|
||||
{
|
||||
bool value2;
|
||||
if (!get_bool(value.c_str(), &value2))
|
||||
FormatFatalError("%s is not a boolean value (yes, true, 1) or "
|
||||
"(no, false, 0) on line %i\n",
|
||||
name.c_str(), line);
|
||||
|
||||
return value2;
|
||||
}
|
||||
|
||||
config_param::config_param(const char *_value, int _line)
|
||||
:next(nullptr), value(_value), line(_line) {}
|
||||
|
||||
config_param::~config_param()
|
||||
{
|
||||
delete next;
|
||||
}
|
||||
|
||||
const block_param *
|
||||
config_param::GetBlockParam(const char *name) const
|
||||
{
|
||||
for (const auto &i : block_params) {
|
||||
if (i.name == name) {
|
||||
i.used = true;
|
||||
return &i;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
config_param::GetBlockValue(const char *name, const char *default_value) const
|
||||
{
|
||||
const block_param *bp = GetBlockParam(name);
|
||||
if (bp == nullptr)
|
||||
return default_value;
|
||||
|
||||
return bp->value.c_str();
|
||||
}
|
||||
|
||||
AllocatedPath
|
||||
config_param::GetBlockPath(const char *name, const char *default_value,
|
||||
Error &error) const
|
||||
{
|
||||
assert(!error.IsDefined());
|
||||
|
||||
int line2 = line;
|
||||
const char *s;
|
||||
|
||||
const block_param *bp = GetBlockParam(name);
|
||||
if (bp != nullptr) {
|
||||
line2 = bp->line;
|
||||
s = bp->value.c_str();
|
||||
} else {
|
||||
if (default_value == nullptr)
|
||||
return AllocatedPath::Null();
|
||||
|
||||
s = default_value;
|
||||
}
|
||||
|
||||
AllocatedPath path = ParsePath(s, error);
|
||||
if (gcc_unlikely(path.IsNull()))
|
||||
error.FormatPrefix("Invalid path in \"%s\" at line %i: ",
|
||||
name, line2);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
AllocatedPath
|
||||
config_param::GetBlockPath(const char *name, Error &error) const
|
||||
{
|
||||
return GetBlockPath(name, nullptr, error);
|
||||
}
|
||||
|
||||
int
|
||||
config_param::GetBlockValue(const char *name, int default_value) const
|
||||
{
|
||||
const block_param *bp = GetBlockParam(name);
|
||||
if (bp == nullptr)
|
||||
return default_value;
|
||||
|
||||
return bp->GetIntValue();
|
||||
}
|
||||
|
||||
unsigned
|
||||
config_param::GetBlockValue(const char *name, unsigned default_value) const
|
||||
{
|
||||
const block_param *bp = GetBlockParam(name);
|
||||
if (bp == nullptr)
|
||||
return default_value;
|
||||
|
||||
return bp->GetUnsignedValue();
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
bool
|
||||
config_param::GetBlockValue(const char *name, bool default_value) const
|
||||
{
|
||||
const block_param *bp = GetBlockParam(name);
|
||||
if (bp == NULL)
|
||||
return default_value;
|
||||
|
||||
return bp->GetBoolValue();
|
||||
}
|
134
src/ConfigData.hxx
Normal file
134
src/ConfigData.hxx
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CONFIG_DATA_HXX
|
||||
#define MPD_CONFIG_DATA_HXX
|
||||
|
||||
#include "ConfigOption.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
class AllocatedPath;
|
||||
class Error;
|
||||
|
||||
struct block_param {
|
||||
std::string name;
|
||||
std::string value;
|
||||
int line;
|
||||
|
||||
/**
|
||||
* This flag is false when nobody has queried the value of
|
||||
* this option yet.
|
||||
*/
|
||||
mutable bool used;
|
||||
|
||||
gcc_nonnull_all
|
||||
block_param(const char *_name, const char *_value, int _line=-1)
|
||||
:name(_name), value(_value), line(_line), used(false) {}
|
||||
|
||||
gcc_pure
|
||||
int GetIntValue() const;
|
||||
|
||||
gcc_pure
|
||||
unsigned GetUnsignedValue() const;
|
||||
|
||||
gcc_pure
|
||||
bool GetBoolValue() const;
|
||||
};
|
||||
|
||||
struct config_param {
|
||||
/**
|
||||
* The next config_param with the same name. The destructor
|
||||
* deletes the whole chain.
|
||||
*/
|
||||
struct config_param *next;
|
||||
|
||||
std::string value;
|
||||
|
||||
unsigned int line;
|
||||
|
||||
std::vector<block_param> block_params;
|
||||
|
||||
/**
|
||||
* This flag is false when nobody has queried the value of
|
||||
* this option yet.
|
||||
*/
|
||||
bool used;
|
||||
|
||||
config_param(int _line=-1)
|
||||
:next(nullptr), line(_line), used(false) {}
|
||||
|
||||
gcc_nonnull_all
|
||||
config_param(const char *_value, int _line=-1);
|
||||
|
||||
config_param(const config_param &) = delete;
|
||||
|
||||
~config_param();
|
||||
|
||||
config_param &operator=(const config_param &) = delete;
|
||||
|
||||
/**
|
||||
* Determine if this is a "null" instance, i.e. an empty
|
||||
* object that was synthesized and not loaded from a
|
||||
* configuration file.
|
||||
*/
|
||||
bool IsNull() const {
|
||||
return line == unsigned(-1);
|
||||
}
|
||||
|
||||
gcc_nonnull_all
|
||||
void AddBlockParam(const char *_name, const char *_value,
|
||||
int _line=-1) {
|
||||
block_params.emplace_back(_name, _value, _line);
|
||||
}
|
||||
|
||||
gcc_nonnull_all gcc_pure
|
||||
const block_param *GetBlockParam(const char *_name) const;
|
||||
|
||||
gcc_pure
|
||||
const char *GetBlockValue(const char *name,
|
||||
const char *default_value=nullptr) const;
|
||||
|
||||
/**
|
||||
* Same as config_dup_path(), but looks up the setting in the
|
||||
* specified block.
|
||||
*/
|
||||
AllocatedPath GetBlockPath(const char *name, const char *default_value,
|
||||
Error &error) const;
|
||||
|
||||
AllocatedPath GetBlockPath(const char *name, Error &error) const;
|
||||
|
||||
gcc_pure
|
||||
int GetBlockValue(const char *name, int default_value) const;
|
||||
|
||||
gcc_pure
|
||||
unsigned GetBlockValue(const char *name, unsigned default_value) const;
|
||||
|
||||
gcc_pure
|
||||
bool GetBlockValue(const char *name, bool default_value) const;
|
||||
};
|
||||
|
||||
struct ConfigData {
|
||||
std::array<config_param *, std::size_t(CONF_MAX)> params;
|
||||
};
|
||||
|
||||
#endif
|
26
src/ConfigDefaults.hxx
Normal file
26
src/ConfigDefaults.hxx
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CONFIG_DEFAULTS_HXX
|
||||
#define MPD_CONFIG_DEFAULTS_HXX
|
||||
|
||||
static constexpr unsigned DEFAULT_PLAYLIST_MAX_LENGTH = 16 * 1024;
|
||||
static constexpr bool DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS = false;
|
||||
|
||||
#endif
|
23
src/ConfigError.cxx
Normal file
23
src/ConfigError.cxx
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "ConfigError.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
|
||||
const Domain config_domain("config");
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,9 +17,9 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_INPUT_FILE_H
|
||||
#define MPD_INPUT_FILE_H
|
||||
#ifndef MPD_CONFIG_ERROR_HXX
|
||||
#define MPD_CONFIG_ERROR_HXX
|
||||
|
||||
extern const struct input_plugin input_plugin_file;
|
||||
extern const class Domain config_domain;
|
||||
|
||||
#endif
|
272
src/ConfigFile.cxx
Normal file
272
src/ConfigFile.cxx
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ConfigFile.hxx"
|
||||
#include "ConfigError.hxx"
|
||||
#include "ConfigData.hxx"
|
||||
#include "ConfigTemplates.hxx"
|
||||
#include "util/Tokenizer.hxx"
|
||||
#include "util/StringUtil.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "fs/Limits.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define MAX_STRING_SIZE MPD_PATH_MAX+80
|
||||
|
||||
#define CONF_COMMENT '#'
|
||||
|
||||
static constexpr Domain config_file_domain("config_file");
|
||||
|
||||
static bool
|
||||
config_read_name_value(struct config_param *param, char *input, unsigned line,
|
||||
Error &error)
|
||||
{
|
||||
Tokenizer tokenizer(input);
|
||||
|
||||
const char *name = tokenizer.NextWord(error);
|
||||
if (name == nullptr) {
|
||||
assert(!tokenizer.IsEnd());
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *value = tokenizer.NextString(error);
|
||||
if (value == nullptr) {
|
||||
if (tokenizer.IsEnd()) {
|
||||
error.Set(config_file_domain, "Value missing");
|
||||
} else {
|
||||
assert(error.IsDefined());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tokenizer.IsEnd() && tokenizer.CurrentChar() != CONF_COMMENT) {
|
||||
error.Set(config_file_domain, "Unknown tokens after value");
|
||||
return false;
|
||||
}
|
||||
|
||||
const struct block_param *bp = param->GetBlockParam(name);
|
||||
if (bp != nullptr) {
|
||||
error.Format(config_file_domain,
|
||||
"\"%s\" is duplicate, first defined on line %i",
|
||||
name, bp->line);
|
||||
return false;
|
||||
}
|
||||
|
||||
param->AddBlockParam(name, value, line);
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct config_param *
|
||||
config_read_block(FILE *fp, int *count, char *string, Error &error)
|
||||
{
|
||||
struct config_param *ret = new config_param(*count);
|
||||
|
||||
while (true) {
|
||||
char *line;
|
||||
|
||||
line = fgets(string, MAX_STRING_SIZE, fp);
|
||||
if (line == nullptr) {
|
||||
delete ret;
|
||||
error.Set(config_file_domain,
|
||||
"Expected '}' before end-of-file");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
(*count)++;
|
||||
line = strchug_fast(line);
|
||||
if (*line == 0 || *line == CONF_COMMENT)
|
||||
continue;
|
||||
|
||||
if (*line == '}') {
|
||||
/* end of this block; return from the function
|
||||
(and from this "while" loop) */
|
||||
|
||||
line = strchug_fast(line + 1);
|
||||
if (*line != 0 && *line != CONF_COMMENT) {
|
||||
delete ret;
|
||||
error.Format(config_file_domain,
|
||||
"line %i: Unknown tokens after '}'",
|
||||
*count);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* parse name and value */
|
||||
|
||||
if (!config_read_name_value(ret, line, *count, error)) {
|
||||
assert(*line != 0);
|
||||
delete ret;
|
||||
error.FormatPrefix("line %i: ", *count);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gcc_nonnull_all
|
||||
static void
|
||||
Append(config_param *&head, config_param *p)
|
||||
{
|
||||
assert(p->next == nullptr);
|
||||
|
||||
config_param **i = &head;
|
||||
while (*i != nullptr)
|
||||
i = &(*i)->next;
|
||||
|
||||
*i = p;
|
||||
}
|
||||
|
||||
static bool
|
||||
ReadConfigFile(ConfigData &config_data, FILE *fp, Error &error)
|
||||
{
|
||||
assert(fp != nullptr);
|
||||
|
||||
char string[MAX_STRING_SIZE + 1];
|
||||
int count = 0;
|
||||
struct config_param *param;
|
||||
|
||||
while (fgets(string, MAX_STRING_SIZE, fp)) {
|
||||
char *line;
|
||||
const char *name, *value;
|
||||
|
||||
count++;
|
||||
|
||||
line = strchug_fast(string);
|
||||
if (*line == 0 || *line == CONF_COMMENT)
|
||||
continue;
|
||||
|
||||
/* the first token in each line is the name, followed
|
||||
by either the value or '{' */
|
||||
|
||||
Tokenizer tokenizer(line);
|
||||
name = tokenizer.NextWord(error);
|
||||
if (name == nullptr) {
|
||||
assert(!tokenizer.IsEnd());
|
||||
error.FormatPrefix("line %i: ", count);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* get the definition of that option, and check the
|
||||
"repeatable" flag */
|
||||
|
||||
const ConfigOption o = ParseConfigOptionName(name);
|
||||
if (o == CONF_MAX) {
|
||||
error.Format(config_file_domain,
|
||||
"unrecognized parameter in config file at "
|
||||
"line %i: %s\n", count, name);
|
||||
return false;
|
||||
}
|
||||
|
||||
const unsigned i = unsigned(o);
|
||||
const ConfigTemplate &option = config_templates[i];
|
||||
config_param *&head = config_data.params[i];
|
||||
|
||||
if (head != nullptr && !option.repeatable) {
|
||||
param = head;
|
||||
error.Format(config_file_domain,
|
||||
"config parameter \"%s\" is first defined "
|
||||
"on line %i and redefined on line %i\n",
|
||||
name, param->line, count);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* now parse the block or the value */
|
||||
|
||||
if (option.block) {
|
||||
/* it's a block, call config_read_block() */
|
||||
|
||||
if (tokenizer.CurrentChar() != '{') {
|
||||
error.Format(config_file_domain,
|
||||
"line %i: '{' expected", count);
|
||||
return false;
|
||||
}
|
||||
|
||||
line = strchug_fast(tokenizer.Rest() + 1);
|
||||
if (*line != 0 && *line != CONF_COMMENT) {
|
||||
error.Format(config_file_domain,
|
||||
"line %i: Unknown tokens after '{'",
|
||||
count);
|
||||
return false;
|
||||
}
|
||||
|
||||
param = config_read_block(fp, &count, string, error);
|
||||
if (param == nullptr) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
/* a string value */
|
||||
|
||||
value = tokenizer.NextString(error);
|
||||
if (value == nullptr) {
|
||||
if (tokenizer.IsEnd())
|
||||
error.Format(config_file_domain,
|
||||
"line %i: Value missing",
|
||||
count);
|
||||
else
|
||||
error.FormatPrefix("line %i: ", count);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tokenizer.IsEnd() &&
|
||||
tokenizer.CurrentChar() != CONF_COMMENT) {
|
||||
error.Format(config_file_domain,
|
||||
"line %i: Unknown tokens after value",
|
||||
count);
|
||||
return false;
|
||||
}
|
||||
|
||||
param = new config_param(value, count);
|
||||
}
|
||||
|
||||
Append(head, param);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ReadConfigFile(ConfigData &config_data, Path path, Error &error)
|
||||
{
|
||||
assert(!path.IsNull());
|
||||
const std::string path_utf8 = path.ToUTF8();
|
||||
|
||||
FormatDebug(config_file_domain, "loading file %s", path_utf8.c_str());
|
||||
|
||||
FILE *fp = FOpen(path, FOpenMode::ReadText);
|
||||
if (fp == nullptr) {
|
||||
error.FormatErrno("Failed to open %s", path_utf8.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = ReadConfigFile(config_data, fp, error);
|
||||
fclose(fp);
|
||||
return result;
|
||||
}
|
30
src/ConfigFile.hxx
Normal file
30
src/ConfigFile.hxx
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CONFIG_FILE_HXX
|
||||
#define MPD_CONFIG_FILE_HXX
|
||||
|
||||
class Error;
|
||||
class Path;
|
||||
struct ConfigData;
|
||||
|
||||
bool
|
||||
ReadConfigFile(ConfigData &data, Path path, Error &error);
|
||||
|
||||
#endif
|
177
src/ConfigGlobal.cxx
Normal file
177
src/ConfigGlobal.cxx
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ConfigGlobal.hxx"
|
||||
#include "ConfigParser.hxx"
|
||||
#include "ConfigData.hxx"
|
||||
#include "ConfigFile.hxx"
|
||||
#include "ConfigPath.hxx"
|
||||
#include "ConfigError.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "system/FatalError.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static ConfigData config_data;
|
||||
|
||||
void config_global_finish(void)
|
||||
{
|
||||
for (auto i : config_data.params)
|
||||
delete i;
|
||||
}
|
||||
|
||||
void config_global_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ReadConfigFile(Path path, Error &error)
|
||||
{
|
||||
return ReadConfigFile(config_data, path, error);
|
||||
}
|
||||
|
||||
static void
|
||||
Check(const config_param *param)
|
||||
{
|
||||
if (!param->used)
|
||||
/* this whole config_param was not queried at all -
|
||||
the feature might be disabled at compile time?
|
||||
Silently ignore it here. */
|
||||
return;
|
||||
|
||||
for (const auto &i : param->block_params) {
|
||||
if (!i.used)
|
||||
FormatWarning(config_domain,
|
||||
"option '%s' on line %i was not recognized",
|
||||
i.name.c_str(), i.line);
|
||||
}
|
||||
}
|
||||
|
||||
void config_global_check(void)
|
||||
{
|
||||
for (auto i : config_data.params)
|
||||
for (const config_param *p = i; p != nullptr; p = p->next)
|
||||
Check(p);
|
||||
}
|
||||
|
||||
const struct config_param *
|
||||
config_get_next_param(ConfigOption option, const struct config_param * last)
|
||||
{
|
||||
config_param *param = last != nullptr
|
||||
? last->next
|
||||
: config_data.params[unsigned(option)];
|
||||
if (param != nullptr)
|
||||
param->used = true;
|
||||
return param;
|
||||
}
|
||||
|
||||
const char *
|
||||
config_get_string(ConfigOption option, const char *default_value)
|
||||
{
|
||||
const struct config_param *param = config_get_param(option);
|
||||
|
||||
if (param == nullptr)
|
||||
return default_value;
|
||||
|
||||
return param->value.c_str();
|
||||
}
|
||||
|
||||
AllocatedPath
|
||||
config_get_path(ConfigOption option, Error &error)
|
||||
{
|
||||
const struct config_param *param = config_get_param(option);
|
||||
if (param == nullptr)
|
||||
return AllocatedPath::Null();
|
||||
|
||||
return config_parse_path(param, error);
|
||||
}
|
||||
|
||||
AllocatedPath
|
||||
config_parse_path(const struct config_param *param, Error & error)
|
||||
{
|
||||
AllocatedPath path = ParsePath(param->value.c_str(), error);
|
||||
if (gcc_unlikely(path.IsNull()))
|
||||
error.FormatPrefix("Invalid path at line %i: ",
|
||||
param->line);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
unsigned
|
||||
config_get_unsigned(ConfigOption option, unsigned default_value)
|
||||
{
|
||||
const struct config_param *param = config_get_param(option);
|
||||
long value;
|
||||
char *endptr;
|
||||
|
||||
if (param == nullptr)
|
||||
return default_value;
|
||||
|
||||
value = strtol(param->value.c_str(), &endptr, 0);
|
||||
if (*endptr != 0 || value < 0)
|
||||
FormatFatalError("Not a valid non-negative number in line %i",
|
||||
param->line);
|
||||
|
||||
return (unsigned)value;
|
||||
}
|
||||
|
||||
unsigned
|
||||
config_get_positive(ConfigOption option, unsigned default_value)
|
||||
{
|
||||
const struct config_param *param = config_get_param(option);
|
||||
long value;
|
||||
char *endptr;
|
||||
|
||||
if (param == nullptr)
|
||||
return default_value;
|
||||
|
||||
value = strtol(param->value.c_str(), &endptr, 0);
|
||||
if (*endptr != 0)
|
||||
FormatFatalError("Not a valid number in line %i", param->line);
|
||||
|
||||
if (value <= 0)
|
||||
FormatFatalError("Not a positive number in line %i",
|
||||
param->line);
|
||||
|
||||
return (unsigned)value;
|
||||
}
|
||||
|
||||
bool
|
||||
config_get_bool(ConfigOption option, bool default_value)
|
||||
{
|
||||
const struct config_param *param = config_get_param(option);
|
||||
bool success, value;
|
||||
|
||||
if (param == nullptr)
|
||||
return default_value;
|
||||
|
||||
success = get_bool(param->value.c_str(), &value);
|
||||
if (!success)
|
||||
FormatFatalError("Expected boolean value (yes, true, 1) or "
|
||||
"(no, false, 0) on line %i\n",
|
||||
param->line);
|
||||
|
||||
return value;
|
||||
}
|
95
src/ConfigGlobal.hxx
Normal file
95
src/ConfigGlobal.hxx
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CONFIG_GLOBAL_HXX
|
||||
#define MPD_CONFIG_GLOBAL_HXX
|
||||
|
||||
#include "ConfigOption.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
class Error;
|
||||
class Path;
|
||||
class AllocatedPath;
|
||||
|
||||
void config_global_init(void);
|
||||
void config_global_finish(void);
|
||||
|
||||
/**
|
||||
* Call this function after all configuration has been evaluated. It
|
||||
* checks for unused parameters, and logs warnings.
|
||||
*/
|
||||
void config_global_check(void);
|
||||
|
||||
bool
|
||||
ReadConfigFile(Path path, Error &error);
|
||||
|
||||
/* don't free the returned value
|
||||
set _last_ to nullptr to get first entry */
|
||||
gcc_pure
|
||||
const struct config_param *
|
||||
config_get_next_param(enum ConfigOption option,
|
||||
const struct config_param *last);
|
||||
|
||||
gcc_pure
|
||||
static inline const struct config_param *
|
||||
config_get_param(enum ConfigOption option)
|
||||
{
|
||||
return config_get_next_param(option, nullptr);
|
||||
}
|
||||
|
||||
/* Note on gcc_pure: Some of the functions declared pure are not
|
||||
really pure in strict sense. They have side effect such that they
|
||||
validate parameter's value and signal an error if it's invalid.
|
||||
However, if the argument was already validated or we don't care
|
||||
about the argument at all, this may be ignored so in the end, we
|
||||
should be fine with calling those functions pure. */
|
||||
|
||||
gcc_pure
|
||||
const char *
|
||||
config_get_string(enum ConfigOption option, const char *default_value);
|
||||
|
||||
/**
|
||||
* Returns an optional configuration variable which contains an
|
||||
* absolute path. If there is a tilde prefix, it is expanded.
|
||||
* Returns AllocatedPath::Null() if the value is not present. If the path
|
||||
* could not be parsed, returns AllocatedPath::Null() and sets the error.
|
||||
*/
|
||||
AllocatedPath
|
||||
config_get_path(enum ConfigOption option, Error &error);
|
||||
|
||||
/**
|
||||
* Parse a configuration parameter as a path.
|
||||
* If there is a tilde prefix, it is expanded. If the path could
|
||||
* not be parsed, returns AllocatedPath::Null() and sets the error.
|
||||
*/
|
||||
AllocatedPath
|
||||
config_parse_path(const struct config_param *param, Error & error_r);
|
||||
|
||||
gcc_pure
|
||||
unsigned
|
||||
config_get_unsigned(enum ConfigOption option, unsigned default_value);
|
||||
|
||||
gcc_pure
|
||||
unsigned
|
||||
config_get_positive(enum ConfigOption option, unsigned default_value);
|
||||
|
||||
gcc_pure
|
||||
bool config_get_bool(enum ConfigOption option, bool default_value);
|
||||
|
||||
#endif
|
90
src/ConfigOption.hxx
Normal file
90
src/ConfigOption.hxx
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CONFIG_OPTION_HXX
|
||||
#define MPD_CONFIG_OPTION_HXX
|
||||
|
||||
#include "Compiler.h"
|
||||
|
||||
enum ConfigOption {
|
||||
CONF_MUSIC_DIR,
|
||||
CONF_PLAYLIST_DIR,
|
||||
CONF_FOLLOW_INSIDE_SYMLINKS,
|
||||
CONF_FOLLOW_OUTSIDE_SYMLINKS,
|
||||
CONF_DB_FILE,
|
||||
CONF_STICKER_FILE,
|
||||
CONF_LOG_FILE,
|
||||
CONF_PID_FILE,
|
||||
CONF_STATE_FILE,
|
||||
CONF_RESTORE_PAUSED,
|
||||
CONF_USER,
|
||||
CONF_GROUP,
|
||||
CONF_BIND_TO_ADDRESS,
|
||||
CONF_PORT,
|
||||
CONF_LOG_LEVEL,
|
||||
CONF_ZEROCONF_NAME,
|
||||
CONF_ZEROCONF_ENABLED,
|
||||
CONF_PASSWORD,
|
||||
CONF_DEFAULT_PERMS,
|
||||
CONF_AUDIO_OUTPUT,
|
||||
CONF_AUDIO_OUTPUT_FORMAT,
|
||||
CONF_MIXER_TYPE,
|
||||
CONF_REPLAYGAIN,
|
||||
CONF_REPLAYGAIN_PREAMP,
|
||||
CONF_REPLAYGAIN_MISSING_PREAMP,
|
||||
CONF_REPLAYGAIN_LIMIT,
|
||||
CONF_VOLUME_NORMALIZATION,
|
||||
CONF_SAMPLERATE_CONVERTER,
|
||||
CONF_AUDIO_BUFFER_SIZE,
|
||||
CONF_BUFFER_BEFORE_PLAY,
|
||||
CONF_HTTP_PROXY_HOST,
|
||||
CONF_HTTP_PROXY_PORT,
|
||||
CONF_HTTP_PROXY_USER,
|
||||
CONF_HTTP_PROXY_PASSWORD,
|
||||
CONF_CONN_TIMEOUT,
|
||||
CONF_MAX_CONN,
|
||||
CONF_MAX_PLAYLIST_LENGTH,
|
||||
CONF_MAX_COMMAND_LIST_SIZE,
|
||||
CONF_MAX_OUTPUT_BUFFER_SIZE,
|
||||
CONF_FS_CHARSET,
|
||||
CONF_ID3V1_ENCODING,
|
||||
CONF_METADATA_TO_USE,
|
||||
CONF_SAVE_ABSOLUTE_PATHS,
|
||||
CONF_DECODER,
|
||||
CONF_INPUT,
|
||||
CONF_GAPLESS_MP3_PLAYBACK,
|
||||
CONF_PLAYLIST_PLUGIN,
|
||||
CONF_AUTO_UPDATE,
|
||||
CONF_AUTO_UPDATE_DEPTH,
|
||||
CONF_DESPOTIFY_USER,
|
||||
CONF_DESPOTIFY_PASSWORD,
|
||||
CONF_DESPOTIFY_HIGH_BITRATE,
|
||||
CONF_AUDIO_FILTER,
|
||||
CONF_DATABASE,
|
||||
CONF_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* @return #CONF_MAX if not found
|
||||
*/
|
||||
gcc_pure
|
||||
enum ConfigOption
|
||||
ParseConfigOptionName(const char *name);
|
||||
|
||||
#endif
|
40
src/ConfigParser.cxx
Normal file
40
src/ConfigParser.cxx
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "ConfigParser.hxx"
|
||||
#include "util/StringUtil.hxx"
|
||||
|
||||
bool
|
||||
get_bool(const char *value, bool *value_r)
|
||||
{
|
||||
static const char *t[] = { "yes", "true", "1", nullptr };
|
||||
static const char *f[] = { "no", "false", "0", nullptr };
|
||||
|
||||
if (string_array_contains(t, value)) {
|
||||
*value_r = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (string_array_contains(f, value)) {
|
||||
*value_r = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
26
src/ConfigParser.hxx
Normal file
26
src/ConfigParser.hxx
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CONFIG_PARSER_HXX
|
||||
#define MPD_CONFIG_PARSER_HXX
|
||||
|
||||
bool
|
||||
get_bool(const char *value, bool *value_r);
|
||||
|
||||
#endif
|
132
src/ConfigPath.cxx
Normal file
132
src/ConfigPath.cxx
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ConfigPath.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/Traits.hxx"
|
||||
#include "fs/Domain.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "ConfigGlobal.hxx"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef WIN32
|
||||
#include <pwd.h>
|
||||
|
||||
/**
|
||||
* Determine a given user's home directory.
|
||||
*/
|
||||
static AllocatedPath
|
||||
GetHome(const char *user, Error &error)
|
||||
{
|
||||
passwd *pw = getpwnam(user);
|
||||
if (pw == nullptr) {
|
||||
error.Format(path_domain,
|
||||
"no such user: %s", user);
|
||||
return AllocatedPath::Null();
|
||||
}
|
||||
|
||||
return AllocatedPath::FromFS(pw->pw_dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the current user's home directory.
|
||||
*/
|
||||
static AllocatedPath
|
||||
GetHome(Error &error)
|
||||
{
|
||||
const char *home = g_get_home_dir();
|
||||
if (home == nullptr) {
|
||||
error.Set(path_domain,
|
||||
"problems getting home for current user");
|
||||
return AllocatedPath::Null();
|
||||
}
|
||||
|
||||
return AllocatedPath::FromUTF8(home, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the configured user's home directory.
|
||||
*/
|
||||
static AllocatedPath
|
||||
GetConfiguredHome(Error &error)
|
||||
{
|
||||
const char *user = config_get_string(CONF_USER, nullptr);
|
||||
return user != nullptr
|
||||
? GetHome(user, error)
|
||||
: GetHome(error);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
AllocatedPath
|
||||
ParsePath(const char *path, Error &error)
|
||||
{
|
||||
assert(path != nullptr);
|
||||
|
||||
#ifndef WIN32
|
||||
if (path[0] == '~') {
|
||||
++path;
|
||||
|
||||
if (*path == '\0')
|
||||
return GetConfiguredHome(error);
|
||||
|
||||
AllocatedPath home = AllocatedPath::Null();
|
||||
|
||||
if (*path == '/') {
|
||||
home = GetConfiguredHome(error);
|
||||
|
||||
++path;
|
||||
} else {
|
||||
const char *slash = strchr(path, '/');
|
||||
const char *end = slash == nullptr
|
||||
? path + strlen(path)
|
||||
: slash;
|
||||
const std::string user(path, end);
|
||||
home = GetHome(user.c_str(), error);
|
||||
|
||||
if (slash == nullptr)
|
||||
return home;
|
||||
|
||||
path = slash + 1;
|
||||
}
|
||||
|
||||
if (home.IsNull())
|
||||
return AllocatedPath::Null();
|
||||
|
||||
AllocatedPath path2 = AllocatedPath::FromUTF8(path, error);
|
||||
if (path2.IsNull())
|
||||
return AllocatedPath::Null();
|
||||
|
||||
return AllocatedPath::Build(home, path2);
|
||||
} else if (!PathTraits::IsAbsoluteUTF8(path)) {
|
||||
error.Format(path_domain,
|
||||
"not an absolute path: %s", path);
|
||||
return AllocatedPath::Null();
|
||||
} else {
|
||||
#endif
|
||||
return AllocatedPath::FromUTF8(path, error);
|
||||
#ifndef WIN32
|
||||
}
|
||||
#endif
|
||||
}
|
29
src/ConfigPath.hxx
Normal file
29
src/ConfigPath.hxx
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CONFIG_PATH_HXX
|
||||
#define MPD_CONFIG_PATH_HXX
|
||||
|
||||
class AllocatedPath;
|
||||
class Error;
|
||||
|
||||
AllocatedPath
|
||||
ParsePath(const char *path, Error &error);
|
||||
|
||||
#endif
|
96
src/ConfigTemplates.cxx
Normal file
96
src/ConfigTemplates.cxx
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "ConfigTemplates.hxx"
|
||||
#include "ConfigOption.hxx"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
const ConfigTemplate config_templates[] = {
|
||||
{ "music_directory", false, false },
|
||||
{ "playlist_directory", false, false },
|
||||
{ "follow_inside_symlinks", false, false },
|
||||
{ "follow_outside_symlinks", false, false },
|
||||
{ "db_file", false, false },
|
||||
{ "sticker_file", false, false },
|
||||
{ "log_file", false, false },
|
||||
{ "pid_file", false, false },
|
||||
{ "state_file", false, false },
|
||||
{ "restore_paused", false, false },
|
||||
{ "user", false, false },
|
||||
{ "group", false, false },
|
||||
{ "bind_to_address", true, false },
|
||||
{ "port", false, false },
|
||||
{ "log_level", false, false },
|
||||
{ "zeroconf_name", false, false },
|
||||
{ "zeroconf_enabled", false, false },
|
||||
{ "password", true, false },
|
||||
{ "default_permissions", false, false },
|
||||
{ "audio_output", true, true },
|
||||
{ "audio_output_format", false, false },
|
||||
{ "mixer_type", false, false },
|
||||
{ "replaygain", false, false },
|
||||
{ "replaygain_preamp", false, false },
|
||||
{ "replaygain_missing_preamp", false, false },
|
||||
{ "replaygain_limit", false, false },
|
||||
{ "volume_normalization", false, false },
|
||||
{ "samplerate_converter", false, false },
|
||||
{ "audio_buffer_size", false, false },
|
||||
{ "buffer_before_play", false, false },
|
||||
{ "http_proxy_host", false, false },
|
||||
{ "http_proxy_port", false, false },
|
||||
{ "http_proxy_user", false, false },
|
||||
{ "http_proxy_password", false, false },
|
||||
{ "connection_timeout", false, false },
|
||||
{ "max_connections", false, false },
|
||||
{ "max_playlist_length", false, false },
|
||||
{ "max_command_list_size", false, false },
|
||||
{ "max_output_buffer_size", false, false },
|
||||
{ "filesystem_charset", false, false },
|
||||
{ "id3v1_encoding", false, false },
|
||||
{ "metadata_to_use", false, false },
|
||||
{ "save_absolute_paths_in_playlists", false, false },
|
||||
{ "decoder", true, true },
|
||||
{ "input", true, true },
|
||||
{ "gapless_mp3_playback", false, false },
|
||||
{ "playlist_plugin", true, true },
|
||||
{ "auto_update", false, false },
|
||||
{ "auto_update_depth", false, false },
|
||||
{ "despotify_user", false, false },
|
||||
{ "despotify_password", false, false},
|
||||
{ "despotify_high_bitrate", false, false },
|
||||
{ "filter", true, true },
|
||||
{ "database", false, true },
|
||||
};
|
||||
|
||||
static constexpr unsigned n_config_templates =
|
||||
sizeof(config_templates) / sizeof(config_templates[0]);
|
||||
|
||||
static_assert(n_config_templates == unsigned(CONF_MAX),
|
||||
"Wrong number of config_templates");
|
||||
|
||||
ConfigOption
|
||||
ParseConfigOptionName(const char *name)
|
||||
{
|
||||
for (unsigned i = 0; i < n_config_templates; ++i)
|
||||
if (strcmp(config_templates[i].name, name) == 0)
|
||||
return ConfigOption(i);
|
||||
|
||||
return CONF_MAX;
|
||||
}
|
33
src/ConfigTemplates.hxx
Normal file
33
src/ConfigTemplates.hxx
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CONFIG_TEMPLATES_HXX
|
||||
#define MPD_CONFIG_TEMPLATES_HXX
|
||||
|
||||
#include "ConfigOption.hxx"
|
||||
|
||||
struct ConfigTemplate {
|
||||
const char *const name;
|
||||
const bool repeatable;
|
||||
const bool block;
|
||||
};
|
||||
|
||||
extern const ConfigTemplate config_templates[];
|
||||
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -18,32 +18,25 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "crossfade.h"
|
||||
#include "pcm_mix.h"
|
||||
#include "chunk.h"
|
||||
#include "audio_format.h"
|
||||
#include "tag.h"
|
||||
#include "CrossFade.hxx"
|
||||
#include "MusicChunk.hxx"
|
||||
#include "AudioFormat.hxx"
|
||||
#include "util/NumberParser.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <glib.h>
|
||||
|
||||
#undef G_LOG_DOMAIN
|
||||
#define G_LOG_DOMAIN "crossfade"
|
||||
static constexpr Domain cross_fade_domain("cross_fade");
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
static char *
|
||||
strtok_r(char *str, const char *delim, G_GNUC_UNUSED char **saveptr)
|
||||
gcc_pure
|
||||
static float
|
||||
mixramp_interpolate(const char *ramp_list, float required_db)
|
||||
{
|
||||
return strtok(str, delim);
|
||||
}
|
||||
#endif
|
||||
|
||||
static float mixramp_interpolate(char *ramp_list, float required_db)
|
||||
{
|
||||
float db, secs, last_db = nan(""), last_secs = 0;
|
||||
char *ramp_str, *save_str = NULL;
|
||||
float last_db = 0, last_secs = 0;
|
||||
bool have_last = false;
|
||||
|
||||
/* ramp_list is a string of pairs of dBs and seconds that describe the
|
||||
* volume profile. Delimiters are semi-colons between pairs and spaces
|
||||
@@ -51,24 +44,22 @@ static float mixramp_interpolate(char *ramp_list, float required_db)
|
||||
* The dB values must be monotonically increasing for this to work. */
|
||||
|
||||
while (1) {
|
||||
/* Parse the dB tokens out of the input string. */
|
||||
ramp_str = strtok_r(ramp_list, " ", &save_str);
|
||||
|
||||
/* Tell strtok to continue next time round. */
|
||||
ramp_list = NULL;
|
||||
|
||||
/* Parse the dB value. */
|
||||
if (NULL == ramp_str) {
|
||||
return nan("");
|
||||
}
|
||||
db = (float)atof(ramp_str);
|
||||
char *endptr;
|
||||
const float db = ParseFloat(ramp_list, &endptr);
|
||||
if (endptr == ramp_list || *endptr != ' ')
|
||||
break;
|
||||
|
||||
ramp_list = endptr + 1;
|
||||
|
||||
/* Parse the time. */
|
||||
ramp_str = strtok_r(NULL, ";", &save_str);
|
||||
if (NULL == ramp_str) {
|
||||
return nan("");
|
||||
}
|
||||
secs = (float)atof(ramp_str);
|
||||
float secs = ParseFloat(ramp_list, &endptr);
|
||||
if (endptr == ramp_list || (*endptr != ';' && *endptr != 0))
|
||||
break;
|
||||
|
||||
ramp_list = endptr;
|
||||
if (*ramp_list == ';')
|
||||
++ramp_list;
|
||||
|
||||
/* Check for exact match. */
|
||||
if (db == required_db) {
|
||||
@@ -79,58 +70,70 @@ static float mixramp_interpolate(char *ramp_list, float required_db)
|
||||
if (db < required_db) {
|
||||
last_db = db;
|
||||
last_secs = secs;
|
||||
have_last = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If required db < any stored value, use the least. */
|
||||
if (isnan(last_db)) {
|
||||
if (!have_last)
|
||||
return secs;
|
||||
}
|
||||
|
||||
/* Finally, interpolate linearly. */
|
||||
secs = last_secs + (required_db - last_db) * (secs - last_secs) / (db - last_db);
|
||||
return secs;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned cross_fade_calc(float duration, float total_time,
|
||||
float mixramp_db, float mixramp_delay,
|
||||
float replay_gain_db, float replay_gain_prev_db,
|
||||
char *mixramp_start, char *mixramp_prev_end,
|
||||
const struct audio_format *af,
|
||||
const struct audio_format *old_format,
|
||||
unsigned max_chunks)
|
||||
unsigned
|
||||
CrossFadeSettings::Calculate(float total_time,
|
||||
float replay_gain_db, float replay_gain_prev_db,
|
||||
const char *mixramp_start, const char *mixramp_prev_end,
|
||||
const AudioFormat af,
|
||||
const AudioFormat old_format,
|
||||
unsigned max_chunks) const
|
||||
{
|
||||
unsigned int chunks = 0;
|
||||
float chunks_f;
|
||||
float mixramp_overlap;
|
||||
|
||||
if (duration < 0 || duration >= total_time ||
|
||||
/* we can't crossfade when the audio formats are different */
|
||||
!audio_format_equals(af, old_format))
|
||||
af != old_format)
|
||||
return 0;
|
||||
|
||||
assert(duration >= 0);
|
||||
assert(audio_format_valid(af));
|
||||
assert(af.IsValid());
|
||||
|
||||
chunks_f = (float)audio_format_time_to_size(af) / (float)CHUNK_SIZE;
|
||||
chunks_f = (float)af.GetTimeToSize() / (float)CHUNK_SIZE;
|
||||
|
||||
if (isnan(mixramp_delay) || !(mixramp_start) || !(mixramp_prev_end)) {
|
||||
if (mixramp_delay <= 0 || !mixramp_start || !mixramp_prev_end) {
|
||||
chunks = (chunks_f * duration + 0.5);
|
||||
} else {
|
||||
/* Calculate mixramp overlap. */
|
||||
mixramp_overlap = mixramp_interpolate(mixramp_start, mixramp_db - replay_gain_db)
|
||||
+ mixramp_interpolate(mixramp_prev_end, mixramp_db - replay_gain_prev_db);
|
||||
if (!isnan(mixramp_overlap) && (mixramp_delay <= mixramp_overlap)) {
|
||||
const float mixramp_overlap_current =
|
||||
mixramp_interpolate(mixramp_start,
|
||||
mixramp_db - replay_gain_db);
|
||||
const float mixramp_overlap_prev =
|
||||
mixramp_interpolate(mixramp_prev_end,
|
||||
mixramp_db - replay_gain_prev_db);
|
||||
const float mixramp_overlap =
|
||||
mixramp_overlap_current + mixramp_overlap_prev;
|
||||
|
||||
if (mixramp_overlap_current >= 0 &&
|
||||
mixramp_overlap_prev >= 0 &&
|
||||
mixramp_delay <= mixramp_overlap) {
|
||||
chunks = (chunks_f * (mixramp_overlap - mixramp_delay));
|
||||
g_debug("will overlap %d chunks, %fs", chunks,
|
||||
mixramp_overlap - mixramp_delay);
|
||||
FormatDebug(cross_fade_domain,
|
||||
"will overlap %d chunks, %fs", chunks,
|
||||
mixramp_overlap - mixramp_delay);
|
||||
}
|
||||
}
|
||||
|
||||
if (chunks > max_chunks) {
|
||||
chunks = max_chunks;
|
||||
g_warning("audio_buffer_size too small for computed MixRamp overlap");
|
||||
LogWarning(cross_fade_domain,
|
||||
"audio_buffer_size too small for computed MixRamp overlap");
|
||||
}
|
||||
|
||||
return chunks;
|
71
src/CrossFade.hxx
Normal file
71
src/CrossFade.hxx
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CROSSFADE_HXX
|
||||
#define MPD_CROSSFADE_HXX
|
||||
|
||||
#include "Compiler.h"
|
||||
|
||||
struct AudioFormat;
|
||||
|
||||
struct CrossFadeSettings {
|
||||
/**
|
||||
* The configured cross fade duration [s].
|
||||
*/
|
||||
float duration;
|
||||
|
||||
float mixramp_db;
|
||||
|
||||
/**
|
||||
* The configured MixRapm delay [s]. A non-positive value
|
||||
* disables MixRamp.
|
||||
*/
|
||||
float mixramp_delay;
|
||||
|
||||
CrossFadeSettings()
|
||||
:duration(0),
|
||||
mixramp_db(0),
|
||||
mixramp_delay(-1)
|
||||
{}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate how many music pipe chunks should be used for crossfading.
|
||||
*
|
||||
* @param total_time total_time the duration of the new song
|
||||
* @param replay_gain_db the ReplayGain adjustment used for this song
|
||||
* @param replay_gain_prev_db the ReplayGain adjustment used on the last song
|
||||
* @param mixramp_start the next songs mixramp_start tag
|
||||
* @param mixramp_prev_end the last songs mixramp_end setting
|
||||
* @param af the audio format of the new song
|
||||
* @param old_format the audio format of the current song
|
||||
* @param max_chunks the maximum number of chunks
|
||||
* @return the number of chunks for crossfading, or 0 if cross fading
|
||||
* should be disabled for this song change
|
||||
*/
|
||||
gcc_pure
|
||||
unsigned Calculate(float total_time,
|
||||
float replay_gain_db, float replay_gain_prev_db,
|
||||
const char *mixramp_start,
|
||||
const char *mixramp_prev_end,
|
||||
AudioFormat af, AudioFormat old_format,
|
||||
unsigned max_chunks) const;
|
||||
};
|
||||
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -18,14 +18,18 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "daemon.h"
|
||||
#include "Daemon.hxx"
|
||||
#include "system/FatalError.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -37,8 +41,7 @@
|
||||
#include <grp.h>
|
||||
#endif
|
||||
|
||||
#undef G_LOG_DOMAIN
|
||||
#define G_LOG_DOMAIN "daemon"
|
||||
static constexpr Domain daemon_domain("daemon");
|
||||
|
||||
#ifndef WIN32
|
||||
|
||||
@@ -49,10 +52,10 @@ static char *user_name;
|
||||
static uid_t user_uid = (uid_t)-1;
|
||||
|
||||
/** the Unix group id which MPD runs as */
|
||||
static gid_t user_gid = (pid_t)-1;
|
||||
static gid_t user_gid = (gid_t)-1;
|
||||
|
||||
/** the absolute path of the pidfile */
|
||||
static char *pidfile;
|
||||
static AllocatedPath pidfile = AllocatedPath::Null();
|
||||
|
||||
/* whether "group" conf. option was given */
|
||||
static bool had_group = false;
|
||||
@@ -64,24 +67,27 @@ daemonize_kill(void)
|
||||
FILE *fp;
|
||||
int pid, ret;
|
||||
|
||||
if (pidfile == NULL)
|
||||
MPD_ERROR("no pid_file specified in the config file");
|
||||
if (pidfile.IsNull())
|
||||
FatalError("no pid_file specified in the config file");
|
||||
|
||||
fp = fopen(pidfile, "r");
|
||||
if (fp == NULL)
|
||||
MPD_ERROR("unable to open pid file \"%s\": %s",
|
||||
pidfile, g_strerror(errno));
|
||||
fp = FOpen(pidfile, "r");
|
||||
if (fp == nullptr) {
|
||||
const std::string utf8 = pidfile.ToUTF8();
|
||||
FormatFatalSystemError("Unable to open pid file \"%s\"",
|
||||
utf8.c_str());
|
||||
}
|
||||
|
||||
if (fscanf(fp, "%i", &pid) != 1) {
|
||||
MPD_ERROR("unable to read the pid from file \"%s\"",
|
||||
pidfile);
|
||||
const std::string utf8 = pidfile.ToUTF8();
|
||||
FormatFatalError("unable to read the pid from file \"%s\"",
|
||||
utf8.c_str());
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
ret = kill(pid, SIGTERM);
|
||||
if (ret < 0)
|
||||
MPD_ERROR("unable to kill proccess %i: %s",
|
||||
pid, g_strerror(errno));
|
||||
FormatFatalSystemError("unable to kill process %i",
|
||||
int(pid));
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
@@ -96,33 +102,36 @@ daemonize_close_stdin(void)
|
||||
void
|
||||
daemonize_set_user(void)
|
||||
{
|
||||
if (user_name == NULL)
|
||||
if (user_name == nullptr)
|
||||
return;
|
||||
|
||||
/* set gid */
|
||||
if (user_gid != (gid_t)-1 && user_gid != getgid()) {
|
||||
if (setgid(user_gid) == -1) {
|
||||
MPD_ERROR("cannot setgid to %d: %s",
|
||||
(int)user_gid, g_strerror(errno));
|
||||
}
|
||||
if (user_gid != (gid_t)-1 && user_gid != getgid() &&
|
||||
setgid(user_gid) == -1) {
|
||||
FormatFatalSystemError("Failed to set group %d",
|
||||
(int)user_gid);
|
||||
}
|
||||
|
||||
#ifdef _BSD_SOURCE
|
||||
/* init suplementary groups
|
||||
/* init supplementary groups
|
||||
* (must be done before we change our uid)
|
||||
*/
|
||||
if (!had_group && initgroups(user_name, user_gid) == -1) {
|
||||
g_warning("cannot init supplementary groups "
|
||||
"of user \"%s\": %s",
|
||||
user_name, g_strerror(errno));
|
||||
if (!had_group &&
|
||||
/* no need to set the new user's supplementary groups if
|
||||
we are already this user */
|
||||
user_uid != getuid() &&
|
||||
initgroups(user_name, user_gid) == -1) {
|
||||
FormatFatalSystemError("Failed to set supplementary groups "
|
||||
"of user \"%s\"",
|
||||
user_name);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* set uid */
|
||||
if (user_uid != (uid_t)-1 && user_uid != getuid() &&
|
||||
setuid(user_uid) == -1) {
|
||||
MPD_ERROR("cannot change to uid of user \"%s\": %s",
|
||||
user_name, g_strerror(errno));
|
||||
FormatFatalSystemError("Failed to set user \"%s\"",
|
||||
user_name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,12 +140,12 @@ daemonize_detach(void)
|
||||
{
|
||||
/* flush all file handles before duplicating the buffers */
|
||||
|
||||
fflush(NULL);
|
||||
fflush(nullptr);
|
||||
|
||||
#ifdef HAVE_DAEMON
|
||||
|
||||
if (daemon(0, 1))
|
||||
MPD_ERROR("daemon() failed: %s", g_strerror(errno));
|
||||
FatalSystemError("daemon() failed");
|
||||
|
||||
#elif defined(HAVE_FORK)
|
||||
|
||||
@@ -144,7 +153,7 @@ daemonize_detach(void)
|
||||
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
MPD_ERROR("fork() failed: %s", g_strerror(errno));
|
||||
FatalSystemError("fork() failed");
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
@@ -155,52 +164,53 @@ daemonize_detach(void)
|
||||
/* release the current working directory */
|
||||
|
||||
if (chdir("/") < 0)
|
||||
MPD_ERROR("problems changing to root directory");
|
||||
FatalError("problems changing to root directory");
|
||||
|
||||
/* detach from the current session */
|
||||
|
||||
setsid();
|
||||
|
||||
#else
|
||||
MPD_ERROR("no support for daemonizing");
|
||||
FatalError("no support for daemonizing");
|
||||
#endif
|
||||
|
||||
g_debug("daemonized!");
|
||||
LogDebug(daemon_domain, "daemonized");
|
||||
}
|
||||
|
||||
void
|
||||
daemonize(bool detach)
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
FILE *fp = nullptr;
|
||||
|
||||
if (pidfile != NULL) {
|
||||
if (!pidfile.IsNull()) {
|
||||
/* do this before daemon'izing so we can fail gracefully if we can't
|
||||
* write to the pid file */
|
||||
g_debug("opening pid file");
|
||||
fp = fopen(pidfile, "w+");
|
||||
LogDebug(daemon_domain, "opening pid file");
|
||||
fp = FOpen(pidfile, "w+");
|
||||
if (!fp) {
|
||||
MPD_ERROR("could not create pid file \"%s\": %s",
|
||||
pidfile, g_strerror(errno));
|
||||
const std::string utf8 = pidfile.ToUTF8();
|
||||
FormatFatalSystemError("Failed to create pid file \"%s\"",
|
||||
pidfile.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (detach)
|
||||
daemonize_detach();
|
||||
|
||||
if (pidfile != NULL) {
|
||||
g_debug("writing pid file");
|
||||
if (!pidfile.IsNull()) {
|
||||
LogDebug(daemon_domain, "writing pid file");
|
||||
fprintf(fp, "%lu\n", (unsigned long)getpid());
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
daemonize_init(const char *user, const char *group, const char *_pidfile)
|
||||
daemonize_init(const char *user, const char *group, AllocatedPath &&_pidfile)
|
||||
{
|
||||
if (user) {
|
||||
struct passwd *pwd = getpwnam(user);
|
||||
if (!pwd)
|
||||
MPD_ERROR("no such user \"%s\"", user);
|
||||
if (pwd == nullptr)
|
||||
FormatFatalError("no such user \"%s\"", user);
|
||||
|
||||
user_uid = pwd->pw_uid;
|
||||
user_gid = pwd->pw_gid;
|
||||
@@ -212,25 +222,26 @@ daemonize_init(const char *user, const char *group, const char *_pidfile)
|
||||
}
|
||||
|
||||
if (group) {
|
||||
struct group *grp = grp = getgrnam(group);
|
||||
if (!grp)
|
||||
MPD_ERROR("no such group \"%s\"", group);
|
||||
struct group *grp = getgrnam(group);
|
||||
if (grp == nullptr)
|
||||
FormatFatalError("no such group \"%s\"", group);
|
||||
user_gid = grp->gr_gid;
|
||||
had_group = true;
|
||||
}
|
||||
|
||||
|
||||
pidfile = g_strdup(_pidfile);
|
||||
pidfile = std::move(_pidfile);
|
||||
}
|
||||
|
||||
void
|
||||
daemonize_finish(void)
|
||||
{
|
||||
if (pidfile != NULL)
|
||||
unlink(pidfile);
|
||||
if (!pidfile.IsNull()) {
|
||||
RemoveFile(pidfile);
|
||||
pidfile = AllocatedPath::Null();
|
||||
}
|
||||
|
||||
g_free(user_name);
|
||||
g_free(pidfile);
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,19 +17,17 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef DAEMON_H
|
||||
#define DAEMON_H
|
||||
#ifndef MPD_DAEMON_HXX
|
||||
#define MPD_DAEMON_HXX
|
||||
|
||||
#include "mpd_error.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
class AllocatedPath;
|
||||
|
||||
#ifndef WIN32
|
||||
void
|
||||
daemonize_init(const char *user, const char *group, const char *pidfile);
|
||||
daemonize_init(const char *user, const char *group, AllocatedPath &&pidfile);
|
||||
#else
|
||||
static inline void
|
||||
daemonize_init(const char *user, const char *group, const char *pidfile)
|
||||
daemonize_init(const char *user, const char *group, AllocatedPath &&pidfile)
|
||||
{ (void)user; (void)group; (void)pidfile; }
|
||||
#endif
|
||||
|
||||
@@ -50,10 +48,12 @@ daemonize_finish(void)
|
||||
void
|
||||
daemonize_kill(void);
|
||||
#else
|
||||
#include <glib.h>
|
||||
#include "system/FatalError.hxx"
|
||||
static inline void
|
||||
daemonize_kill(void)
|
||||
{ MPD_ERROR("--kill is not available on WIN32"); }
|
||||
{
|
||||
FatalError("--kill is not available on WIN32");
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
24
src/DatabaseError.cxx
Normal file
24
src/DatabaseError.cxx
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "DatabaseError.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
|
||||
const Domain db_domain("db");
|
37
src/DatabaseError.hxx
Normal file
37
src/DatabaseError.hxx
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_DB_ERROR_HXX
|
||||
#define MPD_DB_ERROR_HXX
|
||||
|
||||
class Domain;
|
||||
|
||||
enum db_error {
|
||||
/**
|
||||
* The database is disabled, i.e. none is configured in this
|
||||
* MPD instance.
|
||||
*/
|
||||
DB_DISABLED,
|
||||
|
||||
DB_NOT_FOUND,
|
||||
};
|
||||
|
||||
extern const Domain db_domain;
|
||||
|
||||
#endif
|
159
src/DatabaseGlue.cxx
Normal file
159
src/DatabaseGlue.cxx
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "DatabaseGlue.hxx"
|
||||
#include "DatabaseSimple.hxx"
|
||||
#include "DatabaseRegistry.hxx"
|
||||
#include "DatabaseSave.hxx"
|
||||
#include "DatabaseError.hxx"
|
||||
#include "Directory.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "ConfigData.hxx"
|
||||
#include "Stats.hxx"
|
||||
#include "DatabasePlugin.hxx"
|
||||
#include "db/SimpleDatabasePlugin.hxx"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
static Database *db;
|
||||
static bool db_is_open;
|
||||
static bool is_simple;
|
||||
|
||||
bool
|
||||
DatabaseGlobalInit(const config_param ¶m, Error &error)
|
||||
{
|
||||
assert(db == nullptr);
|
||||
assert(!db_is_open);
|
||||
|
||||
const char *plugin_name =
|
||||
param.GetBlockValue("plugin", "simple");
|
||||
is_simple = strcmp(plugin_name, "simple") == 0;
|
||||
|
||||
const DatabasePlugin *plugin = GetDatabasePluginByName(plugin_name);
|
||||
if (plugin == nullptr) {
|
||||
error.Format(db_domain,
|
||||
"No such database plugin: %s", plugin_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
db = plugin->create(param, error);
|
||||
return db != nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
DatabaseGlobalDeinit(void)
|
||||
{
|
||||
if (db_is_open)
|
||||
db->Close();
|
||||
|
||||
if (db != nullptr)
|
||||
delete db;
|
||||
}
|
||||
|
||||
const Database *
|
||||
GetDatabase()
|
||||
{
|
||||
assert(db == nullptr || db_is_open);
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
const Database *
|
||||
GetDatabase(Error &error)
|
||||
{
|
||||
assert(db == nullptr || db_is_open);
|
||||
|
||||
if (db == nullptr)
|
||||
error.Set(db_domain, DB_DISABLED, "No database");
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
bool
|
||||
db_is_simple(void)
|
||||
{
|
||||
assert(db == nullptr || db_is_open);
|
||||
|
||||
return is_simple;
|
||||
}
|
||||
|
||||
Directory *
|
||||
db_get_root(void)
|
||||
{
|
||||
assert(db != nullptr);
|
||||
assert(db_is_simple());
|
||||
|
||||
return ((SimpleDatabase *)db)->GetRoot();
|
||||
}
|
||||
|
||||
Directory *
|
||||
db_get_directory(const char *name)
|
||||
{
|
||||
if (db == nullptr)
|
||||
return nullptr;
|
||||
|
||||
Directory *music_root = db_get_root();
|
||||
if (name == nullptr)
|
||||
return music_root;
|
||||
|
||||
return music_root->LookupDirectory(name);
|
||||
}
|
||||
|
||||
bool
|
||||
db_save(Error &error)
|
||||
{
|
||||
assert(db != nullptr);
|
||||
assert(db_is_open);
|
||||
assert(db_is_simple());
|
||||
|
||||
return ((SimpleDatabase *)db)->Save(error);
|
||||
}
|
||||
|
||||
bool
|
||||
DatabaseGlobalOpen(Error &error)
|
||||
{
|
||||
assert(db != nullptr);
|
||||
assert(!db_is_open);
|
||||
|
||||
if (!db->Open(error))
|
||||
return false;
|
||||
|
||||
db_is_open = true;
|
||||
|
||||
stats_update();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
db_exists()
|
||||
{
|
||||
assert(db != nullptr);
|
||||
assert(db_is_open);
|
||||
assert(db_is_simple());
|
||||
|
||||
return ((SimpleDatabase *)db)->GetUpdateStamp() > 0;
|
||||
}
|
59
src/DatabaseGlue.hxx
Normal file
59
src/DatabaseGlue.hxx
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_DATABASE_GLUE_HXX
|
||||
#define MPD_DATABASE_GLUE_HXX
|
||||
|
||||
#include "Compiler.h"
|
||||
|
||||
struct config_param;
|
||||
class Database;
|
||||
class Error;
|
||||
|
||||
/**
|
||||
* Initialize the database library.
|
||||
*
|
||||
* @param param the database configuration block
|
||||
*/
|
||||
bool
|
||||
DatabaseGlobalInit(const config_param ¶m, Error &error);
|
||||
|
||||
void
|
||||
DatabaseGlobalDeinit(void);
|
||||
|
||||
bool
|
||||
DatabaseGlobalOpen(Error &error);
|
||||
|
||||
/**
|
||||
* Returns the global #Database instance. May return nullptr if this MPD
|
||||
* configuration has no database (no music_directory was configured).
|
||||
*/
|
||||
gcc_pure
|
||||
const Database *
|
||||
GetDatabase();
|
||||
|
||||
/**
|
||||
* Returns the global #Database instance. May return nullptr if this MPD
|
||||
* configuration has no database (no music_directory was configured).
|
||||
*/
|
||||
gcc_pure
|
||||
const Database *
|
||||
GetDatabase(Error &error);
|
||||
|
||||
#endif
|
134
src/DatabaseHelpers.cxx
Normal file
134
src/DatabaseHelpers.cxx
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "DatabaseHelpers.hxx"
|
||||
#include "DatabasePlugin.hxx"
|
||||
#include "Song.hxx"
|
||||
#include "tag/Tag.hxx"
|
||||
|
||||
#include <functional>
|
||||
#include <set>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
struct StringLess {
|
||||
gcc_pure
|
||||
bool operator()(const char *a, const char *b) const {
|
||||
return strcmp(a, b) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::set<const char *, StringLess> StringSet;
|
||||
|
||||
static bool
|
||||
CollectTags(StringSet &set, TagType tag_type, Song &song)
|
||||
{
|
||||
Tag *tag = song.tag;
|
||||
if (tag == nullptr)
|
||||
return true;
|
||||
|
||||
bool found = false;
|
||||
for (unsigned i = 0; i < tag->num_items; ++i) {
|
||||
if (tag->items[i]->type == tag_type) {
|
||||
set.insert(tag->items[i]->value);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
set.insert("");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
VisitUniqueTags(const Database &db, const DatabaseSelection &selection,
|
||||
TagType tag_type,
|
||||
VisitString visit_string,
|
||||
Error &error)
|
||||
{
|
||||
StringSet set;
|
||||
|
||||
using namespace std::placeholders;
|
||||
const auto f = std::bind(CollectTags, std::ref(set), tag_type, _1);
|
||||
if (!db.Visit(selection, f, error))
|
||||
return false;
|
||||
|
||||
for (auto value : set)
|
||||
if (!visit_string(value, error))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
StatsVisitTag(DatabaseStats &stats, StringSet &artists, StringSet &albums,
|
||||
const Tag &tag)
|
||||
{
|
||||
if (tag.time > 0)
|
||||
stats.total_duration += tag.time;
|
||||
|
||||
for (unsigned i = 0; i < tag.num_items; ++i) {
|
||||
const TagItem &item = *tag.items[i];
|
||||
|
||||
switch (item.type) {
|
||||
case TAG_ARTIST:
|
||||
artists.insert(item.value);
|
||||
break;
|
||||
|
||||
case TAG_ALBUM:
|
||||
albums.insert(item.value);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
StatsVisitSong(DatabaseStats &stats, StringSet &artists, StringSet &albums,
|
||||
Song &song)
|
||||
{
|
||||
++stats.song_count;
|
||||
|
||||
if (song.tag != nullptr)
|
||||
StatsVisitTag(stats, artists, albums, *song.tag);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
GetStats(const Database &db, const DatabaseSelection &selection,
|
||||
DatabaseStats &stats, Error &error)
|
||||
{
|
||||
stats.Clear();
|
||||
|
||||
StringSet artists, albums;
|
||||
using namespace std::placeholders;
|
||||
const auto f = std::bind(StatsVisitSong,
|
||||
std::ref(stats), std::ref(artists),
|
||||
std::ref(albums), _1);
|
||||
if (!db.Visit(selection, f, error))
|
||||
return false;
|
||||
|
||||
stats.artist_count = artists.size();
|
||||
stats.album_count = albums.size();
|
||||
return true;
|
||||
}
|
42
src/DatabaseHelpers.hxx
Normal file
42
src/DatabaseHelpers.hxx
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_MEMORY_DATABASE_PLUGIN_HXX
|
||||
#define MPD_MEMORY_DATABASE_PLUGIN_HXX
|
||||
|
||||
#include "DatabaseVisitor.hxx"
|
||||
#include "tag/TagType.h"
|
||||
#include "Compiler.h"
|
||||
|
||||
class Error;
|
||||
class Database;
|
||||
struct DatabaseSelection;
|
||||
struct DatabaseStats;
|
||||
|
||||
bool
|
||||
VisitUniqueTags(const Database &db, const DatabaseSelection &selection,
|
||||
TagType tag_type,
|
||||
VisitString visit_string,
|
||||
Error &error);
|
||||
|
||||
bool
|
||||
GetStats(const Database &db, const DatabaseSelection &selection,
|
||||
DatabaseStats &stats, Error &error);
|
||||
|
||||
#endif
|
28
src/DatabaseLock.cxx
Normal file
28
src/DatabaseLock.cxx
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "DatabaseLock.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
Mutex db_mutex;
|
||||
|
||||
#ifndef NDEBUG
|
||||
ThreadId db_mutex_holder;
|
||||
#endif
|
97
src/DatabaseLock.hxx
Normal file
97
src/DatabaseLock.hxx
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
*
|
||||
* Support for locking data structures from the database, for safe
|
||||
* multi-threading.
|
||||
*/
|
||||
|
||||
#ifndef MPD_DB_LOCK_HXX
|
||||
#define MPD_DB_LOCK_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "thread/Mutex.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
extern Mutex db_mutex;
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
#include "thread/Id.hxx"
|
||||
|
||||
extern ThreadId db_mutex_holder;
|
||||
|
||||
/**
|
||||
* Does the current thread hold the database lock?
|
||||
*/
|
||||
gcc_pure
|
||||
static inline bool
|
||||
holding_db_lock(void)
|
||||
{
|
||||
return db_mutex_holder.IsInside();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Obtain the global database lock. This is needed before
|
||||
* dereferencing a #song or #directory. It is not recursive.
|
||||
*/
|
||||
static inline void
|
||||
db_lock(void)
|
||||
{
|
||||
assert(!holding_db_lock());
|
||||
|
||||
db_mutex.lock();
|
||||
|
||||
assert(db_mutex_holder.IsNull());
|
||||
#ifndef NDEBUG
|
||||
db_mutex_holder = ThreadId::GetCurrent();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the global database lock.
|
||||
*/
|
||||
static inline void
|
||||
db_unlock(void)
|
||||
{
|
||||
assert(holding_db_lock());
|
||||
#ifndef NDEBUG
|
||||
db_mutex_holder = ThreadId::Null();
|
||||
#endif
|
||||
|
||||
db_mutex.unlock();
|
||||
}
|
||||
|
||||
class ScopeDatabaseLock {
|
||||
public:
|
||||
ScopeDatabaseLock() {
|
||||
db_lock();
|
||||
}
|
||||
|
||||
~ScopeDatabaseLock() {
|
||||
db_unlock();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user