//============================================================================== // outtextstream.cpp // Generic output text stream. // // Notice // Copyright ©2008-2011 by David R. Tribble, all rights reserved. // Permission is granted to any person or entity except those designated // by the United States Department of State as a terrorist, or terrorist // government or agency, to use and distribute this source code provided // that the original copyright notice remains present and unaltered. //============================================================================== // Identification static char REV[] = "@(#)drt/src/lib/outtextstream.cpp $Revision: 1.4 $$Date: 2011/04/30 00:36:10 $"; // Includes #include #define sys_ctype_h #include #define sys_errno_h #include #define sys_iso646_h #include #define sys_stdio_h #include #define sys_string_h #if _WIN32 #define WINDOWS_LEAN_AND_MEAN #include #include #endif #include "outtextstream.hpp" // Manifest constants #define BUF_SIZE (16 * 1024) // I/O vbuffer size //------------------------------------------------------------------------------ // OutTextStream::parity_even[] // Character table containing 7-bit ASCII codes with the high (MSB) bit // set or cleared to give the characters even parity (i.e., an even number // of 1 bits). //------------------------------------------------------------------------------ /*static*/ const unsigned char OutTextStream::parity_even[128] = { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif 0x00, 0x81, 0x82, 0x03, 0x84, 0x05, 0x06, 0x87, // 0x00 0x88, 0x09, 0x0A, 0x8B, 0x0C, 0x8D, 0x8E, 0x0F, 0x90, 0x11, 0x12, 0x93, 0x14, 0x95, 0x96, 0x17, // 0x10 0x18, 0x99, 0x9A, 0x1B, 0x9C, 0x1D, 0x1E, 0x9F, 0xA0, 0x21, 0x22, 0xA3, 0x24, 0xA5, 0xA6, 0x27, // 0x20 0x28, 0xA9, 0xAA, 0x2B, 0xAC, 0x2D, 0x2E, 0xAF, 0x30, 0xB1, 0xB2, 0x33, 0xB4, 0x35, 0x36, 0xB7, // 0x30 0xB8, 0x39, 0x3A, 0xBB, 0x3C, 0xBD, 0xBE, 0x3F, 0xC0, 0x41, 0x42, 0xC3, 0x44, 0xC5, 0xC6, 0x47, // 0x40 0x48, 0xC9, 0xCA, 0x4B, 0xCC, 0x4D, 0x4E, 0xCF, 0x50, 0xD1, 0xD2, 0x53, 0xD4, 0x55, 0x56, 0xD7, // 0x50 0xD8, 0x59, 0x5A, 0xDB, 0x5C, 0xDD, 0xDE, 0x5F, 0x60, 0xE1, 0xE2, 0x63, 0xE4, 0x65, 0x66, 0xE7, // 0x60 0xE8, 0x69, 0x6A, 0xEB, 0x6C, 0xED, 0xEE, 0x6F, 0xF0, 0x71, 0x72, 0xF3, 0x74, 0xF5, 0xF6, 0x77, // 0x70 0x78, 0xF9, 0xFA, 0x7B, 0xFC, 0x7D, 0x7E, 0xFF, }; //------------------------------------------------------------------------------ // OutTextStream::~OutTextStream() // Destructor. //------------------------------------------------------------------------------ /*virtual*/ OutTextStream::~OutTextStream() { #if OutTextStream_VS != 201 #error class OutTextStream has changed #endif // De-initialize close(); } //------------------------------------------------------------------------------ // OutTextStream::OutTextStream() // Constructor. //------------------------------------------------------------------------------ OutTextStream::OutTextStream(): TextStream(), m_put(NULL) { #if OutTextStream_VS != 201 #error class OutTextStream has changed #endif // Initialize } //------------------------------------------------------------------------------ // OutTextStream::openPrep() // Prepare to open an output stream. //------------------------------------------------------------------------------ bool OutTextStream::openPrep(enum FileType ftype, enum EolnType eoln) { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif // Sanity check if (m_fp != NULL) return false; // Check the file type switch (ftype) { case FTYPE_8BIT: m_put = &put_8bit; break; case FTYPE_7NONE: m_put = &put_7none; break; case FTYPE_7MARK: m_put = &put_7mark; break; case FTYPE_7EVEN: m_put = &put_7even; break; case FTYPE_7ODD: m_put = &put_7odd; break; case FTYPE_UTF8: m_put = &put_utf8; break; case FTYPE_UTF16: m_put = &put_utf16; break; case FTYPE_UTF16_R: m_put = &put_utf16r; break; case FTYPE_UTF32: m_put = &put_utf32; break; case FTYPE_UTF32_R: m_put = &put_utf32r; break; case FTYPE_24BIT: m_put = &put_24bit; break; case FTYPE_24BIT_R: m_put = &put_24bitr; break; default: return false; } // Check the newline type switch (eoln) { case EOLN_NONE: case EOLN_CR: case EOLN_CRLF: case EOLN_LF: case EOLN_NEL: case EOLN_ANY: break; default: return false; } // Done return true; } //------------------------------------------------------------------------------ // OutTextStream::open() // Open an existing stream as an output stream. //------------------------------------------------------------------------------ /*virtual*/ bool OutTextStream::open(FILE *fp, enum FileType ftype, enum EolnType eoln) { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif // Sanity check if (fp == NULL) return false; // Prepare to open the stream if (not openPrep(ftype, eoln)) return false; // Open the stream #if _WIN32 ::setmode(::fileno(fp), O_BINARY); #endif #ifdef IS_NOT_USED___ delete[] m_vbuf; m_vbuf = new char[BUF_SIZE]; if (::setvbuf(fp, m_vbuf, _IOFBF, BUF_SIZE) != 0) { delete[] m_vbuf; m_vbuf = NULL; } #endif m_fp = fp; m_mode = MODE_WRITE; m_ftype = ftype; m_eoln = eoln; return true; } //------------------------------------------------------------------------------ // OutTextStream::open() // Open a file as an output stream. //------------------------------------------------------------------------------ /*virtual*/ bool OutTextStream::open(const char *fname, enum FileType ftype, enum EolnType eoln) { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif FILE * fp; // Sanity check if (fname == NULL or fname[0] == '\0') return false; // Prepare to open the stream if (not openPrep(ftype, eoln)) return false; // Open a file m_errno = 0; fp = ::fopen(fname, "wb"); if (fp == NULL) { m_errno = errno; return false; } // Success delete[] m_vbuf; m_vbuf = new char[BUF_SIZE]; if (::setvbuf(fp, m_vbuf, _IOFBF, BUF_SIZE) != 0) { delete[] m_vbuf; m_vbuf = NULL; } m_fp = fp; m_mode = MODE_WRITE; m_ftype = ftype; m_eoln = eoln; return true; } //------------------------------------------------------------------------------ // OutTextStream::close() // Close the output stream. //------------------------------------------------------------------------------ /*virtual*/ bool OutTextStream::close() { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif FILE * fp; bool rc = true; // Sanity check if (m_fp == NULL) return true; // Close the stream fp = m_fp; m_fp = NULL; if (fp != stdout and fp != stderr) { m_errno = 0; if (::fclose(fp) < 0) { m_errno = errno; rc = false; } else { delete[] m_vbuf; m_vbuf = NULL; } } m_mode = MODE_NONE; return rc; } //------------------------------------------------------------------------------ // OutTextStream::flush() // Flush any pending output to the stream. //------------------------------------------------------------------------------ bool OutTextStream::flush() { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif // Sanity check if (m_fp == NULL) return false; // Flush the stream m_errno = 0; if (::fflush(m_fp) < 0) { m_errno = errno; return false; } return true; } //------------------------------------------------------------------------------ // OutTextStream::write() // Write a character to the (output) stream. //------------------------------------------------------------------------------ bool OutTextStream::write(int ch) { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif int rc; // Write a character if (ch == CH_EOLN) return writeln(); else { rc = (*m_put)(m_fp, ch); return (rc != EOF); } } //------------------------------------------------------------------------------ // OutTextStream::write() // Write a character string to the (output) stream. // Newline ('\n') characters within 's' cause newlines to be written by // invoking writeln(). // All other control characters are written as is to the output stream. // // Return // Number of characters written to the output stream from 'buf'. //------------------------------------------------------------------------------ int OutTextStream::write(const char *s) { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif int cnt; // Sanity checks if (s == NULL) return -1; if (m_fp == NULL) return -1; // Write characters for (cnt = 0; *s != '\0'; cnt++) { int ch; // Write a character ch = *s; s++; if (ch == '\n') { if (not writeln()) break; } else if ((*m_put)(m_fp, ch) == EOF) break; } return cnt; } //------------------------------------------------------------------------------ // OutTextStream::write() // Write characters to the (output) stream. // CH_EOLN codes within 'buf' cause newlines to be written by invoking // writeln(). // All other character codes are written as is to the output stream. // // Return // Number of characters written to the output stream from 'buf'. //------------------------------------------------------------------------------ int OutTextStream::write(const int buf[], int len) { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif int cnt; // Sanity checks if (buf == NULL) return -1; if (m_fp == NULL) return -1; // Write characters for (cnt = 0; len-- > 0; cnt++) { int ch; // Write a character ch = buf[0]; buf++; if (ch == CH_EOLN) { if (not writeln()) break; } else if ((*m_put)(m_fp, ch) == EOF) break; } return cnt; } //------------------------------------------------------------------------------ // OutTextStream::writeln() // Write a newline sequence to the (output) stream. //------------------------------------------------------------------------------ bool OutTextStream::writeln() { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif int rc; // Write a newline sequence switch (m_eoln) { case EOLN_CRLF: rc = (*m_put)(m_fp, CH_CR); if (rc != EOF) rc = (*m_put)(m_fp, CH_LF); break; case EOLN_CR: rc = (*m_put)(m_fp, CH_CR); break; case EOLN_LF: rc = (*m_put)(m_fp, CH_LF); break; case EOLN_NEL: rc = (*m_put)(m_fp, CH_NEL); break; default: case EOLN_ANY: case EOLN_NONE: rc = 0; break; } return (rc != EOF); } //------------------------------------------------------------------------------ // OutTextStream::put_7none() // Write a 7-bit ASCII with no (space) parity character to the (output) // stream. // // Unicode characters outside the range [0x0000,0x007F] are truncated, so // that only the lower 7 bits are written. //------------------------------------------------------------------------------ /*static*/ int OutTextStream::put_7none(FILE *out, int ch) { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif // Write a character return putc(ch & 0x7F, out); } //------------------------------------------------------------------------------ // OutTextStream::put_7mark() // Write a 7-bit ASCII with mark parity character to the (output) stream. // // Unicode characters outside the range [0x0000,0x007F] are truncated, so // that only the lower 7 bits are written. //------------------------------------------------------------------------------ /*static*/ int OutTextStream::put_7mark(FILE *out, int ch) { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif // Write a character return putc((ch & 0x7F) | 0x80, out); } //------------------------------------------------------------------------------ // OutTextStream::put_7even() // Write a 7-bit ASCII with even parity character to the (output) stream. // // Unicode characters outside the range [0x0000,0x007F] are truncated, so // that only the lower 7 bits are written. //------------------------------------------------------------------------------ /*static*/ int OutTextStream::put_7even(FILE *out, int ch) { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif // Write a character ch = parity_even[ch & 0x7F]; return putc(ch, out); } //------------------------------------------------------------------------------ // OutTextStream::put_7odd() // Write a 7-bit ASCII with odd parity character to the (output) stream. // // Unicode characters outside the range [0x0000,0x007F] are truncated, so // that only the lower 7 bits are written. //------------------------------------------------------------------------------ /*static*/ int OutTextStream::put_7odd(FILE *out, int ch) { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif // Write a character ch = parity_even[ch & 0x7F] ^ 0x80; return putc(ch, out); } //------------------------------------------------------------------------------ // OutTextStream::put_8bit() // Write an 8-bit (ASCII or ISO 8859-x) character to the (output) stream. // // Unicode characters outside the range [0x0000,0x00FF] are truncated, so // that only the lower 8 bits are written. //------------------------------------------------------------------------------ /*static*/ int OutTextStream::put_8bit(FILE *out, int ch) { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif // Write a character return putc(ch & 0xFF, out); } //------------------------------------------------------------------------------ // OutTextStream::put_utf8() // Write a UTF-8 character to the (output) stream. //------------------------------------------------------------------------------ /*static*/ int OutTextStream::put_utf8(FILE *out, int ch) { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif // Write a UTF-8 character if (ch < 0x0080) { // 1-byte encoding for [0x0000,0x007F] // as <0xxxxxxx>, 7 bits in 1 byte return putc(ch, out); } else if (ch < 0x0800) { // 2-byte encoding for [0x0080,0x07FF] // as <110xxxxx,10xxxxxx>, 11 bits in 2 bytes if (putc(((ch >> 6) & 0x1F) | 0xC0, out) < 0) return EOF; return putc((ch & 0x3F) | 0x80, out); } else if (ch < 0x00010000) { // 3-byte encoding for [0x0800,0xFFFF] // as <1110xxxx,10xxxxxx,10xxxxxx>, 16 bits in 3 bytes if (putc(((ch >> 2*6) & 0x0F) | 0xE0, out) < 0) return EOF; if (putc(((ch >> 1*6) & 0x3F) | 0x80, out) < 0) return EOF; return putc((ch & 0x3F) | 0x80, out); } else { // 4-byte encoding for [0x00010000,0x0010FFFF] // as <11110xxx,10xxxxxx,10xxxxxx,10xxxxxx>, 21 bits in 4 bytes if (putc(((ch >> 3*6) & 0x07) | 0xF0, out) < 0) return EOF; if (putc(((ch >> 2*6) & 0x3F) | 0x80, out) < 0) return EOF; if (putc(((ch >> 1*6) & 0x3F) | 0x80, out) < 0) return EOF; return putc((ch & 0x3F) | 0x80, out); } } //------------------------------------------------------------------------------ // OutTextStream::put_utf16() // Write a UTF-16 character to the (output) stream. //------------------------------------------------------------------------------ /*static*/ int OutTextStream::put_utf16(FILE *out, int ch) { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif int rc; // Write a UTF-16 character if (ch < 0x00010000) { rc = putc((ch >> 8) & 0xFF, out); if (rc == EOF) return rc; rc = putc(ch & 0xFF, out); } else { int lo, hi; // Write a surrogate pair sequence // Encoding for [0b000000000001xxyyyyyyyywwzzzzzzzz] // as <110110xx,yyyyyyyy,110111ww,zzzzzzzz>, 20 bits in 4 bytes lo = 0xDC00 | (0x03FF & ch); hi = 0xD800 | (0x03FF & (ch >> 10)); rc = putc((hi >> 8) & 0xFF, out); if (rc == EOF) return rc; rc = putc(hi & 0xFF, out); if (rc == EOF) return rc; rc = putc((lo >> 8) & 0xFF, out); if (rc == EOF) return rc; rc = putc(lo & 0xFF, out); } return rc; } //------------------------------------------------------------------------------ // OutTextStream::put_utf16r() // Write a UTF-16 little-endian character to the (output) stream. //------------------------------------------------------------------------------ /*static*/ int OutTextStream::put_utf16r(FILE *out, int ch) { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif int rc; // Write a UTF-16 reversed character if (ch < 0x00010000) { rc = putc(ch & 0xFF, out); if (rc == EOF) return rc; rc = putc((ch >> 8) & 0xFF, out); } else { int lo, hi; // Write a surrogate pair sequence // Encoding for [0b000000000001xxyyyyyyyywwzzzzzzzz] // as , 20 bits in 4 bytes lo = 0xDC00 | (0x03FF & ch); hi = 0xD800 | (0x03FF & (ch >> 10)); rc = putc(hi & 0xFF, out); if (rc == EOF) return rc; rc = putc((hi >> 8) & 0xFF, out); if (rc == EOF) return rc; rc = putc(lo & 0xFF, out); if (rc == EOF) return rc; rc = putc((lo >> 8) & 0xFF, out); } return rc; } //------------------------------------------------------------------------------ // OutTextStream::put_utf32() // Write a UTF-32 character to the (output) stream. //------------------------------------------------------------------------------ /*static*/ int OutTextStream::put_utf32(FILE *out, int ch) { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif int rc; // Write a UTF-32 character rc = putc((ch >> 8*3) & 0xFF, out); if (rc == EOF) return rc; rc = putc((ch >> 8*2) & 0xFF, out); if (rc == EOF) return rc; rc = putc((ch >> 8*1) & 0xFF, out); if (rc == EOF) return rc; rc = putc(ch & 0xFF, out); return rc; } //------------------------------------------------------------------------------ // OutTextStream::put_utf32r() // Write a UTF-32 little-endian character to the (output) stream. //------------------------------------------------------------------------------ /*static*/ int OutTextStream::put_utf32r(FILE *out, int ch) { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif int rc; // Write a UTF-32 reversed character rc = putc(ch & 0xFF, out); if (rc == EOF) return rc; rc = putc((ch >> 8*1) & 0xFF, out); if (rc == EOF) return rc; rc = putc((ch >> 8*2) & 0xFF, out); if (rc == EOF) return rc; rc = putc((ch >> 8*3) & 0xFF, out); return rc; } //------------------------------------------------------------------------------ // OutTextStream::put_24bit() // Write a 24-bit character to the (output) stream. //------------------------------------------------------------------------------ /*static*/ int OutTextStream::put_24bit(FILE *out, int ch) { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif int rc; // Write a 24-bit Unicode character rc = putc((ch >> 8*2) & 0xFF, out); if (rc == EOF) return rc; rc = putc((ch >> 8*1) & 0xFF, out); if (rc == EOF) return rc; rc = putc(ch & 0xFF, out); return rc; } //------------------------------------------------------------------------------ // OutTextStream::put_24bitr() // Write a 24-bit little-endian character to the (output) stream. //------------------------------------------------------------------------------ /*static*/ int OutTextStream::put_24bitr(FILE *out, int ch) { #if OutTextStream_VS/100 != 2 #error class OutTextStream has changed #endif int rc; // Write a 24-bit Unicode reversed character rc = putc(ch & 0xFF, out); if (rc == EOF) return rc; rc = putc((ch >> 8*1) & 0xFF, out); if (rc == EOF) return rc; rc = putc((ch >> 8*2) & 0xFF, out); return rc; } // End outtextstream.cpp