pax_global_header00006660000000000000000000000064115337452370014524gustar00rootroot0000000000000052 comment=79ab35c7bfdc215f2d91b6f4fd9e933725d02667 mpdcron-0.3+git20110303/000077500000000000000000000000001153374523700144215ustar00rootroot00000000000000mpdcron-0.3+git20110303/.gitignore000066400000000000000000000010521153374523700164070ustar00rootroot00000000000000# gitignore for mpdhooker .*.swp *~ *.[oa] *.lo *.la *.tar.bz2 *.sha1sum *.asc .deps .libs Makefile.in Makefile /INSTALL /aclocal.m4 /autom4te.cache /config.guess /config.h /config.h.in /config.log /config.sub /config.status /configure /compile /depcomp /install-sh /missing /stamp-h1 # libtool libtool ltmain.sh m4/libtool.m4 m4/ltversion.m4 m4/ltversion.m4 m4/lt~obsolete.m4 m4/ltoptions.m4 m4/ltsugar.m4 # binaries src/mpdcron src/gmodule/stats/eugene src/gmodule/stats/walrus src/gmodule/stats/homescrape # GNU global GPATH GRTAGS GSYMS GTAGS mpdcron-0.3+git20110303/COPYING000066400000000000000000000431331153374523700154600ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. mpdcron-0.3+git20110303/GPATH000066400000000000000000001000001153374523700151760ustar00rootroot00000000000000b1 X  0t0 PL|Td    DX \ <pXX4T |hTt0T4tT4D  ,   dldd4D$d$D$dD$d4 t   \ | < | 4 $ t <  T 4<t$DT |,t,90./conf/hooks/dump-stats.basho./conf/hooks/dump-stats.bash90o89./conf/hooks/mixero./conf/hooks/mixer89o88./debian/copyrighto./debian/copyright88o87./debian/docso./debian/docs87o86./debian/ruleso./debian/rules86o85./debian/compato./debian/compat85o84./debian/source/formato./debian/source/format84o83./debian/mpdcron.installo./debian/mpdcron.install83o82./debian/controlo./debian/control82o81./debian/changelogo./debian/changelog81o80./debian/patches/serieso./debian/patches/series80o479./debian/patches/debian-changes-0.3+git20100302-1o2./debian/patches/debian-changes-0.3+git20100302-179o78./debian/mpdcron.manpageso./debian/mpdcron.manpages78o77./debian/watcho./debian/watch77o76./README.mkdo ./README.mkd76o75./zsh-completion/_mpdcrono./zsh-completion/_mpdcron75o74./zsh-completion/_walruso./zsh-completion/_walrus74o73./zsh-completion/Makefile.amo./zsh-completion/Makefile.am73o72./zsh-completion/_eugeneo./zsh-completion/_eugene72o"71./m4/ax_check_compiler_flags.m4o ./m4/ax_check_compiler_flags.m471o 70./COPYINGo ./COPYING70o69./data/mpdcron.1.pdco./data/mpdcron.1.pdc69o68./data/Makefile.amo./data/Makefile.am68o67./data/mpdcron.1o./data/mpdcron.167o66./Makefile.amo./Makefile.am66o65./Makefile.ino./Makefile.in65o64./aclocal.m4o ./aclocal.m464o 63./NEWS.mkdo ./NEWS.mkd63o62./src/cron-defs.h./src/cron-defs.h6261./src/cron-main.c./src/cron-main.c6160./src/cron-env.c./src/cron-env.c6059./src/cron-keyfile.c./src/cron-keyfile.c5958./src/cron-config.h./src/cron-config.h5857./src/cron-log.c./src/cron-log.c5756./src/cron-event.c./src/cron-event.c5655./src/cron-loop.c./src/cron-loop.c5554./src/Makefile.amo./src/Makefile.am54o53./src/valgrind.sho./src/valgrind.sh53o"52./src/gmodule/stats/walrus-defs.h"./src/gmodule/stats/walrus-defs.h52#51./src/gmodule/stats/walrus-utils.c#./src/gmodule/stats/walrus-utils.c51 50./src/gmodule/stats/tokenizer.h ./src/gmodule/stats/tokenizer.h50#49./src/gmodule/stats/stats-sqlite.h#./src/gmodule/stats/stats-sqlite.h49"48./src/gmodule/stats/walrus-main.c"./src/gmodule/stats/walrus-main.c48#47./src/gmodule/stats/stats-module.c#./src/gmodule/stats/stats-module.c47(46./src/gmodule/stats/eugene-connection.c(./src/gmodule/stats/eugene-connection.c46!45./src/gmodule/stats/stats-defs.h!./src/gmodule/stats/stats-defs.h45&44./src/gmodule/stats/eugene-listtags.c&./src/gmodule/stats/eugene-listtags.c44#43./src/gmodule/stats/eugene-rmtag.c#./src/gmodule/stats/eugene-rmtag.c43"42./src/gmodule/stats/eugene-list.c"./src/gmodule/stats/eugene-list.c42$41./src/gmodule/stats/stats-command.c$./src/gmodule/stats/stats-command.c41$40./src/gmodule/stats/eugene-addtag.c$./src/gmodule/stats/eugene-addtag.c40"39./src/gmodule/stats/Makefile.amo ./src/gmodule/stats/Makefile.am39o"38./src/gmodule/stats/eugene-defs.h"./src/gmodule/stats/eugene-defs.h38"37./src/gmodule/stats/valgrind.sho ./src/gmodule/stats/valgrind.sh37o#36./src/gmodule/stats/stats-server.c#./src/gmodule/stats/stats-server.c36#35./src/gmodule/stats/stats-sqlite.c#./src/gmodule/stats/stats-sqlite.c35"34./src/gmodule/stats/eugene-main.c"./src/gmodule/stats/eugene-main.c34"33./src/gmodule/stats/eugene-love.c"./src/gmodule/stats/eugene-love.c33 32./src/gmodule/stats/tokenizer.c ./src/gmodule/stats/tokenizer.c32&31./src/gmodule/stats/eugene-listinfo.c&./src/gmodule/stats/eugene-listinfo.c31$30./src/gmodule/stats/homescrape.ino"./src/gmodule/stats/homescrape.in30o"29./src/gmodule/stats/eugene-kill.c"./src/gmodule/stats/eugene-kill.c29"28./src/gmodule/stats/eugene-rate.c"./src/gmodule/stats/eugene-rate.c28#27./src/gmodule/stats/eugene-utils.c#./src/gmodule/stats/eugene-utils.c27!26./src/gmodule/stats/stats-file.c!./src/gmodule/stats/stats-file.c26#25./src/gmodule/stats/eugene-count.c#./src/gmodule/stats/eugene-count.c2524./src/gmodule/gmodule.h./src/gmodule/gmodule.h24/23./src/gmodule/notification/notification-file.c/./src/gmodule/notification/notification-file.c23022./src/gmodule/notification/notification-cover.c0./src/gmodule/notification/notification-cover.c22/21./src/gmodule/notification/notification-defs.h/./src/gmodule/notification/notification-defs.h21/20./src/gmodule/notification/notification-dhms.c/./src/gmodule/notification/notification-dhms.c20)19./src/gmodule/notification/Makefile.amo'./src/gmodule/notification/Makefile.am19o018./src/gmodule/notification/notification-spawn.c0./src/gmodule/notification/notification-spawn.c18117./src/gmodule/notification/notification-module.c1./src/gmodule/notification/notification-module.c1716./src/gmodule/Makefile.amo./src/gmodule/Makefile.am16o15./src/gmodule/utils.h./src/gmodule/utils.h15)14./src/gmodule/scrobbler/scrobbler-file.c)./src/gmodule/scrobbler/scrobbler-file.c14+13./src/gmodule/scrobbler/scrobbler-module.c+./src/gmodule/scrobbler/scrobbler-module.c13)12./src/gmodule/scrobbler/scrobbler-curl.c)./src/gmodule/scrobbler/scrobbler-curl.c12,11./src/gmodule/scrobbler/scrobbler-journal.c,./src/gmodule/scrobbler/scrobbler-journal.c11&10./src/gmodule/scrobbler/Makefile.amo$./src/gmodule/scrobbler/Makefile.am10o+9./src/gmodule/scrobbler/scrobbler-record.c+./src/gmodule/scrobbler/scrobbler-record.c9*8./src/gmodule/scrobbler/scrobbler-timer.c*./src/gmodule/scrobbler/scrobbler-timer.c8+7./src/gmodule/scrobbler/scrobbler-submit.c+./src/gmodule/scrobbler/scrobbler-submit.c7)6./src/gmodule/scrobbler/scrobbler-defs.h)./src/gmodule/scrobbler/scrobbler-defs.h65./src/cron-gmodule.c./src/cron-gmodule.c54./src/valgrind.suppressionso./src/valgrind.suppressions4o3./src/cron-hooker.c./src/cron-hooker.c32./src/cron-conf.c./src/cron-conf.c21./configure.aco./confi./zsh-completion/_walrus__2pT8 ,hPDt8`d@ p0pP0pL, h,x8\$|D xHP `0p@X(X,  __.NEXTKEY109 ./autogen.sh108o./conf/modules/Makefile107o./conf/modules/example.c106./conf/mpdcron.conf105o./conf/hooks/dump-outputs.rb104o./conf/hooks/submit.rb103o./conf/hooks/stored_playlist102o./conf/hooks/options101o./conf/hooks/player100o./conf/hooks/update99o./conf/hooks/database98o./conf/hooks/dump-playlists.rb97o./conf/hooks/dump-song.bash96o./conf/hooks/dump-status.bash95o./conf/hooks/playlist94o./conf/hooks/output93o./conf/hooks/queue92o./conf/hooks/dump-queue.rb91o./zsh-completion/_mpdcron75o./zsh-completion/_eugene72o./zsh-completion/Makefile.am73o./src/valgrind.suppressions4o./src/valgrind.sh53o./src/gmodule/utils.h15#./src/gmodule/stats/walrus-utils.c51"./src/gmodule/stats/walrus-main.c48"./src/gmodule/stats/walrus-defs.h52 ./src/gmodule/stats/valgrind.sh37o ./src/gmodule/stats/tokenizer.h50 ./src/gmodule/stats/tokenizer.c32#./src/gmodule/stats/stats-sqlite.h49#./src/gmodule/stats/stats-sqlite.c35#./src/gmodule/stats/stats-server.c36#./src/gmodule/stats/stats-module.c47!./src/gmodule/stats/stats-file.c26!./src/gmodule/stats/stats-defs.h45$./src/gmodule/stats/stats-command.c41"./src/gmodule/stats/homescrape.in30o#./src/gmodule/stats/eugene-utils.c27#./src/gmodule/stats/eugene-rmtag.c43"./src/gmodule/stats/eugene-rate.c28"./src/gmodule/stats/eugene-main.c34"./src/gmodule/stats/eugene-love.c33&./src/gmodule/stats/eugene-listtags.c44&./src/gmodule/stats/eugene-listinfo.c31"./src/gmodule/stats/eugene-list.c42"./src/gmodule/stats/eugene-kill.c29"./src/gmodule/stats/eugene-defs.h38#./src/gmodule/stats/eugene-count.c25(./src/gmodule/stats/eugene-connection.c46$./src/gmodule/stats/eugene-addtag.c40 ./src/gmodule/stats/Makefile.am39o*./src/gmodule/scrobbler/scrobbler-timer.c8+./src/gmodule/scrobbler/scrobbler-submit.c7+./src/gmodule/scrobbler/scrobbler-record.c9+./src/gmodule/scrobbler/scrobbler-module.c13,./src/gmodule/scrobbler/scrobbler-journal.c11)./src/gmodule/scrobbler/scrobbler-file.c14)./src/gmodule/scrobbler/scrobbler-defs.h6)./src/gmodule/scrobbler/scrobbler-curl.c12$./src/gmodule/scrobbler/Makefile.am10o0./src/gmodule/notification/notification-spawn.c181./src/gmodule/notification/notification-module.c17/./src/gmodule/notification/notification-file.c23/./src/gmodule/notification/notification-dhms.c20/./src/gmodule/notification/notification-defs.h210./src/gmodule/notification/notification-cover.c22'./src/gmodule/notification/Makefile.am19o./src/gmodule/gmodule.h24./src/gmodule/Makefile.am16o./src/cron-main.c61./src/cron-loop.c55./src/cron-log.c57./src/cron-keyfile.c59./src/cron-hooker.c3./src/cron-gmodule.c5./src/cron-event.c56./src/cron-env.c60./src/cron-defs.h62./src/cron-config.h58./src/cron-conf.c2./src/Makefile.am54o ./m4/ax_check_compiler_flags.m471o./debian/watch77o./debian/source/format84o./debian/rules86o./debian/patches/series80o2./debian/patches/debian-changes-0.3+git20100302-179o./debian/mpdcron.manpages78o./debian/mpdcron.install83o./debian/docs87o./debian/copyright88o./debian/control82o./debian/compat85o./debian/changelog81o./data/mpdcron.1.pdc69o./data/mpdcron.167o./data/Makefile.am68o./configure.ac1o./conf/hooks/mixer89o./conf/hooks/dump-stats.bash90o ./aclocal.m464o ./README.mkd76o ./NEWS.mkd63o./Makefile.in65o./Makefile.am66o ./COPYING70o  __.VERSION __.VERSION 2 $\4  P\LtDd4tDX(d4pP$dD$lL0t<$xP4tL(x\8`<lH108./autogen.sho107./conf/modules/Makefileo106./conf/modules/example.c105./conf/mpdcron.confo104./conf/hooks/dump-outputs.rbo103./conf/hooks/submit.rbo102./conf/hooks/stored_playlisto101./conf/hooks/optionso100./conf/hooks/playero99./conf/hooks/updateo98./conf/hooks/databaseo!97./conf/hooks/dump-playlists.rbo96./conf/hooks/dump-song.basho 95./conf/hooks/dump-status.basho94./conf/hooks/playlisto93./conf/hooks/outputo92./conf/hooks/queueo91./conf/hooks/dump-queue.rbo90./conf/hooks/dump-stats.basho+9./src/gmodule/scrobbler/scrobbler-record.c89./conf/hooks/mixero88./debian/copyrighto87./debian/docso86./debian/ruleso85./debian/compato84./debian/source/formato83./debian/mpdcron.installo82./debian/controlo81./debian/changelogo80./debian/patches/serieso*8./src/gmodule/scrobbler/scrobbler-timer.c479./debian/patches/debian-changes-0.3+git20100302-1o78./debian/mpdcron.manpageso77./debian/watcho76./README.mkdo75./zsh-completion/_mpdcrono74./zsh-completion/_walruso73./zsh-completion/Makefile.amo72./zsh-completion/_eugeneo"71./m4/ax_check_compiler_flags.m4o 70./COPYINGo+7./src/gmodule/scrobbler/scrobbler-submit.c69./data/mpdcron.1.pdco68./data/Makefile.amo67./data/mpdcron.1o66./Makefile.amo65./Makefile.ino64./aclocal.m4o 63./NEWS.mkdo62./src/cron-defs.h61./src/cron-main.c60./src/cron-env.c)6./src/gmodule/scrobbler/scrobbler-defs.h59./src/cron-keyfile.c58./src/cron-config.h57./src/cron-log.c56./src/cron-event.c55./src/cron-loop.c54./src/Makefile.amo53./src/valgrind.sho"52./src/gmodule/stats/walrus-defs.h#51./src/gmodule/stats/walrus-utils.c 50./src/gmodule/stats/tokenizer.h5./src/cron-gmodule.c#49./src/gmodule/stats/stats-sqlite.h"48./src/gmodule/stats/walrus-main.c#47./src/gmodule/stats/stats-module.c(46./src/gmodule/stats/eugene-connection.c!45./src/gmodule/stats/stats-defs.h&44./src/gmodule/stats/eugene-listtags.c#43./src/gmodule/stats/eugene-rmtag.c"42./src/gmodule/stats/eugene-list.c$41./src/gmodule/stats/stats-command.c$40./src/gmodule/stats/eugene-addtag.c4./src/valgrind.suppressionso"39./src/gmodule/stats/Makefile.amo"38./src/gmodule/stats/eugene-defs.h"37./src/gmodule/stats/valgrind.sho#36./src/gmodule/stats/stats-server.c#35./src/gmodule/stats/stats-sqlite.c"34./src/gmodule/stats/eugene-main.c"33./src/gmodule/stats/eugene-love.c 32./src/gmodule/stats/tokenizer.c&31./src/gmodule/stats/eugene-listinfo.c$30./src/gmodule/stats/homescrape.ino3./src/cron-hooker.c"29./src/gmodule/stats/eugene-kill.c"28./src/gmodule/stats/eugene-rate.c#27./src/gmodule/stats/eugene-utils.c!26./src/gmodule/stats/stats-file.c#25./src/gmodule/stats/eugene-count.c24./src/gmodule/gmodule.h/23./src/gmodule/notification/notification-file.c022./src/gmodule/notification/notification-cover.c/21./src/gmodule/notification/notification-defs.h/20./src/gmodule/notification/notification-dhms.c2./src/cron-conf.c)19./src/gmodule/notification/Makefile.amo018./src/gmodule/notification/notification-spawn.c117./src/gmodule/notification/notification-module.c16./src/gmodule/Makefile.amo15./src/gmodule/utils.h)14./src/gmodule/scrobbler/scrobbler-file.c+13./src/gmodule/scrobbler/scrobbler-module.c)12./src/gmodule/scrobbler/scrobbler-curl.c,11./src/gmodule/scrobbler/scrobbler-journal.c&10./src/gmodule/scrobbler/Makefile.amo1./configure.aco./zsh-completion/_walrus74ompdcron-0.3+git20110303/GRTAGS000066400000000000000000002400001153374523700153270ustar00rootroot00000000000000b1 $XhXx@0Pp 4 |hX4@d$ Hh\ d  @D$4`T\4 P pXh,@  | $ h 4 (X8$ H(\dxXp t XL    Lp,h(44xl P D` @4LH  song_stopped17 @n 201 song_continued17 @n 207 song_playing17 @n 219 notify_send17 @n 94,80,64,26,18 event_mixer17 @n 292 cover_find17 @n 71 event_player17 @n 291 mpdcron_module17 @n 286 destroy17 @n 289 song_changed17 @n 102 file_load17 @n 122l event_options17 @n 293 kf_quark15 @n 61,37 MPDCRON_GUARD_UTILS_H15 @n 23 load_integer14 @n 119 scrobbler_config_free_callback14 @n 151stMPDCRON_MODULE14 @n 114-1,4-1 config14 @n 32,78 load_scrobbler14 @n 127,4 6scrobbler14 @n 39,5,3-1,4,6,4,4,4,5-3,3-2,3-1,6,2-5,8,19-1,3-1escscrobbler_config14 @n 34,5,5,49,15 load_string14 @n 114n song_started13 @n 142,85c MPDCRON_EVENT_UNLOAD13 @n 243 MPDCRON_EVENT_SUCCESS13 @n 209,37 file_cleanup13 @n 191 MPDCRON_INIT_SUCCESS13 @n 181 as_now_playing13 @n 81_UN MPDCRON_INIT_FAILURE13 @n 173,2ce as_init13 @n 176o song_paused13 @n 208s init13 @n 251 song_ended13 @n 141,81UNU http_client_finish13 @n 190t as_cleanup13 @n 189i_ song_repeated13 @n 139voi song_stopped13 @n 212 song_continued13 @n 216tt timer_save_journal13 @n 179n( song_playing13 @n 232 as_songchange13 @n 124ent event_player13 @n 253 mpdcron_module13 @n 249_a as_save_cache13 @n 188 http_client_init13 @n 174 destroy13 @n 252q song_changed13 @n 91l file_load13 @n 172voi played_long_enough13 @n 58,51 curl_source_dispatch12 @n 322http_multi_perform12 @n 308,151tt http_request_done12 @n 247n(s curl_source_prepare12 @n 320 http_client_update_fds12 @n 281,84MAX MAX_RESPONSE_BODY12 @n 392 http_request_writefunction12 @n 429 *http_multi_info_read12 @n 309,158 http_client_callback_t12 @n 36,364imphttp_request_abort12 @n 194,200p) http_client_fd_events12 @n 138,24 http_request12 @n 193,8,4,38,107,38,14rd) http_client_find_request12 @n 244 http_request_free_callback12 @n 360ou curl_source_check12 @n 321d @ http_client_abort_all_requests12 @n 268echttp_request_free12 @n 182,47,123,110 journal_commit_record11 @n 175,18/record11 @n 36,5-2,26,3,6,3,60,15,19-1,2,2,2,2,2,2,5c record_deinit11 @n 78 journal_write_record11 @n 60 record_clear11 @n 81,75d_ parse_timestamp11 @n 184* import_old_timestamp11 @n 129record9 @n 38,2,7-4,5-1,5-6UNrecord_deinit9 @n 56_ record_copy9 @n 41)as_save_cache8 @n 30k AS_CLIENT_VERSION7 @n 449,239# scrobbler_parse_handshake_response7 @n 308cac scrobbler_queue_remove_oldest7 @n 389record_is_defined7 @n 392,169,148 AS_SUBMIT_FAILED7 @n 236,167a scrobbler_handshake_callback7 @n 459  SCROBBLER_STATE_HANDSHAKE7 @n 296,143 MAX_SUBMIT_COUNT7 @n 5793scrobbler_submit7 @n 194,153,54,300s_4record7 @n 91,43,222,166,12,2-5,2,2,36,34,2,11,23-6,3-1,2 scrobbler_send_now_playing7 @n 562ter scrobbler_parse_submit_response7 @n 382record_deinit7 @n 146,248,130,21, record_free_callback7 @n 143 scrobbler_new7 @n 669 as_submitting7 @n 212 http_client_uri_escape7 @n 171bblscrobbler_schedule_handshake7 @n 192,111,9,27,70,274_ as_timestamp7 @n 441,215c scrobbler_push_callback7 @n 663cr scrobbler_save_callback7 @n 73451 scrobbler_submit_timer7 @n 713use scrobbler_free_callback7 @n 746g record_clear7 @n 124s scrobbler_new_callback7 @n 690Rconfig7 @n 78,37,94,41,4,4,3,3,3,34,17,4,4,46,10,60,3,5,60-2,88-1,2,65-1,2,3,5,41,3,3,2_hadd_var7 @n 447-5,50-6,68http_client_request7 @n 459,55,92 journal_write7 @n 723 AS_SUBMIT_OK7 @n 218,166 next_line7 @n 307,9,4,4@nSCROBBLER_STATE_READY7 @n 343,25,126,33,28,64,78_ record_copy7 @n 525id AS_CLIENT_ID7 @n 448,240ascrobbler_state7 @n 80st  as_md57 @n 442a) scrobbler7 @n 111,2,2-7,2,2-1,2,14-1,2,2-3,2-3,44-1,2,2-1,3,41,4,4,3,3,3,23,5-1,2,3-2,5,3-1,4,2,2,2,2,2,2-2,2-1,2-1,2-1,4-1,3,17,3-1,3-3,8,3,3-2,2,2-2,5,3-1,3-1,30,3,3,5,9,7,2,2,9-1,2-2,10-1,2,5,8-2,2,7,3-1,2-1,27-1,2,3-6,5,4,2,22-1,2,2-2,7,3,2-1,49,5,2,6-1,12,2,2,2,6-2,2-2,5,2,3-1,2,2,11,2se add_var_internal7 @n 179,5,5s record_free7 @n 135,222inSCROBBLER_STATE_NOTHING7 @n 116,182,110,60,11 scrobbler_handshake_timer7 @n 484scrobbler_schedule_submit7 @n 196,178,31,123,92ic journal_read7 @n 674b scrobbler_config7 @n 78,590at add_var_i7 @n 586-8ch scrobbler_submit_callback7 @n 514,93SCROBBLER_STATE_SUBMITTING7 @n 367,130,75 scrobbler_handshake7 @n 472 first_var7 @n 446( scrobbler_schedule_now_playing_callback7 @n 543 AS_SUBMIT_HANDSHAKE7 @n 222,185 @ record_dup7 @n 617t cscrobbler_increase_interval7 @n 302,9,27,35,31@n( scrobbler_free7 @n 741 file_cleanup6 @n 1661 record6 @n 91,20drecord_deinit6 @n 97r as_now_playing6 @n 149@n  http_client_uri_escape6 @n 135uct as_init6 @n 143 as_timestamp6 @n 160cMPDCRON_GUARD_SCROBBLER_DEFS_H6 @n 23 http_client_callback_t6 @n 139 record_clear6 @n 1067config6 @n 80 http_client_request6 @n 138_S journal_write6 @n 115 http_client_finish6 @n 129, as_cleanup6 @n 146SHA record_copy6 @n 87BBL timer_save_journal6 @n 169 as_songchange6 @n 152 record_free6 @n 1037 journal_read6 @n 118E as_save_cache6 @n 157 http_client_init6 @n 124@ file_load6 @n 163 record_dup6 @n 92 MPDCRON_EVENT_UNLOAD5 @n 150l MPDCRON_EVENT_SUCCESS5 @n 138!MPDCRON_EVENT_RECONNECT_NOW5 @n 145,51,20,20,21,20,20,20,20 v module_init_one5 @n 171*) MPDCRON_INIT_FAILURE5 @n 102,2 MPDCRON_EVENT_RECONNECT5 @n 140bl event_update5 @n 333,2LER init5 @n 101-1 module_destroy_one5 @n 155,22 event_database5 @n 192,2o module_path5 @n 78) 7module_data5 @n 75,2,43,2,65,4,16,4,16,4,17,4,16,4,16,4,16,4,16,4 DOT_MODULES5 @n 59s) event_mixer5 @n 273,2 event_output5 @n 293,2n * event_player5 @n 253,25 mpdcron_module5 @n 33module_process_ret5 @n 195,20,20,21,20,20,20,20od destroy5 @n 126-1 event_queue5 @n 232,2 event_stored_playlist5 @n 212,2od event_options5 @n 313,2ru DOT_HOOKS3 @n 69hooker_increment3 @n 66nmpdcron_config2 @n 28,18,39n DOT_MPDCRON2 @n 52aseENV_MPD_PASSWORD2 @n 71co ENV_HOME_DIR2 @n 49-1,4ta DOT_MODULES2 @n 63los ENV_MPD_HOST2 @n 67  db_co2 MAX_RESPONSE_BODYNmpdcron_connection_newdb_stNApas_now_playinghttp_request_free_slip\,d,xL$X0P0\8X`@ `D$0pH X0h@xP(dxDT  HANDLE_SIGNAL61 @n 176-3DEFAULT_DATE_FORMAT60 @n 152,154,46DEFAULT_DATE_FORMAT_SIZE60 @n 144,8,133,21,37,9DEFAULT_MPD_RECONNECT59 @n 44,65,8-1 DEFAULT_LOG_LEVEL59 @n 43,50DEFAULT_PID_KILL_WAIT59 @n 42,29,7-1DEFAULT_MPD_TIMEOUT59 @n 45,91,7-1 DOT_MPDCRON51 @n 39 ENV_HOME_DIR51 @n 36-1,4 ENV_MPD_PASSWORD48 @n 54 ENV_MPD_HOST48 @n 50 ENV_MPD_PORT48 @n 52 ENV_MPD_PORT2 @n 69on ENV_MPD_PORT27 @n 61-1ENV_MPD_PASSWORD2 @n 71co ENV_MPD_PASSWORD27 @n 64 ENV_MPD_HOST2 @n 67 ENV_MPD_HOST27 @n 58-1 ENV_MPDCRON_PORT25 @n 205-1 ENV_MPDCRON_PORT33 @n 209-1 ENV_MPDCRON_PORT31 @n 246-1 ENV_MPDCRON_PORT42 @n 143-1UN ENV_MPDCRON_PORT44 @n 279-1 ENV_MPDCRON_PORT40 @n 212-1 ENV_MPDCRON_PORT28 @n 205-1 ENV_MPDCRON_PORT43 @n 212-1me ENV_MPDCRON_PORT29 @n 209-1 ENV_MPDCRON_PASSWORD25 @n 208 ENV_MPDCRON_PASSWORD40 @n 215 ENV_MPDCRON_PASSWORD33 @n 212 ENV_MPDCRON_PASSWORD28 @n 208 ENV_MPDCRON_PASSWORD44 @n 282 ENV_MPDCRON_PASSWORD42 @n 146 ENV_MPDCRON_PASSWORD31 @n 249 ENV_MPDCRON_PASSWORD43 @n 215 ENV_MPDCRON_PASSWORD29 @n 212 ENV_MPDCRON_HOST25 @n 202-1 ENV_MPDCRON_HOST44 @n 276-1st ENV_MPDCRON_HOST31 @n 243-1 ENV_MPDCRON_HOST28 @n 202-1 ENV_MPDCRON_HOST42 @n 140-1cr ENV_MPDCRON_HOST33 @n 206-1 ENV_MPDCRON_HOST43 @n 209-1_r ENV_MPDCRON_HOST40 @n 209-1 ENV_MPDCRON_HOST29 @n 206-1 ENV_HOME_DIR2 @n 49-1,4ta DOT_MPDCRON2 @n 52ase DOT_MODULES2 @n 63los DOT_MODULES5 @n 59s) DOT_HOOKS3 @n 69 DEFAULT_PORT25 @n 207 DEFAULT_PORT28 @n 207 DEFAULT_PORT29 @n 211 DEFAULT_PORT40 @n 214 DEFAULT_PORT31 @n 248 DEFAULT_PORT33 @n 211 DEFAULT_PORT26 @n 58 DEFAULT_PORT43 @n 214 DEFAULT_PORT44 @n 281 DEFAULT_PORT42 @n 145 DEFAULT_MAX_CONNECTIONS26 @n 69 DEFAULT_HOSTNAME25 @n 204 DEFAULT_HOSTNAME28 @n 204 DEFAULT_HOSTNAME42 @n 142 DEFAULT_HOSTNAME29 @n 208 DEFAULT_HOSTNAME40 @n 211 DEFAULT_HOSTNAME33 @n 208 DEFAULT_HOSTNAME31 @n 245 DEFAULT_HOSTNAME44 @n 278 DEFAULT_HOSTNAME43 @n 211 DEFAULT_HOST26 @n 189 DB_VERSION35 @n 965,3vCOMMAND_RETURN_OK41 @n 183,22,22,22,28,28,29,28,32,32,33,32,22,22,22,22,42,42,42,42,19,19,19,19,19,19,19,19,32,33,32,32,42,42,42,42,10COMMAND_RETURN_ERROR41 @n 179,22,22,22,20,28,28,29,28,32,32,33,34,22,22,22,25,4,6,7,25,4,6,7,25,4,6,7,25,4,6,7,19,19,19,19,19,19,19,19,20,32,33,32,37,4,6,7,25,4,6,7,25,4,6,7,25,4,6,7,17,162,17,18,7,9 COMMAND_ARGV_MAX41 @n 1370 @n AS_SUBMIT_OK7 @n 218,166 AS_SUBMIT_HANDSHAKE7 @n 222,185 @ AS_SUBMIT_FAILED7 @n 236,167a AS_CLIENT_VERSION7 @n 449,239 AS_CLIENT_ID7 @n 448,240aACK_ERROR_UNKNOWN41 @n 1353,26,3 ACK_ERROR_PERMISSION41 @n 130564  ACK_ERROR_PASSWORD41 @n 1208 ACK_ERROR_NO_TAGS35 @n 1189 ACK_ERROR_INVALID_TAG35 @n 247 ACK_ERROR_DATABASE_VERSION35 @n 966aACK_ERROR_DATABASE_STEP35 @n 494,36,33,74,35,38,35,81,32,248,20,24,20,122,49,48,48,54,55,54,54,501,82,82,82,76,49,48,48ACK_ERROR_DATABASE_SELECT35 @n 341,39,39,398ACK_ERROR_DATABASE_RESET35 @n 319,39,39,39,42,33,36,32,75,35,38,36,337,20,24,20FACK_ERROR_DATABASE_PREPARE35 @n 850,162,16,18,224,48,49,48,50,54,55,54,504,82,82,82,76,48,49,48 ACK_ERROR_DATABASE_OPEN35 @n 993,9ACK_ERROR_DATABASE_CREATE35 @n 916,9,9,26+ACK_ERROR_DATABASE_BIND35 @n 327,39,39,39,44,36,33,74,35,38,35,77 ACK_ERROR_DATABASE_AUTH35 @n 1083QACK_ERROR_ARG41 @n 155,442,6,4,32,6,4,32,6,4,32,6,4,313,6,4,32,6,4,32,6,4,32,6,4,134,7,6,75,6  __.VERSION __.VERSION 5f __.COMPNAME __.COMPNAMEn __.COMPLINE __.COMPLINE __.COMPACT __.COMPACTsli" pH0 xX8@ x pP0`<p@X(xX4 ` X4  @`8t (T  8  x   < `| P l< T pH X0p@T(xL 35 @n 35 @n, SQL_PRAGMA_SYNC_ON35 @ SQL_PRAGMA_SYNC_OFF35 @n 48 @n, SQL_INSERT_SONG35 @n 57 @n, SQL_INSERT_GENRE35 @n 60 @n, SQL_INSERT_ARTIST35 @n 58 @n, SQL_INSERT_ALBUM35 @n 59 @n, SQL_HAS_SONG35 @n 52 @n, SQL_HAS_GENRE35 @n 55 @n, SQL_HAS_ARTIST35 @n 53 @n, SQL_HAS_ALBUM35 @n 54 @n, SQL_GET_VERSION35 @n 34 @n, C  35 @n 45 @n, SQL_DB_CREATE_SONG35 @n 37 @n, SQL_DB_CREATE_GENRE35 @n 40 @n, SQL_DB_CREATE_ARTIST35 @n 38 @n, SQL_DB_CREATE_ALBUM35 @n 39 @n, SQL_BEGIN_TRANSACTION35 @n 44 @n, SECSPERMIN20 @n 29 #@d @n 60 SECSPERMIN20 @n 41 #undef @n SECSPERHOUR20 @n 28 #@d @n 3600 SECSPERHOUR20  @n SECSPERDAY20 @n 27 #@d @n 86400 SECSPERDAY20 @n 39 #undef @n SCROBBLER_STATE_SUBMITTING7 @n 68 @n, SCROBBLER_STATE_READY7 @n 63 @n, SCROBBLER_STATE_NOTHING7 @n 52 @n, SCROBBLER_STATE_HANDSHAKE7 @n 58 @n,PROTOCOL_VERSION45 @n 41 #@d @n "0.1" PROTOCOL_OK41 @n 33 #@d @n "OK" PROCOTOL_ACK41 @n 34 # 8Z PERMISSION_UPDATE45 @n 48 #@d @n 2PERMISSION_SELECT45 @n 47 #@d @n 1PERMISSION_NONE45 @n 46 #@d @n@40 \(Px@tDd@T,h<T(d< L@Ll4L` 18   &  init106 @n 48,9 loop_disconnect62 @n 83 log_handler62 @n 76 mpdcron_config62 @n 28 keyfile_load_modules62 @n 72 module_options_run62 @n 112 module_output_run62 @n 109 module_load62 @n 87 module_mixer_run62 @n 106 loop_connect62 @n 80 module_database_run62 @n 93 module_update_run62 @n 115 module_queue_run62 @n 99 module_close62 @n 90 module_stored_playlist_run62 @n 96 module_player_run62 @n 102 keyfile_load62 @n 68 log_handler61 @n 162 keyfile_load_modules61 @n 189,65 loop_connect61 @n 195,65 module_close61 @n 61 keyfile_load61 @n 145 internal_cleanup61 @n 78,8 module_load59 @n 177 log_output57 @n 68 module_options_run56 @n 188 module_output_run56 @n 165 module_mixer_run56 @n 141 module_database_run56 @n 42 module_update_run56 @n 212 module_queue_run56 @n 90 module_stored_playlist_run56 @n 66 module_player_run56 @n 115loop_schedule_reconnect55 @n 35,80,17,31,14 loop_reconnect55 @n 147,29 loop_failure55 @n 61,53,17,31 loop_idle55 @n 169loop_schedule_idle55 @n 36,49,33,20 load_string51 @n 69 init47 @n 229mpdcron_connection46 @n 682,5,2 mpdcron_connection_new38 @n 126 mpdcron_connection_new43 @n 217ns mpdcron_connection_new44 @n 284n, mpdcron_connection_new40 @n 217 mpdcron_connection_new42 @n 148 mpdcron_connection_new28 @n 210sc mpdcron_connection_new29 @n 214 mpdcron_connection_new33 @n 214mpdcron_connection_free25 @n 213,7,13mpdcron_connection_free28 @n 213,7,13 mpdcron_connection_free38 @n 129mpdcron_connection_free40 @n 220,7,13mpdcron_connection_free31 @n 254,7,13mpdcron_connection_free44 @n 287,7,13mpdcron_connection_free33 @n 217,7,13mpdcron_connection_free43 @n 220,7,13mpdcron_connection_free42 @n 151,7,13mpdcron_connection_free29 @n 217,7,13 mpdcron_connection25 @n 192 @ mpdcron_connection31 @n 233 mpdcron_connection28 @n 192n( mpdcron_connection42 @n 130 mpdcron_connection38 @n 125 mpdcron_connection29 @n 196 mpdcron_connection40 @n 199 mpdcron_connection44 @n 266st mpdcron_connection33 @n 196 mpdcron_connection43 @n 199mpdcron_config2 @n 28,18,39n  mpdcron_config24 @n 6742  mpdcron_addtag_genre_expr38 @n 231 mpdcron_addtag_genre_expr40 @n 122,23 mpdcron_addtag_expr38 @n 219 mpdcron_addtag_expr40 @n 163,18 mpdcron_addtag_artist_expr38 @n 223 mpdcron_addtag_artist_expr40 @n 33,23 mpdcron_addtag_album_expr38 @n 227 mpdcron_addtag_album_expr40 @n 74,30module_process_ret5 @n 195,20,20,21,20,20,20,20od module_path5 @n 78) module_init_one5 @n 171*) module_destroy_one5 @n 155,22 7module_data5 @n 75,2,43,2,65,4,16,4,16,4,17,4,16,4,16,4,16,4,16,4Tmap35 @n 2122,22-3,24,2,4,7-1,19,22-3,24,2,4,7-1,19,22-3,24,2,4,7-1,19,22-3,24,2,4,7-1 love_song33 @n 236 love_genre33 @n 234 love_artist33 @n 230 love_album33 @n 232 load_string14 @n 114n load_string26 @n 41 * load_string23 @n 44,5,5,5,5 { load_scrobbler14 @n 127,4 load_integer14 @n 119 load_integer26 @n 52,1115load_current_song25 @n 43,41,41,41 *cload_current_song31 @n 44,51,52,51 load_current_song38 @n 286load_current_song43 @n 43,41,48,41erload_current_song40 @n 43,41,48,41load_current_song44 @n 44,57,65,57r,load_current_song33 @n 43,42,42,42load_current_song29 @n 43,42,42,42yFiload_current_song28 @n 43,41,41,41 listtags_song44 @n 306@n  listtags_genre44 @n 304ro listtags_artist44 @n 3009 listtags_album44 @n 302co listinfo_song31 @n 273 listinfo_genre31 @n 271 listinfo_artist31 @n 267 listinfo_album31 @n 269 list_song42 @n 170alu list_genre42 @n 168 list_artist42 @n 164 list_album42 @n 166 kill_song29 @n 236loo kill_genre29 @n 234nu kill_artist29 @n 230f kill_album29 @n 232gs kf_quark15 @n 61,37 journal_write_record11 @n 60  journal_write6 @n 115 journal_write7 @n 723 journal_read6 @n 118E journal_read7 @n 674b journal_commit_record11 @n 175,18 init5 @n 101-1 init17 @n 288 init24 @n 67_ init13 @n 251 import_old_timestamp11 @n 129 http_request_writefunction12 @n 429 * http_request_free_callback12 @n 360ouhttp_request_free12 @n 182,47,123,110 tT00(x\4|`8`D(|`8\0 \88\xdH0tT4pDpL, conf_init62 @n 35 conf_free62 @n 38 conf_pid_file_proc62 @n 32 conf_init61 @n 104cleanup61 @n 121,25,7,4,42,18,47 conf_free61 @n 63 conf_pid_file_proc61 @n 103 db_add_genre_tag_expr49 @n 203POR db_close35 @n 1014,6,10,4,14 db_close47 @n 171 db_close49 @n 103 db_close48 @n 154,8,7 db_check_ver35 @n 1033 db_add_song_tag_expr41 @n 759 db_add_song_tag_expr49 @n 206 db_add_genre_tag_expr41 @n 816 db_add_artist_tag_expr41 @n 797 db_add_artist_tag_expr49 @n 197 db_add_album_tag_expr41 @n 778db_ db_add_album_tag_expr49 @n 200 curl_source_prepare12 @n 320 curl_source_dispatch12 @n 322 curl_source_check12 @n 321d @ cover_find17 @n 71 cover_find21 @n 45 count_song25 @n 232 count_genre25 @n 230 count_artist25 @n 226 count_album25 @n 228Aconnection_quark46 @n 70,93,6,6,30,6,11,34,7,43,37,7,39,37,7,40,37,7,39,21,9,117config6 @n 80 config14 @n 32,78 config26 @n 29,8 config45 @n 90 config23 @n 32,9 config21 @n 42Rconfig7 @n 78,37,94,41,4,4,3,3,3,34,17,4,4,46,10,60,3,5,60-2,88-1,2,65-1,2,3,5,41,3,3,2_hcommand_return41 @n 43,121,22,22,22,22,28,28,29,28,32,32,33,32,22,22,22,22,42,42,42,42,19,19,19,19,19,19,19,19,32,33,32,32,42,42,42,42,167,5 command_return45 @n 108 command_putv41 @n 107 command_process36 @n 116 command_process45 @n 108 vcommand_ok41 @n 182,22,22,22,28,28,29,28,32,32,33,32,22,22,22,22,42,42,42,42,19,19,19,19,19,19,19,19,32,33,32,32,42,42,42,42,10co command_lookup41 @n 1350 command_error_v41 @n 142 command_checked_lookup41 @n 1426 command_check_request41 @n 1360l, command_authorizer41 @n 1418command41 @n 1213,61,64,5,29command46 @n 638,35-1 cmd_unkill34 @n 84 cmd_unkill38 @n 298 cmd_rmtag_internal43 @n 281,2 cmd_rmtag34 @n 94 cmd_rmtag38 @n 313 cmd_rate_internal28 @n 276,2 cmd_rate34 @n 86 cmd_rate38 @n 301cmd_love_internal33 @n 273,2,36,2 cmd_love34 @n 80 cmd_love38 @n 292 cmd_listtags_internal44 @n 344,2 cmd_listtags34 @n 96 cmd_listtags38 @n 316 cmd_listinfo_internal31 @n 310,2 cmd_listinfo34 @n 90 cmd_listinfo38 @n 307 cmd_list_internal42 @n 206 cmd_list34 @n 88 cmd_list38 @n 304cmd_kill_internal29 @n 273,2,36,2 cmd_kill34 @n 82 cmd_kill38 @n 295 cmd_hate34 @n 78 cmd_hate38 @n 289 cmd_count_internal25 @n 277,2 cmd_count34 @n 98 cmd_count38 @n 319 cmd_addtag_internal40 @n 281,2 cmd_addtag34 @n 92 cmd_addtag38 @n 310 client_destroy36 @n 298Fclient36 @n 50-4,8,2-1,8,15,2-1,8,17,4-1,8,10-3,2-1,2-1,2,5,3-1,3-1,152,6-1 client38 @n 107 client46 @n 690,8,7-1,8,6-1,14,4,17-1client41 @n 43,12,12,11-2,8,5,3,11,10,8,3,4,10,13,22,4-1,17,4-1,17,4-1,17,4-1,15,7-1,5,15,7-1,5,15,7-2,5,15,7-1,5,15,7-5,5,15,7-5,5,15,7-6,5,15,7-5,5,17,4-1,17,4-1,17,4-1,17,4-1,19,6,4,8,4-1,19,6,4,8,4-1,19,6,4,8,4-1,19,6,4,8,4-1,14,4-1,14,4-1,14,4-1,14,4-1,14,4-1,14,4-1,14,4-1,14,4-1,15,7-1,3,6,15,7-2,3,6,15,7-1,3,6,15,7-1,3,6,19,6,4,8,4-1,19,6,4,8,4-1,19,6,4,8,4-1,19,6,4,8,4-1,9-1,3,96-1,10-1,6-1,5-1,23-1,7,19,3,22,6,8-1,7,2 bind_callback36 @n 225 as_timestamp6 @n 160c as_timestamp7 @n 441,215c as_submitting7 @n 212 as_songchange6 @n 152 as_songchange13 @n 124ent as_save_cache6 @n 157as_save_cache8 @n 30k as_save_cache13 @n 188 as_now_playing6 @n 149@n  as_now_playing13 @n 81_UN  lH@0d$<tT4hH,lTt P@ |H$t8T$|@`  8 d< @ L   tp T4` , PX0   | X4 \ scrobbler_state7 @n 47 enum @n {Iscrobbler_send_now_playing7 @n 487 static void @n(struct scrobbler *scrobbler, const char *artist,5scrobbler_schedule_submit7 @n 705 static void @n(struct scrobbler *scrobbler)(;scrobbler_schedule_now_playing_callback7 @n 519 static void @n(gpointer data, gpointer user_data))scrobbler_schedule_handshake7 @n 477 @n(struct scrobbler *scrobbler)Iscrobbler_save_callback7 @n 716 static void @n(gpointer data, G_GNUC_UNUSED gpointer user_data)+scrobbler_queue_remove_oldest7 @n 351 @n(GQueue *queue, unsigned count);scrobbler_push_callback7 @n 612 static void @n(gpointer data, gpointer user_data) =scrobbler_parse_submit_response7 @n 212 static as_submitting @n(const char *scrobbler_name,#;scrobbler_parse_handshake_response7 @n 240 @n(struct scrobbler *scrobbler, const char *line)Iscrobbler_new_callback7 @n 666 static void @n(gpointer data, G_GNUC_UNUSED gpointer user_data)Lscrobbler_new7 @n 111 static struct scrobbler *@n(const struct scrobbler_config *config)5scrobbler_increase_interval7 @n 198 static void @n(struct scrobbler *scrobbler)+scrobbler_handshake_timer7 @n 464 static gboolean @n(gpointer data)Iscrobbler_handshake_callback7 @n 288 static void @n(size_t length, const char *response, void *dat@0    x   |xTd|X4p @d,(h|X<T@px<|,l run106 @n 50,9 sig_cleanup61 @n 165 xload_dbpath52 @n 27 tokenizer_next_word50 @n 37 tokenizer_next_param50 @n 81 tokenizer_next_string50 @n 66 tokenizer_next_unquoted50 @n 51 xload_dbpath48 @n 136 run_update48 @n 153,8 song_started47 @n 101,104 server_bind47 @n 151,2,2 server_init47 @n 148 song_paused47 @n 186 song_ended47 @n 100,100 server_start47 @n 157 song_stopped47 @n 190 song_continued47 @n 194 song_playing47 @n 210 server_close47 @n 170 song_changed47 @n 57 played_long_enough47 @n 70 server_bind45 @n 99 server_init45 @n 98 server_schedule_write45 @n 102 server_start45 @n 100 server_close45 @n 101 server_flush_write45 @n 103quote44 @n 52,62-1,59,52 rmtag_genre43 @n 237 rmtag_artist43 @n 233quote43 @n 51,46-1,42,36 rmtag_album43 @n 235 rmtag_song43 @n 239 tokenizer_next_word41 @n 1375server_schedule_write41 @n 79,17,32 tokenizer_next_param41 @n 1395 server_flush_write41 @n 80,52quote40 @n 51,46-1,42,36 quote38 @n 283 server_schedule_write36 @n 158 server_flush_write36 @n 159 validate_tag35 @n 2024,24,24,24,31,82,82,82 valid_word_first_char32 @n 61 valid_word_char32 @n 79 valid_unquoted_char32 @n 115,18usage34 @n 72,28,24tokenizer_quark32 @n 62,19,35,19,30,20,15 tokenizer_next_unquoted32 @n 221 tokenizer_next_string32 @n 219 timer_save_journal6 @n 169 timer_save_journal13 @n 179n(sql_update_song35 @n 1740,87,87,87,99,324sql_update_genre35 @n 1719,87,87,87,96,266sql_update_entry35 @n 871,7,7,7sql_update_artist35 @n 1677,87,87,87,90,150sql_update_album35 @n 1698,87,87,87,93,208 song_stopped13 @n 212 song_stopped17 @n 201 song_started13 @n 142,85c song_started17 @n 111,103 song_repeated13 @n 139voi song_playing13 @n 232 song_playing17 @n 219 song_paused13 @n 208s song_paused17 @n 197p song_ended13 @n 141,81UNU song_continued13 @n 216tt song_continued17 @n 207 song_changed13 @n 91l song_changed17 @n 102 scrobbler_submit_timer7 @n 713use scrobbler_submit_callback7 @n 514,93scrobbler_submit7 @n 194,153,54,300s_scrobbler_state7 @n 80st  scrobbler_send_now_playing7 @n 562terscrobbler_schedule_submit7 @n 196,178,31,123,92ic( scrobbler_schedule_now_playing_callback7 @n 543scrobbler_schedule_handshake7 @n 192,111,9,27,70,274_ scrobbler_save_callback7 @n 73451 scrobbler_queue_remove_oldest7 @n 389 scrobbler_push_callback7 @n 663cr scrobbler_parse_submit_response7 @n 382# scrobbler_parse_handshake_response7 @n 308cac scrobbler_new_callback7 @n 690 scrobbler_new7 @n 669scrobbler_increase_interval7 @n 302,9,27,35,31@n( scrobbler_handshake_timer7 @n 484 scrobbler_handshake_callback7 @n 459  scrobbler_handshake7 @n 472 scrobbler_free_callback7 @n 746g scrobbler_free7 @n 741 scrobbler_config_free_callback14 @n 151st scrobbler_config7 @n 78,590atscrobbler_config14 @n 34,5,5,49,15 scrobbler7 @n 111,2,2-7,2,2-1,2,14-1,2,2-3,2-3,44-1,2,2-1,3,41,4,4,3,3,3,23,5-1,2,3-2,5,3-1,4,2,2,2,2,2,2-2,2-1,2-1,2-1,4-1,3,17,3-1,3-3,8,3,3-2,2,2-2,5,3-1,3-1,30,3,3,5,9,7,2,2,9-1,2-2,10-1,2,5,8-2,2,7,3-1,2-1,27-1,2,3-6,5,4,2,22-1,2,2-2,7,3,2-1,49,5,2,6-1,12,2,2,2,6-2,2-2,5,2,3-1,2,2,11,2se 6scrobbler14 @n 39,5,3-1,4,6,4,4,4,5-3,3-2,3-1,6,2-5,8,19-1,3-1esc run_cmd34 @n 125 remove_tag35 @n 2146,82,82,82record_is_defined7 @n 392,169,148 record_free_callback7 @n 143 record_free6 @n 1037 record_free7 @n 135,222in record_dup6 @n 92 record_dup7 @n 617t crecord_deinit6 @n 97rrecord_deinit7 @n 146,248,130,21,record_deinit9 @n 56_ record_deinit11 @n 78 record_copy6 @n 87BBL record_copy9 @n 41) record_copy7 @n 525id record_clear6 @n 1067 record_clear11 @n 81,75d_ record_clear7 @n 124s record6 @n 91,20d/record11 @n 36,5-2,26,3,6,3,60,15,19-1,2,2,2,2,2,2,5crecord9 @n 38,2,7-4,5-1,5-6UN4record7 @n 91,43,222,166,12,2-5,2,2,36,34,2,11,23-6,3-1,2 rate_song28 @n 232) rate_genre28 @n 230 rate_artist28 @n 226 rate_album28 @n 228 *quote25 @n 51,41,41,36tagquote33 @n 51,42,42,37quote31 @n 52,51,52,46quote28 @n 51,41,41,3655 quote29 @n 51,42,42,37 played_long_enough13 @n 58,51 parse_timestamp11 @n 184* R( p `8 D HhLh0 h ( |  P   XH \ 8(@ pP, ` d$ @  P `@   |` @ ` pL$tP\8 Hl@, x`0 $h<xP p< @n VERSION AS_CLIENT_ID7 7 #@d @n "mcn"ACK_ERROR_UNKNOWN45 @n 65 @n = 4,ACK_ERROR_PERMISSION45 @n 64 @n = 3,ACK_ERROR_PASSWORD45 @n 63 @n = 2,ACK_ERROR_NO_TAGS49 @n 84 @n = 102,ACK_ERROR_INVALID_TAG49 @n 83 @n = 101,ACK_ERROR_DATABASE_VERSION49 @n 73 @n = 52,ACK_ERROR_DATABASE_UPDATE49 @n 77 @n = 56,ACK_ERROR_DATABASE_STEP49 @n 80 @n = 59,ACK_ERROR_DATABASE_SELECT49 @n 76 @n = 55,ACK_ERROR_DATABASE_RESET49 @n 81 @n = 60,ACK_ERROR_DATABASE_PREPARE49 @n 78 @n = 57,ACK_ERROR_DATABASE_OPEN49 @n 71 @n = 50,ACK_ERROR_DATABASE_INSERT49 @n 75 @n = 54,ACK_ERROR_DATABASE_CREATE49 @n 72 @n = 51,ACK_ERROR_DATABASE_BIND49 @n 79 @n = 58,ACK_ERROR_DATABASE_AUTH49 @n 74 @n = 53,ACK_ERROR_ARG45 @n 62 @n = 1,  __.VERSION __.VERSION 5  __.COMPRESS __.COMPRESS ddefine ttypedef __.COMPNAME __.COMPNAME event_mixer106 @n 59 destroy106 @n 49,9 env_clearenv62 @n 41 env_status62 @n 53 hooker_run_hook62 @n 65 env_list_all_meta62 @n 44 event_run62 @n 62 env_stats62 @n 50 env_list_queue_meta62 @n 47 env_status_currentsong62 @n 56 env_outputs62 @n 59env_export_song60 @n 331,127 env_export_status60 @n 392,64 env_strstate60 @n 117 env_status56 @n 136,47,24hooker_run_hook56 @n 45,23,24,28,24,23,24,24 env_list_all_meta56 @n 60 event_update56 @n 238 env_stats56 @n 37 env_list_queue_meta56 @n 84 event_database56 @n 224 env_status_currentsong56 @n 110 event_mixer56 @n 232 event_output56 @n 234 event_player56 @n 228 event_queue56 @n 230 env_outputs56 @n 159 event_stored_playlist56 @n 226 event_options56 @n 236 env_clearenv55 @n 128 event_run55 @n 130 db_start_transaction49 @n 110 db_vacuum49 @n 119 db_start_transaction48 @n 148 db_vacuum48 @n 168 file_cleanup47 @n 143,29 event_player47 @n 231 destroy47 @n 230 file_load47 @n 134 escape46 @n 647 file_cleanup45 @n 93 file_load45 @n 92.eulog44 @n 35,12,11,34,12,5,13,35,12,11,34,18,54,7.eulog43 @n 34,12,11,18,12,5,13,18,12,11,18,18,37,7eulog42 @n 36,24,25,24,41,7handle_love_genre41 @n 1227,25 handle_count_artist41 @n 1221handle_love_artist41 @n 1226,25 handle_listtags_genre41 @n 1247 handle_password41 @n 1254 handle_list_album41 @n 1235 handle_rate_album41 @n 1257 handle_rmtag_artist41 @n 1263 handle_listtags_artist41 @n 1246 handle_addtag_genre41 @n 1217handle_kill_album41 @n 1230,37 handle_listinfo_genre41 @n 1242 handle_count_genre41 @n 1222 handle_rmtag_genre41 @n 1264 handle_kill41 @n 1229,37 http_request_done12 @n 247n(shttp_request_abort12 @n 194,200p) http_request12 @n 193,8,4,38,107,38,14rd)http_multi_perform12 @n 308,151tthttp_multi_info_read12 @n 309,158 http_client_uri_escape6 @n 135uct http_client_uri_escape7 @n 171bbl http_client_update_fds12 @n 281,84MAX http_client_request6 @n 138_Shttp_client_request7 @n 459,55,92 http_client_init6 @n 124@ http_client_init13 @n 174 http_client_finish6 @n 129, http_client_finish13 @n 190t  http_client_find_request12 @n 244 http_client_fd_events12 @n 138,24 http_client_callback_t6 @n 139 http_client_callback_t12 @n 36,364imp http_client_abort_all_requests12 @n 268ec,host36 @n 175,3,3,2,6,7,11,3,8,2-1,4-2,54,2-2,4hooker_increment3 @n 66n handle_rmtag_album41 @n 1262 handle_rmtag41 @n 1261d_s handle_rate_genre41 @n 1259 @ handle_rate_artist41 @n 1258 handle_rate41 @n 1256handle_love_album41 @n 1225,25art handle_love41 @n 1224,25E handle_listtags_album41 @n 1245_e handle_listtags41 @n 1244 handle_listinfo_artist41 @n 1241 handle_listinfo_album41 @n 1240 handle_listinfo41 @n 1239 handle_list_genre41 @n 1237or handle_list_artist41 @n 1236g handle_list41 @n 1234handle_kill_genre41 @n 1232,37 handle_kill_artist41 @n 1231,37 * handle_count_album41 @n 1220 handle_count41 @n 1219ong handle_addtag_artist41 @n 1216xpr handle_addtag_album41 @n 1215 handle_addtag41 @n 1214 first_var7 @n 446 file_load6 @n 163 file_load13 @n 172voi file_load17 @n 122l file_load21 @n 51 file_cleanup6 @n 1661 file_cleanup13 @n 191 file_cleanup17 @n 134 file_cleanup21 @n 54 event_update5 @n 333,2LER event_update24 @n 95 event_update17 @n 294 event_stored_playlist5 @n 212,2od event_stored_playlist24 @n 76 event_resolve36 @n 289 event_read_line36 @n 121,42 event_queue5 @n 232,2 event_queue24 @n 79 event_player5 @n 253,25 event_player17 @n 291 event_player13 @n 253 event_player24 @n 82 event_output5 @n 293,2n * event_output24 @n 89 event_options5 @n 313,2ru event_options17 @n 293 event_options24 @n 92 event_mixer5 @n 273,2 event_mixer24 @n 86 event_mixer17 @n 292 event_incoming36 @n 296 event_flush36 @n 322 event_database5 @n 192,2o event_database24 @n 73 event_database17 @n 290b_)eulog25 @n 37,9,11,21,9,11,21,9,11,21,15,37,7)eulog28 @n 37,9,11,21,9,11,21,9,11,21,15,37,7 eulog38 @n 322,eulog31 @n 35,12,11,28,12,11,29,12,11,28,18,46,7.eulog40 @n 34,12,11,18,12,5,13,18,12,11,18,18,37,7,eulog29 @n 36,10,11,21,10,11,21,10,11,21,16,38,7,eulog33 @n 36,10,11,21,10,11,21,10,11,21,16,38,7eulog27 @n 67,5,8,9,5escape_string35 @n 2173,82,82,82 dhms17 @n 152-2 dhms21 @n 48 destroy5 @n 126-1 destroy17 @n 289  destroy24 @n 70 destroy13 @n 252q db_update_song35 @n 1202 db_update_genre35 @n 1240 db_update_artist35 @n 1213 db_update_album35 @n 12266db_step35 @n 493,36,33,74,35,38,35,113,55-3,9,9,172,20,24,20db_start_transaction35 @n 2168,82,82,82D |Ll<$h< `4`4t@P d0xH xTh D 0  XxT0 |X4|T,(tDd`DDp$ion *conn, const char *expr,Ampdcron_rmtag_expr46 @n 1069 @n(struct mpdcron_connection *conn, const char *expr,Ampdt_expr46 @n 1083 @n(struct mpdcron_connection *conn, const char *expr,Ampdcron_rmtag_album_expr46 @n 1097 @n(struct mpdcron_connection *conn, const char *expr,>mpdcron_recv_line46 @n 64 @n(struct mpdcron_connection *conn, gsize *length_r)@mpdcron_rate_genre_expr46 @n 989 @n(struct mpdcron_connection *conn, const char *expr,Ampdcron_rate_expr40uct mpdcron_connection *conn, const char *expr,@mpdcron_rate_artist_expr46 @n 977 @n(struct mpdcron_connection *conn, const char *expr,@mpdcron_rate_album_expr46 @n 965 @n(struct mpdcron_connection *conn, const char *expr,Dmpdcron_password46 @n 763 @n(struct mpdcron_connection *conn, const char *password)mpdcron_parser_result38 @n 46 enum @n {<D 0( ser_feed46 @n 81 @n(struct mpdcron_parser *parser, char *line)mpdcron_parser46 @n 40 struct @n {Bmpdcron_parse_welcome46 @n 571 @n(struct mpdcron_connection *conn, const char *output)?mpdcron_parse_songs46 @n 488 @n(struct mpdcron_connection *conn, GSList **values).mpdcron_parse_single46 @n 145 @n(struct mpdcron_connection *conn)se_genres46 @n 404 @n(struct mpdcron_connection *conn, GSList **values)1    x   XtH`0`8 \0tL$|T,h|D$`4d8P l$X|H8h0aK g   0  (/  mpdcron_module106 @n 55 mpdcron_module47 @n 227 mpdcron_parse_welcome46 @n 742Ompdcron_parse_changes46 @n 878,12,12,12,12,12,11,12,12,12,12,12,14,14,14,14,14,14,14,14,65,13,13,13mpdcron_recv_line46 @n 153,42,47,87,83,84,235vmpdcron_send_command46 @n 768,12,12,12,12,12,12,12,12,12,12,12,12,12,12,11,12,12,12,12,12,14,14,14,14,14,14,14,14,13,13,13,13,13,13,13,13mpdcron_parse_songs46 @n 818,48,268mpdcron_entity46 @n 239,35,52,35,48,35mpdcron_parser_feed46 @n 157,42,49,87,83,84mpdcron_parse_genres46 @n 806,48,319mpdcron_parse_albums46 @n 782,48,317 mpdcron_parser46 @n 728mpdcron_parse_artists46 @n 794,48,318 mpdcron_send_commandv46 @n 674 mpdcron_song46 @n 493,35 notify_send17 @n 94,80,64,26,18 notify_send21 @n 57 1 next_line7 @n 307,9,4,4@n mpdcron_song31 @n 216 mpdcron_song42 @n 115 mpdcron_song44 @n 241 mpdcron_rmtag_genre_expr38 @n 247 mpdcron_rmtag_genre_expr43 @n 122,23o mpdcron_rmtag_expr38 @n 235 mpdcron_rmtag_expr43 @n 163,18par mpdcron_rmtag_artist_expr38 @n 239 mpdcron_rmtag_artist_expr43 @n 33,23d mpdcron_rmtag_album_expr38 @n 243 mpdcron_rmtag_album_expr43 @n 74,30rs mpdcron_rate_genre_expr28 @n 118,20ai mpdcron_rate_genre_expr38 @n 209 mpdcron_rate_expr28 @n 159,15 mpdcron_rate_expr38 @n 214 mpdcron_rate_artist_expr28 @n 36,20 mpdcron_rate_artist_expr38 @n 204 mpdcron_rate_album_expr28 @n 77,20_co mpdcron_rate_album_expr38 @n 199 mpdcron_password25 @n 218 mpdcron_password40 @n 225 mpdcron_password42 @n 156 mpdcron_password28 @n 218 mpdcron_password31 @n 259 mpdcron_password44 @n 292 mpdcron_password43 @n 225 mpdcron_password33 @n 222 mpdcron_password38 @n 132 mpdcron_password29 @n 222 mpdcron_parser_result46 @n 80 mpdcron_parser38 @n 91,31 mpdcron_parse_single46 @n 770mpdcron_module5 @n 33 mpdcron_module24 @n 99r) mpdcron_module13 @n 249_a mpdcron_module17 @n 286 mpdcron_love_genre_expr33 @n 119,21 mpdcron_love_genre_expr38 @n 175 mpdcron_love_expr33 @n 161,16 mpdcron_love_expr38 @n 179 mpdcron_love_artist_expr33 @n 35,21 mpdcron_love_artist_expr38 @n 171 mpdcron_love_album_expr33 @n 77,21 mpdcron_love_album_expr38 @n 167 mpdcron_listtags_genre_expr38 @n 263 mpdcron_listtags_genre_expr44 @n 156,23*e mpdcron_listtags_expr38 @n 251 mpdcron_listtags_expr44 @n 213,18 mpdcron_listtags_artist_expr38 @n 259 mpdcron_listtags_artist_expr44 @n 34,23tr mpdcron_listtags_album_expr38 @n 255 mpdcron_listtags_album_expr44 @n 91,3046 mpdcron_listinfo_genre_expr31 @n 137,23 mpdcron_listinfo_genre_expr38 @n 159 mpdcron_listinfo_expr31 @n 188,18 mpdcron_listinfo_expr38 @n 163 mpdcron_listinfo_artist_expr31 @n 34,23 mpdcron_listinfo_artist_expr38 @n 155 mpdcron_listinfo_album_expr31 @n 85,23 mpdcron_listinfo_album_expr38 @n 151 mpdcron_list_genre_expr38 @n 143 mpdcron_list_genre_expr42 @n 84 mpdcron_list_expr38 @n 147 mpdcron_list_expr42 @n 108@n  mpdcron_list_artist_expr38 @n 139 mpdcron_list_artist_expr42 @n 35* mpdcron_list_album_expr38 @n 135 mpdcron_list_album_expr42 @n 59L mpdcron_kill_genre_expr29 @n 119,21*a mpdcron_kill_genre_expr38 @n 191 mpdcron_kill_expr29 @n 161,16 mpdcron_kill_expr38 @n 195 mpdcron_kill_artist_expr29 @n 35,21on mpdcron_kill_artist_expr38 @n 187 mpdcron_kill_album_expr29 @n 77,21ruc mpdcron_kill_album_expr38 @n 183mpdcron_entity31 @n 67,51,52mpdcron_entity44 @n 67,64,58 mpdcron_entity42 @n 42,24,25 mpdcron_count_genre_expr25 @n 118,20 mpdcron_count_genre_expr38 @n 275 mpdcron_count_expr25 @n 159,15re mpdcron_count_expr38 @n 279 mpdcron_count_artist_expr25 @n 36,20s mpdcron_count_artist_expr38 @n 271 mpdcron_count_album_expr25 @n 77,20on mpdcron_count_album_expr38 @n 267 mpdcron_connection_new25 @n 210n, mpdcron_connection_new31 @n 251 @L   `4lxxP$LDDh,Hdh8pP0pDX,P$l8xT4 hH,3 @n, SQL_SET_ENCODING35 @n 35 @n, SQL_PRAGMA_SYNC_ON35 @ SQL_PRAGMA_SYNC_OFF35 @n 48 @n, SQL_INSERT_SONG35 @n 57 @n, SQL_INSERT_GENRE35 @n 60 @n, SQL_INSERT_ARTIST35 @n 58 @n, SQL_INSERT_ALBUM35 @n 59 @n, SQL_HAS_SONG35 @n 52 @n, SQL_HAS_GENRE35 @n 55 @n, SQL_HAS_ARTIST35 @n 53 @n, SQL_HAS_ALBUM35 @n 54 @n, SQL_GET_VERSION35 @n 34 @n, C  35 @n 45 @n, SQL_DB_CREATE_SONG35 @n 37 @n, SQL_DB_CREATE_GENRE35 @n 40 @n, SQL_DB_CREATE_ARTIST35 @n 38 @n, SQL_DB_CREATE_ALBUM35 @n 39 @n, SQL_BEGIN_TRANSACTION35 @n 44 @n, SECSPERMIN20 @n 29 #@d @n 60 SECSPERMIN20 @n 41 #undef @n SECSPERHOUR20 @n 28 #@d @n 3600 SECSPERHOUR20  @n SECSPERDAY20 @n 27 #@d @n 86400 SECSPERDAY20 @n 39 #undef @n SCROBBLER_STATE_SUBMITTING7 @n 68 @n, SCROBBLER_STATE_READY7 @n 63 @n, SCROBBLER_STATE_NOTHING7 @n 52 @n, SCROBBLER_STATE_HANDSHAKE7 @n 58 @n,PROTOCOL_VERSION45 @n 41 #@d @n "0.1" PROTOCOL_OK41 @n 33 #@d @n "OK" PROCOTOL_ACK41 @n 34 # 8Z PERMISSION_UPDATE45 @n 48 #@d @n 2PERMISSION_SELECT45 @n 47 #@d @n 1PERMISSION_NONE45 @n 46 #@d @n@40 \(Px@tDd@T,h<T(d< L@Ll4L` 18   & ` MPDCRON_EVENT_UNLOAD106 @n 132 MPDCRON_EVENT_SUCCESS106 @n 144 MPDCRON_INIT_SUCCESS106 @n 100 MPDCRON_INIT_FAILURE106 @n 93 MPDCRON_GUARD_CRON_DEFS_H62 @n 20 about61 @n 120 MPDCRON_GUARD_CRON_CONFIG_H58 @n 20 MPDCRON_GUARD_WALRUS_DEFS_H52 @n 20 MPD_TOKENIZER_H50 @n 20 MPDCRON_GUARD_STATS_SQLITE_H49 @n 20 MPDCRON_EVENT_UNLOAD47 @n 221 MPDCRON_EVENT_SUCCESS47 @n 187,37 MPDCRON_INIT_SUCCESS47 @n 160 MPDCRON_INIT_FAILURE47 @n 135,9MPDCRON_PARSER_ERROR46 @n 96,28,38,42,51,87,83,84 MPDCRON_WELCOME_MESSAGE46 @n 576,7MPDCRON_PARSER_MALFORMED46 @n 104,29,35,42,52,87,83,84on_ as_md57 @n 442a) as_init6 @n 143 as_init13 @n 176o as_cleanup6 @n 146SHA as_cleanup13 @n 189i_ addtag_song40 @n 239 addtag_genre40 @n 237 addtag_artist40 @n 233 addtag_album40 @n 235 add_var_internal7 @n 179,5,5s add_var_i7 @n 586-8chadd_var7 @n 447-5,50-6,68 about34 @n 74 SQL_VACUUM35 @n 137,1026,64SQL_UPDATE_SONG35 @n 174,588,6-1,3,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,7SQL_UPDATE_GENRE35 @n 202,524,6,2,3,7SQL_UPDATE_ARTIST35 @n 194,459,6,2,3,7SQL_UPDATE_ALBUM35 @n 198,490,6,2,3,3,7SQL_SET_VERSION35 @n 70,837,26SQL_SET_ENCODING35 @n 73,833,18SQL_PRAGMA_SYNC_ON35 @n 134,1007SQL_PRAGMA_SYNC_OFF35 @n 135,10062SQL_INSERT_SONG35 @n 144,434,6-1,3,2,2,3,3,3,3,3,3,3,3,3,3,3,3,8SQL_INSERT_GENRE35 @n 168,378,6,2,8SQL_INSERT_ARTIST35 @n 158,319,6,2,8SQL_INSERT_ALBUM35 @n 163,347,6,2,3,8 SQL_HAS_SONG35 @n 139,296,7,10,2SQL_HAS_GENRE35 @n 142,254,7,10,2SQL_HAS_ARTIST35 @n 140,178,7,10,2SQL_HAS_ALBUM35 @n 141,216,7,10,2SQL_GET_VERSION35 @n 71,877,6,2,69-1,11-1SQL_END_TRANSACTION35 @n 132,987,6SQL_DB_CREATE_SONG35 @n 75,827,10SQL_DB_CREATE_GENRE35 @n 118,787,10SQL_DB_CREATE_ARTIST35 @n 99,804,10SQL_DB_CREATE_ALBUM35 @n 108,796,10SQL_BEGIN_TRANSACTION35 @n 131,968,6 SECSPERMIN20 @n 35-1 SECSPERHOUR20 @n 33-1 SECSPERDAY20 @n 31-1SCROBBLER_STATE_SUBMITTING7 @n 367,130,75SCROBBLER_STATE_READY7 @n 343,25,126,33,28,64,78_SCROBBLER_STATE_NOTHING7 @n 116,182,110,60,11 SCROBBLER_STATE_HANDSHAKE7 @n 296,143 PROTOCOL_VERSION36 @n 43 PROTOCOL_OK41 @n 78-1 PROCOTOL_ACK41 @n 121PERMISSION_UPDATE26 @n 95,11,44-1 PERMISSION_UPDATE45 @n 49-PERMISSION_UPDATE41 @n 67,1147-3,2-3,2-3,2-3,17-3,4-3,2-3,2-3PERMISSION_SELECT26 @n 93,13,38-1 PERMISSION_SELECT45 @n 49PERMISSION_SELECT41 @n 1234-3,2-3,2-3 PERMISSION_NONE41 @n 1254 PERMISSION_ALL26 @n 156-1MPDCRON_PARSER_SUCCESS46 @n 84,75,42,49,87,83,84 MPDCRON_PARSER_PAIR46 @n 140rMPDCRON_MODULE14 @n 114-1,4-1 MPDCRON_MODULE24 @n 25-1 MPDCRON_MODULE45 @n 277 @)MPDCRON_MODULE26 @n 41,11,11,10,10,17,10,10,16,25,10,9&MPDCRON_MODULE23 @n 44-1,4-1,4-1,4-1,4-1,6,9,7,8,10 MPDCRON_INTERNAL24 @n 98 MPDCRON_INIT_SUCCESS13 @n 181 MPDCRON_INIT_SUCCESS17 @n 127 MPDCRON_INIT_FAILURE5 @n 102,2 MPDCRON_INIT_FAILURE13 @n 173,2ce MPDCRON_INIT_FAILURE17 @n 123 MPDCRON_GUARD_UTILS_H15 @n 23 MPDCRON_GUARD_STATS_DEFS_H45 @n 2051 MPDCRON_GUARD_SCROBBLER_DEFS_H6 @n 23" MPDCRON_GUARD_NOTIFICATION_DEFS_H21 @n 20 MPDCRON_GUARD_MODULE_H24 @n 20 MPDCRON_EVENT_UNLOAD5 @n 150l MPDCRON_EVENT_UNLOAD13 @n 243 MPDCRON_EVENT_SUCCESS5 @n 138'MPDCRON_EVENT_SUCCESS17 @n 150,29,12,7,4,21,12,5,12,14,12,5 MPDCRON_EVENT_SUCCESS13 @n 209,37!MPDCRON_EVENT_RECONNECT_NOW5 @n 145,51,20,20,21,20,20,20,20 v MPDCRON_EVENT_RECONNECT5 @n 140bl MPDCRON_EUGENE_DEFS_H38 @n 20 MPDCRON_ERROR_SERVER_UNK46 @n 88  MPDCRON_ERROR_NO_UNIX46 @n 704ack/MPDCRON_ERROR_MALFORMED46 @n 170,6,36,11,41,43,44,39,44,40,44,39,21,9 MPDCRON_ERROR_EOF46 @n 70 MAX_SUBMIT_COUNT7 @n 5793 MAX_RESPONSE_BODY12 @n 392 l\4 tL`tX<X0l`d8l@h<`4 l8D$|T,\,xL$\(, /** Success **/HMPDCRON_EVENT_RECONNECT_NOW24 @n 40 @n, /** Schedule a reconnection to mpd server immediately. **/;MPDCRON_EVENT_RECONNECT)HFzHFz( (  dd!U Cz456>>@AIXXZ[ctxy;AB_ERROR_NO_UNIX38 @n 38 @n, MPDCRON_ERROR_MALFORMED38 @n 40 @n, MPDCRON_ERROR_EOF38 @n 42 @n, MPDCRON_ERROR_BAD_ADDRESS38 @n 39 @n,MAX_SUBMIT_COUNT7 @n 41 #@d @n 10MAX_RES1h& dda& X  n MPDCRON_MODULEG_GNUC_PRINTF41 @n8 2 ddYh Cz @n(3, 4)ENV_MPDCRON_PORT38 @n 32 #@d @n "MPDCRON_PORT"#ENV_MPDCRON_PASSWORD38 @n 33 #@d @n "MPDCRON_PASSWORD"ENV_MPDCRON_HOST38 @n 31 #@d @n "MPDCRON_HOST" DEFAULT_PORT38 @n 35 #@d @n 6/8SCz Ez+ + 45 @n 44 #@d @n 16DEFAULT_HOSTNAME38 @n 34 #@d @n "l0 dd1  n "any" DB_VERSION35 @n 68 #@d @n 10COMMAND_RETURN_OK45 @n 70 @n = 0,COMMAND_RETURN_KILL45 @n 71 @n = 10,COMMAND_RETURN_ERROR45 @n 69 @n = -1,COMMAND_RETURN_CLOS [COMMAND_ARGV_MAX45 @n 51 #XXX d h] @n, AS_SUBMIT_FHx# dd#  @n 38 #@d @n VERSION AS_CLIENT_ID7 @n 37 #@d @n "mcn"ACK_ERROR_UNKNOWN45 @n 65 @n = 4,ACK_ERROR_PERMISSION45 @n 64 @n = 3,ACK_ERROR_PASSWORD45 @n 63yE a l(<PGS49 @n 84 @n = 102,ACK_ERRAGzGz. . F2W^,AJ(% ddI  _ERROR_DATABASE_STEP49 @n 80 @n = 59,ACK_ERROR_DATABASE_SELECT49 @n 76 @n = 55,ACK_ERROR_DATABASE_RESET49 @n 81 @n = 60,ACK_ERROR_D! @n = 57,ACK_ERROR_DATABASE_OPEN49 @n 71 @n = 50,ACK_ERR  dda8 CzOR_DATABASE_CREATE49 @n 72 @n = 51,ACK_ERRh9 dd  _ERROR_DATABASE_AUTH49 @n 74 @n = 53,ACK_ERROR_ARG45 @n 62 @n = 1,  __.VERSION __.VERSION 5  __.COMPRESS __.COI ؼ __.COMPNAME __.COMPNAMEa` D 2  ~<H@D0  ddn L 1-2,8,5,3,11,10,8,3,4,10,13,22,4-1,17,4-1,17,4-1,17,4-1,15,7-1,5,15,7-1,5,15,7-2,5,15,7-1,5,15,7-5,5,15,7-5,5,15,7-6,5,15,7-5,5,17,4-1,17,4-1,17,4-1,17,4-1,19,6,4,8,4-1,19,6,4,8,4-1,19,6,4,8,4-1,19,6,4,8,4-1,14,4-1,14,4-1,14,4-1,14,4-1,14,4-1,14,4-1,14,4-1,14,4-1,15,7-1,3,6,15,7-2,3,6,15,7-1,3,6,15,7-1,3,6,19,6,4,8,4-1,19,6,4,8,4-1,19,6,4,8,4-1,19,6,4,8,4-1,9-1,3,96-1,10-1,6-1,5-1,Fz3 3 dXk @FzXk Xk + dd,    dd Q 4:>BFKLMNQRSVW]_`abcdqFzFz5 5 d[ % y#CL`x db_kill_song_expr49 @n 182 db_kill_album_expr49 @n 176 db_rate_song_expr49 @n 194 db_list_artist_tag_expr49 @n 221 db_list_song_expr49 @n 134 db_love_song_expr49 @n 170 db_list_album_expr49 @n 128 db_initialized49 @n 97 db_love_album_expr49 @n 164db_song_data_free41 @n 272,117,537 db_song_data_free49 @n 94 db_song_data35 @n 1406,20,191,22,952,19 db_song_data41 @n 269,113,537 db_set_sync35 @n 2167,22,60,22,60,22,60,22 db_set_sync49 @n 116 db_set_sync48 @n 147 db_set_authorizer41 @n 1417-1 db_set_authorizer49 @n 106 db_remove_song_tag_expr41 @n 835 db_remove_song_tag_expr49 @n 218 db_remove_genre_tag_expr41 @n 892 db_remove_genre_tag_expr49 @n 215 db_remove_artist_tag_expr41 @n 873 db_remove_artist_tag_expr49 @n 209 db_remove_album_tag_expr41 @n 854 db_remove_album_tag_expr49 @n 212 db_rate_song_expr41 @n 614 db_rate_genre_expr41 @n 740*e db_rate_genre_expr49 @n 191 db_rate_artist_expr41 @n 656 db_rate_artist_expr49 @n 185 db_rate_album_expr41 @n 698 db_rate_album_expr49 @n 188 db_quark35 @n 246,73,8,14,17,8,14,17,8,14,17,8,14,20,10,6,17,13,6,17,10,6,16,52,6,17,12,6,17,15,6,17,12,6,18,53,10,24,8,58,9,9,26,6,27,9,10,16,18,37,17,6,14,6,18,6,14,6,19,81,22,26,23,26,22,26,22,28,26,28,27,28,26,28,26,478,23,59,23,59,23,59,23,53,23,25,24,25,23,25,23 db_process47 @n 84 db_process48 @n 89 db_process49 @n 122 db_love_song_expr41 @n 506 db_love_genre_expr41 @n 572 db_love_genre_expr49 @n 167 db_love_artist_expr41 @n 5503 db_love_artist_expr49 @n 161 db_love_album_expr41 @n 528 db_listinfo_song_expr41 @n 375 db_listinfo_song_expr49 @n 146 db_listinfo_genre_expr41 @n 472 db_listinfo_genre_expr49 @n 143 db_listinfo_artist_expr41 @n 407 db_listinfo_artist_expr49 @n 137 db_listinfo_album_expr41 @n 439 db_listinfo_album_expr49 @n 140 db_list_song_tag_expr41 @n 912 db_list_song_tag_expr49 @n 230 db_list_song_expr41 @n 262 db_list_genre_tag_expr41 @n 1009 db_list_genre_tag_expr49 @n 227 db_list_genre_expr41 @n 347 db_list_genre_expr49 @n 131 db_list_artist_tag_expr41 @n 977 db_list_artist_expr41 @n 290 db_list_artist_expr49 @n 125 db_list_album_tag_expr41 @n 944 db_list_album_tag_expr49 @n 224 db_list_album_expr41 @n 318 db_kill_song_expr41 @n 176 db_kill_genre_expr41 @n 242 db_kill_genre_expr49 @n 179 db_kill_artist_expr41 @n 220  db_kill_artist_expr49 @n 173 db_kill_album_expr41 @n 198 db_insert_song35 @n 1198 db_insert_genre35 @n 1236 db_insert_artist35 @n 1209 db_insert_album35 @n 1222 db_init47 @n 139 db_init49 @n 100 db_init48 @n 139 db_has_song35 @n 1195 db_has_genre35 @n 1232 db_has_artist35 @n 1206 db_has_album35 @n 1218#db_generic_data_free41 @n 300,29,28,64,33,32,473,32,32 db_generic_data_free49 @n 91?db_generic_data35 @n 1262,19,28,20,29,20,77,21,33,21,34,21,861,19,29,19,30,19#db_generic_data41 @n 297,28,29,60,32,33,472,33,32db_end_transaction35 @n 2188,82,82,82 db_end_transaction49 @n 113 db_end_transaction48 @n 167 db_create35 @n 1019 db_count_song_expr41 @n 10631 db_count_song_expr49 @n 158 db_count_genre_expr41 @n 1189 db_count_genre_expr49 @n 155 db_count_artist_expr41 @n 1105har db_count_artist_expr49 @n 149 db_count_album_expr41 @n 1147 db_count_album_expr49 @n 152mpdcron-0.3+git20110303/GSYMS000066400000000000000000006000001153374523700152420ustar00rootroot00000000000000b1 @tX0x$@th8$L4<   $ |xHP<,`(\ `|  X4@X$Tt `lxx  0 | 8 $h@, @ l X  hD44P @H |T| p  < d    \  |x| XLX< 4 @ \0T  nowplay_url7 @n 88,33,33,166,2,6,4-1,179,2tin g_string_append_printf7 @n 166@n  BADAUTH7 @n 243,12n 9timestr7 @n 437,4-1,9,3erstrlen7 @n 225,2-1,20,4,3,4,3,375,7len7 @n 492,7,7,76,2,4 g_message7 @n 216,33,261,89,60,18,11,37 2str7 @n 459,52,3,87,6 strstr7 @n 657 @ng_slist_foreach7 @n 543,120,27,44,12 BANNED7 @n 242,10 memcmp7 @n 215,4,5H1strncmp7 @n 248,4,3,4,3n  g_queue_peek_head_link7 @n 578ck g_source_remove7 @n 149,2 response7 @n 288,3,16,9,4,4,38,16,2,3 input_r7 @n 273,2,84ENULL7 @n 120-2,21,27,109,16,35,3,3,43,48,32,42,39,35,4,78,14,19,30,14,12 scrobbler_config7 @n 1115 snprintf7 @n 499,85 1 username7 @n 4503 memchr7 @n 276,102EVE password7 @n 423,4,15g_debug7 @n 317,4,4,186-1,89-1filkey7 @n 159,4,14,2,3,2,3,2INI g_queue_get_length7 @n 676,48 memset7 @n 395MPD sep7 @n 159,3album7 @n 488,17,27,6,27,27,33,28true7 @n 251,210,55,93ini tv_sec7 @n 420_en BADSESSION7 @n 44,175 cat7 @n 425,2-2t mbid7 @n 489,19,24,7,27,28,31,29n scrobbler_configs7 @n 686,4stret7 @n 293,15,2n GTimeVal7 @n 417!g_free7 @n 153-3,17,136,20,3,3,94,25-1_pl9state7 @n 80,36,180,2,45,24-1,40,31,29,11,15,3,30,28,17,47,78 GSList7 @n 105,581mod"artist7 @n 487,16,28,5,27,23,38,13,14,9 GList7 @n 578 list7 @n 578-3roy?data7 @n 132,2,154,2,72,2,100,2,6,47,2,60,31,2,52,2,25,2,21,2,19,2 source7 @n 590,67FAILED7 @n 45,178-2,2-1,3412 g_queue_push_tail7 @n 617scrobbler_name7 @n 212,4,4,7,3,31s7 @n 159,3-1,3,2,4,5,2,3,2,3,22 guint7 @n 84-1,587,527line7 @n 213,2,4,5,4,5,7,8,4,2-1,3-1,2-1,2,3,10,5,3,7,15-2pending7 @n 102,25,244,17-2,2157name7 @n 209,41,4,4,3,3,3,34,17,4,4,46,10,128-2,88-1,2,123_tnewline7 @n 276,3,3-1,82,13-2time7 @n 540,49,67,4t session7 @n 87,33,33,163,2,11-1,172,74uesfile7 @n 624,16,7,10)g_string_free7 @n 461,55,93n GString7 @n 159,18,5,5,249,55,62@ post_data6 @n 138track6 @n 40,71,38,3tjournal_interval6 @n 5726proxy6 @n 51s fd6 @n 16323,length6 @n 44,106,3corecord6 @n 87,5,5,6,3,3 @ bool6 @n 108,69-1 file_config6 @n 80rd_ scrobblers6 @n 59 gboolean6 @n 168 GKeyFile6 @n 163  GQueue6 @n 115,3s queue6 @n 115,3mpdest6 @n 87n  url6 @n 69,69 gpointer6 @n 169journal6 @n 77size_t6 @n 48 src6 @n 87,5,438 NULL6 @n 111E username6 @n 70 password6 @n 71haalbum6 @n 41,108,4 callback6 @n 139embid6 @n 42,108,3 scrobbler_configs6 @n 143 path6 @n 115,36,1artist6 @n 39,72,38,3 GSList6 @n 59,84 data6 @n 139,307source6 @n 45HAVE_CONFIG_H6 @n 26 time6 @n 43,111,1name6 @n 67ec file6 @n 1521 g_slist_next5 @n 190,20,20,21,20,20,20,20 mod_path5 @n 48er config_fd5 @n 73,29,67,2pg_module_error5 @n 86 userdata5 @n 117,6 g_slist_prepend5 @n 112G_MODULE_SUFFIX5 @n 44gclose5 @n 119,4,6,46,2VERSION5 @n 59 @n/conn5 @n 183,11,9,11,9,11,9,12,9,11,9,11,9,11,9,1127, song5 @n 243,12am conf5 @n 48,54scr G_FILE_TEST_EXISTS5 @n 50,11PACKAGE5 @n 597 @ mpd_status5 @n 244,20,40,20rg_module_open5 @n 84fmpd_connection5 @n 183,20,20,20,21,20,20,20s g_slist_remove_link5 @n 15490g_strdup_printf5 @n 44,41 GKeyFile5 @n 73,96,3, mpd_stats5 @n 183g_new05 @n 77GModule5 @n 32mod5 @n 75,2-1,3,3,2-2,3,2-3,5-1,2-3,4-1,8,2,4-2,2-1,4,7-1,4-1,4-1,2,6-1,25,4-1,2-1,12,4-1,2-1,12,4-1,2-1,13,4-1,2-1,12,4-1,2-1,12,4-1,2-1,12,4-1,2-1,12,4-1,2-17module5 @n 32,52,7,3,12,24rec gpointer5 @n 91,26AS_ GPOINTER_TO_INT5 @n 123crstatus5 @n 244,11,9,11,29,11,9,11 g_slist_free5 @n 156,221, module_data5 @n 1354- g_warning5 @n 79,6,7,11,57,3- mpd_song5 @n 243- g_message5 @n 141,5,58modules5 @n 36,76,65-2,11,5,15,5,15,5,16,5,15,5,15,5,15,5,15,5,4, g_slist_foreach5 @n 177,2G_MODULE_BIND_LOCAL5 @n 84slist_r5 @n 135,19,3sKNULL5 @n 36,12,11,10,9,6,7,10,25,29,24,11,2,18,2,18,2,19,2,18,2,18,2,18,2,18,211g_debug5 @n 43,2,4,2,9,2,49n user5 @n 30,48,64,5,5,9bm g_file_test5 @n 50,11slink_r5 @n 135,19,2-1LIBDIR5 @n 59_ret5 @n 135,2,26,22,4,6-1,3,6,4,6-1,3,6,4,6-1,3,7,4,6-1,3,6,4,6-1,3,6,4,6-1,3,6,4,6-1,3,6,4,6-1,38mret5 @n 185,9-2,9,9-2,9,9-2,10,9-2,9,9-2,9,9-2,9,9-2,9,9-2$g_free5 @n 52,4,7,4-1,13,6-1,7-1,9,2,21,3 stats5 @n 183,11@:path5 @n 31,10,7-3,3,2,3-3,3,3,10,6,2-1,6,2,9-1,6,17,15,5,5,9#GSList5 @n 36,99,51,20,20,21,20,20,20,20@Qdata5 @n 33,58,10-1,15,5,4-1,64-1,2,17-1,2,17-1,2,18-1,2,17-1,2,17-1,2,17-1,2,17-1,2g_module_symbol5 @n 91rd_ g_build_filename5 @n 48,11ngg_module_close5 @n 94,12,24scuser_r5 @n 39,14,11itmodname5 @n 39,4-2,6,11,11,5,2,89,2name5 @n 41,3-1,3,4,7,4,4Nwalk5 @n 186,4-1,4,11,4-1,4,11,4-1,4,12,4-1,4,11,4-1,4,11,4-1,4,11,4-1,4,11,4-1,4 GINT_TO_POINTER5 @n 177nt home_path3 @n 72,3jou message3 @n 79,2 g_setenv3 @n 5229 gchar3 @n 63,5 @n conf3 @n 72,3code3 @n 78 g_malloc3 @n 6869g_strdup_printf3 @n 5152G_SPAWN_LEAVE_DESCRIPTORS_OPEN3 @n 76G_SPAWN_ERROR_NOENT3 @n 78e6 env3 @n 29,23-1nt ncalls3 @n 30,21l calls3 @n 31,18-4 g_warning3 @n 79D envstr3 @n 47,4-3G_SPAWN_CHILD_INHERITS_STDIN3 @n 76strcmp3 @n 50NULL3 @n 41,8,20-1,4-1,22 g_debug3 @n 53,19,9 @GError3 @n 64error3 @n 64,10,3-2,2,3 g_error_free3 @n 8414myargv3 @n 63,5-2,2,3,7-1,4-1g_free3 @n 54,28-1,4-1g_spawn_async3 @n 75 i3 @n 49-4seg_build_filename3 @n 69 @name3 @n 28,17,4-1,11,5,3,10,2,2,G_SPAWN_ERROR_NOEXEC3 @n 784 mod_path2 @n 63,20s) pid_path2 @n 35-1,2,2,41 home_path2 @n 38,12,2,7,4,16 port2 @n 69-1 g_getenv2 @n 49-3,15,2,2&conf2 @n 28,7-1,2,2,6,4,2,7,4,4-4,8-2,2,2PACKAGE2 @n 595 @ g_strdup2 @n 50ueg_strdup_printf2 @n 37_pl g_critical2 @n 54 HAVE_GMODULE2 @n 61,21 conf_path2 @n 59,21oo hosret-1error name,2mpd_response_finish52 g_messagencodeP@n hostnametlGSList g_p exprd_ mpd_stats_get_p artist sourceg_freememsetMPD_STATE_PLAYpecurl_multi_strerrorEg_string_free_DfpP0|L(lL$d@  T  d H(dH$ l  $   @ d |`D( x T | d8<4  `DH 0   P ( |\4 4 P l x tP( X0 \< (X  x\< tP FzFz  #$%&'()*+,-./0TW(+Y\(+^a$'<?QTfi{~ 58VY# ' , - L O u y ~  ) ,    )78ALMVabkvw "-.:LMS[twxz    ) 0 1 B C I Q j m n p { ((9OPRRT`vwyy{679./BL_`r|./EOde{C]^py|    & 3 9 S T f o r x   / 0 C  "#$'5:;<?JOPQT_defityz{~ +3458D~" & P(;b?@KLMN8I]s !"QR GKeyFile106 @n 48,22 GError106 @n 72 GLogLevelFlags62 @n 76 GKeyFile62 @n 68,4,15 GMainLoop62 @n 29 GOptionContext61 @n 99 GKeyFile61 @n 37 GMainLoop61 @n 36 DAEMON_SET_VERBOSITY_AVAILABLE61 @n 125 FALSE61 @n 185,65 GError61 @n 100GITHEAD61 @n 50,59,24 EXIT_SUCCESS61 @n 122,36,42,65,2 GOptionEntry61 @n 40 EXIT_FAILURE61 @n 105,10,32,7,52,8,22,7 GINT_TO_POINTER61 @n 162 GKeyFile59 @n 26,142 GError59 @n 30 GLogLevelFlags57 @n 26,32 GPOINTER_TO_INT57 @n 61 GIOCondition55 @n 92 GIOChannel55 @n 91,63FALSE55 @n 87,29,3,14,6 EXIT_FAILURE55 @n 58,14,39 GKeyFile51 @n 32 GError51 @n 33GError50 @n 37,14,15,15^GError49 @n 100,7,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 GOptionContext48 @n 120 GError48 @n 45,76 GITHEAD48 @n 125 GOptionEntry48 @n 32 GKeyFile47 @n 128 GError47 @n 64,66 GOutputStream46 @n 635 FALSE46 @n 625 GQuark46 @n 54 GHashTable45 @n 84 GDataInputStream45 @n 57 GOutputStream45 @n 58 GKeyFile45 @n 92 GIOStream45 @n 56 GOptionContext44 @n 325 GError44 @n 315 GITHEAD44 @n 329 GOptionEntry44 @n 316 GOptionContext43 @n 258 GError43 @n 248 GITHEAD43 @n 262 GOptionEntry43 @n 249 GOptionContext42 @n 189 GError42 @n 179 GITHEAD42 @n 193 GOptionEntry42 @n 180 GPOINTER_TO_INT41 @n 1204wGError41 @n 169,22,22,22,20,28,28,29,28,32,32,33,34,22,22,22,23,42,42,42,40,19,19,19,19,19,19,19,18,32,33,32,35,42,42,42,210 GOptionContext40 @n 258 GError40 @n 248 GITHEAD40 @n 262 GSList6 @n 59,84&GSList38 @n 77,11,48,4,4,4,4,4,4,4,88,4,4,4ceGSList31 @n 30,51,52,519GSList35 @n 1256,48,49,48,48,54,55,54,505,82,82,82,78,48,49,48,2 GSList7 @n 105,581mod GResolver36 @n 206,74 GREETING36 @n 43,1152 GQueue6 @n 115,3sGQueue11 @n 46,21,70u GQueue7 @n 96,255 GQuark15 @n 30eve GQuark35 @n 2124 GQuark32 @n 2834GPollFD12 @n 137,27,1284- GPOINTER_TO_INT5 @n 123crGPOINTER_TO_INT36 @n 103,5,72 GOptionEntry25 @n 242 GOptionEntry33 @n 246,38 GOptionEntry31 @n 283 GOptionEntry40 @n 249 GOptionEntry28 @n 242 GOptionEntry29 @n 246,38 GOptionContext25 @n 251 GOptionContext29 @n 255,38 GOptionContext31 @n 292@n GOptionContext33 @n 255,3892 GOptionContext28 @n 2516-GObject36 @n 58,24,44,76GModule5 @n 32 GList7 @n 578 GList36 @n 205 GLIB_CHECK_VERSION11 @n 85,43 GKeyFile5 @n 73,96,3, GKeyFile23 @n 35t GKeyFile14 @n 35,69,4 GKeyFile17 @n 116 GKeyFile6 @n 163 GKeyFile21 @n 51 GKeyFile24 @n 67y GKeyFile13 @n 169 GKeyFile26 @n 321 GKeyFile15 @n 38,37 GInetAddress36 @n 172,542 GITHEAD25 @n 2558 GITHEAD31 @n 296 GITHEAD33 @n 259,3834 GITHEAD28 @n 2556 GITHEAD29 @n 259,3815 GITHEAD34 @n 38ov GINT_TO_POINTER5 @n 177ntGINT_TO_POINTER26 @n 144,6,6cGINT_TO_POINTER36 @n 121,29,13,159,42 GHashTable36 @n 45GError3 @n 64 GError29 @n 245,387,4 GError28 @n 2417- GError23 @n 39 GError18 @n 32 GError38 @n 102 GError26 @n 35 GError25 @n 241GError36 @n 61,26,87,30,37GError15 @n 39,3,34,3GError35 @n 243,68,39,39,39,43,33,36,33,74,35,38,35,83,31,7,7,7,9,44,39,96,17,20,20,24,21,77,48,49,48,48,54,55,54,56,21,21,21,24,21,21,21,24,21,21,21,24,21,21,21,24,24,24,24,24,82,82,82,82,48,49,48 GError14 @n 38,69 GError33 @n 245,38 @nGError32 @n 47,54,48,64@n GError31 @n 282md GDataInputStream38 @n 117 GAsyncResult36 @n 58,24,120FILE11 @n 37,11,91 FILE34 @n 43  FD_ZERO12 @n 122-2 FD_ISSET12 @n 92,5,54 FD_CLR12 @n 94,5,5@n  FALSE12 @n 283,14 FALSE35 @n 274che FALSE27 @n 47 FALSE36 @n 165FAILED7 @n 45,178-2,2-1,3412 ENOENT11 @n 147(s CURLcode12 @n 217,110,76- CURL_GLOBAL_ALL12 @n 327 CURL_ERROR_SIZE12 @n 492, CURLOPT_WRITEFUNCTION12 @n 429,11 CURLOPT_WRITEDATA12 @n 4307 CURLOPT_USERAGENT12 @n 42835 CURLOPT_URL12 @n 444s CURLOPT_PROXY12 @n 4367-1 CURLOPT_POSTFIELDS12 @n 4415 CURLOPT_POST12 @n 440 CURLOPT_FAILONERROR12 @n 431e CURLOPT_ERRORBUFFER12 @n 432 CURLOPT_BUFFERSIZE12 @n 433@n CURLMsg12 @n 238o CURLMcode12 @n 119,138,1470 CURLM_OK12 @n 127,138,155 CURLM_CALL_MULTI_PERFORM12 @n 263,2 CURLMSG_DONE12 @n 242 CURLM12 @n 54 CURLE_OK12 @n 220,108,117 CURL12 @n 40,162 BANNED7 @n 242,10 BADTIME7 @n 244,15 sc BADSESSION7 @n 44,175 BADAUTH7 @n 243,12n 9  __.VERSION __.VERSION 5n __.COMPNAME __.COMPNAME __.COMPLINE __.COMPLINE __.COMPACT __.COMPACTD & xPl0p0T4p xL(  DpPT 0 l<L P l $d<dD(    tL(P,< lt h<0 4 TlT 4|\<  d8 gclose62 @n 90 gchar62 @n 76 gpointer62 @n 77 g_unsetenv61 @n 137 g_warning61 @n 152 Dg_unsetenv60 @n 470-19,3-19,7,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5-6,9,4 g_warning59 @n 66,11,11,27,16,11,14gchar57 @n 26-1,31 gpointer57 @n 59 g_warning56 @n 240 g_timeout_add_seconds55 @n 147 gboolean55 @n 51,39 gpointer55 @n 52,41 g_warning55 @n 45,35 guint55 @n 31 homepath51 @n 31,6,2,7,27,8,2 genre49 @n 58 g_timer_new47 @n 159 globalconf47 @n 139,2,8-4,2 g_timer_continue47 @n 108 g_timer_destroy47 @n 169 g_timer_stop47 @n 115 g_timer_start47 @n 46 g_warning47 @n 85,4 g_timer_elapsed47 @n 68,29 g_string_new46 @n 617,20gchar46 @n 66,83,40,49,87,83,84,194 g_unix_socket_address_new46 @n 6960genre46 @n 409,5,8-1,6,7,6-3,3-1,3-1,3-1,3-1,3-1,3-1 g_string_printf46 @n 638gsize46 @n 64,84,40,49,87,83,84,194g_string_free46 @n 625,35,3 globalconf45 @n 90 gchar45 @n 102 gsize45 @n 102genre44 @n 262,6,2-1,32genre43 @n 195,6,2-1,32genre42 @n 126,6,2-1,32 g_string_new41 @n 90,30 globalconf41 @n 1202 gpointer41 @n 1202 hostname26 @n 193 hostname40 @n 198,11,8 hostname25 @n 191,11,8 hostname36 @n 239,8,2,7,6,5,17,4 hostname38 @n 126 hostname24 @n 49 hostname27 @n 54,4,8 home_path2 @n 38,12,2,7,4,16 home_path3 @n 72,3jou home_path24 @n 45 home_path26 @n 47hints18 @n 35,14-225 hints23 @n 71,66-1lbu hints21 @n 39handshake_source_id7 @n 84,34,30-1,321,10,2handler41 @n 43,1385handle11 @n 48,5-1,6,2gushort12 @n 88,2,48,24guint7 @n 84-1,587,52 guint12 @n 61gsize36 @n 40,45,225grp14 @n 35,6,6-1,3,7,3,5,3,17cmdgrp15 @n 38,11,14,12,11,14@n gpointer5 @n 91,26AS_ gpointer14 @n 91 gpointer12 @n 306,421 gpointer36 @n 48,11,24,43,43,33 gpointer6 @n 169 gpointer7 @n 132,332,55,93,54,27,23,21val gpointer8 @n 28pd gpointer11 @n 344 glong7 @n 420 Pglobalconf26 @n 29,8,4,5-1,4-1,5-1,4-1,5-1,17,7,2,2,9,17,5,15,2,4,2,4,2,14,12,4-2,4-2,8-5 globalconf36 @n 132,9 gint12 @n 279genre25 @n 188,6,2-1,32genre40 @n 195,6,2-1,32 genre35 @n 296,4genre29 @n 192,6,2-1,32genre33 @n 192,6,2-1,32genre28 @n 188,6,2-1,32genre31 @n 229,6,2-1,32gdb35 @n 30,285,5,8,14,12,5,8,14,12,5,8,14,12,5,8,14,14,6,10,6,11,6,13,6,11,6,10,6,11,5,52,6,11,6,12,6,11,6,15,6,11,6,12,6,13,5,53,10,16,6,2,8,42,16,9,9,12,14,16,10,14,2-1,6,3,12,4,15,3,24-1,8,2,3,12,4,6,10,4,6,12,6,6,10,4,6,13,80,5,2,22,19,5,2,23,19,5,2,22,19,5,2,22,19,7,2,26,19,7,2,27,19,7,2,26,19,7,2,26,18,11,10,11,10,11,10,11,13,11,10,11,10,11,10,11,13,11,10,11,10,11,10,11,13,11,10,11,10,11,10,11,13,14,10,14,10,14,10,14,17,7,2,23,26,24,7,2,23,26,24,7,2,23,26,24,7,2,23,26,20,5,2,23,18,5,2,24,18,5,2,23,18,5,2,23gclose5 @n 119,4,6,46,2 gchar3 @n 63,5 @ngchar36 @n 39,47,224 gboolean6 @n 168 gboolean8 @n 27e_ gboolean7 @n 464,229n gboolean12 @n 278,11,15 gboolean35 @n 985 gboolean36 @n 124 g_warning3 @n 79D )g_warning7 @n 208,12,6,4,2,21,3,4,3,3,35,71,266,7 g_warning26 @n 99,36,25 g_warning5 @n 79,6,7,11,57,3- g_warning12 @n 128,95,43c g_warning36 @n 74,33,26,55,29,38,9,8 g_warning18 @n 57 g_warning23 @n 105,5 g_warning11 @n 55,96  g_uri_escape_string12 @n 380 g_unix_socket_address_new36 @n 249 g_type_init34 @n 121 g_type_init36 @n 234 g_timer_stop13 @n 156 g_timer_stop17 @n 45 g_timer_start13 @n 71,3s_ g_timer_start17 @n 69 g_timer_new13 @n 178 g_timer_new17 @n 125g_timer_elapsed13 @n 101,30,7 g_timer_elapsed17 @n 108n g_timer_destroy13 @n 1926 g_timer_destroy17 @n 135d g_timer_continue13 @n 149 g_timer_continue17 @n 59 g_timeout_add_seconds7 @n 483,229 g_timeout_add_seconds13 @n 179 10 g_time_val_from_iso860111 @n 108  g_strv_length18 @n 35 g_strv_length26 @n 134 g_strstrip11 @n 1725- g_strsplit26 @n 133 g_strsplit35 @n 263,2205,49,48,48 g_strndup7 @n 282 g_string_sized_new12 @n 453 g_string_printf41 @n 121 g_string_new7 @n 445,56,74ron g_string_new27 @n 36 g_string_new35 @n 262g_string_free7 @n 461,55,93n  g_string_free35 @n 274 g_string_free41 @n 97,32 g_string_free27 @n 47 g_string_free12 @n 77h$0H\ `8HpT`@pT|X4 t8d<`@(d<lLlH,p|< domain62 @n 76 daemon_ident_from_argv061 @n 102 daemon_log_ident61 @n 102 daemon_pid_file_kill_wait61 @n 151daemon_retval_send61 @n 235,7,5 daemon_pid_file_is_running61 @n 204 daemon_pid_file_remove61 @n 156 daemon_retval_init61 @n 209 daemon_retval_done61 @n 213 daemon_pid_file_proc61 @n 103 daemon_pid_file_create61 @n 239 daemon_fork61 @n 210 daemon_set_verbosity61 @n 127 daemon_close_all61 @n 232 daemon_retval_wait61 @n 219errno61 @n 152,60,9,13,7?error59 @n 30,2-2,5,11-1,10-3,2-1,3,13-3,2-1,3,7-3,2-1,3,18-3,2-1,3Ierror36 @n 61,11-3,12,11,2-1,8-1,64,10,3,2-1,14,8-1,3,2-1,22,7,6,2-1,12,2,2-1.error14 @n 38,8,2-1,2,2,4-2,2,2,3-1,2,2,36,6-3,3-2@n errno11 @n 56,91,5_da@errno41 @n 593,3,3,36,3,3,36,3,3,36,3,3,317,3,3,36,3,3,36,3,3,36,3,3 envstr3 @n 47,4-3envstr60 @n 54,3-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,9-2,2-2,2-2,8,18-1,2,4-1,2,4-1,2,164,14-2,2-2,2-2,2-2,2-2,2-2,2envname60 @n 142,4,2-1,4,2-1,2,3-1,3,3-1,3,3-1,7,2-1,3,2-1,3,2-1,3,2-1,3,2-1,3,2-1,3,2-1,3,2-1,3,2-1,3,2-1,3,2-1,3,2-1,3,2-1,3,2-1,3,2-1,3,2-1,8,17-2,4-2,92,13-2,2-2,47,46-2,3-1,2-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,2-2,14-2,3-1,2-2_envid60 @n 139,7-1,6-1,4-1,6-1,6-1,10-1,5-1,5-1,5-1,5-1,5-1,5-1,5-1,5-1,5-1,5-1,5-1,5-1,5-1,5-1,5-1 env3 @n 29,23-1ntentity48 @n 47,38-2,21entity60 @n 288,9-2,12Xendptr41 @n 151,2-1,433,7-1,7,27,7-1,7,27,7-1,7,27,7-1,7,308,7-1,7,27,7-1,7,27,7-1,7,27,7-1,7end7 @n 273,3,15,16,9,4,4elapsed13 @n 43,8,4,2-1,39,4,8,27,34, elapsed17 @n 106,3-3elapsed47 @n 36,2,25,5,2,25,3efds12 @n 88,14,2,13,7,2,13,23 easy_handle12 @n 244,$e15 @n 42,7-2,4,6,2-1,15,7-2,4,6,2-1N!e44 @n 67-2,6-3,53-2,6-4,46-2,6-3e31 @n 67,2-3,46,2-4,46,2-3e42 @n 42-3,21-4,21-3,21-3 duration49 @n 51domain57 @n 26,25-1,6,10dlevel57 @n 29,6,3,3,2,4,5,2 dispatch12 @n 322 disc49 @n 62dest6 @n 87n dest46 @n 613,4,4-1,3dest9 @n 27,2-6,5-2dest35 @n 222,3,2dest32 @n 151,5,35,16dest27 @n 32,4,5,2,3-1default_permissions26 @n 93,2,2,9,39,6,6 default_permissions36 @n 141 default_permissions45 @n 83dbpath26 @n 41,5-1,39,37,60,20dbpath48 @n 29,4,102-1,3-1,2,3 dbpath47 @n 139,2dbpath51 @n 31,18,20,11-1,4 dbpath45 @n 82 dbname41 @n 52/db_stmt_maint35 @n 128,774-5,5-3,9,9,15,6,2,55,15,11-1,22-2db_stmt35 @n 207,111,7,10,2,20,7,10,2,20,7,10,2,20,7,10,2,23,6,2,8,17,6,2,3,8,17,6,2,8,16,6-1,3,2,2,3,3,3,3,3,3,3,3,3,3,3,3,8,17,6,2,3,7,17,6,2,3,3,7,17,6,2,3,7,18,6-1,3,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,7,221,2,21-2,31,6,14,6,16,22,6 db_sql_maint35 @n 69,59,881-1,15,34db_sql35 @n 130,77,835,2,21 db_song_data35 @n 287 db_song_data49 @n 94 db_play_time17 @n 144,10,18,5 db_generic_data35 @n 278 db_generic_data49 @n 91days20 @n 25,6,12 date35 @n 297 date49 @n 59date60 @n 144,8,3,130,21,2,35,9-1Qdata5 @n 33,58,10-1,15,5,4-1,64-1,2,17-1,2,17-1,2,18-1,2,17-1,2,17-1,2,17-1,2,17-1,2 data14 @n 91,2209 data55 @n 52,41Ddata41 @n 269,28-3,25-4,25-3,25,32-7,25-8,25-7,433,32-6,2,25-5,2,25-5,2 data49 @n 91 data45 @n 102"data44 @n 67,3,3,58,3,3,52,3,3,46,4,3data42 @n 42,24,25,24data36 @n 39,9,2,119,8,133,5ddata35 @n 278,2-3,979,19-3,25,20-4,25,20-3,74,21-7,26,21-8,26,21-7,579,82,82,82,29,19-4,25,19-5,25,19-4data31 @n 67,51,52,46*data12 @n 137,56,12,42,45,56,2,50,8,7,8,26,14 data11 @n 34,2ackdata8 @n 28?data7 @n 132,2,154,2,72,2,100,2,6,47,2,60,31,2,52,2,25,2,21,2,19,2 data6 @n 139,307daemon_pid_file_ident2 @n 37 daemon_pid_file_ident61 @n 102 daemon_log57 @n 52,2)current_command41 @n 46,72,4,8,1215,13,19,9,15,4,6,9,10 curl_source_funcs12 @n 319,21curl_multi_strerror12 @n 129,138i\DtL,p8hH(`@(hL0hD |`<lP0lD,d,lL p\4xL,'ret33 @n 194,36,2,2,2,2,6,29,2-1,6,29,2-1 ret28 @n 190,36,2,2,2,2,6,33,3,2-1istret41 @n 1371,57,3ret42 @n 128,36,2,2,2,2,6,28,3,2ret40 @n 197,36,2,2,2,2,6,34,2-1ret7 @n 293,15,2n'ret29 @n 194,36,2,2,2,2,6,29,2-1,6,29,2-1 result7 @n 425,3,3necresult12 @n 217,3,27result36 @n 58,15,9,18,102,11 response7 @n 288,3,16,9,4,4,38,16,2,3 resolver36 @n 206,3,4-1,66,7-1 #requests12 @n 67,112,13-1,11,24,132-1,94,6lrequest12 @n 75,2-4,96,2,2-1,11-1,11,2-1,9,4,2-1,4-1,14,2,2,103,2,36,2,2,2,8,5-1,4-2,5,2-1,6-5,3,2-3,3,2-2,5,2,6-1,2,record6 @n 87,5,5,6,3,3 @record9 @n 27,11,7,9,6 *c record11 @n 67g2reconnect_sid55 @n 31,55,59,2,39-1 reconnect24 @n 55 reconnect59 @n 44,56,9,5,2,2,3 reconnect55 @n 146-1 readonly35 @n 981,11,5 readonly49 @n 1002rating28 @n 29,7,20,14,7,20,14,7,20,14,7,15,13,39,2,2,2e_)rating35 @n 1482,55,54,53,286,7,14,7,14,7,14,7%rating46 @n 295,83,83,84,421,5,7,5,7,5,7,5rating38 @n 76,11,113,5,5,5Drating41 @n 388,32,33,32,101,9,11,3,5,14,9,11,3,5,14,9,11,3,5,14,9,11,3,5rating49 @n 35,13,137,3,3,3rating31 @n 70,51,52,4661 raise61 @n 91 queue_length7 @n 672,4,2,46,3 queue6 @n 115,3mpqueue11 @n 46,4,10,7,5,65,38,18 0queue7 @n 96,30,17-1,207,5,33,169,20,39,57,2,32,15-1dq11 @n 94,11-1,2-1, cq35 @n 222,2-2q46 @n 86,16-1,5-5 ptr12 @n 386,4proxy6 @n 51sproxy14 @n 114,27-1,8 proxy12 @n 435-1printf25 @n 64,41,41,36nuprintf42 @n 43,24,25,24"printf44 @n 68,2,5,57,2,5,51,2,5,46,2,5printf43 @n 64,48,41,36 printf61 @n 50printf33 @n 65,42,42,37 printf48 @n 106,7printf29 @n 65,42,42,374,printf28 @n 64,41,41,36an printf34 @n 38printf31 @n 68,51,52,46clprintf40 @n 64,48,41,36 prev_elapsed13 @n 55,2-1,80-1 prev_elapsed47 @n 97-1 prev_elapsed17 @n 108-1@nprev13 @n 38,156-1,26-1,14-2,3prev47 @n 32,135-1,31-1,14-2,3 prepare12 @n 320 post_data6 @n 138 post_data12 @n 43,37,319,39-1,2t 'post_data7 @n 491,10-7,3,3,2,37,22-1,10-8,7,6,2 port2 @n 69-1port31 @n 231,15,544port33 @n 194,15,5_cl port24 @n 50tport25 @n 190,15,5port36 @n 35,148,6,7,43,5,27,2,3,9port26 @n 51-1,5-1,136hanport40 @n 197,15,5 port38 @n 126 port27 @n 53,8,5port43 @n 197,15,5port44 @n 264,15,5port42 @n 128,15,5 port45 @n 81,18 port47 @n 151,4port48 @n 43,9-1,3 port55 @n 55-1 port46 @n 683,29port28 @n 190,15,5artport29 @n 194,15,58&poll_fd12 @n 137-1,3,2,2-1,3-2,4,2,7-4,124-1 play_time17 @n 144,8,17,6 play_count31 @n 69,51,52,46 play_count41 @n 385,32,33,32 play_count38 @n 72,10 play_count35 @n 1478,54,55,54 play_count49 @n 32,13 play_count46 @n 299,83,83,84pl60 @n 287,12,3,3 pid_path2 @n 35-1,2,2,41 pid_path59 @n 57-1 pid_path24 @n 47pid61 @n 97,107-1,5-1,5 permission41 @n 40,1258,5,36,21 perm36 @n 141perm41 @n 67,1135-2,222 perm45 @n 55 performer35 @n 299 performer49 @n 61pending7 @n 102,25,244,17-2,215T  lH$$`| 4l @xP   |X$@ $p < H |P,, 4 0 hxX0 8 8 t ` \ dHL<t\ NULL106 @n 81,2/NULL61 @n 36-1,4-3,20,2,2,3,17,2,10,71,2,12,7,58,7 MPD_TAG_ALBUM60 @n 188 MPD_TAG_MUSICBRAINZ_TRACKID60 @n 272 MPD_TAG_COMMENT60 @n 242 MPD_TAG_PERFORMER60 @n 236 MPD_TAG_ARTIST60 @n 182 MPD_STATE_STOP60 @n 35 MPD_STATE_UNKNOWN60 @n 32 MPD_TAG_TRACK60 @n 206 MPD_STATE_PLAY60 @n 38,405 MPD_TAG_ALBUM_ARTIST60 @n 194 MPD_TAG_MUSICBRAINZ_ARTISTID60 @n 254 MPD_TAG_GENRE60 @n 218 MPD_TAG_DISC60 @n 248 MPD_TAG_NAME60 @n 212[NULL60 @n 30,89,63,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,18,3,4,26,7,15,3,31,7,3,15,6,21,9,10,7,57,100 MPD_TAG_DATE60 @n 224" MPD_TAG_MUSICBRAINZ_ALBUMARTISTID60 @n 266 MPD_TAG_MUSICBRAINZ_ALBUMID60 @n 260 MPD_TAG_TITLE60 @n 200 MPD_TAG_COMPOSER60 @n 230-NULL59 @n 32,25-1,3,2,20,2,14,2,25,2,24-1,19,3-1 NULL57 @n 31,20NULL56 @n 30,71-1,15,12,47,243NULL55 @n 32-1,10-1,4,8,9,6,5,24,10,14,23,10,12,7,13,2NULL51 @n 39,7,3-1,18,12-1/NULL48 @n 29,4-2,15,2,4,12,17,3,10,37,3,9-1,13,6-1MPD_TAG_ARTIST47 @n 49,23,7 MPD_STATE_PLAY47 @n 1898NULL47 @n 32-1,11,22,2,15,5,9,41,11,2,16,14,12,6,3,12,2,2-1MPD_TAG_TITLE47 @n 50,23,7NULL46 @n 68-1,3,18,5,8,7,2,20,19,3,37-1,4,43,4,9,20,6,4,4,4,4,4,4,24,4,9,20,6,4,4,4,4,4,24,4,9,20,6,4,4,4,4,4,25,4,9,20,6,4,4,4,4,4,63,25,19,40,7,9,4,2,10-1,4-3,11,2,2,2,9-1,2,9-1,2,9-1,2,9-1,2,9-1,2,9-1,2,9-1,2,9-1,2,9-1,2,9-1,2,9-1,2,9-1,2,9-1,2,9-1,2,9-1,2,9,2,9-1,2,9-1,2,9-1,2,9-1,2,9-1,2,9-3,2,9-3,2,9-3,2,9-3,2,9-3,2,9-3,2,9-3,2,9-3,2,9-2,2,9-2,2,9-2,2,9-2,2,9-2,2,9-2,2,9-2,2,9-2,2 MPD_TAG_ALBUM44 @n 108,6MPD_TAG_ARTIST44 @n 46,6,51,12 MPD_TAG_GENRE44 @n 168,6_NULL44 @n 32-1,11,2,20,3,2,18-1,11,2,5,22,3,2,19-1,11,2,20,3,2,18-1,11,17,2,2,2,39,6,24,3,2,2-1,23 MPD_TAG_ALBUM43 @n 91,6MPD_TAG_ARTIST43 @n 45,6,35,12 MPD_TAG_GENRE43 @n 134,69NULL43 @n 32,11,2,28,11,2,5,30,11,2,28,11,45,6,24,3,2,2-1,272NULL42 @n 32,2,7,15,2,7,16,2,7,15,2,7,35,6,24,3,2,2-1OK7 @n 43,172,33iNULL2 @n 38,14,7,4,4,2n <NULL36 @n 65,7,19,7,2-1,20,42,21,3,25,4,32,6,13,2,2,17,8,2,17,71NULL17 @n 38,30,5,35,39,27,14,6,12,5,21,6,11,15,11,7 NULL9 @n 62-4 NULL6 @n 111ENULL18 @n 38,2,2,2,5-1,3,2-1_NULL15 @n 42,2,6,6,23,8KNULL5 @n 36,12,11,10,9,6,7,10,25,29,24,11,2,18,2,18,2,19,2,18,2,18,2,18,2,18,211NULL3 @n 41,8,20-1,4-1,22*NULL14 @n 42,4,3,5,3,2,5,3,5,14,27,14,4,10,10NULL11 @n 69,30,13,18,16,12,83NULL25 @n 35,8,2,31,8,2,31,8,2,31,8,45,6,24,3,2,2-1,30NULL41 @n 88,29-1,12,45,22,22,22,19-1,7,20-1,7,20-1,7,21-1,7,20-1,7,24-1,7,24-1,7,25-1,7,27,22,22,22,23,19,23,19,23,19,23,19,19,19,19,19,19,19,19,19,19-1,7,4,20-1,7,5,20-1,7,4,20-1,7,4,24,19,23,19,23,19,23,19,15,90,11,11,7,6,20,3-1,3,6,9,3,3,10,9,10,6,6,3,10=NULL12 @n 134,2,45,11,12,7,13,17,4,46,44,25,20,33,2,8,12,4,10,1429NULL40 @n 32,11,2,28,11,2,5,30,11,2,28,11,45,6,24,3,2,2-1,27NULL35 @n 30,98,79,38,20,50,39,39,39,41-1,32-1,35-1,32-1,73-1,34-1,37-1,36-1,83-3,3,52-6,40-1,29,10,14,3,7,16,11,5,2,15,2,4,2,4,8,17,20,22,22,23-1,2-1,29,14,33-2,3,43-2,3,44-2,3,43-2,3,43-2,5,47-2,5,48-2,5,47-2,5,46-1,9,11-1,9,11-1,9,11-1,9,14-1,9,11-1,9,11-1,9,11-1,9,14-1,9,11-1,9,11-1,9,11-1,9,14-1,9,11-1,9,11-1,9,11-1,9,14-1,12,11-1,12,11-1,12,11-1,12,18-1,6,8,25,3-1,2,11,7-1,17-1,6,8,25,3-1,2,11,7-1,17-1,6,8,25,3-1,2,11,7-1,17-1,6,8,25,3-1,2,11,7-1,13-2,3,43-2,3,44-2,3,43-2,3 NULL22 @n 31,63NULL28 @n 35,8,2,31,8,2,31,8,2,31,8,45,6,24,3,2,2-1,29NULL27 @n 34,32,2,7,3,5,4,5,44NULL26 @n 40,6-1,3,11,11,2-1,15-1,18,2-1,16-1,3,38-2,15 @@NULL29 @n 34,9,2,31,9,2,31,9,2,31,9,46,6,24,3,2,2-1,22,8,3,2,2-1,22pr NULL23 @n 43,27-2,14-2,14-1,19-2,13pdKNULL31 @n 32-1,11,2,20,17-1,11,2,20,18-1,11,2,20,17-1,11,17,37,6,24,3,2,2-1,22;NULL13 @n 38-1,26,2-1,31,2,2-1,27-1,6,41,15,11,10,6,3,12,2,2-12ENULL7 @n 120-2,21,27,109,16,35,3,3,43,48,32,42,39,35,4,78,14,19,30,14,12@NULL33 @n 34,9,2,31,9,2,31,9,2,31,9,46,6,24,3,2,2-1,22,8,3,2,2-1,222NULL32 @n 51-1,5,7,19,22-1,5,7,19,16-1,6,7,20,15,13-1MPD_TAG_TRACK35 @n 602,184MPD_TAG_TITLE13 @n 68,10,4,22,8,7,7veMPD_TAG_TITLE35 @n 599,184,405 MPD_TAG_TITLE17 @n 81,3-1MPD_TAG_PERFORMER35 @n 617,184 MPD_TAG_NAME35 @n 605,184 MPD_TAG_MUSICBRAINZ_TRACKID13 @n 84,44MPD_TAG_MUSICBRAINZ_TRACKID35 @n 629,184MPD_TAG_MUSICBRAINZ_ARTISTID35 @n 623,184MPD_TAG_MUSICBRAINZ_ALBUMID35 @n 626,184 MPD_TAG_GENRE25 @n 127,6t MPD_TAG_GENRE33 @n 129,6  MPD_TAG_GENRE40 @n 134,6 MPD_TAG_GENRE29 @n 129,6 MPD_TAG_GENRE31 @n 149,6_MPD_TAG_GENRE35 @n 555,53,127,57,439-1 MPD_TAG_GENRE28 @n 127,6_ MPD_TAG_DISC35 @n 620,184 MPD_TAG_DATE35 @n 611,184MPD_TAG_COMPOSER35 @n 614,184MPD_TAG_ARTIST13 @n 67,10,4,22,8,7,7u MPD_TAG_ARTIST29 @n 45,6MPD_TAG_ARTIST40 @n 45,6,35,12 MPD_TAG_ARTIST35 @n 486,33,74,69,35,80,410,19 MPD_TAG_ARTIST31 @n 46,6 MPD_TAG_ARTIST33 @n 45,6 MPD_TAG_ARTIST28 @n 45,6 MPD_TAG_ARTIST25 @n 45,6MPD_TAG_ARTIST17 @n 71,4,5,8-1 MPD_TAG_ALBUM13 @n 83,44p MPD_TAG_ALBUM29 @n 87,6b_ MPD_TAG_ALBUM40 @n 91,6MPD_TAG_ALBUM35 @n 522,74,104,80,437-1 MPD_TAG_ALBUM31 @n 97,6 MPD_TAG_ALBUM33 @n 87,6  MPD_TAG_ALBUM28 @n 86,6 MPD_TAG_ALBUM25 @n 86,6MPD_TAG_ALBUM17 @n 72,4,15-1 MPD_STATE_PLAY13 @n 205,6 MPD_STATE_PLAY17 @n 194,6 |D$h0t  lL(dH$\@$ X d<t$`0 |tX40 h8p8lD$lL`@ tP(HL lL( message106 @n 91 mpd_connection106 @n 50,77 module106 @n 55 message62 @n 76 mpd_idle62 @n 62&mpd_connection62 @n 44,3,3,3,3,3,3,31,3,3,3,4,3,3,3 modname62 @n 87 message61 @n 112 mpd_entity_free60 @n 311 mpd_output_free60 @n 422 mpd_entity_get_playlist60 @n 299 mpd_playlist60 @n 287mpd_recv_song60 @n 330,120 mpd_recv_entity60 @n 297 mpd_entity60 @n 288 mpd_command_list_end60 @n 437 mpd_output_get_name60 @n 412mpd_connection60 @n 281,37,21,45,13,32 mpd_recv_status60 @n 440 mpd_output_get_enabled60 @n 419 mpd_output_get_id60 @n 411 mpd_playlist_get_path60 @n 302 mpd_entity_get_type60 @n 298 mpd_audio_format60 @n 55 mpd_output60 @n 402 mpd_response_finish55 @n 104 mpd_recv_output60 @n 410 mpd_recv_idle55 @n 103 mpd_recv_entity48 @n 85 mpd_port26 @n 194,11, mpd_port45 @n 86 mpd_playlist_get_last_modified60 @n 305 mpd_password26 @n 195,113 mpd_password45 @n 87 mpd_idle_name_parse23 @n 104e mpd_idle_name_parse59 @n 154 mpd_idle_name55 @n 124mpd_idle_name56 @n 35,23,24,26,26,23,24,24 mpd_idle24 @n 59 mpd_idle55 @n 97 mpd_idle56 @n 220 mpd_idle59 @n 154 mpd_hostname26 @n 193,111 mpd_hostname45 @n 85 mpd_entity_get_type48 @n 86 mpd_entity_get_song48 @n 87 mpd_entity_free48 @n 108 mpd_entity48 @n 47 mpd_connection_new27 @n 66ag mpd_connection_new48 @n 56 mpd_connection_new55 @n 56" mpd_connection_get_server_version55 @n 76 mpd_connection_get_server_error55 @n 107 mpd_connection_get_fd55 @n 168! mpd_connection_get_error_message27 @n 73,8,9d! mpd_connection_get_error_message55 @n 44,25! mpd_connection_get_error_message48 @n 63,8,8 mpd_connection_get_error27 @n 71,17 mpd_connection_get_error48 @n 61mpd_connection_get_error55 @n 60,46,7,48mpd_connection_free27 @n 74,8,9,4,443mpd_connection_free55 @n 47,23,39,81mpd_connection_free48 @n 64,8,8,32" mpd_connection_cmp_server_version55 @n 79mpd_connection5 @n 183,20,20,20,21,20,20,20s mpd_connection47 @n 176 mpd_connection27 @n 55 mpd_connection48 @n 46!mpd_connection56 @n 26,24,23,24,28,24,23,24,24mpd_connection17 @n 139,44,44,17,2642 mpd_connection55 @n 33mpd_connection24 @n 73,3,3,3,4,3,3,3 mpd_connection13 @n 199,1 mpd_command_list_begin60 @n 4348modules5 @n 36,76,65-2,11,5,15,5,15,5,16,5,15,5,15,5,15,5,15,5,4,modules59 @n 170,5-3 module_data5 @n 1354-module5 @n 32,52,7,3,12,24rec module47 @n 227 module17 @n 286 module13 @n 249,4 module24 @n 99patmodname5 @n 39,4-2,6,11,11,5,2,89,2 mod_path2 @n 63,20s) mod_path24 @n 48 mod_path5 @n 48ermod5 @n 75,2-1,3,3,2-2,3,2-3,5-1,2-3,4-1,8,2,4-2,2-1,4,7-1,4-1,4-1,2,6-1,25,4-1,2-1,12,4-1,2-1,12,4-1,2-1,13,4-1,2-1,12,4-1,2-1,12,4-1,2-1,12,4-1,2-1,12,4-1,2-17mins20 @n 25,10,8min41 @n 41,1259,11,3,7 message3 @n 79,2)message33 @n 38,21,21,21,21,21,21,16,36,7,42,38message57 @n 27,4,21,2,4,10message59 @n 50,16,22,16,27&message40 @n 35,23,18,30,18,23,18,18,36,7,42message41 @n 86,4-1,2-1,2-1,18,5-1,2,2-1,2-1,48,22,22,22,20,28,28,29,28,32,32,33,34,22,22,22,42,42,42,42,19,19,19,19,19,19,19,19,20,32,33,32,54,42,42,42,193,27,9 message51 @n 61,9message48 @n 92,10,26,12&message43 @n 35,23,18,30,18,23,18,18,36,7,42message47 @n 85,4,52!message46 @n 45,45,32,43,42,51,87,83,84&message44 @n 36,23,34,30,35,23,34,18,53,7,43message42 @n 37,24,25,24,40,7,41message36 @n 74,35,80,29,38,17&message31 @n 36,23,28,23,29,23,28,18,45,7,422)message29 @n 38,21,21,21,21,21,21,16,36,7,42,38ut&message28 @n 38,20,21,20,21,20,21,15,36,7,45message26 @n 42,11,11,20,37,601,1 message18 @n 585  message15 @n 63,37ern&message25 @n 38,20,21,20,21,20,21,15,36,7,46,message23 @n 45,5,5,5,5,15,16message14 @n 51,10,8,46,5 memset2 @n 46,39R memset26 @n 37-1, P|`,HHTdD$X4hD$pL$T(|L(|L@PX0p@|xX,ll g_key_file_get_integer106 @n 82 g_key_file_new61 @n 144 g_main_loop_quit61 @n 69 g_main_loop_unref61 @n 70 g_main_loop_new61 @n 185,65 g_log_set_default_handler61 @n 162g_key_file_free61 @n 65,126,65 g_main_loop_run61 @n 198,65 g_getenv60 @n 514,100g_free60 @n 59,4,4,4,4,4,4,4,4,4,4,4,4,4,4,11,4,4,15,7,6-1,6-1,6-1,9,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,27,6,48,4,4,4,4,4,39,4,95,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,16,4,4 g_message55 @n 54,12,11,6,63 g_message13 @n 69,36,5,77 g_message47 @n 166 g_memdup11 @n 72 g_malloc018 @n 36 g_malloc3 @n 6869 g_main_context_default12 @n 343@n g_list_next7 @n 580ha g_list_foreach36 @n 225 g_key_file_new51 @n 51 g_key_file_load_from_file51 @n 52 g_key_file_load_from_file59 @n 33 g_key_file_has_group14 @n 41D g_key_file_get_string_list23 @n 71,16 g_key_file_get_string_list59 @n 152,23g_key_file_get_string_list26 @n 73,37,61g_key_file_get_string14 @n 48,10,8,20 g_key_file_get_string59 @n 58 g_key_file_get_string15 @n 49 g_key_file_get_integer15 @n 86LEg_key_file_get_integer59 @n 62,22,16,27g_key_file_free51 @n 57,6,11,3 g_io_stream_get_output_stream36 @n 147 g_io_stream_get_output_stream46 @n 658 g_io_stream_get_input_stream36 @n 144 g_io_stream_get_input_stream46 @n 725 g_io_stream_close46 @n 732 g_io_channel_unref55 @n 170 g_io_channel_unix_new55 @n 168 g_io_add_watch55 @n 169 g_inet_socket_address_new36 @n 183 g_inet_address_to_string36 @n 180 g_hash_table_size36 @n 131g_hash_table_remove36 @n 76,28,7 g_hash_table_new_full26 @n 128 g_hash_table_new_full36 @n 298 g_hash_table_lookup36 @n 64,26 g_hash_table_lookup41 @n 1202g_hash_table_insert26 @n 143,6,6 g_hash_table_insert36 @n 150 g_hash_table_destroy26 @n 208 g_hash_table_destroy36 @n 306 g_getenv2 @n 49-3,15,2,2 g_getenv28 @n 202-1,2-1,2 g_getenv29 @n 206-1,2-1,2 g_getenv33 @n 206-1,2-1,2 g_getenv31 @n 243-1,2-1,2 g_getenv27 @n 58-1,2-1,2 g_getenv14 @n 141-1NI g_getenv42 @n 140-1,2-1,2 g_getenv48 @n 50,2,2 g_getenv44 @n 276-1,2-1,2 g_getenv51 @n 36-3 g_getenv43 @n 209-1,2-1,2 g_getenv40 @n 209-1,2-1,2 g_getenv23 @n 122-1,3 g_getenv25 @n 202-1,2-1,2 g_get_current_time7 @n 419,g_free2 @n 39,40-2,2I g_free18 @n 63-1  g_free9 @n 47-4,6g_free26 @n 86,37,5,14,6,6,29,20-3 g_free55 @n 46 g_free59 @n 123,26g_free51 @n 72-1,9-1 g_free48 @n 142,3qg_free46 @n 160,6,6,6,24,6,6,5,6,19,7,8-1,6-1,42,3,19,7,8-1,6-1,38,3,19,7,8-1,6-1,39,3,19,7,8-1,6-1,38,3,87,84,10,15-1@g_free44 @n 54,6,3,10,4-1,39-1,6,3,10,4-2,33,6,3,10,4-1,28,6,3,11,5-1$g_free43 @n 53,6,3,38-1,6,3,32,6,3,27,6,3g_free42 @n 44-1,23-2,23-1,23-1$g_free40 @n 53,6,3,38-1,6,3,32,6,3,27,6,3"g_free33 @n 53,7,3,32,7,3,32,7,3,27,7,3g_free36 @n 54,63,74,6,23-1,5-1g_free35 @n 281-2,7-14,548,3,417,3,45,3,46,3,45,3,47,3,51,3,52,3,51,3,45,3,18,3,18,3,18,3,21,3,18,3,18,3,18,3,21,3,18,3,18,3,18,3,21,3,18,3,18,3,18,3,24,3,21,3,21,3,21,3,30,3,38,4,5-1,31,3,38,4,5-1,31,3,38,4,5-1,31,3,38,4,5-1,25,3,45,3,46,3,45,32g_free31 @n 54,6,3,8-1,33,6,3,8-2,33,6,3,8-1,28,6,3,8-1"g_free29 @n 53,7,3,32,7,3,32,7,3,27,7,3"g_free28 @n 53,6,3,32,6,3,32,6,3,27,6,3 g_free11 @n 109st g_free22 @n 32,4!g_free7 @n 153-3,17,136,20,3,3,94,25-1_pl g_free23 @n 132-4g_free17 @n 95-1,79-3,61,26"g_free25 @n 53,6,3,32,6,3,32,6,3,27,6,310g_free12 @n 80-1,76,257,8,261   Pd |8ThL40@   0 dD  \  xl\$4PL X<P t D\ status106 @n 127,7status62 @n 53,3,47,3,6,3 stats62 @n 50,43 strerror61 @n 152,60,9,13,7 strstate60 @n 30,3,3,3,3,6Cstatus60 @n 52,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,265,5,3,37,11,3-1,2,5,5 strftime60 @n 152,154,46stats60 @n 339,9,3,4,4,4,4,4,4 state60 @n 28,3+status56 @n 102,8,5,4,10,7,5,2,33,7,5,2,10,7,5,2stats56 @n 30,7,5,2 source55 @n 91 stderr48 @n 152,8status47 @n 177,4,2,27 strncmp47 @n 150state47 @n 179,4,2,4stream46 @n 658,40,16,5,6,7,6,16-1strtol46 @n 98,2,484,9,2 strlen46 @n 576,7 str46 @n 659src46 @n 610,5,3Kstrcmp46 @n 83,134,54,6,4,4,4,4,4,4,57,6,4,4,4,4,4,57,6,4,4,4,4,4,58,6,4,4,4,4,4 strncmp46 @n 576strchr46 @n 94,8,7,2,20 stream45 @n 56stdout44 @n 72,64,58,53$strtol41 @n 153,442,42,42,42,323,42,42,42str41 @n 93,3,29,3'strcmp41 @n 63,110,22,22,22,264,22,22,22,715&summary17 @n 65,19,10,49,14,17,56,7-2,34,7,2e summary21 @n 57 summary18 @n 28,1824success11 @n 95,13,2success12 @n 405,54-1 submit_url7 @n 89,33,33,169,2,2,7-1,267,3$submit_source_id7 @n 85,34,31-1,344,32,29,63,80,8,4rs strstr7 @n 657 @nstrncmp7 @n 248,4,3,4,3n !strncmp34 @n 71,2,4,2,2,2,2,2,2,2,2,2,2strncmp26 @n 92,2,2,45,6,6 strncmp35 @n 268D strncmp14 @n 7538strlen7 @n 225,2-1,20,4,3,4,3,375,7 strlen35 @n 261 strlen11 @n 98gh stream12 @n 386,2 stream38 @n 112stream36 @n 53,89,2,3,8strcmp3 @n 50strcmp11 @n 174,3,2,2,2,2,21  strchr11 @n 165 strchr35 @n 245str7 @n 459,52,3,87,6 str12 @n 221stmt35 @n 231,5,601,8,3,20,3,4,3,4,3,4,3,245,4,2,6,112,8,9,4-1,11,5,11,7,9,4-2,11,5,11,7,9,4-1,11,5,11,7,9,4-1,11,5,10,10,9,4-5,11,5,10,10,9,4-6,11,5,10,10,9,4-5,11,5,11,9,9,4-5,11,5,10,5-2,3,11,5-2,3,11,5-2,3,11,5-2,3,14,5-2,3,11,5-2,3,11,5-2,3,11,5-2,3,14,5-2,3,11,5-2,3,11,5-2,3,11,5-2,3,14,5-2,3,11,5-2,3,11,5-2,3,11,5-2,3,14,8-2,3,11,8-2,3,11,8-2,3,11,8-2,3,12,58,4,20,58,4,20,58,4,20,58,4,21,8,9,4-2,11,5,9,8,9,4-3,11,5,9,8,9,4-2,11,5,9,8,9,4-2,11,5 stdout34 @n 72stderr34 @n 99-1,12,3,9status5 @n 244,11,9,11,29,11,9,11&status17 @n 184,4,5,26,9,4,5,8,4,10-4,8,4,6_mstatus13 @n 200,4,28 stats5 @n 183,11@stats17 @n 140,7,5-3,11-29state7 @n 80,36,180,2,45,24-1,40,31,29,11,15,3,30,28,17,47,78state13 @n 202,2-1,2,4168state17 @n 186,7-1,2,421, srv36 @n 125 src6 @n 87,5,438 src35 @n 220,4 src27 @n 29,5,3e_src9 @n 27,2-6,3,3 src12 @n 378,2 src38 @n 283 Jsqlite3_stmt35 @n 128,79,24,610,296,124,49,49,48,47,54,55,55,500,82,82,82,81,48,49,48 Psqlite3_step35 @n 236,99,39,39,39,370,132,324,48,49,48,50,54,55,54,505,82,82,82,75,48,49,48@n sqlite3_set_authorizer35 @n 10828sqlite3_reset35 @n 318,39,39,39,42,33,36,32,75,35,38,36,337,20,24,20Fsqlite3_prepare_v235 @n 849,161,15,19,225,48,49,48,50,54,55,54,504,82,82,82,76,48,49,48 sqlite3_open_v235 @n 1001 sqlite3_mprintf35 @n 224 sqlite3_free35 @n 226gsqlite3_finalize35 @n 860,3,174,24,6,227,5,44,5,43,5,43,5,49,5,50,5,49,5,49,5,496,5,77,5,77,5,77,5,71,5,44,5,43,5,43,5sqlite3_errmsg35 @n 320,8,14,17,8,14,17,8,14,17,8,14,20,10,6,17,13,6,17,10,6,16,52,6,17,12,6,17,15,6,17,12,6,18,53,10,24,8,58,9,9,26,42,10,16,18,38,16,6,14,6,18,6,14,6,100,22,26,23,26,22,26,22,28,26,28,27,28,26,28,26,478,23,59,23,59,23,59,23,53,23,25,24,25,23,25,23Esqlite3_column_text35 @n 1283,48-1,48,48,51,54-1,54,57,501,82,82,82,75-1,47-2,47-1,47-1]sqlite3_column_int35 @n 337,39,39,39,502,326,48,49,48,50-1,2-2,49-1,3-2,49-1,2-2,49-4,501,82,82,82,75,48,49,48 sqlite3_close35 @n 1071Psqlite3_changes35 @n 1684,21,21,21,24,21,21,21,24,21,21,21,24,21,21,21,27,24,24,24,75,82,82,82csqlite3_bind_text35 @n 325,39,39,39,43,33,3,33,31,7,3,3,3,3,3,3,3,3,3,3,3,3,33,35,3,35,35,7,3,3,3,3,3,3,3,3,3,3,3,3NV0sqlite3_bind_int35 @n 483,33,36,32,4,2,69,5,30,8,30,5,31,4,2,41 sqlite335 @n 30 \sql_stmt35 @n 841,8,8,3,3,1255,13,10,4-1,11,5,38,13,10,4-1,11,5,38,13,10,4-1,11,5,38,13,10,4-1,11,5sql35 @n 840,8-1,3,3,405,8-1,3,3,33,8-1,3,3,34,8-1,3,3,33,8-1,3,3,33,8,3,3,3,37,8,3,3,3,38,8,3,3,3,37,8,3,3,3,483,14-1,3,3,40-2,19,14-1,3,3,40-2,19,14-1,3,3,40-2,19,14-1,3,3,40-2,19,8-1,3,3,33,8-1,3,3,34,8-1,3,3,33,8-1,3,3X split26 @n 130,3-1,4,3-2,4-2,4-2,815 source_id12 @n 61,281,28source6 @n 45source36 @n 58,24,44,76,7 source11 @n 43,145(st ( h$4tH(pXLh| dHX< PdD dLhL\ ( tTLH X, < |`0tP4l cfd_r62 @n 68,4 bool61 @n 58!cfd61 @n 37,27-2,78-1,44,2-1,62,2-1 channels60 @n 132 bits60 @n 128%cfd_r59 @n 26,7,25,4,22,16,27,25,16,4,3,2 clevel57 @n 61,2-2 bool55 @n 153channel55 @n 154,14-2 atoi55 @n 56ubool49 @n 96,3-1,5,4,3,3-1,2,3-1,2,3,3,3,3,3,3,3,3,3,3,3,3-1,2-1,2-1,2-1,2-1,2-1,2-1,2-1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,38changes49 @n 149,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 artist49 @n 38,15 bool48 @n 38 atoi48 @n 56 bool47 @n 31,4at46 @n 44,45,11bool46 @n 144,40,49,87,83,84,83,58,39,3,92,11,12,12,12,12,12,12,12,12-1,11-1,11-1,11-1,11-1,11-1,11-1,10-1,11,12,12,12,12,14,14,14,14,14,14,14,14,13,13,13,13,13,13,13wcode41 @n 177,22,22,22,20,28,28,29,28,32,32,33,34,22,22,22,42,42,42,42,19,19,19,19,19,19,19,19,20,32,33,32,54,42,42,42,229code15 @n 51,10,27,10code26 @n 76,37,60nfo code23 @n 73,16code12 @n 327-1,2,73,41-1cmp41 @n 1278,6-1,2,2-cmd41 @n 39,1245,13,3-1,2,4,36,7-1,7,2,3,9,54-2cmd46 @n 634,3-1,4,3,3,3,4,4-1,3#clients36 @n 45,19,12,14,14,7,20,19,148,8 clientid36 @n 59,5,12,7,7,13-1,4,3,4 client36 @n 310,9client41 @n 76,8,18,10,25,11,17,22,22,22,22,28,28,29,28,32,32,33,32,22,22,22,22,42,42,42,42,19,19,19,19,19,19,19,19,32,33,32,32,42,42,42,42,97,42,28client45 @n 102-1,5 check12 @n 321+changes25 @n 31,5,20,8,8,5,20,8,8,5,20,8,8,5,15,8+changes33 @n 30,5,21,9,7,5,21,9,7,5,21,9,7,5,16,9+changes29 @n 30,5,21,9,7,5,21,9,7,5,21,9,7,5,16,9+changes28 @n 31,5,20,8,8,5,20,8,8,5,20,8,8,5,15,89changes38 @n 168,4,4,4,4,4,4,4,5,5,5,5,4,4,4,4,4,4,4,4,20,4,4,4changes41 @n 168,8,5,9,8,5,9,8,5,9,8,5,251,8,5,9,8,5,9,8,5,9,8,5,8,29,5,8,29,5,8,29,5,8,29,5,8,6,5,8,6,5,8,6,5,8,6,5,8,6,5,8,6,5,8,6,5,8,6,5,137,29,5,8,29,5,8,29,5,8,29,5+changes40 @n 30,3,23,8,7,3,30,8,7,3,23,8,7,3,18,8+changes43 @n 30,3,23,8,7,3,30,8,7,3,23,8,7,3,18,8changes35 @n 1669,14-1,6,14-1,6,14-1,6,14-1,9,14-1,6,14-1,6,14-1,6,14-1,9,14-1,6,14-1,6,14-1,6,14-1,9,14-1,6,14-1,6,14-1,6,14-1,9,17-1,6,17-1,6,17-1,6,17-1,6,51-1,16-1,13,51-1,16-1,13,51-1,16-1,13,51-1,16-1changes46 @n 185,7,26,653,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,6,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,5,4,5,5,4,5,5,4,5,5,4,5,5,4,5,5,4,5,5,4,5,5,4,57,4,4,5,4,4,5,4,4,5,4,4ch32 @n 35,2,4,2,52,2 cfile22 @n 27,3-2 cat7 @n 425,2-2t  calls3 @n 31,18-4callback_data12 @n 37,144,40,3,184 callback6 @n 139e $callback12 @n 36,145,40,3,81,95,7,8,8,26,14,7 bool6 @n 108,69-1 bool26 @n 31bool33 @n 28,42,42,42,37-1bool32 @n 34,6,54 bool17 @n 36bbool38 @n 131,3,4,4,4,4,4,4,4,4-1,3-1,3-1,3-1,3-1,3-1,3-1,3-1,3,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 bool28 @n 188)bool41 @n 147-1,19,22,22,22,264,22,22,22,733 bool42 @n 125-1 bool40 @n 195 bool43 @n 195 bool45 @n 92 bool44 @n 262bool35 @n 242,227-1,32-1,35-1,32-1,73,2,33,2,36,2,33,2,81,31,7,7,7,10,44,32,6-1,94,19,20,20-1,23,20-1,76,48,49,48,48,54,55,54,57,21,21,21,24-1,20-1,20-1,20-1,23-1,20-1,20-1,20-1,23,21,21,21,24,24,24,24,24,82,82,82,82,48,49,48 bool11 @n 46,49dbool15 @n 37-1,36bool13 @n 37,5,12bool29 @n 28,42,42,42,37-1_exbool12 @n 255,150 bool7 @n 239,54bl bool31 @n 228-1at bool25 @n 188body12 @n 46,31,144,169,2,61,%body17 @n 66,21,7-1,50,13,16,4,69,7,10-1 body18 @n 28,19e_ body21 @n 57ab41 @n 1277,5,6,3 atoi11 @n 186 atoi40 @n 213 atoi42 @n 144 atoi43 @n 213 atoi44 @n 2807atoi46 @n 218,57,12,4,4,4,63,8,4,4,4,63,8,4,4,4,64,8,4,4,4 atoi27 @n 62n atoi25 @n 206 atoi33 @n 210 atoi31 @n 247 atoi29 @n 210 atoi28 @n 206/assert7 @n 295-1,57,14,25,76,11-1,14-1,60-1,141,10-1t assert17 @n 68,126 *assert32 @n 51-1,53-1,47-1,61-1exassert12 @n 141,104artist6 @n 39,72,38,3artist42 @n 69,56,7-2,29artist43 @n 195,6-2,29artist44 @n 141,121,6-2,293artist46 @n 283,43,5,8-1,6,7,6-3,3-1,3-1,3-1,3-1,3-1,3-1artist41 @n 328,121,505 artist38 @n 84artist40 @n 195,6-2,29"artist7 @n 487,16,28,5,27,23,38,13,14,9 84\44`|P l< |L$xL\$xXL(8h8xT$X<lDl$LL4<  mpdcron_config106 @n 48,22 mpd_status106 @n 50,77 mpd_status_get_volume106 @n 134 mpdcron_log106 @n 78,13,19,27,2 mpd_status62 @n 53,3,47,3,6,3 mpd_status_get_total_time60 @n 105 mpd_stats_get_uptime60 @n 371 mpd_status_get_song_pos60 @n 89 mpd_status_get_consume60 @n 73 mpd_status_get_single60 @n 69 mpd_status_get_update_id60 @n 113 mpd_status_get_kbit_rate60 @n 109 mpd_status60 @n 52,332,45 mpd_status_get_volume60 @n 57Sname46 @n 49,88,80,54,6,2,2,4,4,4,4,4,57,6,2,2,4,4,4,4,57,6,2,2,4,4,4,4,58,6,4,4,4,4,4 name47 @n 228name44 @n 75,2,62,3,55,2 name55 @n 96,28?name56 @n 29,6,10,8,5,10,8,6,10,8,8,12,8,6,10,8,5,10,8,6,10,8,6,10name60 @n 400,12,3 name49 @n 37,207name7 @n 209,41,4,4,3,3,3,34,17,4,4,46,10,128-2,88-1,2,123_tname42 @n 43-1,23-1,24-1 name24 @n 64@)name41 @n 299,28,29,60,32,33,472,33,32,257,9 name38 @n 83name36 @n 34,147,37,2,6,58naddr46 @n 711-1,3-1myidle55 @n 97,6,236myexpr25 @n 32,20,4,3,3,11,20,4,3,3,11,20,4,3,3,11,15,4,3,3bm6myexpr33 @n 31,21,4,4,3,10,21,4,4,3,10,21,4,4,3,10,16,4,4,3,45myexpr44 @n 41,12,4,3,3,35,18,5,3,3,36,12,4,3,3,35,7,4,3,36myexpr29 @n 31,21,4,4,3,10,21,4,4,3,10,21,4,4,3,10,16,4,4,3 @5myexpr43 @n 40,12,4,3,3,19,18,5,3,3,19,12,4,3,3,19,7,4,3,35myexpr31 @n 41,12,4,3,3,29,12,4,3,3,30,12,4,3,3,29,7,4,3,36myexpr28 @n 32,20,4,3,3,11,20,4,3,3,11,20,4,3,3,11,15,4,3,3@n5myexpr40 @n 40,12,4,3,3,19,18,5,3,3,19,12,4,3,3,19,7,4,3,3myargv3 @n 63,5-2,2,3,7-1,4-1!myargv18 @n 31,5-1,2,2,2,2-2,4,2,3,7-1con mustload15 @n 38,16,21,16$multi12 @n 54,24,48,115,20,73-1,39,45,27 msgs_in_queue12 @n 239,2cmsg12 @n 238,3-1,2,32 msg55 @n 41,3-28mret5 @n 185,9-2,9,9-2,9,9-2,10,9-2,9,9-2,9,9-2,9,9-2,9,9-2 mpdcron_parser46 @n 81mpdcron_connection25 @n 28,41,41,41 @mpdcron_connection29 @n 28,42,42,42ovmpdcron_connection33 @n 28,42,42,4260Tmpdcron_connection38 @n 129,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4mpdcron_connection46 @n 64,81,40,49,87,83,84,83,58,39,80,15,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,11,12,12,12,12,12,14,14,14,14,14,14,14,14,13,13,13,13,13,13,13mpdcron_connection31 @n 28,51,52,51mpdcron_connection44 @n 28,57,65,57mpdcron_connection43 @n 28,41,48,41mpdcron_connection42 @n 28,24,25,24mpdcron_connection28 @n 28,41,41,41lmpdcron_connection40 @n 28,41,48,41 mpdcron_config13 @n 1692, mpdcron_config17 @n 11617 mpdcron_config47 @n 128 mpdcron_config26 @n 32-1, mpdcron_config45 @n 92 mpd_status_get_volume17 @n 237k_r mpd_status_get_update_id17 @n 281 mpd_status_get_state13 @n 204 mpd_status_get_state47 @n 183mpd_status_get_state60 @n 117,326-1 mpd_status_get_state17 @n 193 mpd_status_get_song_id60 @n 93 mpd_status_get_single17 @n 261com mpd_status_get_repeat17 @n 259 mpd_status_get_repeat60 @n 61 mpd_status_get_random17 @n 260g_i mpd_status_get_random60 @n 65 mpd_status_get_queue_version60 @n 81 mpd_status_get_queue_length60 @n 77 mpd_status_get_elapsed_time13 @n 232_ mpd_status_get_elapsed_time47 @n 210 mpd_status_get_elapsed_time60 @n 97 mpd_status_get_elapsed_time17 @n 219d mpd_status_get_elapsed_ms60 @n 101 mpd_status_get_crossfade17 @n 263 mpd_status_get_crossfade60 @n 85 mpd_status_get_consume17 @n 26222 mpd_status_get_audio_format60 @n 119mpd_status_free56 @n 119,24,47,24 mpd_status_free60 @n 446,5 mpd_status5 @n 244,20,40,20r mpd_status13 @n 2005, mpd_status56 @n 102,27,47,24 mpd_status47 @n 177 mpd_status17 @n 184,44,17,262 mpd_status24 @n 83,3,6,3 mpd_stats_get_uptime17 @n 153 mpd_stats_get_play_time17 @n 1523 mpd_stats_get_play_time60 @n 367 N L4 T 4D$x\ L0xL$  hpT$L|T4 |0  \4(<    tP,pxX,  (8 dp4PX 0 `dD 0 \xL@p 4  t@ p< fd106 @n 70,12 g_error_free106 @n 88,4 g_critical61 @n 205,7,8,6,2,5,7 false61 @n 78 g_error_free61 @n 114fmt60 @n 55,64,5,4,4 g_assert_not_reached60 @n 45 g_assert60 @n 290,33,22,41,18,27g_debug60 @n 292,33,22,41,18,27 g_critical59 @n 49 g_assert59 @n 172 g_debug59 @n 37,67 g_error_free59 @n 39,12,16,3,19,3,13,3,24,3 g_critical55 @n 57,11,40 false55 @n 103 g_assert55 @n 43,56-1,45,11-1 g_debug55 @n 159 false51 @n 69 g_error_free51 @n 56,6,9g_build_filename51 @n 39,7,35Pexpr49 @n 125,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 fprintf48 @n 152,8false48 @n 58,7,8,8,8,7,43,8 g_error_free48 @n 93,10,26,12 fd47 @n 128,6 g_critical47 @n 140,80false47 @n 123,16,56 g_assert47 @n 44,22,115g_debug47 @n 48,23,7,21,32 g_error_free47 @n 86,4,52expr46 @n 775,5,7,5,7,5,7,5,7,5,7,5,7,5,7,5,7,5,7,5,7,5,7,5,7,5,7,5,7,4,7,5,6,6,6,6,6,6,6,6,6,4,4,6,4,4,6,4,4,6,4,4,6,4,4,6,4,4,6,4,4,6,4,4,6,4,3,6,4,3,6,4,3,6,4,3,6,7,6,7,6,7,6,7 g_data_input_stream_read_line46 @n 68false46 @n 155,12,6,6,18,12,6,11,19,16,7,42,7,15,16,7,38,7,15,16,7,39,7,15,16,7,38,7,13,9,72,108,12,12,12,12,12,12,12,12,12,12,12,12,12,12,11,12,12,12,12,12,14,14,14,14,14,14,14,14,13,13,13,13,13,13,13,13 g_assert_not_reached46 @n 181,49 g_assert46 @n 151,40-1,86,4,4,4,4,4,4,63,4,4,4,4,4,63,4,4,4,4,4,64,4,4,4,4,4,213-1,11-1,11-1,11-1,11-1,11-1,11-1,11-1,11-1,11-1,11-1,11-1,11-1,11-1,11-1,11,11-1,11-1,11-1,11-1,11-1,11-3,11-3,11-3,11-3,11-3,11-3,11-3,11-3,11-2,11-2,11-2,11-2,11-2,11-2,11-2,11-2% g_data_input_stream_set_newline_type46 @n 726 g_error_free46 @n 751 g_data_input_stream_new46 @n 725 fd45 @n 92.expr44 @n 28,5-1,51,5-1,59,5-1,51,5-1,48,39,2,2,2fputc44 @n 72,64,58,53 g_error_free44 @n 337.expr43 @n 28,4-1,36,4-1,43,4-1,36,4-1,31,39,2,2,2 g_error_free43 @n 269.expr42 @n 28,4,3,17,4,3,18,4,3,17,4,3,17,39,2,2,2 g_assert42 @n 32,24,25,24 g_error_free42 @n 199fmt41 @n 84,7,11,4-1,5,11,14,4-1false41 @n 157,1151,11,6,6 zg_assert41 @n 88,29-1,53,22,22,22,21,28,28,29,28,32,32,33,33,22,22,22,23,42,42,42,40,19,19,19,19,19,19,19,19,32,33,32,34,42,42,42g_debug41 @n 78,15,32 |g_error_free41 @n 178,22,22,22,20,28,28,29,28,32,32,33,34,22,22,22,42,42,42,42,19,19,19,19,19,19,19,19,20,32,33,32,54,42,42,42,193,28,9.expr40 @n 28,4-1,36,4-1,43,4-1,36,4-1,31,39,2,2,2 g_error_free40 @n 269Pexpr38 @n 136,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 fmt38 @n 322 g_direct_equal36 @n 298$ g_data_input_stream_read_line_async36 @n 120,42' g_buffered_output_stream_set_auto_grow36 @n 148g_debug36 @n 103,12,21,45,14,67,14% g_data_input_stream_set_newline_type36 @n 145 g_error_free36 @n 75,35,80,29,38,17 g_buffered_output_stream_new36 @n 147 g_data_input_stream_new36 @n 144% g_data_input_stream_read_line_finish36 @n 99 g_direct_hash36 @n 298flags35 @n 984,7,6,2,2 expr35 @n 838,8,2,20,3,4,3,4,3,4,3,364,9,3,36,9,3,37,9,3,36,9,3,36,9,5,40,9,5,41,9,5,40,9,5,43,5,3,13,5,3,13,5,3,13,5,3,16,5,3,13,5,3,13,5,3,13,5,3,16,5,3,13,5,3,13,5,3,13,5,3,16,5,3,13,5,3,13,5,3,13,5,3,16,5,6,13,5,6,13,5,6,13,5,6,13,12,5,65,12,5,65,12,5,65,12,5,65,8,3,37,8,3,38,8,3,37,8,3taTfalse35 @n 249,231,10,6,17,13,6,17,10,6,16,52,6,17,12,6,17,15,6,17,12,6,18,53,10,25,8,57,9,9,26,7,26,10,10,6,10,4,14,37,16,6,14,6,18,6,14,6,24,3,4,4,3,4,6,3,4,7,3,4,32,22,26,23,26,22,26,22,28,26,28,27,28,26,28,26,22,21,21,21,24,21,21,21,24,21,21,21,24,21,21,21,22,5,19,5,19,5,19,5,26,7,23,9,43,7,23,9,43,7,23,9,43,7,23,9,44,23,25,24,25,23,25,231$g_free5 @n 52,4,7,4-1,13,6-1,7-1,9,2,21,3g_free14 @n 52,10,8,7,5,13-5,50PDg_free3 @n 54,28-1,4-1 g_file_test5 @n 50,11 g_file_test22 @n 34_A g_file_test35 @n 989 g_error_free3 @n 8414 g_error_free26 @n 43,11,11,14,6,31,6,54,6 g_error_free29 @n 266,38 g_error_free31 @n 303 g_error_free33 @n 266,38 g_error_free28 @n 265 g_error_free18 @n 59R g_error_free15 @n 55,9,28,944 g_error_free25 @n 266 g_error_free14 @n 53,10,8,45,5150 g_error_free23 @n 46,5,5,5,5,10,5,11,5-1 g_debug3 @n 53,19,9 @g_debug7 @n 317,4,4,186-1,89-1filg_debug17 @n 74,5,31Sg_debug13 @n 76,41,23g_debug11 @n 101,10,4g_debug5 @n 43,2,4,2,9,2,49n g_critical2 @n 54 g_critical23 @n 45,5,5,5,5,14,16 g_critical12 @n 329,7 g_critical26 @n 42,11,11,18,37,60 g_critical14 @n 50,10,8,47,5,17LS g_critical13 @n 242ON g_compute_checksum_for_string7 @n 428 g_compute_checksum_for_string14 @n 81g_build_filename2 @n 38,14,7,4 63 g_build_filename5 @n 48,11ng g_build_filename23 @n 123 g_build_filename26 @n 47 g_build_filename22 @n 317g_build_filename3 @n 69 @ g_assert13 @n 65,34,106UN g_assert35 @n 315,39,39,39,41-1,32-1,35-1,32-1,73-1,34-1,37-1,36-1,83-3,55-6,40-1,39,56,37,17,20,22,22,23-1,79-2,46-2,47-2,46-2,46-2,52-2,53-2,52-2,51-1,20-1,20-1,20-1,23-1,20-1,20-1,20-1,23-1,20-1,20-1,20-1,23-1,20-1,20-1,20-1,23-1,23-1,23-1,23-1,30-1,81-1,81-1,81-1,77-2,46-2,47-2,46-2 g_assert17 @n 147,41,44,17,26g_ascii_isspace32 @n 71,54,73 g_ascii_isalpha32 @n 37 g_ascii_isalnum32 @n 43 fputc34 @n 115 fprintf11 @n 39 fprintf34 @n 45,54 fopen11 @n 53,92 fmt34 @n 104,7-1 file_config6 @n 80rd_ file_config18 @n 35,3-5,6-2G file_config21 @n 42 file_config22 @n 30-1 &file_config23 @n 32,9,3,5,5,5,5,7,42,5-1,3-3,7-6 file_config13 @n 176,30,2 file_config17 @n 77,72,41,44,17,26PDA "file_config14 @n 32,78-1,3,5,5-1,3,4,9-1,8-23 file_config12 @n 435-1DEF file6 @n 1521file11 @n 37,2,100,6-1,12,33file7 @n 624,16,7,10) fgets11 @n 158r_s!fds12 @n 64,56,13-1,2-1,6,11,14,123 fd_set12 @n 88,29 fd6 @n 16323, fd17 @n 116,6fd23 @n 35,9,5,5,5,5,7,16 fd21 @n 51BEG!fd14 @n 35,6,7,10,8,20,18,10,5,8,4CES fd13 @n 169,3fd26 @n 32,9,11,11,10,37,61fd12 @n 88,4,2,3,2,3,2,34,23-1,3Rfd15 @n 38,11,26,11IR fclose11 @n 62,129false7 @n 270,203,229 false29 @n 311,2false11 @n 51,6,17ic  false13 @n 164,53 false15 @n 65,37Nfalse26 @n 41,3,8,3,8,3,21,37,60false17 @n 53,65,903, false33 @n 273,2false23 @n 44,5,5,5,5 false14 @n 114,5Nfalse12 @n 269,111.expr25 @n 28,7-1,33,7-1,33,7-1,34,6-1,28,39,2,2,2.expr33 @n 28,6-1,35,6-1,35,6-1,35,6-1,30,39,2,2,2.expr28 @n 28,7-1,33,7-1,33,7-1,34,6-1,28,39,2,2,2.expr31 @n 28,5-1,45,5-1,46,5-1,45,5-1,40,39,2,2,2 :< p \$ L  \8|Xd4x 0  <|P,D X tT0( 0  ` P<T , <hxX4`@ X8 tP L` HxL P,  |P,, T  l@ g_setenv61 @n 131-2,6 g_printerr61 @n 112 vg_setenv60 @n 58,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,2,3,4,4,15,7,6,7,7,10,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,27,6,45,3,4,4,4,4,4,39,4 3g_strdup60 @n 146,7,5,7,7,11,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6g_strdup_printf60 @n 57,4,4,4,4,4,4,4,4,4,4,4,4,4,4,11,4,4,15,7,5-1,6-1,6-1,10,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,27,6,48,4,4,4,4,4,39,4,95,8,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,16,8 g_setenv59 @n 122,26 g_strfreev59 @n 160,18 g_strdup_printf59 @n 121,26 g_return_if_fail57 @n 31 g_strescape55 @n 44 g_source_remove55 @n 184,3 g_strdup51 @n 37 g_printerr51 @n 41,19,10 g_printerr48 @n 57,5,8,8,12,10,28,12g_string_append_c46 @n 621-1,20,3,6,4 g_quark_from_static_string46 @n 57+g_slist_prepend46 @n 253,20,30,37,20,26,37,20,26,38,20,26 !g_strdup46 @n 279,4,20,63,20,63,21,63,20 g_socket_client_new46 @n 690 g_socket_client_connect46 @n 698,16 g_string_append46 @n 648 g_return_val_if_fail46 @n 615 Ag_set_error46 @n 70,93,6,6,30,6,11,34,7,43,37,7,39,37,7,40,37,7,39,21,9,117 "g_slist_next44 @n 66,3,2,59,3,2,53,3,2,47,4,2g_strdup_printf44 @n 53,63,59,52 g_slist_free44 @n 76,4,60,5,53,4,50,4 g_printerr44 @n 272,64g_strdup_printf43 @n 52,47,42,36 g_printerr43 @n 205,63,8 g_slist_next42 @n 41,24,25,24 g_slist_free42 @n 47,25,24,24 g_printerr42 @n 136,62,10 ,g_slist_next41 @n 268,28,28,29,28,32,32,33,440,32,33,32 g_strerror41 @n 599,42,42,42,323,42,42,42 g_string_append_c41 @n 94,32 ,g_slist_free41 @n 274,28,29,28,32,32,33,32,440,33,32,32 g_string_append_vprintf41 @n 91,32g_strdup_printf40 @n 52,47,42,36 g_printerr40 @n 205,63,8 g_socket_service_start36 @n 297 g_resolver_lookup_by_name_async36 @n 288 g_socket_service_stop36 @n 304 g_strdup36 @n 284 g_socket_listener_add_inet_port36 @n 270 g_signal_connect36 @n 296 g_socket_listener_add_address36 @n 185,65 g_socket_service_new36 @n 235 g_resolver_free_addresses36 @n 228! g_resolver_lookup_by_name_finish36 @n 213 g_resolver_get_default36 @n 287 g_slist_next35 @n 2170,82,82,82 g_quark_from_static_string35 @n 2159g_slist_prepend35 @n 1284,49,48,48,54,55,54,54,501,82,82,82,76,49,48,48xi g_strfreev35 @n 272,8,934 7g_strdup35 @n 225,1058,48-1,48,48,51,54-1,54,57,822,48-1,48,48ts g_string_append_printf7 @n 166@n  g_string_append_printf35 @n 270 g_string_append_len12 @n 390t g_string_append_c7 @n 162,6 g_string_append_c27 @n 43,3 g_string_append7 @n 163,9 g_string_append27 @n 41 g_strfreev23 @n 115,23LBU g_strfreev26 @n 103,35,25,3,41 g_strerror11 @n 56,96g_strdup_printf2 @n 37_pl g_strdup_printf7 @n 420r  g_strdup_printf11 @n 116g_strdup_printf5 @n 44,41g_strdup_printf3 @n 5152g_strdup_printf17 @n 87,71,79,17,26@n g_strdup_printf22 @n 30,7 g_strdup_printf20 @n 431g_strdup_printf28 @n 52,41,41,36g_strdup_printf29 @n 52,42,42,37g_strdup_printf31 @n 53,51,52,46g_strdup_printf25 @n 52,41,41,36g_strdup_printf35 @n 848,420,48,49,48,48,54,55,54,52,21,21,21,24,21,21,21,24,21,21,21,24,21,21,21,27,24,24,24,31,44,3,35,44,3,35,44,3,35,44,3,29,48,49,48g_strdup_printf33 @n 52,42,42,37g_strdup_printf18 @n 39,2,2,2,6_ g_strdup2 @n 50ue g_strdup12 @n 438 g_strdup7 @n 280,256-3,112-3,2 g_strdup14 @n 47,29,66_FA g_strdup23 @n 125 g_strdup26 @n 189,4-2 g_strdup18 @n 37,9-1L g_strdup11 @n 105,29,42,2,2,2 g_strdup9 @n 29-4 g_strconcat7 @n 427 g_strchug11 @n 161ubm g_strchug32 @n 75,54,79 g_strchomp11 @n 171\ g_str_hash26 @n 128 g_str_equal26 @n 128g_spawn_async3 @n 75 g_spawn_async18 @n 56 g_source_unref12 @n 369NS g_source_remove_poll12 @n 146 g_source_remove7 @n 149,2 g_source_remove12 @n 370i g_source_remove13 @n 193N g_source_new12 @n 340 g_source_attach12 @n 342f g_source_add_poll12 @n 151,16 g_slist_remove_link5 @n 15490g_slist_remove12 @n 143,36,49,233 g_slist_prepend5 @n 112 g_slist_prepend7 @n 68266 g_slist_prepend14 @n 128,4INIg_slist_prepend12 @n 154,14,287T_ g_slist_next5 @n 190,20,20,21,20,20,20,20 g_slist_next31 @n 66,51,52,46 g_slist_next12 @n 204 g_slist_free5 @n 156,221, g_slist_free14 @n 152 g_slist_free35 @n 2187,82,82,82 g_slist_free12 @n 361 g_slist_free7 @n 747 g_slist_free31 @n 74,52,51,46 g_slist_foreach5 @n 177,2 g_slist_foreach14 @n 151Dg_slist_foreach7 @n 543,120,27,44,12 g_slist_foreach12 @n 360 g_setenv3 @n 5229 g_set_error15 @n 61,37RON g_set_error35 @n 246,73,8,14,17,8,14,17,8,14,17,8,14,20,10,6,17,13,6,17,10,6,16,52,6,17,12,6,17,15,6,17,12,6,18,53,10,24,8,58,9,9,26,6,27,9,10,16,18,37,17,6,14,6,18,6,14,6,19,81,22,26,23,26,22,26,22,28,26,28,27,28,26,28,26,478,23,59,23,59,23,59,23,53,23,25,24,25,23,25,23 g_set_error32 @n 62,19,35,19,30,20,15 g_return_val_if_fail27 @n 34 g_queue_push_tail7 @n 617 g_queue_push_tail11 @n 72 g_queue_pop_head7 @n 356 g_queue_peek_head_link7 @n 578ck g_queue_new7 @n 126 g_queue_is_empty7 @n 558,150 g_queue_is_empty11 @n 50 g_queue_get_length7 @n 676,48 g_queue_free7 @n 144 g_queue_foreach7 @n 143ob g_queue_foreach11 @n 60ay g_quark_from_static_string15 @n 3383, g_quark_from_static_string32 @n 31 g_printerr25 @n 198,67,8n g_printerr33 @n 202,63,38 g_printerr29 @n 202,63,38 g_printerr28 @n 198,66,8 g_printerr31 @n 239,63x  , \X 4 h LD0( hH$  |T40 @pL$ t( X<D Tp<`  0X  X< <p \ dX(h \0X 8 @dH l`@ D $H D 8| d@` `$ <h  result12 @n 21BCz ponse7 @n 288,3,16,9,4,4,38,16,2,3 #requests12 @n 67,112,13-1,/94,6lrequest12 @n 75,2-4,96,2,2-1,11-1,11,2-1,9,4,2-1,4-1,14,2,2,103,2,36,2,2,2,8,5-1,4-2,5,2-1,6-5,3,2-3,3,2-2,5,2,6-1,2,record6 @n 87,5,5,6,3,3 @record9 @n 27,11,7,9,6 *c record11 @n 67g2 reconnect24 @n 552rating28 @n 29,7,20,14,7,20,14,7,20,14,7,15,13,39,2,2,2e_rating31 @n 70,51,52,4661 queue_length7 @n 604: queue6 @n 115,3mpqueue11 @n 46,4,10,7,5,65,38,18 0queue7 @n LOG_INFO106 @n 137,27 LOG_NOTICE106 @n 78,321-1 G_GNUC_UNUSED106 @n 70,57! G_KEY_FILE_ERROR_GROUP_NOT_FOUND106 @n 85 LOG_ERR106 @n 915 G_KEY_FILE_ERROR_KEY_NOT_FOUND106 @n 86ri HAVE_GMODULE62 @n 70,15ri LOG_DEBUG61 @n 127 G_GNUC_UNUSED61 @n 56 G_OPTION_ARG_NONE61 @n 41-2 HAVE_GMODULE61 @n 55,5,127,65 MPD_STATE_PAUSE60 @n 41,403re MPD_ENTITY_TYPE_PLAYLIST60 @n 298G_KEY_FILE_ERROR_INVALID_VALUE59 @n 65,22,16,27da G_KEY_FILE_NONE59 @n 33 HAVE_GMODULE59 @n 166 G_KEY_FILE_ERROR_NOT_FOUND59 @n 36 G_FILE_ERROR_NOENT59 @n 35por HAVE_CONFIG_H58 @n 23 LOG_INFO57 @n 435 LOG_DEBUG57 @n 47 LOG_NOTICE57 @n 41 G_LOG_LEVEL_DEBUG57 @n 45,20  G_LOG_LEVEL_INFO57 @n 42,22,5 G_LOG_LEVEL_CRITICAL57 @n 341 LOG_CRIT57 @n 35 G_LOG_LEVEL_WARNING57 @n 37la LOG_WARNING57 @n 38 G_LOG_LEVEL_MESSAGE57 @n 40,23pid MPD_IDLE_DATABASE56 @n 35,188 MPD_IDLE_OUTPUT56 @n 157,76MPD_IDLE_PLAYER56 @n 108,119 G_GNUC_UNUSED56 @n 73MPD_IDLE_OPTIONS56 @n 181,24,30 MPD_IDLE_QUEUE56 @n 82,147 MPD_IDLE_UPDATE56 @n 237 HAVE_GMODULE56 @n 41,23,24,26,26,23,24,24 MPD_IDLE_MIXER56 @n 134,97 MPD_IDLE_STORED_PLAYLIST56 @n 58,167G_GNUC_UNUSED55 @n 52,39-2MPD_ERROR_SUCCESS55 @n 60,53,48 G_IO_IN55 @n 169 MPD_ERROR_SERVER55 @n 106 MPD_SERVER_ERROR_UNKNOWN_CMD55 @n 107 G_KEY_FILE_NONE51 @n 52 G_KEY_FILE_ERROR_NOT_FOUND51 @n 55 G_FILE_ERROR_NOENT51 @n 54!GSList49 @n 125,3,3,3,3,3,3,3,75,3,3,3 MPD_ENTITY_TYPE_SONG48 @n 86 G_OPTION_ARG_NONE48 @n 34 MPD_ERROR_SUCCESS48 @n 61 G_OPTION_ARG_FILENAME48 @n 33 MPD_STATE_PAUSE47 @n 185 G_GNUC_UNUSED47 @n 176 GTimer47 @n 33 G_IO_STREAM46 @n 658,67,7 G_SOCKET_CONNECTABLE46 @n 699 GSocketAddress46 @n 694 G_DATA_STREAM_NEWLINE_TYPE_LF46 @n 726  HAVE_GIO_UNIX46 @n 36,6579GSList46 @n 234,87,83,84,287,12,12,12,12,12,12,12,267,13,13,13 GSocketConnectable46 @n 711 GString46 @n 613,21 HAVE_CONFIG_H45 @n 23G_OPTION_ARG_NONE44 @n 317,2,2.LOG_ERR44 @n 35,12,11,34,12,5,13,35,12,11,34,18,54,7GSList44 @n 30,39,18,46,19,39,18,35G_OPTION_ARG_NONE43 @n 250,2,2.LOG_ERR43 @n 34,12,11,18,12,5,13,18,12,11,18,18,37,7G_OPTION_ARG_NONE42 @n 181,2,2LOG_ERR42 @n 36,24,25,24,41,7GSList42 @n 30,24,25,24G_GNUC_UNUSED41 @n 50,2-1,93,1054 INT_MIN41 @n 606,42,42,42,323,42,42,420INT_MAX41 @n 606,3,39,3,39,3,39,3,320,3,39,3,39,3,39,3,GSList41 @n 256,28,28,29,28,32,32,33,440,32,33,32 G_N_ELEMENTS41 @n 1394,9 GString41 @n 86,29G_OPTION_ARG_NONE40 @n 250,2,2 .LOG_ERR40 @n 34,12,11,18,12,5,13,18,12,11,18,18,37,7 MPD_STATE_PAUSE13 @n 207 MPD_STATE_PAUSE17 @n 196e MPD_IDLE_UPDATE17 @n 2772 MPD_IDLE_UPDATE23 @n 120c MPD_IDLE_STORED_PLAYLIST23 @n 107 MPD_IDLE_QUEUE23 @n 1082- MPD_IDLE_PLAYER17 @n 190 MPD_IDLE_PLAYER23 @n 119 MPD_IDLE_OUTPUT23 @n 109 MPD_IDLE_OPTIONS17 @n 251 MPD_IDLE_OPTIONS23 @n 120 MPD_IDLE_MIXER17 @n 234_a MPD_IDLE_MIXER23 @n 120nt MPD_IDLE_DATABASE17 @n 149han MPD_IDLE_DATABASE23 @n 119 MPD_ERROR_SUCCESS27 @n 71,174 LOG_WARNING27 @n 94,1)LOG_ERR25 @n 37,9,11,21,9,11,21,9,11,21,15,37,7,LOG_ERR31 @n 35,12,11,28,12,11,29,12,11,28,18,46,7cmd,LOG_ERR29 @n 36,10,11,21,10,11,21,10,11,21,16,38,7nt,LOG_ERR33 @n 36,10,11,21,10,11,21,10,11,21,16,38,7LOG_ERR27 @n 67,5,8,9)LOG_ERR28 @n 37,9,11,21,9,11,21,9,11,21,15,37,7,7 LOG_DEBUG34 @n 33LIBDIR5 @n 59 HAVE_GMODULE2 @n 61,21 HAVE_GIO_UNIX36 @n 29,216HAVE_CONFIG_H6 @n 26 HAVE_CONFIG_H21 @n 23 G_SPAWN_SEARCH_PATH18 @n 5641G_SPAWN_LEAVE_DESCRIPTORS_OPEN3 @n 76G_SPAWN_ERROR_NOEXEC3 @n 784G_SPAWN_ERROR_NOENT3 @n 78e6G_SPAWN_CHILD_INHERITS_STDIN3 @n 76 G_SOCKET_TYPE_STREAM36 @n 186,66e G_SOCKET_PROTOCOL_TCP36 @n 186com G_SOCKET_PROTOCOL_DEFAULT36 @n 2534,4G_SOCKET_LISTENER36 @n 185,65,20 G_SOCKET_ADDRESS36 @n 251 G_RESOLVER36 @n 209G_PRIORITY_DEFAULT36 @n 121,41,1593,3G_OPTION_ARG_NONE25 @n 243,2,2ongG_OPTION_ARG_NONE29 @n 247,2,2,34,2,2G_OPTION_ARG_NONE31 @n 284,2,2G_OPTION_ARG_NONE33 @n 247,2,2,34,2,2G_OPTION_ARG_NONE28 @n 243,2,22,1 G_OBJECT36 @n 155 G_N_ELEMENTS35 @n 1009,33,17,6cmdG_MODULE_SUFFIX5 @n 44G_MODULE_BIND_LOCAL5 @n 84 G_KEY_FILE_ERROR_KEY_NOT_FOUND15 @n 53,37G_KEY_FILE_ERROR_KEY_NOT_FOUND26 @n 78,37,601 G_KEY_FILE_ERROR_KEY_NOT_FOUND23 @n 75,16! G_KEY_FILE_ERROR_GROUP_NOT_FOUND15 @n 52,37!G_KEY_FILE_ERROR_GROUP_NOT_FOUND26 @n 77,37,60-1,! G_KEY_FILE_ERROR_GROUP_NOT_FOUND23 @n 74,16 G_IO_STREAM36 @n 142 G_IO_OUT12 @n 986 G_IO_IN12 @n 93 G_IO_HUP12 @n 93,10t G_IO_ERR12 @n 93,5,52G_GNUC_UNUSED7 @n 132,534,50,21*c G_GNUC_UNUSED14 @n 91 G_GNUC_UNUSED15 @n 36,37lG_GNUC_UNUSED36 @n 58,24,43-1 G_GNUC_UNUSED35 @n 218upG_GNUC_UNUSED8 @n 28 G_GNUC_UNUSED17 @n 116,23,44,44,17,26G_GNUC_UNUSED12 @n 279,10,15-2,42 G_GNUC_UNUSED13 @n 169,30 G_GNUC_NORETURN34 @n 41@n G_GNUC_CONST32 @n 27  G_FILE_TEST_EXISTS5 @n 50,11 G_FILE_TEST_EXISTS35 @n 9891  G_FILE_TEST_EXISTS22 @n 34_cl G_DATA_STREAM_NEWLINE_TYPE_LF36 @n 145121 G_CHECKSUM_MD57 @n 428se_ G_CHECKSUM_MD514 @n 81 G_CALLBACK36 @n 2966, G_BUFFERED_OUTPUT_STREAM36 @n 148 GTimer13 @n 3917  GTimer17 @n 3841 GTimeVal7 @n 417 GTimeVal11 @n 96dGString7 @n 159,18,5,5,249,55,62@ GString27 @n 321, GString35 @n 259 GString12 @n 461 GSourceFuncs12 @n 319 GSourceFunc12 @n 3051GSource12 @n 58,221,10,15 GSocketService36 @n 44,81 GSocketConnection36 @n 125_so GSocketConnection38 @n 112 79 GSocketClient38 @n 107_ta GSocketAddress36 @n 173,69ig#GSList5 @n 36,99,51,20,20,21,20,20,20,20@GSList12 @n 64,3,53,84,87 L 4 $PDd $ pT0 8 dH,( x xX4 l tT< T< \ hL4< xddP T L pP0|8\<  l   X` 0 l d D@$ x@  d8 FzFz  #$%&'()*+,-./0TW(+Y\(+^a$'<?QTfi{~ 58VY# ' , - L O u y ~  ) ,    )78ALMVabkvw "-.:LMS[twxz    ) 0 1 B C I Q j m n p { ((9OPRRT`vwyy{679./BL_`r|./EOde{C]^py|    & 3 9 S T f o r x   / 0 C  "volume106 @n 129,5,3,3-1,2 userdata62 @n 77 true61 @n 86time_t60 @n 143,143,56Ktag60 @n 141,41,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 true60 @n 434t60 @n 143,8-1,134,19-1,36,9-1 timeout59 @n 45,82,9,5,3,3 userdata57 @n 59,2 timeout55 @n 55-1version55 @n 32,44,2 tags49 @n 40,27 track49 @n 56 userdata49 @n 107 xAuth49 @n 106 uri49 @n 50!values49 @n 125,3,3,3,3,3,3,3,75,3,3,3 title49 @n 55tag49 @n 197,3,3,3,3,3,3,3 true48 @n 114,25timer47 @n 33,13,22,29,11,7,44,10true47 @n 84,32,23 was_paused47 @n 31,83,2,7,69,3tags46 @n 303,83,83,84 va_start46 @n 673test46 @n 574,10-1,7-3 uri46 @n 533 va_end46 @n 675wvalues46 @n 234,19,20,48,19,20,44,19,20,45,19,20,248,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,260,4,4,5,4,4,5,4,4,5,4,4Mvalue46 @n 49,89,80,57,4,4,4,4,4,4,4,59,4,4,4,4,4,4,59,4,4,4,4,4,5,59,4,4,4,4,4,4tmp46 @n 573,10-29tag46 @n 1014,4,3,7,4,3,7,4,3,7,4,3,7,4,3,7,4,3,7,4,3,7,4,3true46 @n 161,42,51,87,83,84,95,61 va_list46 @n 630,41 va_arg46 @n 640u46 @n 51,37-2,8,2,22,15-1,26-1,41-1,10-1,39-1,13,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,41-1,13,4,2,2,2,2,2,2,2,2,2,2,2,2,41-1,13,4,2,2,2,2,2,2,2,2,2,2,2,3,41-1,13,4,2,2,2,2,2,2,2,2,2,2,2,2version46 @n 584,9,2,2,2-1tags44 @n 69,7,57,7,51,7,44,2,8 uri44 @n 250,3?values44 @n 30,2,2,23,9,14,7,2,2,30,9,15,7,2,2,23,9,14,7,2,2,18,9,16"walk244 @n 69-2,2,60-2,2,54-2,2,49-2,2&walk44 @n 30,36-1,20,43-1,21,36-1,20,31-12tag43 @n 28,5,23,13,5,30,13,5,23,13,5,18,13,39,2,2,2 uri42 @n 116-1/values42 @n 30,4-1,6,6,7,4-1,6,7,7,4-1,6,6,7,4-1,6,6&walk42 @n 30,11-1,12,11-1,13,11-1,12,11-1tags41 @n 922-2,31-2,30-2,30-2 view41 @n 53 userdata41 @n 49,6 va_start41 @n 106,35 unknown41 @n 1342,3 what41 @n 49,8uri41 @n 271,113,537 va_end41 @n 108,35 values41 @n 256,5-1,6,6,10,5-1,6,6,10,5-1,6,7,10,5-1,6,6,10,5-1,6,10,10,5-1,6,10,10,5-1,6,11,10,5-1,6,10,418,5-1,6,10,10,5-1,6,11,10,5-1,6,10,10,5-1,6,10value41 @n 150,3-1,6 value_r41 @n 148,12true41 @n 161,1151,22va_list41 @n 84,20,9,26,4 xAuth35 @n 1076,6word32 @n 49,5,37,12,5,37,6,5,53twfds12 @n 88,9,2,18,6,3,13,23 was_paused13 @n 37,118,2,7,50,333 was_paused17 @n 36,8,2,7,65,87,3 Nwalk5 @n 186,4-1,4,11,4-1,4,11,4-1,4,12,4-1,4,11,4-1,4,11,4-1,4,11,4-1,4,11,4-1,4(walk35 @n 2117,53-1,28,53-1,28,53-1,28,53-1hwalk41 @n 256,12-1,15,12-1,15,12-1,16,12-1,15,12-1,19,12-1,19,12-1,20,12-1,427,12-1,19,12-1,20,12-1,19,12-1&walk31 @n 30,36-1,14,36-1,15,36-1,14,31-1 vfprintf34 @n 112version35 @n 945,11,9,3NV version38 @n 97 verbosity34 @n 33,75-values23 @n 38,49,15-2,2,5,4;values31 @n 30,2,2,23,9,8,7,2,2,23,9,9,7,2,2,23,9,8,7,2,2,18,9,8 qvalues35 @n 1256,10,18,20,10,19,20,10,18,20,10,18,20,10,24,20,10,25,20,10,24,20,10,24,795,9,19,20,9,20,20,9,19,20,9,19!values38 @n 136,4,4,4,4,4,4,4,88,4,4,4)values26 @n 34,39,17-2,2,2,5,2,7,19,3-1,4,25,4@n value_r15 @n 39,5,25,7,5,25,1 value11 @n 159,6-1,3,3,4,2,2,2,2,2-1value15 @n 41,8,7,13,9,8,7,13val7 @n 160,10-1,6,2,3,2,3,26 va_start34 @n 111 va_list34 @n 106 va_end34 @n 113tt username6 @n 70 username7 @n 4503 username14 @n 58,398, userdata5 @n 117,6 userdata36 @n 126,43,9,24,8 userdata35 @n 1078,4user_r5 @n 39,14,11it user_data7 @n 132,387,3,90,3,51,50,21 user_data14 @n 91 user_data11 @n 34,3 * user_data12 @n 306,42user5 @n 30,48,64,5,5,9bm url6 @n 69,69 url12 @n 399,454, url14 @n 48,487url7 @n 436,9-7,7,2** uri31 @n 219-1364uri35 @n 290,138,14,986,217,967  uri38 @n 73 urgency18 @n 38-1 urgency23 @n 64,72eve urgency21 @n 38_ruptime17 @n 144,9,17,6,7 unlink36 @n 247 type18 @n 42-1tpu type23 @n 59,76@n type21 @n 37q tv_sec7 @n 420_en tv_sec11 @n 115-1true7 @n 251,210,55,93ini true11 @n 64,79`true12 @n 77,195,39,120,9 true26 @n 197true15 @n 46,24,13,24 true33 @n 311,2n  true17 @n 46itrue35 @n 251,248,36,33,74,35,38,35,80,34,75,32,82,35,23,20,24,20,17,53,55,49,48,48,54,55,54,54,24,21,21,21,24,21,21,21,24,21,21,21,24,21,21,21,27,24,24,24,60,20,62,20,62,20,62,20,50,49,48,48 true29 @n 273,2gs true13 @n 157track6 @n 40,71,38,3t track35 @n 294!track7 @n 488,16,27,6,27,23,37,20,8,9track11 @n 42,27,1092track9 @n 30,18,15mpd tmp7 @n 356-1 title35 @n 293timestr7 @n 437,4-1,9,3er timestamp7 @n 423,4ru!timer13 @n 39,32,3,27,30,7,11,7,22,14timer17 @n 38,7,14,10,39,17,10n 8 timeout_12 @n 279 timeout18 @n 40-1 timeout23 @n 54,80eve timeout24 @n 54n  timeout21 @n 363 time_val7 @n 417,2-1 time_val11 @n 96,12,7-1,4 time_t17 @n 1427  time27 @n 626,30 time6 @n 43,111,1time7 @n 540,49,67,4t time11 @n 42,142ntime9 @n 33,18,15tbl35 @n 837,7,4Otags35 @n 255,8,17,9,1832,25,27,11,19,25,27,11,19,25,27,11,19,25,27,11,38,49,48,48 tags38 @n 77,11Vtag35 @n 243,2,3,7,6,7,1749,7,3,14,7,3,14,7,3,14,7,3,14,14,19,49,14,19,49,14,19,49,14,192tag40 @n 28,5,23,13,5,30,13,5,23,13,5,18,13,39,2,2,2tag38 @n 220,4,4,4,4,4,4,4t17 @n 142,13,16n t21 @n 48 t20 @n 23,8-6J0pt@ H T4  d h8dLT(p8  P, 8 H h L  P0h xH | ( `<   |\   dH$| \ |\< DX4` ,\P(  0 P ( `h @lL , last_volume106 @n 45,54,37,4-1,2 loop62 @n 29 level62 @n 76loop61 @n 36,32-3,114,13,52,13 loglevel61 @n 162 killwait61 @n 151id60 @n 399,12,3,4 localtime60 @n 152,154,46Pi60 @n 283,13,5,6,13,9,2,135,46-1,8,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,15-1,8 loglevel59 @n 43,41,9 idle59 @n 158 killwait59 @n 42,20,9,5,3i59 @n 153-1,2,20-1level57 @n 26,7,25,5-2,3 idle_sid55 @n 31,68,3,54,13,14-1 idle55 @n 159-1 hostname55 @n 55-1j55 @n 95,27-1i55 @n 97,26-1,2,4kf51 @n 32,19-1,5,6,6,5,3input_p50 @n 37,14,15,15 id49 @n 31,13love49 @n 33,13,115,3,3,3 mb_artist_id49 @n 63 increment49 @n 122 mb_track_id49 @n 65 last_modified49 @n 52 mb_album_id49 @n 64 kill49 @n 34,13kkill49 @n 173,3,3,3 keepgoing48 @n 30,4,119,8 kg48 @n 39,55 hostname48 @n 42,8-1,5 i48 @n 151-2 length47 @n 36,2last_id47 @n 30,92,71,10,3i47 @n 149-1,2-1,2input46 @n 68,657-1,8,2,16-1id46 @n 275,87,83,84%love46 @n 287,83,83,84,333,6,6,6,6,6,6,6,length46 @n 148,5,35,7,42,5,82,5,78,5,79,5,189,46 length_r46 @n 64,4 hostname46 @n 683,9,4,16 len46 @n 659 memcmp46 @n 85line46 @n 66,2-1,5,7,2,2,9,37,6,12,4-1,3,3,6,5-1,5-1,11,6-1,3,3,6,5-1,5,5-1,13,4-1,5,3,9,5,2,41-1,3,13,4-1,5,3,9,5,2,37-1,3,13,4-1,5,3,9,5,2,38-1,3,13,4-1,5,3,9,5,2,37-1,3,124,45,11-1kill46 @n 291,83,83,84kkill46 @n 918,6,6,6,6,5,6,6 input45 @n 57 id45 @n 54 max_connections45 @n 79 hostname45 @n 99 line45 @n 108id44 @n 68,64,58,53 hostname44 @n 265,11,8 hostname43 @n 198,11,8id42 @n 43,24,25,24 hostname42 @n 129,11,817 5id41 @n 78,15,32,145,28,28,29,28,32,32,33,440,32,33,32/love41 @n 386,32,33,32,14,6,3,13,6,3,13,6,3,13,6,3 memset14 @n 110,2 memset7 @n 395MPD memset23 @n 41n 3 memcmp7 @n 215,4,5H1 memchr7 @n 276,102EVEmd57 @n 437,5,10,3%mcode12 @n 119,7-1,2,128,4,2,2,2,137,15-1mbid6 @n 42,108,3mbid9 @n 32,18,15 mbid11 @n 42,140mbid7 @n 489,19,24,7,27,28,31,29n mb_track_id35 @n 303 mb_artist_id35 @n 301 mb_album_id35 @n 302max_fd12 @n 118,8,35max_connections26 @n 62-1,5-1 max_connections36 @n 132max41 @n 42,1259,13,13love31 @n 69,51,52,46)love35 @n 1480,55,54,53,114,7,14,7,14,7,14,7love38 @n 74,11,82,4,4,4Blove33 @n 28,7,2,19,2,12,7,2,19,2,12,7,2,19,2,12,7,2,14,2,12,39,2,2,2 loglevel24 @n 57 list7 @n 578-3roylist35 @n 258,5,2-1,2,2,27line7 @n 213,2,4,5,4,5,7,8,4,2-1,3-1,2-1,2,3,10,5,3,7,15-2line41 @n 1367,8,3,17,14line36 @n 86,13,16-2line11 @n 140,18,3str level34 @n 104,4 level38 @n 322length6 @n 44,106,3co length11 @n 43,14323- length9 @n 34,33  length13 @n 43,8i length36 @n 85,15Alength7 @n 213,2,4,4-2,2,6,55,3,9,62,8,8,2,3,106,10,33,9,26,17,41,30,6crolen7 @n 492,7,7,76,2,4len12 @n 221,171_ len41 @n 96,32len35 @n 257,4,7 len18 @n 30,5-1,1last_id13 @n 36,127,52,10,3,7last_id17 @n 37,15,67,87,6,32Bkkill29 @n 28,7,2,19,2,12,7,2,19,2,12,7,2,19,2,12,7,2,14,2,12,39,2,2,2kkill38 @n 183,4,4,4#kkill41 @n 167,6,3,13,6,3,13,6,3,13,6,3kkill35 @n 1843,7,14,7,14,7,14,7 killwait24 @n 56_kill31 @n 70,51,52,46kill41 @n 387,32,33,32kill35 @n 1481,55,54,53 kill38 @n 75,11key7 @n 159,4,14,2,3,2,3,2INI"key11 @n 159,2-1,3-1,5,3,3,2,2,2,2,2journal_interval6 @n 5726 journal_interval13 @n 179journal_interval14 @n 111,8,5-1n journal_file_empty11 @n 32,18,24,69journal6 @n 77 journal14 @n 86,13journal7 @n 671,3,5,41,3,5j18 @n 30,20-1con &interval7 @n 82,35,83-1,2,2-1,3,135,41,98,229 input_r7 @n 273,2,84@input_p32 @n 47,4-1,2,26,10,11,4-1,2,26,10,5,4-1,2,28,15,9,5,2-1,2-1,2 input7 @n 275-1,6input36 @n 52,47,21,24-1,17Rinput32 @n 49,5,2,5,9-1,2,2,4-1,10,13,5,2,5,9-1,2,2,4-1,10,7,5,2,6,6,4-1,3,2,4,7,6-2,9 input38 @n 117 Mincrement35 @n 470,14,19,14,22,14,19,12,63,13,22,13,25,13,22,13,411,19,4,7,4,9,4,10,4idx7 @n 160,5-1,21,2o@ids35 @n 2117,22,8,23,17,12,22,8,23,17,12,22,8,23,17,12,22,8,23,17 idle24 @n 59id31 @n 69,51,52,46id35 @n 313,20,4,9,6,20,4,9,6,20,4,9,6,20,4,9,183,19,16,22,16,19,16,61,367,13,2,5,4,2,5,5,3,5,6,3,5,42,48,49,48,50,54,55,54,480,25,32,25,25,32,25,25,32,25,25,32,43,48,49,48id36 @n 121,19,10,13,159 id38 @n 71,10icon18 @n 28,16-1 icon21 @n 57 i3 @n 49-4sei26 @n 91-1,2,2,5,31-1,4,25i18 @n 30,4,3,2,2,2,2-2,4,2,9-1)i41 @n 922-2,31-2,30-2,30-2,256,5,2,2,2,2$i35 @n 265-1,2,2,739-2,31-3,14-3,3-3i23 @n 103-1,2,5si12 @n 204-1,86-1 http_request12 @n 75,102,40d_ Thttp_client12 @n 68,10,48,7-1,12,5,3,13-1,11,13-1,11,24,13,20,30,43-1,5-2,18-1,8-1,4,45,27,9,6hours20 @n 25,8,10 hostname2 @n 67-1 hostname31 @n 232,11,8 hostname28 @n 191,11,8 hostname29 @n 195,11,8 hostname33 @n 195,11,8     H , d `@\<@<hL 0x@tTx8  d8`l@(|`@ Dl|XL(  hH|\88 \h@$ SIGABRT61 @n 176VERSION61 @n 50,59,23 SIGINT61 @n 151,27 SIG_IGN61 @n 172PACKAGE61 @n 50,58-1,22 SIGTERM61 @n 179 SIGSEGV61 @n 177 SIG_DFL61 @n 89 argv61 @n 95,7,9 argc61 @n 95,16 action61 @n 84,4-2 PACKAGE58 @n 33 TRUE55 @n 62 PACKAGE51 @n 46 album49 @n 54 VERSION48 @n 125argv48 @n 118,9,25-1argc48 @n 118,9,23-1addrs47 @n 149-1,2-1,2 TRUE46 @n 660,34album46 @n 239,5,8-1,6,7,6-3,3-1,3-1,3-1,3-1,3-1,3-1,3-1args46 @n 630,10,31,2-2arg46 @n 632,8,7 addrs45 @n 80 VERSION44 @n 329album44 @n 262,6-1,2,30argv44 @n 312,23,9argc44 @n 312,23,8 VERSION43 @n 262album43 @n 195,6-1,2,30argv43 @n 245,22,14,2argc43 @n 245,22,8,5 VERSION42 @n 193artist35 @n 282,9,1041,202,982,4- artist21 @n 453 artist29 @n 191,7-2,2914,artist25 @n 188,6-2,29 artist22 @n 25,5nartist31 @n 123,105,7-2,29e_sartist9 @n 29,18,15seartist33 @n 191,7-2,29artist11 @n 41,28,107artist28 @n 188,6-2,29argv25 @n 238,26,13,2argv34 @n 69,2,2,4-22,20,6argv29 @n 242,22,9,7,22,9argv31 @n 279,22,9 @nargv33 @n 242,22,9,7,22,9argv28 @n 238,25,13,2argv40 @n 245,22,14,2,argv41 @n 43,122,8,3,11,8,3,11,8,3,11,8,3,11,9,19,9,19,9,20,9,19,9,23,9,23,9,24,9,23,8,3,11,8,3,11,8,3,11,8,3,11,12,7,12,11,12,7,12,11,12,7,12,11,12,7,12,11,8,11,8,11,8,11,8,11,8,11,8,11,8,11,8,11,9,23,9,24,9,23,9,23,12,7,12,11,12,7,12,11,12,7,12,11,12,7,12,11,2,96,20,6,6,10,10,4,6,10,5-1,18-1,6,2,23,2argv38 @n 289,3,3,3,3,3,3,3,3,3,3argv42 @n 176,21,9args34 @n 106,5-2args41 @n 84,7,13,2-2,5,10,16,2-2argc25 @n 238,26,8,4&argc34 @n 69,9,2,2,2,2,2,2,2,2,2,2,21,4,2argc29 @n 242,22,8,8,22,8argc33 @n 242,22,8,8,22,8argc28 @n 238,25,8,4argc31 @n 279,22,8ng_argc40 @n 245,22,8,5argc42 @n 176,21,8argc41 @n 43,122,6,16,6,16,6,16,6,16,5,23,5,23,5,24,5,23,5,27,5,27,5,28,5,27,6,16,6,16,6,16,6,16,7,35,7,35,7,35,7,35,5,14,5,14,5,14,5,14,5,14,5,14,5,14,5,14,5,27,5,28,5,27,5,27,7,35,7,35,7,35,7,35,98,16,7,6,12,8,13,9,22,3-2,7,23,2argc38 @n 289,3,3,3,3,3,3,3,3,3,3 arg241 @n 51,12 arg141 @n 50album6 @n 41,108,4album9 @n 31,18,15 @n album35 @n 292 album11 @n 42,138album42 @n 125,7-1,2,30 album21 @n 45album40 @n 195,6-1,2,30album25 @n 188,6-1,2,30album33 @n 191,7-1,2,30album7 @n 488,17,27,6,27,27,33,28album28 @n 188,6-1,2,30ve album22 @n 25,5esalbum29 @n 191,7-1,2,30_ealbum31 @n 228,7-1,2,301addrs26 @n 171,16-2,18db_addrs36 @n 205,8,12,3 addr_string36 @n 171,9-1,8,2,5-1addr36 @n 172,5,3,3,59,7,2,7,3 ack41 @n 112,25a41 @n 1277,5,8-1VERSION5 @n 59 @n VERSION34 @n 384- VERSION12 @n 4286 VERSION28 @n 255 VERSION25 @n 2554 VERSION29 @n 259,3849 VERSION40 @n 262 VERSION31 @n 296@ VERSION33 @n 259,38VERSION7 @n 38 usTRUE8 @n 3121 TRUE12 @n 294 TRUE25 @n 263 TRUE36 @n 134,14 TRUE41 @n 97,32 TRUE28 @n 262 SQLITE_UPDATE41 @n 66cSQLITE_STATIC35 @n 326,39,39,39,44,33,3,33,31,7,3,3,3,3,3,3,3,3,3,3,3,3,33,35,3,35,35,7,3,3,3,3,3,3,3,3,3,3,3,3 SQLITE_SELECT41 @n 59 SSQLITE_ROW35 @n 336,2,37,2,37,2,37,2,500,2,323,48,49,48,50,54,55,54,505,82,82,82,75,48,49,48 SQLITE_READ41 @n 58 SQLITE_OPEN_READWRITE35 @n 997 SQLITE_OPEN_READONLY35 @n 997 SQLITE_OPEN_CREATE35 @n 999 SQLITE_OK35 @n 318,8,31,8,31,8,31,8,34,7,3,23,7,3,3,23,7,3,22,6,3,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,23,7,3,2,23,7,3,3,2,23,7,3,2,24,6,3,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,35,162,16,18,37,17,20,24,20,106,48,49,48,50,54,55,54,504,82,82,82,76,48,49,48 SQLITE_OK41 @n 60,5,3 SQLITE_FUNCTION41 @n 61 SQLITE_DONE35 @n 340,39,39,39,36,36,33,74,35,38,35,81,32,55-3,9,9,26,146,20,24,20,117,11,38,11,37,11,37,11,43,11,44,11,43,11,43,11,490,11,71,11,71,11,71,11,65,11,38,11,37,11,37,11 SQLITE_DENY41 @n 64,7 QSQLITE_BUSY35 @n 237,101,39,39,39,368,134,331,49,48,48,54,55,54,54,501,82,82,82,76,49,48,48PACKAGE2 @n 595 @PACKAGE5 @n 597 @  ( $,  h  x`D d  8|dX P0P d  $H@ pT(lL,h`D xT0 |X(`<3-2,3,43-2,3,44-2,3,43-2,3,43-2,5,47-2,5,48-2,5,47-2,5,46-1,9,11-1,9,11-1,9,11-1,9,14-1,9,11-1,9,11-1,9,11-1,9,14-1,9,11-1,9,11-1,9,11-1,9,14-1,9,11-1,9,11-1,9,11-1,9,14-1,12,11-1,12,11-1,12,11-1,12,18-1,6,8,25,3-1,2,11,7-1,17-1,6,8,25,3-1,2,11,7-1,17-1,6,8,25,3-1,2,11,7-1,17-1,6,8,25,3-1,2,11,7-1,13-2,3,43-2,3,44-2,3,43-2,3 NULL22 @n 31,63NULL28 @n 35,8,2,31,8,2,31,8,2,31,8,45,6,24,3,2,2-1,29NULL27 @n 34,32,2,7,3,5,4,5,44NULL26 @n 40,6-1,3,11,11,2-1,15-1,18,2-1,16-1,3,38-2,15 @@NULL29 @n 34,9,2,31,9,2,31,9,2,31,9,46,6,24,3,2,2-1,22,8,3,2,2-1,22pr NULL23 @n 43,27-2,14-2,14-1,19-2,13pdKNULL31 @n 32-1,11,2,20,17-1,11,2,20,18-1,11,2,20,17-1,11,17,37,6,24,3,2,2-1,22;NULL13 @n 38-1,26,2-1,31,2,2-1,27-1,6,41,15,11,10,6,3,12,2,2-12ENULL7 @n 120-2,21,27,109,16,35,3,3,43,48,32,42,39,35,4,78,14,19,30,14,12@NULL33 @n 34,9,2,31,9,2,31,9,2,31,9,46,6,24,3,2,2-1,22,8,3,2,2-1,222NULL32 @n 51-1,5,7,19,22-1,5,7,19,16-1,6,7,20,15,13-1MPD_TAG_TRACK35 @n 602,184MPD_TAG_TITLE13 @n 68,10,4,22,8,7,7veMPD_TAG_TITLE35 @n 599,184,405 MPD_TAG_TITLE17 @n 81,3-1MPD_TAG_PERFORMER35 @n 617,184 MPD_TAG_NAME35 @n 605,184 MPD_TAG_MUSICBRAINZ_TRACKID13 @n 84,44MPD_TAG_MUSICBRAINZ_TRACKID35 @n 629,184MPD_TAG_MUSICBRAINZ_ARTISTID35 @n 623,184MPD_TAG_MUSICBRAINZ_ALBUMID35 @n 626,184 MPD_TAG_GENRE25 @n 127,6t MPD_TAG_GENRE33 @n 129,6  MPD_TAG_GENRE40 @n 134,6 MPD_TAG_GENRE29 @n 129,6 MPD_TAG_GENRE31 @n 149,6_MPD_TAG_GENRE35 @n 555,53,127,57,439-1 MPD_TAG_GENRE28 @n 127,6_ MPD_TAG_DISC35 @n 620,184 MPD_TAG_DATE35 @n 611,184MPD_TAG_COMPOSER35 @n 614,184MPD_TAG_ARTIST13 @n 67,10,4,22,8,7,7u MPD_TAG_ARTIST29 @n 45,6MPD_TAG_ARTIST40 @n 45,6,35,12 MPD_TAG_ARTIST35 @n 486,33,74,69,35,80,410,19 MPD_TAG_ARTIST31 @n 46,6 MPD_TAG_ARTIST33 @n 45,6 MPD_TAG_ARTIST28 @n 45,6 MPD_TAG_ARTIST25 @n 45,6MPD_TAG_ARTIST17 @n 71,4,5,8-1 MPD_TAG_ALBUM13 @n 83,44p MPD_TAG_ALBUM29 @n 87,6b_ MPD_TAG_ALBUM40 @n 91,6MPD_TAG_ALBUM35 @n 522,74,104,80,437-1 MPD_TAG_ALBUM31 @n 97,6 MPD_TAG_ALBUM33 @n 87,6  MPD_TAG_ALBUM28 @n 86,6 MPD_TAG_ALBUM25 @n 86,6 conn106 @n 127,15 conf106 @n 70 code106 @n 84count106 @n 44,38,14-1,34 config_fd62 @n 87&conn62 @n 44,3,3,3,3,3,3,31,3,3,3,4,3,3,3 conf62 @n 28conf61 @n 43,83,10,15,11,21ctx61 @n 99,8-2,2,2,4Fconn60 @n 281,9,3,4,17,4,5,3,4,5,4,6,3,36,2,3,8,7,3,3,15,4,2,3-3,3,5,5,10:conf59 @n 33,5,4-3,5,7-1,4,9,5,3,5,9,7,9,5,2,2,3,6,9,5,3,3,11code59 @n 34,30,22,16,27 conf_path59 @n 33,5,12Rconn56 @n 26,11,5,8,10,6,7,11,6,7,13,5,10,11,5,8,10,6,7,11,5,8,11,5,8,4,2,2,2,2,2,2,2Bconn55 @n 33,10-1,3-1,8,4,7,2-2,5,3,21,3-1,2-1,2-1,3,17,27,3-1,7,21-2conf55 @n 55-1,9,2,79-1,12-1 condition55 @n 92 confpath51 @n 31,15,6,9,11,10 code51 @n 53 composer49 @n 60count49 @n 149,3,3,3 create49 @n 100%conn48 @n 46,10,5,2-1,5,2-1,5,2-1,5,26-1count48 @n 41,43,21,8ctx48 @n 120,3-2,2,3,3 conn47 @n 176 conf47 @n 128,6Wconn46 @n 64,4-2,75,6,2,4,6-2,4,6,10,6,4,4,6-2,4,6-1,4,12,8,6,8-2,5,8,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,15,8,6,8-2,5,8,4,2,2,2,2,2,2,2,2,2,2,2,2,3,15,8,6,8-2,5,8,4,2,2,2,2,2,2,2,2,2,2,2,3,3,15,8,6,8-2,5,8,4,2,2,2,2,2,2,2,2,2,2,2,2,3,15,6,7,2,7,2,2,2-1,29,29-1,9,6,13,2-1,8-1,4,2-2,7-1,4-3,3-1,2,3-9,2,2,4,2-9,4,2,3,2,4,3,3,2,4,3,3,2,4,3,3,2,4,3,3,2,4,3,3,2,4,3,3,2,4,3,3,2,4,3,3,2,4,3,3,2,4,3,3,2,4,3,3,2,4,3,3,2,4,3,3,2,4,3,3,2,4,3,2,2,4,3,3,2,4,3,3,2,4,3,3,2,4,3,3,2,4,3,3,2,4,3,5,2,4,3,5,2,4,3,5,2,4,3,5,2,4,3,5,2,4,3,5,2,4,3,5,2,4,3,5,2,4,3,4,2,4,3,4,2,4,3,4,2,4,3,4,2,4,3,4,2,4,3,4,2,4,3,4,2,4,3,4,2 curl_multi_remove_handle12 @n 78,368 curl_multi_perform12 @n 261is curl_multi_init12 @n 334e curl_multi_info_read12 @n 241 curl_multi_fdset12 @n 126 curl_multi_cleanup12 @n 37419 curl_multi_add_handle12 @n 419ern curl_global_init12 @n 327 curl_global_cleanup12 @n 3754 curl_easy_strerror12 @n 330oycurl_easy_setopt12 @n 428-5,3,4-1,317 curl_easy_init12 @n 412mdcurl_easy_cleanup12 @n 79,342,26m.curl12 @n 40,38-1,123,5,205-1,6,2,7-5,3,4-1,3,2-1ctx25 @n 251,2-2,2,6-1,3,3ctx31 @n 292,2-3,4,3,367,ctx42 @n 189,2-3,3,3,3ctx43 @n 258,2-3,4,3,3ctx44 @n 325,2-2,2,4,3,3ctx28 @n 251,2-3,6-1,3,3@ctx40 @n 258,2-3,4,3,3!ctx33 @n 255,2-3,4,3,3,23,2-3,4,3,3!ctx29 @n 255,2-3,4,3,3,23,2-3,4,3,3r ctime17 @n 171filcreate35 @n 981,11,6cpath17 @n 66,5,2,21,2_locpath22 @n 28,3,3-2 cover_suffix17 @n 77 cover_suffix22 @n 30t cover_suffix23 @n 49,75-1,8nt cover_suffix21 @n 35 cover_path21 @n 34 cover_path23 @n 44,78-1,9 cover_path22 @n 31_clcount7 @n 351,2,2,197,27,7-8,2,4,58count41 @n 1035,9,11,3,5,14,9,11,3,5,14,9,11,3,5,14,9,11,3,5count35 @n 1669,7,14,7,14,7,14,7 count45 @n 102count36 @n 40,270,5!count46 @n 1178,3,3,7,3,3,7,3,3,7,3,32count25 @n 29,7,20,14,7,20,14,7,20,14,7,15,13,39,2,2,2count38 @n 268,4,4,4/conn5 @n 183,11,9,11,9,11,9,12,9,11,9,11,9,11,9,1127,Lconn31 @n 28,6,2,21,2,20,6,2,21,2,21,6,2,21,2,20,6,2,16,2,25,18-3,5-2,6,2,2,2-1n $conn27 @n 55,11,5,2-1,5,2-1,5-1,2-1,4,4Lconn28 @n 28,8,2,18,2,11,8,2,18,2,11,8,2,18,2,11,8,2,13,2,16,18-3,5-2,6,2,2,2-1b_Lconn33 @n 28,7,3,18,3,11,7,3,18,3,11,7,3,18,3,11,7,3,13,3,16,18-3,5-2,6,2,2,2-1conn17 @n 139,44,44,17,26 conn36 @n 125,17Tconn38 @n 129,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4Lconn40 @n 28,5,2,21,2,11,5,2,28,2,11,5,2,21,2,11,5,2,16,2,16,18-3,5-2,6,2,2,2-1Lconn25 @n 28,8,2,18,2,11,8,2,18,2,11,8,2,18,2,11,8,2,13,2,16,18-3,5-2,6,2,2,2-1stLconn29 @n 28,7,3,18,3,11,7,3,18,3,11,7,3,18,3,11,7,3,13,3,16,18-3,5-2,6,2,2,2-1  conn24 @n 73 conn13 @n 1998conn42 @n 28,7,2,15,7,2,16,7,2,15,7,2,20,18-3,5-2,6,2,2,2-1Lconn43 @n 28,5,2,21,2,11,5,2,28,2,11,5,2,21,2,11,5,2,16,2,16,18-3,5-2,6,2,2,2-1Lconn44 @n 28,6,2,21,2,26,6,2,28,2,27,6,2,21,2,26,6,2,16,2,33,18-3,5-2,6,2,2,2-1 config_fd5 @n 73,29,67,2p config7 @n 111r7 conf_path2 @n 59,21oo conf_path24 @n 46&conf2 @n 28,7-1,2,2,6,4,2,7,4,4-4,8-2,2,2 conf5 @n 48,54scr conf3 @n 72,3 conf13 @n 169conf26 @n 32,15,146-2 conf45 @n 92 conf17 @n 116 composer35 @n 298 commands41 @n 1213,59,12,2 command_puts41 @n 102,79,22,22,22,23-1,27-1,27-2,27-1,27-5,27-5,27-6,27-5,26,22,22,22,42,42,42,42,19,19,19,19,19,19,19,19,23-1,3,28-2,3,28-1,3,28-1,3,47,42,42,42command_error41 @n 137,18,22,22,22,22,20,28,28,29,28,32,32,33,34,22,22,22,24,6,4,8,24,6,4,8,24,6,4,8,24,6,4,8,19,19,19,19,19,19,19,19,20,32,33,32,36,6,4,8,24,6,4,8,24,6,4,8,24,6,4,8,18,97,11,7,6,24,26,3,22,6,9 command41 @n 1297 command46 @n 629,39code3 @n 78 dD$`<$ pPt<\$p\0|L xP(\0D|P$h4d,@P X(  -./345    !"#%&')*+-./1235679:;=>?ABCEFGIJKMNOQRSUVWefgjkmnowcode41 @n 177,22,22,22,20,28,28,29,28,32,32,33,34,22,22,22,42,42,42,42,19,19,19,19,19,19,19,19,20,32,33,32,54,42,42,42,229code15 @n 51,10,27,10code26 @n 76,37,60nfo code23 @n 73,16code12 @n 327-1,2,73,41-1cmp41 @n 1278,6-1,2,2-cmd41 @n 39,1245,13,3-1,2,4,36,7-1,7,2,3,9,54-2cmd46 @n 634,3-1,4,3,3,3,4,4-1,3#clients36 @n 45,19,12,14,14,7,20,19,148,8 clientid36 @n 59,5,12,7,7,13-1,4,3,4 client36 @n 310,9client41 @n 76,8,18,10,25,11,17,22,22,22,22,28,28,29,28,32,32,33,32,22,22,22,22,42,42,42,42,19,19,19,19,19,19,19,19,32,33," g_option_context_add_main_entries61 @n 108-1, g_option_context_set_summary61 @n 109 g_option_context_new61 @n 107 g_option_context_parse61 @n 1111, g_option_context_free61 @n 113,43 g_output_stream_write_all46 @n 659 g_output_stream_write36 @n 315 g_output_stream_flush_finish36 @n 73 g_output_stream_flush_async36 @n 321 g_option_context_set_summary25 @n 255 g_option_context_set_summary31 @n 296 g_option_context_set_summary29 @n 259,38 g_option_context_set_summary33 @n 259,38 g_option_context_set_summary42 @n 193 g_option_context_set_summary48 @n 125 g_option_context_set_summary44 @n 329 g_option_context_set_summary43 @n 262 g_option_context_set_summary40 @n 262 g_option_context_set_summary28 @n 255, g_option_context_set_ignore_unknown_options25 @n 263L, g_option_context_set_ignore_unknown_options28 @n 262! g_option_context_set_description25 @n 257! g_option_context_set_description29 @n 260,38! g_option_context_set_description33 @n 260,38! g_option_context_set_description44 @n 331! g_option_context_set_description43 @n 263! g_option_context_set_description42 @n 194! g_option_context_set_description40 @n 263! g_option_context_set_description31 @n 297! g_option_context_set_description28 @n 256 g_option_context_parse25 @n 264 g_option_context_parse33 @n 264,38 g_option_context_parse44 @n 335 g_option_context_parse31 @n 301 g_option_context_parse48 @n 127 g_option_context_parse43 @n 267 g_option_context_parse42 @n 197 g_option_context_parse29 @n 264,38 g_option_context_parse40 @n 267 g_option_context_parse28 @n 263 g_option_context_new25 @n 253 g_option_context_new28 @n 253 g_option_context_new33 @n 257,38 g_option_context_new44 @n 327 g_option_context_new48 @n 123 g_option_context_new31 @n 294 g_option_context_new43 @n 260 g_option_context_new29 @n 257,38 g_option_context_new42 @n 191 g_option_context_new40 @n 260 g_option_context_free25 @n 267,3m g_option_context_free40 @n 270,3g_option_context_free29 @n 267,3,35,3 g_option_context_free44 @n 338,3 g_option_context_free48 @n 130,3 g_option_context_free31 @n 304,3g_option_context_free33 @n 267,3,35,3 g_option_context_free43 @n 270,3 g_option_context_free28 @n 266,3 g_option_context_free42 @n 200,3" g_option_context_add_main_entries25 @n 254" g_option_context_add_main_entries44 @n 328" g_option_context_add_main_entries48 @n 124" g_option_context_add_main_entries31 @n 295" g_option_context_add_main_entries28 @n 254" g_option_context_add_main_entries43 @n 261" g_option_context_add_main_entries40 @n 261" g_option_context_add_main_entries33 @n 258,38" g_option_context_add_main_entries42 @n 192" g_option_context_add_main_entries29 @n 258,38g_object_unref36 @n 51-2,139,6,16,44,3,44g_object_unref46 @n 701,4,11,4,14-1,18,2,2 g_object_ref36 @n 155g_new05 @n 77-g_new035 @n 1281,48,49,48,50,54,55,54,826,48,49,48g_new046 @n 274,87,83,84,161 g_new026 @n 188 g_new7 @n 113g_new9 @n 40Gg_new35 @n 2144,82,82,82g_new12 @n 164,238 g_new36 @n 139,144 g_new14 @n 44 g_new46 @n 728 g_network_address_new46 @n 712g_module_symbol5 @n 91rd_g_module_open5 @n 84fg_module_error5 @n 86g_module_close5 @n 94,12,24sc g_message5 @n 141,5,5 g_message17 @n 126,7E g_message7 @n 216,33,261,89,60,18,11,37 2  lLhD H\8\0 hL,tDh4|Tt `,$X(\,|@pL $d80 d@l |8H mpd_stats62 @n 50,43 mpd_song62 @n 56,46 mpd_song_get_uri60 @n 148 mpd_stats_get_number_of_albums60 @n 359 mpd_song_get_pos60 @n 167 mpd_run_status60 @n 389 mpd_stats_get_db_update_time60 @n 351 mpd_send_outputs60 @n 407 mpd_send_current_song60 @n 436 mpd_song_get_id60 @n 174 mpd_stats60 @n 339 mpd_state60 @n 28 mpd_response_next60 @n 445 mpd_song_get_duration60 @n 160 mpd_song60 @n 139,182,108 mpd_send_list_queue_meta60 @n 326 mpd_send_status60 @n 435 mpd_song_free60 @n 332 mpd_stats_get_number_of_artists60 @n 355mpd_response_finish60 @n 314,21,90,35(mpd_song_get_tag60 @n 182,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 mpd_stats_get_number_of_songs17 @n 16838 mpd_stats_get_number_of_songs60 @n 363 mpd_stats_get_number_of_artists17 @n 166 mpd_stats_get_number_of_albums17 @n 1672, mpd_stats_get_db_update_time17 @n 155 mpd_stats_get_db_play_time17 @n 154in mpd_stats_get_db_play_time60 @n 375 mpd_stats_free56 @n 44 mpd_stats5 @n 183 mpd_stats24 @n 73 mpd_stats56 @n 30 mpd_stats17 @n 14041 mpd_state13 @n 2026,1 mpd_state47 @n 179 mpd_state17 @n 186kedmpd_song_get_uri13 @n 70,36,182-1mpd_song_get_uri35 @n 586,184,421,4 mpd_song_get_uri40 @n 176 mpd_song_get_uri44 @n 226 mpd_song_get_uri43 @n 176 mpd_song_get_uri25 @n 169mpd_song_get_uri48 @n 91,10,5 mpd_song_get_uri17 @n 86n mpd_song_get_uri33 @n 172 mpd_song_get_uri31 @n 201 mpd_song_get_uri28 @n 169 mpd_song_get_uri29 @n 172$mpd_song_get_tag13 @n 67-1,9-1,3-3,19-1,7-1,6-1,6-3s_mpd_song_get_tag17 @n 71-1,3-1,4-1,3-1,3-1,2-12fmpd_song_get_tag35 @n 486,33,3,33,38,3,3,3,3,3,3,3,3,3,3,3,3,33,35,3,35,42,3,3,3,3,3,3,3,3,3,3,3,3,374-1,18,11-1,13-1mpd_song_get_tag33 @n 45,6,36,6,36,6nmpd_song_get_tag28 @n 45,6,35,6,35,6mpd_song_get_tag40 @n 45,6,35,5,6-1,36,6mpd_song_get_tag43 @n 45,6,35,5,6-1,36,6mpd_song_get_tag44 @n 46,6,51,5,6-1,53,6mpd_song_get_tag47 @n 49-1,22-1,6-1mpd_song_get_tag31 @n 46,6,45,6,46,61mpd_song_get_tag25 @n 45,6,35,6,35,6_mpd_song_get_tag29 @n 45,6,36,6,36,63mpd_song_get_pos13 @n 79,34,7mpd_song_get_pos47 @n 51,23,7 mpd_song_get_pos17 @n 829mpd_song_get_last_modified35 @n 591,184b_ mpd_song_get_last_modified60 @n 151mpd_song_get_id13 @n 79,34,7,95,6,4,3mpd_song_get_id17 @n 82,124,6,313mpd_song_get_id47 @n 51,23,7,112,6,4,3mpd_song_get_duration13 @n 59,26,24,20-11 mpd_song_get_duration47 @n 70mpd_song_get_duration35 @n 589,184 mpd_song_free13 @n 195,42mpd_song_free33 @n 47,7,35,7,35,7,37@mpd_song_free44 @n 48,7,50,5,9,51,7,52mpd_song_free40 @n 47,7,34,5,9,34,7,36 mpd_song_free47 @n 168,47 mpd_song_free56 @n 118mpd_song_free43 @n 47,7,34,5,9,34,7,36mpd_song_free25 @n 47,7,34,7,34,7,36mpd_song_free31 @n 48,7,44,7,45,7,463mpd_song_free28 @n 47,7,34,7,34,7,36mpd_song_free29 @n 47,7,35,7,35,7,37d mpd_song_dup13 @n 241 mpd_song_dup47 @n 219 mpd_song5 @n 243- mpd_song13 @n 38,17,8,26,6,41,64 mpd_song27 @n 50,62-1 mpd_song56 @n 101 mpd_song29 @n 32,42,42,42 mpd_song25 @n 33,41,41,41 #mpd_song35 @n 470,33,36,33,74,35,38,35,425 mpd_song40 @n 41,41,48,41 mpd_song44 @n 42,57,65,57 mpd_song47 @n 32,10,13,6,34,82 mpd_song43 @n 41,41,48,41 mpd_song48 @n 48 mpd_song28 @n 33,41,41,41 mpd_song38 @n 285 mpd_song33 @n 32,42,42,42 mpd_song24 @n 82_ mpd_song49 @n 122 mpd_song31 @n 42,51,52,51 mpd_song17 @n 63,37,6,78t mpd_send_list_meta60 @n 293 mpd_send_list_all_meta48 @n 77 mpd_send_idle_mask55 @n 160 mpd_send_idle55 @n 160 mpd_run_stats60 @n 348 mpd_run_password27 @n 79n mpd_run_password48 @n 69 mpd_run_password55 @n 67 mpd_run_current_song27 @n 87@ mpd_response_finish48 @n 111 pT0|T,Dd<ldD$pP0`@pP0lH($\<hD$tP,x\@ d parse_error106 @n 72,9-3,4,3-1 name106 @n 56 name62 @n 65optv61 @n 38,3,78optk61 @n 38,4,108 no_daemon61 @n 43,83,10,26,21 old_action61 @n 98,73-1 options61 @n 40,68ons new_action61 @n 98,67-2,6 parse_err61 @n 100,11-1,2output60 @n 402,8-2,7,3ec:path5 @n 31,10,7-3,3,2,3-3,3,3,10,6,2-1,6,2,9-1,6,17,15,5,5,9path11 @n 46,7,3,81,8,7pdpath35 @n 981,8,12r4 path6 @n 115,36,1 path48 @n 39,38 path49 @n 100 passwords26 @n 128,15,6,6,535 passwords41 @n 1202 passwords45 @n 84 password2 @n 7152 password14 @n 66,9-3,3-2,15md password7 @n 423,4,15 password27 @n 54,10,14-1, password28 @n 191,17,9-1l password29 @n 195,17,9-18 password33 @n 195,17,9-1 password31 @n 232,17,9-11 password42 @n 129,17,9-1 password43 @n 198,17,9-1 password44 @n 265,17,9-1 password55 @n 65,2 password48 @n 44,10,14-1 password46 @n 763,3,2 password40 @n 198,17,9-1 password38 @n 132 password25 @n 191,17,9-1 password26 @n 195 password24 @n 51e password6 @n 71ha parser38 @n 122parser46 @n 81,7-2,8,2,22,15-1,19,7-1,34,7-1,10-1,30,9-1,13,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,32,9-1,13,4,2,2,2,2,2,2,2,2,2,2,2,2,32,9-1,13,4,2,2,2,2,2,2,2,2,2,2,2,3,32,9-1,13,4,2,2,2,2,2,2,2,2,2,2,2,2,175,5,4,21parsed59 @n 154-1,3pair46 @n 50,87-1,79-1,53,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,55,4,2,2,2,2,2,2,2,2,2,2,2,2,55,4,2,2,2,2,2,2,2,2,2,2,2,3,55,4,2,2,2,2,2,2,2,2,2,2,2,2p11 @n 92,6,3,4,6,4,11,3,59p46 @n 86,8-1,3-2,2,6-1,2,2,5-1,2-1,7,2-1,3,3,474,6-2,2-1p14 @n 37,39,2,3,2 92p27 @n 31,6,2-1,3-169output36 @n 51,22,74-1,167,6 output45 @n 58output46 @n 571,5,7,52,23-1 outf34 @n 43,2curoptstr59 @n 28,93-2,24-2 options25 @n 242,12ng options48 @n 32,92options29 @n 246,12,26,12 options40 @n 249,12 options43 @n 249,12 options44 @n 316,12 options42 @n 180,12options33 @n 246,12,26,12 options31 @n 283,12 options28 @n 242,12n optg25 @n 240,7,30,2,optg31 @n 281,7,22,2hoptg29 @n 244,7,22,2,7,7,22,2optg42 @n 178,7,21optg44 @n 314,7,23,2optg43 @n 247,7,27,2optg33 @n 244,29,2,7,7,22,2ovoptg40 @n 247,7,27,2optg28 @n 240,7,29,2gopta25 @n 240,3,34,21opta29 @n 244,3,26,2,7,3,26,2opta40 @n 247,3,31,2opta33 @n 244,3,26,2,7,3,26,2opta31 @n 281,3,26,2,opta44 @n 314,3,27,2opta43 @n 247,3,31,2opta42 @n 178,3,25opta28 @n 240,3,33,2optA25 @n 240,5,32,2noptA33 @n 244,5,2,22,2,7,5,24,2n optA29 @n 244,5,24,2,7,5,24,2optA40 @n 247,5,29,2optA31 @n 281,5,24,29optA44 @n 314,5,25,2optA28 @n 240,5,31,23optA43 @n 247,5,29,2optA42 @n 178,5,23 on35 @n 1135,6 on49 @n 116 num_commands41 @n 1272,5 num_clients36 @n 128,3-1,4,4 nowplay_url7 @n 88,33,33,166,2,6,4-1,179,2tin 'now_playing7 @n 91,33,22,246,2-2,128-1,36,2-4,142 no_daemon24 @n 53nmemb12 @n 386,4,6,4, next12 @n 291newline7 @n 276,3,3-1,82,13-2new35 @n 259,3,8,4,711,4,194- ncalls3 @n 30,21l name2 @n 33,4-2name6 @n 67ecname15 @n 38,11,14,12,11,142name31 @n 70-1,50-1,51-1 name17 @n 287name5 @n 41,3-1,3,4,7,4,4 name14 @n 47,48 @ name13 @n 250name3 @n 28,17,4-1,11,5,3,10,2,2,<name35 @n 281,14,16,14,25,14,25,14,880,48,49,99,54,55,879,48,49is 4|l0l(h(`<d@ d8 |X4xX8|\8|\<$Sname46 @n 49,88,80,54,6,2,2,4,4,4,4,4,57,6,2,2,4,4,4,4,57,6,2,2,4, event62 @n 62.expr29 @n 28,6-1,35,6-1,35,6-1,35,6-1,30,39,2,2,2 exitval34 @n 43,22 exit34 @n 65exit51 @n 42,22,11exit55 @n 58,14,39#events12 @n 90,3,5,5,4,31,3,4,3-2,12-1,3Fevents23 @n 113,5-158 events21 @n 33 35events17 @n 149,41,44,17,26N_events59 @n 29,123-2,2,4event23 @n 37,67,3-2,47,1event56 @n 220,2,18 escaped7 @n 171-2esc_uri25 @n 155,14-2esc_uri29 @n 157,15-2esc_uri40 @n 170,6-2esc_uri33 @n 157,15-2esc_uri43 @n 170,6-2esc_uri44 @n 220,6-2esc_uri28 @n 155,14-2esc_uri31 @n 195,6-2 esc_tags35 @n 2173-2,80-2,80-2,80-2 esc_genre25 @n 114,19-2ON esc_genre40 @n 129,11-2 esc_genre43 @n 129,11-2 esc_genre44 @n 163,11-2 esc_genre28 @n 114,19-2 esc_genre31 @n 144,11-2 esc_genre29 @n 115,20-2 esc_genre33 @n 115,20-2 esc_artist25 @n 32,19-2 esc_artist40 @n 40,11-2,28,17-1,2 esc_artist44 @n 41,11-2,44,17-1,2 esc_artist43 @n 40,11-2,28,17-1,2 esc_artist29 @n 31,20-2 esc_artist28 @n 32,19-2 esc_artist31 @n 41,11-2 esc_artist33 @n 31,20-2esc_arg46 @n 633,14-2 esc_album25 @n 73,19-2 esc_album44 @n 98,16,2-1 esc_album43 @n 81,16,2-1 esc_album29 @n 73,20-2 esc_album33 @n 73,20-2 esc_album40 @n 81,16,2-1 esc_album31 @n 92,11-2 esc_album28 @n 73,19-2+error_r32 @n 47,15,19,20,15,19,14,16,20,15,13,6,2error_r50 @n 37,14,15,15error3 @n 64,10,3-2,2,3error12 @n 49,174,209error15 @n 39,22,15,2225,1error23 @n 39,4-3,3-2,3-2,3-2,3-2,4-3,3,4-1,5-3,3,4-1 error35 @n 243,3,65,8,8,14,9,8,8,14,9,8,8,14,9,8,8,14,13,7,10,6,10,7,13,6,10,7,10,6,10,6,52,6,10,7,12,6,10,7,15,6,10,7,12,6,10,8,53,10,12,12,8,11,2,5,2,5,2,5,2,7,17,9,9,9,17,6,16,11,9,10,7,9,5,13,32,5,12,5,6,9,5,6,9,9,6,9,5,6,10,9,6,3,4,4,3,4,6,3,4,7,3,4,17,13,22,13,13,23,13,13,22,13,13,22,13,15,26,13,15,27,13,15,26,13,15,26,15,8,13,8,13,8,13,8,16,8,13,8,13,8,13,8,16,8,13,8,13,8,13,8,16,8,13,8,13,8,13,8,16,7,4,13,7,4,13,7,4,13,7,4,13,14,5,23,23,17,14,5,23,23,17,14,5,23,23,17,14,5,23,23,17,13,23,12,13,24,12,13,23,12,13,235error33 @n 38,21,21,21,21,21,21,16,35-1,7,22,19-2,17,19-2error18 @n 32,23-1,2-17 @-error31 @n 36,23,28,23,29,23,28,18,44-1,7,22,19-25error29 @n 38,21,21,21,21,21,21,16,35-1,7,22,19-2,17,19-2-error25 @n 38,20,21,20,21,20,21,15,35-1,7,22,23-2-error28 @n 38,20,21,20,21,20,21,15,35-1,7,22,22-2=error26 @n 35,5-3,7,2-2,7,2-2,7,2-2,3,5-1,24,2-2,3,5-1,48-3,3,5-1-error40 @n 35,23,18,30,18,23,18,18,35-1,7,22,19-2Ferror41 @n 112,10,15,5,27,6-3,13,6-3,13,6-3,13,6-3,11,5,2-2,19,5,2-2,19,5,2-2,20,5,2-2,19,5,2-2,23,5,2-2,23,5,2-2,24,5,2-2,25,6-3,13,6-3,13,6-3,13,6-3,14,25-3,14,25-3,14,25-3,14,25-3,12,4-3,12,4-3,12,4-3,12,4-3,12,4-3,12,4-3,12,4-3,12,4-3,11,5,2-2,23,5,2-2,24,5,2-2,23,5,2-2,26,25-3,14,25-3,14,25-3,14,25-3,182,2,8-1,11,15,2,5-2,2-error44 @n 36,23,34,30,35,23,34,18,52-1,7,22,20-2lerror46 @n 46,22-2,18-2,8,2,22,41-2,4,6,30-2,4,11,34-2,5,43,37-2,5,39,37-2,5,40,37-2,5,39,21,9,73,40,4,12,4,31-1error47 @n 64,19-3,2-2,40,8-1,2-1 error38 @n 102#error48 @n 45,43-1,3-1,5,4-1,18,6-2,9-3-error43 @n 35,23,18,30,18,23,18,18,35-1,7,22,19-2^error49 @n 100,7,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3!error42 @n 37,24,25,24,39-1,7,22,18-2error51 @n 33,17,2-1,3,5-1,6-3 pD\@d< \@ xT,dH,|`8hL(L,X PlLl$L0?error59 @n 30,2-2,5,11-1,10-3,2-1,3,13 song62 @n 56,461, source9 @n 35,33n#source12 @n 58,88,5,16,112,10,15,36-2,27a source7 @n 590,67 song5 @n 243,12am6song31 @n 42,2,2,2,4,3,38,2,2,2,4,3,39,2,2,2,4,3,38,2,3,3:song33 @n 32,11,2,2,4,3,20,11,2,2,4,3,20,11,2,2,4,3,20,11,3,3>song17 @n 63,5,3-1,3-1,4-2,2-2,2-1,2-1,8,2,4,5,73,10,12,5-1,2-1,4:song28 @n 33,10,2,2,4,3,20,10,2,2,4,3,20,10,2,2,4,3,20,10,3,3Ysong13 @n 55,4,4,2,2-1,2,7-2,2-4,4,2,4,4,4-1,2,3,2-2,5-2,4-6,6,3,2-1,58,5,10,6,3-1,2-1,4,8-1:song29 @n 32,11,2,2,4,3,20,11,2,2,4,3,20,11,2,2,4,3,20,11,3,3"song7 @n 134-1,387,3,56,3,2-1,2-1,2,2song27 @n 56,31,131 @song35 @n 287,2-15,166,4,12,17,4,12,3,17,4,12,17,4,10,3,2,2,3,3,3,3,3,3,3,3,3,3,3,3,17,4,12,19,4,12,3,19,4,12,19,6,10,3,2,2,3,3,3,3,3,3,3,3,3,3,3,3,366,6,2-1,3,4,3,4,4,3,4,4-1,4,4,5-1,4,4,166,20-3,188,22-7,945,19-4:song25 @n 33,10,2,2,4,3,20,10,2,2,4,3,20,10,2,2,4,3,20,10,3,3<song40 @n 41,2,2,2,4,3,28,2,2,2,3,2,4-1,4,28,2,2,2,4,3,28,2,3,3song41 @n 269-3,110-7,530-5,2<song44 @n 42,2,2,2,4,3,44,2,2,2,3,2,4-1,4,45,2,2,2,4,3,44,2,3,30song46 @n 493,5,8-1,6,7,6-3,3-1,3-1,3-1,3-1,3-1,3-1song48 @n 48,39,2,2,10,5 song49 @n 94,28Esong60 @n 139,9,3,9,7,7,8,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,49,9-2,97,21,7-1song56 @n 101,9,5,2-1@song47 @n 42,2,5-2,4,2,4,5,4,2-2,5-2,3,11,5-1,76,16,6,3-1,2-1,4,8-1<song43 @n 41,2,2,2,4,3,28,2,2,2,3,2,4-1,4,28,2,2,2,4,3,28,2,3,3 snprintf7 @n 499,85 1slist_r5 @n 135,19,3sslink_r5 @n 135,19,2-1 skip51 @n 58,21size_t6 @n 48 size_t12 @n 386eesize_t7 @n 213,75,74size12 @n 386,4,6signum61 @n 82,6,2-1 signaled61 @n 58,3 sigemptyset61 @n 166 sigaction61 @n 84,4,2,8,73,2sig61 @n 169,2,2 session7 @n 87,33,33,163,2,11-1,172,74uesserver36 @n 44,141,50,15,20,26-1,7-1!server46 @n 43,45,10,66,42,51,87,83,84 sep7 @n 159,3secs20 @n 25,12,7 scrobblers6 @n 59 scrobblers7 @n 105,438,120,19,52,12-1 scrobblers14 @n 128,4,19-1,21 scrobblers13 @n 176scrobbler_name7 @n 212,4,4,7,3,31 scrobbler_configs6 @n 143 scrobbler_configs7 @n 686,4st scrobbler_config7 @n 1115 &scrobbler7 @n 141,51,2,2,2,42,194,43,10,62,156K save_source_id13 @n 40,139,14 sample_rate60 @n 124saddr36 @n 173,10,2,7,6saddr46 @n 694,2,3,2 sa_mask61 @n 166 sa_handler61 @n 89,76,7 sa_flags61 @n 167s7 @n 159,3-1,3,2,4,5,2,3,2,3,22 s44 @n 241-3,6,2-2s31 @n 216,2-3 12s41 @n 148,5,3s14 @n 106,23,4,3 running_handles12 @n 258,42,2rfds12 @n 88,4,2,23,5,4,12,24 revents12 @n 293_ret5 @n 135,2,26,22,4,6-1,3,6,4,6-1,3,6,4,6-1,3,7,4,6-1,3,6,4,6-1,3,6,4,6-1,3,6,4,6-1,3,6,4,6-1,3ret44 @n 264,36,2,2,2,2,6,30,2,2 ret11 @n 129-2 *c?ret46 @n 147,10-1,29,12-1,36,12-1,74,12-1,70,12-1,71,12-1,167,4,2ret61 @n 97,122,6-1,2-1ret55 @n 153,7-1Oret56 @n 28,12,2,4,6,11,3,3,6,12,3,3,6,14,2,6,6,12,2,4,6,11,3,3,6,12,2,4,6,12,2,4ret43 @n 197,36,2,2,2,2,6,34,2,2ret31 @n 231,36,2,2,2,2,6,29,2,2&ret35 @n 233,3-1,2,74,22-1,2,2,12,22-1,2,2,12,22-1,2,2,12,22-1,2,2,300,65-1,2,120,9-1,2,2,300,19-1,18,10,19-1,19,10,19-1,18,10,19-1,18,10,21-1,22,10,21-1,23,10,21-1,22,10,21-1,22,456,26-1,18,9,3,6,13,6,26-1,18,9,3,6,13,6,26-1,18,9,3,6,13,6,26-1,18,9,3,6,13,6,19-1,19,9,19-1,20,9,19-1,19,9,19-1,19 ret25 @n 190,36,2,2,2,2,6,34,3,2-1mpdcron-0.3+git20110303/GTAGS000066400000000000000000002600001153374523700152070ustar00rootroot00000000000000b1 &p8Pd4|<P (  H 0Td<l 8Hl<H`p Xt@`Hhp8 0 p 4| ,LPh X0    8 x :http_client_request12 @n 399 void @n(const char *url, const char *post_data,Ohttp_request_writefunction12 @n 386 static size_t @n(void *ptr, size_t size, size_t nmemb, void *stream)$http_client_uri_escape12 @n 378 char *@n(const char *src)http_client_finish12 @n 356 @n(void)>http_request_free_callback12 @n 348 @n(gpointer data, G_GNUC_UNUSED gpointer user_data)http_client_init12 @n 325 int @n(void)<curl_source_dispatch12 @n 304 static gboolean @n(G_GNUC_UNUSED GSource *source,<curl_source_check12 @n 289 static gboolean @n(G_GNUC_UNUSED GSource *source)Jcurl_source_prepare12 @n 279 @n(G_GNUC_UNUSED GSource *source, G_GNUC_UNUSED gint *timeout_)http_multi_perform12 @n 255 static bool @n(void)http_multi_info_read12 @n 236 @n(void)Hhttp_request_done12 @n 217 static void @n(struct http_request *request, CURLcode result)http_client_find_request12 @n 202 @n(CURL *curl)http_client_abort_all_requests12 @n 190 @n(void)7http_request_abort12 @n 177 static void @n(struct http_request *request)http_client_update_fds12 @n 115 @n(void)Mhttp_client_fd_events12 @n 88 static gushort @n(int fd, fd_set *rfds, fd_set *wfds, fd_set *efds)6http_request_free12 @n 75 static void @n(struct http_request *request) http_request12 @n 35 struct @n {MAX_RESPONSE_BODY12 @n 32 @n = 8192, 3journal_read11 @n 137 void @n(const char *path, GQueue *queue)parse_timestamp11 @n 126 @n(const char *p)import_old_timestamp11 @n 92 @n(const char *p)>journal_commit_record11 @n 67 static void @n(GQueue *queue, struct record *record)2journal_write11 @n 46 bool @n(const char *path, GQueue *queue);journal_write_record11 @n 34 static void @n(gpointer data, gpointer user_data) 'record_clear9 @n 60 void @n(struct record *record) 'record_free9 @n 54 void @n(struct record *record)'record_deinit9 @n 45 void @n(struct record *record) 4record_dup9 @n 38 struct record *@n(const struct record *src) ?record_copy9 @n 27 void @n(struct record *dest, const struct record *src)(timer_save_journal8 @n 28 @n(G_GNUC_UNUSED gpointer data) as_cleanup7 @n 744 void @n(void)Iscrobbler_free_callback7 @n 737 static void @n(gpointer data, G_GNUC_UNUSED gpointer user_data)as_save_cache7 @n 732 void @n(void)Iscrobbler_save_callback7 @n 716 static void @n(gpointer data, G_GNUC_UNUSED gpointer user_data)5scrobbler_schedule_submit7 @n 705 static void @n(struct scrobbler *scrobbler)+scrobbler_submit_timer7 @n 693 static gboolean @n(gpointer data),as_init7 @n 686 void @n(GSList *scrobbler_configs)Iscrobbler_new_callback7 @n 666 static void @n(gpointer data, G_GNUC_UNUSED gpointer user_data)Eas_songchange7 @n 624 @n(const char *file, const char *artist, const char *track,;scrobbler_push_callback7 @n 612 static void @n(gpointer data, gpointer user_data))scrobbler_submit7 @n 549 @n(struct scrobbler *scrobbler)8as_now_playing7 @n 531 void @n(const char *artist, const char *track,(;scrobbler_schedule_now_playing_callback7 @n 519 static void @n(gpointer data, gpointer user_data)Iscrobbler_send_now_playing7 @n 487 static void @n(struct scrobbler *scrobbler, const char *artist,)scrobbler_schedule_handshake7 @n 477 @n(struct scrobbler *scrobbler)+scrobbler_handshake_timer7 @n 464 static gboolean @n(gpointer data)5scrobbler_handshake7 @n 434 static void @n(struct scrobbler *scrobbler)Fas_md57 @n 423 static char *@n(const char *password, const char *timestamp) as_timestamp7 @n 414 char *@n(void)=scrobbler_submit_callback7 @n 362 @n(size_t length, const char *response, void *data)+scrobbler_queue_remove_oldest7 @n 351 @n(GQueue *queue, unsigned count)Iscrobbler_handshake_callback7 @n 288 static void @n(size_t length, const char *response, void *data) @next_line7 @n 273 static char *@n(const char **input_r, const char *end)#;scrobbler_parse_handshake_response7 @n 240 @n(struct scrobbler *scrobbler, const char *line) =scrobbler_parse_submit_response7 @n 212 static as_submitting @n(const char *scrobbler_name,5scrobbler_increase_interval7 @n 198 static void @n(struct scrobbler *scrobbler) Xadd_var_i7 @n 187 static void @n(GString * s, const char *key, signed char idx, const char *val)Gadd_var7 @n 182 static void @n(GString * s, const char *key, const char *val) Gfirst_var7 @n 177 static void @n(GString * s, const char *key, const char *val)@add_var_internal7 @n 159 static void @n(GString * s, char sep, const char *key,5scrobbler_free7 @n 141 static void @n(struct scrobbler *scrobbler)Irecord_free_callback7 @n 132 static void @n(gpointer data, G_GNUC_UNUSED gpointer user_data)Lscrobbler_new7 @n 111 static struct scrobbler *@n(const struct scrobbler_config *config) scrobbler7 @n 77 struct @n {as_submitting7 @n 75 } @n; AS_SUBMIT_HANDSHAKE7 @n 74 @n, AS_SUBMIT_FAILED7 @n 73 @n, AS_SUBMIT_OK7 @n 72 @n, SCROBBLER_STATE_SUBMITTING7 @n 68 @n, SCROBBLER_STATE_READY7 @n 63 @n, SCROBBLER_STATE_HANDSHAKE7 @n 58 @n, SCROBBLER_STATE_NOTHING7 @n 52 @n,scrobbler_state7 @n 47 enum @n {MAX_SUBMIT_COUNT7 @n 41 #@d @n 10AS_CLIENT_VERSION7 @n 38 #@d @n VERSION AS_CLIENT_ID7 @n 37 #@d @n "mcn")record_is_defined6 @n 109 @n(const struct record *record)scrobbler_config6 @n 62 struct @n {config6 @n 50 struct @n {2http_client_callback_t6 @n 48 @t void @n(size_t, const char *, void *);record6 @n 38 struct @n {MPDCRON_MODULE6 @n 30 #@d @n "scrobbler"MPDCRON_GUARD_SCROBBLER_DEFS_H6 @n 24 #@d @n 1Pmodule_update_run5 @n 324 @n(const struct mpd_connection *conn, const struct mpd_status *status)Pmodule_options_run5 @n 304 @n(const struct mpd_connection *conn, const struct mpd_status *status)/module_output_run5 @n 284 @n(const struct mpd_connection *conn)Pmodule_mixer_run5 @n 264 @n(const struct mpd_connection *conn, const struct mpd_status *status)Lmodule_player_run5 @n 243 @n(const struct mpd_connection *conn, const struct mpd_song *song,/module_queue_run5 @n 223 @n(const struct mpd_connection *conn)/module_stored_playlist_run5 @n 203 @n(const struct mpd_connection *conn)Nmodule_database_run5 @n 183 @n(const struct mpd_connection *conn, const struct mpd_stats *stats) module_close5 @n 175 @n(int gclose) 6module_load5 @n 169 @n(const char *modname, GKeyFile *config_fd)Rmodule_process_ret5 @n 135 @n(int ret, struct module_data *mod, GSList **slink_r, GSList **slist_r).module_destroy_one5 @n 117 @n(gpointer data, gpointer userdata)5module_init_one5 @n 73 @n(const char *modname, GKeyFile *config_fd) -module_path5 @n 39 @n(const char *modname, int *user_r) module_data5 @n 29 struct @n {hooker_run_hook3 @n 61 @n(const char *name)hooker_increment3 @n 45 @n(const char *name) hook_calls3 @n 27 static struct @n { conf_fr PERMISSION_NONE mpdcron_parse_gscrobbler_handshake_oc db_insert_g handle_rmtag___cmd_listinfo_internalMevent_databasemodule_database_runefief 8 \(Px@tDd@T,h 0   Dh<`,h4T( xd< L@l Ll4 8 L` MPDCRON_MODULE106 @n 35 #@d @n "example"MPDCRON_GUARD_CRON_DEFS_H62 @n 21 #@d @n 1HANDLE_SIGNAL61 @n 181 #undef @nHANDLE_SIGNAL61 @n 169 #@d @n(sig) \MPDCRON_INTERNAL58 @n 49 #@d @n 1DEFAULT_DATE_FORMAT_SIZE58 @n 47 #@d @n 64(DEFAULT_DATE_FORMAT58 @n 46 #@d @n "%Y-%m-%d %H-%M-%S %Z"DEFAULT_LOG_LEVEL58 @n 44 #@d @n 0DEFAULT_MPD_TIMEOUT58 @n 43 #@d @n 0DEFAULT_MPD_RECONNECT58 @n 42 #@d @n 5DEFAULT_PID_KILL_WAIT58 @n 41 #@d @n 3 MODULE_RUN_FUNC58 @n 39 #@d @n "mpdcron_run"!MODULE_CLOSE_FUNC58 @n 38 #@d @n "mpdcron_close" MODULE_INIT_FUNC58 @n 37 #@d @n "mpdcron_init" DOT_MODULES58 @n 35 #@d @n "modules" DOT_HOOKS58 @n 34 #@d @n "hooks" DOT_MPDCRON58 @n 33 #@d @n "." PACKAGEENV_MPD_PASSWORD58 @n 31 #@d @n "MPD_PASSWORD" ENV_MPD_PORT58 @n 30 #@d @n "MPD_PORT" ENV_MPD_HOST58 @n 29 #@d @n "MPD_HOST" ENV_HOME_DIR58 @n 28 #@d @n "MPDCRON_DIR"9PERMISSION_ALL45 @n 49 #@d @n@5(PERMISSION_SELECT | PERMISSION_UPDATE)MPD_TOKENIZER_H50 @n 21 #@d @nMPDCRON_WELCOME_MESSAGE46 @n 27 #@d @n "OK MPDCRON " MPDCRON_PARSER_SUCCESS38 @n 55 @n, MPDCRON_PARSER_PAIR38 @n 67 @n, MPDCRON_PARSER_MALFORMED38 @n 50 @n, MPDCRON_PARSER_ERROR38 @n 61 @n,MPDCRON_MODULE6 @n 30 #@d @n "scrobbler"MPDCRON_MODULE45 @n 28 #@d @n "stats" MPDCRON_MODULE21 @n 27 #@d @n "notification" MPDCRON_INIT_SUCCESS24 @n 33 @n = 0, /** Success */MPDCRON_INIT_FAILURE24 @n 34 @n, /** Failure */MPDCRON_GUARD_WALRUS_DEFS_H52 @n 21 #@d @n 1MPDCRON_GUARD_UTILS_H15 @n 24 #@d @n 1MPDCRON_GUARD_STATS_SQLITE_H49 @n 21 #@d @n 1MPDCRON_GUARD_STATS_DEFS_H45 @n 21 #@d @n 1MPDCRON_GUARD_SCROBBLER_DEFS_H6 @n 24 #@d @n 1"MPDCRON_GUARD_NOTIFICATION_DEFS_H21 @n 21 #@d @n 1MPDCRON_GUARD_MODULE_H24 @n 21 #@d @n 1MPDCRON_GUARD_CRON_CONFIG_H58 @n 21 #@d @n 1'MPDCRON_EVENT_UNLOAD24 @n 41 @n, /** Unload the module **/!MPDCRON_EVENT_SUCCESS24 @n 38 @n = 0, /** Success **/HMPDCRON_EVENT_RECONNECT_NOW24 @n 40 @n, /** Schedule a reconnection to mpd server immediately. **/;MPDCRON_EVENT_RECONNECT24 @n 39 @n, /** Schedule a reconnection to mpd server **/MPDCRON_EUGENE_DEFS_H38 @n 21 #@d @n 1 MPDCRON_ERROR_SERVER_UNK38 @n 43 @n, MPDCRON_ERROR_OVERFLOW38 @n 41 @n, MPDCRON_ERROR_NO_UNIX38 @n 38 @n, MPDCRON_ERROR_MALFORMED38 @n 40 @n, MPDCRON_ERROR_EOF38 @n 42 @n, MPDCRON_ERROR_BAD_ADDRESS38 @n 39 @n,MAX_SUBMIT_COUNT7 @n 41 #@d @n 10MAX_RESPONSE_BODY12 @n 32 @n = 8192, G_LOG_DOMAIN24 @n 26 #@d @n MPDCRON_MODULEG_GNUC_PRINTF41 @n 100 @n(2, 3)G_GNUC_PRINTF41 @n 135 @n(3, 4)ENV_MPDCRON_PORT38 @n 32 #@d @n "MPDCRON_PORT"#ENV_MPDCRON_PASSWORD38 @n 33 #@d @n "MPDCRON_PASSWORD"ENV_MPDCRON_HOST38 @n 31 #@d @n "MPDCRON_HOST" DEFAULT_PORT38 @n 35 #@d @n 6601 DEFAULT_PORT45 @n 43 #@d @n 6601DEFAULT_MAX_CONNECTIONS45 @n 44 #@d @n 16DEFAULT_HOSTNAME38 @n 34 #@d @n "localhost" DEFAULT_HOST45 @n 42 #@d @n "any" DB_VERSION35 @n 68 #@d @n 10COMMAND_RETURN_OK45 @n 70 @n = 0,COMMAND_RETURN_KILL45 @n 71 @n = 10,COMMAND_RETURN_ERROR45 @n 69 @n = -1,COMMAND_RETURN_CLOSE45 @n 72 @n = 20,COMMAND_ARGV_MAX45 @n 51 #@d @n 16 AS_SUBMIT_OK7 @n 72 @n, AS_SUBMIT_HANDSHAKE7 @n 74 @n, AS_SUBMIT_FAILED7 @n 73 @n,AS_CLIENT_VERSION7 @n 38 #@d @n VERSION AS_CLIENT_ID7 @n 37 #@d @n "mcn"ACK_ERROR_UNKNOWN45 @n 65 @n = 4,ACK_ERROR_PERMISSION45 @n 64 @n = 3,ACK_ERROR_PASSWORD45 @n 63 @n = 2,ACK_ERROR_NO_TAGS49 @n 84 @n = 102,ACK_ERROR_INVALID_TAG49 @n 83 @n = 101,ACK_ERROR_DATABASE_VERSION49 @n 73 @n = 52,ACK_ERROR_DATABASE_UPDATE49 @n 77 @n = 56,ACK_ERROR_DATABASE_STEP49 @n 80 @n = 59,ACK_ERROR_DATABASE_SELECT49 @n 76 @n = 55,ACK_ERROR_DATABASE_RESET49 @n 81 @n = 60,ACK_ERROR_DATABASE_PREPARE49 @n 78 @n = 57,ACK_ERROR_DATABASE_OPEN49 @n 71 @n = 50,ACK_ERROR_DATABASE_INSERT49 @n 75 @n = 54,ACK_ERROR_DATABASE_CREATE49 @n 72 @n = 51,ACK_ERROR_DATABASE_BIND49 @n 79 @n = 58,ACK_ERROR_DATABASE_AUTH49 @n 74 @n = 53,ACK_ERROR_ARG45 @n 62 @n = 1,  __.VERSION __.VERSION 5  __.COMPRESS __.COMPRESS ddefine ttypedef __.COMPNAME __.COMPNAME ~<H@Dl `4ld<l8PL` h0L@TAmpdcron_count_expr46 @n 1216 @n(struct mpdcron_connection *conn, const char *expr,Ampdcron_count_genre_expr46 @n 1203 @n(struct mpdcron_connection *conn, const char *expr,Ampdcron_count_artist_expr46 @n 1190 @n(struct mpdcron_connection *conn, const char *expr,Ampdcron_count_album_expr46 @n 1177 @n(struct mpdcron_connection *conn, const char *expr,Ampdcron_listtags_genre_expr46 @n 1164 @n(struct mpdcron_connection *conn, const char *expr,Ampdcron_listtags_artist_expr46 @n 1151 @n(struct mpdcron_connection *conn, const char *expr,<mpdcron_parse_changes46 @n 185 @n(struct mpdcron_connection *conn, int *changes)?mpdcron_parse_artists46 @n 321 @n(struct mpdcron_connection *conn, GSList **values)?mpdcron_parse_albums46 @n 234 @n(struct mpdcron_connection *conn, GSList **values)mpdcron_module24 @n 62 struct @n {.9mpdcron_love_genre_expr46 @n 894 @n(struct mpdcron_connection *conn, bool love,9mpdcron_love_expr46 @n 906 @n(struct mpdcron_connection *conn, bool love,9mpdcron_love_artist_expr46 @n 882 @n(struct mpdcron_connection *conn, bool love,9mpdcron_love_album_expr46 @n 870 @n(struct mpdcron_connection *conn, bool love,Ampdcron_listtags_expr46 @n 1125 @n(struct mpdcron_connection *conn, const char *expr,Ampdcron_listtags_album_expr46 @n 1138 @n(struct mpdcron_connection *conn, const char *expr,.mpdcron_listinfo_genre_expr46 @n 846 @n(struct mpdcron_connection *conn,.mpdcron_listinfo_expr46 @n 858 @n(struct mpdcron_connection *conn,.mpdcron_listinfo_artist_expr46 @n 834 @n(struct mpdcron_connection *conn,.mpdcron_listinfo_album_expr46 @n 822 @n(struct mpdcron_connection *conn,.mpdcron_list_genre_expr46 @n 798 @n(struct mpdcron_connection *conn,.mpdcron_list_expr46 @n 810 @n(struct mpdcron_connection *conn,.mpdcron_list_artist_expr46 @n 786 @n(struct mpdcron_connection *conn,.mpdcron_list_album_expr46 @n 774 @n(struct mpdcron_connection *conn,:mpdcron_kill_genre_expr46 @n 942 @n(struct mpdcron_connection *conn, bool kkill,:mpdcron_kill_expr46 @n 953 @n(struct mpdcron_connection *conn, bool kkill,:mpdcron_kill_artist_expr46 @n 930 @n(struct mpdcron_connection *conn, bool kkill,:mpdcron_kill_album_expr46 @n 918 @n(struct mpdcron_connection *conn, bool kkill,mpdcron_init_retval24 @n 32 enum @n {mpdcron_event_retval24 @n 37 enum @n { pmpdcron_error38 @n 37 enum @n {mpdcron_entity38 @n 80 struct @n {2mpdcron_connection_new46 @n 683 @n(const char *hostname, unsigned port).mpdcron_connection_free46 @n 748 @n(struct mpdcron_connection *conn)mpdcron_connection38 @n 93 struct @n {mpdcron_config24 @n 44 struct @n {duAmpdcron_addtag_genre_expr46 @n 1055 @n(struct mpdcron_connection *conn, const char *expr,Ampdcron_addtag_expr46 @n 1013 @n(struct mpdcron_connection *conn, const char *expr,Ampdcron_addtag_artist_expr46 @n 1027 @n(struct mpdcron_connection *conn, const char *expr,Ampdcron_addtag_album_expr46 @n 1041 @n(struct mpdcron_connection *conn, const char *expr,Pmodule_update_run5 @n 324 @n(const struct mpd_connection *conn, const struct mpd_status *status)/module_stored_playlist_run5 @n 203 @n(const struct mpd_connection *conn)/module_queue_run5 @n 223 @n(const struct mpd_connection *conn)Rmodule_process_ret5 @n 135 @n(int ret, struct module_data *mod, GSList **slink_r, GSList **slist_r)Lmodule_player_run5 @n 243 @n(const struct mpd_connection *conn, const struct mpd_song *song, -module_path5 @n 39 @n(const char *modname, int *user_r)/module_output_run5 @n 284 @n(const struct mpd_connection *conn)Pmodule_options_run5 @n 304 @n(const struct mpd_connection *conn, const struct mpd_status *status)Pmodule_mixer_run5 @n 264 @n(const struct mpd_connection *conn, const struct mpd_status *status) 6module_load5 @n 169 @n(const char *modname, GKeyFile *config_fd)5module_init_one5 @n 73 @n(const char *modname, GKeyFile *config_fd).module_destroy_one5 @n 117 @n(gpointer data, gpointer userdata)Nmodule_database_run5 @n 183 @n(const struct mpd_connection *conn, const struct mpd_stats *stats) P  @d $< p \0TLD dDD@,| tlPla ?event_run56 @n 220 @n(struct mpd_connection *conn, enum mpd_idle event) *event_update56 @n 196 @n(struct mpd_connection *conn)*event_options56 @n 172 @n(struct mpd_connection *conn) *event_output56 @n 149 @n(struct mpd_connection *conn) *event_mixer56 @n 125 @n(struct mpd_connection *conn) )event_player56 @n 97 @n(struct mpd_connection *conn) 7event_queue56 @n 73 @n(struct mpd_connection *conn G_GNUC_UNUSED))event_stored_playlist56 @n 50 @n(struct mpd_connection *conn))event_database56 @n 26 @n(struct mpd_connection *conn) >event_player47 @n 176 @n(G_GNUC_UNUSED const struct mpd_connection *conn,Jhandle_password41 @n 1200 @n(struct client *client, G_GNUC_UNUSED int argc, char **argv)<handle_count_genre41 @n 1158 @n(struct client *client, int argc, char **argv)<handle_count_album41 @n 1116 @n(struct client *client, int argc, char **argv)<handle_count_artist41 @n 1074 @n(struct client *client, int argc, char **argv) ;handle_rmtag41 @n 827 @n(struct client *client, int argc, char **argv);handle_rate_genre41 @n 709 @n(struct client *client, int argc, char **argv);handle_rate_artist41 @n 625 @n(struct client *client, int argc, char **argv);handle_rate_album41 @n 667 @n(struct client *client, int argc, char **argv) ;handle_rate41 @n 583 @n(struct client *client, int argc, char **argv);handle_love_genre41 @n 561 @n(struct client *client, int argc, char **argv);handle_love_artist41 @n 539 @n(struct client *client, int argc, char **argv);handle_love_album41 @n 517 @n(struct client *client, int argc, char **argv) ;handle_love41 @n 495 @n(struct client *client, int argc, char **argv)<handle_listtags_genre41 @n 1000 @n(struct client *client, int argc, char **argv);handle_listtags_artist41 @n 968 @n(struct client *client, int argc, char **argv);handle_listtags_album41 @n 935 @n(struct client *client, int argc, char **argv);handle_listtags41 @n 903 @n(struct client *client, int argc, char **argv);handle_listinfo_genre41 @n 463 @n(struct client *client, int argc, char **argv);handle_listinfo_artist41 @n 398 @n(struct client *client, int argc, char **argv);handle_listinfo_album41 @n 430 @n(struct client *client, int argc, char **argv);handle_listinfo41 @n 366 @n(struct client *client, int argc, char **argv);handle_list_genre41 @n 338 @n(struct client *client, int argc, char **argv);handle_list_artist41 @n 281 @n(struct client *client, int argc, char **argv);handle_list_album41 @n 309 @n(struct client *client, int argc, char **argv) ;handle_list41 @n 253 @n(struct client *client, int argc, char **argv);handle_kill_genre41 @n 231 @n(struct client *client, int argc, char **argv);handle_kill_artist41 @n 209 @n(struct client *client, int argc, char **argv);handle_kill_album41 @n 187 @n(struct client *client, int argc, char **argv) ;handle_kill41 @n 165 @n(struct client *client, int argc, char **argv) <handle_count41 @n 1032 @n(struct client *client, int argc, char **argv);handle_addtag_genre41 @n 808 @n(struct client *client, int argc, char **argv);handle_addtag_artist41 @n 789 @n(struct client *client, int argc, char **argv);handle_addtag_album41 @n 770 @n(struct client *client, int argc, char **argv);handle_addtag41 @n 751 @n(struct client *client, int argc, char **argv) Gfirst_var7 @n 177 static void @n(GString * s, const char *key, const char *val) file_load14 @n 104 @n(GKeyFile *fd) =file_load26 @n 32 @n(const struct mpdcron_config *conf, GKeyFile *fd) file_load23 @n 35 @n(GKeyFile *fd) file_cleanup14 @n 148 @n(void) file_cleanup26 @n 201 @n(void) file_cleanup23 @n 130 @n(void) >event_update17 @n 270 @n(G_GNUC_UNUSED const struct mpd_connection *conn,Gevent_resolve36 @n 202 @n(GObject *source, GAsyncResult *result, gpointer userdata)Aevent_read_line36 @n 82 @n(G_GNUC_UNUSED GObject *source, GAsyncResult *result, >event_player13 @n 199 @n(G_GNUC_UNUSED const struct mpd_connection *conn, >event_player17 @n 183 @n(G_GNUC_UNUSED const struct mpd_connection *conn,>event_options17 @n 244 @n(G_GNUC_UNUSED const struct mpd_connection *conn, >event_mixer17 @n 227 @n(G_GNUC_UNUSED const struct mpd_connection *conn,Ievent_incoming36 @n 125 @n(G_GNUC_UNUSED GSocketService *srv, GSocketConnection *conn, Aevent_flush36 @n 58 @n(G_GNUC_UNUSED GObject *source, GAsyncResult *result,>event_database17 @n 139 @n(G_GNUC_UNUSED const struct mpd_connection *conn, l,\ P( P\88 ht,x0P$T$T 4`db_generic_data49 @n 30 struct @n {connection_quark46 @n 55 @n(void)config45 @n 78 struct @n {:db_insert_artist35 @n 470 @n(const struct mpd_song *song, bool increment,:db_insert_album35 @n 503 @n(const struct mpd_song *song, bool increment,db_initialized35 @n 975 @n(void);db_init35 @n 981 @n(const char *path, bool create, bool readonly, .db_has_song35 @n 428 @n(const char *uri, GError **error) /db_has_genre35 @n 389 @n(const char *name, GError **error)/db_has_artist35 @n 311 @n(const char *name, GError **error) /db_has_album35 @n 350 @n(const char *name, GError **error)+db_generic_data_free35 @n 278 @n(struct db_generic_data *data)db_end_transaction35 @n 1115 @n(GError **error) db_create35 @n 899 @n(GError **error)Idb_count_song_expr35 @n 1732 @n(const char *expr, int count, int *changes, GError **error)Idb_count_genre_expr35 @n 1711 @n(const char *expr, int count, int *changes, GError **error)Idb_count_artist_expr35 @n 1669 @n(const char *expr, int count, int *changes, GError **error)Idb_count_album_expr35 @n 1690 @n(const char *expr, int count, int *changes, GError **error) db_close35 @n 1057 @n(void) db_check_ver35 @n 943 @n(GError **error)Odb_add_song_tag_expr35 @n 2089 @n(const char *expr, const char *tag, int *changes, GError **error)Odb_add_genre_tag_expr35 @n 2065 @n(const char *expr, const char *tag, int *changes, GError **error)Odb_add_artist_tag_expr35 @n 2017 @n(const char *expr, const char *tag, int *changes, GError **error)Odb_add_album_tag_expr35 @n 2041 @n(const char *expr, const char *tag, int *changes, GError **error)Jcurl_source_prepare12 @n 279 @n(G_GNUC_UNUSED GSource *source, G_GNUC_UNUSED gint *timeout_)<curl_source_dispatch12 @n 304 static gboolean @n(G_GNUC_UNUSED GSource *source,<curl_source_check12 @n 289 static gboolean @n(G_GNUC_UNUSED GSource *source) 3cover_find22 @n 25 @n(const char *artist, const char *album) .count_song25 @n 151 @n(struct mpdcron_connection *conn, @count_genre25 @n 110 @n(struct mpdcron_connection *conn, const char *expr, ?count_artist25 @n 28 @n(struct mpdcron_connection *conn, const char *expr, ?count_album25 @n 69 @n(struct mpdcron_connection *conn, const char *expr,config6 @n 50 struct @n {config21 @n 32 struct @n {conf_pid_file_proc2 @n 31 @n(void) conf_init2 @n 44 @n(void) conf_free2 @n 77 @n(void)command_return45 @n 68 enum @n { Bcommand_putv41 @n 84 @n(struct client *client, const char *fmt, va_list args)1command_process41 @n 1367 @n(struct client *client, char *line) #command_ok41 @n 76 @n(struct client *client) command_lookup41 @n 1275 @n(const char *name)Ecommand_error_v41 @n 112 @n(struct client *client, enum ack error, const char *fmt,Dcommand_checked_lookup41 @n 1339 @n(struct client *client, unsigned permission, int argc,@command_check_request41 @n 1297 @n(const struct command *cmd, struct client *client,&command_authorizer41 @n 49 @n(void *userdata, int what,command41 @n 38 struct @n { $cmd_unkill29 @n 280 @n(int argc, char **argv)0cmd_rmtag_internal43 @n 194 @n(const char *tag, const char *expr, $cmd_rmtag43 @n 245 @n(int argc, char **argv)3cmd_rate_internal28 @n 187 @n(const char *expr, const char *rating, $cmd_rate28 @n 238 @n(int argc, char **argv)Ccmd_love_internal33 @n 191 @n(bool love, const char *expr, bool artist, bool album, $cmd_love33 @n 280 @n(int argc, char **argv)cmd_listtags_internal44 @n 261 @n(const char *expr, $cmd_listtags44 @n 312 @n(int argc, char **argv)8cmd_listinfo_internal31 @n 228 @n(const char *expr, bool artist, bool album, PX48@ X,t H $  T8 \ ` |\x$T x p h$ 8 @ `@  |L Pinit106 @n 70 int @n(G_GNUC_UNUSED const struct mpdcron_config *conf, GKeyFile *fd)#main61 @n 95 @n(int argc, char **argv) internal_cleanup61 @n 54 @n(keyfile_load_modules59 @n 168 @n(GKeyFile **cfd_r) keyfile_load59 @n 26 @n(GKeyFile **cfd_r) Mlog_handler57 @n 58 @n(const gchar *domain, GLogLevelFlags level, const gchar *message, 7log_output57 @n 26 @n(const gchar *domain, GLogLevelFlags level,loop_disconnect55 @n 181 @n(void) loop_connect55 @n 174 @n(void)loop_schedule_idle55 @n 151 @n(void)loop_schedule_reconnect55 @n 143 @n(void) .loop_idle55 @n 91 @n(G_GNUC_UNUSED GIOChannel *source,)loop_reconnect55 @n 52 @n(G_GNUC_UNUSED gpointer data) loop_failure55 @n 39 @n(void)$main48 @n 118 @n(int argc, char **argv)>init47 @n 128 @n(const struct mpdcron_config *conf, GKeyFile *fd)@listtags_song44 @n 207 @n(struct mpdcron_connection *conn, const char *expr)@listtags_genre44 @n 150 @n(struct mpdcron_connection *conn, const char *expr)?listtags_album44 @n 85 @n(struct mpdcron_connection *conn, const char *expr)?listtags_artist44 @n 28 @n(struct mpdcron_connection *conn, const char *expr) @list_song42 @n 101 @n(struct mpdcron_connection *conn, const char *expr) ?list_genre42 @n 77 @n(struct mpdcron_connection *conn, const char *expr) ?list_album42 @n 52 @n(struct mpdcron_connection *conn, const char *expr) ?list_artist42 @n 28 @n(struct mpdcron_connection *conn, const char *expr) module_data5 @n 29 struct @n { module_close5 @n 175 @n(int gclose)map35 @n 2119 struct @n {map35 @n 2283 struct @n {map35 @n 2365 struct @n {map35 @n 2201 struct @n {$main34 @n 119 @n(int argc, char **argv) Klove_song33 @n 154 @n(struct mpdcron_connection *conn, bool love, const char *expr) Klove_genre33 @n 112 @n(struct mpdcron_connection *conn, bool love, const char *expr) Jlove_artist33 @n 28 @n(struct mpdcron_connection *conn, bool love, const char *expr) Jlove_album33 @n 70 @n(struct mpdcron_connection *conn, bool love, const char *expr) Lload_string15 @n 38 @n(GKeyFile *fd, const char *grp, const char *name, bool mustload,+load_scrobbler14 @n 35 @n(GKeyFile *fd, const char *grp) Kload_integer15 @n 75 @n(GKeyFile *fd, const char *grp, const char *name, int mustload,load_current_song27 @n 51 @n(void)@listinfo_song31 @n 182 @n(struct mpdcron_connection *conn, const char *expr)@listinfo_genre31 @n 131 @n(struct mpdcron_connection *conn, const char *expr)?listinfo_artist31 @n 28 @n(struct mpdcron_connection *conn, const char *expr)?listinfo_album31 @n 79 @n(struct mpdcron_connection *conn, const char *expr) Lkill_song29 @n 154 @n(struct mpdcron_connection *conn, bool kkill, const char *expr) Lkill_genre29 @n 112 @n(struct mpdcron_connection *conn, bool kkill, const char *expr) Kkill_artist29 @n 28 @n(struct mpdcron_connection *conn, bool kkill, const char *expr) Kkill_album29 @n 70 @n(struct mpdcron_connection *conn, bool kkill, const char *expr) kf_quark15 @n 31 @n(void);journal_write_record11 @n 34 static void @n(gpointer data, gpointer user_data)2journal_write11 @n 46 bool @n(const char *path, GQueue *queue) 3journal_read11 @n 137 void @n(const char *path, GQueue *queue)>journal_commit_record11 @n 67 static void @n(GQueue *queue, struct record *record)Linit13 @n 169 @n(G_GNUC_UNUSED const struct mpdcron_config *conf, GKeyFile *fd)Linit17 @n 116 @n(G_GNUC_UNUSED const struct mpdcron_config *conf, GKeyFile *fd)import_old_timestamp11 @n 92 @n(const char *p)Ohttp_request_writefunction12 @n 386 static size_t @n(void *ptr, size_t size, size_t nmemb, void *stream)>http_request_free_callback12 @n 348 @n(gpointer data, G_GNUC_UNUSED gpointer user_data)6http_request_free12 @n 75 static void @n(struct http_request *request)Hhttp_request_done12 @n 217 static void @n(struct http_request *request, CURLcode result)7http_request_abort12 @n 177 static void @n(struct http_request *request) http_request12 @n 35 struct @n {http_multi_perform12 @n 255 static bool @n(void)http_multi_info_read12 @n 236 @n(void)$http_client_uri_escape12 @n 378 char *@n(const char *src)http_client_update_fds12 @n 115 @n(void):http_client_request12 @n 399 void @n(const char *url, const char *post_data,http_client_init12 @n 325 int @n(void)http_client_finish12 @n 356 @n(void)http_client_find_request12 @n 202 @n(CURL *curl)Mhttp_client_fd_events12 @n 88 static gushort @n(int fd, fd_set *rfds, fd_set *wfds, fd_set *efds)2http_client_callback_t6 @n 48 @t void @n(size_t, const char *, void *);http_client_abort_all_requests12 @n 190 @n(void)host36 @n 33 struct @n {hooker_run_hook3 @n 61 @n(const char *name)hooker_increment3 @n 45 @n(const char *name) hook_calls3 @n 27 static struct @n {;handle_rmtag_genre41 @n 884 @n(struct client *client, int argc, char **argv);handle_rmtag_artist41 @n 865 @n(struct client *client, int argc, char **argv);handle_rmtag_album41 @n 846 @n(struct client *client, int argc, char **argv) X hPPH<l(X|$D<p0L ( d 4  Tadestroy106 @n 108 void @n(void) env_clearenv60 @n 464 @n(void)^env_status_currentsong60 @n 429 @n(struct mpd_connection *conn, struct mpd_song **song, struct mpd_status **status) *env_outputs60 @n 397 @n(struct mpd_connection *conn) Fenv_status60 @n 384 @n(struct mpd_connection *conn, struct mpd_status **status) Denv_stats60 @n 339 @n(struct mpd_connection *conn, struct mpd_stats **stats)*env_list_queue_meta60 @n 318 @n(struct mpd_connection *conn)*env_list_all_meta60 @n 281 @n(struct mpd_connection *conn)/env_export_song60 @n 139 @n(struct mpd_song *song, int envid)'env_export_status60 @n 52 @n(struct mpd_status *status) "env_strstate60 @n 28 @n(enum mpd_state state)dback49 @n 70 enum @n { db_song_data49 @n 43 struct @n {destroy47 @n 164 @n(void)escape46 @n 610 @n(const char *src).eulog34 @n 104 @n(int level, const char *fmt, ...)escape_string35 @n 220 @n(const char *src)dhms20 @n 23 @n(unsigned long t)destroy13 @n 185 @n(void)destroy17 @n 131 @n(void) db_vacuum35 @n 1159 @n(GError **error)2db_update_song35 @n 754 @n(const struct mpd_song *song, int id,2db_update_genre35 @n 719 @n(const struct mpd_song *song, int id,2db_update_artist35 @n 646 @n(const struct mpd_song *song, int id,2db_update_album35 @n 681 @n(const struct mpd_song *song, int id,!db_step35 @n 231 @n(sqlite3_stmt *stmt)db_start_transaction35 @n 1095 @n(GError **error)(db_song_data_free35 @n 287 @n(struct db_song_data *song) 'db_set_sync35 @n 1135 @n(bool on, GError **error)Ddb_set_authorizer35 @n 1076 @n(int (*xAuth)(void *, int, const char *, const char *,Odb_remove_song_tag_expr35 @n 2359 @n(const char *expr, const char *tag, int *changes, GError **error)Odb_remove_genre_tag_expr35 @n 2277 @n(const char *expr, const char *tag, int *changes, GError **error)Odb_remove_artist_tag_expr35 @n 2113 @n(const char *expr, const char *tag, int *changes, GError **error)Odb_remove_album_tag_expr35 @n 2195 @n(const char *expr, const char *tag, int *changes, GError **error)Jdb_rate_song_expr35 @n 1993 @n(const char *expr, int rating, int *changes, GError **error)Jdb_rate_genre_expr35 @n 1972 @n(const char *expr, int rating, int *changes, GError **error)Jdb_rate_artist_expr35 @n 1930 @n(const char *expr, int rating, int *changes, GError **error)Jdb_rate_album_expr35 @n 1951 @n(const char *expr, int rating, int *changes, GError **error) db_quark35 @n 213 @n(void) ;db_process35 @n 1179 @n(const struct mpd_song *song, bool increment,Idb_love_song_expr35 @n 1819 @n(const char *expr, bool love, int *changes, GError **error)Idb_love_genre_expr35 @n 1798 @n(const char *expr, bool love, int *changes, GError **error)Idb_love_artist_expr35 @n 1756 @n(const char *expr, bool love, int *changes, GError **error)Idb_love_album_expr35 @n 1777 @n(const char *expr, bool love, int *changes, GError **error)1db_listinfo_song_expr35 @n 1612 @n(const char *expr, GSList **values,1db_listinfo_genre_expr35 @n 1558 @n(const char *expr, GSList **values,(G_1db_listinfo_artist_expr35 @n 1449 @n(const char *expr, GSList **values,1db_listinfo_album_expr35 @n 1503 @n(const char *expr, GSList **values,Adb_list_song_tag_expr35 @n 2586 @n(const char *expr, GSList **values, GError **error)1db_list_song_expr35 @n 1401 @n(const char *expr, GSList **values,Adb_list_genre_tag_expr35 @n 2538 @n(const char *expr, GSList **values, GError **error)1db_list_genre_expr35 @n 1353 @n(const char *expr, GSList **values,Adb_list_artist_tag_expr35 @n 2441 @n(const char *expr, GSList **values, GError **error)1db_list_artist_expr35 @n 1256 @n(const char *expr, GSList **values,Adb_list_album_tag_expr35 @n 2489 @n(const char *expr, GSList **values, GError **error)1db_list_album_expr35 @n 1304 @n(const char *expr, GSList **values,Jdb_kill_song_expr35 @n 1906 @n(const char *expr, bool kkill, int *changes, GError **error)Jdb_kill_genre_expr35 @n 1885 @n(const char *expr, bool kkill, int *changes, GError **error)Jdb_kill_artist_expr35 @n 1843 @n(const char *expr, bool kkill, int *changes, GError **error)Jdb_kill_album_expr35 @n 1864 @n(const char *expr, bool kkill, int *changes, GError **error):db_insert_song35 @n 572 @n(const struct mpd_song *song, bool increment,:db_insert_genre35 @n 539 @n(const struct mpd_song *song, bool increment, @D(lx4DL dtdH<dp$0D`|,L sig_cleanup61 @n 82 @n(int signum) xload_dbpath51 @n 29 @n(void) song_stopped47 @n 120 @n(void) song_paused47 @n 112 @n(void)song_continued47 @n 106 @n(void) ;song_playing47 @n 95 @n(const struct mpd_song *song, unsigned elapsed) )song_ended47 @n 61 @n(const struct mpd_song *song) )song_started47 @n 55 @n(const struct mpd_song *song) )song_changed47 @n 42 @n(const struct mpd_song *song)_ .validate_tag35 @n 243 @n(const char *tag, GError **error)valid_word_first_char32 @n 35 @n(char ch)valid_word_char32 @n 41 @n(char ch)valid_unquoted_char32 @n 95 @n(char ch)%usage34 @n 43 @n(FILE *outf, int exitval)tokenizer_quark32 @n 29 @n(void).tokenizer_next_word32 @n 47 @n(char **input_p, GError **error_r)/tokenizer_next_unquoted32 @n 101 @n(char **input_p, GError **error_r)/tokenizer_next_string32 @n 149 @n(char **input_p, GError **error_r)/tokenizer_next_param32 @n 213 @n(char **input_p, GError **error_r)(timer_save_journal8 @n 28 @n(G_GNUC_UNUSED gpointer data)1sql_update_song35 @n 889 @n(const char *stmt, const char *expr,1sql_update_genre35 @n 882 @n(const char *stmt, const char *expr,0sql_update_entry35 @n 837 @n(const char *tbl, const char *stmt,1sql_update_artist35 @n 868 @n(const char *stmt, const char *expr,1sql_update_album35 @n 875 @n(const char *stmt, const char *expr, song_stopped13 @n 161 @n(void)-ut song_stopped17 @n 50 @n(void) )song_started13 @n 89 @n(const struct mpd_song *song) *song_started17 @n 100 @n(const struct mpd_song *song)Hsong_repeated13 @n 55 @n(const struct mpd_song *song, int elapsed, int prev_elapsed) <song_playing13 @n 136 @n(const struct mpd_song *song, unsigned elapsed) <song_playing17 @n 106 @n(const struct mpd_song *song, unsigned elapsed). song_paused13 @n 153 @n(void) song_paused17 @n 42 @n(void)e )song_ended13 @n 95 @n(const struct mpd_song *song)song_continued13 @n 147 @n(void)song_continued17 @n 57 @n(void)en )song_changed13 @n 63 @n(const struct mpd_song *song) )song_changed17 @n 63 @n(const struct mpd_song *song) server_start36 @n 294 @n(void)Dserver_schedule_write36 @n 310 @n(struct client *client, const gchar *data, gsize count) server_init36 @n 232 @n(void)$server_flush_write36 @n 319 @n(struct client *client) server_close36 @n 302 @n(void) -server_bind36 @n 239 @n(const char *hostname, int port)+scrobbler_submit_timer7 @n 693 static gboolean @n(gpointer data)=scrobbler_submit_callback7 @n 362 @n(size_t length, const char *response, void *data))scrobbler_submit7 @n 549 @n(struct scrobbler *scrobbler)scrobbler_state7 @n 47 enum @n {Iscrobbler_send_now_playing7 @n 487 static void @n(struct scrobbler *scrobbler, const char *artist,5scrobbler_schedule_submit7 @n 705 static void @n(struct scrobbler *scrobbler)(;scrobbler_schedule_now_playing_callback7 @n 519 static void @n(gpointer data, gpointer user_data))scrobbler_schedule_handshake7 @n 477 @n(struct scrobbler *scrobbler)Iscrobbler_save_callback7 @n 716 static void @n(gpointer data, G_GNUC_UNUSED gpointer user_data)+scrobbler_queue_remove_oldest7 @n 351 @n(GQueue *queue, unsigned count);scrobbler_push_callback7 @n 612 static void @n(gpointer data, gpointer user_data) =scrobbler_parse_submit_response7 @n 212 static as_submitting @n(const char *scrobbler_name,#;scrobbler_parse_handshake_response7 @n 240 @n(struct scrobbler *scrobbler, const char *line)Iscrobbler_new_callback7 @n 666 static void @n(gpointer data, G_GNUC_UNUSED gpointer user_data)Lscrobbler_new7 @n 111 static struct scrobbler *@n(const struct scrobbler_config *config)5scrobbler_increase_interval7 @n 198 static void @n(struct scrobbler *scrobbler)+scrobbler_handshake_timer7 @n 464 static gboolean @n(gpointer data)Iscrobbler_handshake_callback7 @n 288 static void @n(size_t length, const char *response, void *data) |xTd|X4p @d,(h|X<T@px<|,ldrun106 @n 127 int @n(G_GNUC_UNUSED const struct mpd_connection *conn, const struct mpd_status *status) &run_update48 @n 39 @n(int kg, const char *path)%played_long_enough47 @n 36 @n(int elapsed, int length)bbl5scrobbler_handshake7 @n 434 static void @n(struct scrobbler *scrobbler)Iscrobbler_free_callback7 @n 737 static void @n(gpointer data, G_GNUC_UNUSED gpointer user_data)5scrobbler_free7 @n 141 static void @n(struct scrobbler *scrobbler)=scrobbler_config_free_callback14 @n 91 @n(gpointer data, G_GNUC_UNUSED gpointer user_data)scrobbler_config6 @n 62 struct @n { scrobbler7 @n 77 struct @n {#run_cmd34 @n 69 @n(int argc, char **argv) Qrmtag_song43 @n 158 @n(struct mpdcron_connection *conn, const char *tag, const char *expr) Qrmtag_genre43 @n 117 @n(struct mpdcron_connection *conn, const char *tag, const char *expr) Prmtag_artist43 @n 28 @n(struct mpdcron_connection *conn, const char *tag, const char *expr) Prmtag_album43 @n 69 @n(struct mpdcron_connection *conn, const char *tag, const char *expr) 0remove_tag35 @n 255 @n(const char *tags, const char *tag))record_is_defined6 @n 109 @n(const struct record *record)Irecord_free_callback7 @n 132 static void @n(gpointer data, G_GNUC_UNUSED gpointer user_data) 'record_free9 @n 54 void @n(struct record *record) 4record_dup9 @n 38 struct record *@n(const struct record *src)'record_deinit9 @n 45 void @n(struct record *record) ?record_copy9 @n 27 void @n(struct record *dest, const struct record *src) 'record_clear9 @n 60 void @n(struct record *record)record6 @n 38 struct @n { .rate_song28 @n 151 @n(struct mpdcron_connection *conn, @rate_genre28 @n 110 @n(struct mpdcron_connection *conn, const char *expr, ?rate_artist28 @n 28 @n(struct mpdcron_connection *conn, const char *expr, ?rate_album28 @n 69 @n(struct mpdcron_connection *conn, const char *expr,Rquote27 @n 29 @n(const char *src)%played_long_enough13 @n 43 @n(int elapsed, int length)parse_timestamp11 @n 126 @n(const char *p) Enotify_send18 @n 28 @n(const char *icon, const char *summary, const char *body)|g @next_line7 @n 273 static char *@n(const char **input_r, const char *end) mpdcron_song38 @n 70 struct @n {Cmpdcron_send_commandv46 @n 629 @n(struct mpdcron_connection *conn, const char *command,Hmpdcron_send_command46 @n 668 @n(struct mpdcron_connection *conn, const char *command, ...)Ampdcron_rmtag_genre_expr46 @n 1111 @n(struct mpdcron_connection *conn, const char *expr,Ampdcron_rmtag_expr46 @n 1069 @n(struct mpdcron_connection *conn, const char *expr,Ampdcron_rmtag_artist_expr46 @n 1083 @n(struct mpdcron_connection *conn, const char *expr,Ampdcron_rmtag_album_expr46 @n 1097 @n(struct mpdcron_connection *conn, const char *expr,>mpdcron_recv_line46 @n 64 @n(struct mpdcron_connection *conn, gsize *length_r)@mpdcron_rate_genre_expr46 @n 989 @n(struct mpdcron_connection *conn, const char *expr,Ampdcron_rate_expr46 @n 1001 @n(struct mpdcron_connection *conn, const char *expr,@mpdcron_rate_artist_expr46 @n 977 @n(struct mpdcron_connection *conn, const char *expr,@mpdcron_rate_album_expr46 @n 965 @n(struct mpdcron_connection *conn, const char *expr,Dmpdcron_password46 @n 763 @n(struct mpdcron_connection *conn, const char *password)mpdcron_parser_result38 @n 46 enum @n {7mpdcron_parser_feed46 @n 81 @n(struct mpdcron_parser *parser, char *line)mpdcron_parser46 @n 40 struct @n {Bmpdcron_parse_welcome46 @n 571 @n(struct mpdcron_connection *conn, const char *output)?mpdcron_parse_songs46 @n 488 @n(struct mpdcron_connection *conn, GSList **values).mpdcron_parse_single46 @n 145 @n(struct mpdcron_connection *conn)?mpdcron_parse_genres46 @n 404 @n(struct mpdcron_connection *conn, GSList **values) XtH`0`8 \0tL$|T,h|D$`4d8P l$X|H8h0cleanup61 @n 76 @n(void)about61 @n 48 @n(void) @n $cmd_listinfo31 @n 279 @n(int argc, char **argv)8cmd_list_internal42 @n 125 @n(const char *expr, bool artist, bool album, $cmd_list42 @n 176 @n(int argc, char **argv)Dcmd_kill_internal29 @n 191 @n(bool kkill, const char *expr, bool artist, bool album, $cmd_kill29 @n 242 @n(int argc, char **argv) $cmd_hate33 @n 242 @n(int argc, char **argv)2cmd_count_internal25 @n 187 @n(const char *expr, const char *count, $cmd_count25 @n 238 @n(int argc, char **argv)0cmd_addtag_internal40 @n 194 @n(const char *tag, const char *expr, $cmd_addtag40 @n 245 @n(int argc, char **argv)client_destroy36 @n 48 @n(gpointer data)client45 @n 53 struct @n { Bcheck_bool41 @n 148 @n(struct client *client, bool *value_r, const char *s)buffer36 @n 38 struct @n {/bind_callback36 @n 169 @n(gpointer data, gpointer userdata) as_timestamp7 @n 414 char *@n(void)as_submitting7 @n 75 } @n;Eas_songchange7 @n 624 @n(const char *file, const char *artist, const char *track,as_save_cache7 @n 732 void @n(void)8as_now_playing7 @n 531 void @n(const char *artist, const char *track,Fas_md57 @n 423 static char *@n(const char *password, const char *timestamp),as_init7 @n 686 void @n(GSList *scrobbler_configs) as_cleanup7 @n 744 void @n(void) Qaddtag_song40 @n 158 @n(struct mpdcron_connection *conn, const char *tag, const char *expr) Qaddtag_genre40 @n 117 @n(struct mpdcron_connection *conn, const char *tag, const char *expr)Paddtag_artist40 @n 28 @n(struct mpdcron_connection *conn, const char *tag, const char *expr) Paddtag_album40 @n 69 @n(struct mpdcron_connection *conn, const char *tag, const char *expr)@add_var_internal7 @n 159 static void @n(GString * s, char sep, const char *key, Xadd_var_i7 @n 187 static void @n(GString * s, const char *key, signed char idx, const char *val)Gadd_var7 @n 182 static void @n(GString * s, const char *key, const char *val)ack45 @n 61 enum @n {about34 @n 36 @n(void) SQL_VACUUM35 @n 50 @n, SQL_UPDATE_SONG35 @n 62 @n, SQL_UPDATE_GENRE35 @n 65 @n, SQL_UPDATE_ARTIST35 @n 63 @n, SQL_UPDATE_ALBUM35 @n 64 @n, SQL_SET_VERSION35 @n 33 @n, SQL_SET_ENCODING35 @n 35 @n, SQL_PRAGMA_SYNC_ON35 @n 47 @n, SQL_PRAGMA_SYNC_OFF35 @n 48 @n, SQL_INSERT_SONG35 @n 57 @n, SQL_INSERT_GENRE35 @n 60 @n, SQL_INSERT_ARTIST35 @n 58 @n, SQL_INSERT_ALBUM35 @n 59 @n, SQL_HAS_SONG35 @n 52 @n, SQL_HAS_GENRE35 @n 55 @n, SQL_HAS_ARTIST35 @n 53 @n, SQL_HAS_ALBUM35 @n 54 @n, SQL_GET_VERSION35 @n 34 @n, SQL_END_TRANSACTION35 @n 45 @n, SQL_DB_CREATE_SONG35 @n 37 @n, SQL_DB_CREATE_GENRE35 @n 40 @n, SQL_DB_CREATE_ARTIST35 @n 38 @n, SQL_DB_CREATE_ALBUM35 @n 39 @n, SQL_BEGIN_TRANSACTION35 @n 44 @n, SECSPERMIN20 @n 29 #@d @n 60 SECSPERMIN20 @n 41 #undef @n SECSPERHOUR20 @n 28 #@d @n 3600 SECSPERHOUR20 @n 40 #undef @n SECSPERDAY20 @n 27 #@d @n 86400 SECSPERDAY20 @n 39 #undef @n SCROBBLER_STATE_SUBMITTING7 @n 68 @n, SCROBBLER_STATE_READY7 @n 63 @n, SCROBBLER_STATE_NOTHING7 @n 52 @n, SCROBBLER_STATE_HANDSHAKE7 @n 58 @n,PROTOCOL_VERSION45 @n 41 #@d @n "0.1" PROTOCOL_OK41 @n 33 #@d @n "OK" PROCOTOL_ACK41 @n 34 #@d @n "ACK"PERMISSION_UPDATE45 @n 48 #@d @n 2PERMISSION_SELECT45 @n 47 #@d @n 1PERMISSION_NONE45 @n 46 #@d @n@40mpdcron-0.3+git20110303/Makefile.am000066400000000000000000000033741153374523700164640ustar00rootroot00000000000000CLEANFILES= *~ MAINTAINERCLEANFILES= Makefile.in configure aclocal.m4 \ config.h config.h.in INSTALL ACLOCAL_AMFLAGS= -I m4 AUTOMAKE_OPTIONS= dist-bzip2 no-dist-gzip std-options foreign EXTRA_DIST= autogen.sh README.mkd NEWS.mkd \ conf/mpdcron.conf \ conf/hooks/database conf/hooks/mixer conf/hooks/options \ conf/hooks/output conf/hooks/player conf/hooks/playlist \ conf/hooks/queue conf/hooks/stored_playlist conf/hooks/update \ conf/hooks/dump-outputs.rb conf/hooks/dump-playlists.rb \ conf/hooks/dump-queue.rb conf/hooks/dump-song.bash \ conf/hooks/dump-stats.bash conf/hooks/dump-status.bash \ conf/hooks/submit.rb \ conf/modules/Makefile conf/modules/example.c SUBDIRS = src data zsh-completion . doc_DATA = README.mkd NEWS.mkd doc_confdir= $(docdir)/conf doc_conf_DATA= conf/mpdcron.conf doc_conf_hooksdir= $(doc_confdir)/hooks doc_conf_hooks_DATA= conf/hooks/database conf/hooks/mixer conf/hooks/options \ conf/hooks/output conf/hooks/player conf/hooks/playlist \ conf/hooks/queue conf/hooks/stored_playlist conf/hooks/update \ conf/hooks/dump-outputs.rb conf/hooks/dump-playlists.rb \ conf/hooks/dump-queue.rb conf/hooks/dump-song.bash \ conf/hooks/dump-stats.bash conf/hooks/dump-status.bash \ conf/hooks/submit.rb doc_conf_modulesdir=$(doc_confdir)/modules doc_conf_modules_DATA= conf/modules/example.c conf/modules/Makefile checksum: dist @echo "SHA1 $(PACKAGE)-$(VERSION).tar.bz2" sha1sum $(PACKAGE)-$(VERSION).tar.bz2 > $(PACKAGE)-$(VERSION).tar.bz2.sha1sum @echo "SIGN $(PACKAGE)-$(VERSION).tar.bz2" gpg --detach-sign --armor $(PACKAGE)-$(VERSION).tar.bz2 upload: @echo "UPLOAD $(PACKAGE)-$(VERSION).tar.bz2*" scp $(PACKAGE)-$(VERSION).tar.bz2* bach.exherbo.org:public_html/mpdcron mpdcron-0.3+git20110303/Makefile.in000066400000000000000000000733651153374523700165040ustar00rootroot00000000000000# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = . DIST_COMMON = $(am__configure_deps) $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in $(srcdir)/config.h.in \ $(top_srcdir)/configure COPYING compile config.guess \ config.sub depcomp install-sh ltmain.sh missing mkinstalldirs ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compiler_flags.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ html-recursive info-recursive install-data-recursive \ install-dvi-recursive install-exec-recursive \ install-html-recursive install-info-recursive \ install-pdf-recursive install-ps-recursive install-recursive \ installcheck-recursive installdirs-recursive pdf-recursive \ ps-recursive uninstall-recursive am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__installdirs = "$(DESTDIR)$(docdir)" "$(DESTDIR)$(doc_confdir)" \ "$(DESTDIR)$(doc_conf_hooksdir)" \ "$(DESTDIR)$(doc_conf_modulesdir)" DATA = $(doc_DATA) $(doc_conf_DATA) $(doc_conf_hooks_DATA) \ $(doc_conf_modules_DATA) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ distdir dist dist-all distcheck ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ { test ! -d "$(distdir)" \ || { find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -fr "$(distdir)"; }; } am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" GZIP_ENV = --best DIST_ARCHIVES = $(distdir).tar.bz2 distuninstallcheck_listfiles = find . -type f -print distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GITHEAD = @GITHEAD@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ MPDCRON_CFLAGS = @MPDCRON_CFLAGS@ NM = @NM@ NMEDIT = @NMEDIT@ NOTIFY_SEND = @NOTIFY_SEND@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STANDARD_MODULES = @STANDARD_MODULES@ STRIP = @STRIP@ VERSION = @VERSION@ VERSION_FULL = @VERSION_FULL@ VERSION_MAJOR = @VERSION_MAJOR@ VERSION_MINOR = @VERSION_MINOR@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ gio_CFLAGS = @gio_CFLAGS@ gio_LIBS = @gio_LIBS@ gio_unix_CFLAGS = @gio_unix_CFLAGS@ gio_unix_LIBS = @gio_unix_LIBS@ glib_CFLAGS = @glib_CFLAGS@ glib_LIBS = @glib_LIBS@ gmodule_CFLAGS = @gmodule_CFLAGS@ gmodule_LIBS = @gmodule_LIBS@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libcurl_CFLAGS = @libcurl_CFLAGS@ libcurl_LIBS = @libcurl_LIBS@ libdaemon_CFLAGS = @libdaemon_CFLAGS@ libdaemon_LIBS = @libdaemon_LIBS@ libdir = @libdir@ libexecdir = @libexecdir@ libmpdclient_CFLAGS = @libmpdclient_CFLAGS@ libmpdclient_LIBS = @libmpdclient_LIBS@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sqlite_CFLAGS = @sqlite_CFLAGS@ sqlite_LIBS = @sqlite_LIBS@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ CLEANFILES = *~ MAINTAINERCLEANFILES = Makefile.in configure aclocal.m4 \ config.h config.h.in INSTALL ACLOCAL_AMFLAGS = -I m4 AUTOMAKE_OPTIONS = dist-bzip2 no-dist-gzip std-options foreign EXTRA_DIST = autogen.sh README.mkd NEWS.mkd \ conf/mpdcron.conf \ conf/hooks/database conf/hooks/mixer conf/hooks/options \ conf/hooks/output conf/hooks/player conf/hooks/playlist \ conf/hooks/queue conf/hooks/stored_playlist conf/hooks/update \ conf/hooks/dump-outputs.rb conf/hooks/dump-playlists.rb \ conf/hooks/dump-queue.rb conf/hooks/dump-song.bash \ conf/hooks/dump-stats.bash conf/hooks/dump-status.bash \ conf/hooks/submit.rb \ conf/modules/Makefile conf/modules/example.c SUBDIRS = src data zsh-completion . doc_DATA = README.mkd NEWS.mkd doc_confdir = $(docdir)/conf doc_conf_DATA = conf/mpdcron.conf doc_conf_hooksdir = $(doc_confdir)/hooks doc_conf_hooks_DATA = conf/hooks/database conf/hooks/mixer conf/hooks/options \ conf/hooks/output conf/hooks/player conf/hooks/playlist \ conf/hooks/queue conf/hooks/stored_playlist conf/hooks/update \ conf/hooks/dump-outputs.rb conf/hooks/dump-playlists.rb \ conf/hooks/dump-queue.rb conf/hooks/dump-song.bash \ conf/hooks/dump-stats.bash conf/hooks/dump-status.bash \ conf/hooks/submit.rb doc_conf_modulesdir = $(doc_confdir)/modules doc_conf_modules_DATA = conf/modules/example.c conf/modules/Makefile all: config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive .SUFFIXES: am--refresh: @: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(top_srcdir)/configure: $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): config.h: stamp-h1 @if test ! -f $@; then \ rm -f stamp-h1; \ $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \ else :; fi stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status @rm -f stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status config.h $(srcdir)/config.h.in: $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f stamp-h1 touch $@ distclean-hdr: -rm -f config.h stamp-h1 mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs distclean-libtool: -rm -f libtool config.lt install-docDATA: $(doc_DATA) @$(NORMAL_INSTALL) test -z "$(docdir)" || $(MKDIR_P) "$(DESTDIR)$(docdir)" @list='$(doc_DATA)'; test -n "$(docdir)" || list=; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(docdir)" || exit $$?; \ done uninstall-docDATA: @$(NORMAL_UNINSTALL) @list='$(doc_DATA)'; test -n "$(docdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ test -n "$$files" || exit 0; \ echo " ( cd '$(DESTDIR)$(docdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(docdir)" && rm -f $$files install-doc_confDATA: $(doc_conf_DATA) @$(NORMAL_INSTALL) test -z "$(doc_confdir)" || $(MKDIR_P) "$(DESTDIR)$(doc_confdir)" @list='$(doc_conf_DATA)'; test -n "$(doc_confdir)" || list=; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(doc_confdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(doc_confdir)" || exit $$?; \ done uninstall-doc_confDATA: @$(NORMAL_UNINSTALL) @list='$(doc_conf_DATA)'; test -n "$(doc_confdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ test -n "$$files" || exit 0; \ echo " ( cd '$(DESTDIR)$(doc_confdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(doc_confdir)" && rm -f $$files install-doc_conf_hooksDATA: $(doc_conf_hooks_DATA) @$(NORMAL_INSTALL) test -z "$(doc_conf_hooksdir)" || $(MKDIR_P) "$(DESTDIR)$(doc_conf_hooksdir)" @list='$(doc_conf_hooks_DATA)'; test -n "$(doc_conf_hooksdir)" || list=; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(doc_conf_hooksdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(doc_conf_hooksdir)" || exit $$?; \ done uninstall-doc_conf_hooksDATA: @$(NORMAL_UNINSTALL) @list='$(doc_conf_hooks_DATA)'; test -n "$(doc_conf_hooksdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ test -n "$$files" || exit 0; \ echo " ( cd '$(DESTDIR)$(doc_conf_hooksdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(doc_conf_hooksdir)" && rm -f $$files install-doc_conf_modulesDATA: $(doc_conf_modules_DATA) @$(NORMAL_INSTALL) test -z "$(doc_conf_modulesdir)" || $(MKDIR_P) "$(DESTDIR)$(doc_conf_modulesdir)" @list='$(doc_conf_modules_DATA)'; test -n "$(doc_conf_modulesdir)" || list=; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(doc_conf_modulesdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(doc_conf_modulesdir)" || exit $$?; \ done uninstall-doc_conf_modulesDATA: @$(NORMAL_UNINSTALL) @list='$(doc_conf_modules_DATA)'; test -n "$(doc_conf_modulesdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ test -n "$$files" || exit 0; \ echo " ( cd '$(DESTDIR)$(doc_conf_modulesdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(doc_conf_modulesdir)" && rm -f $$files # This directory's subdirectories are mostly independent; you can cd # into them and run `make' without going through this Makefile. # To change the values of `make' variables: instead of editing Makefiles, # (1) if the variable is set in `config.status', edit `config.status' # (which will cause the Makefiles to be regenerated when you run `make'); # (2) otherwise, pass the desired values on the `make' command line. $(RECURSIVE_TARGETS): @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ *k*) failcom='fail=yes';; \ esac; \ done; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ list='$(SUBDIRS)'; for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" $(RECURSIVE_CLEAN_TARGETS): @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ *k*) failcom='fail=yes';; \ esac; \ done; \ dot_seen=no; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ rev=''; for subdir in $$list; do \ if test "$$subdir" = "."; then :; else \ rev="$$subdir $$rev"; \ fi; \ done; \ rev="$$rev ."; \ target=`echo $@ | sed s/-recursive//`; \ for subdir in $$rev; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done && test -z "$$fail" tags-recursive: list='$(SUBDIRS)'; for subdir in $$list; do \ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ done ctags-recursive: list='$(SUBDIRS)'; for subdir in $$list; do \ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ done ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: CTAGS CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz $(am__remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2 $(am__remove_distdir) dist-lzma: distdir tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma $(am__remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | xz -c >$(distdir).tar.xz $(am__remove_distdir) dist-tarZ: distdir tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__remove_distdir) dist-shar: distdir shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz $(am__remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__remove_distdir) dist dist-all: distdir tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2 $(am__remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lzma*) \ lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac chmod -R a-w $(distdir); chmod a+w $(distdir) mkdir $(distdir)/_build mkdir $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build \ && ../configure --srcdir=.. --prefix="$$dc_install_base" \ $(DISTCHECK_CONFIGURE_FLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ distuninstallcheck \ && chmod -R a-w "$$dc_install_base" \ && ({ \ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ } || { rm -rf "$$dc_destdir"; exit 1; }) \ && rm -rf "$$dc_destdir" \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && rm -rf $(DIST_ARCHIVES) \ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 $(am__remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @$(am__cd) '$(distuninstallcheck_dir)' \ && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ fi ; \ $(distuninstallcheck_listfiles) ; \ exit 1; } >&2 distcleancheck: distclean @if test '$(srcdir)' = . ; then \ echo "ERROR: distcleancheck can only run from a VPATH build" ; \ exit 1 ; \ fi @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left in build directory after distclean:" ; \ $(distcleancheck_listfiles) ; \ exit 1; } >&2 check-am: all-am check: check-recursive all-am: Makefile $(DATA) config.h installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(docdir)" "$(DESTDIR)$(doc_confdir)" "$(DESTDIR)$(doc_conf_hooksdir)" "$(DESTDIR)$(doc_conf_modulesdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -f Makefile distclean-am: clean-am distclean-generic distclean-hdr \ distclean-libtool distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-docDATA install-doc_confDATA \ install-doc_conf_hooksDATA install-doc_conf_modulesDATA install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-docDATA uninstall-doc_confDATA \ uninstall-doc_conf_hooksDATA uninstall-doc_conf_modulesDATA .MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) all \ ctags-recursive install-am install-strip tags-recursive .PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ all all-am am--refresh check check-am clean clean-generic \ clean-libtool ctags ctags-recursive dist dist-all dist-bzip2 \ dist-gzip dist-lzma dist-shar dist-tarZ dist-xz dist-zip \ distcheck distclean distclean-generic distclean-hdr \ distclean-libtool distclean-tags distcleancheck distdir \ distuninstallcheck dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am \ install-docDATA install-doc_confDATA \ install-doc_conf_hooksDATA install-doc_conf_modulesDATA \ install-dvi install-dvi-am install-exec install-exec-am \ install-html install-html-am install-info install-info-am \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \ uninstall uninstall-am uninstall-docDATA \ uninstall-doc_confDATA uninstall-doc_conf_hooksDATA \ uninstall-doc_conf_modulesDATA checksum: dist @echo "SHA1 $(PACKAGE)-$(VERSION).tar.bz2" sha1sum $(PACKAGE)-$(VERSION).tar.bz2 > $(PACKAGE)-$(VERSION).tar.bz2.sha1sum @echo "SIGN $(PACKAGE)-$(VERSION).tar.bz2" gpg --detach-sign --armor $(PACKAGE)-$(VERSION).tar.bz2 upload: @echo "UPLOAD $(PACKAGE)-$(VERSION).tar.bz2*" scp $(PACKAGE)-$(VERSION).tar.bz2* bach.exherbo.org:public_html/mpdcron # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: mpdcron-0.3+git20110303/NEWS.mkd000066400000000000000000000015521153374523700156750ustar00rootroot00000000000000## News for mpdcron This file lists the major changes between versions. For a more detailed list of every change, see git log. * stats: ability to set count regardless of existing count. * Be more conservative when exporting variables to the environment (fixes: #3) * stats: ability to set rating regardless of existing rating. ### 0.3 * Added stats module to keep statistics of played songs in a sqlite database * Added notification module to send notifications via notify-send * Added scrobbler module to submit songs to Last.fm or Libre.fm * Added module support through GModule * Added initial manpage * Changed name to mpdcron ### 0.2 * Initial public release * Require Mpd's idle mode * Port to libmpdclient-2, mpdhooker now requires libmpdclient-2.1 or newer. ### 0.1 * Unreleased first version mpdcron-0.3+git20110303/README.mkd000066400000000000000000000011521153374523700160520ustar00rootroot00000000000000## What is mpdcron? [mpdcron](http://alip.github/mpdcron) is a cron like daemon for [mpd](http://mpd.wikia.com/). ## Features See [http://alip.github.com/mpdcron/#features](http://alip.github.com/mpdcron/#features) ## Configuration See [http://alip.github.com/mpdcron/configuration/](http://alip.github.com/mpdcron/configuration/) ## Requirements See [http://alip.github.com/mpdcron/#requirements](http://alip.github.com/mpdcron/#requirements) ## Contribute See [http://alip.github.com/mpdcron/#contribute](http://alip.github.com/mpdcron/#contribute) mpdcron-0.3+git20110303/aclocal.m4000066400000000000000000001206311153374523700162640ustar00rootroot00000000000000# generated automatically by aclocal 1.11.1 -*- Autoconf -*- # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, # 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.65],, [m4_warning([this file was generated for autoconf 2.65. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically `autoreconf'.])]) # pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # # Copyright © 2004 Scott James Remnant . # # 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # PKG_PROG_PKG_CONFIG([MIN-VERSION]) # ---------------------------------- AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])# PKG_PROG_PKG_CONFIG # PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # # Check to see whether a particular set of modules exists. Similar # to PKG_CHECK_MODULES(), but does not set variables or print errors. # # # Similar to PKG_CHECK_MODULES, make sure that the first instance of # this or PKG_CHECK_MODULES is called, or make sure to call # PKG_CHECK_EXISTS manually # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_ifval([$2], [$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) # _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) # --------------------------------------------- m4_define([_PKG_CONFIG], [if test -n "$PKG_CONFIG"; then if test -n "$$1"; then pkg_cv_[]$1="$$1" else PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], [pkg_failed=yes]) fi else pkg_failed=untried fi[]dnl ])# _PKG_CONFIG # _PKG_SHORT_ERRORS_SUPPORTED # ----------------------------- AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])# _PKG_SHORT_ERRORS_SUPPORTED # PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], # [ACTION-IF-NOT-FOUND]) # # # Note that if there is a possibility the first call to # PKG_CHECK_MODULES might not happen, you should be sure to include an # explicit call to PKG_PROG_PKG_CONFIG in your configure.ac # # # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"` else $1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD ifelse([$4], , [AC_MSG_ERROR(dnl [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT ])], [AC_MSG_RESULT([no]) $4]) elif test $pkg_failed = untried; then ifelse([$4], , [AC_MSG_FAILURE(dnl [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])], [$4]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) ifelse([$3], , :, [$3]) fi[]dnl ])# PKG_CHECK_MODULES # Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.11' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.11.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.11.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to # `$srcdir', `$srcdir/..', or `$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is `.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [dnl Rely on autoconf to set up CDPATH properly. AC_PREREQ([2.50])dnl # expand $ac_aux_dir to an absolute path am_aux_dir=`cd $ac_aux_dir && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006, 2008 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 9 # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ(2.52)dnl ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 10 # There are a few dirty hacks below to avoid letting `AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "GCJ", or "OBJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl ifelse([$1], CC, [depcc="$CC" am_compiler_list=], [$1], CXX, [depcc="$CXX" am_compiler_list=], [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], UPC, [depcc="$UPC" am_compiler_list=], [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named `D' -- because `-MD' means `put the output # in D'. mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with # Solaris 8's {/usr,}/bin/sh. touch sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with `-c' and `-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle `-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # after this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvisualcpp | msvcmsys) # This compiler won't grok `-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE(dependency-tracking, [ --disable-dependency-tracking speeds up one-time build --enable-dependency-tracking do not reject slow dependency extractors]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. #serial 5 # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Autoconf 2.62 quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in *\'*) eval set x "$CONFIG_FILES" ;; *) set x $CONFIG_FILES ;; esac shift for mf do # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. # We used to match only the files named `Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then dirpart=`AS_DIRNAME("$mf")` else continue fi # Extract the definition of DEPDIR, am__include, and am__quote # from the Makefile without running `make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` test -z "am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` # When using ansi2knr, U may be empty or an underscore; expand it U=`sed -n 's/^U = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`AS_DIRNAME(["$file"])` AS_MKDIR_P([$dirpart/$fdir]) # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking # is enabled. FIXME. This creates each `.P' file that we will # need in order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) ]) # Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 8 # AM_CONFIG_HEADER is obsolete. It has been replaced by AC_CONFIG_HEADERS. AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, # 2005, 2006, 2008, 2009 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 16 # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.62])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}) AM_MISSING_PROG(AUTOCONF, autoconf) AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) AM_MISSING_PROG(AUTOHEADER, autoheader) AM_MISSING_PROG(MAKEINFO, makeinfo) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AM_PROG_MKDIR_P])dnl # We need awk for the "check" target. The system "awk" is bad on # some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES(CC)], [define([AC_PROG_CC], defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES(CXX)], [define([AC_PROG_CXX], defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES(OBJC)], [define([AC_PROG_OBJC], defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl ]) _AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl dnl The `parallel-tests' driver may need to know about EXEEXT, so add the dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro dnl is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl ]) dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001, 2003, 2005, 2008 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST(install_sh)]) # Copyright (C) 2003, 2005 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 2 # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001, 2002, 2003, 2005, 2009 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 4 # AM_MAKE_INCLUDE() # ----------------- # Check to see how make treats includes. AC_DEFUN([AM_MAKE_INCLUDE], [am_make=${MAKE-make} cat > confinc << 'END' am__doit: @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. AC_MSG_CHECKING([for style of include used by $am_make]) am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # Ignore all kinds of additional output from `make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi AC_SUBST([am__include]) AC_SUBST([am__quote]) AC_MSG_RESULT([$_am_result]) rm -f confinc confmf ]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 6 # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it supports --run. # If it does, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --run true"; then am_missing_run="$MISSING --run " else am_missing_run= AC_MSG_WARN([`missing' script is too old or missing]) fi ]) # Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_MKDIR_P # --------------- # Check for `mkdir -p'. AC_DEFUN([AM_PROG_MKDIR_P], [AC_PREREQ([2.60])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P, dnl while keeping a definition of mkdir_p for backward compatibility. dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile. dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of dnl Makefile.ins that do not define MKDIR_P, so we do our own dnl adjustment using top_builddir (which is defined more often than dnl MKDIR_P). AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl case $mkdir_p in [[\\/$]]* | ?:[[\\/]]*) ;; */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; esac ]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001, 2002, 2003, 2005, 2008 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 4 # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # ------------------------------ # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), 1)]) # _AM_SET_OPTIONS(OPTIONS) # ---------------------------------- # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 5 # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Just in case sleep 1 echo timestamp > conftest.file # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);; esac # Do `set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi rm -f conftest.file if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT(yes)]) # Copyright (C) 2009 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 1 # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # (`yes' being less verbose, `no' or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [ --enable-silent-rules less verbose build output (undo: `make V=1') --disable-silent-rules verbose build output (undo: `make V=0')]) case $enable_silent_rules in yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor `install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in `make install-strip', and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using `strip' when the user # run `make install-strip'. However `strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the `STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be `maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006, 2008 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 2 # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004, 2005 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 2 # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of `v7', `ustar', or `pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. AM_MISSING_PROG([AMTAR], [tar]) m4_if([$1], [v7], [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'], [m4_case([$1], [ustar],, [pax],, [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' _am_tools=${am_cv_prog_tar_$1-$_am_tools} # Do not fold the above two line into one, because Tru64 sh and # Solaris sh will not grok spaces in the rhs of `-'. for _am_tool in $_am_tools do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR m4_include([m4/ax_check_compiler_flags.m4]) m4_include([m4/libtool.m4]) m4_include([m4/ltoptions.m4]) m4_include([m4/ltsugar.m4]) m4_include([m4/ltversion.m4]) m4_include([m4/lt~obsolete.m4]) mpdcron-0.3+git20110303/autogen.sh000077500000000000000000000010321153374523700164160ustar00rootroot00000000000000#!/bin/sh # vim: set sw=4 et sts=4 tw=80 : die() { echo "$@" >&2 exit 1 } echo ">>> rm -f config.cache" rm -f config.cache echo ">>> libtoolize --copy --force --automake" libtoolize --copy --force --automake || die "libtoolize failed" echo ">>> aclocal -I m4" aclocal -I m4 || die "aclocal failed" echo ">>> autoheader" autoheader || die "autoheader failed" echo ">>> autoconf" autoconf || die "autoconf failed" echo ">>> automake --foreign --add-missing --copy" automake --foreign --add-missing --copy || die "automake failed" mpdcron-0.3+git20110303/conf/000077500000000000000000000000001153374523700153465ustar00rootroot00000000000000mpdcron-0.3+git20110303/conf/hooks/000077500000000000000000000000001153374523700164715ustar00rootroot00000000000000mpdcron-0.3+git20110303/conf/hooks/database000077500000000000000000000003571153374523700201700ustar00rootroot00000000000000#!/usr/bin/env bash # vim: set sw=4 et sts=4 tw=80 : # Copyright 2009 Ali Polatel # Distributed under the terms of the GNU General Public License v2 # Example database hook echo "* Database event" hooks/dump-stats.bash mpdcron-0.3+git20110303/conf/hooks/dump-outputs.rb000077500000000000000000000007221153374523700215100ustar00rootroot00000000000000#!/usr/bin/env ruby # vim: set sw=2 sts=2 tw=100 et nowrap fenc=utf-8 : # Copyright 2009 Ali Polatel # Distributed under the terms of the GNU General Public License v2 # Dump outputs puts "=== MPD OUTPUTS ===" i = 1 loop do env_output = "MPD_OUTPUT_ID_#{i}" break unless ENV[env_output] puts "output[#{i}].id = #{ENV[env_output]}" puts "output[#{i}].enabled = #{ENV["MPD_OUTPUT_ID_#{i}_ENABLED"]}" i += 1 end puts "===================" mpdcron-0.3+git20110303/conf/hooks/dump-playlists.rb000077500000000000000000000012171153374523700220110ustar00rootroot00000000000000#!/usr/bin/env ruby # vim: set sw=2 sts=2 tw=100 et nowrap fenc=utf-8 : # Copyright 2009 Ali Polatel # Distributed under the terms of the GNU General Public License v2 # Dump playlists # Playlists are listed using environment variables like: # MPD_PLAYLIST_%d_PATH = /path/to/playlist # MPD_PLAYLIST_%d_LAST_MODIFIED = PLAYLIST_LAST_MODIFIED_DATE puts "=== MPD PLAYLISTS ===" i = 1 loop do envp = "MPD_PLAYLIST_#{i}_PATH" envlm = "MPD_PLAYLIST_#{i}_LAST_MODIFIED" break unless ENV[envp] puts "playlist[#{i}].path = #{ENV[envp]}" puts "playlist[#{i}].last_modified = #{ENV[envlm]}" i += 1 end puts "=====================" mpdcron-0.3+git20110303/conf/hooks/dump-queue.rb000077500000000000000000000033411153374523700211110ustar00rootroot00000000000000#!/usr/bin/env ruby # vim: set sw=2 sts=2 tw=100 et nowrap fenc=utf-8 : # Copyright 2009 Ali Polatel # Distributed under the terms of the GNU General Public License v2 # Dump queue puts "=== MPD QUEUE ===" i = 1 loop do env_uri = "MPD_SONG_#{i}_URI" break unless ENV[env_uri] puts "song[#{i}].uri = #{ENV[env_uri]}" puts "song[#{i}].last_modified = #{ENV["MPD_SONG_#{i}_LAST_MODIFIED]"]}" puts "song[#{i}].duration = #{ENV["MPD_SONG_#{i}_DURATION"]}" puts "song[#{i}].pos = #{ENV["MPD_SONG_#{i}_POS"]}" puts "song[#{i}].id = #{ENV["MPD_SONG_#{i}_ID"]}" puts "song[#{i}].tag.artist = #{ENV["MPD_SONG_#{i}_TAG_ARTIST"]}" puts "song[#{i}].tag.album = #{ENV["MPD_SONG_#{i}_TAG_ALBUM"]}" puts "song[#{i}].tag.album_artist = #{ENV["MPD_SONG_#{i}_TAG_ALBUM_ARTIST"]}" puts "song[#{i}].tag.title = #{ENV["MPD_SONG_#{i}_TAG_TITLE"]}" puts "song[#{i}].tag.track = #{ENV["MPD_SONG_#{i}_TAG_TRACK"]}" puts "song[#{i}].tag.name = #{ENV["MPD_SONG_#{i}_TAG_NAME"]}" puts "song[#{i}].tag.genre = #{ENV["MPD_SONG_#{i}_TAG_GENRE"]}" puts "song[#{i}].tag.date = #{ENV["MPD_SONG_#{i}_TAG_DATE"]}" puts "song[#{i}].tag.composer = #{ENV["MPD_SONG_#{i}_TAG_COMPOSER"]}" puts "song[#{i}].tag.comment = #{ENV["MPD_SONG_#{i}_TAG_COMMENT"]}" puts "song[#{i}].tag.disc = #{ENV["MPD_SONG_#{i}_TAG_DISC"]}" puts "song[#{i}].tag.musicbrainz.artistid = #{ENV["MPD_SONG_#{i}_TAG_MUSICBRAINZ_ARTISTID"]}" puts "song[#{i}].tag.musicbrainz.albumid = #{ENV["MPD_SONG_#{i}_TAG_MUSICBRAINZ_ALBUMID"]}" puts "song[#{i}].tag.musicbrainz.albumartistid = #{ENV["MPD_SONG_#{i}_TAG_MUSICBRAINZ_ALBUMARTISTID"]}" puts "song[#{i}].tag.musicbrainz.trackid = #{ENV["MPD_SONG_#{i}_TAG_MUSICBRAINZ_TRACKID"]}" i += 1 end puts "=================" mpdcron-0.3+git20110303/conf/hooks/dump-song.bash000077500000000000000000000022471153374523700212510ustar00rootroot00000000000000#!/usr/bin/env bash # vim: set sw=4 et sts=4 tw=80 : # Copyright 2009 Ali Polatel # Distributed under the terms of the GNU General Public License v2 echo "=== MPD CURRENT SONG ===" echo "Uri: $MPD_SONG_URI" echo "Last modified: $MPD_SONG_LAST_MODIFIED" echo "Duration: $MPD_SONG_DURATION" echo "Position: $MPD_SONG_POSITION" echo "ID: $MPD_SONG_ID" # Note: Currently only the first tags are exported echo "--- TAGS ---" echo "Artist: $MPD_SONG_TAG_ARTIST" echo "Album: $MPD_SONG_TAG_ALBUM" echo "Album_Artist: $MPD_SONG_ALBUM_ARTIST" echo "Title: $MPD_SONG_TAG_TITLE" echo "Track: $MPD_SONG_TAG_TRACK" echo "Name: $MPD_SONG_TAG_NAME" echo "Genre: $MPD_SONG_TAG_GENRE" echo "Date: $MPD_SONG_TAG_GENRE" echo "Composer: $MPD_SONG_TAG_COMPOSER" echo "Performer: $MPD_SONG_TAG_PERFORMER" echo "Comment: $MPD_SONG_TAG_COMMENT" echo "Disc: $MPD_SONG_TAG_DISC" echo "Musicbrainz Artist ID: $MPD_SONG_TAG_MUSICBRAINZ_ARTISTID" echo "Musicbrainz Album ID: $MPD_SONG_TAG_MUSICBRAINZ_ALBUMID" echo "Musicbrainz AlbumArtist ID: $MPD_SONG_TAG_MUSICBRAINZ_ALBUMARTISTID" echo "Musicbrainz Track ID: $MPD_SONG_TAG_MUSICBRAINZ_TRACKID" echo "------------" echo "========================" mpdcron-0.3+git20110303/conf/hooks/dump-stats.bash000077500000000000000000000007711153374523700214410ustar00rootroot00000000000000#!/usr/bin/env bash # vim: set sw=4 et sts=4 tw=80 : # Copyright 2009 Ali Polatel # Distributed under the terms of the GNU General Public License v2 echo "=== MPD STATS ===" echo "Update Time: $MPD_DATABASE_UPDATE_TIME" echo "Artists: $MPD_DATABASE_ARTISTS" echo "Albums: $MPD_DATABASE_ALBUMS" echo "Songs: $MPD_DATABASE_SONGS" echo "Play time: $MPD_DATABASE_PLAY_TIME" echo "Uptime: $MPD_DATABASE_UPTIME" echo "Database Play time: $MPD_DATABASE_DB_PLAY_TIME" echo "=================" mpdcron-0.3+git20110303/conf/hooks/dump-status.bash000077500000000000000000000023001153374523700216140ustar00rootroot00000000000000#!/usr/bin/env bash # vim: set sw=4 et sts=4 tw=80 : # Copyright 2009 Ali Polatel # Distributed under the terms of the GNU General Public License v2 echo "==========" echo "MPD STATUS" echo "==========" echo "Volume: $MPD_STATUS_VOLUME" echo "Repeat: $MPD_STATUS_REPEAT" # Boolean, 0 or 1 echo "Random: $MPD_STATUS_RANDOM" # Boolean, 0 or 1 echo "Single: $MPD_STATUS_SINGLE" # Boolean, 0 or 1 echo "Consume: $MPD_STATUS_CONSUME" # Boolean, 0 or 1 echo "Queue version: $MPD_STATUS_QUEUE_VERSION" echo "Crossfade: $MPD_STATUS_CROSSFADE" echo "Song position: $MPD_STATUS_SONG_POS" echo "Song ID: $MPD_STATUS_SONG_ID" echo "Elapsed time: $MPD_STATUS_ELAPSED_TIME" echo "Elapsed milliseconds: $MPD_STATUS_ELAPSED_MS" echo "Total time: $MPD_STATUS_TOTAL_TIME" echo "Kbit Rate: $MPD_STATUS_KBIT_RATE" echo "Update id: $MPD_STATUS_UPDATE_ID" echo "State: $MPD_STATUS_STATE" # One of play, pause, stop, unknown echo "Audio format: $MPD_STATUS_AUDIO_FORMAT" # Boolean, 0 or 1 if [[ "$MPD_STATUS_AUDIO_FORMAT" == 1 ]]; then echo "Samplerate: $MPD_STATUS_AUDIO_FORMAT_SAMPLE_RATE" echo "Bits: $MPD_STATUS_AUDIO_FORMAT_BITS" echo "Channels: $MPD_STATUS_AUDIO_FORMAT_CHANNELS" fi echo "==========" mpdcron-0.3+git20110303/conf/hooks/mixer000077500000000000000000000003521153374523700175430ustar00rootroot00000000000000#!/usr/bin/env bash # vim: set sw=4 et sts=4 tw=80 : # Copyright 2009 Ali Polatel # Distributed under the terms of the GNU General Public License v2 # Example mixer hook echo "* Mixer event" hooks/dump-status.bash mpdcron-0.3+git20110303/conf/hooks/options000077500000000000000000000003561153374523700201160ustar00rootroot00000000000000#!/usr/bin/env bash # vim: set sw=4 et sts=4 tw=80 : # Copyright 2009 Ali Polatel # Distributed under the terms of the GNU General Public License v2 # Example options hook echo "* Options event" hooks/dump-status.bash mpdcron-0.3+git20110303/conf/hooks/output000077500000000000000000000003251153374523700177570ustar00rootroot00000000000000#!/usr/bin/env bash # vim: set sw=4 et sts=4 tw=80 : # Copyright 2009 Ali Polatel # Distributed under the terms of the GNU General Public License v2 # Example output hook hooks/dump-outputs.rb mpdcron-0.3+git20110303/conf/hooks/player000077500000000000000000000003541153374523700177150ustar00rootroot00000000000000#!/usr/bin/env bash # vim: set sw=4 et sts=4 tw=80 : # Copyright 2009 Ali Polatel # Distributed under the terms of the GNU General Public License v2 # Example player hook. hooks/dump-status.bash hooks/dump-song.bash mpdcron-0.3+git20110303/conf/hooks/playlist000077700000000000000000000000001153374523700213172queueustar00rootroot00000000000000mpdcron-0.3+git20110303/conf/hooks/queue000077500000000000000000000003121153374523700175370ustar00rootroot00000000000000#!/usr/bin/env bash # vim: set sw=4 et sts=4 tw=80 : # Copyright 2009 Ali Polatel # Distributed under the terms of the GNU General Public License v2 # Dump queue hooks/dump-queue.rb mpdcron-0.3+git20110303/conf/hooks/stored_playlist000077500000000000000000000003441153374523700216410ustar00rootroot00000000000000#!/usr/bin/env bash # vim: set sw=4 et sts=4 tw=80 : # Copyright 2009 Ali Polatel # Distributed under the terms of the GNU General Public License v2 # Example hook for stored_playlist hooks/dump-playlists.rb mpdcron-0.3+git20110303/conf/hooks/submit.rb000077500000000000000000000402571153374523700203340ustar00rootroot00000000000000#!/usr/bin/env ruby # vim: set sw=2 sts=2 tw=100 et nowrap fenc=utf-8 : # Copyright 2009 Ali Polatel # Based in part upon Scrobbler which is: # Copyright (c) John Nunemaker # Distributed under the terms of the GNU General Public License v2 =begin XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX THIS SCRIPT IS DEPRECATED! XXX XXX USE THE SCROBBLER MODULE INSTEAD XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX mpdcron script to submit songs to libre.fm or last.fm Execute this hook from your player hook. Configuration file is MPDCRON_DIR/submit.yaml which is a yaml file that looks like: ### Submit configuration file librefm: user: USERNAME password: PASSWORD journal: /path/to/librefm.yaml lastfm: user: USERNAME password: PASSWORD journal: /path/to/lastfm.yaml ### where MPDCRON_DIR is ~/.mpdcron by default. PASSWORD can be specified either bare or in the form md5:MD5_HASH_OF_THE_PASSWORD TODO: Use MPD_STATUS_ELAPSED_TIME =end %w{digest/md5 net/http uri yaml}.each {|m| require m } # Constants LASTFM_AUTH_URL = 'http://post.audioscrobbler.com' LASTFM_AUTH_VER = '1.2.1' LIBREFM_AUTH_URL = 'http://turtle.libre.fm' LIBREFM_AUTH_VER = LASTFM_AUTH_VER MYNAME = File.basename($0, ".rb") HTTP_PROXY = ENV['http_proxy'] MPDCRON_PACKAGE = ENV['MPDCRON_PACKAGE'] MPDCRON_VERSION = ENV['MPDCRON_VERSION'] MPDCRON_GITHEAD = ENV['MPDCRON_GITHEAD'] MCOPT_DAEMONIZE = ENV['MCOPT_DAEMONIZE'] MC_CALLS_PLAYER = ENV['MC_CALLS_PLAYER'] ? ENV['MC_CALLS_PLAYER'].to_i : 0 MPD_STATUS_STATE = ENV['MPD_STATUS_STATE'] MPD_STATUS_TOTAL_TIME = ENV['MPD_STATUS_TOTAL_TIME'] ? ENV['MPD_STATUS_TOTAL_TIME'].to_i : 0 MPD_SONG_ID = ENV['MPD_SONG_ID'] ? ENV['MPD_SONG_ID'].to_i : -1 MPD_SONG_URI = ENV['MPD_SONG_URI'] MPD_SONG_TAG_ALBUM = ENV['MPD_SONG_TAG_ALBUM'] MPD_SONG_TAG_ARTIST = ENV['MPD_SONG_TAG_ARTIST'] MPD_SONG_TAG_TITLE = ENV['MPD_SONG_TAG_TITLE'] MPD_SONG_TAG_TRACK = ENV['MPD_SONG_TAG_TRACK'] MPD_SONG_TAG_MUSICBRAINZ_TRACKID = ENV['MPD_SONG_TAG_MUSICBRAINZ_TRACKID'] # Errors class BannedError < StandardError; end class BadAuthError < StandardError; end class RequestFailedError < StandardError; end class BadTimeError < StandardError; end class BadSessionError < StandardError; end class NonImplementedError < StandardError; end def log format, *args return if MCOPT_DAEMONIZE $stderr.print MYNAME + "[" + MC_CALLS_PLAYER.to_s + "]@" + Time.now.to_i.to_s + ": " $stderr.puts(sprintf(format, *args)) $stderr.flush end class Connection def initialize base_url, args = {} @base_url = base_url @username = args[:username] @password = args[:password] if HTTP_PROXY proxy_url = URI.parse(HTTP_PROXY) @proxy_host = proxy_url.host if proxy_url and proxy_url.host @proxy_port = proxy_url.port if proxy_url and proxy_url.port @proxy_user, @proxy_pass = proxy_url.userinfo.split(/:/) if proxy_url and proxy_url.userinfo log("Successfully set up proxy host: %s, port: %d", @proxy_host, @proxy_port) end end def request resource, method = 'get', args = nil url = URI.join(@base_url, resource) headers = { 'Accept-Charset' => 'UTF-8', 'Content-type' => 'application/x-www-form-urlencoded', 'Host' => url.host, 'User-Agent' => MPDCRON_PACKAGE + '/' + MPDCRON_VERSION } case method when 'get' if args url.query = args.map { |k,v| "%s=%s" % [escape(k.to_s), escape(v.to_s)] }.join("&") end req = Net::HTTP::Get.new(url.request_uri, headers) when 'post' req = Net::HTTP::Post.new(url.request_uri, headers) req.set_form_data(args) if args else raise ArgumentError, "Invalid method" end if @username and @password req.basic_auth(@username, @password) end http = Net::HTTP::Proxy(@proxy_host, @proxy_port, @proxy_user, @proxy_pass) res = http.start(url.host, url.port) { |conn| conn.request(req) } res.body end def get resource, args = nil; request(resource, method = 'get', args) end def post resource, args = nil; request(resource, method = 'post', args) end private def escape(str) URI.escape(str, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")) end end class Scrobbler attr_reader :auth_url, :auth_ver attr_reader :status, :session_id, :now_playing_url, :submission_url attr_accessor :user, :password, :cid, :cver, :journal def initialize args = {} @auth_url = args[:auth_url] @auth_ver = args[:auth_ver] @user = args[:user] @password = args[:password] raise ArgumentError, "Missing required argument: auth_url" if @auth_url.nil? or @auth_url.empty? raise ArgumentError, "Missing required argument: auth_ver" if @auth_ver.nil? or @auth_ver.empty? raise ArgumentError, "Missing required argument: user" if @user.nil? or @user.empty? raise ArgumentError, "Missing required argument: password" if @password.nil? or @password.empty? @journal = args[:journal] # TODO: Apply for a client ID @cid = 'mcn' @cver = MPDCRON_VERSION # Parse password if @password =~ /^md5:(.*)/ @password = "$1" else @password = Digest::MD5.hexdigest(@password) end end def handshake! timestamp = Time.now.to_i.to_s token = Digest::MD5.hexdigest(@password + timestamp) query = { :hs => 'true', :p => @auth_ver, :c => @cid, :v => @cver, :u => @user, :t => timestamp, :a => token } connection = Connection.new(@auth_url) result = connection.get('/', query) @status = result.split(/\n/)[0] case @status when /OK/ @session_id, @now_playing_url, @submission_url = result.split(/\n/)[1,3] when /BANNED/ raise BannedError when /BADAUTH/ raise BadAuthError when /FAILED/ raise RequestFailedError, @status when /BADTIME/ raise BadTimeError else raise RequestFailedError, @status end end def playing song artist = song.tag['artist'] title = song.tag['title'] album = song.tag['album'] length = song.total_time track_number = song.tag['track'] mb_track_id = song.tag['mb_track_id'] raise ArgumentError, 'Session ID missing' if @session_id.nil? or @session_id.empty? raise ArgumentError, 'Now playing URL missing' if @now_playing_url.nil? or @now_playing_url.empty? return false if artist.nil? or artist.empty? return false if title.nil? or title.empty? return false if not length.to_s.empty? and length.to_i <= 30 connection = Connection.new(@now_playing_url) query = { :s => @session_id, :a => artist, :t => title, :b => album, :l => length, :n => track_number, :m => mb_track_id } @status = connection.post('', query) case @status when /OK/ return true when /BADSESSION/ raise BadSessionError else raise RequestFailedError, @status end end def scrobble song artist = song.tag['artist'] title = song.tag['title'] time = song.start source = 'P' length = song.total_time album = song.tag['album'] track_number = song.tag['track'] mb_track_id = song.tag['mb_track_id'] raise ArgumentError, 'Session ID missing' if @session_id.nil? or @session_id.empty? raise ArgumentError, 'Submission URL missing' if @submission_url.nil? or @submission_url.empty? return false if artist.nil? or artist.empty? return false if title.nil? or title.empty? # When to submit (from: http://www.audioscrobbler.net/development/protocol/) # ... # 2. The track must have been played for a duration of at least 240 seconds or half the track's # total length, whichever comes first. Skipping or pausing the track is irrelevant as long as # the appropriate amount has been played. # 3. The total playback time for the track must be more than 30 seconds. Do not submit tracks # shorter than this. # ... if not length.to_s.empty? and length.to_i <= 30 # 3. log "Not submitting song `%s' because it's shorter than 30 seconds", song.uri return false end duration = (Time.now - time).to_i if duration < 240 && duration < (length / 2) log "Not submitting song `%s' because neither it was played for 240 seconds,", song.uri log "nor half of it (%d seconds) has passed", (length / 2) return false end connection = Connection.new(@submission_url) query = { :s => @session_id, 'a[0]' => artist, 't[0]' => title, 'i[0]' => time.utc.to_i, 'o[0]' => source, 'r[0]' => '', 'l[0]' => length, 'b[0]' => album, 'n[0]' => track_number, 'm[0]' => mb_track_id } @status = connection.post('', query) case @status when /OK/ return true when /BADSESSION/ raise BadSessionError when /FAILED/ raise RequestFailedError, @status else raise RequestFailedError, @status end end def queue song unless @journal log "No journal defined in configuration file, skipping caching" begin return scrobble(song) rescue BadAuthError, BadSessionError, BannedError, RequestFailedError, BadTimeError => e log "Scrobbling song `%s' failed: %s", song.uri, e.message return false end end newcache = [] cache = [] if File.exists?(@journal) cache = open(@journal) {|f| YAML.load(f) } raise RuntimeError, "Failed to load journal `#{journal}'" unless cache end log "Caching song `%s' to journal `%s'", song.uri, @journal cache << song cache.each do |s| log "Scrobbling cached song `%s' loaded from journal `%s'", song.uri, @journal begin scrobble(s) rescue BadAuthError, BadSessionError, BannedError, RequestFailedError, BadTimeError => e log "Scrobbling song `%s' failed, caching song for later submission: %s", song.uri, e.message newcache << s next end end log "Saving updated journal to `%s'", @journal open(@journal, 'w') {|f| f.puts newcache.to_yaml} return true end end class Song attr_reader :id, :uri, :total_time, :tag attr_accessor :start def initialize @id = -1 @uri = '' @total_time = 0 @start = Time.now @tag = {} end def load! @id = MPD_SONG_ID @uri = MPD_SONG_URI @total_time = MPD_STATUS_TOTAL_TIME @tag = {'album' => MPD_SONG_TAG_ALBUM, 'artist' => MPD_SONG_TAG_ARTIST, 'title' => MPD_SONG_TAG_TITLE, 'track' => MPD_SONG_TAG_TRACK, 'mb_track_id' => MPD_SONG_TAG_MUSICBRAINZ_TRACKID} end end class Submit attr_reader :config attr_accessor :lastfm, :librefm, :settings def initialize @confpath = "./submit.yaml" @path = "./.settings.yaml" @settings = {'pause' => false, 'song' => Song.new} end def load! @config = open(@confpath) {|f| YAML.load(f)} raise RuntimeError, "Error loading YAML from `#{@confpath}'" unless @config raise RuntimeError, "Neither lastfm nor librefm section defined in `#{@confpath}'" unless @config['lastfm'] or @config['librefm'] if @config['librefm'] raise RuntimeError, "librefm.user not defined in `#{confpath}'" unless @config['librefm']['user'] raise RuntimeError, "librefm.password not defined in `#{confpath}'" unless @config['librefm']['password'] @librefm = Scrobbler.new(:auth_url => LIBREFM_AUTH_URL, :auth_ver => LIBREFM_AUTH_VER, :user => @config['librefm']['user'], :password => @config['librefm']['password'], :journal => @config['librefm']['journal']) else @librefm = nil end if @config['lastfm'] raise RuntimeError, "lastfm.user not defined in `#{confpath}'" unless @config['lastfm']['user'] raise RuntimeError, "lastfm.password not defined in `#{confpath}'" unless @config['lastfm']['password'] @lastfm = Scrobbler.new(:auth_url => LASTFM_AUTH_URL, :auth_ver => LASTFM_AUTH_VER, :user => @config['lastfm']['user'], :password => @config['lastfm']['password'], :journal => @config['lastfm']['journal']) else @lastfm = nil end if File.exists?(@path) @settings = open(@path) { |f| YAML.load(f) } File.delete(@path) end end def save open(@path, 'w') {|f| f.puts @settings.to_yaml} end def clear File.delete(@path) if File.exists?(@path) end end s = Submit.new if MC_CALLS_PLAYER == 1 log "First run, removing cruft..." s.clear end s.load! csong = Song.new csong.load! case MPD_STATUS_STATE when 'play' # Check if a paused song is continued. if s.settings['pause'] if csong.id == s.settings['song'].id log "Paused song `%s' continued.", csong.uri s.settings['pause'] = false s.save exit 0 end end if s.settings['song'].id < 0 log "Initial song playing `%s', saving to cache", csong.uri csong.start = Time.now s.settings['song'] = csong s.save ################################### ### Send now playing notifications# ################################### if s.librefm log "Sending now playing notification for song `%s' to Libre.fm", csong.uri begin s.librefm.handshake! if s.librefm.playing(csong) then log "Successfully submitted song `%s'", csong.uri end rescue BadAuthError, BadSessionError, BannedError, RequestFailedError, BadTimeError => e log "Failed to submit song: %s", e.message end end if s.lastfm log "Sending now playing notification for song `%s' to Last.fm", csong.uri begin s.lastfm.handshake! if s.lastfm.playing(csong) then log "Successfully submitted song `%s' to Last.fm", csong.uri end rescue BadAuthError, BadSessionError, BannedError, RequestFailedError, BadTimeError => e log "Failed to submit song to Last.fm: %s", e.message end end ################################### elsif s.settings['song'].id != csong.id csong.start = Time.now psong = s.settings['song'] s.settings['song'] = csong s.save ################################### ### Scrobble previous song ######## ################################### if s.librefm log "Scrobbling previous song `%s' to Libre.fm", psong.uri begin s.librefm.handshake! rescue BadAuthError, BadSessionError, BannedError, RequestFailedError, BadTimeError => e log "Handshake with Libre.fm failed: %s", e.message end s.librefm.queue psong end if s.lastfm log "Submitting previous song `%s' to Last.fm", psong begin s.lastfm.handshake! rescue BadAuthError, BadSessionError, BannedError, RequestFailedError, BadTimeError => e log "Handshake with Last.fm failed: %s", e.message end s.lastfm.queue psong end ################################### ################################### ### Send now playing notifications# ################################### if s.librefm log "Sending now playing notification for song `%s' to Libre.fm", csong.uri begin s.librefm.handshake! if s.librefm.playing(csong) then log "Successfully submitted song `%s' to Libre.fm", csong.uri end rescue BadAuthError, BadSessionError, BannedError, RequestFailedError, BadTimeError => e log "Failed to submit song to Libre.fm: %s", e.message end end if s.lastfm log "Sending now playing notification for song `%s' to Last.fm", csong.uri begin s.lastfm.handshake! if s.lastfm.playing(csong) then log "Successfully submitted song `%s' to Last.fm", csong.uri end rescue BadAuthError, BadSessionError, BannedError, RequestFailedError, BadTimeError => e log "Failed to submit song to Last.fm: %s", e.message end end ################################### else log "Seek called on the song `%s'.", csong.uri s.save end when 'pause' log "Song `%s' paused.", csong.uri s.settings['pause'] = true s.settings['song'] = csong s.save else log "Stopped." s.settings['pause'] = false s.settings['song'] = csong s.save end mpdcron-0.3+git20110303/conf/hooks/update000077500000000000000000000003261153374523700177020ustar00rootroot00000000000000#!/usr/bin/env bash # vim: set sw=4 et sts=4 tw=80 : # Copyright 2009 Ali Polatel # Distributed under the terms of the GNU General Public License v2 # Example update hook hooks/dump-status.bash mpdcron-0.3+git20110303/conf/modules/000077500000000000000000000000001153374523700170165ustar00rootroot00000000000000mpdcron-0.3+git20110303/conf/modules/Makefile000066400000000000000000000005621153374523700204610ustar00rootroot00000000000000# Makefile for example module example.so: example.c $(CC) -fPIC -shared -Wall -W -Wextra \ $(CFLAGS) $(LDFLAGS) \ $(shell pkg-config --cflags --libs glib-2.0) \ $(shell pkg-config --cflags --libs libmpdclient) \ $< -o $@ clean: rm example.so || true install: install -m644 -s example.so ~/.mpdcron/modules uninstall: rm ~/.mpdcron/modules/example.so || true mpdcron-0.3+git20110303/conf/modules/example.c000066400000000000000000000101671153374523700206220ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet ai cin fdm=syntax : */ /* * Copyright (c) 2009 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ /* mpdcron example module to print volume on stdout. * Compile this file like: * gcc -fPIC -shared \ * $(pkg-config --cflags --libs glib-2.0) \ * $(pkg-config --cflags --libs libmpdclient) \ * example.c -o example.so * Put it under MPDCRON_DIR/modules where MPDCRON_DIR is ~/.mpdcron by default. * Load it from your configuration file like: * [main] * modules = example */ /* Define MPDCRON_MODULE before including the file. */ #define MPDCRON_MODULE "example" #include #include #include #include static int count; static int last_volume; /* Prototypes */ int init(const struct mpdcron_config *, GKeyFile *); void destroy(void); int run(const struct mpd_connection *, const struct mpd_status *); /* Every module should define module which is of type struct mpdcron_module. * This is the initial symbol mpdcron looks for in the module. */ struct mpdcron_module module = { .name = "Example", .init = init, .destroy = destroy, .event_mixer = run, }; /** * Initialization function. * @param conf mpdcron's global configuration. * @param fd This file descriptor points to mpdcron's configuration file. * Never call g_key_file_free() on this! * @return On success this function should return MPDCRON_INIT_SUCCESS. * On failure this function should return MPDCRON_INIT_FAILURE. */ int init(G_GNUC_UNUSED const struct mpdcron_config *conf, GKeyFile *fd) { GError *parse_error; /* You may use GLib logging functions to do the logging. */ g_message("Hello from example module!"); /* Parse configuration here. */ parse_error = NULL; count = g_key_file_get_integer(fd, "example", "count", &parse_error); if (parse_error != NULL) { switch (parse_error->code) { case G_KEY_FILE_ERROR_GROUP_NOT_FOUND: case G_KEY_FILE_ERROR_KEY_NOT_FOUND: /* ignore */ g_error_free(parse_error); break; default: g_critical("Parse error: %s", parse_error->message); g_error_free(parse_error); return MPDCRON_INIT_FAILURE; } } if (count <= 0) count = 5; last_volume = -1; return MPDCRON_INIT_SUCCESS; } /** * Destroy function. * This function should be used to free any allocated data and do other * cleaning up that the module has to make. */ void destroy(void) { g_message("Bye from example module!"); /* Do the cleaning up. */ } /** * Mixer event function. * @param conn: mpd connection * @param status: mpd status * @return This function should return MPDCRON_RUN_SUCCESS on success. * This function should return MPDCRON_RUN_RECONNECT to schedule * a reconnection to mpd server. * This function should return MPDCRON_RUN_RECONNECT_NOW to * schedule a reconnection by cancelling to run any of the next * modules in the queue. * This function should return MPDCRON_RUN_UNLOAD when the * module needs to be unloaded. */ int run(G_GNUC_UNUSED const struct mpd_connection *conn, const struct mpd_status *status) { int volume; if (count-- <= 0) return MPDCRON_EVENT_UNLOAD; volume = mpd_status_get_volume(status); if (last_volume < 0) g_message("Volume set to: %d%%", volume); else g_message("Volume %s from: %d to: %d%%", (last_volume < volume) ? "increased" : "decreased", last_volume, volume); last_volume = volume; return MPDCRON_EVENT_SUCCESS; } mpdcron-0.3+git20110303/conf/mpdcron.conf000066400000000000000000000012751153374523700176640ustar00rootroot00000000000000# mpdcron example configuration # vim: set et sw=4 sts=4 tw=80 ft=desktop : # Copy this file to MPDCRON_DIR/mpdcron.conf where MPDCRON_DIR is # ~/.mpdcron by default. # mpdcron related options are specified in the main group [main] # Location of the pid file. # pidfile = /var/run/mpdcron.pid # Wait this many seconds after sending signal to kill the daemon killwait = 3 # Mpd related options are specified in the mpd group. [mpd] # The list of events to wait for events = database;stored_playlist;playlist;player;mixer;output;options;update # Inverval in seconds for reconnecting to Mpd. reconnect = 5 # Timeout in milliseconds for Mpd timeout, 0 for default timeout of # libmpdclient. timeout = 0 mpdcron-0.3+git20110303/configure.ac000066400000000000000000000132661153374523700167170ustar00rootroot00000000000000dnl vim: set sw=4 sts=4 ts=4 noet ft=config foldmethod=marker foldmarker={{{,}}} : dnl {{{ program, version AC_PREREQ(2.59) AC_INIT([src/cron-main.c]) VERSION_MAJOR=0 VERSION_MINOR=3 VERSION_FULL="$VERSION_MAJOR.$VERSION_MINOR" VERSION="$VERSION_FULL" AC_SUBST([VERSION_MAJOR]) AC_SUBST([VERSION_MINOR]) AC_SUBST([VERSION_FULL]) AM_INIT_AUTOMAKE(mpdcron, [$VERSION_FULL]) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) dnl {{{ git revision AC_MSG_CHECKING([for git head]) if test -d "${GIT_DIR:-${ac_top_srcdir:-./}/.git}" ; then GITHEAD=`git describe 2>/dev/null` if test -z ${GITHEAD} ; then GITHEAD=`git rev-parse --short HEAD` fi if test -n "`git diff-index -m --name-only HEAD`" ; then GITHEAD=${GITHEAD}-dirty fi if test -n "${GITHEAD}" ; then GITHEAD="-${GITHEAD}" fi fi AC_MSG_RESULT([$GITHEAD]) AC_SUBST([GITHEAD]) dnl }}} dnl }}} dnl {{{ toolchain checks AC_PROG_CC AC_PROG_CC_C99 if test x"$ac_cv_prog_cc_c99" = x"no"; then AC_MSG_ERROR([mpdcron requires a C compiler that supports ISO C99!]) fi AC_PROG_INSTALL AC_PROG_LIBTOOL AC_PROG_MAKE_SET AC_PROG_SED dnl }}} dnl {{{ Make pkg-config work PKG_PROG_PKG_CONFIG([0.9.0]) dnl }}} dnl {{{ Check for libraries GLIB_REQUIRED=2.16 GIO_REQUIRED=2.22 LIBDAEMON_REQUIRED=0.12 LIBMPDCLIENT_REQUIRED=2.1 PKG_CHECK_MODULES([glib], [glib-2.0 >= $GLIB_REQUIRED],, [AC_MSG_ERROR([mpdcron requires glib-$GLIB_REQUIRED or newer])]) PKG_CHECK_MODULES([libdaemon], [libdaemon >= $LIBDAEMON_REQUIRED],, [AC_MSG_ERROR([mpdcron requires libdaemon-$LIBDAEMON_REQUIRED or newer])]) PKG_CHECK_MODULES([libmpdclient], [libmpdclient >= $LIBMPDCLIENT_REQUIRED],, [AC_MSG_ERROR([mpdcron requires libmpdclient-$LIBMPDCLIENT_REQUIRED or newer])]) dnl }}} dnl {{{ --enable-gmodule AC_MSG_CHECKING([whether module support is wanted]) AC_ARG_ENABLE([gmodule], [AS_HELP_STRING([--enable-gmodule], [enable support for C modules (via GModule)])], WANT_GMODULE="$enableval", WANT_GMODULE="no") AC_MSG_RESULT([$WANT_GMODULE]) AM_CONDITIONAL(HAVE_GMODULE, test x"$WANT_GMODULE" = x"yes") if test x"$WANT_GMODULE" = x"yes"; then PKG_CHECK_MODULES([gmodule], [gmodule-2.0 >= $GLIB_REQUIRED],, [AC_MSG_ERROR([mpdcron requires gmodule-$GLIB_REQUIRED or newer for module support])]) AC_DEFINE([HAVE_GMODULE], 1, [Define for gmodule support]) fi dnl }}} dnl {{{ standard modules ALL_STANDARD_MODULES="notification scrobbler stats" AC_MSG_CHECKING([which standard modules are wanted]) AC_ARG_WITH([standard-modules], [AS_HELP_STRING([--with-standard-modules=foo,bar,... Build the specified standard modules: all Build all available standard modules notification Build the notification module scrobbler Build the scrobbler module stats Build the stats module])], [standard_modules="`echo $with_standard_modules | tr ',' ' '`"], [standard_modules=""]) standard_modules=`echo $standard_modules | sed -e "s,^all\$,$ALL_STANDARD_MODULES,"` AC_MSG_RESULT([$standard_modules]) STANDARD_MODULES="$standard_modules" AC_SUBST([STANDARD_MODULES]) WANT_NOTIFICATION=no WANT_SCROBBLER=no WANT_STATS=no for m in $STANDARD_MODULES ; do if test x"$m" = x"notification" ; then if test x"$WANT_GMODULE" != x"yes"; then AC_MSG_WARN([GModule support is disabled, notification module won't be built]) else # Notification requires notify-send AC_CHECK_PROGS(NOTIFY_SEND, notify-send, ) if test x"$NOTIFY_SEND" = x; then AC_MSG_ERROR([notification standard module requires notify-send]) else WANT_NOTIFICATION=yes fi fi fi if test x"$m" = x"scrobbler" ; then if test x"$WANT_GMODULE" != x"yes"; then AC_MSG_WARN([GModule support is disabled, scrobbler module won't be built]) else # Scrobbler requires curl. PKG_CHECK_MODULES([libcurl], [libcurl], [WANT_SCROBBLER=yes], AC_MSG_ERROR([scrobbler standard module requires curl])) fi fi if test x"$m" = x"stats" ; then if test x"$WANT_GMODULE" != x"yes"; then AC_MSG_WARN([GModule support is disabled, stats module won't be built]) else # Stats module requires gio and sqlite3 PKG_CHECK_MODULES([sqlite], [sqlite3], [WANT_STATS=yes], AC_MSG_ERROR([stats standard module requires sqlite])) PKG_CHECK_MODULES([gio_unix], [gio-unix-2.0 >= $GIO_REQUIRED], [HAVE_GIO_UNIX=yes], [HAVE_GIO_UNIX=no]) if test x"$HAVE_GIO_UNIX" = x"no" ; then PKG_CHECK_MODULES([gio], [gio-2.0 >= $GIO_REQUIRED], [WANT_STATS=yes], [AC_MSG_ERROR([stats standard module requires gio-$GIO_REQUIRED or newer])]) AC_DEFINE(HAVE_GIO_UNIX, 0, "Define for gio-unix") else AC_DEFINE(HAVE_GIO_UNIX, 1, "Define for gio-unix") fi fi fi done AM_CONDITIONAL([WANT_NOTIFICATION], test x"$WANT_NOTIFICATION" = x"yes") AM_CONDITIONAL([WANT_SCROBBLER], test x"$WANT_SCROBBLER" = x"yes") AM_CONDITIONAL([WANT_STATS], test x"$WANT_STATS" = x"yes") AM_CONDITIONAL([HAVE_GIO_UNIX], test x"$HAVE_GIO_UNIX" = x"yes") dnl }}} dnl {{{ Extra CFLAGS MPDCRON_CFLAGS= WANTED_CFLAGS="-Wall -W -Wextra -Wvla -Wformat=2 -Wformat-security -Wformat-nonliteral -Winit-self -Wfloat-equal -Wno-deprecated-declarations -Wmissing-declarations -Wmissing-noreturn -Wmissing-prototypes -Wredundant-decls -Wshadow -Wpointer-arith -Wstrict-prototypes -Wcast-qual -Wwrite-strings -pedantic" for flag in $WANTED_CFLAGS ; do AX_CHECK_COMPILER_FLAGS([$flag], [MPDCRON_CFLAGS="$MPDCRON_CFLAGS $flag"],) done AC_SUBST([MPDCRON_CFLAGS]) dnl }}} dnl {{{ Output AM_CONFIG_HEADER(config.h) AC_OUTPUT( Makefile src/Makefile src/gmodule/Makefile src/gmodule/notification/Makefile src/gmodule/scrobbler/Makefile src/gmodule/stats/Makefile src/gmodule/stats/homescrape data/Makefile zsh-completion/Makefile ) dnl }}} mpdcron-0.3+git20110303/data/000077500000000000000000000000001153374523700153325ustar00rootroot00000000000000mpdcron-0.3+git20110303/data/Makefile.am000066400000000000000000000003271153374523700173700ustar00rootroot00000000000000EXTRA_DIST = mpdcron.1.pdc mpdcron.1 eugene.1.pdf eugene.1 BUILT_SOURCES = mpdcron.1 eugene.1 dist_man_MANS= mpdcron.1 eugene.1 all: mpdcron.1 eugene.1 %.1: %.1.pdc pandoc -s -S --from=markdown --to=man $< -o $@ mpdcron-0.3+git20110303/data/eugene.1000066400000000000000000000051631153374523700166710ustar00rootroot00000000000000.TH eugene 1 "February 25, 2011" "manual" .SH NAME .PP eugene - mpdcron statistics client .SH SYNOPSIS .PP eugene \f[I]command\f[] [\f[I]option\f[]] .SH DESCRIPTION .PP \f[B]eugene\f[] is an \f[B]mpdcron\f[] client which can be used to interact with the statistics database. .SH COMMANDS .PP By default these commands (except the first three) work on the current playing song. .PP For more information about the \f[I]expression\f[] syntax, see: . .TP .B help Display help and exit. .RS .RE .TP .B version Display version and exit. .RS .RE .TP .B list [\f[I]option\f[]\&...] \f[I]expression\f[] List song/artist/album/genre. .RS .RE .TP .B listinfo [\f[I]option\f[]\&...] [\f[I]expression\f[]] List song/artist/album/genre. .RS .RE .TP .B count [\f[I]option\f[]\&...] \f[I]count\f[] [\f[I]expression\f[]] Change play count of song/artist/album/genre. .RS .RE .TP .B countabs [\f[I]option\f[]\&...] \f[I]count\f[] [\f[I]expression\f[]] Change (absolute fashion) play count of song/artist/album/genre. .RS .RE .TP .B hate [\f[I]option\f[]\&...] [\f[I]expression\f[]] Hate song/artist/album/genre. .RS .RE .TP .B love [\f[I]option\f[]\&...] [\f[I]expression\f[]] Love song/artist/album/genre. .RS .RE .TP .B kill [\f[I]option\f[]\&...] [\f[I]expression\f[]] Kill song/artist/album/genre. .RS .RE .TP .B unkill [\f[I]option\f[]\&...] [\f[I]expression\f[]] Unkill song/artist/album/genre. .RS .RE .TP .B rate [\f[I]option\f[]\&...] \f[I]rating\f[] [\f[I]expression\f[]] Rate song/artist/album/genre. .RS .RE .TP .B rateabs [\f[I]option\f[]\&...] \f[I]rating\f[] [\f[I]expression\f[]] Rate (absolute fashion) song/artist/album/genre. .RS .RE .TP .B addtag [\f[I]option\f[]\&...] \f[I]tag\f[] [\f[I]expression\f[]] Add tag to song/artist/album/genre. .RS .RE .TP .B rmtag [\f[I]option\f[]\&...] \f[I]tag\f[] [\f[I]expression\f[]] Remove tag from song/artist/album/genre. .RS .RE .TP .B listtags [\f[I]option\f[]\&...] [\f[I]expression\f[]] List tags of song/artist/album/genre. .RS .RE .SH OPTIONS .TP .B -h, --help Show help options. .RS .RE .TP .B -a, --artist Apply on artists instead of songs. .RS .RE .TP .B -A, --album Apply on albums instead of songs. .RS .RE .TP .B -g, --genre Apply on genres instead of songs. .RS .RE .SH SEE ALSO .IP \[bu] 2 \f[B]mpdcron\f[](1) .IP \[bu] 2 .SH REPORTING BUGS .PP If you find a bug, please report it at . .SH COPYRIGHT .PP Copyright (c) 2009 Ali Polatel .PD 0 .P .PD Free use of this software is granted under the terms of the GNU General Public License (GPL). .SH AUTHORS David Prévot . mpdcron-0.3+git20110303/data/eugene.1.pdc000066400000000000000000000042121153374523700174300ustar00rootroot00000000000000% eugene(1) manual % David Prévot % February 25, 2011 # NAME eugene - mpdcron statistics client # SYNOPSIS eugene *command* [*option*] # DESCRIPTION **eugene** is an **mpdcron** client which can be used to interact with the statistics database. # COMMANDS By default these commands (except the first three) work on the current playing song. For more information about the *expression* syntax, see: . help : Display help and exit. version : Display version and exit. list [*option*...] *expression* : List song/artist/album/genre. listinfo [*option*...] [*expression*] : List song/artist/album/genre. count [*option*...] *count* [*expression*] : Change play count of song/artist/album/genre. countabs [*option*...] *count* [*expression*] : Change (absolute fashion) play count of song/artist/album/genre. hate [*option*...] [*expression*] : Hate song/artist/album/genre. love [*option*...] [*expression*] : Love song/artist/album/genre. kill [*option*...] [*expression*] : Kill song/artist/album/genre. unkill [*option*...] [*expression*] : Unkill song/artist/album/genre. rate [*option*...] *rating* [*expression*] : Rate song/artist/album/genre. rateabs [*option*...] *rating* [*expression*] : Rate (absolute fashion) song/artist/album/genre. addtag [*option*...] *tag* [*expression*] : Add tag to song/artist/album/genre. rmtag [*option*...] *tag* [*expression*] : Remove tag from song/artist/album/genre. listtags [*option*...] [*expression*] : List tags of song/artist/album/genre. # OPTIONS -h, \--help : Show help options. -a, \--artist : Apply on artists instead of songs. -A, \--album : Apply on albums instead of songs. -g, \--genre : Apply on genres instead of songs. # SEE ALSO - `mpdcron`(1) - # REPORTING BUGS If you find a bug, please report it at . # COPYRIGHT Copyright (c) 2009 Ali Polatel \ Free use of this software is granted under the terms of the GNU General Public License (GPL). mpdcron-0.3+git20110303/data/mpdcron.1000066400000000000000000000032561153374523700170640ustar00rootroot00000000000000.TH mpdcron 1 "December 23, 2009" "manual" .SH NAME .PP mpdcron - cron like daemon for mpd .SH SYNOPSIS .PP mpdcron [\f[I]option\f[]] .SH DESCRIPTION .PP mpdcron is a cron like daemon for mpd. It connects to mpd, waits for idle events. It has two interfaces: .IP \[bu] 2 Hooks which are executed after setting environment variables which hooks can use to get more information about the event without having to connect to mpd. .IP \[bu] 2 Modules, which are dynamically loaded and have direct access to mpd connection and the file descriptor of mpdcron's configuration file. .PP By default it will get the host name and port for mpd from \f[B]MPD_HOST\f[] and \f[B]MPD_PORT\f[] environment variables. \f[B]MPD_PASSWORD\f[] environment variable can be set to make mpdcron connect to a password-protected mpd. If these environment variables aren't set, mpdcron connects to localhost on port 6600. .SH OPTIONS .TP .B -?, --help Show help options and exit. .RS .RE .TP .B -V, --version Print version information and exit. .RS .RE .TP .B -k, --kill Instead of launching the daemon, attempt to kill the already running daemon. .RS .RE .TP .B -n, --no-daemon Don't detach from console. This option can be used for debugging. .RS .RE .SH FILES .IP \[bu] 2 \f[B]~/.mpdcron/mpdcron.conf\f[] User configuration file .SH SEE ALSO .IP \[bu] 2 \f[B]mpd\f[](1) .IP \[bu] 2 .SH REPORTING BUGS .PP If you find a bug, please report it at .SH COPYRIGHT .PP Copyright (c) 2009 Ali Polatel .PD 0 .P .PD Free use of this software is granted under the terms of the GNU General Public License (GPL). .SH AUTHOR Ali Polatel mpdcron-0.3+git20110303/data/mpdcron.1.pdc000066400000000000000000000031131153374523700176210ustar00rootroot00000000000000% mpdcron(1) manual % Ali Polatel % December 23, 2009 # NAME mpdcron - cron like daemon for mpd # SYNOPSIS mpdcron [*option*] # DESCRIPTION mpdcron is a cron like daemon for mpd. It connects to mpd, waits for idle events. It has two interfaces: - Hooks which are executed after setting environment variables which hooks can use to get more information about the event without having to connect to mpd. - Modules, which are dynamically loaded and have direct access to mpd connection and the file descriptor of mpdcron's configuration file. By default it will get the host name and port for mpd from **MPD\_HOST** and **MPD\_PORT** environment variables. **MPD\_PASSWORD** environment variable can be set to make mpdcron connect to a password-protected mpd. If these environment variables aren't set, mpdcron connects to localhost on port 6600. # OPTIONS -?, \--help : Show help options and exit. -V, \--version : Print version information and exit. -k, \--kill : Instead of launching the daemon, attempt to kill the already running daemon. -n, \--no-daemon : Don't detach from console. This option can be used for debugging. # FILES - **~/.mpdcron/mpdcron.conf** User configuration file # SEE ALSO - `mpd`(1) - # REPORTING BUGS If you find a bug, please report it at # COPYRIGHT Copyright (c) 2009 Ali Polatel \ Free use of this software is granted under the terms of the GNU General Public License (GPL). mpdcron-0.3+git20110303/m4/000077500000000000000000000000001153374523700147415ustar00rootroot00000000000000mpdcron-0.3+git20110303/m4/ax_check_compiler_flags.m4000066400000000000000000000063231153374523700220220ustar00rootroot00000000000000# =========================================================================== # http://www.nongnu.org/autoconf-archive/ax_check_compiler_flags.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_COMPILER_FLAGS(FLAGS, [ACTION-SUCCESS], [ACTION-FAILURE]) # # DESCRIPTION # # Check whether the given compiler FLAGS work with the current language's # compiler, or whether they give an error. (Warnings, however, are # ignored.) # # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on # success/failure. # # LICENSE # # Copyright (c) 2009 Steven G. Johnson # Copyright (c) 2009 Matteo Frigo # # 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 . # # 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. AC_DEFUN([AX_CHECK_COMPILER_FLAGS], [AC_PREREQ(2.59) dnl for _AC_LANG_PREFIX AC_MSG_CHECKING([whether _AC_LANG compiler accepts $1]) dnl Some hackery here since AC_CACHE_VAL can't handle a non-literal varname: AS_LITERAL_IF([$1], [AC_CACHE_VAL(AS_TR_SH(ax_cv_[]_AC_LANG_ABBREV[]_flags_[$1]), [ ax_save_FLAGS=$[]_AC_LANG_PREFIX[]FLAGS _AC_LANG_PREFIX[]FLAGS="$1" AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], AS_TR_SH(ax_cv_[]_AC_LANG_ABBREV[]_flags_[$1])=yes, AS_TR_SH(ax_cv_[]_AC_LANG_ABBREV[]_flags_[$1])=no) _AC_LANG_PREFIX[]FLAGS=$ax_save_FLAGS])], [ax_save_FLAGS=$[]_AC_LANG_PREFIX[]FLAGS _AC_LANG_PREFIX[]FLAGS="$1" AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], eval AS_TR_SH(ax_cv_[]_AC_LANG_ABBREV[]_flags_[$1])=yes, eval AS_TR_SH(ax_cv_[]_AC_LANG_ABBREV[]_flags_[$1])=no) _AC_LANG_PREFIX[]FLAGS=$ax_save_FLAGS]) eval ax_check_compiler_flags=$AS_TR_SH(ax_cv_[]_AC_LANG_ABBREV[]_flags_[$1]) AC_MSG_RESULT($ax_check_compiler_flags) if test "x$ax_check_compiler_flags" = xyes; then m4_default([$2], :) else m4_default([$3], :) fi ])dnl AX_CHECK_COMPILER_FLAGS mpdcron-0.3+git20110303/src/000077500000000000000000000000001153374523700152105ustar00rootroot00000000000000mpdcron-0.3+git20110303/src/Makefile.am000066400000000000000000000010641153374523700172450ustar00rootroot00000000000000DEFS+= -DGITHEAD=\"$(GITHEAD)\" -DLIBDIR=\"$(libdir)\" AM_CFLAGS= @MPDCRON_CFLAGS@ $(glib_CFLAGS) $(libdaemon_CFLAGS) $(libmpdclient_CFLAGS) bin_PROGRAMS= mpdcron noinst_HEADERS= cron-config.h cron-defs.h mpdcron_SOURCES= cron-conf.c cron-env.c cron-event.c cron-hooker.c \ cron-keyfile.c cron-log.c cron-loop.c cron-main.c mpdcron_LDADD= $(glib_LIBS) $(libdaemon_LIBS) $(libmpdclient_LIBS) if HAVE_GMODULE AM_CFLAGS+= $(gmodule_CFLAGS) mpdcron_LDADD+= $(gmodule_LIBS) mpdcron_SOURCES+= cron-gmodule.c endif SUBDIRS= . if HAVE_GMODULE SUBDIRS+= gmodule endif mpdcron-0.3+git20110303/src/cron-conf.c000066400000000000000000000044071153374523700172450ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "cron-defs.h" #include #include #include #include struct mpdcron_config conf; const char * conf_pid_file_proc(void) { char *name; if (conf.pid_path) return conf.pid_path; name = g_strdup_printf("%s.pid", daemon_pid_file_ident); conf.pid_path = g_build_filename(conf.home_path, name, NULL); g_free(name); return conf.pid_path; } int conf_init(void) { memset(&conf, 0, sizeof(struct mpdcron_config)); /* Get home directory */ if (g_getenv(ENV_HOME_DIR)) conf.home_path = g_strdup(g_getenv(ENV_HOME_DIR)); else if (g_getenv("HOME")) conf.home_path = g_build_filename(g_getenv("HOME"), DOT_MPDCRON, NULL); else { g_critical("Neither "ENV_HOME_DIR" nor HOME is set, exiting!"); return -1; } /* Set keyfile path */ conf.conf_path = g_build_filename(conf.home_path, PACKAGE".conf", NULL); #ifdef HAVE_GMODULE /* Set module path */ conf.mod_path = g_build_filename(conf.home_path, DOT_MODULES, NULL); #endif /* HAVE_GMODULE */ /* Get Mpd host, port, password */ if ((conf.hostname = g_getenv(ENV_MPD_HOST)) == NULL) conf.hostname = "localhost"; if ((conf.port = g_getenv(ENV_MPD_PORT)) == NULL) conf.port = "6600"; conf.password = g_getenv(ENV_MPD_PASSWORD); return 0; } void conf_free(void) { g_free(conf.home_path); g_free(conf.conf_path); g_free(conf.pid_path); #ifdef HAVE_GMODULE g_free(conf.mod_path); #endif /* HAVE_GMODULE */ memset(&conf, 0, sizeof(struct mpdcron_config)); } mpdcron-0.3+git20110303/src/cron-config.h000066400000000000000000000031671153374523700175740ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef MPDCRON_GUARD_CRON_CONFIG_H #define MPDCRON_GUARD_CRON_CONFIG_H 1 #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ /* Configuration */ #define ENV_HOME_DIR "MPDCRON_DIR" #define ENV_MPD_HOST "MPD_HOST" #define ENV_MPD_PORT "MPD_PORT" #define ENV_MPD_PASSWORD "MPD_PASSWORD" #define DOT_MPDCRON "." PACKAGE #define DOT_HOOKS "hooks" #define DOT_MODULES "modules" #define MODULE_INIT_FUNC "mpdcron_init" #define MODULE_CLOSE_FUNC "mpdcron_close" #define MODULE_RUN_FUNC "mpdcron_run" #define DEFAULT_PID_KILL_WAIT 3 #define DEFAULT_MPD_RECONNECT 5 #define DEFAULT_MPD_TIMEOUT 0 #define DEFAULT_LOG_LEVEL 0 #define DEFAULT_DATE_FORMAT "%Y-%m-%d %H-%M-%S %Z" #define DEFAULT_DATE_FORMAT_SIZE 64 #define MPDCRON_INTERNAL 1 #include "gmodule/gmodule.h" #endif /* !MPDCRON_GUARD_CRON_CONFIG_H */ mpdcron-0.3+git20110303/src/cron-defs.h000066400000000000000000000047631153374523700172530ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef MPDCRON_GUARD_CRON_DEFS_H #define MPDCRON_GUARD_CRON_DEFS_H 1 #include "cron-config.h" #include #include extern struct mpdcron_config conf; extern GMainLoop *loop; const char * conf_pid_file_proc(void); int conf_init(void); void conf_free(void); void env_clearenv(void); void env_stats(struct mpd_stats *stats); void env_status(struct mpd_status *status); void env_status_currentsong(struct mpd_song *song, struct mpd_status *status); int event_run(struct mpd_connection *conn, enum mpd_idle event); int hooker_run_hook(const char *name); int keyfile_load(GKeyFile **cfd_r); #ifdef HAVE_GMODULE int keyfile_load_modules(GKeyFile **cfd_r); #endif /* HAVE_GMODULE */ void log_handler(const gchar *domain, GLogLevelFlags level, const gchar *message, gpointer userdata); void loop_connect(void); void loop_disconnect(void); #ifdef HAVE_GMODULE int module_load(const char *modname, GKeyFile *config_fd); void module_close(int gclose); int module_database_run(const struct mpd_connection *conn, const struct mpd_stats *stats); int module_stored_playlist_run(const struct mpd_connection *conn); int module_queue_run(const struct mpd_connection *conn); int module_player_run(const struct mpd_connection *conn, const struct mpd_song *song, const struct mpd_status *status); int module_mixer_run(const struct mpd_connection *conn, const struct mpd_status *status); int module_output_run(const struct mpd_connection *conn); int module_options_run(const struct mpd_connection *conn, const struct mpd_status *status); int module_update_run(const struct mpd_connection *conn, const struct mpd_status *status); #endif /* HAVE_GMODULE */ #endif /* !MPDCRON_GUARD_CRON_DEFS_H */ mpdcron-0.3+git20110303/src/cron-env.c000066400000000000000000000231421153374523700171050ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "cron-defs.h" #include #include #include static const char * env_strstate(enum mpd_state state) { const char *strstate = NULL; switch (state) { case MPD_STATE_UNKNOWN: strstate = "unknown"; break; case MPD_STATE_STOP: strstate = "stop"; break; case MPD_STATE_PLAY: strstate = "play"; break; case MPD_STATE_PAUSE: strstate = "pause"; break; default: g_assert_not_reached(); break; } return strstate; } static void env_export_status(struct mpd_status *status) { char *envstr; const struct mpd_audio_format *fmt; envstr = g_strdup_printf("%d", mpd_status_get_volume(status)); g_setenv("MPD_STATUS_VOLUME", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%d", mpd_status_get_repeat(status)); g_setenv("MPD_STATUS_REPEAT", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%d", mpd_status_get_random(status)); g_setenv("MPD_STATUS_RANDOM", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%d", mpd_status_get_single(status)); g_setenv("MPD_STATUS_SINGLE", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%d", mpd_status_get_consume(status)); g_setenv("MPD_STATUS_CONSUME", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%d", mpd_status_get_queue_length(status)); g_setenv("MPD_STATUS_QUEUE_LENGTH", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%d", mpd_status_get_queue_version(status)); g_setenv("MPD_STATUS_QUEUE_VERSION", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%d", mpd_status_get_crossfade(status)); g_setenv("MPD_STATUS_CROSSFADE", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%d", mpd_status_get_song_pos(status)); g_setenv("MPD_STATUS_SONG_POS", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%d", mpd_status_get_song_id(status)); g_setenv("MPD_STATUS_SONG_ID", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%u", mpd_status_get_elapsed_time(status)); g_setenv("MPD_STATUS_ELAPSED_TIME", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%u", mpd_status_get_elapsed_ms(status)); g_setenv("MPD_STATUS_ELAPSED_MS", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%u", mpd_status_get_total_time(status)); g_setenv("MPD_STATUS_TOTAL_TIME", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%u", mpd_status_get_kbit_rate(status)); g_setenv("MPD_STATUS_KBIT_RATE", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%u", mpd_status_get_update_id(status)); g_setenv("MPD_STATUS_UPDATE_ID", envstr, 1); g_free(envstr); g_setenv("MPD_STATUS_STATE", env_strstate(mpd_status_get_state(status)), 1); if ((fmt = mpd_status_get_audio_format(status)) == NULL) g_setenv("MPD_STATUS_AUDIO_FORMAT", "0", 1); else { g_setenv("MPD_STATUS_AUDIO_FORMAT", "1", 1); envstr = g_strdup_printf("%u", fmt->sample_rate); g_setenv("MPD_STATUS_AUDIO_FORMAT_SAMPLE_RATE", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%u", fmt->bits); g_setenv("MPD_STATUS_AUDIO_FORMAT_BITS", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%u", fmt->channels); g_setenv("MPD_STATUS_AUDIO_FORMAT_CHANNELS", envstr, 1); g_free(envstr); } } static void env_export_song(struct mpd_song *song) { const char *tag; char *envstr; time_t t; char date[DEFAULT_DATE_FORMAT_SIZE] = { 0 }; g_setenv("MPD_SONG_URI", mpd_song_get_uri(song), 1); t = mpd_song_get_last_modified(song); strftime(date, DEFAULT_DATE_FORMAT_SIZE, DEFAULT_DATE_FORMAT, localtime(&t)); g_setenv("MPD_SONG_LAST_MODIFIED", date, 1); envstr = g_strdup_printf("%u", mpd_song_get_duration(song)); g_setenv("MPD_SONG_DURATION", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%u", mpd_song_get_pos(song)); g_setenv("MPD_SONG_POS", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%u", mpd_song_get_id(song)); g_setenv("MPD_SONG_ID", envstr, 1); g_free(envstr); /* Export tags. FIXME: For now we just export the first tag value to * the environment. */ if ((tag = mpd_song_get_tag(song, MPD_TAG_ARTIST, 0)) != NULL) g_setenv("MPD_SONG_TAG_ARTIST", tag, 1); if ((tag = mpd_song_get_tag(song, MPD_TAG_ALBUM, 0)) != NULL) g_setenv("MPD_SONG_TAG_ALBUM", tag, 1); if ((tag = mpd_song_get_tag(song, MPD_TAG_ALBUM_ARTIST, 0)) != NULL) g_setenv("MPD_SONG_TAG_ALBUM_ARTIST", tag, 1); if ((tag = mpd_song_get_tag(song, MPD_TAG_TITLE, 0)) != NULL) g_setenv("MPD_SONG_TAG_TITLE", tag, 1); if ((tag = mpd_song_get_tag(song, MPD_TAG_TRACK, 0)) != NULL) g_setenv("MPD_SONG_TAG_TRACK", tag, 1); if ((tag = mpd_song_get_tag(song, MPD_TAG_NAME, 0)) != NULL) g_setenv("MPD_SONG_TAG_NAME", tag, 1); if ((tag = mpd_song_get_tag(song, MPD_TAG_GENRE, 0)) != NULL) g_setenv("MPD_SONG_TAG_GENRE", tag, 1); if ((tag = mpd_song_get_tag(song, MPD_TAG_DATE, 0)) != NULL) g_setenv("MPD_SONG_TAG_DATE", tag, 1); if ((tag = mpd_song_get_tag(song, MPD_TAG_COMPOSER, 0)) != NULL) g_setenv("MPD_SONG_TAG_COMPOSER", tag, 1); if ((tag = mpd_song_get_tag(song, MPD_TAG_PERFORMER, 0)) != NULL) g_setenv("MPD_SONG_TAG_PERFORMER", tag, 1); if ((tag = mpd_song_get_tag(song, MPD_TAG_COMMENT, 0)) != NULL) g_setenv("MPD_SONG_TAG_COMMENT", tag, 1); if ((tag = mpd_song_get_tag(song, MPD_TAG_DISC, 0)) != NULL) g_setenv("MPD_SONG_TAG_DISC", tag, 1); if ((tag = mpd_song_get_tag(song, MPD_TAG_MUSICBRAINZ_ARTISTID, 0)) != NULL) g_setenv("MPD_SONG_TAG_MUSICBRAINZ_ARTISTID", tag, 1); if ((tag = mpd_song_get_tag(song, MPD_TAG_MUSICBRAINZ_ALBUMID, 0)) != NULL) g_setenv("MPD_SONG_TAG_MUSICBRAINZ_ALBUMID", tag, 1); if ((tag = mpd_song_get_tag(song, MPD_TAG_MUSICBRAINZ_ALBUMARTISTID, 0)) != NULL) g_setenv("MPD_SONG_TAG_MUSICBRAINZ_ALBUMARTISTID", tag, 1); if ((tag = mpd_song_get_tag(song, MPD_TAG_MUSICBRAINZ_TRACKID, 0)) != NULL) g_setenv("MPD_SONG_TAG_MUSICBRAINZ_TRACKID", tag, 1); } void env_stats(struct mpd_stats *stats) { char *envstr; time_t t; char date[DEFAULT_DATE_FORMAT_SIZE] = { 0 }; t = mpd_stats_get_db_update_time(stats); strftime(date, DEFAULT_DATE_FORMAT_SIZE, DEFAULT_DATE_FORMAT, localtime(&t)); g_setenv("MPD_DATABASE_UPDATE_TIME", date, 1); envstr = g_strdup_printf("%d", mpd_stats_get_number_of_artists(stats)); g_setenv("MPD_DATABASE_ARTISTS", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%d", mpd_stats_get_number_of_albums(stats)); g_setenv("MPD_DATABASE_ALBUMS", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%d", mpd_stats_get_number_of_songs(stats)); g_setenv("MPD_DATABASE_SONGS", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%lu", mpd_stats_get_play_time(stats)); g_setenv("MPD_DATABASE_PLAY_TIME", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%lu", mpd_stats_get_uptime(stats)); g_setenv("MPD_DATABASE_UPTIME", envstr, 1); g_free(envstr); envstr = g_strdup_printf("%lu", mpd_stats_get_db_play_time(stats)); g_setenv("MPD_DATABASE_DB_PLAY_TIME", envstr, 1); g_free(envstr); } void env_status(struct mpd_status *status) { env_export_status(status); } void env_status_currentsong(struct mpd_song *song, struct mpd_status *status) { env_export_status(status); if (song) env_export_song(song); } void env_clearenv(void) { /* status */ g_unsetenv("MPD_STATUS_VOLUME"); g_unsetenv("MPD_STATUS_REPEAT"); g_unsetenv("MPD_STATUS_RENDOM"); g_unsetenv("MPD_STATUS_SINGLE"); g_unsetenv("MPD_STATUS_CONSUME"); g_unsetenv("MPD_STATUS_QUEUE_LENGTH"); g_unsetenv("MPD_STATUS_QUEUE_VERSION"); g_unsetenv("MPD_STATUS_CROSSFADE"); g_unsetenv("MPD_STATUS_SONG_POS"); g_unsetenv("MPD_STATUS_SONG_ID"); g_unsetenv("MPD_STATUS_ELAPSED_TIME"); g_unsetenv("MPD_STATUS_ELAPSED_MS"); g_unsetenv("MPD_STATUS_TOTAL_TIME"); g_unsetenv("MPD_STATUS_KBIT_RATE"); g_unsetenv("MPD_STATUS_UPDATE_ID"); g_unsetenv("MPD_STATUS_STATE"); g_unsetenv("MPD_STATUS_AUDIO_FORMAT"); g_unsetenv("MPD_STATUS_AUDIO_FORMAT_SAMPLE_RATE"); g_unsetenv("MPD_STATUS_AUDIO_FORMAT_BITS"); g_unsetenv("MPD_STATUS_AUDIO_FORMAT_CHANNELS"); /* song */ g_unsetenv("MPD_SONG_URI"); g_unsetenv("MPD_SONG_TAG_ARTIST"); g_unsetenv("MPD_SONG_TAG_ALBUM"); g_unsetenv("MPD_SONG_TAG_ALBUM_ARTIST"); g_unsetenv("MPD_SONG_TAG_TITLE"); g_unsetenv("MPD_SONG_TAG_TRACK"); g_unsetenv("MPD_SONG_TAG_NAME"); g_unsetenv("MPD_SONG_TAG_GENRE"); g_unsetenv("MPD_SONG_TAG_DATE"); g_unsetenv("MPD_SONG_TAG_COMPOSER"); g_unsetenv("MPD_SONG_TAG_PERFORMER"); g_unsetenv("MPD_SONG_TAG_COMMENT"); g_unsetenv("MPD_SONG_TAG_DISC"); g_unsetenv("MPD_SONG_TAG_MUSICBRAINZ_ARTISTID"); g_unsetenv("MPD_SONG_TAG_MUSICBRAINZ_ALBUMID"); g_unsetenv("MPD_SONG_TAG_MUSICBRAINZ_ALBUMARTISTID"); g_unsetenv("MPD_SONG_TAG_MUSICBRAINZ_TRACKID"); g_unsetenv("MPD_SONG_LAST_MODIFIED"); g_unsetenv("MPD_SONG_POS"); g_unsetenv("MPD_SONG_ID"); /* stats */ g_unsetenv("MPD_DATABASE_UPDATE_TIME"); g_unsetenv("MPD_DATABASE_ARTISTS"); g_unsetenv("MPD_DATABASE_ALBUMS"); g_unsetenv("MPD_DATABASE_SONGS"); g_unsetenv("MPD_DATABASE_PLAY_TIME"); g_unsetenv("MPD_DATABASE_UPTIME"); g_unsetenv("MPD_DATABASE_DB_PLAY_TIME"); } mpdcron-0.3+git20110303/src/cron-event.c000066400000000000000000000136221153374523700174400ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "cron-defs.h" #include #include static int event_database(struct mpd_connection *conn) { int ret; const char *name; struct mpd_stats *stats; /* Song database has been updated. * Send stats command and add the variables to the environment. */ name = mpd_idle_name(MPD_IDLE_DATABASE); g_debug("Sending stats command to Mpd server"); if ((stats = mpd_run_stats(conn)) == NULL) return -1; ret = 0; #ifdef HAVE_GMODULE ret = module_database_run(conn, stats); #endif /* HAVE_GMODULE */ env_stats(stats); hooker_run_hook(name); mpd_stats_free(stats); return ret; } static int event_stored_playlist(struct mpd_connection *conn) { int ret; const char *name; /* A playlist has been updated, modified or deleted. */ name = mpd_idle_name(MPD_IDLE_STORED_PLAYLIST); ret = 0; #ifdef HAVE_GMODULE /* TODO: Send some data to the module. */ ret = module_stored_playlist_run(conn); #endif /* HAVE_GMODULE */ hooker_run_hook(name); return ret; } static int event_queue(struct mpd_connection *conn G_GNUC_UNUSED) { int ret; const char *name; /* The playlist has been changed. */ name = mpd_idle_name(MPD_IDLE_QUEUE); ret = 0; #ifdef HAVE_GMODULE /* TODO: Send some data to the module. */ ret = module_queue_run(conn); #endif /* HAVE_GMODULE */ hooker_run_hook(name); return ret; } static int event_player(struct mpd_connection *conn) { int ret; const char *name; struct mpd_song *song; struct mpd_status *status; /* The player state has changed. * Send status & currentsong command and add the variables to the * environment. */ name = mpd_idle_name(MPD_IDLE_PLAYER); g_debug("Sending status & currentsong commands to Mpd server"); if (!mpd_command_list_begin(conn, true) || !mpd_send_status(conn) || !mpd_send_current_song(conn) || !mpd_command_list_end(conn)) return -1; if ((status = mpd_recv_status(conn)) == NULL) return -1; if (mpd_status_get_state(status) == MPD_STATE_PLAY || mpd_status_get_state(status) == MPD_STATE_PAUSE) { if (!mpd_response_next(conn)) { mpd_status_free(status); return -1; } if ((song = mpd_recv_song(conn)) == NULL) { mpd_status_free(status); return -1; } } else song = NULL; if (!mpd_response_finish(conn)) { if (song) mpd_song_free(song); mpd_status_free(status); return -1; } ret = 0; #ifdef HAVE_GMODULE ret = module_player_run(conn, song, status); #endif /* HAVE_GMODULE */ env_status_currentsong(song, status); hooker_run_hook(name); if (song) mpd_song_free(song); mpd_status_free(status); return ret; } static int event_mixer(struct mpd_connection *conn) { int ret; const char *name; struct mpd_status *status; /* The volume has been modified. * Send status command and add the variables to the environment. */ name = mpd_idle_name(MPD_IDLE_MIXER); g_debug("Sending status command to Mpd server"); if ((status = mpd_run_status(conn)) == NULL) return -1; ret = 0; #ifdef HAVE_GMODULE ret = module_mixer_run(conn, status); #endif /* HAVE_GMODULE */ env_status(status); hooker_run_hook(name); mpd_status_free(status); return ret; } static int event_output(struct mpd_connection *conn) { int ret; const char *name; /* Outputs have been modified. */ name = mpd_idle_name(MPD_IDLE_OUTPUT); ret = 0; #ifdef HAVE_GMODULE /* TODO: Send some data to the module. */ ret = module_output_run(conn); #endif /* HAVE_GMODULE */ hooker_run_hook(name); return ret; } static int event_options(struct mpd_connection *conn) { int ret; const char *name; struct mpd_status *status; /* One of the options has been modified. * Send status command and add the variables to the environment. */ name = mpd_idle_name(MPD_IDLE_OPTIONS); g_debug("Sending status command to Mpd server"); if ((status = mpd_run_status(conn)) == NULL) return -1; ret = 0; #ifdef HAVE_GMODULE ret = module_options_run(conn, status); #endif /* HAVE_GMODULE */ env_status(status); hooker_run_hook(name); mpd_status_free(status); return ret; } static int event_update(struct mpd_connection *conn) { int ret; const char *name; struct mpd_status *status; /* A database update has started or finished. * Send status command and add the variables to the environment. */ name = mpd_idle_name(MPD_IDLE_OPTIONS); g_debug("Sending status command to Mpd server"); if ((status = mpd_run_status(conn)) == NULL) return -1; ret = 0; #ifdef HAVE_GMODULE ret = module_update_run(conn, status); #endif /* HAVE_GMODULE */ env_status(status); hooker_run_hook(name); mpd_status_free(status); return ret; } int event_run(struct mpd_connection *conn, enum mpd_idle event) { switch (event) { case MPD_IDLE_DATABASE: return event_database(conn); case MPD_IDLE_STORED_PLAYLIST: return event_stored_playlist(conn); case MPD_IDLE_PLAYER: return event_player(conn); case MPD_IDLE_QUEUE: return event_queue(conn); case MPD_IDLE_MIXER: return event_mixer(conn); case MPD_IDLE_OUTPUT: return event_output(conn); case MPD_IDLE_OPTIONS: return event_options(conn); case MPD_IDLE_UPDATE: return event_update(conn); default: g_warning("Unknown event 0x%x", event); return 0; } } mpdcron-0.3+git20110303/src/cron-gmodule.c000066400000000000000000000207341153374523700177550ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "cron-defs.h" #include #include #include #include #include struct module_data { int user; char *path; GModule *module; struct mpdcron_module *data; }; static GSList *modules = NULL; static char * module_path(const char *modname, int *user_r) { char *name, *path; g_debug("Trying to load module: %s", modname); name = g_strdup_printf("%s.%s", modname, G_MODULE_SUFFIX); g_debug("Added module suffix %s -> %s", modname, name); /* First check user path */ path = g_build_filename(conf.mod_path, name, NULL); g_debug("Trying user configured path `%s'", path); if (g_file_test(path, G_FILE_TEST_EXISTS)) { g_debug("Found %s -> `%s'", modname, path); g_free(name); *user_r = 1; return path; } g_free(path); /* Next check system path */ path = g_build_filename(LIBDIR, PACKAGE"-"VERSION, DOT_MODULES, name, NULL); g_debug("Trying system path `%s'", path); if (g_file_test(path, G_FILE_TEST_EXISTS)) { g_debug("Found %s -> `%s'", modname, path); g_free(name); *user_r = 0; return path; } g_free(name); g_free(path); return NULL; } static int module_init_one(const char *modname, GKeyFile *config_fd) { struct module_data *mod; mod = g_new0(struct module_data, 1); if ((mod->path = module_path(modname, &(mod->user))) == NULL) { g_warning("Error loading module %s: file not found", modname); g_free(mod); return -1; } if ((mod->module = g_module_open(mod->path, G_MODULE_BIND_LOCAL)) == NULL) { g_warning("Error loading module `%s': %s", mod->path, g_module_error()); g_free(mod->path); g_free(mod); return -1; } if (!g_module_symbol(mod->module, "module", (gpointer *)&mod->data) || mod->data == NULL) { g_warning("Error loading module `%s': no module structure", mod->path); g_module_close(mod->module); g_free(mod->path); g_free(mod); return -1; } /* Run the init() function if there's any. */ if (mod->data->init != NULL) { if ((mod->data->init)(&conf, config_fd) == MPDCRON_INIT_FAILURE) { g_warning("Skipped loading module `%s': init() returned %d", mod->path, MPDCRON_INIT_FAILURE); g_free(mod->path); g_module_close(mod->module); g_free(mod); return -1; } } g_debug("Loaded module `%s'", mod->path); modules = g_slist_prepend(modules, mod); return 0; } static void module_destroy_one(gpointer data, gpointer userdata) { int gclose; struct module_data *mod; mod = (struct module_data *)data; gclose = GPOINTER_TO_INT(userdata); /* Run the destroy function if there's any */ if (mod->data->destroy != NULL) (mod->data->destroy)(); g_free(mod->path); if (gclose) g_module_close(mod->module); g_free(mod); } static int module_process_ret(int ret, struct module_data *mod, GSList **slink_r, GSList **slist_r) { switch (ret) { case MPDCRON_EVENT_SUCCESS: return 0; case MPDCRON_EVENT_RECONNECT: g_message("%s module `%s' scheduled reconnect", mod->user ? "User" : "Standard", mod->path); return -1; case MPDCRON_EVENT_RECONNECT_NOW: g_message("%s module `%s' scheduled reconnect NOW!", mod->user ? "User" : "Standard", mod->path); return -1; case MPDCRON_EVENT_UNLOAD: g_message("Unloading %s module `%s'", mod->user ? "user" : "standard", mod->path); *slist_r = g_slist_remove_link(*slist_r, *slink_r); module_destroy_one(mod, NULL); g_slist_free(*slink_r); *slink_r = *slist_r; return 0; default: g_warning("Unknown return from %s module `%s': %d", mod->user ? "user" : "standard", mod->path, ret); return 0; } } int module_load(const char *modname, GKeyFile *config_fd) { return module_init_one(modname, config_fd); } void module_close(int gclose) { g_slist_foreach(modules, module_destroy_one, GINT_TO_POINTER(gclose)); g_slist_free(modules); modules = NULL; } int module_database_run(const struct mpd_connection *conn, const struct mpd_stats *stats) { int mret, ret; GSList *walk; struct module_data *mod; ret = 0; for (walk = modules; walk != NULL; walk = g_slist_next(walk)) { mod = (struct module_data *)walk->data; if (mod->data->event_database == NULL) continue; mret = (mod->data->event_database)(conn,stats); ret = module_process_ret(mret, mod, &walk, &modules); if (ret < 0 && mret == MPDCRON_EVENT_RECONNECT_NOW) break; } return ret; } int module_stored_playlist_run(const struct mpd_connection *conn) { int mret, ret; GSList *walk; struct module_data *mod; ret = 0; for (walk = modules; walk != NULL; walk = g_slist_next(walk)) { mod = (struct module_data *)walk->data; if (mod->data->event_stored_playlist == NULL) continue; mret = (mod->data->event_stored_playlist)(conn); ret = module_process_ret(mret, mod, &walk, &modules); if (ret < 0 && mret == MPDCRON_EVENT_RECONNECT_NOW) break; } return ret; } int module_queue_run(const struct mpd_connection *conn) { int mret, ret; GSList *walk; struct module_data *mod; ret = 0; for (walk = modules; walk != NULL; walk = g_slist_next(walk)) { mod = (struct module_data *)walk->data; if (mod->data->event_queue == NULL) continue; mret = (mod->data->event_queue)(conn); ret = module_process_ret(mret, mod, &walk, &modules); if (ret < 0 && mret == MPDCRON_EVENT_RECONNECT_NOW) break; } return ret; } int module_player_run(const struct mpd_connection *conn, const struct mpd_song *song, const struct mpd_status *status) { int mret, ret; GSList *walk; struct module_data *mod; ret = 0; for (walk = modules; walk != NULL; walk = g_slist_next(walk)) { mod = (struct module_data *)walk->data; if (mod->data->event_player == NULL) continue; mret = (mod->data->event_player)(conn, song, status); ret = module_process_ret(mret, mod, &walk, &modules); if (ret < 0 && mret == MPDCRON_EVENT_RECONNECT_NOW) break; } return ret; } int module_mixer_run(const struct mpd_connection *conn, const struct mpd_status *status) { int mret, ret; GSList *walk; struct module_data *mod; ret = 0; for (walk = modules; walk != NULL; walk = g_slist_next(walk)) { mod = (struct module_data *)walk->data; if (mod->data->event_mixer == NULL) continue; mret = (mod->data->event_mixer)(conn, status); ret = module_process_ret(mret, mod, &walk, &modules); if (ret < 0 && mret == MPDCRON_EVENT_RECONNECT_NOW) break; } return ret; } int module_output_run(const struct mpd_connection *conn) { int mret, ret; GSList *walk; struct module_data *mod; ret = 0; for (walk = modules; walk != NULL; walk = g_slist_next(walk)) { mod = (struct module_data *)walk->data; if (mod->data->event_output == NULL) continue; mret = (mod->data->event_output)(conn); ret = module_process_ret(mret, mod, &walk, &modules); if (ret < 0 && mret == MPDCRON_EVENT_RECONNECT_NOW) break; } return ret; } int module_options_run(const struct mpd_connection *conn, const struct mpd_status *status) { int mret, ret; GSList *walk; struct module_data *mod; ret = 0; for (walk = modules; walk != NULL; walk = g_slist_next(walk)) { mod = (struct module_data *)walk->data; if (mod->data->event_options == NULL) continue; mret = (mod->data->event_options)(conn, status); ret = module_process_ret(mret, mod, &walk, &modules); if (ret < 0 && mret == MPDCRON_EVENT_RECONNECT_NOW) break; } return ret; } int module_update_run(const struct mpd_connection *conn, const struct mpd_status *status) { int mret, ret; GSList *walk; struct module_data *mod; ret = 0; for (walk = modules; walk != NULL; walk = g_slist_next(walk)) { mod = (struct module_data *)walk->data; if (mod->data->event_update == NULL) continue; mret = (mod->data->event_update)(conn, status); ret = module_process_ret(mret, mod, &walk, &modules); if (ret < 0 && mret == MPDCRON_EVENT_RECONNECT_NOW) break; } return ret; } mpdcron-0.3+git20110303/src/cron-hooker.c000066400000000000000000000050251153374523700176040ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "cron-defs.h" #include #include /* Count the number of hook calls */ static struct hook_calls { const char *name; const char *env; unsigned int ncalls; } calls[] = { {"database", "MC_CALLS_DATABASE", 0}, {"stored_playlist", "MC_CALLS_STORED_PLAYLIST", 0}, {"queue", "MC_CALLS_QUEUE", 0}, {"playlist", "MC_CALLS_PLAYLIST", 0}, {"player", "MC_CALLS_PLAYER", 0}, {"mixer", "MC_CALLS_MIXER", 0}, {"output", "MC_CALLS_OUTPUT", 0}, {"options", "MC_CALLS_OPTIONS", 0}, {"update", "MC_CALLS_UPDATE", 0}, {NULL, NULL, 0}, }; static void hooker_increment(const char *name) { char *envstr; for (unsigned int i = 0; calls[i].name != NULL; i++) { if (strcmp(name, calls[i].name) == 0) { envstr = g_strdup_printf("%u", ++calls[i].ncalls); g_setenv(calls[i].env, envstr, 1); g_debug("Setting environment variable %s=%s", calls[i].env, envstr); g_free(envstr); break; } } } int hooker_run_hook(const char *name) { gchar **myargv; GError *error; hooker_increment(name); myargv = g_malloc(2 * sizeof(gchar *)); myargv[0] = g_build_filename(DOT_HOOKS, name, NULL); myargv[1] = NULL; g_debug("Running hook: %s home directory: %s", myargv[0], conf.home_path); error = NULL; if (!g_spawn_async(conf.home_path, myargv, NULL, G_SPAWN_LEAVE_DESCRIPTORS_OPEN | G_SPAWN_CHILD_INHERITS_STDIN, NULL, NULL, NULL, &error)) { if (error->code != G_SPAWN_ERROR_NOENT && error->code != G_SPAWN_ERROR_NOEXEC) g_warning("Failed to execute hook %s: %s", name, error->message); else g_debug("Failed to execute hook %s: %s", name, error->message); g_free(myargv[0]); g_free(myargv); g_error_free(error); return -1; } g_free(myargv[0]); g_free(myargv); return 0; } mpdcron-0.3+git20110303/src/cron-keyfile.c000066400000000000000000000113631153374523700177470ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "cron-defs.h" #include #include int keyfile_load(GKeyFile **cfd_r) { char *optstr; char **events; GError *error; error = NULL; if (!g_key_file_load_from_file(*cfd_r, conf.conf_path, G_KEY_FILE_NONE, &error)) { switch (error->code) { case G_FILE_ERROR_NOENT: case G_KEY_FILE_ERROR_NOT_FOUND: g_debug("Configuration file `%s' not found, skipping", conf.conf_path); g_error_free(error); /* Set defaults */ conf.killwait = DEFAULT_PID_KILL_WAIT; conf.loglevel = DEFAULT_LOG_LEVEL; conf.reconnect = DEFAULT_MPD_RECONNECT; conf.timeout = DEFAULT_MPD_TIMEOUT; return 0; default: g_critical("Failed to parse configuration file `%s': %s", conf.conf_path, error->message); g_error_free(error); return -1; } } /* Get main.pidfile */ if (conf.pid_path == NULL) conf.pid_path = g_key_file_get_string(*cfd_r, "main", "pidfile", NULL); /* Get main.killwait */ error = NULL; conf.killwait = g_key_file_get_integer(*cfd_r, "main", "killwait", &error); if (error != NULL) { switch (error->code) { case G_KEY_FILE_ERROR_INVALID_VALUE: g_warning("main.killwait not an integer: %s", error->message); g_error_free(error); return -1; default: g_error_free(error); conf.killwait = DEFAULT_PID_KILL_WAIT; break; } } if (conf.killwait <= 0) { g_warning("killwait smaller than zero, adjusting to default %d", DEFAULT_PID_KILL_WAIT); conf.killwait = DEFAULT_PID_KILL_WAIT; } /* Get main.loglevel */ error = NULL; conf.loglevel = g_key_file_get_integer(*cfd_r, "main", "loglevel", &error); if (error != NULL) { switch (error->code) { case G_KEY_FILE_ERROR_INVALID_VALUE: g_warning("main.loglevel not an integer: %s", error->message); g_error_free(error); return -1; default: g_error_free(error); conf.loglevel = DEFAULT_LOG_LEVEL; break; } } /* Get mpd.reconnect */ error = NULL; conf.reconnect = g_key_file_get_integer(*cfd_r, "mpd", "reconnect", &error); if (error != NULL) { switch (error->code) { case G_KEY_FILE_ERROR_INVALID_VALUE: g_debug("mpd.reconnect not an integer: %s", error->message); g_error_free(error); return -1; default: g_error_free(error); conf.reconnect = DEFAULT_MPD_RECONNECT; break; } } if (conf.reconnect <= 0) { g_warning("reconnect %s zero, adjusting to default %d", (conf.reconnect == 0) ? "equal to" : "smaller than", DEFAULT_MPD_RECONNECT); conf.reconnect = DEFAULT_MPD_RECONNECT; } optstr = g_strdup_printf("%d", conf.reconnect); g_setenv("MCOPT_RECONNECT", optstr, 1); g_free(optstr); /* Get mpd.timeout */ error = NULL; conf.timeout = g_key_file_get_integer(*cfd_r, "mpd", "timeout", &error); if (error != NULL) { switch (error->code) { case G_KEY_FILE_ERROR_INVALID_VALUE: g_warning("mpd.timeout not an integer: %s", error->message); g_error_free(error); return -1; default: g_error_free(error); conf.timeout = DEFAULT_MPD_TIMEOUT; break; } } if (conf.timeout < 0) { g_warning("timeout smaller than zero, adjusting to default %d", DEFAULT_MPD_TIMEOUT); conf.timeout = DEFAULT_MPD_TIMEOUT; } optstr = g_strdup_printf("%d", conf.timeout); g_setenv("MCOPT_TIMEOUT", optstr, 1); g_free(optstr); /* Get mpd.events */ if ((events = g_key_file_get_string_list(*cfd_r, "mpd", "events", NULL, NULL)) != NULL) { for (unsigned int i = 0; events[i] != NULL; i++) { enum mpd_idle parsed = mpd_idle_name_parse(events[i]); if (parsed == 0) g_warning("Unrecognized idle event: %s", events[i]); else conf.idle |= parsed; } g_strfreev(events); } return 0; } #ifdef HAVE_GMODULE int keyfile_load_modules(GKeyFile **cfd_r) { char **modules; g_assert(*cfd_r != NULL); /* Load modules */ if ((modules = g_key_file_get_string_list(*cfd_r, "main", "modules", NULL, NULL)) != NULL) { for (unsigned int i = 0; modules[i] != NULL; i++) module_load(modules[i], *cfd_r); g_strfreev(modules); } return 0; } #endif /* HAVE_GMODULE */ mpdcron-0.3+git20110303/src/cron-log.c000066400000000000000000000034401153374523700170750ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "cron-defs.h" static void log_output(const gchar *domain, GLogLevelFlags level, const gchar *message) { int dlevel; g_return_if_fail(message != NULL && message[0] != '\0'); switch (level) { case G_LOG_LEVEL_CRITICAL: dlevel = LOG_CRIT; break; case G_LOG_LEVEL_WARNING: dlevel = LOG_WARNING; break; case G_LOG_LEVEL_MESSAGE: dlevel = LOG_NOTICE; case G_LOG_LEVEL_INFO: dlevel = LOG_INFO; break; case G_LOG_LEVEL_DEBUG: default: dlevel = LOG_DEBUG; break; } if (domain != NULL) daemon_log(dlevel, "[%s] %s", domain, message); else daemon_log(dlevel, "%s", message); } void log_handler(const gchar *domain, GLogLevelFlags level, const gchar *message, gpointer userdata) { int clevel = GPOINTER_TO_INT(userdata); if (((level & G_LOG_LEVEL_MESSAGE) && clevel < 1) || ((level & G_LOG_LEVEL_INFO) && clevel < 2) || ((level & G_LOG_LEVEL_DEBUG) && clevel < 3)) return; log_output(domain, level, message); } mpdcron-0.3+git20110303/src/cron-loop.c000066400000000000000000000115631153374523700172720ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * Based in part upon mpdscribble which is: * Copyright (C) 2008-2009 The Music Player Daemon Project * Copyright (C) 2005-2008 Kuno Woudt * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "cron-defs.h" #include #include #include #include static guint idle_sid, reconnect_sid; static const unsigned *version = NULL; static struct mpd_connection *conn = NULL; static void loop_schedule_reconnect(void); static void loop_schedule_idle(void); static void loop_failure(void) { char *msg; g_assert(conn != NULL); msg = g_strescape(mpd_connection_get_error_message(conn), NULL); g_warning("Mpd error: %s", msg); g_free(msg); mpd_connection_free(conn); conn = NULL; } static gboolean loop_reconnect(G_GNUC_UNUSED gpointer data) { g_message("Connecting to `%s' on port %s with timeout %d", conf.hostname, conf.port, conf.timeout); if ((conn = mpd_connection_new(conf.hostname, atoi(conf.port), conf.timeout)) == NULL) { g_critical("Error creating mpd connection: out of memory"); exit(EXIT_FAILURE); } if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) { loop_failure(); return TRUE; } if (conf.password != NULL) { g_message("Sending password"); if (!mpd_run_password(conn, conf.password)) { g_critical("Authentication failed: %s", mpd_connection_get_error_message(conn)); mpd_connection_free(conn); conn = NULL; exit(EXIT_FAILURE); } } if ((version = mpd_connection_get_server_version(conn)) != NULL) { g_message("Connected to Mpd server, running version %d.%d.%d", version[0], version[1], version[2]); if (mpd_connection_cmp_server_version(conn, 0, 14, 0) < 0) g_warning("Mpd version 0.14.0 is required for idle command"); } else g_message("Connected to Mpd server, running version unknown"); loop_schedule_idle(); reconnect_sid = 0; return FALSE; } static gboolean loop_idle(G_GNUC_UNUSED GIOChannel *source, G_GNUC_UNUSED GIOCondition condition, G_GNUC_UNUSED gpointer data) { unsigned j; const char *name; enum mpd_idle i, myidle; g_assert(idle_sid != 0); g_assert(conn != NULL); idle_sid = 0; myidle = mpd_recv_idle(conn, false); if (!mpd_response_finish(conn)) { /* Check whether idle command is supported */ if (mpd_connection_get_error(conn) == MPD_ERROR_SERVER && mpd_connection_get_server_error(conn) == MPD_SERVER_ERROR_UNKNOWN_CMD) { g_critical("Idle command not supported by Mpd"); mpd_connection_free(conn); conn = NULL; exit(EXIT_FAILURE); } if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) { loop_failure(); loop_schedule_reconnect(); return FALSE; } loop_schedule_idle(); return FALSE; } for (j = 0 ;; j++) { i = 1 << j; if ((name = mpd_idle_name(i)) == NULL) break; if (myidle & i) { /* Clear the environment */ env_clearenv(); /* Run the appropriate event */ if (event_run(conn, i) < 0) { loop_failure(); loop_schedule_reconnect(); return FALSE; } } } loop_schedule_idle(); return FALSE; } static void loop_schedule_reconnect(void) { g_assert(reconnect_sid == 0); g_message("Waiting for %d seconds before reconnecting", conf.reconnect); reconnect_sid = g_timeout_add_seconds(conf.reconnect, loop_reconnect, NULL); } static void loop_schedule_idle(void) { bool ret; GIOChannel *channel; g_assert(idle_sid == 0); g_assert(conn != NULL); g_debug("Sending idle command with mask 0x%x", conf.idle); ret = (conf.idle == 0) ? mpd_send_idle(conn) : mpd_send_idle_mask(conn, conf.idle); if (!ret && mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) { loop_failure(); loop_schedule_reconnect(); return; } /* Add a GLib watch on the libmpdclient socket. */ channel = g_io_channel_unix_new(mpd_connection_get_fd(conn)); idle_sid = g_io_add_watch(channel, G_IO_IN, loop_idle, NULL); g_io_channel_unref(channel); } void loop_connect(void) { if (loop_reconnect(NULL)) loop_schedule_reconnect(); } void loop_disconnect(void) { if (idle_sid != 0) g_source_remove(idle_sid); if (reconnect_sid != 0) g_source_remove(reconnect_sid); if (conn != NULL) { mpd_connection_free(conn); conn = NULL; } } mpdcron-0.3+git20110303/src/cron-main.c000066400000000000000000000142651153374523700172470ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "cron-defs.h" #include #include #include #include #include #include #include #include #include #include GMainLoop *loop = NULL; static GKeyFile *cfd = NULL; static int optv, optk; static GOptionEntry options[] = { {"version", 'V', 0, G_OPTION_ARG_NONE, &optv, "Display version", NULL}, {"kill", 'k', 0, G_OPTION_ARG_NONE, &optk, "Kill daemon", NULL}, {"no-daemon", 'n', 0, G_OPTION_ARG_NONE, &conf.no_daemon, "Don't detach from console", NULL}, {NULL, 0, 0, 0, NULL, NULL, NULL}, }; static void about(void) { printf(PACKAGE"-"VERSION GITHEAD "\n"); } static void internal_cleanup( #ifndef HAVE_GMODULE G_GNUC_UNUSED #endif /* !HAVE_GMODULE */ bool signaled) { #ifdef HAVE_GMODULE module_close(signaled ? 0 : 1); #endif /* HAVE_GMODULE */ conf_free(); if (cfd != NULL) { g_key_file_free(cfd); cfd = NULL; } if (loop != NULL) { g_main_loop_quit(loop); g_main_loop_unref(loop); loop = NULL; } } static void cleanup(void) { internal_cleanup(false); } static void sig_cleanup(int signum) { struct sigaction action; internal_cleanup(true); sigaction(signum, NULL, &action); action.sa_handler = SIG_DFL; sigaction(signum, &action, NULL); raise(signum); } int main(int argc, char **argv) { int pid, ret; struct sigaction new_action, old_action; GOptionContext *ctx; GError *parse_err = NULL; daemon_pid_file_ident = daemon_log_ident = daemon_ident_from_argv0(argv[0]); daemon_pid_file_proc = conf_pid_file_proc; if (conf_init() < 0) return EXIT_FAILURE; ctx = g_option_context_new(""); g_option_context_add_main_entries(ctx, options, PACKAGE); g_option_context_set_summary(ctx, PACKAGE"-"VERSION GITHEAD" - mpd cron daemon"); if (!g_option_context_parse(ctx, &argc, &argv, &parse_err)) { g_printerr("option parsing failed: %s\n", parse_err->message); g_option_context_free(ctx); g_error_free(parse_err); return EXIT_FAILURE; } g_option_context_free(ctx); if (optv) { about(); cleanup(); return EXIT_SUCCESS; } #ifdef DAEMON_SET_VERBOSITY_AVAILABLE if (conf.no_daemon) daemon_set_verbosity(LOG_DEBUG); #endif /* DAEMON_SET_VERBOSITY_AVAILABLE */ /* Version to environment variable */ g_setenv("MPDCRON_PACKAGE", PACKAGE, 1); g_setenv("MPDCRON_VERSION", VERSION, 1); g_setenv("MPDCRON_GITHEAD", GITHEAD, 1); /* Command line options to environment variables */ if (conf.no_daemon) g_unsetenv("MCOPT_DAEMONIZE"); else g_setenv("MCOPT_DAEMONIZE", "1", 1); /* Important! Parse configuration file before killing the daemon * because the configuration file has a pidfile and killwait option. */ cfd = g_key_file_new(); if (keyfile_load(&cfd) < 0) { cleanup(); return EXIT_FAILURE; } if (optk) { if (daemon_pid_file_kill_wait(SIGINT, conf.killwait) < 0) { g_warning("Failed to kill daemon: %s", strerror(errno)); cleanup(); return EXIT_FAILURE; } daemon_pid_file_remove(); cleanup(); return EXIT_SUCCESS; } /* Logging */ g_log_set_default_handler(log_handler, GINT_TO_POINTER(conf.no_daemon ? 5 : conf.loglevel)); /* Signal handling */ new_action.sa_handler = sig_cleanup; sigemptyset(&new_action.sa_mask); new_action.sa_flags = 0; #define HANDLE_SIGNAL(sig) \ do { \ sigaction((sig), NULL, &old_action); \ if (old_action.sa_handler != SIG_IGN) \ sigaction((sig), &new_action, NULL); \ } while (0) HANDLE_SIGNAL(SIGABRT); HANDLE_SIGNAL(SIGSEGV); HANDLE_SIGNAL(SIGINT); HANDLE_SIGNAL(SIGTERM); #undef HANDLE_SIGNAL if (conf.no_daemon) { /* Create the main loop */ loop = g_main_loop_new(NULL, FALSE); #ifdef HAVE_GMODULE /* Load modules which may add initial events */ keyfile_load_modules(&cfd); #endif /* HAVE_GMODULE */ g_key_file_free(cfd); cfd = NULL; /* Add default initial events */ loop_connect(); /* Run the main loop */ g_main_loop_run(loop); cleanup(); return EXIT_SUCCESS; } /* Daemonize */ if ((pid = daemon_pid_file_is_running()) > 0) { g_critical("Daemon already running on PID %u", pid); return EXIT_FAILURE; } daemon_retval_init(); pid = daemon_fork(); if (pid < 0) { g_critical("Failed to fork: %s", strerror(errno)); daemon_retval_done(); return EXIT_FAILURE; } else if (pid != 0) { /* Parent */ cleanup(); if ((ret = daemon_retval_wait(2)) < 0) { g_critical("Could not receive return value from daemon process: %s", strerror(errno)); return 255; } if (ret != 0) g_critical("Daemon returned %i as return value", ret); else g_critical("Daemon returned %i as return value", ret); return ret; } else { /* Daemon */ if (daemon_close_all(-1) < 0) { g_critical("Failed to close all file descriptors: %s", strerror(errno)); daemon_retval_send(1); return EXIT_FAILURE; } if (daemon_pid_file_create() < 0) { g_critical("Failed to create PID file: %s", strerror(errno)); daemon_retval_send(2); return EXIT_FAILURE; } /* Send OK to parent process */ daemon_retval_send(0); /* Create the main loop */ loop = g_main_loop_new(NULL, FALSE); #ifdef HAVE_GMODULE /* Load modules which may add initial events */ keyfile_load_modules(&cfd); #endif /* HAVE_GMODULE */ g_key_file_free(cfd); cfd = NULL; /* Add default initial events */ loop_connect(); /* Run the main loop */ g_main_loop_run(loop); cleanup(); return EXIT_SUCCESS; } return EXIT_SUCCESS; } mpdcron-0.3+git20110303/src/gmodule/000077500000000000000000000000001153374523700166445ustar00rootroot00000000000000mpdcron-0.3+git20110303/src/gmodule/Makefile.am000066400000000000000000000004011153374523700206730ustar00rootroot00000000000000SUBDIRS= . if WANT_NOTIFICATION SUBDIRS+= notification endif if WANT_SCROBBLER SUBDIRS+= scrobbler endif if WANT_STATS SUBDIRS+= stats endif noinst_HEADERS= utils.h mpdcron_module_includedir=$(includedir)/mpdcron mpdcron_module_include_HEADERS= gmodule.h mpdcron-0.3+git20110303/src/gmodule/gmodule.h000066400000000000000000000056001153374523700204520ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef MPDCRON_GUARD_MODULE_H #define MPDCRON_GUARD_MODULE_H 1 #include #ifdef MPDCRON_MODULE #define G_LOG_DOMAIN MPDCRON_MODULE #endif /* MPDCRON_MODULE */ #include #include enum mpdcron_init_retval { MPDCRON_INIT_SUCCESS = 0, /** Success */ MPDCRON_INIT_FAILURE, /** Failure */ }; enum mpdcron_event_retval { MPDCRON_EVENT_SUCCESS = 0, /** Success **/ MPDCRON_EVENT_RECONNECT, /** Schedule a reconnection to mpd server **/ MPDCRON_EVENT_RECONNECT_NOW, /** Schedule a reconnection to mpd server immediately. **/ MPDCRON_EVENT_UNLOAD, /** Unload the module **/ }; struct mpdcron_config { char *home_path; char *conf_path; char *pid_path; char *mod_path; const char *hostname; const char *port; const char *password; int no_daemon; int timeout; int reconnect; int killwait; int loglevel; enum mpd_idle idle; }; struct mpdcron_module { /** Name of the module */ const char *name; /** Initialization function */ int (*init) (const struct mpdcron_config *, GKeyFile *); /** Cleanup function */ void (*destroy) (void); /** Function for database event */ int (*event_database) (const struct mpd_connection *conn, const struct mpd_stats *); /** Function for stored playlist event */ int (*event_stored_playlist) (const struct mpd_connection *); /** Function for queue event */ int (*event_queue) (const struct mpd_connection *); /** Function for player event */ int (*event_player) (const struct mpd_connection *, const struct mpd_song *, const struct mpd_status *); /** Function for mixer event */ int (*event_mixer) (const struct mpd_connection *, const struct mpd_status *); /** Function for output event */ int (*event_output) (const struct mpd_connection *); /** Function for options event */ int (*event_options) (const struct mpd_connection *, const struct mpd_status *); /** Function for update event */ int (*event_update) (const struct mpd_connection *, const struct mpd_status *); }; #ifndef MPDCRON_INTERNAL extern struct mpdcron_module module; #endif /* !MPDCRON_INTERNAL */ #endif /* !MPDCRON_GUARD_MODULE_H */ mpdcron-0.3+git20110303/src/gmodule/notification/000077500000000000000000000000001153374523700213325ustar00rootroot00000000000000mpdcron-0.3+git20110303/src/gmodule/notification/Makefile.am000066400000000000000000000010251153374523700233640ustar00rootroot00000000000000SUBDIRS= . DEFS+= -DGITHEAD=\"$(GITHEAD)\" -DLIBDIR=\"$(libdir)\" AM_CFLAGS= @MPDCRON_CFLAGS@ $(glib_CFLAGS) $(libmpdclient_CFLAGS) MODULE_DIR=$(libdir)/$(PACKAGE)-$(VERSION)/modules noinst_HEADERS= notification-defs.h notification_LTLIBRARIES= notification.la notificationdir=$(MODULE_DIR) notification_la_SOURCES= notification-cover.c notification-dhms.c notification-file.c \ notification-spawn.c notification-module.c notification_la_LDFLAGS= -module -avoid-version notification_la_LIBADD= $(glib_LIBS) $(libmpdclient_LIBS) mpdcron-0.3+git20110303/src/gmodule/notification/notification-cover.c000066400000000000000000000022711153374523700253020ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "notification-defs.h" #include char * cover_find(const char *artist, const char *album) { char *cfile; char *cpath; cfile = g_strdup_printf("%s-%s.%s", artist, album, file_config.cover_suffix); cpath = g_build_filename(file_config.cover_path, cfile, NULL); g_free(cfile); if (g_file_test(cpath, G_FILE_TEST_EXISTS)) return cpath; g_free(cpath); return NULL; } mpdcron-0.3+git20110303/src/gmodule/notification/notification-defs.h000066400000000000000000000027271153374523700251200ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef MPDCRON_GUARD_NOTIFICATION_DEFS_H #define MPDCRON_GUARD_NOTIFICATION_DEFS_H 1 #ifdef HAVE_CONFIG_H #include "config.h" #endif /* !HAVE_CONFIG_H */ #define MPDCRON_MODULE "notification" #include "../gmodule.h" #include struct config { int events; char *cover_path; char *cover_suffix; char *timeout; char *type; char *urgency; char **hints; }; extern struct config file_config; char * cover_find(const char *artist, const char *album); char * dhms(unsigned long t); int file_load(GKeyFile *fd); void file_cleanup(void); void notify_send(const char *icon, const char *summary, const char *body); #endif /* !MPDCRON_GUARD_NOTIFICATION_DEFS_H */ mpdcron-0.3+git20110303/src/gmodule/notification/notification-dhms.c000066400000000000000000000023551153374523700251220ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "notification-defs.h" char * dhms(unsigned long t) { int days, hours, mins, secs; #define SECSPERDAY 86400 #define SECSPERHOUR 3600 #define SECSPERMIN 60 days = t / SECSPERDAY; t %= SECSPERDAY; hours = t / SECSPERHOUR; t %= SECSPERHOUR; mins = t / SECSPERMIN; t %= SECSPERMIN; secs = t; #undef SECSPERDAY #undef SECSPERHOUR #undef SECSPERMIN return g_strdup_printf("%d days, %d:%02d:%02d", days, hours, mins, secs); } mpdcron-0.3+git20110303/src/gmodule/notification/notification-file.c000066400000000000000000000101771153374523700251070ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * Based in part upon mpdscribble which is: * Copyright (C) 2008-2009 The Music Player Daemon Project * Copyright (C) 2005-2008 Kuno Woudt * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "notification-defs.h" #include #include #include #include "../utils.h" struct config file_config; int file_load(GKeyFile *fd) { int event; char **values; GError *error; memset(&file_config, 0, sizeof(struct config)); error = NULL; if (!load_string(fd, MPDCRON_MODULE, "cover_path", false, &file_config.cover_path, &error)) { g_critical("Failed to load "MPDCRON_MODULE".cover_path: %s", error->message); g_error_free(error); return -1; } if (!load_string(fd, MPDCRON_MODULE, "cover_suffix", false, &file_config.cover_suffix, &error)) { g_critical("Failed to load "MPDCRON_MODULE".cover_suffix: %s", error->message); g_error_free(error); return -1; } if (!load_string(fd, MPDCRON_MODULE, "timeout", false, &file_config.timeout, &error)) { g_critical("Failed to load "MPDCRON_MODULE".timeout: %s", error->message); g_error_free(error); return -1; } if (!load_string(fd, MPDCRON_MODULE, "type", false, &file_config.type, &error)) { g_critical("Failed to load "MPDCRON_MODULE".type: %s", error->message); g_error_free(error); return -1; } if (!load_string(fd, MPDCRON_MODULE, "urgency", false, &file_config.urgency, &error)) { g_critical("Failed to load "MPDCRON_MODULE".urgency: %s", error->message); g_error_free(error); return -1; } error = NULL; file_config.hints = g_key_file_get_string_list(fd, MPDCRON_MODULE, "hints", NULL, &error); if (error != NULL) { switch (error->code) { case G_KEY_FILE_ERROR_GROUP_NOT_FOUND: case G_KEY_FILE_ERROR_KEY_NOT_FOUND: g_error_free(error); break; default: g_critical("Failed to load %s.hints: %s", MPDCRON_MODULE, error->message); g_error_free(error); return -1; } } error = NULL; values = g_key_file_get_string_list(fd, MPDCRON_MODULE, "events", NULL, &error); if (error != NULL) { switch (error->code) { case G_KEY_FILE_ERROR_GROUP_NOT_FOUND: case G_KEY_FILE_ERROR_KEY_NOT_FOUND: g_error_free(error); break; default: g_critical("Failed to load "MPDCRON_MODULE".events: %s", error->message); g_error_free(error); return -1; } } if (values != NULL) { for (unsigned int i = 0; values[i] != NULL; i++) { if ((event = mpd_idle_name_parse(values[i])) < 0) g_warning("Invalid value `%s' in "MPDCRON_MODULE".events", values[i]); else if (event == MPD_IDLE_STORED_PLAYLIST || event == MPD_IDLE_QUEUE || event == MPD_IDLE_OUTPUT) g_warning("Event `%s' not a supported event", values[i]); else file_config.events |= event; } g_strfreev(values); } if (file_config.events == 0) file_config.events = MPD_IDLE_DATABASE | MPD_IDLE_PLAYER | MPD_IDLE_MIXER | MPD_IDLE_OPTIONS | MPD_IDLE_UPDATE; if (file_config.cover_path == NULL && g_getenv("HOME") != NULL) file_config.cover_path = g_build_filename(g_getenv("HOME"), ".covers", NULL); if (file_config.cover_suffix == NULL) file_config.cover_suffix = g_strdup("jpg"); return 0; } void file_cleanup(void) { g_free(file_config.cover_path); g_free(file_config.cover_suffix); g_free(file_config.timeout); g_free(file_config.type); g_free(file_config.urgency); if (file_config.hints != NULL) g_strfreev(file_config.hints); } mpdcron-0.3+git20110303/src/gmodule/notification/notification-module.c000066400000000000000000000155451153374523700254610ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ /* Notes about the module: * Using libnotify directly doesn't work due to many reasons. * That's why we spawn notify-send. */ #include "notification-defs.h" #include #include #include #include #include #include #include static bool was_paused; static unsigned last_id = -1; static GTimer *timer = NULL; /* Utility functions */ static void song_paused(void) { if (!was_paused) g_timer_stop(timer); was_paused = true; } static void song_stopped(void) { last_id = -1; was_paused = false; } static void song_continued(void) { g_timer_continue(timer); } static void song_changed(const struct mpd_song *song) { const char *summary; char *cpath, *body; assert(song != NULL); g_timer_start(timer); cpath = cover_find(mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), mpd_song_get_tag(song, MPD_TAG_ALBUM, 0)); if (cpath == NULL) g_debug("Failed to find cover for album (%s - %s), suffix: %s", mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), mpd_song_get_tag(song, MPD_TAG_ALBUM, 0), file_config.cover_suffix); g_debug("Sending notify for song (%s - %s), id: %u, pos: %u", mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), mpd_song_get_tag(song, MPD_TAG_TITLE, 0), mpd_song_get_id(song), mpd_song_get_pos(song)); summary = mpd_song_get_tag(song, MPD_TAG_TITLE, 0) ? mpd_song_get_tag(song, MPD_TAG_TITLE, 0) : mpd_song_get_uri(song); body = g_strdup_printf("by %s - %s", mpd_song_get_tag(song, MPD_TAG_ARTIST, 0) ? mpd_song_get_tag(song, MPD_TAG_ARTIST, 0) : "Unknown", mpd_song_get_tag(song, MPD_TAG_ALBUM, 0) ? mpd_song_get_tag(song, MPD_TAG_ALBUM, 0) : "Unknown"); notify_send(cpath, summary, body); g_free(body); g_free(cpath); } static void song_started(const struct mpd_song *song) { song_changed(song); } static void song_playing(const struct mpd_song *song, unsigned elapsed) { unsigned prev_elapsed = g_timer_elapsed(timer, NULL); if (prev_elapsed > elapsed) { g_debug("Repeated song detected"); song_started(song); } } static int init(G_GNUC_UNUSED const struct mpdcron_config *conf, GKeyFile *fd) { was_paused = false; last_id = -1; /* Parse configuration */ if (file_load(fd) < 0) return MPDCRON_INIT_FAILURE; timer = g_timer_new(); g_message("Initialized"); return MPDCRON_INIT_SUCCESS; } static void destroy(void) { g_message("Exiting"); file_cleanup(); g_timer_destroy(timer); } static int event_database(G_GNUC_UNUSED const struct mpd_connection *conn, const struct mpd_stats *stats) { time_t t; const char *summary; char *play_time, *uptime, *db_play_time; char *body; g_assert(stats != NULL); if ((file_config.events | MPD_IDLE_DATABASE) == 0) return MPDCRON_EVENT_SUCCESS; play_time = dhms(mpd_stats_get_play_time(stats)); uptime = dhms(mpd_stats_get_uptime(stats)); db_play_time = dhms(mpd_stats_get_db_play_time(stats)); t = mpd_stats_get_db_update_time(stats); summary = "Mpd Database has been updated"; body = g_strdup_printf("Artists: %u\n" "Albums: %u\n" "Songs: %u\n" "\n" "Play Time: %s\n" "Uptime: %s\n" "DB Updated: %s" "DB Play Time: %s", mpd_stats_get_number_of_artists(stats), mpd_stats_get_number_of_albums(stats), mpd_stats_get_number_of_songs(stats), play_time, uptime, ctime(&t), db_play_time); notify_send(NULL, summary, body); g_free(play_time); g_free(uptime); g_free(db_play_time); g_free(body); return MPDCRON_EVENT_SUCCESS; } static int event_player(G_GNUC_UNUSED const struct mpd_connection *conn, const struct mpd_song *song, const struct mpd_status *status) { enum mpd_state state; g_assert(status != NULL); if ((file_config.events | MPD_IDLE_PLAYER) == 0) return MPDCRON_EVENT_SUCCESS; state = mpd_status_get_state(status); assert(song != NULL || state != MPD_STATE_PLAY); if (state == MPD_STATE_PAUSE) { song_paused(); return MPDCRON_EVENT_SUCCESS; } else if (state != MPD_STATE_PLAY) { song_stopped(); return MPDCRON_EVENT_SUCCESS; } if (was_paused) { if (song != NULL && mpd_song_get_id(song) == last_id) song_continued(); was_paused = false; } if (song != NULL) { if (mpd_song_get_id(song) != last_id) { /* New song */ song_started(song); last_id = mpd_song_get_id(song); } else { /* Still playing the previous song */ song_playing(song, mpd_status_get_elapsed_time(status)); } } return MPDCRON_EVENT_SUCCESS; } static int event_mixer(G_GNUC_UNUSED const struct mpd_connection *conn, const struct mpd_status *status) { char *summary; g_assert(status != NULL); if ((file_config.events | MPD_IDLE_MIXER) == 0) return MPDCRON_EVENT_SUCCESS; summary = g_strdup_printf("Mpd Volume: %d%%", mpd_status_get_volume(status)); notify_send(NULL, summary, ""); g_free(summary); return MPDCRON_EVENT_SUCCESS; } static int event_options(G_GNUC_UNUSED const struct mpd_connection *conn, const struct mpd_status *status) { char *body; g_assert(status != NULL); if ((file_config.events | MPD_IDLE_OPTIONS) == 0) return MPDCRON_EVENT_SUCCESS; body = g_strdup_printf("Repeat: %s\n" "Random: %s\n" "Single: %s\n" "Consume: %s\n" "Crossfade: %u", mpd_status_get_repeat(status) ? "on" : "off", mpd_status_get_random(status) ? "on" : "off", mpd_status_get_single(status) ? "on" : "off", mpd_status_get_consume(status) ? "on" : "off", mpd_status_get_crossfade(status)); notify_send(NULL, "Mpd Options have changed!", body); g_free(body); return MPDCRON_EVENT_SUCCESS; } static int event_update(G_GNUC_UNUSED const struct mpd_connection *conn, const struct mpd_status *status) { char *summary; g_assert(status != NULL); if ((file_config.events | MPD_IDLE_UPDATE) == 0) return MPDCRON_EVENT_SUCCESS; summary = g_strdup_printf("Mpd Update ID: %u", mpd_status_get_update_id(status)); notify_send(NULL, summary, ""); return MPDCRON_EVENT_SUCCESS; } struct mpdcron_module module = { .name = "Notification", .init = init, .destroy = destroy, .event_database = event_database, .event_player = event_player, .event_mixer = event_mixer, .event_options = event_options, .event_update = event_update, }; mpdcron-0.3+git20110303/src/gmodule/notification/notification-spawn.c000066400000000000000000000040531153374523700253140ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "notification-defs.h" #include #include #include void notify_send(const char *icon, const char *summary, const char *body) { int i, j, len; char **myargv; GError *error; i = 0; len = 8 + (file_config.hints ? g_strv_length(file_config.hints) : 0); myargv = g_malloc0(sizeof(char *) * len); myargv[i++] = g_strdup("notify-send"); if (file_config.urgency != NULL) myargv[i++] = g_strdup_printf("--urgency=%s", file_config.urgency); if (file_config.timeout != NULL) myargv[i++] = g_strdup_printf("--expire-time=%s", file_config.timeout); if (file_config.type != NULL) myargv[i++] = g_strdup_printf("--category=%s", file_config.type); if (icon != NULL) myargv[i++] = g_strdup_printf("--icon=%s", icon); myargv[i++] = g_strdup(summary); myargv[i++] = g_strdup(body); if (file_config.hints != NULL) { for (j = 0; file_config.hints[j] != NULL; j++) myargv[i++] = g_strdup_printf("--hint=%s", file_config.hints[j]); } myargv[i] = NULL; error = NULL; if (!g_spawn_async(NULL, myargv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error)) { g_warning("Failed to execute notify-send: %s", error->message); g_error_free(error); } for (; i >= 0; i--) g_free(myargv[i]); g_free(myargv); } mpdcron-0.3+git20110303/src/gmodule/scrobbler/000077500000000000000000000000001153374523700206215ustar00rootroot00000000000000mpdcron-0.3+git20110303/src/gmodule/scrobbler/Makefile.am000066400000000000000000000011111153374523700226470ustar00rootroot00000000000000SUBDIRS= . DEFS+= -DGITHEAD=\"$(GITHEAD)\" -DLIBDIR=\"$(libdir)\" AM_CFLAGS= @MPDCRON_CFLAGS@ $(glib_CFLAGS) $(libcurl_CFLAGS) $(libmpdclient_CFLAGS) MODULE_DIR=$(libdir)/$(PACKAGE)-$(VERSION)/modules noinst_HEADERS= scrobbler-defs.h scrobbler_LTLIBRARIES= scrobbler.la scrobblerdir=$(MODULE_DIR) scrobbler_la_SOURCES= scrobbler-curl.c scrobbler-file.c scrobbler-journal.c \ scrobbler-record.c scrobbler-submit.c scrobbler-timer.c \ scrobbler-module.c scrobbler_la_LDFLAGS= -module -avoid-version scrobbler_la_LIBADD= $(glib_LIBS) $(libcurl_LIBS) $(libmpdclient_LIBS) mpdcron-0.3+git20110303/src/gmodule/scrobbler/scrobbler-curl.c000066400000000000000000000257501153374523700237160ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * Based in part upon mpdscribble which is: * Copyright (C) 2008-2009 The Music Player Daemon Project * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "scrobbler-defs.h" #include #include #include #include enum { /** maximum length of a response body */ MAX_RESPONSE_BODY = 8192, }; struct http_request { http_client_callback_t *callback; void *callback_data; /** the CURL easy handle */ CURL *curl; /** the POST request body */ char *post_data; /** the response body */ GString *body; /** error message provided by libcurl */ char error[CURL_ERROR_SIZE]; }; static struct { /** the CURL multi handle */ CURLM *multi; /** the GMainLoop source used to poll all CURL file descriptors */ GSource *source; /** the source id of #source */ guint source_id; /** a linked list of all registered GPollFD objects */ GSList *fds; /** a linked list of all active HTTP requests */ GSList *requests; } http_client; /** * Frees all resources of a #http_request object. Also unregisters * the CURL easy handle from the CURL multi handle. This function * does not affect the linked list http_client.requests. */ static void http_request_free(struct http_request *request) { g_string_free(request->body, true); curl_multi_remove_handle(http_client.multi, request->curl); curl_easy_cleanup(request->curl); g_free(request->post_data); g_free(request); } /** * Calculates the GLib event bit mask for one file descriptor, * obtained from three #fd_set objects filled by curl_multi_fdset(). */ static gushort http_client_fd_events(int fd, fd_set *rfds, fd_set *wfds, fd_set *efds) { gushort events = 0; if (FD_ISSET(fd, rfds)) { events |= G_IO_IN | G_IO_HUP | G_IO_ERR; FD_CLR(fd, rfds); } if (FD_ISSET(fd, wfds)) { events |= G_IO_OUT | G_IO_ERR; FD_CLR(fd, wfds); } if (FD_ISSET(fd, efds)) { events |= G_IO_HUP | G_IO_ERR; FD_CLR(fd, efds); } return events; } /** * Updates all registered GPollFD objects, unregisters old ones, * registers new ones. */ static void http_client_update_fds(void) { fd_set rfds, wfds, efds; int max_fd; CURLMcode mcode; GSList *fds; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); mcode = curl_multi_fdset(http_client.multi, &rfds, &wfds, &efds, &max_fd); if (mcode != CURLM_OK) { g_warning("curl_multi_fdset() failed: %s\n", curl_multi_strerror(mcode)); return; } fds = http_client.fds; http_client.fds = NULL; while (fds != NULL) { GPollFD *poll_fd = fds->data; gushort events = http_client_fd_events(poll_fd->fd, &rfds, &wfds, &efds); assert(poll_fd->events != 0); fds = g_slist_remove(fds, poll_fd); if (events != poll_fd->events) g_source_remove_poll(http_client.source, poll_fd); if (events != 0) { if (events != poll_fd->events) { poll_fd->events = events; g_source_add_poll(http_client.source, poll_fd); } http_client.fds = g_slist_prepend(http_client.fds, poll_fd); } else { g_free(poll_fd); } } for (int fd = 0; fd <= max_fd; ++fd) { gushort events = http_client_fd_events(fd, &rfds, &wfds, &efds); if (events != 0) { GPollFD *poll_fd = g_new(GPollFD, 1); poll_fd->fd = fd; poll_fd->events = events; g_source_add_poll(http_client.source, poll_fd); http_client.fds = g_slist_prepend(http_client.fds, poll_fd); } } } /** * Aborts and frees a running HTTP request and report an error to its * callback. */ static void http_request_abort(struct http_request *request) { http_client.requests = g_slist_remove(http_client.requests, request); request->callback(0, NULL, request->callback_data); http_request_free(request); } /** * Abort and free all HTTP requests, but don't invoke their callback * functions. */ static void http_client_abort_all_requests(void) { while (http_client.requests != NULL) { struct http_request *request = http_client.requests->data; http_request_abort(request); } } /** * Find a request by its CURL "easy" handle. */ static struct http_request * http_client_find_request(CURL *curl) { for (GSList *i = http_client.requests; i != NULL; i = g_slist_next(i)) { struct http_request *request = i->data; if (request->curl == curl) return request; } return NULL; } /** * A HTTP request is finished: invoke its callback and free it. */ static void http_request_done(struct http_request *request, CURLcode result) { /* invoke the callback function */ if (result == CURLE_OK) request->callback(request->body->len, request->body->str, request->callback_data); else { g_warning("curl failed: %s", request->error); request->callback(0, NULL, request->callback_data); } /* remove it from the list and free resources */ http_client.requests = g_slist_remove(http_client.requests, request); http_request_free(request); } /** * Check for finished HTTP responses. */ static void http_multi_info_read(void) { CURLMsg *msg; int msgs_in_queue; while ((msg = curl_multi_info_read(http_client.multi, &msgs_in_queue)) != NULL) { if (msg->msg == CURLMSG_DONE) { struct http_request *request = http_client_find_request(msg->easy_handle); assert(request != NULL); http_request_done(request, msg->data.result); } } } /** * Give control to CURL. */ static bool http_multi_perform(void) { CURLMcode mcode; int running_handles; do { mcode = curl_multi_perform(http_client.multi, &running_handles); } while (mcode == CURLM_CALL_MULTI_PERFORM); if (mcode != CURLM_OK && mcode != CURLM_CALL_MULTI_PERFORM) { g_warning("curl_multi_perform() failed: %s\n", curl_multi_strerror(mcode)); http_client_abort_all_requests(); return false; } return true; } /** * The GSource prepare() method implementation. */ static gboolean curl_source_prepare(G_GNUC_UNUSED GSource *source, G_GNUC_UNUSED gint *timeout_) { http_client_update_fds(); return FALSE; } /** * The GSource check() method implementation. */ static gboolean curl_source_check(G_GNUC_UNUSED GSource *source) { for (GSList *i = http_client.fds; i != NULL; i = i->next) { GPollFD *poll_fd = i->data; if (poll_fd->revents != 0) return TRUE; } return FALSE; } /** * The GSource dispatch() method implementation. The callback isn't * used, because we're handling all events directly. */ static gboolean curl_source_dispatch(G_GNUC_UNUSED GSource *source, G_GNUC_UNUSED GSourceFunc callback, G_GNUC_UNUSED gpointer user_data) { if (http_multi_perform()) http_multi_info_read(); return true; } /** * The vtable for our GSource implementation. Unfortunately, we * cannot declare it "const", because g_source_new() takes a non-const * pointer, for whatever reason. */ static GSourceFuncs curl_source_funcs = { .prepare = curl_source_prepare, .check = curl_source_check, .dispatch = curl_source_dispatch, }; int http_client_init(void) { CURLcode code = curl_global_init(CURL_GLOBAL_ALL); if (code != CURLE_OK) { g_critical("curl_global_init() failed: %s", curl_easy_strerror(code)); return -1; } http_client.multi = curl_multi_init(); if (http_client.multi == NULL) { g_critical("curl_multi_init() failed"); return -1; } http_client.source = g_source_new(&curl_source_funcs, sizeof(*http_client.source)); http_client.source_id = g_source_attach(http_client.source, g_main_context_default()); return 0; } static void http_request_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data) { struct http_request *request = data; http_request_free(request); } void http_client_finish(void) { /* free all requests */ g_slist_foreach(http_client.requests, http_request_free_callback, NULL); g_slist_free(http_client.requests); /* unregister all GPollFD instances */ http_client_update_fds(); /* free the GSource object */ g_source_unref(http_client.source); g_source_remove(http_client.source_id); /* clean up CURL */ curl_multi_cleanup(http_client.multi); curl_global_cleanup(); } char *http_client_uri_escape(const char *src) { return g_uri_escape_string(src, NULL, false); } /** * Called by curl when new data is available. */ static size_t http_request_writefunction(void *ptr, size_t size, size_t nmemb, void *stream) { struct http_request *request = stream; g_string_append_len(request->body, ptr, size * nmemb); if (request->body->len > MAX_RESPONSE_BODY) /* response body too large */ http_request_abort(request); return size * nmemb; } void http_client_request(const char *url, const char *post_data, http_client_callback_t *callback, void *data) { struct http_request *request = g_new(struct http_request, 1); CURLcode code; CURLMcode mcode; bool success; request->callback = callback; request->callback_data = data; /* create a CURL request */ request->curl = curl_easy_init(); if (request->curl == NULL) { g_free(request); callback(0, NULL, data); return; } mcode = curl_multi_add_handle(http_client.multi, request->curl); if (mcode != CURLM_OK) { curl_easy_cleanup(request->curl); g_free(request); callback(0, NULL, data); return; } /* .. and set it up */ curl_easy_setopt(request->curl, CURLOPT_USERAGENT, "mpdcron/" VERSION); curl_easy_setopt(request->curl, CURLOPT_WRITEFUNCTION, http_request_writefunction); curl_easy_setopt(request->curl, CURLOPT_WRITEDATA, request); curl_easy_setopt(request->curl, CURLOPT_FAILONERROR, true); curl_easy_setopt(request->curl, CURLOPT_ERRORBUFFER, request->error); curl_easy_setopt(request->curl, CURLOPT_BUFFERSIZE, 2048); if (file_config.proxy != NULL) curl_easy_setopt(request->curl, CURLOPT_PROXY, file_config.proxy); request->post_data = g_strdup(post_data); if (request->post_data != NULL) { curl_easy_setopt(request->curl, CURLOPT_POST, true); curl_easy_setopt(request->curl, CURLOPT_POSTFIELDS, request->post_data); } code = curl_easy_setopt(request->curl, CURLOPT_URL, url); if (code != CURLE_OK) { curl_multi_remove_handle(http_client.multi, request->curl); curl_easy_cleanup(request->curl); g_free(request); callback(0, NULL, data); return; } request->body = g_string_sized_new(256); http_client.requests = g_slist_prepend(http_client.requests, request); /* initiate the transfer */ success = http_multi_perform(); if (!success) { http_client.requests = g_slist_remove(http_client.requests, request); http_request_free(request); callback(0, NULL, data); return; } http_multi_info_read(); } mpdcron-0.3+git20110303/src/gmodule/scrobbler/scrobbler-defs.h000066400000000000000000000072061153374523700236730ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009 Ali Polatel * Based in part upon mpdscribble which is: * Copyright (C) 2008-2009 The Music Player Daemon Project * Copyright (C) 2005-2008 Kuno Woudt * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef MPDCRON_GUARD_SCROBBLER_DEFS_H #define MPDCRON_GUARD_SCROBBLER_DEFS_H 1 #ifdef HAVE_CONFIG_H #include "config.h" #endif /* !HAVE_CONFIG_H */ #define MPDCRON_MODULE "scrobbler" #include "../gmodule.h" #include #include #include struct record { char *artist; char *track; char *album; char *mbid; char *time; int length; const char *source; }; typedef void http_client_callback_t(size_t, const char *, void *); struct config { char *proxy; /** * The interval in seconds after which the journal is saved to * the file system. */ int journal_interval; GSList *scrobblers; }; struct scrobbler_config { /** * The name of the mpdscribble.conf section. It is used in * log messages. */ char *name; char *url; char *username; char *password; /** * The path of the journal file. It contains records which * have not been submitted yet. */ char *journal; }; extern struct config file_config; /** * Copies attributes from one record to another. Does not free * existing values in the destination record. */ void record_copy(struct record *dest, const struct record *src); /** * Duplicates a record object. */ struct record * record_dup(const struct record *src); /** * Deinitializes a record object, freeing all members. */ void record_deinit(struct record *record); /** * Frees a record object: free all members with record_deinit(), and * free the record pointer itself. */ void record_free(struct record *record); void record_clear(struct record *record); static inline bool record_is_defined(const struct record *record) { return record->artist != NULL && record->track != NULL; } bool journal_write(const char *path, GQueue *queue); void journal_read(const char *path, GQueue *queue); /** * Perform global initialization on the HTTP client library. */ int http_client_init(void); /** * Global deinitializaton. */ void http_client_finish(void); /** * Escapes URI parameters with '%'. Free the return value with * g_free(). */ char * http_client_uri_escape(const char *src); void http_client_request(const char *url, const char *post_data, http_client_callback_t * callback, void *data); void as_init(GSList *scrobbler_configs); void as_cleanup(void); void as_now_playing(const char *artist, const char *track, const char *album, const char *mbid, const int length); void as_songchange(const char *file, const char *artist, const char *track, const char *album, const char *mbid, const int length, const char *time); void as_save_cache(void); char * as_timestamp(void); int file_load(GKeyFile *fd); void file_cleanup(void); gboolean timer_save_journal(gpointer data); #endif /* !MPDCRON_GUARD_SCROBBLER_DEFS_H */ mpdcron-0.3+git20110303/src/gmodule/scrobbler/scrobbler-file.c000066400000000000000000000103051153374523700236560ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * Based in part upon mpdscribble which is: * Copyright (C) 2008-2009 The Music Player Daemon Project * Copyright (C) 2005-2008 Kuno Woudt * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "scrobbler-defs.h" #include #include #include #include "../utils.h" struct config file_config; static struct scrobbler_config * load_scrobbler(GKeyFile *fd, const char *grp) { char *p; GError *error; struct scrobbler_config *scrobbler; if (!g_key_file_has_group(fd, grp)) return NULL; scrobbler = g_new(struct scrobbler_config, 1); error = NULL; scrobbler->name = g_strdup(grp); scrobbler->url = g_key_file_get_string(fd, grp, "url", &error); if (error != NULL) { g_critical("Error while reading url from group %s: %s", grp, error->message); g_free(scrobbler); g_error_free(error); return NULL; } error = NULL; scrobbler->username = g_key_file_get_string(fd, grp, "username", &error); if (error != NULL) { g_critical("Error while reading username from group %s: %s", grp, error->message); g_free(scrobbler); g_error_free(error); return NULL; } scrobbler->password = g_key_file_get_string(fd, grp, "password", &error); if (error != NULL) { g_critical("Error while reading password from group %s: %s", grp, error->message); g_free(scrobbler); g_error_free(error); return NULL; } /* Parse password */ if (strncmp(scrobbler->password, "md5:", 4) == 0) { p = g_strdup(scrobbler->password + 4); g_free(scrobbler->password); scrobbler->password = p; } else { p = g_compute_checksum_for_string(G_CHECKSUM_MD5, scrobbler->password, -1); g_free(scrobbler->password); scrobbler->password = p; } scrobbler->journal = g_key_file_get_string(fd, grp, "journal", NULL); return scrobbler; } static void scrobbler_config_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data) { struct scrobbler_config *scrobbler = data; g_free(scrobbler->name); g_free(scrobbler->url); g_free(scrobbler->username); g_free(scrobbler->password); g_free(scrobbler->journal); g_free(scrobbler); } int file_load(GKeyFile *fd) { int s = 0; GError *error; struct scrobbler_config *scrobbler; memset(&file_config, 0, sizeof(struct config)); file_config.journal_interval = -1; error = NULL; if (!load_string(fd, MPDCRON_MODULE, "proxy", false, &file_config.proxy, &error)) { g_critical("Failed to load "MPDCRON_MODULE".proxy: %s", error->message); g_error_free(error); return -1; } if (!load_integer(fd, MPDCRON_MODULE, "journal_interval", false, &file_config.journal_interval, &error)) { g_critical("Failed to load "MPDCRON_MODULE".journal_interval: %s", error->message); g_error_free(error); return -1; } else if (file_config.journal_interval <= 0) file_config.journal_interval = 60; if ((scrobbler = load_scrobbler(fd, "libre.fm")) != NULL) { file_config.scrobblers = g_slist_prepend(file_config.scrobblers, scrobbler); ++s; } if ((scrobbler = load_scrobbler(fd, "last.fm")) != NULL) { file_config.scrobblers = g_slist_prepend(file_config.scrobblers, scrobbler); ++s; } if (s == 0) { g_critical("Neither last.fm nor libre.fm group defined"); return -1; } if (file_config.proxy == NULL && g_getenv("http_proxy")) file_config.proxy = g_strdup(g_getenv("http_proxy")); return 0; } void file_cleanup(void) { g_free(file_config.proxy); g_slist_foreach(file_config.scrobblers, scrobbler_config_free_callback, NULL); g_slist_free(file_config.scrobblers); } mpdcron-0.3+git20110303/src/gmodule/scrobbler/scrobbler-journal.c000066400000000000000000000106721153374523700244200ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * Based in part upon mpdscribble which is: * Copyright (C) 2008-2009 The Music Player Daemon Project * Copyright (C) 2005-2008 Kuno Woudt * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "scrobbler-defs.h" #include #include #include #include #include static int journal_file_empty; static void journal_write_record(gpointer data, gpointer user_data) { struct record *record = data; FILE *file = user_data; fprintf(file, "a = %s\nt = %s\nb = %s\nm = %s\n" "i = %s\nl = %i\no = %s\n\n", record->artist, record->track, record->album, record->mbid, record->time, record->length, record->source); } bool journal_write(const char *path, GQueue *queue) { FILE *handle; if (g_queue_is_empty(queue) && journal_file_empty) return false; handle = fopen(path, "wb"); if (!handle) { g_warning("Failed to save %s: %s\n", path, g_strerror(errno)); return false; } g_queue_foreach(queue, journal_write_record, handle); fclose(handle); return true; } static void journal_commit_record(GQueue *queue, struct record *record) { if (record->artist != NULL && record->track != NULL) { /* append record to the queue; reuse allocated strings */ g_queue_push_tail(queue, g_memdup(record, sizeof(*record))); journal_file_empty = false; } else { /* free and clear the record, it was not used */ record_deinit(record); } record_clear(record); } /* g_time_val_from_iso8601() was introduced in GLib 2.12 */ #if GLIB_CHECK_VERSION(2,12,0) /** * Imports an old (protocol v1.2) timestamp, format "%Y-%m-%d * %H:%M:%S". */ static char * import_old_timestamp(const char *p) { char *q; bool success; GTimeVal time_val; if (strlen(p) <= 10 || p[10] != ' ') return NULL; g_debug("Importing time stamp '%s'", p); /* replace a space with 'T', as expected by g_time_val_from_iso8601() */ q = g_strdup(p); q[10] = 'T'; success = g_time_val_from_iso8601(q, &time_val); g_free(q); if (!success) { g_debug("Import of '%s' failed", p); return NULL; } g_debug("'%s' -> %ld", p, time_val.tv_sec); return g_strdup_printf("%ld", time_val.tv_sec); } #endif /** * Parses the time stamp. If needed, converts the time stamp, and * returns an allocated string. */ static char * parse_timestamp(const char *p) { #if GLIB_CHECK_VERSION(2,12,0) char *ret = import_old_timestamp(p); if (ret != NULL) return ret; #endif return g_strdup(p); } void journal_read(const char *path, GQueue *queue) { FILE *file; char line[1024]; struct record record; journal_file_empty = true; file = fopen(path, "r"); if (file == NULL) { if (errno != ENOENT) /* ENOENT is ignored silently, because the * user might be starting mpdcron for the * first time */ g_warning("Failed to load %s: %s", path, g_strerror(errno)); return; } record_clear(&record); while (fgets(line, sizeof(line), file) != NULL) { char *key, *value; key = g_strchug(line); if (*key == 0 || *key == '#') continue; value = strchr(key, '='); if (value == NULL || value == key) continue; *value++ = 0; key = g_strchomp(key); value = g_strstrip(value); if (!strcmp("a", key)) { journal_commit_record(queue, &record); record.artist = g_strdup(value); } else if (!strcmp("t", key)) record.track = g_strdup(value); else if (!strcmp("b", key)) record.album = g_strdup(value); else if (!strcmp("m", key)) record.mbid = g_strdup(value); else if (!strcmp("i", key)) record.time = parse_timestamp(value); else if (!strcmp("l", key)) record.length = atoi(value); else if (strcmp("o", key) == 0 && value[0] == 'R') record.source = "R"; } fclose(file); journal_commit_record(queue, &record); } mpdcron-0.3+git20110303/src/gmodule/scrobbler/scrobbler-module.c000066400000000000000000000146661153374523700242420ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * Based in part upon mpdscribble which is: * Copyright (C) 2008-2009 The Music Player Daemon Project * Copyright (C) 2005-2008 Kuno Woudt * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "scrobbler-defs.h" #include #include #include #include #include #include #include #include /* Globals */ static unsigned last_id = -1; static bool was_paused = 0; static struct mpd_song *prev = NULL; static GTimer *timer = NULL; static int save_source_id = -1; static bool played_long_enough(int elapsed, int length) { /* http://www.lastfm.de/api/submissions "The track must have been played for a duration of at least 240 seconds or half the track's total length, whichever comes first. Skipping or pausing the track is irrelevant as long as the appropriate amount has been played." */ return elapsed > 240 || (length >= 30 && elapsed > length / 2); } static bool song_repeated(const struct mpd_song *song, int elapsed, int prev_elapsed) { return elapsed < 60 && prev_elapsed > elapsed && played_long_enough(prev_elapsed - elapsed, mpd_song_get_duration(song)); } static void song_changed(const struct mpd_song *song) { g_assert(song != NULL); if (mpd_song_get_tag(song, MPD_TAG_ARTIST, 0) == NULL || mpd_song_get_tag(song, MPD_TAG_TITLE, 0) == NULL) { g_message("New song detected with tags missing (%s)", mpd_song_get_uri(song)); g_timer_start(timer); return; } g_timer_start(timer); g_debug("New song detected (%s - %s), id: %u, pos: %u", mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), mpd_song_get_tag(song, MPD_TAG_TITLE, 0), mpd_song_get_id(song), mpd_song_get_pos(song)); as_now_playing(mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), mpd_song_get_tag(song, MPD_TAG_TITLE, 0), mpd_song_get_tag(song, MPD_TAG_ALBUM, 0), mpd_song_get_tag(song, MPD_TAG_MUSICBRAINZ_TRACKID, 0), mpd_song_get_duration(song)); } static void song_started(const struct mpd_song *song) { song_changed(song); } static void song_ended(const struct mpd_song *song) { int elapsed; g_assert(song != NULL); elapsed = g_timer_elapsed(timer, NULL); if (mpd_song_get_tag(song, MPD_TAG_ARTIST, 0) == NULL || mpd_song_get_tag(song, MPD_TAG_TITLE, 0) == NULL) { g_message("Song (%s) has missing tags, skipping", mpd_song_get_uri(song)); return; } else if (!played_long_enough(elapsed, mpd_song_get_duration(song))) { g_message("Song (%s - %s), id: %u, pos: %u not played long enough, skipping", mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), mpd_song_get_tag(song, MPD_TAG_TITLE, 0), mpd_song_get_id(song), mpd_song_get_pos(song)); return; } g_debug("Submitting old song (%s - %s), id: %u, pos: %u", mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), mpd_song_get_tag(song, MPD_TAG_TITLE, 0), mpd_song_get_id(song), mpd_song_get_pos(song)); /* FIXME: libmpdclient doesn't have any way to fetch the musicbrainz id. */ as_songchange(mpd_song_get_uri(song), mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), mpd_song_get_tag(song, MPD_TAG_TITLE, 0), mpd_song_get_tag(song, MPD_TAG_ALBUM, 0), mpd_song_get_tag(song, MPD_TAG_MUSICBRAINZ_TRACKID, 0), mpd_song_get_duration(song) > 0 ? mpd_song_get_duration(song) : g_timer_elapsed(timer, NULL), NULL); } static void song_playing(const struct mpd_song *song, unsigned elapsed) { int prev_elapsed = g_timer_elapsed(timer, NULL); if (song_repeated(song, elapsed, prev_elapsed)) { g_debug("Repeated song detected"); song_ended(song); song_started(song); } } static void song_continued(void) { g_timer_continue(timer); } static void song_paused(void) { if (!was_paused) g_timer_stop(timer); was_paused = true; } static void song_stopped(void) { last_id = -1; was_paused = false; } /* Module functions */ static int init(G_GNUC_UNUSED const struct mpdcron_config *conf, GKeyFile *fd) { /* Parse configuration */ if (file_load(fd) < 0) return MPDCRON_INIT_FAILURE; if (http_client_init() < 0) return MPDCRON_INIT_FAILURE; as_init(file_config.scrobblers); timer = g_timer_new(); save_source_id = g_timeout_add_seconds(file_config.journal_interval, timer_save_journal, NULL); return MPDCRON_INIT_SUCCESS; } static void destroy(void) { g_message("Exiting"); as_save_cache(); as_cleanup(); http_client_finish(); file_cleanup(); g_timer_destroy(timer); g_source_remove(save_source_id); if (prev != NULL) mpd_song_free(prev); } static int event_player(G_GNUC_UNUSED const struct mpd_connection *conn, const struct mpd_song *song, const struct mpd_status *status) { enum mpd_state state; state = mpd_status_get_state(status); g_assert(song != NULL || state != MPD_STATE_PLAY); if (state == MPD_STATE_PAUSE) { song_paused(); return MPDCRON_EVENT_SUCCESS; } else if (state != MPD_STATE_PLAY) song_stopped(); if (was_paused) { if (song != NULL && mpd_song_get_id(song) == last_id) song_continued(); was_paused = false; } /* Submit the previous song */ if (prev != NULL && (song == NULL || mpd_song_get_id(prev) != mpd_song_get_id(song))) song_ended(prev); if (song != NULL) { if (mpd_song_get_id(song) != last_id) { /* New song. */ song_started(song); last_id = mpd_song_get_id(song); } else { /* still playing the previous song */ song_playing(song, mpd_status_get_elapsed_time(status)); } } if (prev != NULL) { mpd_song_free(prev); prev = NULL; } if (song != NULL) { if ((prev = mpd_song_dup(song)) == NULL) { g_critical("mpd_song_dup failed: out of memory"); return MPDCRON_EVENT_UNLOAD; } } return MPDCRON_EVENT_SUCCESS; } struct mpdcron_module module = { .name = "Scrobbler", .init = init, .destroy = destroy, .event_player = event_player, }; mpdcron-0.3+git20110303/src/gmodule/scrobbler/scrobbler-record.c000066400000000000000000000036161153374523700242240ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009 Ali Polatel * Based in part upon mpdscribble which is: * Copyright (C) 2008-2009 The Music Player Daemon Project * Copyright (C) 2005-2008 Kuno Woudt * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "scrobbler-defs.h" #include void record_copy(struct record *dest, const struct record *src) { dest->artist = g_strdup(src->artist); dest->track = g_strdup(src->track); dest->album = g_strdup(src->album); dest->mbid = g_strdup(src->mbid); dest->time = g_strdup(src->time); dest->length = src->length; dest->source = src->source; } struct record *record_dup(const struct record *src) { struct record *dest = g_new(struct record, 1); record_copy(dest, src); return dest; } void record_deinit(struct record *record) { g_free(record->artist); g_free(record->track); g_free(record->album); g_free(record->mbid); g_free(record->time); } void record_free(struct record *record) { record_deinit(record); g_free(record); } void record_clear(struct record *record) { record->artist = NULL; record->track = NULL; record->album = NULL; record->mbid = NULL; record->time = NULL; record->length = 0; record->source = "P"; } mpdcron-0.3+git20110303/src/gmodule/scrobbler/scrobbler-submit.c000066400000000000000000000464031153374523700242520ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * Based in part upon mpdscribble which is: * Copyright (C) 2008-2009 The Music Player Daemon Project * Copyright (C) 2005-2008 Kuno Woudt * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "scrobbler-defs.h" #include #include #include #include #include #include #include #include #include #include #define AS_CLIENT_ID "mcn" #define AS_CLIENT_VERSION VERSION /* don't submit more than this amount of songs in a batch. */ #define MAX_SUBMIT_COUNT 10 static const char OK[] = "OK"; static const char BADSESSION[] = "BADSESSION"; static const char FAILED[] = "FAILED"; enum scrobbler_state { /** * mpdscribble has started, and doesn't have a session yet. * Handshake to be submitted. */ SCROBBLER_STATE_NOTHING, /** * Handshake is in progress, waiting for the server's * response. */ SCROBBLER_STATE_HANDSHAKE, /** * We have a session, and we're ready to submit. */ SCROBBLER_STATE_READY, /** * Submission in progress, waiting for the server's response. */ SCROBBLER_STATE_SUBMITTING, }; typedef enum { AS_SUBMIT_OK, AS_SUBMIT_FAILED, AS_SUBMIT_HANDSHAKE, } as_submitting; struct scrobbler { const struct scrobbler_config *config; enum scrobbler_state state; unsigned interval; guint handshake_source_id; guint submit_source_id; char *session; char *nowplay_url; char *submit_url; struct record now_playing; /** * A queue of #record objects. */ GQueue *queue; /** * How many songs are we trying to submit right now? This * many will be shifted from #queue if the submit succeeds. */ unsigned pending; }; static GSList *scrobblers; /** * Creates a new scrobbler object based on the specified * configuration. */ static struct scrobbler *scrobbler_new(const struct scrobbler_config *config) { struct scrobbler *scrobbler = g_new(struct scrobbler, 1); scrobbler->config = config; scrobbler->state = SCROBBLER_STATE_NOTHING; scrobbler->interval = 1; scrobbler->handshake_source_id = 0; scrobbler->submit_source_id = 0; scrobbler->session = NULL; scrobbler->nowplay_url = NULL; scrobbler->submit_url = NULL; record_clear(&scrobbler->now_playing); scrobbler->queue = g_queue_new(); scrobbler->pending = 0; return scrobbler; } static void record_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data) { struct record *song = data; record_free(song); } /** * Frees a scrobbler object. */ static void scrobbler_free(struct scrobbler *scrobbler) { g_queue_foreach(scrobbler->queue, record_free_callback, NULL); g_queue_free(scrobbler->queue); record_deinit(&scrobbler->now_playing); if (scrobbler->handshake_source_id != 0) g_source_remove(scrobbler->handshake_source_id); if (scrobbler->submit_source_id != 0) g_source_remove(scrobbler->submit_source_id); g_free(scrobbler->session); g_free(scrobbler->nowplay_url); g_free(scrobbler->submit_url); g_free(scrobbler); } static void add_var_internal(GString * s, char sep, const char *key, signed char idx, const char *val) { g_string_append_c(s, sep); g_string_append(s, key); if (idx >= 0) g_string_append_printf(s, "[%i]", idx); g_string_append_c(s, '='); if (val != NULL) { char *escaped = http_client_uri_escape(val); g_string_append(s, escaped); g_free(escaped); } } static void first_var(GString * s, const char *key, const char *val) { add_var_internal(s, '?', key, -1, val); } static void add_var(GString * s, const char *key, const char *val) { add_var_internal(s, '&', key, -1, val); } static void add_var_i(GString * s, const char *key, signed char idx, const char *val) { add_var_internal(s, '&', key, idx, val); } static void scrobbler_schedule_handshake(struct scrobbler *scrobbler); static void scrobbler_submit(struct scrobbler *scrobbler); static void scrobbler_schedule_submit(struct scrobbler *scrobbler); static void scrobbler_increase_interval(struct scrobbler *scrobbler) { if (scrobbler->interval < 60) scrobbler->interval = 60; else scrobbler->interval <<= 1; if (scrobbler->interval > 60 * 60 * 2) scrobbler->interval = 60 * 60 * 2; g_warning("[%s] waiting %u seconds before trying again", scrobbler->config->name, scrobbler->interval); } static as_submitting scrobbler_parse_submit_response(const char *scrobbler_name, const char *line, size_t length) { if (length == sizeof(OK) - 1 && memcmp(line, OK, length) == 0) { g_message("[%s] OK", scrobbler_name); return AS_SUBMIT_OK; } else if (length == sizeof(BADSESSION) - 1 && memcmp(line, BADSESSION, length) == 0) { g_warning("[%s] invalid session", scrobbler_name); return AS_SUBMIT_HANDSHAKE; } else if (length == sizeof(FAILED) - 1 && memcmp(line, FAILED, length) == 0) { if (length > strlen(FAILED)) g_warning("[%s] submission rejected: %.*s", scrobbler_name, (int)(length - strlen(FAILED)), line + strlen(FAILED)); else g_warning("[%s] submission rejected", scrobbler_name); } else { g_warning("[%s] unknown response: %.*s", scrobbler_name, (int)length, line); } return AS_SUBMIT_FAILED; } static bool scrobbler_parse_handshake_response(struct scrobbler *scrobbler, const char *line) { static const char *BANNED = "BANNED"; static const char *BADAUTH = "BADAUTH"; static const char *BADTIME = "BADTIME"; /* FIXME: some code duplication between this and as_parse_submit_response. */ if (!strncmp(line, OK, strlen(OK))) { g_message("[%s] handshake successful", scrobbler->config->name); return true; } else if (!strncmp(line, BANNED, strlen(BANNED))) { g_warning("[%s] handshake failed, we're banned (%s)", scrobbler->config->name, line); } else if (!strncmp(line, BADAUTH, strlen(BADAUTH))) { g_warning("[%s] handshake failed, " "username or password incorrect (%s)", scrobbler->config->name, line); } else if (!strncmp(line, BADTIME, strlen(BADTIME))) { g_warning("[%s] handshake failed, clock not synchronized (%s)", scrobbler->config->name, line); } else if (!strncmp(line, FAILED, strlen(FAILED))) { g_warning("[%s] handshake failed (%s)", scrobbler->config->name, line); } else { g_warning("[%s] error parsing handshake response (%s)", scrobbler->config->name, line); } return false; } static char *next_line(const char **input_r, const char *end) { const char *input = *input_r; const char *newline = memchr(input, '\n', end - input); char *line; if (newline == NULL) return g_strdup(""); line = g_strndup(input, newline - input); *input_r = newline + 1; return line; } static void scrobbler_handshake_callback(size_t length, const char *response, void *data) { struct scrobbler *scrobbler = data; const char *end = response + length; char *line; bool ret; assert(scrobbler != NULL); assert(scrobbler->state == SCROBBLER_STATE_HANDSHAKE); scrobbler->state = SCROBBLER_STATE_NOTHING; if (!length) { g_warning("[%s] handshake timed out", scrobbler->config->name); scrobbler_increase_interval(scrobbler); scrobbler_schedule_handshake(scrobbler); return; } line = next_line(&response, end); ret = scrobbler_parse_handshake_response(scrobbler, line); g_free(line); if (!ret) { scrobbler_increase_interval(scrobbler); scrobbler_schedule_handshake(scrobbler); return; } scrobbler->session = next_line(&response, end); g_debug("[%s] session: %s", scrobbler->config->name, scrobbler->session); scrobbler->nowplay_url = next_line(&response, end); g_debug("[%s] now playing url: %s", scrobbler->config->name, scrobbler->nowplay_url); scrobbler->submit_url = next_line(&response, end); g_debug("[%s] submit url: %s", scrobbler->config->name, scrobbler->submit_url); if (*scrobbler->nowplay_url == 0 || *scrobbler->submit_url == 0) { g_free(scrobbler->session); scrobbler->session = NULL; g_free(scrobbler->nowplay_url); scrobbler->nowplay_url = NULL; g_free(scrobbler->submit_url); scrobbler->submit_url = NULL; scrobbler_increase_interval(scrobbler); scrobbler_schedule_handshake(scrobbler); return; } scrobbler->state = SCROBBLER_STATE_READY; scrobbler->interval = 1; /* handshake was successful: see if we have songs to submit */ scrobbler_submit(scrobbler); } static void scrobbler_queue_remove_oldest(GQueue *queue, unsigned count) { assert(count > 0); while (count--) { struct record *tmp = g_queue_pop_head(queue); record_free(tmp); } } static void scrobbler_submit_callback(size_t length, const char *response, void *data) { struct scrobbler *scrobbler = data; char *newline; assert(scrobbler->state == SCROBBLER_STATE_SUBMITTING); scrobbler->state = SCROBBLER_STATE_READY; if (!length) { scrobbler->pending = 0; g_warning("[%s] submit timed out", scrobbler->config->name); scrobbler_increase_interval(scrobbler); scrobbler_schedule_submit(scrobbler); return; } newline = memchr(response, '\n', length); if (newline != NULL) length = newline - response; switch (scrobbler_parse_submit_response(scrobbler->config->name, response, length)) { case AS_SUBMIT_OK: scrobbler->interval = 1; /* submission was accepted, so clean up the cache. */ if (scrobbler->pending > 0) { scrobbler_queue_remove_oldest(scrobbler->queue, scrobbler->pending); scrobbler->pending = 0; } else { assert(record_is_defined(&scrobbler->now_playing)); record_deinit(&scrobbler->now_playing); memset(&scrobbler->now_playing, 0, sizeof(scrobbler->now_playing)); } /* submit the next chunk (if there is some left) */ scrobbler_submit(scrobbler); break; case AS_SUBMIT_FAILED: scrobbler_increase_interval(scrobbler); scrobbler_schedule_submit(scrobbler); break; case AS_SUBMIT_HANDSHAKE: scrobbler->state = SCROBBLER_STATE_NOTHING; scrobbler_schedule_handshake(scrobbler); break; } } char *as_timestamp(void) { /* create timestamp for 1.2 protocol. */ GTimeVal time_val; g_get_current_time(&time_val); return g_strdup_printf("%ld", (glong)time_val.tv_sec); } static char *as_md5(const char *password, const char *timestamp) { char *cat, *result; cat = g_strconcat(password, timestamp, NULL); result = g_compute_checksum_for_string(G_CHECKSUM_MD5, cat, -1); g_free(cat); return result; } static void scrobbler_handshake(struct scrobbler *scrobbler) { GString *url; char *timestr, *md5; scrobbler->state = SCROBBLER_STATE_HANDSHAKE; timestr = as_timestamp(); md5 = as_md5(scrobbler->config->password, timestr); /* construct the handshake url. */ url = g_string_new(scrobbler->config->url); first_var(url, "hs", "true"); add_var(url, "p", "1.2"); add_var(url, "c", AS_CLIENT_ID); add_var(url, "v", AS_CLIENT_VERSION); add_var(url, "u", scrobbler->config->username); add_var(url, "t", timestr); add_var(url, "a", md5); g_free(timestr); g_free(md5); // notice ("handshake url:\n%s", url); http_client_request(url->str, NULL, &scrobbler_handshake_callback, scrobbler); g_string_free(url, true); } static gboolean scrobbler_handshake_timer(gpointer data) { struct scrobbler *scrobbler = data; assert(scrobbler->state == SCROBBLER_STATE_NOTHING); scrobbler->handshake_source_id = 0; scrobbler_handshake(data); return false; } static void scrobbler_schedule_handshake(struct scrobbler *scrobbler) { assert(scrobbler->state == SCROBBLER_STATE_NOTHING); assert(scrobbler->handshake_source_id == 0); scrobbler->handshake_source_id = g_timeout_add_seconds(scrobbler->interval, scrobbler_handshake_timer, scrobbler); } static void scrobbler_send_now_playing(struct scrobbler *scrobbler, const char *artist, const char *track, const char *album, const char *mbid, const int length) { GString *post_data; char len[16]; assert(scrobbler->state == SCROBBLER_STATE_READY); assert(scrobbler->submit_source_id == 0); scrobbler->state = SCROBBLER_STATE_SUBMITTING; snprintf(len, sizeof(len), "%i", length); post_data = g_string_new(NULL); add_var(post_data, "s", scrobbler->session); add_var(post_data, "a", artist); add_var(post_data, "t", track); add_var(post_data, "b", album); add_var(post_data, "l", len); add_var(post_data, "n", ""); add_var(post_data, "m", mbid); g_message("[%s] sending 'now playing' notification", scrobbler->config->name); g_debug("[%s] post data: %s", scrobbler->config->name, post_data->str); g_debug("[%s] url: %s", scrobbler->config->name, scrobbler->nowplay_url); http_client_request(scrobbler->nowplay_url, post_data->str, scrobbler_submit_callback, scrobbler); g_string_free(post_data, true); } static void scrobbler_schedule_now_playing_callback(gpointer data, gpointer user_data) { struct scrobbler *scrobbler = data; const struct record *song = user_data; record_deinit(&scrobbler->now_playing); record_copy(&scrobbler->now_playing, song); if (scrobbler->state == SCROBBLER_STATE_READY && scrobbler->submit_source_id == 0) scrobbler_schedule_submit(scrobbler); } void as_now_playing(const char *artist, const char *track, const char *album, const char *mbid, const int length) { struct record record; record.artist = g_strdup(artist); record.track = g_strdup(track); record.album = g_strdup(album); record.mbid = g_strdup(mbid); record.time = NULL; record.length = length; g_slist_foreach(scrobblers, scrobbler_schedule_now_playing_callback, &record); record_deinit(&record); } static void scrobbler_submit(struct scrobbler *scrobbler) { //MAX_SUBMIT_COUNT unsigned count = 0; GString *post_data; assert(scrobbler->state == SCROBBLER_STATE_READY); assert(scrobbler->submit_source_id == 0); if (g_queue_is_empty(scrobbler->queue)) { /* the submission queue is empty. See if a "now playing" song is scheduled - these should be sent after song submissions */ if (record_is_defined(&scrobbler->now_playing)) scrobbler_send_now_playing(scrobbler, scrobbler->now_playing.artist, scrobbler->now_playing.track, scrobbler->now_playing.album, scrobbler->now_playing.mbid, scrobbler->now_playing.length); return; } scrobbler->state = SCROBBLER_STATE_SUBMITTING; /* construct the handshake url. */ post_data = g_string_new(NULL); add_var(post_data, "s", scrobbler->session); for (GList *list = g_queue_peek_head_link(scrobbler->queue); list != NULL && count < MAX_SUBMIT_COUNT; list = g_list_next(list)) { struct record *song = list->data; char len[16]; snprintf(len, sizeof(len), "%i", song->length); add_var_i(post_data, "a", count, song->artist); add_var_i(post_data, "t", count, song->track); add_var_i(post_data, "l", count, len); add_var_i(post_data, "i", count, song->time); add_var_i(post_data, "o", count, song->source); add_var_i(post_data, "r", count, ""); add_var_i(post_data, "b", count, song->album); add_var_i(post_data, "n", count, ""); add_var_i(post_data, "m", count, song->mbid); count++; } g_message("[%s] submitting %i song%s", scrobbler->config->name, count, count == 1 ? "" : "s"); g_debug("[%s] post data: %s", scrobbler->config->name, post_data->str); g_debug("[%s] url: %s", scrobbler->config->name, scrobbler->submit_url); scrobbler->pending = count; http_client_request(scrobbler->submit_url, post_data->str, &scrobbler_submit_callback, scrobbler); g_string_free(post_data, true); } static void scrobbler_push_callback(gpointer data, gpointer user_data) { struct scrobbler *scrobbler = data; const struct record *record = user_data; g_queue_push_tail(scrobbler->queue, record_dup(record)); if (scrobbler->state == SCROBBLER_STATE_READY && scrobbler->submit_source_id == 0) scrobbler_schedule_submit(scrobbler); } void as_songchange(const char *file, const char *artist, const char *track, const char *album, const char *mbid, const int length, const char *time2) { struct record record; /* from the 1.2 protocol draft: You may still submit if there is no album title (variable b) You may still submit if there is no musicbrainz id available (variable m) everything else is mandatory. */ if (!(artist && strlen(artist))) { g_warning("empty artist, not submitting; " "please check the tags on %s", file); return; } if (!(track && strlen(track))) { g_warning("empty title, not submitting; " "please check the tags on %s", file); return; } record.artist = g_strdup(artist); record.track = g_strdup(track); record.album = g_strdup(album); record.mbid = g_strdup(mbid); record.length = length; record.time = time2 ? g_strdup(time2) : as_timestamp(); record.source = strstr(file, "://") == NULL ? "P" : "R"; g_message("%s, songchange: %s - %s (%i)", record.time, record.artist, record.track, record.length); g_slist_foreach(scrobblers, scrobbler_push_callback, &record); } static void scrobbler_new_callback(gpointer data, G_GNUC_UNUSED gpointer user_data) { const struct scrobbler_config *config = data; struct scrobbler *scrobbler = scrobbler_new(config); if (config->journal != NULL) { guint queue_length; journal_read(config->journal, scrobbler->queue); queue_length = g_queue_get_length(scrobbler->queue); g_message("Loaded %i song%s from %s", queue_length, queue_length == 1 ? "" : "s", config->journal); } scrobblers = g_slist_prepend(scrobblers, scrobbler); scrobbler_schedule_handshake(scrobbler); } void as_init(GSList *scrobbler_configs) { g_message("Starting mpdcron/scrobbler ("AS_CLIENT_ID" "AS_CLIENT_VERSION")"); g_slist_foreach(scrobbler_configs, scrobbler_new_callback, NULL); } static gboolean scrobbler_submit_timer(gpointer data) { struct scrobbler *scrobbler = data; assert(scrobbler->state == SCROBBLER_STATE_READY); scrobbler->submit_source_id = 0; scrobbler_submit(scrobbler); return false; } static void scrobbler_schedule_submit(struct scrobbler *scrobbler) { assert(scrobbler->submit_source_id == 0); assert(!g_queue_is_empty(scrobbler->queue) || record_is_defined(&scrobbler->now_playing)); scrobbler->submit_source_id = g_timeout_add_seconds(scrobbler->interval, scrobbler_submit_timer, scrobbler); } static void scrobbler_save_callback(gpointer data, G_GNUC_UNUSED gpointer user_data) { struct scrobbler *scrobbler = data; if (scrobbler->config->journal == NULL) return; if (journal_write(scrobbler->config->journal, scrobbler->queue)) { guint queue_length = g_queue_get_length(scrobbler->queue); g_message("[%s] saved %i song%s to %s", scrobbler->config->name, queue_length, queue_length == 1 ? "" : "s", scrobbler->config->journal); } } void as_save_cache(void) { g_slist_foreach(scrobblers, scrobbler_save_callback, NULL); } static void scrobbler_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data) { struct scrobbler *scrobbler = data; scrobbler_free(scrobbler); } void as_cleanup(void) { g_slist_foreach(scrobblers, scrobbler_free_callback, NULL); g_slist_free(scrobblers); } mpdcron-0.3+git20110303/src/gmodule/scrobbler/scrobbler-timer.c000066400000000000000000000021361153374523700240620ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009 Ali Polatel * Based in part upon mpdscribble which is: * Copyright (C) 2008-2009 The Music Player Daemon Project * Copyright (C) 2005-2008 Kuno Woudt * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "scrobbler-defs.h" #include gboolean timer_save_journal(G_GNUC_UNUSED gpointer data) { as_save_cache(); return TRUE; } mpdcron-0.3+git20110303/src/gmodule/stats/000077500000000000000000000000001153374523700200025ustar00rootroot00000000000000mpdcron-0.3+git20110303/src/gmodule/stats/Makefile.am000066400000000000000000000031261153374523700220400ustar00rootroot00000000000000SUBDIRS= . DEFS+= -DGITHEAD=\"$(GITHEAD)\" -DLIBDIR=\"$(libdir)\" AM_CFLAGS= @MPDCRON_CFLAGS@ \ $(gio_unix_CFLAGS) $(glib_CFLAGS) $(gio_CFLAGS) \ $(libmpdclient_CFLAGS) $(sqlite_CFLAGS) MODULE_DIR=$(libdir)/$(PACKAGE)-$(VERSION)/modules noinst_HEADERS= tokenizer.h stats-defs.h stats-sqlite.h stats_LTLIBRARIES= stats.la statsdir=$(MODULE_DIR) stats_la_SOURCES= tokenizer.c \ stats-command.c stats-file.c stats-server.c \ stats-sqlite.c stats-module.c stats_la_LDFLAGS= -module -avoid-version stats_la_LIBADD= $(glib_LIBS) $(gio_unix_LIBS) $(gio_LIBS) \ $(libdaemon_LIBS) $(libmpdclient_LIBS) $(sqlite_LIBS) # I am the eggman! noinst_HEADERS+= walrus-defs.h bin_PROGRAMS= walrus walrus_SOURCES= stats-sqlite.c walrus-utils.c walrus-main.c # Hack to workaround the error: # object x created both with libtool and without. # See: http://bit.ly/libtool_both walrus_CFLAGS= $(AM_CFLAGS) walrus_LDADD= $(glib_LIBS) $(libmpdclient_LIBS) $(sqlite_LIBS) # Careful with that axe! # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa! noinst_HEADERS+= eugene-defs.h bin_PROGRAMS+= eugene eugene_SOURCES= eugene-connection.c eugene-addtag.c \ eugene-count.c eugene-kill.c eugene-list.c \ eugene-listinfo.c eugene-listtags.c eugene-love.c \ eugene-rate.c eugene-rate-absolute.c eugene-rmtag.c \ eugene-count-absolute.c eugene-utils.c eugene-main.c # Hack to workaround the error: # object x created both with libtool and without. # See: http://bit.ly/libtool_both eugene_CFLAGS= $(AM_CFLAGS) eugene_LDADD= $(gio_unix_LIBS) $(gio_LIBS) $(glib_LIBS) $(libmpdclient_LIBS) bin_SCRIPTS= homescrape mpdcron-0.3+git20110303/src/gmodule/stats/eugene-addtag.c000066400000000000000000000167641153374523700226560ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "eugene-defs.h" #include #include #include static int addtag_artist(struct mpdcron_connection *conn, const char *tag, const char *expr) { int changes; if (expr != NULL) { if (!mpdcron_addtag_artist_expr(conn, expr, tag, &changes)) { eulog(LOG_ERR, "Failed to add tag to artist: %s", conn->error->message); return 1; } } else { char *esc_artist, *myexpr; struct mpd_song *song; if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_ARTIST, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no artist tag!"); mpd_song_free(song); return 1; } esc_artist = quote(mpd_song_get_tag(song, MPD_TAG_ARTIST, 0)); myexpr = g_strdup_printf("name=%s", esc_artist); g_free(esc_artist); mpd_song_free(song); if (!mpdcron_addtag_artist_expr(conn, myexpr, tag, &changes)) { eulog(LOG_ERR, "Failed to add tag to current playing artist: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int addtag_album(struct mpdcron_connection *conn, const char *tag, const char *expr) { int changes; if (expr != NULL) { if (!mpdcron_addtag_album_expr(conn, expr, tag, &changes)) { eulog(LOG_ERR, "Failed to add tag to album: %s", conn->error->message); return 1; } } else { char *esc_album, *esc_artist, *myexpr; struct mpd_song *song; if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_ARTIST, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no artist tag!"); mpd_song_free(song); return 1; } else if (mpd_song_get_tag(song, MPD_TAG_ALBUM, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no album tag!"); mpd_song_free(song); return 1; } esc_album = quote(mpd_song_get_tag(song, MPD_TAG_ALBUM, 0)); esc_artist = quote(mpd_song_get_tag(song, MPD_TAG_ARTIST, 0)); myexpr = g_strdup_printf("name=%s and artist=%s", esc_album, esc_artist); g_free(esc_album); g_free(esc_artist); mpd_song_free(song); if (!mpdcron_addtag_album_expr(conn, myexpr, tag, &changes)) { eulog(LOG_ERR, "Failed to add tag to current playing album: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int addtag_genre(struct mpdcron_connection *conn, const char *tag, const char *expr) { int changes; if (expr != NULL) { if (!mpdcron_addtag_genre_expr(conn, expr, tag, &changes)) { eulog(LOG_ERR, "Failed to add tag to genre: %s", conn->error->message); return 1; } } else { char *esc_genre, *myexpr; struct mpd_song *song; if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_GENRE, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no genre tag!"); mpd_song_free(song); return 1; } esc_genre = quote(mpd_song_get_tag(song, MPD_TAG_GENRE, 0)); myexpr = g_strdup_printf("name=%s", esc_genre); g_free(esc_genre); mpd_song_free(song); if (!mpdcron_addtag_genre_expr(conn, myexpr, tag, &changes)) { eulog(LOG_ERR, "Failed to add tag to current playing genre: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int addtag_song(struct mpdcron_connection *conn, const char *tag, const char *expr) { int changes; if (expr != NULL) { if (!mpdcron_addtag_expr(conn, expr, tag, &changes)) { eulog(LOG_ERR, "Failed to add tag to song: %s", conn->error->message); return 1; } } else { char *esc_uri, *myexpr; struct mpd_song *song; if ((song = load_current_song()) == NULL) return 1; esc_uri = quote(mpd_song_get_uri(song)); myexpr = g_strdup_printf("uri=%s", esc_uri); g_free(esc_uri); mpd_song_free(song); if (!mpdcron_addtag_expr(conn, myexpr, tag, &changes)) { eulog(LOG_ERR, "Failed to add tag to current playing song: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int cmd_addtag_internal(const char *tag, const char *expr, bool artist, bool album, bool genre) { int port, ret; const char *hostname, *password; struct mpdcron_connection *conn; if ((artist && album && genre) || (artist && album) || (artist && genre) || (album && genre)) { g_printerr("--artist, --album and --genre options are mutually exclusive\n"); return 1; } hostname = g_getenv(ENV_MPDCRON_HOST) ? g_getenv(ENV_MPDCRON_HOST) : DEFAULT_HOSTNAME; port = g_getenv(ENV_MPDCRON_PORT) ? atoi(g_getenv(ENV_MPDCRON_PORT)) : DEFAULT_PORT; password = g_getenv(ENV_MPDCRON_PASSWORD); conn = mpdcron_connection_new(hostname, port); if (conn->error != NULL) { eulog(LOG_ERR, "Failed to connect: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } if (password != NULL) { if (!mpdcron_password(conn, password)) { eulog(LOG_ERR, "Authentication failed: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } } if (artist) ret = addtag_artist(conn, tag, expr); else if (album) ret = addtag_album(conn, tag, expr); else if (genre) ret = addtag_genre(conn, tag, expr); else ret = addtag_song(conn, tag, expr); mpdcron_connection_free(conn); return ret; } int cmd_addtag(int argc, char **argv) { int opta = 0, optA = 0, optg = 0, ret; GError *error = NULL; GOptionEntry options[] = { {"artist", 'a', 0, G_OPTION_ARG_NONE, &opta, "Add tag to artists instead of songs", NULL}, {"album", 'A', 0, G_OPTION_ARG_NONE, &optA, "Add tag to albums instead of songs", NULL}, {"genre", 'g', 0, G_OPTION_ARG_NONE, &optg, "Add tag to genres instead of songs", NULL}, { NULL, 0, 0, 0, NULL, NULL, NULL }, }; GOptionContext *ctx; ctx = g_option_context_new("TAG [EXPRESSION]"); g_option_context_add_main_entries(ctx, options, "eugene-addtag"); g_option_context_set_summary(ctx, "eugene-addtag-"VERSION GITHEAD" - Add tag to song/artist/album/genre"); g_option_context_set_description(ctx, "" "By default this command works on the current playing song.\n" "For more information about the expression syntax, see:\n" "http://www.sqlite.org/lang_expr.html"); if (!g_option_context_parse(ctx, &argc, &argv, &error)) { g_printerr("Option parsing failed: %s\n", error->message); g_error_free(error); g_option_context_free(ctx); return 1; } g_option_context_free(ctx); if (argc <= 1) { g_printerr("No tag given\n"); return 1; } if (argc > 2) ret = cmd_addtag_internal(argv[1], argv[2], opta, optA, optg); else ret = cmd_addtag_internal(argv[1], NULL, opta, optA, optg); return ret; } mpdcron-0.3+git20110303/src/gmodule/stats/eugene-connection.c000066400000000000000000001010301153374523700235460ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * Based in part upon libmpdclient which is: * Copyright (c) 2003-2009 The Music Player Daemon Project * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ /* TODO: This file could use quite a bit of refactoring. */ #include "eugene-defs.h" #define MPDCRON_WELCOME_MESSAGE "OK MPDCRON " #include #include #include #include #include #include #if HAVE_GIO_UNIX #include #endif /* HAVE_GIO_UNIX */ struct mpdcron_parser { union { struct { int server; unsigned at; const char *message; } error; struct { const char *name, *value; } pair; } u; }; static GQuark connection_quark(void) { return g_quark_from_static_string("connection"); } /** * Receiving data */ static char * mpdcron_recv_line(struct mpdcron_connection *conn, gsize *length_r) { gchar *line; line = g_data_input_stream_read_line(conn->input, length_r, NULL, &conn->error); if (line == NULL && conn->error == NULL) { g_set_error(&conn->error, connection_quark(), MPDCRON_ERROR_EOF, "EOF while trying to read a line"); return NULL; } return line; } /** * Parsing received data */ static enum mpdcron_parser_result mpdcron_parser_feed(struct mpdcron_parser *parser, char *line) { if (strcmp(line, "OK") == 0) return MPDCRON_PARSER_SUCCESS; else if (memcmp(line, "ACK", 3) == 0) { char *p, *q; parser->u.error.server = MPDCRON_ERROR_SERVER_UNK; parser->u.error.at = 0; parser->u.error.message = NULL; /* parse [ACK@AT] */ p = strchr(line + 3, '['); if (p == NULL) return MPDCRON_PARSER_ERROR; parser->u.error.server = strtol(p + 1, &p, 10); if (*p == '@') parser->u.error.at = strtol(p + 1, &p, 10); q = strchr(p, ']'); if (q == NULL) return MPDCRON_PARSER_MALFORMED; /* skip the {COMMAND} */ p = q + 1; q = strchr(p, '{'); if (q != NULL) { q = strchr(p, '}'); if (q != NULL) p = q + 1; } /* obtain error message */ while (*p == ' ') ++p; if (*p != 0) parser->u.error.message = p; return MPDCRON_PARSER_ERROR; } else { /* so this must be a name-value pair */ char *p; p = strchr(line, ':'); if (p == NULL || p[1] != ' ') return MPDCRON_PARSER_MALFORMED; *p = 0; parser->u.pair.name = line; parser->u.pair.value = p + 2; return MPDCRON_PARSER_PAIR; } } static bool mpdcron_parse_single(struct mpdcron_connection *conn) { int ret; gsize length; gchar *line; g_assert(conn != NULL); line = mpdcron_recv_line(conn, &length); if (line == NULL) return false; ret = mpdcron_parser_feed(conn->parser, line); switch (ret) { case MPDCRON_PARSER_SUCCESS: g_free(line); return true; case MPDCRON_PARSER_ERROR: g_set_error(&conn->error, connection_quark(), conn->parser->u.error.server, "%s", conn->parser->u.error.message); g_free(line); return false; case MPDCRON_PARSER_MALFORMED: g_set_error(&conn->error, connection_quark(), MPDCRON_ERROR_MALFORMED, "Malformed line `%s' received from server", line); g_free(line); return false; default: g_set_error(&conn->error, connection_quark(), MPDCRON_ERROR_MALFORMED, "Received unexpected name/value pair `%s'", line); g_free(line); return false; } g_assert_not_reached(); } static bool mpdcron_parse_changes(struct mpdcron_connection *conn, int *changes) { int ret; gsize length; gchar *line; g_assert(conn != NULL); g_assert(changes != NULL); for (;;) { line = mpdcron_recv_line(conn, &length); if (line == NULL) return false; ret = mpdcron_parser_feed(conn->parser, line); switch (ret) { case MPDCRON_PARSER_SUCCESS: g_free(line); return true; case MPDCRON_PARSER_ERROR: g_set_error(&conn->error, connection_quark(), conn->parser->u.error.server, "%s", conn->parser->u.error.message); g_free(line); return false; case MPDCRON_PARSER_MALFORMED: g_set_error(&conn->error, connection_quark(), MPDCRON_ERROR_MALFORMED, "Malformed line `%s' received from server", line); g_free(line); return false; default: if (strcmp(conn->parser->u.pair.name, "changes") == 0) { *changes = atoi(conn->parser->u.pair.value); g_free(line); } else { g_set_error(&conn->error, connection_quark(), MPDCRON_ERROR_MALFORMED, "Received unexpected name/value pair `%s'", line); g_free(line); return false; } } } g_assert_not_reached(); } static bool mpdcron_parse_albums(struct mpdcron_connection *conn, GSList **values) { int ret; gsize length; gchar *line; struct mpdcron_entity *album = NULL; for (;;) { line = mpdcron_recv_line(conn, &length); if (line == NULL) { g_free(album); return false; } ret = mpdcron_parser_feed(conn->parser, line); switch (ret) { case MPDCRON_PARSER_SUCCESS: g_free(line); if (album != NULL) *values = g_slist_prepend(*values, album); return true; case MPDCRON_PARSER_ERROR: g_set_error(&conn->error, connection_quark(), conn->parser->u.error.server, "%s", conn->parser->u.error.message); g_free(album); g_free(line); return false; case MPDCRON_PARSER_MALFORMED: g_set_error(&conn->error, connection_quark(), MPDCRON_ERROR_MALFORMED, "Malformed line `%s' received from server", line); g_free(album); g_free(line); return false; default: /* We have a pair! */ if (strcmp(conn->parser->u.pair.name, "id") == 0) { if (album != NULL) *values = g_slist_prepend(*values, album); album = g_new0(struct mpdcron_entity, 1); album->id = atoi(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Album") == 0) { g_assert(album != NULL); album->name = g_strdup(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Artist") == 0) { g_assert(album != NULL); album->artist = g_strdup(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Love") == 0) { g_assert(album != NULL); album->love = atoi(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Kill") == 0) { g_assert(album != NULL); album->kill = atoi(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Rating") == 0) { g_assert(album != NULL); album->rating = atoi(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Play Count") == 0) { g_assert(album != NULL); album->play_count = atoi(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Tag") == 0) { g_assert(album != NULL); album->tags = g_slist_prepend(album->tags, g_strdup(conn->parser->u.pair.value)); } else { g_set_error(&conn->error, connection_quark(), MPDCRON_ERROR_MALFORMED, "Received unexpected name/value pair `%s'", line); g_free(line); return false; } g_free(line); break; } } /* never reached */ return false; } static bool mpdcron_parse_artists(struct mpdcron_connection *conn, GSList **values) { int ret; gsize length; gchar *line; struct mpdcron_entity *artist = NULL; for (;;) { line = mpdcron_recv_line(conn, &length); if (line == NULL) { g_free(artist); return false; } ret = mpdcron_parser_feed(conn->parser, line); switch (ret) { case MPDCRON_PARSER_SUCCESS: g_free(line); if (artist != NULL) *values = g_slist_prepend(*values, artist); return true; case MPDCRON_PARSER_ERROR: g_set_error(&conn->error, connection_quark(), conn->parser->u.error.server, "%s", conn->parser->u.error.message); g_free(artist); g_free(line); return false; case MPDCRON_PARSER_MALFORMED: g_set_error(&conn->error, connection_quark(), MPDCRON_ERROR_MALFORMED, "Malformed line `%s' received from server", line); g_free(artist); g_free(line); return false; default: /* We have a pair! */ if (strcmp(conn->parser->u.pair.name, "id") == 0) { if (artist != NULL) *values = g_slist_prepend(*values, artist); artist = g_new0(struct mpdcron_entity, 1); artist->id = atoi(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Artist") == 0) { g_assert(artist != NULL); artist->name = g_strdup(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Love") == 0) { g_assert(artist != NULL); artist->love = atoi(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Kill") == 0) { g_assert(artist != NULL); artist->kill = atoi(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Rating") == 0) { g_assert(artist != NULL); artist->rating = atoi(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Play Count") == 0) { g_assert(artist != NULL); artist->play_count = atoi(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Tag") == 0) { g_assert(artist != NULL); artist->tags = g_slist_prepend(artist->tags, g_strdup(conn->parser->u.pair.value)); } else { g_set_error(&conn->error, connection_quark(), MPDCRON_ERROR_MALFORMED, "Received unexpected name/value pair `%s'", line); g_free(line); return false; } g_free(line); break; } } /* never reached */ return false; } static bool mpdcron_parse_genres(struct mpdcron_connection *conn, GSList **values) { int ret; gsize length; gchar *line; struct mpdcron_entity *genre = NULL; for (;;) { line = mpdcron_recv_line(conn, &length); if (line == NULL) { g_free(genre); return false; } ret = mpdcron_parser_feed(conn->parser, line); switch (ret) { case MPDCRON_PARSER_SUCCESS: g_free(line); if (genre != NULL) *values = g_slist_prepend(*values, genre); return true; case MPDCRON_PARSER_ERROR: g_set_error(&conn->error, connection_quark(), conn->parser->u.error.server, "%s", conn->parser->u.error.message); g_free(genre); g_free(line); return false; case MPDCRON_PARSER_MALFORMED: g_set_error(&conn->error, connection_quark(), MPDCRON_ERROR_MALFORMED, "Malformed line `%s' received from server", line); g_free(genre); g_free(line); return false; default: /* We have a pair! */ if (strcmp(conn->parser->u.pair.name, "id") == 0) { if (genre != NULL) *values = g_slist_prepend(*values, genre); genre = g_new0(struct mpdcron_entity, 1); genre->id = atoi(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Genre") == 0) { g_assert(genre != NULL); genre->name = g_strdup(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Love") == 0) { g_assert(genre != NULL); genre->love = atoi(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Kill") == 0) { g_assert(genre != NULL); genre->kill = atoi(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Rating") == 0) { g_assert(genre != NULL); genre->rating = atoi(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Play Count") == 0) { g_assert(genre != NULL); genre->play_count = atoi(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Tag") == 0) { g_assert(genre != NULL); genre->tags = g_slist_prepend(genre->tags, g_strdup(conn->parser->u.pair.value)); } else { g_set_error(&conn->error, connection_quark(), MPDCRON_ERROR_MALFORMED, "Received unexpected name/value pair `%s'", line); g_free(line); return false; } g_free(line); break; } } /* never reached */ return false; } static bool mpdcron_parse_songs(struct mpdcron_connection *conn, GSList **values) { int ret; gsize length; gchar *line; struct mpdcron_song *song = NULL; for (;;) { line = mpdcron_recv_line(conn, &length); if (line == NULL) { g_free(song); return false; } ret = mpdcron_parser_feed(conn->parser, line); switch (ret) { case MPDCRON_PARSER_SUCCESS: g_free(line); if (song != NULL) *values = g_slist_prepend(*values, song); return true; case MPDCRON_PARSER_ERROR: g_set_error(&conn->error, connection_quark(), conn->parser->u.error.server, "%s", conn->parser->u.error.message); g_free(song); g_free(line); return false; case MPDCRON_PARSER_MALFORMED: g_set_error(&conn->error, connection_quark(), MPDCRON_ERROR_MALFORMED, "Malformed line `%s' received from server", line); g_free(song); g_free(line); return false; default: /* We have a pair! */ if (strcmp(conn->parser->u.pair.name, "id") == 0) { if (song != NULL) *values = g_slist_prepend(*values, song); song = g_new0(struct mpdcron_song, 1); song->id = atoi(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "file") == 0) { g_assert(song != NULL); song->uri = g_strdup(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Love") == 0) { g_assert(song != NULL); song->love = atoi(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Kill") == 0) { g_assert(song != NULL); song->kill = atoi(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Rating") == 0) { g_assert(song != NULL); song->rating = atoi(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Play Count") == 0) { g_assert(song != NULL); song->play_count = atoi(conn->parser->u.pair.value); } else if (strcmp(conn->parser->u.pair.name, "Tag") == 0) { g_assert(song != NULL); song->tags = g_slist_prepend(song->tags, g_strdup(conn->parser->u.pair.value)); } else { g_set_error(&conn->error, connection_quark(), MPDCRON_ERROR_MALFORMED, "Received unexpected name/value pair `%s'", line); g_free(line); return false; } g_free(line); break; } } /* never reached */ return false; } static bool mpdcron_parse_welcome(struct mpdcron_connection *conn, const char *output) { const char *tmp; char *test; if (strncmp(output,MPDCRON_WELCOME_MESSAGE,strlen(MPDCRON_WELCOME_MESSAGE))) { g_set_error(&conn->error, connection_quark(), MPDCRON_ERROR_MALFORMED, "Malformed connect message received"); return false; } tmp = &output[strlen(MPDCRON_WELCOME_MESSAGE)]; conn->version[0] = strtol(tmp, &test, 10); if (test == tmp) { g_set_error(&conn->error, connection_quark(), MPDCRON_ERROR_MALFORMED, "Malformed version number in connect message"); return false; } if (*test == '.') { conn->version[1] = strtol(test + 1, &test, 10); if (*test == '.') conn->version[2] = strtol(test + 1, &test, 10); else conn->version[2] = 0; } else { conn->version[1] = 0; conn->version[2] = 0; } return true; } /** * Sending data */ static char * escape(const char *src) { const char *p; GString *dest; g_return_val_if_fail(src != NULL, NULL); dest = g_string_new(""); p = src; while (*p != 0) { if (*p == '"' || *p == '\\') g_string_append_c(dest, '\\'); g_string_append_c(dest, *p); ++p; } return g_string_free(dest, FALSE); } static bool mpdcron_send_commandv(struct mpdcron_connection *conn, const char *command, va_list args) { const char *arg; char *esc_arg; GString *cmd; GOutputStream *output; cmd = g_string_new(""); g_string_printf(cmd, "%s", command); while ((arg = va_arg(args, const char *)) != NULL) { /* Append a space separator */ g_string_append_c(cmd, ' '); /* Escape and quote the argument */ g_string_append_c(cmd, '"'); esc_arg = escape(arg); g_string_append(cmd, esc_arg); g_free(esc_arg); g_string_append_c(cmd, '"'); } /* Add a newline to finish the command */ g_string_append_c(cmd, '\n'); /* Send the command */ output = g_io_stream_get_output_stream(G_IO_STREAM(conn->stream)); if (!g_output_stream_write_all(output, cmd->str, cmd->len, NULL, NULL, &conn->error)) { g_string_free(cmd, TRUE); return false; } g_string_free(cmd, TRUE); return true; } static bool mpdcron_send_command(struct mpdcron_connection *conn, const char *command, ...) { bool ret; va_list args; va_start(args, command); ret = mpdcron_send_commandv(conn, command, args); va_end(args); return ret; } /** * Global functions */ struct mpdcron_connection * mpdcron_connection_new(const char *hostname, unsigned port) { gsize length; gchar *line; struct mpdcron_connection *conn; conn = g_new0(struct mpdcron_connection, 1); conn->client = g_socket_client_new(); if (hostname[0] == '/') { #if HAVE_GIO_UNIX GSocketAddress *saddr; saddr = g_unix_socket_address_new(hostname); conn->stream = g_socket_client_connect(conn->client, G_SOCKET_CONNECTABLE(saddr), NULL, &conn->error); g_object_unref(saddr); #else g_set_error(&conn->error, connection_quark(), MPDCRON_ERROR_NO_UNIX, "Unix sockets not supported"); g_object_unref(conn->client); conn->client = NULL; return conn; #endif /* HAVE_GIO_UNIX */ } else { GSocketConnectable *naddr; naddr = g_network_address_new(hostname, port); conn->stream = g_socket_client_connect(conn->client, naddr, NULL, &conn->error); g_object_unref(naddr); } if (conn->stream == NULL || conn->error != NULL) { g_object_unref(conn->client); conn->client = NULL; return conn; } conn->input = g_data_input_stream_new(g_io_stream_get_input_stream(G_IO_STREAM(conn->stream))); g_data_input_stream_set_newline_type(conn->input, G_DATA_STREAM_NEWLINE_TYPE_LF); conn->parser = g_new(struct mpdcron_parser, 1); /* Parse welcome message */ if ((line = mpdcron_recv_line(conn, &length)) == NULL) { g_io_stream_close(G_IO_STREAM(conn->stream), NULL, NULL); g_free(conn->parser); g_object_unref(conn->input); g_object_unref(conn->client); conn->input = NULL; conn->parser = NULL; conn->stream = NULL; conn->client = NULL; return conn; } mpdcron_parse_welcome(conn, line); g_free(line); return conn; } void mpdcron_connection_free(struct mpdcron_connection *conn) { if (conn->error != NULL) g_error_free(conn->error); if (conn->input != NULL) g_object_unref(conn->input); if (conn->stream != NULL) g_object_unref(conn->stream); if (conn->client != NULL) g_object_unref(conn->client); g_free(conn->parser); g_free(conn); } bool mpdcron_password(struct mpdcron_connection *conn, const char *password) { g_assert(conn != NULL); g_assert(password != NULL); if (!mpdcron_send_command(conn, "password", password, NULL)) return false; return mpdcron_parse_single(conn); } bool mpdcron_list_album_expr(struct mpdcron_connection *conn, const char *expr, GSList **values) { g_assert(conn != NULL); g_assert(values != NULL); if (!mpdcron_send_command(conn, "list_album", expr, NULL)) return false; return mpdcron_parse_albums(conn, values); } bool mpdcron_list_artist_expr(struct mpdcron_connection *conn, const char *expr, GSList **values) { g_assert(conn != NULL); g_assert(values != NULL); if (!mpdcron_send_command(conn, "list_artist", expr, NULL)) return false; return mpdcron_parse_artists(conn, values); } bool mpdcron_list_genre_expr(struct mpdcron_connection *conn, const char *expr, GSList **values) { g_assert(conn != NULL); g_assert(values != NULL); if (!mpdcron_send_command(conn, "list_genre", expr, NULL)) return false; return mpdcron_parse_genres(conn, values); } bool mpdcron_list_expr(struct mpdcron_connection *conn, const char *expr, GSList **values) { g_assert(conn != NULL); g_assert(values != NULL); if (!mpdcron_send_command(conn, "list", expr, NULL)) return false; return mpdcron_parse_songs(conn, values); } bool mpdcron_listinfo_album_expr(struct mpdcron_connection *conn, const char *expr, GSList **values) { g_assert(conn != NULL); g_assert(values != NULL); if (!mpdcron_send_command(conn, "listinfo_album", expr, NULL)) return false; return mpdcron_parse_albums(conn, values); } bool mpdcron_listinfo_artist_expr(struct mpdcron_connection *conn, const char *expr, GSList **values) { g_assert(conn != NULL); g_assert(values != NULL); if (!mpdcron_send_command(conn, "listinfo_artist", expr, NULL)) return false; return mpdcron_parse_artists(conn, values); } bool mpdcron_listinfo_genre_expr(struct mpdcron_connection *conn, const char *expr, GSList **values) { g_assert(conn != NULL); g_assert(values != NULL); if (!mpdcron_send_command(conn, "listinfo_genre", expr, NULL)) return false; return mpdcron_parse_genres(conn, values); } bool mpdcron_listinfo_expr(struct mpdcron_connection *conn, const char *expr, GSList **values) { g_assert(conn != NULL); g_assert(values != NULL); if (!mpdcron_send_command(conn, "listinfo", expr, NULL)) return false; return mpdcron_parse_songs(conn, values); } bool mpdcron_love_album_expr(struct mpdcron_connection *conn, bool love, const char *expr, int *changes) { g_assert(conn != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, love ? "love_album" : "hate_album", expr, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_love_artist_expr(struct mpdcron_connection *conn, bool love, const char *expr, int *changes) { g_assert(conn != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, love ? "love_artist" : "hate_artist", expr, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_love_genre_expr(struct mpdcron_connection *conn, bool love, const char *expr, int *changes) { g_assert(conn != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, love ? "love_genre" : "hate_genre", expr, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_love_expr(struct mpdcron_connection *conn, bool love, const char *expr, int *changes) { g_assert(conn != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, love ? "love" : "hate", expr, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_kill_album_expr(struct mpdcron_connection *conn, bool kkill, const char *expr, int *changes) { g_assert(conn != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, kkill ? "kill_album" : "unkill_album", expr, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_kill_artist_expr(struct mpdcron_connection *conn, bool kkill, const char *expr, int *changes) { g_assert(conn != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, kkill ? "kill_artist" : "unkill_artist", expr, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_kill_genre_expr(struct mpdcron_connection *conn, bool kkill, const char *expr, int *changes) { g_assert(conn != NULL); if (!mpdcron_send_command(conn, kkill ? "kill_genre" : "unkill_genre", expr, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_kill_expr(struct mpdcron_connection *conn, bool kkill, const char *expr, int *changes) { g_assert(conn != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, kkill ? "kill" : "unkill", expr, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_rate_album_expr(struct mpdcron_connection *conn, const char *expr, const char *rating, int *changes) { g_assert(conn != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "rate_album", expr, rating, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_rate_artist_expr(struct mpdcron_connection *conn, const char *expr, const char *rating, int *changes) { g_assert(conn != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "rate_artist", expr, rating, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_rate_genre_expr(struct mpdcron_connection *conn, const char *expr, const char *rating, int *changes) { g_assert(conn != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "rate_genre", expr, rating, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_rate_expr(struct mpdcron_connection *conn, const char *expr, const char *rating, int *changes) { g_assert(conn != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "rate", expr, rating, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_rate_absolute_album_expr(struct mpdcron_connection *conn, const char *expr, const char *rating, int *changes) { g_assert(conn != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "rate_absolute_album", expr, rating, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_rate_absolute_artist_expr(struct mpdcron_connection *conn, const char *expr, const char *rating, int *changes) { g_assert(conn != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "rate_absolute_artist", expr, rating, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_rate_absolute_genre_expr(struct mpdcron_connection *conn, const char *expr, const char *rating, int *changes) { g_assert(conn != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "rate_absolute_genre", expr, rating, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_rate_absolute_expr(struct mpdcron_connection *conn, const char *expr, const char *rating, int *changes) { g_assert(conn != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "rate_absolute", expr, rating, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_count_absolute_album_expr(struct mpdcron_connection *conn, const char *expr, const char *rating, int *changes) { g_assert(conn != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "count_absolute_album", expr, rating, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_count_absolute_artist_expr(struct mpdcron_connection *conn, const char *expr, const char *rating, int *changes) { g_assert(conn != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "count_absolute_artist", expr, rating, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_count_absolute_genre_expr(struct mpdcron_connection *conn, const char *expr, const char *rating, int *changes) { g_assert(conn != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "count_absolute_genre", expr, rating, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_count_absolute_expr(struct mpdcron_connection *conn, const char *expr, const char *rating, int *changes) { g_assert(conn != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "count_absolute", expr, rating, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_addtag_expr(struct mpdcron_connection *conn, const char *expr, const char *tag, int *changes) { g_assert(conn != NULL); g_assert(expr != NULL); g_assert(tag != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "addtag", expr, tag, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_addtag_artist_expr(struct mpdcron_connection *conn, const char *expr, const char *tag, int *changes) { g_assert(conn != NULL); g_assert(expr != NULL); g_assert(tag != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "addtag_artist", expr, tag, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_addtag_album_expr(struct mpdcron_connection *conn, const char *expr, const char *tag, int *changes) { g_assert(conn != NULL); g_assert(expr != NULL); g_assert(tag != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "addtag_album", expr, tag, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_addtag_genre_expr(struct mpdcron_connection *conn, const char *expr, const char *tag, int *changes) { g_assert(conn != NULL); g_assert(expr != NULL); g_assert(tag != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "addtag_genre", expr, tag, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_rmtag_expr(struct mpdcron_connection *conn, const char *expr, const char *tag, int *changes) { g_assert(conn != NULL); g_assert(expr != NULL); g_assert(tag != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "rmtag", expr, tag, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_rmtag_artist_expr(struct mpdcron_connection *conn, const char *expr, const char *tag, int *changes) { g_assert(conn != NULL); g_assert(expr != NULL); g_assert(tag != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "rmtag_artist", expr, tag, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_rmtag_album_expr(struct mpdcron_connection *conn, const char *expr, const char *tag, int *changes) { g_assert(conn != NULL); g_assert(expr != NULL); g_assert(tag != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "rmtag_album", expr, tag, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_rmtag_genre_expr(struct mpdcron_connection *conn, const char *expr, const char *tag, int *changes) { g_assert(conn != NULL); g_assert(expr != NULL); g_assert(tag != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "rmtag_genre", expr, tag, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_listtags_expr(struct mpdcron_connection *conn, const char *expr, GSList **values) { g_assert(conn != NULL); g_assert(expr != NULL); g_assert(values != NULL); if (!mpdcron_send_command(conn, "listtags", expr, NULL)) return false; return mpdcron_parse_songs(conn, values); } bool mpdcron_listtags_album_expr(struct mpdcron_connection *conn, const char *expr, GSList **values) { g_assert(conn != NULL); g_assert(expr != NULL); g_assert(values != NULL); if (!mpdcron_send_command(conn, "listtags_album", expr, NULL)) return false; return mpdcron_parse_albums(conn, values); } bool mpdcron_listtags_artist_expr(struct mpdcron_connection *conn, const char *expr, GSList **values) { g_assert(conn != NULL); g_assert(expr != NULL); g_assert(values != NULL); if (!mpdcron_send_command(conn, "listtags_artist", expr, NULL)) return false; return mpdcron_parse_artists(conn, values); } bool mpdcron_listtags_genre_expr(struct mpdcron_connection *conn, const char *expr, GSList **values) { g_assert(conn != NULL); g_assert(expr != NULL); g_assert(values != NULL); if (!mpdcron_send_command(conn, "listtags_genre", expr, NULL)) return false; return mpdcron_parse_genres(conn, values); } bool mpdcron_count_album_expr(struct mpdcron_connection *conn, const char *expr, const char *count, int *changes) { g_assert(conn != NULL); g_assert(count != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "count_album", expr, count, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_count_artist_expr(struct mpdcron_connection *conn, const char *expr, const char *count, int *changes) { g_assert(conn != NULL); g_assert(count != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "count_artist", expr, count, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_count_genre_expr(struct mpdcron_connection *conn, const char *expr, const char *count, int *changes) { g_assert(conn != NULL); g_assert(count != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "count_genre", expr, count, NULL)) return false; return mpdcron_parse_changes(conn, changes); } bool mpdcron_count_expr(struct mpdcron_connection *conn, const char *expr, const char *count, int *changes) { g_assert(conn != NULL); g_assert(count != NULL); g_assert(changes != NULL); if (!mpdcron_send_command(conn, "count", expr, count, NULL)) return false; return mpdcron_parse_changes(conn, changes); } mpdcron-0.3+git20110303/src/gmodule/stats/eugene-count-absolute.c000066400000000000000000000170061153374523700243640ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "eugene-defs.h" #include #include #include static int count_absolute_artist(struct mpdcron_connection *conn, const char *expr, const char *rating) { int changes; char *esc_artist, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_count_absolute_artist_expr(conn, expr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate artist: %s", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_ARTIST, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no artist tag!"); mpd_song_free(song); return 1; } esc_artist = quote(mpd_song_get_tag(song, MPD_TAG_ARTIST, 0)); myexpr = g_strdup_printf("name=%s", esc_artist); g_free(esc_artist); mpd_song_free(song); if (!mpdcron_count_absolute_artist_expr(conn, myexpr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate current playing artist: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int count_absolute_album(struct mpdcron_connection *conn, const char *expr, const char *rating) { int changes; char *esc_album, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_count_absolute_album_expr(conn, expr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate album: %s", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_ALBUM, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no album tag!"); mpd_song_free(song); return 1; } esc_album = quote(mpd_song_get_tag(song, MPD_TAG_ALBUM, 0)); myexpr = g_strdup_printf("name=%s", esc_album); g_free(esc_album); mpd_song_free(song); if (!mpdcron_count_absolute_album_expr(conn, myexpr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate current playing album: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int count_absolute_genre(struct mpdcron_connection *conn, const char *expr, const char *rating) { int changes; char *esc_genre, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_count_absolute_genre_expr(conn, expr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate genre: %s", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_GENRE, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no genre tag!"); mpd_song_free(song); return 1; } esc_genre = quote(mpd_song_get_tag(song, MPD_TAG_GENRE, 0)); myexpr = g_strdup_printf("name=%s", esc_genre); g_free(esc_genre); mpd_song_free(song); if (!mpdcron_count_absolute_genre_expr(conn, myexpr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate current playing genre: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int count_absolute_song(struct mpdcron_connection *conn, const char *expr, const char *rating) { int changes; char *esc_uri, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_count_absolute_expr(conn, expr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate song: %s", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; esc_uri = quote(mpd_song_get_uri(song)); myexpr = g_strdup_printf("uri=%s", esc_uri); g_free(esc_uri); mpd_song_free(song); if (!mpdcron_count_absolute_expr(conn, myexpr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate current playing song: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int cmd_count_absolute_internal(const char *expr, const char *rating, bool artist, bool album, bool genre) { int port, ret; const char *hostname, *password; struct mpdcron_connection *conn; if ((artist && album && genre) || (artist && album) || (artist && genre) || (album && genre)) { g_printerr("--artist, --album and --genre options are mutually exclusive\n"); return 1; } hostname = g_getenv(ENV_MPDCRON_HOST) ? g_getenv(ENV_MPDCRON_HOST) : DEFAULT_HOSTNAME; port = g_getenv(ENV_MPDCRON_PORT) ? atoi(g_getenv(ENV_MPDCRON_PORT)) : DEFAULT_PORT; password = g_getenv(ENV_MPDCRON_PASSWORD); conn = mpdcron_connection_new(hostname, port); if (conn->error != NULL) { eulog(LOG_ERR, "Failed to connect: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } if (password != NULL) { if (!mpdcron_password(conn, password)) { eulog(LOG_ERR, "Authentication failed: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } } if (artist) ret = count_absolute_artist(conn, expr, rating); else if (album) ret = count_absolute_album(conn, expr, rating); else if (genre) ret = count_absolute_genre(conn, expr, rating); else ret = count_absolute_song(conn, expr, rating); mpdcron_connection_free(conn); return ret; } int cmd_count_absolute(int argc, char **argv) { int opta = 0, optA = 0, optg = 0, ret; GError *error = NULL; GOptionEntry options[] = { {"artist", 'a', 0, G_OPTION_ARG_NONE, &opta, "Rate artists instead of songs", NULL}, {"album", 'A', 0, G_OPTION_ARG_NONE, &optA, "Rate albums instead of songs", NULL}, {"genre", 'g', 0, G_OPTION_ARG_NONE, &optg, "Rate genres instead of songs", NULL}, { NULL, 0, 0, 0, NULL, NULL, NULL }, }; GOptionContext *ctx; ctx = g_option_context_new("RATING [EXPRESSION]"); g_option_context_add_main_entries(ctx, options, "eugene-count-absolute"); g_option_context_set_summary(ctx, "eugene-count-absolute"VERSION GITHEAD" - Rate (absolute fashion) song/artist/album/genre"); g_option_context_set_description(ctx, "" "By default this command works on the current playing song.\n" "For more information about the expression syntax, see:\n" "http://www.sqlite.org/lang_expr.html"); /* Ignore unknown options so that the user can pass negative numbers. */ g_option_context_set_ignore_unknown_options(ctx, TRUE); if (!g_option_context_parse(ctx, &argc, &argv, &error)) { g_printerr("Option parsing failed: %s\n", error->message); g_error_free(error); g_option_context_free(ctx); return 1; } g_option_context_free(ctx); if (argc <= 1) { g_printerr("No rating given\n"); ret = 1; } else if (argc > 2) ret = cmd_count_absolute_internal(argv[2], argv[1], opta, optA, optg); else ret = cmd_count_absolute_internal(NULL, argv[1], opta, optA, optg); return ret; } mpdcron-0.3+git20110303/src/gmodule/stats/eugene-count.c000066400000000000000000000167611153374523700225570ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "eugene-defs.h" #include #include #include static int count_artist(struct mpdcron_connection *conn, const char *expr, const char *count) { int changes; char *esc_artist, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_count_artist_expr(conn, expr, count, &changes)) { eulog(LOG_ERR, "Failed to change play count of artist: %s", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_ARTIST, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no artist tag!"); mpd_song_free(song); return 1; } esc_artist = quote(mpd_song_get_tag(song, MPD_TAG_ARTIST, 0)); myexpr = g_strdup_printf("name=%s", esc_artist); g_free(esc_artist); mpd_song_free(song); if (!mpdcron_count_artist_expr(conn, myexpr, count, &changes)) { eulog(LOG_ERR, "Failed to change play count of current playing artist: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int count_album(struct mpdcron_connection *conn, const char *expr, const char *count) { int changes; char *esc_album, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_count_album_expr(conn, expr, count, &changes)) { eulog(LOG_ERR, "Failed to change play count of count album: %s", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_ALBUM, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no album tag!"); mpd_song_free(song); return 1; } esc_album = quote(mpd_song_get_tag(song, MPD_TAG_ALBUM, 0)); myexpr = g_strdup_printf("name=%s", esc_album); g_free(esc_album); mpd_song_free(song); if (!mpdcron_count_album_expr(conn, myexpr, count, &changes)) { eulog(LOG_ERR, "Failed to change play count of current playing album: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int count_genre(struct mpdcron_connection *conn, const char *expr, const char *count) { int changes; char *esc_genre, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_count_genre_expr(conn, expr, count, &changes)) { eulog(LOG_ERR, "Failed to change play count of genre: %s", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_GENRE, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no genre tag!"); mpd_song_free(song); return 1; } esc_genre = quote(mpd_song_get_tag(song, MPD_TAG_GENRE, 0)); myexpr = g_strdup_printf("name=%s", esc_genre); g_free(esc_genre); mpd_song_free(song); if (!mpdcron_count_genre_expr(conn, myexpr, count, &changes)) { eulog(LOG_ERR, "Failed to change play count of count current playing genre: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int count_song(struct mpdcron_connection *conn, const char *expr, const char *count) { int changes; char *esc_uri, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_count_expr(conn, expr, count, &changes)) { eulog(LOG_ERR, "Failed to change play count of count song: %s", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; esc_uri = quote(mpd_song_get_uri(song)); myexpr = g_strdup_printf("uri=%s", esc_uri); g_free(esc_uri); mpd_song_free(song); if (!mpdcron_count_expr(conn, myexpr, count, &changes)) { eulog(LOG_ERR, "Failed to change play count of current playing song: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int cmd_count_internal(const char *expr, const char *count, bool artist, bool album, bool genre) { int port, ret; const char *hostname, *password; struct mpdcron_connection *conn; if ((artist && album && genre) || (artist && album) || (artist && genre) || (album && genre)) { g_printerr("--artist, --album and --genre options are mutually exclusive\n"); return 1; } hostname = g_getenv(ENV_MPDCRON_HOST) ? g_getenv(ENV_MPDCRON_HOST) : DEFAULT_HOSTNAME; port = g_getenv(ENV_MPDCRON_PORT) ? atoi(g_getenv(ENV_MPDCRON_PORT)) : DEFAULT_PORT; password = g_getenv(ENV_MPDCRON_PASSWORD); conn = mpdcron_connection_new(hostname, port); if (conn->error != NULL) { eulog(LOG_ERR, "Failed to connect: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } if (password != NULL) { if (!mpdcron_password(conn, password)) { eulog(LOG_ERR, "Authentication failed: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } } if (artist) ret = count_artist(conn, expr, count); else if (album) ret = count_album(conn, expr, count); else if (genre) ret = count_genre(conn, expr, count); else ret = count_song(conn, expr, count); mpdcron_connection_free(conn); return ret; } int cmd_count(int argc, char **argv) { int opta = 0, optA = 0, optg = 0, ret; GError *error = NULL; GOptionEntry options[] = { {"artist", 'a', 0, G_OPTION_ARG_NONE, &opta, "Change play count of artists instead of songs", NULL}, {"album", 'A', 0, G_OPTION_ARG_NONE, &optA, "Change play count of albums instead of songs", NULL}, {"genre", 'g', 0, G_OPTION_ARG_NONE, &optg, "Change play count of genres instead of songs", NULL}, { NULL, 0, 0, 0, NULL, NULL, NULL }, }; GOptionContext *ctx; ctx = g_option_context_new("COUNT [EXPRESSION]"); g_option_context_add_main_entries(ctx, options, "eugene-count"); g_option_context_set_summary(ctx, "eugene-count-"VERSION GITHEAD " - Change play count of song/artist/album/genre"); g_option_context_set_description(ctx, "" "By default this command works on the current playing song.\n" "For more information about the expression syntax, see:\n" "http://www.sqlite.org/lang_expr.html"); /* Ignore unknown options so that the user can pass negative numbers. */ g_option_context_set_ignore_unknown_options(ctx, TRUE); if (!g_option_context_parse(ctx, &argc, &argv, &error)) { g_printerr("Option parsing failed: %s\n", error->message); g_error_free(error); g_option_context_free(ctx); return 1; } g_option_context_free(ctx); if (argc <= 1) { g_printerr("No count given\n"); ret = 1; } else if (argc > 2) ret = cmd_count_internal(argv[2], argv[1], opta, optA, optg); else ret = cmd_count_internal(NULL, argv[1], opta, optA, optg); return ret; } mpdcron-0.3+git20110303/src/gmodule/stats/eugene-defs.h000066400000000000000000000203571153374523700223510ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef MPDCRON_EUGENE_DEFS_H #define MPDCRON_EUGENE_DEFS_H 1 #include "../../cron-config.h" #include #include #include #include #define ENV_MPDCRON_HOST "MPDCRON_HOST" #define ENV_MPDCRON_PORT "MPDCRON_PORT" #define ENV_MPDCRON_PASSWORD "MPDCRON_PASSWORD" #define DEFAULT_HOSTNAME "localhost" #define DEFAULT_PORT 6601 enum mpdcron_error { MPDCRON_ERROR_NO_UNIX, MPDCRON_ERROR_BAD_ADDRESS, MPDCRON_ERROR_MALFORMED, MPDCRON_ERROR_OVERFLOW, MPDCRON_ERROR_EOF, MPDCRON_ERROR_SERVER_UNK, }; enum mpdcron_parser_result { /** * Response line was not understood. */ MPDCRON_PARSER_MALFORMED, /** * mpdcron has returned "OK". */ MPDCRON_PARSER_SUCCESS, /** * mpdcron has returned "ACK" with an error code. Call * mpd_parser_get_server_error() to get the error code. */ MPDCRON_PARSER_ERROR, /** * mpdcron has returned a name-value pair. Call * mpdcron_parser_get_name() and mpdcron_parser_get_value(). */ MPDCRON_PARSER_PAIR, }; struct mpdcron_song { int id; int play_count; char *uri; int love; int kill; int rating; GSList *tags; }; struct mpdcron_entity { int id; int play_count; char *name; char *artist; int love; int kill; int rating; GSList *tags; }; struct mpdcron_parser; struct mpdcron_connection { /** * The version number reported by mpdcron server. */ unsigned version[3]; /** * The last error occured. */ GError *error; /** * Client object. */ GSocketClient *client; /** * IO Stream that represents the connection. */ GSocketConnection *stream; /** * Data Input stream */ GDataInputStream *input; /** * Parser */ struct mpdcron_parser *parser; }; struct mpdcron_connection * mpdcron_connection_new(const char *hostname, unsigned port); void mpdcron_connection_free(struct mpdcron_connection *conn); bool mpdcron_password(struct mpdcron_connection *conn, const char *password); bool mpdcron_list_album_expr(struct mpdcron_connection *conn, const char *expr, GSList **values); bool mpdcron_list_artist_expr(struct mpdcron_connection *conn, const char *expr, GSList **values); bool mpdcron_list_genre_expr(struct mpdcron_connection *conn, const char *expr, GSList **values); bool mpdcron_list_expr(struct mpdcron_connection *conn, const char *expr, GSList **values); bool mpdcron_listinfo_album_expr(struct mpdcron_connection *conn, const char *expr, GSList **values); bool mpdcron_listinfo_artist_expr(struct mpdcron_connection *conn, const char *expr, GSList **values); bool mpdcron_listinfo_genre_expr(struct mpdcron_connection *conn, const char *expr, GSList **values); bool mpdcron_listinfo_expr(struct mpdcron_connection *conn, const char *expr, GSList **values); bool mpdcron_love_album_expr(struct mpdcron_connection *conn, bool love, const char *expr, int *changes); bool mpdcron_love_artist_expr(struct mpdcron_connection *conn, bool love, const char *expr, int *changes); bool mpdcron_love_genre_expr(struct mpdcron_connection *conn, bool love, const char *expr, int *changes); bool mpdcron_love_expr(struct mpdcron_connection *conn, bool love, const char *expr, int *changes); bool mpdcron_kill_album_expr(struct mpdcron_connection *conn, bool kkill, const char *expr, int *changes); bool mpdcron_kill_artist_expr(struct mpdcron_connection *conn, bool kkill, const char *expr, int *changes); bool mpdcron_kill_genre_expr(struct mpdcron_connection *conn, bool kkill, const char *expr, int *changes); bool mpdcron_kill_expr(struct mpdcron_connection *conn, bool kkill, const char *expr, int *changes); bool mpdcron_rate_album_expr(struct mpdcron_connection *conn, const char *rating, const char *expr, int *changes); bool mpdcron_rate_artist_expr(struct mpdcron_connection *conn, const char *rating, const char *expr, int *changes); bool mpdcron_rate_genre_expr(struct mpdcron_connection *conn, const char *rating, const char *expr, int *changes); bool mpdcron_rate_expr(struct mpdcron_connection *conn, const char *rating, const char *expr, int *changes); bool mpdcron_rate_absolute_album_expr(struct mpdcron_connection *conn, const char *rating, const char *expr, int *changes); bool mpdcron_rate_absolute_artist_expr(struct mpdcron_connection *conn, const char *rating, const char *expr, int *changes); bool mpdcron_rate_absolute_genre_expr(struct mpdcron_connection *conn, const char *rating, const char *expr, int *changes); bool mpdcron_rate_absolute_expr(struct mpdcron_connection *conn, const char *rating, const char *expr, int *changes); bool mpdcron_count_absolute_album_expr(struct mpdcron_connection *conn, const char *rating, const char *expr, int *changes); bool mpdcron_count_absolute_artist_expr(struct mpdcron_connection *conn, const char *rating, const char *expr, int *changes); bool mpdcron_count_absolute_genre_expr(struct mpdcron_connection *conn, const char *rating, const char *expr, int *changes); bool mpdcron_count_absolute_expr(struct mpdcron_connection *conn, const char *rating, const char *expr, int *changes); bool mpdcron_addtag_expr(struct mpdcron_connection *conn, const char *expr, const char *tag, int *changes); bool mpdcron_addtag_artist_expr(struct mpdcron_connection *conn, const char *expr, const char *tag, int *changes); bool mpdcron_addtag_album_expr(struct mpdcron_connection *conn, const char *expr, const char *tag, int *changes); bool mpdcron_addtag_genre_expr(struct mpdcron_connection *conn, const char *expr, const char *tag, int *changes); bool mpdcron_rmtag_expr(struct mpdcron_connection *conn, const char *expr, const char *tag, int *changes); bool mpdcron_rmtag_artist_expr(struct mpdcron_connection *conn, const char *expr, const char *tag, int *changes); bool mpdcron_rmtag_album_expr(struct mpdcron_connection *conn, const char *expr, const char *tag, int *changes); bool mpdcron_rmtag_genre_expr(struct mpdcron_connection *conn, const char *expr, const char *tag, int *changes); bool mpdcron_listtags_expr(struct mpdcron_connection *conn, const char *expr, GSList **values); bool mpdcron_listtags_album_expr(struct mpdcron_connection *conn, const char *expr, GSList **values); bool mpdcron_listtags_artist_expr(struct mpdcron_connection *conn, const char *expr, GSList **values); bool mpdcron_listtags_genre_expr(struct mpdcron_connection *conn, const char *expr, GSList **values); bool mpdcron_count_album_expr(struct mpdcron_connection *conn, const char *expr, const char *count, int *changes); bool mpdcron_count_artist_expr(struct mpdcron_connection *conn, const char *expr, const char *count, int *changes); bool mpdcron_count_genre_expr(struct mpdcron_connection *conn, const char *expr, const char *count, int *changes); bool mpdcron_count_expr(struct mpdcron_connection *conn, const char *expr, const char *count, int *changes); char * quote(const char *src); struct mpd_song * load_current_song(void); int cmd_hate(int argc, char **argv); int cmd_love(int argc, char **argv); int cmd_kill(int argc, char **argv); int cmd_unkill(int argc, char **argv); int cmd_rate(int argc, char **argv); int cmd_rate_absolute(int argc, char **argv); int cmd_count_absolute(int argc, char **argv); int cmd_list(int argc, char **argv); int cmd_listinfo(int argc, char **argv); int cmd_addtag(int argc, char **argv); int cmd_rmtag(int argc, char **argv); int cmd_listtags(int argc, char **argv); int cmd_count(int argc, char **argv); void eulog(int level, const char *fmt, ...); #endif /* !MPDCRON_EUGENE_DEFS_H */ mpdcron-0.3+git20110303/src/gmodule/stats/eugene-kill.c000066400000000000000000000207421153374523700223540ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "eugene-defs.h" #include #include #include static int kill_artist(struct mpdcron_connection *conn, bool kkill, const char *expr) { int changes; char *esc_artist, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_kill_artist_expr(conn, kkill, expr, &changes)) { eulog(LOG_ERR, "Failed to %s artist: %s", kkill ? "kill" : "unkill", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_ARTIST, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no artist tag!"); mpd_song_free(song); return 1; } esc_artist = quote(mpd_song_get_tag(song, MPD_TAG_ARTIST, 0)); myexpr = g_strdup_printf("name=%s", esc_artist); g_free(esc_artist); mpd_song_free(song); if (!mpdcron_kill_artist_expr(conn, kkill, myexpr, &changes)) { eulog(LOG_ERR, "Failed to %s current playing artist: %s", kkill ? "kill" : "unkill", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int kill_album(struct mpdcron_connection *conn, bool kkill, const char *expr) { int changes; char *esc_album, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_kill_album_expr(conn, kkill, expr, &changes)) { eulog(LOG_ERR, "Failed to %s album: %s", kkill ? "kill" : "unkill", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_ALBUM, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no album tag!"); mpd_song_free(song); return 1; } esc_album = quote(mpd_song_get_tag(song, MPD_TAG_ALBUM, 0)); myexpr = g_strdup_printf("name=%s", esc_album); g_free(esc_album); mpd_song_free(song); if (!mpdcron_kill_album_expr(conn, kkill, myexpr, &changes)) { eulog(LOG_ERR, "Failed to %s current playing album: %s", kkill ? "kill" : "unkill", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int kill_genre(struct mpdcron_connection *conn, bool kkill, const char *expr) { int changes; char *esc_genre, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_kill_genre_expr(conn, kkill, expr, &changes)) { eulog(LOG_ERR, "Failed to %s genre: %s", kkill ? "kill" : "unkill", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_GENRE, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no genre tag!"); mpd_song_free(song); return 1; } esc_genre = quote(mpd_song_get_tag(song, MPD_TAG_GENRE, 0)); myexpr = g_strdup_printf("name=%s", esc_genre); g_free(esc_genre); mpd_song_free(song); if (!mpdcron_kill_genre_expr(conn, kkill, myexpr, &changes)) { eulog(LOG_ERR, "Failed to %s current playing genre: %s", kkill ? "kill" : "unkill", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int kill_song(struct mpdcron_connection *conn, bool kkill, const char *expr) { int changes; char *esc_uri, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_kill_expr(conn, kkill, expr, &changes)) { eulog(LOG_ERR, "Failed to %s song: %s", kkill ? "kill" : "unkill", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; esc_uri = quote(mpd_song_get_uri(song)); myexpr = g_strdup_printf("uri=%s", esc_uri); g_free(esc_uri); mpd_song_free(song); if (!mpdcron_kill_expr(conn, kkill, myexpr, &changes)) { eulog(LOG_ERR, "Failed to %s current playing song: %s", kkill ? "kill" : "unkill", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int cmd_kill_internal(bool kkill, const char *expr, bool artist, bool album, bool genre) { int port, ret; const char *hostname, *password; struct mpdcron_connection *conn; if ((artist && album && genre) || (artist && album) || (artist && genre) || (album && genre)) { g_printerr("--artist, --album and --genre options are mutually exclusive\n"); return 1; } hostname = g_getenv(ENV_MPDCRON_HOST) ? g_getenv(ENV_MPDCRON_HOST) : DEFAULT_HOSTNAME; port = g_getenv(ENV_MPDCRON_PORT) ? atoi(g_getenv(ENV_MPDCRON_PORT)) : DEFAULT_PORT; password = g_getenv(ENV_MPDCRON_PASSWORD); conn = mpdcron_connection_new(hostname, port); if (conn->error != NULL) { eulog(LOG_ERR, "Failed to connect: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } if (password != NULL) { if (!mpdcron_password(conn, password)) { eulog(LOG_ERR, "Authentication failed: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } } if (artist) ret = kill_artist(conn, kkill, expr); else if (album) ret = kill_album(conn, kkill, expr); else if (genre) ret = kill_genre(conn, kkill, expr); else ret = kill_song(conn, kkill, expr); mpdcron_connection_free(conn); return ret; } int cmd_kill(int argc, char **argv) { int opta = 0, optA = 0, optg = 0, ret; GError *error = NULL; GOptionEntry options[] = { {"artist", 'a', 0, G_OPTION_ARG_NONE, &opta, "Kill artists instead of songs", NULL}, {"album", 'A', 0, G_OPTION_ARG_NONE, &optA, "Kill albums instead of songs", NULL}, {"genre", 'g', 0, G_OPTION_ARG_NONE, &optg, "Kill genres instead of songs", NULL}, { NULL, 0, 0, 0, NULL, NULL, NULL }, }; GOptionContext *ctx; ctx = g_option_context_new("[EXPRESSION]"); g_option_context_add_main_entries(ctx, options, "eugene-kill"); g_option_context_set_summary(ctx, "eugene-kill-"VERSION GITHEAD" - Kill song/artist/album/genre"); g_option_context_set_description(ctx, "" "By default this command works on the current playing song.\n" "For more information about the expression syntax, see:\n" "http://www.sqlite.org/lang_expr.html"); if (!g_option_context_parse(ctx, &argc, &argv, &error)) { g_printerr("Option parsing failed: %s\n", error->message); g_error_free(error); g_option_context_free(ctx); return 1; } g_option_context_free(ctx); if (argc > 1) ret = cmd_kill_internal(true, argv[1], opta, optA, optg); else ret = cmd_kill_internal(true, NULL, opta, optA, optg); return ret; } int cmd_unkill(int argc, char **argv) { int opta = 0, optA = 0, optg = 0, ret; GError *error = NULL; GOptionEntry options[] = { {"artist", 'a', 0, G_OPTION_ARG_NONE, &opta, "Unkill artists instead of songs", NULL}, {"album", 'A', 0, G_OPTION_ARG_NONE, &optA, "Unkill albums instead of songs", NULL}, {"genre", 'g', 0, G_OPTION_ARG_NONE, &optg, "Unkill genres instead of songs", NULL}, { NULL, 0, 0, 0, NULL, NULL, NULL }, }; GOptionContext *ctx; ctx = g_option_context_new("[EXPRESSION]"); g_option_context_add_main_entries(ctx, options, "eugene-unkill"); g_option_context_set_summary(ctx, "eugene-unkill-"VERSION GITHEAD" - Unkill song/artist/album/genre"); g_option_context_set_description(ctx, "" "By default this command works on the current playing song.\n" "For more information about the expression syntax, see:\n" "http://www.sqlite.org/lang_expr.html"); if (!g_option_context_parse(ctx, &argc, &argv, &error)) { g_printerr("Option parsing failed: %s\n", error->message); g_error_free(error); g_option_context_free(ctx); return 1; } g_option_context_free(ctx); if (argc > 1) ret = cmd_kill_internal(false, argv[1], opta, optA, optg); else ret = cmd_kill_internal(false, NULL, opta, optA, optg); return ret; } mpdcron-0.3+git20110303/src/gmodule/stats/eugene-list.c000066400000000000000000000123451153374523700223740ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "eugene-defs.h" #include #include #include static int list_artist(struct mpdcron_connection *conn, const char *expr) { GSList *values, *walk; g_assert(expr != NULL); values = NULL; if (!mpdcron_list_artist_expr(conn, expr, &values)) { eulog(LOG_ERR, "Failed to list artist: %s", conn->error->message); return 1; } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct mpdcron_entity *e = walk->data; printf("%d: %s\n", e->id, e->name); g_free(e->name); g_free(e); } g_slist_free(values); return 0; } static int list_album(struct mpdcron_connection *conn, const char *expr) { GSList *values, *walk; g_assert(expr != NULL); values = NULL; if (!mpdcron_list_album_expr(conn, expr, &values)) { eulog(LOG_ERR, "Failed to list album: %s", conn->error->message); return 1; } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct mpdcron_entity *e = walk->data; printf("%d: %s\n", e->id, e->name); g_free(e->name); g_free(e->artist); /* TODO: We don't use artist information. */ g_free(e); } g_slist_free(values); return 0; } static int list_genre(struct mpdcron_connection *conn, const char *expr) { GSList *values, *walk; g_assert(expr != NULL); values = NULL; if (!mpdcron_list_genre_expr(conn, expr, &values)) { eulog(LOG_ERR, "Failed to list genre: %s", conn->error->message); return 1; } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct mpdcron_entity *e = walk->data; printf("%d: %s\n", e->id, e->name); g_free(e->name); g_free(e); } g_slist_free(values); return 0; } static int list_song(struct mpdcron_connection *conn, const char *expr) { GSList *values, *walk; g_assert(expr != NULL); values = NULL; if (!mpdcron_list_expr(conn, expr, &values)) { eulog(LOG_ERR, "Failed to list song: %s", conn->error->message); return 1; } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct mpdcron_song *e = walk->data; printf("%d: %s\n", e->id, e->uri); g_free(e->uri); g_free(e); } g_slist_free(values); return 0; } static int cmd_list_internal(const char *expr, bool artist, bool album, bool genre) { int port, ret; const char *hostname, *password; struct mpdcron_connection *conn; if ((artist && album && genre) || (artist && album) || (artist && genre) || (album && genre)) { g_printerr("--artist, --album and --genre options are mutually exclusive\n"); return 1; } hostname = g_getenv(ENV_MPDCRON_HOST) ? g_getenv(ENV_MPDCRON_HOST) : DEFAULT_HOSTNAME; port = g_getenv(ENV_MPDCRON_PORT) ? atoi(g_getenv(ENV_MPDCRON_PORT)) : DEFAULT_PORT; password = g_getenv(ENV_MPDCRON_PASSWORD); conn = mpdcron_connection_new(hostname, port); if (conn->error != NULL) { eulog(LOG_ERR, "Failed to connect: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } if (password != NULL) { if (!mpdcron_password(conn, password)) { eulog(LOG_ERR, "Authentication failed: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } } if (artist) ret = list_artist(conn, expr); else if (album) ret = list_album(conn, expr); else if (genre) ret = list_genre(conn, expr); else ret = list_song(conn, expr); mpdcron_connection_free(conn); return ret; } int cmd_list(int argc, char **argv) { int opta = 0, optA = 0, optg = 0, ret; GError *error = NULL; GOptionEntry options[] = { {"artist", 'a', 0, G_OPTION_ARG_NONE, &opta, "List artists instead of songs", NULL}, {"album", 'A', 0, G_OPTION_ARG_NONE, &optA, "List albums instead of songs", NULL}, {"genre", 'g', 0, G_OPTION_ARG_NONE, &optg, "List genres instead of songs", NULL}, { NULL, 0, 0, 0, NULL, NULL, NULL }, }; GOptionContext *ctx; ctx = g_option_context_new("EXPRESSION"); g_option_context_add_main_entries(ctx, options, "eugene-list"); g_option_context_set_summary(ctx, "eugene-list-"VERSION GITHEAD" - List song/artist/album/genre"); g_option_context_set_description(ctx, "" "For more information about the expression syntax, see:\n" "http://www.sqlite.org/lang_expr.html"); if (!g_option_context_parse(ctx, &argc, &argv, &error)) { g_printerr("Option parsing failed: %s\n", error->message); g_error_free(error); g_option_context_free(ctx); return 1; } g_option_context_free(ctx); if (argc > 1) ret = cmd_list_internal(argv[1], opta, optA, optg); else { g_printerr("No expression given\n"); ret = 1; } return ret; } mpdcron-0.3+git20110303/src/gmodule/stats/eugene-listinfo.c000066400000000000000000000201251153374523700232430ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "eugene-defs.h" #include #include #include static int listinfo_artist(struct mpdcron_connection *conn, const char *expr) { GSList *values, *walk; values = NULL; if (expr != NULL) { if (!mpdcron_listinfo_artist_expr(conn, expr, &values)) { eulog(LOG_ERR, "Failed to list artist: %s", conn->error->message); return 1; } } else { char *esc_artist, *myexpr; struct mpd_song *song; if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_ARTIST, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no artist tag!"); mpd_song_free(song); return 1; } esc_artist = quote(mpd_song_get_tag(song, MPD_TAG_ARTIST, 0)); myexpr = g_strdup_printf("name=%s", esc_artist); g_free(esc_artist); mpd_song_free(song); if (!mpdcron_listinfo_artist_expr(conn, myexpr, &values)) { eulog(LOG_ERR, "Failed to list current playing artist: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct mpdcron_entity *e = walk->data; printf("%d: Play_Count:%d Love:%d Kill:%d Rating:%d %s\n", e->id, e->play_count, e->love, e->kill, e->rating, e->name); g_free(e->name); g_free(e); } g_slist_free(values); return 0; } static int listinfo_album(struct mpdcron_connection *conn, const char *expr) { GSList *values, *walk; values = NULL; if (expr != NULL) { if (!mpdcron_listinfo_album_expr(conn, expr, &values)) { eulog(LOG_ERR, "Failed to list album: %s", conn->error->message); return 1; } } else { char *esc_album, *myexpr; struct mpd_song *song; if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_ALBUM, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no album tag!"); mpd_song_free(song); return 1; } esc_album = quote(mpd_song_get_tag(song, MPD_TAG_ALBUM, 0)); myexpr = g_strdup_printf("name=%s", esc_album); g_free(esc_album); mpd_song_free(song); if (!mpdcron_listinfo_album_expr(conn, myexpr, &values)) { eulog(LOG_ERR, "Failed to list current playing album: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct mpdcron_entity *e = walk->data; printf("%d: Play_Count:%d Love:%d Kill:%d Rating:%d %s\n", e->id, e->play_count, e->love, e->kill, e->rating, e->name); g_free(e->name); g_free(e->artist); /* TODO: We don't use artist information. */ g_free(e); } g_slist_free(values); return 0; } static int listinfo_genre(struct mpdcron_connection *conn, const char *expr) { GSList *values, *walk; values = NULL; if (expr != NULL) { if (!mpdcron_listinfo_genre_expr(conn, expr, &values)) { eulog(LOG_ERR, "Failed to list genre: %s", conn->error->message); return 1; } } else { char *esc_genre, *myexpr; struct mpd_song *song; if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_GENRE, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no genre tag!"); mpd_song_free(song); return 1; } esc_genre = quote(mpd_song_get_tag(song, MPD_TAG_GENRE, 0)); myexpr = g_strdup_printf("name=%s", esc_genre); g_free(esc_genre); mpd_song_free(song); if (!mpdcron_listinfo_genre_expr(conn, myexpr, &values)) { eulog(LOG_ERR, "Failed to list current playing genre: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct mpdcron_entity *e = walk->data; printf("%d: Play_Count:%d Love:%d Kill:%d Rating:%d %s\n", e->id, e->play_count, e->love, e->kill, e->rating, e->name); g_free(e->name); g_free(e); } g_slist_free(values); return 0; } static int listinfo_song(struct mpdcron_connection *conn, const char *expr) { GSList *values, *walk; values = NULL; if (expr != NULL) { if (!mpdcron_listinfo_expr(conn, expr, &values)) { eulog(LOG_ERR, "Failed to list song: %s", conn->error->message); return 1; } } else { char *esc_uri, *myexpr; struct mpd_song *song; if ((song = load_current_song()) == NULL) return 1; esc_uri = quote(mpd_song_get_uri(song)); myexpr = g_strdup_printf("uri=%s", esc_uri); g_free(esc_uri); mpd_song_free(song); if (!mpdcron_listinfo_expr(conn, myexpr, &values)) { eulog(LOG_ERR, "Failed to list current playing song: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct mpdcron_song *s = walk->data; printf("%d: Play_Count:%d Love:%d Kill:%d Rating:%d %s\n", s->id, s->play_count, s->love, s->kill, s->rating, s->uri); g_free(s->uri); g_free(s); } g_slist_free(values); return 0; } static int cmd_listinfo_internal(const char *expr, bool artist, bool album, bool genre) { int port, ret; const char *hostname, *password; struct mpdcron_connection *conn; if ((artist && album && genre) || (artist && album) || (artist && genre) || (album && genre)) { g_printerr("--artist, --album and --genre options are mutually exclusive\n"); return 1; } hostname = g_getenv(ENV_MPDCRON_HOST) ? g_getenv(ENV_MPDCRON_HOST) : DEFAULT_HOSTNAME; port = g_getenv(ENV_MPDCRON_PORT) ? atoi(g_getenv(ENV_MPDCRON_PORT)) : DEFAULT_PORT; password = g_getenv(ENV_MPDCRON_PASSWORD); conn = mpdcron_connection_new(hostname, port); if (conn->error != NULL) { eulog(LOG_ERR, "Failed to connect: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } if (password != NULL) { if (!mpdcron_password(conn, password)) { eulog(LOG_ERR, "Authentication failed: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } } if (artist) ret = listinfo_artist(conn, expr); else if (album) ret = listinfo_album(conn, expr); else if (genre) ret = listinfo_genre(conn, expr); else ret = listinfo_song(conn, expr); mpdcron_connection_free(conn); return ret; } int cmd_listinfo(int argc, char **argv) { int opta = 0, optA = 0, optg = 0, ret; GError *error = NULL; GOptionEntry options[] = { {"artist", 'a', 0, G_OPTION_ARG_NONE, &opta, "List artists instead of songs", NULL}, {"album", 'A', 0, G_OPTION_ARG_NONE, &optA, "List albums instead of songs", NULL}, {"genre", 'g', 0, G_OPTION_ARG_NONE, &optg, "List genres instead of songs", NULL}, { NULL, 0, 0, 0, NULL, NULL, NULL }, }; GOptionContext *ctx; ctx = g_option_context_new("[EXPRESSION]"); g_option_context_add_main_entries(ctx, options, "eugene-listinfo"); g_option_context_set_summary(ctx, "eugene-listinfo-"VERSION GITHEAD" - List song/artist/album/genre"); g_option_context_set_description(ctx, "" "By default this command works on the current playing song.\n" "For more information about the expression syntax, see:\n" "http://www.sqlite.org/lang_expr.html"); if (!g_option_context_parse(ctx, &argc, &argv, &error)) { g_printerr("Option parsing failed: %s\n", error->message); g_error_free(error); g_option_context_free(ctx); return 1; } g_option_context_free(ctx); if (argc > 1) ret = cmd_listinfo_internal(argv[1], opta, optA, optg); else ret = cmd_listinfo_internal(NULL, opta, optA, optg); return ret; } mpdcron-0.3+git20110303/src/gmodule/stats/eugene-listtags.c000066400000000000000000000221521153374523700232500ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "eugene-defs.h" #include #include #include static int listtags_artist(struct mpdcron_connection *conn, const char *expr) { GSList *values, *walk; values = NULL; if (expr != NULL) { if (!mpdcron_listtags_artist_expr(conn, expr, &values)) { eulog(LOG_ERR, "Failed to list tags of artist: %s", conn->error->message); return 1; } } else { char *esc_artist, *myexpr; struct mpd_song *song; if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_ARTIST, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no artist tag!"); mpd_song_free(song); return 1; } esc_artist = quote(mpd_song_get_tag(song, MPD_TAG_ARTIST, 0)); myexpr = g_strdup_printf("name=%s", esc_artist); g_free(esc_artist); mpd_song_free(song); if (!mpdcron_listtags_artist_expr(conn, myexpr, &values)) { eulog(LOG_ERR, "Failed to list tags of current playing artist: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct mpdcron_entity *e = walk->data; printf("%d: Tags:", e->id); for (GSList *walk2 = e->tags; walk2 != NULL; walk2 = g_slist_next(walk2)) { printf("%s", (char *)walk2->data); if (g_slist_next(walk2) != NULL) fputc(',', stdout); g_free(walk2->data); } printf(" %s\n", e->name); g_slist_free(e->tags); g_free(e->name); g_free(e); } g_slist_free(values); return 0; } static int listtags_album(struct mpdcron_connection *conn, const char *expr) { GSList *values, *walk; values = NULL; if (expr != NULL) { if (!mpdcron_listtags_album_expr(conn, expr, &values)) { eulog(LOG_ERR, "Failed to list tags of album: %s", conn->error->message); return 1; } } else { char *esc_album, *esc_artist, *myexpr; struct mpd_song *song; if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_ARTIST, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no artist tag!"); mpd_song_free(song); return 1; } else if (mpd_song_get_tag(song, MPD_TAG_ALBUM, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no album tag!"); mpd_song_free(song); return 1; } esc_album = quote(mpd_song_get_tag(song, MPD_TAG_ALBUM, 0)); esc_artist = quote(mpd_song_get_tag(song, MPD_TAG_ARTIST, 0)); myexpr = g_strdup_printf("name=%s and artist=%s", esc_album, esc_artist); g_free(esc_album); g_free(esc_artist); mpd_song_free(song); if (!mpdcron_listtags_album_expr(conn, myexpr, &values)) { eulog(LOG_ERR, "Failed to list tags of current playing album: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct mpdcron_entity *e = walk->data; printf("%d: Tags:", e->id); for (GSList *walk2 = e->tags; walk2 != NULL; walk2 = g_slist_next(walk2)) { printf("%s", (char *)walk2->data); if (g_slist_next(walk2) != NULL) fputc(',', stdout); g_free(walk2->data); } printf(" %s\n", e->name); g_slist_free(e->tags); g_free(e->artist); /* TODO: we don't use artist */ g_free(e->name); g_free(e); } g_slist_free(values); return 0; } static int listtags_genre(struct mpdcron_connection *conn, const char *expr) { GSList *values, *walk; values = NULL; if (expr != NULL) { if (!mpdcron_listtags_genre_expr(conn, expr, &values)) { eulog(LOG_ERR, "Failed to list tags of genre: %s", conn->error->message); return 1; } } else { char *esc_genre, *myexpr; struct mpd_song *song; if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_GENRE, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no genre tag!"); mpd_song_free(song); return 1; } esc_genre = quote(mpd_song_get_tag(song, MPD_TAG_GENRE, 0)); myexpr = g_strdup_printf("name=%s", esc_genre); g_free(esc_genre); mpd_song_free(song); if (!mpdcron_listtags_genre_expr(conn, myexpr, &values)) { eulog(LOG_ERR, "Failed to list tags of current playing genre: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct mpdcron_entity *e = walk->data; printf("%d: Tags:", e->id); for (GSList *walk2 = e->tags; walk2 != NULL; walk2 = g_slist_next(walk2)) { printf("%s", (char *)walk2->data); if (g_slist_next(walk2) != NULL) fputc(',', stdout); g_free(walk2->data); } printf(" %s\n", e->name); g_slist_free(e->tags); g_free(e->name); g_free(e); } g_slist_free(values); return 0; } static int listtags_song(struct mpdcron_connection *conn, const char *expr) { GSList *values, *walk; values = NULL; if (expr != NULL) { if (!mpdcron_listtags_expr(conn, expr, &values)) { eulog(LOG_ERR, "Failed to list tags of song: %s", conn->error->message); return 1; } } else { char *esc_uri, *myexpr; struct mpd_song *song; if ((song = load_current_song()) == NULL) return 1; esc_uri = quote(mpd_song_get_uri(song)); myexpr = g_strdup_printf("uri=%s", esc_uri); g_free(esc_uri); mpd_song_free(song); if (!mpdcron_listtags_expr(conn, myexpr, &values)) { eulog(LOG_ERR, "Failed to list tags of current playing song: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct mpdcron_song *s = walk->data; if (s->tags != NULL) { printf("%d: Tags:", s->id); for (GSList *walk2 = s->tags; walk2 != NULL; walk2 = g_slist_next(walk2)) { printf("%s", (char *)walk2->data); if (g_slist_next(walk2) != NULL) fputc(',', stdout); g_free(walk2->data); } printf(" %s\n", s->uri); } g_slist_free(s->tags); g_free(s->uri); g_free(s); } g_slist_free(values); return 0; } static int cmd_listtags_internal(const char *expr, bool artist, bool album, bool genre) { int port, ret; const char *hostname, *password; struct mpdcron_connection *conn; if ((artist && album && genre) || (artist && album) || (artist && genre) || (album && genre)) { g_printerr("--artist, --album and --genre options are mutually exclusive\n"); return 1; } hostname = g_getenv(ENV_MPDCRON_HOST) ? g_getenv(ENV_MPDCRON_HOST) : DEFAULT_HOSTNAME; port = g_getenv(ENV_MPDCRON_PORT) ? atoi(g_getenv(ENV_MPDCRON_PORT)) : DEFAULT_PORT; password = g_getenv(ENV_MPDCRON_PASSWORD); conn = mpdcron_connection_new(hostname, port); if (conn->error != NULL) { eulog(LOG_ERR, "Failed to connect: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } if (password != NULL) { if (!mpdcron_password(conn, password)) { eulog(LOG_ERR, "Authentication failed: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } } if (artist) ret = listtags_artist(conn, expr); else if (album) ret = listtags_album(conn, expr); else if (genre) ret = listtags_genre(conn, expr); else ret = listtags_song(conn, expr); mpdcron_connection_free(conn); return ret; } int cmd_listtags(int argc, char **argv) { int opta = 0, optA = 0, optg = 0, ret; GError *error = NULL; GOptionEntry options[] = { {"artist", 'a', 0, G_OPTION_ARG_NONE, &opta, "List tags of artists instead of songs", NULL}, {"album", 'A', 0, G_OPTION_ARG_NONE, &optA, "List tags of albums instead of songs", NULL}, {"genre", 'g', 0, G_OPTION_ARG_NONE, &optg, "List tags of genres instead of songs", NULL}, { NULL, 0, 0, 0, NULL, NULL, NULL }, }; GOptionContext *ctx; ctx = g_option_context_new("[EXPRESSION]"); g_option_context_add_main_entries(ctx, options, "eugene-listtags"); g_option_context_set_summary(ctx, "eugene-listtags-"VERSION GITHEAD " - List tags of song/artist/album/genre"); g_option_context_set_description(ctx, "" "By default this command works on the current playing song.\n" "For more information about the expression syntax, see:\n" "http://www.sqlite.org/lang_expr.html"); if (!g_option_context_parse(ctx, &argc, &argv, &error)) { g_printerr("Option parsing failed: %s\n", error->message); g_error_free(error); g_option_context_free(ctx); return 1; } g_option_context_free(ctx); if (argc > 1) ret = cmd_listtags_internal(argv[1], opta, optA, optg); else ret = cmd_listtags_internal(NULL, opta, optA, optg); return ret; } mpdcron-0.3+git20110303/src/gmodule/stats/eugene-love.c000066400000000000000000000206531153374523700223670ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "eugene-defs.h" #include #include #include static int love_artist(struct mpdcron_connection *conn, bool love, const char *expr) { int changes; char *esc_artist, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_love_artist_expr(conn, love, expr, &changes)) { eulog(LOG_ERR, "Failed to %s artist: %s", love ? "love" : "hate", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_ARTIST, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no artist tag!"); mpd_song_free(song); return 1; } esc_artist = quote(mpd_song_get_tag(song, MPD_TAG_ARTIST, 0)); myexpr = g_strdup_printf("name=%s", esc_artist); g_free(esc_artist); mpd_song_free(song); if (!mpdcron_love_artist_expr(conn, love, myexpr, &changes)) { eulog(LOG_ERR, "Failed to %s current playing artist: %s", love ? "love" : "hate", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int love_album(struct mpdcron_connection *conn, bool love, const char *expr) { int changes; char *esc_album, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_love_album_expr(conn, love, expr, &changes)) { eulog(LOG_ERR, "Failed to %s album: %s", love ? "love" : "hate", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_ALBUM, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no album tag!"); mpd_song_free(song); return 1; } esc_album = quote(mpd_song_get_tag(song, MPD_TAG_ALBUM, 0)); myexpr = g_strdup_printf("name=%s", esc_album); g_free(esc_album); mpd_song_free(song); if (!mpdcron_love_album_expr(conn, love, myexpr, &changes)) { eulog(LOG_ERR, "Failed to %s current playing album: %s", love ? "love" : "hate", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int love_genre(struct mpdcron_connection *conn, bool love, const char *expr) { int changes; char *esc_genre, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_love_genre_expr(conn, love, expr, &changes)) { eulog(LOG_ERR, "Failed to %s genre: %s", love ? "love" : "hate", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_GENRE, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no genre tag!"); mpd_song_free(song); return 1; } esc_genre = quote(mpd_song_get_tag(song, MPD_TAG_GENRE, 0)); myexpr = g_strdup_printf("name=%s", esc_genre); g_free(esc_genre); mpd_song_free(song); if (!mpdcron_love_genre_expr(conn, love, myexpr, &changes)) { eulog(LOG_ERR, "Failed to %s current playing genre: %s", love ? "love" : "hate", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int love_song(struct mpdcron_connection *conn, bool love, const char *expr) { int changes; char *esc_uri, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_love_expr(conn, love, expr, &changes)) { eulog(LOG_ERR, "Failed to %s song: %s", love ? "love" : "hate", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; esc_uri = quote(mpd_song_get_uri(song)); myexpr = g_strdup_printf("uri=%s", esc_uri); g_free(esc_uri); mpd_song_free(song); if (!mpdcron_love_expr(conn, love, myexpr, &changes)) { eulog(LOG_ERR, "Failed to %s current playing song: %s", love ? "love" : "hate", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int cmd_love_internal(bool love, const char *expr, bool artist, bool album, bool genre) { int port, ret; const char *hostname, *password; struct mpdcron_connection *conn; if ((artist && album && genre) || (artist && album) || (artist && genre) || (album && genre)) { g_printerr("--artist, --album and --genre options are mutually exclusive\n"); return 1; } hostname = g_getenv(ENV_MPDCRON_HOST) ? g_getenv(ENV_MPDCRON_HOST) : DEFAULT_HOSTNAME; port = g_getenv(ENV_MPDCRON_PORT) ? atoi(g_getenv(ENV_MPDCRON_PORT)) : DEFAULT_PORT; password = g_getenv(ENV_MPDCRON_PASSWORD); conn = mpdcron_connection_new(hostname, port); if (conn->error != NULL) { eulog(LOG_ERR, "Failed to connect: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } if (password != NULL) { if (!mpdcron_password(conn, password)) { eulog(LOG_ERR, "Authentication failed: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } } if (artist) ret = love_artist(conn, love, expr); else if (album) ret = love_album(conn, love, expr); else if (genre) ret = love_genre(conn, love, expr); else ret = love_song(conn, love, expr); mpdcron_connection_free(conn); return ret; } int cmd_hate(int argc, char **argv) { int opta = 0, optA = 0, optg = 0, ret; GError *error = NULL; GOptionEntry options[] = { {"artist", 'a', 0, G_OPTION_ARG_NONE, &opta, "Hate artists instead of songs", NULL}, {"album", 'A', 0, G_OPTION_ARG_NONE, &optA, "Hate albums instead of songs", NULL}, {"genre", 'g', 0, G_OPTION_ARG_NONE, &optA, "Hate genres instead of songs", NULL}, { NULL, 0, 0, 0, NULL, NULL, NULL }, }; GOptionContext *ctx; ctx = g_option_context_new("[EXPRESSION]"); g_option_context_add_main_entries(ctx, options, "eugene-hate"); g_option_context_set_summary(ctx, "eugene-hate-"VERSION GITHEAD" - Hate song/artist/album/genre"); g_option_context_set_description(ctx, "" "By default this command works on the current playing song.\n" "For more information about the expression syntax, see:\n" "http://www.sqlite.org/lang_expr.html"); if (!g_option_context_parse(ctx, &argc, &argv, &error)) { g_printerr("Option parsing failed: %s\n", error->message); g_error_free(error); g_option_context_free(ctx); return 1; } g_option_context_free(ctx); if (argc > 1) ret = cmd_love_internal(false, argv[1], opta, optA, optg); else ret = cmd_love_internal(false, NULL, opta, optA, optg); return ret; } int cmd_love(int argc, char **argv) { int opta = 0, optA = 0, optg = 0, ret; GError *error = NULL; GOptionEntry options[] = { {"artist", 'a', 0, G_OPTION_ARG_NONE, &opta, "Love artists instead of songs", NULL}, {"album", 'A', 0, G_OPTION_ARG_NONE, &optA, "Love albums instead of songs", NULL}, {"genre", 'g', 0, G_OPTION_ARG_NONE, &optg, "Love genres instead of songs", NULL}, { NULL, 0, 0, 0, NULL, NULL, NULL }, }; GOptionContext *ctx; ctx = g_option_context_new("[EXPRESSION]"); g_option_context_add_main_entries(ctx, options, "eugene-love"); g_option_context_set_summary(ctx, "eugene-love-"VERSION GITHEAD" - Love song/artist/album/genre"); g_option_context_set_description(ctx, "" "By default this command works on the current playing song.\n" "For more information about the expression syntax, see:\n" "http://www.sqlite.org/lang_expr.html"); if (!g_option_context_parse(ctx, &argc, &argv, &error)) { g_printerr("Option parsing failed: %s\n", error->message); g_error_free(error); g_option_context_free(ctx); return 1; } g_option_context_free(ctx); if (argc > 1) ret = cmd_love_internal(true, argv[1], opta, optA, optg); else ret = cmd_love_internal(true, NULL, opta, optA, optg); return ret; } mpdcron-0.3+git20110303/src/gmodule/stats/eugene-main.c000066400000000000000000000072721153374523700223500ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "eugene-defs.h" #include #include #include #include #include #include #include #include #include static int verbosity = LOG_DEBUG; static void about(void) { printf("eugene-"VERSION GITHEAD "\n"); } G_GNUC_NORETURN static void usage(FILE *outf, int exitval) { fprintf(outf, "" "eugene -- mpdcron statistics client\n" "eugene COMMAND [OPTIONS]\n" "\n" "Commands:\n" "help Display help and exit\n" "version Display version and exit\n" "list List song/artist/album/genre\n" "listinfo List song/artist/album/genre\n" "count Change play count of song/artist/album/genre\n" "countabs Change play count (absolute fashion) of song/artist/album/genre\n" "hate Hate song/artist/album/genre\n" "love Love song/artist/album/genre\n" "kill Kill song/artist/album/genre\n" "unkill Unkill song/artist/album/genre\n" "rate Rate song/artist/album/genre\n" "rateabs Rate (absolute fashion) song/artist/album/genre\n" "addtag Add tag to song/artist/album/genre\n" "rmtag Remove tag from song/artist/album/genre\n" "listtags List tags of song/artist/album/genre\n" "\n" "See eugene COMMAND --help for more information\n"); exit(exitval); } static int run_cmd(int argc, char **argv) { if (strncmp(argv[0], "help", 4) == 0) usage(stdout, 0); else if (strncmp(argv[0], "version", 8) == 0) { about(); return 0; } else if (strncmp(argv[0], "hate", 5) == 0) return cmd_hate(argc, argv); else if (strncmp(argv[0], "love", 5) == 0) return cmd_love(argc, argv); else if (strncmp(argv[0], "kill", 5) == 0) return cmd_kill(argc, argv); else if (strncmp(argv[0], "unkill", 7) == 0) return cmd_unkill(argc, argv); else if (strncmp(argv[0], "rate", 5) == 0) return cmd_rate(argc, argv); else if (strncmp(argv[0], "rateabs", 8) == 0) return cmd_rate_absolute(argc, argv); else if (strncmp(argv[0], "list", 5) == 0) return cmd_list(argc, argv); else if (strncmp(argv[0], "listinfo", 9) == 0) return cmd_listinfo(argc, argv); else if (strncmp(argv[0], "addtag", 7) == 0) return cmd_addtag(argc, argv); else if (strncmp(argv[0], "rmtag", 6) == 0) return cmd_rmtag(argc, argv); else if (strncmp(argv[0], "listtags", 9) == 0) return cmd_listtags(argc, argv); else if (strncmp(argv[0], "count", 6) == 0) return cmd_count(argc, argv); else if (strncmp(argv[0], "countabs", 9) == 0) return cmd_count_absolute(argc, argv); fprintf(stderr, "Unknown command `%s'\n", argv[0]); usage(stderr, 1); } void eulog(int level, const char *fmt, ...) { va_list args; if (level > verbosity) return; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fputc('\n', stderr); } int main(int argc, char **argv) { g_type_init(); if (argc < 2) usage(stderr, 1); return run_cmd(argc - 1, argv + 1); } mpdcron-0.3+git20110303/src/gmodule/stats/eugene-rate-absolute.c000066400000000000000000000167601153374523700241750ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "eugene-defs.h" #include #include #include static int rate_absolute_artist(struct mpdcron_connection *conn, const char *expr, const char *rating) { int changes; char *esc_artist, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_rate_absolute_artist_expr(conn, expr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate artist: %s", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_ARTIST, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no artist tag!"); mpd_song_free(song); return 1; } esc_artist = quote(mpd_song_get_tag(song, MPD_TAG_ARTIST, 0)); myexpr = g_strdup_printf("name=%s", esc_artist); g_free(esc_artist); mpd_song_free(song); if (!mpdcron_rate_absolute_artist_expr(conn, myexpr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate current playing artist: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int rate_absolute_album(struct mpdcron_connection *conn, const char *expr, const char *rating) { int changes; char *esc_album, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_rate_absolute_album_expr(conn, expr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate album: %s", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_ALBUM, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no album tag!"); mpd_song_free(song); return 1; } esc_album = quote(mpd_song_get_tag(song, MPD_TAG_ALBUM, 0)); myexpr = g_strdup_printf("name=%s", esc_album); g_free(esc_album); mpd_song_free(song); if (!mpdcron_rate_absolute_album_expr(conn, myexpr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate current playing album: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int rate_absolute_genre(struct mpdcron_connection *conn, const char *expr, const char *rating) { int changes; char *esc_genre, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_rate_absolute_genre_expr(conn, expr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate genre: %s", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_GENRE, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no genre tag!"); mpd_song_free(song); return 1; } esc_genre = quote(mpd_song_get_tag(song, MPD_TAG_GENRE, 0)); myexpr = g_strdup_printf("name=%s", esc_genre); g_free(esc_genre); mpd_song_free(song); if (!mpdcron_rate_absolute_genre_expr(conn, myexpr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate current playing genre: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int rate_absolute_song(struct mpdcron_connection *conn, const char *expr, const char *rating) { int changes; char *esc_uri, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_rate_absolute_expr(conn, expr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate song: %s", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; esc_uri = quote(mpd_song_get_uri(song)); myexpr = g_strdup_printf("uri=%s", esc_uri); g_free(esc_uri); mpd_song_free(song); if (!mpdcron_rate_absolute_expr(conn, myexpr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate current playing song: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int cmd_rate_absolute_internal(const char *expr, const char *rating, bool artist, bool album, bool genre) { int port, ret; const char *hostname, *password; struct mpdcron_connection *conn; if ((artist && album && genre) || (artist && album) || (artist && genre) || (album && genre)) { g_printerr("--artist, --album and --genre options are mutually exclusive\n"); return 1; } hostname = g_getenv(ENV_MPDCRON_HOST) ? g_getenv(ENV_MPDCRON_HOST) : DEFAULT_HOSTNAME; port = g_getenv(ENV_MPDCRON_PORT) ? atoi(g_getenv(ENV_MPDCRON_PORT)) : DEFAULT_PORT; password = g_getenv(ENV_MPDCRON_PASSWORD); conn = mpdcron_connection_new(hostname, port); if (conn->error != NULL) { eulog(LOG_ERR, "Failed to connect: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } if (password != NULL) { if (!mpdcron_password(conn, password)) { eulog(LOG_ERR, "Authentication failed: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } } if (artist) ret = rate_absolute_artist(conn, expr, rating); else if (album) ret = rate_absolute_album(conn, expr, rating); else if (genre) ret = rate_absolute_genre(conn, expr, rating); else ret = rate_absolute_song(conn, expr, rating); mpdcron_connection_free(conn); return ret; } int cmd_rate_absolute(int argc, char **argv) { int opta = 0, optA = 0, optg = 0, ret; GError *error = NULL; GOptionEntry options[] = { {"artist", 'a', 0, G_OPTION_ARG_NONE, &opta, "Rate artists instead of songs", NULL}, {"album", 'A', 0, G_OPTION_ARG_NONE, &optA, "Rate albums instead of songs", NULL}, {"genre", 'g', 0, G_OPTION_ARG_NONE, &optg, "Rate genres instead of songs", NULL}, { NULL, 0, 0, 0, NULL, NULL, NULL }, }; GOptionContext *ctx; ctx = g_option_context_new("RATING [EXPRESSION]"); g_option_context_add_main_entries(ctx, options, "eugene-rate-absolute"); g_option_context_set_summary(ctx, "eugene-rate-absolute"VERSION GITHEAD" - Rate (absolute fashion) song/artist/album/genre"); g_option_context_set_description(ctx, "" "By default this command works on the current playing song.\n" "For more information about the expression syntax, see:\n" "http://www.sqlite.org/lang_expr.html"); /* Ignore unknown options so that the user can pass negative numbers. */ g_option_context_set_ignore_unknown_options(ctx, TRUE); if (!g_option_context_parse(ctx, &argc, &argv, &error)) { g_printerr("Option parsing failed: %s\n", error->message); g_error_free(error); g_option_context_free(ctx); return 1; } g_option_context_free(ctx); if (argc <= 1) { g_printerr("No rating given\n"); ret = 1; } else if (argc > 2) ret = cmd_rate_absolute_internal(argv[2], argv[1], opta, optA, optg); else ret = cmd_rate_absolute_internal(NULL, argv[1], opta, optA, optg); return ret; } mpdcron-0.3+git20110303/src/gmodule/stats/eugene-rate.c000066400000000000000000000164301153374523700223530ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "eugene-defs.h" #include #include #include static int rate_artist(struct mpdcron_connection *conn, const char *expr, const char *rating) { int changes; char *esc_artist, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_rate_artist_expr(conn, expr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate artist: %s", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_ARTIST, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no artist tag!"); mpd_song_free(song); return 1; } esc_artist = quote(mpd_song_get_tag(song, MPD_TAG_ARTIST, 0)); myexpr = g_strdup_printf("name=%s", esc_artist); g_free(esc_artist); mpd_song_free(song); if (!mpdcron_rate_artist_expr(conn, myexpr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate current playing artist: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int rate_album(struct mpdcron_connection *conn, const char *expr, const char *rating) { int changes; char *esc_album, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_rate_album_expr(conn, expr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate album: %s", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_ALBUM, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no album tag!"); mpd_song_free(song); return 1; } esc_album = quote(mpd_song_get_tag(song, MPD_TAG_ALBUM, 0)); myexpr = g_strdup_printf("name=%s", esc_album); g_free(esc_album); mpd_song_free(song); if (!mpdcron_rate_album_expr(conn, myexpr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate current playing album: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int rate_genre(struct mpdcron_connection *conn, const char *expr, const char *rating) { int changes; char *esc_genre, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_rate_genre_expr(conn, expr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate genre: %s", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_GENRE, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no genre tag!"); mpd_song_free(song); return 1; } esc_genre = quote(mpd_song_get_tag(song, MPD_TAG_GENRE, 0)); myexpr = g_strdup_printf("name=%s", esc_genre); g_free(esc_genre); mpd_song_free(song); if (!mpdcron_rate_genre_expr(conn, myexpr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate current playing genre: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int rate_song(struct mpdcron_connection *conn, const char *expr, const char *rating) { int changes; char *esc_uri, *myexpr; struct mpd_song *song; if (expr != NULL) { if (!mpdcron_rate_expr(conn, expr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate song: %s", conn->error->message); return 1; } } else { if ((song = load_current_song()) == NULL) return 1; esc_uri = quote(mpd_song_get_uri(song)); myexpr = g_strdup_printf("uri=%s", esc_uri); g_free(esc_uri); mpd_song_free(song); if (!mpdcron_rate_expr(conn, myexpr, rating, &changes)) { eulog(LOG_ERR, "Failed to rate current playing song: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int cmd_rate_internal(const char *expr, const char *rating, bool artist, bool album, bool genre) { int port, ret; const char *hostname, *password; struct mpdcron_connection *conn; if ((artist && album && genre) || (artist && album) || (artist && genre) || (album && genre)) { g_printerr("--artist, --album and --genre options are mutually exclusive\n"); return 1; } hostname = g_getenv(ENV_MPDCRON_HOST) ? g_getenv(ENV_MPDCRON_HOST) : DEFAULT_HOSTNAME; port = g_getenv(ENV_MPDCRON_PORT) ? atoi(g_getenv(ENV_MPDCRON_PORT)) : DEFAULT_PORT; password = g_getenv(ENV_MPDCRON_PASSWORD); conn = mpdcron_connection_new(hostname, port); if (conn->error != NULL) { eulog(LOG_ERR, "Failed to connect: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } if (password != NULL) { if (!mpdcron_password(conn, password)) { eulog(LOG_ERR, "Authentication failed: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } } if (artist) ret = rate_artist(conn, expr, rating); else if (album) ret = rate_album(conn, expr, rating); else if (genre) ret = rate_genre(conn, expr, rating); else ret = rate_song(conn, expr, rating); mpdcron_connection_free(conn); return ret; } int cmd_rate(int argc, char **argv) { int opta = 0, optA = 0, optg = 0, ret; GError *error = NULL; GOptionEntry options[] = { {"artist", 'a', 0, G_OPTION_ARG_NONE, &opta, "Rate artists instead of songs", NULL}, {"album", 'A', 0, G_OPTION_ARG_NONE, &optA, "Rate albums instead of songs", NULL}, {"genre", 'g', 0, G_OPTION_ARG_NONE, &optg, "Rate genres instead of songs", NULL}, { NULL, 0, 0, 0, NULL, NULL, NULL }, }; GOptionContext *ctx; ctx = g_option_context_new("RATING [EXPRESSION]"); g_option_context_add_main_entries(ctx, options, "eugene-rate"); g_option_context_set_summary(ctx, "eugene-rate-"VERSION GITHEAD" - Rate song/artist/album/genre"); g_option_context_set_description(ctx, "" "By default this command works on the current playing song.\n" "For more information about the expression syntax, see:\n" "http://www.sqlite.org/lang_expr.html"); /* Ignore unknown options so that the user can pass negative numbers. */ g_option_context_set_ignore_unknown_options(ctx, TRUE); if (!g_option_context_parse(ctx, &argc, &argv, &error)) { g_printerr("Option parsing failed: %s\n", error->message); g_error_free(error); g_option_context_free(ctx); return 1; } g_option_context_free(ctx); if (argc <= 1) { g_printerr("No rating given\n"); ret = 1; } else if (argc > 2) ret = cmd_rate_internal(argv[2], argv[1], opta, optA, optg); else ret = cmd_rate_internal(NULL, argv[1], opta, optA, optg); return ret; } mpdcron-0.3+git20110303/src/gmodule/stats/eugene-rmtag.c000066400000000000000000000170331153374523700225320ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "eugene-defs.h" #include #include #include static int rmtag_artist(struct mpdcron_connection *conn, const char *tag, const char *expr) { int changes; if (expr != NULL) { if (!mpdcron_rmtag_artist_expr(conn, expr, tag, &changes)) { eulog(LOG_ERR, "Failed to remove tag from artist: %s", conn->error->message); return 1; } } else { char *esc_artist, *myexpr; struct mpd_song *song; if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_ARTIST, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no artist tag!"); mpd_song_free(song); return 1; } esc_artist = quote(mpd_song_get_tag(song, MPD_TAG_ARTIST, 0)); myexpr = g_strdup_printf("name=%s", esc_artist); g_free(esc_artist); mpd_song_free(song); if (!mpdcron_rmtag_artist_expr(conn, myexpr, tag, &changes)) { eulog(LOG_ERR, "Failed to remove tag from current playing artist: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int rmtag_album(struct mpdcron_connection *conn, const char *tag, const char *expr) { int changes; if (expr != NULL) { if (!mpdcron_rmtag_album_expr(conn, expr, tag, &changes)) { eulog(LOG_ERR, "Failed to remove tag from album: %s", conn->error->message); return 1; } } else { char *esc_album, *esc_artist, *myexpr; struct mpd_song *song; if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_ARTIST, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no artist tag!"); mpd_song_free(song); return 1; } else if (mpd_song_get_tag(song, MPD_TAG_ALBUM, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no album tag!"); mpd_song_free(song); return 1; } esc_album = quote(mpd_song_get_tag(song, MPD_TAG_ALBUM, 0)); esc_artist = quote(mpd_song_get_tag(song, MPD_TAG_ARTIST, 0)); myexpr = g_strdup_printf("name=%s and artist=%s", esc_album, esc_artist); g_free(esc_album); g_free(esc_artist); mpd_song_free(song); if (!mpdcron_rmtag_album_expr(conn, myexpr, tag, &changes)) { eulog(LOG_ERR, "Failed to remove tag from current playing album: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int rmtag_genre(struct mpdcron_connection *conn, const char *tag, const char *expr) { int changes; if (expr != NULL) { if (!mpdcron_rmtag_genre_expr(conn, expr, tag, &changes)) { eulog(LOG_ERR, "Failed to remove tag from genre: %s", conn->error->message); return 1; } } else { char *esc_genre, *myexpr; struct mpd_song *song; if ((song = load_current_song()) == NULL) return 1; else if (mpd_song_get_tag(song, MPD_TAG_GENRE, 0) == NULL) { eulog(LOG_ERR, "Current playing song has no genre tag!"); mpd_song_free(song); return 1; } esc_genre = quote(mpd_song_get_tag(song, MPD_TAG_GENRE, 0)); myexpr = g_strdup_printf("name=%s", esc_genre); g_free(esc_genre); mpd_song_free(song); if (!mpdcron_rmtag_genre_expr(conn, myexpr, tag, &changes)) { eulog(LOG_ERR, "Failed to remove tag from current playing genre: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int rmtag_song(struct mpdcron_connection *conn, const char *tag, const char *expr) { int changes; if (expr != NULL) { if (!mpdcron_rmtag_expr(conn, expr, tag, &changes)) { eulog(LOG_ERR, "Failed to remove tag from song: %s", conn->error->message); return 1; } } else { char *esc_uri, *myexpr; struct mpd_song *song; if ((song = load_current_song()) == NULL) return 1; esc_uri = quote(mpd_song_get_uri(song)); myexpr = g_strdup_printf("uri=%s", esc_uri); g_free(esc_uri); mpd_song_free(song); if (!mpdcron_rmtag_expr(conn, myexpr, tag, &changes)) { eulog(LOG_ERR, "Failed to remove tag from current playing song: %s", conn->error->message); g_free(myexpr); return 1; } g_free(myexpr); } printf("Modified %d entries\n", changes); return 0; } static int cmd_rmtag_internal(const char *tag, const char *expr, bool artist, bool album, bool genre) { int port, ret; const char *hostname, *password; struct mpdcron_connection *conn; if ((artist && album && genre) || (artist && album) || (artist && genre) || (album && genre)) { g_printerr("--artist, --album and --genre options are mutually exclusive\n"); return 1; } hostname = g_getenv(ENV_MPDCRON_HOST) ? g_getenv(ENV_MPDCRON_HOST) : DEFAULT_HOSTNAME; port = g_getenv(ENV_MPDCRON_PORT) ? atoi(g_getenv(ENV_MPDCRON_PORT)) : DEFAULT_PORT; password = g_getenv(ENV_MPDCRON_PASSWORD); conn = mpdcron_connection_new(hostname, port); if (conn->error != NULL) { eulog(LOG_ERR, "Failed to connect: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } if (password != NULL) { if (!mpdcron_password(conn, password)) { eulog(LOG_ERR, "Authentication failed: %s", conn->error->message); mpdcron_connection_free(conn); return 1; } } if (artist) ret = rmtag_artist(conn, tag, expr); else if (album) ret = rmtag_album(conn, tag, expr); else if (genre) ret = rmtag_genre(conn, tag, expr); else ret = rmtag_song(conn, tag, expr); mpdcron_connection_free(conn); return ret; } int cmd_rmtag(int argc, char **argv) { int opta = 0, optA = 0, optg = 0, ret; GError *error = NULL; GOptionEntry options[] = { {"artist", 'a', 0, G_OPTION_ARG_NONE, &opta, "Remove tag from artists instead of songs", NULL}, {"album", 'A', 0, G_OPTION_ARG_NONE, &optA, "Remove tag from albums instead of songs", NULL}, {"genre", 'g', 0, G_OPTION_ARG_NONE, &optg, "Remove tag from genres instead of songs", NULL}, { NULL, 0, 0, 0, NULL, NULL, NULL }, }; GOptionContext *ctx; ctx = g_option_context_new("TAG [EXPRESSION]"); g_option_context_add_main_entries(ctx, options, "eugene-rmtag"); g_option_context_set_summary(ctx, "eugene-rmtag-"VERSION GITHEAD" - Remove tag from song/artist/album/genre"); g_option_context_set_description(ctx, "" "By default this command works on the current playing song.\n" "For more information about the expression syntax, see:\n" "http://www.sqlite.org/lang_expr.html"); if (!g_option_context_parse(ctx, &argc, &argv, &error)) { g_printerr("Option parsing failed: %s\n", error->message); g_error_free(error); g_option_context_free(ctx); return 1; } g_option_context_free(ctx); if (argc <= 1) { g_printerr("No tag given\n"); return 1; } if (argc > 2) ret = cmd_rmtag_internal(argv[1], argv[2], opta, optA, optg); else ret = cmd_rmtag_internal(argv[1], NULL, opta, optA, optg); return ret; } mpdcron-0.3+git20110303/src/gmodule/stats/eugene-utils.c000066400000000000000000000050201153374523700225510ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "eugene-defs.h" #include #include #include #include char * quote(const char *src) { const char *p; GString *dest; g_return_val_if_fail(src != NULL, NULL); dest = g_string_new("'"); p = src; while (*p != 0) { if (*p == '\'') g_string_append(dest, "''"); else g_string_append_c(dest, *p); ++p; } g_string_append_c(dest, '\''); return g_string_free(dest, FALSE); } struct mpd_song * load_current_song(void) { int port; const char *hostname, *password; struct mpd_connection *conn; struct mpd_song *song; hostname = g_getenv(ENV_MPD_HOST) ? g_getenv(ENV_MPD_HOST) : "localhost"; port = g_getenv(ENV_MPD_PORT) ? atoi(g_getenv(ENV_MPD_PORT)) : 6600; password = g_getenv(ENV_MPD_PASSWORD); if ((conn = mpd_connection_new(hostname, port, 0)) == NULL) { eulog(LOG_ERR, "Error creating mpd connection: out of memory"); return NULL; } if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) { eulog(LOG_ERR, "Failed to connect to Mpd: %s", mpd_connection_get_error_message(conn)); mpd_connection_free(conn); return NULL; } if (password != NULL) { if (!mpd_run_password(conn, password)) { eulog(LOG_ERR, "Authentication failed: %s", mpd_connection_get_error_message(conn)); mpd_connection_free(conn); return NULL; } } if ((song = mpd_run_current_song(conn)) == NULL) { if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) { eulog(LOG_ERR, "Failed to get current song: %s", mpd_connection_get_error_message(conn)); mpd_connection_free(conn); return NULL; } eulog(LOG_WARNING, "No song playing at the moment"); mpd_connection_free(conn); return NULL; } mpd_connection_free(conn); return song; } mpdcron-0.3+git20110303/src/gmodule/stats/homescrape.in000066400000000000000000000067521153374523700224720ustar00rootroot00000000000000#!/usr/bin/env ruby # vim: set sw=2 sts=2 tw=100 et nowrap fenc=utf-8 : # Copyright 2010 Ali Polatel # Distributed under the terms of the GNU General Public License v2 %w{getoptlong net/http time uri rubygems nokogiri}.each {|m| require m } begin require 'chronic' has_chronic = true rescue LoadError has_chronic = false end MYNAME = File.basename $0, ".rb" MYVERSION = "@VERSION@" + "@GITHEAD@" class UserNotFound < StandardError; end class Scraper LASTFM_URL = 'http://www.last.fm/user/%s/tracks' LASTFM_DATE_FORMAT = '%Y-%m-%dT%H:%M:%SZ' attr_accessor :username, :url def initialize username @username = username @url = sprintf(LASTFM_URL, username) # Set up proxy @proxy_url = URI.parse(ENV['http_proxy']) if ENV['http_proxy'] @proxy_host = @proxy_url.host if @proxy_url and @proxy_url.host @proxy_port = @proxy_url.port if @proxy_url and @proxy_url.port @proxy_user, @proxy_pass = @proxy_url.userinfo.split(/:/) if @proxy_url and @proxy_url.userinfo end def fetch since, page=1, &block uri = URI.parse(@url + "?page=#{page}") req = Net::HTTP::Get.new(uri.request_uri) res = Net::HTTP::Proxy(@proxy_host, @proxy_port, @proxy_user, @proxy_pass).start(uri.host, uri.port) {|http| http.request(req) } data = res.body raise UserNotFound if data =~ /User not found/ doc = Nokogiri::HTML data if page == 1 if doc.css('a.lastpage').length != 0 @lastpage = doc.css('a.lastpage')[0].content.to_i else @lastpage = 1 end end tags = doc.xpath(<<-EOF) //tr[ td[@class="subjectCell"] and td[@class="lovedCell"] and td[@class="dateCell last"] ] EOF tags.each do |tag| subjectCell = tag.children[2] lovedCell = tag.children[4] dateCell = tag.children[8] artist = subjectCell.children[1].content title = subjectCell.children[3].content love = lovedCell.children[1] ? true : false date = Date.strptime(dateCell.at('//abbr/@title').to_s, LASTFM_DATE_FORMAT) return if since > date block.call artist, title, love end if page <= @lastpage fetch since, page + 1, &block end end end def usage out, code out.puts < * Based in part upon mpd which is: * Copyright (C) 2003-2009 The Music Player Daemon Project * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "stats-defs.h" #include "tokenizer.h" #include #include #include #include #include #include #define PROTOCOL_OK "OK" #define PROCOTOL_ACK "ACK" /* if min: -1 don't check args * * if max: -1 no max args */ struct command { const char *cmd; unsigned permission; int min; int max; enum command_return (*handler)(struct client *client, int argc, char **argv); }; static const char *current_command; static int command_authorizer(void *userdata, int what, G_GNUC_UNUSED const char *arg1, const char *arg2, G_GNUC_UNUSED const char *dbname, G_GNUC_UNUSED const char *view) { struct client *client = (struct client *) userdata; switch (what) { case SQLITE_READ: case SQLITE_SELECT: return SQLITE_OK; case SQLITE_FUNCTION: /* We allow everything but load_extension() */ if (strcmp(arg2, "load_extension") == 0) return SQLITE_DENY; return SQLITE_OK; case SQLITE_UPDATE: if (client->perm & PERMISSION_UPDATE) return SQLITE_OK; /* fall through */ default: return SQLITE_DENY; } } static void command_ok(struct client *client) { g_debug("[%d]> "PROTOCOL_OK, client->id); server_schedule_write(client, PROTOCOL_OK"\n", sizeof(PROTOCOL_OK"\n") - 1); server_flush_write(client); } static void command_putv(struct client *client, const char *fmt, va_list args) { GString *message; g_assert(client != NULL); message = g_string_new(""); g_string_append_vprintf(message, fmt, args); g_debug("[%d]> %s", client->id, message->str); g_string_append_c(message, '\n'); server_schedule_write(client, message->str, message->len); g_string_free(message, TRUE); } G_GNUC_PRINTF(2, 3) static void command_puts(struct client *client, const char *fmt, ...) { va_list args; va_start(args, fmt); command_putv(client, fmt, args); va_end(args); } static void command_error_v(struct client *client, enum ack error, const char *fmt, va_list args) { GString *message; g_assert(client != NULL); g_assert(current_command != NULL); message = g_string_new(""); g_string_printf(message, PROCOTOL_ACK" [%i] {%s} ", (int)error, current_command); g_string_append_vprintf(message, fmt, args); g_debug("[%d]> %s", client->id, message->str); g_string_append_c(message, '\n'); server_schedule_write(client, message->str, message->len); g_string_free(message, TRUE); current_command = NULL; server_flush_write(client); } G_GNUC_PRINTF(3, 4) static void command_error(struct client *client, enum ack error, const char *fmt, ...) { va_list args; va_start(args, fmt); command_error_v(client, error, fmt, args); va_end(args); } G_GNUC_UNUSED static bool check_bool(struct client *client, bool *value_r, const char *s) { long value; char *endptr; value = strtol(s, &endptr, 10); if (*endptr != 0 || (value != 0 && value != 1)) { command_error(client, ACK_ERROR_ARG, "Boolean (0/1) expected: %s", s); return false; } *value_r = !!value; return true; } static enum command_return handle_kill(struct client *client, int argc, char **argv) { bool kkill; int changes; GError *error; g_assert(argc == 2); kkill = (strcmp(argv[0], "kill") == 0); error = NULL; if (!db_kill_song_expr(argv[1], kkill, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_kill_album(struct client *client, int argc, char **argv) { bool kkill; int changes; GError *error; g_assert(argc == 2); kkill = (strcmp(argv[0], "kill_album") == 0); error = NULL; if (!db_kill_album_expr(argv[1], kkill, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_kill_artist(struct client *client, int argc, char **argv) { bool kkill; int changes; GError *error; g_assert(argc == 2); kkill = (strcmp(argv[0], "kill_artist") == 0); error = NULL; if (!db_kill_artist_expr(argv[1], kkill, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_kill_genre(struct client *client, int argc, char **argv) { bool kkill; int changes; GError *error; g_assert(argc == 2); kkill = (strcmp(argv[0], "kill_genre") == 0); error = NULL; if (!db_kill_genre_expr(argv[1], kkill, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_list(struct client *client, int argc, char **argv) { GError *error; GSList *values, *walk; g_assert(argc == 2); error = NULL; values = NULL; if (!db_list_song_expr(argv[1], &values, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct db_song_data *song = (struct db_song_data *) walk->data; command_puts(client, "id: %d", song->id); command_puts(client, "file: %s", song->uri); db_song_data_free(song); } g_slist_free(values); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_list_artist(struct client *client, int argc, char **argv) { GError *error; GSList *values, *walk; g_assert(argc == 2); error = NULL; values = NULL; if (!db_list_artist_expr(argv[1], &values, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct db_generic_data *data = (struct db_generic_data *) walk->data; command_puts(client, "id: %d", data->id); command_puts(client, "Artist: %s", data->name); db_generic_data_free(data); } g_slist_free(values); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_list_album(struct client *client, int argc, char **argv) { GError *error; GSList *values, *walk; g_assert(argc == 2); error = NULL; values = NULL; if (!db_list_album_expr(argv[1], &values, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct db_generic_data *data = (struct db_generic_data *) walk->data; command_puts(client, "id: %d", data->id); command_puts(client, "Album: %s", data->name); command_puts(client, "Artist: %s", data->artist); db_generic_data_free(data); } g_slist_free(values); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_list_genre(struct client *client, int argc, char **argv) { GError *error; GSList *values, *walk; g_assert(argc == 2); error = NULL; values = NULL; if (!db_list_genre_expr(argv[1], &values, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct db_generic_data *data = (struct db_generic_data *) walk->data; command_puts(client, "id: %d", data->id); command_puts(client, "Genre: %s", data->name); db_generic_data_free(data); } g_slist_free(values); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_listinfo(struct client *client, int argc, char **argv) { GError *error; GSList *values, *walk; g_assert(argc == 2); error = NULL; values = NULL; if (!db_listinfo_song_expr(argv[1], &values, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct db_song_data *song = (struct db_song_data *) walk->data; command_puts(client, "id: %d", song->id); command_puts(client, "file: %s", song->uri); command_puts(client, "Play Count: %d", song->play_count); command_puts(client, "Love: %d", song->love); command_puts(client, "Kill: %d", song->kill); command_puts(client, "Rating: %d", song->rating); db_song_data_free(song); } g_slist_free(values); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_listinfo_artist(struct client *client, int argc, char **argv) { GError *error; GSList *values, *walk; g_assert(argc == 2); error = NULL; values = NULL; if (!db_listinfo_artist_expr(argv[1], &values, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct db_generic_data *data = (struct db_generic_data *) walk->data; command_puts(client, "id: %d", data->id); command_puts(client, "Artist: %s", data->name); command_puts(client, "Play Count: %d", data->play_count); command_puts(client, "Love: %d", data->love); command_puts(client, "Kill: %d", data->kill); command_puts(client, "Rating: %d", data->rating); db_generic_data_free(data); } g_slist_free(values); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_listinfo_album(struct client *client, int argc, char **argv) { GError *error; GSList *values, *walk; g_assert(argc == 2); error = NULL; values = NULL; if (!db_listinfo_album_expr(argv[1], &values, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct db_generic_data *data = (struct db_generic_data *) walk->data; command_puts(client, "id: %d", data->id); command_puts(client, "Album: %s", data->name); command_puts(client, "Artist: %s", data->artist); command_puts(client, "Play Count: %d", data->play_count); command_puts(client, "Love: %d", data->love); command_puts(client, "Kill: %d", data->kill); command_puts(client, "Rating: %d", data->rating); db_generic_data_free(data); } g_slist_free(values); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_listinfo_genre(struct client *client, int argc, char **argv) { GError *error; GSList *values, *walk; g_assert(argc == 2); error = NULL; values = NULL; if (!db_listinfo_genre_expr(argv[1], &values, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct db_generic_data *data = (struct db_generic_data *) walk->data; command_puts(client, "id: %d", data->id); command_puts(client, "Genre: %s", data->name); command_puts(client, "Play Count: %d", data->play_count); command_puts(client, "Love: %d", data->love); command_puts(client, "Kill: %d", data->kill); command_puts(client, "Rating: %d", data->rating); db_generic_data_free(data); } g_slist_free(values); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_love(struct client *client, int argc, char **argv) { bool love; int changes; GError *error; g_assert(argc == 2); love = (strcmp(argv[0], "love") == 0); error = NULL; if (!db_love_song_expr(argv[1], love, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_love_album(struct client *client, int argc, char **argv) { bool love; int changes; GError *error; g_assert(argc == 2); love = (strcmp(argv[0], "love_album") == 0); error = NULL; if (!db_love_album_expr(argv[1], love, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_love_artist(struct client *client, int argc, char **argv) { bool love; int changes; GError *error; g_assert(argc == 2); love = (strcmp(argv[0], "love_artist") == 0); error = NULL; if (!db_love_artist_expr(argv[1], love, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_love_genre(struct client *client, int argc, char **argv) { bool love; int changes; GError *error; g_assert(argc == 2); love = (strcmp(argv[0], "love_genre") == 0); error = NULL; if (!db_love_genre_expr(argv[1], love, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_rate(struct client *client, int argc, char **argv) { int changes; long rating; char *endptr; GError *error; g_assert(argc == 3); /* Convert second argument to number */ errno = 0; endptr = NULL; rating = strtol(argv[2], &endptr, 10); if (errno != 0) { command_error(client, ACK_ERROR_ARG, "Failed to convert to number: %s", g_strerror(errno)); return COMMAND_RETURN_ERROR; } else if (endptr == argv[2]) { command_error(client, ACK_ERROR_ARG, "No digits found"); return COMMAND_RETURN_ERROR; } else if (rating > INT_MAX || rating < INT_MIN) { command_error(client, ACK_ERROR_ARG, "Number too %s", (rating > INT_MAX) ? "big" : "small"); return COMMAND_RETURN_ERROR; } error = NULL; if (!db_rate_song_expr(argv[1], (int)rating, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_rate_artist(struct client *client, int argc, char **argv) { int changes; long rating; char *endptr; GError *error; g_assert(argc == 3); /* Convert second argument to number */ errno = 0; endptr = NULL; rating = strtol(argv[2], &endptr, 10); if (errno != 0) { command_error(client, ACK_ERROR_ARG, "Failed to convert to number: %s", g_strerror(errno)); return COMMAND_RETURN_ERROR; } else if (endptr == argv[2]) { command_error(client, ACK_ERROR_ARG, "No digits found"); return COMMAND_RETURN_ERROR; } else if (rating > INT_MAX || rating < INT_MIN) { command_error(client, ACK_ERROR_ARG, "Number too %s", (rating > INT_MAX) ? "big" : "small"); return COMMAND_RETURN_ERROR; } error = NULL; if (!db_rate_artist_expr(argv[1], (int)rating, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_rate_album(struct client *client, int argc, char **argv) { int changes; long rating; char *endptr; GError *error; g_assert(argc == 3); /* Convert second argument to number */ errno = 0; endptr = NULL; rating = strtol(argv[2], &endptr, 10); if (errno != 0) { command_error(client, ACK_ERROR_ARG, "Failed to convert to number: %s", g_strerror(errno)); return COMMAND_RETURN_ERROR; } else if (endptr == argv[2]) { command_error(client, ACK_ERROR_ARG, "No digits found"); return COMMAND_RETURN_ERROR; } else if (rating > INT_MAX || rating < INT_MIN) { command_error(client, ACK_ERROR_ARG, "Number too %s", (rating > INT_MAX) ? "big" : "small"); return COMMAND_RETURN_ERROR; } error = NULL; if (!db_rate_album_expr(argv[1], (int)rating, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_rate_genre(struct client *client, int argc, char **argv) { int changes; long rating; char *endptr; GError *error; g_assert(argc == 3); /* Convert second argument to number */ errno = 0; endptr = NULL; rating = strtol(argv[2], &endptr, 10); if (errno != 0) { command_error(client, ACK_ERROR_ARG, "Failed to convert to number: %s", g_strerror(errno)); return COMMAND_RETURN_ERROR; } else if (endptr == argv[2]) { command_error(client, ACK_ERROR_ARG, "No digits found"); return COMMAND_RETURN_ERROR; } else if (rating > INT_MAX || rating < INT_MIN) { command_error(client, ACK_ERROR_ARG, "Number too %s", (rating > INT_MAX) ? "big" : "small"); return COMMAND_RETURN_ERROR; } error = NULL; if (!db_rate_genre_expr(argv[1], (int)rating, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_rate_absolute(struct client *client, int argc, char **argv) { int changes; long rating; char *endptr; GError *error; g_assert(argc == 3); /* Convert second argument to number */ errno = 0; endptr = NULL; rating = strtol(argv[2], &endptr, 10); if (errno != 0) { command_error(client, ACK_ERROR_ARG, "Failed to convert to number: %s", g_strerror(errno)); return COMMAND_RETURN_ERROR; } else if (endptr == argv[2]) { command_error(client, ACK_ERROR_ARG, "No digits found"); return COMMAND_RETURN_ERROR; } else if (rating > INT_MAX || rating < INT_MIN) { command_error(client, ACK_ERROR_ARG, "Number too %s", (rating > INT_MAX) ? "big" : "small"); return COMMAND_RETURN_ERROR; } error = NULL; if (!db_rate_absolute_song_expr(argv[1], (int)rating, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_rate_absolute_artist(struct client *client, int argc, char **argv) { int changes; long rating; char *endptr; GError *error; g_assert(argc == 3); /* Convert second argument to number */ errno = 0; endptr = NULL; rating = strtol(argv[2], &endptr, 10); if (errno != 0) { command_error(client, ACK_ERROR_ARG, "Failed to convert to number: %s", g_strerror(errno)); return COMMAND_RETURN_ERROR; } else if (endptr == argv[2]) { command_error(client, ACK_ERROR_ARG, "No digits found"); return COMMAND_RETURN_ERROR; } else if (rating > INT_MAX || rating < INT_MIN) { command_error(client, ACK_ERROR_ARG, "Number too %s", (rating > INT_MAX) ? "big" : "small"); return COMMAND_RETURN_ERROR; } error = NULL; if (!db_rate_absolute_artist_expr(argv[1], (int)rating, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_rate_absolute_album(struct client *client, int argc, char **argv) { int changes; long rating; char *endptr; GError *error; g_assert(argc == 3); /* Convert second argument to number */ errno = 0; endptr = NULL; rating = strtol(argv[2], &endptr, 10); if (errno != 0) { command_error(client, ACK_ERROR_ARG, "Failed to convert to number: %s", g_strerror(errno)); return COMMAND_RETURN_ERROR; } else if (endptr == argv[2]) { command_error(client, ACK_ERROR_ARG, "No digits found"); return COMMAND_RETURN_ERROR; } else if (rating > INT_MAX || rating < INT_MIN) { command_error(client, ACK_ERROR_ARG, "Number too %s", (rating > INT_MAX) ? "big" : "small"); return COMMAND_RETURN_ERROR; } error = NULL; if (!db_rate_absolute_album_expr(argv[1], (int)rating, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_rate_absolute_genre(struct client *client, int argc, char **argv) { int changes; long rating; char *endptr; GError *error; g_assert(argc == 3); /* Convert second argument to number */ errno = 0; endptr = NULL; rating = strtol(argv[2], &endptr, 10); if (errno != 0) { command_error(client, ACK_ERROR_ARG, "Failed to convert to number: %s", g_strerror(errno)); return COMMAND_RETURN_ERROR; } else if (endptr == argv[2]) { command_error(client, ACK_ERROR_ARG, "No digits found"); return COMMAND_RETURN_ERROR; } else if (rating > INT_MAX || rating < INT_MIN) { command_error(client, ACK_ERROR_ARG, "Number too %s", (rating > INT_MAX) ? "big" : "small"); return COMMAND_RETURN_ERROR; } error = NULL; if (!db_rate_absolute_genre_expr(argv[1], (int)rating, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_count_absolute(struct client *client, int argc, char **argv) { int changes; long rating; char *endptr; GError *error; g_assert(argc == 3); /* Convert second argument to number */ errno = 0; endptr = NULL; rating = strtol(argv[2], &endptr, 10); if (errno != 0) { command_error(client, ACK_ERROR_ARG, "Failed to convert to number: %s", g_strerror(errno)); return COMMAND_RETURN_ERROR; } else if (endptr == argv[2]) { command_error(client, ACK_ERROR_ARG, "No digits found"); return COMMAND_RETURN_ERROR; } else if (rating > INT_MAX || rating < INT_MIN) { command_error(client, ACK_ERROR_ARG, "Number too %s", (rating > INT_MAX) ? "big" : "small"); return COMMAND_RETURN_ERROR; } error = NULL; if (!db_count_absolute_song_expr(argv[1], (int)rating, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_count_absolute_artist(struct client *client, int argc, char **argv) { int changes; long rating; char *endptr; GError *error; g_assert(argc == 3); /* Convert second argument to number */ errno = 0; endptr = NULL; rating = strtol(argv[2], &endptr, 10); if (errno != 0) { command_error(client, ACK_ERROR_ARG, "Failed to convert to number: %s", g_strerror(errno)); return COMMAND_RETURN_ERROR; } else if (endptr == argv[2]) { command_error(client, ACK_ERROR_ARG, "No digits found"); return COMMAND_RETURN_ERROR; } else if (rating > INT_MAX || rating < INT_MIN) { command_error(client, ACK_ERROR_ARG, "Number too %s", (rating > INT_MAX) ? "big" : "small"); return COMMAND_RETURN_ERROR; } error = NULL; if (!db_count_absolute_artist_expr(argv[1], (int)rating, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_count_absolute_album(struct client *client, int argc, char **argv) { int changes; long rating; char *endptr; GError *error; g_assert(argc == 3); /* Convert second argument to number */ errno = 0; endptr = NULL; rating = strtol(argv[2], &endptr, 10); if (errno != 0) { command_error(client, ACK_ERROR_ARG, "Failed to convert to number: %s", g_strerror(errno)); return COMMAND_RETURN_ERROR; } else if (endptr == argv[2]) { command_error(client, ACK_ERROR_ARG, "No digits found"); return COMMAND_RETURN_ERROR; } else if (rating > INT_MAX || rating < INT_MIN) { command_error(client, ACK_ERROR_ARG, "Number too %s", (rating > INT_MAX) ? "big" : "small"); return COMMAND_RETURN_ERROR; } error = NULL; if (!db_count_absolute_album_expr(argv[1], (int)rating, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_count_absolute_genre(struct client *client, int argc, char **argv) { int changes; long rating; char *endptr; GError *error; g_assert(argc == 3); /* Convert second argument to number */ errno = 0; endptr = NULL; rating = strtol(argv[2], &endptr, 10); if (errno != 0) { command_error(client, ACK_ERROR_ARG, "Failed to convert to number: %s", g_strerror(errno)); return COMMAND_RETURN_ERROR; } else if (endptr == argv[2]) { command_error(client, ACK_ERROR_ARG, "No digits found"); return COMMAND_RETURN_ERROR; } else if (rating > INT_MAX || rating < INT_MIN) { command_error(client, ACK_ERROR_ARG, "Number too %s", (rating > INT_MAX) ? "big" : "small"); return COMMAND_RETURN_ERROR; } error = NULL; if (!db_count_absolute_genre_expr(argv[1], (int)rating, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_addtag(struct client *client, int argc, char **argv) { int changes; GError *error; g_assert(argc == 3); error = NULL; if (!db_add_song_tag_expr(argv[1], argv[2], &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_addtag_album(struct client *client, int argc, char **argv) { int changes; GError *error; g_assert(argc == 3); error = NULL; if (!db_add_album_tag_expr(argv[1], argv[2], &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_addtag_artist(struct client *client, int argc, char **argv) { int changes; GError *error; g_assert(argc == 3); error = NULL; if (!db_add_artist_tag_expr(argv[1], argv[2], &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_addtag_genre(struct client *client, int argc, char **argv) { int changes; GError *error; g_assert(argc == 3); error = NULL; if (!db_add_genre_tag_expr(argv[1], argv[2], &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_rmtag(struct client *client, int argc, char **argv) { int changes; GError *error; g_assert(argc == 3); error = NULL; if (!db_remove_song_tag_expr(argv[1], argv[2], &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_rmtag_album(struct client *client, int argc, char **argv) { int changes; GError *error; g_assert(argc == 3); error = NULL; if (!db_remove_album_tag_expr(argv[1], argv[2], &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_rmtag_artist(struct client *client, int argc, char **argv) { int changes; GError *error; g_assert(argc == 3); error = NULL; if (!db_remove_artist_tag_expr(argv[1], argv[2], &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_rmtag_genre(struct client *client, int argc, char **argv) { int changes; GError *error; g_assert(argc == 3); error = NULL; if (!db_remove_genre_tag_expr(argv[1], argv[2], &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_listtags(struct client *client, int argc, char **argv) { GError *error; GSList *values, *walk; g_assert(argc == 2); error = NULL; values = NULL; if (!db_list_song_tag_expr(argv[1], &values, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct db_song_data *song = (struct db_song_data *)walk->data; command_puts(client, "id: %d", song->id); command_puts(client, "file: %s", song->uri); for (unsigned int i = 0; song->tags[i] != NULL; i++) { if (song->tags[i][0] != '\0') command_puts(client, "Tag: %s", song->tags[i]); } db_song_data_free(song); } g_slist_free(values); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_listtags_album(struct client *client, int argc, char **argv) { GError *error; GSList *values, *walk; g_assert(argc == 2); error = NULL; values = NULL; if (!db_list_album_tag_expr(argv[1], &values, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct db_generic_data *data = (struct db_generic_data *)walk->data; command_puts(client, "id: %d", data->id); command_puts(client, "Album: %s", data->name); command_puts(client, "Artist: %s", data->artist); for (unsigned int i = 0; data->tags[i] != NULL; i++) { if (data->tags[i][0] != '\0') command_puts(client, "Tag: %s", data->tags[i]); } db_generic_data_free(data); } g_slist_free(values); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_listtags_artist(struct client *client, int argc, char **argv) { GError *error; GSList *values, *walk; g_assert(argc == 2); error = NULL; values = NULL; if (!db_list_artist_tag_expr(argv[1], &values, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct db_generic_data *data = (struct db_generic_data *)walk->data; command_puts(client, "id: %d", data->id); command_puts(client, "Artist: %s", data->name); for (unsigned int i = 0; data->tags[i] != NULL; i++) { if (data->tags[i][0] != '\0') command_puts(client, "Tag: %s", data->tags[i]); } db_generic_data_free(data); } g_slist_free(values); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_listtags_genre(struct client *client, int argc, char **argv) { GError *error; GSList *values, *walk; g_assert(argc == 2); error = NULL; values = NULL; if (!db_list_genre_tag_expr(argv[1], &values, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } for (walk = values; walk != NULL; walk = g_slist_next(walk)) { struct db_generic_data *data = (struct db_generic_data *)walk->data; command_puts(client, "id: %d", data->id); command_puts(client, "Genre: %s", data->name); for (unsigned int i = 0; data->tags[i] != NULL; i++) { if (data->tags[i][0] != '\0') command_puts(client, "Tag: %s", data->tags[i]); } db_generic_data_free(data); } g_slist_free(values); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_count(struct client *client, int argc, char **argv) { int changes; long count; char *endptr; GError *error; g_assert(argc == 3); /* Convert second argument to number */ errno = 0; endptr = NULL; count = strtol(argv[2], &endptr, 10); if (errno != 0) { command_error(client, ACK_ERROR_ARG, "Failed to convert to number: %s", g_strerror(errno)); return COMMAND_RETURN_ERROR; } else if (endptr == argv[2]) { command_error(client, ACK_ERROR_ARG, "No digits found"); return COMMAND_RETURN_ERROR; } else if (count > INT_MAX || count < INT_MIN) { command_error(client, ACK_ERROR_ARG, "Number too %s", (count > INT_MAX) ? "big" : "small"); return COMMAND_RETURN_ERROR; } error = NULL; if (!db_count_song_expr(argv[1], (int)count, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_count_artist(struct client *client, int argc, char **argv) { int changes; long count; char *endptr; GError *error; g_assert(argc == 3); /* Convert second argument to number */ errno = 0; endptr = NULL; count = strtol(argv[2], &endptr, 10); if (errno != 0) { command_error(client, ACK_ERROR_ARG, "Failed to convert to number: %s", g_strerror(errno)); return COMMAND_RETURN_ERROR; } else if (endptr == argv[2]) { command_error(client, ACK_ERROR_ARG, "No digits found"); return COMMAND_RETURN_ERROR; } else if (count > INT_MAX || count < INT_MIN) { command_error(client, ACK_ERROR_ARG, "Number too %s", (count > INT_MAX) ? "big" : "small"); return COMMAND_RETURN_ERROR; } error = NULL; if (!db_count_artist_expr(argv[1], (int)count, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_count_album(struct client *client, int argc, char **argv) { int changes; long count; char *endptr; GError *error; g_assert(argc == 3); /* Convert second argument to number */ errno = 0; endptr = NULL; count = strtol(argv[2], &endptr, 10); if (errno != 0) { command_error(client, ACK_ERROR_ARG, "Failed to convert to number: %s", g_strerror(errno)); return COMMAND_RETURN_ERROR; } else if (endptr == argv[2]) { command_error(client, ACK_ERROR_ARG, "No digits found"); return COMMAND_RETURN_ERROR; } else if (count > INT_MAX || count < INT_MIN) { command_error(client, ACK_ERROR_ARG, "Number too %s", (count > INT_MAX) ? "big" : "small"); return COMMAND_RETURN_ERROR; } error = NULL; if (!db_count_album_expr(argv[1], (int)count, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_count_genre(struct client *client, int argc, char **argv) { int changes; long count; char *endptr; GError *error; g_assert(argc == 3); /* Convert second argument to number */ errno = 0; endptr = NULL; count = strtol(argv[2], &endptr, 10); if (errno != 0) { command_error(client, ACK_ERROR_ARG, "Failed to convert to number: %s", g_strerror(errno)); return COMMAND_RETURN_ERROR; } else if (endptr == argv[2]) { command_error(client, ACK_ERROR_ARG, "No digits found"); return COMMAND_RETURN_ERROR; } else if (count > INT_MAX || count < INT_MIN) { command_error(client, ACK_ERROR_ARG, "Number too %s", (count > INT_MAX) ? "big" : "small"); return COMMAND_RETURN_ERROR; } error = NULL; if (!db_count_genre_expr(argv[1], (int)count, &changes, &error)) { command_error(client, error->code, "%s", error->message); g_error_free(error); return COMMAND_RETURN_ERROR; } command_puts(client, "changes: %d", changes); command_ok(client); return COMMAND_RETURN_OK; } static enum command_return handle_password(struct client *client, G_GNUC_UNUSED int argc, char **argv) { gpointer perm = g_hash_table_lookup(globalconf.passwords, argv[1]); if (perm != NULL) { client->perm |= GPOINTER_TO_INT(perm); command_ok(client); return COMMAND_RETURN_OK; } command_error(client, ACK_ERROR_PASSWORD, "Invalid password"); return COMMAND_RETURN_ERROR; } /* This has to be sorted! */ static const struct command commands[] = { { "addtag", PERMISSION_UPDATE, 2, 2, handle_addtag }, { "addtag_album", PERMISSION_UPDATE, 2, 2, handle_addtag_album }, { "addtag_artist", PERMISSION_UPDATE, 2, 2, handle_addtag_artist }, { "addtag_genre", PERMISSION_UPDATE, 2, 2, handle_addtag_genre }, { "count", PERMISSION_UPDATE, 2, 2, handle_count }, { "count_absolute", PERMISSION_UPDATE, 2, 2, handle_count_absolute }, { "count_absolute_album", PERMISSION_UPDATE, 2, 2, handle_count_absolute_album }, { "count_absolute_artist", PERMISSION_UPDATE, 2, 2, handle_count_absolute_artist }, { "count_absolute_genre", PERMISSION_UPDATE, 2, 2, handle_count_absolute_genre }, { "count_album", PERMISSION_UPDATE, 2, 2, handle_count_album }, { "count_artist", PERMISSION_UPDATE, 2, 2, handle_count_artist }, { "count_genre", PERMISSION_UPDATE, 2, 2, handle_count_genre }, { "hate", PERMISSION_UPDATE, 1, 1, handle_love }, { "hate_album", PERMISSION_UPDATE, 1, 1, handle_love_album }, { "hate_artist", PERMISSION_UPDATE, 1, 1, handle_love_artist }, { "hate_genre", PERMISSION_UPDATE, 1, 1, handle_love_genre }, { "kill", PERMISSION_UPDATE, 1, 1, handle_kill }, { "kill_album", PERMISSION_UPDATE, 1, 1, handle_kill_album }, { "kill_artist", PERMISSION_UPDATE, 1, 1, handle_kill_artist }, { "kill_genre", PERMISSION_UPDATE, 1, 1, handle_kill_genre }, { "list", PERMISSION_SELECT, 1, 1, handle_list }, { "list_album", PERMISSION_SELECT, 1, 1, handle_list_album }, { "list_artist", PERMISSION_SELECT, 1, 1, handle_list_artist }, { "list_genre", PERMISSION_SELECT, 1, 1, handle_list_genre }, { "listinfo", PERMISSION_SELECT, 1, 1, handle_listinfo }, { "listinfo_album", PERMISSION_SELECT, 1, 1, handle_listinfo_album }, { "listinfo_artist", PERMISSION_SELECT, 1, 1, handle_listinfo_artist }, { "listinfo_genre", PERMISSION_SELECT, 1, 1, handle_listinfo_genre }, { "listtags", PERMISSION_SELECT, 1, 1, handle_listtags }, { "listtags_album", PERMISSION_SELECT, 1, 1, handle_listtags_album }, { "listtags_artist", PERMISSION_SELECT, 1, 1, handle_listtags_artist }, { "listtags_genre", PERMISSION_SELECT, 1, 1, handle_listtags_genre }, { "love", PERMISSION_UPDATE, 1, 1, handle_love }, { "love_album", PERMISSION_UPDATE, 1, 1, handle_love_album }, { "love_artist", PERMISSION_UPDATE, 1, 1, handle_love_artist }, { "love_genre", PERMISSION_UPDATE, 1, 1, handle_love_genre }, { "password", PERMISSION_NONE, 1, 1, handle_password }, { "rate", PERMISSION_UPDATE, 2, 2, handle_rate }, { "rate_album", PERMISSION_UPDATE, 2, 2, handle_rate_album }, { "rate_artist", PERMISSION_UPDATE, 2, 2, handle_rate_artist }, { "rate_genre", PERMISSION_UPDATE, 2, 2, handle_rate_genre }, { "rate_absolute", PERMISSION_UPDATE, 2, 2, handle_rate_absolute }, { "rate_absolute_album", PERMISSION_UPDATE, 2, 2, handle_rate_absolute_album }, { "rate_absolute_artist", PERMISSION_UPDATE, 2, 2, handle_rate_absolute_artist }, { "rate_absolute_genre", PERMISSION_UPDATE, 2, 2, handle_rate_absolute_genre }, { "rmtag", PERMISSION_UPDATE, 2, 2, handle_rmtag }, { "rmtag_album", PERMISSION_UPDATE, 2, 2, handle_rmtag_album }, { "rmtag_artist", PERMISSION_UPDATE, 2, 2, handle_rmtag_artist }, { "rmtag_genre", PERMISSION_UPDATE, 2, 2, handle_rmtag_genre }, { "unkill", PERMISSION_UPDATE, 1, 1, handle_kill }, { "unkill_album", PERMISSION_UPDATE, 1, 1, handle_kill_album }, { "unkill_artist", PERMISSION_UPDATE, 1, 1, handle_kill_artist }, { "unkill_genre", PERMISSION_UPDATE, 1, 1, handle_kill_genre }, }; static const unsigned num_commands = sizeof(commands) / sizeof(commands[0]); static const struct command * command_lookup(const char *name) { unsigned a = 0, b = num_commands, i; int cmp; /* binary search */ do { i = (a + b) / 2; cmp = strcmp(name, commands[i].cmd); if (cmp == 0) return &commands[i]; else if (cmp < 0) b = i; else if (cmp > 0) a = i + 1; } while (a < b); return NULL; } static bool command_check_request(const struct command *cmd, struct client *client, unsigned permission, int argc, char **argv) { int min = cmd->min + 1; int max = cmd->max + 1; if (cmd->permission != (permission & cmd->permission)) { if (client != NULL) command_error(client, ACK_ERROR_PERMISSION, "you don't have permission for \"%s\"", cmd->cmd); return false; } if (min == 0) return true; if (min == max && max != argc) { if (client != NULL) command_error(client, ACK_ERROR_ARG, "wrong number of arguments for \"%s\"", argv[0]); return false; } else if (argc < min) { if (client != NULL) command_error(client, ACK_ERROR_ARG, "too few arguments for \"%s\"", argv[0]); return false; } else if (argc > max && max /* != 0 */ ) { if (client != NULL) command_error(client, ACK_ERROR_ARG, "too many arguments for \"%s\"", argv[0]); return false; } else return true; } static const struct command * command_checked_lookup(struct client *client, unsigned permission, int argc, char **argv) { static char unknown[] = ""; const struct command *cmd; current_command = unknown; if (argc == 0) return NULL; cmd = command_lookup(argv[0]); if (cmd == NULL) { if (client != NULL) command_error(client, ACK_ERROR_UNKNOWN, "unknown command \"%s\"", argv[0]); return NULL; } current_command = cmd->cmd; if (!command_check_request(cmd, client, permission, argc, argv)) return NULL; return cmd; } enum command_return command_process(struct client *client, char *line) { int argc; char *argv[COMMAND_ARGV_MAX] = { NULL }; enum command_return ret = COMMAND_RETURN_ERROR; const struct command *cmd; GError *error = NULL; argv[0] = tokenizer_next_word(&line, &error); if (argv[0] == NULL) { current_command = ""; if (line[0] == '\0') command_error(client, ACK_ERROR_UNKNOWN, "No command given"); else { command_error(client, ACK_ERROR_UNKNOWN, "%s", error->message); g_error_free(error); } current_command = NULL; return COMMAND_RETURN_ERROR; } argc = 1; /* now parse the arguments (quoted or unquoted) */ while (argc < (int)G_N_ELEMENTS(argv) && (argv[argc] = tokenizer_next_param(&line, &error)) != NULL) ++argc; /* Some error checks; we have to set current_command because * command_error() expects it to be set. */ current_command = argv[0]; if (argc >= (int)G_N_ELEMENTS(argv)) { command_error(client, ACK_ERROR_ARG, "Too many arguments"); current_command = NULL; return COMMAND_RETURN_ERROR; } if (*line != 0) { command_error(client, ACK_ERROR_ARG, "%s", error->message); current_command = NULL; g_error_free(error); return COMMAND_RETURN_ERROR; } /* Remove the previous authorizer */ if (!db_set_authorizer(NULL, NULL, &error) || !db_set_authorizer(command_authorizer, client, &error)) { command_error(client, error->code, "%s", error->message); current_command = NULL; g_error_free(error); return COMMAND_RETURN_ERROR; } /* Look up and invoke the command handler. */ cmd = command_checked_lookup(client, client->perm, argc, argv); if (cmd) ret = cmd->handler(client, argc, argv); current_command = NULL; return ret; } mpdcron-0.3+git20110303/src/gmodule/stats/stats-defs.h000066400000000000000000000051071153374523700222330ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef MPDCRON_GUARD_STATS_DEFS_H #define MPDCRON_GUARD_STATS_DEFS_H 1 #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #ifndef MPDCRON_MODULE #define MPDCRON_MODULE "stats" #include "../gmodule.h" #endif /* !MPDCRON_MODULE */ #include #include #include #include #include #include "stats-sqlite.h" #define PROTOCOL_VERSION "0.1" #define DEFAULT_HOST "any" #define DEFAULT_PORT 6601 #define DEFAULT_MAX_CONNECTIONS 16 #define PERMISSION_NONE 0 #define PERMISSION_SELECT 1 #define PERMISSION_UPDATE 2 #define PERMISSION_ALL (PERMISSION_SELECT | PERMISSION_UPDATE) #define COMMAND_ARGV_MAX 16 struct client { int id; unsigned perm; GIOStream *stream; GDataInputStream *input; GOutputStream *output; }; enum ack { ACK_ERROR_ARG = 1, ACK_ERROR_PASSWORD = 2, ACK_ERROR_PERMISSION = 3, ACK_ERROR_UNKNOWN = 4, }; enum command_return { COMMAND_RETURN_ERROR = -1, COMMAND_RETURN_OK = 0, COMMAND_RETURN_KILL = 10, COMMAND_RETURN_CLOSE = 20, }; /** * Configuration */ struct config { int max_connections; char **addrs; int port; char *dbpath; int default_permissions; GHashTable *passwords; char *mpd_hostname; char *mpd_port; char *mpd_password; }; extern struct config globalconf; bool file_load(const struct mpdcron_config *conf, GKeyFile *fd); void file_cleanup(void); /** * Remote query interface */ void server_init(void); void server_bind(const char *hostname, int port); void server_start(void); void server_close(void); void server_schedule_write(struct client *client, const gchar *data, gsize count); void server_flush_write(struct client *client); /** * Commands */ enum command_return command_process(struct client *client, char *line); #endif /* !MPDCRON_GUARD_STATS_DEFS_H */ mpdcron-0.3+git20110303/src/gmodule/stats/stats-file.c000066400000000000000000000135431153374523700222270ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111false307 USA */ #include "stats-defs.h" #include #include #include #include "../utils.h" struct config globalconf; bool file_load(const struct mpdcron_config *conf, GKeyFile *fd) { char **values; GError *error; memset(&globalconf, 0, sizeof(struct config)); /* Load database path */ error = NULL; if (!load_string(fd, MPDCRON_MODULE, "dbpath", false, &globalconf.dbpath, &error)) { g_critical("%s", error->message); g_error_free(error); return false; } if (globalconf.dbpath == NULL) globalconf.dbpath = g_build_filename(conf->home_path, "stats.db", NULL); /* Load port */ error = NULL; globalconf.port = -1; if (!load_integer(fd, MPDCRON_MODULE, "port", false, &globalconf.port, &error)) { g_critical("%s", error->message); g_error_free(error); return false; } if (globalconf.port <= 0) globalconf.port = DEFAULT_PORT; /* Load max connections */ error = NULL; globalconf.max_connections = -1; if (!load_integer(fd, MPDCRON_MODULE, "max_connections", false, &globalconf.max_connections, &error)) { g_critical("%s", error->message); g_error_free(error); return false; } if (globalconf.max_connections <= 0) globalconf.max_connections = DEFAULT_MAX_CONNECTIONS; /* Load default permissions */ error = NULL; values = g_key_file_get_string_list(fd, MPDCRON_MODULE, "default_permissions", NULL, &error); if (error != NULL) { switch (error->code) { case G_KEY_FILE_ERROR_GROUP_NOT_FOUND: case G_KEY_FILE_ERROR_KEY_NOT_FOUND: g_error_free(error); break; default: g_critical("Failed to load " MPDCRON_MODULE".default_permissions: %s", error->message); g_error_free(error); g_free(globalconf.dbpath); return false; } } if (values != NULL) { for (unsigned int i = 0; values[i] != NULL; i++) { if (strncmp(values[i], "select", 7) == 0) globalconf.default_permissions |= PERMISSION_SELECT; else if (strncmp(values[i], "update", 7) == 0) globalconf.default_permissions |= PERMISSION_UPDATE; else if (strncmp(values[i], "none", 5) == 0) globalconf.default_permissions = 0; else g_warning("Invalid value in " MPDCRON_MODULE".default_permissions `%s'", values[i]); } g_strfreev(values); } else globalconf.default_permissions = PERMISSION_SELECT | PERMISSION_UPDATE; /* Load passwords */ error = NULL; values = g_key_file_get_string_list(fd, MPDCRON_MODULE, "passwords", NULL, &error); if (error != NULL) { switch (error->code) { case G_KEY_FILE_ERROR_GROUP_NOT_FOUND: case G_KEY_FILE_ERROR_KEY_NOT_FOUND: g_error_free(error); break; default: g_critical("Failed to load " MPDCRON_MODULE".passwords: %s", error->message); g_error_free(error); g_free(globalconf.dbpath); return false; } } globalconf.passwords = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); if (values != NULL) { char **split; for (unsigned int i = 0; values[i] != NULL; i++) { split = g_strsplit(values[i], "@", 2); if (g_strv_length(split) != 2) { g_warning("Invalid value in " MPDCRON_MODULE".passwords `%s'", values[i]); g_strfreev(split); continue; } if (strncmp(split[1], "select", 7) == 0) { g_free(split[1]); g_hash_table_insert(globalconf.passwords, split[0], GINT_TO_POINTER(PERMISSION_SELECT)); globalconf.default_permissions = globalconf.default_permissions & ~PERMISSION_SELECT; } else if (strncmp(split[1], "update", 7) == 0) { g_free(split[1]); g_hash_table_insert(globalconf.passwords, split[0], GINT_TO_POINTER(PERMISSION_UPDATE)); globalconf.default_permissions = globalconf.default_permissions & ~PERMISSION_UPDATE; } else if (strncmp(split[1], "all", 4) == 0) { g_free(split[1]); g_hash_table_insert(globalconf.passwords, split[0], GINT_TO_POINTER(PERMISSION_ALL)); globalconf.default_permissions = globalconf.default_permissions & ~PERMISSION_ALL; } else { g_warning("Invalid value in " MPDCRON_MODULE".passwords `%s'", values[i]); g_strfreev(split); } } g_strfreev(values); } /* Load addresses */ error = NULL; globalconf.addrs = g_key_file_get_string_list(fd, MPDCRON_MODULE, "bind_to_addresses", NULL, &error); if (error != NULL) { switch (error->code) { case G_KEY_FILE_ERROR_GROUP_NOT_FOUND: case G_KEY_FILE_ERROR_KEY_NOT_FOUND: g_error_free(error); break; default: g_critical("Failed to load " MPDCRON_MODULE".bind_to_address: %s", error->message); g_error_free(error); g_free(globalconf.dbpath); return false; } } if (globalconf.addrs == NULL) { globalconf.addrs = g_new0(char *, 2); globalconf.addrs[0] = g_strdup(DEFAULT_HOST); } /* Information about Mpd */ globalconf.mpd_hostname = g_strdup(conf->hostname); globalconf.mpd_port = g_strdup(conf->port); globalconf.mpd_password = g_strdup(conf->password); return true; } void file_cleanup(void) { g_free(globalconf.dbpath); g_free(globalconf.mpd_hostname); g_free(globalconf.mpd_port); g_free(globalconf.mpd_password); g_strfreev(globalconf.addrs); g_hash_table_destroy(globalconf.passwords); } mpdcron-0.3+git20110303/src/gmodule/stats/stats-module.c000066400000000000000000000125231153374523700225720ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * Based in part upon mpdscribble which is: * Copyright (C) 2008-2009 The Music Player Daemon Project * Copyright (C) 2005-2008 Kuno Woudt * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "stats-defs.h" #include #include #include static unsigned last_id = -1; static bool was_paused = 0; static struct mpd_song *prev = NULL; static GTimer *timer = NULL; static bool played_long_enough(int elapsed, int length) { return elapsed > 240 || (length >= 30 && elapsed > length / 2); } static void song_changed(const struct mpd_song *song) { g_assert(song != NULL); g_timer_start(timer); g_debug("New song detected (%s - %s), id: %u, pos: %u", mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), mpd_song_get_tag(song, MPD_TAG_TITLE, 0), mpd_song_get_id(song), mpd_song_get_pos(song)); } static void song_started(const struct mpd_song *song) { song_changed(song); } static void song_ended(const struct mpd_song *song) { int elapsed; GError *error; g_assert(song != NULL); elapsed = g_timer_elapsed(timer, NULL); if (!played_long_enough(elapsed, mpd_song_get_duration(song))) { g_debug("Song (%s - %s), id: %u, pos: %u not played long enough, skipping", mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), mpd_song_get_tag(song, MPD_TAG_TITLE, 0), mpd_song_get_id(song), mpd_song_get_pos(song)); return; } g_debug("Saving old song (%s - %s), id: %u, pos: %u", mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), mpd_song_get_tag(song, MPD_TAG_TITLE, 0), mpd_song_get_id(song), mpd_song_get_pos(song)); error = NULL; if (!db_process(song, true, &error)) { g_warning("Saving old song failed: %s", error->message); g_error_free(error); } else if (error != NULL) { g_warning("Skipped saving old song: %s", error->message); g_error_free(error); } } static void song_playing(const struct mpd_song *song, unsigned elapsed) { unsigned prev_elapsed = g_timer_elapsed(timer, NULL); if (prev_elapsed > elapsed) { g_debug("Repeated song detected"); song_ended(song); song_started(song); } } static void song_continued(void) { g_timer_continue(timer); } static void song_paused(void) { if (!was_paused) g_timer_stop(timer); was_paused = true; } static void song_stopped(void) { last_id = -1; was_paused = false; } /* Module functions */ static int init(const struct mpdcron_config *conf, GKeyFile *fd) { GError *error; g_debug("Initializing"); /* Load configuration */ if (file_load(conf, fd) < 0) return MPDCRON_INIT_FAILURE; /* Initialize database */ error = NULL; if (!db_init(globalconf.dbpath, true, false, &error)) { g_critical("Failed to initialize database `%s': %s", globalconf.dbpath, error->message); g_error_free(error); file_cleanup(); return MPDCRON_INIT_FAILURE; } /* Initialize, bind and start the server */ server_init(); for (unsigned int i = 0; globalconf.addrs[i] != NULL; i++) { if (strncmp(globalconf.addrs[i], "any", 4) == 0) server_bind(NULL, globalconf.port); else if (globalconf.addrs[i][0] == '/') server_bind(globalconf.addrs[i], -1); else server_bind(globalconf.addrs[i], globalconf.port); } server_start(); timer = g_timer_new(); return MPDCRON_INIT_SUCCESS; } static void destroy(void) { g_message("Exiting"); if (prev != NULL) mpd_song_free(prev); g_timer_destroy(timer); server_close(); db_close(); file_cleanup(); } static int event_player(G_GNUC_UNUSED const struct mpd_connection *conn, const struct mpd_song *song, const struct mpd_status *status) { enum mpd_state state; g_assert(status != NULL); state = mpd_status_get_state(status); if (state == MPD_STATE_PAUSE) { song_paused(); return MPDCRON_EVENT_SUCCESS; } else if (state != MPD_STATE_PLAY) song_stopped(); if (was_paused) { if (song != NULL && mpd_song_get_id(song) == last_id) song_continued(); was_paused = false; } /* Submit the previous song */ if (prev != NULL && (song == NULL || mpd_song_get_id(prev) != mpd_song_get_id(song))) song_ended(prev); if (song != NULL) { if (mpd_song_get_id(song) != last_id) { /* New song. */ song_started(song); last_id = mpd_song_get_id(song); } else { /* still playing the previous song */ song_playing(song, mpd_status_get_elapsed_time(status)); } } if (prev != NULL) { mpd_song_free(prev); prev = NULL; } if (song != NULL) { if ((prev = mpd_song_dup(song)) == NULL) { g_critical("mpd_song_dup failed: out of memory"); return MPDCRON_EVENT_UNLOAD; } } return MPDCRON_EVENT_SUCCESS; } struct mpdcron_module module = { .name = "Stats", .init = init, .destroy = destroy, .event_player = event_player, }; mpdcron-0.3+git20110303/src/gmodule/stats/stats-server.c000066400000000000000000000177671153374523700226320ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "stats-defs.h" #include "tokenizer.h" #include #include #include #include #include #if HAVE_GIO_UNIX #include #endif /* HAVE_GIO_UNIX */ struct host { char *name; int port; }; struct buffer { gchar *data; gsize count; }; static const char GREETING[] = "OK MPDCRON "PROTOCOL_VERSION"\n"; static GSocketService *server; static GHashTable *clients; static void client_destroy(gpointer data) { struct client *client = (struct client *)data; g_object_unref(client->output); g_object_unref(client->input); g_object_unref(client->stream); g_free(client); } static void event_flush(G_GNUC_UNUSED GObject *source, GAsyncResult *result, gpointer clientid) { GError *error; struct client *client; client = g_hash_table_lookup(clients, clientid); if (client == NULL) { /* Already disconnected. * Nothing left to do. */ return; } error = NULL; if (!g_output_stream_flush_finish(client->output, result, &error)) { g_warning("Write failed: %s", error->message); g_error_free(error); g_hash_table_remove(clients, clientid); return; } } static void event_read_line(G_GNUC_UNUSED GObject *source, GAsyncResult *result, gpointer clientid) { gsize length; gchar *line; GError *error; struct client *client; client = g_hash_table_lookup(clients, clientid); if (client == NULL) { /* Already disconnected. * Nothing left to do. */ return; } error = NULL; if ((line = g_data_input_stream_read_line_finish(client->input, result, &length, &error)) == NULL) { if (error == NULL) { /* Client disconnected */ g_debug("[%d]? Disconnected", GPOINTER_TO_INT(clientid)); g_hash_table_remove(clients, clientid); return; } g_warning("[%d] Read failed: %s", GPOINTER_TO_INT(clientid), error ? error->message : "unknown"); g_error_free(error); g_hash_table_remove(clients, clientid); return; } g_debug("[%d]< %s", GPOINTER_TO_INT(clientid), line); command_process(client, line); g_free(line); /* Schedule another read */ g_data_input_stream_read_line_async(client->input, G_PRIORITY_DEFAULT, NULL, event_read_line, GINT_TO_POINTER(client->id)); } static gboolean event_incoming(G_GNUC_UNUSED GSocketService *srv, GSocketConnection *conn, G_GNUC_UNUSED GObject *source, G_GNUC_UNUSED gpointer userdata) { unsigned num_clients; struct client *client; num_clients = g_hash_table_size(clients); if (num_clients >= (unsigned)globalconf.max_connections) { g_warning("Maximum connections reached!"); return TRUE; } g_debug("[%d]! Connected", num_clients); /* Prepare struct client */ client = g_new(struct client, 1); client->id = num_clients; client->perm = globalconf.default_permissions; client->stream = G_IO_STREAM(conn); client->input = g_data_input_stream_new(g_io_stream_get_input_stream(client->stream)); g_data_input_stream_set_newline_type(client->input, G_DATA_STREAM_NEWLINE_TYPE_LF); client->output = g_buffered_output_stream_new(g_io_stream_get_output_stream(client->stream)); g_buffered_output_stream_set_auto_grow(G_BUFFERED_OUTPUT_STREAM(client->output), TRUE); g_hash_table_insert(clients, GINT_TO_POINTER(client->id), client); /* Increase reference count of the stream, * We'll free it manually on when client disconnects. */ g_object_ref(G_OBJECT(client->stream)); /* Schedule to send greeting */ server_schedule_write(client, GREETING, sizeof(GREETING) - 1); server_flush_write(client); /* Schedule read */ g_data_input_stream_read_line_async(client->input, G_PRIORITY_DEFAULT, NULL, event_read_line, GINT_TO_POINTER(client->id)); return FALSE; } static void bind_callback(gpointer data, gpointer userdata) { char *addr_string; GInetAddress *addr; GSocketAddress *saddr; GError *error; struct host *host; addr = (GInetAddress *)data; host = (struct host *)userdata; addr_string = g_inet_address_to_string(addr); g_debug("Resolved `%s' to %s", host->name, addr_string); saddr = g_inet_socket_address_new(addr, host->port); error = NULL; if (!g_socket_listener_add_address(G_SOCKET_LISTENER(server), saddr, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP, NULL, NULL, &error)) { g_warning("Failed bind to address %s:%d: %s", addr_string, host->port, error->message); g_error_free(error); g_free(addr_string); g_object_unref(saddr); return; } g_debug("Successful bind to %s:%d", addr_string, host->port); g_free(addr_string); g_object_unref(saddr); } static void event_resolve(GObject *source, GAsyncResult *result, gpointer userdata) { GError *error; GList *addrs; GResolver *resolver; struct host *host; resolver = G_RESOLVER(source); host = (struct host *)userdata; error = NULL; addrs = g_resolver_lookup_by_name_finish(resolver, result, &error); g_object_unref(resolver); if (error != NULL) { g_warning("Resolving hostname %s failed: %s", host->name, error->message); g_error_free(error); g_free(host->name); g_free(host); return; } g_list_foreach(addrs, bind_callback, host); g_free(host->name); g_free(host); g_resolver_free_addresses(addrs); } void server_init(void) { g_type_init(); server = g_socket_service_new(); } void server_bind(const char *hostname, int port) { GError *error; GSocketAddress *addr; if (port == -1) { #if HAVE_GIO_UNIX /* Unix socket */ unlink(hostname); error = NULL; addr = g_unix_socket_address_new(hostname); if (!g_socket_listener_add_address(G_SOCKET_LISTENER(server), G_SOCKET_ADDRESS(addr), G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, NULL, NULL, &error)) { g_warning("Failed bind to UNIX socket `%s': %s", hostname, error->message); g_error_free(error); g_object_unref(addr); return; } g_object_unref(addr); g_debug("Successful bind to %s", hostname); #else g_warning("No support for Unix sockets"); #endif /* HAVE_GIO_UNIX */ } else if (hostname == NULL) { /* Bind on any interface. */ error = NULL; if (!g_socket_listener_add_inet_port(G_SOCKET_LISTENER(server), port, NULL, &error)) { g_warning("Failed bind to 0.0.0.0:%d: %s", port, error->message); g_error_free(error); } g_debug("Successful bind to 0.0.0.0:%d", port); } else { /* Resolve the given host and bind to it. */ GResolver *resolver; struct host *host; host = g_new(struct host, 1); host->name = g_strdup(hostname); host->port = port; resolver = g_resolver_get_default(); g_resolver_lookup_by_name_async(resolver, hostname, NULL, event_resolve, host); } } void server_start(void) { g_signal_connect(server, "incoming", G_CALLBACK(event_incoming), NULL); g_socket_service_start(server); clients = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, client_destroy); } void server_close(void) { g_socket_service_stop(server); g_object_unref(server); g_hash_table_destroy(clients); } void server_schedule_write(struct client *client, const gchar *data, gsize count) { /* Since we're writing to a BufferedOutputStream that autogrows this * call shouldn't fail or block. */ g_output_stream_write(client->output, data, count, NULL, NULL); } void server_flush_write(struct client *client) { g_output_stream_flush_async(client->output, G_PRIORITY_DEFAULT, NULL, event_flush, GINT_TO_POINTER(client->id)); } mpdcron-0.3+git20110303/src/gmodule/stats/stats-sqlite.c000066400000000000000000002020251153374523700226040ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "stats-sqlite.h" #include #include #include #include #include #include static sqlite3 *gdb = NULL; enum { SQL_SET_VERSION, SQL_GET_VERSION, SQL_SET_ENCODING, SQL_DB_CREATE_SONG, SQL_DB_CREATE_ARTIST, SQL_DB_CREATE_ALBUM, SQL_DB_CREATE_GENRE, }; enum { SQL_BEGIN_TRANSACTION, SQL_END_TRANSACTION, SQL_PRAGMA_SYNC_ON, SQL_PRAGMA_SYNC_OFF, SQL_VACUUM, SQL_HAS_SONG, SQL_HAS_ARTIST, SQL_HAS_ALBUM, SQL_HAS_GENRE, SQL_INSERT_SONG, SQL_INSERT_ARTIST, SQL_INSERT_ALBUM, SQL_INSERT_GENRE, SQL_UPDATE_SONG, SQL_UPDATE_ARTIST, SQL_UPDATE_ALBUM, SQL_UPDATE_GENRE, }; #define DB_VERSION 10 static const char * const db_sql_maint[] = { [SQL_SET_VERSION] = "PRAGMA user_version = 10;", [SQL_GET_VERSION] = "PRAGMA user_version;", [SQL_SET_ENCODING] = "PRAGMA encoding = \"UTF-8\";", [SQL_DB_CREATE_SONG] = "create table song(\n" "\tid INTEGER PRIMARY KEY,\n" "\tplay_count INTEGER,\n" "\tlove INTEGER,\n" "\tkill INTEGER,\n" "\trating INTEGER,\n" "\ttags TEXT NOT NULL,\n" "\turi TEXT UNIQUE NOT NULL,\n" "\tduration INTEGER,\n" "\tlast_modified INTEGER,\n" "\tartist TEXT,\n" "\talbum TEXT,\n" "\ttitle TEXT,\n" "\ttrack TEXT,\n" "\tname TEXT,\n" "\tgenre TEXT,\n" "\tdate TEXT,\n" "\tcomposer TEXT,\n" "\tperformer TEXT,\n" "\tdisc TEXT,\n" "\tmb_artistid TEXT,\n" "\tmb_albumid TEXT,\n" "\tmb_trackid TEXT);\n", [SQL_DB_CREATE_ARTIST] = "create table artist(\n" "\tid INTEGER PRIMARY KEY,\n" "\tplay_count INTEGER,\n" "\ttags TEXT NOT NULL,\n" "\tname TEXT UNIQUE NOT NULL,\n" "\tlove INTEGER,\n" "\tkill INTEGER,\n" "\trating INTEGER);\n", [SQL_DB_CREATE_ALBUM] = "create table album(\n" "\tid INTEGER PRIMARY KEY,\n" "\tplay_count INTEGER,\n" "\ttags TEXT NOT NULL,\n" "\tartist TEXT,\n" "\tname TEXT UNIQUE NOT NULL,\n" "\tlove INTEGER,\n" "\tkill INTEGER,\n" "\trating INTEGER);\n", [SQL_DB_CREATE_GENRE] = "create table genre(\n" "\tid INTEGER PRIMARY KEY,\n" "\tplay_count INTEGER,\n" "\ttags TEXT NOT NULL,\n" "\tname TEXT UNIQUE NOT NULL,\n" "\tlove INTEGER,\n" "\tkill INTEGER,\n" "\trating INTEGER);", }; static sqlite3_stmt *db_stmt_maint[G_N_ELEMENTS(db_sql_maint)] = { NULL }; static const char * const db_sql[] = { [SQL_BEGIN_TRANSACTION] = "BEGIN TRANSACTION;", [SQL_END_TRANSACTION] = "END TRANSACTION;", [SQL_PRAGMA_SYNC_ON] = "PRAGMA synchronous=ON;", [SQL_PRAGMA_SYNC_OFF] = "PRAGMA synchronous=OFF;", [SQL_VACUUM] = "VACUUM;", [SQL_HAS_SONG] = "select id from song where uri=?", [SQL_HAS_ARTIST] = "select id from artist where name=?", [SQL_HAS_ALBUM] = "select id from album where name=?", [SQL_HAS_GENRE] = "select id from genre where name=?", [SQL_INSERT_SONG] = "insert into song (" "play_count," "love, kill, rating, tags," "uri, duration, last_modified," "artist, album, title," "track, name, genre," "date, composer, performer, disc," "mb_artistid, mb_albumid, mb_trackid)" " values (?, 0, 0, 0, ':'," "?, ?, ?, ?," "?, ?, ?, ?," "?, ?, ?, ?," "?, ?, ?, ?);", [SQL_INSERT_ARTIST] = "insert into artist (" "play_count, name," "love, kill, rating, tags)" " values (?, ?, 0, 0, 0, ':');", [SQL_INSERT_ALBUM] = "insert into album (" "play_count, artist, name," "love, kill, rating, tags)" " values (?, ?, ?, 0, 0, 0, ':');", [SQL_INSERT_GENRE] = "insert into genre (" "play_count, name," "love, kill, rating, tags)" " values (?, ?, 0, 0, 0, ':');", [SQL_UPDATE_SONG] = "update song " "set play_count = play_count + ?," "uri=?," "duration=?," "last_modified=?," "artist=?," "album=?," "title=?," "track=?," "name=?," "genre=?," "date=?," "composer=?," "performer=?," "disc=?," "mb_artistid=?," "mb_albumid=?," "mb_trackid=?" " where id=?;", [SQL_UPDATE_ARTIST] = "update artist " "set play_count = play_count + ?," "name=? where id=?;", [SQL_UPDATE_ALBUM] = "update album " "set play_count = play_count + ?," "artist=?, name=? where id=?;", [SQL_UPDATE_GENRE] = "update genre " "set play_count = play_count + ?," "name=? where id=?;", }; static sqlite3_stmt *db_stmt[G_N_ELEMENTS(db_sql)] = { NULL }; /** * Utility Functions */ static GQuark db_quark(void) { return g_quark_from_static_string("database"); } G_GNUC_UNUSED static char * escape_string(const char *src) { char *q, *dest; q = sqlite3_mprintf("%Q", src); dest = g_strdup(q); sqlite3_free(q); return dest; } static int db_step(sqlite3_stmt *stmt) { int ret; do { ret = sqlite3_step(stmt); } while (ret == SQLITE_BUSY); return ret; } static bool validate_tag(const char *tag, GError **error) { if (tag[0] == '\0' || strchr(tag, ':') != NULL) { g_set_error(error, db_quark(), ACK_ERROR_INVALID_TAG, "Invalid tag `%s'", tag); return false; } return true; } static char * remove_tag(const char *tags, const char *tag) { int len; char **list; GString *new; len = strlen(tag) + 1; new = g_string_new(":"); list = g_strsplit(tags, ":", -1); for (unsigned int i = 0; list[i] != NULL; i++) { if (list[i][0] == '\0') continue; else if (strncmp(list[i], tag, len) == 0) continue; g_string_append_printf(new, "%s:", list[i]); } g_strfreev(list); return g_string_free(new, FALSE); } void db_generic_data_free(struct db_generic_data *data) { g_strfreev(data->tags); g_free(data->name); g_free(data->artist); g_free(data); } void db_song_data_free(struct db_song_data *song) { g_strfreev(song->tags); g_free(song->uri); g_free(song->artist); g_free(song->album); g_free(song->title); g_free(song->track); g_free(song->name); g_free(song->genre); g_free(song->date); g_free(song->composer); g_free(song->performer); g_free(song->genre); g_free(song->mb_artist_id); g_free(song->mb_album_id); g_free(song->mb_track_id); g_free(song); } /** * Database Queries */ static int db_has_artist(const char *name, GError **error) { int id, ret; g_assert(gdb != NULL); /* Reset the statement to its initial state */ if (sqlite3_reset(db_stmt[SQL_HAS_ARTIST]) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_RESET, "sqlite3_reset: %s", sqlite3_errmsg(gdb)); return -2; } /* Bind the argument */ if (sqlite3_bind_text(db_stmt[SQL_HAS_ARTIST], 1, name, -1, SQLITE_STATIC) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_BIND, "sqlite3_bind: %s", sqlite3_errmsg(gdb)); return -2; } /* Step and get the id */ id = -1; do { ret = sqlite3_step(db_stmt[SQL_HAS_ARTIST]); if (ret == SQLITE_ROW) id = sqlite3_column_int(db_stmt[SQL_HAS_ARTIST], 0); } while (ret == SQLITE_BUSY || ret == SQLITE_ROW); if (ret != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_SELECT, "sqlite3_step: %s", sqlite3_errmsg(gdb)); return -2; } return id; } static int db_has_album(const char *name, GError **error) { int id, ret; g_assert(gdb != NULL); /* Reset the statement to its initial state */ if (sqlite3_reset(db_stmt[SQL_HAS_ALBUM]) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_RESET, "sqlite3_reset: %s", sqlite3_errmsg(gdb)); return -2; } /* Bind the argument */ if (sqlite3_bind_text(db_stmt[SQL_HAS_ALBUM], 1, name, -1, SQLITE_STATIC) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_BIND, "sqlite3_bind: %s", sqlite3_errmsg(gdb)); return -2; } /* Step and get the id */ id = -1; do { ret = sqlite3_step(db_stmt[SQL_HAS_ALBUM]); if (ret == SQLITE_ROW) id = sqlite3_column_int(db_stmt[SQL_HAS_ALBUM], 0); } while (ret == SQLITE_BUSY || ret == SQLITE_ROW); if (ret != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_SELECT, "sqlite3_step: %s", sqlite3_errmsg(gdb)); return -2; } return id; } static int db_has_genre(const char *name, GError **error) { int id, ret; g_assert(gdb != NULL); /* Reset the statement to its initial state */ if (sqlite3_reset(db_stmt[SQL_HAS_GENRE]) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_RESET, "sqlite3_reset: %s", sqlite3_errmsg(gdb)); return -2; } /* Bind the argument */ if (sqlite3_bind_text(db_stmt[SQL_HAS_GENRE], 1, name, -1, SQLITE_STATIC) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_BIND, "sqlite3_bind: %s", sqlite3_errmsg(gdb)); return -2; } /* Step and get the id */ id = -1; do { ret = sqlite3_step(db_stmt[SQL_HAS_GENRE]); if (ret == SQLITE_ROW) id = sqlite3_column_int(db_stmt[SQL_HAS_GENRE], 0); } while (ret == SQLITE_BUSY || ret == SQLITE_ROW); if (ret != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_SELECT, "sqlite3_step: %s", sqlite3_errmsg(gdb)); return -2; } return id; } static int db_has_song(const char *uri, GError **error) { int id, ret; g_assert(gdb != NULL); /* Reset the statement to its initial state */ if (sqlite3_reset(db_stmt[SQL_HAS_SONG]) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_RESET, "sqlite3_reset: %s", sqlite3_errmsg(gdb)); return -2; } /* Bind the argument */ if (sqlite3_bind_text(db_stmt[SQL_HAS_SONG], 1, uri, -1, SQLITE_STATIC) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_BIND, "sqlite3_bind: %s", sqlite3_errmsg(gdb)); return -2; } /* Step and get the id */ id = -1; do { ret = sqlite3_step(db_stmt[SQL_HAS_SONG]); if (ret == SQLITE_ROW) id = sqlite3_column_int(db_stmt[SQL_HAS_SONG], 0); } while (ret == SQLITE_BUSY || ret == SQLITE_ROW); if (ret != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_SELECT, "sqlite3_step: %s", sqlite3_errmsg(gdb)); return -2; } return id; } /** * Database Inserts/Updates */ static bool db_insert_artist(const struct mpd_song *song, bool increment, GError **error) { g_assert(gdb != NULL); g_assert(song != NULL); /* Reset the statement to its initial state */ if (sqlite3_reset(db_stmt[SQL_INSERT_ARTIST]) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_RESET, "sqlite3_reset: %s", sqlite3_errmsg(gdb)); return false; } if (sqlite3_bind_int(db_stmt[SQL_INSERT_ARTIST], 1, increment ? 1 : 0) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_INSERT_ARTIST], 2, mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), -1, SQLITE_STATIC) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_BIND, "sqlite3_bind: %s", sqlite3_errmsg(gdb)); return false; } if (db_step(db_stmt[SQL_INSERT_ARTIST]) != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); return false; } return true; } static bool db_insert_album(const struct mpd_song *song, bool increment, GError **error) { g_assert(gdb != NULL); g_assert(song != NULL); /* Reset the statement to its initial state */ if (sqlite3_reset(db_stmt[SQL_INSERT_ALBUM]) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_RESET, "sqlite3_reset: %s", sqlite3_errmsg(gdb)); return false; } if (sqlite3_bind_int(db_stmt[SQL_INSERT_ALBUM], 1, increment ? 1 : 0) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_INSERT_ALBUM], 2, mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_INSERT_ALBUM], 3, mpd_song_get_tag(song, MPD_TAG_ALBUM, 0), -1, SQLITE_STATIC) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_BIND, "sqlite3_bind: %s", sqlite3_errmsg(gdb)); return false; } if (db_step(db_stmt[SQL_INSERT_ALBUM]) != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); return false; } return true; } static bool db_insert_genre(const struct mpd_song *song, bool increment, GError **error) { g_assert(gdb != NULL); g_assert(song != NULL); /* Reset the statement to its initial state */ if (sqlite3_reset(db_stmt[SQL_INSERT_GENRE]) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_RESET, "sqlite3_reset: %s", sqlite3_errmsg(gdb)); return false; } if (sqlite3_bind_int(db_stmt[SQL_INSERT_GENRE], 1, increment ? 1 : 0) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_INSERT_GENRE], 2, mpd_song_get_tag(song, MPD_TAG_GENRE, 0), -1, SQLITE_STATIC) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_BIND, "sqlite3_bind: %s", sqlite3_errmsg(gdb)); return false; } if (db_step(db_stmt[SQL_INSERT_GENRE]) != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); return false; } return true; } static bool db_insert_song(const struct mpd_song *song, bool increment, GError **error) { g_assert(gdb != NULL); g_assert(song != NULL); if (sqlite3_reset(db_stmt[SQL_INSERT_SONG]) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_RESET, "sqlite3_reset: %s", sqlite3_errmsg(gdb)); return false; } if (sqlite3_bind_int(db_stmt[SQL_INSERT_SONG], 1, increment ? 1 : 0) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_INSERT_SONG], 2, mpd_song_get_uri(song), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_int(db_stmt[SQL_INSERT_SONG], 3, mpd_song_get_duration(song)) != SQLITE_OK || sqlite3_bind_int(db_stmt[SQL_INSERT_SONG], 4, mpd_song_get_last_modified(song)) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_INSERT_SONG], 5, mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_INSERT_SONG], 6, mpd_song_get_tag(song, MPD_TAG_ALBUM, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_INSERT_SONG], 7, mpd_song_get_tag(song, MPD_TAG_TITLE, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_INSERT_SONG], 8, mpd_song_get_tag(song, MPD_TAG_TRACK, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_INSERT_SONG], 9, mpd_song_get_tag(song, MPD_TAG_NAME, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_INSERT_SONG], 10, mpd_song_get_tag(song, MPD_TAG_GENRE, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_INSERT_SONG], 11, mpd_song_get_tag(song, MPD_TAG_DATE, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_INSERT_SONG], 12, mpd_song_get_tag(song, MPD_TAG_COMPOSER, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_INSERT_SONG], 13, mpd_song_get_tag(song, MPD_TAG_PERFORMER, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_INSERT_SONG], 14, mpd_song_get_tag(song, MPD_TAG_DISC, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_INSERT_SONG], 15, mpd_song_get_tag(song, MPD_TAG_MUSICBRAINZ_ARTISTID, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_INSERT_SONG], 16, mpd_song_get_tag(song, MPD_TAG_MUSICBRAINZ_ALBUMID, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_INSERT_SONG], 17, mpd_song_get_tag(song, MPD_TAG_MUSICBRAINZ_TRACKID, 0), -1, SQLITE_STATIC) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_BIND, "sqlite3_bind: %s", sqlite3_errmsg(gdb)); return false; } if (db_step(db_stmt[SQL_INSERT_SONG]) != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); return false; } return true; } static bool db_update_artist(const struct mpd_song *song, int id, bool increment, GError **error) { g_assert(gdb != NULL); g_assert(song != NULL); /* Reset the statement to its initial state */ if (sqlite3_reset(db_stmt[SQL_UPDATE_ARTIST]) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_RESET, "sqlite3_reset: %s", sqlite3_errmsg(gdb)); return false; } if (sqlite3_bind_int(db_stmt[SQL_UPDATE_ARTIST], 1, increment ? 1 : 0) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_UPDATE_ARTIST], 2, mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_int(db_stmt[SQL_UPDATE_ARTIST], 3, id) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_BIND, "sqlite3_bind: %s", sqlite3_errmsg(gdb)); return false; } if (db_step(db_stmt[SQL_UPDATE_ARTIST]) != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); return false; } return true; } static bool db_update_album(const struct mpd_song *song, int id, bool increment, GError **error) { g_assert(gdb != NULL); g_assert(song != NULL); /* Reset the statement to its initial state */ if (sqlite3_reset(db_stmt[SQL_UPDATE_ALBUM]) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_RESET, "sqlite3_reset: %s", sqlite3_errmsg(gdb)); return false; } if (sqlite3_bind_int(db_stmt[SQL_UPDATE_ALBUM], 1, increment ? 1 : 0) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_UPDATE_ALBUM], 2, mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_UPDATE_ALBUM], 3, mpd_song_get_tag(song, MPD_TAG_ALBUM, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_int(db_stmt[SQL_UPDATE_ALBUM], 4, id) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_BIND, "sqlite3_bind: %s", sqlite3_errmsg(gdb)); return false; } if (db_step(db_stmt[SQL_UPDATE_ALBUM]) != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); return false; } return true; } static bool db_update_genre(const struct mpd_song *song, int id, bool increment, GError **error) { g_assert(gdb != NULL); g_assert(song != NULL); /* Reset the statement to its initial state */ if (sqlite3_reset(db_stmt[SQL_UPDATE_GENRE]) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_RESET, "sqlite3_reset: %s", sqlite3_errmsg(gdb)); return false; } if (sqlite3_bind_int(db_stmt[SQL_UPDATE_GENRE], 1, increment ? 1 : 0) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_UPDATE_GENRE], 2, mpd_song_get_tag(song, MPD_TAG_GENRE, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_int(db_stmt[SQL_UPDATE_GENRE], 3, id) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_BIND, "sqlite3_bind: %s", sqlite3_errmsg(gdb)); return false; } if (db_step(db_stmt[SQL_UPDATE_GENRE]) != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); return false; } return true; } static bool db_update_song(const struct mpd_song *song, int id, bool increment, GError **error) { int ret; g_assert(gdb != NULL); g_assert(song != NULL); if (sqlite3_reset(db_stmt[SQL_UPDATE_SONG]) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_RESET, "sqlite3_reset: %s", sqlite3_errmsg(gdb)); return false; } if (sqlite3_bind_int(db_stmt[SQL_UPDATE_SONG], 1, increment ? 1 : 0) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_UPDATE_SONG], 2, mpd_song_get_uri(song), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_int(db_stmt[SQL_UPDATE_SONG], 3, mpd_song_get_duration(song)) != SQLITE_OK || sqlite3_bind_int(db_stmt[SQL_UPDATE_SONG], 4, mpd_song_get_last_modified(song)) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_UPDATE_SONG], 5, mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_UPDATE_SONG], 6, mpd_song_get_tag(song, MPD_TAG_ALBUM, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_UPDATE_SONG], 7, mpd_song_get_tag(song, MPD_TAG_TITLE, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_UPDATE_SONG], 8, mpd_song_get_tag(song, MPD_TAG_TRACK, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_UPDATE_SONG], 9, mpd_song_get_tag(song, MPD_TAG_NAME, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_UPDATE_SONG], 10, mpd_song_get_tag(song, MPD_TAG_GENRE, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_UPDATE_SONG], 11, mpd_song_get_tag(song, MPD_TAG_DATE, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_UPDATE_SONG], 12, mpd_song_get_tag(song, MPD_TAG_COMPOSER, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_UPDATE_SONG], 13, mpd_song_get_tag(song, MPD_TAG_PERFORMER, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_UPDATE_SONG], 14, mpd_song_get_tag(song, MPD_TAG_DISC, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_UPDATE_SONG], 15, mpd_song_get_tag(song, MPD_TAG_MUSICBRAINZ_ARTISTID, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_UPDATE_SONG], 16, mpd_song_get_tag(song, MPD_TAG_MUSICBRAINZ_ALBUMID, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(db_stmt[SQL_UPDATE_SONG], 17, mpd_song_get_tag(song, MPD_TAG_MUSICBRAINZ_TRACKID, 0), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_int(db_stmt[SQL_UPDATE_SONG], 18, id)) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_BIND, "sqlite3_bind: %s", sqlite3_errmsg(gdb)); return false; } do { ret = sqlite3_step(db_stmt[SQL_UPDATE_SONG]); } while (ret == SQLITE_BUSY); if (ret != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); return false; } return true; } /** * Database Updates */ static bool sql_update_entry(const char *tbl, const char *stmt, const char *expr, GError **error) { char *sql; sqlite3_stmt *sql_stmt; g_assert(gdb != NULL); g_assert(tbl != NULL); g_assert(stmt != NULL); g_assert(expr != NULL); sql = g_strdup_printf("update %s set %s where %s ;", tbl, stmt, expr); if (sqlite3_prepare_v2(gdb, sql, -1, &sql_stmt, NULL) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_PREPARE, "sqlite3_prepare_v2: %s", sqlite3_errmsg(gdb)); g_free(sql); return false; } g_free(sql); if (db_step(sql_stmt) != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); sqlite3_finalize(sql_stmt); return false; } sqlite3_finalize(sql_stmt); return true; } static inline bool sql_update_artist(const char *stmt, const char *expr, GError **error) { return sql_update_entry("artist", stmt, expr, error); } static inline bool sql_update_album(const char *stmt, const char *expr, GError **error) { return sql_update_entry("album", stmt, expr, error); } static inline bool sql_update_genre(const char *stmt, const char *expr, GError **error) { return sql_update_entry("genre", stmt, expr, error); } static inline bool sql_update_song(const char *stmt, const char *expr, GError **error) { return sql_update_entry("song", stmt, expr, error); } /** * Database Maintenance */ static bool db_create(GError **error) { g_assert(gdb != NULL); g_assert(db_stmt_maint[SQL_DB_CREATE_SONG] != NULL); g_assert(db_stmt_maint[SQL_DB_CREATE_ARTIST] != NULL); g_assert(db_stmt_maint[SQL_DB_CREATE_ALBUM] != NULL); g_assert(db_stmt_maint[SQL_DB_CREATE_GENRE] != NULL); g_assert(db_stmt_maint[SQL_SET_ENCODING] != NULL); g_assert(db_stmt_maint[SQL_SET_VERSION] != NULL); /** * Create tables */ if (db_step(db_stmt_maint[SQL_DB_CREATE_SONG]) != SQLITE_DONE || db_step(db_stmt_maint[SQL_DB_CREATE_ARTIST]) != SQLITE_DONE || db_step(db_stmt_maint[SQL_DB_CREATE_ALBUM]) != SQLITE_DONE || db_step(db_stmt_maint[SQL_DB_CREATE_GENRE]) != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_CREATE, "sqlite3_step: %s", sqlite3_errmsg(gdb)); return false; } /** * Set encoding */ if (db_step(db_stmt_maint[SQL_SET_ENCODING]) != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_CREATE, "sqlite3_step: %s", sqlite3_errmsg(gdb)); return false; } /** * Set database version */ if (db_step(db_stmt_maint[SQL_SET_VERSION]) != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_CREATE, "sqlite3_step: %s", sqlite3_errmsg(gdb)); return false; } return true; } static bool db_check_ver(GError **error) { int ret, version; g_assert(gdb != NULL); g_assert(db_stmt_maint[SQL_GET_VERSION] != NULL); /** * Check version */ do { ret = sqlite3_step(db_stmt_maint[SQL_GET_VERSION]); if (ret == SQLITE_ROW) version = sqlite3_column_int(db_stmt_maint[SQL_GET_VERSION], 0); } while (ret == SQLITE_BUSY || ret == SQLITE_ROW); if (ret != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_CREATE, "sqlite3_step: %s", sqlite3_errmsg(gdb)); return false; } else if (version != DB_VERSION) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_VERSION, "Database version mismatch: %d != %d", version, DB_VERSION); return false; } return true; } bool db_initialized(void) { return (gdb != NULL); } bool db_init(const char *path, bool create, bool readonly, GError **error) { int flags; gboolean new; g_assert(gdb == NULL); new = !g_file_test(path, G_FILE_TEST_EXISTS); flags = 0; if (create && readonly) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_OPEN, "Invalid flags"); return false; } flags |= (readonly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE); if (create) flags |= SQLITE_OPEN_CREATE; if (sqlite3_open_v2(path, &gdb, flags, NULL) != 0) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_OPEN, "sqlite3_open_v2: %s", sqlite3_errmsg(gdb)); gdb = NULL; return false; } if (new) { for (unsigned int i = 0; i < G_N_ELEMENTS(db_sql_maint); i++) { if (sqlite3_prepare_v2(gdb, db_sql_maint[i], -1, &db_stmt_maint[i], NULL) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_PREPARE, "sqlite3_prepare_v2: %s", sqlite3_errmsg(gdb)); db_close(); return false; } } if (!db_create(error)) { db_close(); return false; } } else { if (sqlite3_prepare_v2(gdb, db_sql_maint[SQL_GET_VERSION], -1, &db_stmt_maint[SQL_GET_VERSION], NULL) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_PREPARE, "sqlite3_prepare_v2: %s", sqlite3_errmsg(gdb)); db_close(); return false; } if (!db_check_ver(error)) { db_close(); return false; } sqlite3_finalize(db_stmt_maint[SQL_GET_VERSION]); db_stmt_maint[SQL_GET_VERSION] = NULL; } /* Prepare common statements */ for (unsigned int i = 0; i < G_N_ELEMENTS(db_sql); i++) { g_assert(db_stmt[i] == NULL); if (sqlite3_prepare_v2(gdb, db_sql[i], -1, &db_stmt[i], NULL) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_PREPARE, "sqlite3_prepare_v2: %s", sqlite3_errmsg(gdb)); db_close(); return false; } } return true; } void db_close(void) { for (unsigned int i = 0; i < G_N_ELEMENTS(db_sql_maint); i++) { if (db_stmt_maint[i] != NULL) { sqlite3_finalize(db_stmt_maint[i]); db_stmt_maint[i] = NULL; } } for (unsigned int i = 0; i < G_N_ELEMENTS(db_sql); i++) { if (db_stmt[i] != NULL) { sqlite3_finalize(db_stmt[i]); db_stmt[i] = NULL; } } sqlite3_close(gdb); gdb = NULL; } bool db_set_authorizer(int (*xAuth)(void *, int, const char *, const char *, const char *,const char *), void *userdata, GError **error) { g_assert(gdb != NULL); if (sqlite3_set_authorizer(gdb, xAuth, userdata) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_AUTH, "sqlite3_set_authorizer: %s", sqlite3_errmsg(gdb)); return false; } return true; } /** * Database Interaction */ bool db_start_transaction(GError **error) { g_assert(gdb != NULL); if (sqlite3_reset(db_stmt[SQL_BEGIN_TRANSACTION]) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_RESET, "sqlite3_reset: %s", sqlite3_errmsg(gdb)); return false; } if (db_step(db_stmt[SQL_BEGIN_TRANSACTION]) != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); return false; } return true; } bool db_end_transaction(GError **error) { g_assert(gdb != NULL); if (sqlite3_reset(db_stmt[SQL_END_TRANSACTION]) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_RESET, "sqlite3_reset: %s", sqlite3_errmsg(gdb)); return false; } if (db_step(db_stmt[SQL_END_TRANSACTION]) != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); return false; } return true; } bool db_set_sync(bool on, GError **error) { sqlite3_stmt *stmt; g_assert(gdb != NULL); stmt = on ? db_stmt[SQL_PRAGMA_SYNC_ON] : db_stmt[SQL_PRAGMA_SYNC_OFF]; if (sqlite3_reset(stmt) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_RESET, "sqlite3_reset: %s", sqlite3_errmsg(gdb)); return false; } if (db_step(stmt) != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); return false; } return true; } bool db_vacuum(GError **error) { g_assert(gdb != NULL); if (sqlite3_reset(db_stmt[SQL_VACUUM]) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_RESET, "sqlite3_reset: %s", sqlite3_errmsg(gdb)); return false; } if (db_step(db_stmt[SQL_VACUUM]) != SQLITE_DONE) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); return false; } return true; } bool db_process(const struct mpd_song *song, bool increment, GError **error) { int id; g_assert(gdb != NULL); g_assert(song != NULL); if (mpd_song_get_tag(song, MPD_TAG_ARTIST, 0) == NULL || mpd_song_get_tag(song, MPD_TAG_TITLE, 0) == NULL) { g_set_error(error, db_quark(), ACK_ERROR_NO_TAGS, "Song (%s) doesn't have required tags", mpd_song_get_uri(song)); return true; } if ((id = db_has_song(mpd_song_get_uri(song), error)) < -1) return false; else if (id == -1) { if (!db_insert_song(song, increment, error)) return false; } else { if (!db_update_song(song, id, increment, error)) return false; } if ((id = db_has_artist(mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), error)) < -1) return false; else if (id == -1) { if (!db_insert_artist(song, increment, error)) return false; } else { if (!db_update_artist(song, id, increment, error)) return false; } if (mpd_song_get_tag(song, MPD_TAG_ALBUM, 0) != NULL) { if ((id = db_has_album(mpd_song_get_tag(song, MPD_TAG_ALBUM, 0), error)) < -1) return false; else if (id == -1) { if (!db_insert_album(song, increment, error)) return false; } else { if (!db_update_album(song, id, increment, error)) return false; } } if (mpd_song_get_tag(song, MPD_TAG_GENRE, 0) != NULL) { if ((id = db_has_genre(mpd_song_get_tag(song, MPD_TAG_GENRE, 0), error)) < -1) return false; else if (id == -1) { if (!db_insert_genre(song, increment, error)) return false; } else { if (!db_update_genre(song, id, increment, error)) return false; } } return true; } /** * Main Interface */ /** * List song/artist/album/genre */ bool db_list_artist_expr(const char *expr, GSList **values, GError **error) { int ret; char *sql; sqlite3_stmt *stmt; struct db_generic_data *data; g_assert(gdb != NULL); g_assert(expr != NULL); g_assert(values != NULL); sql = g_strdup_printf("select id, name from artist where %s ;", expr); if (sqlite3_prepare_v2(gdb, sql, -1, &stmt, NULL) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_PREPARE, "sqlite3_prepare_v2: %s", sqlite3_errmsg(gdb)); g_free(sql); return false; } g_free(sql); do { ret = sqlite3_step(stmt); switch (ret) { case SQLITE_ROW: data = g_new0(struct db_generic_data, 1); data->id = sqlite3_column_int(stmt, 0); data->name = g_strdup((const char *)sqlite3_column_text(stmt, 1)); *values = g_slist_prepend(*values, data); break; case SQLITE_DONE: break; case SQLITE_BUSY: /* no-op */ break; default: g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); sqlite3_finalize(stmt); return false; } } while (ret != SQLITE_DONE); sqlite3_finalize(stmt); return true; } bool db_list_album_expr(const char *expr, GSList **values, GError **error) { int ret; char *sql; struct db_generic_data *data; sqlite3_stmt *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); g_assert(values != NULL); sql = g_strdup_printf("select id, name, artist from album where %s ;", expr); if (sqlite3_prepare_v2(gdb, sql, -1, &stmt, NULL) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_PREPARE, "sqlite3_prepare_v2: %s", sqlite3_errmsg(gdb)); g_free(sql); return false; } g_free(sql); do { ret = sqlite3_step(stmt); switch (ret) { case SQLITE_ROW: data = g_new0(struct db_generic_data, 1); data->id = sqlite3_column_int(stmt, 0); data->name = g_strdup((const char *)sqlite3_column_text(stmt, 1)); data->artist = g_strdup((const char *)sqlite3_column_text(stmt, 2)); *values = g_slist_prepend(*values, data); break; case SQLITE_DONE: break; case SQLITE_BUSY: /* no-op */ break; default: g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); sqlite3_finalize(stmt); return false; } } while (ret != SQLITE_DONE); sqlite3_finalize(stmt); return true; } bool db_list_genre_expr(const char *expr, GSList **values, GError **error) { int ret; char *sql; struct db_generic_data *data; sqlite3_stmt *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); g_assert(values != NULL); sql = g_strdup_printf("select id, name from genre where %s ;", expr); if (sqlite3_prepare_v2(gdb, sql, -1, &stmt, NULL) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_PREPARE, "sqlite3_prepare_v2: %s", sqlite3_errmsg(gdb)); g_free(sql); return false; } g_free(sql); do { ret = sqlite3_step(stmt); switch (ret) { case SQLITE_ROW: data = g_new0(struct db_generic_data, 1); data->id = sqlite3_column_int(stmt, 0); data->name = g_strdup((const char *)sqlite3_column_text(stmt, 1)); *values = g_slist_prepend(*values, data); break; case SQLITE_DONE: break; case SQLITE_BUSY: /* no-op */ break; default: g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); sqlite3_finalize(stmt); return false; } } while (ret != SQLITE_DONE); sqlite3_finalize(stmt); return true; } bool db_list_song_expr(const char *expr, GSList **values, GError **error) { int ret; char *sql; struct db_song_data *song; sqlite3_stmt *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); g_assert(values != NULL); sql = g_strdup_printf("select id, uri from song where %s ;", expr); if (sqlite3_prepare_v2(gdb, sql, -1, &stmt, NULL) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_PREPARE, "sqlite3_prepare_v2: %s", sqlite3_errmsg(gdb)); g_free(sql); return false; } g_free(sql); do { ret = sqlite3_step(stmt); switch (ret) { case SQLITE_ROW: song = g_new0(struct db_song_data, 1); song->id = sqlite3_column_int(stmt, 0); song->uri = g_strdup((const char *)sqlite3_column_text(stmt, 1)); *values = g_slist_prepend(*values, song); break; case SQLITE_DONE: break; case SQLITE_BUSY: /* no-op */ break; default: g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); sqlite3_finalize(stmt); return false; } } while (ret != SQLITE_DONE); sqlite3_finalize(stmt); return true; } bool db_listinfo_artist_expr(const char *expr, GSList **values, GError **error) { int ret; char *sql; sqlite3_stmt *stmt; struct db_generic_data *data; g_assert(gdb != NULL); g_assert(expr != NULL); g_assert(values != NULL); sql = g_strdup_printf("select " "id, play_count, name, love, kill, rating " "from artist where %s ;", expr); if (sqlite3_prepare_v2(gdb, sql, -1, &stmt, NULL) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_PREPARE, "sqlite3_prepare_v2: %s", sqlite3_errmsg(gdb)); g_free(sql); return false; } g_free(sql); do { ret = sqlite3_step(stmt); switch (ret) { case SQLITE_ROW: data = g_new0(struct db_generic_data, 1); data->id = sqlite3_column_int(stmt, 0); data->play_count = sqlite3_column_int(stmt, 1); data->name = g_strdup((const char *)sqlite3_column_text(stmt, 2)); data->love = sqlite3_column_int(stmt, 3); data->kill = sqlite3_column_int(stmt, 4); data->rating = sqlite3_column_int(stmt, 5); *values = g_slist_prepend(*values, data); break; case SQLITE_DONE: break; case SQLITE_BUSY: /* no-op */ break; default: g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); sqlite3_finalize(stmt); return false; } } while (ret != SQLITE_DONE); sqlite3_finalize(stmt); return true; } bool db_listinfo_album_expr(const char *expr, GSList **values, GError **error) { int ret; char *sql; sqlite3_stmt *stmt; struct db_generic_data *data; g_assert(gdb != NULL); g_assert(expr != NULL); g_assert(values != NULL); sql = g_strdup_printf("select " "id, play_count, name, artist, love, kill, rating " "from album where %s ;", expr); if (sqlite3_prepare_v2(gdb, sql, -1, &stmt, NULL) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_PREPARE, "sqlite3_prepare_v2: %s", sqlite3_errmsg(gdb)); g_free(sql); return false; } g_free(sql); do { ret = sqlite3_step(stmt); switch (ret) { case SQLITE_ROW: data = g_new0(struct db_generic_data, 1); data->id = sqlite3_column_int(stmt, 0); data->play_count = sqlite3_column_int(stmt, 1); data->name = g_strdup((const char *)sqlite3_column_text(stmt, 2)); data->artist = g_strdup((const char *)sqlite3_column_text(stmt, 3)); data->love = sqlite3_column_int(stmt, 4); data->kill = sqlite3_column_int(stmt, 5); data->rating = sqlite3_column_int(stmt, 6); *values = g_slist_prepend(*values, data); break; case SQLITE_DONE: break; case SQLITE_BUSY: /* no-op */ break; default: g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); sqlite3_finalize(stmt); return false; } } while (ret != SQLITE_DONE); sqlite3_finalize(stmt); return true; } bool db_listinfo_genre_expr(const char *expr, GSList **values, GError **error) { int ret; char *sql; sqlite3_stmt *stmt; struct db_generic_data *data; g_assert(gdb != NULL); g_assert(expr != NULL); g_assert(values != NULL); sql = g_strdup_printf("select " "id, play_count, name, love, kill, rating " "from genre where %s ;", expr); if (sqlite3_prepare_v2(gdb, sql, -1, &stmt, NULL) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_PREPARE, "sqlite3_prepare_v2: %s", sqlite3_errmsg(gdb)); g_free(sql); return false; } g_free(sql); do { ret = sqlite3_step(stmt); switch (ret) { case SQLITE_ROW: data = g_new0(struct db_generic_data, 1); data->id = sqlite3_column_int(stmt, 0); data->play_count = sqlite3_column_int(stmt, 1); data->name = g_strdup((const char *)sqlite3_column_text(stmt, 2)); data->love = sqlite3_column_int(stmt, 3); data->kill = sqlite3_column_int(stmt, 4); data->rating = sqlite3_column_int(stmt, 5); *values = g_slist_prepend(*values, data); break; case SQLITE_DONE: break; case SQLITE_BUSY: /* no-op */ break; default: g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); sqlite3_finalize(stmt); return false; } } while (ret != SQLITE_DONE); sqlite3_finalize(stmt); return true; } bool db_listinfo_song_expr(const char *expr, GSList **values, GError **error) { int ret; char *sql; struct db_song_data *song; sqlite3_stmt *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); g_assert(values != NULL); sql = g_strdup_printf("select " "id, play_count, love, kill, rating, uri " "from song where %s ;", expr); if (sqlite3_prepare_v2(gdb, sql, -1, &stmt, NULL) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_PREPARE, "sqlite3_prepare_v2: %s", sqlite3_errmsg(gdb)); g_free(sql); return false; } g_free(sql); do { ret = sqlite3_step(stmt); switch (ret) { case SQLITE_ROW: song = g_new0(struct db_song_data, 1); song->id = sqlite3_column_int(stmt, 0); song->play_count = sqlite3_column_int(stmt, 1); song->love = sqlite3_column_int(stmt, 2); song->kill = sqlite3_column_int(stmt, 3); song->rating = sqlite3_column_int(stmt, 4); song->uri = g_strdup((const char *)sqlite3_column_text(stmt, 5)); *values = g_slist_prepend(*values, song); break; case SQLITE_DONE: break; case SQLITE_BUSY: /* no-op */ break; default: g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); sqlite3_finalize(stmt); return false; } } while (ret != SQLITE_DONE); sqlite3_finalize(stmt); return true; } /** * Increase/Decrease play count of song/artist/album/genre */ bool db_count_artist_expr(const char *expr, int count, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("play_count = play_count + (%d)", count); if (!sql_update_artist(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_count_album_expr(const char *expr, int count, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("play_count = play_count + (%d)", count); if (!sql_update_album(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_count_genre_expr(const char *expr, int count, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("play_count = play_count + (%d)", count); if (!sql_update_genre(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_count_song_expr(const char *expr, int count, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("play_count = play_count + (%d)", count); if (!sql_update_song(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } /** * Love/Hate song/artist/album/genre */ bool db_love_artist_expr(const char *expr, bool love, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("love = love %s 1", love ? "+" : "-"); if (!sql_update_artist(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_love_album_expr(const char *expr, bool love, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("love = love %s 1", love ? "+" : "-"); if (!sql_update_album(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_love_genre_expr(const char *expr, bool love, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("love = love %s 1", love ? "+" : "-"); if (!sql_update_genre(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_love_song_expr(const char *expr, bool love, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("love = love %s 1", love ? "+" : "-"); if (!sql_update_song(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } /** * Kill/Unkill song/artist/album/genre */ bool db_kill_artist_expr(const char *expr, bool kkill, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("kill = %s", kkill ? "kill + 1" : "0"); if (!sql_update_artist(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_kill_album_expr(const char *expr, bool kkill, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("kill = %s", kkill ? "kill + 1" : "0"); if (!sql_update_album(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_kill_genre_expr(const char *expr, bool kkill, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("kill = %s", kkill ? "kill + 1" : "0"); if (!sql_update_genre(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_kill_song_expr(const char *expr, bool kkill, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("kill = %s", kkill ? "kill + 1" : "0"); if (!sql_update_song(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } /** * Rate song/artist/album/genre */ bool db_rate_artist_expr(const char *expr, int rating, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("rating = rating + (%d)", rating); if (!sql_update_artist(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_rate_album_expr(const char *expr, int rating, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("rating = rating + (%d)", rating); if (!sql_update_album(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_rate_genre_expr(const char *expr, int rating, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("rating = rating + (%d)", rating); if (!sql_update_genre(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_rate_song_expr(const char *expr, int rating, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("rating = rating + (%d)", rating); if (!sql_update_song(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } /** * Rate song/artist/album/genre, absolute fashion (ignore previous rating * altogether) */ bool db_rate_absolute_artist_expr(const char *expr, int rating, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("rating = (%d)", rating); if (!sql_update_artist(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_rate_absolute_album_expr(const char *expr, int rating, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("rating = (%d)", rating); if (!sql_update_album(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_rate_absolute_genre_expr(const char *expr, int rating, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("rating = (%d)", rating); if (!sql_update_genre(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_rate_absolute_song_expr(const char *expr, int rating, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("rating = (%d)", rating); if (!sql_update_song(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } /** * Rate song/artist/album/genre, absolute fashion (ignore previous rating * altogether) */ bool db_count_absolute_artist_expr(const char *expr, int count, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("play_count = (%d)", count); if (!sql_update_artist(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_count_absolute_album_expr(const char *expr, int count, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("play_count = (%d)", count); if (!sql_update_album(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_count_absolute_genre_expr(const char *expr, int count, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("play_count = (%d)", count); if (!sql_update_genre(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_count_absolute_song_expr(const char *expr, int count, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); stmt = g_strdup_printf("play_count = (%d)", count); if (!sql_update_song(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } /** * Tags management */ bool db_add_artist_tag_expr(const char *expr, const char *tag, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); if (!validate_tag(tag, error)) return false; stmt = g_strdup_printf("tags = tags || '%s:'", tag); if (!sql_update_artist(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_add_album_tag_expr(const char *expr, const char *tag, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); if (!validate_tag(tag, error)) return false; stmt = g_strdup_printf("tags = tags || '%s:'", tag); if (!sql_update_album(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_add_genre_tag_expr(const char *expr, const char *tag, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); if (!validate_tag(tag, error)) return false; stmt = g_strdup_printf("tags = tags || '%s:'", tag); if (!sql_update_genre(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_add_song_tag_expr(const char *expr, const char *tag, int *changes, GError **error) { char *stmt; g_assert(gdb != NULL); g_assert(expr != NULL); if (!validate_tag(tag, error)) return false; stmt = g_strdup_printf("tags = tags || '%s:'", tag); if (!sql_update_song(stmt, expr, error)) { g_free(stmt); return false; } g_free(stmt); if (changes != NULL) *changes = sqlite3_changes(gdb); return true; } bool db_remove_artist_tag_expr(const char *expr, const char *tag, int *changes, GError **error) { int ret; char *stmt, *sql; GSList *ids, *walk; sqlite3_stmt *sql_stmt; struct map { int id; char *tags; } *map; g_assert(gdb != NULL); g_assert(expr != NULL); if (!validate_tag(tag, error)) return false; sql = g_strdup_printf("select id, tags from artist where %s ;", expr); if (sqlite3_prepare_v2(gdb, sql, -1, &sql_stmt, NULL) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_PREPARE, "sqlite3_prepare_v2: %s", sqlite3_errmsg(gdb)); g_free(sql); return false; } g_free(sql); ids = NULL; do { ret = sqlite3_step(sql_stmt); switch (ret) { case SQLITE_ROW: map = g_new(struct map, 1); map->id = sqlite3_column_int(sql_stmt, 0); map->tags = remove_tag((const char *)sqlite3_column_text(sql_stmt, 1), tag); ids = g_slist_prepend(ids, map); break; case SQLITE_DONE: break; case SQLITE_BUSY: /* no-op */ break; default: g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); sqlite3_finalize(sql_stmt); return false; } } while (ret != SQLITE_DONE); sqlite3_finalize(sql_stmt); if (changes != NULL) *changes = 0; db_set_sync(false, NULL); db_start_transaction(NULL); ret = true; for (walk = ids; walk != NULL; walk = g_slist_next(walk)) { map = (struct map *) walk->data; if (ret) { char *esc_tags = escape_string(map->tags); stmt = g_strdup_printf("tags = %s", esc_tags); g_free(esc_tags); sql = g_strdup_printf("id = %d", map->id); ret = sql_update_artist(stmt, sql, error); g_free(sql); if (changes != NULL) *changes += sqlite3_changes(gdb); } g_free(map->tags); g_free(map); } g_slist_free(ids); db_end_transaction(NULL); db_set_sync(true, NULL); return ret; } bool db_remove_album_tag_expr(const char *expr, const char *tag, int *changes, GError **error) { int ret; char *stmt, *sql; GSList *ids, *walk; sqlite3_stmt *sql_stmt; struct map { int id; char *tags; } *map; g_assert(gdb != NULL); g_assert(expr != NULL); if (!validate_tag(tag, error)) return false; sql = g_strdup_printf("select id, tags from album where %s ;", expr); if (sqlite3_prepare_v2(gdb, sql, -1, &sql_stmt, NULL) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_PREPARE, "sqlite3_prepare_v2: %s", sqlite3_errmsg(gdb)); g_free(sql); return false; } g_free(sql); ids = NULL; do { ret = sqlite3_step(sql_stmt); switch (ret) { case SQLITE_ROW: map = g_new(struct map, 1); map->id = sqlite3_column_int(sql_stmt, 0); map->tags = remove_tag((const char *)sqlite3_column_text(sql_stmt, 1), tag); ids = g_slist_prepend(ids, map); break; case SQLITE_DONE: break; case SQLITE_BUSY: /* no-op */ break; default: g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); sqlite3_finalize(sql_stmt); return false; } } while (ret != SQLITE_DONE); sqlite3_finalize(sql_stmt); if (changes != NULL) *changes = 0; db_set_sync(false, NULL); db_start_transaction(NULL); ret = true; for (walk = ids; walk != NULL; walk = g_slist_next(walk)) { map = (struct map *) walk->data; if (ret) { char *esc_tags = escape_string(map->tags); stmt = g_strdup_printf("tags = %s'", esc_tags); g_free(esc_tags); sql = g_strdup_printf("id = %d", map->id); ret = sql_update_album(stmt, sql, error); g_free(sql); if (changes != NULL) *changes += sqlite3_changes(gdb); } g_free(map->tags); g_free(map); } g_slist_free(ids); db_end_transaction(NULL); db_set_sync(true, NULL); return ret; } bool db_remove_genre_tag_expr(const char *expr, const char *tag, int *changes, GError **error) { int ret; char *stmt, *sql; GSList *ids, *walk; sqlite3_stmt *sql_stmt; struct map { int id; char *tags; } *map; g_assert(gdb != NULL); g_assert(expr != NULL); if (!validate_tag(tag, error)) return false; sql = g_strdup_printf("select id, tags from genre where %s ;", expr); if (sqlite3_prepare_v2(gdb, sql, -1, &sql_stmt, NULL) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_PREPARE, "sqlite3_prepare_v2: %s", sqlite3_errmsg(gdb)); g_free(sql); return false; } g_free(sql); ids = NULL; do { ret = sqlite3_step(sql_stmt); switch (ret) { case SQLITE_ROW: map = g_new(struct map, 1); map->id = sqlite3_column_int(sql_stmt, 0); map->tags = remove_tag((const char *)sqlite3_column_text(sql_stmt, 1), tag); ids = g_slist_prepend(ids, map); break; case SQLITE_DONE: break; case SQLITE_BUSY: /* no-op */ break; default: g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); sqlite3_finalize(sql_stmt); return false; } } while (ret != SQLITE_DONE); sqlite3_finalize(sql_stmt); if (changes != NULL) *changes = 0; db_set_sync(false, NULL); db_start_transaction(NULL); ret = true; for (walk = ids; walk != NULL; walk = g_slist_next(walk)) { map = (struct map *) walk->data; if (ret) { char *esc_tags = escape_string(map->tags); stmt = g_strdup_printf("tags = %s", esc_tags); g_free(esc_tags); sql = g_strdup_printf("id = %d", map->id); ret = sql_update_genre(stmt, sql, error); g_free(sql); if (changes != NULL) *changes += sqlite3_changes(gdb); } g_free(map->tags); g_free(map); } g_slist_free(ids); db_end_transaction(NULL); db_set_sync(true, NULL); return ret; } bool db_remove_song_tag_expr(const char *expr, const char *tag, int *changes, GError **error) { int ret; char *stmt, *sql; GSList *ids, *walk; sqlite3_stmt *sql_stmt; struct map { int id; char *tags; } *map; g_assert(gdb != NULL); g_assert(expr != NULL); if (!validate_tag(tag, error)) return false; sql = g_strdup_printf("select id, tags from song where %s ;", expr); if (sqlite3_prepare_v2(gdb, sql, -1, &sql_stmt, NULL) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_PREPARE, "sqlite3_prepare_v2: %s", sqlite3_errmsg(gdb)); g_free(sql); return false; } g_free(sql); ids = NULL; do { ret = sqlite3_step(sql_stmt); switch (ret) { case SQLITE_ROW: map = g_new(struct map, 1); map->id = sqlite3_column_int(sql_stmt, 0); map->tags = remove_tag((const char *)sqlite3_column_text(sql_stmt, 1), tag); ids = g_slist_prepend(ids, map); break; case SQLITE_DONE: break; case SQLITE_BUSY: /* no-op */ break; default: g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); sqlite3_finalize(sql_stmt); return false; } } while (ret != SQLITE_DONE); sqlite3_finalize(sql_stmt); if (changes != NULL) *changes = 0; db_set_sync(false, NULL); db_start_transaction(NULL); ret = true; for (walk = ids; walk != NULL; walk = g_slist_next(walk)) { map = (struct map *) walk->data; if (ret) { char *esc_tags = escape_string(map->tags); stmt = g_strdup_printf("tags = %s", esc_tags); g_free(esc_tags); sql = g_strdup_printf("id = %d", map->id); ret = sql_update_song(stmt, sql, error); g_free(sql); if (changes != NULL) *changes += sqlite3_changes(gdb); } g_free(map->tags); g_free(map); } g_slist_free(ids); db_end_transaction(NULL); db_set_sync(true, NULL); return ret; } bool db_list_artist_tag_expr(const char *expr, GSList **values, GError **error) { int ret; char *sql; sqlite3_stmt *stmt; struct db_generic_data *data; g_assert(gdb != NULL); g_assert(expr != NULL); g_assert(values != NULL); sql = g_strdup_printf("select id, name, tags from artist where %s ;", expr); if (sqlite3_prepare_v2(gdb, sql, -1, &stmt, NULL) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_PREPARE, "sqlite3_prepare_v2: %s", sqlite3_errmsg(gdb)); g_free(sql); return false; } g_free(sql); do { ret = sqlite3_step(stmt); switch (ret) { case SQLITE_ROW: data = g_new0(struct db_generic_data, 1); data->id = sqlite3_column_int(stmt, 0); data->name = g_strdup((const char *)sqlite3_column_text(stmt, 1)); data->tags = g_strsplit((const char *)sqlite3_column_text(stmt, 2), ":", -1); *values = g_slist_prepend(*values, data); break; case SQLITE_DONE: break; case SQLITE_BUSY: /* no-op */ break; default: g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); sqlite3_finalize(stmt); return false; } } while (ret != SQLITE_DONE); sqlite3_finalize(stmt); return true; } bool db_list_album_tag_expr(const char *expr, GSList **values, GError **error) { int ret; char *sql; sqlite3_stmt *stmt; struct db_generic_data *data; g_assert(gdb != NULL); g_assert(expr != NULL); g_assert(values != NULL); sql = g_strdup_printf("select id, name, artist, tags from album where %s ;", expr); if (sqlite3_prepare_v2(gdb, sql, -1, &stmt, NULL) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_PREPARE, "sqlite3_prepare_v2: %s", sqlite3_errmsg(gdb)); g_free(sql); return false; } g_free(sql); do { ret = sqlite3_step(stmt); switch (ret) { case SQLITE_ROW: data = g_new0(struct db_generic_data, 1); data->id = sqlite3_column_int(stmt, 0); data->name = g_strdup((const char *)sqlite3_column_text(stmt, 1)); data->artist = g_strdup((const char *)sqlite3_column_text(stmt, 2)); data->tags = g_strsplit((const char *)sqlite3_column_text(stmt, 3), ":", -1); *values = g_slist_prepend(*values, data); break; case SQLITE_DONE: break; case SQLITE_BUSY: /* no-op */ break; default: g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); sqlite3_finalize(stmt); return false; } } while (ret != SQLITE_DONE); sqlite3_finalize(stmt); return true; } bool db_list_genre_tag_expr(const char *expr, GSList **values, GError **error) { int ret; char *sql; sqlite3_stmt *stmt; struct db_generic_data *data; g_assert(gdb != NULL); g_assert(expr != NULL); g_assert(values != NULL); sql = g_strdup_printf("select id, name, tags from genre where %s ;", expr); if (sqlite3_prepare_v2(gdb, sql, -1, &stmt, NULL) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_PREPARE, "sqlite3_prepare_v2: %s", sqlite3_errmsg(gdb)); g_free(sql); return false; } g_free(sql); do { ret = sqlite3_step(stmt); switch (ret) { case SQLITE_ROW: data = g_new0(struct db_generic_data, 1); data->id = sqlite3_column_int(stmt, 0); data->name = g_strdup((const char *)sqlite3_column_text(stmt, 1)); data->tags = g_strsplit((const char *)sqlite3_column_text(stmt, 2), ":", -1); *values = g_slist_prepend(*values, data); break; case SQLITE_DONE: break; case SQLITE_BUSY: /* no-op */ break; default: g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); sqlite3_finalize(stmt); return false; } } while (ret != SQLITE_DONE); sqlite3_finalize(stmt); return true; } bool db_list_song_tag_expr(const char *expr, GSList **values, GError **error) { int ret; char *sql; sqlite3_stmt *stmt; struct db_song_data *song; g_assert(gdb != NULL); g_assert(expr != NULL); g_assert(values != NULL); sql = g_strdup_printf("select id, uri, tags from song where %s ;", expr); if (sqlite3_prepare_v2(gdb, sql, -1, &stmt, NULL) != SQLITE_OK) { g_set_error(error, db_quark(), ACK_ERROR_DATABASE_PREPARE, "sqlite3_prepare_v2: %s", sqlite3_errmsg(gdb)); g_free(sql); return false; } g_free(sql); do { ret = sqlite3_step(stmt); switch (ret) { case SQLITE_ROW: song = g_new0(struct db_song_data, 1); song->id = sqlite3_column_int(stmt, 0); song->uri = g_strdup((const char *)sqlite3_column_text(stmt, 1)); song->tags = g_strsplit((const char *)sqlite3_column_text(stmt, 2), ":", -1); *values = g_slist_prepend(*values, song); break; case SQLITE_DONE: break; case SQLITE_BUSY: /* no-op */ break; default: g_set_error(error, db_quark(), ACK_ERROR_DATABASE_STEP, "sqlite3_step: %s", sqlite3_errmsg(gdb)); sqlite3_finalize(stmt); return false; } } while (ret != SQLITE_DONE); sqlite3_finalize(stmt); return true; } mpdcron-0.3+git20110303/src/gmodule/stats/stats-sqlite.h000066400000000000000000000156371153374523700226240ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009, 2010 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef MPDCRON_GUARD_STATS_SQLITE_H #define MPDCRON_GUARD_STATS_SQLITE_H 1 #include #include #include #include #include struct db_generic_data { int id; int play_count; int love; int kill; int rating; char *name; char *artist; char **tags; }; struct db_song_data { int id; /** Id of the song */ int play_count; /** Play count of the song */ int love; /** Love count of the song */ int kill; /** Kill count of the song */ int rating; /** Rating of the song */ char *uri; /** Uri of the song */ int duration; /** Duration of the song */ int last_modified; /** Last modified date of the song */ char *artist; /** Artist of the song */ char *album; /** Album of the song */ char *title; /** Title of the song */ char *track; /** Track number of the song */ char *name; /** Name tag of the song */ char *genre; /** Genre of the song */ char *date; /** Date tag of the song */ char *composer; /** Composer of the song */ char *performer; /** Performer of the song */ char *disc; /** Disc number tag */ char *mb_artist_id; /** Musicbrainz artist ID */ char *mb_album_id; /** Musicbrainz album ID */ char *mb_track_id; /** Musicbrainz track ID */ char **tags; }; enum dback { ACK_ERROR_DATABASE_OPEN = 50, ACK_ERROR_DATABASE_CREATE = 51, ACK_ERROR_DATABASE_VERSION = 52, ACK_ERROR_DATABASE_AUTH = 53, ACK_ERROR_DATABASE_INSERT = 54, ACK_ERROR_DATABASE_SELECT = 55, ACK_ERROR_DATABASE_UPDATE = 56, ACK_ERROR_DATABASE_PREPARE = 57, ACK_ERROR_DATABASE_BIND = 58, ACK_ERROR_DATABASE_STEP = 59, ACK_ERROR_DATABASE_RESET = 60, ACK_ERROR_INVALID_TAG = 101, ACK_ERROR_NO_TAGS = 102, }; /** * Database Interface */ void db_generic_data_free(struct db_generic_data *data); void db_song_data_free(struct db_song_data *song); bool db_initialized(void); bool db_init(const char *path, bool create, bool readonly, GError **error); void db_close(void); bool db_set_authorizer(int (*xAuth)(void *, int, const char *, const char *, const char *,const char *), void *userdata, GError **error); bool db_start_transaction(GError **error); bool db_end_transaction(GError **error); bool db_set_sync(bool on, GError **error); bool db_vacuum(GError **error); bool db_process(const struct mpd_song *song, bool increment, GError **error); bool db_list_artist_expr(const char *expr, GSList **values, GError **error); bool db_list_album_expr(const char *expr, GSList **values, GError **error); bool db_list_genre_expr(const char *expr, GSList **values, GError **error); bool db_list_song_expr(const char *expr, GSList **values, GError **error); bool db_listinfo_artist_expr(const char *expr, GSList **values, GError **error); bool db_listinfo_album_expr(const char *expr, GSList **values, GError **error); bool db_listinfo_genre_expr(const char *expr, GSList **values, GError **error); bool db_listinfo_song_expr(const char *expr, GSList **values, GError **error); bool db_count_artist_expr(const char *expr, int count, int *changes, GError **error); bool db_count_album_expr(const char *expr, int count, int *changes, GError **error); bool db_count_genre_expr(const char *expr, int count, int *changes, GError **error); bool db_count_song_expr(const char *expr, int count, int *changes, GError **error); bool db_love_artist_expr(const char *expr, bool love, int *changes, GError **error); bool db_love_album_expr(const char *expr, bool love, int *changes, GError **error); bool db_love_genre_expr(const char *expr, bool love, int *changes, GError **error); bool db_love_song_expr(const char *expr, bool love, int *changes, GError **error); bool db_kill_artist_expr(const char *expr, bool kkill, int *changes, GError **error); bool db_kill_album_expr(const char *expr, bool kkill, int *changes, GError **error); bool db_kill_genre_expr(const char *expr, bool kkill, int *changes, GError **error); bool db_kill_song_expr(const char *expr, bool kkill, int *changes, GError **error); bool db_rate_artist_expr(const char *expr, int rating, int *changes, GError **error); bool db_rate_album_expr(const char *expr, int rating, int *changes, GError **error); bool db_rate_genre_expr(const char *expr, int rating, int *changes, GError **error); bool db_rate_song_expr(const char *expr, int rating, int *changes, GError **error); bool db_rate_absolute_artist_expr(const char *expr, int rating, int *changes, GError **error); bool db_rate_absolute_album_expr(const char *expr, int rating, int *changes, GError **error); bool db_rate_absolute_genre_expr(const char *expr, int rating, int *changes, GError **error); bool db_rate_absolute_song_expr(const char *expr, int rating, int *changes, GError **error); bool db_count_absolute_artist_expr(const char *expr, int rating, int *changes, GError **error); bool db_count_absolute_album_expr(const char *expr, int rating, int *changes, GError **error); bool db_count_absolute_genre_expr(const char *expr, int rating, int *changes, GError **error); bool db_count_absolute_song_expr(const char *expr, int rating, int *changes, GError **error); bool db_add_artist_tag_expr(const char *expr, const char *tag, int *changes, GError **error); bool db_add_album_tag_expr(const char *expr, const char *tag, int *changes, GError **error); bool db_add_genre_tag_expr(const char *expr, const char *tag, int *changes, GError **error); bool db_add_song_tag_expr(const char *expr, const char *tag, int *changes, GError **error); bool db_remove_artist_tag_expr(const char *expr, const char *tag, int *changes, GError **error); bool db_remove_album_tag_expr(const char *expr, const char *tag, int *changes, GError **error); bool db_remove_genre_tag_expr(const char *expr, const char *tag, int *changes, GError **error); bool db_remove_song_tag_expr(const char *expr, const char *tag, int *changes, GError **error); bool db_list_artist_tag_expr(const char *expr, GSList **values, GError **error); bool db_list_album_tag_expr(const char *expr, GSList **values, GError **error); bool db_list_genre_tag_expr(const char *expr, GSList **values, GError **error); bool db_list_song_tag_expr(const char *expr, GSList **values, GError **error); #endif /* !MPDCRON_GUARD_STATS_SQLITE_H */ mpdcron-0.3+git20110303/src/gmodule/stats/tokenizer.c000066400000000000000000000110421153374523700221560ustar00rootroot00000000000000/* * Copyright (C) 2003-2009 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 "tokenizer.h" #include #include #include G_GNUC_CONST static GQuark tokenizer_quark(void) { return g_quark_from_static_string("tokenizer"); } static inline bool valid_word_first_char(char ch) { return g_ascii_isalpha(ch); } static inline bool valid_word_char(char ch) { return g_ascii_isalnum(ch) || ch == '_'; } char * tokenizer_next_word(char **input_p, GError **error_r) { char *word, *input; assert(input_p != NULL); assert(*input_p != NULL); word = input = *input_p; if (*input == 0) return NULL; /* check the first character */ if (!valid_word_first_char(*input)) { g_set_error(error_r, tokenizer_quark(), 0, "Letter expected"); return NULL; } /* now iterate over the other characters until we find a whitespace or end-of-string */ while (*++input != 0) { if (g_ascii_isspace(*input)) { /* a whitespace: the word ends here */ *input = 0; /* skip all following spaces, too */ input = g_strchug(input + 1); break; } if (!valid_word_char(*input)) { *input_p = input; g_set_error(error_r, tokenizer_quark(), 0, "Invalid word character"); return NULL; } } /* end of string: the string is already null-terminated here */ *input_p = input; return word; } static inline bool valid_unquoted_char(char ch) { return (unsigned char)ch > 0x20 && ch != '"' && ch != '\''; } char * tokenizer_next_unquoted(char **input_p, GError **error_r) { char *word, *input; assert(input_p != NULL); assert(*input_p != NULL); word = input = *input_p; if (*input == 0) return NULL; /* check the first character */ if (!valid_unquoted_char(*input)) { g_set_error(error_r, tokenizer_quark(), 0, "Invalid unquoted character"); return NULL; } /* now iterate over the other characters until we find a whitespace or end-of-string */ while (*++input != 0) { if (g_ascii_isspace(*input)) { /* a whitespace: the word ends here */ *input = 0; /* skip all following spaces, too */ input = g_strchug(input + 1); break; } if (!valid_unquoted_char(*input)) { *input_p = input; g_set_error(error_r, tokenizer_quark(), 0, "Invalid unquoted character"); return NULL; } } /* end of string: the string is already null-terminated here */ *input_p = input; return word; } char * tokenizer_next_string(char **input_p, GError **error_r) { char *word, *dest, *input; assert(input_p != NULL); assert(*input_p != NULL); word = dest = input = *input_p; if (*input == 0) /* end of line */ return NULL; /* check for the opening " */ if (*input != '"') { g_set_error(error_r, tokenizer_quark(), 0, "'\"' expected"); return NULL; } ++input; /* copy all characters */ while (*input != '"') { if (*input == '\\') /* the backslash escapes the following character */ ++input; if (*input == 0) { /* return input-1 so the caller can see the difference between "end of line" and "error" */ *input_p = input - 1; g_set_error(error_r, tokenizer_quark(), 0, "Missing closing '\"'"); return NULL; } /* copy one character */ *dest++ = *input++; } /* the following character must be a whitespace (or end of line) */ ++input; if (*input != 0 && !g_ascii_isspace(*input)) { *input_p = input; g_set_error(error_r, tokenizer_quark(), 0, "Space expected after closing '\"'"); return NULL; } /* finish the string and return it */ *dest = 0; *input_p = g_strchug(input); return word; } char * tokenizer_next_param(char **input_p, GError **error_r) { assert(input_p != NULL); assert(*input_p != NULL); if (**input_p == '"') return tokenizer_next_string(input_p, error_r); else return tokenizer_next_unquoted(input_p, error_r); } mpdcron-0.3+git20110303/src/gmodule/stats/tokenizer.h000066400000000000000000000057541153374523700222000ustar00rootroot00000000000000/* * Copyright (C) 2003-2009 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_TOKENIZER_H #define MPD_TOKENIZER_H #include /** * Reads the next word from the input string. This function modifies * the input string. * * @param input_p the input string; this function returns a pointer to * the first non-whitespace character of the following token * @param error_r if this function returns NULL and **input_p!=0, it * optionally provides a GError object in this argument * @return a pointer to the null-terminated word, or NULL on error or * end of line */ char * tokenizer_next_word(char **input_p, GError **error_r); /** * Reads the next unquoted word from the input string. This function * modifies the input string. * * @param input_p the input string; this function returns a pointer to * the first non-whitespace character of the following token * @param error_r if this function returns NULL and **input_p!=0, it * optionally provides a GError object in this argument * @return a pointer to the null-terminated word, or NULL on error or * end of line */ char * tokenizer_next_unquoted(char **input_p, GError **error_r); /** * Reads the next quoted string from the input string. A backslash * escapes the following character. This function modifies the input * string. * * @param input_p the input string; this function returns a pointer to * the first non-whitespace character of the following token * @param error_r if this function returns NULL and **input_p!=0, it * optionally provides a GError object in this argument * @return a pointer to the null-terminated string, or NULL on error * or end of line */ char * tokenizer_next_string(char **input_p, GError **error_r); /** * Reads the next unquoted word or quoted string from the input. This * is a wrapper for tokenizer_next_unquoted() and * tokenizer_next_string(). * * @param input_p the input string; this function returns a pointer to * the first non-whitespace character of the following token * @param error_r if this function returns NULL and **input_p!=0, it * optionally provides a GError object in this argument * @return a pointer to the null-terminated string, or NULL on error * or end of line */ char * tokenizer_next_param(char **input_p, GError **error_r); #endif mpdcron-0.3+git20110303/src/gmodule/stats/valgrind.sh000077700000000000000000000000001153374523700247272../../valgrind.shustar00rootroot00000000000000mpdcron-0.3+git20110303/src/gmodule/stats/walrus-defs.h000066400000000000000000000017741153374523700224200ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef MPDCRON_GUARD_WALRUS_DEFS_H #define MPDCRON_GUARD_WALRUS_DEFS_H 1 #include "../../cron-config.h" #include "stats-sqlite.h" char * xload_dbpath(void); #endif /* !MPDCRON_GUARD_WALRUS_DEFS_H */ mpdcron-0.3+git20110303/src/gmodule/stats/walrus-main.c000066400000000000000000000105351153374523700224110ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "walrus-defs.h" #include #include #include #include #include static char *dbpath = NULL; static int keepgoing = 0; static GOptionEntry options[] = { {"dbpath", 'd', 0, G_OPTION_ARG_FILENAME, &dbpath, "Path to the database", NULL}, {"keep-going", 'k', 0, G_OPTION_ARG_NONE, &keepgoing, "Keep going in case of database errors", NULL}, { NULL, 0, 0, 0, NULL, NULL, NULL }, }; static bool run_update(int kg, const char *path) { int count; const char *hostname; const char *port; const char *password; GError *error; struct mpd_connection *conn; struct mpd_entity *entity; const struct mpd_song *song; if ((hostname = g_getenv(ENV_MPD_HOST)) == NULL) hostname = "localhost"; if ((port = g_getenv(ENV_MPD_PORT)) == NULL) port = "6600"; password = g_getenv(ENV_MPD_PASSWORD); if ((conn = mpd_connection_new(hostname, atoi(port), 0)) == NULL) { g_printerr("Error creating mpd connection: out of memory\n"); return false; } if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) { g_printerr("Failed to connect to Mpd: %s\n", mpd_connection_get_error_message(conn)); mpd_connection_free(conn); return false; } if (password != NULL) { if (!mpd_run_password(conn, password)) { g_printerr("Authentication failed: %s\n", mpd_connection_get_error_message(conn)); mpd_connection_free(conn); return false; } } if (!mpd_send_list_all_meta(conn, path)) { g_printerr("Failed to list Mpd database: %s\n", mpd_connection_get_error_message(conn)); mpd_connection_free(conn); return false; } count = 0; while ((entity = mpd_recv_entity(conn)) != NULL) { if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) { song = mpd_entity_get_song(entity); error = NULL; if (!db_process(song, false, &error)) { g_printerr("Failed to process song %s: %s\n", mpd_song_get_uri(song), error->message); g_error_free(error); if (kg) continue; return false; } else if (error != NULL) { /* Song doesn't have required tags */ g_printerr("Skipped processing song %s: %s\n", mpd_song_get_uri(song), error->message); g_error_free(error); } ++count; printf("%s\n", mpd_song_get_uri(song)); } mpd_entity_free(entity); } mpd_response_finish(conn); mpd_connection_free(conn); printf("Successfully processed %d songs\n", count); return true; } int main(int argc, char **argv) { GOptionContext *ctx; GError *error; ctx = g_option_context_new("[PATH...]"); g_option_context_add_main_entries(ctx, options, "walrus"); g_option_context_set_summary(ctx, "walrus-"VERSION GITHEAD" - Create/Update mpdcron-stats database"); if (!g_option_context_parse(ctx, &argc, &argv, &error)) { g_printerr("Option parsing failed: %s\n", error->message); g_error_free(error); g_option_context_free(ctx); return 1; } g_option_context_free(ctx); if (dbpath == NULL) dbpath = xload_dbpath(); error = NULL; if (!db_init(dbpath, true, false, &error)) { g_printerr("Failed to load database `%s': %s\n", dbpath, error->message); g_error_free(error); g_free(dbpath); return 1; } g_free(dbpath); db_set_sync(false, NULL); db_start_transaction(NULL); if (argc > 1) { for (int i = 1; i < argc; i++) { fprintf(stderr, "* Updating %s\n", argv[i]); if (!run_update(keepgoing, argv[i])) { db_close(); return 1; } } } else { fprintf(stderr, "* Updating /\n"); if (!run_update(keepgoing, NULL)) { db_close(); return 1; } } db_end_transaction(NULL); db_vacuum(NULL); db_close(); return 0; } mpdcron-0.3+git20110303/src/gmodule/stats/walrus-utils.c000066400000000000000000000043031153374523700226210ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009 Ali Polatel * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "walrus-defs.h" #include #include #include "../utils.h" char * xload_dbpath(void) { char *homepath, *confpath, *dbpath; GKeyFile *kf; GError *error; /* Get home directory */ if (g_getenv(ENV_HOME_DIR)) homepath = g_strdup(g_getenv(ENV_HOME_DIR)); else if (g_getenv("HOME")) homepath = g_build_filename(g_getenv("HOME"), DOT_MPDCRON, NULL); else { g_printerr("Neither "ENV_HOME_DIR" nor HOME is set, exiting!"); exit(1); } /* Set keyfile path */ confpath = g_build_filename(homepath, PACKAGE".conf", NULL); /* Load key file */ dbpath = NULL; error = NULL; kf = g_key_file_new(); if (!g_key_file_load_from_file(kf, confpath, G_KEY_FILE_NONE, &error)) { switch (error->code) { case G_FILE_ERROR_NOENT: case G_KEY_FILE_ERROR_NOT_FOUND: g_error_free(error); g_key_file_free(kf); goto skip; default: g_printerr("Failed to parse configuration file `%s': %s", confpath, error->message); g_error_free(error); g_key_file_free(kf); exit(1); } } error = NULL; if (!load_string(kf, "stats", "dbpath", false, &dbpath, &error)) { g_printerr("%s\n", error->message); g_error_free(error); g_free(confpath); g_free(homepath); g_key_file_free(kf); exit(1); } g_key_file_free(kf); skip: if (dbpath == NULL) dbpath = g_build_filename(homepath, "stats.db", NULL); g_free(confpath); g_free(homepath); return dbpath; } mpdcron-0.3+git20110303/src/gmodule/utils.h000066400000000000000000000051121153374523700201540ustar00rootroot00000000000000/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */ /* * Copyright (c) 2009 Ali Polatel * Based in part upon mpdscribble which is: * Copyright (C) 2008-2009 The Music Player Daemon Project * Copyright (C) 2005-2008 Kuno Woudt * * This file is part of the mpdcron mpd client. mpdcron is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * mpdcron 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef MPDCRON_GUARD_UTILS_H #define MPDCRON_GUARD_UTILS_H 1 #include #include static GQuark kf_quark(void) { return g_quark_from_static_string("keyfile"); } G_GNUC_UNUSED static bool load_string(GKeyFile *fd, const char *grp, const char *name, bool mustload, char **value_r, GError **error) { char *value; GError *e = NULL; if (*value_r != NULL) { /* already set */ return true; } value = g_key_file_get_string(fd, grp, name, &e); if (e != NULL) { switch (e->code) { case G_KEY_FILE_ERROR_GROUP_NOT_FOUND: case G_KEY_FILE_ERROR_KEY_NOT_FOUND: if (!mustload) { g_error_free(e); value = NULL; break; } /* fall through */ default: g_set_error(error, kf_quark(), e->code, "Failed to load string %s.%s: %s", grp, name, e->message); g_error_free(e); return false; } } *value_r = value; return true; } G_GNUC_UNUSED static bool load_integer(GKeyFile *fd, const char *grp, const char *name, int mustload, int *value_r, GError **error) { int value; GError *e = NULL; if (*value_r != -1) { /* already set */ return true; } value = g_key_file_get_integer(fd, grp, name, &e); if (e != NULL) { switch (e->code) { case G_KEY_FILE_ERROR_GROUP_NOT_FOUND: case G_KEY_FILE_ERROR_KEY_NOT_FOUND: if (!mustload) { g_error_free(e); value = -1; break; } /* fall through */ default: g_set_error(error, kf_quark(), e->code, "Failed to load integer %s.%s: %s", grp, name, e->message); g_error_free(e); return false; } } *value_r = value; return true; } #endif /* !MPDCRON_GUARD_UTILS_H */ mpdcron-0.3+git20110303/src/valgrind.sh000077500000000000000000000006011153374523700173520ustar00rootroot00000000000000#!/bin/sh # vim: set sw=4 et sts=4 tw=80 : # Notes about valgrind & mpdcron: # - Debugging stats module under valgrind fails when the module does a DNS # resolution. abspath=$(readlink -f "$0") absdir=$(dirname "$abspath") export G_SLICE=always-malloc exec valgrind \ --leak-check=full \ --track-origins=yes \ --suppressions="$absdir"/valgrind.suppressions \ "$@" mpdcron-0.3+git20110303/src/valgrind.suppressions000066400000000000000000000164031153374523700215210ustar00rootroot00000000000000# Valgrind suppressions file for mpdcron { g_main_context_dispatch Memcheck:Leak fun:calloc fun:g_malloc0 fun:g_source_new fun:g_idle_source_new fun:g_simple_async_result_complete_in_idle fun:accept_ready fun:g_main_context_dispatch } { g_type_init Memcheck:Leak fun:malloc fun:realloc fun:g_realloc fun:type_node_any_new_W fun:g_type_register_static fun:g_type_plugin_get_type fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:malloc fun:realloc fun:g_realloc fun:type_node_any_new_W fun:g_type_register_static fun:g_boxed_type_register_static fun:g_value_array_get_type fun:g_param_spec_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:malloc fun:realloc fun:g_realloc fun:type_node_any_new_W fun:g_type_register_static fun:g_gtype_get_type fun:g_param_spec_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:realloc fun:g_realloc fun:type_node_any_new_W fun:g_type_register_static fun:g_param_type_register_static fun:g_param_spec_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_add_flags_W fun:g_type_register_fundamental fun:g_value_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:malloc fun:realloc fun:g_realloc fun:type_add_flags_W fun:g_type_register_fundamental fun:g_value_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_data_make_W fun:g_type_register_fundamental fun:g_value_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_add_flags_W fun:g_type_register_fundamental fun:g_enum_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:malloc fun:realloc fun:g_realloc fun:type_add_flags_W fun:g_type_register_fundamental fun:g_enum_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_add_flags_W fun:g_type_register_fundamental fun:g_enum_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:malloc fun:realloc fun:g_realloc fun:type_add_flags_W fun:g_type_register_fundamental fun:g_enum_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_data_make_W fun:g_type_register_fundamental fun:g_enum_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_add_flags_W fun:g_type_register_fundamental fun:g_boxed_type_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:malloc fun:realloc fun:g_realloc fun:type_add_flags_W fun:g_type_register_fundamental fun:g_boxed_type_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_add_flags_W fun:g_type_register_fundamental fun:g_param_type_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:malloc fun:realloc fun:g_realloc fun:type_add_flags_W fun:g_type_register_fundamental fun:g_param_type_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_data_make_W fun:g_type_register_fundamental fun:g_param_type_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_add_flags_W fun:g_type_register_fundamental fun:g_object_type_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:malloc fun:realloc fun:g_realloc fun:type_add_flags_W fun:g_type_register_fundamental fun:g_object_type_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_data_make_W fun:g_type_register_fundamental fun:g_object_type_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_data_make_W fun:g_type_register_fundamental fun:g_boxed_type_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_node_any_new_W fun:type_node_fundamental_new_W fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_data_make_W fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_node_any_new_W fun:type_node_fundamental_new_W fun:g_type_register_fundamental fun:g_object_type_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_node_any_new_W fun:type_node_fundamental_new_W fun:g_type_register_fundamental fun:g_param_type_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_node_any_new_W fun:type_node_fundamental_new_W fun:g_type_register_fundamental fun:g_boxed_type_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_node_any_new_W fun:type_node_fundamental_new_W fun:g_type_register_fundamental fun:g_enum_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_node_any_new_W fun:type_node_fundamental_new_W fun:g_type_register_fundamental fun:g_enum_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_node_any_new_W fun:type_node_fundamental_new_W fun:g_type_register_fundamental fun:g_value_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_node_any_new_W fun:type_node_fundamental_new_W fun:g_type_register_fundamental fun:g_value_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_node_any_new_W fun:type_node_fundamental_new_W fun:g_type_register_fundamental fun:g_value_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_node_any_new_W fun:type_node_fundamental_new_W fun:g_type_register_fundamental fun:g_value_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_node_any_new_W fun:type_node_fundamental_new_W fun:g_type_register_fundamental fun:g_value_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_node_any_new_W fun:type_node_fundamental_new_W fun:g_type_register_fundamental fun:g_value_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_node_any_new_W fun:type_node_fundamental_new_W fun:g_type_register_fundamental fun:g_value_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_node_any_new_W fun:type_node_fundamental_new_W fun:g_type_register_fundamental fun:g_value_types_init fun:g_type_init_with_debug_flags } { g_socket_service_changed Memcheck:Leak fun:malloc fun:g_malloc fun:g_slice_alloc fun:g_slice_alloc0 fun:g_type_create_instance fun:g_object_constructor fun:g_object_newv fun:g_object_new_valist fun:g_object_new fun:g_simple_async_result_new fun:g_socket_listener_accept_socket_async fun:g_socket_service_changed } mpdcron-0.3+git20110303/zsh-completion/000077500000000000000000000000001153374523700173745ustar00rootroot00000000000000mpdcron-0.3+git20110303/zsh-completion/Makefile.am000066400000000000000000000000601153374523700214240ustar00rootroot00000000000000SUBDIRS= . EXTRA_DIST= _eugene _mpdcron _walrus mpdcron-0.3+git20110303/zsh-completion/_eugene000066400000000000000000000057051153374523700207350ustar00rootroot00000000000000#compdef eugene # vim: set et sw=4 sts=4 ts=4 ft=zsh : # ZSH completion for _eugene # Copyright (c) 2010 Ali Polatel # Distributed under the terms of the GNU General Public License v2 _eugene_command() { local eugene_cmds eugene_cmds=( help:"Display help and exit" version:"Display version and exit" list:"List song/artist/album/genre" listinfo:"List information of song/artist/album/genre" count:"Change play count of song/artist/album/genre" hate:"Hate song/artist/album/genre" love:"Love song/artist/album/genre" kill:"Kill song/artist/album/genre" unkill:"Unkill song/artist/album/genre" rate:"Rate song/artist/album/genre" addtag:"Add tag to song/artist/album/genre" rmtag:"Remove tag from song/artist/album/genre" listtags:"List tags of song/artist/album/genre" ) if (( CURRENT == 1 )); then _describe -t command "eugene commands" eugene_cmds else local curcontext="$curcontext" fi local cmd=$words[1] local curcontext="${curcontext%:*}:mpc-${cmd}" _call_function ret _eugene_$cmd } _eugene_helper_expr() { _arguments \ '(-h --help)'{-h,--help}'[Show help options]' \ '(-V --version)'{-V,--version}'[Display version]' \ '(-a --artist)'{-a,--artist}'[List artists instead of songs]' \ '(-A --album)'{-A,--album}'[List albums instead of songs]' \ '(-g --genre)'{-g,--genre}'[List genres instead of songs]' \ ':expression:' } _eugene_helper_number_expr() { _arguments \ '(-h --help)'{-h,--help}'[Show help options]' \ '(-V --version)'{-V,--version}'[Display version]' \ '(-a --artist)'{-a,--artist}'[List artists instead of songs]' \ '(-A --album)'{-A,--album}'[List albums instead of songs]' \ '(-g --genre)'{-g,--genre}'[List genres instead of songs]' \ ':number:' \ ':expression:' } _eugene_helper_tag_expr() { _arguments \ '(-h --help)'{-h,--help}'[Show help options]' \ '(-V --version)'{-V,--version}'[Display version]' \ '(-a --artist)'{-a,--artist}'[List artists instead of songs]' \ '(-A --album)'{-A,--album}'[List albums instead of songs]' \ '(-g --genre)'{-g,--genre}'[List genres instead of songs]' \ ':tag:' \ ':expression:' } _eugene_help() {} _eugene_version() {} _eugene_list() { _eugene_helper_expr } _eugene_listinfo() { _eugene_helper_expr } _eugene_count() { _eugene_helper_number_expr } _eugene_hate() { _eugene_helper_expr } _eugene_love() { _eugene_helper_expr } _eugene_kill() { _eugene_helper_expr } _eugene_unkill() { _eugene_helper_expr } _eugene_rate() { _eugene_helper_number_expr } _eugene_addtag() { _eugene_helper_tag_expr } _eugene_rmtag() { _eugene_helper_tag_expr } _eugene_listtags() { _eugene_helper_expr } _arguments \ '*::eugene command:_eugene_command' mpdcron-0.3+git20110303/zsh-completion/_mpdcron000066400000000000000000000006711153374523700211240ustar00rootroot00000000000000#compdef mpdcron # vim: set et sw=4 sts=4 ts=4 ft=zsh : # ZSH completion for _mpdcron # Copyright (c) 2010 Ali Polatel # Distributed under the terms of the GNU General Public License v2 _arguments \ '(-h --help)'{-h,--help}'[Show help options]' \ '(-V --version)'{-V,--version}'[Display version]' \ '(-k --kill)'{-k,--kill}'[Kill daemon]' \ '(-n --no-daemon)'{-n,--no-daemon}'[Dont detach from console]' mpdcron-0.3+git20110303/zsh-completion/_walrus000066400000000000000000000015051153374523700207740ustar00rootroot00000000000000#compdef walrus # vim: set et sw=4 sts=4 ts=4 ft=zsh : # ZSH completion for _walrus # Requires mpc # Copyright (c) 2010 Ali Polatel # Distributed under the terms of the GNU General Public License v2 _mpc_helper_files() { local -U list expl if [[ $words[CURRENT] != */* ]]; then list=( ${${(f)"$(mpc listall)"}%%/*}) _wanted files expl file compadd -qS/ -a list else list=(${(f)"$(mpc tab $words[CURRENT])"}) _wanted files expl file _multi_parts / list fi } _arguments \ '(-h --help)'{-h,--help}'[Show help options]' \ '(-V --version)'{-V,--version}'[Display version]' \ '(-d --dbpath)'{-d,--dbpath=}'[Path to the database]:file:_files' \ '(-k --keep-going)'{-k,--keep-going}'[Keep going in case of database errors]' \ '*::path:_mpc_helper_files'