Skip to content

Commit

Permalink
Fix file truncation issues (again)
Browse files Browse the repository at this point in the history
The previous fix fixed the issue for OS X/Linux, but on Windows the
call to fflush() was moving the current position in the file, breaking
truncation.

Now the current file position is saved before fflush(). Additionally on
Windows, POSIX function such as ftruncate() are now used directly for
consistency since Mingw-w64 supports them.
  • Loading branch information
calebj0seph committed Feb 22, 2016
1 parent 4f339c9 commit 2a4d1f2
Show file tree
Hide file tree
Showing 7 changed files with 38 additions and 52 deletions.
22 changes: 14 additions & 8 deletions newline.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <unistd.h>

#ifdef _WIN32
#include <io.h>
#include <fcntl.h>
#include <share.h>
#include <sys/stat.h>
#define delete(file) _wunlink(file)
#else
#define delete(file) unlink(file)
#endif // _WIN32

#include "args.h"
Expand Down Expand Up @@ -88,21 +92,23 @@ int main(int argc, char** argv) {
// permission bits or owners. ReplaceFile() does this on
// Windows, but an easy solution for Unix systems doesn't seem
// to exist.
seek(file, 0, SEEK_SET);
seek(temp_file->file, 0, SEEK_SET);
fseeko(file, 0, SEEK_SET);
fseeko(temp_file->file, 0, SEEK_SET);
uint8_t* buffer = malloc(FileBufferLen);
size_t read_bytes = read(
temp_file->file, buffer, FileBufferLen
size_t read_bytes = fread(
buffer, 1, FileBufferLen, temp_file->file
);
while(read_bytes) {
write(file, buffer, read_bytes);
read_bytes = read(
temp_file->file, buffer, FileBufferLen
fwrite(buffer, 1, read_bytes, file);
read_bytes = fread(
buffer, 1, FileBufferLen, temp_file->file
);
}
free(buffer);

off_t file_len = ftello(file);
fflush(file);
truncate(file);
ftruncate(fileno(file), file_len);
}
if(args.verbose) {
if(result) {
Expand Down
1 change: 0 additions & 1 deletion tempfile-apple.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "trim.h"
#include "tempfile.h"

struct TempFile* make_temp_file(const char* format) {
Expand Down
1 change: 0 additions & 1 deletion tempfile-linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#include <string.h>
#include <paths.h>
#include <unistd.h>
#include "trim.h"
#include "tempfile.h"

struct TempFile* make_temp_file(const char* format) {
Expand Down
1 change: 0 additions & 1 deletion tempfile-win32.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include <fcntl.h>
#include <share.h>
#include <sys/stat.h>
#include "trim.h"
#include "tempfile.h"

static wchar_t alpha_num[] = {
Expand Down
3 changes: 3 additions & 0 deletions tempfile.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ struct TempFile {
#endif // _WIN32
};

/* Buffer length for file IO operations (64 KiB) */
static const size_t FileBufferLen = 64*1024;

/* Returns a file handle to a temporary file with read and write access. The
'format' parameter defines the filename of the temporary file. It must contain
a single '%' character which will be replaced with random letters and digits to
Expand Down
40 changes: 21 additions & 19 deletions trim.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include "args.h"
#include "trim.h"

Expand All @@ -15,12 +16,12 @@ bool trim_file(FILE* in_file, FILE* out_file, enum NewlineType newline_type,
size_t num_cr = 0;

uint8_t cur_byte;
size_t cur_bytes_len = read(in_file, &cur_byte, 1);
size_t cur_bytes_len = fread(&cur_byte, 1, 1, in_file);
while(cur_bytes_len) {
if((char)cur_byte == '\r' || (char)cur_byte == '\n') {
// Determine current newline type
uint8_t next_byte;
size_t next_bytes_len = read(in_file, &next_byte, 1);
size_t next_bytes_len = fread(&next_byte, 1, 1, in_file);
enum NewlineType cur_newline;
if(next_bytes_len && (char)cur_byte == '\r' &&
(char)next_byte == '\n') {
Expand All @@ -36,13 +37,13 @@ bool trim_file(FILE* in_file, FILE* out_file, enum NewlineType newline_type,

// Go back one character if we didn't read a CRLF
if(next_bytes_len && cur_newline != CRLF) {
seek(in_file, -1, SEEK_CUR);
fseeko(in_file, -1, SEEK_CUR);
}

// Handle trailing whitespace
if(strip && consecutive_whitespace > 0) {
changes_made = true;
seek(out_file, -consecutive_whitespace, SEEK_CUR);
fseeko(out_file, -consecutive_whitespace, SEEK_CUR);
consecutive_whitespace = 0;
}

Expand All @@ -53,17 +54,17 @@ bool trim_file(FILE* in_file, FILE* out_file, enum NewlineType newline_type,
newline_to_write = newline_type;
}
if(newline_to_write == LF) {
write(out_file, ((uint8_t[]){10}), 1);
fwrite((uint8_t[]){10}, 1, 1, out_file);
if(trailing_newline) {
consecutive_newline += 1;
}
} else if(newline_to_write == CRLF) {
write(out_file, ((uint8_t[]){13, 10}), 2);
fwrite((uint8_t[]){13, 10}, 1, 2, out_file);
if(trailing_newline) {
consecutive_newline += 2;
}
} else {
write(out_file, ((uint8_t[]){13}), 1);
fwrite((uint8_t[]){13}, 1, 1, out_file);
if(trailing_newline) {
consecutive_newline += 1;
}
Expand All @@ -75,36 +76,36 @@ bool trim_file(FILE* in_file, FILE* out_file, enum NewlineType newline_type,
} else {
consecutive_whitespace += 1;
}
write(out_file, &cur_byte, 1);
fwrite(&cur_byte, 1, 1, out_file);
} else {
// Write normal character
consecutive_whitespace = 0;
consecutive_newline = 0;
write(out_file, &cur_byte, 1);
fwrite(&cur_byte, 1, 1, out_file);
}
cur_bytes_len = read(in_file, &cur_byte, 1);
cur_bytes_len = fread(&cur_byte, 1, 1, in_file);
}

// Handle trailing whitespace at end of file
if(strip && consecutive_whitespace > 0) {
changes_made = true;
seek(out_file, -consecutive_whitespace, SEEK_CUR);
fseeko(out_file, -consecutive_whitespace, SEEK_CUR);
consecutive_whitespace = 0;
}

// Handle trailing newlines
if(trailing_newline) {
if(consecutive_newline > 0) {
// Trim excess trailing newlines
seek(out_file, -consecutive_newline, SEEK_CUR);
cur_bytes_len = read(out_file, &cur_byte, 1);
fseeko(out_file, -consecutive_newline, SEEK_CUR);
cur_bytes_len = fread(&cur_byte, 1, 1, out_file);
if(cur_bytes_len && (char)cur_byte == '\n' &&
consecutive_newline > 1) {
// Need to truncate the file after the first LF
changes_made = true;
} else if(cur_bytes_len && (char)cur_byte == '\r') {
// Consume the LF followed by CR if it exists
cur_bytes_len = read(out_file, &cur_byte, 1);
cur_bytes_len = fread(&cur_byte, 1, 1, out_file);
if(cur_bytes_len && (char)cur_byte == '\n') {
if(consecutive_newline > 2) {
// Need to truncate the file after the first CRLF
Expand All @@ -117,7 +118,7 @@ bool trim_file(FILE* in_file, FILE* out_file, enum NewlineType newline_type,
}
if(cur_bytes_len) {
// Seek back if we didn't read a CRLF
seek(out_file, -1, SEEK_CUR);
fseeko(out_file, -1, SEEK_CUR);
}
}
}
Expand All @@ -135,19 +136,20 @@ bool trim_file(FILE* in_file, FILE* out_file, enum NewlineType newline_type,
}
}
if(trailing_newline_type == LF) {
write(out_file, ((uint8_t[]){10}), 1);
fwrite(((uint8_t[]){10}), 1, 1, out_file);
} else if(trailing_newline_type == CRLF) {
write(out_file, ((uint8_t[]){13, 10}), 2);
fwrite((uint8_t[]){13, 10}, 1, 2, out_file);
} else {
write(out_file, ((uint8_t[]){13}), 1);
fwrite(((uint8_t[]){13}), 1, 1, out_file);
}
changes_made = true;
}
}

off_t out_file_len = ftello(out_file);
fflush(out_file);
if(changes_made) {
truncate(out_file);
ftruncate(fileno(out_file), out_file_len);
}
return changes_made;
}
22 changes: 0 additions & 22 deletions trim.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,6 @@
#include <stdio.h>
#include "args.h"

// Rely on _FILE_OFFSET_BITS=64 for LFS on both Windows and POSIX.
#ifdef _WIN32
#include <io.h>
#define seek(file, offset, whence) fseeko(file, offset, whence)
#define truncate(file) _chsize_s(_fileno(file), ftello(file))
#define tell(file) ftello(file)
#define read(file, ptr, size) fread(ptr, 1, size, file)
#define write(file, ptr, size) fwrite(ptr, 1, size, file)
#define delete(file) _wunlink(file)
#else
#include <unistd.h>
#define seek(file, offset, whence) fseeko(file, offset, whence)
#define truncate(file) ftruncate(fileno(file), ftello(file))
#define tell(file) ftello(file)
#define read(file, ptr, size) fread(ptr, 1, size, file)
#define write(file, ptr, size) fwrite(ptr, 1, size, file)
#define delete(file) unlink(file)
#endif

// Buffer length for file IO operations (64 KiB)
static const size_t FileBufferLen = 64*1024;

/* Processes 'in_file', writing the result to 'out_file'. Requires that
'in_file' be opened for reading in binary mode, and 'out_file' be opened for
reading and writing in binary mode. Both 'in_file' and 'out_file' must support
Expand Down

0 comments on commit 2a4d1f2

Please sign in to comment.