file2au.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. #include <assert.h>
  2. #include <errno.h>
  3. #include <inttypes.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 <time.h>
  12. #include <fcntl.h>
  13. #include <poll.h>
  14. #include <unistd.h>
  15. #ifdef USE_CAPSICUM
  16. #include <sys/capsicum.h>
  17. #endif
  18. #include <err.h>
  19. #include <sysexits.h>
  20. #include "au.h"
  21. #include "read/afile.h"
  22. #include "common.h"
  23. #ifndef NAMEMAX
  24. #define NAMEMAX 10
  25. #endif
  26. #ifndef CBSIZE
  27. #define CBSIZE 64
  28. #endif
  29. #ifndef BSIZE
  30. #define BSIZE 0x1000
  31. #endif
  32. #ifdef SIGINFO
  33. #define IFINFO(x) (x)
  34. #else
  35. #define IFINFO(x)
  36. #endif
  37. enum parse_state {
  38. PARSE_DEFAULT,
  39. PARSE_SET,
  40. PARSE_NUMBER,
  41. PARSE_DECIMAL,
  42. PARSE_ZEE
  43. };
  44. enum {
  45. PARSE_SEEK = 1,
  46. PARSE_INFO,
  47. PARSE_PLAYPAUSE,
  48. PARSE_HZ,
  49. PARSE_QUIT
  50. };
  51. #define PARSE_INITIALIZER { { 0 }, { 0 }, PARSE_DEFAULT, 0, 0, 0, 0, 0 }
  52. struct parse {
  53. char buffer[CBSIZE];
  54. struct timespec ts;
  55. enum parse_state state;
  56. int whence;
  57. int negative;
  58. int index;
  59. int count;
  60. int decimals;
  61. };
  62. #ifdef SIGINFO
  63. static size_t info_bytes = 0;
  64. #endif
  65. #ifdef SIGINFO
  66. static void siginfo(int);
  67. #endif
  68. static void printts(int, struct timespec);
  69. static int parsefd(int, struct parse *);
  70. int
  71. main(int argc, char *argv[])
  72. {
  73. #ifdef USE_CAPSICUM
  74. cap_rights_t rights;
  75. #endif
  76. #ifdef USE_MODE
  77. char *amode = NULL;
  78. #endif
  79. #ifdef USE_QUALITY
  80. double quality = 1;
  81. #endif
  82. struct pollfd fds[2] = {
  83. { STDIN_FILENO, POLLIN, 0 },
  84. { -1, POLLIN, 0 }
  85. };
  86. struct parse parse = PARSE_INITIALIZER;
  87. struct au au = AU_INITIALIZER;
  88. struct afile *a;
  89. struct timespec ts;
  90. const char *cfile = NULL;
  91. const char *ifile = NULL;
  92. int cfd = -1;
  93. int ifd = -1;
  94. ssize_t i;
  95. ssize_t r;
  96. ssize_t t;
  97. size_t bsize = BSIZE;
  98. time_t ihz = -1;
  99. size_t bsi = 0;
  100. size_t bps;
  101. void *b;
  102. char *p;
  103. int vflag = 0;
  104. int first = 1;
  105. int c;
  106. while ((c = getopt(argc, argv, "C:I:b:c:i:v")) != -1)
  107. switch (c) {
  108. case 'C':
  109. cfile = NULL;
  110. cfd = strtoul(optarg, &p, 0);
  111. if (p == optarg || *p != '\0')
  112. errx(EX_USAGE, "Invalid number '%s'",
  113. optarg);
  114. if (cfd == STDIN_FILENO ||
  115. cfd == STDOUT_FILENO ||
  116. cfd == STDERR_FILENO ||
  117. fcntl(cfd, F_GETFL, &c) == -1)
  118. errx(EX_USAGE,
  119. "Invalid file-descriptor %d", cfd);
  120. break;
  121. case 'I':
  122. ifile = NULL;
  123. ifd = strtoul(optarg, &p, 0);
  124. if (p == optarg || *p != '\0')
  125. errx(EX_USAGE, "Invalid number '%s'",
  126. optarg);
  127. if (ifd == STDIN_FILENO ||
  128. ifd == STDOUT_FILENO ||
  129. ifd == STDERR_FILENO ||
  130. fcntl(ifd, F_GETFL, &c) == -1)
  131. errx(EX_USAGE,
  132. "Invalid file-descriptor %d", ifd);
  133. break;
  134. case 'b':
  135. bsize = strtoul(optarg, &p, 0);
  136. if (p == optarg || *p != '\0')
  137. errx(EX_USAGE, "Invalid number '%s'",
  138. optarg);
  139. if (bsize < 0x400)
  140. errx(EX_USAGE, "The buffer size (%zu) "
  141. "is too small", bsize);
  142. break;
  143. case 'c':
  144. cfd = -1;
  145. cfile = optarg;
  146. break;
  147. case 'i':
  148. ifd = -1;
  149. ifile = optarg;
  150. break;
  151. case 'v':
  152. vflag = 1;
  153. break;
  154. default:
  155. goto usage;
  156. }
  157. if ((b = malloc(bsize)) == NULL)
  158. err(EX_OSERR, "Could not allocate the buffer");
  159. IFINFO((void)signal(SIGINFO, &siginfo));
  160. if (cfile != NULL)
  161. /* Use O_RDWR to make poll(2) work well with FIFOs */
  162. if ((cfd = open(cfile, O_RDWR | O_CLOEXEC)) < 0)
  163. warn("Could not open '%s'", cfile);
  164. if (ifile != NULL)
  165. if ((ifd = open(ifile, O_APPEND | O_CREAT | O_CLOEXEC))
  166. < 0)
  167. warn("Could not open '%s'", ifile);
  168. #ifdef USE_PLEDGE
  169. pledge("stdio", NULL);
  170. #endif
  171. #ifdef USE_CAPSICUM
  172. if (cap_enter() == -1 && errno != ENOSYS)
  173. err(EX_OSERR, "Could not enter capability mode");
  174. (void)cap_rights_init(&rights, CAP_READ, CAP_EVENT, CAP_FCNTL,
  175. CAP_FSTAT, CAP_SEEK);
  176. if (cap_rights_limit(STDIN_FILENO, &rights) == -1 &&
  177. errno != ENOSYS)
  178. err(EX_OSERR, "Could not limit the standard input");
  179. (void)cap_rights_init(&rights, CAP_EVENT, CAP_FCNTL, CAP_FSTAT,
  180. CAP_READ, CAP_WRITE);
  181. if (cfd >= 0 && cap_rights_limit(cfd, &rights) == -1 &&
  182. errno != ENOSYS)
  183. err(EX_OSERR, "Could not limit the command input");
  184. (void)cap_rights_init(&rights, CAP_WRITE);
  185. if (cap_rights_limit(STDERR_FILENO, &rights) == -1 &&
  186. errno != ENOSYS)
  187. err(EX_OSERR, "Could not limit the standard error");
  188. (void)cap_rights_set(&rights, CAP_EVENT, CAP_FCNTL);
  189. if (cap_rights_limit(STDOUT_FILENO, &rights) == -1 &&
  190. errno != ENOSYS)
  191. err(EX_OSERR, "Could not limit the standard output");
  192. if (ifd >= 0 && cap_rights_limit(ifd, &rights) == -1 &&
  193. errno != ENOSYS)
  194. err(EX_OSERR, "Could not limit the informative output");
  195. #endif /* USE_CAPSICUM */
  196. if ((a = af_fdopen(STDIN_FILENO)) == NULL)
  197. err(EX_DATAERR, "Could not initialize audio codec");
  198. if (setblock(STDIN_FILENO, 0) != 0 ||
  199. setblock(STDOUT_FILENO, 0) != 0 ||
  200. (cfd >= 0 && setblock(cfd, 0) != 0))
  201. err(EX_OSERR, "Could not set O_NONBLOCK");
  202. if (ifd < 0)
  203. ifd = STDERR_FILENO;
  204. #ifdef USE_CAPSICUM
  205. /* CAP_FCNTL is no longer needed */
  206. (void)cap_rights_init(&rights, CAP_READ, CAP_EVENT, CAP_FSTAT,
  207. CAP_SEEK);
  208. if (cap_rights_limit(STDIN_FILENO, &rights) == -1 &&
  209. errno != ENOSYS)
  210. err(EX_OSERR, "Could not limit the standard input");
  211. (void)cap_rights_init(&rights, CAP_WRITE, CAP_EVENT);
  212. if (cap_rights_limit(STDOUT_FILENO, &rights) == -1 &&
  213. errno != ENOSYS)
  214. err(EX_OSERR, "Could not limit the standard output");
  215. (void)cap_rights_init(&rights, CAP_EVENT, CAP_READ, CAP_WRITE);
  216. if (cfd >= 0 && cap_rights_limit(cfd, &rights) == -1 &&
  217. errno != ENOSYS)
  218. err(EX_OSERR, "Could not limit the command input");
  219. #endif
  220. fds[1].fd = cfd;
  221. while ((c = poll(fds, 1 + (cfd >= 0), INFTIM)) >= 0 ||
  222. errno == EINTR) {
  223. if (c <= 0)
  224. continue;
  225. if (fds[0].revents & POLLIN) {
  226. errno = 0;
  227. if (first)
  228. r = af_read(a, (char *)b + AU_SIZE,
  229. bsize - AU_SIZE);
  230. else
  231. r = af_read(a, b, bsize);
  232. if (r == 0 || (r < 0 && errno != EAGAIN &&
  233. errno != EINTR))
  234. break;
  235. if (r > 0) {
  236. if (first) {
  237. if (af_get(a,
  238. "encoding rate channels",
  239. &au.au_enc, &au.au_rate,
  240. &au.au_chan) != 3)
  241. err(EX_SOFTWARE,
  242. "Could not get "
  243. "properties");
  244. if (cfd >= 0) {
  245. ts.tv_sec = 0;
  246. ts.tv_nsec = 0;
  247. ts = af_seek(a, ts,
  248. SEEK_CUR);
  249. }
  250. if (cfd < 0 || ts.tv_sec < 0 ||
  251. ts.tv_nsec < 0)
  252. (void)af_get(a, "size",
  253. &au.au_size);
  254. (void)au_puthdr(&au, b);
  255. r += AU_SIZE;
  256. bps = au_ssize(&au) *
  257. (size_t)au.au_rate;
  258. first = 0;
  259. }
  260. i = 0;
  261. fds[0].fd = STDOUT_FILENO;
  262. fds[0].events = POLLOUT;
  263. }
  264. } else if (fds[0].revents & POLLOUT) {
  265. if ((t = write(STDOUT_FILENO, (char *)b + i,
  266. r - i)) == 0 || (r < 0 && errno != EAGAIN &&
  267. errno != EINTR))
  268. break;
  269. IFINFO(info_bytes += t);
  270. if (r > 0 && (i += t) >= r) {
  271. if (vflag && ifd >= 0 &&
  272. (ihz < 0 || (ihz > 0 &&
  273. (bsi += t) > bps / ihz))) {
  274. bsi = 0;
  275. ts.tv_sec = ts.tv_nsec = 0;
  276. printts(ifd, af_seek(a, ts,
  277. SEEK_CUR));
  278. (void)write(ifd, "/", 1);
  279. printts(ifd, af_length(a));
  280. (void)write(ifd, "\n", 1);
  281. }
  282. fds[0].fd = STDIN_FILENO;
  283. fds[0].events = POLLIN;
  284. }
  285. }
  286. if (fds[0].revents & (POLLHUP | POLLERR | POLLNVAL)) {
  287. /* Finish reading any buffered audio. */
  288. if ((r = af_read(a, b, bsize)) <= 0)
  289. break;
  290. fds[0].fd = STDOUT_FILENO;
  291. fds[0].events = POLLOUT;
  292. }
  293. if (cfd >= 0 && (fds[1].revents & POLLIN)) {
  294. while ((c = parsefd(cfd, &parse)) > 0)
  295. switch (c) {
  296. case PARSE_SEEK:
  297. ts = af_seek(a, parse.ts,
  298. parse.whence);
  299. if (ts.tv_sec < 0 ||
  300. ts.tv_nsec < 0) {
  301. if (errno == ERANGE)
  302. goto write_ret;
  303. warn("seek error");
  304. }
  305. if (vflag) {
  306. printts(ifd, ts);
  307. (void)write(ifd,
  308. "/", 1);
  309. printts(ifd,
  310. af_length(a));
  311. (void)write(ifd,
  312. "\n", 1);
  313. }
  314. break;
  315. case PARSE_INFO:
  316. ts.tv_sec = ts.tv_nsec = 0;
  317. printts(ifd, af_seek(a, ts,
  318. SEEK_CUR));
  319. (void)write(ifd, "/", 1);
  320. printts(ifd, af_length(a));
  321. (void)write(ifd, "\n", 1);
  322. break;
  323. case PARSE_PLAYPAUSE:
  324. fds[0].fd = fds[0].fd == -1 ?
  325. STDIN_FILENO : -1;
  326. break;
  327. case PARSE_HZ:
  328. ihz = parse.ts.tv_sec;
  329. break;
  330. case PARSE_QUIT:
  331. goto write_ret;
  332. }
  333. if (c == -1 && errno != EAGAIN &&
  334. errno != EINTR)
  335. fds[1].revents |= POLLHUP;
  336. }
  337. if (cfd >= 0 && (fds[1].revents & POLLHUP))
  338. (void)close(cfd);
  339. if (cfd >= 0 && (fds[1].revents &
  340. (POLLHUP | POLLERR | POLLNVAL))) {
  341. fds[1].fd = -1;
  342. if (cfile == NULL || fds[0].fd == -1)
  343. (void)close(STDIN_FILENO);
  344. }
  345. }
  346. write_ret:
  347. af_close(a);
  348. free(b);
  349. return (EX_OK);
  350. usage:
  351. c = strlen(getprogname()) + 8;
  352. (void)fprintf(stderr,
  353. "usage: %s [-C command-fd] [-I info-fd] [-b buffer-size]\n"
  354. "%*s[-c command-file] [-i info-file]\n", getprogname(), c,
  355. "");
  356. return (EX_USAGE);
  357. }
  358. #ifdef SIGINFO
  359. void
  360. siginfo(int sig)
  361. {
  362. /* XXXX2au: XXXXXXiB processed */
  363. char b[NAMEMAX + 2 + 8 + 11];
  364. size_t i;
  365. i = strlen(getprogname());
  366. if (i > NAMEMAX) {
  367. (void)memcpy(b, getprogname(), NAMEMAX - 3);
  368. (void)memcpy(b + NAMEMAX - 3, "...", 3);
  369. i = NAMEMAX;
  370. } else
  371. (void)memcpy(b, getprogname(), i);
  372. (void)memcpy(b + i, ": ", 2);
  373. i += 2;
  374. i += szwrite(b + i, info_bytes, "B", "iB");
  375. (void)memcpy(b + i, " processed\n", 11);
  376. i += 11;
  377. (void)write(STDERR_FILENO, b, i);
  378. }
  379. #endif
  380. void
  381. printts(int fd, struct timespec ts)
  382. {
  383. char b[20]; /* >SSSSSSSSS.NNNNNNNNN */
  384. int i = sizeof(b);
  385. int n;
  386. if (fd < 0)
  387. return;
  388. if (ts.tv_sec > 999999999) {
  389. for (i = 1; i < 10; i++)
  390. b[i] = '9';
  391. b[0] = '>';
  392. write(fd, b, i);
  393. }
  394. if (ts.tv_nsec > 0) {
  395. for (n = 9; ts.tv_nsec % 10 == 0; ts.tv_nsec /= 10, n--)
  396. /* do nothing */;
  397. i -= n;
  398. while (ts.tv_nsec > 0) {
  399. b[i + --n] = ts.tv_nsec % 10 + '0';
  400. ts.tv_nsec /= 10;
  401. }
  402. while (n > 0)
  403. b[i + --n] = '0';
  404. b[--i] = '.';
  405. }
  406. do {
  407. b[--i] = ts.tv_sec % 10 + '0';
  408. ts.tv_sec /= 10;
  409. } while (ts.tv_sec > 0);
  410. (void)write(fd, b + i, sizeof(b) - i);
  411. }
  412. int
  413. parsefd(int fd, struct parse *p)
  414. {
  415. ssize_t r;
  416. int c;
  417. assert(p != NULL);
  418. for (;;) {
  419. if (p->index >= p->count) {
  420. if ((r = read(fd, p->buffer,
  421. sizeof(p->buffer))) <= 0)
  422. return (r == 0 ? 0 : -1);
  423. p->index = 0;
  424. p->count = (int)r;
  425. }
  426. while (p->index < p->count) {
  427. c = p->buffer[p->index];
  428. switch (p->state) {
  429. case PARSE_DEFAULT:
  430. if (c == '=') {
  431. p->state = PARSE_SET;
  432. p->negative = 0;
  433. } else if (c == '-' || c == '+') {
  434. p->state = PARSE_NUMBER;
  435. p->negative = c == '-';
  436. p->whence = SEEK_CUR;
  437. p->ts.tv_sec = 0;
  438. p->ts.tv_nsec = 0;
  439. } else if (c >= '0' && c <= '9') {
  440. p->state = PARSE_NUMBER;
  441. p->negative = 0;
  442. p->whence = SEEK_SET;
  443. p->ts.tv_sec = c - '0';
  444. p->ts.tv_nsec = 0;
  445. } else if (c == '.') {
  446. p->state = PARSE_DECIMAL;
  447. p->negative = 0;
  448. p->whence = SEEK_SET;
  449. p->ts.tv_sec = 0;
  450. p->ts.tv_nsec = 0;
  451. } else if (c == '?') {
  452. p->index++;
  453. return (PARSE_INFO);
  454. } else if (c == 'P' || c == 'p') {
  455. p->index++;
  456. return (PARSE_PLAYPAUSE);
  457. } else if (c == 'Q' || c == 'q') {
  458. p->index++;
  459. return (PARSE_QUIT);
  460. }
  461. break;
  462. case PARSE_SET:
  463. if (c == '-' || c == '+') {
  464. p->state = PARSE_NUMBER;
  465. p->whence = c == '-' ?
  466. SEEK_END : SEEK_SET;
  467. p->ts.tv_sec = 0;
  468. } else if (c >= '0' && c <= '9') {
  469. p->state = PARSE_NUMBER;
  470. p->whence = SEEK_SET;
  471. p->ts.tv_sec = c - '0';
  472. } else if (c == '.') {
  473. p->state = PARSE_DECIMAL;
  474. p->whence = SEEK_SET;
  475. p->ts.tv_sec = 0;
  476. p->ts.tv_nsec = 0;
  477. p->decimals = 9;
  478. } else if (c != '=') {
  479. p->state = PARSE_DEFAULT;
  480. p->index--;
  481. }
  482. break;
  483. case PARSE_NUMBER:
  484. if (c >= '0' && c <= '9')
  485. p->ts.tv_sec = p->ts.tv_sec * 10
  486. + (c - '0');
  487. else if (c == '.') {
  488. p->state = PARSE_DECIMAL;
  489. p->decimals = 9;
  490. p->ts.tv_nsec = 0;
  491. } else if (c == 'h') {
  492. p->state = PARSE_ZEE;
  493. } else if (p->ts.tv_sec == 0 &&
  494. p->whence == SEEK_CUR) {
  495. p->state = PARSE_DEFAULT;
  496. p->index--;
  497. } else {
  498. p->state = PARSE_DEFAULT;
  499. if (p->negative)
  500. p->ts.tv_sec =
  501. -p->ts.tv_sec;
  502. p->ts.tv_nsec = 0;
  503. return (PARSE_SEEK);
  504. }
  505. break;
  506. case PARSE_DECIMAL:
  507. if (c >= '0' && c <= '9') {
  508. if (p->decimals > 0) {
  509. p->ts.tv_nsec =
  510. p->ts.tv_nsec * 10 +
  511. (c - '0');
  512. p->decimals--;
  513. }
  514. } else if (p->ts.tv_sec == 0 &&
  515. p->ts.tv_nsec == 0 &&
  516. p->whence == SEEK_CUR) {
  517. p->state = PARSE_DEFAULT;
  518. p->index--;
  519. } else {
  520. p->state = PARSE_DEFAULT;
  521. for (; p->decimals > 0;
  522. p->decimals--)
  523. p->ts.tv_nsec *= 10;
  524. if (p->negative) {
  525. p->ts.tv_sec =
  526. -p->ts.tv_sec;
  527. p->ts.tv_nsec =
  528. -p->ts.tv_nsec;
  529. }
  530. return (PARSE_SEEK);
  531. }
  532. break;
  533. case PARSE_ZEE:
  534. p->state = PARSE_DEFAULT;
  535. if (c == 'z')
  536. return (PARSE_HZ);
  537. break;
  538. }
  539. p->index++;
  540. }
  541. }
  542. }