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 36FILE_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 68typedef union { 69 char s[4]; 70 uint32_t u; 71} myword; 72 73static myword simh_bo; 74 75#define NEED_SWAP (simh_bo.u == CAST(uint32_t, 0x01020304)) 76 77/* 78 * swap an int 79 */ 80static uint32_t 81swap4(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 93static uint32_t 94getlen(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 109static int 110simh_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 148int 149file_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 187int 188main(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