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