/* * 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 #include #include #include #include #include #include #include #include #include #include #include #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")); }