Home | History | Annotate | Line # | Download | only in record
record.c revision 1.55
      1  1.55  riastrad /*	$NetBSD: record.c,v 1.55 2021/06/01 21:08:48 riastradh Exp $	*/
      2   1.2       mrg 
      3   1.2       mrg /*
      4  1.48       mrg  * Copyright (c) 1999, 2002, 2003, 2005, 2010 Matthew R. Green
      5   1.2       mrg  * All rights reserved.
      6   1.2       mrg  *
      7   1.2       mrg  * Redistribution and use in source and binary forms, with or without
      8   1.2       mrg  * modification, are permitted provided that the following conditions
      9   1.2       mrg  * are met:
     10   1.2       mrg  * 1. Redistributions of source code must retain the above copyright
     11   1.2       mrg  *    notice, this list of conditions and the following disclaimer.
     12   1.2       mrg  * 2. Redistributions in binary form must reproduce the above copyright
     13   1.2       mrg  *    notice, this list of conditions and the following disclaimer in the
     14   1.2       mrg  *    documentation and/or other materials provided with the distribution.
     15   1.2       mrg  *
     16   1.2       mrg  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17   1.2       mrg  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18   1.2       mrg  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19   1.2       mrg  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20   1.2       mrg  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21   1.2       mrg  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     22   1.2       mrg  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23   1.2       mrg  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24   1.2       mrg  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25   1.2       mrg  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26   1.2       mrg  * SUCH DAMAGE.
     27   1.2       mrg  */
     28   1.2       mrg 
     29   1.1       mrg /*
     30   1.1       mrg  * SunOS compatible audiorecord(1)
     31   1.1       mrg  */
     32  1.33       agc #include <sys/cdefs.h>
     33  1.33       agc 
     34  1.33       agc #ifndef lint
     35  1.55  riastrad __RCSID("$NetBSD: record.c,v 1.55 2021/06/01 21:08:48 riastradh Exp $");
     36  1.33       agc #endif
     37  1.33       agc 
     38   1.1       mrg 
     39  1.48       mrg #include <sys/param.h>
     40   1.1       mrg #include <sys/audioio.h>
     41   1.1       mrg #include <sys/ioctl.h>
     42   1.1       mrg #include <sys/time.h>
     43   1.1       mrg #include <sys/uio.h>
     44   1.1       mrg 
     45   1.1       mrg #include <err.h>
     46   1.1       mrg #include <fcntl.h>
     47   1.1       mrg #include <paths.h>
     48   1.1       mrg #include <signal.h>
     49   1.1       mrg #include <stdio.h>
     50   1.1       mrg #include <stdlib.h>
     51   1.1       mrg #include <string.h>
     52   1.1       mrg #include <unistd.h>
     53  1.44     lukem #include <util.h>
     54   1.1       mrg 
     55   1.1       mrg #include "libaudio.h"
     56  1.19       mrg #include "auconv.h"
     57   1.1       mrg 
     58  1.51     joerg static audio_info_t info, oinfo;
     59  1.51     joerg static const char *device;
     60  1.53       mrg static int	audiofd;
     61  1.53       mrg static int	aflag, fflag;
     62   1.1       mrg int	verbose;
     63  1.51     joerg static int	monitor_gain, omonitor_gain;
     64  1.51     joerg static int	gain;
     65  1.51     joerg static int	balance;
     66  1.51     joerg static int	port;
     67  1.51     joerg static char	*encoding_str;
     68  1.54       mrg static struct track_info ti;
     69  1.51     joerg static struct timeval record_time;
     70  1.51     joerg static struct timeval start_time;
     71  1.51     joerg 
     72  1.51     joerg static void (*conv_func) (u_char *, int);
     73  1.51     joerg 
     74  1.51     joerg static void usage (void) __dead;
     75  1.51     joerg static int timeleft (struct timeval *, struct timeval *);
     76  1.51     joerg static void cleanup (int) __dead;
     77  1.51     joerg static void rewrite_header (void);
     78   1.1       mrg 
     79   1.1       mrg int
     80  1.51     joerg main(int argc, char *argv[])
     81   1.1       mrg {
     82  1.43       mrg 	u_char	*buffer;
     83  1.48       mrg 	size_t	len, bufsize = 0;
     84  1.55  riastrad 	ssize_t	nread;
     85   1.8       mrg 	int	ch, no_time_limit = 1;
     86  1.24  augustss 	const char *defdevice = _PATH_SOUND;
     87   1.1       mrg 
     88  1.53       mrg 	/*
     89  1.54       mrg 	 * Initialise the track_info.
     90  1.53       mrg 	 */
     91  1.54       mrg 	ti.format = AUDIO_FORMAT_DEFAULT;
     92  1.54       mrg 	ti.total_size = -1;
     93  1.53       mrg 
     94  1.48       mrg 	while ((ch = getopt(argc, argv, "ab:B:C:F:c:d:e:fhi:m:P:p:qt:s:Vv:")) != -1) {
     95   1.1       mrg 		switch (ch) {
     96   1.1       mrg 		case 'a':
     97   1.1       mrg 			aflag++;
     98   1.1       mrg 			break;
     99   1.1       mrg 		case 'b':
    100   1.1       mrg 			decode_int(optarg, &balance);
    101   1.1       mrg 			if (balance < 0 || balance > 63)
    102  1.30     grant 				errx(1, "balance must be between 0 and 63");
    103   1.1       mrg 			break;
    104  1.48       mrg 		case 'B':
    105  1.48       mrg 			bufsize = strsuftoll("read buffer size", optarg,
    106  1.48       mrg 					     1, UINT_MAX);
    107  1.48       mrg 			break;
    108   1.1       mrg 		case 'C':
    109  1.23  jdolecek 			/* Ignore, compatibility */
    110   1.1       mrg 			break;
    111  1.17       mrg 		case 'F':
    112  1.54       mrg 			ti.format = audio_format_from_str(optarg);
    113  1.54       mrg 			if (ti.format < 0)
    114  1.27       mrg 				errx(1, "Unknown audio format; supported "
    115  1.27       mrg 				    "formats: \"sun\", \"wav\", and \"none\"");
    116  1.17       mrg 			break;
    117   1.1       mrg 		case 'c':
    118  1.54       mrg 			decode_int(optarg, &ti.channels);
    119  1.54       mrg 			if (ti.channels < 0 || ti.channels > 16)
    120  1.30     grant 				errx(1, "channels must be between 0 and 16");
    121   1.1       mrg 			break;
    122   1.1       mrg 		case 'd':
    123   1.1       mrg 			device = optarg;
    124   1.1       mrg 			break;
    125   1.1       mrg 		case 'e':
    126   1.1       mrg 			encoding_str = optarg;
    127   1.1       mrg 			break;
    128   1.1       mrg 		case 'f':
    129   1.1       mrg 			fflag++;
    130   1.1       mrg 			break;
    131   1.1       mrg 		case 'i':
    132  1.54       mrg 			ti.header_info = optarg;
    133   1.1       mrg 			break;
    134   1.1       mrg 		case 'm':
    135  1.17       mrg 			decode_int(optarg, &monitor_gain);
    136  1.17       mrg 			if (monitor_gain < 0 || monitor_gain > 255)
    137  1.30     grant 				errx(1, "monitor volume must be between 0 and 255");
    138   1.1       mrg 			break;
    139   1.1       mrg 		case 'P':
    140  1.54       mrg 			decode_int(optarg, &ti.precision);
    141  1.54       mrg 			if (ti.precision != 4 && ti.precision != 8 &&
    142  1.54       mrg 			    ti.precision != 16 && ti.precision != 24 &&
    143  1.54       mrg 			    ti.precision != 32)
    144  1.15   minoura 				errx(1, "precision must be between 4, 8, 16, 24 or 32");
    145   1.1       mrg 			break;
    146   1.1       mrg 		case 'p':
    147   1.1       mrg 			len = strlen(optarg);
    148   1.1       mrg 
    149   1.1       mrg 			if (strncmp(optarg, "mic", len) == 0)
    150   1.1       mrg 				port |= AUDIO_MICROPHONE;
    151   1.1       mrg 			else if (strncmp(optarg, "cd", len) == 0 ||
    152   1.1       mrg 			           strncmp(optarg, "internal-cd", len) == 0)
    153   1.1       mrg 				port |= AUDIO_CD;
    154   1.1       mrg 			else if (strncmp(optarg, "line", len) == 0)
    155   1.1       mrg 				port |= AUDIO_LINE_IN;
    156   1.1       mrg 			else
    157   1.1       mrg 				errx(1,
    158   1.1       mrg 			    "port must be `cd', `internal-cd', `mic', or `line'");
    159   1.1       mrg 			break;
    160   1.1       mrg 		case 'q':
    161  1.54       mrg 			ti.qflag++;
    162   1.1       mrg 			break;
    163   1.1       mrg 		case 's':
    164  1.54       mrg 			decode_int(optarg, &ti.sample_rate);
    165  1.54       mrg 			if (ti.sample_rate < 0 || ti.sample_rate > 48000 * 2)	/* XXX */
    166  1.30     grant 				errx(1, "sample rate must be between 0 and 96000");
    167   1.1       mrg 			break;
    168   1.1       mrg 		case 't':
    169   1.8       mrg 			no_time_limit = 0;
    170   1.1       mrg 			decode_time(optarg, &record_time);
    171   1.1       mrg 			break;
    172   1.1       mrg 		case 'V':
    173   1.1       mrg 			verbose++;
    174   1.1       mrg 			break;
    175   1.1       mrg 		case 'v':
    176  1.17       mrg 			decode_int(optarg, &gain);
    177  1.17       mrg 			if (gain < 0 || gain > 255)
    178  1.30     grant 				errx(1, "volume must be between 0 and 255");
    179   1.1       mrg 			break;
    180   1.1       mrg 		/* case 'h': */
    181   1.1       mrg 		default:
    182   1.1       mrg 			usage();
    183   1.1       mrg 			/* NOTREACHED */
    184   1.1       mrg 		}
    185   1.1       mrg 	}
    186   1.1       mrg 	argc -= optind;
    187   1.1       mrg 	argv += optind;
    188   1.1       mrg 
    189  1.39       mrg 	if (argc != 1)
    190  1.39       mrg 		usage();
    191  1.39       mrg 
    192   1.1       mrg 	/*
    193  1.40       mrg 	 * convert the encoding string into a value.
    194  1.40       mrg 	 */
    195  1.40       mrg 	if (encoding_str) {
    196  1.54       mrg 		ti.encoding = audio_enc_to_val(encoding_str);
    197  1.54       mrg 		if (ti.encoding == -1)
    198  1.40       mrg 			errx(1, "unknown encoding, bailing...");
    199  1.40       mrg 	}
    200  1.40       mrg 
    201  1.40       mrg 	/*
    202  1.40       mrg 	 * open the output file
    203  1.40       mrg 	 */
    204  1.42      gson 	if (argv[0][0] != '-' || argv[0][1] != '\0') {
    205  1.40       mrg 		/* intuit the file type from the name */
    206  1.54       mrg 		if (ti.format == AUDIO_FORMAT_DEFAULT)
    207  1.40       mrg 		{
    208  1.40       mrg 			size_t flen = strlen(*argv);
    209  1.40       mrg 			const char *arg = *argv;
    210  1.40       mrg 
    211  1.40       mrg 			if (strcasecmp(arg + flen - 3, ".au") == 0)
    212  1.54       mrg 				ti.format = AUDIO_FORMAT_SUN;
    213  1.40       mrg 			else if (strcasecmp(arg + flen - 4, ".wav") == 0)
    214  1.54       mrg 				ti.format = AUDIO_FORMAT_WAV;
    215  1.40       mrg 		}
    216  1.54       mrg 		ti.outfd = open(*argv, O_CREAT|(aflag ? O_APPEND : O_TRUNC)|O_WRONLY, 0666);
    217  1.54       mrg 		if (ti.outfd < 0)
    218  1.40       mrg 			err(1, "could not open %s", *argv);
    219  1.40       mrg 	} else
    220  1.54       mrg 		ti.outfd = STDOUT_FILENO;
    221  1.40       mrg 
    222  1.40       mrg 	/*
    223  1.34       mrg 	 * open the audio device
    224   1.1       mrg 	 */
    225   1.6    kleink 	if (device == NULL && (device = getenv("AUDIODEVICE")) == NULL &&
    226   1.6    kleink 	    (device = getenv("AUDIODEV")) == NULL) /* Sun compatibility */
    227  1.24  augustss 		device = defdevice;
    228   1.1       mrg 
    229   1.1       mrg 	audiofd = open(device, O_RDONLY);
    230  1.24  augustss 	if (audiofd < 0 && device == defdevice) {
    231  1.22  augustss 		device = _PATH_SOUND0;
    232  1.28       uwe 		audiofd = open(device, O_RDONLY);
    233  1.22  augustss 	}
    234   1.1       mrg 	if (audiofd < 0)
    235   1.1       mrg 		err(1, "failed to open %s", device);
    236   1.1       mrg 
    237   1.1       mrg 	/*
    238   1.1       mrg 	 * work out the buffer size to use, and allocate it.  also work out
    239   1.1       mrg 	 * what the old monitor gain value is, so that we can reset it later.
    240   1.1       mrg 	 */
    241  1.23  jdolecek 	if (ioctl(audiofd, AUDIO_GETINFO, &oinfo) < 0)
    242   1.1       mrg 		err(1, "failed to get audio info");
    243  1.49  jmcneill 	if (bufsize == 0) {
    244  1.48       mrg 		bufsize = oinfo.record.buffer_size;
    245  1.49  jmcneill 		if (bufsize < 32 * 1024)
    246  1.49  jmcneill 			bufsize = 32 * 1024;
    247  1.49  jmcneill 	}
    248  1.17       mrg 	omonitor_gain = oinfo.monitor_gain;
    249   1.1       mrg 
    250   1.1       mrg 	buffer = malloc(bufsize);
    251   1.1       mrg 	if (buffer == NULL)
    252   1.1       mrg 		err(1, "couldn't malloc buffer of %d size", (int)bufsize);
    253   1.1       mrg 
    254   1.1       mrg 	/*
    255  1.17       mrg 	 * set up audio device for recording with the speified parameters
    256  1.17       mrg 	 */
    257  1.17       mrg 	AUDIO_INITINFO(&info);
    258  1.17       mrg 
    259  1.17       mrg 	/*
    260  1.17       mrg 	 * for these, get the current values for stuffing into the header
    261  1.17       mrg 	 */
    262  1.53       mrg #define SETINFO2(x, y)	if (x) \
    263  1.53       mrg 				info.record.y = x; \
    264  1.40       mrg 			else \
    265  1.53       mrg 				info.record.y = x = oinfo.record.y;
    266  1.54       mrg #define SETINFO(x)	SETINFO2(ti.x, x)
    267  1.53       mrg 
    268  1.40       mrg 	SETINFO (sample_rate)
    269  1.40       mrg 	SETINFO (channels)
    270  1.40       mrg 	SETINFO (precision)
    271  1.40       mrg 	SETINFO (encoding)
    272  1.53       mrg 	SETINFO2 (gain, gain)
    273  1.53       mrg 	SETINFO2 (port, port)
    274  1.53       mrg 	SETINFO2 (balance, balance)
    275  1.17       mrg #undef SETINFO
    276  1.53       mrg #undef SETINFO2
    277  1.17       mrg 
    278  1.17       mrg 	if (monitor_gain)
    279  1.17       mrg 		info.monitor_gain = monitor_gain;
    280  1.17       mrg 	else
    281  1.17       mrg 		monitor_gain = oinfo.monitor_gain;
    282   1.1       mrg 
    283   1.1       mrg 	info.mode = AUMODE_RECORD;
    284  1.23  jdolecek 	if (ioctl(audiofd, AUDIO_SETINFO, &info) < 0)
    285  1.29       mrg 		err(1, "failed to set audio info");
    286   1.1       mrg 
    287   1.1       mrg 	signal(SIGINT, cleanup);
    288  1.53       mrg 
    289  1.54       mrg 	ti.total_size = 0;
    290  1.53       mrg 
    291  1.54       mrg 	write_header(&ti);
    292  1.54       mrg 	if (ti.format == AUDIO_FORMAT_NONE)
    293  1.53       mrg 		errx(1, "unable to determine audio format");
    294  1.54       mrg 	conv_func = write_get_conv_func(&ti);
    295   1.1       mrg 
    296  1.27       mrg 	if (verbose && conv_func) {
    297  1.27       mrg 		const char *s = NULL;
    298  1.27       mrg 
    299  1.27       mrg 		if (conv_func == swap_bytes)
    300  1.27       mrg 			s = "swap bytes (16 bit)";
    301  1.27       mrg 		else if (conv_func == swap_bytes32)
    302  1.27       mrg 			s = "swap bytes (32 bit)";
    303  1.27       mrg 		else if (conv_func == change_sign16_be)
    304  1.27       mrg 			s = "change sign (big-endian, 16 bit)";
    305  1.27       mrg 		else if (conv_func == change_sign16_le)
    306  1.27       mrg 			s = "change sign (little-endian, 16 bit)";
    307  1.27       mrg 		else if (conv_func == change_sign32_be)
    308  1.27       mrg 			s = "change sign (big-endian, 32 bit)";
    309  1.27       mrg 		else if (conv_func == change_sign32_le)
    310  1.27       mrg 			s = "change sign (little-endian, 32 bit)";
    311  1.27       mrg 		else if (conv_func == change_sign16_swap_bytes_be)
    312  1.27       mrg 			s = "change sign & swap bytes (big-endian, 16 bit)";
    313  1.27       mrg 		else if (conv_func == change_sign16_swap_bytes_le)
    314  1.27       mrg 			s = "change sign & swap bytes (little-endian, 16 bit)";
    315  1.27       mrg 		else if (conv_func == change_sign32_swap_bytes_be)
    316  1.27       mrg 			s = "change sign (big-endian, 32 bit)";
    317  1.27       mrg 		else if (conv_func == change_sign32_swap_bytes_le)
    318  1.27       mrg 			s = "change sign & swap bytes (little-endian, 32 bit)";
    319  1.27       mrg 
    320  1.27       mrg 		if (s)
    321  1.27       mrg 			fprintf(stderr, "%s: converting, using function: %s\n",
    322  1.27       mrg 			    getprogname(), s);
    323  1.27       mrg 		else
    324  1.27       mrg 			fprintf(stderr, "%s: using unnamed conversion "
    325  1.27       mrg 					"function\n", getprogname());
    326  1.27       mrg 	}
    327  1.27       mrg 
    328  1.27       mrg 	if (verbose)
    329  1.27       mrg 		fprintf(stderr,
    330  1.27       mrg 		   "sample_rate=%d channels=%d precision=%d encoding=%s\n",
    331  1.27       mrg 		   info.record.sample_rate, info.record.channels,
    332  1.27       mrg 		   info.record.precision,
    333  1.27       mrg 		   audio_enc_from_val(info.record.encoding));
    334  1.32       mrg 
    335  1.32       mrg 	if (!no_time_limit && verbose)
    336  1.32       mrg 		fprintf(stderr, "recording for %lu seconds, %lu microseconds\n",
    337  1.32       mrg 		    (u_long)record_time.tv_sec, (u_long)record_time.tv_usec);
    338  1.27       mrg 
    339   1.1       mrg 	(void)gettimeofday(&start_time, NULL);
    340   1.8       mrg 	while (no_time_limit || timeleft(&start_time, &record_time)) {
    341  1.55  riastrad 		if ((nread = read(audiofd, buffer, bufsize)) == -1)
    342   1.1       mrg 			err(1, "read failed");
    343  1.55  riastrad 		if (nread == 0)
    344  1.55  riastrad 			errx(1, "read eof");
    345  1.55  riastrad 		if ((size_t)nread != bufsize)
    346  1.55  riastrad 			errx(1, "invalid read");
    347  1.18       mrg 		if (conv_func)
    348  1.19       mrg 			(*conv_func)(buffer, bufsize);
    349  1.54       mrg 		if ((size_t)write(ti.outfd, buffer, bufsize) != bufsize)
    350   1.1       mrg 			err(1, "write failed");
    351  1.54       mrg 		ti.total_size += bufsize;
    352   1.1       mrg 	}
    353   1.1       mrg 	cleanup(0);
    354   1.1       mrg }
    355   1.1       mrg 
    356   1.1       mrg int
    357  1.51     joerg timeleft(struct timeval *start_tvp, struct timeval *record_tvp)
    358   1.1       mrg {
    359   1.1       mrg 	struct timeval now, diff;
    360   1.1       mrg 
    361   1.1       mrg 	(void)gettimeofday(&now, NULL);
    362   1.7  dmcmahil 	timersub(&now, start_tvp, &diff);
    363   1.7  dmcmahil 	timersub(record_tvp, &diff, &now);
    364   1.1       mrg 
    365   1.1       mrg 	return (now.tv_sec > 0 || (now.tv_sec == 0 && now.tv_usec > 0));
    366   1.1       mrg }
    367   1.1       mrg 
    368   1.1       mrg void
    369  1.51     joerg cleanup(int signo)
    370   1.1       mrg {
    371   1.1       mrg 
    372   1.1       mrg 	rewrite_header();
    373  1.54       mrg 	close(ti.outfd);
    374  1.17       mrg 	if (omonitor_gain) {
    375   1.1       mrg 		AUDIO_INITINFO(&info);
    376  1.17       mrg 		info.monitor_gain = omonitor_gain;
    377  1.23  jdolecek 		if (ioctl(audiofd, AUDIO_SETINFO, &info) < 0)
    378   1.1       mrg 			err(1, "failed to reset audio info");
    379   1.1       mrg 	}
    380  1.23  jdolecek 	close(audiofd);
    381  1.44     lukem 	if (signo != 0) {
    382  1.44     lukem 		(void)raise_default_signal(signo);
    383  1.44     lukem 	}
    384   1.1       mrg 	exit(0);
    385   1.1       mrg }
    386   1.1       mrg 
    387  1.51     joerg static void
    388  1.51     joerg rewrite_header(void)
    389   1.1       mrg {
    390   1.1       mrg 
    391   1.1       mrg 	/* can't do this here! */
    392  1.54       mrg 	if (ti.outfd == STDOUT_FILENO)
    393   1.1       mrg 		return;
    394   1.1       mrg 
    395  1.54       mrg 	if (lseek(ti.outfd, (off_t)0, SEEK_SET) == (off_t)-1)
    396   1.1       mrg 		err(1, "could not seek to start of file for header rewrite");
    397  1.54       mrg 	write_header(&ti);
    398   1.1       mrg }
    399   1.1       mrg 
    400  1.51     joerg static void
    401  1.51     joerg usage(void)
    402   1.1       mrg {
    403  1.14       cgd 
    404  1.14       cgd 	fprintf(stderr, "Usage: %s [-afhqV] [options] {files ...|-}\n",
    405  1.14       cgd 	    getprogname());
    406   1.4       mrg 	fprintf(stderr, "Options:\n\t"
    407  1.50       wiz 	    "-B buffer size\n\t"
    408   1.4       mrg 	    "-b balance (0-63)\n\t"
    409   1.4       mrg 	    "-c channels\n\t"
    410   1.4       mrg 	    "-d audio device\n\t"
    411   1.4       mrg 	    "-e encoding\n\t"
    412  1.35       wiz 	    "-F format\n\t"
    413   1.4       mrg 	    "-i header information\n\t"
    414   1.4       mrg 	    "-m monitor volume\n\t"
    415  1.35       wiz 	    "-P precision (4, 8, 16, 24, or 32 bits)\n\t"
    416   1.4       mrg 	    "-p input port\n\t"
    417   1.4       mrg 	    "-s sample rate\n\t"
    418   1.4       mrg 	    "-t recording time\n\t"
    419   1.4       mrg 	    "-v volume\n");
    420   1.9    kleink 	exit(EXIT_FAILURE);
    421   1.1       mrg }
    422