1*dbbc8c3fScheloha /* $OpenBSD: midicat.c,v 1.7 2022/12/02 22:36:34 cheloha Exp $ */
2424bdd43Sratchov /*
3424bdd43Sratchov * Copyright (c) 2015 Alexandre Ratchov <alex@caoua.org>
4424bdd43Sratchov *
5424bdd43Sratchov * Permission to use, copy, modify, and distribute this software for any
6424bdd43Sratchov * purpose with or without fee is hereby granted, provided that the above
7424bdd43Sratchov * copyright notice and this permission notice appear in all copies.
8424bdd43Sratchov *
9424bdd43Sratchov * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10424bdd43Sratchov * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11424bdd43Sratchov * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12424bdd43Sratchov * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13424bdd43Sratchov * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14424bdd43Sratchov * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15424bdd43Sratchov * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16424bdd43Sratchov */
17424bdd43Sratchov #include <err.h>
18424bdd43Sratchov #include <fcntl.h>
19424bdd43Sratchov #include <sndio.h>
20424bdd43Sratchov #include <stdio.h>
21424bdd43Sratchov #include <stdlib.h>
22424bdd43Sratchov #include <unistd.h>
23424bdd43Sratchov #include <string.h>
24424bdd43Sratchov
250b869d14Scheloha void __dead usage(void);
26424bdd43Sratchov
27424bdd43Sratchov int
main(int argc,char ** argv)28424bdd43Sratchov main(int argc, char **argv)
29424bdd43Sratchov {
30424bdd43Sratchov #define MIDI_BUFSZ 1024
31424bdd43Sratchov unsigned char buf[MIDI_BUFSZ];
32424bdd43Sratchov struct mio_hdl *ih, *oh;
33424bdd43Sratchov char *port0, *port1, *ifile, *ofile;
34424bdd43Sratchov int ifd, ofd;
35424bdd43Sratchov int dump, c, i, len, n, sep, mode;
36424bdd43Sratchov
37424bdd43Sratchov dump = 0;
38424bdd43Sratchov port0 = port1 = ifile = ofile = NULL;
39424bdd43Sratchov ih = oh = NULL;
40424bdd43Sratchov ifd = ofd = -1;
41424bdd43Sratchov
42424bdd43Sratchov while ((c = getopt(argc, argv, "di:o:q:")) != -1) {
43424bdd43Sratchov switch (c) {
44424bdd43Sratchov case 'd':
45424bdd43Sratchov dump = 1;
46424bdd43Sratchov break;
47424bdd43Sratchov case 'q':
48424bdd43Sratchov if (port0 == NULL)
49424bdd43Sratchov port0 = optarg;
50424bdd43Sratchov else if (port1 == NULL)
51424bdd43Sratchov port1 = optarg;
52*dbbc8c3fScheloha else
53*dbbc8c3fScheloha errx(1, "too many -q options");
54424bdd43Sratchov break;
55424bdd43Sratchov case 'i':
56424bdd43Sratchov ifile = optarg;
57424bdd43Sratchov break;
58424bdd43Sratchov case 'o':
59424bdd43Sratchov ofile = optarg;
60424bdd43Sratchov break;
61424bdd43Sratchov default:
620b869d14Scheloha usage();
63424bdd43Sratchov }
64424bdd43Sratchov }
65424bdd43Sratchov argc -= optind;
66424bdd43Sratchov argv += optind;
670b869d14Scheloha
680b869d14Scheloha if (argc != 0)
690b869d14Scheloha usage();
70424bdd43Sratchov
71424bdd43Sratchov /* we don't support more than one data flow */
72*dbbc8c3fScheloha if (ifile != NULL && ofile != NULL)
73*dbbc8c3fScheloha errx(1, "-i and -o are exclusive");
74424bdd43Sratchov
75424bdd43Sratchov /* second port makes sense only for port-to-port transfers */
76*dbbc8c3fScheloha if (port1 != NULL && !(ifile == NULL && ofile == NULL))
77*dbbc8c3fScheloha errx(1, "too many -q options");
78424bdd43Sratchov
79424bdd43Sratchov /* if there're neither files nor ports, then we've nothing to do */
80424bdd43Sratchov if (port0 == NULL && ifile == NULL && ofile == NULL)
810b869d14Scheloha usage();
82424bdd43Sratchov
83424bdd43Sratchov /* if no port specified, use default one */
84424bdd43Sratchov if (port0 == NULL)
85424bdd43Sratchov port0 = MIO_PORTANY;
86424bdd43Sratchov
87424bdd43Sratchov /* open input or output file (if any) */
88424bdd43Sratchov if (ifile) {
897671136aScheloha if (strcmp(ifile, "-") == 0) {
907671136aScheloha ifile = "stdin";
91424bdd43Sratchov ifd = STDIN_FILENO;
927671136aScheloha } else {
93b7041c07Sderaadt ifd = open(ifile, O_RDONLY);
94*dbbc8c3fScheloha if (ifd == -1)
95*dbbc8c3fScheloha err(1, "%s", ifile);
96424bdd43Sratchov }
97424bdd43Sratchov } else if (ofile) {
987671136aScheloha if (strcmp(ofile, "-") == 0) {
997671136aScheloha ofile = "stdout";
100424bdd43Sratchov ofd = STDOUT_FILENO;
1017671136aScheloha } else {
102424bdd43Sratchov ofd = open(ofile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
103*dbbc8c3fScheloha if (ofd == -1)
104*dbbc8c3fScheloha err(1, "%s", ofile);
105424bdd43Sratchov }
106424bdd43Sratchov }
107424bdd43Sratchov
108424bdd43Sratchov /* open first port for input and output (if output needed) */
109424bdd43Sratchov if (ofile)
110424bdd43Sratchov mode = MIO_IN;
111424bdd43Sratchov else if (ifile)
112424bdd43Sratchov mode = MIO_OUT;
113424bdd43Sratchov else if (port1 == NULL)
114424bdd43Sratchov mode = MIO_IN | MIO_OUT;
115424bdd43Sratchov else
116424bdd43Sratchov mode = MIO_IN;
117424bdd43Sratchov ih = mio_open(port0, mode, 0);
118*dbbc8c3fScheloha if (ih == NULL)
119*dbbc8c3fScheloha errx(1, "%s: couldn't open port", port0);
120424bdd43Sratchov
121424bdd43Sratchov /* open second port, output only */
122424bdd43Sratchov if (port1 == NULL)
123424bdd43Sratchov oh = ih;
124424bdd43Sratchov else {
125424bdd43Sratchov oh = mio_open(port1, MIO_OUT, 0);
126*dbbc8c3fScheloha if (oh == NULL)
127*dbbc8c3fScheloha errx(1, "%s: couldn't open port", port1);
128424bdd43Sratchov }
129424bdd43Sratchov
1303aaa63ebSderaadt if (pledge("stdio", NULL) == -1)
131424bdd43Sratchov err(1, "pledge");
132424bdd43Sratchov
133424bdd43Sratchov /* transfer until end-of-file or error */
134424bdd43Sratchov for (;;) {
135424bdd43Sratchov if (ifile != NULL) {
136424bdd43Sratchov len = read(ifd, buf, sizeof(buf));
137424bdd43Sratchov if (len == 0)
138424bdd43Sratchov break;
1393aaa63ebSderaadt if (len == -1) {
140*dbbc8c3fScheloha warn("%s", ifile);
141424bdd43Sratchov break;
142424bdd43Sratchov }
143424bdd43Sratchov } else {
144424bdd43Sratchov len = mio_read(ih, buf, sizeof(buf));
145424bdd43Sratchov if (len == 0) {
146*dbbc8c3fScheloha warnx("%s: disconnected", port0);
147424bdd43Sratchov break;
148424bdd43Sratchov }
149424bdd43Sratchov }
150424bdd43Sratchov if (ofile != NULL) {
151424bdd43Sratchov n = write(ofd, buf, len);
152424bdd43Sratchov if (n != len) {
153*dbbc8c3fScheloha warn("%s: short write", ofile);
154424bdd43Sratchov break;
155424bdd43Sratchov }
156424bdd43Sratchov } else {
157424bdd43Sratchov n = mio_write(oh, buf, len);
158424bdd43Sratchov if (n != len) {
159*dbbc8c3fScheloha warnx("%s: disconnected", port1);
160424bdd43Sratchov break;
161424bdd43Sratchov }
162424bdd43Sratchov }
163424bdd43Sratchov if (dump) {
164424bdd43Sratchov for (i = 0; i < len; i++) {
165424bdd43Sratchov sep = (i % 16 == 15 || i == len - 1) ?
166424bdd43Sratchov '\n' : ' ';
167424bdd43Sratchov fprintf(stderr, "%02x%c", buf[i], sep);
168424bdd43Sratchov }
169424bdd43Sratchov }
170424bdd43Sratchov }
171424bdd43Sratchov
172424bdd43Sratchov /* clean-up */
173424bdd43Sratchov if (port0)
174424bdd43Sratchov mio_close(ih);
175424bdd43Sratchov if (port1)
176424bdd43Sratchov mio_close(oh);
177424bdd43Sratchov if (ifile)
178424bdd43Sratchov close(ifd);
179424bdd43Sratchov if (ofile)
180424bdd43Sratchov close(ofd);
181424bdd43Sratchov return 0;
182424bdd43Sratchov }
1830b869d14Scheloha
1840b869d14Scheloha void __dead
usage(void)1850b869d14Scheloha usage(void)
1860b869d14Scheloha {
1870b869d14Scheloha fprintf(stderr, "usage: midicat [-d] [-i in-file] [-o out-file] "
1880b869d14Scheloha "[-q in-port] [-q out-port]\n");
1890b869d14Scheloha exit(1);
1900b869d14Scheloha }
191