//
// srecord - manipulate eprom load files
// Copyright (C) 1998-2002, 2005-2007 Peter Miller
//
// 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 3 of the License, 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 this program. If not, see
// .
//
#include
#include
#include
using namespace std;
#include
#include
#include
bool srec_output_file::data_only_flag = false;
bool srec_output_file::crlf_flag = false;
srec_output_file::srec_output_file() :
file_name("standard output"),
line_number(1),
vfp(0),
checksum(0),
position(0),
is_regular(true)
{
vfp = stdout;
set_is_regular();
}
const char *
srec_output_file::mode()
const
{
return (crlf_flag ? "wb" : "w");
}
srec_output_file::srec_output_file(const string &a_file_name) :
file_name(a_file_name),
line_number(1),
vfp(0),
checksum(0),
position(0),
is_regular(true)
{
if (file_name == "-")
{
file_name = "standard output";
vfp = stdout;
set_is_regular();
}
else
{
//
// The call to fopen is deferred until the constructor has
// completed. This is so that the virtual mode() method
// is available (it isn't in the base class constructor).
//
}
}
void *
srec_output_file::get_fp()
{
if (!vfp)
{
//
// The call to fopen is deferred until the constructor has
// completed. This is so that the virtual mode() method
// is available (it isn't in the base class constructor).
//
vfp = fopen(file_name.c_str(), mode());
if (!vfp)
fatal_error_errno("open");
set_is_regular();
}
return vfp;
}
srec_output_file::~srec_output_file()
{
FILE *fp = (FILE *)get_fp();
if (fflush(fp))
fatal_error_errno("write");
if (fp != stdout && fclose(fp))
fatal_error_errno("close");
}
const string
srec_output_file::filename()
const
{
char buffer[20];
sprintf(buffer, ": %d", line_number);
return (file_name + buffer);
}
void
srec_output_file::put_char(int c)
{
FILE *fp = (FILE *)get_fp();
if (crlf_flag && c == '\n')
{
putc('\r', fp);
++position;
}
putc(c, fp);
if (ferror(fp))
fatal_error_errno("write");
if (c == '\n')
++line_number;
++position;
}
void
srec_output_file::put_nibble(int n)
{
put_char("0123456789ABCDEF"[n & 15]);
}
void
srec_output_file::put_byte(unsigned char n)
{
put_nibble(n >> 4);
put_nibble(n);
checksum_add(n);
}
void
srec_output_file::put_word(int n)
{
put_byte(n >> 8);
put_byte(n);
}
void
srec_output_file::put_3bytes(unsigned long n)
{
put_byte(n >> 16);
put_byte(n >> 8);
put_byte(n);
}
void
srec_output_file::put_4bytes(unsigned long n)
{
put_byte(n >> 24);
put_byte(n >> 16);
put_byte(n >> 8);
put_byte(n);
}
int
srec_output_file::checksum_get()
{
return (checksum & 0xFF);
}
int
srec_output_file::checksum_get16()
{
return (checksum & 0xFFFF);
}
void
srec_output_file::checksum_reset()
{
checksum = 0;
}
void
srec_output_file::checksum_add(unsigned char n)
{
checksum += n;
}
void
srec_output_file::seek_to(unsigned long address)
{
//
// Seeking on non-regular files is problematic. Avoid doing
// this if possible. (Usually we can, srec_cat emits records
// in ascending address order.)
//
if (!is_regular)
{
while (position < address)
put_char(0);
}
if (address == position)
return;
//
// We'll have to try a seek.
//
FILE *fp = (FILE *)get_fp();
errno = 0;
if (fseek(fp, address, 0) < 0)
{
if (errno == EINVAL && address >= 0x80000000uL)
{
warning
(
"It appears that the implementation of fseek on your "
"system is unable to cope with addresses which have "
"the most significant bit set (this is POSIX and ANSI "
"C conforming behaviour). You probably did not intend "
"to create a %3.1fGB file. See the manual for a "
"description of the --offset filter, remembering that "
"you can give negative offsets.",
((double)address / (double)(1uL << 30))
);
}
fatal_error_errno("seek 0x%lX", address);
}
position = address;
}
void
srec_output_file::put_string(const char *s)
{
while (*s)
put_char(*s++);
}
void
srec_output_file::put_string(const string &s)
{
const char *cp = s.c_str();
const char *ep = cp + s.size();
while (cp < ep)
put_char(*cp++);
}
void
srec_output_file::put_stringf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
char buffer[1000];
vsprintf(buffer, fmt, ap);
va_end(ap);
put_string(buffer);
}
void
srec_output_file::data_only()
{
data_only_flag = true;
}
void
srec_output_file::crlf()
{
crlf_flag = true;
}
void
srec_output_file::set_is_regular()
{
FILE *fp = (FILE *)vfp;
struct stat st;
is_regular = fstat(fileno(fp), &st) == 0 && S_ISREG(st.st_mode);
}
void
srec_output_file::fatal_alignment_error(int multiple)
{
if (multiple > 4)
{
fatal_error
(
"The %s output format uses %d-byte alignment, but unaligned "
"data is present. Use a \"--fill 0xNN --within "
"--range-padding %d\" filter to fix this problem.",
format_name(),
multiple,
multiple
);
}
else
{
fatal_error
(
"The %s output format uses %d-bit data, but unaligned "
"data is present. Use a \"--fill 0xNN --within "
" --range-padding %d\" filter to fix this "
"problem.",
format_name(),
multiple * 8,
multiple
);
}
}