xref: /openbsd-src/usr.bin/midicat/midicat.c (revision dbbc8c3f84648233aba8868b9333bfa98d3354a7)
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