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