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