auplayer.c 27 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235
  1. #include <assert.h>
  2. #include <ctype.h>
  3. #include <errno.h>
  4. #include <limits.h>
  5. #include <signal.h>
  6. #include <stddef.h>
  7. #include <stdint.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <sys/types.h>
  12. #include <sys/ioctl.h>
  13. #include <sys/stat.h>
  14. #include <sys/wait.h>
  15. #include <dirent.h>
  16. #include <fcntl.h>
  17. #include <fnmatch.h>
  18. #include <glob.h>
  19. #include <poll.h>
  20. #include <regex.h>
  21. #include <unistd.h>
  22. #include <err.h>
  23. #include <sysexits.h>
  24. #include "playlist.h"
  25. #include "util.h"
  26. #ifndef nitems
  27. #define nitems(x) (sizeof((x)) / sizeof((x)[0]))
  28. #endif
  29. #ifndef ERR
  30. #define ERR(e, r) \
  31. do { \
  32. errno = (e); \
  33. return (r); \
  34. } while (0)
  35. #endif
  36. #define RESERVED_FD(fd) \
  37. (((fd) == STDIN_FILENO) || \
  38. ((fd) == STDOUT_FILENO) || \
  39. ((fd) == STDERR_FILENO))
  40. #define FDELTA 4
  41. #define LINEDELTA 32
  42. #ifndef CMDMAX
  43. #define CMDMAX (PATH_MAX * 16)
  44. #endif
  45. #ifndef INFOMAX
  46. #define INFOMAX 64
  47. #endif
  48. #ifndef INFODEC
  49. #define INFODEC 2
  50. #endif
  51. #ifndef HSIZE
  52. #define HSIZE 32
  53. #endif
  54. #ifndef AUDEV_DEFAULT
  55. #define AUDEV_DEFAULT "exec au2sdl"
  56. #endif
  57. enum {
  58. PLAY_DONE = 0,
  59. PLAY_OK,
  60. PLAY_RETRY
  61. };
  62. struct fentry {
  63. char *path;
  64. int fd;
  65. int flags;
  66. };
  67. #define LINE_INITIALIZER { NULL, 0, 0, SIZE_MAX - 1, 0 }
  68. struct line {
  69. char *buffer;
  70. size_t count;
  71. size_t size;
  72. size_t max;
  73. int warn;
  74. };
  75. #define CMD_INITIALIZER { NULL, 0, '\0' }
  76. struct cmd {
  77. char *arg;
  78. unsigned long count;
  79. int cmd;
  80. };
  81. #define INFO_INITIALIZER { { '\0' }, { '?' }, 0, 0 }
  82. struct info {
  83. char cdec[INFODEC + 2];
  84. char tdec[INFODEC + 2];
  85. unsigned long cint;
  86. unsigned long tint;
  87. };
  88. #define PLAY_INITIALIZER { \
  89. NULL, LINE_INITIALIZER, LINE_INITIALIZER, \
  90. INFO_INITIALIZER, AUDEV_DEFAULT, NULL, { 0 }, \
  91. 0, 100, STDIN_FILENO, STDOUT_FILENO, 0, 0, 0, \
  92. PLRONCE \
  93. }
  94. struct play {
  95. struct playlist *plist;
  96. struct line iline;
  97. struct line cline;
  98. struct info info;
  99. char *cstr;
  100. char *dstr;
  101. struct timeval tv;
  102. size_t bsize;
  103. unsigned long ihz;
  104. int in;
  105. int out;
  106. int paused;
  107. int loop;
  108. int retry;
  109. int flags;
  110. };
  111. static pid_t auopen_pid = 0;
  112. static pid_t audev_pid = 0;
  113. static sigset_t sigpend;
  114. static void sighandle(int);
  115. static void onadd(void *, const char *);
  116. static void onrem(void *, const char *);
  117. static int playlist_fpadd(struct playlist *, FILE *, int);
  118. static int play_single(struct play *, sigset_t *);
  119. static int play_exec(struct play *, int *, int *);
  120. static int linefd(struct line *, int);
  121. static int parsecmd(struct cmd *, char *);
  122. static int parseinfo(struct info *, const char *);
  123. static int parseseek(struct cmd *, double *);
  124. static void printinfo(int, struct info *, const char *, int);
  125. static char *shquote(const char *);
  126. static int ichars(int);
  127. static int unreservefd(int);
  128. int
  129. main(int argc, char *argv[])
  130. {
  131. struct play play = PLAY_INITIALIZER;
  132. struct fentry *fs = NULL;
  133. struct fentry *f;
  134. FILE *fp;
  135. char *in = NULL;
  136. char *p;
  137. sigset_t omask;
  138. sigset_t smask;
  139. size_t fcount = 0;
  140. size_t fsize = 0;
  141. size_t hsize = HSIZE;
  142. size_t i;
  143. unsigned long ul;
  144. int c;
  145. #ifdef USE_PLEDGE
  146. (void)pledge("stdio rpath proc exec", NULL);
  147. #endif
  148. if (setvbuf(stdout, NULL, _IOLBF, 0) != 0)
  149. err(EX_IOERR, "Could not set line-buffered output");
  150. play.in = -1;
  151. play.cline.max = CMDMAX;
  152. play.iline.max = INFOMAX;
  153. while ((c = getopt(argc, argv, "F:I:b:c:d:f:g:i:h:lprsv"))
  154. != -1)
  155. switch (c) {
  156. case 'F':
  157. errno = 0;
  158. ul = strtoul(optarg, &p, 0);
  159. if (errno == 0) {
  160. if (ul > INT_MAX)
  161. errno = ERANGE;
  162. else if (p == optarg || *p != '\0')
  163. errno = EINVAL;
  164. else if (ul == STDOUT_FILENO ||
  165. ul == STDERR_FILENO)
  166. errno = EDOM;
  167. }
  168. if (errno != 0)
  169. err(EX_USAGE, "Invalid number '%s'",
  170. optarg);
  171. if (fcntl(ul, F_GETFL, &c) == -1)
  172. err(EX_NOINPUT,
  173. "Invalid file-descriptor %lu", ul);
  174. f = allot((void **)&fs, &fsize, fcount++,
  175. sizeof(struct fentry), FDELTA);
  176. f->path = NULL;
  177. f->fd = ul;
  178. break;
  179. case 'I':
  180. errno = 0;
  181. ul = strtoul(optarg, &p, 0);
  182. if (errno == 0) {
  183. if (ul > INT_MAX)
  184. errno = ERANGE;
  185. else if (p == optarg || *p != '\0')
  186. errno = EINVAL;
  187. else if (ul == STDOUT_FILENO ||
  188. ul == STDERR_FILENO)
  189. errno = EDOM;
  190. }
  191. if (errno != 0)
  192. err(EX_USAGE, "Invalid number '%s'",
  193. optarg);
  194. if (fcntl(ul, F_GETFL, &c) == -1)
  195. err(EX_NOINPUT,
  196. "Invalid file-descriptor %lu", ul);
  197. in = NULL;
  198. play.in = ul;
  199. break;
  200. case 'b':
  201. errno = 0;
  202. ul = strtoul(optarg, &p, 0);
  203. if (ul > INT_MAX)
  204. errno = ERANGE;
  205. if (p == optarg || *p != '\0')
  206. errno = EINVAL;
  207. if (errno != 0)
  208. err(EX_USAGE, "Invalid number %s",
  209. optarg);
  210. play.bsize = ul;
  211. break;
  212. case 'c':
  213. play.cstr = optarg[0] != '\0' ? optarg : NULL;
  214. break;
  215. case 'd':
  216. play.dstr = optarg;
  217. break;
  218. case 'f':
  219. f = allot((void **)&fs, &fsize, fcount++,
  220. sizeof(struct fentry), FDELTA);
  221. f->flags = PLGNONE | PLSCMP;
  222. if (strcmp(optarg, "-") == 0) {
  223. f->path = NULL;
  224. f->fd = STDIN_FILENO;
  225. } else {
  226. f->path = optarg;
  227. f->fd = -1;
  228. }
  229. break;
  230. case 'g':
  231. f = allot((void **)&fs, &fsize, fcount++,
  232. sizeof(struct fentry), FDELTA);
  233. f->flags = PLGNONE | PLSGLOB;
  234. if (strcmp(optarg, "-") == 0) {
  235. f->path = NULL;
  236. f->fd = STDIN_FILENO;
  237. } else {
  238. f->path = optarg;
  239. f->fd = -1;
  240. }
  241. break;
  242. case 'i':
  243. if (strcmp(optarg, "-") == 0) {
  244. in = NULL;
  245. play.in = STDIN_FILENO;
  246. } else {
  247. in = optarg;
  248. play.in = -1;
  249. }
  250. break;
  251. case 'h':
  252. errno = 0;
  253. ul = strtoul(optarg, &p, 0);
  254. if (ul > SIZE_MAX)
  255. errno = ERANGE;
  256. if (p == optarg || *p != '\0')
  257. errno = EINVAL;
  258. if (errno != 0 || p == optarg || *p != '\0')
  259. err(EX_USAGE, "Invalid number %s",
  260. optarg);
  261. hsize = ul;
  262. break;
  263. case 'l':
  264. play.loop = 1;
  265. break;
  266. case 'p':
  267. play.flags = (play.flags & ~PLVTYPE) | PLVFULL;
  268. break;
  269. case 'r':
  270. play.flags = (play.flags & ~PLGTYPE) | PLGRAND;
  271. break;
  272. case 's':
  273. play.flags = (play.flags & ~PLGTYPE) | PLGSHUF;
  274. break;
  275. case 'v':
  276. play.flags = (play.flags & ~PLVTYPE)
  277. | PLVSIMPLE;
  278. break;
  279. default:
  280. goto usage;
  281. }
  282. argc -= optind;
  283. argv += optind;
  284. if (play.in >= 0) {
  285. for (i = 0; i < fcount; i++)
  286. if (play.in == fs[i].fd)
  287. errx(EX_USAGE, "Ambiguous file-"
  288. "descriptor %d", fs[i].fd);
  289. } else if (in != NULL) {
  290. if ((play.in = open(in, O_RDONLY | O_CLOEXEC)) < 0)
  291. err(EX_NOINPUT, "Could not open %s", in);
  292. } else {
  293. for (i = 0, play.in = STDIN_FILENO; i < fcount; i++)
  294. if (fs[i].fd == STDIN_FILENO) {
  295. if ((play.flags & PLVTYPE) == PLVFULL)
  296. errx(EX_USAGE, "The standard "
  297. "input can not be used as "
  298. "a file-list");
  299. if ((play.in = open("/dev/tty",
  300. O_RDONLY | O_CLOEXEC)) < 0)
  301. err(EX_NOINPUT,
  302. "Could not open /dev/tty");
  303. break;
  304. }
  305. }
  306. if ((play.plist = playlist_create(hsize, play.flags)) == NULL)
  307. err(EX_OSERR, "playlist_create");;
  308. if ((play.flags & PLVTYPE) == PLVFULL) {
  309. playlist_onadd(play.plist, &onadd, &play);
  310. playlist_onrem(play.plist, &onrem, &play);
  311. }
  312. if (fs != NULL) {
  313. for (i = 0; i < fcount; i++) {
  314. if (fs[i].path != NULL) {
  315. if ((fp = fopen(fs[i].path, "r"))
  316. == NULL)
  317. warn("Could not open %s",
  318. fs[i].path);
  319. } else if (fs[i].fd >= 0) {
  320. if ((fp = fdopen(fs[i].fd, "r"))
  321. == NULL)
  322. warn("Could not open file-"
  323. "descriptor %d", fs[i].fd);
  324. } else
  325. abort();
  326. if (fp == NULL)
  327. break;
  328. (void)playlist_fpadd(play.plist, fp,
  329. f[i].flags | (play.flags & ~PLGTYPE &
  330. ~PLSTYPE));
  331. fclose(fp);
  332. }
  333. free(fs);
  334. }
  335. for (; *argv != NULL; argv++)
  336. switch ((*argv)[0]) {
  337. case '(':
  338. switch (toupper((*argv)[1])) {
  339. case '\0':
  340. playlist_gbegin(play.plist, PLGNORM);
  341. break;
  342. case 'R':
  343. playlist_gbegin(play.plist, PLGRAND);
  344. break;
  345. case 'S':
  346. playlist_gbegin(play.plist, PLGSHUF);
  347. break;
  348. default:
  349. warnx("Invalid group type: %s",
  350. (*argv) + 1);
  351. break;
  352. }
  353. break;
  354. case ')':
  355. if (playlist_gend(play.plist) != 0)
  356. warnx("Unexpected `)'");
  357. break;
  358. default:
  359. (void)playlist_add(play.plist, *argv,
  360. (play.flags & ~PLGTYPE & ~PLSTYPE) |
  361. PLSCMP);
  362. }
  363. if (play.in < 0 && (play.in = open("/dev/tty", O_RDONLY)) < 0)
  364. err(EX_NOINPUT, "Could not open /dev/tty");
  365. sigemptyset(&smask);
  366. if (sigprocmask(SIG_SETMASK, &smask, &omask) != 0)
  367. err(EX_OSERR, "sigprocmask");
  368. smask = omask;
  369. sigemptyset(&sigpend);
  370. /*
  371. * Ignore SIGTTIN to allow continuous
  372. * operation as a background process
  373. */
  374. if (signal(SIGTTIN, SIG_IGN) == SIG_ERR)
  375. abort();
  376. /*
  377. * SIGCONT is needed if the audio backend disconnects
  378. * while the process is stopped.
  379. */
  380. if (signal(SIGCONT, &sighandle) == SIG_ERR)
  381. abort();
  382. if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
  383. abort();
  384. #ifdef SIGINFO
  385. if (signal(SIGINFO, &sighandle) == SIG_ERR)
  386. abort();
  387. #endif
  388. if ((play.flags & PLVTYPE) != PLVFULL) {
  389. playlist_onadd(play.plist, &onadd, &play);
  390. playlist_onrem(play.plist, &onrem, &play);
  391. }
  392. while ((c = play_single(&play, &smask)) > 0)
  393. /* do nothing */;
  394. playlist_onadd(play.plist, NULL, NULL);
  395. playlist_onrem(play.plist, NULL, NULL);
  396. playlist_destroy(play.plist);
  397. if (play.cline.buffer != NULL)
  398. free(play.cline.buffer);
  399. if (play.iline.buffer != NULL)
  400. free(play.iline.buffer);
  401. return (c ? EX_SOFTWARE : 0);
  402. usage:
  403. (void)fprintf(stderr, "usage: auplayer [-lprsv] "
  404. "[-F file-descriptor] [-I file-descriptor]\n"
  405. " [-b buffer-size] [-c command] [-d device] "
  406. "[-f file-list]\n"
  407. " [-h history-size] [-i input-file] "
  408. "file ...\n");
  409. return (EX_USAGE);
  410. }
  411. void
  412. sighandle(int sig)
  413. {
  414. (void)sigaddset(&sigpend, sig);
  415. }
  416. void
  417. onadd(void *ctx, const char *path)
  418. {
  419. struct play *play = ctx;
  420. assert(ctx != NULL && path != NULL);
  421. switch (play->flags & PLVTYPE) {
  422. case PLVSIMPLE:
  423. (void)dprintf(play->out, "Adding %s\n", path);
  424. break;
  425. case PLVFULL:
  426. (void)dprintf(play->out, "+%s\n", path);
  427. break;
  428. }
  429. }
  430. void
  431. onrem(void *ctx, const char *path)
  432. {
  433. struct play *play = ctx;
  434. assert(ctx != NULL && path != NULL);
  435. switch (play->flags & PLVTYPE) {
  436. case PLVSIMPLE:
  437. (void)dprintf(play->out, "Removing %s\n", path);
  438. break;
  439. case PLVFULL:
  440. (void)dprintf(play->out, "-%s\n", path);
  441. break;
  442. }
  443. }
  444. int
  445. playlist_fpadd(struct playlist *list, FILE *fp, int flags)
  446. {
  447. char *cp = NULL;
  448. ssize_t r;
  449. size_t s = 0;
  450. size_t i;
  451. assert(list != NULL && fp != NULL);
  452. while ((r = getdelim(&cp, &s, '\n', fp)) != -1) {
  453. for (i = 0; cp[i] != '\0' && isspace(cp[i]); i++)
  454. /* do nothing */;
  455. if (i > 0) {
  456. r -= i;
  457. (void)memmove(cp, cp + i, r + 1);
  458. }
  459. if (r > 0) {
  460. for (i = r - 1; i > 0 && isspace(cp[i]); i--)
  461. /* do nothing */;
  462. r = i + 1;
  463. cp[r] = '\0';
  464. }
  465. if (r == 0)
  466. continue;
  467. switch(cp[0]) {
  468. case '(':
  469. switch (toupper(cp[1])) {
  470. case '\0':
  471. playlist_gbegin(list, PLGNORM);
  472. break;
  473. case 'R':
  474. playlist_gbegin(list, PLGRAND);
  475. break;
  476. case 'S':
  477. playlist_gbegin(list, PLGSHUF);
  478. break;
  479. default:
  480. warnx("Invalid group type: %s", cp + 1);
  481. break;
  482. }
  483. break;
  484. case ')':
  485. if (playlist_gend(list) != 0)
  486. warnx("Unexpected `)'");
  487. break;
  488. default:
  489. (void)playlist_add(list, cp, flags);
  490. break;
  491. }
  492. }
  493. if (cp != NULL)
  494. free(cp);
  495. return (0);
  496. }
  497. int
  498. play_single(struct play *play, sigset_t *smask)
  499. {
  500. struct pollfd fds[2] = {
  501. { -1, POLLIN, 0 }, { -1, POLLIN, 0 }
  502. };
  503. struct cmd cmd = CMD_INITIALIZER;
  504. const char *cur;
  505. ssize_t r;
  506. double d;
  507. int ttyin;
  508. int cfd = -1;
  509. int docmd = 0;
  510. int dead = 0;
  511. int done = 0;
  512. int status;
  513. assert(play != NULL && smask != NULL);
  514. assert(play->in >= 0 && play->out >= 0);
  515. if ((cur = playlist_current(play->plist)) == NULL &&
  516. (cur = playlist_next(play->plist)) == NULL) {
  517. if (play->loop) {
  518. playlist_loop(play->plist);
  519. if ((cur = playlist_next(play->plist))
  520. == NULL && play->cstr != NULL) {
  521. play->retry = 0;
  522. return (PLAY_DONE);
  523. }
  524. } else if (play->cstr != NULL) {
  525. play->retry = 0;
  526. return (PLAY_DONE);
  527. }
  528. }
  529. if ((play->flags & PLVTYPE) == PLVFULL && cur != NULL)
  530. (void)dprintf(play->out, "@%s\n", cur);
  531. if (play->cstr != NULL) {
  532. if (play_exec(play, &cfd, &fds[1].fd) != 0) {
  533. play->retry = 0;
  534. return (-1);
  535. }
  536. } else {
  537. if ((cfd = open("/dev/null", O_RDONLY)) == -1) {
  538. play->retry = 0;
  539. return (-1);
  540. }
  541. fds[1].fd = -1;
  542. }
  543. if (play->paused)
  544. (void)dprintf(cfd, "p\n");
  545. if ((play->flags & PLVTYPE) == PLVFULL)
  546. (void)dprintf(cfd, "%luhz\n", play->ihz);
  547. if (play->retry) {
  548. (void)dprintf(cfd, "=%lu%s\n", play->info.cint,
  549. play->info.cdec);
  550. (void)warnx("retry: %lu%s\n", play->info.cint,
  551. play->info.cdec);
  552. play->retry = 0;
  553. }
  554. if ((play->flags & PLVTYPE) == PLVSIMPLE ||
  555. (play->paused && (play->flags & PLVTYPE) == PLVFULL))
  556. (void)dprintf(cfd, "?\n");
  557. play->info.cdec[0] = '\0';
  558. play->info.tdec[0] = '\0';
  559. play->info.cint = 0;
  560. play->info.tint = 0;
  561. ttyin = isatty(play->in);
  562. fds[0].fd = play->in;
  563. for (;;) {
  564. /*
  565. * ppoll(2) generates a events for tty input
  566. * even when reading would block with SIGTTIN.
  567. * This behaviour is quite noisy when using
  568. * a tty in raw character mode.
  569. */
  570. if (ppoll(fds, nitems(fds), NULL, smask) < 0) {
  571. if (errno != EINTR)
  572. goto err;
  573. if (sigismember(&sigpend, SIGCONT)) {
  574. play->retry = 1;
  575. sigdelset(&sigpend, SIGCONT);
  576. }
  577. #ifdef SIGINFO
  578. if (sigismember(&sigpend, SIGINFO)) {
  579. (void)dprintf(cfd, "?\n");
  580. sigdelset(&sigpend, SIGINFO);
  581. }
  582. #endif
  583. continue;
  584. }
  585. if (fds[0].revents &
  586. (POLLERR | POLLHUP | POLLNVAL)) {
  587. (void)kill(auopen_pid, SIGTERM);
  588. (void)kill(audev_pid, SIGTERM);
  589. fds[0].fd = -1;
  590. done = 1;
  591. }
  592. if (fds[1].revents &
  593. (POLLERR | POLLHUP | POLLNVAL)) {
  594. if (waitpid(auopen_pid, &status, 0)
  595. != auopen_pid)
  596. goto err;
  597. dead |= WEXITSTATUS(status);
  598. if (waitpid(audev_pid, &status, 0) != audev_pid)
  599. goto err;
  600. dead |= WEXITSTATUS(status);
  601. break;
  602. }
  603. if (fds[0].revents & POLLIN) {
  604. while (!(docmd || done || dead) &&
  605. (r = linefd(&play->cline, fds[0].fd)) > 0)
  606. switch (parsecmd(&cmd,
  607. play->cline.buffer)) {
  608. case '\0':
  609. break;
  610. case '(':
  611. switch (toupper(cmd.arg[0])) {
  612. case '\0':
  613. playlist_gbegin(
  614. play->plist,
  615. PLGNORM);
  616. break;
  617. case 'R':
  618. playlist_gbegin(
  619. play->plist,
  620. PLGRAND);
  621. break;
  622. case 'S':
  623. playlist_gbegin(
  624. play->plist,
  625. PLGSHUF);
  626. break;
  627. default:
  628. warnx("Invalid group "
  629. "type: %s",
  630. cmd.arg);
  631. }
  632. break;
  633. case ')':
  634. if (playlist_gend(play->plist)
  635. != 0)
  636. warnx("Unexpected `)'");
  637. break;
  638. case '+':
  639. /* FALLTHROUGH */
  640. case '-':
  641. if (parseseek(&cmd, &d) == 0)
  642. (void)dprintf(cfd,
  643. "%+f\n", d);
  644. if ((play->flags & PLVTYPE)
  645. == PLVSIMPLE)
  646. (void)dprintf(cfd,
  647. "?\n");
  648. break;
  649. case '=':
  650. if (parseseek(&cmd, &d) == 0)
  651. (void)dprintf(cfd,
  652. "=%c%f\n",
  653. strchr(cmd.arg, '-')
  654. == NULL ? '+' : '-',
  655. d < 0 ? -d : d);
  656. if ((play->flags & PLVTYPE)
  657. == PLVSIMPLE)
  658. (void)dprintf(cfd,
  659. "?\n");
  660. break;
  661. case '/':
  662. cmd.cmd = '\0';
  663. docmd = playlist_search(
  664. play->plist, cmd.arg,
  665. PLSAUTO | PLGOTO |
  666. (play->loop ? PLLOOP : 0))
  667. != NULL;
  668. break;
  669. case '?':
  670. (void)dprintf(cfd, "?\n");
  671. break;
  672. case 'L':
  673. play->loop = !play->loop;
  674. (void)dprintf(play->out,
  675. "%sloop\n", play->loop ?
  676. "" : "no-");
  677. break;
  678. case 'V':
  679. switch (play->flags & PLVTYPE) {
  680. case PLVQUIET:
  681. play->flags =
  682. (play->flags &
  683. ~PLVTYPE) |
  684. PLVSIMPLE;
  685. (void)dprintf(play->out,
  686. "verbose\n");
  687. break;
  688. case PLVSIMPLE:
  689. play->flags =
  690. (play->flags &
  691. ~PLVTYPE) |
  692. PLVQUIET;
  693. (void)dprintf(play->out,
  694. "quiet\n");
  695. break;
  696. }
  697. break;
  698. case 'a':
  699. if (fds[1].fd >= 0)
  700. (void)playlist_add(
  701. play->plist,
  702. cmd.arg,
  703. (play->flags &
  704. ~PLGTYPE &
  705. ~PLSTYPE) |
  706. PLSGLOB);
  707. else
  708. playlist_append(
  709. play->plist,
  710. cmd.arg);
  711. break;
  712. case 'd':
  713. if (cmd.arg[0] == '\0') {
  714. dead = 1;
  715. play->retry = 0;
  716. break;
  717. }
  718. playlist_rem(play->plist,
  719. cmd.arg, PLSGLOB);
  720. if (playlist_current(
  721. play->plist) == NULL) {
  722. cmd.cmd = '\0';
  723. docmd = 1;
  724. }
  725. break;
  726. case 'h':
  727. if (cmd.count > 0)
  728. docmd = 1;
  729. break;
  730. case 'i':
  731. (void)dprintf(cfd, "?\n");
  732. break;
  733. case 'j':
  734. /* FALLTHROUGH */
  735. case 'k':
  736. /* FALLTHROUGH */
  737. case 'l':
  738. if (cmd.count > 0)
  739. docmd = 1;
  740. break;
  741. case 'p':
  742. (void)dprintf(cfd, "p\n");
  743. switch (play->flags & PLVTYPE) {
  744. case PLVSIMPLE:
  745. (void)dprintf(play->out,
  746. "%spaused\n",
  747. play->paused ?
  748. "un" : "");
  749. break;
  750. case PLVFULL:
  751. (void)dprintf(play->out,
  752. "!%spaused\n",
  753. play->paused ?
  754. "un" : "");
  755. break;
  756. }
  757. play->paused = !play->paused;
  758. break;
  759. case 'q':
  760. done = 1;
  761. break;
  762. case 'r':
  763. (void)dprintf(cfd, "=0\n");
  764. break;
  765. case 'z':
  766. play->ihz = cmd.count;
  767. if ((play->flags & PLVTYPE)
  768. == PLVFULL)
  769. (void)dprintf(play->out,
  770. "!hz %lu\n",
  771. play->ihz);
  772. (void)dprintf(cfd, "%luhz\n",
  773. play->ihz);
  774. break;
  775. default:
  776. warnx("Invalid command: '%s'",
  777. play->cline.buffer);
  778. break;
  779. }
  780. /*
  781. * When SIGTTIN is ignored read(2), on
  782. * a tty, returns with EIO, to allow use
  783. * as a background process this is
  784. * case is ignored.
  785. */
  786. if (r < 0 && errno != EAGAIN &&
  787. (!ttyin || errno != EIO)) {
  788. if (errno == 0)
  789. done = 1;
  790. else
  791. warn("command input");
  792. }
  793. if (docmd || done || dead) {
  794. if (fds[1].fd == -1)
  795. break;
  796. (void)kill(auopen_pid, SIGTERM);
  797. (void)kill(audev_pid, SIGTERM);
  798. fds[0].fd = -1;
  799. }
  800. }
  801. if (fds[1].revents & POLLIN) {
  802. while ((r = linefd(&play->iline, fds[1].fd))
  803. > 0)
  804. if (parseinfo(&play->info,
  805. play->iline.buffer) == 0)
  806. printinfo(play->out,
  807. &play->info, cur,
  808. (play->flags & PLVTYPE)
  809. == PLVFULL);
  810. else
  811. warn("parseinfo: %s",
  812. play->iline.buffer);
  813. if (r < 0 && errno != 0 && errno != EAGAIN)
  814. warn("linefd");
  815. }
  816. }
  817. (void)close(cfd);
  818. if (fds[1].fd >= 0)
  819. (void)close(fds[1].fd);
  820. if (dead) {
  821. if (play->retry)
  822. return (PLAY_RETRY);
  823. if (cur != NULL)
  824. playlist_rem(play->plist, cur, PLSCMP);
  825. cmd.cmd = '\0';
  826. docmd = 1;
  827. }
  828. if (done)
  829. return (PLAY_DONE);
  830. if (!docmd) {
  831. cmd.cmd = 'l';
  832. cmd.count = 1;
  833. }
  834. switch (cmd.cmd) {
  835. case '\0':
  836. break;
  837. case 'h':
  838. /* FALLTHROUGH */
  839. case 'j':
  840. for (; cmd.count > 0; cmd.count--)
  841. playlist_prev(play->plist);
  842. break;
  843. case 'k':
  844. /* FALLTHROUGH */
  845. case 'l':
  846. for (; cmd.count > 0; cmd.count--)
  847. if (playlist_next(play->plist) == NULL) {
  848. if (!play->loop)
  849. return (PLAY_DONE);
  850. playlist_loop(play->plist);
  851. if (playlist_next(play->plist) == NULL)
  852. return (PLAY_DONE);
  853. }
  854. break;
  855. default:
  856. abort(); /* NOTREACHED */
  857. }
  858. return (PLAY_OK);
  859. err:
  860. close(cfd);
  861. close(fds[1].fd);
  862. return (-1);
  863. }
  864. int
  865. play_exec(struct play *play, int *cfd, int *ifd)
  866. {
  867. /*
  868. * auopen <file> -C <cfd> -I <ifd> [-b <bsize>] [-v] |
  869. * <cmd> -R [-b <bsize> [-d <dev>]
  870. */
  871. const char *cur;
  872. char *argv[10] = { NULL };
  873. char *cmd;
  874. char *dev;
  875. int cpipe[2];
  876. int fpipe[2];
  877. int ipipe[2];
  878. char *cp;
  879. size_t i;
  880. assert(play != NULL && cfd != NULL && ifd != NULL);
  881. if ((cur = playlist_current(play->plist)) == NULL)
  882. return (-1);
  883. if (pipe(cpipe) != 0)
  884. err(EX_OSERR, "pipe(2)");
  885. if (pipe(fpipe) != 0)
  886. err(EX_OSERR, "pipe(2)");
  887. if (pipe(ipipe) != 0)
  888. err(EX_OSERR, "pipe(2)");
  889. cpipe[0] = unreservefd(cpipe[0]);
  890. ipipe[1] = unreservefd(ipipe[1]);
  891. if ((auopen_pid = fork()) < 0) {
  892. err(EX_OSERR, "Could not fork auopen(1)");
  893. } else if (auopen_pid == 0) {
  894. close(cpipe[1]);
  895. close(fpipe[0]);
  896. close(ipipe[0]);
  897. if (fpipe[1] != STDOUT_FILENO) {
  898. dup2(fpipe[1], STDOUT_FILENO);
  899. close(fpipe[1]);
  900. }
  901. if ((cp = malloc(ichars(cpipe[0]) + 1 +
  902. ichars(ipipe[1]) + 1 + (play->bsize > 0 ?
  903. ichars(play->bsize) + 1 : 0))) == NULL)
  904. err(EX_OSERR, "Could not allocate argv");
  905. i = 0;
  906. argv[i++] = "auopen";
  907. argv[i++] = (char *)cur;
  908. argv[i++] = "-C";
  909. argv[i++] = cp;
  910. cp += sprintf(cp, "%d", cpipe[0]) + 1;
  911. argv[i++] = "-I";
  912. argv[i++] = cp;
  913. cp += sprintf(cp, "%d", ipipe[1]) + 1;
  914. if ((play->flags & PLVTYPE) == PLVFULL)
  915. argv[i++] = "-v";
  916. if (play->bsize > 0) {
  917. argv[i++] = "-b";
  918. argv[i++] = cp;
  919. cp += sprintf(cp, "%zu", play->bsize);
  920. }
  921. execvp("auopen", argv);
  922. err(EX_OSERR, "Could not execute '%s %s'", "auopen",
  923. cur != NULL ? cur : "(null)");
  924. }
  925. close(cpipe[0]);
  926. close(fpipe[1]);
  927. close(ipipe[1]);
  928. cmd = play->cstr != NULL ? play->cstr : AUDEV_DEFAULT;
  929. if ((audev_pid = fork()) < 0) {
  930. err(EX_OSERR, "Could not fork '%s'", cmd);
  931. } else if (audev_pid == 0) {
  932. close(cpipe[1]);
  933. close(ipipe[0]);
  934. if (fpipe[0] != STDIN_FILENO) {
  935. dup2(fpipe[0], STDIN_FILENO);
  936. close(fpipe[0]);
  937. }
  938. dev = play->dstr != NULL ? shquote(play->dstr) : NULL;
  939. if ((cp = malloc(strlen(cmd) + (dev == NULL ? 0 :
  940. 3 + strlen(dev) + 1) + (play->bsize > 0 ?
  941. 3 + ichars(play->bsize) + 1 : 0))) == NULL)
  942. err(EX_OSERR,
  943. "Could not allocate command string");
  944. i = 0;
  945. i += sprintf(cp + i, "%s", cmd);
  946. if (dev != NULL) {
  947. i += sprintf(cp + i, " -d%s", dev);
  948. free(dev);
  949. }
  950. if (play->bsize > 0)
  951. i += sprintf(cp + i, " -b%zu", play->bsize);
  952. execl("/bin/sh", "sh", "-c", cp, NULL);
  953. err(EX_OSERR, "Could not execute '%s'", argv[0]);
  954. }
  955. close(fpipe[0]);
  956. *cfd = cpipe[1];
  957. *ifd = ipipe[0];
  958. return (0);
  959. }
  960. int
  961. linefd(struct line *line, int fd)
  962. {
  963. ssize_t r;
  964. int n;
  965. if (ioctl(fd, FIONREAD, &n) == -1)
  966. return (-1);
  967. errno = 0;
  968. for (; n > 0 && (r = read(fd, line->count >= line->max ?
  969. line->buffer : allot((void **)&line->buffer, &line->size,
  970. line->count, 1, LINEDELTA), 1)) > 0; n--) {
  971. if (line->count >= line->max && !line->warn) {
  972. line->warn = 1;
  973. warnx("Line too long");
  974. }
  975. if (line->count >= line->max) {
  976. if (*line->buffer == '\n') {
  977. line->warn = 0;
  978. line->count = 0;
  979. }
  980. } else if (line->buffer[line->count] == '\n')
  981. goto out;
  982. else
  983. line->count++;
  984. }
  985. if (errno == 0)
  986. errno = EAGAIN;
  987. return (-1);
  988. out:
  989. *(char *)allot((void **)&line->buffer, &line->size, line->count,
  990. 1, LINEDELTA) = '\0';
  991. r = line->count;
  992. line->count = 0;
  993. return (r);
  994. }
  995. int
  996. parsecmd(struct cmd *cmd, char *s)
  997. {
  998. char *p;
  999. assert(cmd != NULL && s != NULL);
  1000. while (isspace(*s))
  1001. s++;
  1002. if (*s >= '0' && *s <= '9') {
  1003. errno = 0;
  1004. cmd->count = strtoul(s, &p, 10);
  1005. if (errno != 0) {
  1006. warn("parsecmd");
  1007. return (-1);
  1008. }
  1009. s += p - s;
  1010. } else
  1011. cmd->count = 1;
  1012. while (isspace(*s))
  1013. s++;
  1014. cmd->cmd = *s;
  1015. if (*s != '\0')
  1016. while (isspace(*++s))
  1017. /* do nothing */;
  1018. cmd->arg = s;
  1019. return (cmd->cmd);
  1020. }
  1021. int
  1022. parseinfo(struct info *info, const char *s)
  1023. {
  1024. char *p;
  1025. size_t i;
  1026. assert(info != NULL && s != NULL);
  1027. while (isspace(*s))
  1028. s++;
  1029. if (*s == '>') {
  1030. errno = ERANGE;
  1031. return (-1);
  1032. }
  1033. errno = 0;
  1034. info->cint = strtoul(s, &p, 10);
  1035. if (errno != 0)
  1036. return (-1);
  1037. s += p - s;
  1038. info->cdec[0] = '\0';
  1039. if (*s == '.') {
  1040. for (i = 1, s++; *s >= '0' && *s <= '9'; s++)
  1041. if (i < sizeof(info->cdec) - 1)
  1042. info->cdec[i++] = *s;
  1043. if (i > 1)
  1044. info->cdec[0] = '.';
  1045. do
  1046. info->cdec[i] = '\0';
  1047. while (info->cdec[--i] == '0');
  1048. if (info->cdec[i] == '.')
  1049. info->cdec[i] = '\0';
  1050. }
  1051. if (*s != '/') {
  1052. errno = EINVAL;
  1053. return (-1);
  1054. }
  1055. s++;
  1056. errno = 0;
  1057. info->tint = strtoul(s, &p, 10);
  1058. if (errno == 0) {
  1059. s += p - s;
  1060. info->tdec[0] = '\0';
  1061. if (*s == '.') {
  1062. for (i = 1, s++; *s >= '0' && *s <= '9'; s++)
  1063. if (i < sizeof(info->tdec) - 1)
  1064. info->tdec[i++] = *s;
  1065. if (i > 1)
  1066. info->tdec[0] = '.';
  1067. do
  1068. info->tdec[i] = '\0';
  1069. while (info->tdec[--i] == '0');
  1070. if (info->tdec[i] == '.')
  1071. info->tdec[i] = '\0';
  1072. }
  1073. } else
  1074. (void)strcpy(info->tdec, "?");
  1075. return (0);
  1076. }
  1077. int
  1078. parseseek(struct cmd *cmd, double *out)
  1079. {
  1080. double d = 0;
  1081. const char *s;
  1082. char *p;
  1083. assert(cmd != NULL && out != NULL);
  1084. if ((s = cmd->arg) == NULL || *s == '\0')
  1085. return (-1);
  1086. do {
  1087. errno = 0;
  1088. d += strtod(s, &p);
  1089. if (errno != 0 || s == p || (*p != ':' && *p != '\0')) {
  1090. warnx("Invalid number '%s'", cmd->arg);
  1091. return (-1);
  1092. }
  1093. for (s += p - s; *s == ':'; s++)
  1094. d *= 60;
  1095. } while (*s != '\0');
  1096. *out = (cmd->cmd == '-' ? -d : d) * cmd->count;
  1097. return (0);
  1098. }
  1099. void
  1100. printinfo(int fd, struct info *info, const char *file, int raw)
  1101. {
  1102. if (raw) {
  1103. if (info->tdec[0] != '?')
  1104. (void)dprintf(fd, ":%lu%s/%lu%s\n", info->cint,
  1105. info->cdec, info->tint, info->tdec);
  1106. else
  1107. (void)dprintf(fd, ":%lu%s\n", info->cint,
  1108. info->cdec);
  1109. return;
  1110. }
  1111. if (file != NULL)
  1112. (void)dprintf(fd, "%s: ", file);
  1113. if (info->tint >= 3600) {
  1114. (void)dprintf(fd,
  1115. "%lu:%.2lu:%.2lu%s/%lu:%.2lu:%.2lu%s\n",
  1116. info->cint / 3600, info->cint / 60 % 60,
  1117. info->cint % 60, info->cdec, info->tint / 3600,
  1118. info->tint / 60 % 60, info->tint % 60, info->tdec);
  1119. } else if (info->tint >= 60) {
  1120. (void)dprintf(fd, "%lu:%.2lu%s/%lu:%.2lu%s\n",
  1121. info->cint / 60, info->cint % 60, info->cdec,
  1122. info->tint / 60, info->tint % 60, info->tdec);
  1123. } else if (info->tdec[0] != '?') {
  1124. (void)dprintf(fd, "%lu%s/%lu%s\n", info->cint,
  1125. info->cdec, info->tint, info->tdec);
  1126. } else if (info->cint >= 3600) {
  1127. (void)dprintf(fd, "%lu:%.2lu:%.2lu%s\n",
  1128. info->cint / 3600, info->cint / 60 % 60,
  1129. info->cint % 60, info->cdec);
  1130. } else if (info->cint >= 60) {
  1131. (void)dprintf(fd, "%lu:%.2lu%s\n", info->cint / 60,
  1132. info->cint % 60, info->cdec);
  1133. } else
  1134. (void)dprintf(fd, "%lu%s\n", info->cint, info->cdec);
  1135. }
  1136. char *
  1137. shquote(const char *s)
  1138. {
  1139. char *r;
  1140. size_t i;
  1141. size_t n;
  1142. assert(s != NULL);
  1143. for (i = 0, n = 2; s[i] != '\0'; i++, n++)
  1144. if (s[i] == '\'')
  1145. n += 3;
  1146. if ((r = malloc(n + 1)) == NULL)
  1147. err(EX_OSERR, "Could not allocate quoted string");
  1148. r[0] = '\'';
  1149. for (i = 0, n = 1; s[i] != '\0'; i++, n++) {
  1150. if (s[i] == '\'') {
  1151. r[n++] = '\'';
  1152. r[n++] = '\\';
  1153. r[n++] = '\'';
  1154. }
  1155. r[n] = s[i];
  1156. }
  1157. r[n] = '\0';
  1158. return (r);
  1159. }
  1160. int
  1161. ichars(int v)
  1162. {
  1163. int r = 1;
  1164. if (v < 0) {
  1165. r++;
  1166. v = -v;
  1167. }
  1168. for (r = 1; v >= 10; v /= 10, r++)
  1169. /* do nothing */;
  1170. return (r);
  1171. }
  1172. int
  1173. unreservefd(int fd)
  1174. {
  1175. int nfd;
  1176. int fl;
  1177. if (fd < 0 || !RESERVED_FD(fd))
  1178. return (fd);
  1179. for (nfd = 0; RESERVED_FD(nfd) ||
  1180. fcntl(nfd, F_GETFL, &fl) != -1; nfd++)
  1181. if (nfd == INT_MAX)
  1182. errc(EX_SOFTWARE, ENFILE,
  1183. "Could not find a file descriptor");
  1184. if (dup2(fd, nfd) == -1)
  1185. err(EX_OSERR, "Could not duplicate a file descriptor");
  1186. close(fd);
  1187. return (nfd);
  1188. }