1 /* mode_len.c - len mode module 2 * Copyright (C) 2000-2009 Jason Jordan <shnutils@freeshell.org> 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 2 7 * of the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 */ 18 19 #include <string.h> 20 #include "mode.h" 21 22 CVSID("$Id: mode_len.c,v 1.90 2009/03/17 17:23:05 jason Exp $") 23 24 static bool len_main(int,char **); 25 static void len_help(void); 26 27 mode_module mode_len = { 28 "len", 29 "shnlen", 30 "Displays length, size and properties of PCM WAVE data", 31 CVSIDSTR, 32 FALSE, 33 len_main, 34 len_help 35 }; 36 37 #define LEN_OK "-" 38 #define LEN_NOT_APPLICABLE "x" 39 40 typedef enum { 41 LEVEL_UNKNOWN = -1, 42 LEVEL_BYTES, 43 LEVEL_KBYTES, 44 LEVEL_MBYTES, 45 LEVEL_GBYTES, 46 LEVEL_TBYTES 47 } len_levels; 48 49 static int totals_unit_level = LEVEL_UNKNOWN; 50 static int file_unit_level = LEVEL_UNKNOWN; 51 static int num_processed = 0; 52 static bool all_cd_quality = TRUE; 53 static bool suppress_column_names = FALSE; 54 static bool suppress_totals_line = FALSE; 55 56 static double total_size = 0.0; 57 static double total_data_size = 0.0; 58 static double total_disk_size = 0.0; 59 static double total_length = 0.0; 60 static double unit_divs[5] = {1.0, 1024.0, 1048576.0, 1073741824.0, 1099511627776.0}; 61 62 static char *units[5] = {"B ", "KB", "MB", "GB", "TB"}; 63 64 static void len_help() 65 { 66 st_info("Usage: %s [OPTIONS] [files]\n",st_progname()); 67 st_info("\n"); 68 st_info("Mode-specific options:\n"); 69 st_info("\n"); 70 st_info(" -U unit show total size in specified unit (*)\n"); 71 st_info(" -c suppress column names\n"); 72 st_info(" -h show this help screen\n"); 73 st_info(" -t suppress totals line\n"); 74 st_info(" -u unit show file sizes in specified unit (*)\n"); 75 st_info("\n"); 76 st_info(" (*) unit is one of: {[b], kb, mb, gb, tb}\n"); 77 st_info("\n"); 78 } 79 80 static int get_unit(char *unit) 81 { 82 if (!strcmp(optarg,"b")) 83 return LEVEL_BYTES; 84 85 if (!strcmp(optarg,"kb")) 86 return LEVEL_KBYTES; 87 88 if (!strcmp(optarg,"mb")) 89 return LEVEL_MBYTES; 90 91 if (!strcmp(optarg,"gb")) 92 return LEVEL_GBYTES; 93 94 if (!strcmp(optarg,"tb")) 95 return LEVEL_TBYTES; 96 97 return LEVEL_UNKNOWN; 98 } 99 100 static void parse(int argc,char **argv,int *first_arg) 101 { 102 int c; 103 104 file_unit_level = LEVEL_BYTES; 105 totals_unit_level = LEVEL_BYTES; 106 107 while ((c = st_getopt(argc,argv,"U:ctu:")) != -1) { 108 switch (c) { 109 case 'U': 110 if (NULL == optarg) 111 st_error("missing total size unit"); 112 totals_unit_level = get_unit(optarg); 113 if (LEVEL_UNKNOWN == totals_unit_level) 114 st_help("unknown total size unit: [%s]",optarg); 115 break; 116 case 'c': 117 suppress_column_names = TRUE; 118 break; 119 case 't': 120 suppress_totals_line = TRUE; 121 break; 122 case 'u': 123 if (NULL == optarg) 124 st_error("missing file size unit"); 125 file_unit_level = get_unit(optarg); 126 if (LEVEL_UNKNOWN == file_unit_level) 127 st_help("unknown file size unit: [%s]",optarg); 128 break; 129 } 130 } 131 132 *first_arg = optind; 133 } 134 135 static void show_len_banner() 136 { 137 if (suppress_column_names) 138 return; 139 140 st_output(" length expanded size cdr WAVE problems fmt ratio filename\n"); 141 } 142 143 static void print_formatted_length(wave_info *info) 144 { 145 int i,len; 146 147 len = strlen(info->m_ss); 148 149 if (PROB_NOT_CD(info)) { 150 for (i=0;i<13-len;i++) 151 st_output(" "); 152 st_output("%s",info->m_ss); 153 } 154 else { 155 for (i=0;i<12-len;i++) 156 st_output(" "); 157 st_output("%s ",info->m_ss); 158 } 159 } 160 161 static bool show_stats(wave_info *info) 162 { 163 wlong appended_bytes; 164 bool success; 165 166 success = FALSE; 167 168 print_formatted_length(info); 169 170 if (file_unit_level > 0) 171 st_output("%14.2f",(double)info->total_size / unit_divs[file_unit_level]); 172 else 173 st_output("%14lu",info->total_size); 174 175 st_output(" %s",units[file_unit_level]); 176 177 /* CD-R properties */ 178 179 st_output(" "); 180 181 if (PROB_NOT_CD(info)) 182 st_output("c%s%s",LEN_NOT_APPLICABLE,LEN_NOT_APPLICABLE); 183 else { 184 st_output("%s",LEN_OK); 185 if (PROB_BAD_BOUND(info)) 186 st_output("b"); 187 else 188 st_output("%s",LEN_OK); 189 190 if (PROB_TOO_SHORT(info)) 191 st_output("s"); 192 else 193 st_output("%s",LEN_OK); 194 } 195 196 /* WAVE properties */ 197 198 st_output(" "); 199 200 if (PROB_HDR_NOT_CANONICAL(info)) 201 st_output("h"); 202 else 203 st_output("%s",LEN_OK); 204 205 if (PROB_EXTRA_CHUNKS(info)) 206 st_output("e"); 207 else 208 st_output("%s",LEN_OK); 209 210 /* problems */ 211 212 st_output(" "); 213 214 if (info->file_has_id3v2_tag) 215 st_output("3"); 216 else 217 st_output("%s",LEN_OK); 218 219 if (PROB_DATA_NOT_ALIGNED(info)) 220 st_output("a"); 221 else 222 st_output("%s",LEN_OK); 223 224 if (PROB_HDR_INCONSISTENT(info)) 225 st_output("i"); 226 else 227 st_output("%s",LEN_OK); 228 229 if (!info->input_format->is_compressed && !info->input_format->is_translated) { 230 appended_bytes = info->actual_size - info->total_size - info->id3v2_tag_size; 231 232 if (PROB_TRUNCATED(info)) 233 st_output("t"); 234 else 235 st_output("%s",LEN_OK); 236 237 if (PROB_JUNK_APPENDED(info) && appended_bytes > 0) 238 st_output("j"); 239 else 240 st_output("%s",LEN_OK); 241 } 242 else 243 st_output("%s%s",LEN_NOT_APPLICABLE,LEN_NOT_APPLICABLE); 244 245 /* input file format */ 246 st_output(" %5s",info->input_format->name); 247 248 /* ratio */ 249 st_output(" %0.4f",(double)info->actual_size/(double)info->total_size); 250 251 st_output(" %s\n",info->filename); 252 253 success = TRUE; 254 255 return success; 256 } 257 258 static void show_totals_line() 259 { 260 wave_info *info; 261 wlong seconds; 262 double ratio; 263 264 if (suppress_totals_line) 265 return; 266 267 if (NULL == (info = new_wave_info(NULL))) 268 st_error("could not allocate memory for totals"); 269 270 if (all_cd_quality) { 271 /* Note to users from the year 2376: the m:ss.ff value on the totals line will only be 272 * correct as long as the total data size is less than 689 terabytes (2^32 * 176400 bytes). 273 * Hopefully, by then you'll all have 2048-bit processers to go with your petabyte keychain 274 * raid arrays, and this won't be an issue. 275 */ 276 277 /* calculate floor of total length in seconds */ 278 seconds = (wlong)(total_data_size / (double)CD_RATE); 279 280 /* since length_to_str() only considers (data_size % CD_RATE) when the file is CD-quality, 281 * we don't need to risk overflowing a 32-bit unsigned long with a double - we can cheat 282 * and just store the modulus. 283 */ 284 info->data_size = (wlong)(total_data_size - (double)(seconds * CD_RATE)); 285 286 info->length = seconds; 287 info->rate = CD_RATE; 288 } 289 else { 290 info->problems |= PROBLEM_NOT_CD_QUALITY; 291 info->length = (wlong)total_length; 292 } 293 294 info->exact_length = total_length; 295 296 length_to_str(info); 297 298 print_formatted_length(info); 299 300 ratio = (num_processed > 0) ? (total_disk_size / total_size) : 0.0; 301 302 total_size /= unit_divs[totals_unit_level]; 303 304 if (totals_unit_level > 0) 305 st_output("%14.2f",total_size); 306 else 307 st_output("%14.0f",total_size); 308 309 st_output(" %s %0.4f (%d file%s)\n", 310 units[totals_unit_level],ratio,num_processed,(1 != num_processed)?"s":""); 311 312 st_free(info); 313 } 314 315 static void update_totals(wave_info *info) 316 { 317 total_size += (double)info->total_size; 318 total_data_size += (double)info->data_size; 319 total_length += (double)info->data_size / (double)info->avg_bytes_per_sec; 320 321 if (PROB_NOT_CD(info)) 322 all_cd_quality = FALSE; 323 324 total_disk_size += (double)info->actual_size; 325 326 num_processed++; 327 } 328 329 static bool process_file(char *filename) 330 { 331 wave_info *info; 332 bool success; 333 334 if (NULL == (info = new_wave_info(filename))) 335 return FALSE; 336 337 if ((success = show_stats(info))) 338 update_totals(info); 339 340 st_free(info); 341 342 return success; 343 } 344 345 static bool process(int argc,char **argv,int start) 346 { 347 char *filename; 348 bool success; 349 350 success = TRUE; 351 352 show_len_banner(); 353 354 input_init(start,argc,argv); 355 356 while ((filename = input_get_filename())) { 357 success = (process_file(filename) && success); 358 } 359 360 show_totals_line(); 361 362 return success; 363 } 364 365 static bool len_main(int argc,char **argv) 366 { 367 int first_arg; 368 369 parse(argc,argv,&first_arg); 370 371 return process(argc,argv,first_arg); 372 } 373