/*
**********************************************************************
* Copyright (c) 2004,2011 International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
* Author: Alan Liu
* Created: March 19 2004
* Since: ICU 3.0
**********************************************************************
*/
#include "textfile.h"
#include "cmemory.h"
#include "cstring.h"
#include "intltest.h"
#include "util.h"
// If the symbol CCP is defined, then the 'name' and 'encoding'
// constructor parameters are copied. Otherwise they are aliased.
// #define CCP
TextFile::TextFile(const char* _name, const char* _encoding, UErrorCode& ec) :
file(0),
name(0), encoding(0),
buffer(0),
capacity(0),
lineNo(0)
{
if (U_FAILURE(ec) || _name == 0 || _encoding == 0) {
if (U_SUCCESS(ec)) {
ec = U_ILLEGAL_ARGUMENT_ERROR;
}
return;
}
#ifdef CCP
name = uprv_malloc(uprv_strlen(_name) + 1);
encoding = uprv_malloc(uprv_strlen(_encoding) + 1);
if (name == 0 || encoding == 0) {
ec = U_MEMORY_ALLOCATION_ERROR;
return;
}
uprv_strcpy(name, _name);
uprv_strcpy(encoding, _encoding);
#else
name = (char*) _name;
encoding = (char*) _encoding;
#endif
const char* testDir = IntlTest::getSourceTestData(ec);
if (U_FAILURE(ec)) {
return;
}
if (!ensureCapacity((int32_t)(uprv_strlen(testDir) + uprv_strlen(name) + 1))) {
ec = U_MEMORY_ALLOCATION_ERROR;
return;
}
uprv_strcpy(buffer, testDir);
uprv_strcat(buffer, name);
file = T_FileStream_open(buffer, "rb");
if (file == 0) {
ec = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
}
TextFile::~TextFile() {
if (file != 0) T_FileStream_close(file);
if (buffer != 0) uprv_free(buffer);
#ifdef CCP
uprv_free(name);
uprv_free(encoding);
#endif
}
UBool TextFile::readLine(UnicodeString& line, UErrorCode& ec) {
if (T_FileStream_eof(file)) {
return FALSE;
}
// Note: 'buffer' may change after ensureCapacity() is called,
// so don't use
// p=buffer; *p++=c;
// but rather
// i=; buffer[i++]=c;
int32_t n = 0;
for (;;) {
int c = T_FileStream_getc(file); // sic: int, not int32_t
if (c < 0 || c == 0xD || c == 0xA) {
// consume 0xA following 0xD
if (c == 0xD) {
c = T_FileStream_getc(file);
if (c != 0xA && c >= 0) {
T_FileStream_ungetc(c, file);
}
}
break;
}
if (!setBuffer(n++, c, ec)) return FALSE;
}
if (!setBuffer(n++, 0, ec)) return FALSE;
UnicodeString str(buffer, encoding);
// Remove BOM in first line, if present
if (lineNo == 0 && str[0] == 0xFEFF) {
str.remove(0, 1);
}
++lineNo;
line = str.unescape();
return TRUE;
}
UBool TextFile::readLineSkippingComments(UnicodeString& line, UErrorCode& ec,
UBool trim) {
for (;;) {
if (!readLine(line, ec)) return FALSE;
// Skip over white space
int32_t pos = 0;
ICU_Utility::skipWhitespace(line, pos, TRUE);
// Ignore blank lines and comment lines
if (pos == line.length() || line.charAt(pos) == 0x23/*'#'*/) {
continue;
}
// Process line
if (trim) line.remove(0, pos);
return TRUE;
}
}
/**
* Set buffer[index] to c, growing buffer if necessary. Return TRUE if
* successful.
*/
UBool TextFile::setBuffer(int32_t index, char c, UErrorCode& ec) {
if (capacity <= index) {
if (!ensureCapacity(index+1)) {
ec = U_MEMORY_ALLOCATION_ERROR;
return FALSE;
}
}
buffer[index] = c;
return TRUE;
}
/**
* Make sure that 'buffer' has at least 'mincapacity' bytes.
* Return TRUE upon success. Upon return, 'buffer' may change
* value. In any case, previous contents are preserved.
*/
#define LOWEST_MIN_CAPACITY 64
UBool TextFile::ensureCapacity(int32_t mincapacity) {
if (capacity >= mincapacity) {
return TRUE;
}
// Grow by factor of 2 to prevent frequent allocation
// Note: 'capacity' may be 0
int32_t i = (capacity < LOWEST_MIN_CAPACITY)? LOWEST_MIN_CAPACITY: capacity;
while (i < mincapacity) {
i <<= 1;
if (i < 0) {
i = 0x7FFFFFFF;
break;
}
}
mincapacity = i;
// Simple realloc() no good; contents not preserved
// Note: 'buffer' may be 0
char* newbuffer = (char*) uprv_malloc(mincapacity);
if (newbuffer == 0) {
return FALSE;
}
if (buffer != 0) {
uprv_strncpy(newbuffer, buffer, capacity);
uprv_free(buffer);
}
buffer = newbuffer;
capacity = mincapacity;
return TRUE;
}