1 /*************************************************************
2 * mpgtx an mpeg toolbox *
3 * by Laurent Alacoque <laureck@users.sourceforge.net> *
4 * (c) 2001 *
5 * You may copy, modify and redistribute this *
6 * source file under the terms of the GNU Public License *
7 ************************************************************/
8
9 #include "id3command.hh"
10
11 /**
12 * Parses commandline and decide what happens.
13 *
14 * @param argc argc of calling parent programm
15 * @param argv argv of calling parent programm
16 * @param arc_offset index begin of interesting args
17 * @return 0 - ok, 1 - error or return code of called subroutine [0;1]
18 */
ParseID3Command(int argc,char ** argv,int argc_offset)19 int ParseID3Command(int argc, char** argv, int argc_offset){
20 BTRACK;
21 // starting from argc_offset format is :
22 // -h or
23 // set FORMAT file ... file ...
24 // mov FORMAT2 file .... file ...
25 // del file ... file ...
26 int argcount= argc -argc_offset;
27 if (argcount==0){
28 fprintf(stderr,"You must specify at least a mode\n");
29 return 1;
30 }
31
32
33 if (!strcmp(argv[argc_offset],"-h")){
34 // this is -h option
35 printf("tagmp3 an ID3v1 tag editor\n");
36 printf("Usage : tagmp3 [-n] [-v] [set|move|del|list|show] [FORMAT] [file...]\n\n");
37 printf(" OPTIONS:\n");
38 printf(" -v prints version information\n");
39 printf(" set sets ID3v1 tag according to FORMAT\n");
40 printf(" move moves mp3 file according to the FORMAT\n");
41 printf(" del removes ID3v1 tag from file (if any)\n");
42 printf(" list lists known music genres along with their numeric values\n\n");
43 printf(" FORMAT:\n");
44 printf(" for set mode : \"%%Y? %%X:VALUE ...\" sets the X field of tag\n");
45 printf(" with VALUE and asks for the value of Y field\n");
46 printf(" for move mode : \"mymp3/%%X/%%Y/%%Z.mp3\" move file to\n");
47 printf(" ./mymp3/tag{X}/tag{Y}/tag{Z}.mp3\n\n");
48 printf(" valid fields are : %%A artist %%t title %%T track %%y year\n");
49 printf(" %%a album %%c comment %%g genre\n");
50 return 0;
51 }
52
53 if (!strcmp(argv[argc_offset],"-n")){
54 //show only
55 id3_Show_Only=true;
56 argc_offset++;
57 }
58
59
60 if (!strcmp(argv[argc_offset],"list")){
61 // list genres
62 for (int i=0; i< MAX_ID3_GENRE; i+=4){
63 if (i<MAX_ID3_GENRE) printf("%3d %-15.15s",i,genre[i]);
64 if (i+1<MAX_ID3_GENRE) printf(" %3d %-15.15s",i+1,genre[i+1]);
65 if (i+2<MAX_ID3_GENRE) printf(" %3d %-15.15s",i+2,genre[i+2]);
66 if (i+3<MAX_ID3_GENRE) printf(" %3d %-15.15s",i+3,genre[i+3]);
67 printf("\n");
68 }
69 return 0;
70 }
71
72 if (!strcmp(argv[argc_offset],"-v")){
73 printf("tagmp3 version %s\n",MPGTX_VERSION);
74 printf("Copyleft 2001 Laurent Alacoque <laureck@users.sourceforge.net>\n");
75 printf("updates, bugs, patch, money : http://mpgtx.sourceforge.net\n");
76 return 0;
77
78 }
79
80 if (!strcmp(argv[argc_offset],"show")){
81 if (argcount<2){
82 fprintf(stderr,"show id3v1 tag of what file?\n");
83 return 1;
84 }
85 return ParseShow(argc,argv,argc_offset+1);
86 }
87
88 if (!strcmp(argv[argc_offset],"set")){
89 if (argcount<3){
90 fprintf(stderr,"Not enough arguments for set mode\n");
91 return 1;
92 }
93 return ParseSet(argc,argv,argc_offset+1);
94 }
95
96 if (!strcmp(argv[argc_offset],"move")){
97 if (argcount<3){
98 fprintf(stderr,"Not enough argument for mov mode\n");
99 return 1;
100 }
101 return ParseMov(argc,argv,argc_offset+1);
102 }
103
104 if (!strcmp(argv[argc_offset],"del")){
105 if (argcount<2){
106 fprintf(stderr,"remove id3v1 tag from what?\n");
107 return 1;
108 }
109 return ParseDel(argc,argv,argc_offset+1);
110 }
111
112 // not a valid id3 mode
113 fprintf(stderr,"invalid mode : %s\n",argv[argc_offset]);
114 return 1;
115 }
116
117
118 /**
119 * Shows ID3v1 TAG of files.
120 *
121 * @param argc argc of calling parent programm
122 * @param argv argv of calling parent programm
123 * @param arc_offset index begin of interesting args
124 * @return allways 0, no error if there are no ID3v1 TAGs in file
125 */
ParseShow(int argc,char ** argv,int argc_offset)126 int ParseShow(int argc, char** argv, int argc_offset){
127 BTRACK;
128 OpenedFile* my;
129 for (int i = argc_offset; i < argc; i++){
130 my = OpenFile(argv[i]);
131 if ((!my->thefile)){
132 continue;
133 }
134 // okay we have a file here.
135 if (!my->thetag){
136 fprintf(stdout, "%s doesn't have any ID3v1 tag (Note: ID3v2 not supported)\n", argv[i]);
137
138 }
139 else{
140 printf("%s\n", argv[i]);
141 printf(" Artist : %s\n", my->thetag->artist);
142 printf(" Title : %s\n", my->thetag->title);
143 printf(" Album : %s\n", my->thetag->album);
144 if (my->thetag->track>0)
145 printf(" Track : %d\n", my->thetag->track);
146 printf(" Year : %s\n", my->thetag->year);
147 if (my->thetag->genre < MAX_ID3_GENRE)
148 printf(" Genre : %s\n", genre[my->thetag->genre]);
149 printf(" Comment: %s\n\n", my->thetag->comment);
150 }
151
152
153 // lets delete and close before next opened file
154 if (my->thefile) fclose (my->thefile);
155 if (my->thetag) delete my->thetag;
156 delete my;
157 }
158 return 0;
159 }
160
161
162 /**
163 * Sets ID3v1 TAG of files.
164 *
165 * @param argc argc of calling parent programm
166 * @param argv argv of calling parent programm
167 * @param arc_offset index begin of interesting args
168 * @return allways 0, no error if there are no ID3v1 TAGs in file
169 */
ParseSet(int argc,char ** argv,int argc_offset)170 int ParseSet(int argc, char** argv, int argc_offset){
171 BTRACK;
172 OpenedFile* my;
173 int retval=1;
174 for (int i = argc_offset + 1; i < argc; i++) {
175 my = OpenFile(argv[i]);
176 if ((!my->thefile) || (my->filetype != REAL_MP3)) {
177 continue;
178 }
179 // okay we have a file here.
180 // FIXME: This could not be reached, DEPRECATED?
181 if (my->filetype != REAL_MP3) {
182 fprintf(stderr, "Skipping file %s ", argv[i]);
183 if (my->filetype== FAKE_MP3) fprintf(stderr,"(Wave file)\n");
184 else fprintf(stderr,"(not an mp3)\n");
185 }
186 printf("Setting %s tag \n",argv[i]);
187 fflush(stdout);
188 if (!SetID3(argv[argc_offset],my)){
189 // something's wrong with the format
190 fprintf(stderr,"ERROR while setting the format\n");
191 retval=1;
192 break;
193 }
194 else{
195 if(!id3_Show_Only) {
196 // printf("Setting %s tag\n",argv[i]);
197 WriteID3(my);
198 retval=0;
199 }
200 else{
201 // printf("\n");
202 }
203 }
204
205 // lets delete and close before next opened file
206 if (my->thefile) fclose (my->thefile);
207 if (my->thetag) delete my->thetag;
208 delete my;
209 }
210 return retval;
211 }
212
213
214
215
216
217
218
219
220 /**
221 * Parses command line to prepare moving of MP3 files.
222 *
223 * @param argc argc of calling parent programm
224 * @param argv argv of calling parent programm
225 * @param arc_offset index begin of interesting args
226 * @return allways 0, no error if there are no ID3v1 TAGs in file
227 */
ParseMov(int argc,char ** argv,int argc_offset)228 int ParseMov(int argc, char** argv, int argc_offset){
229 BTRACK
230 OpenedFile* my;
231 int i;
232 int j;
233 int last_slash;
234 char* tempfilename=new char[300];
235 char mybasename[300];
236 char myfilename[300];
237
238 // for each remaining arg
239 for ( i=argc_offset+1; i<argc; i++){
240 my=OpenFile(argv[i]);
241 //if not opened, continue;
242 if (!my->thefile){
243 delete my;
244 continue;
245 }
246
247
248
249
250 // opened, with a tag
251 // compute basename and filename
252
253 last_slash=-1;
254 if (!strstr(argv[i],"/")){
255 // no base directory in file name
256 mybasename[0]=0;
257 }
258 else{
259 for(j =0; j < int(strlen(argv[i])); j++){
260 if (argv[i][j]=='/') last_slash=j;
261 }
262
263 for (j=0; j <= last_slash; j++){
264 mybasename[j]=argv[i][j];
265 }
266 mybasename[j]=0;
267 }
268 // now we have the basename, last_slash is either
269 // -1 (no basename) or the offset of the last slash
270 last_slash++;
271 for (j=last_slash; j<=int(strlen(argv[i])); j++){
272 myfilename[j-last_slash]=argv[i][j];
273 }
274
275 //basename and filename are okay
276
277 // if this is a fake move it in fake-mp3
278 // fake is a wav with mp3 extension
279
280 if (my->filetype==FAKE_MP3){
281 sprintf(tempfilename,"fake-mp3/");
282 strcat(tempfilename,myfilename);
283 Move2(argv[i],mybasename,tempfilename);
284 if (my->thetag) delete my->thetag;
285 delete my;
286 continue;
287 }
288
289 // if this file doesn't have a tag
290 // skip it
291 if (! my->thetag){
292 fprintf(stderr,"Skipping file %s (no tag)\n",argv[i]);
293 fclose (my->thefile);
294 delete my;
295 continue;
296 }
297
298
299 // if this is not a mp3
300 if (my->filetype==NOT_MP3){
301 fprintf(stderr,"Skipping %s(not an mpeg file)\n",argv[i]);
302 }
303 else {
304
305 // Now we have to handle the format string.
306 tempfilename[0] = 0;
307 int name_offset=0;
308 int fieldsize=-1;
309 char *source=0;
310 char* format= argv[argc_offset];
311 bool error_found=false;
312 char temptracknum[10];
313 int track;
314 int t,h,k,m;
315 char* fieldname=0;
316 unsigned char genre_num;
317 const char* genre_as_string;
318 int genre_length;
319 j=0;
320 // the format string is argv[argc_offset]
321 while ((j < int(strlen(format)))&& !error_found ){
322 if (format[j] == '%'){
323 if (format[j+1] != '%'){
324 // it starts like a field name
325 fieldsize=-1;
326 switch (format[j+1]){
327 case 'a':
328 fieldname="album";
329 source= my->thetag->album;
330 fieldsize=30;
331 break;
332 case 'A':
333 fieldname="artist";
334 source= my->thetag->artist;
335 fieldsize=30;
336 break;
337 case 't':
338 fieldname="title";
339 source= my->thetag->title;
340 fieldsize=30;
341 break;
342 case 'y':
343 fieldname="year";
344 source= my->thetag->year;
345 fieldsize=4;
346 break;
347 case 'c':
348 fieldname="comment";
349 source= my->thetag->comment;
350 fieldsize=30;
351 break;
352 case 'T':
353 track= my->thetag->track;
354 if (track <0){
355 fprintf(stderr,"%s: empty track value needed by format\n",argv[i]);
356 error_found=true;
357 }
358 else {
359 sprintf(temptracknum,"%02d",track);
360 for (h=0; h < int(strlen(temptracknum));h++){
361 tempfilename[name_offset++]=temptracknum[h];
362 }
363 }
364 break;
365 case 'g':
366 genre_num = my->thetag->genre;
367 if (genre_num >= MAX_ID3_GENRE){
368 genre_as_string = "Unknown";
369 genre_length=7;
370 }
371 else {
372 genre_as_string=genre[genre_num];
373 genre_length=strlen(genre_as_string);
374 }
375 for (t=0; t <genre_length; t++){
376 tempfilename[name_offset++]=genre_as_string[t];
377 }
378
379 break;
380
381 default :
382 //invalid format
383 fprintf(stderr,"Invalid format %s\n",&format[i]);
384 return 1;
385 break;
386 }
387
388 if (fieldsize >0){
389 //eat leading white spaces
390 for (k=0;(k <fieldsize)&& (source[k] == ' ');k++)
391 {/*do nothing*/};
392
393 if ((k>= fieldsize) || (source[k] == 0)){
394 // this was a all white field... skip ?
395 fprintf(stderr,"%s: empty field %s needed by format\n",argv[i],fieldname);
396 error_found=true;
397 }
398 else {
399 // k points to the first valid char
400 int last_valid=k;
401 for (int l =0; (l<fieldsize)&&(source[l]!=0); l++){
402 if (source[l]!=' ') last_valid=l;
403 }
404
405
406 // here k is the first non ws char and last_valid the last one
407 for (m=k;m<=last_valid;m++){
408 //correct the dangerous chars
409 switch (source[m]){
410 case '/' : source[m]='\\'; break;
411 }
412 tempfilename[name_offset++]=source[m];
413 }
414
415 }
416 }
417
418 j+=2;
419 }
420 else {
421 // this is a %%
422 tempfilename[name_offset++]=format[j];
423 j+=2;
424 }
425 }
426 else {
427 // standard char
428 tempfilename[name_offset++]=format[j++];
429 }
430 }
431 tempfilename[name_offset]=0;
432 if (error_found) continue;
433 Move2(myfilename,mybasename,tempfilename);
434
435 }
436
437
438
439 // lets delete and close before next opened file
440 if (my->thefile) fclose (my->thefile);
441 if (my->thetag) delete my->thetag;
442 delete my;
443
444 }
445 delete[] tempfilename;
446 return 0;
447 }
448
449 /**
450 * Removes ID3v1 TAG from files.
451 *
452 * @param argc argc of calling parent programm
453 * @param argv argv of calling parent programm
454 * @param arc_offset index begin of interesting args
455 * @return 1 - error: windows version dos not work yet, otherwise allways 0
456 */
ParseDel(int argc,char ** argv,int argc_offset)457 int ParseDel(int argc, char** argv, int argc_offset){
458 BTRACK
459
460 #ifdef _WIN32
461 // windows version
462 /*
463 * from: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/base/setendoffile.asp
464 *
465 * The SetEndOfFile function moves the end-of-file (EOF) position for the specified file
466 * to the current position of the file pointer.
467 *
468 * This function sets the physical end of a file (as indicated by allocated clusters).
469 * To set the logical end of a file, use the SetFileValidData function.
470 *
471 * BOOL SetEndOfFile(
472 * HANDLE hFile
473 * );
474 */
475 printf("del\n");
476 fprintf(stderr,"del mode is not implemented under windows : \nstill don't know how to truncate a file");
477 return 1;
478
479 #else
480 off_t FileSize;
481 OpenedFile* my;
482 for (int i=argc_offset; i < argc; i++){
483 printf("Deleting tag in %s\n",argv[i]);
484 my=OpenFile(argv[i]);
485 if (!my->thefile){
486 printf("Can not open file\n");
487 if (my->thetag) delete my->thetag;
488 delete my;
489 continue;
490 }
491 // okay we have a file here.
492 if (my->thetag) {
493 // printf("tag is present\n");
494 // this file has a tag
495 FSEEK(my->thefile,0,SEEK_END);
496 // printf("Size before : %ld\n",ftello(my->thefile));
497 FileSize=FTELL(my->thefile)-128;
498 // printf("Size after : %ld\n",FileSize);
499 if (!id3_Show_Only)
500 if(truncate(argv[i],FileSize)){
501 fprintf(stderr,"Unable to truncate file \n");
502 perror(argv[i]);
503 }
504 }
505 //printf("File doesn't have any tag\n");
506 // lets delete and close before next opened file
507 if (my->thefile) fclose (my->thefile);
508 if (my->thetag) delete my->thetag;
509 delete my;
510 }
511 return 0;
512
513 #endif // _WIN32
514
515 }
516
517 /**
518 * Fills a OpenedFile structure with TAG infos of a file according given FileName
519 *
520 * @param FileName Name of file to open
521 * @return Pointer to filled structure
522 */
OpenFile(char * FileName)523 OpenedFile* OpenFile(char* FileName){
524 BTRACK;
525 unsigned char start[4];
526 char TAG[128];
527 OpenedFile* my = new OpenedFile;
528 my->thetag=0;
529 my->thefile=0;
530 my->filetype=NOT_MP3;
531 my->thefile=fopen(FileName,"r+b");
532 if (!my->thefile){
533 // error
534 perror(FileName);
535 return my;
536 }
537
538 fread(start,4,1,my->thefile);
539 if (!strncmp((char*)(start),"RIFF",4)){
540
541 if (!strstr(FileName,".mp")||!strstr(FileName,".MP")){
542 // fprintf(stderr,"Skipping Fake mp3 file %s (Wave file)\n",FileName);
543 my->filetype=FAKE_MP3;
544 return my;
545 }
546 else {
547 // fprintf(stderr,"Skipping Wave file %s\n",FileName);
548 my->filetype=NOT_MP3;
549 return my;
550 }
551 }
552
553 if ((start[0]==0xFF) && ((start[1] & 0xE0) == 0xE0)){
554 my->filetype=REAL_MP3;
555 }
556 else if (start[0]== 'I' && start[1]=='D' && start[2]=='3'){
557 my->filetype=REAL_MP3;
558 }
559 else{
560 //doesn't start like an mp3
561 if (!strstr(FileName,".mp")||!strstr(FileName,".MP")){
562 my->filetype=REAL_MP3;
563 }
564 else {
565 fprintf(stderr,"Skipping unknown file %s\n",FileName);
566 my->filetype=NOT_MP3;
567 return my;
568 }
569 }
570
571
572 FSEEK(my->thefile,-128,SEEK_END);
573 fread(TAG,128,1,my->thefile);
574
575 if ( strncmp (TAG,"TAG",3) ){
576 // doesn't begin with TAG : no tag
577 return my;
578 }
579
580 // begins with TAG : parse it.
581 my->thetag=new myID3tag;
582 strncpy(my->thetag->title,&TAG[3],30);
583 strncpy(my->thetag->artist,&TAG[33],30);
584 strncpy(my->thetag->album,&TAG[63],30);
585 strncpy(my->thetag->year,&TAG[93],4);
586 strncpy(my->thetag->comment,&TAG[97],30);
587 if (TAG[125]==0){
588 my->thetag->track=TAG[126];
589 }
590 else {
591 my->thetag->track=-1;
592 }
593 my->thetag->genre=(unsigned char) TAG[127];
594
595 return my;
596 }
597
598 /**
599 * Sets ID3v1 TAG of a MP3 file according given format string.
600 *
601 * @param format Format string with TAGs to set
602 * @param tag File to tag.
603 * @return false on format errors, true otherwise
604 */
SetID3(char * format,OpenedFile * tag)605 bool SetID3(char* format,OpenedFile* tag){
606 BTRACK;
607 // change tag->thetag to reflect format
608 if (! tag->thetag) {
609 tag->thetag=new myID3tag;
610 tag->thetag->album[0]=0;
611 tag->thetag->artist[0]=0;
612 tag->thetag->year[0]=0;
613 tag->thetag->comment[0]=0;
614 tag->thetag->title[0]=0;
615 tag->thetag->track=-1;
616 tag->thetag->genre=255;
617 }
618 char *target=0;
619 // parse the format
620 int fieldsize;
621 int tracknum=-1;
622 int genre=-1;
623 int length = strlen(format);
624 for(int i=0; i <= length-3;){
625 // first char must be %
626 // second char must be tAaycgT
627 // third char must be : or ?
628 while(format[i]==' ') i++;
629 if (format[i]!='%' || (format[i+2] != ':' && format [i+2] != '?')){
630 fprintf(stderr,"Invalid format \"%s\"\n",format+i);
631 return false;
632 }
633 fieldsize=-1;
634 switch (format[i+1]){
635 case 'a':
636 if (format[i+2]=='?') fprintf(stdout,"Album : ");
637 target= tag->thetag->album;
638 fieldsize=30;
639 break;
640 case 'A':
641 if (format[i+2]=='?') fprintf(stdout,"Artist : ");
642 target= tag->thetag->artist;
643 fieldsize=30;
644 break;
645 case 't':
646 if (format[i+2]=='?') fprintf(stdout,"Title : ");
647 target= tag->thetag->title;
648 fieldsize=30;
649 break;
650 case 'c':
651 if (format[i+2]=='?') fprintf(stdout,"Comment: ");
652 target= tag->thetag->comment;
653 fieldsize=30;
654 break;
655 case 'y':
656 if (format[i+2]=='?') fprintf(stdout,"Year : ");
657 target= tag->thetag->year;
658 fieldsize=4;
659 break;
660 case 'g':
661 if (format[i+2]=='?') fprintf(stdout,"Genre : ");
662 break;
663 case 'T':
664 if (format[i+2]=='?') fprintf(stdout,"Track : ");
665 break;
666 }
667
668 // treat the '?'
669 if (format[i+2]=='?'){
670 // ask user
671 if (fieldsize>0){
672 //any char*
673 fgets(target,fieldsize+1,stdin);
674 // now remove the trailing \n if any
675 if (strlen(target)!=0){
676 if (target[strlen(target)-1]=='\n') target[strlen(target)-1]=0;
677 else {
678 // mmmmm no \n at the end : flush input
679 while (getchar()!='\n');
680 }
681 }
682 }
683 else{
684 // a number for genre or track
685 if (format[i+1]=='T') scanf("%d",&tracknum);
686 if (format[i+1]=='g') scanf("%d",&genre);
687 // printf("read T %d g %d\n",tracknum,genre);
688 }
689 i+=3;
690 continue;
691 }
692 // treat the ':'
693 // is this a 'standard' field?
694 if (fieldsize==-1){
695 if (length-i >3){
696 if (format[i+1]=='T') sscanf(&format[i+3],"%d",&tracknum);
697 if (format[i+1]=='g') sscanf(&format[i+3],"%d",&genre);
698 // printf("scanned T %d g %d\n",tracknum,genre);
699 i+=3;
700 }
701 while ((format[i]>='0' && format[i]<='9')||(format[i]==' ')) i++;
702 continue;
703 }
704 // this is a char * field
705 // move i to the start of field
706 i+=3;
707 // now copy everything until a % or fieldsize or format length
708 bool finished=false;
709 int n_processed=0;
710 while (!finished){
711 if ( // a % not followed by a %
712 (format[i]=='%' && format[i+1]!='%')||
713 // end of format
714 (format[i]=='\0')
715 ) {
716 finished=true;
717 // while (n_processed <fieldsize) target[n_processed++]=' ';
718 }
719 else{
720 if (n_processed<fieldsize){
721 target[n_processed++]=format[i];
722 target[n_processed]=0; // end the string
723 }
724 else {
725 // printf("skipping %c [offset : %d]\n",format[i],n_processed);
726 }
727 if (format[i]=='%') i++; // skip the second % sign
728 i++;
729 }
730 }
731
732
733 }
734
735 //if (genre<0){
736 // tag->thetag->genre=255; // unspecified genre
737 //}
738 if (genre>=0) tag->thetag->genre=genre;
739 if (tracknum >=0 && tracknum <256 ){
740
741 tag->thetag->track=(unsigned char) tracknum;
742 }
743
744 return true;
745 }
746
747 /**
748 * Write ID3v1 TAG to given file.
749 *
750 * @param filename MP3 file to set ID3v1 TAG.
751 */
WriteID3(OpenedFile * filename)752 void WriteID3(OpenedFile* filename){
753 BTRACK
754 //write tag->thetag in tag->thefile
755 /*
756 printf ("artist %s\ntitle %s \nalbum %s\nyear %s\ncomment %s\ngenre %d\ntrack%d\n\n",
757 filename->thetag->artist ,
758 filename->thetag->title ,
759 filename->thetag->album,
760 filename->thetag->year ,
761 filename->thetag->comment ,
762 filename->thetag->genre ,
763 filename->thetag->track);
764 */
765 if (filename->thefile==0){
766 fprintf(stderr,"ERROR : no valid FILE* in WriteID3\n");
767 return;
768 }
769 if (filename->thetag==0){
770 fprintf(stderr,"ERROR : no valid tag in WriteID3\n");
771 return;
772 }
773
774 char TAG[128];
775 TAG[0]='T';
776 TAG[1]='A';
777 TAG[2]='G';
778 int length,i;
779 length=strlen(filename->thetag->title);
780 for (i=0; i < length; i++)
781 TAG[3+i]=filename->thetag->title[i];
782 for (;i<30; i++)
783 TAG[3+i]=' ';
784
785 length=strlen(filename->thetag->artist);
786 for (i=0; i < length; i++)
787 TAG[33+i]=filename->thetag->artist[i];
788 for (;i<30; i++)
789 TAG[33+i]=' ';
790
791 length=strlen(filename->thetag->album);
792 for (i=0; i < length; i++)
793 TAG[63+i]=filename->thetag->album[i];
794 for (;i<30; i++)
795 TAG[63+i]=' ';
796
797 length=strlen(filename->thetag->year);
798 for (i=0; i < length; i++)
799 TAG[93+i]=filename->thetag->year[i];
800 for (;i<4; i++)
801 TAG[93+i]=' ';
802
803 length=strlen(filename->thetag->comment);
804 for (i=0; i < length; i++)
805 TAG[97+i]=filename->thetag->comment[i];
806 for (;i<30; i++)
807 TAG[97+i]=' ';
808
809 if (filename->thetag->track >0){
810 TAG[125]=0; TAG[126]=(unsigned char) filename->thetag->track;
811 }
812
813
814 //if(filename->thetag->genre !=255) TAG[127]=(unsigned char) filename->thetag->genre;
815 TAG[127]= filename->thetag->genre;
816
817
818
819 // okay we have our chunk.
820 char checkTAG[3];
821 FSEEK(filename->thefile, -128, SEEK_END);
822 fread(checkTAG,3,1,filename->thefile);
823 if (!strncmp(checkTAG,"TAG",3)){
824 // a TAG is present
825 FSEEK(filename->thefile, -128, SEEK_END);
826 fwrite(TAG,128,1,filename->thefile);
827 }else{
828 // no tag in this file
829 FSEEK(filename->thefile,0,SEEK_END);
830 fwrite(TAG,128,1,filename->thefile);
831 }
832
833 }
834
835 /**
836 * Asks user for creation of a new directory.
837 *
838 *@param path Path of new directory
839 *@return true if user allows creation, false if deny
840 */
AskDirCreation(char * path)841 bool AskDirCreation(char* path){
842 BTRACK
843 fprintf(stderr,"Create directory %s ?:[N/y]",path);
844 char answer=getchar();
845 while(getchar()!='\n');
846 RTRACK
847 if (answer=='y' || answer=='Y') return true;
848
849 return false;
850 }
851
852 /**
853 * Moves file to destination, creates intermediate directories if necessary.
854 *
855 * @param name Filename
856 * @param basedir Source directory
857 * @param dest Destination directory
858 */
Move2(const char * name,const char * basedir,const char * dest)859 void Move2(const char* name,const char* basedir,const char* dest){
860 BTRACK
861 struct stat mystats;
862 int ret;
863 char *tempdest;
864 char *fullname;
865 int move2dirsize=0;
866 fullname = new char [strlen(name)+strlen(basedir)+1];
867 strcpy(fullname,basedir);
868 strcat(fullname,name);
869
870 // if the dest ends with a / , keep space for the name
871 if (dest[strlen(dest)-1]=='/'){
872 move2dirsize=strlen (name);
873 }
874
875
876 //handle the full path (begins with a /)
877 if (dest[0]=='/'){
878 tempdest=new char[strlen(dest)+1+move2dirsize];
879 strcpy(tempdest,dest);
880 }
881
882 //handle the ~/ (home dir)
883 else if(dest[0]=='~' && dest[1]=='/'){
884 struct passwd* mypassentry;
885 mypassentry=getpwuid(getuid());
886
887 if (!mypassentry || !(mypassentry->pw_dir)){
888 fprintf(stderr,"Could not resolve your home directory, use absolute path\n");
889 return;
890 }
891 tempdest= new char [strlen(mypassentry->pw_dir)+strlen(dest)+1+move2dirsize];
892 if (mypassentry->pw_dir[strlen(mypassentry->pw_dir)-1] == '/'){
893 // pwdir ends with a slash
894 strcpy(tempdest,mypassentry->pw_dir);
895 strcat(tempdest,&dest[2]); // dest[2] after the /
896 }
897 else {
898 //pw_dir doesn't end with a slash
899 strcpy(tempdest,mypassentry->pw_dir);
900 strcat(tempdest,&dest[1]);
901 }
902
903 }
904
905 //handle the ./
906 else if (dest[0]=='.' && dest[1]=='/'){
907 //explicitely relative path
908 tempdest= new char[strlen(dest)+strlen(basedir)+1+move2dirsize];
909 strcpy(tempdest,basedir);
910 strcat(tempdest,&dest[2]); //after the slash
911 }
912
913 // dest is relative, don't change it
914 else
915 {
916 tempdest= new char[strlen(dest)+strlen(basedir)+1+move2dirsize];
917 strcpy(tempdest,basedir);
918 strcat(tempdest,dest);
919 }
920
921 if (move2dirsize){
922 //the format ends with a /, append name
923 strcat(tempdest,name);
924 }
925 //printf("%s\n =>%s\n\n",name,tempdest);
926
927
928 //this for is to create intermediate directories
929 for (int i= 0; i < int(strlen(tempdest)); i++){
930 if (tempdest[i]=='/' && i!=0){
931 //check if dir exists
932
933 //hide the trailing path
934 tempdest[i]=0;
935 ret=stat(tempdest,&mystats);
936 if (!ret){
937 //stat worked
938 //check if this is a directory
939 if ((mystats.st_mode & S_IFMT)==S_IFREG){
940 fprintf(stderr,"Error : %s is not a directory\n",tempdest);
941 delete [] tempdest;
942 return;
943 }
944
945
946 }
947 else{
948 //didn't work... check the error
949 // printf("%s ",tempdest);
950 switch(errno){
951 case ENOENT:
952 // printf("ENOENT\n");
953 if (id3_Show_Only) {
954 printf("Would Create directory %s (if not created before)\n",tempdest);
955 }
956 else{
957 if(AskDirCreation(tempdest)) mkdir(tempdest,0777);
958 else return;
959 }
960 break;
961 case ENOTDIR:
962
963 // a part of the path is a directory
964 fprintf(stderr,"Error : %s is not a directory\n",tempdest);
965 delete[]tempdest;
966 return;
967 break;
968 case EACCES:
969 perror(tempdest);
970 delete[] tempdest;
971 return;
972 break;
973 default :
974 perror(tempdest);
975 delete[] tempdest;
976 return;
977 break;
978 }
979
980 }
981 //show the trailing path again
982 tempdest[i]='/';
983 }
984 }
985 // okay we can rename
986 // check if the destination already exist
987 ret = stat(tempdest,&mystats);
988 if (!ret){
989 printf("Skipping %s (file exists)\n",tempdest);
990 delete[] tempdest;
991 return;
992 }
993 if(id3_Show_Only){
994 printf("Would move %s\n",fullname);
995 printf("to %s\n\n",tempdest);
996 }
997 else{
998 ret=rename(fullname,tempdest);
999 if (ret){
1000 perror(fullname);
1001 }
1002 else{
1003 //printf("%s=>%s\n",name,tempdest);
1004 }
1005 }
1006 }
1007