/* Useful functions for programs dealing with EBS files. Copyright (C) 1993/94 Markus Kuhn, Institut fuer Physiologie und Biokybernetik (IPB), Universitaet Erlangen, Deutschland Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and related publications and that both that copyright notice and this permission notice appear in supporting documentation. The authors make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. Send your comments, suggestions or bug reports to ftpebs@uni-erlangen.de Or mail to Institut fuer Physiologie und Biokybernetik Markus Prosch Universitaetsstrasse 17 D-91054 Erlangen Germany $Id: ebs.c,v 1.21 1994/10/18 14:28:44 msprosch Exp $ */ #include #include #include #include #include "ebs.h" #define MAXFLOAT 100 /* no floatingpoint number should have more characters */ /* for non-ANSI versions of stdio.h */ #ifndef SEEK_SET #define SEEK_SET 0 #endif #ifndef SEEK_END #define SEEK_END 2 #endif /* character set conversion tables between the 16-bit character set ISO 10646 used in EBS and a number of popular 8-bit character sets used by various manufacturers. */ static const unsigned short ibm437[128] = { 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00da, 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, 0x2593, 0x2592, 0x2591, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, 0x2568, 0x2564, 0x2565, 0x2568, 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258b, 0x2590, 0x2580, 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, 0x03c6, 0x03b8, 0x2126, 0x03b4, 0x221e, 0x00f8, 0x03b5, 0x220f, 0x2261, 0x00b1, 0x2265, 0x2266, 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2022, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x220e, 0x00a0 }; static const unsigned short ibm850[128] = { 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00f8, 0x00a3, 0x00d8, 0x00d7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00da, 0x00bf, 0x00ae, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, 0x2593, 0x2592, 0x2591, 0x2502, 0x2528, 0x00c1, 0x00c2, 0x00c0, 0x00a9, 0x2563, 0x2551, 0x2557, 0x255d, 0x00a2, 0x00a5, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x00e3, 0x00c3, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x00a4, 0x00f0, 0x00d0, 0x00ca, 0x00cb, 0x00c8, 0x0131, 0x00cd, 0x00ce, 0x00cf, 0x2518, 0x250c, 0x2588, 0x2584, 0x00a6, 0x00cc, 0x2580, 0x00d3, 0x00df, 0x00d4, 0x00d2, 0x00f5, 0x00d5, 0x00b5, 0x00fe, 0x00de, 0x00da, 0x00db, 0x00d9, 0x00fd, 0x00dd, 0x00af, 0x00b4, 0x00ad, 0x00b1, 0x2017, 0x00be, 0x00b6, 0x00a7, 0x00f7, 0x00b8, 0x00b0, 0x00a8, 0x00b7, 0x00b9, 0x00b3, 0x00b2, 0x220e, 0x00a0 }; static const unsigned short atari_st[128] = { 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x00df, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, 0x00e3, 0x00f5, 0x00d8, 0x00f8, 0x0153, 0x0152, 0x00c0, 0x00c3, 0x00d5, 0x00a8, 0x00b4, 0x2020, 0x00b6, 0x00a9, 0x00ae, 0x2122, 0x0133, 0x0132, 0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6, 0x05d7, 0x05d8, 0x05d9, 0x05db, 0x05dc, 0x05de, 0x05e0, 0x05e1, 0x05e2, 0x05e4, 0x05e6, 0x05e7, 0x05e8, 0x05e9, 0x05ea, 0x05df, 0x05da, 0x05dd, 0x05e3, 0x05e5, 0x00a7, 0x2038, 0x221e, 0x03b1, 0x03b2, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, 0x03a6, 0x03b8, 0x2126, 0x03b4, 0x222e, 0x03c6, 0x03b5, 0x220f, 0x2261, 0x00b1, 0x2265, 0x2266, 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2022, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x00b3, 0x00af }; static const unsigned short mac[128] = { 0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1, 0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8, 0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3, 0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc, 0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df, 0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x00c6, 0x00d8, 0x221e, 0x00b1, 0x2266, 0x2265, 0x00a5, 0x00b5, 0x2202, 0x03a3, 0x03a0, 0x03c0, 0x222b, 0x00aa, 0x00ba, 0x2126, 0x00e6, 0x00f8, 0x00bf, 0x00a1, 0x00ac, 0x211a, 0x0192, 0x2248, 0x2206, 0x00ab, 0x00bb, 0x2026, 0x00a0, 0x00c1, 0x00c3, 0x00d5, 0x0152, 0x0153, 0x00ad, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca, 0x00ff, 0x0178, 0x2044, 0x00a4, 0x2039, 0x203a, 0xfb01, 0xfb02, 0x2021, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x00ca, 0x00c1, 0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4, 0xe000, 0x00d2, 0x00da, 0x00db, 0x00d9, 0x0131, 0x0302, 0x0303, 0x0304, 0x0306, 0x0307, 0x030a, 0x0327, 0x030b, 0x0328, 0x030c, }; /* Convert an ISO 10646/Unicode 16-bit character u to a local character set and store it starting at *c. max specifies how many bytes may be used for the local encoding of u. The return value indicates how many bytes have been stored at c. If more bytes then max would be necessary, ucs2local() returns how many bytes would have been necessary, but stores nothing in *c. */ int ucs2local(unsigned char *c, unsigned short u, int max, int charset) { static const unsigned char l1toibm437[96] = { 255,173,155,156,'o',157,'|', 21,'"','c',166,174,170,'-','R','-', 248,241,253,'3', 39,230, 20,249,',','1',167,175,172,171,'?',168, 'A','A','A','A',142,143,146,128,'E',144,'E','E','I','I','I','I', 'D',165,'O','O','O','O',153,'x',237,'U','U','U',154,'Y','T',225, 133,160,131,'a',132,134,145,135,138,130,136,137,141,161,140,139, 'd',164,149,162,147,'o',148,246,237,151,163,150,129,'y','t',152 }; int i; switch (charset) { case EBS_CS_ASCII: /* good old 7-bit US-ASCII */ if (u == 0xfeff) return 0; if (max > 0) if (u < 128) *c = u; else *c = '?'; return 1; case EBS_CS_LATIN1: /* ISO 8859-1 */ if (u == 0xfeff) return 0; if (max > 0) if (u < 256) *c = u; else *c = '?'; return 1; case EBS_CS_IBM437: /* the IBM PC character set */ if (u == 0xfeff) return 0; if (max > 0) { if (u < 128) *c = u; else if (u >= 0xa0 && u <= 0xff) *c = l1toibm437[u - 0xa0]; else { *c = '?'; for (i = 0; i < 128 && *c == '?'; i++) if (ibm437[i] == u) *c = i + 128; } } return 1; case EBS_CS_IBM850: /* the new OS/2 IBM character set */ if (u == 0xfeff) return 0; if (max > 0) if (u < 128) *c = u; else { *c = '?'; for (i = 0; i < 128 && *c == '?'; i++) if (ibm850[i] == u) *c = i + 128; } return 1; case EBS_CS_ATARI_ST: /* the Atari ST character set */ if (u == 0xfeff) return 0; if (max > 0) if (u < 128) *c = u; else { *c = '?'; for (i = 0; i < 128 && *c == '?'; i++) if (atari_st[i] == u) *c = i + 128; } return 1; case EBS_CS_MAC: /* Macintosh character set */ if (u == 0xfeff) return 0; if (max > 0) if (u < 128) *c = u; else { *c = '?'; for (i = 0; i < 128 && *c == '?'; i++) if (mac[i] == u) *c = i + 128; } return 1; case EBS_CS_UCS2: /* ISO 10646 UCS-2, Bigendian */ if (max > 1) { *(c++) = u >> 8; *c = u & 0xff; } return 2; case EBS_CS_UCS2L: /* ISO 10646 UCS-2, Littleendian */ if (max > 1) { *(c++) = u & 0xff; *c = u >> 8; } return 2; case EBS_CS_UTF2: /* the Plan 9 and POSIX UCS encoding */ if (u < 0x80) { if (max > 0) *c = u; return 1; } else if (u < 0x800) { if (max > 1) { *(c++) = 0xc0 | (u >> 6); *c = 0x80 | (u & 0x3f); } return 2; } if (max > 2) { *(c++) = 0xe0 | (u >> 12); *(c++) = 0x80 | ((u >> 6) & 0x3f); *c = 0x80 | (u & 0x3f); } return 3; default: fprintf(stderr, "ucs2local() has been called with an unknown character" "set identifier!\n"); exit(1); } return 0; } /* Convert a character stored starting at c in a local character set to a 16-bit ISO 10646/Unicode character and store it in *u. The return value indicates, how many bytes long the character starting at c is. */ int local2ucs(unsigned short *u, const unsigned char *c, int charset) { int i; switch (charset) { case EBS_CS_ASCII: /* good old 7-bit US-ASCII */ case EBS_CS_LATIN1: /* ISO 8859-1 */ *u = *c; return 1; case EBS_CS_IBM437: /* the IBM PC character set */ if (*c < 128) *u = *c; else *u = ibm437[*c - 128]; return 1; case EBS_CS_IBM850: /* the new OS/2 IBM character set */ if (*c < 128) *u = *c; else *u = ibm850[*c - 128]; return 1; case EBS_CS_ATARI_ST: /* the Atari ST character set */ if (*c < 128) *u = *c; else *u = ibm850[*c - 128]; return 1; case EBS_CS_MAC: /* Macintosh character set */ if (*c < 128) *u = *c; else *u = mac[*c - 128]; return 1; case EBS_CS_UCS2: /* ISO 10646 UCS-2, Bigendian */ *u = (c[0] << 8) | c[1]; return 2; case EBS_CS_UCS2L: /* ISO 10646 UCS-2, Littleendian */ *u = c[0] | (c[1] << 8); return 2; case EBS_CS_UTF2: /* the Plan 9 and POSIX UCS encoding */ if (*c < 0x80) { *u = *c; return 1; } else if (*c < 0xc0) { for (i = 1; (c[i] & 0xc0) == 0x80; i++); *u = 0xfffd; /* replacement character */ return i; } else if (*c < 0xef) { *u = ((c[0] & 0x1f) << 6) | (c[1] & 0x3f); return 2; } else if (*c < 0xf0) { *u = ((c[0] & 0x1f) << 12) | ((c[1] & 0x3f) << 6) | (c[2] & 0x3f); return 3; } for (i = 1; (c[i] & 0xc0) == 0x80; i++); *u = 0xfffd; /* replacement character */ return i; default: fprintf(stderr, "local2ucs() has been called with an unknown character" "set identifier!\n"); exit(1); } return 0; } /* Determine, how many 32-bit words a 0-terminated string will need. s is encoded with the local character set. */ int s4len(char const *s) { return s4len_cs(s, EBS_CS_LOCAL); } /* Determine, how many 32-bit words a 0-terminated string will need. s is encoded with the character set charset. */ int s4len_cs(char const *s, int charset) { int l = 0; unsigned short u; switch (charset) { case EBS_CS_ASCII: /* good old 7-bit US-ASCII */ case EBS_CS_LATIN1: /* ISO 8859-1 */ case EBS_CS_IBM437: /* the IBM PC character set */ case EBS_CS_IBM850: /* the new OS/2 IBM character set */ case EBS_CS_MAC: /* Macintosh character set */ return((((strlen(s) << 1) | 3) + 1) >> 2); default: do { s += local2ucs(&u, (unsigned char *) s, charset); l++; } while (u != 0); return(((((l - 1) << 1) | 3) + 1) >> 2); } } /* Determine, how many 32-bit words a 0-terminated ASCII floatingpoint number will need */ int f4len(double x) { char buf[MAXFLOAT]; int l; sprintf(buf, "%G%n", x, &l); return(((l | 3) + 1) >> 2); } /**** Functions that operate on files/streams directly *****/ /* Read a Bigendian 64-bit integer value from a file */ #ifdef EBS_64_BIT int64 fgeti64(FILE *f) { int64 i; i = (int64) getc(f) << 56; i |= ((uint64) getc(f)) << 48; i |= ((uint64) getc(f)) << 40; i |= ((uint64) getc(f)) << 32; i |= ((uint64) getc(f)) << 24; i |= ((uint64) getc(f)) << 16; i |= ((uint64) getc(f)) << 8; i |= ((uint64) getc(f)); return i; } #endif /* Read a Bigendian 32-bit integer value from a file */ int32 fgeti32(FILE *f) { int32 i; i = (int32) getc(f) << 24; i |= ((uint32) getc(f)) << 16; i |= ((uint32) getc(f)) << 8; i |= ((uint32) getc(f)); return i; } /* Read a Bigendian SampleT integer value from a file */ #ifdef EBS_64_BIT SampleT fgetst(FILE *f) { return((SampleT) fgeti64(f)); } #else SampleT fgetst(FILE *f) { int32 h, l; h= fgeti32 (f); l= fgeti32 (f); if (h== 0) return ((SampleT) l); if (h== (int32) -1l && l== (int32) -1l) return (SampleT) -1l; /* Too big for this machine */ fprintf (stderr, "Sorry, this machine cannot handle 64-bit values\n"); abort (); } #endif /* Read a Bigendian 16-bit integer value from a file */ short fgeti16(FILE *f) { short i; i = (short) getc(f) << 8; i |= (short) getc(f); return i; } /* Write a Bigendian 64-bit integer value to a file */ #ifdef EBS_64_BIT void fputi64(int64 n, FILE *f) { putc((n >> 56) & 0xff, f); putc((n >> 48) & 0xff, f); putc((n >> 40) & 0xff, f); putc((n >> 32) & 0xff, f); putc((n >> 24) & 0xff, f); putc((n >> 16) & 0xff, f); putc((n >> 8) & 0xff, f); putc( n & 0xff, f); } #endif /* Write a Bigendian 32-bit integer value to a file */ void fputi32(int32 n, FILE *f) { putc((n >> 24) & 0xff, f); putc((n >> 16) & 0xff, f); putc((n >> 8) & 0xff, f); putc( n & 0xff, f); } /* Write a Bigendian SampleT integer value to a file */ #ifdef EBS_64_BIT void fputst(SampleT n, FILE *f) { fputi64 ((int64) n, f); } #else void fputst(SampleT n, FILE *f) { if (n!= (SampleT) -1l) { fputi32 ((int32) 0l, f); fputi32 ((int32) n, f); } else { fputi32 ((int32) -1l, f); fputi32 ((int32) -1l, f); } } #endif /* Write a Bigendian 16-bit integer value to a file */ void fputi16(short n, FILE *f) { putc((n >> 8) & 0xff, f); putc( n & 0xff, f); } /* Read a Littleendian 64-bit integer value from a file */ #ifdef EBS_64_BIT int64 fgeti64l(FILE *f) { int64 i; i = ((uint64) getc(f)); i |= ((uint64) getc(f)) << 8; i |= ((uint64) getc(f)) << 16; i |= ((uint64) getc(f)) << 24; i |= ((uint64) getc(f)) << 32; i |= ((uint64) getc(f)) << 40; i |= ((uint64) getc(f)) << 48; i |= ((int64) getc(f)) << 56; return i; } #endif /* Read a Littleendian 32-bit integer value from a file */ int32 fgeti32l(FILE *f) { int32 i; i = ((uint32) getc(f)); i |= ((uint32) getc(f)) << 8; i |= ((uint32) getc(f)) << 16; i |= ((int32) getc(f)) << 24; return i; } /* Read a Littleendian SampleT integer value from a file */ #ifdef EBS_64_BIT SampleT fgetstl(FILE *f) { return ((SampleT) fgeti64l(f)); } #else SampleT fgetstl(FILE *f) { int32 h, l; l= fgeti32l (f); h= fgeti32l (f); if (h== 0) return ((SampleT) l); if (h== (int32) -1l && l== (int32) -1l) return (SampleT) -1l; /* Too big for this machine */ fprintf (stderr, "Sorry, this machine cannot handle 64-bit values\n"); abort (); } #endif /* Read a Littleendian 16-bit integer value from a file */ short fgeti16l(FILE *f) { short i; i = (short) getc(f); i |= (short) getc(f) << 8; return i; } /* Write a Littleendian 64-bit integer value to a file */ #ifdef EBS_64_BIT void fputi64l(int64 n, FILE *f) { putc( n & 0xff, f); putc((n >> 8) & 0xff, f); putc((n >> 16) & 0xff, f); putc((n >> 24) & 0xff, f); putc((n >> 32) & 0xff, f); putc((n >> 40) & 0xff, f); putc((n >> 48) & 0xff, f); putc((n >> 56) & 0xff, f); } #endif /* Write a Littleendian 32-bit integer value to a file */ void fputi32l(int32 n, FILE *f) { putc( n & 0xff, f); putc((n >> 8) & 0xff, f); putc((n >> 16) & 0xff, f); putc((n >> 24) & 0xff, f); } /* Write a Littleendian SampleT integer value to a file */ #ifdef EBS_64_BIT void fputstl(SampleT n, FILE *f) { fputi64l ((int64) n, f); } #else void fputstl(SampleT n, FILE *f) { if (n!= (SampleT) -1l) { fputi32l ((int32) n, f); fputi32l ((int32) 0l, f); } else { fputi32l ((int32) -1l, f); fputi32l ((int32) -1l, f); } } #endif /* Write a Littleendian 16-bit integer value to a file */ void fputi16l(short n, FILE *f) { putc( n & 0xff, f); putc((n >> 8) & 0xff, f); } /* Write a 0-terminated string that is filled with 0-bytes to a multiple of four bytes length. The string s[] is encoded in the local character set but will be written in ISO 10646. */ void fputs4(char const *s, FILE *f) { fputs4_cs(s, EBS_CS_LOCAL, f); } /* Write a 0-terminated string that is filled with 0-bytes to a multiple of four bytes length. The string s[] is encoded in the charset character set but will be written in ISO 10646. */ void fputs4_cs(char const *s, int charset, FILE *f) { int l = 0; unsigned short u; do { s += local2ucs(&u, (unsigned char *) s, charset); fputi16(u, f); l++; } while (u != 0); if ((l & 1) != 0) fputi16(0, f); } /* Read in a 0-terminated string that is filled with 0-bytes to a multiple of four length together with these 0-bytes. A maximum string length (including final 0-byte) maxlen may be specified, longer strings will be truncated. The return value is the number of characters (without 0-bytes) of the full untruncated string in the file. The string on the file is encoded in ISO 10646, but a string in the local character set will be returned. */ int fgets4(char *buf, int maxlen, FILE *f) { return fgets4_cs(buf, maxlen, EBS_CS_LOCAL, f); } /* Read in a 0-terminated string that is filled with 0-bytes to a multiple of four length together with these 0-bytes. A maximum string length (including final 0-byte) maxlen may be specified, longer strings will be truncated. The return value is the number of characters (without 0-bytes) of the full untruncated string in the file. The string on the file is encoded in ISO 10646, but a string in the character set charset will be returned. */ int fgets4_cs(char *buf, int maxlen, int charset, FILE *f) { int l = 0; /* number of 16-bit words != 0x0000 read so far */ int i = 0; /* number of local bytes read so far */ int j = 0; /* number of local bytes written to buf */ unsigned short u = 1; /* last character read */ int nl; /* length of '\0' in charset */ nl = ucs2local((unsigned char *) buf, 0, 0, charset); if (buf == NULL) maxlen = 0; while (u) { u = fgeti16(f); i += ucs2local((unsigned char *) buf + i, u, maxlen - nl - i, charset); if (i <= maxlen-nl && u) j = i; l++; } ucs2local((unsigned char *) buf + j, 0, maxlen - j, charset); if (l & 1) fgeti16(f); return l-1; } /* Write a 0-terminated ASCII floating point number that is filled with 0-bytes to a multiple of four bytes length. */ void fputf4(double x, FILE *f) { int l; fprintf(f, "%G%c%n", x, 0, &l); while ((l++ & 3) != 0) putc(0, f); } /* Read in a 0-terminated floating point number that is filled with 0-bytes to a multiple of four length together with these 0-bytes. will be truncated. If the floating point number string is empty, *nan will be set to 1, if an error occured it will be set to a negative number and to 0 if everything is all right. The return value is the floating point number. */ double fgetf4(FILE *f, int *nan) { char buf[MAXFLOAT]; int l = 0; int i = 0; int b = 1; int dummy; float x; if (nan == NULL) nan = &dummy; while (i < MAXFLOAT-1 && (buf[i++] = b = getc(f)) != 0) l++; buf[i] = 0; if (b != 0) while (i++, getc(f) != 0) l++; while ((i & 3) != 0) { if (getc(f) != 0) { *nan = -1; return 0.0; } i++; } if (l >= MAXFLOAT) { *nan = -2; return 0.0; } if (l == 0) { *nan = 1; return 0.0; } sscanf(buf, "%g%n", &x, &b); if (b != l) { *nan = -3; return 0.0; } *nan = 0; return x; } /**** Functions that operate on EBS files loaded/mapped into memory *****/ /* Write the byte value to the location pointed to by *pptr and advance *pptr by one byte. */ #define PUTBYTE(pptr, value) (*((*(unsigned char **) (pptr))++) = (value)) /* Return the byte **pptr and increment *pptr by one byte */ #define GETBYTE(pptr) (*((*(unsigned char **) (pptr))++)) /* Read a Bigendian 64-bit integer value */ #ifdef EBS_64_BIT int64 geti64(void **p) { int64 i; i = (int64) GETBYTE(p) << 56; i |= ((USampleT) GETBYTE(p)) << 48; i |= ((USampleT) GETBYTE(p)) << 40; i |= ((USampleT) GETBYTE(p)) << 32; i |= ((USampleT) GETBYTE(p)) << 24; i |= ((USampleT) GETBYTE(p)) << 16; i |= ((USampleT) GETBYTE(p)) << 8; i |= ((USampleT) GETBYTE(p)); return i; } #endif /* Read a Bigendian 32-bit integer value */ int32 geti32(void **p) { int32 i; i = (int32) GETBYTE(p) << 24; i |= (int32) GETBYTE(p) << 16; i |= (int32) GETBYTE(p) << 8; i |= (int32) GETBYTE(p); return i; } /* Read a Bigendian SampleT integer value */ #ifdef EBS_64_BIT SampleT getst(void **p) { return ((SampleT) geti64(p)); } #else SampleT getst(void **p) { int32 h, l; h= geti32 (p); l= geti32 (p); if (h== 0) return ((SampleT) l); if (h== (int32) -1l && l== (int32) -1l) return (SampleT) -1l; /* Too big for this machine */ fprintf (stderr, "Sorry, this machine cannot handle 64-bit values\n"); abort (); } #endif /* Read a Bigendian 16-bit integer value */ short geti16(void **p) { short i; i = (short) GETBYTE(p) << 8; i |= (short) GETBYTE(p); return i; } /* Write a Bigendian 64-bit integer value */ #ifdef EBS_64_BIT void puti64(int64 n, void **p) { PUTBYTE(p, n >> 56); PUTBYTE(p, n >> 48); PUTBYTE(p, n >> 40); PUTBYTE(p, n >> 32); PUTBYTE(p, n >> 24); PUTBYTE(p, n >> 16); PUTBYTE(p, n >> 8); PUTBYTE(p, n); } #endif /* Write a Bigendian 32-bit integer value */ void puti32(int32 n, void **p) { PUTBYTE(p, n >> 24); PUTBYTE(p, n >> 16); PUTBYTE(p, n >> 8); PUTBYTE(p, n); } /* Write a Bigendian SampleT integer value */ #ifdef EBS_64_BIT void putst(SampleT n, void **p) { puti64 ((int64) n, p); } #else void putst(SampleT n, void **p) { if (n!= (SampleT) -1l) { puti32 ((int32) 0l, p); puti32 ((int32) n, p); } else { puti32 ((int32) -1l, p); puti32 ((int32) -1l, p); } } #endif /* Write a Bigendian 16-bit integer value to a file */ void puti16(short n, void **p) { PUTBYTE(p, n >> 8); PUTBYTE(p, n); } /* Read a Littleendian 64-bit integer value */ #ifdef EBS_64_BIT int64 geti64l(void **p) { int64 i; i = ((USampleT) GETBYTE(p)); i |= ((USampleT) GETBYTE(p)) << 8; i |= ((USampleT) GETBYTE(p)) << 16; i |= ((USampleT) GETBYTE(p)) << 24; i |= ((USampleT) GETBYTE(p)) << 32; i |= ((USampleT) GETBYTE(p)) << 40; i |= ((USampleT) GETBYTE(p)) << 48; i |= ((int64) GETBYTE(p)) << 56; return i; } #endif /* Read a Littleendian 32-bit integer value */ int32 geti32l(void **p) { int32 i; i = (int32) GETBYTE(p); i |= (int32) GETBYTE(p) << 8; i |= (int32) GETBYTE(p) << 16; i |= (int32) GETBYTE(p) << 24; return i; } /* Read a Littleendian SampleT integer value */ #ifdef EBS_64_BIT SampleT getstl(void **p) { return ((SampleT) geti64l (p)); } #else SampleT getstl(void **p) { int32 h, l; l= geti32l (p); h= geti32l (p); if (h== 0) return ((SampleT) l); if (h== (int32) -1l && l== (int32) -1l) return (SampleT) -1l; /* Too big for this machine */ fprintf (stderr, "Sorry, this machine cannot handle 64-bit values\n"); abort (); } #endif /* Read a Littleendian 16-bit integer value */ short geti16l(void **p) { short i; i = (short) GETBYTE(p); i |= (short) GETBYTE(p) << 8; return i; } /* Write a Littleendian 64-bit integer value */ #ifdef EBS_64_BIT void puti64l(int64 n, void **p) { PUTBYTE(p, n); PUTBYTE(p, n >> 8); PUTBYTE(p, n >> 16); PUTBYTE(p, n >> 24); PUTBYTE(p, n >> 32); PUTBYTE(p, n >> 40); PUTBYTE(p, n >> 48); PUTBYTE(p, n >> 56); } #endif /* Write a Littleendian 32-bit integer value */ void puti32l(int32 n, void **p) { PUTBYTE(p, n); PUTBYTE(p, n >> 8); PUTBYTE(p, n >> 16); PUTBYTE(p, n >> 24); } /* Write a Littleendian SampleT integer value */ #ifdef EBS_64_BIT void putstl(SampleT n, void **p) { puti64l ((int64) n, p); } #else void putstl(SampleT n, void **p) { if (n!= (SampleT) -1l) { puti32l ((int32) n, p); puti32l ((int32) 0l, p); } else { puti32l ((int32) -1l, p); puti32l ((int32) -1l, p); } } #endif /* Write a Littleendian 16-bit integer value to a file */ void puti16l(short n, void **p) { PUTBYTE(p, n); PUTBYTE(p, n >> 8); } /* Write a 0-terminated string that is filled with 0-bytes to a multiple of four bytes length. The string s[] is encoded in the local character set but will be written in ISO 10646. */ void puts4(char const *s, void **p) { puts4_cs(s, EBS_CS_LOCAL, p); } /* Write a 0-terminated string that is filled with 0-bytes to a multiple of four bytes length. The string s[] is encoded in the character set charset but will be written in ISO 10646. */ void puts4_cs(char const *s, int charset, void **p) { int l = 0; unsigned short u; do { s += local2ucs(&u, (unsigned char *) s, charset); puti16(u, p); l++; } while (u != 0); if ((l & 1) != 0) puti16(0, p); } /* Read in a 0-terminated string that is filled with 0-bytes to a multiple of four length together with these 0-bytes. A maximum string length (including 1 0-byte) maxlen be specified, longer strings will be truncated. The return value is the number of characters (without 0-bytes) of the full untruncated string in the file. The string on the file is encoded in ISO 10646, but a string in the local character set will be returned. */ int gets4(char *buf, int maxlen, void **p) { return gets4_cs(buf, maxlen, EBS_CS_LOCAL, p); } /* Read in a 0-terminated string that is filled with 0-bytes to a multiple of four length together with these 0-bytes. A maximum string length (including 1 0-byte) maxlen be specified, longer strings will be truncated. The return value is the number of characters (without 0-bytes) of the full untruncated string in the file. The string on the file is encoded in ISO 10646, but a string in the character set charset will be returned. */ int gets4_cs(char *buf, int maxlen, int charset, void **p) { int l = 0; /* number of 16-bit words != 0x0000 read so far */ int i = 0; /* number of local bytes read so far */ int j = 0; /* number of local bytes written to buf */ unsigned short u = 1; /* last character read */ int nl; /* length of '\0' in charset */ nl = ucs2local((unsigned char *) buf, 0, 0, charset); if (buf == NULL) maxlen = 0; while (u) { u = geti16(p); i += ucs2local((unsigned char *) buf + i, u, maxlen - nl - i, charset); if (i <= maxlen-nl && u) j = i; l++; } ucs2local((unsigned char *) buf + j, 0, maxlen - j, charset); if (l & 1) geti16(p); return l-1; } /* Write a 0-terminated ASCII floating point number that is filled with 0-bytes to a multiple of four bytes length. */ void putf4(double x, void **p) { int l; sprintf(*(char **) p, "%G%n", x, &l); *(unsigned char **) p += ++l; while ((l++ & 3) != 0) PUTBYTE(p, 0); } /* Read in a 0-terminated floating point number that is filled with 0-bytes to a multiple of four length together with these 0-bytes. will be truncated. If the floating point number string is empty, *nan will be set to 1, if an error occured it will be set to a negative number and to 0 if everything is all right. The return value is the floating point number. */ double getf4(void **p, int *nan) { int i, dummy; float x; void *pp; if (nan == NULL) nan = &dummy; *nan = 0; pp = *p; if (!GETBYTE(&pp) && !GETBYTE(&pp) && !GETBYTE(&pp) && !GETBYTE(&pp)) { *nan = 1; *p = pp; return 0.0; } sscanf(*(char **) p, "%f%n", &x, &i); *(unsigned char **) p += i; do { if (GETBYTE(p) != 0) *nan = -1; i++; } while ((i & 3) != 0); return x; } /* the data part conversion function */ #define CHANNEL_ORDER(eid) ((eid) & 1) /* the following two arrays are global for communication with diffcount */ static SampleT *posdiff; /* output file pointer position for each channel */ static short *lastdiff; /* remember previous value in each channel */ /* a tiny auxiliary function for dataconv() */ static void diffcount(short *start, USampleT length, uint32 channel, USampleT pos) { USampleT i; long diff; for (i = 0; i < length; i++) { diff = (long) start[i] - (long) lastdiff[channel]; if (diff < -127 || diff > 127 || pos + i == 0) posdiff[channel] += 2; lastdiff[channel] = start[i]; } posdiff[channel] += length; } /* Reads in the data part from an input EBS file fin and writes the same data in any EBS data format to a different output file fout. The data file format for both files are specified in incode and outcode, instart and outstart are the positions of the first byte of the data part in both files and channels and samples determine the length of the whole data part. If modify is not NULL, (*modify)() is called for every data block in every channel before the blocks are written to fout. This allows you to modify data during format conversion (e.g. add an offset) and makes this function also useful for converting to/from non-EBS files. If outcode is 0xffffffff or fout is NULL, no data is written to the output file, but (*modify)() is still called for all blocks of the data part (e.g. for collecting statistical data only). The (*modify)() parameter start is a pointer to an array of length short values from channel channel+1 which might be modified. If *samples is 0xffffffff (only allowed for time-based ordered data), the function will determine the number of samples by looking for the end-of-file and will update *samples. return code: >= 0 length of output in bytes -1 error while reading fin -2 error while writing fout -3 not enough memory */ SampleT dataconv(FILE *fin, FILE *fout, uint32 incode, uint32 outcode, USampleT instart, USampleT outstart, uint32 channels, USampleT *samples, void (*modify)(short *start, USampleT length, uint32 channel, USampleT pos)) { #ifdef MSDOS #define BUFSIZE 256 #else #define BUFSIZE 8192 #endif uint32 i, j = 0, k = 0, l, max; int32 si, diff; short **buf; /* an array of buffers, one for each channel */ /* for differential encoded files: */ short *lastin, *lastout; /* remember previous value in each channel */ /* for channel order files: */ USampleT *inpos; /* input file pointer position for each channel */ USampleT *outpos; /* output file pointer position for each channel */ int nomem; int c; long ret = 0L; /* return value (error code) */ long outbytes = 0; char test[4] = {0x01, 0x02, 0x03, 0x04}; /* allocate memory */ buf = (short **) malloc(sizeof(short *) * channels); lastin = (short *) calloc(channels, sizeof(short)); lastout = (short *) calloc(channels, sizeof(short)); outpos = (long *) malloc(sizeof(USampleT) * channels); inpos = (long *) malloc(sizeof(USampleT) * channels); nomem = (buf == NULL || lastin == NULL || lastout == NULL || outpos == NULL || inpos == NULL); if (!nomem) { for (i = 0; i < channels; i++) if ((buf[i] = (short *) malloc(sizeof(short) * BUFSIZE)) == NULL) nomem = 1; if (nomem) for (i = 0; i < channels; i++) free(buf[i]); } if (nomem) { free(inpos); free(outpos); free(lastout); free(lastin); free(buf); return -3; } if (CHANNEL_ORDER(incode) && *samples == 0xffffffffL) { fprintf(stderr, "Channel order input files need a specified " "number of samples (dataconv())!\n"); exit(1); } /* for channel based variable length encoded input files, we'll need to keep file positions for each channel */ if (CHANNEL_ORDER(incode)) switch (incode) { case EBS_CIB_16: case EBS_CIL_16: for (i = 0; i < channels; i++) inpos[i] = instart + 2 * *samples * i; break; case EBS_CI_16D: /* the only way to find out is running through the file */ inpos[0] = instart; if (fseek(fin, instart, SEEK_SET)) {ret = -1; goto error;} for (i = 1; i < channels; i++) { for (j = 0; j < *samples; j++) { c = getc(fin); if (c == 0x80) { fgetc(fin); fgetc(fin); } } if ((inpos[i] = ftell(fin)) == -1L) {ret = -1; goto error;} } break; default: fprintf(stderr, "unknown input file ID (0x%08lx) in dataconv()!\n", (long) incode); exit(1); } /* for channel based output orders, we'll need to know a priori, where the channels will start (especially for variable length codings) */ if (CHANNEL_ORDER(outcode) && outcode != 0xffffffffL) switch (outcode) { case EBS_CIB_16: case EBS_CIL_16: if (*samples == 0xffffffffL) { /* find out the number of samples */ switch (incode) { case EBS_TIB_16: case EBS_TIL_16: if (fseek(fin, 0L, SEEK_END)) {ret = -1; goto error;} *samples = (ftell(fin) - instart) / (2 * channels); break; default: ret = dataconv(fin, NULL, incode, 0xffffffffL, instart, 0, channels, samples, NULL); if (ret < 0) goto error; break; } } for (i = 0; i < channels; i++) outpos[i] = outstart + 2 * *samples * i; break; case EBS_CI_16D: /* use this function recursively to find out */ for (i = 0; i < channels; i++) outpos[i] = 0; lastdiff = lastout; /* lastout will be unused in this recursive call */ posdiff = outpos; ret = dataconv(fin, NULL, incode, 0xffffffffL, instart, 0, channels, samples, diffcount); if (ret < 0) goto error; for (si = channels - 1; si >= 0; si--) { outpos[si] = outstart; for (j = 0; j < si; j++) outpos[si] += outpos[j]; } break; default: fprintf(stderr, "unknown output file ID (0x%08lx) in dataconv()!\n", (long) outcode); exit(1); } /* main conversion loop */ i = 0; /* sample count */ if (fseek(fin, instart, SEEK_SET)) {ret = -1; goto error;} while (i < *samples) { /* fill the buffers */ max = (BUFSIZE > *samples - i) ? *samples - i : BUFSIZE; if (CHANNEL_ORDER(incode)) { /* channel-based order input formats */ for (k = 0; k < channels; k++) { if (fseek(fin, inpos[k], SEEK_SET)) {ret = -1; goto error;} switch (incode) { case EBS_CIB_16: if ((*(short *) test) == 0x0102) j = fread(buf[k], 2, max, fin); else for (j = 0; j < BUFSIZE && i+j < *samples; j++) { buf[k][j] = getc(fin) << 8; buf[k][j] |= getc(fin); } break; case EBS_CIL_16: if ((*(short *) test) == 0x0201) j = fread(buf[k], 2, max, fin); else for (j = 0; j < BUFSIZE && i+j < *samples; j++) { buf[k][j] = getc(fin); buf[k][j] |= getc(fin) << 8; } break; case EBS_CI_16D: for (j = 0; j < BUFSIZE && i+j < *samples; j++) { c = getc(fin); if (c == 0x80) { buf[k][j] = getc(fin) << 8; buf[k][j] |= getc(fin); } else buf[k][j] = lastin[k] + (signed char) c; lastin[k] = buf[k][j]; } break; } if ((inpos[k] = ftell(fin)) == -1L || feof(fin) || ferror(fin)) {ret = -1; goto error;} } /* end of for (k = 0; ...) */ } else { /* time-based order input formats */ switch (incode) { case EBS_TIB_16: for (j = 0; j < BUFSIZE && i+j < *samples && !feof(fin); j++) for (k = 0; k < channels; k++) { buf[k][j] = getc(fin) << 8; buf[k][j] |= getc(fin); } break; case EBS_TIL_16: for (j = 0; j < BUFSIZE && i+j < *samples && !feof(fin); j++) for (k = 0; k < channels; k++) { buf[k][j] = getc(fin); buf[k][j] |= getc(fin) << 8; } break; case EBS_TI_16D: for (j = 0; j < BUFSIZE && i+j < *samples && !feof(fin); j++) for (k = 0; k < channels; k++) { c = getc(fin); if (c == 0x80) { buf[k][j] = getc(fin) << 8; buf[k][j] |= getc(fin); } else buf[k][j] = lastin[k] + (signed char) c; lastin[k] = buf[k][j]; } break; default: fprintf(stderr, "unknown input file ID (0x%08lx) in dataconv()!\n", (long) incode); exit(1); } if (ferror(fin)) {ret = -1; goto error;} if (feof(fin)) { j--; *samples = i + j; clearerr(fin); } } /* now the buffer is filled */ i += j; /* call user modification function for each buffer */ if (modify != NULL) for (k = 0; k < channels; k++) modify(buf[k], j, k, i-j); /* write the buffer to output file */ if (outcode != 0xffffffffL && fout != NULL) if (CHANNEL_ORDER(outcode)) { /* channel-based order output formats */ for (k = 0; k < channels; k++) { if (fseek(fout, outpos[k], SEEK_SET)) {ret = -2; goto error;} switch (outcode) { case EBS_CIB_16: if ((*(short *) test) == 0x0102) fwrite(buf[k], 2, j, fout); else for (l = 0; l < j; l++) { putc((buf[k][l] >> 8) & 0xff, fout); putc(buf[k][l] & 0xff, fout); } outbytes += 2 * j; break; case EBS_CIL_16: if ((*(short *) test) == 0x0201) fwrite(buf[k], 2, j, fout); else for (l = 0; l < j; l++) { putc(buf[k][l] & 0xff, fout); putc((buf[k][l] >> 8) & 0xff, fout); } outbytes += 2 * j; break; case EBS_CI_16D: for (l = 0; l < j; l++) { diff = (long) buf[k][l] - (long) lastout[k]; if (diff < -127 || diff > 127 || i - j + l == 0) { putc(0x80, fout); putc((buf[k][l] >> 8) & 0xff, fout); putc(buf[k][l] & 0xff, fout); outbytes += 2; } else putc((signed char) diff, fout); lastout[k] = buf[k][l]; } outbytes += j; break; default: fprintf(stderr, "unknown output file ID (0x%08lx) in " "dataconv()!\n", (long) outcode); exit(1); } if ((outpos[k] = ftell(fout)) == -1L || feof(fout) || ferror(fout)) {ret = -2; goto error;} } /* end of for (k = 0; ...) */ } else { /* time-based order output formats */ switch (outcode) { case EBS_TIB_16: for (l = 0; l < j; l++) for (k = 0; k < channels; k++) { putc((buf[k][l] >> 8) & 0xff, fout); putc(buf[k][l] & 0xff, fout); } outbytes += 2 * j * channels; break; case EBS_TIL_16: for (l = 0; l < j; l++) for (k = 0; k < channels; k++) { putc(buf[k][l] & 0xff, fout); putc((buf[k][l] >> 8) & 0xff, fout); } outbytes += 2 * j * channels; break; case EBS_TI_16D: for (l = 0; l < j; l++) for (k = 0; k < channels; k++) { diff = (long) buf[k][l] - (long) lastout[k]; if (diff < -127 || diff > 127 || i - j + l == 0) { putc(0x80, fout); putc((buf[k][l] >> 8) & 0xff, fout); putc(buf[k][l] & 0xff, fout); outbytes += 2; } else putc((signed char) diff, fout); lastout[k] = buf[k][l]; } outbytes += j * channels; break; default: fprintf(stderr, "unknown output file ID (0x%08lx) in " "dataconv()!\n", (long) outcode); exit(1); } if (feof(fout) || ferror(fout)) {ret = -2; goto error;} } } /* end of main while loop */ error: /* sorry, Mr. Dijkstra, goto considered useful here ... ;-) */ /* free memory before leaving this function */ for (i = 0; i < channels; i++) free(buf[i]); free(inpos); free(outpos); free(lastout); free(lastin); free(buf); if (ret < 0) return ret; else return outbytes; } /* encoding name table for formatid() and formatname() */ static const struct { unsigned long id; const char *name; } id_list[] = { {EBS_TIB_16, "TIB_16"}, {EBS_CIB_16, "CIB_16"}, {EBS_TIL_16, "TIL_16"}, {EBS_CIL_16, "CIL_16"}, {EBS_TI_16D, "TI_16D"}, {EBS_CI_16D, "CI_16D"}, {0xffffffffL, NULL} }; /* Convert an ASCII string with an EBS data encoding name ("CIB_16", etc.) to a numeric encoding ID (EBS_CIB_16, etc.). Return 0xffffffff if encoding name is unknown. */ int32 formatid(char const *name) { char buf[20]; long id; int i; for (i = 0; name[i] != '\0' && i < 19; i++) buf[i] = toupper(name[i]); buf[i] = 0; id = 0xffffffffL; for (i = 0; id == 0xffffffffL && id_list[i].id != 0xffffffffL; i++) if (!strcmp(buf, id_list[i].name)) id = id_list[i].id; return id; } /* Return a pointer to an ASCII string with the name of the EBS encoding format identified by id (e.g. "CIB_16") or return NULL for unknown or non-standard encoding IDs. */ char const *formatname(int32 id) { int i = 0; while (id_list[i].id != 0xffffffffL) { if (id_list[i].id == id) return id_list[i].name; i++; } return NULL; }