1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright (c) 1993-2001 by Sun Microsystems, Inc.
24 * All rights reserved.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <stdarg.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/file.h>
37 #include <sys/param.h>
38
39 #include <convert.h>
40
41 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
42 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
43 #endif
44
45 const char *opt_string = "pf:o:i:FTD?";
46
47 char *Stdin;
48 char *Stdout;
49 char *Suffix = (char *)".AUDCVTMP";
50
51 char *progname; // program name
52 char *fake_argv[] = {(char *)"-", NULL}; // stdin with no args
53
54 extern char *optarg;
55 extern int optind;
56
57 int Statistics = 0;
58 int Debug = 0;
59
60 void init_header(AudioHdr&);
61 void usage();
62
main(int argc,char * argv[])63 main(
64 int argc,
65 char *argv[])
66 {
67 AudioUnixfile* ifp = NULL; // input & output audio objects
68 AudioUnixfile* ofp = NULL;
69 AudioHdr ihdr; // input/output headers
70 AudioHdr ohdr;
71 char *infile = NULL; // input/output file names
72 char *outfile = NULL;
73 char *realfile = NULL;
74 char *out_fmt = NULL; // output fmt string
75 AudioError err; // for error msgs
76 int c; // for getopt
77 int pflag = 0; // in place flag
78 int fflag = 0; // ignore header (force conversion)
79 int stdin_seen = 0; // already read stdin
80 int israw = 0; // once we've seen -i, it's raw data
81 format_type ofmt = F_SUN; // output format type
82 format_type ifmt = F_SUN; // expected input format type
83 format_type fmt = F_SUN; // actual input format type
84 off_t o_offset = 0; // output offset (ignored)
85 off_t i_offset = 0; // input offset
86 int i;
87 struct stat st;
88
89 setlocale(LC_ALL, "");
90 (void) textdomain(TEXT_DOMAIN);
91
92 // basename of program
93 if (progname = strrchr(argv[0], '/')) {
94 progname++;
95 } else {
96 progname = argv[0];
97 }
98 Stdin = MGET("(stdin)");
99 Stdout = MGET("(stdout)");
100
101 // init the input & output headers
102 init_header(ihdr);
103 init_header(ohdr);
104
105 // some conversions depend on invocation name. we'll create
106 // default input/output formats based on argv[0] that
107 // can be overridden by -o or -i options.
108 if (strcmp(progname, "ulaw2pcm") == 0) {
109 (void) parse_format((char *)"ulaw", ihdr, ifmt, i_offset);
110 (void) parse_format((char *)"pcm", ohdr, ofmt, o_offset);
111 } else if (strcmp(progname, "pcm2ulaw") == 0) {
112 (void) parse_format((char *)"pcm", ihdr, ifmt, i_offset);
113 (void) parse_format((char *)"ulaw", ohdr, ofmt, o_offset);
114 } else if (strcmp(progname, "adpcm_enc") == 0) {
115 (void) parse_format((char *)"ulaw", ihdr, ifmt, i_offset);
116 (void) parse_format((char *)"g721", ohdr, ofmt, o_offset);
117 } else if (strcmp(progname, "adpcm_dec") == 0) {
118 (void) parse_format((char *)"g721", ihdr, ifmt, i_offset);
119 (void) parse_format((char *)"ulaw", ohdr, ofmt, o_offset);
120 } else if (strcmp(progname, "raw2audio") == 0) {
121 (void) parse_format((char *)"ulaw", ihdr, ifmt, i_offset);
122 (void) parse_format((char *)"ulaw", ohdr, ofmt, o_offset);
123 israw++;
124 pflag++;
125 } else if (argc <= 1) {
126 // audioconvert with no arguments
127 usage();
128 }
129
130 // now parse the rest of the arg's
131 while ((c = getopt(argc, argv, opt_string)) != -1) {
132 switch (c) {
133 #ifdef DEBUG
134 case 'D':
135 // enable debug messages
136 Debug++;
137 break;
138 #endif
139 case 'p':
140 // convert files in place
141 if (outfile != NULL) {
142 Err(MGET("can't use -p with -o\n"));
143 exit(1);
144 }
145 pflag++;
146 break;
147 case 'F':
148 // force treatment of audio files as raw files
149 // (ignore filehdr).
150 fflag++;
151 break;
152 case 'f':
153 // save format string to parse later, but verify now
154 out_fmt = optarg;
155 if (parse_format(out_fmt, ohdr, ofmt, o_offset) == -1)
156 exit(1);
157 if (o_offset != 0) {
158 Err(MGET("can't specify an offset with -f\n"));
159 exit(1);
160 }
161 break;
162 case 'o':
163 if (pflag) {
164 Err(MGET("can't use -o with -p\n"));
165 exit(1);
166 }
167 outfile = optarg;
168 break;
169 case 'i':
170 // if bogus input header, exit ...
171 if (parse_format(optarg, ihdr, ifmt, i_offset) == -1) {
172 exit(1);
173 }
174 israw++;
175 break;
176 default:
177 case '?':
178 usage();
179 }
180 }
181
182 // XXX - should check argument consistency here....
183
184 // If no args left, we're taking input from stdin.
185 // In this case, make argv point to a fake argv with "-" as a file
186 // name, and set optind and argc apropriately so we'll go through
187 // the loop below once.
188 if (optind >= argc) {
189 argv = fake_argv;
190 argc = 1;
191 optind = 0;
192 /*
193 * XXX - we turn off pflag if stdin is the only input file.
194 * this is kind of a hack. if invoked as raw2audio, pflag
195 * it turned on. if no files are given, we want to turn
196 * it off, otherwise we'll complain about using -p with
197 * stdin, which won't make sense if invoked as raw2audio.
198 * instead, just silently ignore. the message is still given
199 * and stdin is ignored if it's specified as one of several
200 * input files.
201 */
202 pflag = 0;
203 }
204
205 // From this point on we're looking at file names or -i args
206 // for input format specs.
207 for (; optind < argc; optind++) {
208 // new input format spec.
209 if (strcmp(argv[optind], "-i") == 0) {
210 init_header(ihdr);
211 i_offset = 0;
212 ifmt = F_SUN;
213 // if bogus input header, exit ...
214 if (parse_format(argv[++optind], ihdr, ifmt, i_offset)
215 == -1) {
216 exit(1);
217 }
218 israw++;
219 } else if (strcmp(argv[optind], "-") == 0) {
220 // ignore stdin argument if in place
221 if (pflag) {
222 Err(MGET("can't use %s with -p flag\n"),
223 Stdin);
224 continue;
225 }
226
227 if (stdin_seen) {
228 Err(MGET("already used stdin for input\n"));
229 continue;
230 } else {
231 stdin_seen++;
232 }
233
234 infile = Stdin;
235 } else {
236 infile = argv[optind];
237 }
238
239 // if no audio object returned, just continue to the next
240 // file. if a fatal error occurs, open_input_file()
241 // will exit the program.
242 ifp =
243 open_input_file(infile, ihdr, israw, fflag, i_offset, fmt);
244 if (!ifp) {
245 continue;
246 }
247
248 if ((err = ifp->Open()) != AUDIO_SUCCESS) {
249 Err(MGET("open error on input file %s - %s\n"),
250 infile, err.msg());
251 exit(1);
252 }
253 ifp->Reference();
254
255 // create the output file if not created yet, or if
256 // converting in place. ofp will be NULL only the first
257 // time through. use the header of the first input file
258 // to base the output format on - then create the output
259 // header w/the output format spec.
260 if ((ofp == NULL) && !pflag) {
261
262 ohdr = ifp->GetHeader();
263 ohdr = ifp->GetHeader();
264 ofmt = ifmt;
265 // just use input hdr if no output hdr spec
266 if (out_fmt) {
267 if (parse_format(out_fmt, ohdr, ofmt, o_offset)
268 == -1) {
269 exit(1);
270 }
271 }
272
273 // need to check before output is opened ...
274 if (verify_conversion(ifp->GetHeader(), ohdr) == -1) {
275 // XXX - bomb out or skip?
276 exit(3);
277 }
278
279 // Create the file and set the info string.
280 char *infoString;
281 int infoStringLen;
282 infoString = ifp->GetInfostring(infoStringLen);
283 ofp = create_output_file(outfile, ohdr, ofmt,
284 infoString);
285
286 } else if (pflag) {
287
288 // create new output header based on each input file
289 ohdr = ifp->GetHeader();
290 ofmt = ifmt;
291 // just use input hdr if no output hdr spec
292 if (out_fmt) {
293 if (parse_format(out_fmt, ohdr, ofmt, o_offset)
294 == -1) {
295 exit(1);
296 }
297 }
298
299 // get the *real* path of the infile (follow sym-links),
300 // and the stat info.
301 realfile = infile;
302 get_realfile(realfile, &st);
303
304 // if the file is read-only, give up
305 if (access(realfile, W_OK)) {
306 // XXX - do we really want to exit?
307 perror(infile);
308 Err(MGET("cannot rewrite in place\n"));
309 exit(1);
310 }
311
312 // this is now the output file.
313 i = strlen(realfile) + strlen(Suffix) + 1;
314 outfile = (char *)malloc((unsigned)i);
315 if (outfile == NULL) {
316 Err(MGET("out of memory\n"));
317 exit(1);
318 }
319 (void) sprintf(outfile, "%s%s", realfile, Suffix);
320
321 // outfile will get re-assigned to a tmp file
322 if (verify_conversion(ifp->GetHeader(), ohdr) == -1) {
323 // XXX - bomb out or skip?
324 exit(3);
325 }
326
327 // If no conversion, just skip the file
328 if (noop_conversion(ifp->GetHeader(), ohdr,
329 fmt, ofmt, i_offset, o_offset)) {
330 if (Debug)
331 Err(MGET(
332 "%s: no-op conversion...skipping\n"),
333 infile);
334 continue;
335 }
336
337 // Get the input info string.
338 char *infoString;
339 int infoStringLen;
340 infoString = ifp->GetInfostring(infoStringLen);
341 ofp = create_output_file(outfile, ohdr, ofmt,
342 infoString);
343 }
344
345 // verify that it's a valid conversion by looking at the
346 // file headers. (this will be called twice for the first
347 // file if *not* converting in place. that's ok....
348 if (!pflag && (verify_conversion(ifp->GetHeader(), ohdr)
349 == -1)) {
350 // XXX - bomb out or skip file if invalid conversion?
351 exit(3);
352 }
353
354 // do the conversion, if error, bomb out
355 if (do_convert(ifp, ofp) == -1) {
356 exit(4);
357 }
358
359 ifp->Close();
360 ifp->Dereference();
361
362 // if in place, finish up by renaming the outfile to
363 // back to the infile.
364 if (pflag) {
365 delete(ofp); // will close and deref, etc.
366
367 if (rename(outfile, realfile) < 0) {
368 perror(outfile);
369 Err(MGET("error renaming %s to %s"),
370 outfile, realfile);
371 exit(1);
372 }
373 /* Set the permissions to match the original */
374 if (chmod(realfile, (int)st.st_mode) < 0) {
375 Err(MGET("WARNING: could not reset mode of"));
376 perror(realfile);
377 }
378 }
379 }
380
381 if (!pflag) {
382 delete(ofp); // close output file
383 }
384
385 exit(0); // outta here!
386 }
387
388
389 // initialize audio hdr to default val's
390 void
init_header(AudioHdr & hdr)391 init_header(
392 AudioHdr& hdr)
393 {
394 hdr.encoding = NONE;
395 hdr.sample_rate = 0;
396 hdr.samples_per_unit = 0;
397 hdr.bytes_per_unit = 0;
398 hdr.channels = 0;
399 }
400
401 extern "C" { void _doprnt(char *, ...); }
402
403 // report a fatal error and exit
404 void
Err(char * format,...)405 Err(char *format, ...)
406 {
407 va_list ap;
408
409 va_start(ap, format);
410 fprintf(stderr, "%s: ", progname);
411 _doprnt(format, ap, stderr);
412 fflush(stderr);
413 va_end(ap);
414 }
415
416 void
usage()417 usage()
418 {
419 fprintf(stderr, MGET(
420 "Convert between audio file formats and data encodings -- usage:\n"
421 "\t%s [-pF] [-f outfmt] [-o outfile] [[-i infmt] [file ...]] ...\n"
422 "where:\n"
423 "\t-p\tConvert files in place\n"
424 "\t-F\tForce interpretation of -i (ignore existing file hdr)\n"
425 "\t-f\tOutput format description\n"
426 "\t-o\tOutput file (default: stdout)\n"
427 "\t-i\tInput format description\n"
428 "\tfile\tList of files to convert (default: stdin)\n\n"
429 "Format Description:\n"
430 "\tkeyword=value[,keyword=value...]\n"
431 "where:\n"
432 "\tKeywords:\tValues:\n"
433 "\trate\t\tSample Rate in samples/second\n"
434 "\tchannels\tNumber of interleaved channels\n"
435 "\tencoding\tAudio encoding. One of:\n"
436 "\t\t\t ulaw, alaw, g721, g723,\n"
437 "\t\t\t linear8, linear16, linear32\n"
438 "\t\t\t pcm (same as linear16)\n"
439 "\t\t\t voice (ulaw,mono,rate=8k)\n"
440 "\t\t\t cd (linear16,stereo,rate=44.1k)\n"
441 "\t\t\t dat (linear16,stereo,rate=48k)\n"
442 "\tformat\t\tFile format. One of:\n"
443 "\t\t\t sun, raw (no format)\n"
444 "\toffset\t\tByte offset (raw input only)\n"),
445 progname);
446 exit(1);
447 }
448