Commit 1e912f80 authored by Led's avatar Led

0.5.2

parents
Music Player Daemon - Commands
This document is intended for client developers, not end users.
Format:
-------
If arguments contain spaces, they should be surrounded by double quotation
marks, ".
command <type arg1> <type arg2> ...
explanation: w/ arg1 and arg2
Command Completion:
-------------------
A command returns "OK\n" on completeion or "ACK some error\n" on failure.
These denote the end of command execution.
Commands:
---------
add <string file>
add the file _file_ to the playlist
clear
clears the current playlist
close
close the connection with the mpd
delete <int song>
delete _song_ from playlist
find <string type> <string what>
finds songs in the db that are exactly _what_
_type_ should be "album", "artist", or "title"
_what_ is what to find
info <string file>
fetches id3 tag of _file_
NOTE: do not use this, meant for debugging only
kill
kill mpd
load <string name>
loads the playlist _name_.m3u from the playlist directory
ls <string directory>
list files in _directory_. _directory_ is optional.
NOTE: us this only for debugging, not meant to be used by a client.
instead use 'lsinfo'
lsinfo <string directory>
list contents of _directory_, from the db. _directory_ is optional
pause
pause/resume playing
play <int song>
being playling playlist at song number _song_
playlist
displays the current playlist
NOTE: do not use this, instead use 'playlistinfo'
playlistinfo
displays information about the current playlist
rm <string name>
removes the playlist <name>.m3u from the playlist directory
save <string name>
saves the current playlist to _name_.m3u in the playlist directory
search <string type> <string what>
same as "find" but searches for any song that contain _what_
shuffle
shuffles the current playlist
status
reports current status of player, one of the folloowing:
"stop" - player is stopped
"play <int song> <int elapsed>:<time total>"
- playing _song_ with _elapses_ seconds, and the song
is a total of _total_ seconds
"pause <int song> <int elapsed>:<tine total>"
- same as "play" but the player is currently paused
stop
stop playing
update
searches mp3 directory for new music and removes old music from the db
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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
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.
<signature of Ty Coon>, 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.
Music Player Daemon (MPD) - INSTALL
Requirements
------------
mpg123 - http://www.mpg123.de (MPD has been tested with 0.59q and 0.59r)
(mpg123 is included with many distributions and supports many platforms)
Download
--------
Get the latest release from of MPD from http://musicpd.sourceforge.net
Compile
-------
1) unzip and untar the argchive
$ tar zxvf mpd-x.x.x.tar.gz
2) change to directory created
$ cd mpd-x.x.x
3) compile
$ make
Install
-------
1) copy "mpd" to a directory in path (Optional)
(as root)
$ cp mpd /usr/local/bin
Run
---
1) run mpd:
<port>: port number daemon listens on (if running as a user, this should be
greater than 1024)
<mp3 directory>: directory containing mp3's
<playlist directory>: directory where playlists will be stored (and .mpddb will
be placed)
<mpd log>: log file for mpd
<mpd err>: error log file for mpd
$ mpd <port> <mp3 directory> <playlist directory> <mpd log> <mpd err>
example where mpd executable is in mpd-x.x.x directory:
$ mpd-x.x.x/mpd 2100 mp3 playlists mpd.log mpd.err
Note: The first time you run mpd, it will "explore" your mp3 directory for
mp3's.
Using MPD
---------
You can download a web interface (phpMp) to MPD at
http://musicpd.sourceforge.net .
MPD can be interfaced directly using telnet (see command.c, if you are brave).
EXEC = mpd
CC = gcc
CFLAGS = -Wall
OFLAGS = -Wall
DFLAGS = -MM
SOURCES = main.c buffer2array.c mpg123.c interface.c command.c playlist.c ls.c \
info.c id3v2lib/charset.c id3v2lib/lib_id3v2.3.c id3v1lib/lib_id3v1.c \
id3v2lib/lib_id3v2.2.c song.c list.c directory.c tables.c utils.c \
tag.c
OBJECTS = $(SOURCES:.c=.o)
DEPENDFILE =Makefile.depend
all: $(EXEC)
$(EXEC): $(OBJECTS)
$(CC) $(OFLAGS) -o $(EXEC) $(OBJECTS)
deps:
$(CC) $(DFLAGS) $(SOURCES) > $(DEPENDFILE)
-include $(DEPENDFILE)
clean:
rm -f $(EXEC) $(OBJECTS)
cleandeps:
rm -f $(DEPENDFILE)
cleanall: clean cleandeps
Music Player Daemon (MPD)
http://musicpd.sourceforge.net
A daemon for playing music (currently mp3's). Music is played through the
server's audio device. The daemon stores info about all availabe music, and
this info can be easily searched and retrieved. Player control, info retrieval,
and playlist management can all be managed remotely.
To install MPD, see INSTALL.
MPD is released under the GNU Public License.
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
For the full license, see COPYING.
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#include "buffer2array.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int buffer2array(char * buffer, char *** array) {
int bufferLength = strlen(buffer);
int quotes = 0;
int count = 0;
int i;
int curr;
char * markArray = malloc(sizeof(char)*(bufferLength+1));
int * beginArray;
for(curr=0;curr<bufferLength;curr++) {
if(!quotes && buffer[curr]==' ') {
markArray[curr] = '0';
}
else if(buffer[curr] == '\"') {
if(curr>0 && buffer[curr-1]!='\\') {
quotes = quotes?0:1;
markArray[curr] = '0';
}
else {
markArray[curr] = '1';
}
}
else {
markArray[curr] = '1';
}
if(markArray[curr]=='1') {
if(curr>0) {
if(markArray[curr-1]=='0') {
count++;
}
}
else {
count++;
}
}
}
markArray[bufferLength] = '\0';
beginArray = malloc(sizeof(int)*count);
(*array) = malloc(sizeof(char *)*count);
count = 0;
for(curr=0;curr<bufferLength;curr++) {
if(markArray[curr]=='1') {
if(curr>0) {
if(markArray[curr-1]=='0') {
beginArray[count++] = curr;
}
}
else {
beginArray[count++] = curr;
}
}
else {
buffer[curr] = '\0';
}
}
for(i=0;i<count;i++) {
int len = strlen(buffer+beginArray[i])+1;
int arrayCurr = 0;
(*array)[i] = malloc(sizeof(char)*len);
for(curr=beginArray[i];buffer[curr]!='\0';curr++) {
if(buffer[curr]=='\\') {
if(buffer[curr+1]!='\0') {
curr++;
}
}
(*array)[i][arrayCurr++] = buffer[curr];
}
(*array)[i][arrayCurr] = '\0';
}
free(markArray);
free(beginArray);
return count;
}
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#ifndef BUFFER_2_ARRAY_H
#define BUFFER_2_ARRAY_H
int buffer2array(char * buffer, char *** array);
#endif
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#include "command.h"
#include "mpg123.h"
#include "playlist.h"
#include "ls.h"
#include "info.h"
#include "directory.h"
#include "tables.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define COMMAND_PLAY "play"
#define COMMAND_STOP "stop"
#define COMMAND_PAUSE "pause"
#define COMMAND_STATUS "status"
#define COMMAND_KILL "kill"
#define COMMAND_CLOSE "close"
#define COMMAND_ADD "add"
#define COMMAND_DELETE "delete"
#define COMMAND_PLAYLIST "playlist"
#define COMMAND_SHUFFLE "shuffle"
#define COMMAND_CLEAR "clear"
#define COMMAND_SAVE "save"
#define COMMAND_LOAD "load"
#define COMMAND_LS "ls"
#define COMMAND_LSINFO "lsinfo"
#define COMMAND_RM "rm"
#define COMMAND_INFO "info"
#define COMMAND_PLAYLISTINFO "playlistinfo"
#define COMMAND_FIND "find"
#define COMMAND_SEARCH "search"
#define COMMAND_UPDATE "update"
extern Playlist playlist;
int commandStatus(FILE * fp,int argArrayLength, char ** argArray) {
char * state;
if(argArrayLength!=1) {
fprintf(fp,"%s Wrong number of arguments for \"%s\"\n",COMMAND_RESPOND_ERROR,argArray[0]);
return -1;
}
switch(mpg123_state) {
case MPG123_STATE_STOP:
state = strdup(COMMAND_STOP);
break;
case MPG123_STATE_PAUSE:
state = strdup(COMMAND_PAUSE);
break;
case MPG123_STATE_PLAY:
state = strdup(COMMAND_PLAY);
break;
}
if(mpg123_state>0) {
fprintf(fp,"%s %i %i:%i\n",state,playlist.current,mpg123_elapsedTime,mpg123_totalTime);
}
else {
fprintf(fp,"%s\n",state);
}
free(state);
return 0;
}
int processCommand(FILE * fp, int argArrayLength, char ** argArray) {
if(argArrayLength==0) {
return 0;
}
if(0==strcmp(argArray[0],COMMAND_PLAY)) {
int song;
char * test;
if(argArrayLength!=2) {
fprintf(fp,"%s too many arguments for \"%s\"\n",COMMAND_RESPOND_ERROR,argArray[0]);
return -1;
}
song = strtol(argArray[1],&test,10);
if(*test!='\0') {
fprintf(fp,"%s need a positive integer\n",COMMAND_RESPOND_ERROR);
return -1;
}
return playPlaylist(fp,song);
}
else if(0==strcmp(argArray[0],COMMAND_STOP)) {
if(argArrayLength!=1) {
fprintf(fp,"%s wrong number of arguments for \"%s\"\n",COMMAND_RESPOND_ERROR,argArray[0]);
return -1;
}
return mpg123stop(fp);
}
else if(0==strcmp(argArray[0],COMMAND_PAUSE)) {
if(argArrayLength!=1) {
fprintf(fp,"%s wrong number of arguments for \"%s\"\n",COMMAND_RESPOND_ERROR,argArray[0]);
return -1;
}
return mpg123pause(fp);
}
else if(0==strcmp(argArray[0],COMMAND_STATUS)) {
return commandStatus(fp,argArrayLength,argArray);
}
else if(0==strcmp(argArray[0],COMMAND_KILL)) {
return COMMAND_RETURN_KILL;
}
else if(0==strcmp(argArray[0],COMMAND_CLOSE)) {
return COMMAND_RETURN_CLOSE;
}
else if(0==strcmp(argArray[0],COMMAND_ADD)) {
if(argArrayLength!=2) {
fprintf(fp,"%s wrong number of arguments for \"%s\"\n",COMMAND_RESPOND_ERROR,argArray[0]);
return -1;
}
return addToPlaylist(fp,argArray[1]);
}
else if(0==strcmp(argArray[0],COMMAND_INFO)) {
if(argArrayLength!=2) {
fprintf(fp,"%s wrong number of arguments for \"%s\"\n",COMMAND_RESPOND_ERROR,argArray[0]);
return -1;
}
return info(fp,argArray[1]);
}
else if(0==strcmp(argArray[0],COMMAND_SAVE)) {
char * file;
int ret;
if(argArrayLength!=2) {
fprintf(fp,"%s wrong number of arguments for \"%s\"\n",COMMAND_RESPOND_ERROR,argArray[0]);
return -1;
}
file = malloc(strlen(playlistDir)+strlen(argArray[1])+strlen(PLAYLIST_FILE_SUFFIX)+2);
strcpy(file,playlistDir);
strcat(file,argArray[1]);
strcat(file,".");
ret = savePlaylist(fp,strcat(file,PLAYLIST_FILE_SUFFIX));
free(file);
return ret;
}
else if(0==strcmp(argArray[0],COMMAND_RM)) {
char * file;
int ret;
if(argArrayLength!=2) {
fprintf(fp,"%s wrong number of arguments for \"%s\"\n",COMMAND_RESPOND_ERROR,argArray[0]);
return -1;
}
file = malloc(strlen(playlistDir)+strlen(argArray[1])+strlen(PLAYLIST_FILE_SUFFIX)+2);
strcpy(file,playlistDir);
strcat(file,argArray[1]);
strcat(file,".");
ret = deletePlaylist(fp,strcat(file,PLAYLIST_FILE_SUFFIX));
free(file);
return ret;
}
else if(0==strcmp(argArray[0],COMMAND_DELETE)) {
int song;
char * test;
if(argArrayLength!=2) {
fprintf(fp,"%s wrong number of arguments for \"%s\"\n",COMMAND_RESPOND_ERROR,argArray[0]);
return -1;
}
song = strtol(argArray[1],&test,10);
if(*test!='\0') {
fprintf(fp,"%s need a positive integer\n",COMMAND_RESPOND_ERROR);
return -1;
}
return deleteFromPlaylist(fp,song);
}
else if(0==strcmp(argArray[0],COMMAND_PLAYLISTINFO)) {
int song = -1;
char * test;
if(argArrayLength>2) {
fprintf(fp,"%s wrong number of arguments for \"%s\"\n",COMMAND_RESPOND_ERROR,argArray[0]);
return -1;
}
if(argArrayLength==2) {
song = strtol(argArray[1],&test,10);
if(*test!='\0') {
fprintf(fp,"%s need a positive integer\n",COMMAND_RESPOND_ERROR);
return -1;
}
}
return playlistInfo(fp,song);
}
else if(0==strcmp(argArray[0],COMMAND_PLAYLIST)) {
if(argArrayLength!=1) {
fprintf(fp,"%s wrong number of arguments for \"%s\"\n",COMMAND_RESPOND_ERROR,argArray[0]);
return -1;
}
return showPlaylist(fp);
}
else if(0==strcmp(argArray[0],COMMAND_SHUFFLE)) {
if(argArrayLength!=1) {
fprintf(fp,"%s too many arguments for \"%s\"\n",COMMAND_RESPOND_ERROR,argArray[0]);
return -1;
}
return shufflePlaylist(fp);
}
else if(0==strcmp(argArray[0],COMMAND_CLEAR)) {
if(argArrayLength!=1) {
fprintf(fp,"%s wrong number of arguments for \"%s\"\n",COMMAND_RESPOND_ERROR,argArray[0]);
return -1;
}
return clearPlaylist(fp);
}
else if(0==strcmp(argArray[0],COMMAND_LOAD)) {
char * file;
int ret = 0;
if(argArrayLength!=2) {
fprintf(fp,"%s wrong number of arguments for \"%s\"\n",COMMAND_RESPOND_ERROR,argArray[0]);
return -1;
}
file = malloc(strlen(playlistDir)+strlen(argArray[1])+strlen(PLAYLIST_FILE_SUFFIX)+2);
strcpy(file,playlistDir);
strcat(file,argArray[1]);
strcat(file,".");
if(loadPlaylist(fp,strcat(file,PLAYLIST_FILE_SUFFIX))<0) ret=-1;
free(file);
return ret;
}
else if(0==strcmp(argArray[0],COMMAND_LS)) {
if(argArrayLength>2) {
fprintf(fp,"%s wrong number of arguments for \"%s\"\n",COMMAND_RESPOND_ERROR,argArray[0]);
return -1;
}
if(argArrayLength==1) {
if(ls(fp,NULL)<0) return -1;
else return lsPlaylists(fp);
}
else {
return ls(fp,argArray[1]);
}
}
else if(0==strcmp(argArray[0],COMMAND_LSINFO)) {
if(argArrayLength>2) {
fprintf(fp,"%s wrong number of arguments for \"%s\"\n",COMMAND_RESPOND_ERROR,argArray[0]);
return -1;
}
if(argArrayLength==1) {
if(printDirectoryInfo(fp,NULL)<0) return -1;
else return lsPlaylists(fp);
}
else {
return printDirectoryInfo(fp,argArray[1]);
}
}
else if(0==strcmp(argArray[0],COMMAND_FIND)) {
if(argArrayLength!=3) {
fprintf(fp,"%s wrong number of arguments for \"%s\"\n",COMMAND_RESPOND_ERROR,argArray[0]);
return -1;
}
return findAndPrintSongsInTable(fp,argArray[1],argArray[2]);
}
else if(0==strcmp(argArray[0],COMMAND_SEARCH)) {
if(argArrayLength!=3) {
fprintf(fp,"%s wrong number of arguments for \"%s\"\n",COMMAND_RESPOND_ERROR,argArray[0]);
return -1;
}
return searchForSongsInTable(fp,argArray[1],argArray[2]);
}
else if(0==strcmp(argArray[0],COMMAND_UPDATE)) {
if(argArrayLength!=1) {
fprintf(fp,"%s wrong number of arguments for \"%s\"\n",COMMAND_RESPOND_ERROR,argArray[0]);
return -1;
}
return updateMp3Directory(fp);
}
else {
fprintf(fp,"%s Unknown command \"%s\"\n",COMMAND_RESPOND_ERROR,argArray[0]);
return -1;
}
return 0;
}
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#ifndef COMMAND_H
#define COMMAND_H
#include <stdio.h>
#define COMMAND_RETURN_KILL 10
#define COMMAND_RETURN_CLOSE 20
#define COMMAND_RESPOND_ERROR "ACK"
#define COMMAND_RESPOND_OK "OK"
int processCommand(FILE * fp, int argArrayLength, char ** argArray);
#endif
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#include "directory.h"
#include "ls.h"
#include "command.h"
#include "tables.h"
#include "utils.h"
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <stdio.h>
#include "list.h"
#include "song.h"
#define DIRECTORY_DIR "directory: "
#define DIRECTORY_MTIME "mtime: "
#define DIRECTORY_BEGIN "begin: "
#define DIRECTORY_END "end: "
typedef List DirectoryList;
typedef struct _Directory {
char * name;
DirectoryList * subDirectories;
struct _Directory * parentDirectory;
SongList * songs;
time_t mtime; /* modification time */
} Directory;
Directory * mp3rootDirectory;
char directorydb[MAXPATHLEN+1];
DirectoryList * newDirectoryList();
int addToDirectory(Directory * directory, char * shortname, char * name);
void freeDirectoryList(DirectoryList * list);
void freeDirectory(Directory * directory);
int exploreDirectory(Directory * directory);
int updateDirectory(Directory * directory);
int addSubDirectoryToDirectory(Directory * directory, char * shortname, char * name);
Directory * newDirectory(Directory * parentDirectory, char * dirname) {
Directory * directory;
directory = malloc(sizeof(Directory));
if(dirname!=NULL) directory->name = strdup(dirname);
else directory->name = NULL;
directory->parentDirectory = parentDirectory;
directory->subDirectories = newDirectoryList();
directory->songs = newSongList();
if(dirname!=NULL) directory->mtime = isDir(dirname);
else directory->mtime = 0;
return directory;
}
void freeDirectory(Directory * directory) {
freeDirectoryList(directory->subDirectories);
removeSongsFromTables(directory->songs);
freeSongList(directory->songs);
if(directory->name) free(directory->name);
free(directory);
}
DirectoryList * newDirectoryList() {
return makeList((ListFreeDataFunc *)freeDirectory);
}
void freeDirectoryList(DirectoryList * directoryList) {
freeList(directoryList);
}
int updateInDirectory(Directory * directory, char * shortname, char * name) {
time_t mtime;
Song * song;
Directory * subDir;
if((mtime = isMp3(name))) {
if(0==findInList(directory->songs,shortname,(void **)&song)) {
printf("adding %s\n",name);
addToDirectory(directory,shortname,name);
}
else if(mtime>song->mtime) {
updateSongInfo(song);
}
}
else if((mtime = isDir(name))) {
if(findInList(directory->subDirectories,shortname,(void **)&subDir)) {
updateDirectory(subDir);
}
else addSubDirectoryToDirectory(directory,shortname,name);
}
return 0;
}
void removeSongFromDirectory(Directory * directory, char * shortname, char * name) {
Song * song;
if(findInList(directory->songs,shortname,(void **)&song)) {
removeASongFromTables(song);
deleteFromList(directory->songs,shortname);
}
}
int removeDeletedFromDirectory(Directory * directory) {
DIR * dir;
char cwd[2];
struct dirent * ent;
char s[MAXPATHLEN+1];
char * dirname = directory->name;
List * entList = makeList(free);
char * name;
ListNode * node;
cwd[0] = '.';
cwd[1] = '\0';
if(dirname==NULL) dirname=cwd;
if((dir = opendir(dirname))==NULL) return -1;
while((ent = readdir(dir))) {
if(ent->d_name[0]=='.') continue; /* hide hidden stuff */
if(directory->name) {
sprintf(s,"%s/%s",directory->name,ent->d_name);
}
else {
strcpy(s,ent->d_name);
}
name = strdup(s);
insertInList(entList,ent->d_name,name);
}
closedir(dir);
node = directory->subDirectories->firstNode;
while(node) {
if(findInList(entList,node->key,(void **)&name)) {
if(!isDir(name)) {
deleteFromList(directory->subDirectories,node->key);
}
}
else {
deleteFromList(directory->subDirectories,node->key);
}
node = node->nextNode;
}
node = directory->songs->firstNode;
while(node) {
if(findInList(entList,node->key,(void **)&name)) {
if(!isMp3(name)) {
removeSongFromDirectory(directory,node->key,name);
}
}
else {
removeSongFromDirectory(directory,node->key,name);
}
node = node->nextNode;
}
freeList(entList);
return 0;
}
int updateDirectory(Directory * directory) {
DIR * dir;
char cwd[2];
struct dirent * ent;
char s[MAXPATHLEN+1];
char * dirname = directory->name;
cwd[0] = '.';
cwd[1] = '\0';
if(dirname==NULL) dirname=cwd;
printf("update: %s\n",dirname);
if(isDir(dirname)>directory->mtime) {
removeDeletedFromDirectory(directory);
}
if((dir = opendir(dirname))==NULL) return -1;
while((ent = readdir(dir))) {
if(ent->d_name[0]=='.') continue; /* hide hidden stuff */
if(directory->name) {
sprintf(s,"%s/%s",directory->name,ent->d_name);
}
else {
strcpy(s,ent->d_name);
}
updateInDirectory(directory,ent->d_name,s);
}
closedir(dir);
if(directory->name) directory->mtime = isDir(directory->name);
return 0;
}
int exploreDirectory(Directory * directory) {
DIR * dir;
char cwd[2];
struct dirent * ent;
char s[MAXPATHLEN+1];
char * dirname = directory->name;
cwd[0] = '.';
cwd[1] = '\0';
if(dirname==NULL) dirname=cwd;
if((dir = opendir(dirname))==NULL) return -1;
printf("explore: %s\n",dirname);
while((ent = readdir(dir))) {
if(ent->d_name[0]=='.') continue; /* hide hidden stuff */
if(directory->name) {
sprintf(s,"%s/%s",directory->name,ent->d_name);
}
else {
strcpy(s,ent->d_name);
}
addToDirectory(directory,ent->d_name,s);
}
closedir(dir);
return 0;
}
int addSubDirectoryToDirectory(Directory * directory, char * shortname, char * name) {
Directory * subDirectory = newDirectory(directory,name);
insertInList(directory->subDirectories,shortname,subDirectory);
exploreDirectory(subDirectory);
return 0;
}
int addToDirectory(Directory * directory, char * shortname, char * name) {
if(isDir(name)) {
return addSubDirectoryToDirectory(directory,shortname,name);
}
else if(isMp3(name)) {
Song * song;
song = addSongToList(directory->songs,shortname,name);
if(!song) return -1;
if(song->tag) addSongToTables(song);
return 0;
}
return -1;
}
void initMp3Directory() {
mp3rootDirectory = newDirectory(NULL,NULL);
exploreDirectory(mp3rootDirectory);
}
void closeMp3Directory() {
freeDirectory(mp3rootDirectory);
}
Directory * findSubDirectory(Directory * directory,char * name) {
Directory * subDirectory;
char * dup = strdup(name);
char * key;
key = strtok(dup,"/");
if(findInList(directory->subDirectories,key,(void **)&subDirectory)) {
free(dup);
return subDirectory;
}
free(dup);
return NULL;
}
Directory * getSubDirectory(Directory * directory,char * name) {
Directory * subDirectory;
int len;
if(name==NULL || name[0]=='\0' || strcmp(name,"/")==0) {
return directory;
}
if((subDirectory = findSubDirectory(directory,name))==NULL) return NULL;
len = 0;
while(name[len]!='/' && name[len]!='\0') len++;
while(name[len]=='/') len++;
return getSubDirectory(subDirectory,&(name[len]));
}
Directory * getDirectory(char * name) {
return getSubDirectory(mp3rootDirectory,name);
}
int printDirectoryList(FILE * fp, DirectoryList * directoryList) {
ListNode * node = directoryList->firstNode;
Directory * directory;
while(node!=NULL) {
directory = (Directory *)node->data;
fprintf(fp,"%s%s\n",DIRECTORY_DIR,directory->name);
node = node->nextNode;
}
return 0;
}
int printDirectoryInfo(FILE * fp,char * name) {
Directory * directory;
if((directory = getDirectory(name))==NULL) {
fprintf(fp,"%s: directory not found\n",COMMAND_RESPOND_ERROR);
return -1;
}
printDirectoryList(fp,directory->subDirectories);
printSongInfoFromList(fp,directory->songs);
return 0;
}
void writeDirectoryInfo(FILE * fp, Directory * directory) {
ListNode * node = (directory->subDirectories)->firstNode;
Directory * subDirectory;
if(directory->name) fprintf(fp,"%s%s\n",DIRECTORY_BEGIN,directory->name);
while(node!=NULL) {
subDirectory = (Directory *)node->data;
fprintf(fp,"%s%s\n",DIRECTORY_DIR,node->key);
fprintf(fp,"%s%li\n",DIRECTORY_MTIME,subDirectory->mtime);
writeDirectoryInfo(fp,subDirectory);
node = node->nextNode;
}
writeSongInfoFromList(fp,directory->songs);
if(directory->name) fprintf(fp,"%s%s\n",DIRECTORY_END,directory->name);
}
void readDirectoryInfo(FILE * fp,Directory * directory) {
char buffer[MAXPATHLEN+1024];
int bufferSize = MAXPATHLEN+1024;
char * key;
Directory * subDirectory;
char * name;
time_t mtime;
while(myFgets(buffer,bufferSize,fp) && 0!=strncmp(DIRECTORY_END,buffer,strlen(DIRECTORY_END))) {
if(0==strncmp(DIRECTORY_DIR,buffer,strlen(DIRECTORY_DIR))) {
key = strdup(&(buffer[strlen(DIRECTORY_DIR)]));
if(myFgets(buffer,bufferSize,fp)<0) {
fprintf(stderr,"Error reading db\n");
exit(-1);
}
if(strncmp(DIRECTORY_MTIME,buffer,strlen(DIRECTORY_MTIME))) {
fprintf(stderr,"Error reading db\n");
printf("%s\n",buffer);
exit(-1);
}
mtime = atoi(&(buffer[strlen(DIRECTORY_BEGIN)]));
if(myFgets(buffer,bufferSize,fp)<0) {
fprintf(stderr,"Error reading db\n");
exit(-1);
}
if(strncmp(DIRECTORY_BEGIN,buffer,strlen(DIRECTORY_BEGIN))) {
fprintf(stderr,"Error reading db\n");
exit(-1);
}
name = strdup(&(buffer[strlen(DIRECTORY_BEGIN)]));
subDirectory = newDirectory(directory,name);
insertInList(directory->subDirectories,key,(void *)subDirectory);
free(key);
free(name);
readDirectoryInfo(fp,subDirectory);
}
else if(0==strncmp(SONG_BEGIN,buffer,strlen(SONG_BEGIN))) {
readSongInfoIntoList(fp,directory->songs);
}
else {
fprintf(stderr,"Unknown line in db: %s\n",buffer);
exit(-1);
}
}
}
int writeDirectoryDB() {
FILE * fp;
if(!(fp=fopen(directorydb,"w"))) return -1;
writeDirectoryInfo(fp,mp3rootDirectory);
fclose(fp);
return 0;
}
int readDirectoryDB() {
FILE * fp;
mp3rootDirectory = newDirectory(NULL,NULL);
if(!(fp=fopen(directorydb,"r"))) return -1;
readDirectoryInfo(fp,mp3rootDirectory);
fclose(fp);
return 0;
}
int updateMp3Directory(FILE * fp) {
if(updateDirectory(mp3rootDirectory)<0) {
fprintf(fp,"%s problems updating directory info\n",COMMAND_RESPOND_ERROR);
return -1;
}
if(writeDirectoryDB()<0) {
fprintf(fp,"%s problems writing directory info\n",COMMAND_RESPOND_ERROR);
return -1;
}
return 0;
}
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#ifndef DIRECTORY_H
#define DIRECTORY_H
#include <stdio.h>
#include <sys/param.h>
extern char directorydb[MAXPATHLEN+1];
void initMp3Directory();
void closeMp3Directory();
int printDirectoryInfo(FILE * fp, char * dirname);
int writeDirectoryDB();
int readDirectoryDB();
size_t getDirectoryLine(char ** buffer, int * size, FILE * fp);
int updateMp3Directory(FILE * fp);
#endif
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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
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.
<signature of Ty Coon>, 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.
This library was written by Samuel Abels (sam@manicsadness.com).
If you have any questions, feel free to contact me, or visit the project homepage at
http://software.manicsadness.com
----------------
The library is very easy to use.
It is meant to be compiled directly into your project.
Example:
#include "genre.h"
#include "lib_id3v1.h"
main()
{
id3Tag tag;
strcpy (tag.artist, "Myartist");
strcpy (tag.album, "Myalbum");
strcpy (tag.title, "Mysong");
strcpy (tag.track, "12");
strcpy (tag.year, "2002");
strcpy (tag.comment, "Mycomment");
strcpy (tag.genre, genre[14]);
set_id3v1tag(&tag, "/home/sam/myfile.mp3");
}
If you need more info, look into the header file, it's really simple.
/* the id3v1 library.
* (c)2002 by Samuel Abels (sam@manicsadness.com)
* This project's homepage is: http://software.manicsadness.com/cantus
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#ifndef GENRE_H
#define GENRE_H
#define NUM_GENRES 150
char *genre[]={
/*1*/ "Blues",
/*2*/ "Classic Rock",
/*3*/ "Country",
/*4*/ "Dance",
/*5*/ "Disco",
/*6*/ "Funk",
/*7*/ "Grunge",
/*8*/ "Hip-Hop",
/*9*/ "Jazz",
/*10*/ "Metal",
/*11*/ "New Age",
/*12*/ "Oldies",
/*13*/ "Other",
/*14*/ "Pop",
/*15*/ "R&B",
/*16*/ "Rap",
/*17*/ "Reggae",
/*18*/ "Rock",
/*19*/ "Techno",
/*20*/ "Industrial",
/*21*/ "Alternative",
/*22*/ "Ska",
/*23*/ "Death Metal",
/*24*/ "Pranks",
/*25*/ "Soundtrack",
/*26*/ "Euro-Techno",
/*27*/ "Ambient",
/*28*/ "Trip-Hop",
/*29*/ "Vocal",
/*30*/ "Jazz+Funk",
/*31*/ "Fusion",
/*32*/ "Trance",
/*33*/ "Classical",
/*34*/ "Instrumental",
/*35*/ "Acid",
/*36*/ "House",
/*37*/ "Game",
/*38*/ "Sound Clip",
/*39*/ "Gospel",
/*40*/ "Noise",
/*41*/ "AlternRock",
/*42*/ "Bass",
/*43*/ "Soul",
/*44*/ "Punk",
/*45*/ "Space",
/*46*/ "Meditative",
/*47*/ "Instrumental Pop",
/*48*/ "Instrumental Rock",
/*49*/ "Ethnic",
/*50*/ "Gothic",
/*51*/ "Darkwave",
/*52*/ "Techno-Industrial",
/*53*/ "Electronic",
/*54*/ "Pop-Folk",
/*55*/ "Eurodance",
/*56*/ "Dream",
/*57*/ "Southern Rock",
/*58*/ "Comedy",
/*59*/ "Cult",
/*60*/ "Gangsta",
/*61*/ "Top 40",
/*62*/ "Christian Rap",
/*63*/ "Pop/Funk",
/*64*/ "Jungle",
/*65*/ "Native American",
/*66*/ "Cabaret",
/*67*/ "New Wave",
/*68*/ "Psychadelic",
/*69*/ "Rave",
/*70*/ "Showtunes",
/*71*/ "Trailer",
/*72*/ "Lo-Fi",
/*73*/ "Tribal",
/*74*/ "Acid Punk",
/*75*/ "Acid Jazz",
/*76*/ "Polka",
/*77*/ "Retro",
/*78*/ "Musical",
/*79*/ "Rock & Roll",
/*80*/ "Hard Rock",
// WinAmp extensions:
/*81*/ "Folk",
/*82*/ "Folk-Rock",
/*83*/ "National Folk",
/*84*/ "Swing",
/*85*/ "Fast Fusion",
/*86*/ "Bebob",
/*87*/ "Latin",
/*88*/ "Revival",
/*89*/ "Celtic",
/*90*/ "Bluegrass",
/*91*/ "Avantgarde",
/*92*/ "Gothic Rock",
/*93*/ "Progressive Rock",
/*94*/ "Psychedelic Rock",
/*95*/ "Symphonic Rock",
/*96*/ "Slow Rock",
/*97*/ "Big Band",
/*98*/ "Chorus",
/*99*/ "Easy Listening",
/*100*/ "Acoustic",
/*101*/ "Humour",
/*102*/ "Speech",
/*103*/ "Chanson",
/*104*/ "Opera",
/*105*/ "Chamber Music",
/*106*/ "Sonata",
/*107*/ "Symphony",
/*108*/ "Booty Bass",
/*109*/ "Primus",
/*110*/ "Porn Groove",
/*111*/ "Satire",
/*112*/ "Slow Jam",
/*113*/ "Club",
/*114*/ "Tango",
/*115*/ "Samba",
/*116*/ "Folklore",
/*117*/ "Ballad",
/*118*/ "Power Ballad",
/*119*/ "Rhythmic Soul",
/*120*/ "Freestyle",
/*121*/ "Duet",
/*122*/ "Punk Rock",
/*123*/ "Drum Solo",
/*124*/ "A capella",
/*125*/ "Euro-House",
/*126*/ "Dance Hall",
/*127*/ "Goa",
/*128*/ "Drum & Bass",
/*129*/ "Club-House",
/*130*/ "Hardcore",
/*131*/ "Terror",
/*132*/ "Indie",
/*133*/ "BritPop",
/*134*/ "Negerpunk",
/*135*/ "Polsk Punk",
/*136*/ "Beat",
/*137*/ "Christian Gansta Rap",
/*138*/ "Heavy Metal",
/*139*/ "Black Metal",
/*140*/ "Crossover",
/*141*/ "Contemporary Christian",
/*142*/ "Christian Rock",
/*143*/ "Merengue",
/*144*/ "Salsa",
/*145*/ "Thrash Metal",
/*146*/ "Anime",
/*147*/ "Jpop",
/*148*/ "Synthpop"
};
#endif
/* the id3v1 library.
* (c)2002 by Samuel Abels (sam@manicsadness.com)
* This project's homepage is: http://software.manicsadness.com/cantus
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include "lib_id3v1.h"
#include "genre.h"
/*
* Purpose: Reads the ID3 tag from a file.
* Parameters: tag - The structure to store the tag in, filename - The name of the file to operate on.
* Returns:
* 0 if successful,
* 1 if an error occured when opening the file
* 2 if error while reading tag.
* 3 if no TAG found.
*/
int get_id3v1_tag(id3Tag *tag, char *filename)
{
FILE *mp3file;
char *buffer = malloc(2048);
id3v1Tag *v1 = malloc(sizeof(id3v1Tag));
short int foundtag = FALSE;
int i = -1;
memset(tag, 0, sizeof(id3Tag));
memset(v1, 0, sizeof(id3v1Tag));
memset(buffer, 0, 2048);
mp3file = fopen(filename, "rb");
if (!mp3file)
return 1;
// read the last 200 bytes of the file.
// in most cases, the last 128 bytes would be enough, but some buggy tags, again...
fseek(mp3file, -200, SEEK_END);
if( !fread(buffer, 1, 400, mp3file) )
{
free(buffer);
free(v1);
fclose(mp3file);
return 2;
}
fclose(mp3file);
// parse through the raw data, if a "TAG" sign is found, we copy the raw data to a struct.
i = -1;
while( ++i <= (200-128) )
{
if( (*(buffer + i + 0) == 'T')
&& (*(buffer + i + 1) == 'A')
&& (*(buffer + i + 2) == 'G') )
{
memcpy(v1, buffer + i + 3, 125);
foundtag = TRUE;
break;
}
}
free(buffer);
if(!foundtag)
{
free(v1);
return 3;
}
strncpy(tag->title, v1->title, 30);
strncpy(tag->artist, v1->artist, 30);
strncpy(tag->album, v1->album, 30);
strncpy(tag->year, v1->year, 4);
if(v1->comment[28]=='\0' && v1->comment[29]!='\0')
{
strncpy(tag->comment, v1->comment, 28);
snprintf(tag->track, 3, "%i", v1->comment[29]);
tag->comment[29] = '\0';
}
else
{
strncpy(tag->comment, v1->comment, 30);
tag->comment[30] = '\0';
}
if( v1->genre > 148 )
strncpy(tag->genre, genre[12], 511);
else
strncpy(tag->genre, genre[v1->genre], 511);
free(v1);
return 0;
}
/*
* Purpose: Writes the ID3 tag to the file.
* Parameters: tag - The tag to write, filename - The name of the file to operate on.
* Returns:
* 0 if successful,
* 1 if an error occured when opening the file for reading
* 2 if error while reading whole file.
* 3 if an error occured while writing the whole file
* 4 if error while opening file for tagadding.
* 5 if error while writing the tag.
*/
int set_id3v1_tag(id3Tag *tag, char *filename)
{
FILE *mp3file;
id3v1Tag *v1;
int i = 0;
del_id3v1_tag(filename);
// Prepare users data for writing
v1 = malloc(sizeof(id3v1Tag));
memcpy(v1->title, tag->title, 30);
memcpy(v1->artist, tag->artist, 30);
memcpy(v1->album, tag->album, 30);
memcpy(v1->year, tag->year, 4);
if(tag->track)
{
memcpy(v1->comment, tag->comment, 28);
v1->comment[28]='\0';
*(v1->comment+29) = atoi(tag->track);
}
else
{
memcpy(v1->comment, tag->comment, 30);
}
// Find the genre number
i = -1;
while(genre[++i])
{
if(strcmp(genre[i], tag->genre) == 0)
break;
}
// ...or set the genre to "other"
if(!genre[i])
i = 12;
v1->genre = i;
mp3file = fopen(filename, "r+b");
if (!mp3file)
{
free(v1);
return 4;
}
// now add the new tag
fseek(mp3file, 0, SEEK_END);
fputc('T', mp3file);
fputc('A', mp3file);
fputc('G', mp3file);
if (!fwrite(v1, 1, sizeof(id3v1Tag), mp3file))
{
free(v1);
fclose(mp3file);
return 5;
}
fclose(mp3file);
free(v1);
return 0;
}
/*
* Name says it all.
* Returns:
* 0 if successful,
* 1 if an error occured when opening the file
* 2 if error while reading file.
* 3 if no TAG found.
*/
int del_id3v1_tag(char *filename)
{
FILE *mp3stream;
int mp3file;
long i = 0, len;
char buffer[400];
// Find length of the file
mp3stream = fopen(filename, "r+b");
if(!mp3stream)
return 1;
fseek(mp3stream, 0, SEEK_END);
len = ftell(mp3stream);
fclose(mp3stream);
// Open file for writing
mp3file = open(filename, O_RDWR);
if(mp3file == -1)
return 1;
// if there is an existing tag, delete it.
memset (buffer, 0, 400);
// read the last 400 bytes of the file.
// in most cases, the last 128 bytes would be enough, but some buggy tags, again...
lseek (mp3file, -400L, SEEK_END);
if ( read(mp3file, buffer, 400) < 400 )
{
close(mp3file);
return 2;
}
i = -1;
while ( ++i <= 400 )
{
if( buffer[i] == 'T'
&& buffer[i + 1] == 'A'
&& buffer[i + 2] == 'G' )
{
ftruncate (mp3file, len - (400 - i) );
break;
}
}
close(mp3file);
return 0;
}
/* the id3v1 library.
* (c)2002 by Samuel Abels (sam@manicsadness.com)
* This project's homepage is: http://software.manicsadness.com/cantus
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef id3Tag_def
#define id3Tag_def
typedef struct id3Tag_s
{
char title[1024];
char artist[1024];
char album[1024];
char year[5];
char comment[1024];
char track[10];
char genre[512];
unsigned int size;
short int has_footer;
} id3Tag;
typedef struct id3v1Tag_s
{
char title[30];
char artist[30];
char album[30];
char year[4];
char comment[30];
unsigned char genre;
} id3v1Tag;
#endif
int get_id3v1_tag(id3Tag *tag, char *filename);
int set_id3v1_tag(id3Tag *tag, char *filename);
int del_id3v1_tag(char *filename);
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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
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.
<signature of Ty Coon>, 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.
This library was written by Samuel Abels (sam@manicsadness.com).
The charset conversion were taken from EasyTag, but he took it from XMMS.
If you have any questions, feel free to contact me, or visit the project homepage at
http://software.manicsadness.com
Authors
------------
Samuel Abels (sam@manicsadness.com)
Patches
------------
Matt McClure (mlm@aya.yale.edu)
----------------
The library is very easy to use.
It is meant to be compiled directly into your project.
Example:
#include "genre.h"
#include "lib_id3v1.h"
main()
{
id3Tag tag;
strcpy (tag.artist, "Myartist");
strcpy (tag.album, "Myalbum");
strcpy (tag.title, "Mysong");
strcpy (tag.track, "12");
strcpy (tag.year, "2002");
strcpy (tag.comment, "Mycomment");
strcpy (tag.genre, "Mygenre");
set_id3v2tag(&tag, "/home/sam/myfile.mp3");
}
If you need more info, look into the header file, it's really simple.
/*
* Main part of code, written by:
*
* Copyright (C) 1999-2001 Hvard Kvlen <havardk@xmms.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., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#ifdef HAVE_ICONV_OPEN
#include <iconv.h>
#endif
#ifdef HAVE_LANGINFO_CODESET
#include <langinfo.h>
#endif
#include "charset.h"
/****************
* Declarations *
****************/
#define CHARSET_TRANS_ARRAY_LEN ( sizeof(charset_trans_array) / sizeof((charset_trans_array)[0]) )
const CharsetInfo charset_trans_array[] = {
{N_("Arabic (IBM-864)"), "IBM864" },
{N_("Arabic (ISO-8859-6)"), "ISO-8859-6" },
{N_("Arabic (Windows-1256)"), "windows-1256" },
{N_("Baltic (ISO-8859-13)"), "ISO-8859-13" },
{N_("Baltic (ISO-8859-4)"), "ISO-8859-4" },
{N_("Baltic (Windows-1257)"), "windows-1257" },
{N_("Celtic (ISO-8859-14)"), "ISO-8859-14" },
{N_("Central European (IBM-852)"), "IBM852" },
{N_("Central European (ISO-8859-2)"), "ISO-8859-2" },
{N_("Central European (Windows-1250)"), "windows-1250" },
{N_("Chinese Simplified (GB18030)"), "gb18030" },
{N_("Chinese Simplified (GB2312)"), "GB2312" },
{N_("Chinese Traditional (Big5)"), "Big5" },
{N_("Chinese Traditional (Big5-HKSCS)"), "Big5-HKSCS" },
{N_("Cyrillic (IBM-855)"), "IBM855" },
{N_("Cyrillic (ISO-8859-5)"), "ISO-8859-5" },
{N_("Cyrillic (ISO-IR-111)"), "ISO-IR-111" },
{N_("Cyrillic (KOI8-R)"), "KOI8-R" },
{N_("Cyrillic (Windows-1251)"), "windows-1251" },
{N_("Cyrillic/Russian (CP-866)"), "IBM866" },
{N_("Cyrillic/Ukrainian (KOI8-U)"), "KOI8-U" },
{N_("English (US-ASCII)"), "us-ascii" },
{N_("Greek (ISO-8859-7)"), "ISO-8859-7" },
{N_("Greek (Windows-1253)"), "windows-1253" },
{N_("Hebrew (IBM-862)"), "IBM862" },
{N_("Hebrew (Windows-1255)"), "windows-1255" },
{N_("Japanese (EUC-JP)"), "EUC-JP" },
{N_("Japanese (ISO-2022-JP)"), "ISO-2022-JP" },
{N_("Japanese (Shift_JIS)"), "Shift_JIS" },
{N_("Korean (EUC-KR)"), "EUC-KR" },
{N_("Nordic (ISO-8859-10)"), "ISO-8859-10" },
{N_("South European (ISO-8859-3)"), "ISO-8859-3" },
{N_("Thai (TIS-620)"), "TIS-620" },
{N_("Turkish (IBM-857)"), "IBM857" },
{N_("Turkish (ISO-8859-9)"), "ISO-8859-9" },
{N_("Turkish (Windows-1254)"), "windows-1254" },
{N_("Unicode (UTF-7)"), "UTF-7" },
{N_("Unicode (UTF-8)"), "UTF-8" },
{N_("Unicode (UTF-16BE)"), "UTF-16BE" },
{N_("Unicode (UTF-16LE)"), "UTF-16LE" },
{N_("Unicode (UTF-32BE)"), "UTF-32BE" },
{N_("Unicode (UTF-32LE)"), "UTF-32LE" },
{N_("Vietnamese (VISCII)"), "VISCII" },
{N_("Vietnamese (Windows-1258)"), "windows-1258" },
{N_("Visual Hebrew (ISO-8859-8)"), "ISO-8859-8" },
{N_("Western (IBM-850)"), "IBM850" },
{N_("Western (ISO-8859-1)"), "ISO-8859-1" },
{N_("Western (ISO-8859-15)"), "ISO-8859-15" },
{N_("Western (Windows-1252)"), "windows-1252" }
/*
* From this point, character sets aren't supported by iconv
*/
/* {N_("Arabic (IBM-864-I)"), "IBM864i" },
{N_("Arabic (ISO-8859-6-E)"), "ISO-8859-6-E" },
{N_("Arabic (ISO-8859-6-I)"), "ISO-8859-6-I" },
{N_("Arabic (MacArabic)"), "x-mac-arabic" },
{N_("Armenian (ARMSCII-8)"), "armscii-8" },
{N_("Central European (MacCE)"), "x-mac-ce" },
{N_("Chinese Simplified (GBK)"), "x-gbk" },
{N_("Chinese Simplified (HZ)"), "HZ-GB-2312" },
{N_("Chinese Traditional (EUC-TW)"), "x-euc-tw" },
{N_("Croatian (MacCroatian)"), "x-mac-croatian" },
{N_("Cyrillic (MacCyrillic)"), "x-mac-cyrillic" },
{N_("Cyrillic/Ukrainian (MacUkrainian)"), "x-mac-ukrainian" },
{N_("Farsi (MacFarsi)"), "x-mac-farsi"},
{N_("Greek (MacGreek)"), "x-mac-greek" },
{N_("Gujarati (MacGujarati)"), "x-mac-gujarati" },
{N_("Gurmukhi (MacGurmukhi)"), "x-mac-gurmukhi" },
{N_("Hebrew (ISO-8859-8-E)"), "ISO-8859-8-E" },
{N_("Hebrew (ISO-8859-8-I)"), "ISO-8859-8-I" },
{N_("Hebrew (MacHebrew)"), "x-mac-hebrew" },
{N_("Hindi (MacDevanagari)"), "x-mac-devanagari" },
{N_("Icelandic (MacIcelandic)"), "x-mac-icelandic" },
{N_("Korean (JOHAB)"), "x-johab" },
{N_("Korean (UHC)"), "x-windows-949" },
{N_("Romanian (MacRomanian)"), "x-mac-romanian" },
{N_("Turkish (MacTurkish)"), "x-mac-turkish" },
{N_("User Defined"), "x-user-defined" },
{N_("Vietnamese (TCVN)"), "x-viet-tcvn5712" },
{N_("Vietnamese (VPS)"), "x-viet-vps" },
{N_("Western (MacRoman)"), "x-mac-roman" },
// charsets whithout posibly translatable names
{"T61.8bit", "T61.8bit" },
{"x-imap4-modified-utf7", "x-imap4-modified-utf7"},
{"x-u-escaped", "x-u-escaped" },
{"windows-936", "windows-936" }
*/
};
/*************
* Functions *
*************/
// Return the last item of an doubly linked list
static DLL *
dll_last (DLL *list)
{
DLL *item = list;
while ( item->next )
item = item->next;
return item;
}
// Append an item to the doubly linked list
static DLL *
dll_append (DLL *list, void *data)
{
DLL *item = malloc (sizeof(DLL));
item->data = data;
item->prev = dll_last(list);
item->next = NULL;
return item;
}
static char* get_current_charset (void)
{
char *charset = getenv("CHARSET");
#ifdef HAVE_LANGINFO_CODESET
if (!charset)
charset = nl_langinfo(CODESET);
#endif
if (!charset)
charset = "ISO-8859-1";
return charset;
}
#ifdef HAVE_ICONV_OPEN
static char* convert_string (const char *string, char *from, char *to)
{
size_t outleft, outsize, length;
iconv_t cd;
char *out, *outptr;
const char *input = string;
if (!string)
return NULL;
length = strlen(string);
/* g_message("converting %s from %s to %s", string, from, to); */
if ((cd = iconv_open(to, from)) == (iconv_t)-1)
{
fprintf (stderr, "convert_string(): Conversion not supported. Charsets: %s -> %s", from, to);
return strdup(string);
}
/* Due to a GLIBC bug, round outbuf_size up to a multiple of 4 */
/* + 1 for nul in case len == 1 */
outsize = ((length + 3) & ~3) + 1;
out = g_malloc(outsize);
outleft = outsize - 1;
outptr = out;
retry:
if (iconv(cd, (char **)&input, &length, &outptr, &outleft) == -1)
{
int used;
switch (errno)
{
case E2BIG:
used = outptr - out;
outsize = (outsize - 1) * 2 + 1;
out = g_realloc(out, outsize);
outptr = out + used;
outleft = outsize - 1 - used;
goto retry;
case EINVAL:
break;
case EILSEQ:
/* Invalid sequence, try to get the
rest of the string */
input++;
length = strlen(input);
goto retry;
default:
fprintf (stderr, "convert_string(): Conversion failed. Inputstring: %s; Error: %s", string, strerror(errno));
break;
}
}
*outptr = '\0';
iconv_close(cd);
return out;
}
#else
static char* convert_string (const char *string, char *from, char *to)
{
if (!string)
return NULL;
return strdup(string);
}
#endif
/*
* Conversion with UTF-8 for ogg tags
*/
char* convert_to_utf8 (const char *string)
{
char *charset = get_current_charset();
return convert_string(string, charset, "UTF-8");
}
char* convert_from_utf8 (const char *string)
{
char *charset = get_current_charset();
return convert_string(string, "UTF-8", charset);
}
DLL *Charset_Create_List (void)
{
DLL *list = NULL;
int i;
for (i=0; i<CHARSET_TRANS_ARRAY_LEN; i++)
list = dll_append (list,_(charset_trans_array[i].charset_title));
return list;
}
/*
* Return charset_name from charset_title
*/
char *Charset_Get_Name_From_Title (char *charset_title)
{
int i;
if (charset_title)
for (i=0; i<CHARSET_TRANS_ARRAY_LEN; i++)
if ( strcasecmp(_(charset_title),_(charset_trans_array[i].charset_title)) == 0 )
return charset_trans_array[i].charset_name;
return "";
}
/*
* Return charset_title from charset_name
*/
char *Charset_Get_Title_From_Name (char *charset_name)
{
int i;
if (charset_name)
for (i=0; i<CHARSET_TRANS_ARRAY_LEN; i++)
if ( strcasecmp(charset_name,charset_trans_array[i].charset_name) == 0 )
return _(charset_trans_array[i].charset_title);
return "";
}
/*
* Test if the conversion is supported between two character sets ('from' and 'to)
*/
#ifdef HAVE_ICONV_OPEN
short int test_conversion_charset (char *from, char *to)
{
iconv_t cd;
if ((cd=iconv_open(to,from)) == (iconv_t)-1)
{
/* Conversion not supported */
return FALSE;
}
iconv_close(cd);
return TRUE;
}
#else
short int test_conversion_charset (char *from, char *to)
{
return TRUE;
}
#endif
/* charset.h - 2001/12/04 */
/*
* EasyTAG - Tag editor for MP3 and OGG files
* Copyright (C) 2000-2002 Jerome Couderc <j.couderc@ifrance.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 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.
*/
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
/*
* Standard gettext macros.
*/
#ifdef ENABLE_NLS
# include <libintl.h>
# define _(String) gettext (String)
# ifdef gettext_noop
# define N_(String) gettext_noop (String)
# else
# define N_(String) (String)
# endif
#else
# define textdomain(String) (String)
# define gettext(String) (String)
# define dgettext(Domain,Message) (Message)
# define dcgettext(Domain,Message,Type) (Message)
# define bindtextdomain(Domain,Directory) (Domain)
# define _(String) (String)
# define N_(String) (String)
#endif
#ifndef DLL_H
#define DLL_H
typedef struct DLL_s
{
void *prev;
void *data;
void *next;
} DLL;
#endif
#ifndef __CHARSET_H__
#define __CHARSET_H__
/***************
* Declaration *
***************/
typedef struct {
char *charset_title;
char *charset_name;
} CharsetInfo;
/* translated charset titles */
extern const CharsetInfo charset_trans_array[];
/**************
* Prototypes *
**************/
//static gchar* get_current_charset (void);
/* Used for ogg tags */
char* convert_to_utf8 (const char *string);
char* convert_from_utf8 (const char *string);
char* convert_from_file_to_user (const char *string);
char* convert_from_user_to_file (const char *string);
DLL *Charset_Create_List (void);
char *Charset_Get_Name_From_Title (char *charset_title);
char *Charset_Get_Title_From_Name (char *charset_name);
short int test_conversion_charset (char *from, char *to);
#endif /* __CHARSET_H__ */
/* the id3v1 library.
* (c)2002 by Samuel Abels (sam@manicsadness.com)
* This project's homepage is: http://software.manicsadness.com/cantus
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#ifndef GENRE_H
#define GENRE_H
#define NUM_GENRES 150
char *genre[]={
/*1*/ "Blues",
/*2*/ "Classic Rock",
/*3*/ "Country",
/*4*/ "Dance",
/*5*/ "Disco",
/*6*/ "Funk",
/*7*/ "Grunge",
/*8*/ "Hip-Hop",
/*9*/ "Jazz",
/*10*/ "Metal",
/*11*/ "New Age",
/*12*/ "Oldies",
/*13*/ "Other",
/*14*/ "Pop",
/*15*/ "R&B",
/*16*/ "Rap",
/*17*/ "Reggae",
/*18*/ "Rock",
/*19*/ "Techno",
/*20*/ "Industrial",
/*21*/ "Alternative",
/*22*/ "Ska",
/*23*/ "Death Metal",
/*24*/ "Pranks",
/*25*/ "Soundtrack",
/*26*/ "Euro-Techno",
/*27*/ "Ambient",
/*28*/ "Trip-Hop",
/*29*/ "Vocal",
/*30*/ "Jazz+Funk",
/*31*/ "Fusion",
/*32*/ "Trance",
/*33*/ "Classical",
/*34*/ "Instrumental",
/*35*/ "Acid",
/*36*/ "House",
/*37*/ "Game",
/*38*/ "Sound Clip",
/*39*/ "Gospel",
/*40*/ "Noise",
/*41*/ "AlternRock",
/*42*/ "Bass",
/*43*/ "Soul",
/*44*/ "Punk",
/*45*/ "Space",
/*46*/ "Meditative",
/*47*/ "Instrumental Pop",
/*48*/ "Instrumental Rock",
/*49*/ "Ethnic",
/*50*/ "Gothic",
/*51*/ "Darkwave",
/*52*/ "Techno-Industrial",
/*53*/ "Electronic",
/*54*/ "Pop-Folk",
/*55*/ "Eurodance",
/*56*/ "Dream",
/*57*/ "Southern Rock",
/*58*/ "Comedy",
/*59*/ "Cult",
/*60*/ "Gangsta",
/*61*/ "Top 40",
/*62*/ "Christian Rap",
/*63*/ "Pop/Funk",
/*64*/ "Jungle",
/*65*/ "Native American",
/*66*/ "Cabaret",
/*67*/ "New Wave",
/*68*/ "Psychadelic",
/*69*/ "Rave",
/*70*/ "Showtunes",
/*71*/ "Trailer",
/*72*/ "Lo-Fi",
/*73*/ "Tribal",
/*74*/ "Acid Punk",
/*75*/ "Acid Jazz",
/*76*/ "Polka",
/*77*/ "Retro",
/*78*/ "Musical",
/*79*/ "Rock & Roll",
/*80*/ "Hard Rock",
// WinAmp extensions:
/*81*/ "Folk",
/*82*/ "Folk-Rock",
/*83*/ "National Folk",
/*84*/ "Swing",
/*85*/ "Fast Fusion",
/*86*/ "Bebob",
/*87*/ "Latin",
/*88*/ "Revival",
/*89*/ "Celtic",
/*90*/ "Bluegrass",
/*91*/ "Avantgarde",
/*92*/ "Gothic Rock",
/*93*/ "Progressive Rock",
/*94*/ "Psychedelic Rock",
/*95*/ "Symphonic Rock",
/*96*/ "Slow Rock",
/*97*/ "Big Band",
/*98*/ "Chorus",
/*99*/ "Easy Listening",
/*100*/ "Acoustic",
/*101*/ "Humour",
/*102*/ "Speech",
/*103*/ "Chanson",
/*104*/ "Opera",
/*105*/ "Chamber Music",
/*106*/ "Sonata",
/*107*/ "Symphony",
/*108*/ "Booty Bass",
/*109*/ "Primus",
/*110*/ "Porn Groove",
/*111*/ "Satire",
/*112*/ "Slow Jam",
/*113*/ "Club",
/*114*/ "Tango",
/*115*/ "Samba",
/*116*/ "Folklore",
/*117*/ "Ballad",
/*118*/ "Power Ballad",
/*119*/ "Rhythmic Soul",
/*120*/ "Freestyle",
/*121*/ "Duet",
/*122*/ "Punk Rock",
/*123*/ "Drum Solo",
/*124*/ "A capella",
/*125*/ "Euro-House",
/*126*/ "Dance Hall",
/*127*/ "Goa",
/*128*/ "Drum & Bass",
/*129*/ "Club-House",
/*130*/ "Hardcore",
/*131*/ "Terror",
/*132*/ "Indie",
/*133*/ "BritPop",
/*134*/ "Negerpunk",
/*135*/ "Polsk Punk",
/*136*/ "Beat",
/*137*/ "Christian Gansta Rap",
/*138*/ "Heavy Metal",
/*139*/ "Black Metal",
/*140*/ "Crossover",
/*141*/ "Contemporary Christian",
/*142*/ "Christian Rock",
/*143*/ "Merengue",
/*144*/ "Salsa",
/*145*/ "Thrash Metal",
/*146*/ "Anime",
/*147*/ "Jpop",
/*148*/ "Synthpop"
};
#endif
/* the id3v2.2 library.
* (c)2002 by Samuel Abels (sam@manicsadness.com) and Warren Dukes
* This project's homepage is: http://software.manicsadness.com/cantus
*
* This library is designed for easyest possible access to id3 V2 tags.
*
* 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
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "lib_id3v2.2.h"
#include "charset.h"
/***************************************************************************************
* BELOW FOLLOW THE STATICS
***************************************************************************************/
// Return the last item of an doubly linked list
static DLL *
dll_last (DLL *list)
{
if( list == NULL )
return (NULL);
while ( list->next != NULL )
list = list->next;
return (list);
}
// Append an item to the doubly linked list
static DLL *
dll_append (DLL *list, void *data)
{
DLL *item = malloc (sizeof(DLL));
DLL *lastitem = dll_last(list);
item->data = data;
item->next = NULL;
if ( lastitem == NULL )
{
item->prev = NULL;
return (item);
}
else
{
item->prev = lastitem;
lastitem->next = item;
}
return list;
}
static DLL *
dll_remove (DLL *list, void *data)
{
DLL *item = list;
while ( item )
{
if ( item->data == data )
{
if ( item->prev == NULL
&& item->next == NULL )
{
// No other items there? Then return a zero pointer.
free (item);
return (NULL);
}
if ( item->prev == NULL )
{
// remove the first item of the list here...
list = item->next;
list->prev = NULL;
free (item);
break;
}
if ( item->next == NULL )
{
// ...remove the last item of the list here...
((DLL*)(item->prev))->next = NULL;
free (item);
break;
}
// ...or other items here
((DLL*)(item->prev))->next = item->next;
((DLL*)(item->next))->prev = item->prev;
free (item);
break;
}
item = item->next;
}
return list;
}
// Free a doubly linked list
static DLL *
dll_free (DLL *list)
{
DLL *item = list;
DLL *current = NULL;
while (item)
{
current = item;
item = item->next;
free (current);
}
return NULL;
}
/*
* Converts all occurences of a CR/LF to LF
*/
static void
crlf2cr (char *source)
{
char *psource = source;
char destination[2048];
if(source != NULL)
{
memset (destination, 0, 2048);
for (psource = source; *psource != '\0'; psource++)
{
if(*psource == 13
&& *(psource+1) == 10 )
{
psource++;
}
destination[strlen(destination)] = *psource;
}
}
strncpy (source, destination, strlen(destination)+1);
}
/*
* Converts all occurences of a LF to CR/LF
*/
static void
cr2crlf (char *source)
{
char *psource = source;
char destination[2048];
if( source != NULL )
{
memset (destination, 0, 2048);
for (psource = source; *psource != '\0'; psource++)
{
if (*psource == 10)
destination[strlen(destination)] = 13;
destination[strlen(destination)] = *psource;
}
}
strncpy (source, destination, strlen(destination)+1);
}
/*
* Reads the first ten bytes of an file and checks, if it's a valid ID3 V2.2 file
* If it is, the header flags are stored in the tag struct.
* Returns TRUE on a valid header, otherwise FALSE.
*/
static short int
check_header (FILE *mp3file, id3v22Tag *v22)
{
unsigned char buf[10];
// get header (=first ten bytes of the file)
fseek (mp3file, 0, SEEK_SET);
if ( fread (buf, 1, 10, mp3file) < 10 )
return (FALSE);
// a valid tag must begin with "ID3" followed by the version (checked below)
// followed by a flag byte, where the last fife bytes are unused and must be FALSE
if ( memcmp(buf, "ID3", 3) != 0
|| (buf[5] & 31) != 0 )
return (FALSE);
// check if version is supported
if ( buf[3] != 2
|| buf[4] != 0 )
return (FALSE);
// The next thing to come is the tag size. This are 3 bytes, the MSB should always be set to zero. check!
if ( (buf[6] & 128) != 0
|| (buf[7] & 128) != 0
|| (buf[8] & 128) != 0
|| (buf[9] & 128) != 0 )
return (FALSE);
// The tag size is encoded to be syncsave, so I got to decode it.
// The tag size is the size of the complete tag EXCLUDING the 10-byte header.
v22->tag_size = buf[9] + (buf[8] << 7) + (buf[7] << 14) + (buf[6] << 21);
// ok, so were save. put the flags in the nicer struct.
v22->unsync = (buf[5] & 128) >> 7;
v22->is_experimental = (buf[5] & 32) >> 5;
return (TRUE);
}
/*
* Reads the complete frames of a valid ID3V2.2 file and checks, if they are valid.
* If they are, the flags are stored in a DLL and appended to the tag struct.
* Returns TRUE on success, otherwise FALSE.
*/
static short int
read_frames (FILE *mp3file, id3v22Tag *v22)
{
unsigned char buf[6];
int numframes = 0;
unsigned int totalframesize = 0;
id3v22Frame *frame = NULL;
// set the position to the first frame header (header = 10 bytes + extheadersize + 4 bytes "extheaderheader")
fseek (mp3file, 10, SEEK_SET);
// If the tag size is too small for frames, return with an error.
if ( ((v22->tag_size + 10) - v22->padding_size) <= ftell(mp3file) )
return FALSE;
// now read all the frames
numframes = 0;
v22->frames = NULL;
while ( ftell (mp3file) < ((v22->tag_size + 10) - v22->padding_size) )
{
frame = calloc (1, sizeof(id3v22Frame));
// the frame header is six bytes long
if ( fread (buf, 1, 6, mp3file) < 6 )
goto error;
// if we are already in the padding, we must no longer look for frames...
if ( buf[0] == 0 && buf[1] == 0 && buf[2] == 0 && buf[3] == 0 )
{
if (numframes == 0)
goto error;
free (frame);
break;
}
// first come three characters identifying the frame. It must be alphanumeric.
if ( !isalnum(buf[0]) || !isalnum(buf[1]) || !isalnum(buf[2]) )
goto error;
*(frame->id) = buf[0];
*(frame->id + 1) = buf[1];
*(frame->id + 2) = buf[2];
// then, the frame size is to come. Again, the three MSBs must be zero.
if ( (buf[3] & 128) != 0
|| (buf[4] & 128) != 0
|| (buf[5] & 128) != 0 )
goto error;
frame->datasize = buf[5] + (buf[4] << 7) + (buf[3] << 14);
// A minimum size must be present!
if ( frame->datasize <= 0 )
goto error;
// now, put the flags in the struct.
// none of these flags in v2.2, so set them to something reasonable.
frame->tagalter = (1 & 128) >> 7;
frame->filealter = (1 & 64) >> 6;
frame->readonly = (0 & 32) >> 5;
frame->compression = (0 & 128) >> 7;
frame->encryption = (0 & 64) >> 6;
frame->grouping = (0 & 32) >> 5;
// ok, we are done with the frame header, so now we read the frame data.
frame->data = calloc (1, frame->datasize + 1);
if( fread (frame->data, 1, frame->datasize, mp3file) < frame->datasize )
goto error;
numframes++;
totalframesize += 6 + frame->datasize;
// we append it to a glist, which is appended to the v22 struct.
v22->frames = dll_append (v22->frames, frame);
}
// no extended header in v2.2
// if we have no extheader, that means, we don't know how much padding we have!
// thus, i calculate it here.
v22->padding_size = v22->tag_size - totalframesize;
// A minimum of one frame is mandatory.
if( numframes > 0 )
return (TRUE);
error:
// cleanups in case of an error.
if( frame && frame->data )
free (frame->data);
if( frame )
free (frame);
return (FALSE);
}
/*
* Reads all tag information of a valid ID3V2.2 file.
* When successful, the info is stored in the tag struct.
* Returns:
* 0 = success
* 1 = Cannot open file.
* 2 = No header or wrong version.
* 3 = broken extheader. -- don't have this for v2.2
* 4 = broken frames.
*/
static int
get_id3v22tag_raw (id3v22Tag *v22, char *filename)
{
FILE *mp3file = NULL;
int error = 0;
// open file
error = 1;
mp3file = fopen (filename, "rb");
if (!mp3file)
goto done;
// check/get header
error = 2;
if( !check_header (mp3file, v22) )
goto done;
// get the content frames
error = 4;
if( !read_frames (mp3file, v22) )
goto done;
error = 0;
done:
fclose (mp3file);
return (error);
}
/*
* Find one frames data and give bag its data in the correct format.
* Returns TRUE on success, otherwise FALSE;
*/
static short int
frame_find (id3v22Tag *v22, char *name, char *value)
{
DLL *curframe = NULL;
id3v22Frame *frame = NULL;
// we parse through the whole list of frames, giving back the correct frame value.
curframe = v22->frames;
while ( curframe )
{
frame = (id3v22Frame *)curframe->data;
// Just to be sure...
if( frame->datasize <= 0 )
goto nextframe;
// Matches the users request? Otherwise try the next frame.
if( memcmp (frame->id, name, 3) != 0 )
goto nextframe;
// This types don't need much change, just give the whole data back to the user according to the encoding.
// The first byte is the encoding.
// TP1: Artist
// TT2: Song Title
// TAL: Album Title
// TYE: Year
// TRK: Track
// TCO: Genre
// COM: Comment
if ( memcmp (frame->id, "TP1", 3) == 0
|| memcmp (frame->id, "TT2", 3) == 0
|| memcmp (frame->id, "TAL", 3) == 0
|| memcmp (frame->id, "TYE", 3) == 0
|| memcmp (frame->id, "TRK", 3) == 0
|| memcmp (frame->id, "TCO", 3) == 0
|| memcmp (frame->id, "TAL", 3) == 0 )
{
if ( *frame->data == 0 )
memcpy(value, frame->data + 1, frame->datasize - 1);
if ( *frame->data == 1 )
{
char nulltermvalue[frame->datasize];
char *isovalue = NULL;
// the tag is not null terminated, so i have to create a null terminated string first.
memset (nulltermvalue, 0, frame->datasize);
memcpy (nulltermvalue, frame->data + 1, frame->datasize - 1);
// Convert from UTF to ISO and copy to the users variable.
isovalue = convert_from_utf8 (nulltermvalue);
strncpy (value, isovalue, sizeof(value) - 1);
free (isovalue);
}
// change linefeeds to a single "return" key.
crlf2cr (value);
return (TRUE);
}
// The comment requires special handling.
// Its data has: One byte "encoding" (0 = ISO-8859-1, 1 = UNICODE)
// followed by the language (three bytes, e.g. "eng"),
// followed by a short description,
// then a NULL,
// and the full description
// By now, i simply drop the short description
if( memcmp(frame->id, "COM", 3) == 0 )
{
// check for the right format. (minsize 5, must contain a "\0" after the language)
if ( frame->datasize < 5 )
goto nextframe;
if ( !memchr (frame->data + 4, '\0', frame->datasize - 4) )
goto nextframe;
// now, give the data back to the user, according to the encoding.
if ( *frame->data == 0 )
memcpy (value, frame->data + 5, frame->datasize - 5);
if ( *frame->data == 1 )
{
char nulltermvalue[frame->datasize];
char *isovalue = NULL;
// the tag is not null terminated, so i have to create a null terminated string first.
memset (nulltermvalue, 0, frame->datasize);
memcpy (nulltermvalue, frame->data + 5, frame->datasize - 5);
// Convert from UTF to ISO and copy to the users variable.
isovalue = convert_from_utf8 (nulltermvalue);
strncpy (value, isovalue, sizeof(value) - 1);
free (isovalue);
}
// change linefeeds to a single "return" key.
crlf2cr (value);
return TRUE;
}
nextframe:
curframe = curframe->next;
}
return FALSE;
}
/*
* Remove one frame out of the id3v22Tag struct
* Returns TRUE on success, otherwise FALSE;
*/
static short int
frame_remove (id3v22Tag *v22, char *name)
{
id3v22Frame *frame = NULL;
DLL *curframe = NULL;
DLL *tempframe = NULL;
// Parse through the list of frames.
curframe = v22->frames;
while ( curframe )
{
frame = (id3v22Frame *)curframe->data;
tempframe = curframe;
curframe = curframe->next;
if ( memcmp (frame->id, name, 3) == 0 )
{
// we have found the item! removing will NOT shrink the tag, but increase the padding.
v22->padding_size += (frame->datasize + 6);
// and free memory.
v22->frames = dll_remove (v22->frames, tempframe->data);
free (frame->data);
free (frame);
return TRUE;
}
}
return FALSE;
}
/*
* Add a frame to the framelist. If the frame name is already in the list, it will be replaced.
* Returns:
* TRUE: The tag size HAS BEEN increased.
* FALSE: The tag size has NOT been increased.
*/
static short int
frame_set (id3v22Tag *v22, char *name, char *value)
{
id3v22Frame *frame = NULL;
short int sizechange = FALSE;
// prevent the user to send CR/LF, which is forbidden.
cr2crlf (value);
// eventually remove an existing item!
frame_remove (v22, name);
// alloc space for the new frame.
frame = malloc (sizeof(id3v22Frame));
memcpy (frame->id, name, 3);
frame->datasize = strlen (value);
frame->tagalter = 0;
frame->filealter = 0;
frame->readonly = 0;
frame->compression = 0;
frame->encryption = 0;
frame->grouping = 0;
// The comment requires special handling. If you need to know why, look at the documentation
// of the "frame_find" function above.
if( memcmp (frame->id, "COM", 3) == 0 )
{
char fullvalue[frame->datasize + 6];
sprintf(fullvalue, "%ceng%c%s", 0, 0, value);
frame->datasize += 5;
frame->data = malloc (frame->datasize);
memcpy (frame->data, fullvalue, frame->datasize);
}
else
{
char fullvalue[frame->datasize + 2];
sprintf (fullvalue, "%c%s", 0, value);
frame->datasize += 1;
frame->data = malloc (frame->datasize);
memcpy (frame->data, fullvalue, frame->datasize);
}
// Ok. This decreases the available padding. If we have no padding left, we must increase the padding (and thus, the tag).
if( v22->padding_size - (frame->datasize + 6) <= 0 )
{
// add: framesize + frameheadersize + padding.
v22->padding_size += frame->datasize + 6 + 1024;
v22->tag_size += frame->datasize + 6 + 1024;
sizechange = TRUE;
}
// In every case, we must subtract the new allocated space from the padding.
v22->padding_size -= frame->datasize + 6;
v22->frames = dll_append (v22->frames, frame);
return sizechange;
}
/*
* Create raw header.
* Returns:
* TRUE: successful.
* FALSE: unsuccessful.
*/
static int
create_header_raw (char *raw, id3v22Tag *v22)
{
// now we are going to write the tags raw data into the raw string
memset (raw, 0, v22->tag_size + 10);
// ID3 identifier bytes
memcpy (raw, "ID3", 3);
raw += 3;
// major version byte
*raw++ = 2;
// minor version byte
*raw++ = 0;
// Flags byte
*raw++ = ((v22->unsync & 1) << 7)
| ((v22->is_experimental & 1) << 5);
// Tag size. It must be syncsafe!
*raw++ = ((v22->tag_size & 0x800000) >> 23) | (((v22->tag_size & 0x7f000000) >> 24) << 1);
*raw++ = ((v22->tag_size & 0x8000) >> 15) | (((v22->tag_size & 0x7f0000) >> 16) << 1);
*raw++ = ((v22->tag_size & 0x80) >> 7) | (((v22->tag_size & 0x7f00) >> 8) << 1);
*raw++ = (v22->tag_size & 0x7f);
return TRUE;
}
/*
* Generates the frames. btw.: ID3 sucks!
* Returns: TRUE if succesful, otherwise FALSE.
*/
static short int
create_frames_raw (char *raw, id3v22Tag *v22)
{
id3v22Frame *frame = NULL;
DLL *curframe = NULL;
// if we have no frames, just quit.
if ( v22->frames == NULL )
return FALSE;
// the header and extheader have already been written.
raw += 6;
curframe = v22->frames;
while ( curframe )
{
frame = (id3v22Frame *)curframe->data;
// secure is secure
if ( frame->datasize <= 0 )
goto nextframe;
// add the frame id
memcpy(raw, frame->id, 3);
raw += 3;
// add the frame size (syncsafe)
*raw++ = ((frame->datasize & 0x8000) >> 15) | (((frame->datasize & 0x7f0000) >> 16) << 1);
*raw++ = ((frame->datasize & 0x80) >> 7) | (((frame->datasize & 0x7f00) >> 8) << 1);
*raw++ = (frame->datasize & 0x7f);
// now the frame data.
memcpy(raw, frame->data, frame->datasize);
raw += frame->datasize;
nextframe:
curframe = curframe->next;
}
return TRUE;
}
/***************************************************************************************
* END OF STATICS
***************************************************************************************/
/*
* Purpose: Reads the ID3 tag from a file.
* Parameters: tag - The structure to store the tag in, filename - The name of the file to operate on.
* Returns:
* 0 if successful,
* 1 if an error occured when opening the file
* 2 if error while reading tag.
* 3 if no TAG found.
*/
int
get_id3v22_tag (id3Tag *tag, char *filename)
{
id3v22Tag *v22 = calloc (1, sizeof(id3v22Tag));
DLL *curframe = NULL;
int error = 0;
// Read the tag.
error = get_id3v22tag_raw (v22, filename);
// Init the users tag
memset (tag, 0, sizeof (id3Tag));
if( error == 0 )
{
// if we have a valid tag we copy the raw data to the users struct
tag->size = v22->tag_size;
frame_find (v22, "TP1", tag->artist);
frame_find (v22, "TT2", tag->title);
frame_find (v22, "TAL", tag->album);
frame_find (v22, "TYE", tag->year);
frame_find (v22, "COM", tag->comment);
frame_find (v22, "TRK", tag->track);
frame_find (v22, "TCO", tag->genre);
}
// Free all the stuff
if (v22->frames)
{
id3v22Frame *frame = NULL;
curframe = v22->frames;
while ( curframe )
{
frame = (id3v22Frame *)curframe->data;
free (frame->data);
free (frame);
curframe = curframe->next;
}
v22->frames = dll_free (v22->frames);
}
free (v22);
return (error);
}
/*
* Purpose: Clear the ID3 tag of a file.
* Parameters: a filename.
* Returns:
* 0 if successful,
* 1 if an error occured when opening the file
* 2 if an error while reading/writing the tag.
*/
int
del_id3v22_tag (char *filename)
{
id3v22Tag *v22 = calloc (1, sizeof(id3v22Tag));
long file_len;
FILE *file;
int read;
void *ptr;
// check if an valid old id3v2 tag is present
// In these two error-cases we don't know how big the tag is.
if( get_id3v22tag_raw (v22, filename) == 1
|| get_id3v22tag_raw (v22, filename) == 3 )
return(0);
ptr = malloc (4096);
// open file read/write
file = fopen (filename, "r+b");
if (!file)
return (1);
fseek (file, 0, SEEK_END);
file_len = ftell (file);
if ( file_len < 11
|| v22->tag_size < 11 )
return (2);
// set anything but the header in tag to zero. ill not really remove the tag,
// because this would be much slower and if we write a new tag, this would mean we´d have to
// rewrite the complete tag.
fseek (file, 10, SEEK_SET);
for (read = 0; read < v22->tag_size - 10; read++)
fputc (0, file);
fflush (file);
fclose (file);
free (ptr);
free (v22);
return(0);
}
int
set_id3v22_tag (id3Tag *tag, char *filename)
{
id3v22Tag *v22 = malloc (sizeof(id3v22Tag));
id3v22Frame *frame = NULL;
unsigned char *rawdata = NULL;
DLL *curframe = NULL;
int oldsize = 0;
char track[3];
int error = 0;
// Try to get the content of an old tag
error = 1;
memset (v22, 0, sizeof(id3v22Tag));
get_id3v22tag_raw (v22, filename);
oldsize = v22->tag_size;
// first of all ill generate a valid id3v2 tag struct out of the tag struct we got by the user.
// Set the flags...
v22->unsync = FALSE;
v22->is_experimental = FALSE;
v22->crc_data_present = FALSE;
// Set the contentframes
frame_set (v22, "TT2", tag->title);
frame_set (v22, "TP1", tag->artist);
frame_set (v22, "TAL", tag->album);
frame_set (v22, "TYE", tag->year);
frame_set (v22, "COM", tag->comment);
frame_set (v22, "TCO", tag->genre);
if ( atoi (tag->track) < 10 )
snprintf (track, 3, "0%i", atoi(tag->track));
else
snprintf (track, 3, "%i", atoi(tag->track));
frame_set (v22, "TRK", track);
// Create a header in the raw data string
rawdata = calloc (1, v22->tag_size + 6);
create_header_raw (rawdata, v22);
// Create frames raw data.
create_frames_raw (rawdata, v22);
// is the new tag bigger than the old one? Then we'll have to completely rewrite the file...
if ( v22->tag_size > oldsize )
{
FILE *file = NULL;
FILE *tempfile = NULL;
char *tempfilename = NULL;
int read = 0;
char buf[4096];
// Open a tempfile
error = 2;
tempfilename = malloc (strlen (filename) + 7);
sprintf (tempfilename, "%s%s", filename, ".tempXXXXX");
if( !(tempfile = fopen(tempfilename, "wb")) )
{
remove (tempfilename);
free (tempfilename);
goto done;
}
// Write the tag to the tempfile.
error = 3;
fseek (tempfile, 0, SEEK_SET);
if( fwrite (rawdata, 1, v22->tag_size + 6, tempfile) < v22->tag_size )
{
fclose (tempfile);
remove (tempfilename);
free (tempfilename);
goto done;
}
// Open the mp3file.
error = 4;
if( !(file = fopen(filename, "r+b")) )
{
fclose (file);
remove (tempfilename);
free (tempfilename);
goto done;
}
// skip the old tag (if one existed)
fseek (file, oldsize? oldsize + 6 : oldsize, SEEK_SET);
// copy the rest of the file to the tempfile.
while ( !feof(file) )
{
error = 5;
read = fread (buf, 1, 4096, file);
if( fwrite (buf, 1, read, tempfile) != read
&& !feof (file) )
{
remove (tempfilename);
free (tempfilename);
fflush (tempfile);
fclose (tempfile);
fflush (file);
fclose (file);
goto done;
}
}
fflush (file);
fclose (file);
fflush (tempfile);
fclose (tempfile);
// rename the tempfile, so it is the mp3file.
rename (tempfilename, filename);
free (tempfilename);
}
else
{
FILE *file = NULL;
// If the old tag was bigger than the new one, we can simply overwrite it!
// open.
error = 10;
if( !(file = fopen(filename, "r+b")) )
goto done;
// write.
error = 11;
fseek (file, 0, SEEK_SET);
if( fwrite (rawdata, 1, v22->tag_size + 6, file) < v22->tag_size )
{
fflush (file);
fclose (file);
goto done;
}
fflush (file);
fclose (file);
}
error = 0;
done:
// Free all the stuff
curframe = v22->frames;
while ( curframe )
{
frame = (id3v22Frame *)curframe->data;
free (frame->data);
free (frame);
curframe = curframe->next;
}
dll_free (v22->frames);
if ( rawdata != NULL )
free (rawdata);
free (v22);
return (error);
}
/* the id3v2.2 library.
* (c)2002 by Samuel Abels (sam@manicsadness.com) and Warren Dukes
* This project's homepage is: http://software.manicsadness.com/cantus
*
* This library is designed for easyest possible access to id3 V2 tags.
*
* 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
*/
#ifndef DLL_H
#define DLL_H
typedef struct DLL_s
{
void *prev;
void *data;
void *next;
} DLL;
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef id3Tag_def
#define id3Tag_def
typedef struct id3Tag_s
{
char title[1024];
char artist[1024];
char album[1024];
char year[5];
char comment[1024];
char track[10];
char genre[512];
unsigned int size;
short int has_footer;
} id3Tag;
typedef struct id3v22Tag_s
{
// header
int tag_size;
short int unsync;
short int is_experimental;
//extheader
int padding_size;
short int crc_data_present;
char crc_data[4];
// frames
DLL *frames;
} id3v22Tag;
typedef struct id3v22Frame_s
{
unsigned char id[3];
int datasize;
short int tagalter;
short int filealter;
short int readonly;
short int compression;
short int encryption;
short int grouping;
char *data;
} id3v22Frame;
#endif
extern int get_id3v22_tag(id3Tag *tag, char *filename);
extern int set_id3v22_tag(id3Tag *tag, char *filename);
extern int del_id3v22_tag(char *filename);
/* the id3v2.3 library.
* (c)2002 by Samuel Abels (sam@manicsadness.com)
* This project's homepage is: http://software.manicsadness.com/cantus
*
* This library is designed for easyest possible access to id3 V2 tags.
*
* 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
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "lib_id3v2.3.h"
#include "charset.h"
/***************************************************************************************
* BELOW FOLLOW THE STATICS
***************************************************************************************/
// Return the last item of an doubly linked list
static DLL *
dll_last (DLL *list)
{
if( list == NULL )
return (NULL);
while ( list->next != NULL )
list = list->next;
return (list);
}
// Append an item to the doubly linked list
static DLL *
dll_append (DLL *list, void *data)
{
DLL *item = malloc (sizeof(DLL));
DLL *lastitem = dll_last(list);
item->data = data;
item->next = NULL;
if ( lastitem == NULL )
{
item->prev = NULL;
return (item);
}
else
{
item->prev = lastitem;
lastitem->next = item;
}
return list;
}
static DLL *
dll_remove (DLL *list, void *data)
{
DLL *item = list;
while ( item )
{
if ( item->data == data )
{
if ( item->prev == NULL
&& item->next == NULL )
{
// No other items there? Then return a zero pointer.
free (item);
return (NULL);
}
if ( item->prev == NULL )
{
// remove the first item of the list here...
list = item->next;
list->prev = NULL;
free (item);
break;
}
if ( item->next == NULL )
{
// ...remove the last item of the list here...
((DLL*)(item->prev))->next = NULL;
free (item);
break;
}
// ...or other items here
((DLL*)(item->prev))->next = item->next;
((DLL*)(item->next))->prev = item->prev;
free (item);
break;
}
item = item->next;
}
return list;
}
// Free a doubly linked list
static DLL *
dll_free (DLL *list)
{
DLL *item = list;
DLL *current = NULL;
while (item)
{
current = item;
item = item->next;
free (current);
}
return NULL;
}
/*
* Converts all occurences of a CR/LF to LF
*/
static void
crlf2cr (char *source)
{
char *psource = source;
char destination[2048];
if(source != NULL)
{
memset (destination, 0, 2048);
for (psource = source; *psource != '\0'; psource++)
{
if(*psource == 13
&& *(psource+1) == 10 )
{
psource++;
}
destination[strlen(destination)] = *psource;
}
}
strncpy (source, destination, strlen(destination)+1);
}
/*
* Converts all occurences of a LF to CR/LF
*/
static void
cr2crlf (char *source)
{
char *psource = source;
char destination[2048];
if( source != NULL )
{
memset (destination, 0, 2048);
for (psource = source; *psource != '\0'; psource++)
{
if (*psource == 10)
destination[strlen(destination)] = 13;
destination[strlen(destination)] = *psource;
}
}
strncpy (source, destination, strlen(destination)+1);
}
/*
* Reads the first ten bytes of an file and checks, if it's a valid ID3 V2.3 file
* If it is, the header flags are stored in the tag struct.
* Returns TRUE on a valid header, otherwise FALSE.
*/
static short int
check_header (FILE *mp3file, id3v2Tag *v2)
{
unsigned char buf[10];
// get header (=first ten bytes of the file)
fseek (mp3file, 0, SEEK_SET);
if ( fread (buf, 1, 10, mp3file) < 10 )
return (FALSE);
// a valid tag must begin with "ID3" followed by the version (checked below)
// followed by a flag byte, where the last fife bytes are unused and must be FALSE
if ( memcmp(buf, "ID3", 3) != 0
|| (buf[5] & 31) != 0 )
return (FALSE);
// check if version is supported
if ( buf[3] != 3
|| buf[4] != 0 )
return (FALSE);
// The next thing to come is the tag size. This are 4 bytes, the MSB should always be set to zero. check!
if ( (buf[6] & 128) != 0
|| (buf[7] & 128) != 0
|| (buf[8] & 128) != 0
|| (buf[9] & 128) != 0 )
return (FALSE);
// The tag size is encoded to be syncsave, so I got to decode it.
// The tag size is the size of the complete tag EXCLUDING the 10-byte header.
v2->tag_size = buf[9] + (buf[8] << 7) + (buf[7] << 14) + (buf[6] << 21);
// ok, so were save. put the flags in the nicer struct.
v2->unsync = (buf[5] & 128) >> 7;
v2->has_extheader = (buf[5] & 64) >> 6;
v2->is_experimental = (buf[5] & 32) >> 5;
return (TRUE);
}
/*
* Reads the extheader of a valid ID3V2.3 file and checks, if it's a valid.
* If it is, the extheader flags are stored in the tag struct.
* Returns TRUE on a valid extheader, otherwise FALSE.
*/
static short int
check_extheader (FILE *mp3file, id3v2Tag *v2)
{
unsigned char buf[10];
// Read id3 extheader intro (5 bytes)
fseek (mp3file, 10, SEEK_SET);
if ( fread(buf, 1, 5, mp3file) < 5 )
return (FALSE);
// First comes the extheader size. This are 4 bytes, the MSB should always be set to zero. check!
if( (buf[0] & 128) != 0
|| (buf[1] & 128) != 0
|| (buf[2] & 128) != 0
|| (buf[3] & 128) != 0 )
return (FALSE);
// OK. In ID3V2.3 only six byte or ten byte extheaders are allowed.
if( v2->extheader_size != 6
&& v2->extheader_size != 10 )
return (FALSE);
// The first four bytes specify the extheader size.
v2->extheader_size = buf[3] + (buf[2] << 7) + (buf[1] << 14) + (buf[0] << 21);
// The fifth byte specifies extendened flags. (in fact, only one flag is used for ID3V2.3
// The MSB of the byte 5 specifies, if there is CRC data to come, appended to the extheader.
if( (buf[4] & 127) != 0
|| buf[5] != 0 )
return (FALSE);
v2->crc_data_present = (buf[4] & 128) >> 7;
// if crc data is present, the extheader size must be ten bytes, otherwise 6.
if ( (v2->extheader_size == 6 && v2->crc_data_present == TRUE)
|| (v2->extheader_size == 10 && v2->crc_data_present == FALSE) )
return (FALSE);
// now come four bytes specifying the padding size
if ( (buf[6] & 128) != 0
|| (buf[7] & 128) != 0
|| (buf[8] & 128) != 0
|| (buf[9] & 128) != 0 )
return (FALSE);
v2->padding_size = buf[9] + (buf[8] << 7) + (buf[7] << 14) + (buf[6] << 21);
// Now to the optional crc data.
if( v2->crc_data_present )
{
if( fread (buf, 1, 4, mp3file) < 4 )
return (FALSE);
memcpy (v2->crc_data, buf, 4);
}
return (TRUE);
}
/*
* Reads the complete frames of a valid ID3V2.3 file and checks, if they are valid.
* If they are, the flags are stored in a DLL and appended to the tag struct.
* Returns TRUE on success, otherwise FALSE.
*/
static short int
read_frames (FILE *mp3file, id3v2Tag *v2)
{
unsigned char buf[10];
int numframes = 0;
unsigned int totalframesize = 0;
id3v2Frame *frame = NULL;
// set the position to the first frame header (header = 10 bytes + extheadersize + 4 bytes "extheaderheader")
if (v2->has_extheader)
fseek (mp3file, 10 + v2->extheader_size + 4, SEEK_SET);
else
fseek (mp3file, 10, SEEK_SET);
// If the tag size is too small for frames, return with an error.
if ( ((v2->tag_size + 10) - v2->padding_size) <= ftell(mp3file) )
return FALSE;
// now read all the frames
numframes = 0;
v2->frames = NULL;
while ( ftell (mp3file) < ((v2->tag_size + 10) - v2->padding_size) )
{
frame = calloc (1, sizeof(id3v2Frame));
// the frame header is ten bytes long
if ( fread (buf, 1, 10, mp3file) < 10 )
goto error;
// if we are already in the padding, we must no longer look for frames...
if ( buf[0] == 0 && buf[1] == 0 && buf[2] == 0 && buf[3] == 0 )
{
if (numframes == 0)
goto error;
free (frame);
break;
}
// first come four characters identifying the frame. It must be alphanumeric.
if ( !isalnum(buf[0]) || !isalnum(buf[1]) || !isalnum(buf[2]) || !isalnum(buf[3]) )
goto error;
*(frame->id) = buf[0];
*(frame->id + 1) = buf[1];
*(frame->id + 2) = buf[2];
*(frame->id + 3) = buf[3];
// then, the frame size is to come. Again, the four MSBs must be zero.
if ( (buf[4] & 128) != 0
|| (buf[5] & 128) != 0
|| (buf[6] & 128) != 0
|| (buf[7] & 128) != 0 )
goto error;
frame->datasize = buf[7] + (buf[6] << 7) + (buf[5] << 14) + (buf[4] << 21);
// A minimum size must be present!
if ( frame->datasize <= 0 )
goto error;
// The two following frame header flags have the 5 LSBs not set.
if ( (buf[8] & 31) != 0
|| (buf[9] & 31) != 0 )
goto error;
// now, put the flags in the struct.
frame->tagalter = (buf[8] & 128) >> 7;
frame->filealter = (buf[8] & 64) >> 6;
frame->readonly = (buf[8] & 32) >> 5;
frame->compression = (buf[9] & 128) >> 7;
frame->encryption = (buf[8] & 64) >> 6;
frame->grouping = (buf[8] & 32) >> 5;
// ok, we are done with the frame header, so now we read the frame data.
frame->data = calloc (1, frame->datasize + 1);
if( fread (frame->data, 1, frame->datasize, mp3file) < frame->datasize )
goto error;
numframes++;
totalframesize += 10 + frame->datasize;
// we append it to a glist, which is appended to the v2 struct.
v2->frames = dll_append (v2->frames, frame);
}
// if we have no extheader, that means, we don't know how much padding we have!
// thus, i calculate it here.
if ( !v2->has_extheader )
v2->padding_size = v2->tag_size - totalframesize;
// A minimum of one frame is mandatory.
if( numframes > 0 )
return (TRUE);
error:
// cleanups in case of an error.
if( frame && frame->data )
free (frame->data);
if( frame )
free (frame);
return (FALSE);
}
/*
* Reads all tag information of a valid ID3V2.3 file.
* When successful, the info is stored in the tag struct.
* Returns:
* 0 = success
* 1 = Cannot open file.
* 2 = No header or wrong version.
* 3 = broken extheader.
* 4 = broken frames.
*/
static int
get_id3v2tag_raw (id3v2Tag *v2, char *filename)
{
FILE *mp3file = NULL;
int error = 0;
// open file
error = 1;
mp3file = fopen (filename, "rb");
if (!mp3file)
goto done;
// check/get header
error = 2;
if( !check_header (mp3file, v2) )
goto done;
// check/get extheader
error = 3;
if( v2->has_extheader
&& !check_extheader (mp3file, v2) )
goto done;
// get the content frames
error = 4;
if( !read_frames (mp3file, v2) )
goto done;
error = 0;
done:
fclose (mp3file);
return (error);
}
/*
* Find one frames data and give bag its data in the correct format.
* Returns TRUE on success, otherwise FALSE;
*/
static short int
frame_find (id3v2Tag *v2, char *name, char *value)
{
DLL *curframe = NULL;
id3v2Frame *frame = NULL;
// we parse through the whole list of frames, giving back the correct frame value.
curframe = v2->frames;
while ( curframe )
{
frame = (id3v2Frame *)curframe->data;
// Just to be sure...
if( frame->datasize <= 0 )
goto nextframe;
// Matches the users request? Otherwise try the next frame.
if( memcmp (frame->id, name, 4) != 0 )
goto nextframe;
// This types don't need much change, just give the whole data back to the user according to the encoding.
// The first byte is the encoding.
// TPE1: Artist
// TIT2: Song Title
// TALB: Album Title
// TYER: Year
// TRCK: Track
// TCON: Genre
// COMM: Comment
if ( memcmp (frame->id, "TPE1", 4) == 0
|| memcmp (frame->id, "TIT2", 4) == 0
|| memcmp (frame->id, "TALB", 4) == 0
|| memcmp (frame->id, "TYER", 4) == 0
|| memcmp (frame->id, "TRCK", 4) == 0
|| memcmp (frame->id, "TCON", 4) == 0
|| memcmp (frame->id, "TALB", 4) == 0 )
{
if ( *frame->data == 0 )
memcpy(value, frame->data + 1, frame->datasize - 1);
if ( *frame->data == 1 )
{
char nulltermvalue[frame->datasize];
char *isovalue = NULL;
// the tag is not null terminated, so i have to create a null terminated string first.
memset (nulltermvalue, 0, frame->datasize);
memcpy (nulltermvalue, frame->data + 1, frame->datasize - 1);
// Convert from UTF to ISO and copy to the users variable.
isovalue = convert_from_utf8 (nulltermvalue);
strncpy (value, isovalue, sizeof(value) - 1);
free (isovalue);
}
// change linefeeds to a single "return" key.
crlf2cr (value);
return (TRUE);
}
// The comment requires special handling.
// Its data has: One byte "encoding" (0 = ISO-8859-1, 1 = UNICODE)
// followed by the language (three bytes, e.g. "eng"),
// followed by a short description,
// then a NULL,
// and the full description
// By now, i simply drop the short description
if( memcmp(frame->id, "COMM", 4) == 0 )
{
// check for the right format. (minsize 5, must contain a "\0" after the language)
if ( frame->datasize < 5 )
goto nextframe;
if ( !memchr (frame->data + 4, '\0', frame->datasize - 4) )
goto nextframe;
// now, give the data back to the user, according to the encoding.
if ( *frame->data == 0 )
memcpy (value, frame->data + 5, frame->datasize - 5);
if ( *frame->data == 1 )
{
char nulltermvalue[frame->datasize];
char *isovalue = NULL;
// the tag is not null terminated, so i have to create a null terminated string first.
memset (nulltermvalue, 0, frame->datasize);
memcpy (nulltermvalue, frame->data + 5, frame->datasize - 5);
// Convert from UTF to ISO and copy to the users variable.
isovalue = convert_from_utf8 (nulltermvalue);
strncpy (value, isovalue, sizeof(value) - 1);
free (isovalue);
}
// change linefeeds to a single "return" key.
crlf2cr (value);
return TRUE;
}
nextframe:
curframe = curframe->next;
}
return FALSE;
}
/*
* Remove one frame out of the id3v2Tag struct
* Returns TRUE on success, otherwise FALSE;
*/
static short int
frame_remove (id3v2Tag *v2, char *name)
{
id3v2Frame *frame = NULL;
DLL *curframe = NULL;
DLL *tempframe = NULL;
// Parse through the list of frames.
curframe = v2->frames;
while ( curframe )
{
frame = (id3v2Frame *)curframe->data;
tempframe = curframe;
curframe = curframe->next;
if ( memcmp (frame->id, name, 4) == 0 )
{
// we have found the item! removing will NOT shrink the tag, but increase the padding.
v2->padding_size += (frame->datasize + 10);
// and free memory.
v2->frames = dll_remove (v2->frames, tempframe->data);
free (frame->data);
free (frame);
return TRUE;
}
}
return FALSE;
}
/*
* Add a frame to the framelist. If the frame name is already in the list, it will be replaced.
* Returns:
* TRUE: The tag size HAS BEEN increased.
* FALSE: The tag size has NOT been increased.
*/
static short int
frame_set (id3v2Tag *v2, char *name, char *value)
{
id3v2Frame *frame = NULL;
short int sizechange = FALSE;
// prevent the user to send CR/LF, which is forbidden.
cr2crlf (value);
// eventually remove an existing item!
frame_remove (v2, name);
// alloc space for the new frame.
frame = malloc (sizeof(id3v2Frame));
memcpy (frame->id, name, 4);
frame->datasize = strlen (value);
frame->tagalter = 0;
frame->filealter = 0;
frame->readonly = 0;
frame->compression = 0;
frame->encryption = 0;
frame->grouping = 0;
// The comment requires special handling. If you need to know why, look at the documentation
// of the "frame_find" function above.
if( memcmp (frame->id, "COMM", 4) == 0 )
{
char fullvalue[frame->datasize + 6];
sprintf(fullvalue, "%ceng%c%s", 0, 0, value);
frame->datasize += 5;
frame->data = malloc (frame->datasize);
memcpy (frame->data, fullvalue, frame->datasize);
}
else
{
char fullvalue[frame->datasize + 2];
sprintf (fullvalue, "%c%s", 0, value);
frame->datasize += 1;
frame->data = malloc (frame->datasize);
memcpy (frame->data, fullvalue, frame->datasize);
}
// Ok. This decreases the available padding. If we have no padding left, we must increase the padding (and thus, the tag).
if( v2->padding_size - (frame->datasize + 10) <= 0 )
{
// add: framesize + frameheadersize + padding.
v2->padding_size += frame->datasize + 10 + 1024;
v2->tag_size += frame->datasize + 10 + 1024;
sizechange = TRUE;
}
// In every case, we must subtract the new allocated space from the padding.
v2->padding_size -= frame->datasize + 10;
v2->frames = dll_append (v2->frames, frame);
return sizechange;
}
/*
* Create raw header.
* Returns:
* TRUE: successful.
* FALSE: unsuccessful.
*/
static int
create_header_raw (char *raw, id3v2Tag *v2)
{
// now we are going to write the tags raw data into the raw string
memset (raw, 0, v2->tag_size + 10);
// ID3 identifier bytes
memcpy (raw, "ID3", 3);
raw += 3;
// major version byte
*raw++ = 3;
// minor version byte
*raw++ = 0;
// Flags byte
*raw++ = ((v2->unsync & 1) << 7)
| ((v2->has_extheader & 1) << 6)
| ((v2->is_experimental & 1) << 5);
// Tag size. It must be syncsafe!
*raw++ = ((v2->tag_size & 0x800000) >> 23) | (((v2->tag_size & 0x7f000000) >> 24) << 1);
*raw++ = ((v2->tag_size & 0x8000) >> 15) | (((v2->tag_size & 0x7f0000) >> 16) << 1);
*raw++ = ((v2->tag_size & 0x80) >> 7) | (((v2->tag_size & 0x7f00) >> 8) << 1);
*raw++ = (v2->tag_size & 0x7f);
return TRUE;
}
/*
* Generates the frames. btw.: ID3 sucks!
* Returns: TRUE if succesful, otherwise FALSE.
*/
static short int
create_frames_raw (char *raw, id3v2Tag *v2)
{
id3v2Frame *frame = NULL;
DLL *curframe = NULL;
// if we have no frames, just quit.
if ( v2->frames == NULL )
return FALSE;
// the header and extheader have already been written.
raw += 10;
if ( v2->has_extheader )
raw += 4 + v2->extheader_size;
curframe = v2->frames;
while ( curframe )
{
frame = (id3v2Frame *)curframe->data;
// secure is secure
if ( frame->datasize <= 0 )
goto nextframe;
// add the frame id
memcpy(raw, frame->id, 4);
raw += 4;
// add the frame size (syncsafe)
*raw++ = ((frame->datasize & 0x800000) >> 23) | (((frame->datasize & 0x7f000000) >> 24) << 1);
*raw++ = ((frame->datasize & 0x8000) >> 15) | (((frame->datasize & 0x7f0000) >> 16) << 1);
*raw++ = ((frame->datasize & 0x80) >> 7) | (((frame->datasize & 0x7f00) >> 8) << 1);
*raw++ = (frame->datasize & 0x7f);
// The two flagbytes
*raw++ = ((frame->tagalter & 1) << 7)
| ((frame->filealter & 1) << 6)
| ((frame->readonly & 1) << 5);
*raw++ = ((frame->compression & 1) << 7)
| ((frame->encryption & 1) << 6)
| ((frame->grouping & 1) << 5);
// now the frame data.
memcpy(raw, frame->data, frame->datasize);
raw += frame->datasize;
nextframe:
curframe = curframe->next;
}
return TRUE;
}
/***************************************************************************************
* END OF STATICS
***************************************************************************************/
/*
* Purpose: Reads the ID3 tag from a file.
* Parameters: tag - The structure to store the tag in, filename - The name of the file to operate on.
* Returns:
* 0 if successful,
* 1 if an error occured when opening the file
* 2 if error while reading tag.
* 3 if no TAG found.
*/
int
get_id3v2_tag (id3Tag *tag, char *filename)
{
id3v2Tag *v2 = calloc (1, sizeof(id3v2Tag));
DLL *curframe = NULL;
int error = 0;
// Read the tag.
error = get_id3v2tag_raw (v2, filename);
// Init the users tag
memset (tag, 0, sizeof (id3Tag));
if( error == 0 )
{
// if we have a valid tag we copy the raw data to the users struct
tag->size = v2->tag_size;
frame_find (v2, "TPE1", tag->artist);
frame_find (v2, "TIT2", tag->title);
frame_find (v2, "TALB", tag->album);
frame_find (v2, "TYER", tag->year);
frame_find (v2, "COMM", tag->comment);
frame_find (v2, "TRCK", tag->track);
frame_find (v2, "TCON", tag->genre);
}
// Free all the stuff
if (v2->frames)
{
id3v2Frame *frame = NULL;
curframe = v2->frames;
while ( curframe )
{
frame = (id3v2Frame *)curframe->data;
free (frame->data);
free (frame);
curframe = curframe->next;
}
v2->frames = dll_free (v2->frames);
}
free (v2);
return (error);
}
/*
* Purpose: Clear the ID3 tag of a file.
* Parameters: a filename.
* Returns:
* 0 if successful,
* 1 if an error occured when opening the file
* 2 if an error while reading/writing the tag.
*/
int
del_id3v2_tag (char *filename)
{
id3v2Tag *v2 = calloc (1, sizeof(id3v2Tag));
long file_len;
FILE *file;
int read;
void *ptr;
// check if an valid old id3v2 tag is present
// In these two error-cases we don't know how big the tag is.
if( get_id3v2tag_raw (v2, filename) == 1
|| get_id3v2tag_raw (v2, filename) == 2 )
return(0);
ptr = malloc (4096);
// open file read/write
file = fopen (filename, "r+b");
if (!file)
return (1);
fseek (file, 0, SEEK_END);
file_len = ftell (file);
if ( file_len < 11
|| v2->tag_size < 11 )
return (2);
// set anything but the header in tag to zero. ill not really remove the tag,
// because this would be much slower and if we write a new tag, this would mean we´d have to
// rewrite the complete tag.
fseek (file, 10, SEEK_SET);
for (read = 0; read < v2->tag_size - 10; read++)
fputc (0, file);
fflush (file);
fclose (file);
free (ptr);
free (v2);
return(0);
}
int
set_id3v2_tag (id3Tag *tag, char *filename)
{
id3v2Tag *v2 = malloc (sizeof(id3v2Tag));
id3v2Frame *frame = NULL;
unsigned char *rawdata = NULL;
DLL *curframe = NULL;
int oldsize = 0;
char track[3];
int error = 0;
// Try to get the content of an old tag
error = 1;
memset (v2, 0, sizeof(id3v2Tag));
get_id3v2tag_raw (v2, filename);
oldsize = v2->tag_size;
// If the old tag had an extheader, ill add its size to my tag, because i don't plan to add it again.
if ( v2->has_extheader )
{
v2->padding_size += v2->extheader_size;
v2->has_extheader = FALSE;
v2->extheader_size = 0;
}
// first of all ill generate a valid id3v2 tag struct out of the tag struct we got by the user.
// Set the flags...
v2->unsync = FALSE;
v2->is_experimental = FALSE;
v2->crc_data_present = FALSE;
// Set the contentframes
frame_set (v2, "TIT2", tag->title);
frame_set (v2, "TPE1", tag->artist);
frame_set (v2, "TALB", tag->album);
frame_set (v2, "TYER", tag->year);
frame_set (v2, "COMM", tag->comment);
frame_set (v2, "TCON", tag->genre);
if ( atoi (tag->track) < 10 )
snprintf (track, 3, "0%i", atoi(tag->track));
else
snprintf (track, 3, "%i", atoi(tag->track));
frame_set (v2, "TRCK", track);
// Create a header in the raw data string
rawdata = calloc (1, v2->tag_size + 10);
create_header_raw (rawdata, v2);
// Create frames raw data.
create_frames_raw (rawdata, v2);
// is the new tag bigger than the old one? Then we'll have to completely rewrite the file...
if ( v2->tag_size > oldsize )
{
FILE *file = NULL;
FILE *tempfile = NULL;
char *tempfilename = NULL;
int read = 0;
char buf[4096];
// Open a tempfile
error = 2;
tempfilename = malloc (strlen (filename) + 11);
sprintf (tempfilename, "%s%s", filename, ".tempXXXXX");
if( !(tempfile = fopen(tempfilename, "wb")) )
{
remove (tempfilename);
free (tempfilename);
goto done;
}
// Write the tag to the tempfile.
error = 3;
fseek (tempfile, 0, SEEK_SET);
if( fwrite (rawdata, 1, v2->tag_size + 10, tempfile) < v2->tag_size )
{
fclose (tempfile);
remove (tempfilename);
free (tempfilename);
goto done;
}
// Open the mp3file.
error = 4;
if( !(file = fopen(filename, "r+b")) )
{
fclose (file);
remove (tempfilename);
free (tempfilename);
goto done;
}
// skip the old tag (if one existed)
fseek (file, oldsize? oldsize + 10 : oldsize, SEEK_SET);
// copy the rest of the file to the tempfile.
while ( !feof(file) )
{
error = 5;
read = fread (buf, 1, 4096, file);
if( fwrite (buf, 1, read, tempfile) != read
&& !feof (file) )
{
remove (tempfilename);
free (tempfilename);
fflush (tempfile);
fclose (tempfile);
fflush (file);
fclose (file);
goto done;
}
}
fflush (file);
fclose (file);
fflush (tempfile);
fclose (tempfile);
// rename the tempfile, so it is the mp3file.
rename (tempfilename, filename);
free (tempfilename);
}
else
{
FILE *file = NULL;
// If the old tag was bigger than the new one, we can simply overwrite it!
// open.
error = 10;
if( !(file = fopen(filename, "r+b")) )
goto done;
// write.
error = 11;
fseek (file, 0, SEEK_SET);
if( fwrite (rawdata, 1, v2->tag_size + 10, file) < v2->tag_size )
{
fflush (file);
fclose (file);
goto done;
}
fflush (file);
fclose (file);
}
error = 0;
done:
// Free all the stuff
curframe = v2->frames;
while ( curframe )
{
frame = (id3v2Frame *)curframe->data;
free (frame->data);
free (frame);
curframe = curframe->next;
}
dll_free (v2->frames);
if ( rawdata != NULL )
free (rawdata);
free (v2);
return (error);
}
/* the id3v2.3 library.
* (c)2002 by Samuel Abels (sam@manicsadness.com)
* This project's homepage is: http://software.manicsadness.com/cantus
*
* This library is designed for easyest possible access to id3 V2 tags.
*
* 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
*/
#ifndef DLL_H
#define DLL_H
typedef struct DLL_s
{
void *prev;
void *data;
void *next;
} DLL;
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef id3Tag_def
#define id3Tag_def
typedef struct id3Tag_s
{
char title[1024];
char artist[1024];
char album[1024];
char year[5];
char comment[1024];
char track[10];
char genre[512];
unsigned int size;
short int has_footer;
} id3Tag;
typedef struct id3v2Tag_s
{
// header
int tag_size;
short int unsync;
short int has_extheader;
short int is_experimental;
//extheader
int extheader_size;
int padding_size;
short int crc_data_present;
char crc_data[4];
// frames
DLL *frames;
} id3v2Tag;
typedef struct id3v2Frame_s
{
unsigned char id[4];
int datasize;
short int tagalter;
short int filealter;
short int readonly;
short int compression;
short int encryption;
short int grouping;
char *data;
} id3v2Frame;
#endif
extern int get_id3v2_tag(id3Tag *tag, char *filename);
extern int set_id3v2_tag(id3Tag *tag, char *filename);
extern int del_id3v2_tag(char *filename);
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#include "info.h"
#include "command.h"
#include "tag.h"
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
int info(FILE * fp, char * file) {
MpdTag * tag;
struct stat st;
if(stat(file,&st)) {
fprintf(fp,"%s not able to stat file\n",COMMAND_RESPOND_ERROR);
return -1;
}
if(!S_ISREG(st.st_mode)) {
fprintf(fp,"%s not a file\n",COMMAND_RESPOND_ERROR);
return -1;
}
if((tag=id3Dup(file))) {
printMpdTag(fp,tag);
freeMpdTag(tag);
return 0;
}
fprintf(fp,"%s problems geting id3 info\n",COMMAND_RESPOND_ERROR);
return -1;
}
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#ifndef INFO_H
#define INFO_H
#include <stdio.h>
int info(FILE * fp, char * file);
#endif
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#include "interface.h"
#include "buffer2array.h"
#include "command.h"
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/time.h>
#include <sys/types.h>
#include <string.h>
#define VERSION "0.5.2"
#define GREETING "MPD"
#define INTERFACE_MAX_BUFFER_LENGTH 1024
#define INTERFACE_MAX_CONNECTIONS 5
#define INTERFACE_TIMEOUT 60
typedef struct _Interface {
char buffer[INTERFACE_MAX_BUFFER_LENGTH+1];
int bufferLength;
int fd; /* file descriptor */
FILE * fp; /* file pointer */
int open; /* open/used */
time_t lastTime;
} Interface;
Interface interfaces[INTERFACE_MAX_CONNECTIONS];
void openInterface(Interface * interface, int fd) {
assert(interface->open==0);
interface->bufferLength = 0;
interface->fd = fd;
interface->fp = fdopen(fd,"w");
interface->open = 1;
interface->lastTime = time(NULL);
fprintf(interface->fp,"%s %s %s\n",COMMAND_RESPOND_OK,GREETING,VERSION);
}
void closeInterface(Interface * interface) {
assert(interface->open);
interface->open = 0;
if(fclose(interface->fp)) {
fprintf(stderr,"Error closing file pointer\n");
}
}
void openAInterface(int fd) {
int i;
for(i=0;i<INTERFACE_MAX_CONNECTIONS && interfaces[i].open;i++);
if(i==INTERFACE_MAX_CONNECTIONS) {
FILE * fp = fdopen(fd,"w");
fprintf(fp,"%s Max Connections Reached!\n",COMMAND_RESPOND_ERROR);
fclose(fp);
close(fd);
}
else {
openInterface(&(interfaces[i]),fd);
}
}
int interfaceReadInput(Interface * interface) {
if(read(interface->fd,interface->buffer+interface->bufferLength,1)) {
int ret = 1;
if(interface->buffer[interface->bufferLength]!='\r') {
interface->bufferLength++;
}
if(interface->bufferLength>=INTERFACE_MAX_BUFFER_LENGTH) {
fprintf(stdout,"Buffer Overflow\n");
closeInterface(interface);
}
if(interface->buffer[interface->bufferLength-1]=='\n') {
int i;
char ** argArray;
int argArrayLength;
interface->buffer[interface->bufferLength-1] = '\0';
argArrayLength = buffer2array(interface->buffer,&argArray);
ret = processCommand(interface->fp,argArrayLength,argArray);
for(i=0;i<argArrayLength;i++) {
free(argArray[i]);
}
free(argArray);
interface->bufferLength = 0;
}
if(ret==0) {
fprintf(interface->fp,"%s\n",COMMAND_RESPOND_OK);
}
if(ret==COMMAND_RETURN_CLOSE) {
closeInterface(interface);
}
return ret;
}
else {
closeInterface(interface);
}
return 1;
}
void addInterfacesToFdSet(fd_set * fds) {
int i;
FD_ZERO(fds);
for(i=0;i<INTERFACE_MAX_CONNECTIONS;i++) {
if(interfaces[i].open) {
FD_SET(interfaces[i].fd,fds);
}
}
}
int readInputFromInterfaces() {
fd_set fds;
struct timeval tv;
int i;
tv.tv_sec = 0;
tv.tv_usec = 0;
addInterfacesToFdSet(&fds);
while(select(FD_SETSIZE,&fds,NULL,NULL,&tv)) {
for(i=0;i<INTERFACE_MAX_CONNECTIONS;i++) {
if(interfaces[i].open && FD_ISSET(interfaces[i].fd,&fds)) {
if(COMMAND_RETURN_KILL==interfaceReadInput(&(interfaces[i]))) {
return COMMAND_RETURN_KILL;
}
interfaces[i].lastTime = time(NULL);
}
}
addInterfacesToFdSet(&fds);
}
return 1;
}
void initInterfaces() {
int i;
for(i=0;i<INTERFACE_MAX_CONNECTIONS;i++) {
interfaces[i].open = 0;
}
}
void closeAllInterfaces() {
int i;
for(i=0;i<INTERFACE_MAX_CONNECTIONS;i++) {
if(interfaces[i].open) {
closeInterface(&(interfaces[i]));
}
}
}
void closeOldInterfaces() {
int i;
for(i=0;i<INTERFACE_MAX_CONNECTIONS;i++) {
if(interfaces[i].open && (time(NULL)-interfaces[i].lastTime>INTERFACE_TIMEOUT)) {
closeInterface(&(interfaces[i]));
}
}
}
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#ifndef INTERFACE_H
#define INTERFACE_H
#include <stdio.h>
#include <time.h>
void initInterfaces();
void openAInterface(int fd);
void closeAllInterfaces();
void closeOldInterfaces();
int readInputFromInterfaces();
#endif
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#include "list.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
List * makeList(ListFreeDataFunc * freeDataFunc) {
List * list = malloc(sizeof(List));
assert(list!=NULL);
list->firstNode = NULL;
list->lastNode = NULL;
list->freeDataFunc = freeDataFunc;
list->numberOfNodes = 0;
return list;
}
int insertInList(List * list,char * key,void * data) {
ListNode * node;
assert(list!=NULL);
assert(key!=NULL);
assert(data!=NULL);
node = malloc(sizeof(ListNode));
assert(node!=NULL);
if(list->firstNode==NULL) {
assert(list->lastNode==NULL);
list->firstNode = node;
}
else {
assert(list->lastNode!=NULL);
assert(list->lastNode->nextNode==NULL);
list->lastNode->nextNode = node;
}
node->key = malloc((strlen(key)+1)*sizeof(char));
assert(node->key!=NULL);
strcpy(node->key,key);
node->data = data;
node->nextNode = NULL;
node->prevNode = list->lastNode;
list->lastNode = node;
list->numberOfNodes++;
return 1;
}
int insertInListWithoutKey(List * list, void * data) {
ListNode * node;
assert(list!=NULL);
assert(data!=NULL);
node = malloc(sizeof(ListNode));
assert(node!=NULL);
if(list->firstNode==NULL) {
assert(list->lastNode==NULL);
list->firstNode = node;
}
else {
assert(list->lastNode!=NULL);
assert(list->lastNode->nextNode==NULL);
list->lastNode->nextNode = node;
}
node->key = NULL;
node->data = data;
node->nextNode = NULL;
node->prevNode = list->lastNode;
list->lastNode = node;
list->numberOfNodes++;
return 1;
}
int findInList(List * list,char * key,void ** data) {
ListNode * tmpNode;
assert(list!=NULL);
tmpNode = list->firstNode;
while(tmpNode!=NULL && strcmp(tmpNode->key,key)!=0) {
tmpNode = tmpNode->nextNode;
}
if(tmpNode!=NULL) {
(*data) = tmpNode->data;
}
else {
return 0;
}
return 1;
}
int deleteFromList(List * list,char * key) {
ListNode * tmpNode;
assert(list!=NULL);
tmpNode = list->firstNode;
while(tmpNode!=NULL && strcmp(tmpNode->key,key)!=0) {
tmpNode = tmpNode->nextNode;
}
if(tmpNode!=NULL)
deleteNodeFromList(list,tmpNode);
else
return 0;
return 1;
}
void deleteNodeFromList(List * list,ListNode * node) {
assert(list!=NULL);
assert(node!=NULL);
if(node->prevNode==NULL) {
list->firstNode = node->nextNode;
}
else {
node->prevNode->nextNode = node->nextNode;
}
if(node->nextNode==NULL) {
list->lastNode = node->prevNode;
}
else {
node->nextNode->prevNode = node->prevNode;
}
if(list->freeDataFunc) {
list->freeDataFunc(node->data);
}
free(node->key);
free(node);
list->numberOfNodes--;
}
void freeList(void * list) {
ListNode * tmpNode;
ListNode * tmpNode2;
assert(list!=NULL);
tmpNode = ((List *)list)->firstNode;
while(tmpNode!=NULL) {
tmpNode2 = tmpNode->nextNode;
free(tmpNode->key);
if(((List *)list)->freeDataFunc) {
((List *)list)->freeDataFunc(tmpNode->data);
}
free(tmpNode);
tmpNode = tmpNode2;
}
free(list);
}
void clearList(List * list) {
ListNode * tmpNode;
ListNode * tmpNode2;
assert(list!=NULL);
tmpNode = ((List *)list)->firstNode;
while(tmpNode!=NULL) {
tmpNode2 = tmpNode->nextNode;
free(tmpNode->key);
if(((List *)list)->freeDataFunc) {
((List *)list)->freeDataFunc(tmpNode->data);
}
free(tmpNode);
tmpNode = tmpNode2;
}
list->firstNode = NULL;
list->lastNode = NULL;
}
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#ifndef LIST_H
#define LIST_H
#include <stdlib.h>
/* used to make a list where free() will be used to free data in list */
#define DEFAULT_FREE_DATA_FUNC free
/* typedef for function to free data stored in the list nodes */
typedef void ListFreeDataFunc(void *);
typedef struct _ListNode {
/* used to identify node (ie. when using findInList) */
char * key;
/* data store in node */
void * data;
/* next node in list */
struct _ListNode * nextNode;
/* previous node in list */
struct _ListNode * prevNode;
} ListNode;
typedef struct _List {
/* first node in list */
ListNode * firstNode;
/* last node in list */
ListNode * lastNode;
/* function used to free data stored in nodes of the list */
ListFreeDataFunc * freeDataFunc;
/* number of ndoes */
int numberOfNodes;
} List;
/* allocates memmory for a new list and initializes it
* _freeDataFunc_ -> pointer to function used to free data, use
* DEFAULT_FREE_DATAFUNC to use free()
* returns pointer to new list if successful, NULL otherwise
*/
List * makeList(ListFreeDataFunc * freeDataFunc);
/* inserts a node into _list_ with _key_ and _data_
* _list_ -> list the data will be inserted in
* _key_ -> identifier for node/data to be inserted into list
* _data_ -> data to be inserted in list
* returns 1 if successful, 0 otherwise
*/
int insertInList(List * list,char * key,void * data);
int insertInListWithoutKey(List * list,void * data);
/* deletes the first node in the list with the key _key_
* _list_ -> list the node will be deleted from
* _key_ -> key used to identify node to delete
* returns 1 if node is found and deleted, 0 otherwise
*/
int deleteFromList(List * list,char * key);
void deleteNodeFromList(List * list,ListNode * node);
/* finds data in a list based on key
* _list_ -> list to search for _key_ in
* _key_ -> which node is being searched for
* _data_ -> a pointer to where data will be placed,
* _data_ memmory should not by allocated or freed
* returns 1 if successful, 0 otherwise
*/
int findInList(List * list, char * key, void ** data);
/* frees memmory malloc'd for list and its nodes
* _list_ -> List to be free'd
*/
void freeList(void * list);
void clearList(List * list);
#endif
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#include "ls.h"
#include "command.h"
#include "playlist.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int ls(FILE * fp, char * dirname) {
DIR * dir;
char cwd[2];
char c;
struct stat st;
struct dirent * ent;
char s[MAXPATHLEN];
cwd[0] = '.';
cwd[1] = '\0';
if(dirname==NULL) dirname=cwd;
if((dir = opendir(dirname))==NULL) {
fprintf(fp,"%s problems opening directory\n",COMMAND_RESPOND_ERROR);
return -1;
}
while((ent = readdir(dir))) {
if(ent->d_name[0]=='.') continue; /* hide hidden stuff */
sprintf(s,"%s/%s",dirname,ent->d_name);
if(stat(s,&st)==0) {
c = 0;
if(S_ISDIR(st.st_mode)) c = 'd';
else if(isMp3(s)) c = 'm';
if(c && dirname==cwd) {
fprintf(fp,"%c \"%s\"\n",c,ent->d_name);
}
else if(c) {
fprintf(fp,"%c \"%s/%s\"\n",c,dirname,ent->d_name);
}
}
}
closedir(dir);
return 0;
}
int lsPlaylists(FILE * fp) {
DIR * dir;
struct stat st;
struct dirent * ent;
char * cLast;
char * cNext;
char * dup;
char s[MAXPATHLEN];
if((dir = opendir(playlistDir))==NULL) {
fprintf(fp,"%s problems opening playlist directory\n",COMMAND_RESPOND_ERROR);
return -1;
}
while((ent = readdir(dir))) {
if(ent->d_name[0]=='.') continue; /* hide hidden stuff */
sprintf(s,"%s/%s",playlistDir,ent->d_name);
if(stat(s,&st)==0) {
if(S_ISREG(st.st_mode)) {
dup = strdup(ent->d_name);
cNext = cLast = strtok(dup,".");
while((cNext = strtok(NULL,"."))) cLast = cNext;
if(cLast && 0==strcmp(cLast,PLAYLIST_FILE_SUFFIX)) {
strncpy(dup,ent->d_name,strlen(ent->d_name)-strlen(PLAYLIST_FILE_SUFFIX)-1);
fprintf(fp,"playlist: %s\n",dup);
}
free(dup);
}
}
}
closedir(dir);
return 0;
}
time_t isMp3(char * file) {
struct stat st;
if(stat(file,&st)==0) {
if(S_ISREG(st.st_mode)) {
char * dup;
char * cLast;
char * cNext;
int ret = 0;
dup = strdup(file);
cNext = cLast = strtok(dup,".");
while((cNext = strtok(NULL,"."))) cLast = cNext;
if(cLast && 0==strcasecmp(cLast,"mp3")) {
ret = st.st_mtime;
}
free(dup);
return ret;
}
else return 0;
}
return 0;
}
time_t isDir(char * name) {
struct stat st;
if(stat(name,&st)==0) {
if(S_ISDIR(st.st_mode)) return st.st_mtime;
}
return 0;
}
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#ifndef LS_H
#define LS_H
#include <stdio.h>
#include <time.h>
#include "id3v2lib/lib_id3v2.3.h"
int ls(FILE * fp, char * dir);
int lsPlaylists(FILE * fp);
time_t isMp3(char * file);
time_t isDir(char * name);
#endif
/* the Music Player Daemon (MPD)
* (c)2002 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#include "interface.h"
#include "command.h"
#include "mpg123.h"
#include "playlist.h"
#include "directory.h"
#include "tables.h"
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#define MAXHOSTNAME 1024
void usage(char * argv[]) {
fprintf(stderr,"usage: %s <port> <mp3 dir> <playlist dir> <log file> <error file>\n",argv[0]);
}
int establish(unsigned short port) {
char myname[MAXHOSTNAME+1];
int sock;
struct sockaddr_in sockAddr;
struct hostent * he;
memset(&sockAddr, 0, sizeof(struct sockaddr_in));
gethostname(myname,MAXHOSTNAME);
he = gethostbyname(myname);
if(he == NULL) {
fprintf(stderr,"he == NULL\n");
return -1;
}
sockAddr.sin_family = he->h_addrtype;
sockAddr.sin_port = htons(port);
if((sock = socket(AF_INET,SOCK_STREAM,0)) < 0) {
fprintf(stderr,"socket < 0\n");
return -1;
}
if(bind(sock,(struct sockaddr *)&sockAddr,sizeof(struct sockaddr_in)) < 0) {
fprintf(stderr,"bind < 0\n");
close(sock);
return -1;
}
listen(sock,0);
return sock;
}
void getConnections(int sock) {
fd_set fdsr;
int fd;
struct timeval tv;
tv.tv_sec = tv.tv_usec = 0;
fflush(NULL);
FD_ZERO(&fdsr);
FD_SET(sock,&fdsr);
if(select(sock+1,&fdsr,NULL,NULL,&tv)==1 &&
((fd = accept(sock,NULL,NULL)) >= 0)) {
openAInterface(fd);
}
else if(fd<0) {
fprintf(stderr,"Problems accept()'ing\n");
}
}
int main(int argc, char * argv[]) {
int port;
int sock;
struct stat st;
FILE * out;
FILE * err;
if(argc!=6) {
usage(argv);
return -1;
}
if((port = atoi(argv[1]))<0) {
fprintf(stderr,"problem with port number\n");
return -1;
}
if(NULL==(out=fopen(argv[4],"a"))) {
fprintf(stderr,"problem opening file \"%s\" for writing\n",argv[3]);
return -1;
}
if(NULL==(err=fopen(argv[5],"a"))) {
fprintf(stderr,"problem opening file \"%s\" for writing\n",argv[3]);
return -1;
}
if((stat(argv[2],&st))<0) {
fprintf(stderr,"problem stat'ing \"%s\"\n",argv[2]);
return -1;
}
if(!S_ISDIR(st.st_mode)) {
fprintf(stderr,"\"%s\" is not a directory\n",argv[2]);
return -1;
}
if(argv[3][0]=='/') {
strcpy(playlistDir,argv[3]);
}
else {
getcwd(playlistDir,MAXPATHLEN-strlen(argv[3])-1);
strcat(playlistDir,"/");
strcat(playlistDir,argv[3]);
strcat(playlistDir,"/");
}
if((stat(playlistDir,&st))<0) {
fprintf(stderr,"problem stat'ing \"%s\"\n",argv[3]);
return -1;
}
if(!S_ISDIR(st.st_mode)) {
fprintf(stderr,"\"%s\" is not a directory\n",argv[3]);
return -1;
}
chdir(argv[2]);
initTables();
strcpy(directorydb,playlistDir);
strcat(directorydb,"/");
strcat(directorydb,".mpddb");
if(readDirectoryDB()<0) {
initMp3Directory();
if(writeDirectoryDB()<0) {
fprintf(stderr,"problem opening db for reading or writing\n");
exit(-1);
}
}
if((sock = establish(port))<0) {
fprintf(stderr,"error bindping port\n");
return -1;
}
initInterfaces();
initPlaylist();
daemon(1,0);
dup2(fileno(out),STDOUT_FILENO);
dup2(fileno(err),STDERR_FILENO);
while(COMMAND_RETURN_KILL!=readInputFromInterfaces()) {
mpg123processMessages();
getConnections(sock);
closeOldInterfaces();
usleep(1000);
}
clearPlaylist(stderr);
closeAllInterfaces();
close(sock);
mpg123stop(stderr);
mpg123kill();
closeTables();
closeMp3Directory();
return 0;
}
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#include "mpg123.h"
#include "command.h"
#include "interface.h"
#include "playlist.h"
#include "ls.h"
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#define MAX_BUFFER_LENGTH 1024
#define MPG123_PLAYER_BUFFER_SIZE "2048"
int mpg123_pid = 0;
int mpg123_send = 0;
int mpg123_recv = 0;
int mpg123_stderr = 0;
char mpg123_buffer[MAX_BUFFER_LENGTH+1];
int mpg123_bufferLength = 0;
int mpg123_totalTime = 0;
int mpg123_elapsedTime = 0;
int mpg123_reportedElapsedTime = 0;
int mpg123_state = MPG123_STATE_STOP;
int mpg123_leavePlay = 0;
int mpg123_leavePause = 0;
int mpg123_error = 0;
int mpg123_stopAtEnd = 0;
int mpg123_lastFrame = 0;
void mpg123_sigHandler(int signal) {
if(signal==SIGCHLD) {
wait3(NULL,WNOHANG,NULL);
mpg123_pid = 0;
close(mpg123_recv);
close(mpg123_send);
close(mpg123_stderr);
if(mpg123_state==MPG123_STATE_PLAY) {
mpg123_leavePlay = 1;
fprintf(stderr,"mpg123 died while playing\n");
nextSongInPlaylist(stderr);
}
mpg123_state = MPG123_STATE_STOP;
}
}
int mpg123init() {
int fd_send[2], fd_recv[2], fd_stderr[2];
mpg123_error = 0;
mpg123_stopAtEnd = 0;
if(socketpair(AF_UNIX,SOCK_STREAM,0,fd_send)<0) {
fprintf(stderr,"%s Problems socketpair'ing\n",COMMAND_RESPOND_ERROR);
return -1;
}
if(socketpair(AF_UNIX,SOCK_STREAM,0,fd_recv)<0) {
fprintf(stderr,"%s Problems socketpair()'ing\n",COMMAND_RESPOND_ERROR);
return -1;
}
if(socketpair(AF_UNIX,SOCK_STREAM,0,fd_stderr)<0) {
fprintf(stderr,"%s Problems socketpair()'ing\n",COMMAND_RESPOND_ERROR);
return -1;
}
signal(SIGPIPE,SIG_IGN);
signal(SIGCHLD,mpg123_sigHandler);
mpg123_pid = fork();
if(mpg123_pid==0) {
closeAllInterfaces();
close(fd_send[0]);
close(fd_recv[0]);
close(fd_stderr[0]);
dup2(fd_send[1],STDIN_FILENO);
close(fd_send[1]);
dup2(fd_recv[1],STDOUT_FILENO);
close(fd_recv[1]);
dup2(fd_stderr[1],STDERR_FILENO);
close(fd_stderr[1]);
if(execlp("mpg123","mpg123","-b",MPG123_PLAYER_BUFFER_SIZE,"-R","foo",NULL)<0) {
fprintf(stderr,"%s Problems spawning mpg123\n",COMMAND_RESPOND_ERROR);
return -1;
}
}
else if(mpg123_pid<0) {
fprintf(stderr,"%s Problems fork()'ing\n",COMMAND_RESPOND_ERROR);
mpg123_pid = 0;
close(fd_send[0]);
close(fd_send[1]);
close(fd_recv[0]);
close(fd_recv[1]);
close(fd_stderr[0]);
close(fd_stderr[1]);
return -1;
}
close(fd_send[1]);
close(fd_recv[1]);
close(fd_stderr[1]);
mpg123_send = fd_send[0];
mpg123_recv = fd_recv[0];
mpg123_stderr = fd_stderr[0];
return 0;
}
int mpg123play(FILE * fp, char * file) {
char string[1024];
mpg123_leavePlay = 0;
if(fp==NULL) fp=stderr;
if(!isMp3(file)) {
fprintf(fp,"%s \"%s\" is not a mp3\n",file,COMMAND_RESPOND_ERROR);
return -1;
}
if(!mpg123_pid) {
if(mpg123init()<0) return -1;
}
sprintf(string,"LOAD %s\n",file);
if(write(mpg123_send,string,strlen(string))<0) {
fprintf(fp,"%s problems write'ing\n",COMMAND_RESPOND_ERROR);
return -1;
}
mpg123_state = MPG123_STATE_PLAY;
/*sleep until status is updated*/
while(!mpg123_leavePlay) {
mpg123processMessages();
usleep(1000);
}
if(mpg123_error) {
fprintf(fp,"%s problems playing \"%s\"\n",COMMAND_RESPOND_ERROR,file);
/*nextSongInPlaylist(stderr);*/
mpg123kill();
return -1;
}
return 0;
}
int mpg123stop(FILE * fp) {
if(mpg123_pid>0) {
char string[1024];
if(mpg123_state==MPG123_STATE_PAUSE) {
if(mpg123pause(fp)<0) return -1;
}
mpg123_state = MPG123_STATE_STOP;
sprintf(string,"QUIT\n");
if(write(mpg123_send,string,strlen(string))<0) {
fprintf(fp,"%s problems write'ing\n",COMMAND_RESPOND_ERROR);
return -1;
}
wait3(NULL,WUNTRACED,NULL);
if(mpg123_pid>0) {
fprintf(fp,"oh shit\n");
mpg123kill();
}
}
mpg123_state = MPG123_STATE_STOP;
return 0;
}
int mpg123pause(FILE * fp) {
char string[1024];
if(mpg123_pid>0) {
sprintf(string,"PAUSE\n");
if(write(mpg123_send,string,strlen(string))<0) {
fprintf(fp,"%s problems write'ing\n",COMMAND_RESPOND_ERROR);
}
mpg123_leavePause = 0;
while(!mpg123_leavePause) {
mpg123processMessages();
usleep(1000);
}
if(mpg123_state==MPG123_STATE_PLAY) {
mpg123_state = MPG123_STATE_PAUSE;
}
else if(mpg123_state==MPG123_STATE_PAUSE) {
mpg123_state = MPG123_STATE_PLAY;
}
}
return 0;
}
int mpg123readline() {
fd_set fdsr;
struct timeval tv;
FD_ZERO(&fdsr);
FD_SET(mpg123_recv,&fdsr);
tv.tv_sec = tv.tv_usec = 0;
if(select(mpg123_recv+1,&fdsr,NULL,NULL,&tv)==1) {
int rc;
while(1) {
rc = read(mpg123_recv,mpg123_buffer+mpg123_bufferLength,1);
if(rc<=0) {
/*mpg123_pid = 0;
mpg123_state = MPG123_STATE_STOP;*/
return -1;
}
if(mpg123_buffer[mpg123_bufferLength]=='\0' || mpg123_buffer[mpg123_bufferLength]=='\n' || mpg123_bufferLength == MAX_BUFFER_LENGTH) {
mpg123_buffer[mpg123_bufferLength] = '\0';
mpg123_bufferLength = 0;
return -1;
}
mpg123_bufferLength++;
}
}
return 0;
}
int mpg123gotErrors() {
int ret = 0;
fd_set fdsr;
struct timeval tv;
FD_ZERO(&fdsr);
FD_SET(mpg123_stderr,&fdsr);
tv.tv_sec = tv.tv_usec = 0;
if(select(mpg123_stderr+1,&fdsr,NULL,NULL,&tv)==1) {
int rc;
char errorBuffer[MAX_BUFFER_LENGTH];
int errorBufferLength = 0;
while(1) {
rc = read(mpg123_stderr,errorBuffer+errorBufferLength,1);
if(rc<=0) {
return 0;
}
if(errorBuffer[errorBufferLength]=='\0' || errorBuffer[errorBufferLength]=='\n' || errorBufferLength == MAX_BUFFER_LENGTH) {
errorBuffer[errorBufferLength] = '\0';
errorBufferLength = 0;
if(!strncmp("Junk",errorBuffer,strlen("Junk"))) {
return 0;
}
fprintf(stderr,"%s %s\n",COMMAND_RESPOND_ERROR,errorBuffer);
mpg123kill();
return 1;
}
errorBufferLength++;
}
}
return ret;
}
int mpg123processMessages() {
if(mpg123_pid>0) {
while(mpg123readline()) {
char * c[6];
int i;
c[0] = strtok(mpg123_buffer," \t");
if(c[0]) {
for(i=1;i<6;i++) {
c[i] = c[i-1] ? strtok(NULL," \t") : NULL;
}
if(0==strcmp(c[0],"@F")) {
mpg123_totalTime = atof(c[3])+atof(c[4])+0.5;
mpg123_reportedElapsedTime = atof(c[3])+0.5;
if(atof(c[3])<1) mpg123_leavePlay = 1;
mpg123_lastFrame = time(NULL);
if(mpg123_stopAtEnd && atof(c[4])<1) {
mpg123_stopAtEnd = 0;
mpg123stop(stderr);
}
}
else if(0==strcmp(c[0],"@P")) {
if(mpg123_state==MPG123_STATE_PLAY && atoi(c[1])==MPG123_STATE_STOP) {
if(!mpg123_leavePlay) {
mpg123_leavePlay=1;
mpg123_error = 1;
}
else {
nextSongInPlaylist(stderr);
}
}
else if(mpg123_state==MPG123_STATE_PLAY && atoi(c[1])==MPG123_STATE_PAUSE) {
mpg123_leavePause = 1;
}
else if(mpg123_state==MPG123_STATE_PAUSE && atoi(c[1])==MPG123_STATE_PLAY) {
mpg123_leavePause = 1;
}
//mpg123_state = atoi(c[1]);
}
/*else if(0==strcmp(c[0],"@I")) {
mpg123_leavePlay = 1;
}*/
else if(0==strcmp(c[0],"@E")) {
mpg123_error = 1;
}
}
}
if(mpg123gotErrors()) mpg123_error = 1;
if(mpg123_stopAtEnd) {
mpg123_elapsedTime = mpg123_reportedElapsedTime+time(NULL)-mpg123_lastFrame;
}
else {
mpg123_elapsedTime = mpg123_reportedElapsedTime;
}
if(mpg123_stopAtEnd && mpg123_elapsedTime>mpg123_totalTime) {
mpg123_stopAtEnd = 0;
mpg123stop(stderr);
}
}
return 0;
}
void mpg123kill() {
/* new stuff to try */
//kill(-1,SIGKILL);
/* old new stuff to try */
if(mpg123_pid>0) system("killall -KILL mpg123");
mpg123_state = MPG123_STATE_STOP;
mpg123_sigHandler(SIGCHLD);
}
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#ifndef MPG123_H
#define MPG123_H
#include <stdio.h>
#define MPG123_STATE_STOP 0
#define MPG123_STATE_PAUSE 1
#define MPG123_STATE_PLAY 2
#define MPG123_KILL 1
#define MPG123_NOKILL 0
extern int mpg123_totalTime;
extern int mpg123_elapsedTime;
extern int mpg123_state;
extern int mpg123_stopAtEnd;
void mpg123_sigHandler(int signal);
int mpg123play(FILE * fp, char * file);
int mpg123pause(FILE * fp);
int mpg123stop(FILE * fp);
void mpg123kill();
int mpg123processMessages();
#endif
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#include "playlist.h"
#include "mpg123.h"
#include "command.h"
#include "ls.h"
#include "tag.h"
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
Playlist playlist;
char playlistDir[MAXPATHLEN+1];
void initPlaylist() {
playlist.length = 0;
memset(playlist.songs,(int)NULL,sizeof(char *)*PLAYLIST_MAX_LENGTH);
}
int checkThatPlaylistIsStopped(FILE * fp) {
if(mpg123_state!=MPG123_STATE_STOP) {
fprintf(fp,"%s playlist is not stopped\n",COMMAND_RESPOND_ERROR);
return -1;
}
return 0;
}
int clearPlaylist(FILE * fp) {
int i;
if(mpg123stop(fp)<0) return -1;
for(i=0;i<playlist.length;i++) {
freeSong(playlist.songs[i]);
}
playlist.length = 0;
return 0;
}
int addToPlaylist(FILE * fp, char * file) {
if(!isMp3(file)) {
fprintf(fp,"%s \"%s\" is not a mp3\n",COMMAND_RESPOND_ERROR,file);
return -1;
}
if(playlist.length==PLAYLIST_MAX_LENGTH) {
fprintf(fp,"%s playlist is at the max size\n",COMMAND_RESPOND_ERROR);
return -1;
}
playlist.songs[playlist.length] = newSong(file);
playlist.length++;
return 0;
}
int showPlaylist(FILE * fp) {
int i;
for(i=0;i<playlist.length;i++) {
fprintf(fp,"%i \"%s\"\n",i,(playlist.songs[i])->file);
}
return 0;
}
int playlistInfo(FILE * fp,int song) {
MpdTag * tag;
int i;
int begin = 0;
int end = playlist.length;
if(song>=0) {
begin = song;
end = song+1;
}
if(song>=playlist.length) {
fprintf(fp,"%s song doesn't exist\n",COMMAND_RESPOND_ERROR);
return -1;
}
for(i=begin;i<end;i++) {
fprintf(fp,"file: %s\n",(playlist.songs[i])->file);
if((tag = (playlist.songs[i])->tag)) {
printMpdTag(fp,tag);
}
}
return 0;
}
void swapSongs(int song1, int song2) {
Song * temp;
temp = playlist.songs[song1];
playlist.songs[song1] = playlist.songs[song2];
playlist.songs[song2] = temp;
}
int deleteFromPlaylist(FILE * fp, int song) {
int i;
if(song<0) {
fprintf(fp,"%s need a positive interger\n",COMMAND_RESPOND_ERROR);
return -1;
}
if(song>=playlist.length) {
fprintf(fp,"%s song doesn't exist\n",COMMAND_RESPOND_ERROR);
return -1;
}
freeSong(playlist.songs[song]);
playlist.songs[song] = NULL;
for(i=song;i<playlist.length-1;i++) swapSongs(i,i+1);
playlist.length--;
if(mpg123_state!=MPG123_STATE_STOP && playlist.current==song) {
if(playlist.current>=playlist.length) return mpg123stop(fp);
else return playPlaylist(fp,playlist.current);
}
else if(mpg123_state!=MPG123_STATE_STOP && playlist.current>song) {
playlist.current--;
}
return 0;
}
int playPlaylist(FILE * fp, int song) {
if(song<0) {
fprintf(fp,"%s need a positive interger\n",COMMAND_RESPOND_ERROR);
return -1;
}
if(song>=playlist.length) {
fprintf(fp,"%s song doesn't exist\n",COMMAND_RESPOND_ERROR);
return -1;
}
if(mpg123stop(fp)<0) return -1;
playlist.current = song;
return mpg123play(fp,(playlist.songs[song])->file);
}
int nextSongInPlaylist(FILE * fp) {
while(playlist.current<playlist.length-1) {
playlist.current++;
if(mpg123play(fp,(playlist.songs[playlist.current])->file)==0) return 0;
}
if(playlist.current==playlist.length-1) {
mpg123_stopAtEnd = 1;
}
else {
mpg123stop(fp); /*stop and kill mpg123*/
}
return 0;
}
int shufflePlaylist(FILE * fp) {
int i;
int ri;
srand(time(NULL));
for(i=0;i<playlist.length;i++) {
ri = rand()%playlist.length;
swapSongs(i,ri);
if(i==playlist.current) playlist.current=ri;
else if(ri==playlist.current) playlist.current=i;
}
return 0;
}
int deletePlaylist(FILE * fp, char * file) {
struct stat st;
if(stat(file,&st)<0) {
fprintf(fp,"%s problems stat'ing\n",COMMAND_RESPOND_ERROR);
return -1;
}
if(!S_ISREG(st.st_mode)) {
fprintf(fp,"%s not a file\n",COMMAND_RESPOND_ERROR);
return -1;
}
if(unlink(file)<0) {
fprintf(fp,"%s problems deleting file\n",COMMAND_RESPOND_ERROR);
return -1;
}
return 0;
}
int savePlaylist(FILE * fp, char * file) {
FILE * fileP;
int i;
struct stat st;
if(0==stat(file,&st)) {
fprintf(fp,"%s A file or direcory already exists with the name \"%s\"\n",COMMAND_RESPOND_ERROR,file);
return -1;
}
if((fileP = fopen(file,"w"))==NULL) {
fprintf(fp,"%s Problems opening file\n",COMMAND_RESPOND_ERROR);
return -1;
}
for(i=0;i<playlist.length;i++) {
fprintf(fileP,"%s\n",(playlist.songs[i])->file);
}
fclose(fileP);
return 0;
}
int loadPlaylist(FILE * fp, char * file) {
FILE * fileP;
char s[MAXPATHLEN+1];
int slength = 0;
if((fileP = fopen(file,"r"))==NULL) {
fprintf(fp,"%s Problems opening file\n",COMMAND_RESPOND_ERROR);
return -1;
}
while((s[slength] = fgetc(fileP))!=EOF) {
if(s[slength]=='\n') {
s[slength] = '\0';
if((addToPlaylist(fp,s))<0) return -1;
slength = 0;
}
else if(slength==MAXPATHLEN) {
s[slength] = '\0';
fprintf(fp,"%s \"%s\" too long\n",COMMAND_RESPOND_ERROR,s);
return -1;
}
else {
slength++;
}
}
fclose(fileP);
return 0;
}
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#ifndef PLAYLIST_H
#define PLAYLIST_H
#include "song.h"
#include <stdio.h>
#include <sys/param.h>
#define PLAYLIST_FILE_SUFFIX "m3u"
#define PLAYLIST_MAX_LENGTH 1024
extern char playlistDir[MAXPATHLEN+1];
typedef struct _Playlist {
Song * songs[PLAYLIST_MAX_LENGTH];
int length;
int current;
} Playlist;
void initPlaylist();
int clearPlaylist(FILE * fp);
int addToPlaylist(FILE * fp, char * file);
int showPlaylist(FILE * fp);
int deleteFromPlaylist(FILE * fp, int song);
int playlistInfo(FILE * fp, int song);
int playPlaylist(FILE * fp, int song);
int nextSongInPlaylist(FILE * fp);
int shufflePlaylist(FILE * fp);
int savePlaylist(FILE * fp, char * file);
int deletePlaylist(FILE * fp, char * file);
int loadPlaylist(FILE * fp, char * file);
#endif
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#include "song.h"
#include "ls.h"
#include "directory.h"
#include "tables.h"
#include "utils.h"
#include "tag.h"
#define SONG_KEY "key: "
#define SONG_FILE "file: "
#define SONG_ARTIST "Artist: "
#define SONG_ALBUM "Album: "
#define SONG_TRACK "Track: "
#define SONG_TITLE "Title: "
#define SONG_MTIME "mtime: "
#include <stdlib.h>
#include <string.h>
Song * newSong(char * file) {
Song * song = malloc(sizeof(Song));
song->file = strdup(file);
song->tag = id3Dup(file);
song->mtime = isMp3(file);
return song;
}
void freeSong(Song * song) {
free(song->file);
if(song->tag) freeMpdTag(song->tag);
free(song);
}
SongList * newSongList() {
return makeList((ListFreeDataFunc *)freeSong);
}
Song * addSongToList(SongList * list, char * key, char * file) {
Song * song;
if(isMp3(file)) {
song = newSong(file);
}
else {
return NULL;
}
insertInList(list,key,(void *)song);
return song;
}
void freeSongList(SongList * list) {
freeList(list);
}
int printSongInfo(FILE * fp, Song * song) {
fprintf(fp,"%s%s\n",SONG_FILE,song->file);
if(song->tag) printMpdTag(fp,song->tag);
return 0;
}
int printSongInfoFromList(FILE * fp, SongList * list) {
ListNode * tempNode = list->firstNode;
while(tempNode!=NULL) {
printSongInfo(fp,(Song *)tempNode->data);
tempNode = tempNode->nextNode;
}
return 0;
}
void writeSongInfoFromList(FILE * fp, SongList * list) {
ListNode * tempNode = list->firstNode;
fprintf(fp,"%s\n",SONG_BEGIN);
while(tempNode!=NULL) {
fprintf(fp,"%s%s\n",SONG_KEY,tempNode->key);
printSongInfo(fp,(Song *)tempNode->data);
fprintf(fp,"%s%li\n",SONG_MTIME,((Song *)tempNode->data)->mtime);
tempNode = tempNode->nextNode;
}
fprintf(fp,"%s\n",SONG_END);
}
void readSongInfoIntoList(FILE * fp, SongList * list) {
char buffer[MAXPATHLEN+1024];
int bufferSize = MAXPATHLEN+1024;
Song * song = NULL;
char * key;
while(myFgets(buffer,bufferSize,fp) && 0!=strcmp(SONG_END,buffer)) {
if(0==strncmp(SONG_KEY,buffer,strlen(SONG_KEY))) {
if(song) {
insertInList(list,key,(void *)song);
if(song->tag) addSongToTables(song);
free(key);
}
key = strdup(&(buffer[strlen(SONG_KEY)]));
song = malloc(sizeof(Song));
song->tag = NULL;
song->file = NULL;
}
else if(0==strncmp(SONG_FILE,buffer,strlen(SONG_FILE))) {
if(!song || song->file) {
fprintf(stderr,"Problems reading song info\n");
exit(-1);
}
song->file = strdup(&(buffer[strlen(SONG_FILE)]));
}
else if(0==strncmp(SONG_ARTIST,buffer,strlen(SONG_ARTIST))) {
if(!song->tag) song->tag = newMpdTag();
song->tag->artist = strdup(&(buffer[strlen(SONG_ARTIST)]));
}
else if(0==strncmp(SONG_ALBUM,buffer,strlen(SONG_ALBUM))) {
if(!song->tag) song->tag = newMpdTag();
song->tag->album = strdup(&(buffer[strlen(SONG_ALBUM)]));
}
else if(0==strncmp(SONG_TRACK,buffer,strlen(SONG_TRACK))) {
if(!song->tag) song->tag = newMpdTag();
song->tag->track = strdup(&(buffer[strlen(SONG_TRACK)]));
}
else if(0==strncmp(SONG_TITLE,buffer,strlen(SONG_TITLE))) {
if(!song->tag) song->tag = newMpdTag();
song->tag->title = strdup(&(buffer[strlen(SONG_TITLE)]));
}
else if(0==strncmp(SONG_MTIME,buffer,strlen(SONG_MTIME))) {
song->mtime = atoi(&(buffer[strlen(SONG_TITLE)]));
}
else {
fprintf(stderr,"unknown line in db: %s\n",buffer);
exit(-1);
}
}
if(song) {
insertInList(list,key,(void *)song);
if(song->tag) addSongToTables(song);
free(key);
}
}
int updateSongInfo(Song * song) {
if(song->tag) freeMpdTag(song->tag);
if(!(song->mtime = isMp3(song->file))) return -1;
song->tag = id3Dup(song->file);
return 0;
}
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#ifndef SONG_H
#define SONG_H
#define SONG_BEGIN "songList begin"
#define SONG_END "songList end"
#include <sys/param.h>
#include <time.h>
#include "tag.h"
#include "list.h"
typedef struct _Song {
char * file;
MpdTag * tag;
time_t mtime;
} Song;
typedef List SongList;
Song * newSong(char * file);
void freeSong(Song *);
SongList * newSongList();
void freeSongList(SongList * list);
Song * addSongToList(SongList * list, char * key, char * file);
int printSongInfo(FILE * fp, Song * song);
int printSongInfoFromList(FILE * fp, SongList * list);
void writeSongInfoFromList(FILE * fp, SongList * list);
void readSongInfoIntoList(FILE * fp, SongList * list);
int updateSongInfo(Song * song);
#endif
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#include "tables.h"
#include "list.h"
#include "command.h"
#include "utils.h"
#include <string.h>
#define TABLES_ALBUM "album"
#define TABLES_ARTIST "artist"
#define TABLES_TITLE "title"
List * albumTable;
List * artistTable;
List * titleTable;
void initTables() {
albumTable = makeList(freeList);
artistTable = makeList(freeList);
titleTable = makeList(NULL);
}
void closeTables() {
freeList(albumTable);
freeList(artistTable);
freeList(titleTable);
}
void addSongToAlbumTable(Song * song) {
SongList * album;
if(!song->tag) return;
if(!song->tag->title) return;
if(findInList(albumTable,song->tag->album,(void **)&album)) {
insertInList(album,song->file,song);
}
else {
album = makeList(NULL);
insertInList(albumTable,song->tag->album,album);
insertInList(album,song->file,song);
}
}
void addSongToArtistTable(Song * song) {
SongList * artist;
if(!song->tag) return;
if(!song->tag->artist) return;
if(findInList(artistTable,song->tag->artist,(void **)&artist)) {
insertInList(artist,song->file,song);
}
else {
artist = makeList(NULL);
insertInList(artistTable,song->tag->artist,artist);
insertInList(artist,song->file,song);
}
}
void addSongToSongTable(Song * song) {
if(!song->tag) return;
if(!song->tag->title) return;
insertInList(titleTable,song->file,song);
}
void addSongToTables(Song * song) {
addSongToAlbumTable(song);
addSongToArtistTable(song);
addSongToSongTable(song);
}
int findAndPrintSongsInAlbumTable(FILE * fp,char * find) {
SongList * album;
if(!findInList(albumTable,find,(void **)&album)) {
fprintf(fp,"%s album not found\n",COMMAND_RESPOND_ERROR);
return -1;
}
return printSongInfoFromList(fp,album);
}
int findAndPrintSongsInArtistTable(FILE * fp,char * find) {
SongList * artist;
if(!findInList(artistTable,find,(void **)&artist)) {
fprintf(fp,"%s artist not found\n",COMMAND_RESPOND_ERROR);
return -1;
}
return printSongInfoFromList(fp,artist);
}
int findAndPrintSongsInTable(FILE * fp, char * table, char * find) {
if(strcmp(table,TABLES_ALBUM)==0) {
return findAndPrintSongsInAlbumTable(fp,find);
}
else if(strcmp(table,TABLES_ARTIST)==0) {
return findAndPrintSongsInArtistTable(fp,find);
}
fprintf(fp,"%s unkown table\n",COMMAND_RESPOND_ERROR);
return -1;
}
int searchForSongsInAlbumTable(FILE * fp,char * search) {
SongList * album;
ListNode * node = albumTable->firstNode;
int ret = -1;
char * dup;
char * dupSearch = strDupToUpper(search);
while(node) {
dup = strDupToUpper(node->key);
if(strstr(dup,dupSearch)) {
album = (SongList *)node->data;
if(printSongInfoFromList(fp,album)<0) {
free(dup);
return -1;
}
ret = 0;
}
free(dup);
node = node->nextNode;
}
free(dupSearch);
if(ret<0) fprintf(fp,"%s no songs found\n",COMMAND_RESPOND_ERROR);
return ret;
}
int searchForSongsInArtistTable(FILE * fp,char * search) {
SongList * artist;
ListNode * node = artistTable->firstNode;
int ret = -1;
char * dup;
char * dupSearch = strDupToUpper(search);
while(node) {
dup = strDupToUpper(node->key);
if(strstr(dup,dupSearch)) {
artist = (SongList *)node->data;
if(printSongInfoFromList(fp,artist)<0) {
free(dup);
return -1;
}
ret = 0;
}
free(dup);
node = node->nextNode;
}
free(dupSearch);
if(ret<0) fprintf(fp,"%s no songs found\n",COMMAND_RESPOND_ERROR);
return ret;
}
int searchForSongsInTitleTable(FILE * fp,char * search) {
Song * song;
ListNode * node = titleTable->firstNode;
int ret = -1;
char * dup;
char * dupSearch = strDupToUpper(search);
if(!song->tag || !song->tag->title) return -1;
while(node) {
song = (Song *)node->data;
dup = strDupToUpper(song->tag->title);
if(strstr(dup,dupSearch)) {
if(printSongInfo(fp,song)<0) {
free(dup);
return -1;
}
ret = 0;
}
free(dup);
node = node->nextNode;
}
free(dupSearch);
if(ret<0) fprintf(fp,"%s no songs found\n",COMMAND_RESPOND_ERROR);
return ret;
}
int searchForSongsInTable(FILE * fp, char * table, char * search) {
if(strcmp(table,TABLES_ALBUM)==0) {
return searchForSongsInAlbumTable(fp,search);
}
else if(strcmp(table,TABLES_ARTIST)==0) {
return searchForSongsInArtistTable(fp,search);
}
else if(strcmp(table,TABLES_TITLE)==0) {
return searchForSongsInTitleTable(fp,search);
}
fprintf(fp,"%s unkown table\n",COMMAND_RESPOND_ERROR);
return -1;
}
void removeSongFromAlbumTable(Song * song) {
List * album;
if(!song->tag) return;
if(!song->tag->album) return;
if(findInList(albumTable,song->tag->album,(void **)&album)) {
deleteFromList(album,song->file);
if(album->numberOfNodes==0) {
deleteFromList(albumTable,song->tag->album);
}
}
}
void removeSongFromArtistTable(Song * song) {
List * artist;
if(!song->tag) return;
if(!song->tag->artist) return;
if(findInList(artistTable,song->tag->artist,(void **)&artist)) {
deleteFromList(artist,song->file);
if(artist->numberOfNodes==0) {
deleteFromList(artistTable,song->tag->artist);
}
}
}
void removeSongFromTitleTable(Song * song) {
deleteFromList(titleTable,song->file);
}
void removeASongFromTables(Song * song) {
removeSongFromAlbumTable(song);
removeSongFromArtistTable(song);
removeSongFromTitleTable(song);
}
void removeSongsFromTables(SongList * songList) {
ListNode * node = songList->firstNode;
Song * song;
while(node) {
song = (Song *)node->data;
removeASongFromTables(song);
node = node->nextNode;
}
}
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#ifndef TABLES_H
#define TABLES_H
#include "song.h"
#include <stdio.h>
void initTables();
void closeTables();
void addSongToTables(Song * song);
int findAndPrintSongsInTable(FILE * fp, char * table, char * find);
int searchForSongsInTable(FILE * fp, char * table, char * find);
void removeSongsFromTables(SongList * songList);
void removeASongFromTables(Song * song);
#endif
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#include "tag.h"
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include "id3v2lib/lib_id3v2.3.h"
#include "id3v2lib/lib_id3v2.2.h"
#include "id3v1lib/lib_id3v1.h"
id3Tag * stripSpaces(id3Tag * tag) {
int len;
len=strlen(tag->artist);
while(len && tag->artist[len-1]==' ') {
tag->artist[len-1] = '\0';
len--;
}
len=strlen(tag->album);
while(len && tag->album[len-1]==' ') {
tag->album[len-1] = '\0';
len--;
}
len=strlen(tag->title);
while(len && tag->title[len-1]==' ') {
tag->title[len-1] = '\0';
len--;
}
return tag;
}
id3Tag * newId3() {
id3Tag * id3 = malloc(sizeof(id3Tag));
id3->artist[0] = '\0';
id3->album[0] = '\0';
id3->track[0] = '\0';
id3->title[0] = '\0';
return id3;
}
void printMpdTag(FILE * fp, MpdTag * tag) {
if(tag->artist) fprintf(fp,"Artist: %s\n",tag->artist);
if(tag->album) fprintf(fp,"Album: %s\n",tag->album);
if(tag->track) fprintf(fp,"Track: %s\n",tag->track);
if(tag->title) fprintf(fp,"Title: %s\n",tag->title);
}
MpdTag * id3Dup(char * file) {
id3Tag * tag = newId3();
MpdTag * ret;
if(0==get_id3v2_tag(tag,file)) stripSpaces(tag);
else if(0==get_id3v22_tag(tag,file)) stripSpaces(tag);
else if(0==get_id3v1_tag(tag,file)) stripSpaces(tag);
else {
free(tag);
return NULL;
}
ret = newMpdTag();
ret->artist = strdup(tag->artist);
ret->album = strdup(tag->album);
ret->title = strdup(tag->title);
ret->track = strdup(tag->track);
free(tag);
return ret;
}
MpdTag * newMpdTag() {
MpdTag * ret = malloc(sizeof(MpdTag));
ret->album = NULL;
ret->artist = NULL;
ret->title = NULL;
ret->track = NULL;
return ret;
}
void freeMpdTag(MpdTag * tag) {
if(tag->artist) free(tag->artist);
if(tag->album) free(tag->album);
if(tag->title) free(tag->title);
if(tag->track) free(tag->track);
free(tag);
}
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#ifndef TAG_H
#define TAG_H
#include <stdio.h>
typedef struct _MpdTag {
char * artist;
char * album;
char * track;
char * title;
} MpdTag;
MpdTag * newMpdTag();
void freeMpdTag(MpdTag * tag);
MpdTag * id3Dup(char * file);
void printMpdTag(FILE * fp, MpdTag * tag);
#endif
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#include "utils.h"
#include <string.h>
#include <ctype.h>
char * myFgets(char * buffer, int bufferSize, FILE * fp) {
char * ret = fgets(buffer,bufferSize,fp);
if(ret && strlen(buffer)>0 && buffer[strlen(buffer)-1]=='\n') {
buffer[strlen(buffer)-1] = '\0';
}
return ret;
}
char * strDupToUpper(char * str) {
char * ret = strdup(str);
int i;
for(i=0;i<strlen(str);i++) ret[i] = toupper((int)ret[i]);
return ret;
}
/* the Music Player Daemon (MPD)
* (c)2003 by Warren Dukes (shank@mercury.chem.pitt.edu)
* This project's homepage is: http://musicpd.sourceforge.net
*
* This library is designed for easyest possible access to id3 V1 tags.
*
* 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
*/
#ifndef UTILS_H
#define UTILS_H
#include <stdio.h>
char * myFgets(char * buffer, int bufferSize, FILE * fp);
char * strDupToUpper(char * str);
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment