xref: /illumos-gate/usr/src/cmd/audio/audiorecord/audiorecord.c (revision d8048045f75b60d97deaf376b39d437b027becd1)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Copyright (c) 2018, Joyent, Inc.
28  */
29 
30 /* Command-line audio record utility */
31 
32 #include <stdio.h>
33 #include <libgen.h>
34 #include <errno.h>
35 #include <ctype.h>
36 #include <math.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <string.h>
40 #include <strings.h>
41 #include <locale.h>
42 #include <fcntl.h>
43 #include <signal.h>
44 #include <limits.h>	/* All occurances of INT_MAX used to be ~0  (by MCA) */
45 #include <sys/types.h>
46 #include <sys/file.h>
47 #include <sys/stat.h>
48 #include <sys/param.h>
49 #include <stropts.h>
50 #include <poll.h>
51 #include <sys/ioctl.h>
52 #include <netinet/in.h>
53 
54 #include <libaudio.h>
55 #include <audio_device.h>
56 
57 #define	irint(d)	((int)d)
58 
59 /* localization stuff */
60 #define	MGET(s)		(char *)gettext(s)
61 
62 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
63 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
64 #endif
65 
66 #define	Error		(void) fprintf
67 
68 /* Local variables */
69 static char	*prog;
70 static char	prog_opts[] = "aft:v:d:i:e:s:c:T:?"; /* getopt() flags */
71 static char	*Stdout;
72 
73 /* XXX - the input buffer size should depend on sample_rate */
74 #define	AUDIO_BUFSIZ (1024 * 64)
75 static unsigned char	buf[AUDIO_BUFSIZ];
76 static char		swapBuf[AUDIO_BUFSIZ];	/* for byte swapping */
77 
78 
79 #define	MAX_GAIN		(100)	/* maximum gain */
80 
81 static char	*Info = NULL;		/* pointer to info data */
82 static unsigned	Ilen = 0;		/* length of info data */
83 static unsigned	Volume = INT_MAX;	/* record volume */
84 static double	Savevol;		/* saved  volume */
85 static unsigned	Sample_rate = 0;
86 static unsigned	Channels = 0;
87 static unsigned	Precision = 0;		/* based on encoding */
88 static unsigned	Encoding = 0;
89 
90 static int	NetEndian = TRUE;	/* endian nature of the machines */
91 
92 static int	Append = FALSE;		/* append to output file */
93 static int	Force = FALSE;		/* ignore rate differences on append */
94 static double	Time = -1.;		/* recording time */
95 static unsigned	Limit = AUDIO_UNKNOWN_SIZE;	/* recording limit */
96 static char	*Audio_dev = "/dev/audio";
97 
98 static int		Audio_fd = -1;
99 			/* file descriptor for audio device */
100 static Audio_hdr	Dev_hdr;		/* audio header for device */
101 static Audio_hdr	Save_hdr;		/* saved audio device header */
102 static char		*Ofile;			/* current filename */
103 static int		File_type = FILE_AU;	/* audio file type */
104 static int		File_type_set = FALSE;	/* file type specified as arg */
105 static Audio_hdr	File_hdr;		/* audio header for file */
106 static int		Cleanup = FALSE;	/* SIGINT sets this flag */
107 static unsigned		Size = 0;		/* Size of output file */
108 static unsigned		Oldsize = 0;
109 			/* Size of input file, if append */
110 
111 /* Global variables */
112 extern int optind;
113 extern char *optarg;
114 
115 /* Local Functions */
116 static void usage(void);
117 static void sigint(int sig);
118 static int parse_unsigned(char *str, unsigned *dst, char *flag);
119 static int parse_sample_rate(char *s, unsigned *rate);
120 
121 
122 static void
usage(void)123 usage(void)
124 {
125 	Error(stderr, MGET("Record an audio file -- usage:\n"
126 	    "\t%s [-af] [-v vol]\n"
127 	    "\t%.*s [-c channels] [-s rate] [-e encoding]\n"
128 	    "\t%.*s [-t time] [-i info] [-d dev] [-T au|wav|aif[f]] [file]\n"
129 	    "where:\n"
130 	    "\t-a\tAppend to output file\n"
131 	    "\t-f\tIgnore sample rate differences on append\n"
132 	    "\t-v\tSet record volume (0 - %d)\n"
133 	    "\t-c\tSpecify number of channels to record\n"
134 	    "\t-s\tSpecify rate in samples per second\n"
135 	    "\t-e\tSpecify encoding (ulaw | alaw | [u]linear | linear8 )\n"
136 	    "\t-t\tSpecify record time (hh:mm:ss.dd)\n"
137 	    "\t-i\tSpecify a file header information string\n"
138 	    "\t-d\tSpecify audio device (default: /dev/audio)\n"
139 	    "\t-T\tSpecify the audio file type (default: au)\n"
140 	    "\tfile\tRecord to named file\n"
141 	    "\t\tIf no file specified, write to stdout\n"
142 	    "\t\tDefault audio encoding is ulaw, 8khz, mono\n"
143 	    "\t\tIf -t is not specified, record until ^C\n"),
144 	    prog,
145 	    strlen(prog), "                    ",
146 	    strlen(prog), "                    ",
147 	    MAX_GAIN);
148 	exit(1);
149 }
150 
151 static void
sigint(int sig)152 sigint(int sig)
153 {
154 	/* If this is the first ^C, set a flag for the main loop */
155 	if (!Cleanup && (Audio_fd >= 0)) {
156 		/* flush input queues before exiting */
157 		Cleanup = TRUE;
158 		if (audio_pause_record(Audio_fd) == AUDIO_SUCCESS)
159 			return;
160 		Error(stderr, MGET("%s: could not flush input buffer\n"), prog);
161 	}
162 
163 	/* If double ^C, really quit */
164 	if (Audio_fd >= 0) {
165 		if (Volume != INT_MAX)
166 			(void) audio_set_record_gain(Audio_fd, &Savevol);
167 		if (audio_cmp_hdr(&Save_hdr, &Dev_hdr) != 0) {
168 			(void) audio_set_record_config(Audio_fd, &Save_hdr);
169 		}
170 	}
171 	exit(1);
172 }
173 
174 /*
175  * Record from the audio device to a file.
176  */
177 int
main(int argc,char ** argv)178 main(int argc, char **argv)
179 {
180 	int		i;
181 	int		cnt;
182 	int		err;
183 	int		file_type;
184 	int		ofd;
185 	int		swapBytes = FALSE;
186 	double		vol;
187 	struct stat	st;
188 	struct pollfd	pfd;
189 	char		*cp;
190 
191 	(void) setlocale(LC_ALL, "");
192 	(void) textdomain(TEXT_DOMAIN);
193 
194 	/* Get the program name */
195 	prog = strrchr(argv[0], '/');
196 	if (prog == NULL)
197 		prog = argv[0];
198 	else
199 		prog++;
200 	Stdout = MGET("(stdout)");
201 
202 	/* first check AUDIODEV environment for audio device name */
203 	if (cp = getenv("AUDIODEV")) {
204 		Audio_dev = cp;
205 	}
206 
207 	/* Set the endian nature of the machine */
208 	if ((ulong_t)1 != htonl((ulong_t)1)) {
209 		NetEndian = FALSE;
210 	}
211 
212 	err = 0;
213 	while ((i = getopt(argc, argv, prog_opts)) != EOF) {
214 		switch (i) {
215 		case 'v':
216 			if (parse_unsigned(optarg, &Volume, "-v")) {
217 				err++;
218 			} else if (Volume > MAX_GAIN) {
219 				Error(stderr, MGET("%s: invalid value for "
220 				"-v\n"), prog);
221 				err++;
222 			}
223 			break;
224 		case 't':
225 			Time = audio_str_to_secs(optarg);
226 			if ((Time == HUGE_VAL) || (Time < 0.)) {
227 				Error(stderr, MGET("%s: invalid value for "
228 				"-t\n"), prog);
229 				err++;
230 			}
231 			break;
232 		case 'd':
233 			Audio_dev = optarg;
234 			break;
235 		case 'f':
236 			Force = TRUE;
237 			break;
238 		case 'a':
239 			Append = TRUE;
240 			break;
241 		case 'i':
242 			Info = optarg;		/* set information string */
243 			Ilen = strlen(Info);
244 			break;
245 		case 's':
246 			if (parse_sample_rate(optarg, &Sample_rate)) {
247 				err++;
248 			}
249 			break;
250 		case 'c':
251 			if (strncmp(optarg, "mono", strlen(optarg)) == 0) {
252 				Channels = 1;
253 			} else if (strncmp(optarg, "stereo",
254 			    strlen(optarg)) == 0) {
255 				Channels = 2;
256 			} else if (parse_unsigned(optarg, &Channels, "-c")) {
257 				err++;
258 			} else if ((Channels != 1) && (Channels != 2)) {
259 				Error(stderr, "%s: invalid value for -c\n",
260 				    prog);
261 				err++;
262 			}
263 			break;
264 		case 'e':
265 			if (strncmp(optarg, "ulinear", strlen(optarg)) == 0) {
266 				Encoding = AUDIO_ENCODING_LINEAR8;
267 				Precision = 8;
268 			} else if (strncmp(optarg, "linear8",
269 			    strlen("linear8")) == 0) {
270 				Encoding = AUDIO_ENCODING_LINEAR;
271 				Precision = 8;
272 			} else if (strncmp(optarg, "ulaw",
273 			    strlen(optarg)) == 0) {
274 				Encoding = AUDIO_ENCODING_ULAW;
275 				Precision = 8;
276 			} else if (strncmp(optarg, "alaw",
277 			    strlen(optarg)) == 0) {
278 				Encoding = AUDIO_ENCODING_ALAW;
279 				Precision = 8;
280 			} else if ((strncmp(optarg, "linear",
281 			    strlen(optarg)) == 0) || (strncmp(optarg, "pcm",
282 			    strlen(optarg)) == 0)) {
283 				Encoding = AUDIO_ENCODING_LINEAR;
284 				Precision = 16;
285 			} else {
286 				Error(stderr, MGET("%s: invalid value for "
287 				    "-e\n"), prog);
288 				err++;
289 			}
290 			break;
291 		case 'T':
292 			if (strncmp(optarg, "au", strlen(optarg)) == 0) {
293 				File_type = FILE_AU;
294 			} else if (strncmp(optarg, "wav",
295 			    strlen(optarg)) == 0) {
296 				File_type = FILE_WAV;
297 			} else if (strncmp(optarg, "aif",
298 			    strlen(optarg)) == 0) {
299 				File_type = FILE_AIFF;
300 			} else if (strncmp(optarg, "aiff",
301 			    strlen(optarg)) == 0) {
302 				File_type = FILE_AIFF;
303 			} else {
304 				Error(stderr, MGET("%s: invalid value for "
305 				    "-T\n"), prog);
306 				err++;
307 			}
308 			File_type_set = TRUE;
309 			break;
310 		case '?':
311 			usage();
312 	/*NOTREACHED*/
313 		}
314 	}
315 	if (Append && (Info != NULL)) {
316 		Error(stderr, MGET("%s: cannot specify -a and -i\n"), prog);
317 		err++;
318 	}
319 	if (err > 0)
320 		exit(1);
321 
322 	argc -= optind;		/* update arg pointers */
323 	argv += optind;
324 
325 	/* Open the output file */
326 	if (argc <= 0) {
327 		Ofile = Stdout;
328 	} else {
329 		Ofile = *argv++;
330 		argc--;
331 
332 		/* Interpret "-" filename to mean stdout */
333 		if (strcmp(Ofile, "-") == 0)
334 			Ofile = Stdout;
335 
336 		/* if -T not set then we use the file suffix */
337 		if (File_type_set == FALSE) {
338 			char	*file_name;
339 			char	*start;
340 
341 			/* get the file name without the path */
342 			file_name = basename(Ofile);
343 
344 			/* get the true suffix */
345 			start = strrchr(file_name, '.');
346 
347 			/* if no '.' then there's no suffix */
348 			if (start) {
349 				/* is this a .au file? */
350 				if (strcasecmp(start, ".au") == 0) {
351 					File_type = FILE_AU;
352 				} else if (strcasecmp(start, ".wav") == 0) {
353 					File_type = FILE_WAV;
354 				} else if (strcasecmp(start, ".aif") == 0) {
355 					File_type = FILE_AIFF;
356 				} else if (strcasecmp(start, ".aiff") == 0) {
357 					File_type = FILE_AIFF;
358 				} else {
359 					/* the default is .au */
360 					File_type = FILE_AU;
361 				}
362 			} else {
363 				/* no suffix, so default to .au */
364 				File_type = FILE_AU;
365 			}
366 		}
367 	}
368 
369 	if (Ofile == Stdout) {
370 		ofd = fileno(stdout);
371 		Append = FALSE;
372 	} else {
373 		ofd = open(Ofile,
374 		    (O_RDWR | O_CREAT | (Append ? 0 : O_TRUNC)), 0666);
375 		if (ofd < 0) {
376 			Error(stderr, MGET("%s: cannot open "), prog);
377 			perror(Ofile);
378 			exit(1);
379 		}
380 		if (Append) {
381 			/*
382 			 * Check to make sure we're appending to an audio file.
383 			 * It must be a regular file (if zero-length, simply
384 			 * write it from scratch).  Also, its file header
385 			 * must match the input device configuration.
386 			 */
387 			if ((fstat(ofd, &st) < 0) || (!S_ISREG(st.st_mode))) {
388 				Error(stderr,
389 				    MGET("%s: %s is not a regular file\n"),
390 				    prog, Ofile);
391 				exit(1);
392 			}
393 			if (st.st_size == 0) {
394 				Append = FALSE;
395 				goto openinput;
396 			}
397 
398 			err = audio_read_filehdr(ofd, &File_hdr, &file_type,
399 			    (char *)NULL, 0);
400 
401 			if (err != AUDIO_SUCCESS) {
402 				Error(stderr,
403 				    MGET("%s: %s is not a valid audio file\n"),
404 				    prog, Ofile);
405 				exit(1);
406 			}
407 
408 			/* we need to make sure file types match */
409 			if (File_type_set == TRUE) {
410 				/* specified by the command line, must match */
411 				if (File_type != file_type) {
412 					Error(stderr,
413 					    MGET("%s: file types must match\n"),
414 					    prog);
415 					exit(1);
416 				}
417 			} else {
418 				/* not specified, so force */
419 				File_type = file_type;
420 			}
421 
422 			/*
423 			 * Set the format state to the format
424 			 * in the file header.
425 			 */
426 			Sample_rate = File_hdr.sample_rate;
427 			Channels = File_hdr.channels;
428 			Encoding = File_hdr.encoding;
429 			Precision = File_hdr.bytes_per_unit * 8;
430 
431 			/* make sure we support the encoding method */
432 			switch (Encoding) {
433 				case AUDIO_ENCODING_LINEAR8:
434 				case AUDIO_ENCODING_ULAW:
435 				case AUDIO_ENCODING_ALAW:
436 				case AUDIO_ENCODING_LINEAR:
437 					break;
438 				default: {
439 					char	msg[AUDIO_MAX_ENCODE_INFO];
440 					(void) audio_enc_to_str(&File_hdr, msg);
441 					Error(stderr,
442 					    MGET("%s: Append is not supported "
443 					    "for "), prog);
444 					Error(stderr,
445 					    MGET("this file encoding:\n\t"
446 					    "[%s]\n"), msg);
447 					exit(1);
448 					}
449 			}
450 
451 			/* Get the current size, if possible */
452 			Oldsize = File_hdr.data_size;
453 			if ((Oldsize == AUDIO_UNKNOWN_SIZE) &&
454 			    ((err = (int)lseek(ofd, 0L, SEEK_CUR)) >= 0)) {
455 				if (err < 0) {
456 					Error(stderr,
457 					    MGET("%s: %s is not a valid audio "
458 					    "file\n"), prog, Ofile);
459 					exit(1);
460 				}
461 				Oldsize = st.st_size - err;
462 			}
463 			/* Seek to end to start append */
464 			if ((int)lseek(ofd, st.st_size, SEEK_SET) < 0) {
465 				Error(stderr,
466 				    MGET("%s: cannot find end of %s\n"),
467 				    prog, Ofile);
468 				exit(1);
469 			}
470 		}
471 	}
472 openinput:
473 	/* Validate and open the audio device */
474 	err = stat(Audio_dev, &st);
475 	if (err < 0) {
476 		Error(stderr, MGET("%s: cannot open "), prog);
477 		perror(Audio_dev);
478 		exit(1);
479 	}
480 	if (!S_ISCHR(st.st_mode)) {
481 		Error(stderr, MGET("%s: %s is not an audio device\n"), prog,
482 		    Audio_dev);
483 		exit(1);
484 	}
485 
486 	/*
487 	 * For the mixer environment we need to open the audio device before
488 	 * the control device. If successful we pause right away to keep
489 	 * from queueing up a bunch of useless data.
490 	 */
491 	Audio_fd = open(Audio_dev, O_RDONLY | O_NONBLOCK);
492 	if (Audio_fd < 0) {
493 		if (errno == EBUSY) {
494 			Error(stderr, MGET("%s: %s is busy\n"),
495 			    prog, Audio_dev);
496 		} else {
497 			Error(stderr, MGET("%s: error opening "), prog);
498 			perror(Audio_dev);
499 		}
500 		exit(1);
501 	}
502 	if (audio_pause_record(Audio_fd) != AUDIO_SUCCESS) {
503 		Error(stderr, MGET("%s: not able to pause recording\n"), prog);
504 		exit(1);
505 	}
506 
507 	/* get the current settings */
508 	if (audio_get_record_config(Audio_fd, &Save_hdr) != AUDIO_SUCCESS) {
509 		(void) close(Audio_fd);
510 		Error(stderr, MGET("%s: %s is not an audio device\n"),
511 		    prog, Audio_dev);
512 		exit(1);
513 	}
514 	/* make a copy into the working data structure */
515 	bcopy(&Save_hdr, &Dev_hdr, sizeof (Save_hdr));
516 
517 	/* flush any queued audio data */
518 	if (audio_flush_record(Audio_fd) != AUDIO_SUCCESS) {
519 		Error(stderr, MGET("%s: not able to flush recording\n"), prog);
520 		exit(1);
521 	}
522 
523 	if (Sample_rate != 0) {
524 		Dev_hdr.sample_rate = Sample_rate;
525 	}
526 	if (Channels != 0) {
527 		Dev_hdr.channels = Channels;
528 	}
529 	if (Precision != 0) {
530 		Dev_hdr.bytes_per_unit = Precision / 8;
531 	}
532 	if (Encoding != 0) {
533 		Dev_hdr.encoding = Encoding;
534 	}
535 
536 	/*
537 	 * For .wav we always record 8-bit linear as unsigned. Thus we
538 	 * force unsigned linear to make life a lot easier on the user.
539 	 *
540 	 * For .aiff we set the default to 8-bit signed linear, not
541 	 * u-law, if Encoding isn't already set.
542 	 */
543 	if (File_type == FILE_WAV &&
544 	    Dev_hdr.encoding == AUDIO_ENCODING_LINEAR &&
545 	    Dev_hdr.bytes_per_unit == 1) {
546 		/* force to unsigned */
547 		Dev_hdr.encoding = AUDIO_ENCODING_LINEAR8;
548 	} else if (File_type == FILE_AIFF && Encoding == 0) {
549 		Dev_hdr.encoding = AUDIO_ENCODING_LINEAR;
550 		if (Precision == 0) {
551 			Dev_hdr.bytes_per_unit = AUDIO_PRECISION_8 / 8;
552 		}
553 	}
554 
555 	if (audio_set_record_config(Audio_fd, &Dev_hdr) != AUDIO_SUCCESS) {
556 		Error(stderr, MGET(
557 		    "%s: Audio format not supported by the audio device\n"),
558 		    prog);
559 		exit(1);
560 	}
561 
562 	if (audio_resume_record(Audio_fd) != AUDIO_SUCCESS) {
563 		Error(stderr, MGET("%s: not able to resume recording\n"), prog);
564 		exit(1);
565 	}
566 
567 	/* If appending to an existing file, check the configuration */
568 	if (Append) {
569 		char	msg[AUDIO_MAX_ENCODE_INFO];
570 
571 		switch (audio_cmp_hdr(&Dev_hdr, &File_hdr)) {
572 		case 0:			/* configuration matches */
573 			break;
574 		case 1:			/* all but sample rate matches */
575 			if (Force) {
576 				Error(stderr, MGET("%s: WARNING: appending "
577 				    "%.3fkHz data to %s (%.3fkHz)\n"), prog,
578 				    ((double)Dev_hdr.sample_rate / 1000.),
579 				    Ofile,
580 				    ((double)File_hdr.sample_rate / 1000.));
581 				break;
582 			}		/* if not -f, fall through */
583 			/* FALLTHROUGH */
584 		default:		/* encoding mismatch */
585 			(void) audio_enc_to_str(&Dev_hdr, msg);
586 			Error(stderr,
587 			    MGET("%s: device encoding [%s]\n"), prog, msg);
588 			(void) audio_enc_to_str(&File_hdr, msg);
589 			Error(stderr,
590 			    MGET("\tdoes not match file encoding [%s]\n"), msg);
591 			exit(1);
592 		}
593 	} else if (!isatty(ofd)) {
594 		if (audio_write_filehdr(ofd, &Dev_hdr, File_type, Info,
595 		    Ilen) != AUDIO_SUCCESS) {
596 			Error(stderr,
597 			    MGET("%s: error writing header for %s\n"), prog,
598 			    Ofile);
599 			exit(1);
600 		}
601 	}
602 
603 	/*
604 	 * 8-bit audio isn't a problem, however 16-bit audio is. If the file
605 	 * is an endian that is different from the machine then the bytes
606 	 * will need to be swapped.
607 	 *
608 	 * Note: The following if() could be simplified, but then it gets
609 	 * to be very hard to read. So it's left as is.
610 	 */
611 	if (Dev_hdr.bytes_per_unit == 2 &&
612 	    ((!NetEndian && File_type == FILE_AIFF) ||
613 	    (!NetEndian && File_type == FILE_AU) ||
614 	    (NetEndian && File_type == FILE_WAV))) {
615 		swapBytes = TRUE;
616 	}
617 
618 	/* If -v flag, set the record volume now */
619 	if (Volume != INT_MAX) {
620 		vol = (double)Volume / (double)MAX_GAIN;
621 		(void) audio_get_record_gain(Audio_fd, &Savevol);
622 		err = audio_set_record_gain(Audio_fd, &vol);
623 		if (err != AUDIO_SUCCESS) {
624 			Error(stderr,
625 			    MGET("%s: could not set record volume for %s\n"),
626 			    prog, Audio_dev);
627 			exit(1);
628 		}
629 	}
630 
631 	if (isatty(ofd)) {
632 		Error(stderr, MGET("%s: No files and stdout is a tty\n"),
633 		    prog);
634 		exit(1);
635 	}
636 
637 	/* Set up SIGINT handler so that final buffers may be flushed */
638 	(void) signal(SIGINT, sigint);
639 
640 	/*
641 	 * At this point, we're (finally) ready to copy the data.
642 	 * Init a poll() structure, to use when there's nothing to read.
643 	 */
644 	if (Time > 0)
645 		Limit = audio_secs_to_bytes(&Dev_hdr, Time);
646 	pfd.fd = Audio_fd;
647 	pfd.events = POLLIN;
648 	while ((Limit == AUDIO_UNKNOWN_SIZE) || (Limit != 0)) {
649 		/* Fill the buffer or read to the time limit */
650 		cnt = read(Audio_fd, (char *)buf,
651 		    ((Limit != AUDIO_UNKNOWN_SIZE) && (Limit < sizeof (buf)) ?
652 		    (int)Limit : sizeof (buf)));
653 
654 		if (cnt == 0)		/* normally, eof can't happen */
655 			break;
656 
657 		/* If error, probably have to wait for input */
658 		if (cnt < 0) {
659 			if (Cleanup)
660 				break;		/* done if ^C seen */
661 			switch (errno) {
662 			case EAGAIN:
663 				(void) poll(&pfd, 1L, -1);
664 				break;
665 			case EOVERFLOW:  /* Possibly a Large File */
666 				Error(stderr, MGET("%s: error reading"), prog);
667 				perror("Large File");
668 				exit(1);
669 			default:
670 				Error(stderr, MGET("%s: error reading"), prog);
671 				perror(Audio_dev);
672 				exit(1);
673 			}
674 			continue;
675 		}
676 
677 		/* Swab the output if required. */
678 		if (swapBytes) {
679 			swab((char *)buf, swapBuf, cnt);
680 			err = write(ofd, swapBuf, cnt);
681 		} else {
682 			err = write(ofd, (char *)buf, cnt);
683 		}
684 		if (err < 0) {
685 			Error(stderr, MGET("%s: error writing "), prog);
686 			perror(Ofile);
687 			exit(1);
688 		}
689 		if (err != cnt) {
690 			Error(stderr, MGET("%s: error writing "), prog);
691 			perror(Ofile);
692 			break;
693 		}
694 		Size += cnt;
695 		if (Limit != AUDIO_UNKNOWN_SIZE)
696 			Limit -= cnt;
697 	}
698 
699 	/* Attempt to rewrite the data_size field of the file header */
700 	if (!Append || (Oldsize != AUDIO_UNKNOWN_SIZE)) {
701 		if (Append)
702 			Size += Oldsize;
703 		(void) audio_rewrite_filesize(ofd, File_type, Size,
704 		    Dev_hdr.channels, Dev_hdr.bytes_per_unit);
705 	}
706 
707 	(void) close(ofd);			/* close input file */
708 
709 
710 	/* Check for error during record */
711 	if (audio_get_record_error(Audio_fd, (unsigned *)&err) != AUDIO_SUCCESS)
712 		Error(stderr, MGET("%s: error reading device status\n"), prog);
713 	else if (err)
714 		Error(stderr, MGET("%s: WARNING: Data overflow occurred\n"),
715 		    prog);
716 
717 	/* Reset record volume, encoding */
718 	if (Volume != INT_MAX)
719 		(void) audio_set_record_gain(Audio_fd, &Savevol);
720 	if (audio_cmp_hdr(&Save_hdr, &Dev_hdr) != 0) {
721 		(void) audio_set_record_config(Audio_fd, &Save_hdr);
722 	}
723 	(void) close(Audio_fd);
724 	return (0);
725 }
726 
727 /* Parse an unsigned integer */
728 static int
parse_unsigned(char * str,unsigned * dst,char * flag)729 parse_unsigned(char *str, unsigned *dst, char *flag)
730 {
731 	char		x;
732 
733 	if (sscanf(str, "%u%c", dst, &x) != 1) {
734 		Error(stderr, MGET("%s: invalid value for %s\n"), prog, flag);
735 		return (1);
736 	}
737 	return (0);
738 }
739 
740 /*
741  * set the sample rate. assume anything is ok. check later on to make sure
742  * the sample rate is valid.
743  */
744 static int
parse_sample_rate(char * s,unsigned * rate)745 parse_sample_rate(char *s, unsigned *rate)
746 {
747 	char		*cp;
748 	double		drate;
749 
750 	/*
751 	 * check if it's "cd" or "dat" or "voice". these also set
752 	 * the precision and encoding, etc.
753 	 */
754 	if (strcasecmp(s, "dat") == 0) {
755 		drate = 48000.0;
756 	} else if (strcasecmp(s, "cd") == 0) {
757 		drate = 44100.0;
758 	} else if (strcasecmp(s, "voice") == 0) {
759 		drate = 8000.0;
760 	} else {
761 		/* just do an atof */
762 		drate = atof(s);
763 
764 		/*
765 		 * if the first non-digit is a "k" multiply by 1000,
766 		 * if it's an "h", leave it alone. anything else,
767 		 * return an error.
768 		 */
769 
770 		/*
771 		 * XXX bug alert: could have multiple "." in string
772 		 * and mess things up.
773 		 */
774 		for (cp = s; *cp && (isdigit(*cp) || (*cp == '.')); cp++)
775 			/* NOP */;
776 		if (*cp != '\0') {
777 			if ((*cp == 'k') || (*cp == 'K')) {
778 				drate *= 1000.0;
779 			} else if ((*cp != 'h') && (*cp != 'H')) {
780 				/* bogus! */
781 				Error(stderr,
782 				    MGET("invalid sample rate: %s\n"), s);
783 				return (1);
784 			}
785 		}
786 
787 	}
788 
789 	*rate = irint(drate);
790 	return (0);
791 }
792