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