12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235 |
- #include <assert.h>
- #include <ctype.h>
- #include <errno.h>
- #include <limits.h>
- #include <signal.h>
- #include <stddef.h>
- #include <stdint.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/types.h>
- #include <sys/ioctl.h>
- #include <sys/stat.h>
- #include <sys/wait.h>
- #include <dirent.h>
- #include <fcntl.h>
- #include <fnmatch.h>
- #include <glob.h>
- #include <poll.h>
- #include <regex.h>
- #include <unistd.h>
- #include <err.h>
- #include <sysexits.h>
- #include "playlist.h"
- #include "util.h"
- #ifndef nitems
- #define nitems(x) (sizeof((x)) / sizeof((x)[0]))
- #endif
- #ifndef ERR
- #define ERR(e, r) \
- do { \
- errno = (e); \
- return (r); \
- } while (0)
- #endif
- #define RESERVED_FD(fd) \
- (((fd) == STDIN_FILENO) || \
- ((fd) == STDOUT_FILENO) || \
- ((fd) == STDERR_FILENO))
- #define FDELTA 4
- #define LINEDELTA 32
- #ifndef CMDMAX
- #define CMDMAX (PATH_MAX * 16)
- #endif
- #ifndef INFOMAX
- #define INFOMAX 64
- #endif
- #ifndef INFODEC
- #define INFODEC 2
- #endif
- #ifndef HSIZE
- #define HSIZE 32
- #endif
- #ifndef AUDEV_DEFAULT
- #define AUDEV_DEFAULT "exec au2sdl"
- #endif
- enum {
- PLAY_DONE = 0,
- PLAY_OK,
- PLAY_RETRY
- };
- struct fentry {
- char *path;
- int fd;
- int flags;
- };
- #define LINE_INITIALIZER { NULL, 0, 0, SIZE_MAX - 1, 0 }
- struct line {
- char *buffer;
- size_t count;
- size_t size;
- size_t max;
- int warn;
- };
- #define CMD_INITIALIZER { NULL, 0, '\0' }
- struct cmd {
- char *arg;
- unsigned long count;
- int cmd;
- };
- #define INFO_INITIALIZER { { '\0' }, { '?' }, 0, 0 }
- struct info {
- char cdec[INFODEC + 2];
- char tdec[INFODEC + 2];
- unsigned long cint;
- unsigned long tint;
- };
- #define PLAY_INITIALIZER { \
- NULL, LINE_INITIALIZER, LINE_INITIALIZER, \
- INFO_INITIALIZER, AUDEV_DEFAULT, NULL, { 0 }, \
- 0, 100, STDIN_FILENO, STDOUT_FILENO, 0, 0, 0, \
- PLRONCE \
- }
- struct play {
- struct playlist *plist;
- struct line iline;
- struct line cline;
- struct info info;
- char *cstr;
- char *dstr;
- struct timeval tv;
- size_t bsize;
- unsigned long ihz;
- int in;
- int out;
- int paused;
- int loop;
- int retry;
- int flags;
- };
- static pid_t auopen_pid = 0;
- static pid_t audev_pid = 0;
- static sigset_t sigpend;
- static void sighandle(int);
- static void onadd(void *, const char *);
- static void onrem(void *, const char *);
- static int playlist_fpadd(struct playlist *, FILE *, int);
- static int play_single(struct play *, sigset_t *);
- static int play_exec(struct play *, int *, int *);
- static int linefd(struct line *, int);
- static int parsecmd(struct cmd *, char *);
- static int parseinfo(struct info *, const char *);
- static int parseseek(struct cmd *, double *);
- static void printinfo(int, struct info *, const char *, int);
- static char *shquote(const char *);
- static int ichars(int);
- static int unreservefd(int);
- int
- main(int argc, char *argv[])
- {
- struct play play = PLAY_INITIALIZER;
- struct fentry *fs = NULL;
- struct fentry *f;
- FILE *fp;
- char *in = NULL;
- char *p;
- sigset_t omask;
- sigset_t smask;
- size_t fcount = 0;
- size_t fsize = 0;
- size_t hsize = HSIZE;
- size_t i;
- unsigned long ul;
- int c;
- #ifdef USE_PLEDGE
- (void)pledge("stdio rpath proc exec", NULL);
- #endif
- if (setvbuf(stdout, NULL, _IOLBF, 0) != 0)
- err(EX_IOERR, "Could not set line-buffered output");
- play.in = -1;
- play.cline.max = CMDMAX;
- play.iline.max = INFOMAX;
- while ((c = getopt(argc, argv, "F:I:b:c:d:f:g:i:h:lprsv"))
- != -1)
- switch (c) {
- case 'F':
- errno = 0;
- ul = strtoul(optarg, &p, 0);
- if (errno == 0) {
- if (ul > INT_MAX)
- errno = ERANGE;
- else if (p == optarg || *p != '\0')
- errno = EINVAL;
- else if (ul == STDOUT_FILENO ||
- ul == STDERR_FILENO)
- errno = EDOM;
- }
- if (errno != 0)
- err(EX_USAGE, "Invalid number '%s'",
- optarg);
- if (fcntl(ul, F_GETFL, &c) == -1)
- err(EX_NOINPUT,
- "Invalid file-descriptor %lu", ul);
- f = allot((void **)&fs, &fsize, fcount++,
- sizeof(struct fentry), FDELTA);
- f->path = NULL;
- f->fd = ul;
- break;
- case 'I':
- errno = 0;
- ul = strtoul(optarg, &p, 0);
- if (errno == 0) {
- if (ul > INT_MAX)
- errno = ERANGE;
- else if (p == optarg || *p != '\0')
- errno = EINVAL;
- else if (ul == STDOUT_FILENO ||
- ul == STDERR_FILENO)
- errno = EDOM;
- }
- if (errno != 0)
- err(EX_USAGE, "Invalid number '%s'",
- optarg);
- if (fcntl(ul, F_GETFL, &c) == -1)
- err(EX_NOINPUT,
- "Invalid file-descriptor %lu", ul);
- in = NULL;
- play.in = ul;
- break;
- case 'b':
- errno = 0;
- ul = strtoul(optarg, &p, 0);
- if (ul > INT_MAX)
- errno = ERANGE;
- if (p == optarg || *p != '\0')
- errno = EINVAL;
- if (errno != 0)
- err(EX_USAGE, "Invalid number %s",
- optarg);
- play.bsize = ul;
- break;
- case 'c':
- play.cstr = optarg[0] != '\0' ? optarg : NULL;
- break;
- case 'd':
- play.dstr = optarg;
- break;
- case 'f':
- f = allot((void **)&fs, &fsize, fcount++,
- sizeof(struct fentry), FDELTA);
- f->flags = PLGNONE | PLSCMP;
- if (strcmp(optarg, "-") == 0) {
- f->path = NULL;
- f->fd = STDIN_FILENO;
- } else {
- f->path = optarg;
- f->fd = -1;
- }
- break;
- case 'g':
- f = allot((void **)&fs, &fsize, fcount++,
- sizeof(struct fentry), FDELTA);
- f->flags = PLGNONE | PLSGLOB;
- if (strcmp(optarg, "-") == 0) {
- f->path = NULL;
- f->fd = STDIN_FILENO;
- } else {
- f->path = optarg;
- f->fd = -1;
- }
- break;
- case 'i':
- if (strcmp(optarg, "-") == 0) {
- in = NULL;
- play.in = STDIN_FILENO;
- } else {
- in = optarg;
- play.in = -1;
- }
- break;
- case 'h':
- errno = 0;
- ul = strtoul(optarg, &p, 0);
- if (ul > SIZE_MAX)
- errno = ERANGE;
- if (p == optarg || *p != '\0')
- errno = EINVAL;
- if (errno != 0 || p == optarg || *p != '\0')
- err(EX_USAGE, "Invalid number %s",
- optarg);
- hsize = ul;
- break;
- case 'l':
- play.loop = 1;
- break;
- case 'p':
- play.flags = (play.flags & ~PLVTYPE) | PLVFULL;
- break;
- case 'r':
- play.flags = (play.flags & ~PLGTYPE) | PLGRAND;
- break;
- case 's':
- play.flags = (play.flags & ~PLGTYPE) | PLGSHUF;
- break;
- case 'v':
- play.flags = (play.flags & ~PLVTYPE)
- | PLVSIMPLE;
- break;
- default:
- goto usage;
- }
- argc -= optind;
- argv += optind;
- if (play.in >= 0) {
- for (i = 0; i < fcount; i++)
- if (play.in == fs[i].fd)
- errx(EX_USAGE, "Ambiguous file-"
- "descriptor %d", fs[i].fd);
- } else if (in != NULL) {
- if ((play.in = open(in, O_RDONLY | O_CLOEXEC)) < 0)
- err(EX_NOINPUT, "Could not open %s", in);
- } else {
- for (i = 0, play.in = STDIN_FILENO; i < fcount; i++)
- if (fs[i].fd == STDIN_FILENO) {
- if ((play.flags & PLVTYPE) == PLVFULL)
- errx(EX_USAGE, "The standard "
- "input can not be used as "
- "a file-list");
- if ((play.in = open("/dev/tty",
- O_RDONLY | O_CLOEXEC)) < 0)
- err(EX_NOINPUT,
- "Could not open /dev/tty");
- break;
- }
- }
- if ((play.plist = playlist_create(hsize, play.flags)) == NULL)
- err(EX_OSERR, "playlist_create");;
- if ((play.flags & PLVTYPE) == PLVFULL) {
- playlist_onadd(play.plist, &onadd, &play);
- playlist_onrem(play.plist, &onrem, &play);
- }
- if (fs != NULL) {
- for (i = 0; i < fcount; i++) {
- if (fs[i].path != NULL) {
- if ((fp = fopen(fs[i].path, "r"))
- == NULL)
- warn("Could not open %s",
- fs[i].path);
- } else if (fs[i].fd >= 0) {
- if ((fp = fdopen(fs[i].fd, "r"))
- == NULL)
- warn("Could not open file-"
- "descriptor %d", fs[i].fd);
- } else
- abort();
- if (fp == NULL)
- break;
- (void)playlist_fpadd(play.plist, fp,
- f[i].flags | (play.flags & ~PLGTYPE &
- ~PLSTYPE));
- fclose(fp);
- }
- free(fs);
- }
- for (; *argv != NULL; argv++)
- switch ((*argv)[0]) {
- case '(':
- switch (toupper((*argv)[1])) {
- case '\0':
- playlist_gbegin(play.plist, PLGNORM);
- break;
- case 'R':
- playlist_gbegin(play.plist, PLGRAND);
- break;
- case 'S':
- playlist_gbegin(play.plist, PLGSHUF);
- break;
- default:
- warnx("Invalid group type: %s",
- (*argv) + 1);
- break;
- }
- break;
- case ')':
- if (playlist_gend(play.plist) != 0)
- warnx("Unexpected `)'");
- break;
- default:
- (void)playlist_add(play.plist, *argv,
- (play.flags & ~PLGTYPE & ~PLSTYPE) |
- PLSCMP);
- }
- if (play.in < 0 && (play.in = open("/dev/tty", O_RDONLY)) < 0)
- err(EX_NOINPUT, "Could not open /dev/tty");
- sigemptyset(&smask);
- if (sigprocmask(SIG_SETMASK, &smask, &omask) != 0)
- err(EX_OSERR, "sigprocmask");
- smask = omask;
- sigemptyset(&sigpend);
- /*
- * Ignore SIGTTIN to allow continuous
- * operation as a background process
- */
- if (signal(SIGTTIN, SIG_IGN) == SIG_ERR)
- abort();
- /*
- * SIGCONT is needed if the audio backend disconnects
- * while the process is stopped.
- */
- if (signal(SIGCONT, &sighandle) == SIG_ERR)
- abort();
- if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
- abort();
- #ifdef SIGINFO
- if (signal(SIGINFO, &sighandle) == SIG_ERR)
- abort();
- #endif
- if ((play.flags & PLVTYPE) != PLVFULL) {
- playlist_onadd(play.plist, &onadd, &play);
- playlist_onrem(play.plist, &onrem, &play);
- }
- while ((c = play_single(&play, &smask)) > 0)
- /* do nothing */;
- playlist_onadd(play.plist, NULL, NULL);
- playlist_onrem(play.plist, NULL, NULL);
- playlist_destroy(play.plist);
- if (play.cline.buffer != NULL)
- free(play.cline.buffer);
- if (play.iline.buffer != NULL)
- free(play.iline.buffer);
- return (c ? EX_SOFTWARE : 0);
- usage:
- (void)fprintf(stderr, "usage: auplayer [-lprsv] "
- "[-F file-descriptor] [-I file-descriptor]\n"
- " [-b buffer-size] [-c command] [-d device] "
- "[-f file-list]\n"
- " [-h history-size] [-i input-file] "
- "file ...\n");
- return (EX_USAGE);
- }
- void
- sighandle(int sig)
- {
- (void)sigaddset(&sigpend, sig);
- }
- void
- onadd(void *ctx, const char *path)
- {
- struct play *play = ctx;
- assert(ctx != NULL && path != NULL);
- switch (play->flags & PLVTYPE) {
- case PLVSIMPLE:
- (void)dprintf(play->out, "Adding %s\n", path);
- break;
- case PLVFULL:
- (void)dprintf(play->out, "+%s\n", path);
- break;
- }
- }
- void
- onrem(void *ctx, const char *path)
- {
- struct play *play = ctx;
- assert(ctx != NULL && path != NULL);
- switch (play->flags & PLVTYPE) {
- case PLVSIMPLE:
- (void)dprintf(play->out, "Removing %s\n", path);
- break;
- case PLVFULL:
- (void)dprintf(play->out, "-%s\n", path);
- break;
- }
- }
- int
- playlist_fpadd(struct playlist *list, FILE *fp, int flags)
- {
- char *cp = NULL;
- ssize_t r;
- size_t s = 0;
- size_t i;
- assert(list != NULL && fp != NULL);
- while ((r = getdelim(&cp, &s, '\n', fp)) != -1) {
- for (i = 0; cp[i] != '\0' && isspace(cp[i]); i++)
- /* do nothing */;
- if (i > 0) {
- r -= i;
- (void)memmove(cp, cp + i, r + 1);
- }
- if (r > 0) {
- for (i = r - 1; i > 0 && isspace(cp[i]); i--)
- /* do nothing */;
- r = i + 1;
- cp[r] = '\0';
- }
- if (r == 0)
- continue;
- switch(cp[0]) {
- case '(':
- switch (toupper(cp[1])) {
- case '\0':
- playlist_gbegin(list, PLGNORM);
- break;
- case 'R':
- playlist_gbegin(list, PLGRAND);
- break;
- case 'S':
- playlist_gbegin(list, PLGSHUF);
- break;
- default:
- warnx("Invalid group type: %s", cp + 1);
- break;
- }
- break;
- case ')':
- if (playlist_gend(list) != 0)
- warnx("Unexpected `)'");
- break;
- default:
- (void)playlist_add(list, cp, flags);
- break;
- }
- }
- if (cp != NULL)
- free(cp);
- return (0);
- }
- int
- play_single(struct play *play, sigset_t *smask)
- {
- struct pollfd fds[2] = {
- { -1, POLLIN, 0 }, { -1, POLLIN, 0 }
- };
- struct cmd cmd = CMD_INITIALIZER;
- const char *cur;
- ssize_t r;
- double d;
- int ttyin;
- int cfd = -1;
- int docmd = 0;
- int dead = 0;
- int done = 0;
- int status;
- assert(play != NULL && smask != NULL);
- assert(play->in >= 0 && play->out >= 0);
- if ((cur = playlist_current(play->plist)) == NULL &&
- (cur = playlist_next(play->plist)) == NULL) {
- if (play->loop) {
- playlist_loop(play->plist);
- if ((cur = playlist_next(play->plist))
- == NULL && play->cstr != NULL) {
- play->retry = 0;
- return (PLAY_DONE);
- }
- } else if (play->cstr != NULL) {
- play->retry = 0;
- return (PLAY_DONE);
- }
- }
- if ((play->flags & PLVTYPE) == PLVFULL && cur != NULL)
- (void)dprintf(play->out, "@%s\n", cur);
- if (play->cstr != NULL) {
- if (play_exec(play, &cfd, &fds[1].fd) != 0) {
- play->retry = 0;
- return (-1);
- }
- } else {
- if ((cfd = open("/dev/null", O_RDONLY)) == -1) {
- play->retry = 0;
- return (-1);
- }
- fds[1].fd = -1;
- }
- if (play->paused)
- (void)dprintf(cfd, "p\n");
- if ((play->flags & PLVTYPE) == PLVFULL)
- (void)dprintf(cfd, "%luhz\n", play->ihz);
- if (play->retry) {
- (void)dprintf(cfd, "=%lu%s\n", play->info.cint,
- play->info.cdec);
- (void)warnx("retry: %lu%s\n", play->info.cint,
- play->info.cdec);
- play->retry = 0;
- }
- if ((play->flags & PLVTYPE) == PLVSIMPLE ||
- (play->paused && (play->flags & PLVTYPE) == PLVFULL))
- (void)dprintf(cfd, "?\n");
- play->info.cdec[0] = '\0';
- play->info.tdec[0] = '\0';
- play->info.cint = 0;
- play->info.tint = 0;
- ttyin = isatty(play->in);
- fds[0].fd = play->in;
- for (;;) {
- /*
- * ppoll(2) generates a events for tty input
- * even when reading would block with SIGTTIN.
- * This behaviour is quite noisy when using
- * a tty in raw character mode.
- */
- if (ppoll(fds, nitems(fds), NULL, smask) < 0) {
- if (errno != EINTR)
- goto err;
- if (sigismember(&sigpend, SIGCONT)) {
- play->retry = 1;
- sigdelset(&sigpend, SIGCONT);
- }
- #ifdef SIGINFO
- if (sigismember(&sigpend, SIGINFO)) {
- (void)dprintf(cfd, "?\n");
- sigdelset(&sigpend, SIGINFO);
- }
- #endif
- continue;
- }
- if (fds[0].revents &
- (POLLERR | POLLHUP | POLLNVAL)) {
- (void)kill(auopen_pid, SIGTERM);
- (void)kill(audev_pid, SIGTERM);
- fds[0].fd = -1;
- done = 1;
- }
- if (fds[1].revents &
- (POLLERR | POLLHUP | POLLNVAL)) {
- if (waitpid(auopen_pid, &status, 0)
- != auopen_pid)
- goto err;
- dead |= WEXITSTATUS(status);
- if (waitpid(audev_pid, &status, 0) != audev_pid)
- goto err;
- dead |= WEXITSTATUS(status);
- break;
- }
- if (fds[0].revents & POLLIN) {
- while (!(docmd || done || dead) &&
- (r = linefd(&play->cline, fds[0].fd)) > 0)
- switch (parsecmd(&cmd,
- play->cline.buffer)) {
- case '\0':
- break;
- case '(':
- switch (toupper(cmd.arg[0])) {
- case '\0':
- playlist_gbegin(
- play->plist,
- PLGNORM);
- break;
- case 'R':
- playlist_gbegin(
- play->plist,
- PLGRAND);
- break;
- case 'S':
- playlist_gbegin(
- play->plist,
- PLGSHUF);
- break;
- default:
- warnx("Invalid group "
- "type: %s",
- cmd.arg);
- }
- break;
- case ')':
- if (playlist_gend(play->plist)
- != 0)
- warnx("Unexpected `)'");
- break;
- case '+':
- /* FALLTHROUGH */
- case '-':
- if (parseseek(&cmd, &d) == 0)
- (void)dprintf(cfd,
- "%+f\n", d);
- if ((play->flags & PLVTYPE)
- == PLVSIMPLE)
- (void)dprintf(cfd,
- "?\n");
- break;
- case '=':
- if (parseseek(&cmd, &d) == 0)
- (void)dprintf(cfd,
- "=%c%f\n",
- strchr(cmd.arg, '-')
- == NULL ? '+' : '-',
- d < 0 ? -d : d);
- if ((play->flags & PLVTYPE)
- == PLVSIMPLE)
- (void)dprintf(cfd,
- "?\n");
- break;
- case '/':
- cmd.cmd = '\0';
- docmd = playlist_search(
- play->plist, cmd.arg,
- PLSAUTO | PLGOTO |
- (play->loop ? PLLOOP : 0))
- != NULL;
- break;
- case '?':
- (void)dprintf(cfd, "?\n");
- break;
- case 'L':
- play->loop = !play->loop;
- (void)dprintf(play->out,
- "%sloop\n", play->loop ?
- "" : "no-");
- break;
- case 'V':
- switch (play->flags & PLVTYPE) {
- case PLVQUIET:
- play->flags =
- (play->flags &
- ~PLVTYPE) |
- PLVSIMPLE;
- (void)dprintf(play->out,
- "verbose\n");
- break;
- case PLVSIMPLE:
- play->flags =
- (play->flags &
- ~PLVTYPE) |
- PLVQUIET;
- (void)dprintf(play->out,
- "quiet\n");
- break;
- }
- break;
- case 'a':
- if (fds[1].fd >= 0)
- (void)playlist_add(
- play->plist,
- cmd.arg,
- (play->flags &
- ~PLGTYPE &
- ~PLSTYPE) |
- PLSGLOB);
- else
- playlist_append(
- play->plist,
- cmd.arg);
- break;
- case 'd':
- if (cmd.arg[0] == '\0') {
- dead = 1;
- play->retry = 0;
- break;
- }
- playlist_rem(play->plist,
- cmd.arg, PLSGLOB);
- if (playlist_current(
- play->plist) == NULL) {
- cmd.cmd = '\0';
- docmd = 1;
- }
- break;
- case 'h':
- if (cmd.count > 0)
- docmd = 1;
- break;
- case 'i':
- (void)dprintf(cfd, "?\n");
- break;
- case 'j':
- /* FALLTHROUGH */
- case 'k':
- /* FALLTHROUGH */
- case 'l':
- if (cmd.count > 0)
- docmd = 1;
- break;
- case 'p':
- (void)dprintf(cfd, "p\n");
- switch (play->flags & PLVTYPE) {
- case PLVSIMPLE:
- (void)dprintf(play->out,
- "%spaused\n",
- play->paused ?
- "un" : "");
- break;
- case PLVFULL:
- (void)dprintf(play->out,
- "!%spaused\n",
- play->paused ?
- "un" : "");
- break;
- }
- play->paused = !play->paused;
- break;
- case 'q':
- done = 1;
- break;
- case 'r':
- (void)dprintf(cfd, "=0\n");
- break;
- case 'z':
- play->ihz = cmd.count;
- if ((play->flags & PLVTYPE)
- == PLVFULL)
- (void)dprintf(play->out,
- "!hz %lu\n",
- play->ihz);
- (void)dprintf(cfd, "%luhz\n",
- play->ihz);
- break;
- default:
- warnx("Invalid command: '%s'",
- play->cline.buffer);
- break;
- }
- /*
- * When SIGTTIN is ignored read(2), on
- * a tty, returns with EIO, to allow use
- * as a background process this is
- * case is ignored.
- */
- if (r < 0 && errno != EAGAIN &&
- (!ttyin || errno != EIO)) {
- if (errno == 0)
- done = 1;
- else
- warn("command input");
- }
- if (docmd || done || dead) {
- if (fds[1].fd == -1)
- break;
- (void)kill(auopen_pid, SIGTERM);
- (void)kill(audev_pid, SIGTERM);
- fds[0].fd = -1;
- }
- }
- if (fds[1].revents & POLLIN) {
- while ((r = linefd(&play->iline, fds[1].fd))
- > 0)
- if (parseinfo(&play->info,
- play->iline.buffer) == 0)
- printinfo(play->out,
- &play->info, cur,
- (play->flags & PLVTYPE)
- == PLVFULL);
- else
- warn("parseinfo: %s",
- play->iline.buffer);
- if (r < 0 && errno != 0 && errno != EAGAIN)
- warn("linefd");
- }
- }
- (void)close(cfd);
- if (fds[1].fd >= 0)
- (void)close(fds[1].fd);
- if (dead) {
- if (play->retry)
- return (PLAY_RETRY);
- if (cur != NULL)
- playlist_rem(play->plist, cur, PLSCMP);
- cmd.cmd = '\0';
- docmd = 1;
- }
- if (done)
- return (PLAY_DONE);
- if (!docmd) {
- cmd.cmd = 'l';
- cmd.count = 1;
- }
- switch (cmd.cmd) {
- case '\0':
- break;
- case 'h':
- /* FALLTHROUGH */
- case 'j':
- for (; cmd.count > 0; cmd.count--)
- playlist_prev(play->plist);
- break;
- case 'k':
- /* FALLTHROUGH */
- case 'l':
- for (; cmd.count > 0; cmd.count--)
- if (playlist_next(play->plist) == NULL) {
- if (!play->loop)
- return (PLAY_DONE);
- playlist_loop(play->plist);
- if (playlist_next(play->plist) == NULL)
- return (PLAY_DONE);
- }
- break;
- default:
- abort(); /* NOTREACHED */
- }
- return (PLAY_OK);
- err:
- close(cfd);
- close(fds[1].fd);
- return (-1);
- }
- int
- play_exec(struct play *play, int *cfd, int *ifd)
- {
- /*
- * auopen <file> -C <cfd> -I <ifd> [-b <bsize>] [-v] |
- * <cmd> -R [-b <bsize> [-d <dev>]
- */
- const char *cur;
- char *argv[10] = { NULL };
- char *cmd;
- char *dev;
- int cpipe[2];
- int fpipe[2];
- int ipipe[2];
- char *cp;
- size_t i;
- assert(play != NULL && cfd != NULL && ifd != NULL);
- if ((cur = playlist_current(play->plist)) == NULL)
- return (-1);
- if (pipe(cpipe) != 0)
- err(EX_OSERR, "pipe(2)");
- if (pipe(fpipe) != 0)
- err(EX_OSERR, "pipe(2)");
- if (pipe(ipipe) != 0)
- err(EX_OSERR, "pipe(2)");
- cpipe[0] = unreservefd(cpipe[0]);
- ipipe[1] = unreservefd(ipipe[1]);
- if ((auopen_pid = fork()) < 0) {
- err(EX_OSERR, "Could not fork auopen(1)");
- } else if (auopen_pid == 0) {
- close(cpipe[1]);
- close(fpipe[0]);
- close(ipipe[0]);
- if (fpipe[1] != STDOUT_FILENO) {
- dup2(fpipe[1], STDOUT_FILENO);
- close(fpipe[1]);
- }
- if ((cp = malloc(ichars(cpipe[0]) + 1 +
- ichars(ipipe[1]) + 1 + (play->bsize > 0 ?
- ichars(play->bsize) + 1 : 0))) == NULL)
- err(EX_OSERR, "Could not allocate argv");
- i = 0;
- argv[i++] = "auopen";
- argv[i++] = (char *)cur;
- argv[i++] = "-C";
- argv[i++] = cp;
- cp += sprintf(cp, "%d", cpipe[0]) + 1;
- argv[i++] = "-I";
- argv[i++] = cp;
- cp += sprintf(cp, "%d", ipipe[1]) + 1;
- if ((play->flags & PLVTYPE) == PLVFULL)
- argv[i++] = "-v";
- if (play->bsize > 0) {
- argv[i++] = "-b";
- argv[i++] = cp;
- cp += sprintf(cp, "%zu", play->bsize);
- }
- execvp("auopen", argv);
- err(EX_OSERR, "Could not execute '%s %s'", "auopen",
- cur != NULL ? cur : "(null)");
- }
- close(cpipe[0]);
- close(fpipe[1]);
- close(ipipe[1]);
- cmd = play->cstr != NULL ? play->cstr : AUDEV_DEFAULT;
- if ((audev_pid = fork()) < 0) {
- err(EX_OSERR, "Could not fork '%s'", cmd);
- } else if (audev_pid == 0) {
- close(cpipe[1]);
- close(ipipe[0]);
- if (fpipe[0] != STDIN_FILENO) {
- dup2(fpipe[0], STDIN_FILENO);
- close(fpipe[0]);
- }
- dev = play->dstr != NULL ? shquote(play->dstr) : NULL;
- if ((cp = malloc(strlen(cmd) + (dev == NULL ? 0 :
- 3 + strlen(dev) + 1) + (play->bsize > 0 ?
- 3 + ichars(play->bsize) + 1 : 0))) == NULL)
- err(EX_OSERR,
- "Could not allocate command string");
- i = 0;
- i += sprintf(cp + i, "%s", cmd);
- if (dev != NULL) {
- i += sprintf(cp + i, " -d%s", dev);
- free(dev);
- }
- if (play->bsize > 0)
- i += sprintf(cp + i, " -b%zu", play->bsize);
- execl("/bin/sh", "sh", "-c", cp, NULL);
- err(EX_OSERR, "Could not execute '%s'", argv[0]);
- }
- close(fpipe[0]);
- *cfd = cpipe[1];
- *ifd = ipipe[0];
- return (0);
- }
- int
- linefd(struct line *line, int fd)
- {
- ssize_t r;
- int n;
- if (ioctl(fd, FIONREAD, &n) == -1)
- return (-1);
- errno = 0;
- for (; n > 0 && (r = read(fd, line->count >= line->max ?
- line->buffer : allot((void **)&line->buffer, &line->size,
- line->count, 1, LINEDELTA), 1)) > 0; n--) {
- if (line->count >= line->max && !line->warn) {
- line->warn = 1;
- warnx("Line too long");
- }
- if (line->count >= line->max) {
- if (*line->buffer == '\n') {
- line->warn = 0;
- line->count = 0;
- }
- } else if (line->buffer[line->count] == '\n')
- goto out;
- else
- line->count++;
- }
- if (errno == 0)
- errno = EAGAIN;
- return (-1);
- out:
- *(char *)allot((void **)&line->buffer, &line->size, line->count,
- 1, LINEDELTA) = '\0';
- r = line->count;
- line->count = 0;
- return (r);
- }
- int
- parsecmd(struct cmd *cmd, char *s)
- {
- char *p;
- assert(cmd != NULL && s != NULL);
- while (isspace(*s))
- s++;
- if (*s >= '0' && *s <= '9') {
- errno = 0;
- cmd->count = strtoul(s, &p, 10);
- if (errno != 0) {
- warn("parsecmd");
- return (-1);
- }
- s += p - s;
- } else
- cmd->count = 1;
- while (isspace(*s))
- s++;
- cmd->cmd = *s;
- if (*s != '\0')
- while (isspace(*++s))
- /* do nothing */;
- cmd->arg = s;
- return (cmd->cmd);
- }
- int
- parseinfo(struct info *info, const char *s)
- {
- char *p;
- size_t i;
- assert(info != NULL && s != NULL);
- while (isspace(*s))
- s++;
- if (*s == '>') {
- errno = ERANGE;
- return (-1);
- }
- errno = 0;
- info->cint = strtoul(s, &p, 10);
- if (errno != 0)
- return (-1);
- s += p - s;
- info->cdec[0] = '\0';
- if (*s == '.') {
- for (i = 1, s++; *s >= '0' && *s <= '9'; s++)
- if (i < sizeof(info->cdec) - 1)
- info->cdec[i++] = *s;
- if (i > 1)
- info->cdec[0] = '.';
- do
- info->cdec[i] = '\0';
- while (info->cdec[--i] == '0');
- if (info->cdec[i] == '.')
- info->cdec[i] = '\0';
- }
- if (*s != '/') {
- errno = EINVAL;
- return (-1);
- }
- s++;
- errno = 0;
- info->tint = strtoul(s, &p, 10);
- if (errno == 0) {
- s += p - s;
- info->tdec[0] = '\0';
- if (*s == '.') {
- for (i = 1, s++; *s >= '0' && *s <= '9'; s++)
- if (i < sizeof(info->tdec) - 1)
- info->tdec[i++] = *s;
- if (i > 1)
- info->tdec[0] = '.';
- do
- info->tdec[i] = '\0';
- while (info->tdec[--i] == '0');
- if (info->tdec[i] == '.')
- info->tdec[i] = '\0';
- }
- } else
- (void)strcpy(info->tdec, "?");
- return (0);
- }
- int
- parseseek(struct cmd *cmd, double *out)
- {
- double d = 0;
- const char *s;
- char *p;
- assert(cmd != NULL && out != NULL);
- if ((s = cmd->arg) == NULL || *s == '\0')
- return (-1);
- do {
- errno = 0;
- d += strtod(s, &p);
- if (errno != 0 || s == p || (*p != ':' && *p != '\0')) {
- warnx("Invalid number '%s'", cmd->arg);
- return (-1);
- }
- for (s += p - s; *s == ':'; s++)
- d *= 60;
- } while (*s != '\0');
- *out = (cmd->cmd == '-' ? -d : d) * cmd->count;
- return (0);
- }
- void
- printinfo(int fd, struct info *info, const char *file, int raw)
- {
- if (raw) {
- if (info->tdec[0] != '?')
- (void)dprintf(fd, ":%lu%s/%lu%s\n", info->cint,
- info->cdec, info->tint, info->tdec);
- else
- (void)dprintf(fd, ":%lu%s\n", info->cint,
- info->cdec);
- return;
- }
- if (file != NULL)
- (void)dprintf(fd, "%s: ", file);
- if (info->tint >= 3600) {
- (void)dprintf(fd,
- "%lu:%.2lu:%.2lu%s/%lu:%.2lu:%.2lu%s\n",
- info->cint / 3600, info->cint / 60 % 60,
- info->cint % 60, info->cdec, info->tint / 3600,
- info->tint / 60 % 60, info->tint % 60, info->tdec);
- } else if (info->tint >= 60) {
- (void)dprintf(fd, "%lu:%.2lu%s/%lu:%.2lu%s\n",
- info->cint / 60, info->cint % 60, info->cdec,
- info->tint / 60, info->tint % 60, info->tdec);
- } else if (info->tdec[0] != '?') {
- (void)dprintf(fd, "%lu%s/%lu%s\n", info->cint,
- info->cdec, info->tint, info->tdec);
- } else if (info->cint >= 3600) {
- (void)dprintf(fd, "%lu:%.2lu:%.2lu%s\n",
- info->cint / 3600, info->cint / 60 % 60,
- info->cint % 60, info->cdec);
- } else if (info->cint >= 60) {
- (void)dprintf(fd, "%lu:%.2lu%s\n", info->cint / 60,
- info->cint % 60, info->cdec);
- } else
- (void)dprintf(fd, "%lu%s\n", info->cint, info->cdec);
- }
- char *
- shquote(const char *s)
- {
- char *r;
- size_t i;
- size_t n;
- assert(s != NULL);
- for (i = 0, n = 2; s[i] != '\0'; i++, n++)
- if (s[i] == '\'')
- n += 3;
- if ((r = malloc(n + 1)) == NULL)
- err(EX_OSERR, "Could not allocate quoted string");
- r[0] = '\'';
- for (i = 0, n = 1; s[i] != '\0'; i++, n++) {
- if (s[i] == '\'') {
- r[n++] = '\'';
- r[n++] = '\\';
- r[n++] = '\'';
- }
- r[n] = s[i];
- }
- r[n] = '\0';
- return (r);
- }
- int
- ichars(int v)
- {
- int r = 1;
- if (v < 0) {
- r++;
- v = -v;
- }
- for (r = 1; v >= 10; v /= 10, r++)
- /* do nothing */;
- return (r);
- }
- int
- unreservefd(int fd)
- {
- int nfd;
- int fl;
- if (fd < 0 || !RESERVED_FD(fd))
- return (fd);
- for (nfd = 0; RESERVED_FD(nfd) ||
- fcntl(nfd, F_GETFL, &fl) != -1; nfd++)
- if (nfd == INT_MAX)
- errc(EX_SOFTWARE, ENFILE,
- "Could not find a file descriptor");
- if (dup2(fd, nfd) == -1)
- err(EX_OSERR, "Could not duplicate a file descriptor");
- close(fd);
- return (nfd);
- }
|