Aegisub/devel/vsfilter/libssf/Stream.cpp

360 lines
6.8 KiB
C++

/*
* Copyright (C) 2003-2006 Gabest
* http://www.gabest.org
*
* This Program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This Program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Make; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/gpl.html
*
*/
#include "stdafx.h"
#include "Stream.h"
#include <stdio.h>
namespace ssf
{
Stream::Stream()
: m_encoding(none)
, m_line(0)
, m_col(-1)
{
}
Stream::~Stream()
{
}
void Stream::ThrowError(LPCTSTR fmt, ...)
{
va_list args;
va_start(args, fmt);
int len = _vsctprintf(fmt, args) + 1;
CString str;
if(len > 0) _vstprintf_s(str.GetBufferSetLength(len), len, fmt, args);
va_end(args);
throw Exception(_T("Error (Ln %d Col %d): %s"), m_line+1, m_col+1, str);
}
bool Stream::IsWhiteSpace(int c, LPCWSTR morechars)
{
return c != 0xa0 && iswspace(c) || morechars && wcschr(morechars, (WCHAR)c);
}
//
InputStream::InputStream()
{
}
InputStream::~InputStream()
{
}
int InputStream::NextChar()
{
if(m_encoding == none)
{
m_encoding = unknown;
switch(NextByte())
{
case 0xef:
if(NextByte() == 0xbb && NextByte() == 0xbf) m_encoding = utf8;
break;
case 0xff:
if(NextByte() == 0xfe) m_encoding = utf16le;
break;
case 0xfe:
if(NextByte() == 0xff) m_encoding = utf16be;
break;
}
}
if(m_encoding == unknown)
{
throw Exception(_T("unknown character encoding, missing BOM"));
}
int i, c;
int cur = NextByte();
switch(m_encoding)
{
case utf8:
for(i = 7; i >= 0 && (cur & (1 << i)); i--);
cur &= (1 << i) - 1;
while(++i < 7) {c = NextByte(); if(c == EOS) {cur = EOS; break;} cur = (cur << 6) | (c & 0x3f);}
break;
case utf16le:
c = NextByte();
if(c == EOS) {cur = EOS; break;}
cur = (c << 8) | cur;
break;
case utf16be:
c = NextByte();
if(c == EOS) {cur = EOS; break;}
cur = cur | (c << 8);
break;
case wchar:
break;
}
return cur;
}
int InputStream::PushChar()
{
int c = NextChar();
m_queue.AddTail(c);
return c;
}
int InputStream::PopChar()
{
if(m_queue.IsEmpty()) ThrowError(_T("fatal stream error"));
int c = m_queue.RemoveHead();
if(c != EOS)
{
if(c == '\n') {m_line++; m_col = -1;}
m_col++;
}
return c;
}
int InputStream::PeekChar()
{
while(m_queue.GetCount() < 2) PushChar();
ASSERT(m_queue.GetCount() == 2);
if(m_queue.GetHead() == '/' && m_queue.GetTail() == '/')
{
while(!m_queue.IsEmpty()) PopChar();
int c;
do {PushChar(); c = PopChar();} while(!(c == '\n' || c == EOS));
return PeekChar();
}
else if(m_queue.GetHead() == '/' && m_queue.GetTail() == '*')
{
while(!m_queue.IsEmpty()) PopChar();
int c1, c2;
PushChar();
do {c2 = PushChar(); c1 = PopChar();} while(!((c1 == '*' && c2 == '/') || c1 == EOS));
PopChar();
return PeekChar();
}
return m_queue.GetHead();
}
int InputStream::GetChar()
{
if(m_queue.GetCount() < 2) PeekChar();
return PopChar();
}
int InputStream::SkipWhiteSpace(LPCWSTR morechars)
{
int c = PeekChar();
for(; IsWhiteSpace(c, morechars); c = PeekChar())
GetChar();
return c;
}
// FileInputStream
FileInputStream::FileInputStream(const TCHAR* fn)
: m_file(NULL)
{
if(_tfopen_s(&m_file, fn, _T("r")) != 0) ThrowError(_T("cannot open file '%s'"), fn);
}
FileInputStream::~FileInputStream()
{
if(m_file) {fclose(m_file); m_file = NULL;}
}
int FileInputStream::NextByte()
{
if(!m_file) ThrowError(_T("file pointer is NULL"));
return fgetc(m_file);
}
// MemoryInputStream
MemoryInputStream::MemoryInputStream(BYTE* pBytes, int len, bool fCopy, bool fFree)
: m_pBytes(NULL)
, m_pos(0)
, m_len(len)
{
if(fCopy)
{
m_pBytes = new BYTE[len];
if(m_pBytes) memcpy(m_pBytes, pBytes, len);
m_fFree = true;
}
else
{
m_pBytes = pBytes;
m_fFree = fFree;
}
if(!m_pBytes) ThrowError(_T("memory stream pointer is NULL"));
}
MemoryInputStream::~MemoryInputStream()
{
if(m_fFree) delete [] m_pBytes;
m_pBytes = NULL;
}
int MemoryInputStream::NextByte()
{
if(!m_pBytes) ThrowError(_T("memory stream pointer is NULL"));
if(m_pos >= m_len) return Stream::EOS;
return (int)m_pBytes[m_pos++];
}
// WCharInputStream
WCharInputStream::WCharInputStream(CStringW str)
: m_str(str)
, m_pos(0)
{
m_encoding = Stream::wchar; // HACK: it should return real bytes from NextByte (two per wchar_t), but this way it's a lot more simple...
}
int WCharInputStream::NextByte()
{
if(m_pos >= m_str.GetLength()) return Stream::EOS;
return m_str[m_pos++];
}
// OutputStream
OutputStream::OutputStream(encoding_t e)
{
m_encoding = e;
m_bof = true;
}
OutputStream::~OutputStream()
{
}
void OutputStream::PutChar(WCHAR c)
{
if(m_bof)
{
m_bof = false;
switch(m_encoding)
{
case utf8:
case utf16le:
case utf16be:
PutChar(0xfeff);
break;
}
}
switch(m_encoding)
{
case utf8:
if(0 <= c && c < 0x80) // 0xxxxxxx
{
NextByte(c);
}
else if(0x80 <= c && c < 0x800) // 110xxxxx 10xxxxxx
{
NextByte(0xc0 | ((c<<2)&0x1f));
NextByte(0x80 | ((c<<0)&0x3f));
}
else if(0x800 <= c && c < 0xFFFF) // 1110xxxx 10xxxxxx 10xxxxxx
{
NextByte(0xe0 | ((c<<4)&0x0f));
NextByte(0x80 | ((c<<2)&0x3f));
NextByte(0x80 | ((c<<0)&0x3f));
}
else
{
NextByte('?');
}
break;
case utf16le:
NextByte(c & 0xff);
NextByte((c >> 8) & 0xff);
break;
case utf16be:
NextByte((c >> 8) & 0xff);
NextByte(c & 0xff);
break;
case wchar:
NextByte(c);
break;
}
}
void OutputStream::PutString(LPCWSTR fmt, ...)
{
CStringW str;
va_list args;
va_start(args, fmt);
int len = _vscwprintf(fmt, args) + 1;
if(len > 0) vswprintf_s(str.GetBufferSetLength(len), len, fmt, args);
va_end(args);
LPCWSTR s = str;
while(*s) PutChar(*s++);
}
// WCharOutputStream
WCharOutputStream::WCharOutputStream()
: OutputStream(wchar)
{
}
void WCharOutputStream::NextByte(int b)
{
m_str += (WCHAR)b;
}
// DebugOutputStream
DebugOutputStream::DebugOutputStream()
: OutputStream(wchar)
{
}
DebugOutputStream::~DebugOutputStream()
{
TRACE(_T("%s\n"), m_str);
}
void DebugOutputStream::NextByte(int b)
{
if(b == '\n') {TRACE(_T("%s\n"), m_str); m_str.Empty();}
else if(b != '\r') m_str += (WCHAR)b;
}
}