123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561 |
- #include <assert.h>
- #include <errno.h>
- #include <inttypes.h>
- #include <limits.h>
- #include <signal.h>
- #include <stddef.h>
- #include <stdint.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <time.h>
- #include <fcntl.h>
- #include <poll.h>
- #include <unistd.h>
- #ifdef USE_CAPSICUM
- #include <sys/capsicum.h>
- #endif
- #include <err.h>
- #include <sysexits.h>
- #include "au.h"
- #include "read/afile.h"
- #include "common.h"
- #ifndef NAMEMAX
- #define NAMEMAX 10
- #endif
- #ifndef CBSIZE
- #define CBSIZE 64
- #endif
- #ifndef BSIZE
- #define BSIZE 0x1000
- #endif
- #ifdef SIGINFO
- #define IFINFO(x) (x)
- #else
- #define IFINFO(x)
- #endif
- enum parse_state {
- PARSE_DEFAULT,
- PARSE_SET,
- PARSE_NUMBER,
- PARSE_DECIMAL,
- PARSE_ZEE
- };
- enum {
- PARSE_SEEK = 1,
- PARSE_INFO,
- PARSE_PLAYPAUSE,
- PARSE_HZ,
- PARSE_QUIT
- };
- #define PARSE_INITIALIZER { { 0 }, { 0 }, PARSE_DEFAULT, 0, 0, 0, 0, 0 }
- struct parse {
- char buffer[CBSIZE];
- struct timespec ts;
- enum parse_state state;
- int whence;
- int negative;
- int index;
- int count;
- int decimals;
- };
- #ifdef SIGINFO
- static size_t info_bytes = 0;
- #endif
- #ifdef SIGINFO
- static void siginfo(int);
- #endif
- static void printts(int, struct timespec);
- static int parsefd(int, struct parse *);
- int
- main(int argc, char *argv[])
- {
- #ifdef USE_CAPSICUM
- cap_rights_t rights;
- #endif
- #ifdef USE_MODE
- char *amode = NULL;
- #endif
- #ifdef USE_QUALITY
- double quality = 1;
- #endif
- struct pollfd fds[2] = {
- { STDIN_FILENO, POLLIN, 0 },
- { -1, POLLIN, 0 }
- };
- struct parse parse = PARSE_INITIALIZER;
- struct au au = AU_INITIALIZER;
- struct afile *a;
- struct timespec ts;
- const char *cfile = NULL;
- const char *ifile = NULL;
- int cfd = -1;
- int ifd = -1;
- ssize_t i;
- ssize_t r;
- ssize_t t;
- size_t bsize = BSIZE;
- time_t ihz = -1;
- size_t bsi = 0;
- size_t bps;
- void *b;
- char *p;
- int vflag = 0;
- int first = 1;
- int c;
- while ((c = getopt(argc, argv, "C:I:b:c:i:v")) != -1)
- switch (c) {
- case 'C':
- cfile = NULL;
- cfd = strtoul(optarg, &p, 0);
- if (p == optarg || *p != '\0')
- errx(EX_USAGE, "Invalid number '%s'",
- optarg);
- if (cfd == STDIN_FILENO ||
- cfd == STDOUT_FILENO ||
- cfd == STDERR_FILENO ||
- fcntl(cfd, F_GETFL, &c) == -1)
- errx(EX_USAGE,
- "Invalid file-descriptor %d", cfd);
- break;
- case 'I':
- ifile = NULL;
- ifd = strtoul(optarg, &p, 0);
- if (p == optarg || *p != '\0')
- errx(EX_USAGE, "Invalid number '%s'",
- optarg);
- if (ifd == STDIN_FILENO ||
- ifd == STDOUT_FILENO ||
- ifd == STDERR_FILENO ||
- fcntl(ifd, F_GETFL, &c) == -1)
- errx(EX_USAGE,
- "Invalid file-descriptor %d", ifd);
- break;
- case 'b':
- bsize = strtoul(optarg, &p, 0);
- if (p == optarg || *p != '\0')
- errx(EX_USAGE, "Invalid number '%s'",
- optarg);
- if (bsize < 0x400)
- errx(EX_USAGE, "The buffer size (%zu) "
- "is too small", bsize);
- break;
- case 'c':
- cfd = -1;
- cfile = optarg;
- break;
- case 'i':
- ifd = -1;
- ifile = optarg;
- break;
- case 'v':
- vflag = 1;
- break;
- default:
- goto usage;
- }
- if ((b = malloc(bsize)) == NULL)
- err(EX_OSERR, "Could not allocate the buffer");
- IFINFO((void)signal(SIGINFO, &siginfo));
- if (cfile != NULL)
- /* Use O_RDWR to make poll(2) work well with FIFOs */
- if ((cfd = open(cfile, O_RDWR | O_CLOEXEC)) < 0)
- warn("Could not open '%s'", cfile);
- if (ifile != NULL)
- if ((ifd = open(ifile, O_APPEND | O_CREAT | O_CLOEXEC))
- < 0)
- warn("Could not open '%s'", ifile);
- #ifdef USE_PLEDGE
- pledge("stdio", NULL);
- #endif
- #ifdef USE_CAPSICUM
- if (cap_enter() == -1 && errno != ENOSYS)
- err(EX_OSERR, "Could not enter capability mode");
- (void)cap_rights_init(&rights, CAP_READ, CAP_EVENT, CAP_FCNTL,
- CAP_FSTAT, CAP_SEEK);
- if (cap_rights_limit(STDIN_FILENO, &rights) == -1 &&
- errno != ENOSYS)
- err(EX_OSERR, "Could not limit the standard input");
- (void)cap_rights_init(&rights, CAP_EVENT, CAP_FCNTL, CAP_FSTAT,
- CAP_READ, CAP_WRITE);
- if (cfd >= 0 && cap_rights_limit(cfd, &rights) == -1 &&
- errno != ENOSYS)
- err(EX_OSERR, "Could not limit the command input");
- (void)cap_rights_init(&rights, CAP_WRITE);
- if (cap_rights_limit(STDERR_FILENO, &rights) == -1 &&
- errno != ENOSYS)
- err(EX_OSERR, "Could not limit the standard error");
- (void)cap_rights_set(&rights, CAP_EVENT, CAP_FCNTL);
- if (cap_rights_limit(STDOUT_FILENO, &rights) == -1 &&
- errno != ENOSYS)
- err(EX_OSERR, "Could not limit the standard output");
- if (ifd >= 0 && cap_rights_limit(ifd, &rights) == -1 &&
- errno != ENOSYS)
- err(EX_OSERR, "Could not limit the informative output");
- #endif /* USE_CAPSICUM */
- if ((a = af_fdopen(STDIN_FILENO)) == NULL)
- err(EX_DATAERR, "Could not initialize audio codec");
- if (setblock(STDIN_FILENO, 0) != 0 ||
- setblock(STDOUT_FILENO, 0) != 0 ||
- (cfd >= 0 && setblock(cfd, 0) != 0))
- err(EX_OSERR, "Could not set O_NONBLOCK");
- if (ifd < 0)
- ifd = STDERR_FILENO;
- #ifdef USE_CAPSICUM
- /* CAP_FCNTL is no longer needed */
- (void)cap_rights_init(&rights, CAP_READ, CAP_EVENT, CAP_FSTAT,
- CAP_SEEK);
- if (cap_rights_limit(STDIN_FILENO, &rights) == -1 &&
- errno != ENOSYS)
- err(EX_OSERR, "Could not limit the standard input");
- (void)cap_rights_init(&rights, CAP_WRITE, CAP_EVENT);
- if (cap_rights_limit(STDOUT_FILENO, &rights) == -1 &&
- errno != ENOSYS)
- err(EX_OSERR, "Could not limit the standard output");
- (void)cap_rights_init(&rights, CAP_EVENT, CAP_READ, CAP_WRITE);
- if (cfd >= 0 && cap_rights_limit(cfd, &rights) == -1 &&
- errno != ENOSYS)
- err(EX_OSERR, "Could not limit the command input");
- #endif
- fds[1].fd = cfd;
- while ((c = poll(fds, 1 + (cfd >= 0), INFTIM)) >= 0 ||
- errno == EINTR) {
- if (c <= 0)
- continue;
- if (fds[0].revents & POLLIN) {
- errno = 0;
- if (first)
- r = af_read(a, (char *)b + AU_SIZE,
- bsize - AU_SIZE);
- else
- r = af_read(a, b, bsize);
- if (r == 0 || (r < 0 && errno != EAGAIN &&
- errno != EINTR))
- break;
- if (r > 0) {
- if (first) {
- if (af_get(a,
- "encoding rate channels",
- &au.au_enc, &au.au_rate,
- &au.au_chan) != 3)
- err(EX_SOFTWARE,
- "Could not get "
- "properties");
- if (cfd >= 0) {
- ts.tv_sec = 0;
- ts.tv_nsec = 0;
- ts = af_seek(a, ts,
- SEEK_CUR);
- }
- if (cfd < 0 || ts.tv_sec < 0 ||
- ts.tv_nsec < 0)
- (void)af_get(a, "size",
- &au.au_size);
- (void)au_puthdr(&au, b);
- r += AU_SIZE;
- bps = au_ssize(&au) *
- (size_t)au.au_rate;
- first = 0;
- }
- i = 0;
- fds[0].fd = STDOUT_FILENO;
- fds[0].events = POLLOUT;
- }
- } else if (fds[0].revents & POLLOUT) {
- if ((t = write(STDOUT_FILENO, (char *)b + i,
- r - i)) == 0 || (r < 0 && errno != EAGAIN &&
- errno != EINTR))
- break;
- IFINFO(info_bytes += t);
- if (r > 0 && (i += t) >= r) {
- if (vflag && ifd >= 0 &&
- (ihz < 0 || (ihz > 0 &&
- (bsi += t) > bps / ihz))) {
- bsi = 0;
- ts.tv_sec = ts.tv_nsec = 0;
- printts(ifd, af_seek(a, ts,
- SEEK_CUR));
- (void)write(ifd, "/", 1);
- printts(ifd, af_length(a));
- (void)write(ifd, "\n", 1);
- }
- fds[0].fd = STDIN_FILENO;
- fds[0].events = POLLIN;
- }
- }
- if (fds[0].revents & (POLLHUP | POLLERR | POLLNVAL)) {
- /* Finish reading any buffered audio. */
- if ((r = af_read(a, b, bsize)) <= 0)
- break;
- fds[0].fd = STDOUT_FILENO;
- fds[0].events = POLLOUT;
- }
- if (cfd >= 0 && (fds[1].revents & POLLIN)) {
- while ((c = parsefd(cfd, &parse)) > 0)
- switch (c) {
- case PARSE_SEEK:
- ts = af_seek(a, parse.ts,
- parse.whence);
- if (ts.tv_sec < 0 ||
- ts.tv_nsec < 0) {
- if (errno == ERANGE)
- goto write_ret;
- warn("seek error");
- }
- if (vflag) {
- printts(ifd, ts);
- (void)write(ifd,
- "/", 1);
- printts(ifd,
- af_length(a));
- (void)write(ifd,
- "\n", 1);
- }
- break;
- case PARSE_INFO:
- ts.tv_sec = ts.tv_nsec = 0;
- printts(ifd, af_seek(a, ts,
- SEEK_CUR));
- (void)write(ifd, "/", 1);
- printts(ifd, af_length(a));
- (void)write(ifd, "\n", 1);
- break;
- case PARSE_PLAYPAUSE:
- fds[0].fd = fds[0].fd == -1 ?
- STDIN_FILENO : -1;
- break;
- case PARSE_HZ:
- ihz = parse.ts.tv_sec;
- break;
- case PARSE_QUIT:
- goto write_ret;
- }
- if (c == -1 && errno != EAGAIN &&
- errno != EINTR)
- fds[1].revents |= POLLHUP;
- }
- if (cfd >= 0 && (fds[1].revents & POLLHUP))
- (void)close(cfd);
- if (cfd >= 0 && (fds[1].revents &
- (POLLHUP | POLLERR | POLLNVAL))) {
- fds[1].fd = -1;
- if (cfile == NULL || fds[0].fd == -1)
- (void)close(STDIN_FILENO);
- }
- }
- write_ret:
- af_close(a);
- free(b);
- return (EX_OK);
- usage:
- c = strlen(getprogname()) + 8;
- (void)fprintf(stderr,
- "usage: %s [-C command-fd] [-I info-fd] [-b buffer-size]\n"
- "%*s[-c command-file] [-i info-file]\n", getprogname(), c,
- "");
- return (EX_USAGE);
- }
- #ifdef SIGINFO
- void
- siginfo(int sig)
- {
- /* XXXX2au: XXXXXXiB processed */
- char b[NAMEMAX + 2 + 8 + 11];
- size_t i;
- i = strlen(getprogname());
- if (i > NAMEMAX) {
- (void)memcpy(b, getprogname(), NAMEMAX - 3);
- (void)memcpy(b + NAMEMAX - 3, "...", 3);
- i = NAMEMAX;
- } else
- (void)memcpy(b, getprogname(), i);
- (void)memcpy(b + i, ": ", 2);
- i += 2;
- i += szwrite(b + i, info_bytes, "B", "iB");
- (void)memcpy(b + i, " processed\n", 11);
- i += 11;
- (void)write(STDERR_FILENO, b, i);
- }
- #endif
- void
- printts(int fd, struct timespec ts)
- {
- char b[20]; /* >SSSSSSSSS.NNNNNNNNN */
- int i = sizeof(b);
- int n;
- if (fd < 0)
- return;
- if (ts.tv_sec > 999999999) {
- for (i = 1; i < 10; i++)
- b[i] = '9';
- b[0] = '>';
- write(fd, b, i);
- }
- if (ts.tv_nsec > 0) {
- for (n = 9; ts.tv_nsec % 10 == 0; ts.tv_nsec /= 10, n--)
- /* do nothing */;
- i -= n;
- while (ts.tv_nsec > 0) {
- b[i + --n] = ts.tv_nsec % 10 + '0';
- ts.tv_nsec /= 10;
- }
- while (n > 0)
- b[i + --n] = '0';
- b[--i] = '.';
- }
- do {
- b[--i] = ts.tv_sec % 10 + '0';
- ts.tv_sec /= 10;
- } while (ts.tv_sec > 0);
- (void)write(fd, b + i, sizeof(b) - i);
- }
- int
- parsefd(int fd, struct parse *p)
- {
- ssize_t r;
- int c;
- assert(p != NULL);
- for (;;) {
- if (p->index >= p->count) {
- if ((r = read(fd, p->buffer,
- sizeof(p->buffer))) <= 0)
- return (r == 0 ? 0 : -1);
- p->index = 0;
- p->count = (int)r;
- }
- while (p->index < p->count) {
- c = p->buffer[p->index];
- switch (p->state) {
- case PARSE_DEFAULT:
- if (c == '=') {
- p->state = PARSE_SET;
- p->negative = 0;
- } else if (c == '-' || c == '+') {
- p->state = PARSE_NUMBER;
- p->negative = c == '-';
- p->whence = SEEK_CUR;
- p->ts.tv_sec = 0;
- p->ts.tv_nsec = 0;
- } else if (c >= '0' && c <= '9') {
- p->state = PARSE_NUMBER;
- p->negative = 0;
- p->whence = SEEK_SET;
- p->ts.tv_sec = c - '0';
- p->ts.tv_nsec = 0;
- } else if (c == '.') {
- p->state = PARSE_DECIMAL;
- p->negative = 0;
- p->whence = SEEK_SET;
- p->ts.tv_sec = 0;
- p->ts.tv_nsec = 0;
- } else if (c == '?') {
- p->index++;
- return (PARSE_INFO);
- } else if (c == 'P' || c == 'p') {
- p->index++;
- return (PARSE_PLAYPAUSE);
- } else if (c == 'Q' || c == 'q') {
- p->index++;
- return (PARSE_QUIT);
- }
- break;
- case PARSE_SET:
- if (c == '-' || c == '+') {
- p->state = PARSE_NUMBER;
- p->whence = c == '-' ?
- SEEK_END : SEEK_SET;
- p->ts.tv_sec = 0;
- } else if (c >= '0' && c <= '9') {
- p->state = PARSE_NUMBER;
- p->whence = SEEK_SET;
- p->ts.tv_sec = c - '0';
- } else if (c == '.') {
- p->state = PARSE_DECIMAL;
- p->whence = SEEK_SET;
- p->ts.tv_sec = 0;
- p->ts.tv_nsec = 0;
- p->decimals = 9;
- } else if (c != '=') {
- p->state = PARSE_DEFAULT;
- p->index--;
- }
- break;
- case PARSE_NUMBER:
- if (c >= '0' && c <= '9')
- p->ts.tv_sec = p->ts.tv_sec * 10
- + (c - '0');
- else if (c == '.') {
- p->state = PARSE_DECIMAL;
- p->decimals = 9;
- p->ts.tv_nsec = 0;
- } else if (c == 'h') {
- p->state = PARSE_ZEE;
- } else if (p->ts.tv_sec == 0 &&
- p->whence == SEEK_CUR) {
- p->state = PARSE_DEFAULT;
- p->index--;
- } else {
- p->state = PARSE_DEFAULT;
- if (p->negative)
- p->ts.tv_sec =
- -p->ts.tv_sec;
- p->ts.tv_nsec = 0;
- return (PARSE_SEEK);
- }
- break;
- case PARSE_DECIMAL:
- if (c >= '0' && c <= '9') {
- if (p->decimals > 0) {
- p->ts.tv_nsec =
- p->ts.tv_nsec * 10 +
- (c - '0');
- p->decimals--;
- }
- } else if (p->ts.tv_sec == 0 &&
- p->ts.tv_nsec == 0 &&
- p->whence == SEEK_CUR) {
- p->state = PARSE_DEFAULT;
- p->index--;
- } else {
- p->state = PARSE_DEFAULT;
- for (; p->decimals > 0;
- p->decimals--)
- p->ts.tv_nsec *= 10;
- if (p->negative) {
- p->ts.tv_sec =
- -p->ts.tv_sec;
- p->ts.tv_nsec =
- -p->ts.tv_nsec;
- }
- return (PARSE_SEEK);
- }
- break;
- case PARSE_ZEE:
- p->state = PARSE_DEFAULT;
- if (c == 'z')
- return (PARSE_HZ);
- break;
- }
- p->index++;
- }
- }
- }
|