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