1 /*
2     textfunc.c - Handles most console I/O and interface tasks
3 
4     Copyright (C) 2000-2006 Cedric Tefft <cedric@phreaker.net>
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 
20   ***************************************************************************
21 
22   This program is based in part on MP3Info 0.5 by Ricardo Cerqueira <rmc@rccn.net>
23 
24 */
25 
26 #include "mp3info.h"
27 
28 char *layer_text[] = {
29 	"I", "II", "III"
30 };
31 
32 /*
33  * Name of this one is quite obvious...
34  */
35 
CenterText(int line,char * text)36 void CenterText(int line, char *text) {
37    mvprintw(line,(COLS/2)-((int)strlen(text)/2),text);
38 }
39 
40 /* Convert hex digit to integer */
41 
xtoi(char * string)42 int xtoi(char *string) {
43 	char chr=toupper(string[0]);
44 
45 	if(chr > '9')
46 		return (int) (chr - 'A' + 10);
47 	else
48 		return (int) (chr - '0');
49 }
50 
51 
52 
53 /*
54  * Another one with an obvious name :-)
55  */
56 
display_help()57 void display_help() {
58 
59 	 printf("%s %s\n\nUSAGE:\n\n",VERSION,COPYRIGHT);
60 	 printf("\tmp3info [ -h | -G ]\n"\
61 		"\n\tmp3info [-x] [-F] [-r a|m|v] [-p FORMAT_STRING] file(s)\n"\
62 		"\n\tmp3info [-d] file(s)\n"\
63 		"\n\tmp3info [-i]  [-t title] [-a artist] [-l album] [-y year]\n"\
64 		"\t\t[-c comment] [-n track] [-g genre] file(s)\n"\
65 		"\nOPTIONS\n"\
66 		"\t-a artist\tSpecify ID3 artist name\n"\
67 		"\t-c comment\tSpecify ID3 comment\n"\
68 		"\t-g genre\tSpecify ID3 genre (use -G for genre list)\n"\
69 		"\t-l album\tSpecify ID3 album name\n"\
70 		"\t-n track\tSpecify ID3 v1.1 track number\n"\
71 		"\t-t title\tSpecify ID3 track title\n"\
72 		"\t-y year\t\tSpecify ID3 copyright year\n\n"\
73 		"\t-G\t\tDisplay valid genres\n"\
74 		"\t-h\t\tDisplay this help page\n"\
75 		"\t-x\t\tDisplay technical attributes of the MP3 file\n"\
76 		"\t-r a|m|v\tReport bit rate of (VBR) files as:\n"\
77 		"\t\ta - Average bit rate (float)\n"\
78 		"\t\tm - Median bit rate (integer)\n"\
79 		"\t\tv - Simply  use  the  word 'Variable' (string) [default]\n\n"\
80 		"\t-i\t\tEdit ID3  tag  interactively\n"\
81 		"\t-d\t\tDelete ID3 tag (if one exists)\n"\
82 		"\t-f\t\tForce processing if file is not a standard MP3\n"\
83 		"\t-F\t\tFull Scan (see man page for details)\n"\
84 		"\n\n\t-p \"FORMAT_STRING\" Print FORMAT_STRING with substitutions\n"\
85 		"\n\t\tConversion Specifiers\n\n"\
86 		"\t\t%%f\tFilename without the path (string)\n"\
87 		"\t\t%%F\tFilename with the path (string)\n"\
88 		"\t\t%%k\tFile size in KB (integer)\n"\
89 		"\n\t\t%%a\tArtist (string)\n"\
90 		"\t\t%%c\tComment (string)\n"\
91 		"\t\t%%g\tMusical genre (string)\n"\
92 		"\t\t%%G\tMusical genre (integer)\n"\
93 		"\t\t%%l\tAlbum name (string)\n"\
94 		"\t\t%%n\tTrack (integer)\n"\
95 		"\t\t%%t\tTrack Title (string)\n"\
96 		"\t\t%%y\tYear (string)\n"\
97 		"\n\t\t%%C\tCopyright flag (string)\n"\
98 		"\t\t%%e\tEmphasis (string)\n"\
99 		"\t\t%%E\tCRC Error protection (string)\n"\
100 		"\t\t%%L\tMPEG Layer (string)\n"\
101 		"\t\t%%O\tOriginal material flag (string)\n"\
102 		"\t\t%%o\tStereo/mono mode (string)\n"\
103 		"\t\t%%p\tPadding (string)\n"\
104 		"\t\t%%v\tMPEG Version (float)\n"\
105 		"\n\t\t%%u\tNumber of good audio frames (integer)\n"\
106 		"\t\t%%b\tNumber of corrupt audio frames (integer)\n"\
107 		"\t\t%%Q\tSampling frequency in Hz (integer)\n"\
108 		"\t\t%%q\tSampling frequency in KHz (integer)\n"\
109 		"\t\t%%r\tBit  Rate  in  KB/s  (see also '-r')\n"\
110 		"\n\t\t%%m\tPlaying time: minutes only (integer)\n"\
111 		"\t\t%%s\tPlaying time: seconds only (integer)\n"\
112 		"\t\t%%S\tTotal playing time in seconds (integer)\n"\
113 		"\n\t\t%%%%\tA single percent sign\n"\
114     		"\n\t\tEscape Sequences\n\n"\
115 		"\t\t\\n\tNewline\n"\
116 		"\t\t\\t\tHorizontal tab\n"\
117 		"\t\t\\v\tVertical tab\n"\
118 		"\t\t\\b\tBackspace\n"\
119 		"\t\t\\r\tCarriage Return\n"\
120 		"\t\t\\f\tForm Feed\n"\
121 		"\t\t\\a\tAudible Alert (terminal bell)\n"\
122 		"\t\t\\xhh\tAny  arbitrary character specified by the\n"\
123 		"\t\t\thexidecimal number hh\n"\
124 		"\t\t\\ooo\tAny arbitrary character specified by  the\n"\
125 		"\t\t\toctal number ooo\n"\
126 		"\t\t\\\\\tA single backslash character\n\n"\
127 		"This help screen is only a summary of command-line switches\n"\
128 		"See the man page for complete documentation.\n\n");
129 
130 }
131 
display_genres(int alphagenreindex[],char * typegenre[])132 void display_genres(int alphagenreindex[],char *typegenre[]) {
133 	int i,j,index;
134 	printf("Extended MP3 ID3 Genres\n=======================\n");
135 	for(i=0;i<GENREROWS;i++) {
136 	   for(j=0;j<3;j++) {
137 	      index=(GENREROWS*j)+i;
138 	      if(index <= MAXGENRE) {
139 	         printf("%3d %-21s ",alphagenreindex[index],typegenre[alphagenreindex[index]]);
140               }
141            }
142 	   printf("\n");
143 	}
144 }
145 
get_genre(char * genre)146 unsigned int get_genre (char *genre) {
147 	int num_genre=0;
148 
149         if(genre[0] == '\0') { return 255; }
150 
151 	sscanf(genre,"%u",&num_genre);
152 	if(num_genre == 0) {
153 		if(genre[0] != '0') {
154 			while(num_genre++ <= MAXGENRE) {
155 				if(!strcasecmp(genre,typegenre[num_genre-1])) {
156 					return num_genre-1;
157 				}
158 			}
159 			num_genre=256;
160 		}
161 	}
162 
163 	if(num_genre < 0 || num_genre > MAXGENRE) {
164 		num_genre=256;
165 	}
166 	return num_genre;
167 }
168 
text_genre(unsigned char * genre,char * buffer)169 void text_genre(unsigned char *genre,char *buffer) {
170    int genre_num = (int) genre[0];
171 
172    if(genre_num <= MAXGENRE) {
173 	sprintf(buffer,"%s",typegenre[genre_num]);
174    } else if(genre_num < 255) {
175 	sprintf(buffer,"(UNKNOWN) [%d]",genre_num);
176    } else {
177 	buffer[0]='\0';
178    }
179 }
180 
determine_tasks(char * format_string,int * want_id3,int * scantype,int * fullscan_vbr,int vbr_report)181 void determine_tasks (char *format_string,int *want_id3,int *scantype, int *fullscan_vbr,int vbr_report) {
182 
183 	char *format=format_string;
184 	char *percent;
185 	while((percent=strchr(format,'%'))) {
186 		percent++;
187 		while(*percent && (percent[0] != '%' && !isalpha(*percent))) percent++;
188 		switch (percent[0]) {
189 			case 't':
190 			case 'a':
191 			case 'l':
192 			case 'y':
193 			case 'c':
194 			case 'n':
195 			case 'g':
196 			case 'G': *want_id3=1; break;
197 			case 'm':
198 			case 's':
199 			case 'u':
200 			case 'S': *fullscan_vbr=1;
201 			case 'r': if(vbr_report != VBR_VARIABLE) *fullscan_vbr=1;
202 			case 'q':
203 			case 'Q':
204 			case 'e':
205 			case 'E':
206 			case 'C':
207 			case 'O':
208 			case 'v':
209 			case 'L':
210 			case 'p':
211 			case 'o': if(*scantype != SCAN_FULL)
212 					*scantype=SCAN_QUICK;
213 				  break;
214 			case 'b': *scantype=SCAN_FULL; break;
215 		}
216 		format=percent+1;
217 	}
218 }
219 
220 
format_output(char * format_string,mp3info * mp3,int vbr_report)221 void format_output (char *format_string,mp3info *mp3, int vbr_report) {
222 
223 	char genre[40]="";
224 	char mod[1000],*percent,*pos,*code;
225 	char *format=format_string;
226 	int modlen;
227 
228 	while((percent=strchr(format,'%'))) {
229 		*percent=0;
230 		printf(format);
231 		*percent='%';
232 		code=percent+1;
233 		while(*code && (code[0] != '%' && !isalpha(*code))) code++;
234 
235 		if(*code) {
236 			modlen=code-percent+1;
237 			if(modlen > 1000) {
238 				printf("Format modifier beginning at position %d too long!\n",(int)(percent-format));
239 				exit(5);
240 			}
241 			strncpy(mod,percent,modlen);
242 			mod[modlen]=0;
243 			mod[modlen-1]='s';
244 			switch (*code) {
245 				case 't': printf(mod,mp3->id3.title); break;
246 				case 'f': pos = (pos=strrchr(mp3->filename,'/')) ?
247 						pos+1 : mp3->filename;
248 					  printf(mod,pos); break;
249 				case 'F': printf(mod,mp3->filename); break;
250 				case 'a': printf(mod,mp3->id3.artist); break;
251 				case 'l': printf(mod,mp3->id3.album); break;
252 				case 'k': mod[modlen-1] = 'd'; printf(mod,mp3->datasize / 1024); break;
253 				case 'y': printf(mod,mp3->id3.year); break;
254 				case 'c': printf(mod,mp3->id3.comment); break;
255 				case 'n': if(mp3->id3_isvalid && mp3->id3.track[0]) {
256 						mod[modlen-1]='d';
257 					  	printf(mod, (int) mp3->id3.track[0]);
258 					  }
259 					  break;
260 				case 'g': if(mp3->id3_isvalid) {
261 						text_genre(mp3->id3.genre,genre);
262 					  	printf(mod,genre);
263 					  }
264 					  break;
265 				case 'G': if(mp3->id3_isvalid) {
266 						mod[modlen-1]='d';
267 					  	printf(mod,(int) mp3->id3.genre[0]);
268 					  }
269 					  break;
270 				case 'r': if(mp3->header_isvalid) {
271 						if(mp3->vbr && (vbr_report == VBR_VARIABLE))
272 							printf(mod,"Variable");
273 						else if(vbr_report == VBR_AVERAGE) {
274 							mod[modlen-1]='f';
275 							printf(mod,mp3->vbr_average);
276 						} else {
277 							mod[modlen-1]='d';
278 							printf(mod,header_bitrate(&mp3->header));
279 						}
280 					  }
281 					  break;
282 				case 'q': if(mp3->header_isvalid) {
283 						mod[modlen-1]='d';
284 						printf(mod,header_frequency(&mp3->header)/1000);
285 					  }
286 					  break;
287 				case 'Q': if(mp3->header_isvalid) {
288 						mod[modlen-1]='d';
289 						printf(mod,header_frequency(&mp3->header));
290 					  }
291 					  break;
292 				case 'e': if(mp3->header_isvalid) {
293 						printf(mod,header_emphasis(&mp3->header));
294 					  }
295 					  break;
296 				case 'E': if(mp3->header_isvalid) {
297 						printf(mod,!mp3->header.crc ? "Yes" : "No");
298 					  }
299 					  break;
300 				case 'C': if(mp3->header_isvalid) {
301 						printf(mod,mp3->header.copyright ? "Yes" : "No");
302 					  }
303 					  break;
304 				case 'O': if(mp3->header_isvalid) {
305 						printf(mod,mp3->header.original ? "Yes" : "No");
306 					  }
307 					  break;
308 				case 'm': if(mp3->header_isvalid) {
309 						mod[modlen-1]='d';
310 						printf(mod,mp3->seconds / 60);
311 					  }
312 					  break;
313 				case 's': if(mp3->header_isvalid) {
314 						mod[modlen-1]='d';
315 						printf(mod,mp3->seconds % 60);
316 					  }
317 					  break;
318 				case 'S': if(mp3->header_isvalid) {
319 						mod[modlen-1]='d';
320 						printf(mod,mp3->seconds);
321 					  }
322 					  break;
323 				case 'v': if(mp3->header_isvalid) {
324 						mod[modlen-1]='f';
325 						printf(mod,mp3->header.version ? ((mp3->header.version==2) ? 2.5 : 1.0) : 2.0);
326 					  }
327 					  break;
328 				case 'L': if(mp3->header_isvalid) {
329 						printf(mod,layer_text[header_layer(&mp3->header)-1]);
330 					  }
331 					  break;
332 				case 'o': if(mp3->header_isvalid) {
333 						printf(mod,header_mode(&mp3->header));
334 					  }
335 					  break;
336 				case 'p': if(mp3->header_isvalid) {
337 						printf(mod,mp3->header.padding ? "Yes" : "No");
338 					  }
339 					  break;
340 				case 'u': if(mp3->header_isvalid) {
341 						mod[modlen-1]='d';
342 						printf(mod,mp3->frames);
343 					  }
344 					  break;
345 				case 'b': if(mp3->header_isvalid) {
346 						mod[modlen-1]='d';
347 						printf(mod,mp3->badframes);
348 					  }
349 					  break;
350 			        case '%': printf("%%"); break;
351 				default:  printf("%%%c",*(code=percent+1)); break;
352 			}
353 			format=code+1;
354 		}
355 
356 	}
357 	printf(format);
358 }
359 
360 
361 
translate_escapes(char * string)362 void translate_escapes (char *string) {
363 	char *read=string;
364 	char *write=string;
365 	int val;
366 
367 	while(*read) {
368 		if(*read == '\\') {
369 			read++;
370 			switch (*read) {
371 				case 'n': *(write++)='\n'; read++; break;
372 				case 't': *(write++)='\t'; read++; break;
373 				case 'v': *(write++)='\v'; read++; break;
374 				case 'b': *(write++)='\b'; read++; break;
375 				case 'r': *(write++)='\r'; read++; break;
376 				case 'f': *(write++)='\f'; read++; break;
377 				case 'a': *(write++)='\a'; read++; break;
378 				case 'X':
379 				case 'x': read++; /* HEX */
380 					  val=0;
381 					  if(isxdigit(*read)) val=xtoi(read++);
382 					  if(isxdigit(*read)) val=(val*16) + xtoi(read++);
383 					  *(write++)=(val % 256);
384 				default:  if(*read <= '7' && *read >= '0') { /* octal */
385 						val=xtoi(read++);
386 						if(*read <= '7' && *read >= '0') val=(val*8) + xtoi(read++);
387 						if(*read <= '7' && *read >= '0') val=(val*8) + xtoi(read++);
388 						*(write++)=(val % 256);
389 					  } else {
390 					  	*(write++)=*(read++); break;
391 					  }
392 			}
393 		} else {
394 			*write++=*read++;
395 		}
396 
397 	}
398 	*write=0;
399 }
400 
401