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