1 /* 2 * Schism Tracker - a cross-platform Impulse Tracker clone 3 * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com> 4 * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org> 5 * copyright (c) 2009 Storlek & Mrs. Brisby 6 * copyright (c) 2010-2012 Storlek 7 * URL: http://schismtracker.org/ 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 */ 23 24 #ifdef HAVE_CONFIG_H 25 # include <config.h> 26 #endif 27 28 #include "slurp.h" 29 #include "util.h" 30 31 #include <sys/types.h> 32 #include <sys/stat.h> 33 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 #include <errno.h> 39 #include <fcntl.h> 40 41 /* The dup's are because fclose closes its file descriptor even if the FILE* was acquired with fdopen, and when 42 the control gets back to slurp, it closes the fd (again). It doesn't seem to exist on Amiga OS though, so... */ 43 #ifndef HAVE_DUP 44 # define dup(fd) fd 45 #endif 46 47 /* I hate this... */ 48 #ifndef O_BINARY 49 # ifdef O_RAW 50 # define O_BINARY O_RAW 51 # else 52 # define O_BINARY 0 53 # endif 54 #endif 55 56 static void _slurp_closure_free(slurp_t *t) 57 { 58 free(t->data); 59 } 60 61 /* --------------------------------------------------------------------- */ 62 63 /* CHUNK is how much memory is allocated at once. Too large a number is a 64 * waste of memory; too small means constantly realloc'ing. 65 * 66 * <mml> also, too large a number might take the OS more than an efficient number of reads to read in one 67 * hit -- which you could be processing/reallocing while waiting for the next bit 68 * <mml> we had something for some proggy on the server that was sucking data off stdin 69 * <mml> and had our resident c programmer and resident perl programmer competing for the fastest code 70 * <mml> but, the c coder found that after a bunch of test runs with time, 64k worked out the best case 71 * ... 72 * <mml> but, on another system with a different block size, 64 blocks may still be efficient, but 64k 73 * might not be 64 blocks 74 * (so maybe this should grab the block size from stat() instead...) */ 75 #define CHUNK 65536 76 77 static int _slurp_stdio_pipe(slurp_t * t, int fd) 78 { 79 int old_errno; 80 FILE *fp; 81 uint8_t *read_buf, *realloc_buf; 82 size_t this_len; 83 int chunks = 0; 84 85 t->data = NULL; 86 fp = fdopen(dup(fd), "rb"); 87 if (fp == NULL) 88 return 0; 89 90 do { 91 chunks++; 92 /* Have to cast away the const... */ 93 realloc_buf = realloc((void *) t->data, CHUNK * chunks); 94 if (realloc_buf == NULL) { 95 old_errno = errno; 96 fclose(fp); 97 free(t->data); 98 errno = old_errno; 99 return 0; 100 } 101 t->data = realloc_buf; 102 read_buf = (void *) (t->data + (CHUNK * (chunks - 1))); 103 this_len = fread(read_buf, 1, CHUNK, fp); 104 if (this_len <= 0) { 105 if (ferror(fp)) { 106 old_errno = errno; 107 fclose(fp); 108 free(t->data); 109 errno = old_errno; 110 return 0; 111 } 112 } 113 t->length += this_len; 114 } while (this_len); 115 fclose(fp); 116 t->closure = _slurp_closure_free; 117 return 1; 118 } 119 120 static int _slurp_stdio(slurp_t * t, int fd) 121 { 122 int old_errno; 123 FILE *fp; 124 size_t got = 0, need, len; 125 126 if (t->length == 0) { 127 /* Hrmph. Probably a pipe or something... gotta do it the REALLY ugly way. */ 128 return _slurp_stdio_pipe(t, fd); 129 } 130 131 fp = fdopen(dup(fd), "rb"); 132 133 if (!fp) 134 return 0; 135 136 t->data = (uint8_t *) malloc(t->length); 137 if (t->data == NULL) { 138 old_errno = errno; 139 fclose(fp); 140 errno = old_errno; 141 return 0; 142 } 143 144 /* Read the WHOLE thing -- fread might not get it all at once, 145 * so keep trying until it returns zero. */ 146 need = t->length; 147 do { 148 len = fread(t->data + got, 1, need, fp); 149 if (len <= 0) { 150 if (ferror(fp)) { 151 old_errno = errno; 152 fclose(fp); 153 free(t->data); 154 errno = old_errno; 155 return 0; 156 } 157 158 if (need > 0) { 159 /* short file */ 160 need = 0; 161 t->length = got; 162 } 163 } else { 164 got += len; 165 need -= len; 166 } 167 } while (need > 0); 168 169 fclose(fp); 170 t->closure = _slurp_closure_free; 171 return 1; 172 } 173 174 175 /* --------------------------------------------------------------------- */ 176 177 static slurp_t *_slurp_open(const char *filename, struct stat * buf, size_t size) 178 { 179 slurp_t *t; 180 int fd, old_errno; 181 182 if (buf && S_ISDIR(buf->st_mode)) { 183 errno = EISDIR; 184 return NULL; 185 } 186 187 t = (slurp_t *) mem_alloc(sizeof(slurp_t)); 188 if (t == NULL) 189 return NULL; 190 t->pos = 0; 191 192 if (strcmp(filename, "-") == 0) { 193 if (_slurp_stdio(t, STDIN_FILENO)) 194 return t; 195 free(t); 196 return NULL; 197 } 198 199 if (size <= 0) { 200 size = (buf ? buf->st_size : file_size(filename)); 201 } 202 203 #ifdef WIN32 204 switch (slurp_win32(t, filename, size)) { 205 case 0: free(t); return NULL; 206 case 1: return t; 207 }; 208 #endif 209 210 #if HAVE_MMAP 211 switch (slurp_mmap(t, filename, size)) { 212 case 0: free(t); return NULL; 213 case 1: return t; 214 }; 215 #endif 216 217 fd = open(filename, O_RDONLY | O_BINARY); 218 219 if (fd < 0) { 220 free(t); 221 return NULL; 222 } 223 224 t->length = size; 225 226 if (_slurp_stdio(t, fd)) { 227 close(fd); 228 return t; 229 } 230 231 old_errno = errno; 232 close(fd); 233 free(t); 234 errno = old_errno; 235 return NULL; 236 } 237 238 slurp_t *slurp(const char *filename, struct stat * buf, size_t size) 239 { 240 slurp_t *t = _slurp_open(filename, buf, size); 241 uint8_t *mmdata; 242 size_t mmlen; 243 244 if (!t) { 245 return NULL; 246 } 247 248 mmdata = t->data; 249 mmlen = t->length; 250 if (mmcmp_unpack(&mmdata, &mmlen)) { 251 // clean up the existing data 252 if (t->data && t->closure) { 253 t->closure(t); 254 } 255 // and put the new stuff in 256 t->length = mmlen; 257 t->data = mmdata; 258 t->closure = _slurp_closure_free; 259 } 260 261 // TODO re-add PP20 unpacker, possibly also handle other formats? 262 263 return t; 264 } 265 266 267 void unslurp(slurp_t * t) 268 { 269 if (!t) 270 return; 271 if (t->data && t->closure) { 272 t->closure(t); 273 } 274 free(t); 275 } 276 277 /* --------------------------------------------------------------------- */ 278 279 int slurp_seek(slurp_t *t, long offset, int whence) 280 { 281 switch (whence) { 282 default: 283 case SEEK_SET: 284 break; 285 case SEEK_CUR: 286 offset += t->pos; 287 break; 288 case SEEK_END: 289 offset += t->length; 290 break; 291 } 292 if (offset < 0 || (size_t) offset > t->length) 293 return -1; 294 t->pos = offset; 295 return 0; 296 } 297 298 long slurp_tell(slurp_t *t) 299 { 300 return (long) t->pos; 301 } 302 303 size_t slurp_read(slurp_t *t, void *ptr, size_t count) 304 { 305 count = slurp_peek(t, ptr, count); 306 t->pos += count; 307 return count; 308 } 309 310 size_t slurp_peek(slurp_t *t, void *ptr, size_t count) 311 { 312 size_t bytesleft = t->length - t->pos; 313 if (count > bytesleft) { 314 // short read -- fill in any extra bytes with zeroes 315 size_t tail = count - bytesleft; 316 count = bytesleft; 317 memset(ptr + count, 0, tail); 318 } 319 if (count) 320 memcpy(ptr, t->data + t->pos, count); 321 return count; 322 } 323 324 int slurp_getc(slurp_t *t) 325 { 326 return (t->pos < t->length) ? t->data[t->pos++] : EOF; 327 } 328 329 int slurp_eof(slurp_t *t) 330 { 331 return t->pos >= t->length; 332 } 333 334