1 /*- 2 * Copyright (c) 2023 Christos Zoulas 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 15 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 16 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 * POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 /* 28 * Parse SIM-H tape files 29 * http://simh.trailing-edge.com/docs/simh_magtape.pdf 30 */ 31 32 #ifndef TEST 33 #include "file.h" 34 35 #ifndef lint 36 FILE_RCSID("@(#)$File: is_simh.c,v 1.10 2023/07/27 19:39:55 christos Exp $") 37 #endif 38 39 #include <string.h> 40 #include <stddef.h> 41 #include "magic.h" 42 #else 43 #include <stdint.h> 44 #include <sys/types.h> 45 #include <string.h> 46 #include <stddef.h> 47 #define CAST(a, b) (a)(b) 48 #endif 49 50 51 #ifdef DEBUG 52 #include <stdio.h> 53 #define DPRINTF(fmt, ...) printf(fmt, __VA_ARGS__) 54 #else 55 #define DPRINTF(fmt, ...) 56 #endif 57 58 /* 59 * if SIMH_TAPEMARKS == 0: 60 * check all the records and tapemarks 61 * otherwise: 62 * check only up-to the number of tapemarks specified 63 */ 64 #ifndef SIMH_TAPEMARKS 65 #define SIMH_TAPEMARKS 10 66 #endif 67 68 typedef union { 69 char s[4]; 70 uint32_t u; 71 } myword; 72 73 static myword simh_bo; 74 75 #define NEED_SWAP (simh_bo.u == CAST(uint32_t, 0x01020304)) 76 77 /* 78 * swap an int 79 */ 80 static uint32_t 81 swap4(uint32_t sv) 82 { 83 myword d, s; 84 s.u = sv; 85 d.s[0] = s.s[3]; 86 d.s[1] = s.s[2]; 87 d.s[2] = s.s[1]; 88 d.s[3] = s.s[0]; 89 return d.u; 90 } 91 92 93 static uint32_t 94 getlen(const unsigned char **uc) 95 { 96 uint32_t n; 97 memcpy(&n, *uc, sizeof(n)); 98 *uc += sizeof(n); 99 if (NEED_SWAP) 100 n = swap4(n); 101 if (n == 0xffffffff) /* check for End of Medium */ 102 return n; 103 n &= 0x00ffffff; /* keep only the record len */ 104 if (n & 1) 105 n++; 106 return n; 107 } 108 109 static int 110 simh_parse(const unsigned char *uc, const unsigned char *ue) 111 { 112 uint32_t nbytes, cbytes; 113 const unsigned char *orig_uc = uc; 114 size_t nt = 0, nr = 0; 115 116 (void)memcpy(simh_bo.s, "\01\02\03\04", 4); 117 118 while (ue - uc >= CAST(ptrdiff_t, sizeof(nbytes))) { 119 nbytes = getlen(&uc); 120 if ((nt > 0 || nr > 0) && nbytes == 0xFFFFFFFF) 121 /* EOM after at least one record or tapemark */ 122 break; 123 if (nbytes == 0) { 124 nt++; /* count tapemarks */ 125 #if SIMH_TAPEMARKS 126 if (nt == SIMH_TAPEMARKS) 127 break; 128 #endif 129 continue; 130 } 131 /* handle a data record */ 132 uc += nbytes; 133 if (ue - uc < CAST(ptrdiff_t, sizeof(nbytes))) 134 break; 135 cbytes = getlen(&uc); 136 if (nbytes != cbytes) 137 return 0; 138 nr++; 139 } 140 if (nt * sizeof(uint32_t) == CAST(size_t, uc - orig_uc)) 141 return 0; /* All examined data was tapemarks (0) */ 142 if (nr == 0) /* No records */ 143 return 0; 144 return 1; 145 } 146 147 #ifndef TEST 148 int 149 file_is_simh(struct magic_set *ms, const struct buffer *b) 150 { 151 const unsigned char *uc = CAST(const unsigned char *, b->fbuf); 152 const unsigned char *ue = uc + b->flen; 153 int mime = ms->flags & MAGIC_MIME; 154 155 if ((ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION)) != 0) 156 return 0; 157 158 if (!simh_parse(uc, ue)) 159 return 0; 160 161 if (mime == MAGIC_MIME_ENCODING) 162 return 1; 163 164 if (mime) { 165 if (file_printf(ms, "application/SIMH-tape-data") == -1) 166 return -1; 167 return 1; 168 } 169 170 if (file_printf(ms, "SIMH tape data") == -1) 171 return -1; 172 173 return 1; 174 } 175 176 #else 177 178 #include <sys/types.h> 179 #include <sys/stat.h> 180 #include <stdio.h> 181 #include <fcntl.h> 182 #include <unistd.h> 183 #include <stdlib.h> 184 #include <stdint.h> 185 #include <err.h> 186 187 int 188 main(int argc, char *argv[]) 189 { 190 int fd; 191 struct stat st; 192 unsigned char *p; 193 194 if ((fd = open(argv[1], O_RDONLY)) == -1) 195 err(EXIT_FAILURE, "Can't open `%s'", argv[1]); 196 197 if (fstat(fd, &st) == -1) 198 err(EXIT_FAILURE, "Can't stat `%s'", argv[1]); 199 200 if ((p = CAST(char *, malloc(st.st_size))) == NULL) 201 err(EXIT_FAILURE, "Can't allocate %jd bytes", 202 (intmax_t)st.st_size); 203 if (read(fd, p, st.st_size) != st.st_size) 204 err(EXIT_FAILURE, "Can't read %jd bytes", 205 (intmax_t)st.st_size); 206 printf("is simh %d\n", simh_parse(p, p + st.st_size)); 207 return 0; 208 } 209 #endif 210