00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #if defined HAVE_CONFIG_H
00028 # include <config.h>
00029 #endif
00030
00031
00032
00033 #include <string.h>
00034 #include <fstream.h>
00035 #include <stdlib.h>
00036 #include "utils.h"
00037 #include "writers.h"
00038 #include "io_strings.h"
00039 #include "tag_impl.h"
00040 #include "utils.h"
00041
00042 using namespace dami;
00043
00044 #if !defined HAVE_MKSTEMP
00045 # include <stdio.h>
00046 #endif
00047
00048 #if defined HAVE_UNISTD_H
00049 # include <unistd.h>
00050 #endif
00051
00052 #if defined WIN32 && (!defined(WINCE))
00053 # include <windows.h>
00054 static int truncate(const char *path, size_t length)
00055 {
00056 int result = -1;
00057 HANDLE fh;
00058
00059 fh = ::CreateFile(path,
00060 GENERIC_WRITE | GENERIC_READ,
00061 0,
00062 NULL,
00063 OPEN_EXISTING,
00064 FILE_ATTRIBUTE_NORMAL,
00065 NULL);
00066
00067 if(INVALID_HANDLE_VALUE != fh)
00068 {
00069 SetFilePointer(fh, length, NULL, FILE_BEGIN);
00070 SetEndOfFile(fh);
00071 CloseHandle(fh);
00072 result = 0;
00073 }
00074
00075 return result;
00076 }
00077
00078
00079 # if defined CreateFile
00080 # undef CreateFile
00081 # endif
00082
00083 #elif defined(WINCE)
00084
00085
00086 # include <windows.h>
00087 static int truncate(const char *path, size_t length)
00088 {
00089 int result = -1;
00090 wchar_t wcTempPath[256];
00091 mbstowcs(wcTempPath,path,255);
00092 HANDLE fh;
00093 fh = ::CreateFile(wcTempPath,
00094 GENERIC_WRITE | GENERIC_READ,
00095 0,
00096 NULL,
00097 OPEN_EXISTING,
00098 FILE_ATTRIBUTE_NORMAL,
00099 NULL);
00100
00101 if (INVALID_HANDLE_VALUE != fh)
00102 {
00103 SetFilePointer(fh, length, NULL, FILE_BEGIN);
00104 SetEndOfFile(fh);
00105 CloseHandle(fh);
00106 result = 0;
00107 }
00108
00109 return result;
00110 }
00111
00112 #elif defined(macintosh)
00113
00114 static int truncate(const char *path, size_t length)
00115 {
00116
00117 return -1;
00118 }
00119
00120 #endif
00121
00122 size_t ID3_TagImpl::Link(const char *fileInfo, bool parseID3v1, bool parseLyrics3)
00123 {
00124 flags_t tt = ID3TT_NONE;
00125 if (parseID3v1)
00126 {
00127 tt |= ID3TT_ID3V1;
00128 }
00129 if (parseLyrics3)
00130 {
00131 tt |= ID3TT_LYRICS;
00132 }
00133 return this->Link(fileInfo, tt);
00134 }
00135
00136 size_t ID3_TagImpl::Link(const char *fileInfo, flags_t tag_types)
00137 {
00138 _tags_to_parse.set(tag_types);
00139
00140 if (NULL == fileInfo)
00141 {
00142 return 0;
00143 }
00144
00145 _file_name = fileInfo;
00146 _changed = true;
00147
00148 this->ParseFile();
00149
00150 return this->GetPrependedBytes();
00151 }
00152
00153 size_t RenderV1ToFile(ID3_TagImpl& tag, fstream& file)
00154 {
00155 if (!file)
00156 {
00157 return 0;
00158 }
00159
00160
00161
00162
00163
00164 if (ID3_V1_LEN > tag.GetFileSize())
00165 {
00166 file.seekp(0, ios::end);
00167 }
00168 else
00169 {
00170
00171
00172 file.seekg(0-ID3_V1_LEN, ios::end);
00173 char sID[ID3_V1_LEN_ID];
00174
00175
00176 file.read(sID, ID3_V1_LEN_ID);
00177
00178
00179
00180 if (memcmp(sID, "TAG", ID3_V1_LEN_ID) == 0)
00181 {
00182 file.seekp(0-ID3_V1_LEN, ios::end);
00183 }
00184
00185
00186 else
00187 {
00188 file.seekp(0, ios::end);
00189 }
00190 }
00191
00192 ID3_IOStreamWriter out(file);
00193
00194 id3::v1::render(out, tag);
00195
00196 return ID3_V1_LEN;
00197 }
00198
00199 size_t RenderV2ToFile(const ID3_TagImpl& tag, fstream& file)
00200 {
00201 ID3D_NOTICE( "RenderV2ToFile: starting" );
00202 if (!file)
00203 {
00204 ID3D_WARNING( "RenderV2ToFile: error in file" );
00205 return 0;
00206 }
00207
00208 String tagString;
00209 io::StringWriter writer(tagString);
00210 id3::v2::render(writer, tag);
00211 ID3D_NOTICE( "RenderV2ToFile: rendered v2" );
00212
00213 const char* tagData = tagString.data();
00214 size_t tagSize = tagString.size();
00215
00216
00217
00218 if ((!tag.GetPrependedBytes() && !ID3_GetDataSize(tag)) ||
00219 (tagSize == tag.GetPrependedBytes()))
00220 {
00221 file.seekp(0, ios::beg);
00222 file.write(tagData, tagSize);
00223 }
00224 else
00225 {
00226 String filename = tag.GetFileName();
00227 #if !defined HAVE_MKSTEMP
00228
00229
00230 FILE *tempOut = tmpfile();
00231 if (NULL == tempOut)
00232 {
00233
00234 return 0;
00235
00236 }
00237
00238 fwrite(tagData, 1, tagSize, tempOut);
00239
00240 file.seekg(tag.GetPrependedBytes(), ios::beg);
00241
00242 uchar tmpBuffer[BUFSIZ];
00243 while (!file.eof())
00244 {
00245 file.read((char *)tmpBuffer, BUFSIZ);
00246 size_t nBytes = file.gcount();
00247 fwrite(tmpBuffer, 1, nBytes, tempOut);
00248 }
00249
00250 rewind(tempOut);
00251 openWritableFile(filename, file);
00252
00253 while (!feof(tempOut))
00254 {
00255 size_t nBytes = fread((char *)tmpBuffer, 1, BUFSIZ, tempOut);
00256 file.write((char *)tmpBuffer, nBytes);
00257 }
00258
00259 fclose(tempOut);
00260
00261 #else
00262
00263
00264
00265
00266 String sTmpSuffix = ".XXXXXX";
00267 if (filename.size() + sTmpSuffix.size() > ID3_PATH_LENGTH)
00268 {
00269
00270 return 0;
00271
00272 }
00273 char sTempFile[ID3_PATH_LENGTH];
00274 strcpy(sTempFile, filename.c_str());
00275 strcat(sTempFile, sTmpSuffix.c_str());
00276
00277 int fd = mkstemp(sTempFile);
00278 if (fd < 0)
00279 {
00280 remove(sTempFile);
00281
00282 }
00283
00284 ofstream tmpOut(fd);
00285 if (!tmpOut)
00286 {
00287 tmpOut.close();
00288 remove(sTempFile);
00289 return 0;
00290
00291
00292 }
00293
00294 tmpOut.write(tagData, tagSize);
00295 file.seekg(tag.GetPrependedBytes(), ios::beg);
00296 uchar tmpBuffer[BUFSIZ];
00297 while (file)
00298 {
00299 file.read(tmpBuffer, BUFSIZ);
00300 size_t nBytes = file.gcount();
00301 tmpOut.write(tmpBuffer, nBytes);
00302 }
00303
00304 tmpOut.close();
00305
00306 file.close();
00307
00308 remove(filename.c_str());
00309 rename(sTempFile, filename.c_str());
00310
00311 openWritableFile(filename, file);
00312 #endif
00313 }
00314
00315 return tagSize;
00316 }
00317
00318
00319 flags_t ID3_TagImpl::Update(flags_t ulTagFlag)
00320 {
00321 flags_t tags = ID3TT_NONE;
00322
00323 fstream file;
00324 String filename = this->GetFileName();
00325 ID3_Err err = openWritableFile(filename, file);
00326 _file_size = getFileSize(file);
00327
00328 if (err == ID3E_NoFile)
00329 {
00330 err = createFile(filename, file);
00331 }
00332 if (err == ID3E_ReadOnly)
00333 {
00334 return tags;
00335 }
00336
00337 if ((ulTagFlag & ID3TT_ID3V2) && this->HasChanged())
00338 {
00339 _prepended_bytes = RenderV2ToFile(*this, file);
00340 if (_prepended_bytes)
00341 {
00342 tags |= ID3TT_ID3V2;
00343 }
00344 }
00345
00346 if ((ulTagFlag & ID3TT_ID3V1) &&
00347 (!this->HasTagType(ID3TT_ID3V1) || this->HasChanged()))
00348 {
00349 size_t tag_bytes = RenderV1ToFile(*this, file);
00350 if (tag_bytes)
00351 {
00352
00353 if (! _file_tags.test(ID3TT_ID3V1))
00354 {
00355 _appended_bytes += tag_bytes;
00356 }
00357 tags |= ID3TT_ID3V1;
00358 }
00359 }
00360 _changed = false;
00361 _file_tags.add(tags);
00362 _file_size = getFileSize(file);
00363 file.close();
00364 return tags;
00365 }
00366
00367 flags_t ID3_TagImpl::Strip(flags_t ulTagFlag)
00368 {
00369 flags_t ulTags = ID3TT_NONE;
00370 const size_t data_size = ID3_GetDataSize(*this);
00371
00372
00373 if (ulTagFlag & ID3TT_PREPENDED & _file_tags.get())
00374 {
00375 fstream file;
00376 if (ID3E_NoError != openWritableFile(this->GetFileName(), file))
00377 {
00378 return ulTags;
00379 }
00380 _file_size = getFileSize(file);
00381
00382
00383
00384
00385
00386 file.seekg(this->GetPrependedBytes(), ios::beg);
00387
00388 uchar aucBuffer[BUFSIZ];
00389
00390
00391 size_t nBytesToCopy = data_size;
00392
00393
00394
00395 if (!(ulTagFlag & ID3TT_APPENDED))
00396 {
00397 nBytesToCopy += this->GetAppendedBytes();
00398 }
00399
00400
00401
00402
00403
00404 size_t
00405 nBytesRemaining = nBytesToCopy,
00406 nBytesCopied = 0;
00407 while (!file.eof())
00408 {
00409 size_t nBytesToRead = dami::min<size_t>(nBytesRemaining - nBytesCopied, BUFSIZ);
00410 file.read((char *)aucBuffer, nBytesToRead);
00411 size_t nBytesRead = file.gcount();
00412
00413 if (nBytesRead != nBytesToRead)
00414 {
00415
00416
00417
00418 }
00419 if (nBytesRead > 0)
00420 {
00421 long offset = nBytesRead + this->GetPrependedBytes();
00422 file.seekp(-offset, ios::cur);
00423 file.write((char *)aucBuffer, nBytesRead);
00424 file.seekg(this->GetPrependedBytes(), ios::cur);
00425 nBytesCopied += nBytesRead;
00426 }
00427
00428 if (nBytesCopied == nBytesToCopy || nBytesToRead < BUFSIZ)
00429 {
00430 break;
00431 }
00432 }
00433 file.close();
00434 }
00435
00436 size_t nNewFileSize = data_size;
00437
00438 if ((_file_tags.get() & ID3TT_APPENDED) && (ulTagFlag & ID3TT_APPENDED))
00439 {
00440 ulTags |= _file_tags.get() & ID3TT_APPENDED;
00441 }
00442 else
00443 {
00444
00445
00446 nNewFileSize += this->GetAppendedBytes();
00447 }
00448
00449 if ((ulTagFlag & ID3TT_PREPENDED) && (_file_tags.get() & ID3TT_PREPENDED))
00450 {
00451
00452
00453 ulTags |= _file_tags.get() & ID3TT_PREPENDED;
00454 }
00455 else
00456 {
00457
00458
00459
00460 nNewFileSize += this->GetPrependedBytes();
00461 }
00462
00463 if (ulTags && (truncate(_file_name.c_str(), nNewFileSize) == -1))
00464 {
00465
00466 return 0;
00467
00468 }
00469
00470 _prepended_bytes = (ulTags & ID3TT_PREPENDED) ? 0 : _prepended_bytes;
00471 _appended_bytes = (ulTags & ID3TT_APPENDED) ? 0 : _appended_bytes;
00472 _file_size = data_size + _prepended_bytes + _appended_bytes;
00473
00474 _changed = _file_tags.remove(ulTags) || _changed;
00475
00476 return ulTags;
00477 }