Compare commits
1321 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
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 | ||
![]() |
d0c3fa0150 | ||
![]() |
609f6ce66d | ||
![]() |
7298b6c846 | ||
![]() |
acec9c4fd7 | ||
![]() |
9cdbde4f5e | ||
![]() |
f9147a0706 | ||
![]() |
33364edfb3 | ||
![]() |
5e9ccdec63 | ||
![]() |
071aca60be | ||
![]() |
133e4d5c51 | ||
![]() |
fd56461d5f | ||
![]() |
d5f59f8c86 | ||
![]() |
a5d8b27671 | ||
![]() |
f3f25d5d40 | ||
![]() |
f118812c34 | ||
![]() |
98dbc95913 | ||
![]() |
145c1d214c | ||
![]() |
b62a8e3e3e | ||
![]() |
e47240931a | ||
![]() |
a84874426d | ||
![]() |
f38dfd9231 | ||
![]() |
510c4a3ef1 | ||
![]() |
e7e52c24f5 | ||
![]() |
e2a574e2a0 | ||
![]() |
1536b5a9d6 | ||
![]() |
bab756a5d0 | ||
![]() |
8c763fe458 | ||
![]() |
886255e38a | ||
![]() |
0240e75426 | ||
![]() |
7102ed8026 | ||
![]() |
ebf481e1a1 | ||
![]() |
5a52e91350 | ||
![]() |
18e458154a | ||
![]() |
015e1ab183 | ||
![]() |
2a4c799471 | ||
![]() |
333d226ed0 | ||
![]() |
04a9dec952 | ||
![]() |
6ee76b7154 | ||
![]() |
36b1c08ad7 | ||
![]() |
7b35d5e9d4 | ||
![]() |
6d4ca071a5 | ||
![]() |
684a3d30a6 | ||
![]() |
25740d27bf | ||
![]() |
fb0a52a34a | ||
![]() |
c852970cf6 | ||
![]() |
def21cc87e | ||
![]() |
488c1eb87b | ||
![]() |
e8df7e8da5 | ||
![]() |
af4252bc80 | ||
![]() |
695ffedef9 | ||
![]() |
1c3e03837c | ||
![]() |
7968fa3779 | ||
![]() |
31009bb1f6 | ||
![]() |
3b8532f3fb | ||
![]() |
f45616e5f6 | ||
![]() |
e391f4b17b | ||
![]() |
2bd344549b | ||
![]() |
e96779de48 | ||
![]() |
eb54337c40 | ||
![]() |
916a020173 | ||
![]() |
c2e4fe983d | ||
![]() |
81e898375b | ||
![]() |
3c0dea811d | ||
![]() |
a6ac0f8965 | ||
![]() |
4e1eb03287 | ||
![]() |
fcf0f8291d | ||
![]() |
0ed71fa027 | ||
![]() |
affe21f7c1 | ||
![]() |
9facf7897d | ||
![]() |
8422402c39 | ||
![]() |
b5fde6dfa5 | ||
![]() |
784d666a8e | ||
![]() |
c035910df9 | ||
![]() |
efccb6ac82 | ||
![]() |
7d27d2ea5e | ||
![]() |
5cc3338267 | ||
![]() |
3047bdf653 | ||
![]() |
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 |
17
.gitignore
vendored
17
.gitignore
vendored
@@ -1,12 +1,11 @@
|
||||
*.Plo
|
||||
*.Po
|
||||
*.a
|
||||
*.bz2
|
||||
*.d
|
||||
*.gz
|
||||
*.la
|
||||
*.lo
|
||||
*.o
|
||||
*.exe
|
||||
.deps
|
||||
.dirstamp
|
||||
Makefile
|
||||
@@ -33,7 +32,6 @@ ltmain.sh
|
||||
missing
|
||||
mkinstalldirs
|
||||
mpd
|
||||
mpd.exe
|
||||
mpd.service
|
||||
stamp-h1
|
||||
tags
|
||||
@@ -41,6 +39,7 @@ tags
|
||||
.#*
|
||||
.stgit*
|
||||
src/dsd2pcm/dsd2pcm
|
||||
src/win/mpd_win32_rc.rc
|
||||
doc/doxygen.conf
|
||||
doc/protocol.html
|
||||
doc/protocol
|
||||
@@ -67,3 +66,15 @@ 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>
|
||||
|
51
INSTALL
51
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.16 - 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,14 +75,17 @@ Alternative for MP3 support.
|
||||
Ogg Vorbis - http://www.xiph.org/ogg/vorbis/
|
||||
For Ogg Vorbis support. You will need libogg and libvorbis.
|
||||
|
||||
libopus - http://www.opus-codec.org/
|
||||
Opus codec support
|
||||
|
||||
FLAC - http://flac.sourceforge.net/
|
||||
For FLAC support. You will need version 1.1.0 or higher of libflac.
|
||||
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.
|
||||
@@ -105,7 +102,7 @@ For C64 SID support.
|
||||
libfluidsynth - http://fluidsynth.resonance.org/
|
||||
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/
|
||||
@@ -114,6 +111,9 @@ 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.
|
||||
|
||||
@@ -163,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
|
||||
|
||||
@@ -196,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.
|
||||
|
||||
@@ -210,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/
|
||||
|
1529
Makefile.am
1529
Makefile.am
File diff suppressed because it is too large
Load Diff
117
NEWS
117
NEWS
@@ -1,3 +1,120 @@
|
||||
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
|
||||
|
418
configure.ac
418
configure.ac
@@ -1,19 +1,19 @@
|
||||
AC_PREREQ(2.60)
|
||||
|
||||
AC_INIT(mpd, 0.17.2, musicpd-dev-team@lists.sourceforge.net)
|
||||
AC_INIT(mpd, 0.18.3, musicpd-dev-team@lists.sourceforge.net)
|
||||
|
||||
VERSION_MAJOR=0
|
||||
VERSION_MINOR=17
|
||||
VERSION_MINOR=18
|
||||
VERSION_REVISION=0
|
||||
VERSION_EXTRA=0
|
||||
|
||||
AC_CONFIG_SRCDIR([src/main.c])
|
||||
AM_INIT_AUTOMAKE([foreign 1.11 dist-bzip2 subdir-objects])
|
||||
AC_CONFIG_SRCDIR([src/Main.cxx])
|
||||
AM_INIT_AUTOMAKE([foreign 1.11 dist-xz subdir-objects])
|
||||
AM_SILENT_RULES
|
||||
AC_CONFIG_HEADERS(config.h)
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
AC_DEFINE(PROTOCOL_VERSION, "0.17.0", [The MPD protocol version])
|
||||
AC_DEFINE(PROTOCOL_VERSION, "0.18.0", [The MPD protocol version])
|
||||
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
@@ -23,21 +23,10 @@ AC_PROG_CC_C99
|
||||
AC_PROG_CXX
|
||||
AC_PROG_RANLIB
|
||||
|
||||
HAVE_CXX=yes
|
||||
if test x$CXX = xg++; then
|
||||
# CXX=g++ probably means that autoconf hasn't found any C++
|
||||
# compiler; to be sure, we check again
|
||||
AC_PATH_PROG(CXX, $CXX, no)
|
||||
if test x$CXX = xno; then
|
||||
# no, we don't have C++ - the following hack is
|
||||
# required because automake insists on using $(CXX)
|
||||
# for linking the MPD binary
|
||||
AC_MSG_NOTICE([Disabling C++ support])
|
||||
CXX="$CC"
|
||||
HAVE_CXX=no
|
||||
fi
|
||||
fi
|
||||
AM_CONDITIONAL(HAVE_CXX, test x$HAVE_CXX = xyes)
|
||||
AN_MAKEVAR([AR], [AC_PROG_AR])
|
||||
AN_PROGRAM([ar], [AC_PROG_AR])
|
||||
AC_DEFUN([AC_PROG_AR], [AC_CHECK_TOOL(AR, ar, :)])
|
||||
AC_PROG_AR
|
||||
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_MAKE_SET
|
||||
@@ -76,16 +65,23 @@ dnl OS Specific Defaults
|
||||
dnl ---------------------------------------------------------------------------
|
||||
AC_CANONICAL_HOST
|
||||
|
||||
host_is_darwin=no
|
||||
|
||||
case "$host_os" in
|
||||
mingw32* | windows*)
|
||||
AC_CONFIG_FILES([
|
||||
src/win/mpd_win32_rc.rc
|
||||
])
|
||||
AC_CHECK_TOOL(WINDRES, windres)
|
||||
AM_CPPFLAGS="$AM_CPPFLAGS -DWINVER=0x0501"
|
||||
AM_CPPFLAGS="$AM_CPPFLAGS -DWIN32_LEAN_AND_MEAN"
|
||||
AM_CPPFLAGS="$AM_CPPFLAGS -DWINVER=0x0600 -D_WIN32_WINNT=0x0600"
|
||||
LIBS="$LIBS -lws2_32"
|
||||
HAVE_WINDOWS=1
|
||||
;;
|
||||
|
||||
darwin*)
|
||||
host_is_darwin=yes
|
||||
;;
|
||||
esac
|
||||
AM_CONDITIONAL([HAVE_WINDOWS], [test x$HAVE_WINDOWS = x1])
|
||||
|
||||
@@ -125,6 +121,19 @@ if test -z "$prefix" || test "x$prefix" = xNONE; then
|
||||
done
|
||||
fi
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Language Checks
|
||||
dnl ---------------------------------------------------------------------------
|
||||
|
||||
AC_CXX_COMPILE_STDCXX_0X
|
||||
if test "$ax_cv_cxx_compile_cxx0x_native" != yes; then
|
||||
if test "$ax_cv_cxx_compile_cxx0x_gxx" = yes; then
|
||||
AM_CXXFLAGS="$AM_CXXFLAGS -std=gnu++0x"
|
||||
elif test "$ax_cv_cxx_compile_cxx0x_cxx" = yes; then
|
||||
AM_CXXFLAGS="$AM_CXXFLAGS -std=c++0x"
|
||||
fi
|
||||
fi
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Header/Library Checks
|
||||
dnl ---------------------------------------------------------------------------
|
||||
@@ -137,6 +146,9 @@ AC_SEARCH_LIBS([socket], [socket])
|
||||
AC_SEARCH_LIBS([gethostbyname], [nsl])
|
||||
|
||||
AC_CHECK_FUNCS(pipe2 accept4)
|
||||
MPD_OPTIONAL_FUNC(eventfd, eventfd, USE_EVENTFD)
|
||||
MPD_OPTIONAL_FUNC(signalfd, signalfd, USE_SIGNALFD)
|
||||
MPD_OPTIONAL_FUNC(epoll, epoll_create1, USE_EPOLL)
|
||||
|
||||
AC_SEARCH_LIBS([exp], [m],,
|
||||
[AC_MSG_ERROR([exp() not found])])
|
||||
@@ -147,6 +159,17 @@ AC_CHECK_HEADERS(valgrind/memcheck.h)
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Allow tools to be specifically built
|
||||
dnl ---------------------------------------------------------------------------
|
||||
|
||||
AC_ARG_ENABLE(libmpdclient,
|
||||
AS_HELP_STRING([--enable-libmpdclient],
|
||||
[enable support for the MPD client]),,
|
||||
enable_libmpdclient=auto)
|
||||
|
||||
AC_ARG_ENABLE(adplug,
|
||||
AS_HELP_STRING([--enable-adplug],
|
||||
[enable the AdPlug decoder plugin (default: auto)]),,
|
||||
enable_adplug=auto)
|
||||
|
||||
AC_ARG_ENABLE(alsa,
|
||||
AS_HELP_STRING([--enable-alsa], [enable ALSA support]),,
|
||||
[enable_alsa=auto])
|
||||
@@ -168,8 +191,8 @@ AC_ARG_ENABLE(audiofile,
|
||||
|
||||
AC_ARG_ENABLE(bzip2,
|
||||
AS_HELP_STRING([--enable-bzip2],
|
||||
[enable bzip2 archive support (default: disabled)]),,
|
||||
enable_bzip2=no)
|
||||
[enable bzip2 archive support (default: auto)]),,
|
||||
enable_bzip2=auto)
|
||||
|
||||
AC_ARG_ENABLE(cdio-paranoia,
|
||||
AS_HELP_STRING([--enable-cdio-paranoia],
|
||||
@@ -181,11 +204,6 @@ AC_ARG_ENABLE(curl,
|
||||
[enable support for libcurl HTTP streaming (default: auto)]),,
|
||||
[enable_curl=auto])
|
||||
|
||||
AC_ARG_ENABLE(soup,
|
||||
AS_HELP_STRING([--enable-soup],
|
||||
[enable support for libsoup HTTP streaming (default: auto)]),,
|
||||
[enable_soup=auto])
|
||||
|
||||
AC_ARG_ENABLE(debug,
|
||||
AS_HELP_STRING([--enable-debug],
|
||||
[enable debugging (default: disabled)]),,
|
||||
@@ -196,10 +214,6 @@ AC_ARG_ENABLE(documentation,
|
||||
[build documentation (default: disable)]),,
|
||||
[enable_documentation=no])
|
||||
|
||||
AC_ARG_ENABLE(ffado,
|
||||
AS_HELP_STRING([--enable-ffado], [enable libffado (FireWire) support]),,
|
||||
[enable_ffado=no])
|
||||
|
||||
AC_ARG_ENABLE(ffmpeg,
|
||||
AS_HELP_STRING([--enable-ffmpeg],
|
||||
[enable FFMPEG support]),,
|
||||
@@ -257,11 +271,6 @@ AC_ARG_ENABLE(jack,
|
||||
|
||||
AC_SYS_LARGEFILE
|
||||
|
||||
AC_ARG_ENABLE(lastfm,
|
||||
AS_HELP_STRING([--enable-lastfm],
|
||||
[enable support for last.fm radio (default: disable)]),,
|
||||
[enable_lastfm=no])
|
||||
|
||||
AC_ARG_ENABLE(despotify,
|
||||
AS_HELP_STRING([--enable-despotify],
|
||||
[enable support for despotify (default: disable)]),,
|
||||
@@ -307,30 +316,35 @@ AC_ARG_ENABLE(modplug,
|
||||
enable_modplug=auto)
|
||||
|
||||
AC_ARG_ENABLE(mpc,
|
||||
AS_HELP_STRING([--disable-mpc],
|
||||
[disable musepack (MPC) support (default: enable)]),,
|
||||
enable_mpc=yes)
|
||||
AS_HELP_STRING([--enable-mpc],
|
||||
[disable musepack (MPC) support (default: auto)]),,
|
||||
enable_mpc=auto)
|
||||
|
||||
AC_ARG_ENABLE(mpg123,
|
||||
AS_HELP_STRING([--enable-mpg123],
|
||||
[enable libmpg123 decoder plugin]),,
|
||||
enable_mpg123=auto)
|
||||
|
||||
AC_ARG_ENABLE(mvp,
|
||||
AS_HELP_STRING([--enable-mvp],
|
||||
[enable support for Hauppauge Media MVP (default: disable)]),,
|
||||
enable_mvp=no)
|
||||
|
||||
AC_ARG_ENABLE(openal,
|
||||
AS_HELP_STRING([--enable-openal],
|
||||
[enable OpenAL support (default: disable)]),,
|
||||
enable_openal=no)
|
||||
[enable OpenAL support (default: auto)]),,
|
||||
enable_openal=auto)
|
||||
|
||||
AC_ARG_ENABLE(opus,
|
||||
AS_HELP_STRING([--enable-opus],
|
||||
[enable Opus codec support (default: auto)]),,
|
||||
enable_opus=auto)
|
||||
|
||||
AC_ARG_ENABLE(oss,
|
||||
AS_HELP_STRING([--disable-oss],
|
||||
[disable OSS support (default: enable)]),,
|
||||
enable_oss=yes)
|
||||
|
||||
AC_ARG_ENABLE(osx,
|
||||
AS_HELP_STRING([--enable-osx],
|
||||
[enable the OS X output plugin - unsupported! (default: disable)]),,
|
||||
enable_osx=no)
|
||||
|
||||
AC_ARG_ENABLE(pipe-output,
|
||||
AS_HELP_STRING([--enable-pipe-output],
|
||||
[enable support for writing audio to a pipe (default: disable)]),,
|
||||
@@ -429,8 +443,8 @@ AC_ARG_ENABLE(werror,
|
||||
|
||||
AC_ARG_ENABLE(wildmidi,
|
||||
AS_HELP_STRING([--enable-wildmidi],
|
||||
[enable MIDI support via wildmidi (default: disable)]),,
|
||||
enable_wildmidi=no)
|
||||
[enable MIDI support via wildmidi (default: auto)]),,
|
||||
enable_wildmidi=auto)
|
||||
|
||||
AC_ARG_WITH(zeroconf,
|
||||
AS_HELP_STRING([--with-zeroconf=@<:@auto|avahi|bonjour|no@:>@],
|
||||
@@ -456,8 +470,8 @@ AC_ARG_WITH(tremor-includes,
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Mandatory Libraries
|
||||
dnl ---------------------------------------------------------------------------
|
||||
PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.16 gthread-2.0],,
|
||||
[AC_MSG_ERROR([GLib 2.16 is required])])
|
||||
PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.28 gthread-2.0],,
|
||||
[AC_MSG_ERROR([GLib 2.28 is required])])
|
||||
|
||||
if test x$GCC = xyes; then
|
||||
# suppress warnings in the GLib headers
|
||||
@@ -537,6 +551,15 @@ dnl ---------------------------------------------------------------------------
|
||||
dnl Miscellaneous Libraries
|
||||
dnl ---------------------------------------------------------------------------
|
||||
|
||||
dnl -------------------------------- libmpdclient --------------------------------
|
||||
MPD_AUTO_PKG(libmpdclient, LIBMPDCLIENT, [libmpdclient >= 2.2],
|
||||
[MPD client library], [libmpdclient not found])
|
||||
if test x$enable_libmpdclient = xyes; then
|
||||
AC_DEFINE(HAVE_LIBMPDCLIENT, 1, [Define to use libmpdclient])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_LIBMPDCLIENT, test x$enable_libmpdclient = xyes)
|
||||
|
||||
dnl --------------------------------- inotify ---------------------------------
|
||||
AC_CHECK_FUNCS(inotify_init inotify_init1)
|
||||
|
||||
@@ -552,6 +575,27 @@ AM_CONDITIONAL(ENABLE_INOTIFY, test x$enable_inotify = xyes)
|
||||
dnl --------------------------------- libwrap ---------------------------------
|
||||
if test x$enable_libwrap != xno; then
|
||||
AC_CHECK_LIBWRAP(found_libwrap=yes, found_libwrap=no)
|
||||
|
||||
if test x$found_libwrap = xyes; then
|
||||
dnl See if libwrap is compatible with C++; it is
|
||||
dnl broken on many systems
|
||||
AC_MSG_CHECKING(whether libwrap is compatible with C++)
|
||||
AC_LANG_PUSH([C++])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||
#include <tcpd.h>
|
||||
bool CheckLibWrap(int fd, const char &progname) {
|
||||
struct request_info req;
|
||||
request_init(&req, RQ_FILE, fd, RQ_DAEMON, progname, 0);
|
||||
fromhost(&req);
|
||||
return hosts_access(&req);
|
||||
}
|
||||
])],
|
||||
AC_MSG_RESULT([yes]),
|
||||
[found_libwrap=no; AC_MSG_RESULT([no]);
|
||||
AC_MSG_WARN([Your version of libwrap is broken with C++])])
|
||||
AC_LANG_POP
|
||||
fi
|
||||
|
||||
MPD_AUTO_RESULT(libwrap, libwrap, [libwrap not found])
|
||||
fi
|
||||
|
||||
@@ -595,8 +639,8 @@ avahi)
|
||||
;;
|
||||
esac
|
||||
|
||||
MPD_AUTO_PKG(avahi, AVAHI, [avahi-client avahi-glib],
|
||||
[avahi client library], [avahi client+glib not found])
|
||||
MPD_AUTO_PKG(avahi, AVAHI, [avahi-client],
|
||||
[avahi client library], [avahi-client not found])
|
||||
if test x$enable_avahi = xyes; then
|
||||
AC_DEFINE([HAVE_AVAHI], 1, [Define to enable Avahi Zeroconf support])
|
||||
with_zeroconf=avahi
|
||||
@@ -669,31 +713,13 @@ dnl Input Plugins
|
||||
dnl ---------------------------------------------------------------------------
|
||||
|
||||
dnl ----------------------------------- CURL ----------------------------------
|
||||
MPD_AUTO_PKG(curl, CURL, [libcurl],
|
||||
MPD_AUTO_PKG(curl, CURL, [libcurl >= 7.18],
|
||||
[libcurl HTTP streaming], [libcurl not found])
|
||||
if test x$enable_curl = xyes; then
|
||||
AC_DEFINE(ENABLE_CURL, 1, [Define when libcurl is used for HTTP streaming])
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_CURL, test x$enable_curl = xyes)
|
||||
|
||||
dnl ----------------------------------- SOUP ----------------------------------
|
||||
MPD_AUTO_PKG(soup, SOUP, [libsoup-2.4],
|
||||
[libsoup HTTP streaming], [libsoup not found])
|
||||
if test x$enable_soup = xyes; then
|
||||
AC_DEFINE(ENABLE_SOUP, 1, [Define when libsoup is used for HTTP streaming])
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_SOUP, test x$enable_soup = xyes)
|
||||
|
||||
dnl --------------------------------- Last.FM ---------------------------------
|
||||
if test x$enable_lastfm = xyes; then
|
||||
if test x$enable_curl != xyes; then
|
||||
AC_MSG_ERROR([Cannot enable last.fm radio without curl])
|
||||
fi
|
||||
|
||||
AC_DEFINE(ENABLE_LASTFM, 1, [Define when last.fm radio is enabled])
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_LASTFM, test x$enable_lastfm = xyes)
|
||||
|
||||
dnl --------------------------------- Despotify ---------------------------------
|
||||
MPD_AUTO_PKG(despotify, DESPOTIFY, [despotify],
|
||||
[Despotify support], [despotify not found])
|
||||
@@ -756,12 +782,9 @@ fi
|
||||
AM_CONDITIONAL(ENABLE_ISO9660_TEST, test x$MKISOFS != xno)
|
||||
|
||||
dnl ---------------------------------- libbz2 ---------------------------------
|
||||
if test x$enable_bzip2 = xyes; then
|
||||
AC_CHECK_LIB(bz2, BZ2_bzDecompressInit,
|
||||
[BZ2_LIBS="-lbz2"],
|
||||
[AC_MSG_ERROR([libbz2 not found])])
|
||||
fi
|
||||
AC_SUBST(BZ2_LIBS)
|
||||
|
||||
MPD_AUTO_LIB(bzip2, BZ2, bz2, BZ2_bzDecompressInit, [-lbz2], [],
|
||||
[bzip2], [libbz2 not found])
|
||||
|
||||
AM_CONDITIONAL(HAVE_BZ2, test x$enable_bzip2 = xyes)
|
||||
if test x$enable_bzip2 = xyes; then
|
||||
@@ -806,8 +829,16 @@ dnl ---------------------------------------------------------------------------
|
||||
dnl Decoder Plugins
|
||||
dnl ---------------------------------------------------------------------------
|
||||
|
||||
dnl -------------------------------- libadplug --------------------------------
|
||||
MPD_AUTO_PKG(adplug, ADPLUG, [adplug],
|
||||
[AdPlug decoder plugin], [libadplug not found])
|
||||
if test x$enable_adplug = xyes; then
|
||||
AC_DEFINE(HAVE_ADPLUG, 1, [Define to use libadplug])
|
||||
fi
|
||||
AM_CONDITIONAL(HAVE_ADPLUG, test x$enable_adplug = xyes)
|
||||
|
||||
dnl -------------------------------- audiofile --------------------------------
|
||||
MPD_AUTO_PKG(audiofile, AUDIOFILE, [audiofile >= 0.1.7],
|
||||
MPD_AUTO_PKG(audiofile, AUDIOFILE, [audiofile >= 0.3],
|
||||
[audiofile decoder plugin], [libaudiofile not found])
|
||||
AM_CONDITIONAL(HAVE_AUDIOFILE, test x$enable_audiofile = xyes)
|
||||
if test x$enable_audiofile = xyes; then
|
||||
@@ -818,10 +849,9 @@ dnl ----------------------------------- FAAD ----------------------------------
|
||||
AM_PATH_FAAD()
|
||||
|
||||
AM_CONDITIONAL(HAVE_FAAD, test x$enable_aac = xyes)
|
||||
AM_CONDITIONAL(HAVE_MP4, test x$enable_mp4 = xyes)
|
||||
|
||||
dnl ---------------------------------- ffmpeg ---------------------------------
|
||||
MPD_AUTO_PKG(ffmpeg, FFMPEG, [libavformat >= 52.31 libavcodec >= 52.20 libavutil >= 49.15],
|
||||
MPD_AUTO_PKG(ffmpeg, FFMPEG, [libavformat >= 53.17 libavcodec >= 53.25 libavutil >= 51.17],
|
||||
[ffmpeg decoder library], [libavformat+libavcodec+libavutil not found])
|
||||
|
||||
if test x$enable_ffmpeg = xyes; then
|
||||
@@ -832,7 +862,7 @@ AM_CONDITIONAL(HAVE_FFMPEG, test x$enable_ffmpeg = xyes)
|
||||
|
||||
dnl ----------------------------------- FLAC ----------------------------------
|
||||
|
||||
MPD_AUTO_PKG(flac, FLAC, [flac >= 1.1],
|
||||
MPD_AUTO_PKG(flac, FLAC, [flac >= 1.2],
|
||||
[FLAC decoder], [libFLAC not found])
|
||||
|
||||
if test x$enable_flac = xyes; then
|
||||
@@ -853,7 +883,7 @@ fi
|
||||
AM_CONDITIONAL(ENABLE_FLUIDSYNTH, test x$enable_fluidsynth = xyes)
|
||||
|
||||
dnl ---------------------------------- libgme ---------------------------------
|
||||
MPD_AUTO_PKG(gme, GME, [libgme],
|
||||
MPD_AUTO_PKG_LIB(gme, GME, [libgme], gme, gme_open_file, [-lgme -lstdc++], [],
|
||||
[gme decoder plugin], [libgme not found])
|
||||
AM_CONDITIONAL(HAVE_GME, test x$enable_gme = xyes)
|
||||
if test x$enable_gme = xyes; then
|
||||
@@ -899,9 +929,6 @@ fi
|
||||
AM_CONDITIONAL(ENABLE_MIKMOD_DECODER, test x$enable_mikmod = xyes)
|
||||
|
||||
dnl -------------------------------- libmodplug -------------------------------
|
||||
found_modplug=$HAVE_CXX
|
||||
MPD_AUTO_PRE(modplug, [modplug decoder plugin], [No C++ compiler found])
|
||||
|
||||
MPD_AUTO_PKG(modplug, MODPLUG, [libmodplug],
|
||||
[modplug decoder plugin], [libmodplug not found])
|
||||
|
||||
@@ -910,6 +937,14 @@ if test x$enable_modplug = xyes; then
|
||||
fi
|
||||
AM_CONDITIONAL(HAVE_MODPLUG, test x$enable_modplug = xyes)
|
||||
|
||||
dnl -------------------------------- libopus ----------------------------------
|
||||
MPD_AUTO_PKG(opus, OPUS, [opus ogg],
|
||||
[opus decoder plugin], [libopus not found])
|
||||
if test x$enable_opus = xyes; then
|
||||
AC_DEFINE(HAVE_OPUS, 1, [Define to use libopus])
|
||||
fi
|
||||
AM_CONDITIONAL(HAVE_OPUS, test x$enable_opus = xyes)
|
||||
|
||||
dnl -------------------------------- libsndfile -------------------------------
|
||||
dnl See above test, which may disable this.
|
||||
MPD_AUTO_PKG(sndfile, SNDFILE, [sndfile],
|
||||
@@ -921,33 +956,12 @@ fi
|
||||
AM_CONDITIONAL(ENABLE_SNDFILE, test x$enable_sndfile = xyes)
|
||||
|
||||
dnl --------------------------------- musepack --------------------------------
|
||||
|
||||
MPD_AUTO_LIB(mpc, MPCDEC, mpcdec, main, [-lmpcdec], [],
|
||||
[mpcdec], [libmpcdec not found])
|
||||
if test x$enable_mpc = xyes; then
|
||||
oldcflags=$CFLAGS
|
||||
oldlibs=$LIBS
|
||||
oldcppflags=$CPPFLAGS
|
||||
AC_CHECK_LIB(mpcdec,main,
|
||||
MPCDEC_LIBS="$MPCDEC_LIBS -lmpcdec",
|
||||
enable_mpc=no)
|
||||
CFLAGS=$oldcflags
|
||||
LIBS=$oldlibs
|
||||
CPPFLAGS=$oldcppflags
|
||||
|
||||
if test x$enable_mpc = xyes; then
|
||||
AC_CHECK_HEADER([mpc/mpcdec.h],
|
||||
[AC_DEFINE(HAVE_MPCDEC,1,
|
||||
[Define to use libmpcdec for MPC decoding])],
|
||||
[AC_CHECK_HEADER(mpcdec/mpcdec.h,
|
||||
[AC_DEFINE(MPC_IS_OLD_API, 1,
|
||||
[Define if an old pre-SV8 libmpcdec is used])]
|
||||
)]
|
||||
)
|
||||
else
|
||||
AC_MSG_WARN([mpcdec lib needed for MPC support -- disabling MPC support])
|
||||
fi
|
||||
AC_DEFINE(HAVE_MPCDEC, 1, [Define to use libmpcdec for MPC decoding])
|
||||
fi
|
||||
|
||||
AC_SUBST(MPCDEC_LIBS)
|
||||
AC_SUBST(MPCDEC_CFLAGS)
|
||||
AM_CONDITIONAL(HAVE_MPCDEC, test x$enable_mpc = xyes)
|
||||
|
||||
dnl -------------------------------- Ogg Tremor -------------------------------
|
||||
@@ -1015,9 +1029,6 @@ fi
|
||||
AM_CONDITIONAL(ENABLE_VORBIS_DECODER, test x$enable_vorbis = xyes || test x$enable_tremor = xyes)
|
||||
|
||||
dnl --------------------------------- sidplay ---------------------------------
|
||||
found_sidplay=$HAVE_CXX
|
||||
MPD_AUTO_PRE(sidplay, [sidplay decoder plugin], [No C++ compiler found])
|
||||
|
||||
if test x$enable_sidplay != xno; then
|
||||
# we're not using pkg-config here
|
||||
# because libsidplay2's .pc file requires libtool
|
||||
@@ -1032,7 +1043,7 @@ if test x$enable_sidplay != xno; then
|
||||
[found_sidplay=yes], [found_sidplay=no])
|
||||
|
||||
if test x$found_sidplay = xyes; then
|
||||
AC_CHECK_LIB([sidutils],[main],[],[found_sidplay=no],[])
|
||||
AC_CHECK_LIB([sidutils],[main],[:],[found_sidplay=no],[])
|
||||
fi
|
||||
|
||||
MPD_AUTO_RESULT(sidplay, [sidplay decoder plugin],
|
||||
@@ -1057,25 +1068,9 @@ if test x$enable_wavpack = xyes; then
|
||||
fi
|
||||
|
||||
dnl --------------------------------- WildMidi --------------------------------
|
||||
MPD_AUTO_LIB(wildmidi, WILDMIDI, WildMidi, WildMidi_Init, [-lWildMidi], [],
|
||||
[wildmidi], [libwildmidi not found])
|
||||
if test x$enable_wildmidi = xyes; then
|
||||
oldcflags=$CFLAGS
|
||||
oldlibs=$LIBS
|
||||
oldcppflags=$CPPFLAGS
|
||||
|
||||
AC_CHECK_LIB(WildMidi, WildMidi_Init,,
|
||||
AC_MSG_ERROR([libwildmidi not found]))
|
||||
|
||||
AC_CHECK_LIB(WildMidi, WildMidi_SampledSeek,
|
||||
[AC_DEFINE(HAVE_WILDMIDI_SAMPLED_SEEK, 1,
|
||||
[Defined if WildMidi_SampledSeek() is available (libwildmidi <= 0.2.2)])])
|
||||
|
||||
CFLAGS=$oldcflags
|
||||
LIBS=$oldlibs
|
||||
CPPFLAGS=$oldcppflags
|
||||
|
||||
AC_SUBST(WILDMIDI_LIBS,-lWildMidi)
|
||||
AC_SUBST(WILDMIDI_CFLAGS,)
|
||||
|
||||
AC_DEFINE(ENABLE_WILDMIDI, 1, [Define for wildmidi support])
|
||||
fi
|
||||
|
||||
@@ -1092,9 +1087,9 @@ if
|
||||
test x$enable_mad = xno &&
|
||||
test x$enable_mikmod = xno; then
|
||||
test x$enable_modplug = xno &&
|
||||
test x$enable_mp4 = xno &&
|
||||
test x$enable_mpc = xno &&
|
||||
test x$enable_mpg123 = xno &&
|
||||
test x$enable_opus = xno &&
|
||||
test x$enable_sidplay = xno &&
|
||||
test x$enable_tremor = xno &&
|
||||
test x$enable_vorbis = xno &&
|
||||
@@ -1104,11 +1099,8 @@ if
|
||||
AC_MSG_ERROR([No input plugins supported!])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_OGG_COMMON,
|
||||
test x$enable_vorbis = xyes || test x$enable_tremor = xyes || test x$enable_flac = xyes)
|
||||
|
||||
AM_CONDITIONAL(HAVE_FLAC_COMMON,
|
||||
test x$enable_flac = xyes)
|
||||
AM_CONDITIONAL(HAVE_XIPH,
|
||||
test x$enable_vorbis = xyes || test x$enable_tremor = xyes || test x$enable_flac = xyes || test x$enable_opus = xyes)
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Encoders for Streaming Audio Output Plugins
|
||||
@@ -1154,23 +1146,9 @@ fi
|
||||
AM_CONDITIONAL(ENABLE_VORBIS_ENCODER, test x$enable_vorbis_encoder = xyes)
|
||||
|
||||
dnl ------------------------------- LAME Encoder ------------------------------
|
||||
if test x$enable_lame_encoder != xno; then
|
||||
AC_CHECK_HEADERS(lame/lame.h,,
|
||||
[AC_CHECK_HEADERS(lame.h,, using_lame=no)])
|
||||
AC_CHECK_LIB(mp3lame, lame_init,, using_lame=no)
|
||||
if test x$using_lame != xno; then
|
||||
AC_DEFINE(HAVE_LAME, 1, [Define to 1 if you have lame 3.98 or greater.])
|
||||
LAME_LIBS="-lmp3lame -lm"
|
||||
enable_lame_encoder=yes
|
||||
fi
|
||||
|
||||
if test "$enable_lame_encoder" = "yes" -a "$using_lame" = "no"; then
|
||||
AC_MSG_ERROR([LAME libraries and development support files not found.])
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_SUBST(LAME_LIBS)
|
||||
|
||||
MPD_AUTO_LIB(lame_encoder, LAME, mp3lame, lame_init, [-lmp3lame], [],
|
||||
[libmp3lame], [libmp3lame not found])
|
||||
if test x$enable_lame_encoder = xyes; then
|
||||
AC_DEFINE(ENABLE_LAME_ENCODER, 1,
|
||||
[Define to enable the lame encoder plugin])
|
||||
@@ -1196,6 +1174,7 @@ fi
|
||||
|
||||
dnl --------------------------- encoder plugins test --------------------------
|
||||
if test x$enable_vorbis_encoder != xno ||
|
||||
test x$enable_opus != xno ||
|
||||
test x$enable_lame_encoder != xno ||
|
||||
test x$enable_twolame_encoder != xno ||
|
||||
test x$enable_flac_encoder != xno ||
|
||||
@@ -1216,6 +1195,7 @@ if test x$enable_encoder = xyes; then
|
||||
[Define to enable the encoder plugins])
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_ENCODER, test x$enable_encoder = xyes)
|
||||
AM_CONDITIONAL(HAVE_OGG_ENCODER, test x$enable_vorbis_encoder = xyes || test x$enable_opus = xyes)
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Audio Output Plugins
|
||||
@@ -1241,17 +1221,6 @@ fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_ROAR, test x$enable_roar = xyes)
|
||||
|
||||
dnl ----------------------------------- FFADO ---------------------------------
|
||||
|
||||
MPD_AUTO_PKG(ffado, FFADO, [libffado],
|
||||
[libffado output plugin], [libffado not found])
|
||||
|
||||
if test x$enable_ffado = xyes; then
|
||||
AC_DEFINE(ENABLE_FFADO_OUTPUT, 1, [Define to enable the libffado output plugin])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(ENABLE_FFADO_OUTPUT, test x$enable_ffado = xyes)
|
||||
|
||||
dnl ----------------------------------- FIFO ----------------------------------
|
||||
if test x$enable_fifo = xyes; then
|
||||
AC_CHECK_FUNC([mkfifo],
|
||||
@@ -1307,31 +1276,28 @@ fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_AO, test x$enable_ao = xyes)
|
||||
|
||||
dnl ----------------------------------- MVP -----------------------------------
|
||||
if test x$enable_mvp = xyes; then
|
||||
AC_DEFINE(HAVE_MVP,1,[Define to enable Hauppauge Media MVP support])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_MVP, test x$enable_mvp = xyes)
|
||||
|
||||
dnl ---------------------------------- OpenAL ---------------------------------
|
||||
AC_SUBST(OPENAL_CFLAGS,"")
|
||||
AC_SUBST(OPENAL_LIBS,"")
|
||||
|
||||
if test x$enable_openal = xyes; then
|
||||
if test x$enable_osx = xyes; then
|
||||
AC_CHECK_HEADERS([OpenAL/al.h OpenAL/alc.h], [], [enable_openal=no])
|
||||
if test x$enable_openal = xyes; then
|
||||
OPENAL_LIBS="-framework OpenAL"
|
||||
AC_DEFINE(HAVE_OPENAL, 1, [Define for OpenAL support])
|
||||
else
|
||||
AC_MSG_WARN(OpenAL headers not found -- disabling OpenAL support)
|
||||
fi
|
||||
else
|
||||
PKG_CHECK_MODULES([OPENAL], [openal],
|
||||
AC_DEFINE(HAVE_OPENAL, 1, [Define for OpenAL support]),
|
||||
enable_openal=no)
|
||||
if test x$host_is_darwin = xyes; then
|
||||
if test x$enable_openal != xno; then
|
||||
AC_CHECK_HEADERS([OpenAL/al.h OpenAL/alc.h],
|
||||
[found_openal=yes], [found_openal=no])
|
||||
fi
|
||||
|
||||
MPD_AUTO_RESULT(openal, [OpenAL output plugin], [OpenAL not found])
|
||||
|
||||
if test x$enable_openal = xyes; then
|
||||
OPENAL_LIBS="-framework OpenAL"
|
||||
fi
|
||||
else
|
||||
MPD_AUTO_PKG(openal, [OPENAL], [openal],
|
||||
[OpenAL output plugin], [OpenAL not found])
|
||||
fi
|
||||
|
||||
if test x$enable_openal = xyes; then
|
||||
AC_DEFINE(HAVE_OPENAL, 1, [Define for OpenAL support])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_OPENAL, test x$enable_openal = xyes)
|
||||
@@ -1347,13 +1313,10 @@ fi
|
||||
AM_CONDITIONAL(HAVE_OSS, test x$enable_oss = xyes)
|
||||
|
||||
dnl ----------------------------------- OSX -----------------------------------
|
||||
enable_osx=no
|
||||
case "$host_os" in
|
||||
darwin*)
|
||||
AC_DEFINE(HAVE_OSX, 1, [Define for compiling OS X support])
|
||||
LIBS="$LIBS -framework AudioUnit -framework CoreAudio -framework CoreServices"
|
||||
enable_osx=yes ;;
|
||||
esac
|
||||
if test x$enable_osx = xyes; then
|
||||
AC_DEFINE(HAVE_OSX, 1, [Define for compiling OS X support])
|
||||
LIBS="$LIBS -framework AudioUnit -framework CoreAudio -framework CoreServices"
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_OSX, test x$enable_osx = xyes)
|
||||
|
||||
@@ -1365,7 +1328,7 @@ fi
|
||||
AM_CONDITIONAL(ENABLE_PIPE_OUTPUT, test x$enable_pipe_output = xyes)
|
||||
|
||||
dnl -------------------------------- PulseAudio -------------------------------
|
||||
MPD_AUTO_PKG(pulse, PULSE, [libpulse],
|
||||
MPD_AUTO_PKG(pulse, PULSE, [libpulse >= 0.9.16],
|
||||
[PulseAudio output plugin], [libpulse not found])
|
||||
if test x$enable_pulse = xyes; then
|
||||
AC_DEFINE([HAVE_PULSE], 1,
|
||||
@@ -1449,11 +1412,9 @@ if
|
||||
test x$enable_alsa = xno &&
|
||||
test x$enable_roar = xno &&
|
||||
test x$enable_ao = xno &&
|
||||
test x$enable_ffado = xno &&
|
||||
test x$enable_fifo = xno &&
|
||||
test x$enable_httpd_output = xno &&
|
||||
test x$enable_jack = xno &&
|
||||
test x$enable_mvp = xno; then
|
||||
test x$enable_openal = xno &&
|
||||
test x$enable_oss = xno &&
|
||||
test x$enable_osx = xno &&
|
||||
@@ -1462,7 +1423,7 @@ if
|
||||
test x$enable_recorder_output = xno &&
|
||||
test x$enable_shout = xno &&
|
||||
test x$enable_solaris_output = xno &&
|
||||
test x$enable_winmm_output = xno &&
|
||||
test x$enable_winmm_output = xno; then
|
||||
|
||||
AC_MSG_ERROR([No Audio Output types configured!])
|
||||
fi
|
||||
@@ -1490,15 +1451,52 @@ AM_CONDITIONAL(ENABLE_DOCUMENTATION, test x$enable_documentation = xyes)
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl test suite
|
||||
dnl ---------------------------------------------------------------------------
|
||||
|
||||
if test "x$enable_test" = xyes; then
|
||||
PKG_CHECK_MODULES([CPPUNIT], [cppunit],,
|
||||
[AC_MSG_ERROR([cppunit not found])])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(ENABLE_TEST, test "x$enable_test" = xyes)
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl CFLAGS
|
||||
dnl ---------------------------------------------------------------------------
|
||||
|
||||
dnl ---------------------------- warnings as errors ---------------------------
|
||||
if test "x$enable_werror" = xyes; then
|
||||
CFLAGS="$CFLAGS -Werror -pedantic-errors"
|
||||
CXXFLAGS="$CXXFLAGS -Werror"
|
||||
fi
|
||||
|
||||
dnl ---------------------------- language features ----------------------------
|
||||
AX_APPEND_COMPILE_FLAGS([-fvisibility=hidden])
|
||||
AX_APPEND_COMPILE_FLAGS([-ffast-math])
|
||||
AX_APPEND_COMPILE_FLAGS([-ftree-vectorize])
|
||||
|
||||
AC_LANG_PUSH([C++])
|
||||
AX_APPEND_COMPILE_FLAGS([-fvisibility=hidden])
|
||||
AX_APPEND_COMPILE_FLAGS([-fno-threadsafe-statics])
|
||||
AX_APPEND_COMPILE_FLAGS([-fmerge-all-constants])
|
||||
AX_APPEND_COMPILE_FLAGS([-fno-exceptions])
|
||||
AX_APPEND_COMPILE_FLAGS([-fno-rtti])
|
||||
AX_APPEND_COMPILE_FLAGS([-ffast-math])
|
||||
AX_APPEND_COMPILE_FLAGS([-ftree-vectorize])
|
||||
AC_LANG_POP
|
||||
|
||||
dnl ---------------------------------- debug ----------------------------------
|
||||
if test "x$enable_debug" = xno; then
|
||||
AM_CPPFLAGS="$AM_CPPFLAGS -DNDEBUG"
|
||||
|
||||
AX_APPEND_COMPILE_FLAGS([-ffunction-sections])
|
||||
AX_APPEND_COMPILE_FLAGS([-fdata-sections])
|
||||
|
||||
AC_LANG_PUSH([C++])
|
||||
AX_APPEND_COMPILE_FLAGS([-ffunction-sections])
|
||||
AX_APPEND_COMPILE_FLAGS([-fdata-sections])
|
||||
AC_LANG_POP
|
||||
|
||||
AX_APPEND_LINK_FLAGS([-Wl,--gc-sections])
|
||||
fi
|
||||
|
||||
dnl ----------------------------------- GCC -----------------------------------
|
||||
@@ -1513,12 +1511,17 @@ then
|
||||
AX_APPEND_COMPILE_FLAGS([-Wcast-qual])
|
||||
AX_APPEND_COMPILE_FLAGS([-Wwrite-strings])
|
||||
AX_APPEND_COMPILE_FLAGS([-pedantic])
|
||||
fi
|
||||
|
||||
dnl ---------------------------- warnings as errors ---------------------------
|
||||
if test "x$enable_werror" = xyes; then
|
||||
AM_CFLAGS="$AM_CFLAGS -Werror -pedantic-errors"
|
||||
AM_CXXFLAGS="$AM_CXXFLAGS -Werror"
|
||||
AC_LANG_PUSH([C++])
|
||||
AX_APPEND_COMPILE_FLAGS([-Wall])
|
||||
AX_APPEND_COMPILE_FLAGS([-Wextra])
|
||||
AX_APPEND_COMPILE_FLAGS([-Wmissing-declarations])
|
||||
AX_APPEND_COMPILE_FLAGS([-Wshadow])
|
||||
AX_APPEND_COMPILE_FLAGS([-Wpointer-arith])
|
||||
AX_APPEND_COMPILE_FLAGS([-Wcast-qual])
|
||||
AX_APPEND_COMPILE_FLAGS([-Wwrite-strings])
|
||||
AX_APPEND_COMPILE_FLAGS([-Wsign-compare])
|
||||
AC_LANG_POP
|
||||
fi
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
@@ -1545,20 +1548,21 @@ results(un,[UNIX Domain Sockets])
|
||||
|
||||
printf '\nFile format support:\n\t'
|
||||
results(aac, [AAC])
|
||||
results(adplug, [AdPlug])
|
||||
results(sidplay, [C64 SID])
|
||||
results(ffmpeg, [FFMPEG])
|
||||
results(flac, [FLAC])
|
||||
results(fluidsynth, [FluidSynth])
|
||||
results(gme, [GME])
|
||||
results(sndfile, [libsndfile])
|
||||
printf '\n\t'
|
||||
results(sndfile, [libsndfile])
|
||||
results(mikmod, [MikMod])
|
||||
results(modplug, [MODPLUG])
|
||||
results(mad, [MAD])
|
||||
results(mpg123, [MPG123])
|
||||
results(mp4, [MP4])
|
||||
results(mpc, [Musepack])
|
||||
printf '\n\t'
|
||||
results(opus, [Opus])
|
||||
results(tremor, [OggTremor])
|
||||
results(vorbis, [OggVorbis])
|
||||
results(audiofile, [WAVE])
|
||||
@@ -1567,6 +1571,7 @@ results(wildmidi, [WildMidi])
|
||||
|
||||
printf '\nOther features:\n\t'
|
||||
results(lsr, [libsamplerate])
|
||||
results(libmpdclient, [libmpdclient])
|
||||
results(inotify, [inotify])
|
||||
results(sqlite, [SQLite])
|
||||
|
||||
@@ -1575,14 +1580,12 @@ results(id3,[ID3])
|
||||
|
||||
printf '\nPlayback support:\n\t'
|
||||
results(alsa,ALSA)
|
||||
results(ffado,FFADO)
|
||||
results(fifo,FIFO)
|
||||
results(recorder_output,[File Recorder])
|
||||
results(httpd_output,[HTTP Daemon])
|
||||
results(jack,[JACK])
|
||||
printf '\n\t'
|
||||
results(ao,[libao])
|
||||
results(mvp, [Media MVP])
|
||||
results(oss,[OSS])
|
||||
results(openal,[OpenAL])
|
||||
results(osx, [OS X])
|
||||
@@ -1602,6 +1605,7 @@ if
|
||||
results(flac_encoder, [FLAC])
|
||||
results(lame_encoder, [LAME])
|
||||
results(vorbis_encoder, [Ogg Vorbis])
|
||||
results(opus, [Opus])
|
||||
results(twolame_encoder, [TwoLAME])
|
||||
results(wave_encoder, [WAVE])
|
||||
fi
|
||||
@@ -1610,11 +1614,9 @@ printf '\nStreaming support:\n\t'
|
||||
results(cdio_paranoia, [CDIO_PARANOIA])
|
||||
results(curl,[CURL])
|
||||
results(despotify,[Despotify])
|
||||
results(lastfm,[Last.FM])
|
||||
results(soundcloud,[Soundcloud])
|
||||
printf '\n\t'
|
||||
results(mms,[MMS])
|
||||
results(soup, [SOUP])
|
||||
|
||||
printf '\n\n##########################################\n\n'
|
||||
|
||||
|
@@ -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>
|
||||
|
@@ -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.
|
||||
|
@@ -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
|
||||
@@ -87,8 +81,9 @@ 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. For a
|
||||
system-wide MPD, we suggest the path "\fB/var/run/mpd/socket\fP".
|
||||
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.
|
||||
@@ -99,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.
|
||||
@@ -175,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
|
||||
@@ -186,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%>
|
||||
@@ -227,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
|
||||
@@ -260,7 +266,7 @@ 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
|
||||
@@ -282,7 +288,7 @@ better but requires more processing and higher bandwidth. Default is yes.
|
||||
.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.
|
||||
@@ -488,21 +494,6 @@ This specifies a URL associated with the stream.
|
||||
.TP
|
||||
.B genre <genre>
|
||||
This specifies the genre(s) of the stream.
|
||||
.SH OPTIONAL TCP KEEPALIVE PARAMETERS
|
||||
.TP
|
||||
.B tcp_keep_alive <yes or no>
|
||||
Enable tcp keepalive on new client connections. (default is "no")
|
||||
.TP
|
||||
.B tcp_keep_alive_idle <seconds>
|
||||
Time in seconds since the last communication on the connection and before
|
||||
the keepalive probing is started. (default is 7200 seconds)
|
||||
.TP
|
||||
.B tcp_keep_alive_interval <seconds>
|
||||
Interval in seconds between keepalive probes, once a probe started. (default is 75 seconds)
|
||||
.TP
|
||||
.B tcp_keep_alive_count <number of times>
|
||||
Number of failed probes before the connection is pronounced dead and
|
||||
the connection is closed. (default is 9 times)
|
||||
.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 #######################################################
|
||||
@@ -175,6 +175,15 @@
|
||||
###############################################################################
|
||||
|
||||
|
||||
# Database #######################################################################
|
||||
#
|
||||
|
||||
#database {
|
||||
# plugin "proxy"
|
||||
# host "other.mpd.host"
|
||||
# port "6600"
|
||||
#}
|
||||
|
||||
# Input #######################################################################
|
||||
#
|
||||
|
||||
@@ -195,9 +204,6 @@ 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 {
|
||||
@@ -281,6 +287,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 {
|
||||
@@ -321,8 +346,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"
|
||||
#
|
||||
@@ -331,6 +359,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.
|
||||
@@ -346,7 +388,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,
|
||||
@@ -371,36 +413,6 @@ input {
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
# Client TCP keep alive #######################################################
|
||||
#
|
||||
# For clients connected by TCP on supported platforms.
|
||||
# Allows detection of dangling connections due to clients disappearing from
|
||||
# the network without closing their connections.
|
||||
#
|
||||
# This is not usually necessary but can be useful in cases such as wifi connectected
|
||||
# clients that go in and out of network range or turn off wifi without closing their
|
||||
# connections. Combined with low max_connections this can soon cause clients to not
|
||||
# be able to connect.
|
||||
#
|
||||
#
|
||||
# Enable tcp keepalive on new client connections (default is "no")
|
||||
#
|
||||
#tcp_keep_alive "no"
|
||||
#
|
||||
# Time in seconds since the last communication on the connection and before
|
||||
# the keepalive probing is started. (default is 7200 seconds)
|
||||
#tcp_keep_alive_idle "7200"
|
||||
#
|
||||
# Interval in seconds between keepalive probes, once a probe started.
|
||||
# (default is 75 seconds)
|
||||
#tcp_keep_alive_interval "75"
|
||||
#
|
||||
# Number of failed probes before the connection is pronounced dead and
|
||||
# the connection is closed. (default is 9 times)
|
||||
#tcp_keep_alive_count "9"
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
# Character Encoding ##########################################################
|
||||
#
|
||||
# If file or directory names do not display correctly for your locale then you
|
||||
|
@@ -768,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>
|
||||
|
||||
@@ -1464,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>
|
||||
@@ -1563,6 +1586,32 @@ OK
|
||||
</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">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
@@ -1800,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>
|
||||
@@ -1876,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>
|
||||
|
322
doc/user.xml
322
doc/user.xml
@@ -67,11 +67,11 @@
|
||||
|
||||
<para>
|
||||
Download the source tarball from <ulink
|
||||
url="http://mpd.wikia.com/wiki/Server">the MPD home
|
||||
url="http://www.musicpd.org/download.html">the MPD home
|
||||
page</ulink> and unpack it:
|
||||
</para>
|
||||
|
||||
<programlisting>tar xjf mpd-version.tar.bz
|
||||
<programlisting>tar xf mpd-version.tar.xz
|
||||
cd mpd-version</programlisting>
|
||||
|
||||
<para>
|
||||
@@ -164,6 +164,53 @@ systemctl start mpd.socket</programlisting>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Configuring database plugins</title>
|
||||
|
||||
<para>
|
||||
If a music directory is configured, one database plugin is
|
||||
used. To configure this plugin, add a
|
||||
<varname>database</varname> block to
|
||||
<filename>mpd.conf</filename>:
|
||||
</para>
|
||||
|
||||
<programlisting>database {
|
||||
plugin "simple"
|
||||
path "/var/lib/mpd/db"
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
The following table lists the <varname>database</varname>
|
||||
options valid for all plugins:
|
||||
</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>
|
||||
Name
|
||||
</entry>
|
||||
<entry>
|
||||
Description
|
||||
</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>plugin</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
The name of the plugin.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Configuring input plugins</title>
|
||||
|
||||
@@ -173,7 +220,7 @@ systemctl start mpd.socket</programlisting>
|
||||
</para>
|
||||
|
||||
<programlisting>input {
|
||||
plugin "lastfm"
|
||||
plugin "despotify"
|
||||
user "foo"
|
||||
password "bar"
|
||||
}
|
||||
@@ -389,6 +436,18 @@ systemctl start mpd.socket</programlisting>
|
||||
enabled.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>tags</varname>
|
||||
<parameter>yes|no</parameter>
|
||||
</entry>
|
||||
<entry>
|
||||
If set to "no", then MPD will not send tags to this
|
||||
output. This is only useful for output plugins that
|
||||
can receive tags, for example the
|
||||
<varname>httpd</varname> output plugin.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>always_on</varname>
|
||||
@@ -571,7 +630,7 @@ systemctl start mpd.socket</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <ulink url="http://mpd.wikia.com/wiki/Clients">MPD
|
||||
The <ulink url="http://www.musicpd.org/clients/">MPD
|
||||
Wiki</ulink> contains an extensive list of clients to choose
|
||||
from.
|
||||
</para>
|
||||
@@ -617,6 +676,81 @@ systemctl start mpd.socket</programlisting>
|
||||
<chapter>
|
||||
<title>Plugin reference</title>
|
||||
|
||||
<section>
|
||||
<title>Database plugins</title>
|
||||
|
||||
<section>
|
||||
<title><varname>simple</varname></title>
|
||||
|
||||
<para>
|
||||
The default plugin. Stores a copy of the database in
|
||||
memory. A file is used for permanent storage.
|
||||
</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Setting</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>path</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
The path of the database file.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>proxy</varname></title>
|
||||
|
||||
<para>
|
||||
Provides access to the database of another MPD instance
|
||||
using <filename>libmpdclient</filename>. This is useful
|
||||
when you run mount the music directory via NFS/SMB, and the
|
||||
file server already runs a MPD instance. Only the file
|
||||
server needs to update the database.
|
||||
</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Setting</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>host</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
The host name of the "master" MPD instance.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>port</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
The port number of the "master" MPD instance.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Input plugins</title>
|
||||
|
||||
@@ -683,6 +817,30 @@ systemctl start mpd.socket</programlisting>
|
||||
simplest form <filename>cdda://</filename> plays the whole
|
||||
disc in the default drive.
|
||||
</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Setting</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>default_bute_order</varname>
|
||||
<parameter>little_endian|big_endian</parameter>
|
||||
</entry>
|
||||
<entry>
|
||||
If the CD drive does not specify a byte order, MPD
|
||||
assumes it is the CPU's native byte order. This
|
||||
setting allows overriding this.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
@@ -744,35 +902,6 @@ systemctl start mpd.socket</programlisting>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>soup</varname></title>
|
||||
|
||||
<para>
|
||||
Opens remote files or streams over HTTP.
|
||||
</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Setting</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>proxy</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
Sets the address of the HTTP proxy server.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
@@ -874,6 +1003,16 @@ systemctl start mpd.socket</programlisting>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>loop</varname>
|
||||
<parameter>yes|no</parameter>
|
||||
</entry>
|
||||
<entry>
|
||||
Allow backward loops in modules. Default is
|
||||
<parameter>no</parameter>.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>sample_rate</varname>
|
||||
@@ -888,6 +1027,37 @@ systemctl start mpd.socket</programlisting>
|
||||
</informaltable>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>modplug</varname></title>
|
||||
|
||||
<para>
|
||||
Module player based on MODPlug.
|
||||
</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Setting</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>loop_count</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
Number of times to loop the module if it uses backward loops.
|
||||
Default is <parameter>0</parameter> which prevents looping.
|
||||
<parameter>-1</parameter> loops forever.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>wildmidi</varname></title>
|
||||
|
||||
@@ -1240,43 +1410,6 @@ systemctl start mpd.socket</programlisting>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>ffado</varname></title>
|
||||
|
||||
<para>
|
||||
The <varname>ffado</varname> plugin connects to FireWire
|
||||
audio devices via <filename>libffado</filename>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Warning: this plugin was not tested successfully. I just
|
||||
couldn't keep libffado2 from crashing. Use at your own
|
||||
risk.
|
||||
</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Setting</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>device</varname>
|
||||
<parameter>NAME</parameter>
|
||||
</entry>
|
||||
<entry>
|
||||
Sets the device which should be used, e.g. "hw:0".
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>jack</varname></title>
|
||||
|
||||
@@ -1361,16 +1494,6 @@ systemctl start mpd.socket</programlisting>
|
||||
</informaltable>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>mvp</varname></title>
|
||||
|
||||
<para>
|
||||
The <varname>mvp</varname> plugin uses the proprietary
|
||||
Hauppauge Media MVP interface. We do not know any user of
|
||||
this plugin, and we do not know if it actually works.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>httpd</varname></title>
|
||||
|
||||
@@ -1888,45 +2011,6 @@ systemctl start mpd.socket</programlisting>
|
||||
<section>
|
||||
<title>Playlist plugins</title>
|
||||
|
||||
<section>
|
||||
<title><varname>lastfm</varname></title>
|
||||
|
||||
<para>
|
||||
Plays last.fm radio.
|
||||
</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Setting</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>user</varname>
|
||||
<parameter>USERNAME</parameter>
|
||||
</entry>
|
||||
<entry>
|
||||
The last.fm user name.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>password</varname>
|
||||
<parameter>PWD</parameter>
|
||||
</entry>
|
||||
<entry>
|
||||
The last.fm password.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><varname>embcue</varname></title>
|
||||
|
||||
|
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
|
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
|
||||
])
|
128
m4/faad.m4
128
m4/faad.m4
@@ -8,38 +8,14 @@ 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 $FAAD_CFLAGS -I."
|
||||
CFLAGS="$CFLAGS $FAAD_CFLAGS"
|
||||
LIBS="$LIBS $FAAD_LIBS"
|
||||
CPPFLAGS=$CFLAGS
|
||||
AC_CHECK_HEADER(faad.h,,enable_aac=no)
|
||||
@@ -47,77 +23,36 @@ 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,,enable_aac=no)
|
||||
if test x$enable_aac = xno; then
|
||||
enable_aac=yes
|
||||
AC_CHECK_LIB(faad,NeAACDecInit2,,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])
|
||||
@@ -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,43 +88,12 @@ 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_CFLAGS=""
|
||||
FAAD_LIBS=""
|
||||
FAAD_CFLAGS=""
|
||||
fi
|
||||
|
||||
AC_SUBST(FAAD_CFLAGS)
|
||||
|
@@ -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])
|
||||
])
|
||||
|
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])],)])
|
||||
])
|
@@ -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");
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 INPUT_DESPOTIFY_H
|
||||
#define INPUT_DESPOTIFY_H
|
||||
#ifndef MPD_ARCHIVE_DOMAIN_HXX
|
||||
#define MPD_ARCHIVE_DOMAIN_HXX
|
||||
|
||||
extern const struct input_plugin input_plugin_despotify;
|
||||
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
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 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,15 +18,15 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "archive_list.h"
|
||||
#include "archive_plugin.h"
|
||||
#include "string_util.h"
|
||||
#include "archive/bz2_archive_plugin.h"
|
||||
#include "archive/iso9660_archive_plugin.h"
|
||||
#include "archive/zzip_archive_plugin.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>
|
||||
#include <glib.h>
|
||||
|
||||
const struct archive_plugin *const archive_plugins[] = {
|
||||
#ifdef HAVE_BZ2
|
||||
@@ -38,11 +38,11 @@ const struct archive_plugin *const archive_plugins[] = {
|
||||
#ifdef HAVE_ISO9660
|
||||
&iso9660_archive_plugin,
|
||||
#endif
|
||||
NULL
|
||||
nullptr
|
||||
};
|
||||
|
||||
/** which plugins have been initialized successfully? */
|
||||
static bool archive_plugins_enabled[G_N_ELEMENTS(archive_plugins) - 1];
|
||||
static bool archive_plugins_enabled[ARRAY_SIZE(archive_plugins) - 1];
|
||||
|
||||
#define archive_plugins_for_each_enabled(plugin) \
|
||||
archive_plugins_for_each(plugin) \
|
||||
@@ -51,15 +51,15 @@ static bool archive_plugins_enabled[G_N_ELEMENTS(archive_plugins) - 1];
|
||||
const struct archive_plugin *
|
||||
archive_plugin_from_suffix(const char *suffix)
|
||||
{
|
||||
if (suffix == NULL)
|
||||
return NULL;
|
||||
if (suffix == nullptr)
|
||||
return nullptr;
|
||||
|
||||
archive_plugins_for_each_enabled(plugin)
|
||||
if (plugin->suffixes != NULL &&
|
||||
if (plugin->suffixes != nullptr &&
|
||||
string_array_contains(plugin->suffixes, suffix))
|
||||
return plugin;
|
||||
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const struct archive_plugin *
|
||||
@@ -69,14 +69,14 @@ archive_plugin_from_name(const char *name)
|
||||
if (strcmp(plugin->name, name) == 0)
|
||||
return plugin;
|
||||
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void archive_plugin_init_all(void)
|
||||
{
|
||||
for (unsigned i = 0; archive_plugins[i] != NULL; ++i) {
|
||||
for (unsigned i = 0; archive_plugins[i] != nullptr; ++i) {
|
||||
const struct archive_plugin *plugin = archive_plugins[i];
|
||||
if (plugin->init == NULL || archive_plugins[i]->init())
|
||||
if (plugin->init == nullptr || archive_plugins[i]->init())
|
||||
archive_plugins_enabled[i] = true;
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,7 @@ void archive_plugin_init_all(void)
|
||||
void archive_plugin_deinit_all(void)
|
||||
{
|
||||
archive_plugins_for_each_enabled(plugin)
|
||||
if (plugin->finish != NULL)
|
||||
if (plugin->finish != nullptr)
|
||||
plugin->finish();
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 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,8 +17,8 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_ARCHIVE_LIST_H
|
||||
#define MPD_ARCHIVE_LIST_H
|
||||
#ifndef MPD_ARCHIVE_LIST_HXX
|
||||
#define MPD_ARCHIVE_LIST_HXX
|
||||
|
||||
struct archive_plugin;
|
||||
|
||||
@@ -27,7 +27,7 @@ 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) != NULL; \
|
||||
(plugin = *archive_plugin_iterator) != nullptr; \
|
||||
++archive_plugin_iterator)
|
||||
|
||||
/* interface for using plugins */
|
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-2011 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
|
@@ -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-2011 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_config.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-2011 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_CONFIG_H
|
||||
#define MPD_AUDIO_CONFIG_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-2011 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,71 +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>
|
||||
|
||||
void
|
||||
audio_format_mask_apply(struct audio_format *af,
|
||||
const struct audio_format *mask)
|
||||
AudioFormat::ApplyMask(AudioFormat mask)
|
||||
{
|
||||
assert(audio_format_valid(af));
|
||||
assert(audio_format_mask_valid(mask));
|
||||
assert(IsValid());
|
||||
assert(mask.IsMaskValid());
|
||||
|
||||
if (mask->sample_rate != 0)
|
||||
af->sample_rate = mask->sample_rate;
|
||||
if (mask.sample_rate != 0)
|
||||
sample_rate = mask.sample_rate;
|
||||
|
||||
if (mask->format != SAMPLE_FORMAT_UNDEFINED)
|
||||
af->format = mask->format;
|
||||
if (mask.format != SampleFormat::UNDEFINED)
|
||||
format = mask.format;
|
||||
|
||||
if (mask->channels != 0)
|
||||
af->channels = mask->channels;
|
||||
if (mask.channels != 0)
|
||||
channels = mask.channels;
|
||||
|
||||
assert(audio_format_valid(af));
|
||||
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_P32:
|
||||
case SampleFormat::S24_P32:
|
||||
return "24";
|
||||
|
||||
case SAMPLE_FORMAT_S32:
|
||||
case SampleFormat::S32:
|
||||
return "32";
|
||||
|
||||
case SAMPLE_FORMAT_FLOAT:
|
||||
case SampleFormat::FLOAT:
|
||||
return "f";
|
||||
|
||||
case SAMPLE_FORMAT_DSD:
|
||||
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:%u",
|
||||
af->sample_rate, sample_format_to_string(af->format),
|
||||
af->channels);
|
||||
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-2011 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,27 +23,19 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "audio_parser.h"
|
||||
#include "audio_format.h"
|
||||
#include "audio_check.h"
|
||||
#include "gcc.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;
|
||||
@@ -56,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;
|
||||
@@ -69,45 +61,45 @@ 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 = SAMPLE_FORMAT_FLOAT;
|
||||
*sample_format_r = SampleFormat::FLOAT;
|
||||
*endptr_r = src + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (memcmp(src, "dsd", 3) == 0) {
|
||||
*sample_format_r = SAMPLE_FORMAT_DSD;
|
||||
*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:
|
||||
@@ -115,16 +107,16 @@ parse_sample_format(const char *src, bool mask,
|
||||
/* for backwards compatibility */
|
||||
endptr += 2;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -137,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;
|
||||
@@ -150,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;
|
||||
@@ -162,14 +154,14 @@ 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 */
|
||||
|
||||
@@ -178,12 +170,11 @@ audio_format_parse(struct audio_format *dest, const char *src,
|
||||
rate = 0;
|
||||
#endif
|
||||
|
||||
if (!parse_sample_rate(src, mask, &rate, &src, error_r))
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -191,32 +182,32 @@ audio_format_parse(struct audio_format *dest, const char *src,
|
||||
|
||||
#if GCC_CHECK_VERSION(4,7)
|
||||
/* workaround -Wmaybe-uninitialized false positive */
|
||||
sample_format = SAMPLE_FORMAT_UNDEFINED;
|
||||
sample_format = SampleFormat::UNDEFINED;
|
||||
#endif
|
||||
|
||||
if (!parse_sample_format(src, mask, &sample_format, &src, error_r))
|
||||
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-2011 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
|
||||
@@ -43,7 +40,7 @@ struct audio_format;
|
||||
* @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-2011 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-2011 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();
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2012 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,14 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "client_file.h"
|
||||
#include "client.h"
|
||||
#include "ack.h"
|
||||
#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>
|
||||
@@ -27,18 +32,16 @@
|
||||
#include <unistd.h>
|
||||
|
||||
bool
|
||||
client_allow_file(const struct client *client, const char *path_fs,
|
||||
GError **error_r)
|
||||
client_allow_file(const Client &client, Path path_fs, Error &error)
|
||||
{
|
||||
#ifdef WIN32
|
||||
(void)client;
|
||||
(void)path_fs;
|
||||
|
||||
g_set_error(error_r, ack_quark(), ACK_ERROR_PERMISSION,
|
||||
"Access denied");
|
||||
error.Set(ack_domain, ACK_ERROR_PERMISSION, "Access denied");
|
||||
return false;
|
||||
#else
|
||||
const int uid = client_get_uid(client);
|
||||
const int uid = client.GetUID();
|
||||
if (uid >= 0 && (uid_t)uid == geteuid())
|
||||
/* always allow access if user runs his own MPD
|
||||
instance */
|
||||
@@ -46,22 +49,19 @@ client_allow_file(const struct client *client, const char *path_fs,
|
||||
|
||||
if (uid <= 0) {
|
||||
/* unauthenticated client */
|
||||
g_set_error(error_r, ack_quark(), ACK_ERROR_PERMISSION,
|
||||
"Access denied");
|
||||
error.Set(ack_domain, ACK_ERROR_PERMISSION, "Access denied");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(path_fs, &st) < 0) {
|
||||
g_set_error(error_r, g_file_error_quark(), errno,
|
||||
"%s", g_strerror(errno));
|
||||
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 */
|
||||
g_set_error(error_r, ack_quark(), ACK_ERROR_PERMISSION,
|
||||
"Access denied");
|
||||
error.Set(ack_domain, ACK_ERROR_PERMISSION, "Access denied");
|
||||
return false;
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2012 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,12 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CLIENT_FILE_H
|
||||
#define MPD_CLIENT_FILE_H
|
||||
#ifndef MPD_CLIENT_FILE_HXX
|
||||
#define MPD_CLIENT_FILE_HXX
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct client;
|
||||
class Client;
|
||||
class Path;
|
||||
class Error;
|
||||
|
||||
/**
|
||||
* Is this client allowed to use the specified local file?
|
||||
@@ -36,7 +35,6 @@ struct client;
|
||||
* @return true if access is allowed
|
||||
*/
|
||||
bool
|
||||
client_allow_file(const struct client *client, const char *path_fs,
|
||||
GError **error_r);
|
||||
client_allow_file(const Client &client, Path path_fs, Error &error);
|
||||
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 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;
|
||||
}
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 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,27 +17,23 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_INPUT_INTERNAL_H
|
||||
#define MPD_INPUT_INTERNAL_H
|
||||
#ifndef MPD_CLIENT_INTERNAL_HXX
|
||||
#define MPD_CLIENT_INTERNAL_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "Client.hxx"
|
||||
#include "command/CommandResult.hxx"
|
||||
|
||||
#include <glib.h>
|
||||
static constexpr unsigned CLIENT_MAX_SUBSCRIPTIONS = 16;
|
||||
static constexpr unsigned CLIENT_MAX_MESSAGES = 64;
|
||||
|
||||
struct input_stream;
|
||||
struct input_plugin;
|
||||
extern const class Domain client_domain;
|
||||
|
||||
void
|
||||
input_stream_init(struct input_stream *is, const struct input_plugin *plugin,
|
||||
const char *uri, GMutex *mutex, GCond *cond);
|
||||
extern int client_timeout;
|
||||
extern size_t client_max_command_list_size;
|
||||
extern size_t client_max_output_buffer_size;
|
||||
|
||||
void
|
||||
input_stream_deinit(struct input_stream *is);
|
||||
|
||||
void
|
||||
input_stream_signal_client(struct input_stream *is);
|
||||
|
||||
void
|
||||
input_stream_set_ready(struct input_stream *is);
|
||||
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);
|
||||
}
|
64
src/ClientList.hxx
Normal file
64
src/ClientList.hxx
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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_LIST_HXX
|
||||
#define MPD_CLIENT_LIST_HXX
|
||||
|
||||
#include <list>
|
||||
|
||||
class Client;
|
||||
|
||||
class ClientList {
|
||||
const unsigned max_size;
|
||||
|
||||
unsigned size;
|
||||
std::list<Client *> list;
|
||||
|
||||
public:
|
||||
ClientList(unsigned _max_size)
|
||||
:max_size(_max_size), size(0) {}
|
||||
~ClientList() {
|
||||
CloseAll();
|
||||
}
|
||||
|
||||
std::list<Client *>::iterator begin() {
|
||||
return list.begin();
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 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,26 +17,25 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_SIMPLE_DB_PLUGIN_H
|
||||
#define MPD_SIMPLE_DB_PLUGIN_H
|
||||
#include "ClientMessage.hxx"
|
||||
#include "util/CharUtil.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
|
||||
extern const struct db_plugin simple_db_plugin;
|
||||
|
||||
struct db;
|
||||
|
||||
G_GNUC_PURE
|
||||
struct directory *
|
||||
simple_db_get_root(struct db *db);
|
||||
gcc_const
|
||||
static bool
|
||||
valid_channel_char(const char ch)
|
||||
{
|
||||
return IsAlphaNumericASCII(ch) ||
|
||||
ch == '_' || ch == '-' || ch == '.' || ch == ':';
|
||||
}
|
||||
|
||||
bool
|
||||
simple_db_save(struct db *db, GError **error_r);
|
||||
client_message_valid_channel_name(const char *name)
|
||||
{
|
||||
do {
|
||||
if (!valid_channel_char(*name))
|
||||
return false;
|
||||
} while (*++name != 0);
|
||||
|
||||
G_GNUC_PURE
|
||||
time_t
|
||||
simple_db_get_mtime(const struct db *db);
|
||||
|
||||
#endif
|
||||
return true;
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 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,35 +17,36 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The .mpdignore backend code.
|
||||
*
|
||||
*/
|
||||
#ifndef MPD_CLIENT_MESSAGE_H
|
||||
#define MPD_CLIENT_MESSAGE_H
|
||||
|
||||
#ifndef MPD_EXCLUDE_H
|
||||
#define MPD_EXCLUDE_H
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* Loads and parses a .mpdignore file.
|
||||
* A client-to-client message.
|
||||
*/
|
||||
GSList *
|
||||
exclude_list_load(const char *path_fs);
|
||||
class ClientMessage {
|
||||
std::string channel, message;
|
||||
|
||||
/**
|
||||
* Frees a list returned by exclude_list_load().
|
||||
*/
|
||||
void
|
||||
exclude_list_free(GSList *list);
|
||||
public:
|
||||
template<typename T, typename U>
|
||||
ClientMessage(T &&_channel, U &&_message)
|
||||
:channel(std::forward<T>(_channel)),
|
||||
message(std::forward<U>(_message)) {}
|
||||
|
||||
/**
|
||||
* Checks whether one of the patterns in the .mpdignore file matches
|
||||
* the specified file name.
|
||||
*/
|
||||
const char *GetChannel() const {
|
||||
return channel.c_str();
|
||||
}
|
||||
|
||||
const char *GetMessage() const {
|
||||
return message.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
gcc_pure
|
||||
bool
|
||||
exclude_list_check(GSList *list, const char *name_fs);
|
||||
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);
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 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,30 +18,34 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "cmdline.h"
|
||||
#include "path.h"
|
||||
#include "log.h"
|
||||
#include "conf.h"
|
||||
#include "decoder_list.h"
|
||||
#include "decoder_plugin.h"
|
||||
#include "output_list.h"
|
||||
#include "output_plugin.h"
|
||||
#include "input_registry.h"
|
||||
#include "input_plugin.h"
|
||||
#include "playlist_list.h"
|
||||
#include "playlist_plugin.h"
|
||||
#include "ls.h"
|
||||
#include "mpd_error.h"
|
||||
#include "glib_compat.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 "encoder_list.h"
|
||||
#include "encoder_plugin.h"
|
||||
#include "EncoderList.hxx"
|
||||
#include "EncoderPlugin.hxx"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
#include "archive_list.h"
|
||||
#include "archive_plugin.h"
|
||||
#include "ArchiveList.hxx"
|
||||
#include "ArchivePlugin.hxx"
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
@@ -49,41 +53,38 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#ifdef WIN32
|
||||
#define CONFIG_FILE_LOCATION "\\mpd\\mpd.conf"
|
||||
#else /* G_OS_WIN32 */
|
||||
#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 GQuark
|
||||
cmdline_quark(void)
|
||||
{
|
||||
return g_quark_from_static_string("cmdline");
|
||||
}
|
||||
static constexpr Domain cmdline_domain("cmdline");
|
||||
|
||||
G_GNUC_NORETURN
|
||||
gcc_noreturn
|
||||
static void version(void)
|
||||
{
|
||||
puts(PACKAGE " (MPD: Music Player Daemon) " VERSION " \n"
|
||||
puts("Music Player Daemon " VERSION "\n"
|
||||
"\n"
|
||||
"Copyright (C) 2003-2007 Warren Dukes <warren.dukes@gmail.com>\n"
|
||||
"Copyright (C) 2008-2012 Max Kellermann <max@duempel.org>\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(plugin) {
|
||||
printf(" [%s]", plugin->name);
|
||||
decoder_plugins_for_each([](const DecoderPlugin &plugin){
|
||||
printf(" [%s]", plugin.name);
|
||||
|
||||
const char *const*suffixes = plugin->suffixes;
|
||||
if (suffixes != NULL)
|
||||
for (; *suffixes != NULL; ++suffixes)
|
||||
printf(" %s", *suffixes);
|
||||
const char *const*suffixes = plugin.suffixes;
|
||||
if (suffixes != nullptr)
|
||||
for (; *suffixes != nullptr; ++suffixes)
|
||||
printf(" %s", *suffixes);
|
||||
|
||||
puts("");
|
||||
}
|
||||
puts("");
|
||||
});
|
||||
|
||||
puts("\n"
|
||||
"Output plugins:");
|
||||
@@ -106,8 +107,8 @@ static void version(void)
|
||||
printf(" [%s]", plugin->name);
|
||||
|
||||
const char *const*suffixes = plugin->suffixes;
|
||||
if (suffixes != NULL)
|
||||
for (; *suffixes != NULL; ++suffixes)
|
||||
if (suffixes != nullptr)
|
||||
for (; *suffixes != nullptr; ++suffixes)
|
||||
printf(" %s", *suffixes);
|
||||
|
||||
puts("");
|
||||
@@ -134,11 +135,20 @@ static void version(void)
|
||||
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,
|
||||
GError **error_r)
|
||||
Error &error)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GOptionContext *context;
|
||||
bool ret;
|
||||
static gboolean option_version,
|
||||
@@ -146,20 +156,20 @@ parse_cmdline(int argc, char **argv, struct options *options,
|
||||
option_no_config;
|
||||
const GOptionEntry entries[] = {
|
||||
{ "kill", 0, 0, G_OPTION_ARG_NONE, &options->kill,
|
||||
"kill the currently running mpd session", NULL },
|
||||
"kill the currently running mpd session", nullptr },
|
||||
{ "no-config", 0, 0, G_OPTION_ARG_NONE, &option_no_config,
|
||||
"don't read from config", NULL },
|
||||
"don't read from config", nullptr },
|
||||
{ "no-daemon", 0, 0, G_OPTION_ARG_NONE, &option_no_daemon,
|
||||
"don't detach from console", NULL },
|
||||
"don't detach from console", nullptr },
|
||||
{ "stdout", 0, 0, G_OPTION_ARG_NONE, &options->log_stderr,
|
||||
NULL, NULL },
|
||||
nullptr, nullptr },
|
||||
{ "stderr", 0, 0, G_OPTION_ARG_NONE, &options->log_stderr,
|
||||
"print messages to stderr", NULL },
|
||||
"print messages to stderr", nullptr },
|
||||
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &options->verbose,
|
||||
"verbose logging", NULL },
|
||||
"verbose logging", nullptr },
|
||||
{ "version", 'V', 0, G_OPTION_ARG_NONE, &option_version,
|
||||
"print version number", NULL },
|
||||
{ .long_name = NULL }
|
||||
"print version number", nullptr },
|
||||
{ nullptr, 0, 0, G_OPTION_ARG_NONE, nullptr, nullptr, nullptr }
|
||||
};
|
||||
|
||||
options->kill = false;
|
||||
@@ -168,15 +178,16 @@ parse_cmdline(int argc, char **argv, struct options *options,
|
||||
options->verbose = false;
|
||||
|
||||
context = g_option_context_new("[path/to/mpd.conf]");
|
||||
g_option_context_add_main_entries(context, entries, NULL);
|
||||
g_option_context_add_main_entries(context, entries, nullptr);
|
||||
|
||||
g_option_context_set_summary(context, summary);
|
||||
|
||||
ret = g_option_context_parse(context, &argc, &argv, &error);
|
||||
GError *gerror = nullptr;
|
||||
ret = g_option_context_parse(context, &argc, &argv, &gerror);
|
||||
g_option_context_free(context);
|
||||
|
||||
if (!ret)
|
||||
MPD_ERROR("option parsing failed: %s\n", error->message);
|
||||
FatalError("option parsing failed", gerror);
|
||||
|
||||
if (option_version)
|
||||
version();
|
||||
@@ -188,66 +199,55 @@ parse_cmdline(int argc, char **argv, struct options *options,
|
||||
options->daemon = !option_no_daemon;
|
||||
|
||||
if (option_no_config) {
|
||||
g_debug("Ignoring config, using daemon defaults\n");
|
||||
LogDebug(cmdline_domain,
|
||||
"Ignoring config, using daemon defaults");
|
||||
return true;
|
||||
} else if (argc <= 1) {
|
||||
/* default configuration file path */
|
||||
char *path1;
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
path1 = g_build_filename(g_get_user_config_dir(),
|
||||
CONFIG_FILE_LOCATION, NULL);
|
||||
if (g_file_test(path1, G_FILE_TEST_IS_REGULAR))
|
||||
ret = config_read_file(path1, error_r);
|
||||
else {
|
||||
int i = 0;
|
||||
char *system_path = NULL;
|
||||
const char * const *system_config_dirs;
|
||||
#ifdef WIN32
|
||||
AllocatedPath path = PathBuildChecked(AllocatedPath::FromUTF8(g_get_user_config_dir()),
|
||||
CONFIG_FILE_LOCATION);
|
||||
if (!path.IsNull() && FileExists(path))
|
||||
return ReadConfigFile(path, error);
|
||||
|
||||
system_config_dirs = g_get_system_config_dirs();
|
||||
const char *const*system_config_dirs =
|
||||
g_get_system_config_dirs();
|
||||
|
||||
while(system_config_dirs[i] != NULL) {
|
||||
system_path = g_build_filename(system_config_dirs[i],
|
||||
CONFIG_FILE_LOCATION,
|
||||
NULL);
|
||||
if(g_file_test(system_path,
|
||||
G_FILE_TEST_IS_REGULAR)) {
|
||||
ret = config_read_file(system_path,error_r);
|
||||
break;
|
||||
}
|
||||
++i;;
|
||||
}
|
||||
g_free(system_path);
|
||||
g_free(&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 /* G_OS_WIN32 */
|
||||
char *path2;
|
||||
path1 = g_build_filename(g_get_home_dir(),
|
||||
USER_CONFIG_FILE_LOCATION1, NULL);
|
||||
path2 = g_build_filename(g_get_home_dir(),
|
||||
USER_CONFIG_FILE_LOCATION2, NULL);
|
||||
if (g_file_test(path1, G_FILE_TEST_IS_REGULAR))
|
||||
ret = config_read_file(path1, error_r);
|
||||
else if (g_file_test(path2, G_FILE_TEST_IS_REGULAR))
|
||||
ret = config_read_file(path2, error_r);
|
||||
else if (g_file_test(SYSTEM_CONFIG_FILE_LOCATION,
|
||||
G_FILE_TEST_IS_REGULAR))
|
||||
ret = config_read_file(SYSTEM_CONFIG_FILE_LOCATION,
|
||||
error_r);
|
||||
#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
|
||||
|
||||
g_free(path1);
|
||||
#ifndef G_OS_WIN32
|
||||
g_free(path2);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
error.Set(cmdline_domain, "No configuration file found");
|
||||
return false;
|
||||
} else if (argc == 2) {
|
||||
/* specified configuration file */
|
||||
return config_read_file(argv[1], error_r);
|
||||
return ReadConfigFile(Path::FromFS(argv[1]), error);
|
||||
} else {
|
||||
g_set_error(error_r, cmdline_quark(), 0,
|
||||
"too many arguments");
|
||||
error.Set(cmdline_domain, "too many arguments");
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 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");
|
25
src/ConfigError.hxx
Normal file
25
src/ConfigError.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_CONFIG_ERROR_HXX
|
||||
#define MPD_CONFIG_ERROR_HXX
|
||||
|
||||
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-2011 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,31 +18,25 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "crossfade.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
|
||||
@@ -50,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) {
|
||||
@@ -78,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-2011 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
|
||||
|
||||
@@ -52,7 +55,7 @@ static uid_t user_uid = (uid_t)-1;
|
||||
static gid_t user_gid = (pid_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 process %i: %s",
|
||||
pid, g_strerror(errno));
|
||||
FormatFatalSystemError("unable to kill process %i",
|
||||
int(pid));
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
@@ -96,14 +102,14 @@ 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));
|
||||
FormatFatalSystemError("Failed to set group %d",
|
||||
(int)user_gid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,17 +118,17 @@ daemonize_set_user(void)
|
||||
* (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));
|
||||
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 +137,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 +150,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 +161,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 +219,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-2011 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");
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 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,10 +17,10 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_DB_ERROR_H
|
||||
#define MPD_DB_ERROR_H
|
||||
#ifndef MPD_DB_ERROR_HXX
|
||||
#define MPD_DB_ERROR_HXX
|
||||
|
||||
#include <glib.h>
|
||||
class Domain;
|
||||
|
||||
enum db_error {
|
||||
/**
|
||||
@@ -32,14 +32,6 @@ enum db_error {
|
||||
DB_NOT_FOUND,
|
||||
};
|
||||
|
||||
/**
|
||||
* Quark for GError.domain; the code is an enum #db_error.
|
||||
*/
|
||||
G_GNUC_CONST
|
||||
static inline GQuark
|
||||
db_quark(void)
|
||||
{
|
||||
return g_quark_from_static_string("db");
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
time_t
|
||||
db_get_mtime(void)
|
||||
{
|
||||
assert(db != nullptr);
|
||||
assert(db_is_open);
|
||||
assert(db_is_simple());
|
||||
|
||||
return ((SimpleDatabase *)db)->GetLastModified();
|
||||
}
|
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
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 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,29 +23,31 @@
|
||||
* multi-threading.
|
||||
*/
|
||||
|
||||
#ifndef MPD_DB_LOCK_H
|
||||
#define MPD_DB_LOCK_H
|
||||
#ifndef MPD_DB_LOCK_HXX
|
||||
#define MPD_DB_LOCK_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "thread/Mutex.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
extern GStaticMutex db_mutex;
|
||||
extern Mutex db_mutex;
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
extern GThread *db_mutex_holder;
|
||||
#include "thread/Id.hxx"
|
||||
|
||||
extern ThreadId db_mutex_holder;
|
||||
|
||||
/**
|
||||
* Does the current thread hold the database lock?
|
||||
*/
|
||||
G_GNUC_PURE
|
||||
gcc_pure
|
||||
static inline bool
|
||||
holding_db_lock(void)
|
||||
{
|
||||
return db_mutex_holder == g_thread_self();
|
||||
return db_mutex_holder.IsInside();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -59,11 +61,11 @@ db_lock(void)
|
||||
{
|
||||
assert(!holding_db_lock());
|
||||
|
||||
g_static_mutex_lock(&db_mutex);
|
||||
db_mutex.lock();
|
||||
|
||||
assert(db_mutex_holder == NULL);
|
||||
assert(db_mutex_holder.IsNull());
|
||||
#ifndef NDEBUG
|
||||
db_mutex_holder = g_thread_self();
|
||||
db_mutex_holder = ThreadId::GetCurrent();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -75,10 +77,21 @@ db_unlock(void)
|
||||
{
|
||||
assert(holding_db_lock());
|
||||
#ifndef NDEBUG
|
||||
db_mutex_holder = NULL;
|
||||
db_mutex_holder = ThreadId::Null();
|
||||
#endif
|
||||
|
||||
g_static_mutex_unlock(&db_mutex);
|
||||
db_mutex.unlock();
|
||||
}
|
||||
|
||||
class ScopeDatabaseLock {
|
||||
public:
|
||||
ScopeDatabaseLock() {
|
||||
db_lock();
|
||||
}
|
||||
|
||||
~ScopeDatabaseLock() {
|
||||
db_unlock();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 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,41 +18,33 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "pcm_buffer.h"
|
||||
#include "poison.h"
|
||||
#include "DatabasePlaylist.hxx"
|
||||
#include "DatabaseSelection.hxx"
|
||||
#include "PlaylistFile.hxx"
|
||||
#include "DatabaseGlue.hxx"
|
||||
#include "DatabasePlugin.hxx"
|
||||
|
||||
/**
|
||||
* Align the specified size to the next 8k boundary.
|
||||
*/
|
||||
G_GNUC_CONST
|
||||
static size_t
|
||||
align_8k(size_t size)
|
||||
#include <functional>
|
||||
|
||||
static bool
|
||||
AddSong(const char *playlist_path_utf8,
|
||||
Song &song, Error &error)
|
||||
{
|
||||
return ((size - 1) | 0x1fff) + 1;
|
||||
return spl_append_song(playlist_path_utf8, song, error);
|
||||
}
|
||||
|
||||
void *
|
||||
pcm_buffer_get(struct pcm_buffer *buffer, size_t size)
|
||||
bool
|
||||
search_add_to_playlist(const char *uri, const char *playlist_path_utf8,
|
||||
const SongFilter *filter,
|
||||
Error &error)
|
||||
{
|
||||
assert(buffer != NULL);
|
||||
const Database *db = GetDatabase(error);
|
||||
if (db == nullptr)
|
||||
return false;
|
||||
|
||||
if (size == 0)
|
||||
/* never return NULL, because NULL would be assumed to
|
||||
be an error condition */
|
||||
size = 1;
|
||||
const DatabaseSelection selection(uri, true, filter);
|
||||
|
||||
if (buffer->size < size) {
|
||||
/* free the old buffer */
|
||||
g_free(buffer->buffer);
|
||||
|
||||
buffer->size = align_8k(size);
|
||||
buffer->buffer = g_malloc(buffer->size);
|
||||
} else {
|
||||
/* discard old buffer contents */
|
||||
poison_undefined(buffer->buffer, buffer->size);
|
||||
}
|
||||
|
||||
assert(buffer->size >= size);
|
||||
|
||||
return buffer->buffer;
|
||||
using namespace std::placeholders;
|
||||
const auto f = std::bind(AddSong, playlist_path_utf8, _1, _2);
|
||||
return db->Visit(selection, f, error);
|
||||
}
|
34
src/DatabasePlaylist.hxx
Normal file
34
src/DatabasePlaylist.hxx
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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_PLAYLIST_HXX
|
||||
#define MPD_DATABASE_PLAYLIST_HXX
|
||||
|
||||
#include "Compiler.h"
|
||||
|
||||
class SongFilter;
|
||||
class Error;
|
||||
|
||||
gcc_nonnull(1,2)
|
||||
bool
|
||||
search_add_to_playlist(const char *uri, const char *path_utf8,
|
||||
const SongFilter *filter,
|
||||
Error &error);
|
||||
|
||||
#endif
|
147
src/DatabasePlugin.hxx
Normal file
147
src/DatabasePlugin.hxx
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* This header declares the db_plugin class. It describes a
|
||||
* plugin API for databases of song metadata.
|
||||
*/
|
||||
|
||||
#ifndef MPD_DATABASE_PLUGIN_HXX
|
||||
#define MPD_DATABASE_PLUGIN_HXX
|
||||
|
||||
#include "DatabaseVisitor.hxx"
|
||||
#include "tag/TagType.h"
|
||||
#include "Compiler.h"
|
||||
|
||||
struct config_param;
|
||||
struct DatabaseSelection;
|
||||
struct db_visitor;
|
||||
struct Song;
|
||||
class Error;
|
||||
|
||||
struct DatabaseStats {
|
||||
/**
|
||||
* Number of songs.
|
||||
*/
|
||||
unsigned song_count;
|
||||
|
||||
/**
|
||||
* Total duration of all songs (in seconds).
|
||||
*/
|
||||
unsigned long total_duration;
|
||||
|
||||
/**
|
||||
* Number of distinct artist names.
|
||||
*/
|
||||
unsigned artist_count;
|
||||
|
||||
/**
|
||||
* Number of distinct album names.
|
||||
*/
|
||||
unsigned album_count;
|
||||
|
||||
void Clear() {
|
||||
song_count = 0;
|
||||
total_duration = 0;
|
||||
artist_count = album_count = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class Database {
|
||||
public:
|
||||
/**
|
||||
* Free instance data.
|
||||
*/
|
||||
virtual ~Database() {}
|
||||
|
||||
/**
|
||||
* Open the database. Read it into memory if applicable.
|
||||
*/
|
||||
virtual bool Open(gcc_unused Error &error) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the database, free allocated memory.
|
||||
*/
|
||||
virtual void Close() {}
|
||||
|
||||
/**
|
||||
* Look up a song (including tag data) in the database. When
|
||||
* you don't need this anymore, call ReturnSong().
|
||||
*
|
||||
* @param uri_utf8 the URI of the song within the music
|
||||
* directory (UTF-8)
|
||||
*/
|
||||
virtual Song *GetSong(const char *uri_utf8,
|
||||
Error &error) const = 0;
|
||||
|
||||
/**
|
||||
* Mark the song object as "unused". Call this on objects
|
||||
* returned by GetSong().
|
||||
*/
|
||||
virtual void ReturnSong(Song *song) const = 0;
|
||||
|
||||
/**
|
||||
* Visit the selected entities.
|
||||
*/
|
||||
virtual bool Visit(const DatabaseSelection &selection,
|
||||
VisitDirectory visit_directory,
|
||||
VisitSong visit_song,
|
||||
VisitPlaylist visit_playlist,
|
||||
Error &error) const = 0;
|
||||
|
||||
bool Visit(const DatabaseSelection &selection,
|
||||
VisitDirectory visit_directory,
|
||||
VisitSong visit_song,
|
||||
Error &error) const {
|
||||
return Visit(selection, visit_directory, visit_song,
|
||||
VisitPlaylist(), error);
|
||||
}
|
||||
|
||||
bool Visit(const DatabaseSelection &selection, VisitSong visit_song,
|
||||
Error &error) const {
|
||||
return Visit(selection, VisitDirectory(), visit_song, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit all unique tag values.
|
||||
*/
|
||||
virtual bool VisitUniqueTags(const DatabaseSelection &selection,
|
||||
TagType tag_type,
|
||||
VisitString visit_string,
|
||||
Error &error) const = 0;
|
||||
|
||||
virtual bool GetStats(const DatabaseSelection &selection,
|
||||
DatabaseStats &stats,
|
||||
Error &error) const = 0;
|
||||
};
|
||||
|
||||
struct DatabasePlugin {
|
||||
const char *name;
|
||||
|
||||
/**
|
||||
* Allocates and configures a database.
|
||||
*/
|
||||
Database *(*create)(const config_param ¶m,
|
||||
Error &error);
|
||||
};
|
||||
|
||||
#endif
|
241
src/DatabasePrint.cxx
Normal file
241
src/DatabasePrint.cxx
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* 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 "DatabasePrint.hxx"
|
||||
#include "DatabaseSelection.hxx"
|
||||
#include "SongFilter.hxx"
|
||||
#include "PlaylistVector.hxx"
|
||||
#include "SongPrint.hxx"
|
||||
#include "TimePrint.hxx"
|
||||
#include "Directory.hxx"
|
||||
#include "Client.hxx"
|
||||
#include "tag/Tag.hxx"
|
||||
#include "Song.hxx"
|
||||
#include "DatabaseGlue.hxx"
|
||||
#include "DatabasePlugin.hxx"
|
||||
|
||||
#include <functional>
|
||||
|
||||
static bool
|
||||
PrintDirectoryBrief(Client &client, const Directory &directory)
|
||||
{
|
||||
if (!directory.IsRoot())
|
||||
client_printf(client, "directory: %s\n", directory.GetPath());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
PrintDirectoryFull(Client &client, const Directory &directory)
|
||||
{
|
||||
if (!directory.IsRoot()) {
|
||||
client_printf(client, "directory: %s\n", directory.GetPath());
|
||||
time_print(client, "Last-Modified", directory.mtime);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_playlist_in_directory(Client &client,
|
||||
const Directory &directory,
|
||||
const char *name_utf8)
|
||||
{
|
||||
if (directory.IsRoot())
|
||||
client_printf(client, "playlist: %s\n", name_utf8);
|
||||
else
|
||||
client_printf(client, "playlist: %s/%s\n",
|
||||
directory.GetPath(), name_utf8);
|
||||
}
|
||||
|
||||
static bool
|
||||
PrintSongBrief(Client &client, const Song &song)
|
||||
{
|
||||
assert(song.parent != nullptr);
|
||||
|
||||
song_print_uri(client, song);
|
||||
|
||||
if (song.tag != nullptr && song.tag->has_playlist)
|
||||
/* this song file has an embedded CUE sheet */
|
||||
print_playlist_in_directory(client, *song.parent, song.uri);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
PrintSongFull(Client &client, const Song &song)
|
||||
{
|
||||
assert(song.parent != nullptr);
|
||||
|
||||
song_print_info(client, song);
|
||||
|
||||
if (song.tag != nullptr && song.tag->has_playlist)
|
||||
/* this song file has an embedded CUE sheet */
|
||||
print_playlist_in_directory(client, *song.parent, song.uri);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
PrintPlaylistBrief(Client &client,
|
||||
const PlaylistInfo &playlist,
|
||||
const Directory &directory)
|
||||
{
|
||||
print_playlist_in_directory(client, directory, playlist.name.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
PrintPlaylistFull(Client &client,
|
||||
const PlaylistInfo &playlist,
|
||||
const Directory &directory)
|
||||
{
|
||||
print_playlist_in_directory(client, directory, playlist.name.c_str());
|
||||
|
||||
if (playlist.mtime > 0)
|
||||
time_print(client, "Last-Modified", playlist.mtime);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
db_selection_print(Client &client, const DatabaseSelection &selection,
|
||||
bool full, Error &error)
|
||||
{
|
||||
const Database *db = GetDatabase(error);
|
||||
if (db == nullptr)
|
||||
return false;
|
||||
|
||||
using namespace std::placeholders;
|
||||
const auto d = selection.filter == nullptr
|
||||
? std::bind(full ? PrintDirectoryFull : PrintDirectoryBrief,
|
||||
std::ref(client), _1)
|
||||
: VisitDirectory();
|
||||
const auto s = std::bind(full ? PrintSongFull : PrintSongBrief,
|
||||
std::ref(client), _1);
|
||||
const auto p = selection.filter == nullptr
|
||||
? std::bind(full ? PrintPlaylistFull : PrintPlaylistBrief,
|
||||
std::ref(client), _1, _2)
|
||||
: VisitPlaylist();
|
||||
|
||||
return db->Visit(selection, d, s, p, error);
|
||||
}
|
||||
|
||||
struct SearchStats {
|
||||
int numberOfSongs;
|
||||
unsigned long playTime;
|
||||
};
|
||||
|
||||
static void printSearchStats(Client &client, SearchStats *stats)
|
||||
{
|
||||
client_printf(client, "songs: %i\n", stats->numberOfSongs);
|
||||
client_printf(client, "playtime: %li\n", stats->playTime);
|
||||
}
|
||||
|
||||
static bool
|
||||
stats_visitor_song(SearchStats &stats, Song &song)
|
||||
{
|
||||
stats.numberOfSongs++;
|
||||
stats.playTime += song.GetDuration();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
searchStatsForSongsIn(Client &client, const char *name,
|
||||
const SongFilter *filter,
|
||||
Error &error)
|
||||
{
|
||||
const Database *db = GetDatabase(error);
|
||||
if (db == nullptr)
|
||||
return false;
|
||||
|
||||
const DatabaseSelection selection(name, true, filter);
|
||||
|
||||
SearchStats stats;
|
||||
stats.numberOfSongs = 0;
|
||||
stats.playTime = 0;
|
||||
|
||||
using namespace std::placeholders;
|
||||
const auto f = std::bind(stats_visitor_song, std::ref(stats),
|
||||
_1);
|
||||
if (!db->Visit(selection, f, error))
|
||||
return false;
|
||||
|
||||
printSearchStats(client, &stats);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
printAllIn(Client &client, const char *uri_utf8, Error &error)
|
||||
{
|
||||
const DatabaseSelection selection(uri_utf8, true);
|
||||
return db_selection_print(client, selection, false, error);
|
||||
}
|
||||
|
||||
bool
|
||||
printInfoForAllIn(Client &client, const char *uri_utf8,
|
||||
Error &error)
|
||||
{
|
||||
const DatabaseSelection selection(uri_utf8, true);
|
||||
return db_selection_print(client, selection, true, error);
|
||||
}
|
||||
|
||||
static bool
|
||||
PrintSongURIVisitor(Client &client, Song &song)
|
||||
{
|
||||
song_print_uri(client, song);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
PrintUniqueTag(Client &client, TagType tag_type,
|
||||
const char *value)
|
||||
{
|
||||
client_printf(client, "%s: %s\n", tag_item_names[tag_type], value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
listAllUniqueTags(Client &client, int type,
|
||||
const SongFilter *filter,
|
||||
Error &error)
|
||||
{
|
||||
const Database *db = GetDatabase(error);
|
||||
if (db == nullptr)
|
||||
return false;
|
||||
|
||||
const DatabaseSelection selection("", true, filter);
|
||||
|
||||
if (type == LOCATE_TAG_FILE_TYPE) {
|
||||
using namespace std::placeholders;
|
||||
const auto f = std::bind(PrintSongURIVisitor,
|
||||
std::ref(client), _1);
|
||||
return db->Visit(selection, f, error);
|
||||
} else {
|
||||
using namespace std::placeholders;
|
||||
const auto f = std::bind(PrintUniqueTag, std::ref(client),
|
||||
(TagType)type, _1);
|
||||
return db->VisitUniqueTags(selection, (TagType)type,
|
||||
f, error);
|
||||
}
|
||||
}
|
55
src/DatabasePrint.hxx
Normal file
55
src/DatabasePrint.hxx
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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_PRINT_H
|
||||
#define MPD_DB_PRINT_H
|
||||
|
||||
#include "Compiler.h"
|
||||
|
||||
class SongFilter;
|
||||
struct DatabaseSelection;
|
||||
struct db_visitor;
|
||||
class Client;
|
||||
class Error;
|
||||
|
||||
bool
|
||||
db_selection_print(Client &client, const DatabaseSelection &selection,
|
||||
bool full, Error &error);
|
||||
|
||||
gcc_nonnull(2)
|
||||
bool
|
||||
printAllIn(Client &client, const char *uri_utf8, Error &error);
|
||||
|
||||
gcc_nonnull(2)
|
||||
bool
|
||||
printInfoForAllIn(Client &client, const char *uri_utf8,
|
||||
Error &error);
|
||||
|
||||
gcc_nonnull(2)
|
||||
bool
|
||||
searchStatsForSongsIn(Client &client, const char *name,
|
||||
const SongFilter *filter,
|
||||
Error &error);
|
||||
|
||||
bool
|
||||
listAllUniqueTags(Client &client, int type,
|
||||
const SongFilter *filter,
|
||||
Error &error);
|
||||
|
||||
#endif
|
54
src/DatabaseQueue.cxx
Normal file
54
src/DatabaseQueue.cxx
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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 "DatabaseQueue.hxx"
|
||||
#include "DatabaseSelection.hxx"
|
||||
#include "DatabaseGlue.hxx"
|
||||
#include "DatabasePlugin.hxx"
|
||||
#include "Partition.hxx"
|
||||
#include "util/Error.hxx"
|
||||
|
||||
#include <functional>
|
||||
|
||||
static bool
|
||||
AddToQueue(Partition &partition, Song &song, Error &error)
|
||||
{
|
||||
PlaylistResult result =
|
||||
partition.playlist.AppendSong(partition.pc, &song, nullptr);
|
||||
if (result != PlaylistResult::SUCCESS) {
|
||||
error.Set(playlist_domain, int(result), "Playlist error");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
AddFromDatabase(Partition &partition, const DatabaseSelection &selection,
|
||||
Error &error)
|
||||
{
|
||||
const Database *db = GetDatabase(error);
|
||||
if (db == nullptr)
|
||||
return false;
|
||||
|
||||
using namespace std::placeholders;
|
||||
const auto f = std::bind(AddToQueue, std::ref(partition), _1, _2);
|
||||
return db->Visit(selection, f, error);
|
||||
}
|
31
src/DatabaseQueue.hxx
Normal file
31
src/DatabaseQueue.hxx
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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_QUEUE_HXX
|
||||
#define MPD_DATABASE_QUEUE_HXX
|
||||
|
||||
struct Partition;
|
||||
struct DatabaseSelection;
|
||||
class Error;
|
||||
|
||||
bool
|
||||
AddFromDatabase(Partition &partition, const DatabaseSelection &selection,
|
||||
Error &error);
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user