xref: /openbsd-src/usr.bin/midicat/midicat.c (revision dbbc8c3f84648233aba8868b9333bfa98d3354a7)
1 /*	$OpenBSD: midicat.c,v 1.7 2022/12/02 22:36:34 cheloha Exp $	*/
2 /*
3  * Copyright (c) 2015 Alexandre Ratchov <alex@caoua.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 #include <err.h>
18 #include <fcntl.h>
19 #include <sndio.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <string.h>
24 
25 void __dead usage(void);
26 
27 int
main(int argc,char ** argv)28 main(int argc, char **argv)
29 {
30 #define MIDI_BUFSZ	1024
31 	unsigned char buf[MIDI_BUFSZ];
32 	struct mio_hdl *ih, *oh;
33 	char *port0, *port1, *ifile, *ofile;
34 	int ifd, ofd;
35 	int dump, c, i, len, n, sep, mode;
36 
37 	dump = 0;
38 	port0 = port1 = ifile = ofile = NULL;
39 	ih = oh = NULL;
40 	ifd = ofd = -1;
41 
42 	while ((c = getopt(argc, argv, "di:o:q:")) != -1) {
43 		switch (c) {
44 		case 'd':
45 			dump = 1;
46 			break;
47 		case 'q':
48 			if (port0 == NULL)
49 				port0 = optarg;
50 			else if (port1 == NULL)
51 				port1 = optarg;
52 			else
53 				errx(1, "too many -q options");
54 			break;
55 		case 'i':
56 			ifile = optarg;
57 			break;
58 		case 'o':
59 			ofile = optarg;
60 			break;
61 		default:
62 			usage();
63 		}
64 	}
65 	argc -= optind;
66 	argv += optind;
67 
68 	if (argc != 0)
69 		usage();
70 
71 	/* we don't support more than one data flow */
72 	if (ifile != NULL && ofile != NULL)
73 		errx(1, "-i and -o are exclusive");
74 
75 	/* second port makes sense only for port-to-port transfers */
76 	if (port1 != NULL && !(ifile == NULL && ofile == NULL))
77 		errx(1, "too many -q options");
78 
79 	/* if there're neither files nor ports, then we've nothing to do */
80 	if (port0 == NULL && ifile == NULL && ofile == NULL)
81 		usage();
82 
83 	/* if no port specified, use default one */
84 	if (port0 == NULL)
85 		port0 = MIO_PORTANY;
86 
87 	/* open input or output file (if any) */
88 	if (ifile) {
89 		if (strcmp(ifile, "-") == 0) {
90 			ifile = "stdin";
91 			ifd = STDIN_FILENO;
92 		} else {
93 			ifd = open(ifile, O_RDONLY);
94 			if (ifd == -1)
95 				err(1, "%s", ifile);
96 		}
97 	} else if (ofile) {
98 		if (strcmp(ofile, "-") == 0) {
99 			ofile = "stdout";
100 			ofd = STDOUT_FILENO;
101 		} else {
102 			ofd = open(ofile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
103 			if (ofd == -1)
104 				err(1, "%s", ofile);
105 		}
106 	}
107 
108 	/* open first port for input and output (if output needed) */
109 	if (ofile)
110 		mode = MIO_IN;
111 	else if (ifile)
112 		mode = MIO_OUT;
113 	else if (port1 == NULL)
114 		mode = MIO_IN | MIO_OUT;
115 	else
116 		mode = MIO_IN;
117 	ih = mio_open(port0, mode, 0);
118 	if (ih == NULL)
119 		errx(1, "%s: couldn't open port", port0);
120 
121 	/* open second port, output only */
122 	if (port1 == NULL)
123 		oh = ih;
124 	else {
125 		oh = mio_open(port1, MIO_OUT, 0);
126 		if (oh == NULL)
127 			errx(1, "%s: couldn't open port", port1);
128 	}
129 
130 	if (pledge("stdio", NULL) == -1)
131 		err(1, "pledge");
132 
133 	/* transfer until end-of-file or error */
134 	for (;;) {
135 		if (ifile != NULL) {
136 			len = read(ifd, buf, sizeof(buf));
137 			if (len == 0)
138 				break;
139 			if (len == -1) {
140 				warn("%s", ifile);
141 				break;
142 			}
143 		} else {
144 			len = mio_read(ih, buf, sizeof(buf));
145 			if (len == 0) {
146 				warnx("%s: disconnected", port0);
147 				break;
148 			}
149 		}
150 		if (ofile != NULL) {
151 			n = write(ofd, buf, len);
152 			if (n != len) {
153 				warn("%s: short write", ofile);
154 				break;
155 			}
156 		} else {
157 			n = mio_write(oh, buf, len);
158 			if (n != len) {
159 				warnx("%s: disconnected", port1);
160 				break;
161 			}
162 		}
163 		if (dump) {
164 			for (i = 0; i < len; i++) {
165 				sep = (i % 16 == 15 || i == len - 1) ?
166 				    '\n' : ' ';
167 				fprintf(stderr, "%02x%c", buf[i], sep);
168 			}
169 		}
170 	}
171 
172 	/* clean-up */
173 	if (port0)
174 		mio_close(ih);
175 	if (port1)
176 		mio_close(oh);
177 	if (ifile)
178 		close(ifd);
179 	if (ofile)
180 		close(ofd);
181 	return 0;
182 }
183 
184 void __dead
usage(void)185 usage(void)
186 {
187 	fprintf(stderr, "usage: midicat [-d] [-i in-file] [-o out-file] "
188 	    "[-q in-port] [-q out-port]\n");
189 	exit(1);
190 }
191