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 25void __dead usage(void); 26 27int 28main(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 184void __dead 185usage(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