Main Page | Namespace List | Class Hierarchy | Class List | File List | Namespace Members | Class Members | File Members

src/tag_file.cpp

Go to the documentation of this file.
00001 // $Id: tag_file.cpp,v 1.43 2003/03/02 14:14:08 t1mpy Exp $ 00002 00003 // id3lib: a C++ library for creating and manipulating id3v1/v2 tags 00004 // Copyright 1999, 2000 Scott Thomas Haug 00005 // Copyright 2002 Thijmen Klok (thijmen@id3lib.org) 00006 00007 // This library is free software; you can redistribute it and/or modify it 00008 // under the terms of the GNU Library General Public License as published by 00009 // the Free Software Foundation; either version 2 of the License, or (at your 00010 // option) any later version. 00011 // 00012 // This library is distributed in the hope that it will be useful, but WITHOUT 00013 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00014 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00015 // License for more details. 00016 // 00017 // You should have received a copy of the GNU Library General Public License 00018 // along with this library; if not, write to the Free Software Foundation, 00019 // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 00020 00021 // The id3lib authors encourage improvements and optimisations to be sent to 00022 // the id3lib coordinator. Please see the README file for details on where to 00023 // send such submissions. See the AUTHORS file for a list of people who have 00024 // contributed to id3lib. See the ChangeLog file for a list of changes to 00025 // id3lib. These files are distributed with id3lib at 00026 // http://download.sourceforge.net/id3lib/ 00027 00028 #include <stdio.h> //for BUFSIZ and functions remove & rename 00029 #include "writers.h" 00030 #include "io_strings.h" 00031 #include "tag_impl.h" //has <stdio.h> "tag.h" "header_tag.h" "frame.h" "field.h" "spec.h" "id3lib_strings.h" "utils.h" 00032 00033 using namespace dami; 00034 00035 #if !defined HAVE_MKSTEMP 00036 # include <stdio.h> 00037 #endif 00038 00039 #if defined HAVE_UNISTD_H 00040 # include <unistd.h> 00041 #endif 00042 00043 #if defined HAVE_SYS_STAT_H 00044 # include <sys/stat.h> 00045 #endif 00046 00047 #if defined WIN32 && (!defined(WINCE)) 00048 # include <windows.h> 00049 static int truncate(const char *path, size_t length) 00050 { 00051 int result = -1; 00052 HANDLE fh; 00053 00054 fh = ::CreateFile(path, 00055 GENERIC_WRITE | GENERIC_READ, 00056 0, 00057 NULL, 00058 OPEN_EXISTING, 00059 FILE_ATTRIBUTE_NORMAL, 00060 NULL); 00061 00062 if(INVALID_HANDLE_VALUE != fh) 00063 { 00064 SetFilePointer(fh, length, NULL, FILE_BEGIN); 00065 SetEndOfFile(fh); 00066 CloseHandle(fh); 00067 result = 0; 00068 } 00069 00070 return result; 00071 } 00072 00073 // prevents a weird error I was getting compiling this under windows 00074 # if defined CreateFile 00075 # undef CreateFile 00076 # endif 00077 00078 #elif defined(WINCE) 00079 // Createfile is apparently to defined to CreateFileW. (Bad Bad Bad), so we 00080 // work around it by converting path to Unicode 00081 # include <windows.h> 00082 static int truncate(const char *path, size_t length) 00083 { 00084 int result = -1; 00085 wchar_t wcTempPath[256]; 00086 mbstowcs(wcTempPath,path,255); 00087 HANDLE fh; 00088 fh = ::CreateFile(wcTempPath, 00089 GENERIC_WRITE | GENERIC_READ, 00090 0, 00091 NULL, 00092 OPEN_EXISTING, 00093 FILE_ATTRIBUTE_NORMAL, 00094 NULL); 00095 00096 if (INVALID_HANDLE_VALUE != fh) 00097 { 00098 SetFilePointer(fh, length, NULL, FILE_BEGIN); 00099 SetEndOfFile(fh); 00100 CloseHandle(fh); 00101 result = 0; 00102 } 00103 00104 return result; 00105 } 00106 00107 #elif defined(macintosh) 00108 00109 static int truncate(const char *path, size_t length) 00110 { 00111 /* not implemented on the Mac */ 00112 return -1; 00113 } 00114 00115 #endif 00116 00117 size_t ID3_TagImpl::Link(const char *fileInfo, bool parseID3v1, bool parseLyrics3) 00118 { 00119 flags_t tt = ID3TT_NONE; 00120 if (parseID3v1) 00121 { 00122 tt |= ID3TT_ID3V1; 00123 } 00124 if (parseLyrics3) 00125 { 00126 tt |= ID3TT_LYRICS; 00127 } 00128 return this->Link(fileInfo, tt); 00129 } 00130 00131 size_t ID3_TagImpl::Link(const char *fileInfo, flags_t tag_types) 00132 { 00133 _tags_to_parse.set(tag_types); 00134 00135 if (NULL == fileInfo) 00136 { 00137 return 0; 00138 } 00139 00140 _file_name = fileInfo; 00141 _changed = true; 00142 00143 this->ParseFile(); 00144 00145 return this->GetPrependedBytes(); 00146 } 00147 00148 // used for streaming: 00149 size_t ID3_TagImpl::Link(ID3_Reader &reader, flags_t tag_types) 00150 { 00151 _tags_to_parse.set(tag_types); 00152 00153 _file_name = ""; 00154 _changed = true; 00155 00156 this->ParseReader(reader); 00157 00158 return this->GetPrependedBytes(); 00159 } 00160 00161 size_t RenderV1ToFile(ID3_TagImpl& tag, fstream& file) 00162 { 00163 if (!file) 00164 { 00165 return 0; 00166 } 00167 00168 // Heck no, this is stupid. If we do not read in an initial V1(.1) 00169 // header then we are constantly appending new V1(.1) headers. Files 00170 // can get very big that way if we never overwrite the old ones. 00171 // if (ID3_V1_LEN > tag.GetAppendedBytes()) - Daniel Hazelbaker 00172 if (ID3_V1_LEN > tag.GetFileSize()) 00173 { 00174 file.seekp(0, ios::end); 00175 } 00176 else 00177 { 00178 // We want to check if there is already an id3v1 tag, so we can write over 00179 // it. First, seek to the beginning of any possible id3v1 tag 00180 file.seekg(0-ID3_V1_LEN, ios::end); 00181 char sID[ID3_V1_LEN_ID]; 00182 00183 // Read in the TAG characters 00184 file.read(sID, ID3_V1_LEN_ID); 00185 00186 // If those three characters are TAG, then there's a preexisting id3v1 tag, 00187 // so we should set the file cursor so we can overwrite it with a new tag. 00188 if (memcmp(sID, "TAG", ID3_V1_LEN_ID) == 0) 00189 { 00190 file.seekp(0-ID3_V1_LEN, ios::end); 00191 } 00192 // Otherwise, set the cursor to the end of the file so we can append on 00193 // the new tag. 00194 else 00195 { 00196 file.seekp(0, ios::end); 00197 } 00198 } 00199 00200 ID3_IOStreamWriter out(file); 00201 00202 id3::v1::render(out, tag); 00203 00204 return ID3_V1_LEN; 00205 } 00206 00207 size_t RenderV2ToFile(const ID3_TagImpl& tag, fstream& file) 00208 { 00209 ID3D_NOTICE( "RenderV2ToFile: starting" ); 00210 if (!file) 00211 { 00212 ID3D_WARNING( "RenderV2ToFile: error in file" ); 00213 return 0; 00214 } 00215 00216 String tagString; 00217 io::StringWriter writer(tagString); 00218 id3::v2::render(writer, tag); 00219 ID3D_NOTICE( "RenderV2ToFile: rendered v2" ); 00220 00221 const char* tagData = tagString.data(); 00222 size_t tagSize = tagString.size(); 00223 // if the new tag fits perfectly within the old and the old one 00224 // actually existed (ie this isn't the first tag this file has had) 00225 if ((!tag.GetPrependedBytes() && !ID3_GetDataSize(tag)) || 00226 (tagSize == tag.GetPrependedBytes())) 00227 { 00228 file.seekp(0, ios::beg); 00229 file.write(tagData, tagSize); 00230 } 00231 else 00232 { 00233 String filename = tag.GetFileName(); 00234 String sTmpSuffix = ".XXXXXX"; 00235 if (filename.size() + sTmpSuffix.size() > ID3_PATH_LENGTH) 00236 { 00237 // log this 00238 return 0; 00239 //ID3_THROW_DESC(ID3E_NoFile, "filename too long"); 00240 } 00241 char sTempFile[ID3_PATH_LENGTH]; 00242 strcpy(sTempFile, filename.c_str()); 00243 strcat(sTempFile, sTmpSuffix.c_str()); 00244 00245 #if ((defined(__GNUC__) && __GNUC__ >= 3 ) || !defined(HAVE_MKSTEMP)) 00246 // This section is for Windows folk && gcc 3.x folk 00247 fstream tmpOut; 00248 createFile(sTempFile, tmpOut); 00249 00250 tmpOut.write(tagData, tagSize); 00251 file.seekg(tag.GetPrependedBytes(), ios::beg); 00252 char *tmpBuffer[BUFSIZ]; 00253 while (!file.eof()) 00254 { 00255 file.read((char *)tmpBuffer, BUFSIZ); 00256 size_t nBytes = file.gcount(); 00257 tmpOut.write((char *)tmpBuffer, nBytes); 00258 } 00259 00260 #else //((defined(__GNUC__) && __GNUC__ >= 3 ) || !defined(HAVE_MKSTEMP)) 00261 00262 // else we gotta make a temp file, copy the tag into it, copy the 00263 // rest of the old file after the tag, delete the old file, rename 00264 // this new file to the old file's name and update the handle 00265 00266 int fd = mkstemp(sTempFile); 00267 if (fd < 0) 00268 { 00269 remove(sTempFile); 00270 //ID3_THROW_DESC(ID3E_NoFile, "couldn't open temp file"); 00271 } 00272 00273 ofstream tmpOut(fd); 00274 if (!tmpOut) 00275 { 00276 tmpOut.close(); 00277 remove(sTempFile); 00278 return 0; 00279 // log this 00280 //ID3_THROW(ID3E_ReadOnly); 00281 } 00282 00283 tmpOut.write(tagData, tagSize); 00284 file.seekg(tag.GetPrependedBytes(), ios::beg); 00285 uchar tmpBuffer[BUFSIZ]; 00286 while (file) 00287 { 00288 file.read(tmpBuffer, BUFSIZ); 00289 size_t nBytes = file.gcount(); 00290 tmpOut.write(tmpBuffer, nBytes); 00291 } 00292 00293 close(fd); //closes the file 00294 00295 #endif 00296 00297 tmpOut.close(); 00298 file.close(); 00299 00300 // the following sets the permissions of the new file 00301 // to be the same as the original 00302 #if defined(HAVE_SYS_STAT_H) 00303 struct stat fileStat; 00304 if(stat(filename.c_str(), &fileStat) == 0) 00305 { 00306 #endif //defined(HAVE_SYS_STAT_H) 00307 remove(filename.c_str()); 00308 rename(sTempFile, filename.c_str()); 00309 #if defined(HAVE_SYS_STAT_H) 00310 chmod(filename.c_str(), fileStat.st_mode); 00311 } 00312 #endif //defined(HAVE_SYS_STAT_H) 00313 00314 // file = tmpOut; 00315 file.clear();//to clear the eof mark 00316 openWritableFile(filename, file); 00317 } 00318 00319 return tagSize; 00320 } 00321 00322 00323 flags_t ID3_TagImpl::Update(flags_t ulTagFlag) 00324 { 00325 flags_t tags = ID3TT_NONE; 00326 00327 fstream file; 00328 String filename = this->GetFileName(); 00329 ID3_Err err = openWritableFile(filename, file); 00330 _file_size = getFileSize(file); 00331 00332 if (err == ID3E_NoFile) 00333 { 00334 err = createFile(filename, file); 00335 } 00336 if (err == ID3E_ReadOnly) 00337 { 00338 return tags; 00339 } 00340 00341 if ((ulTagFlag & ID3TT_ID3V2) && this->HasChanged()) 00342 { 00343 _prepended_bytes = RenderV2ToFile(*this, file); 00344 if (_prepended_bytes) 00345 { 00346 tags |= ID3TT_ID3V2; 00347 } 00348 } 00349 00350 if ((ulTagFlag & ID3TT_ID3V1) && 00351 (!this->HasTagType(ID3TT_ID3V1) || this->HasChanged())) 00352 { 00353 size_t tag_bytes = RenderV1ToFile(*this, file); 00354 if (tag_bytes) 00355 { 00356 // only add the tag_bytes if there wasn't an id3v1 tag before 00357 if (! _file_tags.test(ID3TT_ID3V1)) 00358 { 00359 _appended_bytes += tag_bytes; 00360 } 00361 tags |= ID3TT_ID3V1; 00362 } 00363 } 00364 _changed = false; 00365 _file_tags.add(tags); 00366 _file_size = getFileSize(file); 00367 file.close(); 00368 return tags; 00369 } 00370 00371 flags_t ID3_TagImpl::Strip(flags_t ulTagFlag) 00372 { 00373 flags_t ulTags = ID3TT_NONE; 00374 const size_t data_size = ID3_GetDataSize(*this); 00375 00376 // First remove the v2 tag, if requested 00377 if (ulTagFlag & ID3TT_PREPENDED & _file_tags.get()) 00378 { 00379 fstream file; 00380 if (ID3E_NoError != openWritableFile(this->GetFileName(), file)) 00381 { 00382 return ulTags; 00383 } 00384 _file_size = getFileSize(file); 00385 00386 // We will remove the id3v2 tag in place: since it comes at the beginning 00387 // of the file, we'll effectively move all the data that comes after the 00388 // tag back n bytes, where n is the size of the id3v2 tag. Once we've 00389 // copied the data, we'll truncate the file. 00390 file.seekg(this->GetPrependedBytes(), ios::beg); 00391 00392 uchar aucBuffer[BUFSIZ]; 00393 00394 // The nBytesRemaining variable indicates how many bytes are to be copied 00395 size_t nBytesToCopy = data_size; 00396 00397 // Here we increase the nBytesToCopy by the size of any tags that appear 00398 // at the end of the file if we don't want to strip them 00399 if (!(ulTagFlag & ID3TT_APPENDED)) 00400 { 00401 nBytesToCopy += this->GetAppendedBytes(); 00402 } 00403 00404 // The nBytesRemaining variable indicates how many bytes are left to be 00405 // moved in the actual file. 00406 // The nBytesCopied variable keeps track of how many actual bytes were 00407 // copied (or moved) so far. 00408 size_t nBytesRemaining = nBytesToCopy, 00409 nBytesCopied = 0; 00410 while (!file.eof()) 00411 { 00412 #if (defined(__GNUC__) && __GNUC__ == 2) 00413 size_t nBytesToRead = (size_t)dami::min((unsigned int)(nBytesRemaining - nBytesCopied), (unsigned int)BUFSIZ); 00414 #else 00415 size_t nBytesToRead = min((unsigned int)(nBytesRemaining - nBytesCopied), (unsigned int)BUFSIZ); 00416 #endif 00417 file.read((char *)aucBuffer, nBytesToRead); 00418 size_t nBytesRead = file.gcount(); 00419 00420 if (nBytesRead != nBytesToRead) 00421 { 00422 // TODO: log this 00423 //cerr << "--- attempted to write " << nBytesRead << " bytes, " 00424 // << "only wrote " << nBytesWritten << endl; 00425 } 00426 if (nBytesRead > 0) 00427 { 00428 long offset = nBytesRead + this->GetPrependedBytes(); 00429 file.seekp(-offset, ios::cur); 00430 file.write((char *)aucBuffer, nBytesRead); 00431 file.seekg(this->GetPrependedBytes(), ios::cur); 00432 nBytesCopied += nBytesRead; 00433 } 00434 00435 if (nBytesCopied == nBytesToCopy || nBytesToRead < BUFSIZ) 00436 { 00437 break; 00438 } 00439 } 00440 file.close(); 00441 } 00442 00443 size_t nNewFileSize = data_size; 00444 00445 if ((_file_tags.get() & ID3TT_APPENDED) && (ulTagFlag & ID3TT_APPENDED)) 00446 { 00447 ulTags |= _file_tags.get() & ID3TT_APPENDED; 00448 } 00449 else 00450 { 00451 // if we're not stripping the appended tags, be sure to increase the file 00452 // size by those bytes 00453 nNewFileSize += this->GetAppendedBytes(); 00454 } 00455 00456 if ((ulTagFlag & ID3TT_PREPENDED) && (_file_tags.get() & ID3TT_PREPENDED)) 00457 { 00458 // If we're stripping the ID3v2 tag, there's no need to adjust the new 00459 // file size, since it doesn't account for the ID3v2 tag size 00460 ulTags |= _file_tags.get() & ID3TT_PREPENDED; 00461 } 00462 else 00463 { 00464 // add the original prepended tag size since we don't want to delete it, 00465 // and the new file size represents the file size _not_ counting the ID3v2 00466 // tag 00467 nNewFileSize += this->GetPrependedBytes(); 00468 } 00469 00470 if (ulTags && (truncate(_file_name.c_str(), nNewFileSize) == -1)) 00471 { 00472 // log this 00473 return 0; 00474 //ID3_THROW(ID3E_NoFile); 00475 } 00476 00477 _prepended_bytes = (ulTags & ID3TT_PREPENDED) ? 0 : _prepended_bytes; 00478 _appended_bytes = (ulTags & ID3TT_APPENDED) ? 0 : _appended_bytes; 00479 _file_size = data_size + _prepended_bytes + _appended_bytes; 00480 00481 _changed = _file_tags.remove(ulTags) || _changed; 00482 00483 return ulTags; 00484 } 00485

Generated on Thu Jun 3 16:57:09 2004 for id3lib by doxygen 1.3.7