/*
* fhist - file history and comparison tools
* Copyright (C) 1991-1994, 1998-2000, 2002 Peter Miller;
* All rights reserved.
*
* Derived from a work
* Copyright (C) 1990 David I. Bell.
*
* 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 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
*
* MANIFEST: functions to read and write ascii files
*
* Functions to position within an ascii file and read lines from it.
*/
#include <ac/errno.h>
#include <fcntl.h>
#include <ac/libintl.h>
#include <ac/stdarg.h>
#include <ac/stdio.h>
#include <ac/string.h>
#include <ac/unistd.h>
#include <error_intl.h>
#include <fcheck.h>
#include <fileio.h>
#include <cmalloc.h>
#include <str.h>
#define LINEALLOCSIZE 132 /* allocation size for line buffer */
/*
* Position absolutely within a file to the indicated byte position,
* returns a negative value if failed.
*/
void
seekf(FILE *fp, long pos, const char *filename)
{
if (fseek(fp, pos, SEEK_SET) == -1)
{
sub_context_ty *scp;
scp = sub_context_new();
sub_errno_set(scp);
sub_var_set_charstar(scp, "File_Name", filename);
sub_var_set_long(scp, "Number", pos);
fatal_intl(scp, i18n("fseek(\"$filename\", $number): $errno"));
}
}
/*
* Skip forwards through a file by the specified number of lines.
* Returns nonzero on end of file or any error.
*/
int
skipf(FILE *fp, long count, const char *filename)
{
int c;
while (count > 0)
{
c = getc(fp);
if (c == EOF)
{
if (ferror(fp))
{
sub_context_ty *scp;
scp = sub_context_new();
sub_errno_set(scp);
sub_var_set_charstar(scp, "File_Name", filename);
fatal_intl(scp, i18n("read \"$filename\": $errno"));
}
return -1;
}
if (c == '\n')
--count;
}
return 0;
}
/*
* Function to read in the next line from a file, no matter how long it is.
* This returns a pointer to the string, and also indirectly its length.
* The string is normally returned from a static string, which is reused
* for each call. But if keep is non-zero, then the returned string
* belongs to the caller and must later be freed. The returned line
* is ended with a newline and null character, but the returned length
* does not count the null character. Returns 0 on an error, with the
* error stored in the fp structure.
*/
char *
readlinef(FILE *fp, long *retlen, int keep, const char *filename,
int *is_bin_file)
{
char *cp; /* pointer to character string */
char *dp; /* destination pointer */
long cursize; /* current size */
long count; /* length of data read from file */
long totallen; /* total length of data */
static char *linebuffer; /* common line buffer */
static long linelength; /* total length of line */
totallen = 0;
if (linelength == 0)
{
/* allocate line buffer */
linebuffer = r_alloc_and_check(LINEALLOCSIZE + 1);
linelength = LINEALLOCSIZE;
}
cp = linebuffer;
cursize = linelength;
for (;;)
{
count = readf(fp, cp, cursize, filename, is_bin_file);
if (count <= 0)
{
if (totallen == 0)
return 0;
warning_last_line_unterminated(filename);
count = 1;
cp[0] = '\n';
}
totallen += count;
if (cp[count - 1] == '\n')
break;
linebuffer =
r_realloc_and_check(linebuffer, linelength + LINEALLOCSIZE + 1);
cp = linebuffer + totallen;
linelength += LINEALLOCSIZE;
cursize = LINEALLOCSIZE;
}
if (retlen)
*retlen = totallen;
if (!keep)
dp = linebuffer;
else
{
dp = cm_alloc_and_check(totallen + 1);
memcpy(dp, linebuffer, (size_t)totallen);
}
dp[totallen] = 0;
return dp;
}
/*
* Read data from the current position in a file until a newline is found
* or the supplied buffer is full. These two cases can be distinguished by
* checking the last character of the returned data. Returns the number of
* characters in the line (including the newline), or -1 if an error or end
* of file in the middle of the line is found. Line is terminated by a null
* character if space allows. Returns zero if the end of file occurs at
* the front of the line.
*/
long
readf(FILE *fp, char *buf, long size, const char *filename, int *is_bin_file)
{
char *bp;
char *ep;
int c;
bp = buf;
ep = buf + size;
while (bp < ep)
{
c = getc(fp);
if (c == EOF)
{
if (ferror(fp))
{
sub_context_ty *scp;
scp = sub_context_new();
sub_errno_set(scp);
sub_var_set_charstar(scp, "File_Name", filename);
fatal_intl(scp, i18n("read \"$filename\": $errno"));
}
if (bp != buf)
break;
return -1;
}
if (c == 0)
{
c = 0x80;
*is_bin_file = 1;
}
*bp++ = c;
if (c == '\n')
break;
}
return (bp - buf);
}
/*
* Write data at the current position in a file.
* This buffers the data in the current block, thus flushing is needed later.
* Prints a fatal error message and exits on errors.
*/
void
writefx(FILE *fp, char *buf, long size, const char *filename)
{
fwrite(buf, 1, size, fp);
if (ferror(fp))
{
sub_context_ty *scp;
scp = sub_context_new();
sub_errno_set(scp);
sub_var_set_charstar(scp, "File_Name", filename);
fatal_intl(scp, i18n("write \"$filename\": $errno"));
}
}
/*
* Function to copy data from one file to another. Data is copied from
* the current position of the input file to the current position of the
* output file until end of file is reached on the input file, or until
* the specified number of characters has been read. Minus one means
* there is no limit to the amount to be copied. Line boundaries and
* space expansions are ignored. Returns nonzero on errors.
*/
int
copyf(FILE *ip, FILE *op, long count, const char *ifn, const char *ofn)
{
char buffer[1 << 10];
long len;
while (count)
{
if (count < 0 || count > sizeof(buffer))
len = sizeof(buffer);
else
len = count;
len = fread(buffer, 1, len, ip);
if (len < 0)
return EOF;
if (len == 0)
break;
fwrite(buffer, 1, len, op);
if (ferror(op))
return EOF;
if (count >= 0)
count -= len;
}
return 0;
}
void
copyfx(FILE *ip, FILE *op, long count, const char *ifn, const char *ofn)
{
char buffer[1 << 10];
long len;
sub_context_ty *scp;
while (count)
{
if (count < 0 || count > sizeof(buffer))
len = sizeof(buffer);
else
len = count;
len = fread(buffer, 1, len, ip);
if (len < 0)
{
scp = sub_context_new();
sub_errno_set(scp);
sub_var_set_charstar(scp, "File_Name", ifn);
fatal_intl(scp, i18n("read \"$filename\": $errno"));
}
if (len == 0)
break;
fwrite(buffer, 1, len, op);
if (ferror(op))
{
scp = sub_context_new();
sub_errno_set(scp);
sub_var_set_charstar(scp, "File_Name", ofn);
fatal_intl(scp, i18n("write \"$filename\": $errno"));
}
if (count >= 0)
count -= len;
}
fflush_and_check(op, ofn);
}
/*
* Function to type out the rest of an input file to the terminal.
* It is assumed that we are positioned at the beginning of a line.
*/
int
typef(FILE *fp, const char *filename)
{
return copyf(fp, stdout, -1L, filename, gettext("standard output"));
}
void
typefx(FILE *fp, const char *filename)
{
copyfx(fp, stdout, -1L, filename, gettext("standard output"));
}
syntax highlighted by Code2HTML, v. 0.9.1